Home/Engineer/Docker / Containers/Docker Compose in Practice —…
Docker / ContainersIntermediateUbuntuDebianCentOSdockerdocker-composeContainers

Docker Compose in Practice — Configuring Multi-Container Apps

A practical rundown of patterns from writing docker-compose.yml through service dependencies, volumes, environment variables, and health checks.

Contents
  1. Basic docker-compose.yml Structure
  2. Commonly Used Commands
  3. Separating Environment Files
  4. Resource Limits
  5. Production-Style Nginx + App + DB Setup

Basic docker-compose.yml Structure

YAML
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
    depends_on:
      db:
        condition: service_healthy
    volumes:
      - ./logs:/app/logs

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: secret
    volumes:
      - pg_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  pg_data:

Commonly Used Commands

Bash
# Build + start (detached)
docker compose up -d --build

# Restart a specific service only
docker compose restart app

# Follow logs in real time
docker compose logs -f app

# Status of running containers
docker compose ps

# Shell into a container
docker compose exec app sh

# Stop everything + remove networks/volumes
docker compose down -v

# Scale up (run 3 instances of the app service)
docker compose up -d --scale app=3

Separating Environment Files

Bash
# .env file (loaded automatically by default)
DB_PASSWORD=secret
APP_PORT=3000
YAML
# Used in docker-compose.yml
environment:
  DB_PASSWORD: ${DB_PASSWORD}
  APP_PORT: ${APP_PORT}
Bash
# Environment-specific override
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Resource Limits

YAML
services:
  app:
    image: myapp:latest
    deploy:
      resources:
        limits:
          cpus: "0.50"
          memory: 512M
        reservations:
          cpus: "0.25"
          memory: 256M

Production-Style Nginx + App + DB Setup

YAML
services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./certbot/conf:/etc/letsencrypt:ro
    depends_on:
      - app

  app:
    build:
      context: .
      dockerfile: Dockerfile
    expose:
      - "3000"
    env_file:
      - .env
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    volumes:
      - pg_data:/var/lib/postgresql/data
      - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql
    env_file:
      - .env.db
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 10s
      retries: 5
    restart: unless-stopped

volumes:
  pg_data:
#docker#docker-compose#Containers#devops
Editorial note

AI tools may assist drafting. Check the evidence in each guide for whether commands were run and which environment was used. Confirm the current docs for your version before applying instructions, and email us about errors. email us

Topic-related official docs (auto-linked · not per-claim sources)Docker documentation ↗

Questions & answers

Ask a question about this guide. We'll review and reply.