Lately, I have been finding a lot of the documentation on these tools to steer towards enterprise use cases such as managing and deploying multiple machines; so I decided to document a single-user local development setup.
This example will setup the latest Ruby on Rails running on Ubuntu Server 14.04. The parent OS used is OS X.
The purpose of this example is to try to be as explicit as possible to explain how these tools work without going too far in scope.
##Virtual Machine Base box
Vagrant uses Ruby, though the package includes an embedded Ruby interpreter.
Vagrant works by starting with a ‘base box’ such as Ubuntu, Debian, Windows… that is then built on top of. You can create your own base boxes, but this example will start with a pre-existing box.
Using a box from Vagrant Cloud, import Ubuntu Server 14.04 via the command line
vagrant box add ubuntu/trusty64. Vagrant stores base boxes at
~/.vagrant.d/boxes, not in the directory you run the command.
Navigate to a directory where you want this project and all its files to reside.
Initialise a new Vagrant project
vagrant init ubuntu/trusty64.
Vagrant will create a
Vagrantfile file in the directory. This is a configuration file for the VM. The
# character in a Vagrantfile is a comment. If you open the Vagrantfile you will see the lots of helpful settings commented out.
At this stage you can boot up the virtual machine using
vagrant up and once it is booted you can shell into the machine using
vagrant ssh (you should not have to enter a password because when base boxes are created the usual configuration is for the root password, main account username, and main account password to all be
Leave the machines’ shell with
exit and then shutdown the machine using
Vagrant uses Provisioners to setup the virtual machine performing tasks such as installing packages, starting services, creating users. As well as provisioners like Puppet and Chef you can just use shell scripts and batch scripts.
When working on provisioning a Vagrant box the following commands are useful:
vagrant reload --provisionequivalent of running
vagrant upand running the provision files again (will not do it by default)
vagrant destroyif you mess up the base box with incorrect provisioning destroy the machine and then start again with
When you have finally finished setting up an environment you can then just run
vagrant up and
vagrant halt each time and not have to keep re-doing the provisioning. Vagrant should only run provisioning if there are differences compared to the last time it ran.
The provisioner we will use is Puppet. Puppet comes installed on the base box we started with.
In puppet manifest files (.pp) contain Puppet code.
Create a directory structure that looks like this:
site.pp will be our global provision file, in some documentation, you see this file named
default.pp. The rest of the required code will be split into separate modules. Each module is a
class - we will come onto this later.
Puppet has expectations on module directory structure and so adhering to this is important.
Open the Vagrantfile created previously and find this part:
#config.vm.provision "puppet" do |puppet|. Add:
This will match the directory structure set up previously.
Puppet uses resource types for each step. These perform different functions such as installing software packages, running commands, creating users, copying files… A full explanation of these is out of the scope of this article. You can find one here: docs.puppetlabs.com/puppet/latest/reference/lang_visual_index.html
Puppet expects the class name to match the directory name, in this case,
The first steps are to add a third-party Ubuntu package repository with a package for Ruby 2.1 and then update the package manager for the addition to take effect. This requires
path attributes are probably explanatory. The
timeout attribute is useful for commands that may take a long time to run and the
require attribute is how you declare dependencies in Puppet. Note that when using require the resource type should be capitalised.
After that install the two packages needed with the
For the rails module declare that Ruby needs to be installed first using the
require function (if you have setup your modules directories correctly Puppet will automatically know where to find it).
Then install the package dependencies for Rails and the rails gem.
To indicate that
rails is a gem and should be installed from RubyGems the
provider attribute is used.
Because the modules have been structured as Puppet expects, Puppet will automatically include these in the site.pp manifest. You could be explicit with:
But this is not needed.
To run what we have created we just need to run the rails module because the other ruby module has been declared as a dependency.
You can test all this by running
vagrant reload --provision.
After it has completed installation, shell into the box
vagrant ssh and check versions with
ruby -v and
rails -v. Run
rails new demo to generate a project.
To keep this box as a machine that can be destroyed easily we will host our application code in a directory in the parent OS and map the folder to a location inside the VM.
Vagrant maps one directory by default: the directory containing the Vagrantfile in the parent OS is mapped to
/vagrant in the box.
To map other directories search for
config.vm.synced_folder in the Vagrantfile.
If you were working on an already existing code base, at this point you would be putting it into the directory in the parent OS. However as we are initialising a new project use
config.vm.synced_folder "project", "/home/vagrant/myproject/".
Create this directory in the parent OS.
Reload the VM and ssh into the box. In
rails new myproject. This shared directory should now contain the Rails project code.
Lastly, we need to be able to see the application running. Halt the machine and go back to the Vagrantfile.
Rails by default runs on port 3000. Add this line
config.vm.network "forwarded_port", guest: 3000, host: 3030.
Boot up the VM and naviagate to
rails server, then in the parent OS visit
http://localhost:3030 to see the Rails new application screen.