You read thoughtbot’s Rails on Docker article, moved your entire development environment into Docker containers via Docker Compose, then realized everytime you want to bump or add a gem to your project you have to rebuild the rails container and wait forever to run bundle from scratch.
With Docker data containers and a little hack in the docker-compose.yml file you don’t have to waste so much time running bundler from scratch everytime you update the Gemfile.
Building on thoughtbot’s Dockerfile example, set the BUNDLE_PATH environment variable to store the bundler cache outside of the rails project path:
FROM ruby:2.2.0
RUN apt-get update -qq && apt-get install -y build-essential
# for postgres
RUN apt-get install -y libpq-dev
# for nokogiri
RUN apt-get install -y libxml2-dev libxslt1-dev
# for capybara-webkit
RUN apt-get install -y libqt4-webkit libqt4-dev xvfb
# for a JS runtime
RUN apt-get install -y nodejs
ENV APP_HOME /myapp
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
ADD Gemfile* $APP_HOME/
# --- Add this to your Dockerfile ---
ENV BUNDLE_GEMFILE=$APP_HOME/Gemfile \
BUNDLE_JOBS=2 \
BUNDLE_PATH=/bundle
RUN bundle install
ADD . $APP_HOME
Now add a bundle container to the docker-compose.yml file. This may be the fig.yml file if you haven’t upgraded from Fig 1.0 to Docker Compose 1.1 yet.
web:
build: .
command: bin/rails server --port 3000 --binding 0.0.0.0
ports:
- "3000:3000"
links:
- db
volumes:
- .:/myapp
# This tells the web container to mount the `bundle` images'
# /bundle volume to the `web` containers /bundle path.
volumes_from:
- bundle
# --- Add this to your fig.yml or docker-compose.yml file ---
bundle:
# 'image' will vary depending on your docker-compose
# project name. You may need to run `docker-compose build web`
# before this works.
image: myapp_web
command: echo I'm a little data container, short and stout...
volumes:
- /bundle
Now when the web container runs, it mounts the bundle container’s /bundle path that we specified in the BUNDLE_GEMFILE environment variable.
To update the project’s bundle execute:
$ docker-compose run web bundle
Bundler picks up the existing bundler cache from the /bundle volume on the bundle container and updates all the gems. After the bundle container exits, the /bundle folder sticks around because we told Docker to persist it to the host machine via the volumes directive. The web container picks up the changes from the /bundle path without needing to rebuild everything from scratch.
If you intend on deploying the web container you’ll need to rebuild it without linking the bundle data container, but that’s not a big deal since you’d have to rebuild the container anyway with your code changes.
A few months back decided that I’d only run development environments inside Docker containers so that I’d Learn The Hard Way™ all of the workflow pitfalls before rolling it out to the rest of the crew at Poll Everywhere. I’ve got a few more articles in the works that uncover more Docker development environment naunces that I’ll tweet from @bradgessler.