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 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.