Installing Postgres, Redis, and your app's runtime directly on your machine works until a teammate's "works on my machine" version drifts from yours. Docker Compose defines your whole stack in one file, so anyone on the team gets an identical environment with one command.
01Install Docker Desktop (or Docker Engine + Compose on Linux)
On Windows and macOS, Docker Desktop bundles everything you need including Compose. On Linux, install Docker Engine and the Compose plugin separately, then confirm with:
docker compose version
02Write a docker-compose.yml for a typical stack
Here's a working example: a Node app, Postgres, and Redis.
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- DATABASE_URL=postgres://dev:devpass@db:5432/appdb
- REDIS_URL=redis://cache:6379
depends_on:
- db
- cache
db:
image: postgres:16
environment:
- POSTGRES_USER=dev
- POSTGRES_PASSWORD=devpass
- POSTGRES_DB=appdb
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
cache:
image: redis:7
ports:
- "6379:6379"
volumes:
pgdata:
03Understand the two volume lines for the app service
This is the part most tutorials skip and most people copy-paste without understanding:
.:/appmounts your local code into the container, so edits on your machine show up instantly without rebuilding./app/node_modules(with nothing before the colon) is an "anonymous volume" that prevents your host'snode_modulesfrom overwriting the one built inside the container — critical if you're on a different OS than the container, since some native packages compile differently per platform.
04Start everything
docker compose up -d
The -d flag runs it in the background. Check logs with:
docker compose logs -f app
05Why service names double as hostnames
Notice the app's DATABASE_URL uses db, not localhost, as the host. Compose creates an internal network where each service is reachable by its name from every other service — this is the actual mechanism that lets containers talk to each other, and it's the detail people get stuck on when copying a docker-compose file that assumes it.
06Reset to a clean state when things get weird
docker compose down -v
The -v flag also removes named volumes — meaning your database gets wiped along with the containers. Use this when you want a genuinely fresh start; leave it off if you just want to stop containers without losing data.
docker-compose.yml to your repo. A new teammate's entire environment setup becomes git clone then docker compose up, instead of a wiki page of manual install steps that's always slightly out of date.