Docker Compose

Real applications are rarely one container. A typical web app needs the app itself, a database, a cache, maybe a queue and a worker. Docker Compose describes all of them in one YAML file and runs them with one command. It creates a private network where services find each other by name, attaches volumes so data survives restarts, and starts things in dependency order.

Compose is the standard tool for local development environments and integration tests. It's also used for small single-host deployments, though clusters are Kubernetes territory.

TL;DR

Quick Example

An API with PostgreSQL and Redis for local development:

Core Concepts

Services

Each service becomes one or more containers from either an image or a build context. Services accept most docker run options: ports, environment, volumes, command, restart, resource limits, and healthchecks. docker compose up --scale worker=3 runs several replicas of a service.

Networking

Compose creates a default bridge network per project, and every service joins it. Built-in DNS resolves service names, so the API connects to postgres:5432, not localhost. ports publishes a container port to the host ("5432:5432") and is only needed for access from outside Compose, such as a database GUI on your laptop. See Docker networking for custom networks and isolation.

Volumes

More in Docker volumes.

Startup Order and Health

depends_on alone only waits for the container to start, not for Postgres to accept connections. With condition: service_healthy, Compose waits until the dependency's healthcheck passes. Apps should still retry connections, since dependencies can restart later too.

Configuration and Overrides

Profiles

Tag optional services with profiles: [tools] and they only start when you ask for them with docker compose --profile tools up. That's good for admin UIs, seeders, and load generators you don't always need.

Local Development Workflows

Best Practices

Healthchecks on Every Dependency

Define healthchecks for databases, caches, and brokers, and use service_healthy conditions. It removes "works on the second try" flakiness from startup.

Don't Publish Ports You Don't Need

Services talk over the Compose network. Publish only what you access from the host, and bind to localhost ("127.0.0.1:5432:5432") so dev databases aren't exposed to your whole network.

Keep Secrets Out of compose.yaml

Commit a .env.example and gitignore the real .env. For anything sensitive beyond local dev, use Compose secrets: (mounted as files) or your platform's secret store.

Pin Image Versions

postgres:17 rather than postgres:latest, so everyone on the team and CI runs the same versions. Match production versions where possible.

Common Mistakes

Connecting to localhost From Inside a Container

Losing Data With down -v

docker compose down -v deletes named volumes, including your local database. Use plain down day to day, and reserve -v for when you really want a clean slate.

Using Compose as a Production Orchestrator at Scale

Compose runs on one host. It has no multi-node scheduling, no rolling updates across machines, and no self-healing beyond restart policies. For a single small server it's fine; beyond that, use a real orchestrator or a managed container service.

FAQ

Is it docker-compose or docker compose?

Use docker compose (with a space). Compose v2 is a Docker CLI plugin written in Go; the old Python docker-compose v1 is end-of-life. The file can be named compose.yaml (preferred) or docker-compose.yml, and the top-level version: key is obsolete.

Should I use Compose or Kubernetes for local development?

Compose for most teams: it's simpler, faster, and good enough to run dependencies. Kubernetes-in-a-box tools (kind, minikube, Tilt, Skaffold) make sense when you're developing Kubernetes-specific behavior such as operators, Helm charts, or ingress rules.

How do I run a one-off command?

docker compose run --rm api npm run migrate starts a fresh container for the service and removes it afterwards. docker compose exec api sh runs a command inside the already running container.

Can Compose files be used in production?

For single-host deployments, yes, with restart: unless-stopped, resource limits, and a reverse proxy in front. Some platforms also accept Compose files as input. Anything needing multiple hosts or zero-downtime rollouts should move to an orchestrator.

Related Topics

References