Docker Compose — Compose vs Docker Run
This page gives you a side-by-side comparison of running containers with docker run commands versus defining them in a compose.yml file. By the end, you’ll see why Compose is the better approach for anything beyond a single container.
Side-by-Side Comparison
Example: WordPress + MySQL
With docker run (imperative):
# Step 1: Create a network
docker network create wordpress-net
# Step 2: Create a volume
docker volume create wp-db-data
# Step 3: Start MySQL
docker run -d \
--name wordpress-db \
--network wordpress-net \
-e MYSQL_ROOT_PASSWORD=rootpass \
-e MYSQL_DATABASE=wordpress \
-e MYSQL_USER=wpuser \
-e MYSQL_PASSWORD=wppass \
-v wp-db-data:/var/lib/mysql \
mysql:8
# Step 4: Start WordPress
docker run -d \
--name wordpress-app \
--network wordpress-net \
-e WORDPRESS_DB_HOST=wordpress-db \
-e WORDPRESS_DB_USER=wpuser \
-e WORDPRESS_DB_PASSWORD=wppass \
-e WORDPRESS_DB_NAME=wordpress \
-p 8080:80 \
wordpress:latest
With Docker Compose (declarative):
# compose.yml
services:
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppass
volumes:
- db-data:/var/lib/mysql
wordpress:
image: wordpress:latest
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppass
WORDPRESS_DB_NAME: wordpress
depends_on:
- db
volumes:
db-data:
docker compose up -d
Comparison Table
| Aspect | docker run |
Docker Compose |
|---|---|---|
| Commands | Multiple commands | One command (up / down) |
| Network | Must create manually | Created automatically |
| Volumes | Must create manually | Declared in file |
| Reproducibility | Copy-paste commands | Share the YAML file |
| Version control | Hard to track changes | Git-friendly YAML |
| Cleanup | Must stop/remove each container | docker compose down cleans all |
| Dependencies | Manual ordering | depends_on handles it |
| Scaling | Run same command N times | --scale web=3 |
| Documentation | Commands in a README | YAML is self-documenting |
| Onboarding | “Run these 7 commands in order” | “Run docker compose up” |
What Compose Automates
docker run approach: Docker Compose approach:
───────────────────── ─────────────────────
1. Create network ┐ 1. docker compose up
2. Create volume │ │
3. Start container A │ ├─ Creates networks ✓
4. Start container B ├─ Manual ├─ Creates volumes ✓
5. Start container C │ ├─ Starts services ✓
6. Hope order is right │ ├─ Handles ordering ✓
7. Debug connection ┘ └─ Connects DNS ✓
When docker run is Still Fine
| Scenario | Why |
|---|---|
| Quick one-off container | docker run -it ubuntu bash — fast and simple |
| Testing a single image | docker run -p 8080:80 nginx — no compose needed |
| Scripted automation | When the script handles orchestration already |
The Cleanup Problem
With docker run, cleanup is manual and error-prone:
# Stopping everything — hope you remember all the names
docker stop wordpress-app wordpress-db
docker rm wordpress-app wordpress-db
docker network rm wordpress-net
# Forgot the volume? It's still there, eating disk space
With Compose:
# Everything, gone, clean
docker compose down
# Including volumes
docker compose down -v
Summary
| Item | Detail |
|---|---|
docker run |
Good for single, quick containers |
| Docker Compose | Good for multi-container applications |
| Automatic networking | Compose creates a default network |
| Cleanup | docker compose down removes everything |
| Best practice | Use Compose for anything with 2+ containers |
Quick Reference
| Item | Detail |
|---|---|
| docker run | Good for single quick containers |
| Docker Compose | Required for multi-container apps |
| Network creation | Automatic in Compose, manual with docker run |
| Cleanup | docker compose down removes everything |
| Version control | compose.yml is Git-friendly |
| Team sharing | Just share the compose.yml file |