Docker Compose Tip #43: Read-only root filesystems

Making a container’s root filesystem read-only is one of the simplest and most effective hardening measures. If an attacker gets in, they can’t modify binaries or drop malicious files. Basic usage services: app: image: myapp read_only: true That’s it. The container’s filesystem is now immutable. But most applications need to write somewhere — logs, temp files, caches. That’s where tmpfs comes in. Read-only with tmpfs for writable directories Combine read_only with tmpfs to allow writes only where needed: ...

March 23, 2026 · 2 min · 336 words · Guillaume Lours

Docker Compose Tip #42: Variable substitution and defaults

Docker Compose supports shell-style variable substitution in your Compose files. Combined with defaults and error messages, it makes your configurations flexible and safe. Basic substitution Reference environment variables or .env file values: services: app: image: myapp:${TAG} environment: DATABASE_URL: postgres://${DB_USER}:${DB_PASS}@db/${DB_NAME} # .env TAG=2.1.0 DB_USER=admin DB_PASS=secret DB_NAME=myapp Default values Provide fallback values when a variable is unset or empty: services: app: image: myapp:${TAG:-latest} environment: LOG_LEVEL: ${LOG_LEVEL:-info} PORT: ${PORT:-3000} deploy: replicas: ${REPLICAS:-1} Two syntaxes with a subtle difference: ...

March 20, 2026 · 2 min · 417 words · Guillaume Lours

Docker Compose Tip #41: Container lifecycle hooks

Docker Compose supports lifecycle hooks that let you run commands at specific points in a container’s lifecycle, right after it starts and just before it stops. post_start hook Run a command inside the container right after it starts — perfect for initialization tasks like database migrations: services: api: image: myapp-api post_start: - command: /app/migrate.sh depends_on: postgres: condition: service_healthy Or warming up caches so the service is ready to handle traffic: ...

March 18, 2026 · 2 min · 306 words · Guillaume Lours

Docker Compose Tip #40: Using labels for service organization and monitoring

Labels are key-value metadata attached to containers. They cost nothing at runtime but unlock powerful filtering, organization, and tool integrations. Adding labels to services services: api: image: myapp-api labels: com.example.team: "backend" com.example.env: "production" com.example.version: "2.1.0" worker: image: myapp-worker labels: com.example.team: "backend" com.example.env: "production" com.example.role: "async-processing" You can also use the list syntax: services: api: image: myapp-api labels: - "com.example.team=backend" - "com.example.env=production" Filtering with labels Labels become powerful with docker compose ps: ...

March 16, 2026 · 2 min · 258 words · Guillaume Lours

Docker Compose Tip #39: Combining include, extends, and overrides

The real power comes from using all three mechanisms together, each doing what it does best. The scenario A team maintaining a web application with: Shared infrastructure (database, monitoring) reused across projects Common service configuration (logging, labels) applied to all services Different settings for local development vs CI vs production Project structure my-project/ ├── compose.yml # Main entry point ├── compose.override.yml # Local dev overrides ├── compose.ci.yml # CI-specific overrides ├── base/ │ └── service-base.yml # Shared service config (extends) └── infra/ ├── database.yml # Postgres stack (include) └── monitoring.yml # Prometheus + Grafana (include) Step 1: Shared service config with extends Define common configuration once: ...

March 13, 2026 · 2 min · 324 words · Guillaume Lours