Sitemap

Vagrant setup for Ansible on Docker

7 min readAug 5, 2025
Press enter or click to view image in full size

What is Vagrant

Vagrant is open source tool for provision and manage virtual machines. It is managed by HashiCorp.

Why need Vagrant

Let me take a simple example, Lets say you have to build 5 Virtual Machines on VMware or VirtualBox (or any hypervisor platform). You need to build each and every VM manually by assign the OS, packages to install, users to configure and mounts to be available. It is okay to do for a single instance for one time, but lets assume you need to build 5 VMs multiple times with rebuilding(destroy and rebuild). Isn’t it receptive task ?

Thats how Vagrant came in to the picture, you can easily use Vagrant (Vagrantfile) to automate build and destroy using a few commands.

image : https://howtoprogram.xyz/2016/07/24/vagrant-docker-provider-tutorial/

What need for Vagrant?

Vagrant need to have a hypervisor tool (VMware, VirtalBox,…) to run VMs, it is just VM automate tool on top of hypervisor.

Can Vagrant work without Hypervisors (VMware, VirtBox,…) ?

Yes, if you have Docker (Docker Engine) or LibVirt/KVM. So instead of VMware (or any hypervisor) you can use docker as platform to build docker container to use as VMs. So in this case you can use docker containers (for Virtual machines) same purpose.

Why prefer docker on Vagrant?

Lets see why I prefer docker using vagrant instead of vmware/virtualabox on vagrant.

First of all it is container based, so it has shared kernerl with host node and quick start/boot time compared to a VM. Docker image is very light weight but VM OS image is bigger size. Docker setup can use for micro services environments, but VMs are mostly for monolithic setups. Docker has no licences cost but VMware does.

Now lets see drawbacks..

Docker not suitable for isolated setups. You only can provision same kernel containers (not support for Windows on Linux or Linux on Windows). Lower security, network isolations and not suitable for persistent storage use-cases.

Repository of code
Code for below example has been uploaded into git repository can be accessible from here.

https://github.com/heshand/Vagrant-ansible/tree/main

How to use Vagrant using Docker

Make sure docker is running (docker engine) on your machine

# docker info

Create a new file called Vagrantfile
# vim Vagrantfile

Vagrant.configure("2") do |config|
# Docker provider configuration
config.vm.provider "docker" do |d|
# Build from local Dockerfile
d.build_dir = "./"

# Container settings
d.name = "alpine-vagrant-ansible"
d.remains_running = true
d.has_ssh = true
d.privileged = true

# Volume mounts
d.create_args = [
"-v", "/var/run/docker.sock:/var/run/docker.sock",
"-v", "/Users/uwidha/test1/vagrant/local:/local"
]
end

# SSH configuration
config.ssh.username = "vagrant"
config.ssh.password = "vagrant"

# Install Ansible
config.vm.provision "shell", inline: <<-SHELL
apk update
apk add ansible
SHELL
end

Okay, now we need to pass Docker container configuration into Vagrant.

config.vm.provider "docker" do |d|
# Build from local Dockerfile
d.build_dir = "./"

Above config in Vagrantfile is looking for a docker config, so we need to have a Dockerfile lie below.

# vim Dockerfile

FROM alpine:3.18

# Install essential packages
RUN apk add --no-cache \
sudo \
openssh \
openssh-server \
python3 \
py3-pip \
bash \
&& rm -rf /var/cache/apk/*

# Create vagrant user with sudo access
RUN adduser -D -s /bin/bash vagrant && \
echo "vagrant:vagrant" | chpasswd && \
echo "vagrant ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

# Configure SSH
RUN mkdir -p /var/run/sshd && \
ssh-keygen -A && \
mkdir -p /home/vagrant/.ssh && \
chmod 700 /home/vagrant/.ssh

# Set up SSH key for vagrant user (Vagrant's insecure key)
RUN echo "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+vU2RW3gYpKdMckIdPWf/6Ll/xmYMPdnyZIGVq4LB9uPKcsO9JfGTi1i2yReQqyxV06B/ouAkFjDVNYJRufGc4iaEzgaxFvgfFuv5R/wIDAQAB vagrant insecure public key" > /home/vagrant/.ssh/authorized_keys && \
chmod 600 /home/vagrant/.ssh/authorized_keys && \
chown -R vagrant:vagrant /home/vagrant/.ssh

# Configure SSH daemon for Alpine
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config && \
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config && \
sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config && \
sed -i 's/#AuthorizedKeysFile/AuthorizedKeysFile/' /etc/ssh/sshd_config && \
echo "UsePAM no" >> /etc/ssh/sshd_config

# Expose SSH port
EXPOSE 22

# Start SSH daemon and keep container running
CMD ["/usr/sbin/sshd", "-D"]

This Dockerfile in to setup a docker container with ansible, vagrant user, ssh config and ssh port.

Now, when we hit “vagrant up” ; it executes Vagrantfile, Vagrantfile is trying to spin up continer using docker prodiver. Docker provider needs a Dockerfile to start a container and it is available there now.

# vagrant up

# vagrant up
Bringing machine 'default' up with 'docker' provider...
==> default: Creating and configuring docker networks...
==> default: Building the container from a Dockerfile...
default: #1 [internal] load build definition from Dockerfile
default: #1 sha256:d6b5c75ae7b13dc31ad2f75cabdcba14de5ab986b0fa78d98e66bb81bf7c25d6
default: #1 transferring dockerfile: 74B done
default: #1 DONE 0.0s
default:
default: #2 [internal] load .dockerignore
default: #2 sha256:a819eb4554d4169ca1494d8cf68dd6504621392b5fc945db7af5b2fe96f40460
default: #2 transferring context: 2B done
default: #2 DONE 0.0s
default:
default: #3 [internal] load metadata for docker.io/library/alpine:3.18
default: #3 sha256:372b7bc0d0f7e0ed512d224c53cd851ad9eb6958e4ca991f1a6b6e63325d9a04
default: #3 DONE 2.3s
default:
default: #4 [1/6] FROM docker.io/library/alpine:3.18@sha256:de0eb0b3f2a47ba1eb89389859a9bd88b28e82f5826b6969ad604979713c2d4f
default: #4 sha256:82956e6596f60dd82d72694fbd054903f9041222b13d9d2b7f1b0bc8939a9abd
default: #4 DONE 0.0s
default:
default: #8 [5/6] RUN echo "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+vU2RW3gYpKdMckIdPWf/6Ll/xmYMPdnyZIGVq4LB9uPKcsO9JfGTi1i2yReQqyxV06B/ouAkFjDVNYJRufGc4iaEzgaxFvgfFuv5R/wIDAQAB vagrant insecure public key" > /home/vagrant/.ssh/authorized_keys && chmod 600 /home/vagrant/.ssh/authorized_keys && chown -R vagrant:vagrant /home/vagrant/.ssh
default: #8 sha256:d7e108f6b870e9d5014c676b307846168bdde087f9dcba2e021fccd78a7ada10
default: #8 CACHED
default:
default: #5 [2/6] RUN apk add --no-cache sudo openssh openssh-server python3 py3-pip bash && rm -rf /var/cache/apk/*
default: #5 sha256:694034aa4a6c255da852c54ebd7f12e7c242f70ce7ba58cfa28bc8c46539c1ae
default: #5 CACHED
default:
default: #6 [3/6] RUN adduser -D -s /bin/bash vagrant && echo "vagrant:vagrant" | chpasswd && echo "vagrant ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
default: #6 sha256:7c060a78ad258533b2420395c90c96bd209f473c8a464e559d3c72ad34d8d9db
default: #6 CACHED
default: #7 [4/6] RUN mkdir -p /var/run/sshd && ssh-keygen -A && mkdir -p /home/vagrant/.ssh && chmod 700 /home/vagrant/.ssh
default: #7 sha256:a1ae6b5e1307117c7edf2e15a21f1035ff802c2684026af593ad9b8babe43b87
default: #7 CACHED
default:
default: #9 [6/6] RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config && sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config && sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config && sed -i 's/#AuthorizedKeysFile/AuthorizedKeysFile/' /etc/ssh/sshd_config && echo "UsePAM no" >> /etc/ssh/sshd_config
default: #9 sha256:f0cdf8098fd398ecdc41a6faf6f5cafb00e2638cfacc762067c0079f76a159ba
default: #9 CACHED
default:
default: #10 exporting to image
default: #10 sha256:e8c613e07b0b7ff33893b694f7759a10d42e180f2b4dc349fb57dc6b71dcab00
default: #10 exporting layers done
default: #10 writing image sha256:141bd6f0808c7aa7d3458200f578bae5a3c86bb3d68cc558871c706e72e50d08 done
default: #10 DONE 0.0s
default:
........
default: Container created: 4a77330f5e48112c
==> default: Enabling network interfaces...
==> default: Starting container...
==> default: Waiting for machine to boot. This may take a few minutes...
default: SSH address: 127.0.0.1:2222
default: SSH username: vagrant
default: SSH auth method: password
default:
default: Inserting generated public key within guest...
default: Removing insecure key from the guest if it's present...
default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Running provisioner: shell...
default: Running: inline script
default: fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/main/aarch64/APKINDEX.tar.gz
default: fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/community/aarch64/APKINDEX.tar.gz
default: v3.18.12-136-gf28e1c9c9f8 [https://dl-cdn.alpinelinux.org/alpine/v3.18/main]
default: v3.18.12-132-ge2ea6f61358 [https://dl-cdn.alpinelinux.org/alpine/v3.18/community]
default: OK: 19946 distinct packages available
default: (1/25) Installing py3-cparser (2.21-r2)
default: (2/25) Installing py3-cparser-pyc (2.21-r2)
default: (3/25) Installing py3-cffi (1.15.1-r3)
default: (4/25) Installing py3-cffi-pyc (1.15.1-r3)
default: (5/25) Installing py3-cryptography (41.0.3-r0)
default: (6/25) Installing py3-cryptography-pyc (41.0.3-r0)
default: (7/25) Installing py3-markupsafe (2.1.2-r1)
default: (8/25) Installing py3-markupsafe-pyc (2.1.2-r1)
default: (9/25) Installing py3-jinja2 (3.1.6-r0)
default: (10/25) Installing py3-jinja2-pyc (3.1.6-r0)
default: (11/25) Installing py3-bcrypt (4.0.1-r2)
default: (12/25) Installing py3-bcrypt-pyc (4.0.1-r2)
default: (13/25) Installing py3-pynacl (1.5.0-r4)
default: (14/25) Installing py3-pynacl-pyc (1.5.0-r4)
default: (15/25) Installing py3-paramiko (3.1.0-r1)
default: (16/25) Installing py3-paramiko-pyc (3.1.0-r1)
default: (17/25) Installing py3-resolvelib (1.0.1-r0)
default: (18/25) Installing py3-resolvelib-pyc (1.0.1-r0)
default: (19/25) Installing yaml (0.2.5-r1)
default: (20/25) Installing py3-yaml (6.0-r3)
default: (21/25) Installing py3-yaml-pyc (6.0-r3)
default: (22/25) Installing ansible-core (2.14.5-r0)
default: (23/25) Installing ansible-core-pyc (2.14.5-r0)
default: (24/25) Installing ansible-pyc (7.5.0-r0)
default: (25/25) Installing ansible (7.5.0-r0)
default: Executing busybox-1.36.1-r7.trigger
default: OK: 544 MiB in 75 packages

Now, vagrant has spin up a ansible container for us.

# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4a77330f5e48 141bd6f0808c "/usr/sbin/sshd -D" 2 minutes ago Up 2 minutes 127.0.0.1:2222->22/tcp alpine-vagrant-ansible

You may see a docker container running

# vagrant status
Current machine states:

default running (docker)

The container is created and running. You can stop it using
`vagrant halt`, see logs with `vagrant docker-logs`, and
kill/destroy it with `vagrant destroy`.

Log into Docker

# vagrant ssh
Welcome to Alpine!

The Alpine Wiki contains a large amount of how-to guides and general
information about administrating Alpine systems.
See <https://wiki.alpinelinux.org/>.

You can setup the system with the command: setup-alpine

You may change this message by editing /etc/motd.

4a77330f5e48:~$
4a77330f5e48:~$ cat /local/file1.txt
Hello World

you can see local folder has mounted to docker container. Go inside /local/file1.txt and see the content. This way you can mount anything into your docker container via vagrant.

Important Vagrant commands

# vagrant validate

To validate Vagrantfile before run “ vagrant up”

# vagrant up

Start vagrant container

# vagrant status

To check status of vagrant container

# vagrant ssh

SSH into vagrant container

# vagrant reload

Rebuild vagrant container ( Similar to destroy and up)

# vagrant destroy

# vagrant destroy -f

Destroy vagrant container. -f to destroy. running container forcefully.

# vagrant halt

# vagrant resume

To stop container without destroying and start stopped container.

Heshan Dharmasena
Heshan Dharmasena

Written by Heshan Dharmasena

Passionate about streamlining software delivery and building scalable, resilient infrastructure. https://CloudOps.lk/