
A Docker Compose restart policy controls what happens after a container’s main process exits. Choose it according to the service’s job: a completed import, a failed worker, and a web service that should remain available need different behavior.
This guide covers ordinary Docker Engine containers managed with Compose. It explains the four policies, how to apply a change, and how to inspect the result. The example configuration passed local checks; container restarts and Docker daemon recovery have not been run in our test environment.
Set restart directly under the service. The Compose service reference accepts the following values. Quote "no" so it remains a string.
| Policy | After exit code 0 | After a nonzero exit | Typical decision |
|---|---|---|---|
| "no" | Stay stopped | Stay stopped | Inspect failures manually or run a one-time task |
| on-failure:3 | Stay stopped | Retry up to three times | Bound retries for a task that can safely repeat |
| always | Restart | Restart | Keep a service running, including after Docker restarts |
| unless-stopped | Restart | Restart | Keep a service running while retaining an intentional stop |
These exit rules assume the container has started and has not been intentionally stopped by an operator. Omitting restart uses no. An on-failure policy without a retry limit keeps retrying failures; the numeric suffix belongs to that policy.
An intentional stop suppresses immediate automatic restarts. The distinction appears when Docker starts again: always can start the stopped container, while unless-stopped keeps it stopped. on-failure does not provide automatic startup after a daemon restart. Docker’s restart-policy guide describes these cases.
For a web service, unless-stopped is a useful starting choice when maintenance stops should survive a Docker restart. For a batch job, decide whether repetition is safe before enabling retries. A second attempt might duplicate an email, payment request, or export unless the application handles repeated work.
Recovery after a host reboot also depends on Docker itself starting and the host’s data and configuration remaining available. A container policy cannot restore a lost VM. Test host recovery separately from process recovery.
Create a new test directory and save the following as compose.yaml. Use a Docker Engine configured for Linux containers. The two services have no ports or data mounts, use a read-only filesystem, and have networking disabled. They only print a message, wait, and exit.
services:
fails:
image: alpine:3.22
user: "1000:1000"
network_mode: none
read_only: true
command: ["sh", "-c", "echo 'Intentional failure'; sleep 12; exit 1"]
restart: "on-failure:3"
succeeds:
image: alpine:3.22
user: "1000:1000"
network_mode: none
read_only: true
command: ["sh", "-c", "echo 'Intentional success'; sleep 12; exit 0"]
restart: "on-failure:3"
The services differ in their exit status. Both use the same retry limit, so the comparison isolates that difference. The 12-second pause avoids an immediate startup failure; Docker documents a successful-start monitoring condition of at least ten seconds. It is demonstration timing, not an application readiness check.
The image tag is readable but can change. Record a tested image digest when repeatability matters. From this directory, validate the model and then start the disposable project:
docker compose -p restart-demo config --quiet
docker compose -p restart-demo up -d
docker compose -p restart-demo ps --all
config --quiet validates the configuration without running containers. Our checks used Compose v5.1.4 and covered all four policy values, an invalid Boolean value, and a change to one service. The Docker engine was unavailable locally, so the following are expected results to verify, not observed runtime results.
succeeds should finish with exit code 0 and no automatic retry.fails should exit with code 1 and receive at most three automatic retries under this policy.Use ps --all so completed containers remain visible. The -d option keeps the project detached from the terminal.
Run the first command to obtain the fails container ID. Replace CONTAINER_ID in the three inspection commands with that ID; then inspect both services’ logs.
docker compose -p restart-demo ps --all --quiet fails
docker inspect --type container --format '{{json .HostConfig.RestartPolicy}}' CONTAINER_ID
docker inspect --type container --format '{{.RestartCount}}' CONTAINER_ID
docker inspect --type container --format '{{json .State}}' CONTAINER_ID
docker compose -p restart-demo logs --tail 30 fails succeeds
docker inspect reads the existing container. Check the configured policy and retry maximum, the restart count, and the state’s exit code, error, and timestamps. Repeat the ID lookup if Compose recreates the container.
The Engine’s restart reference documents restart counters and backoff. Treat the counter as evidence about this container, rather than a lifetime count for the application. Combine it with timestamps and logs before deciding what failed.
Our Docker logs guide covers log inspection and rotation. In a real incident, preserve the relevant error output before replacing a container. Remove credentials or personal data before sharing diagnostics.
To change the demonstration, set only fails.restart to "no" in the YAML. Then run:
docker compose -p restart-demo config --quiet
docker compose -p restart-demo up -d --no-deps fails
docker compose up applies changed service configuration by recreating the affected container when needed. This starts the demonstration again, now without automatic retries for fails. The recreated container has its own state and counter. --no-deps limits this operation to the selected service; this example has no dependencies.
docker compose restart restarts existing containers but does not apply edited Compose settings. After a configuration change, inspect the resulting container again rather than assuming the YAML and runtime match.
For a real service, plan for the interruption and confirm where durable data lives before recreation. Our Docker volumes guide explains persistence and backup considerations. A restart policy does not replace those preparations.
A failed healthcheck can mark a running container unhealthy without ending its main process. Ordinary Engine restart policies do not react to that status alone. Our Compose healthcheck guide covers probes and readiness. Configure monitoring and any health-based recovery separately.
depends_on.<service>.restart: true has another purpose: it restarts a dependent service after an explicit Compose operation on its dependency. It does not follow every automatic Engine restart. Docker’s startup-order guide explains this alongside service_healthy. Applications still need to handle connections that fail after startup.
deploy.restart_policy belongs to the optional Deploy specification, which defines fields such as condition, delay, attempts, and window. Support depends on the deployment implementation. Do not assume every field behaves identically in local Compose and Swarm, or mix both policy forms without checking the target platform. This example uses service-level restart.
docker compose stop retains containers for later use with docker compose start. To remove this disposable example after inspecting it, run from its directory:
docker compose -p restart-demo down
docker compose down removes the project’s service containers and relevant networks. A removed container cannot restart itself. This example has no persistent volumes; avoid adding volume-removal flags to unrelated projects.
When a real service keeps restarting, start with the failure: an invalid configuration, unavailable dependency, missing file, or resource limit. Check the exit state and logs, correct the cause, apply the change, and verify the application’s actual work. Keep the recovery policy and the alert that tells you recovery failed as separate operational decisions.
Pick one AI, compute, or storage workload and see the difference for yourself. Spin it up in minutes, or let our team map your fastest path to production.