Rails on Docker: Using an Entrypoint File in your Containers

When using Docker to run your Rails apps in development, you might come across a leftover server.pid file in your app's tmp/ folder.

This file is created when you run rails server to start your app. If your container is not shut down properly, this server.pid file is not removed. This prevents you from starting a new container; as the file is copied into the new container, you will be confronted with the following message when you try to run your app:

web_1    | A server is already running. Check /usr/src/app/tmp/pids/server.pid.
...
web_1    | Exiting

Of course, the simple way to resolve this is to remove the offending file ($ rm tmp/pids/server.pid) and restart your container.

This can quickly become annoying, though, and won't help if (for example) you are trying to scale the number of containers in your development environment. Every new container will inherit the tmp/pids/server.pid file, and so exit immediately.

Using an Entrypoint File

To properly handle this situation is to use a docker entrypoint file. This is a simple shell script that becomes the command that is called when your container starts.

Entrypoint files are commonly used to set up or configure a container at runtime. For example, when you run the postgres or mariadb containers, their entrypoint files create and configure a default database.

An example entrypoint file for our Rails app to resolve the problem above might be:

#!/bin/sh

rm -f tmp/pids/server.pid
bin/rails server -b 0.0.0.0 -p $PORT

Save this file as docker-entrypoint.sh in your app's folder (in the same location as Dockerfile). You'll also need to make sure it is executable by modifying its permissions:

$ chmod u+x ./docker-entrypoint.sh

Next, in your app's Dockerfile, set the CMD line to run your new entrypoint file:

# Dockerfile
FROM ruby:2.5
# ...
EXPOSE $PORT
CMD ./docker-entrypoint.sh

Note that you could use the _ENTRYPOINT_ command in this instance, but as we're supplying default arguments (_server_, _-b_ and _-p_) to the _bin/rails_ command, I prefer to use _CMD_ in line with the Docker documentation.

Alternatively, if you only want to use your entrypoint file for local development, you could simply override the container's default command in your docker-compose.yml file:

# docker-compose.yml
# ...
web:
  build: .
  command: ./docker-entrypoint.sh
  # ...

Now you can spin up your container(s) in the normal way using Docker compose:

$ docker-compose up -d --scale web=2

Next Steps

In this simple example, you've seen how to use an entrypoint file to initialise the state of a container at runtime. You can also use entrypoint files to perform much more complicated setup, such as creating runtime files and databases, configuring local services, and more.

👋 Thanks for reading - I hope you enjoyed this post. If you find it helpful and want to support further writing and tutorials like this one, please consider supporting my work with a coffee!

Support ☕️