Welcome to the third part of our Docker series. We started with the basics, understanding what Docker is and how to run a simple container with an interactive Bash shell. Then, we escalated things by showing you how to quickly spin up a web server using apache httpd.
If you're new to Docker or missed our earlier posts, I highly recommend checking them out. The foundational knowledge we've built there will certainly help you as we dive into today's topic, Container Lifecycle and Restart Policies.
Docker's ability to manage containers' lifecycles and restart them as needed is one of its most powerful features. It provides reliability, scalability, and helps you maintain your services running smoothly.
Container Lifecycle
So far we've discovered Docker's ability to create isolated environments. But what about the persistence of data? Let's dive into an example that showcases how data is preserved across the life cycle of a Docker container.
Imagine you're working inside an Ubuntu container, and you create a file called testfile
with some content. You then exit the container (stopping the container) but don't delete it. What happens to the file?
1. First, We Create a Container
PS C:\Users\vsurr> docker run --name ubuntu_test -it ubuntu:latest bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
3153aa388d02: Pull complete
Digest: sha256:0bced47fffa3361afa981854fcabcd4577cd43cebbb808cea2b1f33a3dd7f508
Status: Downloaded newer image for ubuntu:latest
This command will create a new Ubuntu container and place us inside a bash shell within the container. If the Ubuntu image is not found locally, Docker will download it.
2. Next, We Make a File Inside the Container
root@5e01e4cd7ac7:/# echo "This is a test file" > testfile
root@5e01e4cd7ac7:/# cat testfile
This is a test file
root@5e01e4cd7ac7:/#
We just created a file named testfile
with the content This is a test file
3. Exiting Without Killing the Container
In Docker, you can detach from the container and leave it running by using the escape sequence CTRL-p CTRL-q
.
- Press
CTRL-p
(hold theCTRL
key and press thep
key) - Then immediately press
CTRL-q
(hold theCTRL
key and press theq
key).
After pressing this sequence, you'll be returned to your host command prompt, and the container will continue to run in the background.
4. Stopping the Container
PS C:\Users\vsurr> docker stop ubuntu_test
ubuntu_test
We've now stopped our container, everything remains exactly how we left it (hopefully, we will see)
5. Checking Containers
PS C:\Users\vsurr> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
PS C:\Users\vsurr> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5e01e4cd7ac7 ubuntu:latest "bash" 2 minutes ago Exited (137) 53 seconds ago ubuntu_test
We can see our stopped container but there are no running containers.
6. Starting the Container Again
PS C:\Users\vsurr> docker start ubuntu_test
ubuntu_test
7. Reconnecting to the Container
PS C:\Users\vsurr> docker exec -it ubuntu_test bash
root@5e01e4cd7ac7:/#
8. Verifying Our File is Still There
root@5e01e4cd7ac7:/# cat testfile
This is a test file
Our file is still there, with the content intact. So what's happening here? The magic lies in how Docker handles the filesystem of a container. When you stop a container, its filesystem remains preserved. Anything you create or modify will still be there when you restart it. It's like stopping and starting a virtual machine.
If you stop and then remove the container using the command docker rm ubuntu_test
, all the data within the container, including our test file, will be gone. This removal deletes the container and all associated files. Once a container is removed, everything inside it is erased, and you start fresh the next time. If you're working with vital data, be mindful of this behaviour, and consider using volumes or other mechanisms for essential persistent storage. We will cover Docker Volumes in the upcoming posts.
Container Restart Policies
Container restart policies determine how Docker should treat containers when they exit. Managing the lifecycle of containers is crucial, and Docker provides several options to control this behaviour.
always
- Always restart the container if it stops, regardless of the reason.unless-stopped
- Restart the container unless it has been explicitly stopped by the user.on-failure
- Restart the container if it exits due to an error (a non-zero exit status)
You can set the restart policy directly in the command line when you run a container (or via a Dockerfile), here is the syntax
docker container run --restart=<policy>
no
restart policy. This means that if a container exits for any reason, whether due to an error or a normal termination, it will not be automatically restarted by Docker.always
This policy always restarts a failed container unless it's been explicitly stopped. This is valuable for critical services that must remain running. When the Docker daemon restarts, the container with this policy will be restarted (even if you have manually stopped the container). For example, if you stop the container with docker stop
and then restart the docker daemon, the container will be restarted.
To demonstrate this, I'm going to spin up a container with the always
policy and attach my terminal to its shell. When I type exit
, the container's main process which is bash
exists so the container also stops. But since we have the always
policy, docker just restarts the container again.
PS C:\Users\vsurr> docker run --name ubuntu_test --restart=always -it ubuntu bash
root@0a57bfa8eb7d:/# [I'm inside the container for like 10 seconds]
root@0a57bfa8eb7d:/# exit
exit
PS C:\Users\vsurr> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0a57bfa8eb7d ubuntu "bash" 17 seconds ago Up 2 seconds ubuntu_test
PS C:\Users\vsurr>
unless-stopped
This policy will restart the container unless it has been explicitly stopped by the user. This means if you restart the Docker daemon or the host machine, the container will be restarted, but it won't be restarted if you manually stop it.
Let me spin up a new web-server, stop it manually and then restart docker. With the unless-stopped
policy, the web-server will not be restarted.
PS C:\Users\vsurr> docker run -d --name web --restart=unless-stopped -p 8080:80 httpd
5bd33af2ddf204ce04cafe1cd50efbe0c6fcedfbcdde7b692a6a8d77e23ce7dd
PS C:\Users\vsurr> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5bd33af2ddf2 httpd "httpd-foreground" 4 seconds ago Up 2 seconds 0.0.0.0:8080->80/tcp web
PS C:\Users\vsurr> docker stop web
web
PS C:\Users\vsurr> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5bd33af2ddf2 httpd "httpd-foreground" 4 minutes ago Exited (0) 3 minutes ago web
As you can see the container remains stopped.
on-failure
With on-failure
policy, the container will be restarted if it exits with a non-zero exit code, indicating an error or abnormal termination. In computer programming, a non-zero exit code usually signifies that the program has terminated because of an error. Optionally, you can limit the number of times the Docker daemon attempts to restart the container using the :max-retries
option.
PS C:\Users\vsurr> docker run -d --name failure-demo --restart=on-failure ubuntu bash -c 'sleep 10; exit 1;'
e50952d916842463855578f3455c86cb1a7b50ea551d144802dbcd8bb817bc48
The shell command bash -c 'sleep 10; exit 1;'
within the container is designed to cause a failure by exiting with a status of 1 after a 10-second pause. As a result, the container will be restarted by Docker according to the on-failure
policy (every 10 seconds)
PS C:\Users\vsurr> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e50952d91684 ubuntu "bash -c 'sleep 10; …" About a minute ago Up 9 seconds failure-demo
PS C:\Users\vsurr>
PS C:\Users\vsurr> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e50952d91684 ubuntu "bash -c 'sleep 10; …" About a minute ago Up 2 seconds failure-demo
on-failure
policy, if you manually stop the container and restart docker, the container will be restarted. By selecting the appropriate policy, you can ensure that your containers behave in alignment with your specific use cases and system requirements.