Docker image improvements using multi-stage builds and build args

People coming from the Docker background, but new to Discourse, often seem to wonder about question like:

  • How the Docker image is setup?
  • Why do we need a bootstrap process?
  • Why is there a launcher script?
  • Why can’t we just use pure Docker or compose based setup?

There is a long conversation about these things already.

@sam has recently summarized some long-term priorities while responding to a similar set of questions in the following points:

That said, I think some recent advances in the Docker ecosystem can help solve some of these issues. I would note here a few things to begin with:

Multi-stage builds in conjunction with Build args can take care of many of the responsibilities currently coded in the bootstrap command. Suppose, we have two base images in the Docker Hub, one for the application build process that can have many libraries and tool sets used in the bootstrap process, while other base image provides minimal light-weight run-time environment (perhaps based on Alpine Linux). In the first stage of the Dockerfile we utilize the bulky builder base image, configure stuff, compile assets and whatnot, then in the second stage we inherit from the minimal runner base image and copy only necessary stuff from the first stage into appropriate places. Some configurations can be passed at build-time using build-args or using a config file before running the build process. This will yield a cleaner, faster, and light-weight image, ready for production deployment.

# Setup build-args with appropriate defaults if possible

# Building and bootstraping
FROM discourse/discourse-builder AS builder
#... Run various bootstraping processes and
#... asset precompilation

# Now build the runnable container
FROM discourse/discourse-runner
COPY --from=builder /path/to/origin /path/to/dest
CMD ["/path/to/server"]

I am not suggesting to get rid of the launcher script as it has utilities that are not related to the image building process. However, some improvements can be introduced to it such as incorporating new docker image pruning command, if suitable. A more reasonable addition to the launcher script would be the ability to generate “compose” and “stack” files and a flag for “dryrun” to output docker run commands, but not executing them.

We should also consider Dockerfile best practices to improve image building time by utilizing better layer cache. Some improvements include, minimizing number of layers, strategically reordering instructions to better utilize layer cache, and combine related instructions into a single layer.


The right place to discuss this is the topic you linked to. Best possible contribution you could make is a PR showing how all this can be done.