Setting up a Rails development environment on OS X using Docker

One of our designers wanted to make some changes to our job offer page. At first it seemed trivial - just change some HTML inside a rails app. Then we realised that we would need to setup the whole development environment on his Mac. With the help of homebrew this didn't sound like a challenge at all. But then it came to my mind that this might be the perfect case for setting up a development environment using docker and docker-compose (formerly fig).

Setting up docker on Mac OS X

Docker does not have native Mac OS X support yet, but there are already tools that allow running docker inside a virtual machine with the feeling of having it installed locally. After struggling with boot2docker, kitematic and vagrant I finally found a setup that just works using docker-machine.

This setup is using homebrew and Virtual Box, but you can install docker binaries in any way you like, as well as use VMWare instead.

Update your homebrew

This may sound obvious, but I've spent enough time fighting with older versions of docker and docker-compose that it's worth mentioning here. Simply run

brew update

Install docker-compose

docker-compose (fig) is a simple utility for running a set of containers defined in a YAML file. It removes the pain of managing separate containers for app, database and supporting services and lets you focus on your app development. Head over to the great documentation for more details. We will use it to define our app dependencies as well as the rails app container itself.

brew install docker-compose

Install docker-machine

docker-machine allows for quick provisioning of docker-ready nodes, from local virtual machines (Virtual Box, VMWare) to cloud servers (EC2, DigitalOcean, Rackspace and more). This is a single point of management for multiple local or remote environments from one terminal.

brew install docker-machine

Validate your setup

After installing all docker packages you should be able to access the following commands. Please pay attention to the version numbers, there is a significant chance that earlier versions of those tools won't work.

As you can see above, our new machine has an IP address 192.168.99.100. This address is required to tell the docker command line tool where the docker daemon is running. Fortunately, we don't need to remember it.

Those ENV variables tell docker CLI the location of the desired host you want to operate on. In order to make your life easier, add this line to your .zshrc/.bashrc file to have the environment setup correctly in every new terminal:

# .zshrc/.bashrceval"$(docker-machine env docker-vm)"

(bonus) Edit /etc/hosts

While this step is not necessary, it might be easier to use an easy-to-remember name instead of an IP address. Edit your /etc/hosts file and add this line at the very bottom:

192.168.99.100 docker

NOTE: Your IP address might be different. You can always check it using docker-machine ip command

And it's done — now you have a fully functional docker environment running on OS X!

Easy development of Ruby on Rails apps with docker-compose

Now to the most exciting part.

You MUST put your application directory under the /Users directory. If you don't, the file sharing between OS X file system and Virtual Box VM will not work. Symlinking won't work either.

docker-compose requires at least two files to be added to your app's directory: Dockerfile and docker-compose.yml.

Dockerfile

The minimal content of your Dockerfile should contain something like this:

Redis configuration

Connecting to redis also needs to be tweaked a little - now the redis URL will be redis://redis:6379/0 (using the redis host).

Up and running

Now we can build our app and start everything together with a single command:

docker-compose up

You should see output similar to foreman's, with three differently colored log files. And if everything went fine, you should be able to see your app at http://docker:3000 (or http://192.168.99.100:3000 if you haven't edited /etc/hosts)

Tips & tricks

It might be useful to have a separate database.docker.yml file and a bin/docker-setup script to set everything up automatically after a fresh cloning of the repository.

The result

I can't think of a better summary than this quote from one of the Head of our designers, Paweł:

Design can seldom be validated without actually looking at what you made. And when what you made is code, it's getting harder by the minute. One can deploy after every little change which is counter productive or wait until those changes accumulate which isn't fast. Docker helps with that. I'm happy now.