Skip to content

Docker Compose Quick Start

Docker Compose is a tool for defining and running multi-container Docker applications. With a single YAML file, you configure all your application's services, networks, and volumes, then create and start everything with a single command.

What is Docker Compose?

Docker Compose simplifies the management of multi-container applications:

┌─────────────────────────────────────────────────────────┐
│                    docker-compose.yml                     │
│                                                          │
│  ┌──────────┐   ┌──────────┐   ┌──────────────────┐    │
│  │ Service:  │   │ Service: │   │ Service:          │    │
│  │ frontend  │   │   api    │   │   database        │    │
│  │ (nginx)   │──▶│ (node)  │──▶│   (postgres)      │    │
│  │ Port 80   │   │ Port 3000│   │   Port 5432       │    │
│  └──────────┘   └──────────┘   └──────────────────┘    │
│                                                          │
│  ┌────────────────────┐   ┌────────────────────────┐    │
│  │ Network: app-net   │   │ Volume: db-data        │    │
│  └────────────────────┘   └────────────────────────┘    │
└─────────────────────────────────────────────────────────┘


              docker compose up

Prerequisites

Docker Compose V2 is included with Docker Desktop and modern Docker Engine installations:

bash
# Verify Docker Compose is installed
docker compose version
# Docker Compose version v2.x.x

INFO

Docker Compose V2 uses docker compose (with a space). The legacy V1 used docker-compose (with a hyphen). V2 is the recommended version.

Your First Compose Application

Step 1: Create the Application

Create a project directory with the following structure:

my-app/
├── docker-compose.yml
├── frontend/
│   ├── Dockerfile
│   └── index.html
└── api/
    ├── Dockerfile
    └── server.js

api/server.js:

javascript
const http = require('http');

const server = http.createServer((req, res) => {
  if (req.url === '/api/health') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ status: 'healthy', service: 'api' }));
    return;
  }

  if (req.url === '/api/data') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({
      message: 'Hello from the API!',
      timestamp: new Date().toISOString()
    }));
    return;
  }

  res.writeHead(404);
  res.end('Not Found');
});

server.listen(3000, '0.0.0.0', () => {
  console.log('API server running on port 3000');
});

api/Dockerfile:

dockerfile
FROM node:20-alpine
WORKDIR /app
COPY server.js .
EXPOSE 3000
CMD ["node", "server.js"]

frontend/index.html:

html
<!DOCTYPE html>
<html>
<head><title>Docker Compose Demo</title></head>
<body>
  <h1>Docker Compose Demo</h1>
  <p>Frontend served by Nginx, API powered by Node.js</p>
</body>
</html>

frontend/Dockerfile:

dockerfile
FROM nginx:1.25-alpine
COPY index.html /usr/share/nginx/html/
EXPOSE 80

Step 2: Create the Compose File

docker-compose.yml:

yaml
services:
  frontend:
    build: ./frontend
    ports:
      - "80:80"
    depends_on:
      - api
    networks:
      - app-network

  api:
    build: ./api
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
    depends_on:
      - db
    networks:
      - app-network

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: appsecret
      POSTGRES_DB: appdb
    volumes:
      - db-data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  db-data:

Step 3: Start the Application

bash
# Build and start all services
docker compose up -d

# Check service status
docker compose ps

# Expected output:
# NAME                 SERVICE     STATUS    PORTS
# my-app-frontend-1    frontend    running   0.0.0.0:80->80/tcp
# my-app-api-1         api         running   0.0.0.0:3000->3000/tcp
# my-app-db-1          db          running   0.0.0.0:5432->5432/tcp

Step 4: Test the Application

bash
# Test the frontend
curl http://localhost

# Test the API
curl http://localhost:3000/api/health
curl http://localhost:3000/api/data

Step 5: Manage the Application

bash
# View logs
docker compose logs
docker compose logs api       # Specific service
docker compose logs -f api    # Follow logs

# Scale a service
docker compose up -d --scale api=3

# Restart a service
docker compose restart api

# Stop all services
docker compose stop

# Stop and remove everything
docker compose down

# Stop, remove, and delete volumes
docker compose down -v

Essential Compose Commands

CommandDescription
docker compose upCreate and start all services
docker compose up -dStart in detached (background) mode
docker compose up --buildRebuild images before starting
docker compose downStop and remove containers, networks
docker compose down -vAlso remove volumes
docker compose down --rmi allAlso remove images
docker compose psList running services
docker compose logsView logs for all services
docker compose logs -f <svc>Follow logs for a specific service
docker compose buildBuild or rebuild services
docker compose pullPull service images
docker compose pushPush service images
docker compose restartRestart services
docker compose stopStop services without removing
docker compose startStart stopped services
docker compose exec <svc> <cmd>Execute a command in a running service
docker compose run <svc> <cmd>Run a one-off command in a new container
docker compose topDisplay running processes
docker compose configValidate and view the compose file

Development Workflow

Live Code Reloading

Use bind mounts to reflect code changes without rebuilding:

yaml
services:
  api:
    build: ./api
    ports:
      - "3000:3000"
    volumes:
      # Mount source code for live reloading
      - ./api/src:/app/src
    environment:
      - NODE_ENV=development
    command: npm run dev  # Use nodemon or similar

  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    volumes:
      - ./frontend/src:/app/src
    command: npm run dev

Override Files

Use docker-compose.override.yml for development-specific settings:

docker-compose.yml (base):

yaml
services:
  api:
    build: ./api
    ports:
      - "3000:3000"

docker-compose.override.yml (development — auto-loaded):

yaml
services:
  api:
    volumes:
      - ./api/src:/app/src
    environment:
      - DEBUG=true
    command: npm run dev

docker-compose.prod.yml (production):

yaml
services:
  api:
    environment:
      - NODE_ENV=production
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
    command: npm start
bash
# Development (uses docker-compose.yml + docker-compose.override.yml)
docker compose up -d

# Production (uses docker-compose.yml + docker-compose.prod.yml)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Health Checks

yaml
services:
  api:
    build: ./api
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

  db:
    image: postgres:16-alpine
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser"]
      interval: 10s
      timeout: 5s
      retries: 5

  api-dependent:
    build: ./service
    depends_on:
      api:
        condition: service_healthy
      db:
        condition: service_healthy

Environment Variables

yaml
services:
  api:
    image: my-api:latest

    # Inline environment variables
    environment:
      - NODE_ENV=production
      - DB_HOST=db
      - DB_PORT=5432

    # Environment file
    env_file:
      - .env
      - .env.local

    # Map syntax
    environment:
      NODE_ENV: production
      DB_HOST: db

.env:

bash
# Project-level env file (auto-loaded by Compose)
COMPOSE_PROJECT_NAME=myapp
POSTGRES_USER=appuser
POSTGRES_PASSWORD=secret123

Networking in Compose

yaml
services:
  frontend:
    networks:
      - frontend-net

  api:
    networks:
      - frontend-net
      - backend-net

  db:
    networks:
      - backend-net

networks:
  frontend-net:
    driver: bridge
  backend-net:
    driver: bridge
    internal: true  # No external access

Next Steps

基于 MIT 许可发布