Use Vagrant to configure VMs

Devian Apache on VMware Fusion by Vagrant

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.

Devian Apache on VMware Fusion by Vagrant

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.

Similar posts

Join our monthly newsletter and receive
updates about the new tutorials!

We won't send you spam. Unsubscribe at any time.