Docker Compose’s restart policies are often misunderstood, leading to services that don’t behave as expected when containers crash or the Docker daemon restarts. The most surprising truth is that restart: always doesn’t guarantee a container will always be running; it only means Docker tries to restart it.
Let’s see this in action. Imagine a simple web service defined in docker-compose.yml:
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "80:80"
restart: always
If you run docker-compose up -d, Nginx starts. Now, let’s simulate a crash. From outside the container, you can kill the Nginx process:
docker exec <container_id> kill 1
Docker’s restart: always policy will detect that the container’s main process has exited and attempt to restart it. You’ll see the container status change from Up to Exited briefly, and then back to Up.
However, what happens if the Docker daemon itself crashes or the host machine reboots? This is where the restart policies truly shine or fail. When the daemon comes back online, it consults the restart policy for each container it was managing.
The core problem people run into is assuming restart: always means "never stop, no matter what." This isn’t true. The policy dictates Docker’s behavior when a container stops.
Here’s the breakdown of the policies:
no: This is the default. The container will not be restarted automatically. If it stops, it stays stopped.on-failure[:max_retries]: The container will be restarted only if it exits with a non-zero exit code (indicating an error). You can optionally specify a maximum number of retries. For example,on-failure:5will try to restart the container at most 5 times.unless-stopped: This is the most common and often the most practical choice for production. The container will be restarted unless it was explicitly stopped by the user (e.g., viadocker stopordocker-compose down). This means it will restart on crashes and on Docker daemon restarts/host reboots, but won’t restart if you intentionally shut it down.always: This policy restarts the container regardless of the exit status, even if it was explicitly stopped. The only way to keep a container stopped withrestart: alwaysis to remove it from the Docker daemon’s management entirely (e.g.,docker rm). This is rarely what you want in practice, as it can lead to unexpected restarts after manual interventions.
When to use which:
For most production services that should be available continuously, unless-stopped is the ideal policy. It ensures your application comes back up after a crash or a host reboot, but respects your explicit commands to stop it.
on-failure is useful for batch jobs or tasks that are expected to complete successfully and then exit. If they fail, you want them retried, but you don’t want them to restart indefinitely if they have a persistent, unrecoverable error.
always is less commonly used in production because it can be disruptive if you need to manually stop a container for maintenance. It might be suitable for specific scenarios where you want a container to always be managed by Docker, and you have separate orchestration or manual processes to handle maintenance stops.
Let’s update our docker-compose.yml to use the recommended unless-stopped:
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "80:80"
restart: unless-stopped
Now, if you run docker-compose up -d, kill the Nginx process inside, or reboot your host machine (and subsequently the Docker daemon), the web container will automatically restart. However, if you run docker-compose down, the container will remain stopped until you explicitly start it again with docker-compose up -d.
The subtle but critical difference between always and unless-stopped lies in how they handle explicit stops. With always, if you docker stop <container_id>, Docker will immediately attempt to restart it. This can be a source of confusion and frustration, as it seems to ignore your command. unless-stopped, on the other hand, respects the explicit stop command, preventing immediate restarts.
If you’re using restart: always and find your containers restarting even after you’ve stopped them manually, you’re likely experiencing this exact behavior. The fix is to change the policy to unless-stopped in your docker-compose.yml and then run docker-compose up -d again to apply the change. The Docker daemon will then stop managing the restart for containers that you have explicitly signaled to stop.
The next logical step after mastering restart policies is understanding how to manage container health and implement more sophisticated restart strategies using external orchestrators like Kubernetes or Docker Swarm.