Vagrant paired with VirtualBox solves this elegantly. You define your environment as code, and anyone on your team — Windows, macOS, or Linux — spins up an identical VM in minutes.
In this guide, you'll go from zero to a fully reproducible development playground, complete with provisioning, shared folders, and port forwarding.
Prerequisites
Before we start, make sure you have:
Verify your installs:
vagrant --version
# Vagrant 2.4.x
VBoxManage --version
# 7.x.x
Step 1 — Initialize Your Project
Create a new directory for your playground and initialize Vagrant inside it:
mkdir my-dev-playground && cd my-dev-playground
vagrant init ubuntu/jammy64
This generates a Vagrantfile in your current directory. The ubuntu/jammy64 box is Ubuntu 22.04 LTS — a solid, well-maintained base image pulled from the Vagrant Cloud.
Step 2 — Configure the Vagrantfile
Open the generated Vagrantfile and replace its contents with this well-commented configuration:
Vagrant.configure("2") do |config|
# ── Base Box ────────────────────────────────────────────────────
config.vm.box = "ubuntu/jammy64"
config.vm.box_check_update = false
# ── Networking ──────────────────────────────────────────────────
# Access your VM's web server at http://localhost:8080 on the host
config.vm.network "forwarded_port", guest: 80, host: 8080
config.vm.network "forwarded_port", guest: 3000, host: 3000 # Node/React
config.vm.network "forwarded_port", guest: 5432, host: 5432 # PostgreSQL
config.vm.network "forwarded_port", guest: 6379, host: 6379 # Redis
# Private network — access VM directly at this IP from the host
config.vm.network "private_network", ip: "192.168.56.10"
# ── Shared Folders ──────────────────────────────────────────────
# Your project folder syncs automatically into the VM
config.vm.synced_folder ".", "/vagrant", type: "virtualbox"
# ── Provider Settings (VirtualBox) ──────────────────────────────
config.vm.provider "virtualbox" do |vb|
vb.name = "dev-playground"
vb.memory = "2048" # 2 GB RAM — adjust as needed
vb.cpus = 2
# Faster DNS resolution inside the VM
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
end
# ── Provisioning ────────────────────────────────────────────────
# This shell script runs ONCE when you first `vagrant up`
config.vm.provision "shell", inline: <<-SHELL
echo "==> Updating package lists..."
apt-get update -qq
echo "==> Installing core tools..."
apt-get install -y -qq \
git curl wget unzip build-essential \
software-properties-common apt-transport-https
echo "==> Installing Node.js 20.x..."
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y nodejs
echo "==> Installing Python 3 + pip..."
apt-get install -y python3 python3-pip python3-venv
echo "==> Installing Docker..."
curl -fsSL https://get.docker.com | sh
usermod -aG docker vagrant
echo "==> Installing PostgreSQL..."
apt-get install -y postgresql postgresql-contrib
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';"
sudo -u postgres psql -c "CREATE DATABASE devdb;" 2>/dev/null || true
echo "==> Installing Redis..."
apt-get install -y redis-server
sed -i 's/^bind 127.0.0.1/bind 0.0.0.0/' /etc/redis/redis.conf
systemctl restart redis-server
echo "==> All done! Your playground is ready."
SHELL
end
💡 Tip: The <<-SHELL block is a Bash heredoc — any valid shell script goes here. You can also point to an external script file with config.vm.provision "shell", path: "provision.sh" to keep things tidy.
Step 3 — Boot the VM
vagrant up
The first run will:
- Download the
ubuntu/jammy64 base box (~500 MB, cached locally for future use)
- Create and configure a VirtualBox VM
- Run the provisioning script
Subsequent vagrant up calls take only 5–10 seconds since the box is already downloaded and provisioned.
Step 4 — Connect to Your VM
vagrant ssh
You're now inside the VM. Your project folder is mounted at /vagrant:
cd /vagrant
ls # same files as your host machine!
Everything you install globally inside the VM is isolated from your host OS. Clean and reproducible.
Step 5 — Verify Your Stack
Once SSH'd in, quickly verify the tools are running:
# Node.js
node --version && npm --version
# Python
python3 --version && pip3 --version
# Docker
docker --version
docker run hello-world
# PostgreSQL
psql -U postgres -h localhost -c "\l"
# Redis
redis-cli ping # should return PONG
From your host machine, you can connect to services using the forwarded ports:
# PostgreSQL from host (use any GUI like TablePlus, DBeaver)
psql -h localhost -p 5432 -U postgres -d devdb
# Redis from host
redis-cli -h localhost -p 6379 ping
Step 6 — Everyday Workflow
| Command |
What it does |
vagrant up |
Start the VM |
vagrant ssh |
SSH into the VM |
vagrant halt |
Gracefully shut down the VM |
vagrant reload |
Restart the VM (picks up Vagrantfile changes) |
vagrant provision |
Re-run the provisioning script |
vagrant destroy |
Delete the VM entirely (Vagrantfile stays) |
vagrant snapshot save <name> |
Save a snapshot of the current VM state |
Reprovisioning: If you update the Vagrantfile provisioning script, run vagrant reload --provision to apply changes without destroying the VM.
Step 7 — Share Your Environment with the Team
The beauty of this setup: commit your Vagrantfile (and any provision.sh scripts) to your repository. Anyone with Vagrant + VirtualBox installed runs:
git clone https://github.com/yourorg/your-repo.git
cd your-repo
vagrant up
...and they have an identical environment to yours. No more "works on my machine."
A minimal .gitignore for Vagrant projects:
.vagrant/
*.log
Bonus: Multi-Machine Setup
Vagrant can spin up multiple VMs in a single Vagrantfile — perfect for simulating a microservices or client/server architecture:
Vagrant.configure("2") do |config|
config.vm.define "web" do |web|
web.vm.box = "ubuntu/jammy64"
web.vm.network "private_network", ip: "192.168.56.11"
web.vm.provision "shell", inline: "apt-get update && apt-get install -y nginx"
end
config.vm.define "db" do |db|
db.vm.box = "ubuntu/jammy64"
db.vm.network "private_network", ip: "192.168.56.12"
db.vm.provision "shell", inline: "apt-get update && apt-get install -y postgresql"
end
end
Start all machines with vagrant up, or target one with vagrant up web.
Troubleshooting
VirtualBox kernel module error on Linux:
sudo /sbin/vboxconfig
Slow synced folder performance on macOS:
Consider switching to NFS: config.vm.synced_folder ".", "/vagrant", type: "nfs"
Port already in use:
Change the host: port number in the forwarded_port config, or stop the conflicting service on your host.
"Box not found" error:
Search available boxes at app.vagrantup.com/boxes/search.
Wrapping Up
You now have a fully portable, version-controlled development playground that:
- Runs identically on any OS
- Installs your entire stack automatically on first boot
- Forwards ports so host tools work seamlessly
- Syncs your code files in real time
- Can be destroyed and recreated in minutes
Once you're comfortable with this setup, consider exploring Ansible provisioning for more complex stacks, or Docker-in-Vagrant for a hybrid container + VM workflow.
Happy hacking! 🚀
Found this useful? Drop a ❤️ and share it with a teammate who's still fighting environment issues. Questions or improvements? Leave a comment below!