Methods to enable environments in Docker Compose
In this blog post I'll show two methods how to enable different environments in Docker Compose and which of them I prefer most.
I use Docker Compose when I need to run a multi-container Docker application.
Often I need to have different setups for different environments.
For example, I want to set debug
variable to false
for a production environment and have an
additional service for database administration for a development.
Docker Compose is a great tool, and it natively enables this in different ways. But usually we need to define common and environment-specific configurations and tell Compose how to merge them.
Code for this blog post is available here
Extending using multiple Compose files
With this method we just define environment-specific variables with the same service name across different configs.
If a service is defined in both files, Compose merges the configurations using the rules described in Adding and overriding configuration.
So if we have docker-compose.yml
version: "3.9"
services:
db:
image: postgres
environment:
- POSTGRES_PASSWORD
app:
image: python:3.9
depends_on:
- db
ports:
- 8080:8080
And docker-compose.dev.yml
services:
db:
ports:
- 5432:5432
app:
environment:
- DEBUG=true
adminer:
image: adminer
The merged services are as follows:
$ docker-compose -f docker-compose.yml -f docker-compose.dev.yml config
services:
adminer:
image: adminer
app:
depends_on:
db:
condition: service_started
environment:
DEBUG: "true"
image: python:3.9
ports:
- published: 8080
target: 8080
db:
environment:
POSTGRES_PASSWORD: null
image: postgres
ports:
- published: 5432
target: 5432
version: '3.9'
Extending using the extends field
Docker Compose’s extends keyword enables the sharing of common configurations among different files, or even different projects entirely.
With this method, we explicitly define services we extend from. There'll be a base config with some common
things.
It's worth noting that this method is deprecated and work only for Compose file versions up to 2.1.
This is our common config common-services.yml
version: "2.1"
services:
db:
image: postgres
environment:
- POSTGRES_PASSWORD
app:
image: python:3.9
depends_on:
- db
ports:
- 8080:8080
And this is the environment-specific config docker-compose.dev.yml
services:
db:
extends:
file: common-services.yml
service: db
ports:
- 5432:5432
app:
extends:
file: common-services.yml
service: db
environment:
- DEBUG=true
adminer:
image: adminer
The merged services are as follows:
$ docker-compose -f docker-compose.dev.yml config
services:
adminer:
image: adminer
app:
environment:
DEBUG: "true"
POSTGRES_PASSWORD: null
image: postgres
db:
environment:
POSTGRES_PASSWORD: null
image: postgres
ports:
- published: 5432
target: 5432
version: '3.9'
$ docker-compose -f docker-compose.dev.yml config
services:
adminer:
image: adminer
app:
environment:
DEBUG: "true"
POSTGRES_PASSWORD: null
image: postgres
db:
environment:
POSTGRES_PASSWORD: null
image: postgres
ports:
- published: 5432
target: 5432
version: '3.9'
Conclusion
Personally, I prefer the first method because:
- It is more transparent for you as to what parts your final config is constructed of as you define all files in a command;
- It is not deprecated
But of course it requires more typing for a command :(