Docker Compose — What is Docker Compose

3 minute read

Docker Compose is a tool for defining and running multi-container Docker applications. You describe your entire application stack in a single YAML file, then bring everything up — or tear everything down — with one command.


The Problem Compose Solves

Imagine you’re building a web application that needs:

  • A Node.js API server
  • A PostgreSQL database
  • A Redis cache
  • An NGINX reverse proxy

Without Compose, you’d run four separate commands:

# Create a network
docker network create myapp

# Start PostgreSQL
docker run -d \
  --name db \
  --network myapp \
  -e POSTGRES_PASSWORD=secret \
  -v pgdata:/var/lib/postgresql/data \
  postgres:16

# Start Redis
docker run -d \
  --name cache \
  --network myapp \
  redis:7-alpine

# Start the API
docker run -d \
  --name api \
  --network myapp \
  -e DATABASE_URL=postgres://postgres:secret@db:5432/app \
  -e REDIS_URL=redis://cache:6379 \
  -p 3000:3000 \
  myapp-api:latest

# Start NGINX
docker run -d \
  --name proxy \
  --network myapp \
  -p 80:80 \
  -v ./nginx.conf:/etc/nginx/nginx.conf:ro \
  nginx:alpine

That’s 5 commands with complex flags, volume mounts, environment variables, and network configuration. Now imagine doing this every day. Or sharing it with a teammate.


The Compose Solution

With Docker Compose, all of that becomes one file:

services:
  proxy:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - api

  api:
    image: myapp-api:latest
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://postgres:secret@db:5432/app
      REDIS_URL: redis://cache:6379
    depends_on:
      - db
      - cache

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data

  cache:
    image: redis:7-alpine

volumes:
  pgdata:

And one command to start everything:

docker compose up -d

Expected output:

[+] Running 5/5
 ✔ Network myapp_default   Created
 ✔ Container myapp-db-1    Started
 ✔ Container myapp-cache-1 Started
 ✔ Container myapp-api-1   Started
 ✔ Container myapp-proxy-1 Started

One command to stop everything:

docker compose down

How Compose Works

┌─────────────────────────────────────────────┐
│              Developer Machine               │
│                                             │
│  compose.yml ──→ Docker Compose CLI         │
│                      │                      │
│                      ▼                      │
│              Docker Engine API              │
│                      │                      │
│         ┌────────────┼────────────┐         │
│         ▼            ▼            ▼         │
│    ┌─────────┐ ┌─────────┐ ┌─────────┐    │
│    │Container│ │Container│ │Container│    │
│    │  (web)  │ │  (api)  │ │  (db)   │    │
│    └─────────┘ └─────────┘ └─────────┘    │
│         │            │            │         │
│         └────────────┴────────────┘         │
│              Compose Network                │
└─────────────────────────────────────────────┘

Docker Compose:

  1. Reads your compose.yml file
  2. Translates each service definition into Docker API calls
  3. Creates networks, volumes, and containers
  4. Manages the lifecycle of all containers together

Think of Compose as a recipe book for your application. The compose.yml is the recipe — it describes what ingredients (images) you need, how to combine them (networks), and what to store (volumes). docker compose up is saying “make the dish.”


Declarative vs Imperative

Docker Compose uses a declarative approach — you describe what you want, not how to get there.

Approach Example How it works
Imperative docker run -d --name db postgres You tell Docker each step
Declarative services: db: image: postgres You describe the desired state

The declarative approach means:

  • Reproducible — Same file, same result, every time
  • Self-documenting — The YAML file IS the documentation
  • Version-controlled — Track changes in Git
  • Shareable — Anyone can run your stack with docker compose up

Real-World Analogy

Think of Docker Compose like an orchestra conductor’s score.

  • Each service is a musician (container)
  • The compose.yml is the musical score (what to play)
  • docker compose up is the conductor raising the baton (start playing)
  • docker compose down is the concert ending (everyone stops)
  • Networks are like sections (strings, brass, woodwinds talking to each other)
  • Volumes are the music stands (persistent data)

Without a score, you’d have to tell each musician individually what to play. With a score, everyone knows their part and plays together.


What Compose Creates

When you run docker compose up, Compose creates:

Resource Naming Convention Example
Network <project>_default myapp_default
Container <project>-<service>-<number> myapp-db-1
Volume <project>_<volume> myapp_pgdata

The project name defaults to the directory name. If your compose.yml is in /home/user/myapp/, the project name is myapp.


Summary

Concept Detail
What Tool for defining multi-container Docker apps
Format YAML file (compose.yml)
Start docker compose up -d
Stop docker compose down
Approach Declarative — describe desired state
Creates Containers, networks, volumes
Project Name Defaults to directory name

Quick Reference

Item Detail
Docker Compose Tool for multi-container Docker applications
compose.yml YAML file defining services, networks, volumes
docker compose up -d Start all services in background
docker compose down Stop and remove all services
Declarative Describe what you want, not how to do it
Project name Defaults to current directory name

← Introduction Compose V1 vs V2 →