docker compose — a container stack in one file
docker compose describes a container stack with network, volumes and variables in compose.yaml and starts it at once.
Cheat sheet
8 commands Printable version-
sudo docker compose up -dstart all of compose.yaml -
sudo docker compose psproject containers and health -
sudo docker compose logs -f backendone service's logs -
sudo docker compose exec proxy sha shell in a service -
sudo docker compose config -qvalidate the file -
sudo docker compose restart proxyrestart a service -
sudo docker compose downremove containers and network -
sudo docker compose down -v… and volumes with their data
Overview
docker compose is the Docker plugin for a stack
of several containers. Services, their images, ports, volumes, variables
and start order live in one compose.yaml file. docker compose up -d
creates the network, volumes and containers from it; down removes them.
Everything compose does can be done by hand with docker commands —
the file saves you long docker run lines and keeping the start order in
your head.
The lab machine has Docker Compose 2.40, written with a space:
docker compose. The old hyphenated docker-compose program is not
installed. Like other Docker commands, compose goes through sudo.
Compose looks for compose.yaml (or docker-compose.yml) in the
current directory. Another file — -f stack.yaml.
The compose.yaml file
A stack of two services: backend serves a page from a volume, proxy
is nginx in front of it and the only one with a port to the outside.
Next to it lies nginx.conf with proxy_pass http://backend:8000; —
nginx reaches its neighbour by service name.
| Service key | What it sets | Same in docker run |
|---|---|---|
image |
the image | image name |
build |
a directory with a Dockerfile — build the image | docker build |
command |
a command instead of the image CMD |
words after the image |
environment |
environment variables | -e |
ports |
"host:container" |
-p |
volumes |
volume:/path or ./dir:/path |
-v |
depends_on |
what to start first | — |
healthcheck |
the healthcheck | --health-cmd |
restart |
the restart policy | --restart |
The top-level volumes key declares named volumes, networks extra
networks. The syntax is YAML: indent with spaces, tabs are
not allowed. Quote ports: "8090:80".
A volume created beforehand (docker volume create keep) is attached as
external — compose neither creates nor removes it:
If the external volume is missing, up fails: external volume "keep"
not found. Without external: true compose creates its own empty
<project>_data volume, even if keep with data sits right there.
Without a networks key all project services share one network. Your
own networks are declared under top-level networks: and listed per
service: networks: [front, back]. A service name is visible only on
shared networks: web on front cannot find db on back
(server can't find db), while app, attached to both, sees both.
Project: names of containers, network and volumes
The file describes a project. The project name is the name of the
directory holding compose.yaml; everything compose creates is named
after it:
| What | Name |
|---|---|
| network | <project>_default — stack_default |
volume data |
<project>_data — stack_data |
| service container | <project>-<service>-1 — stack-backend-1 |
All project services share one network, and each is visible to the others by its service name:
Compose commands address services by their names from the file:
logs backend, exec proxy, restart proxy. Run them from the project
directory — or with -f and -p.
Variables
Compose substitutes ${GREETING:-hello} itself, before any container
starts: the environment variable's value, or hello if it is unset.
Variables come from a .env file next to compose.yaml:
up -d re-created only backend: its environment changed. proxy did
not change and kept running.
A shell variable in front of sudo never reaches compose: sudo clears
the environment, so GREETING=privet sudo docker compose up -d changes
nothing. Put the value in .env.
$$ is a literal dollar sign: compose leaves $$GREETING in command
alone, and the shell inside the container expands it. docker compose
config shows the result of substitution.
Start order and healthcheck
depends_on: [backend] only sets the order: proxy starts once the
backend container is running, whether it is ready to answer or not.
condition: service_healthy waits for readiness — compose waits for the
healthy status from the backend healthcheck.
healthcheck.test is a command inside the container: code 0 means
healthy. Docker runs it every interval, waits at most timeout, and
after retries failures in a row marks it unhealthy. The status shows
in ps:
The PORTS column of proxy shows
0.0.0.0:8090->80/tcp, [::]:8090->80/tcp.
The check command must exist in the image: busybox and alpine have
wget but no curl.
Stack commands
| Command | What it does |
|---|---|
up -d |
creates what is missing and re-creates services whose description changed (Recreate), leaves the rest alone (Running) |
restart backend |
restarts the same container: environment and options stay the same |
stop, start |
stops and starts without removing containers |
ps |
container status: Up … (healthy), Restarting (1) …, Exited (137) |
logs backend |
a service's output; without a name, all services |
down |
removes the project's containers and network; named volumes stay |
down -v |
the same plus the project's volumes |
Only up -d applies a compose.yaml or .env edit. restart restarts
the process in the old container: it re-reads files from the host, but
the variables stay as they were.
Options
20The main subcommands and options are in the table below. The full list
is docker compose --help and docker compose <command> --help.
| Option | What it does | Example |
|---|---|---|
up |
Create and start everything in compose.yaml; changed services are re-created, the rest are left alone. | docker compose up -d |
-d --detach |
Start in the background and return the prompt; without it up holds the terminal and shows logs. | docker compose up -d |
ps |
Project containers — status, health, ports; with -a, stopped ones too. | docker compose ps -a |
logs |
Service output prefixed with the service name; -f follows, --tail shows the last lines. | docker compose logs -f --tail 20 backend |
exec |
A command in the running container of a service, named as in compose.yaml. | docker compose exec proxy nslookup backend |
config |
Validate the file and print the final configuration with variables substituted. | docker compose config |
down |
Stop and remove the project's containers and network; named volumes stay. | docker compose down |
--build |
Rebuild images of services that have a build key before starting. | docker compose up -d --build |
--wait |
Wait until services are running or healthy; if not, exit with a non-zero code. | docker compose up -d --wait |
--format json |
One JSON object per line for each container — for scripts and jq. | docker compose ps --format json |
-q --quiet |
Validation only — no output, exit code 0 if the file is valid. | docker compose config -q |
restart |
Restart a service's containers without re-creating them; compose.yaml edits are not applied this way. | docker compose restart proxy |
stop |
Stop services without removing containers; bring back with start or up -d. | docker compose stop backend |
-v --volumes |
Remove the project's volumes too — their data is lost. dangerous | docker compose down -v |
build |
Build images for services that have a build key. | docker compose build |
-f --file FILE |
Use another file instead of compose.yaml in the current directory. | docker compose -f stack.yaml up -d |
-T |
No terminal — for scripts and pipelines. | docker compose exec -T backend cat /etc/hosts |
--services |
Only the service names, one per line. | docker compose config --services |
-p --project-name NAME |
Project name instead of the directory name — the prefix of containers, network and volumes. | docker compose -p demo up -d |
--env-file FILE |
A variables file for substitution instead of .env next to compose.yaml. | docker compose --env-file prod.env up -d |
Nothing found. Try a shorter query.
Recipes
4Stack state for a script
--format json prints one JSON object per line for each container —
jq parses them one by one.
Stop one service
Logs of all services at once
Each line starts with the service name. One service — logs backend;
follow — logs -f.
Troubleshooting
11no configuration file provided: not found
The current directory has no compose.yaml. Go to the project directory
or name the file: docker compose -f /srv/stack/compose.yaml ps.
additional properties '…' not allowed
A misspelled key or a key at the wrong indent level. The path to the
error is services.backend; compare the key name with the table above.
yaml: line 3: found character that cannot start any token
Line 3 has a tab or another character YAML cannot start a value with. YAML indents with spaces only. The site's "YAML ⇄ JSON" tool in the Tools section also points to the error.
service "backend" refers to undefined volume
The service mounts a named volume missing from the top-level volumes:.
Declare it there or write a bind mount path starting with ./ or /.
external volume "…" not found
A volume with external: true must exist before up; by then compose has
already created the project network. Compare the name: with
docker volume ls. If the volume is missing, create it
(docker volume create keep) or restore the data from a backup.
dependency failed to start: container … is unhealthy
backend failed its healthcheck, and proxy with condition:
service_healthy stayed Created. See what the check returns:
docker compose ps -a (status unhealthy) and
docker inspect -f '{{json .State.Health}}' <container> — it holds the
output of the last attempts. Common causes: the wrong port in test, no
wget in the image, a service that starts slower than
interval × retries.
The "X" variable is not set
The "MODE" variable is not set. Defaulting to a blank string. — the
variable is neither in the environment nor in .env, so compose put an
empty string in. Set it in .env or give a default: ${MODE:-prod}.
the attribute version is obsolete
A version: "3.8" line at the top of the file is obsolete and compose
ignores it. Delete it and the warning goes away.
A service in a restart loop
docker compose ps shows Restarting: the main process crashes and
restart: brings it up again. The cause is in docker compose logs
<service>: a wrong variable, a missing file, a busy port. Fix
compose.yaml or .env and run up -d.
Pulling … failed to resolve reference
The service image is not on the machine, so compose went to the registry
for it. Without network, load the image from a file (docker load -i)
and check the name and tag in image: against docker images.
service "proxy" is not running
exec works only in a running service. docker compose ps -a shows what
happened to it; docker compose up -d brings it up.
Danger zone
2docker compose down -v
down removes containers and the network and keeps volumes. -v
removes the project volumes too: the database, uploads, everything the
services kept in volumes: is gone for good. External volumes
(external: true) are left alone by -v.
Another project name means other volumes
A volume name is built from the project name: stack_data. Copy
compose.yaml to another directory or run it with -p, and compose
creates a new empty volume <new name>_data — the service starts with no
data. The old data is safe in the old volume: docker volume ls.
Terms
2- compose service
- An entry under services in compose.yaml: the image, command, ports and volumes of a container. The service name is its DNS name on the project network.
- healthcheck
- A command Docker runs inside a container at intervals: exit code 0 means healthy, several failures in a row mean unhealthy. The status shows in docker ps. Also appears in: Docker
See also
- docker — containers, volumes, networks and
inspectwithout compose. - container-images — where service images come from.
- dockerfile — building your own image for
build:. - env-vars — environment variables in services.
- nginx — a proxy in front of the stack's services.
- curl — checking the stack's answer from the host.
- jq — parsing
ps --format json.
Found an inaccuracy? Report an error in the article