Docker Compose YAML
๐ฅ Vibe Prompt
"Create a docker-compose.yml for a web app with FastAPI, Postgres, Redis. Define health checks, volumes, networks."
version: "3.9"
services:
api:
build: ./api
ports: ["8000:8000"]
depends_on:
db: { condition: service_healthy }
redis: { condition: service_started }
env_file: .env
volumes: ["./api:/app"]
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
retries: 3
db:
image: postgres:16-alpine
volumes: ["pgdata:/var/lib/postgresql/data"]
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: ${DB_PASSWORD}
healthcheck:
test: ["CMD", "pg_isready"]
interval: 10s
redis:
image: redis:7-alpine
volumes: ["redisdata:/data"]
volumes:
pgdata:
redisdata:
Key Directives
| Directive | Purpose |
|-----------|---------|
| depends_on | Service startup order |
| healthcheck | Service readiness probe |
| volumes | Persistent data (named/bind) |
| networks | Service isolation |
| env_file | Environment variables |
| restart | Restart policy (always/no/on-failure) |
Commands
docker compose up -d # Start all
docker compose logs -f api # Follow logs
docker compose exec api bash # Exec into container
docker compose down -v # Stop + remove volumes
docker compose restart api # Restart single service
Chapter Summary
- Understand core concepts and principles
- Master implementation methods and techniques
- Familiar with common issues and solutions
- Able to apply in real projects
Further Reading
- Official documentation and API references
- Open source examples on GitHub
- Technical books and online courses
- Community discussions and tech blogs
Implementation Example
Basic Example
# This section provides a complete implementation example
Steps
- Setup: Configure development environment
- Data: Prepare required data
- Implementation: Build core functionality
- Testing: Verify correctness
- Optimization: Improve performance
Common Errors
| Error Type | Cause | Solution | |------------|-------|----------| | Compilation | Syntax | Check code syntax | | Runtime | Environment | Verify dependencies installed | | Logic | Algorithm | Step-by-step debugging | | Performance | Efficiency | Use profilers |
Code Example
import sys
def main():
print("Hello, World!")
if __name__ == "__main__":
main()
References
- Official documentation
- API reference
- Open source examples
- Community discussions
Docker Compose File Structure
A docker-compose.yml file has three top-level sections:
version: "3.9" # Compose file format version
services: # The containers to run
web:
image: nginx
ports:
- "80:80"
volumes: # Persistent data storage
mydata:
networks: # Communication between services
mynetwork:
Service Configuration Reference
| Key | Description | Example |
|-----|-------------|--------|
| image | Container image to use | image: postgres:16-alpine |
| build | Build context and Dockerfile | build: . or build: context: ./dir dockerfile: Dockerfile.dev |
| ports | Port mappings (HOST:CONTAINER) | ports: "3000:3000" |
| environment | Environment variables | environment: NODE_ENV=production |
| env_file | Load variables from file | env_file: .env |
| volumes | Mount volumes or bind mounts | volumes: ./data:/data |
| networks | Attach to networks | networks: frontend |
| depends_on | Startup dependencies | depends_on: db |
| restart | Restart policy | restart: always |
| healthcheck | Container health check | healthcheck: test: ["CMD", "pg_isready"] |
| command | Override default command | command: npm start |
| entrypoint | Override entrypoint | entrypoint: ["/bin/sh", "-c"] |
| user | Run as specific user | user: "1000:1000" |
| working_dir | Working directory inside container | working_dir: /app |
| container_name | Custom container name | container_name: my-app |
| labels | Metadata labels | labels: com.example.version="1.0" |
| logging | Log driver configuration | logging: driver: json-file options: max-size: "10m" |
Complete Example: Web App with PostgreSQL
version: "3.9"
services:
web:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
NODE_ENV: production
DB_HOST: db
DB_PORT: 5432
DB_USER: myapp
DB_PASSWORD: ${DB_PASSWORD}
DB_NAME: myapp_prod
depends_on:
db:
condition: service_healthy
restart: always
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
environment:
POSTGRES_USER: myapp
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: myapp_prod
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myapp"]
interval: 10s
timeout: 5s
retries: 5
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
redis:
image: redis:7-alpine
restart: always
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
pgdata:
Run Commands
# Start in background
docker compose up -d
# View logs
docker compose logs -f
# View logs for a specific service
docker compose logs -f web
# Execute command in container
docker compose exec web bash
# View running containers
docker compose ps
# Stop and remove
docker compose down
# Stop and remove including volumes
docker compose down -v
# Rebuild and start
docker compose up -d --build
# Restart a specific service
docker compose restart web
Environment Variables (.env)
# .env file (in the same directory as docker-compose.yml)
DB_PASSWORD=your_strong_password_here
NODE_ENV=production
API_KEY=sk-abc123...
Variables are referenced in docker-compose.yml with ${VARIABLE_NAME} syntax.
Best Practices
| Practice | Reason |
|----------|--------|
| Pin specific image versions | postgres:16-alpine not postgres:latest |
| Add health checks to every service | depends_on waits for health, not just startup |
| Use .env files for secrets | Never hardcode passwords in compose files |
| Use named volumes for databases | Data persists across container restarts |
| Configure log rotation | Prevents disks from filling up |
| Add restart: always | Containers restart automatically after crashes |
| Use multi-stage Dockerfiles | Smaller, more secure production images |
| Separate dev and prod compose files | Keep dev overrides separate from production |
| Validate with docker compose config | Check for syntax errors before deploying |
| Set container_name only when needed | Docker auto-generates unique names by default |
Common Mistakes
| Mistake | Fix | |---------|-----| | Forgetting to add health check | depends_on waits for container start, not readiness | | Hardcoding passwords in YAML | Use ${DB_PASSWORD} with .env file | | Using latest tag | Pin to specific versions for reproducibility | | No restart policy | Container stops after crash and stays down | | No log rotation | Logs grow until disk is full | | Mounting host directories in production | Use named volumes instead of bind mounts | | Exposing database ports to host | Only expose application ports (DB should be internal) | | Putting secrets in docker-compose.yml | Use .env files or Docker secrets |
Summary
Docker Compose simplifies multi-container application management. Define services, volumes, and networks in a YAML file, then start everything with a single command. Add health checks, restart policies, log rotation, and environment variables for production readiness.
Key takeaways:
- Services: define what containers to run
- Volumes: persist data beyond container lifetime
- Networks: control service-to-service communication
- Health checks: depends_on waits for service readiness
- .env files: keep secrets out of compose files
- docker compose up -d: start all services
- docker compose logs -f: view all logs
- Pin image versions, never use latest in production
What's Next: Multi-Service Setup
The next chapter covers multi-service Docker Compose setups โ coordinating web, API, database, cache, and queue services with proper dependency management.