Intro
Docker restart policies are a core piece of container reliability, but they are also a common source of confusion. Setting the wrong policy can leave a production service down after a reboot, mask application bugs by restarting a failing container indefinitely, or cause a container to restart at the worst possible moment during maintenance. This guide explains how Docker restart policies actually work, walks through common configuration mistakes with concrete examples, and shows you how to validate, roll back, and troubleshoot restart behavior safely.
We'll cover the four policy types, what happens in different failure scenarios (daemon restarts, manual stops, exit codes), and how to choose correctly. You'll see real commands and expected output for inspecting and changing policies, plus a practical testing workflow you can run locally before deploying to production.
Version and Environment Inventory
Before changing any restart policy, confirm your environment and capture the current state. This prevents making changes to the wrong container or assuming behavior from a different Docker version.
Check your Docker version and runtime
Restart policies have been stable for years, but syntax and defaults can vary slightly across Docker Engine, Docker Desktop, and rootless installations. Run:
docker version --format '{{.Server.Version}}'
Expected output example:
24.0.7
Also check the container runtime (e.g., containerd) and storage driver if you are troubleshooting filesystem persistence after restarts:
docker info --format '{{.Driver}} {{.CgroupVersion}}'
Capture current restart policies
List all containers and their restart policies using a custom format:
docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Label "com.docker.compose.project"}}'
To see the exact restart policy of a specific container, use docker inspect:
docker inspect --format '{{.HostConfig.RestartPolicy.Name}} (max retries: {{.HostConfig.RestartPolicy.MaximumRetryCount}})' my-app
Example output:
always (max retries: 0)
For Compose projects, the policy is defined in docker-compose.yml under the restart: key. View all services and their current container state with:
docker compose ps
Confirm data persistence before restart tests
If your container writes stateful data, verify that the data lives in a volume or bind mount, not in the container's writable layer. Otherwise, a restart may lose data.
Inspect mounts for a running container:
docker inspect --format '{{range .Mounts}}{{.Type}} {{.Source}} -> {{.Destination}}{{"\n"}}{{end}}' my-app
Example output:
volume app_data -> /var/lib/app
If you see no mounts and the application writes to /var/lib/app inside the container, that data will be lost when the container is removed (but not when it merely restarts). Always design stateful containers to use volumes or bind mounts.
Understanding Restart Policies
Docker provides four restart policy values:
| Policy | Behavior when container exits | Behavior on daemon restart | Typical use case |
|---|---|---|---|
no | Never restart automatically | Never restarted | One-off jobs, debugging |
on-failure | Restart only if exit code != 0, up to max retries | Restarted only if last exit was failure | Services that should stop on repeated failure |
always | Always restart regardless of exit code | Always restarted, even if manually stopped before daemon restart | Core production services (web, API) |
unless-stopped | Always restart unless the container was manually stopped by user | Restarted unless user had stopped it before daemon restart | Most production services; safer than always |
Key differences:
alwayswill restart a container even if you manually stopped it before the Docker daemon restarts. This can surprise you during maintenance windows.unless-stoppedremembers that you manually stopped the container and will not restart it after a daemon restart. It is the recommended choice for most production workloads.on-failureallows a maximum retry count. Once exceeded, the container stops. This helps avoid infinite restart loops for broken applications.
What triggers a restart?
A restart policy kicks in when the container's main process exits. Docker does not restart a container that is running but unhealthy (unless you use a health check with autoheal, which is separate). The restart policy also does not apply if the container is removed or if the daemon is stopped while the container is still running (the container is stopped normally and then restarted according to policy when daemon starts).
Default policies
docker runwithout--restartdefaults tono.- Docker Compose defaults to
no(unless overridden). - Some orchestrators (e.g., Kubernetes) handle restarts themselves; this article focuses on plain Docker and Compose.
Safe Configuration Path
Now that you understand the policies, follow a controlled process to change one.
Step 1: Identify the target and current policy
Choose a single container and record its current policy using docker inspect as shown earlier. For Compose, note the service name and the project directory.
Step 2: Choose the new policy with justification
Ask:
- Is this a long-running service that should always be up? Use
unless-stopped(preferred) oralways. - Is it a batch job or a short-lived worker? Use
nooron-failurewith a small retry count. - Do I need to cap restart attempts to avoid crash loops? Use
on-failure:5(retry 5 times, then stop).
Example: a web server should restart unless an operator deliberately stopped it for maintenance. So:
services:
web:
image: nginx:1.25
restart: unless-stopped
Step 3: Apply the change
For a running container, you cannot change the restart policy via docker update. Actually, docker update does support --restart since Docker 1.11. So you can update a running container without recreating:
docker update --restart unless-stopped my-app
For a container created with docker run, specify the policy at creation:
docker run -d --name my-app --restart unless-stopped nginx:1.25
For Compose, edit docker-compose.yml and run:
docker compose up -d
This recreates the service with the new policy if the configuration changed.
Step 4: Verify the new policy
Immediately check that the policy is set correctly:
docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' my-app
Expected output: unless-stopped.
Step 5: Run a controlled restart test
Do not just trust the setting; simulate the failure condition. For unless-stopped, stop the container manually and confirm it does not restart on its own:
docker stop my-app
sleep 5
docker ps -a --filter name=my-app --format '{{.Status}}'
Expected output shows the container as Exited (0) and not running. That is correct for a manual stop with unless-stopped. To test automatic restart on failure, kill the main process with a nonzero exit code:
docker kill --signal=SIGKILL my-app
sleep 2
docker ps --filter name=my-app --format '{{.Status}}'
You should see the container Restarting or Up (with a new start time).
Rollback plan
If the new policy causes unexpected behavior (e.g., a restart loop), revert to the previous policy immediately:
- For
docker runcontainers, usedocker update --restart <old-policy> my-app. - For Compose, change
restart:in the YAML and rundocker compose up -dagain. - If the container is stuck in a restart loop, you may need to stop it first:
docker stop my-app(this works even with restart policy unless it'salwaysand the daemon restarts? Actually stopping a container overrides restart policy until daemon restart foralways; forunless-stoppedit persists).
Verification and Diagnostics
After changing a restart policy, verify both the configuration and the actual behavior under failure.
Inspect restart count and last exit
Use docker inspect to see how many times the container has restarted:
docker inspect --format '{{.RestartCount}}' my-app
If the count is climbing rapidly, the container is crash-looping. Investigate the exit code:
docker inspect --format '{{.State.ExitCode}}' my-app
Exit code 0 means clean exit; nonzero indicates error. For on-failure, a high retry count may exhaust the maximum and stop the container permanently.
Check logs for the cause of failure
docker logs my-app --tail 100
Look for stack traces, missing files, or port conflicts. If the application needs a health check, combine restart policy with a Docker healthcheck to avoid restarting a container that is running but unhealthy:
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD curl -f http://localhost/ || exit 1
Then use unless-stopped so Docker restarts only on process failure, not on health check failure (unless you use an orchestrator feature).
Test daemon restart behavior
A common mistake is assuming a container with always will start after a host reboot. Always test by restarting the Docker daemon or the host in a staging environment.
On a Linux server with systemd:
sudo systemctl restart docker
sleep 10
docker ps --filter name=my-app
If the container does not appear, check docker ps -a to see if it is stopped. For unless-stopped, if you had manually stopped it before the daemon restart, it will remain stopped. That is expected.
Failure Modes and Recovery
Several failure modes arise from restart policy misconfiguration. Here are common ones and how to recover.
1. Setting always when unless-stopped is intended
Symptom: You stop a container for maintenance, reboot the server, and the container starts again unexpectedly. This can cause split-brain or resource contention if the service should remain down.
Why: always ignores manual stops across daemon restarts. Docker assumes you want the container running no matter what.
Recovery: Change policy to unless-stopped:
docker update --restart unless-stopped my-app
Then if you need it stopped, stop it before reboot; it will stay stopped.
2. Using on-failure without a limit on a broken app
Symptom: Container constantly restarts, exhausting CPU and filling logs.
Why: on-failure without MaximumRetryCount (or on-failure:0) means unlimited retries. The app may exit with code 1 repeatedly.
Recovery: Set a finite retry count, e.g., 5:
docker update --restart on-failure:5 my-app
Then diagnose the root cause by checking logs while the container is stopped.
3. Forgetting to set a restart policy at all
Symptom: After a host reboot, your service is down.
Why: Default policy is no. The container never restarts automatically.
Recovery: Add --restart unless-stopped (or always) to docker run, or restart: unless-stopped in Compose. Recreate the container.
4. Applying a restart policy to a one-off job
Symptom: A data migration or batch job runs repeatedly after completion because the container exits with code 0 and the policy is always or unless-stopped.
Why: These policies restart regardless of exit code. A job that finishes successfully should not be restarted.
Recovery: Use --restart no or on-failure with retries for jobs. If already running, stop and remove the container, then run a new one with the correct policy.
5. Misunderstanding on-failure and exit code 0
Symptom: You expect on-failure to restart when the app crashes, but it does not restart when the app exits with code 0 after a fatal error (some apps exit 0 even on failure).
Why: on-failure only triggers on nonzero exit codes. If the app interprets a crash as a clean shutdown (exit 0), Docker will not restart it.
Recovery: Fix the application to exit nonzero on fatal errors, or use a wrapper script that maps errors to exit 1. Then use on-failure or unless-stopped.
6. Restart policy conflicts with Compose depends_on
Symptom: In Compose, service A (with restart: always) restarts continuously, causing service B (which depends on A) to also restart because depends_on makes B recreate when A restarts. This creates a cascade.
Why: Compose's default depends_on condition is service_started, not service_healthy. If A crashes, B may start fine but A restarts, and Compose may restart both.
Recovery: Use depends_on with condition: service_healthy and define healthchecks. Limit restart policy on A to on-failure with a finite retry count to prevent infinite loops.
7. Losing data because of volume misconfiguration
Symptom: Container restarts fine but data is missing after a restart (e.g., database files gone).
Why: The data was written to the container's writable layer instead of a volume. A simple restart preserves the writable layer, but if the container is recreated (e.g., by Compose up after a policy change), all data is lost.
Recovery: Redesign with a volume or bind mount. If data is critical, recover from backups. Test persistence by recreating the container deliberately before production.
Operations Checklist
Use this checklist before and after modifying restart policies in production. Assign a single owner for the change and a review date.
- [ ] Owner: Priya Shah, Engineering Lead. Review date: after every change and quarterly audit.
- [ ] Confirm Docker version and runtime are known (
docker version,docker info). - [ ] Capture current restart policy of target container:
docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' <container>. - [ ] Check if container has volumes or bind mounts and that data is persisted outside the container.
- [ ] Choose new policy with documented justification. Example: change
notounless-stoppedfor web service because it must survive host reboots unless deliberately stopped. - [ ] Apply change via
docker update --restartor Compose file edit +docker compose up -d. - [ ] Verify new policy with
docker inspect. - [ ] Run controlled restart tests:
- Manual stop: container should remain stopped for
unless-stopped; should restart foralwaysonly after daemon restart? Actually, manual stop overridesalwaysuntil daemon restart. Test daemon restart separately. - Simulate failure:
docker killthe main process and confirm restart. - Check restart count:
docker inspect --format '{{.RestartCount}}'should increment. - [ ] Check logs for error messages:
docker logs --tail 100. - [ ] Confirm rollback path: know how to revert policy quickly.
- [ ] Document the change in the runbook with timestamp and owner.
- [ ] Schedule a quarterly review of all container restart policies to ensure they still match service requirements.
Common Pitfalls and How to Avoid Them
Beyond the failure modes above, these pitfalls occur frequently:
- Assuming
alwaysis safer thanunless-stopped. In reality,alwayscan cause containers to start after a reboot even when you intended to keep them down for maintenance. Useunless-stoppedfor most services.
- Not testing daemon restart behavior. Many developers only test killing the container process, not rebooting the Docker daemon. Always test both in a staging environment.
- Ignoring exit codes. An application that exits 0 on fatal error defeats
on-failure. Ensure your app follows Unix conventions: nonzero for errors.
- Using
on-failurewith too many retries. If the app fails permanently, a high retry count delays detection. Use a low number (3-5) and monitor logs.
- Changing policy without checking data persistence. If you recreate a container to apply a policy change (e.g., Compose
uprecreates), data in the container's writable layer is lost. Always use volumes for stateful services.
- Not documenting the policy choice. Teams may not know why a policy was set, leading to accidental changes. Add comments in Compose files:
services:
db:
image: postgres:16
restart: unless-stopped # stay up unless manually stopped for maintenance
Conclusion
Docker restart policies are simple in concept but easy to get wrong in production. By understanding the exact behavior of no, on-failure, always, and unless-stopped, and by following a careful change process with verification and rollback, you can ensure your containers are available when they should be and down when you need them down.
Start with a low-risk container in a staging environment. Capture its current policy, apply a change, run the restart tests described here, and document the results. Then extend the process to production. Regularly audit your restart policies as part of operational hygiene, and you will avoid the most common pitfalls that lead to unexpected downtime or uncontrolled restarts.