After working with individual Docker containers for a while, I quickly realized that most real-world applications require multiple containers working together. Managing these with individual docker run commands became tedious and error-prone. That's where Docker Compose comes in - it's been a game-changer for my workflow.
What is Docker Compose? π€
Docker Compose is a tool for defining and running multi-container Docker applications. With a single YAML file and a few commands, you can configure all your application's services and spin up your entire stack with one command.
Why Use Docker Compose?
Before diving into the how, let me explain why I switched to Docker Compose:
- Simplicity: Define your entire application stack in a single file
- Reproducibility: Everyone on your team gets the exact same environment
- Service coordination: Automatic networking between containers
- Environment variables: Easy configuration management
- One-command operations: Start, stop, and rebuild your entire application stack
Writing docker-compose.yml Files
The heart of Docker Compose is the docker-compose.yml file. Here's a basic example:
version: '3'
services:
web:
image: nginx
ports:
- "80:80"
db:
image: postgres
environment:
POSTGRES_PASSWORD: example
Let's break down the structure:
Basic Structure
- version: Specifies the Compose file format version
- services: Defines the containers to be created
- volumes: (Optional) Defines named volumes
- networks: (Optional) Defines custom networks
Defining Services
Each service represents a container. You can configure:
services:
web:
image: nginx # Use an existing image
# OR
build: ./web # Build from a Dockerfile
build: # More build options
context: ./web
dockerfile: Dockerfile.dev
ports:
- "8080:80" # Port mapping
environment: # Environment variables
NODE_ENV: development
env_file: .env # Or use an env file
volumes: # Mount volumes
- ./web:/usr/share/nginx/html
depends_on: # Service dependencies
- api
restart: always # Restart policy
Managing Application Stacks
A Complete Example: Web App with Database and Cache
Here's a more realistic example of an application stack I might use:
version: '3'
services:
web:
build: ./frontend
ports:
- "3000:3000"
volumes:
- ./frontend:/app
- /app/node_modules
environment:
- REACT_APP_API_URL=http://api:5000
depends_on:
- api
api:
build: ./backend
ports:
- "5000:5000"
volumes:
- ./backend:/app
- /app/node_modules
environment:
- DB_HOST=db
- REDIS_HOST=redis
depends_on:
- db
- redis
db:
image: postgres:13
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=secretpassword
- POSTGRES_USER=myuser
- POSTGRES_DB=myapp
redis:
image: redis:alpine
volumes:
- redis-data:/data
volumes:
postgres-data:
redis-data:
This setup includes:
- A React frontend
- A backend API
- A PostgreSQL database
- A Redis cache
- Persistent volumes for data
- Environment configuration
- Service dependencies
Environment Configuration
There are several ways to manage environment variables:
- Inline in the compose file:
services:
web:
environment:
- NODE_ENV=development
- API_URL=http://api:5000
- From a .env file:
services:
web:
env_file:
- ./web.env
- From the shell environment:
services:
web:
environment:
- NODE_ENV
- API_KEY
For sensitive data, I prefer using .env files that are git-ignored.
Compose Commands
Building and Running Services
These are the commands I use most often:
# Start all services
docker-compose up
# Start in detached mode (background)
docker-compose up -d
# Build or rebuild services
docker-compose build
# Build and start
docker-compose up --build
# Stop services
docker-compose down
# Stop and remove volumes
docker-compose down -v
Viewing Logs and Status
# View logs of all services
docker-compose logs
# Follow logs of a specific service
docker-compose logs -f web
# See running services
docker-compose ps
Conclusion
Docker Compose has fundamentally changed how I develop and deploy multi-container applications. What used to take dozens of commands and careful coordination can now be defined in a single file and launched with one command.
The key benefits I've experienced:
- Development environments that perfectly match production
- Easy onboarding for new team members
- Consistent deployments across environments
- Simplified local development workflow
If you're working with Docker and managing more than one container, Compose should definitely be part of your toolkit.
In the next post, I'll discuss how to move your Docker Compose setups towards production-ready deployments.
Next up: "Docker in Production: From Development to Deployment"
Top comments (0)