Before Compose, I had 12 terminal tabs open and constantly forgot which containers were running. Your web app needs a database, Redis, maybe Elasticsearch, and that one microservice Dave wrote that nobody understands but somehow everything breaks when you touch it. Starting everything manually is like playing whack-a-mole with docker run
commands.
Multi-container apps get messy fast. Each service needs its own networking configs, environment variables, and volume mounts. The Docker CLI reference shows way too many flags to remember - there's gotta be like 30+ different options just for docker run
and I keep forgetting which ones I actually need.
docker run -d --name redis redis:7-alpine
docker run -d --name postgres -e POSTGRES_PASSWORD=password postgres:15
docker run -d --name web --link redis --link postgres -p 3000:3000 myapp
Fuck up the order? Your app can't connect to the database. Forget a port mapping? Good luck debugging why nothing responds. Kill one container by accident? Start over and hope you remember all the flags.
Docker Compose fixes this shit show. You write one YAML file that describes everything, and docker-compose up
starts it all in the right order with proper networking.
How Compose Actually Works
Docker Compose reads a YAML file and handles all the networking bullshit for you. Here's the same setup, but now I don't want to throw my laptop:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
depends_on:
- redis
- postgres
environment:
- DATABASE_URL=postgres://user:password@postgres:5432/myapp
redis:
image: redis:7-alpine
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: password
POSTGRES_DB: myapp
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Copy this: docker-compose up
. Everything starts in order, containers can reach each other by service name, and the database actually persists data. No more IP address guessing games.
Gotcha: depends_on
doesn't wait for your app to be ready, just started. PostgreSQL might still be initializing when your web app tries to connect. Add health checks or retry logic to handle startup race conditions.
Why It Actually Matters
Before Compose, I had 20 minutes of setup bullshit before I could write a single line of code. Now new team members run git clone
, docker-compose up
, and they're coding in 2 minutes. No more "works on my machine" arguments during code reviews.
Environment Consistency: Everyone gets the exact same PostgreSQL version, Redis config, and whatever weird dependencies your app needs. TestContainers built an entire testing framework around this idea.
Actually Useful Integration Tests: Instead of mocking everything and hoping it works in production, you test against real databases. This has saved my ass more times than I can count.
Networking That Just Works: Your web service connects to postgres:5432
instead of me trying to figure out what random IP Docker assigned this time. Compose creates isolated networks per project, so my Bitcoin mining experiment doesn't fuck up my work project. I learned this the hard way when containers started talking to the wrong databases.
Docker Compose networking handles service discovery automatically, which is great because I always screw up the manual networking.
The Gotcha Nobody Tells You: I stick with version 3.8. Don't use 3.9 - I think it breaks health checks in Docker Engine 20.10.x, but honestly I stopped testing new versions after one update killed our CI pipeline for a day and my PM was breathing down my neck asking why the build was broken.
Docker Compose networking confused the hell out of me for months. Turns out it's just DNS that actually works, which honestly surprised me because networking usually doesn't work the first time.