How to use Vagrant for local web development
Last updated: 2020-03-05 :: Published: 2015-01-25 :: [ history ]You can also subscribe to the RSS or Atom feed, or follow me on Twitter.
Heads-up!While Vagrant served me well for years, I do not recommend using it for local development anymore and advise to use Docker instead, for which I wrote an entire tutorial series which I invite you to read.
This article shows how to quickly get up and running with Vagrant, to create and use local Virtual Machines as development environments, all with a single command. This is indeed written from a web developer's standing point, and I will not spend too much time describing how things work under the hood (not that I am an expert anyway).
The point of Vagrant is precisely not to have to worry too much about it.
The final result of this tutorial is available as a Github repository. In case of trouble, don't hesitate to refer to it.
Summary
- Vagrant?
- Basic installation
- Picking a box
- Vagrantfile
- Provisioning
- Matching the private IP
- Tips
- What now?
Vagrant?
Vagrant greatly simplifies the use of Virtual Machines to spawn development environments in no time (well, it's probably more like no effort than time).
To understand what a Virtual Machine (VM) is, think of an emulator: you install it on your computer so you can then run software that believe they are running in the environment they were designed for. All inside your own machine (which is then called the host).
That is essentially what a VM is.
Vagrant is a VM manager, in the sense that it reduces the management and the configuration of VMs to a handful of commands.
It relies on a VM provider, that deals with virtualization itself. As its support is shipped with Vagrant, we will use VirtualBox, but others exist.
So what is actually the point? The main argument is the consistency of the environments among developers working on the same project, and more importantly that these environments reflect the production ones. Ship a Vagrant configuration with each project, and every developer will work on the same environment locally. No surprises when pushing the code live, no more "it works on my machine".
The other advantages I see is the fact that one can still use their editor of choice, as Vagrant folders are shared with the host by default. It also permits not to clutter up your computer with tons of different libraries and software that aren't used by every project. If something goes wrong, you don't screw your machine: you destroy your VM instance and recreate it instead.
Easy. And safe.
Basic installation
First, download VirtualBox at https://www.virtualbox.org/wiki/Downloads ("platform packages") and install it. Then, download Vagrant at https://www.vagrantup.com/downloads.html (v1.7.2 at the time of writing), install.
Open up a terminal and type:
vagrant version
The version should be displayed. If not, log out your session, log back in and try again.
Picking a box
Vagrant's documentation uses a Ubuntu 12.04 LTS 32-bit server box. Now let's say you want Ubuntu 14.0 LTS 32-bit: go to the catalog of Vagrant boxes and type "Ubuntu 14.04" in the search field. Spot the Ubuntu one in the list: "ubuntu/trusty32".
Now get back to your terminal, browse to the directory you want your project to reside in, and type this:
vagrant init ubuntu/trusty32
The terminal should display something like:
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 about using Vagrant.
Good. Now type:
vagrant up
This is how you start your Virtual Machine. As you picked Ubuntu 14.04, Vagrant will try to install it: as it won't find a corresponding box on your computer, it will fetch it directly from the catalog (this can take a while according to your connection speed), and boot it once the download is over.
All you have to do now is:
vagrant ssh
BOOM! You are now in your Ubuntu 14.04 server.
Note for Windows users: the vagrant ssh
command might not work for you if SSH is not in your PATH variable. I invite you to take a look at this separate article and come back.
What exactly happened here? vagrant init
created a Vagrantfile
file in the directory. This file contains the various settings Vagrant needs to spawn a VM. You can have a look at it now, it is basically full of commented examples (don't freak out, it is not as bad as it looks).
You will find one uncommented line tho:
config.vm.box = "ubuntu/trusty32"
Yeah, you got it: as we invoked vagrant init
followed by the box we wanted to use, Vagrant created its config file with the corresponding setting.
While you are connected to your box, type this:
ls /vagrant
The Vagrantfile should be listed. This is the same Vagrantfile you have just updated: /vagrant
is the folder that is shared between the host machine and the VM.
You can simply leave your box typing ctrl
+ d
. I won't explain how to shutdown it; just take a couple of minutes to read about the different available options in the doc, they are well explained.
Just know that even if you destroy your box, files under /vagrant
remain untouched.
Vagrantfile
I am not going to go through all the options here, only those I most often use.
The Vagrantfiles are written in Ruby, but no Ruby background is required (I don't know much about Ruby myself).
Let's look into accessing your VM from the host. Using your host machine's editor to update files on your VM via shared folders is nice, but at some point you will want to admire your work in a browser, which implies for it to have access to your Vagrant box somehow.
The easiest way to achieve this is probably using port-forwarding.
Port-forwarding
First let's run a quick test. Open a terminal on your host machine and type the following command:
telnet localhost 8080
Unless the port 8080 is already in use by something else (in which case change 8080 for whatever port you know is available), you should get something like:
Trying 127.0.0.1...
telnet: connect to address 127.0.0.1: Connection refused
Trying ::1...
telnet: connect to address ::1: Connection refused
telnet: Unable to connect to remote host
Edit your Vagrantfile and add the following:
config.vm.network :forwarded_port, guest: 80, host: 8080
Now reload your VM:
vagrant reload
or vagrant up
if you had shut it down. Once it is booted, try the telnet
command above again in the other terminal window. This is what you should read:
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Port 8080 is now being listened to, and connections to it are forwarded to port 80 of the Vagrant box. This comes in handy when you use something like grunt-contrib-connect
to quickly spawn a static HTTP server on a specific port. In our case, using port 80 in the Grunt config and accessing http://localhost:8080
from our host machine would display the website contained in the folder set with the base
parameter.
This is nice for quick prototyping for example, as no further server configuration is required. In most cases however, as we want to replicate a production environment as accurately as possible, we will want to use a proper server config.
To do so, we are going to assign a private IP to our box.
Private IP
First, go back to the terminal and try:
ping 192.168.68.8
You should get request timeout
responses. Edit the Vagrantfile again and add:
config.vm.network :private_network, ip: "192.168.68.8"
Reload your VM and try again: you should now get responses.
This is it for the options I mainly use; I rarely need the rest. Be curious and scan through the official documentation, it is well written and I am sure you will find something useful to you.
On a side note, and to emphasize the usefulness of Vagrantfiles, I think their beauty resides in the fact that all it takes to make the environment available to other developers is to version them with your projects. Whenever you clone a project containing a Vagrantfile to a machine with Vagrant installed on it, you are at a vagrant up
away from having it running locally.
The rest of the process to get your website displayed using the private IP is covered in the next section.
Provisioning
Alright, so you've got your VM running, you can edit files from outside of it, and access it from the host machine using its private IP.
How to properly display your work in a browser now?
Let's cut to the chase here: it implies setting a server on your VM and matching its IP to the domain name you chose in the host machine's hosts
file.
As our Vagrant box is almost empty at this point, we need to install a HTTP server on it. We will go for Nginx here, but instead of installing it and setting up the server from the VM itself, we are going to use provisioning.
Shell scripts
Provisioning is achieved from the Vagrantfile as well and, if different means are available to do so, I will only cover the most basic one for now, i.e. using shell scripts (I will mention the other ways later on).
Open the Vagrantfile in your editor, and add this:
config.vm.provision :shell, :path => ".provision/bootstrap.sh"
Basically what we tell Vagrant is "Use the shell script that you will find in .provision/bootstrap.sh
to provision the box".
Don't reload your box just yet, as we need to create this file first. Add a new folder named ".provision" in your project (same level as the Vagranfile), and create a bootstrap.sh
file in it. Here is the full content of this file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/usr/bin/env bash
# nginx
sudo apt-get -y install nginx
sudo service nginx start
# set up nginx server
sudo cp /vagrant/.provision/nginx/nginx.conf /etc/nginx/sites-available/site.conf
sudo chmod 644 /etc/nginx/sites-available/site.conf
sudo ln -s /etc/nginx/sites-available/site.conf /etc/nginx/sites-enabled/site.conf
sudo service nginx restart
# clean /var/www
sudo rm -Rf /var/www
# symlink /var/www => /vagrant
ln -s /vagrant /var/www
|
Now, let's describe it step by step:
1 | #!/usr/bin/env bash
|
We are basically telling where to look for the bash
program.
# nginx
sudo apt-get -y install nginx
sudo service nginx start
Install Nginx and start it. The -y
option allows to automatically answer "yes" where user input is normally required.
# set up nginx server
sudo cp /vagrant/.provision/nginx/nginx.conf /etc/nginx/sites-available/site.conf
sudo chmod 644 /etc/nginx/sites-available/site.conf
sudo ln -s /etc/nginx/sites-available/site.conf /etc/nginx/sites-enabled/site.conf
sudo service nginx restart
We copy the server configuration from .provision/nginx/nginx.conf
(yet to be written at this point, we're getting there) to Nginx's sites-available
folder, ensure the permissions are right, then create the symlink from sites-enabled/site.conf
to sites-available/site.conf
, and restart Nginx to take this new config into account.
Finally, we create a symbolic link from /var/www
to /vagrant
, after having removed the default files Nginx creates at installation:
# clean /var/www
sudo rm -Rf /var/www
# symlink /var/www => /vagrant
ln -s /vagrant /var/www
/var/www
is often where the code goes on a server (even though /srv/www/
might be more relevant, but that's another debate). As mentioned earlier, on a Vagrant VM the shared folder is /vagrant
. Using such a symlink allows to match your production server's settings (or staging or whatevs).
This is completely optional tho.
Server config
Almost there! If you are attentive, you know that we now need the Nginx server config. Under the .provision
folder, create a new folder named "nginx" and open a new nginx.conf
file in it. Here is its content:
server {
listen 80;
server_name vagrant-test.local.com;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
root /var/www;
location / {
}
}
Nothing too esoteric here, this is a very basic Nginx server config. Just note that our server name will be "vagrant-test.local.com". Save your file, and reload the box using this command:
vagrant reload --provision
Normally, the provisioning is done only at the first boot of the VM. By specifying the --provision
option, we force Vagrant to perform the provisioning again.
The start up will take slightly longer than previously, and you should see quite a lot more instructions on the screen: the packages listed in the bootstrap.sh
file are being installed.
Matching the private IP
We only need two little extra steps now: adding the private IP and the server name to the host machine's hosts
file and creating a simple index.html
file in our box to be displayed in the browser later on. On Mac OS, edit the hosts
file with the following command:
sudo vim /etc/hosts
On Windows, you will find it under Windows/System32/Drivers/etc/
(you will have to edit it in admin
mode).
Add the following line:
192.168.68.8 vagrant-test.local.com
Save and quit.
Now add a little index.html
file at your project's root:
<!DOCTYPE html>
<html>
<head>
<title>Vagrant test</title>
</head>
<body>
<h1>Oh hi!</h1>
</body>
</html>
Alright ladies and gentlemen, drumroll please: open your favorite browser and navigate to http://vagrant-test.local.com
. You should see the HTML page you have just created \(^o^)/
Phew! That was a rocky ride, eh!
Well, yeah. But think about it this way: save this basic config somewhere (craft it to your needs, adding PHP or whatever floats your boat), and use it anytime you start a new project. You can now get up and running with a simple vagrant up
command and a new line in the hosts
file.
I don't know about you, but it sounds quite nice to me.
Tips
.gitignore
If you use Git, add ".vagrant/" to your .gitignore
file. Vagrant creates this folder when booting the VM: it contains auto-generated stuff you don't want to version.
Handle port collisions
When using port-forwarding, it can happen that a Vagrant box uses a port that is not available. It will tell you about it anyway, but if the host machine's port doesn't really matter to you, amend the corresponding line in the Vagrantfile this way:
config.vm.network :forwarded_port, guest: 8080, host: 80, auto_correct: true
The auto_correct
parameter will allow Vagrant to automatically assign another port of the host machine in case of unavailability (and tell you about it, of course).
Copy host git config
I usually try to avoid having too many terminal windows open at the same time. That's why I like to install Git on my VMs so I don't need another terminal window only to perform the versioning operations. This implies having your Git config inside the Vagrant box, which is actually quite easy to do automatically. In your Vagrantfile, add this:
config.vm.provision "file", source: "~/.gitconfig", destination: "~/.gitconfig"
At the first boot of the VM, your Git config file will be copied over.
GUI console
Occasionaly, you may have some trouble booting your VM. This can notably happen when your box wasn't properly shut down the last time you used it. The boot process might just hang there, and the reason may not be obvious. In that case, add the following lines to your Vagrantfile:
config.vm.provider :virtualbox do |vb|
vb.gui = true
end
and try to boot again. What this does is it will open VirtualBox's GUI console, which displays what is happening behind the scenes, and thus should help you troubleshoot the issue.
Access the host machine when using a private network
Sometimes, you may need to access the host machine from the guest one. How to do so?
When you set up a private network, 192.168.68.8 in our case, the host machine automatically takes 192.168.68.1 as its private IP address.
What now?
Well, that is quite a lot to digest already. And yet this is just the beginning, there are many more features to explore.
Just to mention a couple of them tho:
Provisioning tools
In this tutorial, we used simple shell scripts. But it turns out Vagrant (supposedly) plays nicely with more advanced solutions such as Chef or Puppet.
Admitedly tho, I tried to use Puppet something like a year ago, and completely failed to get it working with Vagrant. I am yet to give Chef a try.
Vagrant Share
Local development is good, but soon you will be confronted with the need to expose your work to the world, whether it is to show some progress to a client or to allow an API to reach your application. This is achievable with nice tools such as ngrok (hopefully I will soon publish a tutorial about it), but Vagrant unveiled a new service a few months ago, called "Vagrant Share", that does just that.
I am yet to try it out (a tutorial would probably follow), but it definitely looks promising.
You can also subscribe to the RSS or Atom feed, or follow me on Twitter.