DEV Community

Michael Lip
Michael Lip

Posted on • Originally published at zovo.one

Docker Compose Files: The YAML Configuration That Replaces 20 Pages of Setup Documentation

Every project has a setup document. "Install Postgres 15. Create a database called app_dev. Install Redis. Set the REDIS_URL environment variable. Install Node 20. Run npm install. Start the server." Docker Compose replaces all of that with a single file that anyone can run with one command.

I converted my team's 4-page setup document into a docker-compose.yml file that takes 30 seconds to start. Here is what I learned about getting it right.

The structure of a compose file

A docker-compose.yml defines services (containers), their configuration, and how they connect:

version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/app
      - REDIS_URL=redis://cache:6379
    depends_on:
      - db
      - cache

  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=app
    volumes:
      - pgdata:/var/lib/postgresql/data

  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  pgdata:
Enter fullscreen mode Exit fullscreen mode

docker compose up starts all three services, creates the network, sets up the volume, and your app is running with a real database and cache.

Key configuration patterns

Networking: By default, all services in a compose file share a network. Service names resolve as hostnames. The app container can reach Postgres at db:5432 and Redis at cache:6379. No IP addresses, no manual network configuration.

Volumes: Named volumes persist data between container restarts. Without the pgdata volume, your database would be wiped every time you recreate containers. Bind mounts (./src:/app/src) map host directories into containers for development with live reload.

Build vs Image: build: . builds from a local Dockerfile. image: postgres:15 pulls a pre-built image from Docker Hub. Your application services typically use build, while databases, caches, and other infrastructure use image.

Environment variables: Can be set inline, loaded from a file (env_file: .env), or reference host environment variables. For sensitive values, use .env files that are in .gitignore.

Depends_on: Controls startup order but does not wait for the service to be "ready." Postgres might start before it is accepting connections. For true readiness checks, use healthcheck with depends_on condition:

db:
  image: postgres:15
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -U user"]
    interval: 5s
    timeout: 5s
    retries: 5

app:
  depends_on:
    db:
      condition: service_healthy
Enter fullscreen mode Exit fullscreen mode

Development vs production patterns

Development compose files should include:

  • Volume mounts for live code reload
  • Debug ports exposed
  • Development-mode environment variables
  • Hot reload configurations

Production compose files should include:

  • Resource limits (CPU, memory)
  • Restart policies (restart: unless-stopped)
  • No volume mounts for source code (baked into the image)
  • Health checks
  • Logging configuration

Use multiple compose files with docker compose -f docker-compose.yml -f docker-compose.dev.yml up to override production settings with development ones.

Common service templates

Postgres + pgAdmin:

db:
  image: postgres:15
  environment:
    POSTGRES_PASSWORD: devpass
  volumes:
    - pgdata:/var/lib/postgresql/data

pgadmin:
  image: dpage/pgadmin4
  ports:
    - "5050:80"
  environment:
    PGADMIN_DEFAULT_EMAIL: admin@dev.local
    PGADMIN_DEFAULT_PASSWORD: admin
Enter fullscreen mode Exit fullscreen mode

Node.js with hot reload:

app:
  build: .
  command: npm run dev
  volumes:
    - .:/app
    - /app/node_modules
  ports:
    - "3000:3000"
Enter fullscreen mode Exit fullscreen mode

The /app/node_modules anonymous volume prevents the host's node_modules from overriding the container's.

The generator

Building compose files from scratch means remembering service-specific environment variables, port mappings, volume paths, and configuration options for every service. I built a Docker Compose generator that lets you select your services, configure the options, and generates a ready-to-use compose file.


I'm Michael Lip. I build free developer tools at zovo.one. 500+ tools, all private, all free.

Top comments (0)