Use Vagrant to configure VMs
In the first part of tutorial we learned how to provision local virtual machine servers on MacBook Pro (Apple silicon) with Vagrant and VMware Fusion.
This time, we will use Vagrant and Vagrantfile to run setup on VMs while they are being created.
What is Vagrantfile
Each Vagrant project has a single configuration file called Vagranfile
.
Vagrantfile describes the type of the virtual machine we use and how it has to be configured while provisioning on hypervisor.
Vagrantfile should be located anywhere within your project directory structure. Or alternatevly you can provide enviroenment variable called VAGRANT_CWD
.
vagrant init
allows us to automatically generate Vagrantfiles. We have created one such file in our previous part of tutorial. Here is the command we used
vagrant init bento/debian-12.6 --box-version 202407.22.0
It produces the following Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://vagrantcloud.com/search.
config.vm.box = "bento/debian-12.6"
config.vm.box_version = "202407.22.0"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
# NOTE: This will enable public access to the opened port
# config.vm.network "forwarded_port", guest: 80, host: 8080
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine and only allow access
# via 127.0.0.1 to disable public access
# config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
# Create a private network, which allows host-only access to the machine
# using a specific IP.
# config.vm.network "private_network", ip: "192.168.33.10"
...
There are multiple configurations examples present, they are commented out. With a good explanation and documentation for each setting.
We are going to remove most of the commented out settings and will add back a few sections we need for our setup.
Opening server ports
First, we are going to add forwarded port mapping, so we can map the ports on our development machine (a host machine) to the open ports on VM (a guest machine).
Here is the cleaned up Vagrant file
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://vagrantcloud.com/search.
config.vm.box = "bento/debian-12.6"
config.vm.box_version = "202407.22.0"
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine and only allow access
# via 127.0.0.1 to disable public access
# accessing "127.0.0.1:8080" will access port 80 on the guest machine.
config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
# Enable provisioning with a shell script. Additional provisioners such as
# Ansible, Chef, Docker, Puppet and Salt are available.
config.vm.provision "shell", inline: <<-SHELL
apt-get update
apt-get install -y apache2
SHELL
end
We also add config.vm.provision
configuration, which allows us to run the shell commands on the VM while its being created. Notice that we are installing Apache HTTP server.
Now run vagrant up
. Once VM creation is finished, open web browser and go to 127.0.0.1:8080
.
You will see Apache HTTP servers default landing page.
Run vagrant destroy
to destroy VM we just created.
Provisioning with shell scripts
Provisioning with shell is a powerful approach which allows us to use shell scripts to setup VMs in repeatable manner.
Create file setup.sh
in the same directory as your Vagrantfile.
#!/bin/sh
# steps to setup a new VM
echo "Hello from new VM! Starting setup..."
apt-get update
apt-get install -y apache2
Update the vm.provision line in your Vagrantile to config.vm.provision "shell", path: "setup.sh"
instead of inline shell commands.
If you run vagrant up
new VM will be created, and during the provisioning Vagran will run the shell script with setup commands.
Running multiple VMs
Next, we will provision 2 VMs in a single Vagrantfile. Vagrant supports multi VM setup.
It's useful if you want to have scenarios where applications are running on the separate servers, separate database servers, etc.
Update the Vagrantfile
to the following
# All Vagrant configuration is done below.
# The "2" in Vagrant.configure defines the configuration version
Vagrant.configure("2") do |config|
# common configuration bo forth VMs
config.vm.provision "shell", inline: "echo Hello_All!"
config.vm.box = "bento/debian-12.6"
config.vm.box_version = "202407.22.0"
config.vm.define "web" do |web|
web.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
web.vm.provision "shell", path: "web_setup.sh"
end
config.vm.define "db" do |db|
db.vm.provision "shell", path: "db_setup.sh"
end
end
Notice that we point to different shell setup scripts for each of the VMs. This means that each VM can can be configured differently, based on what you are planning to host in them.
Shell scripts in this example are basic, we install Apache HTTP server in one case, and PostgreSQL DB in another.
#!/bin/sh
# steps to setup a new VM
echo "Hello from WEB VM! Starting setup..."
apt-get update
apt-get install -y apache2
and
#!/bin/sh
# steps to setup a new VM
echo "Hello from WEB VM! Starting setup..."
apt-get update
apt-get install -y apache2
Now, once you run vagrant up
you should see 2 VM provisioned, and should be able to SSH into them using vagrant ssh web
and vagrant ssh db
.
Summary
This shows you basic Vagrant configurations.
Vagrant is very flexible and allows many setup options. Official documentation is good and available options in detail.
Here are few commonly used provisioners:
You can find a full source code of this tutorial in GitHub repository.