Running one Docker container is straightforward. But most real apps need multiple things—a web server, a database, a cache, maybe a message queue. Managing five docker run commands with their various port mappings and environment variables? That's where Compose saves your sanity.
What's Compose?
Compose lets you define your entire application stack in one file. Instead of running multiple docker run commands, you write a YAML file describing all your services, then start everything with one command.
It's basically "infrastructure as code" for development environments.
The Basic Setup
If you followed my Docker guide, you probably have Docker installed. Compose usually comes with it, but if not:
sudo apt install docker-compose
# or
sudo curl -L "https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
A Simple Example
Here's a basic docker-compose.yml that runs an Nginx container:
version: '3.8'
services:
web:
image: nginx
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
Save that as docker-compose.yml, create an "html" folder with an index.html, and run:
docker-compose up -d
Visit localhost. There's your Nginx. Stop it with docker-compose down.
The Real Power: Multi-Container
Okay, that's trivial. Let's do something actually useful—a WordPress site with a database:
version: '3.8'
services:
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: wordpress
MYSQL_USER: wp
MYSQL_PASSWORD: wppass
volumes:
- db_data:/var/lib/mysql
wordpress:
image: wordpress:latest
depends_on:
- db
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wp
WORDSQL_PASSWORD: wppass
WORDPRESS_DB_NAME: wordpress
volumes:
- ./wp-content:/var/www/html/wp-content
volumes:
db_data:
That's it. One command, and you've got WordPress running with MySQL, linked together, with persistent storage.
The depends_on line means "don't start WordPress until the database is ready."
The Commands You'll Use
# Start everything in background
docker-compose up -d
# Stop everything
docker-compose down
# Watch logs ( Ctrl+C to exit)
docker-compose logs -f
# Rebuild after changing the yml
docker-compose up -d --build
# Run a command in a service
docker-compose exec web ls
# See what's running
docker-compose ps
Why This Is Actually Great
Here's the thing: new developer joins your team. They clone the repo, run docker-compose up, and 30 seconds later they have a fully working development environment. No "but it works on my machine" discussions. No "I spent three days setting up the local environment" stories.
Everything is defined in code. The version of the database. The environment variables. The port mappings. It's all right there in the file.
What's Next?
You can extend this to run:
- A development stack (app + database + redis + mailhog)
- A full production-replica for testing
- CI/CD pipelines that spin up clean environments for each test
Once you go Compose, you never go back.