Accessing your Vagrant boxes through a reverse proxy
16 Mar 2016
The past year we’ve been making increased use of Vagrant to streamline our development systems. Vagrant makes it easy to manage and provision virtual machines for each project we work on. When a new developer joins a project team, there is no more need to spend half a day fiddling to make the dev stack “just so” that it works for the project. In this post, we’d like to show why we made a Vagrant plugin for proxying your local HTTP server to your vagrant boxes.
Accessing a dev machine
While Vagrant makes it easy to access a virtual machine from the host box (especially with the host manager plugin), it is a bit more difficult to make it accessible from elsewhere in the network.
One way to do it is to forward a port from the VM’s local network interface to a port on your real machine’s physical network interface. Another way is to use a public network. These approaches are both viable and allow you to quickly provide access to a visiting client or a colleague, but they have some limitations.
Our network is getting crowded
At Code Yellow, we have several projects where a web application is supported by tablets or other devices. And to make things even more complex, sometimes the web application is available on multiple virtual hosts and uses the host name to control its behaviour.
Unless you’re doing load balancing or other tricks, only one IP address can have a certain host name within the network. Of course, several people will be working on the same project and running the same Vagrant box simultaneously. This means you’ll quickly run into trouble with assigning IP addresses and host names. We also want to avoid assigning a static IP address for every combination of Vagrant box and employee, as that quickly becomes intractable.
All this means that using the public network method is not very
practical for us. We could rely on port forwarding, but that still
doesn’t solve the virtual hosts problem. It’s easy to (temporarily)
edit /etc/hosts
on a developer’s box to go look at another
developer’s work, but explaining how to do that to a visiting client
is a bit trickier, and on a tablet you don’t even have access to the
hosts file as far as I’m aware.
Using a proxy to solve our problems
So we’ve come up with a reasonably elegant solution to this: instead of fiddling with the network, we decided that if you want to share your application to the outside network, you can run NGINX (or, in theory, any other web server) and proxy incoming requests to boxes depending on the URL.
For example, if you have a dev box for REX
with two test customers, they’ll be available under
customer1.rex.test
and customer2.rex.test
. Let’s say your IP
address is 192.168.1.123
, then you can use
http://192.168.1.123/customer1.rex.test
to access that customer’s
REX instance. The proxy will set the Host
header based on the first
path component, and forward the remaining part of the URL to the web
server running inside the VM.
To help automate this proxy configuration, we’ve created a Vagrant plugin called vagrant-reverse-proxy. It can be installed in the usual way:
$ vagrant plugin install vagrant-reverse-proxy
After that, all you need is to add a few lines to your Vagrantfile
:
if Vagrant.has_plugin?("vagrant-reverse-proxy")
config.reverse_proxy.enabled = true
config.reverse_proxy.vhosts = ['client1.rex.test', 'client2.rex.test']
end
And you’ll need to do a one-time manual configuration of NGINX on your
dev box. If you’re on a Debian system (or derivative) and don’t have
any other web sites configured, just put this in
/etc/nginx/sites-enabled/default
:
server {
listen [::]:80 default ipv6only=off;
server_name default;
include "vagrant-proxy-config";
}
The Vagrant plugin will write to a file called
/etc/nginx/vagrant-proxy-config
and issue a service nginx reload
command each time you bring up or halt a box. Only running boxes are
forwarded.
This setup makes it very easy to configure a tablet to contact a Vagrant VM, simply through your dev box’s public IP address or its host name if you have a local DNS server.