Compose turns a page of docker run commands into one versioned file that the whole team runs the same way.
Real apps are several containers: API, database, cache, worker. Docker Compose describes them declaratively in compose.yaml and manages them as one project with the docker compose command (Compose v2, built into the Docker CLI).
The file's top-level keys:
services: each service is a container definition, withimageorbuild,ports,environment,env_file,volumes,depends_on,commandand so on, mirroringdocker runflags.volumes: named volumes the services use.networks: optional; by default Compose creates one network for the project, and every service can reach every other by service name.
The old top-level version: key is obsolete and ignored.
Core commands, run in the folder with the file:
docker compose up -d: build or pull as needed, create the network and volumes, start everything in the background. Run it again after editing the file and only changed services are recreated.docker compose ps,logs -f api,exec db psql -U postgres.docker compose up -d --build: rebuild images first.docker compose down: stop and remove containers and the network. Volumes are kept unless you add-v.
Compose substitutes variables like ${TAG:-latest} from your shell or from a .env file next to compose.yaml. That .env is for interpolating the YAML; env_file: passes variables into containers. They are different mechanisms.
depends_on controls start order only; by default it doesn't wait for the database to be ready. Combine it with healthchecks (condition: service_healthy), covered later, or make the app retry its connection.
Example
# compose.yaml
services:
api:
build: .
image: acme/api:${TAG:-dev}
ports:
- "127.0.0.1:8000:8000"
environment:
DATABASE_URL: postgres://postgres:dev@db:5432/postgres
depends_on:
- db
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: dev
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
# docker compose up -d --build ; docker compose logs -f api ; docker compose downRemember
- compose.yaml declares services, volumes and networks for one project
- Services reach each other by service name on the project network
- up -d is idempotent; down keeps volumes unless -v
- .env interpolates the YAML; env_file passes variables into containers
Watch out for
- Assuming depends_on waits until the database accepts connections — it only orders startup unless you use a health condition.