Slim Docker Images for Rails

At Tinfoil we’ve been building and distributing our applications with Docker for a few years now. One aspect we value of our Docker images is keeping them small and nimble. By default it’s easy to have a Docker image become bloated because each command introduces a new layer and history of changes to the file system. Luckily there are some tricks to reducing the final image size without squashing all of the layers together.

We can start with a modern Rails application that uses Yarn in addition to Sprockets to manage JavaScript dependencies, Bundler to manage the Ruby gem dependencies, and an expectation that we'll be connecting to an external PostgreSQL database.

A simple starting Dockerfile might look like the one below. We need Node.JS and Yarn installed to precompile our JavaScript assets.

The final image size is 1.11GB! We can start off the weight loss program by combining the commands to install Node.js and Yarn, as well as cleaning up the apt package caches.

That made it a tiny bit smaller: 1.09GB. The ruby:2.5 image is based off of Debian, and has a lot of extra utilities and functionality preinstalled. We’ve found a lot of success making smaller images by basing the image off of Alpine Linux. Most ruby code works fine under Alpine, but since it uses musl instead of glibc, you have to be careful with some C dependencies or ruby gem extensions.

421MB now, so we’re making some nice improvements. We don’t need all of the NPM packages at runtime, so we can use a multi-stage Dockerfile to avoid storing those layers in the final image. Multiple stages split up the build and precompilation steps in their own Docker images, and we can copy out the build artifacts into our final image.

It’s now 231MB, a savings of around 75% 🎉. Note that the `uglifier` gem used by default in Rails 5.2 still requires you to have a Javascript runtime available, otherwise our final docker image could be even smaller.

For the next related post we’ll go over how to use multi-stage Dockerfiles for an Elixir Phoenix project for some more impressive size savings. 

Ben Sedat

Ben Sedat is the Engineering Wizard of Tinfoil Security. He's a bit of a blend between a traditional software engineer (builder) and security engineer (breaker). He spends a lot of time thinking about security: both detection as well as creating solutions for the security issues that exist in software and the internet. He also plays lots of video games. Lots.