I have been using Vagrant and it is the most useful tool for working with VM, such as Virtualbox and Parallels. Before Vagrant, I’d been using virtual disk images, which were cloned and made into snapshots between each changes in configuration. Now, Vagrant makes this process effortless with simple commands.

Also some recommend skipping Vagrant and instead, using Docker

References

My current Vagrantfile

boxcutter/debian82, ansible, parallel

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

Vagrant.configure(2) do |config|
  config.vm.box = "boxcutter/debian82"
  config.vm.network "private_network", ip: "192.168.200.10"
  config.ssh.forward_agent = true
  config.vm.provision "ansible" do |ansible|
     ansible.playbook = "ansible.yml"
     ansible.verbose = 'vvvv'
  end

end

Simple Setup

  1. download Vagrant.

  2. Create dir and cd if it doesn’t already exist

    $ mkdir <MyVagrantDir>; cd <MyVagrantDir>
  3. Create config file Vagrantfile

    $ vagrant init
  4. Create a box (it downloads box if it is needed)

    $ vagrant box add debian/jessie64   # load Debian box
  5. Edit config file

    Vagrant.configure("2") do |config|
        config.vm.box = "debian/jessie64"
    end
  6. Start VM

    $ vagrant up

Basic usage

SSH into VM

$ vagrant ssh

Reload VM if needed

$ vagrant reload

Manually SSH

$ ssh vagrant@127.0.0.1 -p 2222 -i /.../.vagrant/machines/default/virtualbox/private_key

Status

$ vagrant status
$ vagrant global-status
  • Best way to keep track of running vagrant, recommended

Box

  • Box: image file for Vagrant.
  • Boxes are not interchangeable between providers (ie box for Vbox vs Vmware)
  • Boxes can have same name but different providers
    • make sure to get box that has Parallels provider, if using Parallels

Quick summary if adding boxes, a little different from above doc. not sure

$ vagrant box add {TITLE} {SOURCE}   # adds box
$ vagrant init
$ vagrant up

Basic operations

$ vagrant box         # help
$ vagrant box list    #lists all boxes

Install box

$ vagrant box add {TITLE} {SOURCE}   # adds box

$ vagrant box add MyBox http://example.com/somedistro.box

Other operations

$ vagrant box remove <NAME>   # remove box

$ vagrant box update   # update box image
* I will use it to upgrade when debian box was updated from 8.2 to 8.6
* this only updates original box image, will have to recreate vagrant
* for all that uses this box
* HOWEVER, outdated boxes are checked automatically by default and updated
* see configuration

Repackage and put it in current directory to make it redistributable

$ vagrant box repackge <NAME> <PROVIDER> <VERSION>
# not the same as packaging?

Box repository

https://app.vagrantup.com/boxes/search

http://www.vagrantbox.es/

  • flat list of boxes, but is a little outdated? It seems do be not updated frequently.

https://chef.github.io/bento/

  • has several major vagrant boxes
  • bento/debian 8.2

Debian Box notes

For Parallels, use parallels/* or boxcutter/* or bento/*

  • however, all Debian v8.6 has user called “vagrant@bad”.
  • boxcutter/* no longer has parallel boxes

Hashicorp’s Debian Jessie 8.* Vanilla box (VirtualBox only) https://app.vagrantup.com/debian/boxes/jessie64

vagrant init debian/jessie64; vagrant up --provider virtualbox

Debian’s official box (https://wiki.debian.org/Teams/Cloud/VagrantBaseBoxes)

  • since 8.2.2, it defaults to rsync instead of synced folder because vboxsf requires contrib/non-free packages
  • This had me scratching my head for a day!!

Debian doc suggests using this to share files (via rsync)

vagrant rsync-auto

Alternative is to use boxcutter’s Debian

boxcutter/debian8
  • has everything installed and works
  • seems to be safe

HN Discussion: What’s THE way to setup a OS X dev machine 2015?

Box file location

Box is stored locally (via synced folder).

  • Mac OS x: ~/.vagrant.d/boxes
  • Windows: C:/Users/USERNAME/.vagrant.d/boxes

The current disk content is stored in ~/VirtualBox\ VMs\vagrant*/

To see the names of vagrant running on Virtualbox

$ VBoxManage list vms
  • Note, Debian box didn’t have VirtualBox Guest Addition, which is required for shared folder to work
  • However, official Debian doesn’t do it intentionally (see debian)
  • Boxcutter’s Debian has it already installed, so it works

https://brianfisher.name/content/alternative-vagrant-synced-folders

  • Excellent short guide to all the ways that folder can be synced in Vagrant.
  • error in article: SMB is not only for Windows

Might have to do this if it fails to mount

$ mount -t vboxsf -o uid=`id -u vagrant`,gid=`getent group vagrant | cut -d: -f3` /var/www /var/www
$ mount -t vboxsf -o uid=`id -u vagrant`,gid=`id -g vagrant` /var/www /var/www

Teardown

Save the VM state

$ vagrant suspend #suspend, quick, all states preserved
$ vagrant up      # wake

# alternatively
$ vagrant resume

Shutdown VM gracefully

$ vagrant halt   # completely shut-down, but content of disk remains
$ vagrant up  # boot vm

Shutdown VM by powering off

$ vagrant halt -f  [--force]

Destroy VM permanently

  • all installed software will be deleted. Thus, any new VM will start from initial state (often the base image).
  • it is important to “provision” (see below)
  • This does NOT delete the original BOX, since they are images. (see box)

    $ vagrant destroy
    $ vagrant up   # starts from beginning with provisioning,

Delete Box

$ vagrant box remove NAME
$ vagrant box remove NAME --provider=".."

Make a new box

https://blog.engineyard.com/2014/building-a-vagrant-box

  • one of the most informative doc
  • Virtualbox, Ubuntu

https://www.skoblenick.com/vagrant/creating-a-custom-box-from-scratch/

  • Virtualbox and VMWare Fusion, on RHEL

Also see O’Reilly book Vagrant: Up and Running

  • Ch6 – creating new boxes from scratch/ existing environment

Create base box

once all the software are installed, package it into box

  • currently only supports Virtualbox->Box

    vagrant package --base my-virtual-machine

Convert Virtualbox to Vagrant box

https://www.vagrantup.com/docs/virtualbox/boxes.html https://docs.vagrantup.com/v2/virtualbox/boxes.html

  • when packaging, make sure you are referring to Virtualbox’s vbox, not Vagrant’s box image.

Use boxcutter’s boxes.

# find vbox's name
$ VBoxManage list vms
# package into vagrant box
$ vagrant package --base centos5_default_1416400560587_23130 --output virtualbox.box
# name the box
$ vagrant box add box-dimka/centos511-i386 virtualbox.box

Debian recommends this to improve speed

  • disable DNS resolution in sshd (speed up logins)
  • use a 0s grub timeout to speed up booting

Installing guest addon

$ sudo apt-get install linux-headers-generic build-essential dkms
$ sudo mount /dev/cdrom /media/cdrom
$ sudo sh /media/cdrom/VBoxLinuxAdditions.run

Download GuestAddition manually

wget http://download.virtualbox.org/virtualbox/4.3.8/VBoxGuestAdditions_4.3.8.iso
sudo mkdir /media/VBoxGuestAdditions
sudo mount -o loop,ro VBoxGuestAdditions_4.3.8.iso /media/VBoxGuestAdditions
sudo sh /media/VBoxGuestAdditions/VBoxLinuxAdditions.run
rm VBoxGuestAdditions_4.3.8.iso
sudo umount /media/VBoxGuestAdditions
sudo rmdir /media/VBoxGuestAdditions

Package as box, so it doesn’t need to do this again

$ vagrant package --base my-virtual-machine

See vagrant-vbguest plugin instead

Tools for making a new box

Chef Bento: Packer templates for building minimal Vagrant baseboxes

Synced folder

Virtualbox’s Issues

bad sync performance

vagrant-project-dir is shared in Vagrant VM’s /vagrant dir.

From https://github.com/mitchellh/vagrant/issues/3062

Here’s my attempt to summarize the issue:

VirtualBox shared folder provides two-way synchronization but it does not fire inotify notifications so we can’t run programs to watch changes and compile them (e.g., Less compiler). The polling approach falls apart quickly because VirtualBox shared folder is slow. Setting up a shared folder via rsync is fast and fires inotify but it is only one-way. My workaround is to set up the source directory as two shared folders, one via rsync and another via virtualbox shared folder. e.g.,

config.vm.synced_folder “.”, “/vagrant”, type: “rsync” config.vm.synced_folder “.”, “/vagrant_sf” I set things up so that any program that watches file changes via inotify reads from /vagrant. Any changes that needs to be persisted goes out to /vagrant_sf (e.g., alembic migration files). This workflow seems to work reasonably well.

vbox’s shared folder doesnt’ support POSIX permissions.

  • cannot chmod files in /Vagrant mount

Alternatives

  • Run unison inside VM

    • Many users do this for improved performance.
  • Parallel’s shared folder is fast, and this is what I use.

  • NFS (not recommended)

  • vagrant-rsync-back plugin: not reliable

  • grunt watch in VM

  • vagrant-unison plugin

  • SSHFS in either VM or in host (although it depends)

  • ResillioSync (Bittorrent Sync)

    • suggested by HN reader

Vagrantfile: Config

# makes precise32 as "base" image
Vagrant.configure("2") do |config|
    config.vm.box = "hashicorp/precise32"
    #.... all settings inside here
    #.....
end

Same as above, but more flexible using var, just in case version changes

VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

Networking

In shell, to take effect after changing Vagrantfile

$ vagrant reload  # shutdown and restart VM
# OR
$ vagrant up      # if it wasn't running

Forwarding ports

  • Forwarding port is good for securing VM by limiting its ports.
  • However, if more ports are needed, it is better to use private network.

Vagrantfile

Vagrant.configure("2") do |config|
    config.vm.box = "hashicorp/precise32"
    config.vm.provision :shell, path: "bootstrap.sh"
    config.vm.network :forwarded_port, guest: 80, host: 4567
end

Once forwarded, (on host):

  • go to http://localhost:4567
  • or go to http://127.0.0.1:4567

Instead of IP address, use domain name, via change in /etc/hosts

# /etc/hosts
192.168.100.10   dev.otter.pro
  • now, go to http://dev.otter.pro/

Example: forward vagrant’s port 80,443 to host’s 8080,8443

# Vagrantfile
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
    ...
    config.vm.network "forwarded_port", guest: 80, host: 8080
    config.vm.network "forwarded_port", guest: 443, host: 8443

# in Shell
$ vagrant reload

NAT (Private network)

  • by default, uses NAT only.

Enable internet connection on Debian/ Ubuntu in NAT

config.vm.provider "virtualbox" do |v|
    v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
    v.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
end
  • this is probably not needed if using bridged network or private network

Private Network (NAT AND Host-only, 2 NIC)

# Create a private network, which allows host-only access to the machine # using a specific IP. config.vm.network “private_network”, ip: “192.168.100.10”

Bridged / Public Network

# 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"

Enable SSH forwarding

  • this is recommended, if using git over SSH

    # If true, then any SSH connections made will enable agent forwarding.
    # Default value: false
    config.ssh.forward_agent = true
    #config.vm.network "public_network"

Share more folders

config.vm.synced_folder "./app", "/var/www/django-app"

Automated provisioning

Provision process runs when:

  1. $ vagrant up # only on the 1st “vagrant up”!!!
  2. $ vagrant reload --provision # if VM is already running
  3. $ vagrant provision # always provisions on this command

Run a command inline

Vagrant.configure("2") do |config|
config.vm.provision "shell",
    inline: "echo Hello, World"
end

Run inline script Ex (from VagrantFile)

 config.vm.provision "shell", inline: <<-SHELL
   sudo apt-get update
   sudo apt-get install -y apache2
 SHELL

Example

Vagrant.configure("2") do |config|
    config.vm.provision "shell", inline: "echo foo"
    config.vm.provision "shell", inline: "echo baz"
end

named :

config.vm.provision "NAME is Bootstrap", type: "shell" do |s|
  s.inline = "echo hello"
end

Example: run external script

Upload the script to Guest OS and execute it.

Create bootstrap.sh file.

When vagrant is run, it will run the bootstrap.sh once

#!/usr/bin/env bash
apt-get update
apt-get install -y apache2
if ! [ -L /var/www ]; then
    rm -rf /var/www
    ln -fs /vagrant /var/www
fi

Edit Vagrantfile to point to bootstrap.sh

Vagrant.configure("2") do |config|
    config.vm.box = "hashicorp/precise32"
    ...
    config.vm.provision :shell, path: "bootstrap.sh"
end

Ansible

https://docs.vagrantup.com/v2/provisioning/ansible.html

Vagrantfile example

Vagrant.configure("2") do |config|
    config.vm.provision "ansible" do |ansible|
        ansible.playbook = "playbook.yml"
    end
end

# optional ansible example
ansible.extra_vars = {
    ntp_server: "pool.ntp.org",
    nginx: {
        port: 8008,
        workers: 4
    }
}

Recommends using separate provisioning/ to store playbook

$ tree
.
|-- Vagrantfile
|-- provisioning
|   |-- group_vars
|           |-- all
|   |-- playbook.yml

# make sure to put the correct path to playbook.yml
Vagrant.configure("2") do |config|
config.vm.provision "ansible" do |ansible|
    ansible.playbook = "provisioning/playbook.yml"
end
end

Sharing

  • Create quick share folder for the web/internet
  • require login to hashicorp

    $ vagrant login
    $ vagrant share

Providers

  • by default, works with Virtualbox

Selecting different provider:

option 1. vagrant up command

$ vagrant up --provider=vmware_fusion
$ vagrant up --provider=aws

option 2. set priority in Vagrantfile

Vagrant.configure("2") do |config|
    # ... other config up here

    # Prefer VMware Fusion before VirtualBox
    config.vm.provider "vmware_fusion"
    config.vm.provider "virtualbox"
end

option 3: set env var VAGRANT_DEFAULT_PROVIDER to given provider.

export VAGRANT_DEFAULT_PROVIDER="vmware_fusion"

Virtualbox

Enable linked clone - save HD space (vagrant 1.8+)

config.vm.provider "virtualbox" do |v|
    v.linked_clone = true
end

Set config in single line

config.vm.provider :virtualbox do |vb|

vb.customize ["modifyvm", :id, "--name", "MyCoolApp", "--memory", "512"]

end

Parallels (Mac OSX)

http://parallels.github.io/vagrant-parallels/docs/

Install plugin first

$ vagrant plugin install vagrant-parallels

To update plugin if needed

$ vagrant plugin update vagrant-parallels

Parallels will be used as default provider once plugin is installed

$ vagrant init parallels/ubuntu..

$ vagrant init boxcutter/debian82; vagrant up --provider parallels

More Parallels configurations

Use linked clone instead of default regular clone to save disk space and speed up

  • available only on Parallels v11+, not on v10, which I own.

    config.vm.provider "parallels" do |prl|
        prl.use_linked_clone = true
    end
  • make sure original box/image is never deleted, since linked clones depend

Change name (shown on desktop GUI)

config.vm.provider "parallels" do |prl|
    prl.name = "my_vm"
end

Issue vagrant halt on Debian stuck for 30 minutes

Latest Parallels’ boxes don’t work, says bad tty

  • vagrant@bad for all latest Debian

Plugins

List of all Plugins

Plugins are provided by 3rd party (mostly).

Install plugin

$ vagrant plugin install <plugin-name>

List all plugins

$ vagrant plugin list

Automatically install Guest Additions on Guest OS

$ vagrant plugin install vagrant-vbguest

Useful plugins

  • Virtualbox vbguest plugin allows Vagrant to automatically install vbguest.

    $ vagrant plugin install vagrant-vbguest

Help

Adding “-h” at end will show help

$ vagrant <command> -h

Tools

http://vagrantmanager.com/

  • Desktop app, MacOS, Windows

rundeck

  • ops automation
  • works with many tools, Java, recommended
  • Reddit user mentions slow performance

otto successor, runs on top of Vagrant, deprecated/cancelled (HN Discussion)