🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

What Is Docker Compose? A Beginner's Guide (2026)

Docker Compose lets you define and run multi-container applications with a single file. Here's what it does, how it works, and when to use it — explained simply.

DevOpsBoysMay 1, 20264 min read
Share:Tweet

Running a web application usually means running multiple things at once — a web server, a database, maybe a cache. Docker Compose lets you define all of them in one file and start everything with one command.


The Problem Docker Compose Solves

Without Compose, starting a web app with a database looks like:

bash
# Step 1: Create a network
docker network create myapp-network
 
# Step 2: Start PostgreSQL
docker run -d \
  --name postgres \
  --network myapp-network \
  -e POSTGRES_PASSWORD=secret \
  -v postgres-data:/var/lib/postgresql/data \
  postgres:16
 
# Step 3: Start Redis
docker run -d \
  --name redis \
  --network myapp-network \
  redis:7
 
# Step 4: Start your app
docker run -d \
  --name myapp \
  --network myapp-network \
  -p 3000:3000 \
  -e DATABASE_URL=postgres://postgres:secret@postgres:5432/mydb \
  -e REDIS_URL=redis://redis:6379 \
  myapp:latest

That's 4 commands. And you need to remember the right order. And if you want to stop everything, you need 3 more commands.

With Docker Compose, all of that becomes one file and one command.


The Same Thing With Docker Compose

yaml
# docker-compose.yml
version: "3.9"
 
services:
  app:
    image: myapp:latest
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://postgres:secret@db:5432/mydb
      REDIS_URL: redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
 
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
 
  cache:
    image: redis:7
    volumes:
      - redis-data:/data
 
volumes:
  postgres-data:
  redis-data:

Now:

bash
docker compose up -d      # start everything
docker compose down       # stop everything
docker compose logs -f    # see all logs
docker compose ps         # see status of all services

Key Concepts

Services

Each container is a service. In the example above: app, db, and cache are three services.

yaml
services:
  web:        # service name (also the hostname inside Docker network)
    image: nginx

Services can talk to each other using the service name as hostname. Your app can connect to PostgreSQL at db:5432 because the service is named db.

Volumes

Volumes save data permanently. Without a volume, all database data disappears when you stop the container.

yaml
services:
  db:
    image: postgres:16
    volumes:
      - postgres-data:/var/lib/postgresql/data   # named volume
 
volumes:
  postgres-data:   # define the volume here

Networks

Compose automatically creates a network for your project. All services in the same docker-compose.yml can talk to each other by service name. No manual network setup needed.

depends_on

Tells Compose which services to start first:

yaml
services:
  app:
    depends_on:
      db:
        condition: service_healthy  # wait until db is healthy

Without depends_on, Compose starts everything at once — your app might start before the database is ready.


Building From Source

Instead of using a pre-built image, Compose can build your app from a Dockerfile:

yaml
services:
  app:
    build:
      context: .           # build from current directory
      dockerfile: Dockerfile  # using this Dockerfile
    ports:
      - "3000:3000"
bash
docker compose up --build   # rebuild image and start

Environment Variables and .env Files

Don't hardcode secrets in docker-compose.yml. Use a .env file:

bash
# .env
POSTGRES_PASSWORD=mysecretpassword
APP_PORT=3000
yaml
# docker-compose.yml
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}  # reads from .env
 
  app:
    image: myapp:latest
    ports:
      - "${APP_PORT}:3000"

Add .env to .gitignore — never commit secrets.


Useful Commands

bash
# Start all services in background
docker compose up -d
 
# Start and rebuild images
docker compose up -d --build
 
# Stop all services (keeps data)
docker compose stop
 
# Stop and remove containers + networks
docker compose down
 
# Stop and remove everything including volumes (DELETES DATA)
docker compose down -v
 
# See logs from all services
docker compose logs -f
 
# See logs from one service
docker compose logs -f app
 
# Run a command in a running container
docker compose exec app sh
docker compose exec db psql -U postgres
 
# See running services
docker compose ps
 
# Pull latest images
docker compose pull

Multiple Environments with Override Files

Use multiple files for different environments:

yaml
# docker-compose.yml (base)
services:
  app:
    image: myapp:latest
    
# docker-compose.override.yml (local dev — auto-loaded)
services:
  app:
    build: .           # build from source locally
    volumes:
      - .:/app         # mount source code for hot reload
    environment:
      DEBUG: "true"
 
# docker-compose.prod.yml (production)
services:
  app:
    image: myapp:v1.2.3   # specific version in prod
    restart: always
bash
# Local (uses base + override automatically)
docker compose up
 
# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up

When to Use Docker Compose

Good for:

  • Local development environments
  • Running all project dependencies with one command
  • Integration testing in CI
  • Simple single-server deployments

Not for:

  • Production at scale (use Kubernetes)
  • Running across multiple servers
  • Auto-scaling based on load

Docker Compose is your best friend for local development. For production clusters handling real traffic, you'll eventually want Kubernetes — but Compose is where most people start, and it's the right tool for local work.

Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments