Manage environmental differences with docker-compose overrides
Foreword: So recently I had the problem that I've deployed some software (Sentry) via docker-compose and needed to add some changes to the docker-compose.yml file to implement the correct routing for my setup. In doing so, I didn't want to fork the project to make my changes in the official docker-compose file and so I found the override "function" for docker-compose. And here is how you can use it.
Different Environments with differing scopes
While it is good practice to keep your environments as similar as possible, there can still be differences in their configuration. A few examples of this would be:
- As a software developer, you want to test your code on your local machine and you want to set up your application from source code, rather than from a final built container.
- You don't use the same database for your live and test systems.
- Your testing environment maybe use a different instance for example for your logging or your monitoring.
How docker-compose overrides work?
So docker-compose has an inbuilt function to merge two or more compose files together into a single merged file.
From the official docker-compose documentation:
By default, Compose reads two files, adocker-compose.yml
and an optionaldocker-compose.override.yml
file. By convention, thedocker-compose.yml
contains your base configuration. The override file, as its name implies, can contain configuration overrides for existing services or entirely new services.
If a service is defined in both files, Compose merges the configurations using the rules described in Adding and overriding configuration.
Through this feature, in my specific case, I was able to take the official docker-compose file and make my changes to docker-compose.override.yml. This spared me the creation of a repository fork and additionally, I get notified about direct changes in the official repository.
Example
To show the explained functionality a bit clearer I would like to show an example how the whole thing can look like. The example is based on the official Sentry docker-compose.yml and my docker-compose.override.yml to implement my traefik setup in front of the Sentry containers.
So here is a snippet of one of the services out of the official docker-compose.yml that I want to modify for my personal needs:
services:
nginx:
<<: *restart_policy
ports:
- "$SENTRY_BIND:80/tcp"
image: "nginx:1.21.5-alpine"
volumes:
- type: bind
read_only: true
source: ./nginx
target: /etc/nginx
depends_on:
- web
- relay
And here is the content from my docker-compose.override.yml file:
version: '3'
networks:
default:
traefik:
external:
name: traefik_network
services:
nginx:
networks:
- traefik
- default
labels:
traefik.enable: true
traefik.http.routers.sentry.rule: "Host(`example.com`)"
traefik.docker.network: traefik_network
traefik.http.routers.sentry.entrypoints: websecure
traefik.http.routers.sentry.tls: true
traefik.http.routers.sentry.tls.certresolver: default
As you can see, we add the traefik labels and some network configuration in our 'override' file.
After that, you can safe both compose files in the same location and just do your normal
docker-compose up -d
This is just a simple example. With the infinite flexibility of overrides, you can create any combination of environments to suit your needs.
Sources
Docker Compose documentation - https://docs.docker.com/compose/
Official Sentry repository - https://github.com/getsentry/self-hosted
Traefik documentation - https://doc.traefik.io/traefik/