Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

By updated

Improve this page

Logo Docker

In a Compose stack, depends_on orders the launch of the containers, not their readiness. A service can start while its database is still initializing its data, and its first connection attempt fails. This lesson is for a beginner who can already write a Compose stack: you learn to make a service wait until its database really answers.

To do that, you give the database a health check (healthcheck), a command that Docker runs regularly to know whether the service is ready. You then make the dependent service wait, first on that health with service_healthy, then on the end of a migration with service_completed_successfully. Finally you see what Docker does not do: a service that became unhealthy is neither restarted nor isolated. The application must therefore retry by itself.

What you will learn

  • See that a depends_on without a condition does not guarantee that a database is ready.
  • Declare a healthcheck and choose a probe that tests what the dependent service needs.
  • Make a service depend on the health of another one with service_healthy.
  • Make a service wait for a one-off task, such as a migration, with service_completed_successfully, and read its failure.
  • Explain why the application must retry when its dependency disappears, and recognize a client with no retry.
  • See that an unhealthy service is not restarted by Docker.

Prerequisites

You need a Linux host with Docker Engine 29.9.2 and its Compose 5.6.1 plugin, installed from the official repository as shown in the lesson on installing Docker. The images are pinned by their digest.

You know how to write a stack thanks to the lesson Describe an application with Compose. Three neighboring lessons provide the base:

The states of a container (Exited, Restarting) and the restart policies are explained in Read a container that does not start or restarts in a loop. This page does not repeat them.

You work in a ~/librairie folder, which the lesson has you create. The folder name becomes the project name: the containers are called librairie-....

Why is a started service not a ready service?

Because depends_on, written in its short form, only waits for the container it depends on to be launched. The program inside may not answer yet. On a fast machine, a database that initializes in half a second gives the impression that the order is enough. To see the problem, you therefore need a database that really takes time.

Create the folder, then an initialization script that pauses for six seconds. This script simulates a long initialization: the postgres image runs it at first startup, when its data folder is empty.

Terminal window
mkdir -p ~/librairie/initdb ~/librairie/migrations ~/librairie/site
cd ~/librairie
printf 'SELECT pg_sleep(6);\n' > initdb/01-delai.sql

Then write a first compose.yaml. The base service is a PostgreSQL. The catalogue service tries only once to read the database, prints a message if it fails, then stays alive so that you can read its logs.

services:
base:
image: postgres:18.6-alpine3.24@sha256:10a5c69a83035ab5239f5620d708520e8b28f5aa1f2b20143e6024532edadfb5
environment:
POSTGRES_USER: librairie
POSTGRES_PASSWORD: demo-seulement
POSTGRES_DB: librairie
volumes:
- ./initdb:/docker-entrypoint-initdb.d:ro
catalogue:
image: postgres:18.6-alpine3.24@sha256:10a5c69a83035ab5239f5620d708520e8b28f5aa1f2b20143e6024532edadfb5
environment:
PGPASSWORD: demo-seulement
command:
- sh
- -c
- psql -h base -U librairie -d librairie -tAc 'SELECT 1' || echo 'connection failed'; exec sleep infinity
depends_on:
- base

The value demo-seulement is that of a disposable demonstration, as in the neighboring lessons: a real database receives its password through a secret. The PGPASSWORD variable gives this password to the psql client of the catalogue service.

The catalogue service reuses the postgres image because it contains the psql client. Start the stack and read what it printed. On the very first run, Compose downloads the image first, which adds progress lines before the ones below.

Terminal window
docker compose up -d
docker compose logs catalogue
[+] up 3/3
✔ Network librairie_default Created 0.0s
✔ Container librairie-base-1 Started 0.3s
✔ Container librairie-catalogue-1 Started 0.4s
catalogue-1 | psql: error: connection to server at "base" (172.18.0.2), port 5432 failed: Connection refused
catalogue-1 | Is the server running on that host and accepting TCP/IP connections?
catalogue-1 | connection failed

Look at two things. Compose launched catalogue as soon as base was started: the output shows no wait. And the connection is refused, because the database is not listening on the network yet. The IP address shown may differ on your machine. Wait ten seconds, then repeat the same query from catalogue.

Terminal window
docker compose exec catalogue psql -h base -U librairie -d librairie -tAc 'SELECT 1'
1

The database answers now. It was therefore not down: catalogue left too early, and nothing in the file forbade it.

How do you declare a health check?

A health check is a command that Docker runs regularly inside the container: exit code 0 declares it successful, any non-zero code declares it failed (Docker recommends code 1). Docker derives a state from it, visible in docker inspect and docker compose ps.

Thanks to the health check, the container is in one of three states. It is starting before the first success, healthy when the last probe succeeded, and unhealthy after retries consecutive failures outside the grace period. The table gives the six settings of a Compose healthcheck, with Docker's default values.

SettingRoleDefault
testthe command, as ["CMD", ...] without a shell or ["CMD-SHELL", "..."] with a shellnone
intervaldelay between two probes30 s
timeoutduration beyond which a probe counts as failed30 s
retriesnumber of consecutive failures before unhealthy3
start_periodgrace period during which failures do not count0 s
start_intervaldelay between two probes during start_period (Docker Engine 25.0 or later)5 s

The grace period does not delay the healthy state: a probe that succeeds during start_period declares the service healthy right away. It only avoids counting as failures the attempts of a service that is slow to start. During this period, the probes follow one another at the pace of start_interval, not of interval.

These settings also set the failure detection delay: roughly interval times retries, plus the duration of each probe, and a little less if the failure happens in the middle of an interval. With interval: 2s and retries: 3, the switch to unhealthy takes a few seconds (4.6 seconds measured below, on an nginx service). With the default values, it takes about a minute and a half, which is long for a demonstration and often reasonable in production.

Which command should you pick to test a PostgreSQL database?

The pg_isready command answers the question "does the server accept connections?", but its answer depends on how you query it. At first startup, the postgres image first launches a temporary server, which only listens on a connection point local to the container (the socket). It runs the initialization script, stops, then the real server starts on the network.

A pg_isready without an address queries the socket: it can answer "ready" too early, while the other containers, which go through the network, are still refused.

Measure it. Start again from an empty volume, start the database alone, then run both forms of the command within the first five seconds.

Terminal window
docker compose down -v
docker compose up -d base
docker compose exec base pg_isready -U librairie -d librairie
docker compose exec base pg_isready -h 127.0.0.1 -U librairie -d librairie
/var/run/postgresql:5432 - accepting connections
127.0.0.1:5432 - no response

The first line announces "accepting connections" at a moment when no other container can connect yet. The second tells the truth. In the measurement, repeated every half second, the form without an address answered from 0.8 seconds, and the -h 127.0.0.1 form only from 7.4 seconds, after the end of the initialization. The probe must therefore go through -h 127.0.0.1, which the temporary server does not listen on.

If your attempt does not give this result, it is a matter of timing. Two "no response" lines: the temporary server is not started yet, wait a second and run the two commands again. Two "accepting connections" lines: you went past the seven seconds, start over with docker compose down -v.

Keep the principle in mind: a probe tests what the dependent service needs, here being able to connect through the network, and not only the existence of the process.

Add the health check to the base service of the compose.yaml.

healthcheck:
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U librairie -d librairie"]
interval: 2s
timeout: 3s
retries: 10
start_period: 15s

The values are short for the demonstration. The grace period of 15 seconds comfortably covers the 6-second initialization. Recreate the database and follow its state every two seconds.

Terminal window
docker compose down -v
docker compose up -d base
for i in 1 2 3 4 5 6 7 8; do docker inspect -f '{{.State.Health.Status}}' librairie-base-1; sleep 2; done
starting
starting
starting
starting
starting
healthy
healthy
healthy

Look at the switch from starting to healthy. It comes after the end of the initialization, not when the container starts: here about ten seconds after launch. The probe calculation is in the optional box above.

How do you make a service wait until its dependency is healthy?

With the long form of depends_on, which attaches a condition to each dependency. There are three conditions, and only the first is that of the short form.

ConditionCompose waits until the dependency is…Use case
service_startedlaunchedno need for it to be ready
service_healthyhealthydatabase, API, server that takes time to answer
service_completed_successfullyfinished with code 0one-off task, such as a migration

The service_healthy condition assumes that the dependency declares a healthcheck: without a probe, there is nothing to wait for. Change the catalogue service to request it.

depends_on:
base:
condition: service_healthy

Start the stack again from an empty volume and watch the sequence.

Terminal window
docker compose down -v
docker compose up -d
docker compose logs catalogue
[+] up 3/3
✔ Network librairie_default Created 0.0s
✔ Container librairie-base-1 Healthy 10.8s
✔ Container librairie-catalogue-1 Started 10.9s
catalogue-1 | 1

Look at the durations: base becomes Healthy after about ten seconds, and catalogue starts right after. This time its query returns 1. The dependent service was delayed until the database really served.

How do you wait for a one-off task such as a migration?

A migration is a script that prepares the schema of the database (tables, columns) before the application uses it. It runs once then stops: it is a task, not a service that keeps running. You therefore declare it as a separate service, and Compose waits for its successful end rather than for its health.

Write a repeatable migration, like the one in the lesson Persist the data of a recreated stack: it does nothing if its work is already done. Here, IF NOT EXISTS and ON CONFLICT DO NOTHING avoid the failure and the duplicate on the second run.

Terminal window
cat > migrations/001-livres.sql <<'EOF'
CREATE TABLE IF NOT EXISTS livres (
id serial PRIMARY KEY,
titre text NOT NULL UNIQUE
);
INSERT INTO livres (titre) VALUES ('Candide'), ('Germinal')
ON CONFLICT DO NOTHING;
EOF
printf 'SELEKT 1;\n' > migrations/002-erreur.sql

The second file contains a deliberate mistake, used further down. Then add the migration service, and make catalogue wait for it. The file to run is chosen by a variable, with a default value, so that you can simulate a failure without editing the file.

migration:
image: postgres:18.6-alpine3.24@sha256:10a5c69a83035ab5239f5620d708520e8b28f5aa1f2b20143e6024532edadfb5
environment:
PGPASSWORD: demo-seulement
command:
- psql
- -h
- base
- -U
- librairie
- -d
- librairie
- -v
- ON_ERROR_STOP=1
- -f
- /migrations/${FICHIER_MIGRATION:-001-livres.sql}
volumes:
- ./migrations:/migrations:ro
restart: "no"
depends_on:
base:
condition: service_healthy

The -v ON_ERROR_STOP=1 option makes psql exit with a non-zero code at the first error of the script. Without it, a broken migration could end with code 0, and Compose would believe it succeeded.

The restart: "no" value is the default behavior. It is written here to say that Docker must not restart a finished task. It does not prevent a new docker compose up -d from starting it again, as the test further down shows; the restart policies are detailed in the troubleshooting lesson cited above. Now replace the depends_on of catalogue and its query.

catalogue:
image: postgres:18.6-alpine3.24@sha256:10a5c69a83035ab5239f5620d708520e8b28f5aa1f2b20143e6024532edadfb5
environment:
PGPASSWORD: demo-seulement
command:
- sh
- -c
- psql -h base -U librairie -d librairie -tAc 'SELECT count(*) FROM livres' || echo 'read failed'; exec sleep infinity
depends_on:
migration:
condition: service_completed_successfully

The diagram follows the order in time, from top to bottom. Compose starts base and waits for it to be healthy, launches the migration and waits for its code 0, then only catalogue. Look at the note at the bottom right: once catalogue is launched, Compose no longer watches anything.

Compose creates and starts the base service, whose probe fails during initialization (starting state) then succeeds (healthy state). Compose then creates the migration service, which ends with code 0. Compose finally creates the catalogue service. Once catalogue is launched, base can fail later without Compose reacting.cataloguemigrationbaseComposeinitialization,probe failing:startingruns, but basemay fail laterand Composedoes notreactcreates and startsbasethe probe succeeds:healthycreates and starts migrationexits with code 0creates and starts catalogue
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

Start the stack and read the state of the three services. The -a option is needed, because migration has stopped and docker compose ps alone only shows the containers that are running.

Terminal window
docker compose down -v
docker compose up -d
docker compose ps -a --format '{{.Service}} {{.Status}}'
docker compose logs catalogue
[+] up 4/4
✔ Network librairie_default Created 0.0s
✔ Container librairie-base-1 Healthy 10.9s
✔ Container librairie-migration-1 Exited 11.5s
✔ Container librairie-catalogue-1 Started 11.6s
base Up 11 seconds (healthy)
catalogue Up Less than a second
migration Exited (0) Less than a second ago
catalogue-1 | 2

Look at the state of each service: base is healthy, migration exited with code 0 (Exited (0)) and catalogue reads the two books. The durations in docker compose ps depend on when you read the state.

Now replay the migration alone. The --no-deps option avoids restarting its dependencies, and --rm removes the container of the task at the end.

Terminal window
docker compose run --rm --no-deps migration
docker compose exec base psql -U librairie -d librairie -tAc 'SELECT count(*) FROM livres'
Container librairie-migration-run-53a89a924edb Creating
Container librairie-migration-run-53a89a924edb Created
psql:/migrations/001-livres.sql:4: NOTICE: relation "livres" already exists, skipping
CREATE TABLE
INSERT 0 0
2

The second run creates nothing and inserts nothing. psql reports that the table already exists, the insert returns INSERT 0 0 and the table still contains two rows. The CREATE TABLE line is only the name of the command that was run, even when it created nothing, and the identifier of the temporary container changes at every run.

This repeatability is necessary: a new docker compose up -d also restarts the migration that already finished, because up starts every stopped service of the file, dependent or not.

The same test on a task with no dependent at all, with or without restart: "no", restarts it the same way. Check it by running the command again without changing anything.

Terminal window
docker compose up -d
[+] up 3/3
✔ Container librairie-catalogue-1 Running 0.0s
✔ Container librairie-base-1 Healthy 0.5s
✔ Container librairie-migration-1 Exited 0.7s

Compose touches neither base nor catalogue, which are already running, but it restarts the migration container: the word Exited at the end shows that it ran and stopped again. The log of the migration (docker compose logs migration) then contains two runs. The second one prints INSERT 0 0, because the two books already exist. A repeatable migration can therefore be replayed safely at every deployment.

What happens when the migration fails?

The dependent service never starts. Start the stack again from scratch, pointing to the broken file through the variable.

Terminal window
docker compose down -v
FICHIER_MIGRATION=002-erreur.sql docker compose up -d
docker compose ps -a --format '{{.Service}} {{.State}}'
docker inspect -f '{{.State.ExitCode}}' librairie-migration-1
[+] up 4/4
✔ Network librairie_default Created 0.0s
✔ Container librairie-base-1 Healthy 10.9s
✘ Container librairie-migration-1 Error service "migration" didn't co... 11.5s
✔ Container librairie-catalogue-1 Created 0.1s
service "migration" didn't complete successfully: exit 3
base running
catalogue created
migration exited
3

Look at three points:

  • Compose stops with a message that names the service and its exit code. The container line is cut by the width of the terminal, but the last line of Compose gives the full message.
  • catalogue never reached the running state. It stays in the created state: the container exists, but Compose did not start it.
  • Code 3 is the one psql gives to a script error with ON_ERROR_STOP.

To understand the cause, read docker compose logs migration, which contains ERROR: syntax error at or near "SELEKT".

Then put the stack back in shape with the correct file.

Terminal window
docker compose down -v
docker compose up -d

Why must the application retry anyway?

Because depends_on only protects the startup. Once catalogue is launched, the database can stop for maintenance or because of an incident, and Compose no longer watches anything. A client that tries only once, like the one used so far, then never resumes its reading.

A robust client retries after a pause. Replace the command of catalogue with a loop that reads every two seconds and reports each failure. The $$ leaves the $ sign to the shell of the container, instead of letting Compose interpret it. A failing attempt itself lasts about five seconds, the time for the resolution of the name base to fail: the name of a stopped container no longer resolves, and psql answers "could not translate host name "base" to address" after 5.0 seconds measured. The failure message therefore comes back about every seven seconds.

command:
- sh
- -c
- |
while true; do
n=$$(psql -h base -U librairie -d librairie -tAc 'SELECT count(*) FROM livres' 2>/dev/null) \
&& echo "read succeeded: $$n books" \
|| echo "database unavailable, retrying in 2 s"
sleep 2
done

Apply the change, then stop the database. Run the commands one by one, with a pause before reading the logs.

Terminal window
docker compose up -d
docker compose stop base
sleep 20
docker compose logs --tail 2 catalogue
docker compose ps --format '{{.Service}} {{.Status}}'
catalogue-1 | database unavailable, retrying in 2 s
catalogue-1 | database unavailable, retrying in 2 s
catalogue Up 20 seconds

Look at the catalogue service: it is still started, and Compose did nothing more than the requested stop. The stopped database no longer appears, because docker compose ps without -a only lists the containers that are running. Turn the database back on and give it time to answer.

Terminal window
docker compose start base
sleep 10
docker compose logs --tail 2 catalogue
docker compose ps --format '{{.Service}} {{.Status}}'
catalogue-1 | read succeeded: 2 books
catalogue-1 | read succeeded: 2 books
base Up 10 seconds (healthy)
catalogue Up 30 seconds

The reading resumed without anyone touching catalogue, which stayed Up during the whole stop. The data of the database survived, because stopping does not erase its volume. The retries of the application therefore cover the case that neither the probe nor the startup order covers: a dependency that disappears after startup.

An infinite and silent loop remains a trap, though, because it hides a lasting failure. A real application limits the number of attempts, lengthens the pause between two attempts (this is called backoff) and ends up failing with a clear message, so that the incident is visible.

What does Docker do when a service becomes unhealthy?

Nothing. The unhealthy state is information, read by docker compose ps, by the service_healthy condition at startup, or by an outside tool such as a reverse proxy. Docker does not restart the container, and a restart policy does not deal with it either: it reacts to the end of the process, not to its health state, as the troubleshooting lesson recalls.

A reverse proxy such as Traefik can on the other hand read this state to set the instance aside, which Traefik with Docker details.

To see it, add a web server whose probe looks at a state file, and not at the presence of the process. Create the two files, then the service.

Terminal window
printf 'Welcome\n' > site/index.html
printf 'ok\n' > site/etat.txt
vitrine:
image: nginx:1.30.6-alpine@sha256:0e52bd054126cdc040b1276c0af347aeb02a41b9cfe305d6094767ac30e97949
volumes:
- ./site:/usr/share/nginx/html:ro
healthcheck:
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1/etat.txt"]
interval: 2s
timeout: 2s
retries: 3

The state file stands for what a real application would check before declaring itself available: database reachable, cache loaded. Start the service, check that it is healthy, then delete the state file on the host. The folder is read-only in the container, not on your host: you can therefore delete the file there.

Terminal window
docker compose up -d vitrine
sleep 6
docker inspect -f '{{.State.Status}} {{.State.Health.Status}} restarts={{.RestartCount}}' librairie-vitrine-1
rm site/etat.txt
sleep 10
docker inspect -f '{{.State.Status}} {{.State.Health.Status}} restarts={{.RestartCount}}' librairie-vitrine-1
docker compose exec vitrine wget -q -O - http://127.0.0.1/
running healthy restarts=0
running unhealthy restarts=0
Welcome

Read the three lines. The container is first healthy, then unhealthy a few seconds after the file is deleted (4.6 seconds measured). Yet its status stays running, the restart counter stays at 0 and the server keeps answering. Only the health label changed.

The probe uses wget, provided by the Alpine variant of the nginx image, which is not the case of every image. The probe log says why it fails.

Terminal window
docker inspect -f '{{range .State.Health.Log}}code={{.ExitCode}} {{.Output}}{{end}}' librairie-vitrine-1
code=1 wget: server returned error: HTTP/1.1 404 Not Found
code=1 wget: server returned error: HTTP/1.1 404 Not Found
code=1 wget: server returned error: HTTP/1.1 404 Not Found
code=1 wget: server returned error: HTTP/1.1 404 Not Found
code=1 wget: server returned error: HTTP/1.1 404 Not Found

Each line is a failed probe: wget did not find the state file and returned code 1. Docker only keeps the last five probes in this log. Put the file back and wait for one probe.

Terminal window
printf 'ok\n' > site/etat.txt
sleep 4
docker inspect -f '{{.State.Health.Status}} restarts={{.RestartCount}}' librairie-vitrine-1
healthy restarts=0

The service is healthy again without a restart: it is the same process that resumed. Keep the consequence in mind: if you want a detected failure to trigger an action, another tool has to do it, not the probe.

What should you do when startup does not wait as expected?

Start from what you see, then read the probe log before changing the file. The table links the symptoms of this lesson to their cause.

What you observeProbable causeWhat you do
docker compose up -d stops with exit code 1 and the message "dependency failed to start: container librairie-base-1 is unhealthy"The probe never succeeded: wrong command, wrong host, or grace period too shortRead docker inspect -f '{{range .State.Health.Log}}{{.Output}}{{end}}' <container>, test the command with docker compose exec, then fix test or start_period
The probe log contains "executable file not found" or code 127The tool of the probe does not exist in the imageChoose a command present in the image, or install the tool in the image
The service stays in starting for a long timeinterval too long (or start_interval during the grace period), or initialization longer than expectedShorten interval or start_interval and check that start_period covers the initialization
The probe is healthy but the client is refusedThe probe tests something other than what the client needs, for example the local socket instead of the networkMake the probe test through the same path as the client
Compose prints "didn't complete successfully" followed by a codeThe task failedRead docker compose logs <task>, fix it, and run docker compose up -d again
The dependent service starts before the migration endsdepends_on in short form, hence service_startedUse the long form with service_completed_successfully
The service is unhealthy and nothing happensNormal behavior: Docker does not restart on this state aloneFix the cause, or hand the reaction to a tool that reads the state
The client fails for good when the database restartsNo retry in the applicationAdd bounded retries with a pause

If up -d seems stuck, open a second terminal: docker compose ps shows the starting or unhealthy state, and the probe log is already filled.

How do you clean everything up after the demonstration?

The demonstration leaves four containers, one network, an anonymous volume created by the postgres image, the ~/librairie folder and two images. The docker compose down -v command removes the containers, the network and this volume.

Terminal window
cd ~/librairie
docker compose down -v

Then remove the images, then the folder.

Terminal window
docker rmi postgres:18.6-alpine3.24@sha256:10a5c69a83035ab5239f5620d708520e8b28f5aa1f2b20143e6024532edadfb5
docker rmi nginx:1.30.6-alpine@sha256:0e52bd054126cdc040b1276c0af347aeb02a41b9cfe305d6094767ac30e97949
cd ~ && rm -r librairie

After that, docker ps -a shows no librairie-... container, docker volume ls shows no volume of the stack, and ls ~/librairie answers that the folder no longer exists.

Practice

The first lab gives you two ready images and no running container: a server whose readiness comes after a slow initialization, and a client that tries only once. You write the compose.yaml with a readiness probe, not a simple presence probe, a short interval and a sufficient grace period, then you make the client depend on the health of the server. The validation temporarily removes the readiness of the server and waits for it to become unhealthy within a bounded delay. It also starts isolated projects with a slower server, which must delay the client, and a server that is never ready, with which the client must never start.

The project name is imposed, with the -p option seen in the first Compose lesson. The images already carry their command: unlike the demonstration, you do not change it and you add neither a mount nor a restart policy.

The second lab gives an image with a migration script and an application script that refuses to start without a schema. You declare the migration as a one-off task and you make the application wait for its successful end, with a shared volume that the migration and the application both mount in read-write mode. This shared named volume is taught by the lesson Persist the data of a recreated stack. The validation replays the migration twice to check that it preserves the data already written, then tests, in separate projects, an empty volume and a migration that exits with an error, where the application must never start.

The project name is imposed, with the -p option. The migration receives the provided script as its command, without replacing the entrypoint of the image. The shared volume is the only mount, and the migration carries no restart policy that would run it again.

Key points

  1. A depends_on in its short form only waits for the container to be launched. It says nothing about the readiness of the program inside, and on a fast machine it may seem to be enough.

  2. A healthcheck runs a command at a regular interval and derives the starting, healthy and unhealthy states from it. The failure detection delay is roughly interval times retries. The start_period grace period does not count the failures of a service that is slow to start, without delaying the healthy state.

  3. A probe tests what the dependent service needs, through the same path as it. On PostgreSQL, pg_isready without an address can answer during initialization: aim at 127.0.0.1.

  4. The long form of depends_on sets a condition: service_healthy for a dependency that must serve, service_completed_successfully for a one-off task that must end with code 0.

  5. A migration is declared as a separate, repeatable service, with ON_ERROR_STOP so that the error becomes a non-zero code. If it fails, the dependent service does not start. A new docker compose up -d runs it again, hence the value of a migration that can be replayed safely.

  6. An unhealthy service is neither restarted nor stopped by Docker: the state is information, read by those who want to use it.

  7. depends_on only protects the startup. A dependency that disappears afterwards is handled in the application, with bounded retries spaced by a pause.

Knowledge check

Check that the essentials of this lesson have stuck. The questions cover only what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

8 questions
8 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn