Compose v2 is a Docker CLI plugin, so the command is with a space. The hyphenated was v1, written in Python, and has been retired; if a tutorial still uses it, swap in the space. The file is by default ( still works), and the old key at the top is obsolete and ignored.
Start and Stop
Run these from the directory that holds , or point at it with .
| Command | What it does |
|---|---|
| Create and start every service, attached to the logs (Ctrl+C stops) | |
| Start in the background | |
| Rebuild images that have a section first | |
| Only one service (and what it ) | |
| Only one service, without its dependencies | |
| Recreate containers even if nothing changed | |
| Pull newer images before starting | |
| Return only when services are running and healthy | |
| Also remove containers for services no longer in the file | |
| Run three copies of a service | |
| Stop containers, keep them and their networks | |
| Stop one service | |
| Start stopped containers again | |
| Restart every service | |
| Restart one service (does not pick up file changes; use ) | |
| Stop and remove containers and networks | |
| Also remove named volumes declared in the file. This deletes data | |
| Also remove the images | |
| Also remove leftover containers from removed services | |
| / | Freeze and thaw |
| SIGKILL a service ( for another signal) |
is also how you apply changes: it compares the file against the running containers and recreates only the ones whose configuration changed. does not do that.
Status and Logs
| Command | What it does |
|---|---|
| Containers for this project, with state and ports | |
| Include stopped ones | |
| Just the service names | |
| Every Compose project running on this host | |
| Logs from every service | |
| Follow | |
| Follow one service | |
| Last 100 lines, then follow | |
| Only the last ten minutes | |
| With timestamps | |
| Processes in every container | |
| Live CPU and memory per container | |
| Stream container events for the project | |
| Which image and tag each service runs | |
| Which host port maps to container port 80 |
Exec and Run
runs inside an existing container. starts a new one from the service definition, which is the right tool for one-off jobs like migrations.
| Command | What it does |
|---|---|
| Shell in the running container ( where the image has it) | |
| As root | |
| Run one command | |
| No TTY, for scripts and pipes | |
| With an extra variable | |
| In a specific directory | |
| New throwaway container, with the service's volumes, env and network | |
| Run a command in one | |
| Without starting the services it depends on | |
| Publish the service's ports too ( skips them by default) | |
| Override an environment variable | |
| Ignore the image's entrypoint |
Without , every leaves a stopped container behind. shows them and clears them.
Build and Pull
| Command | What it does |
|---|---|
| Build every service with a section | |
| Build one | |
| Rebuild every layer | |
| Pull a newer base image first | |
| Pass a build argument | |
| Pull the latest image for every service | |
| Pull one | |
| Keep going if one image is unavailable | |
| Push built images to their registry | |
| Create containers without starting them |
Update a running stack to newer images:
docker compose pull
docker compose up -drecreates only the containers whose image changed. For services you build yourself, does the same thing in one step. The Dockerfile generator is a quick way to get the side started.
Files, Projects and Profiles
| Command | What it does |
|---|---|
| Use a specific file | |
| Layer files; later ones override earlier ones | |
| Set the project name (default is the directory name) | |
| Variables for substitution in the file | |
| Also start services tagged | |
| Profiles apply to too, or profiled containers stay behind | |
| Resolve relative paths from another directory | |
| Check what the merged file looks like |
is loaded automatically alongside when you do not pass , which is the usual way to keep dev-only ports and bind mounts out of the main file. The same flags can live in the environment: , , .
A file in the project directory is read automatically for substitution inside . It is not the same as on a service, which passes variables into the container.
Validate and Clean Up
| Command | What it does |
|---|---|
| Validate the file and print the fully resolved version | |
| Validate only, no output; non-zero exit on error | |
| List service names | |
| List volume names | |
| List profiles | |
| Remove stopped service containers | |
| Stop, remove, and drop anonymous volumes without asking | |
| Remove containers for services you deleted from the file | |
| Which Compose you have |
on is a warning, not an error: a service was renamed or removed from the file and its old container is still around. on or clears it.
compose.yaml Keys
The keys you actually use, per service. Indentation is two spaces and matters.
| Key | Example | Notes |
|---|---|---|
| top level | One entry per container | |
| Image to pull | ||
| or | Build from a Dockerfile instead. Add too to name the result | |
| Fixed name; default is . Prevents | ||
| . Quote them; binds to localhost only | ||
| Reachable by other services only, not the host | ||
| Named volume (declare it under top-level ) | ||
| Bind mount; relative paths are allowed here | ||
| Read-only | ||
| or | Variables in list or map form | |
| Load variables from a file into the container | ||
| (default), , , | ||
| Start order only; does not wait for readiness | ||
| Wait for the healthcheck to pass | ||
| see below | How Compose decides the service is healthy | |
| Attach to a named network (declare under top-level ) | ||
| Replace the image's CMD | ||
| Replace the image's ENTRYPOINT | ||
| Run as a UID and GID | ||
| Working directory | ||
| Only start with | ||
| Metadata; see the Traefik label generator | ||
| Cap log size | ||
| Memory and CPU caps (honoured by too, not only Swarm) |
Top-level and declare the names the services refer to:
volumes:
data:
networks:
backend:Services on the same project network reach each other by service name (), which is why works. Compose creates a default network per project, so you only need when you want to split services up.
Example compose.yaml
A web app built from the local Dockerfile, a Postgres database with a healthcheck, and a named volume for its data:
services:
web:
build: .
image: myapp:latest
ports:
- "8080:3000"
env_file: .env
environment:
DB_HOST: db
NODE_ENV: production
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16
volumes:
- data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
restart: unless-stopped
volumes:
data:reads from the shell or the project file and falls back to . The Docker Compose generator builds files like this for common stacks.
Healthcheck and depends_on
alone only orders startup: the container starts first, but Postgres may still be initialising when connects. A healthcheck plus makes Compose wait.
| Key | Example | Notes |
|---|---|---|
| Exit 0 is healthy. runs directly, runs through | ||
| Shell form when you need , pipes or variables | ||
| Time between checks (default 30s) | ||
| Fail the check if it takes longer | ||
| Consecutive failures before | ||
| Grace period at startup during which failures do not count | ||
| Turn off a healthcheck the image defines | ||
| in | The default: container started | |
| in | Wait until the healthcheck passes | |
| in | Wait for a one-shot service (migrations) to exit 0 | |
| in | Restart this service when the dependency is recreated |
The check runs inside the container, so or has to exist in that image. Alpine images often lack ; usually works instead. shows or next to the state, and shows the last few results.
command vs entrypoint
| Situation | What to write |
|---|---|
| Change the arguments, keep the image's entrypoint | |
| Replace the whole command (image has no entrypoint) | |
| Run a shell script with or pipes | |
| Same, in list form | |
| Ignore the image's entrypoint entirely | plus |
| Keep a container alive with no real process | (or ) |
| Interactive shell on or | add and |
| Pass arguments to an entrypoint script | ; the script gets them as , |
The string form () is split on spaces by Compose without a shell, so quotes inside it are kept literally and is passed as an argument. When in doubt, use the list form and name explicitly. A multi-line script can go under a folded block scalar:
command: >
sh -c "npm run migrate &&
npm run seed &&
npm start"Gotchas
- does not read the file again. After editing , run ; it recreates only what changed.
- deletes every named volume declared in the file, including the database. alone keeps them.
- without a only orders container starts. Add a healthcheck and when the app crashes on boot because the database is not ready.
- Ports in YAML must be quoted (); unquoted is parsed as a base-60 number by some YAML parsers and becomes garbage.
- in the project directory feeds substitution in the file. feeds the container. They are different files with different jobs, and a variable in does not reach the container unless you also pass it through or .
- Services started with need the same on , or their containers are left running.
- on means another container, or a process on the host, holds that port. and find it; see Error response from daemon for the other variants.
- Bare commands work on Compose containers too: , . The docker commands sheet covers them.