Vagrant up and running

Charles Vissol
13 min readSep 18, 2023

--

Introduction

Vagrant is an open-source tool that helps you create and manage virtual machines. It provides a consistent way to create and manage virtual machines across different platforms.

Vagrant uses a configuration file, called a Vagrantfile, to define the virtual machine environment. The Vagrantfile can specify the operating system, the software to install, and the configuration of the virtual machine.

Once the Vagrantfile is defined, you can use the Vagrant command-line tool to create, start, stop, and destroy virtual machines. Vagrant also provides a number of plugins that can be used to automate tasks, such as provisioning the virtual machine with software or connecting to it via SSH.

Install Vagrant

The installation is made in Debian 11 and you need Virtualbox installed because we will use Virtualbox as Provider to create VMs via Vagrant. However, you can perform the installation on any Debian/Ubuntu distro.

Vagrant is in the official repository of Debian but in version 2.2.14. In August 2023, the last release of Vagrant is 2.3.7…

In this article, we decide to install the last version of Vagrant from the official documentation.

1# Run wget to install the GPG key:

sudo wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vagrant

2# Run the following line to update source.list of your local system:

sudo echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list

Verify the content of the /etc/apt/sources.list.d/hashicorp.list. In the case of Debian 11, you must have:

deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com bullseye main

3# Update your system by running:

sudo apt update

In this case, you must have an output without error (means a valid GPG key) with the following output (this is an example):

Get:1 http://security.debian.org/debian-security bullseye-security InRelease [48.4 kB]  
Hit:2 http://deb.debian.org/debian bullseye InRelease
Get:3 http://deb.debian.org/debian bullseye-updates InRelease [44.1 kB]
Hit:4 https://repo.protonvpn.com/debian stable InRelease
Get:5 https://dl.google.com/linux/chrome/deb stable InRelease [1,825 B]
Hit:6 https://download.docker.com/linux/debian bullseye InRelease
Get:7 https://apt.releases.hashicorp.com bullseye InRelease [12.9 kB]
Get:8 https://dl.google.com/linux/chrome/deb stable/main amd64 Packages [1,083 B]
Get:9 https://apt.releases.hashicorp.com bullseye/main amd64 Packages [95.6 kB]
Fetched 204 kB in 2s (120 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
1 package can be upgraded. Run 'apt list --upgradable' to see it.

4# Install Vagrant by running:

sudo apt install vagrant

Output should should be:

Reading package lists... Done  
Building dependency tree... Done
Reading state information... Done
The following NEW packages will be installed:
vagrant
0 upgraded, 1 newly installed, 0 to remove and 1 not upgraded.
Need to get 149 MB of archives.
After this operation, 383 MB of additional disk space will be used.
Get:1 https://apt.releases.hashicorp.com bullseye/main amd64 vagrant amd64 2.3.7-1 [149 MB]
Fetched 149 MB in 1min 18s (1,909 kB/s)
Selecting previously unselected package vagrant.
(Reading database ... 465193 files and directories currently installed.)
Preparing to unpack .../vagrant_2.3.7-1_amd64.deb ...
Unpacking vagrant (2.3.7-1) ...
Setting up vagrant (2.3.7-1) ...

Verify your Vagrant installation:

$ vagrant --version  
Vagrant 2.3.7

Success

Vagrant is operational!

Create my first VM with Vagrant

Before starting with Vagrant, you have 2 key concepts to know:

  • Boxes: the Boxes represent the VMs available for Vagrant. The list of the Boxes available is in the Discover Vagrant Boxes page and depends on the Provider
  • Providers: they are the underlying engines and hypervisors used to provision VMs or containers. The Discover Vagrant Boxes allows you to see all the Providers available

In our case we select debian/bullseye64 to create our first Vagrant VM, a Debian 11 amd64.

1# Create a directory to go into

This folder represents the context of you Vagrant VM where the Vagrantfile will be created

Tip

Any Vagrant automation action is described in a Vagrantfile which is a descriptive file written in Ruby. This is likely similar to the Dockerfile concept.

In this article we create a vagrant directory and we move into this folder:

mkdir ~/softs/VMs/vagrant && cd ~/softs/VMs/vagrant

2# Create the Vagrantfile

Run the following command which will create a Vagrantfile

vagrant init debian/bullseye64

Tip

If a Vagrantfile already exists in the folder, rename, move or delete it.

The output should be:

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.

3# Create the VM

Now open VirtualBox and run:

vagrant up

Vagrant configure the VM for you (network), boot it, install SSH and mount a shared folder (current folder). Vagrant creates also the default username vagrant.

The output should be:

Bringing machine 'default' up with 'virtualbox' provider...  
==> default: Box 'debian/bullseye64' could not be found. Attempting to find and install...
default: Box Provider: virtualbox
default: Box Version: >= 0
==> default: Loading metadata for box 'debian/bullseye64'
default: URL: https://vagrantcloud.com/debian/bullseye64
==> default: Adding box 'debian/bullseye64' (v11.20230615.1) for provider: virtualbox
default: Downloading: https://vagrantcloud.com/debian/boxes/bullseye64/versions/11.20230615.1/providers/virtualbox.box
==> default: Successfully added box 'debian/bullseye64' (v11.20230615.1) for 'virtualbox'!
==> default: Importing base box 'debian/bullseye64'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'debian/bullseye64' version '11.20230615.1' is up to date...
==> default: Setting the name of the VM: vagrant_default_1693394573380_95314
Vagrant is currently configured to create VirtualBox synced folders with
the `SharedFoldersEnableSymlinksCreate` option enabled. If the Vagrant
guest is not trusted, you may want to disable this option. For more
information on this option, please refer to the VirtualBox manual:
https://www.virtualbox.org/manual/ch04.html#sharedfolders
This option can be disabled globally with an environment variable:
VAGRANT_DISABLE_VBOXSYMLINKCREATE=1
or on a per folder basis within the Vagrantfile:
config.vm.synced_folder '/host/path', '/guest/path', SharedFoldersEnableSymlinksCreate: false
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
default: Adapter 1: nat
==> default: Forwarding ports...
default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
default: SSH address: 127.0.0.1:2222
ticle to install Vagrant: https://itslinuxfoss.com/install-vagrant-debian/ default: SSH username: vagrant
default: SSH auth method: private key
default:
default: Vagrant insecure key detected. Vagrant will automatically replace
default: this with a newly generated keypair for better security.
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: Checking for guest additions in VM...
default: The guest additions on this VM do not match the installed version of
default: VirtualBox! In most cases this is fine, but in rare cases it can
default: prevent things such as shared folders from working properly. If you see
default: shared folder errors, please make sure the guest additions within the
default: virtual machine match the version of VirtualBox you have installed on
default: your host and reload your VM.
default:
default: Guest Additions Version: 6.0.0 r127566
default: VirtualBox Version: 7.0
==> default: Mounting shared folders...
default: /vagrant => /home/vissol/softs/VMs/vagrant
==> default: Machine 'default' has a post `vagrant up` message. This is a message
==> default: from the creator of the Vagrantfile, and not from Vagrant itself:
==> default:
==> default: Vanilla Debian box. See https://app.vagrantup.com/debian for help and bug reports

Illustration: Terminal output:

Illustration: result in VirtualBox:

Success

Your VM has been created !

4# Verify the status of your VM

Simply run:

vagrant status

The output is:

Current machine states:  

default running (virtualbox)

The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.

5# Connect to your VM

Now you are ready to log into the VM. Run:

vagrant ssh

Without any credentials, you connect directly the VM environment with the vagrant user account with the following prompt

vagrant@bullseye:~$

Example: Starting inside your VM…

6# Exit from the VM

Simply run:

exit

And you come back to your host shell.

Example:

6# Shutdown the the VM

To stop your VM run:

vagrant halt

Success

You had your first experiment of a VM made using Vagrant. Congrats !

The destroy chapter :-)

What about destroying your VM ?
That is simple, run:

vagrant destroy

That’s it !

VM Provisioning

The directory you create to store your Vagrantfile, can contains other resources.

A simple way to custom your VM is to execute shell script once the VM is created. In this case you can:

  • insert inline script
  • external script

We take the Vagrant default sample were we install apache2 package.

Inline Shell script Provisioning

# -*- mode: ruby -*-
# vi: set ft=ruby :

External Shell script Provisioning

You store your script in the folder were you have the Vagrantfile. Here it is script.sh.

# -*- 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 = "debian/bullseye64"
# Enable provisioning with a shell script. Additional provisioners such as
# Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
# documentation for more information about their specific syntax and use.
config.vm.provision "shell", inline: <<-SHELL
# apt-get update
# apt-get install -y apache2
# SHELL
end

script.sh should contain:

#!/bin/bash
sudo apt-get update -y
sudo apt-get install -y apache2

More information at https://developer.hashicorp.com/vagrant/docs/provisioning/shell

Tip

If you make Vagrantfile modifications when your VM is running, don’t worry !
By running:

vagrant reload

you restart the VM and re-provision it depending on any changes you have made in the Vagrantfile.

Provisioning Docker image

Vagrant is able to deploy containers into VMs using Docker or Podman, and running a container image automatically.

Example: Deploy Nginx container

# -*- 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|
# Enable agent forwarding.
config.vm.box = "debian/bullseye64"
config.vm.provision "docker" do |d|
d.run "nginx"
end
end

Provisioning by Ansible Playbook

In addition to Shell scripts, Vagrant can deploy Ansible, Chef, Puppet, Salt and CFEngine.

Here an example of Playbook execution: in this case, the resources are located in the same directory than Vagrantfile or in sub-directories. Vagrant install ansible automatically in the VM.

# -*- 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|
# Enable agent forwarding.
config.vm.box = "debian/bullseye64"
config.vm.provision "ansible_local" do |ansible|
ansible.compatibility_mode = "2.0"
ansible.galaxy_role_file = "roles/requirements.yml"
ansible.galaxy_roles_path = "roles"
ansible.playbook = "playbook.yml"
ansible.verbose = "vv"
end
end

Managing the Provisioners

When you run vagrant up for the first time, Vagrant will execute the provisioner and record that the provisioner has been executed: if you halt and restart the VM the provisioner won't run another time by default.

  • If you want to force Vagrant to run the provisioner, run:
vagrant provision
  • If you want to reboot a VM and run the provisioner, run:
vagrant up --provision
  • If you want to startup a VM but not run the provisioner, run:
vagrant up --no-provision

Agent forwarding

SSH agent forwarding is a feature of the SSH protocol that allows you to use your local SSH agent remotely when connecting to other SSH servers. This means that you can avoid having to store your private SSH keys on the remote server, which can improve security.

For example, if you want to checkout a remote Git repository over SSH from you local Vagrant machine, you must enable the agent forwarding:

# -*- 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|
# Enable agent forwarding.
config.ssh.forward_agent = true
end

Getting help

To get the full list of Vagrant CLI run:

$ vagrant --help
Usage: vagrant [options] <command> [<args>]

-h, --help Print this help.

Common commands:
autocomplete manages autocomplete installation on host
box manages boxes: installation, removal, etc.
cloud manages everything related to Vagrant Cloud
destroy stops and deletes all traces of the vagrant machine
global-status outputs status Vagrant environments for this user
halt stops the vagrant machine
help shows the help for a subcommand
init initializes a new Vagrant environment by creating a Vagrantfile
login
package packages a running vagrant environment into a box
plugin manages plugins: install, uninstall, update, etc.
port displays information about guest port mappings
powershell connects to machine via powershell remoting
provision provisions the vagrant machine
push deploys code in this environment to a configured destination
rdp connects to machine via RDP
reload restarts vagrant machine, loads new Vagrantfile configuration
resume resume a suspended vagrant machine
serve start Vagrant server
snapshot manages snapshots: saving, restoring, etc.
ssh connects to machine via SSH
ssh-config outputs OpenSSH valid configuration to connect to the machine
status outputs status of the vagrant machine
suspend suspends the machine
up starts and provisions the vagrant environment
upload upload to machine via communicator
validate validates the Vagrantfile
version prints current and latest Vagrant version
winrm executes commands on a machine via WinRM
winrm-config outputs WinRM configuration to connect to the machine

For help on any individual command run `vagrant COMMAND -h`

Additional subcommands are available, but are either more advanced
or not commonly used. To see all subcommands, run the command
`vagrant list-commands`.
--[no-]color Enable or disable color output
--machine-readable Enable machine readable output
-v, --version Display Vagrant version
--debug Enable debug output
--timestamp Enable timestamps on log output
--debug-timestamp Enable debug output with timestamps
--no-tty Enable non-interactive output

An example of a default 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 = "debian/bullseye64"
# 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"
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# config.vm.network "public_network"
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
# config.vm.synced_folder "../data", "/vagrant_data"
# Disable the default share of the current code directory. Doing this
# provides improved isolation between the vagrant box and your host
# by making sure your Vagrantfile isn't accessable to the vagrant box.
# If you use this you may want to enable additional shared subfolders as
# shown above.
# config.vm.synced_folder ".", "/vagrant", disabled: true
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
# # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
# # Customize the amount of memory on the VM:
# vb.memory = "1024"
# end
#
# View the documentation for the provider you are using for more
# information on available options.
# Enable provisioning with a shell script. Additional provisioners such as
# Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
# documentation for more information about their specific syntax and use.
# config.vm.provision "shell", inline: <<-SHELL
# apt-get update
# apt-get install -y apache2
# SHELL
end

Conclusion

This article is a starting point to have Vagrant up and running in Linux with a first sample to show you how to create a VM.

Vagrant allows you more, but you need to understand more concepts such as sync folders and provisioning and also the Ruby syntax to make more complex automated configurations.

--

--