Docker Compose Tutorial: From Beginner to Pro
Step-by-step guide for Docker Compose. Learn how to easily orchestrate and deploy multi-container applications.

Docker Compose Tutorial: Everything You Need to Know
Docker Compose is the essential tool for anyone working with multiple Docker containers. This guide takes you from basics to advanced concepts.
What is Docker Compose?
Docker Compose allows you to define and run multi-container Docker applications. Instead of configuring and starting each container individually, you describe your entire application in a single YAML file.
Without Docker Compose:
docker network create myapp docker run -d --name db --network myapp -e POSTGRES_PASSWORD=secret postgres:15 docker run -d --name redis --network myapp redis:7 docker run -d --name app --network myapp -p 3000:3000 myapp:latest
With Docker Compose:
# docker-compose.yml services: db: image: postgres:15 environment: POSTGRES_PASSWORD: secret redis: image: redis:7 app: build: . ports: - "3000:3000"
A single docker compose up starts everything.
Installation
macOS and Windows
Docker Compose is integrated into Docker Desktop. After installing Docker Desktop, docker compose is automatically available.
Linux (Ubuntu/Debian)
# Install Docker sudo apt update sudo apt install docker.io # Install Docker Compose Plugin sudo apt install docker-compose-plugin # Verify docker compose version
Your First docker-compose.yml
Create a file named docker-compose.yml:
version: '3.8' services: web: image: nginx:latest ports: - "8080:80" volumes: - ./html:/usr/share/nginx/html
Start with:
docker compose up
Open http://localhost:8080 – done!
Essential Commands
| Command | Description |
|---|---|
docker compose up | Starts all services |
docker compose up -d | Starts in background (detached) |
docker compose down | Stops and removes containers |
docker compose ps | Shows running services |
docker compose logs | Shows logs of all services |
docker compose logs -f web | Follows logs of a service |
docker compose exec web sh | Open shell in container |
docker compose build | Rebuilds images |
docker compose pull | Pulls latest images |
Practical Example: WordPress with MySQL
version: '3.8' services: wordpress: image: wordpress:latest ports: - "8080:80" environment: WORDPRESS_DB_HOST: db WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: wordpress_password WORDPRESS_DB_NAME: wordpress volumes: - wordpress_data:/var/www/html depends_on: - db restart: unless-stopped db: image: mysql:8.0 environment: MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: wordpress_password MYSQL_ROOT_PASSWORD: root_password volumes: - db_data:/var/lib/mysql restart: unless-stopped volumes: wordpress_data: db_data:
What's happening here?
- Two services:
wordpressanddb - WordPress waits for MySQL (
depends_on) - Data persists in named volumes
- Containers restart on crash (
restart: unless-stopped)
Key Concepts in Detail
1. Volumes: Persistent Data Storage
services: db: image: postgres:15 volumes: # Named volume (managed by Docker) - db_data:/var/lib/postgresql/data # Bind mount (host directory) - ./init.sql:/docker-entrypoint-initdb.d/init.sql volumes: db_data:
2. Networks: Connecting Containers
services: frontend: networks: - frontend_net backend: networks: - frontend_net - backend_net database: networks: - backend_net networks: frontend_net: backend_net:
Containers can only communicate if they're in the same network.
3. Environment Variables
services: app: environment: - NODE_ENV=production - API_KEY=${API_KEY} # From .env file env_file: - .env.production
.env file (DON'T commit!):
API_KEY=secret123
DB_PASSWORD=secure456
4. Healthchecks
services: web: image: nginx healthcheck: test: ["CMD", "curl", "-f", "http://localhost"] interval: 30s timeout: 10s retries: 3 start_period: 40s
5. Resource Limits
services: app: deploy: resources: limits: cpus: '0.5' memory: 512M reservations: cpus: '0.25' memory: 256M
Development vs. Production
Development (docker-compose.override.yml)
# docker-compose.yml (base) version: '3.8' services: app: build: . environment: - NODE_ENV=development
# docker-compose.override.yml (automatically merged in dev) version: '3.8' services: app: volumes: - .:/app - /app/node_modules command: npm run dev ports: - "3000:3000" - "9229:9229" # Debugger
Production (docker-compose.prod.yml)
# docker-compose.prod.yml version: '3.8' services: app: image: myapp:latest environment: - NODE_ENV=production restart: always deploy: replicas: 3
Start with:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Common Problems and Solutions
Problem: Port already in use
Error: bind: address already in use
Solution:
# Find process on port lsof -i :8080 # Or use different port ports: - "8081:80"
Problem: Container can't reach another
Solution: Services reach each other by service name, not localhost:
# Wrong in app code DATABASE_URL=localhost:5432 # Correct DATABASE_URL=db:5432 # "db" is the service name
Problem: Changes not applied
Solution:
docker compose up --build # Rebuilds images docker compose down -v && docker compose up # Also deletes volumes
Problem: Volumes taking too much space
Solution:
docker system prune -a --volumes # CAUTION: Deletes all unused! docker volume prune # Only unused volumes
Best Practices
-
Always use .env for secrets – Never commit passwords in docker-compose.yml
-
Use specific image tags –
postgres:15instead ofpostgres:latest -
Define healthchecks – For better monitoring and depends_on
-
Set restart policies –
unless-stoppedoralwaysfor production -
Set resource limits – Prevent one container from consuming everything
-
Isolate networks – Don't put everything in the default network
Useful Compose Extensions
Configure Logging
services: app: logging: driver: "json-file" options: max-size: "10m" max-file: "3"
Profiles for Optional Services
services: app: # Always started debug: profiles: ["debug"] # Only with: docker compose --profile debug up testing: profiles: ["test"]
Conclusion
Docker Compose significantly simplifies container orchestration. With a single YAML file, you can define, start, and manage complex multi-container applications.
Next steps:
- Experiment with the examples
- Containerize your existing projects
- Explore Docker Swarm or Kubernetes for larger deployments
For questions about container strategy or DevOps optimization, Balane Tech is happy to help.


