In my last post about Docker, I outlined a technique I used for having a single container configurable via environment variables, even if the app we are trying to run does not support it by default. I used nginx as an example use case.

Today we are going one step further: what if we want to be kind and release said image to the public? Clearly, we have to generalize some steps of the process so we can save users from inheriting the Dockerfile.

We want an image that meets the following criteria:

Extends the official nginx image. This is important since it’s the one most users will trust, is most supported and can be expected to be up to date.

Supports a configuration file template that is evaluated from environment variables, as described in my previous blog post. Twelve factor all the way!

Its base configuration stems from best practices outlined in h5bp. As html5boilerplate which you may already know, lots of the repos in this project attempt to gather knowledge from the community in order to build config files with sensible defaults and best practices, to use with a variety of software.

Can run arbitrary (bash) scripts at startup easily. I do not want to replicate what’s already in docker-entrypoint.sh.

Nginx config files will be generated from Jinja2 templates. The values of the variables are resolved from the environment variables (ie. those used by Docker and those passed by docker run).

Here is what will happen when a container from this image runs:

Whatever is in the /templates directory is copied to /etc/nginx. All files of the form .j2 (the extension of Jinja2 templates) inside /etc/nginx are found (recursively) and evaluated, leaving them as . For example, /etc/nginx/sites-enabled/mysite.com.j2 would be stored as /etc/nginx/sites-enabled/mysite.com. All .sh scripts present in /docker-entrypoint-init.d directory (if any) are executed. Finally, nginx starts.

The way we achieve all this is with a custom docker intialization script. You may view the sources on github.

The image is also available on the docker registry. It leverages automated builds, so it will get rebuilt whenever a change (such as version upgrade) is pushed to the official nginx image.