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 upPrerequisites
Docker Compose V2 is included with Docker Desktop and modern Docker Engine installations:
# Verify Docker Compose is installed
docker compose version
# Docker Compose version v2.x.xINFO
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.jsapi/server.js:
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:
FROM node:20-alpine
WORKDIR /app
COPY server.js .
EXPOSE 3000
CMD ["node", "server.js"]frontend/index.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:
FROM nginx:1.25-alpine
COPY index.html /usr/share/nginx/html/
EXPOSE 80Step 2: Create the Compose File
docker-compose.yml:
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
# 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/tcpStep 4: Test the Application
# Test the frontend
curl http://localhost
# Test the API
curl http://localhost:3000/api/health
curl http://localhost:3000/api/dataStep 5: Manage the Application
# 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 -vEssential Compose Commands
| Command | Description |
|---|---|
docker compose up | Create and start all services |
docker compose up -d | Start in detached (background) mode |
docker compose up --build | Rebuild images before starting |
docker compose down | Stop and remove containers, networks |
docker compose down -v | Also remove volumes |
docker compose down --rmi all | Also remove images |
docker compose ps | List running services |
docker compose logs | View logs for all services |
docker compose logs -f <svc> | Follow logs for a specific service |
docker compose build | Build or rebuild services |
docker compose pull | Pull service images |
docker compose push | Push service images |
docker compose restart | Restart services |
docker compose stop | Stop services without removing |
docker compose start | Start 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 top | Display running processes |
docker compose config | Validate and view the compose file |
Development Workflow
Live Code Reloading
Use bind mounts to reflect code changes without rebuilding:
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 devOverride Files
Use docker-compose.override.yml for development-specific settings:
docker-compose.yml (base):
services:
api:
build: ./api
ports:
- "3000:3000"docker-compose.override.yml (development — auto-loaded):
services:
api:
volumes:
- ./api/src:/app/src
environment:
- DEBUG=true
command: npm run devdocker-compose.prod.yml (production):
services:
api:
environment:
- NODE_ENV=production
deploy:
replicas: 3
resources:
limits:
cpus: '0.5'
memory: 256M
command: npm start# 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 -dHealth Checks
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_healthyEnvironment Variables
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:
# Project-level env file (auto-loaded by Compose)
COMPOSE_PROJECT_NAME=myapp
POSTGRES_USER=appuser
POSTGRES_PASSWORD=secret123Networking in Compose
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 accessNext Steps
- Compose File Reference — Complete Compose file syntax
- Compose in Production — Deploy Compose apps to production
- Docker Networking Guide — Advanced networking concepts
- Storage Management — Volume management with Compose
- Compose File Format — Detailed reference for all options