DEV Community

Cover image for Build a Portable Development Playground with Vagrant and VirtualBox
Durrell  Gemuh
Durrell Gemuh

Posted on

Build a Portable Development Playground with Vagrant and VirtualBox

Stop saying "it works on my machine." Start shipping consistent environments.

Introduction

Every developer has been there: you clone a repo, follow the README, and two hours later you're deep in dependency hell — wrong Python version, missing system library, or some obscure config that only works on macOS Ventura and above.

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

💡 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
Enter fullscreen mode Exit fullscreen mode

The first run will:

  1. Download the ubuntu/jammy64 base box (~500 MB, cached locally for future use)
  2. Create and configure a VirtualBox VM
  3. 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
Enter fullscreen mode Exit fullscreen mode

You're now inside the VM. Your project folder is mounted at /vagrant:

cd /vagrant
ls        # same files as your host machine!
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

...and they have an identical environment to yours. No more "works on my machine."

A minimal .gitignore for Vagrant projects:

.vagrant/
*.log
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Start all machines with vagrant up, or target one with vagrant up web.

Troubleshooting

VirtualBox kernel module error on Linux:

sudo /sbin/vboxconfig
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)