backFebruary 21, 2026

Docker Compose Cheat Sheet

Docker Compose is part of my daily workflow. Here's an overview of the commands and configuration options I actually use.

The basics

Start all services defined in your compose.yaml:

docker compose up

Run in the background (detached mode):

docker compose up -d

Stop and remove containers:

docker compose down

Want to remove volumes too? Add -v:

docker compose down -v

Building images

Build images before starting:

docker compose up --build

Just build, don't start:

docker compose build

Force a rebuild without cache:

docker compose build --no-cache

Working with services

Start a specific service:

docker compose up -d postgres

Stop a specific service:

docker compose stop postgres

Restart a service:

docker compose restart postgres

View logs:

docker compose logs

Follow logs in real-time:

docker compose logs -f

Logs for a specific service:

docker compose logs -f postgres

Running commands

Execute a command in a running container:

docker compose exec app bash

Run a one-off command (starts a new container):

docker compose run --rm app php bin/console cache:clear

The --rm flag removes the container after the command finishes. You'll want this most of the time.

A practical compose.yaml

Here's a setup I use for Symfony projects:

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - .:/var/www/html
    ports:
      - "8080:80"
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      DATABASE_URL: postgresql://app:secret@postgres:5432/app

  postgres:
    image: postgres:18-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: app
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:8-alpine
    ports:
      - "6379:6379"

  mailpit:
    image: axllent/mailpit
    ports:
      - "1025:1025"
      - "8025:8025"

volumes:
  postgres_data:

Environment variables

Use a .env file in the same directory as your compose.yaml:

POSTGRES_PASSWORD=secret
APP_DEBUG=true

Reference them in your compose file:

services:
  postgres:
    image: postgres:18-alpine
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

Or load an entire env file into a container:

services:
  app:
    image: my-app
    env_file:
      - .env
      - .env.local

Healthchecks

Don't just use depends_on. Your database container might be running, but Postgres itself isn't ready yet. Use healthchecks:

services:
  postgres:
    image: postgres:18-alpine
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      timeout: 5s
      retries: 5

  app:
    depends_on:
      postgres:
        condition: service_healthy

Now app waits until Postgres actually accepts connections.

Volumes

Named volumes persist data:

services:
  postgres:
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Bind mounts for development:

services:
  app:
    volumes:
      - .:/var/www/html
      - /var/www/html/vendor

That second line creates an anonymous volume for vendor, so it won't sync with your host. Useful when dependencies differ between your host and container.

Networks

By default, Compose creates a network for your project. All services can reach each other by their service name.

Need a custom network?

services:
  app:
    networks:
      - frontend
      - backend

  postgres:
    networks:
      - backend

networks:
  frontend:
  backend:

Now postgres isn't reachable from frontend.

Multiple compose files

Override settings for different environments:

docker compose -f compose.yaml -f compose.prod.yaml up -d

The second file overrides or extends the first.

Or use compose.override.yaml. Compose picks this up automatically:

compose.yaml           # Base configuration
compose.override.yaml  # Local development overrides (auto-loaded)

Profiles

Define services that only run when you need them:

services:
  app:
    image: my-app

  debug:
    image: busybox
    profiles:
      - debug

The debug service won't start with docker compose up. Enable it explicitly:

docker compose --profile debug up

Scaling services

Run multiple instances of a service:

docker compose up -d --scale worker=3

This starts 3 containers for the worker service. Don't define a host port when scaling, or you'll get port conflicts.

Useful commands I always forget

See running containers:

docker compose ps

See all containers (including stopped):

docker compose ps -a

View resource usage:

docker compose top

Pull latest images:

docker compose pull

Remove stopped containers:

docker compose rm

Nuclear option (remove everything, including volumes and images):

docker compose down -v --rmi all

Quick reference

CommandWhat it does
up -dStart in background
downStop and remove
down -vStop, remove, and delete volumes
logs -fFollow logs
exec app bashShell into running container
run --rm app cmdRun one-off command
build --no-cacheRebuild from scratch
psList containers
pullUpdate images