Containerizing Rails: Techniques, Pitfalls, and Best Practices (RailsConf 2018)
Tags: ruby, rails, docker
Published: 2019-07-08 20:49:42

---
title: Containerizing Rails: Techniques, Pitfalls, and Best Practices (RailsConf 2018)
published: true
tags: ruby, rails, docker
canonical_url: https://daniel-azuma.com/articles/talks/railsconf-2018
---

On April 18, 2018, I spoke on "Containerizing Rails: Techniques, Pitfalls, and Best Practices" at RailsConf in Pittsburgh, PA. The talk discussed best practices for designing Docker containers for Rails applications.

## Talk resources

- [Video on Confreaks](http://confreaks.tv/videos/railsconf2018-containerizing-rails-techniques-pitfalls-best-practices)
- [Slides on SpeakerDeck](https://speakerdeck.com/dazuma/containerizing-rails-techniques-pitfalls-and-best-practices)

## A list of tips

Here is the full list of tips we covered in the talk.

1.  **Read and understand your base image.**

    Dockerfiles are generally accessible on Github. It’s important to understand what your base image is doing, so you can make a judgment on whether it is appropriate for your application.

2.  **Combine install commands with cleanup**

    It is important to ensure temporary/unneeded files do not appear in any intermediate layers in your image.

3.  **Use a separate build stage**

    Multi-stage Dockerfiles are very powerful and useful for ensuring your final image has only the files you need at runtime.

4.  **Set the system locale**

    Some Linux distributions do not set it by default, and that could have unexpected effects on your string encodings in Ruby.

5.  **Create an unprivileged user**

    Set up defense in depth. By default, processes in a container run as superuser, which is not what you want for Rails.

6.  **Prefer exec form for CMD**

    Many reasons, including interoperability with ENTRYPOINT. But in particular, it’s needed for proper signal propagation and safe container shutdown.

7.  **Prefix shell form with the “exec” keyword**

    If you must use shell form, prefix with the “exec” keyword to cause bash to propagate signals.

8.  **Avoid ONBUILD**

    It’s just not that useful a feature. It makes assumptions about the needs of your app, and involves implicit rather than explicit builds.

9.  **Always specify resource constraints**

    This is crucial to ensuring container behavior is stable and predictable in production. If you can’t define static constraints, it’s an indication your container may be doing too much.

10. **Avoid preforking in a container**

    Preforking has complicated interactions with resources such as file handles, and can lead to less predictable memory behavior

11. **Scale by adding containers**

    Containers are best used as the unit of scaling. If you need more resources, add more containers.

    I’m sorry I didn’t make this that clear in the talk itself, but it’s okay to have multiple processes in a single container if they work together to perform a service, provided that the container still has static, predictable resource constraints.

12. **Log to STDOUT or an external agent**

    Direct your production logs outside the container. The easiest way is to log to STDOUT by setting the `RAILS_LOG_TO_STDOUT` environment variable.

## Links

- The Docker documentation publishes another list of good [Dockerfile best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/).
- [Phusion’s baseimage](https://github.com/phusion/baseimage-docker) provides support for a number of complex use cases such as controlling the init procedure and running daemons within a container. It’s well documented and very useful for learning about Docker’s edge cases, though it may be overkill for most applications.