Compose, Break, Repeat

Welcome! I’m Guillaume Lours, Docker Compose maintainer, passionate about containerization and developer tools.
I’m currently Software engineer at Docker working on Compose and Docker Sandboxes.

Exploring the iterative world of software engineering, Docker, and the art of building things that sometimes break.

Docker Compose Tip #46: Build args vs environment variables

Build args and environment variables both pass values to your containers, but they work at different times and serve different purposes. Mixing them up is a common source of confusion. Build args: build-time only Build args are available during docker build and are not present in the running container: services: app: build: context: . args: NODE_VERSION: "20" APP_VERSION: "2.1.0" In the Dockerfile, they’re consumed with ARG: ...

March 30, 2026 · 3 min · 471 words · Guillaume Lours

Docker Compose Tip #45: Multi-stage builds with target

Multi-stage Dockerfiles let you define multiple build stages. With the target option in Compose, you can choose which stage to build — giving you different images from the same Dockerfile. A multi-stage Dockerfile # Stage 1: dependencies FROM node:20-slim AS deps WORKDIR /app COPY package*.json ./ RUN npm ci # Stage 2: development (with dev dependencies and tools) FROM deps AS dev RUN npm install --include=dev COPY . . CMD ["npm", "run", "dev"] # Stage 3: build FROM deps AS build COPY . . RUN npm run build # Stage 4: production (minimal) FROM node:20-slim AS production WORKDIR /app COPY --from=build /app/dist ./dist COPY --from=deps /app/node_modules ./node_modules CMD ["node", "dist/index.js"] Targeting stages in Compose Use target to pick which stage to build: ...

March 27, 2026 · 2 min · 354 words · Guillaume Lours

Docker Compose Tip #44: Signal handling in containers

When you run docker compose down or docker compose stop, Compose sends a signal to your containers. Understanding which signal is sent and how your application handles it is key to graceful shutdowns. Default behavior By default, Compose sends SIGTERM to the main process (PID 1), waits 10 seconds, then sends SIGKILL: services: app: image: myapp # Default: SIGTERM, 10s grace period, then SIGKILL Changing the stop signal Some applications expect a different signal. Nginx, for example, uses SIGQUIT for graceful shutdown: ...

March 25, 2026 · 2 min · 386 words · Guillaume Lours

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