A OSX + Vagrant + Docker + Ruby on Rails Setup

Guy Maliar
Feb 3, 2014 · 4 min read

I’ve started a newsletter to share my stories and interesting posts I find, http://eepurl.com/gcld-T, don’t worry I won’t post unwanted or any promotional emails.

Side note: A lot of time has passed since the writing of this and easier solutions such as docker compose and docker machine have since been emerged, I recommend reading about better solutions here https://blog.codeship.com/building-microservices-docker-rails-api-gem/

Docker is getting bigger everyday that passes, and as a developer I like to toy around with bleeding edge technology.
It doesn’t support OSX right now so I needed to set up a virtual machine, obviously I’d chose Vagrant for the job.

The newest version of Vagrant supports Docker provisioning out of the box and the dear friends over at Phusion have released Docker-friendly Vagrant boxes.

Over at Hummingbird Soft we have seen some nasty bugs caused by development/production developer environments inconsistencies so obviously we should mimic production environments as much as possible.

I've drawn inspiration from the relateiq blog: A docker dev environment in 24 hours Part 1 and Part 2

By the time of writing Vagrantfile can’t tell Vagrant to build images from a Dockerfile only to pull from the index so I had to hack a bit to get it to work.
That being said the Vagrant guys had already merged a PR to build images as well.

I set my directory structure to be:

 /project-name
├── project
| ├── Dockerfile
| ├── Gemfile
| ├── Gemfile.lock
| ├── Procfile
| ├── Rakefile
| ├── bin
| ├── app
| ├── bin
| ├── config
| ├── config.ru
| ├── db
| ├── lib
| ├── log
| ├── public
| ├── test
| ├── tmp
| └── vendor
└── Vagrantfile

This directory structure allows me to work on my Mac using my favorite text editor and keep both a virtualized and isolated environment using both Vagrant’s and Docker’s synced directories.

So using all this knowledge I built this Vagrantfile

 VAGRANTFILE_API_VERSION = “2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = “docker-ubuntu-12.04.3-amd64-vbox”
config.vm.box_url = “https://oss-binaries.phusionpassenger.com/vagrant/boxes/ubuntu-12.04.3-amd64-vbox.box
config.vm.network :forwarded_port, guest: 3000, host: 3000
config.vm.synced_folder “./project”, “/project”

config.vm.provision “docker”

cmd = <<SCRIPT
echo -e “nameserver 127.0.0.1\nnameserver 8.8.8.8\nnameserver 8.8.4.4" | tee /etc/resolv.conf
docker build -t rails /project/.
SCRIPT

config.vm.provision :shell, :inline => cmd

config.vm.provision “docker” do |d|
d.run “rails”,
args: “-v /project:/project -p 3000:3000"
end
end

Let’s break it down:

config.vm.box = “docker-ubuntu-12.04.3-amd64-vbox”config.vm.box_url = “https://oss-binaries.phusionpassenger.com/vagrant/boxes/ubuntu-12.04.3-amd64-vbox.box

This is how we fetch the precompiled Ubuntu 12.04 with Docker dependencies already set up for us.

config.vm.network :forwarded_port, guest: 3000, host: 3000

The usual port forwarding from Vagrant to the host so we could access the application from our browser

config.vm.synced_folder “./project”, “/project”

Standard sharing folders from host to Vagrant machine

config.vm.provision “docker”

This is how we tell Vagrant to download Docker onto the machine as the provisioner we will use, this just installs Docker for us on the Vagrant machine.

cmd = <<SCRIPT
echo -e “nameserver 127.0.0.1\nnameserver 8.8.8.8\nnameserver 8.8.4.4" | tee /etc/resolv.conf
docker build -t rails /project/.
SCRIPT
config.vm.provision :shell, :inline => cmd

This is the hack that allows us to build images inside the Vagrant machine, we’re just running a shell command :-)

The line before the docker build is just setting the our DNS nameservers, I am not sure if this is an issue with my Vagrant version or not but this seems to allow my Vagrant machine to communicate with the outside world and actually download images from the Docker index, YMMV.

config.vm.provision “docker” do |d|
d.run “rails”,
args: “-v /project:/project -p 3000:3000 -d”
end

Now we use the docker provisioner again to run our newly created image and adding the arguments to mount the /project folder into Docker and forward the port from the docker container back to Vagrant.

So when I send a request from the browser to localhost:3000 it goes to Vagrant which in turn goes to Docker which has my Rails application running.

The Dockerfile I am using is taken mostly out of Matt Garrison from Iconoclast Labs’s github experiment called vagrant-docker-rails but I stripped it down for brevity.

 ## Dockerfile

FROM ubuntu
RUN apt-get update

## RUBY
RUN apt-get install -y -q git ruby1.9.1 ruby1.9.1-dev rubygems1.9.1 irb1.9.1 build-essential libopenssl-ruby1.9.1 libssl-dev zlib1g-dev

## RAILS
RUN gem install rails —no-ri —no-rdoc

## For execjs — needs node
RUN apt-get install -y python-software-properties python python-setuptools ruby rubygems
RUN add-apt-repository ppa:chris-lea/node.js
RUN echo “deb http://us.archive.ubuntu.com/ubuntu/ precise universe” >> /etc/apt/sources.list
RUN apt-get update
RUN apt-get install -y nodejs

## SQLite
RUN apt-get install -y sqlite3 libsqlite3-dev libyaml-dev libreadline-dev libxml2-dev libxslt1-dev
ADD . /project RUN cd /project;bundle install EXPOSE 3000

CMD [“/usr/local/bin/foreman”, “start”, “-d”, “/project”]

This is a pretty standard configuration for Rails, the only thing that is important to pinpoint is the use of a Procfile as the `CMD` that Docker expects.
This is nice because it controls the start of our Rails server and later allows us to deploy to Heroku with ease if we’d like (Just remember to have foreman in your Gemfile for this to work).

An alternative is to use supervisord or any init.d(?)

Now all that remains is to

vagrant up

and see our virtual machine and container getting built, when we go to http://localhost:3000 we will be able to see our new Rails application.

Deploying to heroku is as easy as just going inside the project directory and

git push heroku master

I’ve started a newsletter to share my stories and interesting posts I find, http://eepurl.com/gcld-T, don’t worry I won’t post unwanted or any promotional emails.

    Guy Maliar

    Written by

    Director of Technology @ Tailor Brands Interested in web development, cloud infrastructure and data engineering

    Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
    Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
    Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade