Day 11: Advanced Docker Compose
This is a crosspost from adventofdocker.com
Welcome to day 12 of Advent of Docker! Yesterday we learned the basics of Docker Compose. Today, we’ll explore some more advanced features that make Docker Compose even more powerful.
Environment Files
Remember when we hardcoded database credentials in our compose file yesterday? Let’s fix that using environment files. Create a file called .env
:
POSTGRES_USER=postgres
POSTGRES_PASSWORD=my-super-secret-password
POSTGRES_DB=myapp
PORT=8080
SECRET_MESSAGE=Hello Docker!
Now modify the compose file to use the environment file:
services:
api:
build: .
ports:
- "8080:8080"
env_file:
- .env
volumes:
- mydata:/data
networks:
- myapp-network
depends_on:
- db
db:
image: postgres
ports:
- "5432:5432"
env_file:
- .env
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- myapp-network
nginx:
image: nginx
ports:
- "80:80"
networks:
- myapp-network
depends_on:
- api
volumes:
mydata:
postgres_data:
networks:
myapp-network:
You can specify different environment files per service:
services:
db:
image: postgres
env_file:
- .env.db # Database specific env file
api:
build: .
env_file:
- .env.api # API specific env file
Remember to add your .env
files to .gitignore
to keep your secrets safe! You can provide an example configuration by creating a .env.example
file:
POSTGRES_USER=
POSTGRES_PASSWORD=
POSTGRES_DB=
PORT=8080
SECRET_MESSAGE=
Healthchecks
Docker Compose provides a healthcheck feature to check if a service is healthy, and to wait for a service to be healthy before starting another service if it depends on it.
services:
api:
build: .
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
image: postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
Here we’ve added:
-
healthcheck
: Defines how Docker should check if a service is healthy -
depends_on
withcondition
: Waits for the database to be healthy before starting the API
This ensures our API only starts when the database is ready and your application is healthy.
Compose Watch
Docker Compose Watch is a fantastic feature for development. It automatically syncs your code changes into containers and can trigger rebuilds:
services:
api:
build: .
ports:
- "8081:8081"
environment:
- PORT=8081
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
develop:
watch:
- path: ./
target: /go
action: rebuild
With this configuration:
- Any changes to the
./
directory are synced instantly into the container and trigger a rebuild
Start your development environment with:
docker compose watch
Profiles
Profiles help you manage different service combinations. For example, you might want development tools only in development:
services:
api:
build: .
db:
image: postgres
adminer:
image: adminer
profiles:
- dev
ports:
- "9000:8080"
Now you can:
- Start normal services:
docker compose up
- Include dev tools:
docker compose --profile dev up
Best Practices
- Use health checks for critical services
-
Never store secrets in your compose file
- Use secret files
- Or use environment variables
- Use profiles to separate development and production services
- Leverage watch for faster development
-
Document dependencies using
depends_on
Next Steps
Try these features in your own projects! Some ideas:
- Convert your development environment to use compose watch
- Add health checks to your services
- Set up proper secret management
Remember, the best way to learn is by doing. If you run into any issues or have questions, feel free to reach out by email.
Happy coding! 🎄
Jonas