Creating a Project Directory
Every Vagrant environment lives in its own directory. Let's create one:
mkdir ~/vagrant-lab
cd ~/vagrant-labInitializing a Vagrant Environment
The vagrant init command creates a template Vagrantfile:
vagrant init hashicorp/bionic64This creates a Vagrantfile with comments explaining each option. However, we want to use Docker — not VirtualBox. Let's replace the contents entirely.
Writing Your First Vagrantfile
Open the Vagrantfile and replace everything with:
ENV['VAGRANT_DEFAULT_PROVIDER'] = 'docker'
Vagrant.configure("2") do |config|
config.vm.provider "docker" do |container|
container.image = "nginx"
container.ports = ["8080:80"]
container.name = "my-first-container"
end
config.vm.synced_folder ".", "/vagrant"
endUnderstanding Each Line
| Line | Meaning |
|---|---|
ENV['VAGRANT_DEFAULT_PROVIDER'] = 'docker' | Use Docker as the provider |
| `Vagrant.configure("2") do | config |
config.vm.provider "docker" | Configure Docker-specific settings |
container.image = "nginx" | Use the nginx Docker image |
container.ports = ["8080:80"] | Map host port 8080 → container port 80 |
container.name = "my-first-container" | Name the container |
config.vm.synced_folder ".", "/vagrant" | Sync current directory to /vagrant in container |
Validating the Vagrantfile
Always validate before deploying:
vagrant validateExpected output:
Vagrantfile validated successfully.
Deploying the Environment
vagrant upOutput:
Bringing machine 'default' up with 'docker' provider...
==> default: Creating and configuring docker networks...
==> default: Creating the container...
default: Name: my-first-container
default: Image: nginx
default: Volume: /home/user/vagrant-lab:/vagrant
default: Port: 8080:80
default:
default: Container created: 46a172843afc
==> default: Starting container...
Vagrant pulled the nginx image and started a container. You can verify with:
vagrant statusOutput:
Current machine states:
default running (docker)
Checking the Container
Since we mapped port 8080, you can test the nginx server:
curl http://localhost:8080You should see the nginx welcome page HTML.
The SSH Problem
Try connecting:
vagrant sshThis will fail because the standard nginx image doesn't include an SSH server. To use vagrant ssh, we need a custom image.
Building an SSH-Enabled Image
Create a Dockerfile in your project directory:
FROM ubuntu:focal
RUN apt-get update && \
apt-get -y install openssh-server passwd sudo iproute2 \
git curl iputils-ping net-tools wget && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Enable systemd
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*; \
rm -f /etc/systemd/system/*.wants/*; \
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*; \
rm -f /lib/systemd/system/anaconda.target.wants/*
RUN systemctl enable ssh.service
# Create vagrant user with SSH access
RUN useradd --create-home -s /bin/bash vagrant && \
echo -e "vagrant\nvagrant" | (passwd --stdin vagrant) && \
echo 'vagrant ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/vagrant && \
chmod 440 /etc/sudoers.d/vagrant
# Set up SSH keys
RUN mkdir -p /home/vagrant/.ssh && \
chmod 700 /home/vagrant/.ssh
ADD https://raw.githubusercontent.com/hashicorp/vagrant/master/keys/vagrant.pub /home/vagrant/.ssh/authorized_keys
RUN chmod 600 /home/vagrant/.ssh/authorized_keys && \
chown -R vagrant:vagrant /home/vagrant/.ssh
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]What This Dockerfile Does
- Base image — Ubuntu 20.04 (Focal)
- Installs SSH — OpenSSH server for remote access
- Enables systemd — Service management inside the container
- Creates vagrant user — With sudo privileges, no password required
- Configures SSH keys — Uses Vagrant's insecure keypair for initial access
- Mounts cgroup — Required for systemd to function
Building the Image
docker build -t nextgenplayground:vagrant .Updating the Vagrantfile
Now destroy the old container and update the Vagrantfile:
vagrant destroy -fReplace the Vagrantfile contents:
ENV['VAGRANT_DEFAULT_PROVIDER'] = 'docker'
Vagrant.configure("2") do |config|
config.vm.define "lab" do |lab|
lab.vm.network "forwarded_port", guest: 80, host: 8080
lab.vm.provider "docker" do |docker|
docker.image = "nextgenplayground:vagrant"
docker.has_ssh = true
docker.privileged = true
docker.create_args = ["-v", "/sys/fs/cgroup:/sys/fs/cgroup:ro"]
docker.name = "lab"
end
end
endKey Differences
| Setting | Purpose |
|---|---|
docker.has_ssh = true | Tells Vagrant SSH is available |
docker.privileged = true | Allows systemd to run |
docker.create_args | Mounts cgroup volume |
Deploying and Connecting
vagrant upOutput:
==> lab: Creating the container...
==> lab: Starting container...
==> lab: Waiting for machine to boot...
lab: SSH address: 127.0.0.1:2222
lab: SSH username: vagrant
lab: Vagrant insecure key detected. Vagrant will automatically replace
lab: this with a newly generated keypair for better security.
==> lab: Machine booted and ready!
Now connect:
vagrant sshOutput:
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.x.x x86_64)
vagrant@lab:~$
You're inside the container! Run any command:
whoami # vagrant
hostname # lab
ip addr # see network interfaces
exit # return to hostVagrant Lifecycle Commands
# Start environment
vagrant up
# Connect via SSH
vagrant ssh
# Stop (preserve state)
vagrant halt
# Restart (reload config)
vagrant reload
# Delete everything
vagrant destroy -f
# Check status
vagrant status
vagrant global-statusSummary
- A Vagrantfile describes your environment in Ruby syntax
vagrant validatechecks for configuration errorsvagrant upcreates and starts the environment- Standard Docker images don't support SSH — you need a custom image
- The custom Dockerfile adds SSH, systemd, and the vagrant user
vagrant sshconnects you to the running containervagrant destroy -fremoves everything cleanly
Next Steps
Now that you can create and connect to environments, let's explore networking — port forwarding, private networks, and multi-container architectures.