How to Set Up a Local Docker Host by Using Vagrant: Development Environment Configuration Guide

How T Set Up a Local Docker Host by Using Vagrant

How T Set Up a Local Docker Host by Using Vagrant

Long before Docker Desktop existed for Mac and Windows, running Docker meant running Linux, and the easiest way to get a consistent Linux VM on any host operating system was Vagrant. Even today, I still reach for Vagrant when I want a fully isolated, reproducible Docker host that’s separate from my actual laptop — useful for testing daemon configuration changes, experimenting with different Docker versions side by side, or giving a team a byte-for-byte identical environment regardless of whether they’re on macOS, Windows, or Linux.

What Vagrant Actually Provides

Vagrant is a tool for describing and provisioning virtual machines declaratively, using a Vagrantfile. It sits on top of a virtualization provider — VirtualBox, VMware, Hyper-V, or libvirt — and automates VM creation, networking, shared folders, and provisioning scripts. For this guide I’ll use VirtualBox, since it’s free and cross-platform.

Prerequisites

vagrant --version
Vagrant 2.4.1

Step 1: Create a Project Directory

mkdir docker-vagrant-host && cd docker-vagrant-host

Step 2: Initialize a Vagrantfile

vagrant init ubuntu/jammy64
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

Step 3: Edit the Vagrantfile to Provision Docker Automatically

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/jammy64"
  config.vm.hostname = "docker-host"

  config.vm.network "private_network", ip: "192.168.56.10"
  config.vm.network "forwarded_port", guest: 80, host: 8080

  config.vm.provider "virtualbox" do |vb|
    vb.memory = "4096"
    vb.cpus = 2
  end

  config.vm.provision "shell", inline: <<-SHELL
    apt-get update
    apt-get install -y ca-certificates curl gnupg

    install -m 0755 -d /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    chmod a+r /etc/apt/keyrings/docker.asc

    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
      https://download.docker.com/linux/ubuntu \
      $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
      tee /etc/apt/sources.list.d/docker.list > /dev/null

    apt-get update
    apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

    usermod -aG docker vagrant
  SHELL
end

A few decisions worth explaining:

Step 4: Bring Up the VM

vagrant up

Expected output (abbreviated):

Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'ubuntu/jammy64'...
==> default: Setting the name of the VM: docker-vagrant-host_default
==> default: Configuring and enabling network interfaces...
==> default: Running provisioner: shell...
    default: Running: inline script
    default: Reading package lists...
    default: Setting up docker-ce (5:27.3.1-1~ubuntu.22.04~jammy) ...
    default: Setting up docker-ce-cli (5:27.3.1-1~ubuntu.22.04~jammy) ...

The full provisioning run typically takes three to six minutes depending on connection speed.

Step 5: SSH Into the VM

vagrant ssh
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-101-generic x86_64)
vagrant@docker-host:~$

Verify Docker:

docker --version
docker run hello-world
Docker version 27.3.1, build ce12230
...
Hello from Docker!
This message shows that your installation appears to be working correctly.

Step 6: Run a Real Container and Reach It From the Host

Inside the VM:

docker run -d -p 80:80 --name vm-nginx nginx:latest

From my actual laptop (outside the VM):

curl http://localhost:8080
<!DOCTYPE html>
<html>
<head><title>Welcome to nginx!</title></head>
...

The forwarded port I defined in the Vagrantfile makes port 80 inside the VM reachable at port 8080 on my host machine.

Step 7: Shared Folders for Development

By default, Vagrant syncs the project directory (where the Vagrantfile lives) into /vagrant inside the VM — handy for editing Dockerfiles and Compose files on my host with my normal editor, while building and running them inside the VM:

vagrant ssh -c "ls /vagrant"
Vagrantfile

Step 8: Managing the VM Lifecycle

vagrant halt      # gracefully shuts down the VM
vagrant suspend    # saves state and pauses, faster to resume
vagrant resume     # resumes a suspended VM
vagrant destroy    # deletes the VM entirely
vagrant destroy -f
==> default: Forcing shutdown of VM...
==> default: Destroying VM and associated drives...

Internal Working: What Vagrant Is Doing Underneath

Vagrant itself doesn’t run VMs — it’s an orchestration layer over a provider (VirtualBox here). When I run vagrant up, Vagrant reads the Vagrantfile, calls the VirtualBox provider driver to import the specified base box (a pre-built VM disk image plus metadata), configures the VM’s virtual NICs and forwarded ports via VirtualBox’s own CLI (VBoxManage), boots it, waits for SSH to become reachable, then executes the shell provisioner script over that SSH connection exactly as if I’d typed the commands myself. The Docker installation itself is entirely standard — Vagrant contributes nothing Docker-specific; it’s simply automating “boot a fresh Ubuntu VM and run these exact commands on it.”

Networking Considerations

Two networking modes are active simultaneously here: the default NAT interface (which is how vagrant ssh reaches the VM, and how the VM reaches the internet to install packages), and the private_network interface I added, which puts the VM on an isolated host-only network at a fixed IP. Port forwarding is a third, independent mechanism layered on top of the NAT interface — it’s why localhost:8080 on my host reaches port 80 inside the VM without needing the private network IP at all.

Storage Considerations

The VM’s virtual disk is separate from my host filesystem entirely, except for the synced /vagrant folder. Docker images and containers pulled/created inside the VM live on that virtual disk, which means destroying the VM (vagrant destroy) wipes all pulled images and container data — intentional for a disposable dev environment, but worth remembering if I’ve built something I want to keep.

Security Considerations

Troubleshooting

“VT-x is not available” or virtualization errors Usually means hardware virtualization isn’t enabled in the host machine’s BIOS, or it’s conflicting with another hypervisor (like Hyper-V on Windows) that’s already claimed it.

Provisioning script fails partway through Re-run just the provisioner without recreating the whole VM:

vagrant provision

Forwarded port already in use on the host

Vagrant cannot forward the specified ports on this VM, since they
would collide with some other application that is already listening
on these ports.

Change the host-side port number in the Vagrantfile’s forwarded_port line and run vagrant reload.

Monitoring

vagrant ssh -c "docker stats --no-stream"
vagrant global-status
id       name    provider   state   directory
a1b2c3d  default virtualbox running /home/user/docker-vagrant-host

Best Practices

Summary

Vagrant gives me a fully reproducible, disposable Linux VM with Docker installed via a single declarative file and one command, independent of whatever operating system my actual laptop runs. It’s an excellent way to test Docker daemon configurations, try different Docker versions, or hand a team an identical environment — all without touching my host machine’s own software directly.

References

Exit mobile version