Classic Scenario: “It works on my machine, so why does it fail on the server?”
Every developer has likely experienced the bitter taste of code running perfectly locally but crashing as soon as it hits staging. The culprit is usually a mismatch in PHP versions, missing system libraries, or differing Nginx configurations. Instead of manually installing every package and wasting an entire morning, I choose to automate the whole process.
Currently, I run a Proxmox homelab with about 12 VMs. This is where I test everything before pushing it to production. To ensure my 5-person team always uses a standardized environment, I use the **Vagrant** and **Ansible** duo. Vagrant handles the skeleton (VM, IP, RAM), while Ansible takes care of the “meat” (installing software, configuring services).
Why use Ansible instead of Shell Scripts?
Many of you might wonder: “Vagrant has a shell provisioner, why bother learning Ansible?” The answer lies in **Idempotency**. If you run a shell script to install Nginx twice, it might throw an error or overwrite configuration files unpredictably. With Ansible, you can run a playbook 100 times; if the machine is already correctly configured, it won’t do anything. This makes it extremely safe for your systems.
- Vagrant: Manages the infrastructure layer. It controls VirtualBox or KVM to quickly create VMs.
- Ansible: Manages configuration. It uses SSH to log into the VM and execute commands based on a predefined script.
Required Tools
To get started, download these tools to your host machine:
- VirtualBox: A popular and free virtual machine emulator.
- Vagrant: A tool for orchestrating the virtual machine lifecycle.
- Ansible: Install on macOS/Linux to control the VMs. If you’re on Windows, don’t worry—we’ll use the
ansible_localmode.
Building an Automated Web Server in 5 Minutes
We will create an Ubuntu 22.04 VM, automatically install Nginx, and create a custom index page. Everything is contained within just two configuration files.
Step 1: Create the project directory
mkdir vagrant-ansible-demo && cd vagrant-ansible-demo
touch Vagrantfile playbook.yml
Step 2: Configure the Vagrantfile
This file defines the VM parameters. Note the config.vm.provision section; this is where Vagrant hands over control to Ansible.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/jammy64"
config.vm.network "private_network", ip: "192.168.56.10"
config.vm.provider "virtualbox" do |vb|
vb.memory = "1024"
vb.cpus = 1
end
config.vm.provision "ansible" do |ansible|
ansible.playbook = "playbook.yml"
end
end
Step 3: Write the Ansible Playbook
The playbook.yml file uses the highly readable YAML format. You just list what you want, and Ansible handles the execution.
---
- name: Setup Web Server
hosts: all
become: true
tasks:
- name: Update apt cache
apt: { update_cache: yes, cache_valid_time: 3600 }
- name: Install Nginx
apt: { name: nginx, state: present }
- name: Custom Index Page
copy:
content: "<h1>Successfully deployed from itfromzero.com!</h1>"
dest: /var/www/html/index.html
- name: Start Nginx
service: { name: nginx, state: started, enabled: yes }
Execution and Verifying Results
Now it’s time to enjoy the results. Open your terminal and type:
vagrant up
Vagrant will automatically download the image, create the VM, and call Ansible. You’ll see logs scrolling by. If the text appears green or yellow, everything is fine. It takes about 2-3 minutes to complete the entire server setup. Visit http://192.168.56.10, and if you see the welcome message, congratulations—you’ve succeeded!
Troubleshooting Tips (Debug)
Working with VMs can occasionally lead to minor issues. Here are a few tips I’ve gathered:
- SSH Connection Errors: If Ansible cannot access the VM, run
vagrant ssh-configto check the port and key. Sometimes, a simplevagrant reloadis all you need. - Using Windows? Switch to
config.vm.provision "ansible_local". In this mode, Vagrant will install Ansible inside the VM itself and run the playbook from within. - Check Syntax: YAML files are very sensitive to whitespace. Use the command
ansible-playbook --syntax-check playbook.ymlto spot errors before running.
Conclusion
Combining Vagrant and Ansible makes your workflow much more professional. When a new team member joins, they just need to git clone and vagrant up to have an environment identical to yours. This is a solid stepping stone for diving deeper into the world of DevOps and Infrastructure as Code (IaC).

