Running something in a container using Docker or Podman is cool, but maybe you want an automated way to always run the latest container? Using the :latest
tag alone does not to this, that just pulls the latest container at the time. You could have a cronjob that just always pulls the latest containers and restarts the container but then if there’s no update you have an outage for no reason.
It’s not too hard to write a script to pull the latest container and restart the service only if required, then tie that together with a systemd timer.
To restart a container you need to know how it was started. If you have only one container then you could just hard-code it, however it gets more tricky to manage if you have a number of containers. This is where something like runlike can help!
First, start up your container however you need to (OwnTracks recorder, for example).
Next, let’s install runlike
with pip.
sudo pip install runlike
Now, let’s create a simple script that takes one optional argument, the name of a running container. If the argument is omitted, it will default to all containers. The script will check if the latest image is different to the running image, and if so, restart the container using the new image with the same arguments as before (determined by runlike
). If there is no newer image, then it will just leave the running container alone.
Create the script.
cat << \EOF | sudo tee /usr/local/bin/update-containers.sh #!/bin/bash # Abort on all errors, set -x set -o errexit # Get the containers from first argument, else get all containers CONTAINER_LIST="${1:-$(docker ps -q)}" for container in ${CONTAINER_LIST}; do # Get the image and hash of the running container CONTAINER_IMAGE="$(docker inspect --format "{{.Config.Image}}" --type container ${container})" RUNNING_IMAGE="$(docker inspect --format "{{.Image}}" --type container "${container}")" # Pull in latest version of the container and get the hash docker pull "${CONTAINER_IMAGE}" LATEST_IMAGE="$(docker inspect --format "{{.Id}}" --type image "${CONTAINER_IMAGE}")" # Restart the container if the image is different if [[ "${RUNNING_IMAGE}" != "${LATEST_IMAGE}" ]]; then echo "Updating ${container} image ${CONTAINER_IMAGE}" DOCKER_COMMAND="$(runlike "${container}")" docker rm --force "${container}" eval ${DOCKER_COMMAND} fi done EOF
Make the script executable.
sudo chmod a+x /usr/local/bin/update-containers.sh
You can test the script by just running it.
/usr/local/bin/update-containers.sh
Now that you have a script which will check for a new images and update containers, let’s make a systemd service and timer for it. This way you can schedule regular update checks whenever you like. If you want a script for a specific container, just add the container names as arguments to the script.
First, create the service
cat << EOF | sudo tee /etc/systemd/system/update-containers.service [Unit] Description=Update containers After=network-online.target [Service] Type=oneshot ExecStart=/usr/local/bin/update-containers.sh EOF
Next, create the matching timer service (note that the service and timer names need to match).
cat << EOF | sudo tee /etc/systemd/system/update-containers.timer [Unit] Description=Timer for updating containers Wants=network-online.target [Timer] OnActiveSec=24h OnUnitActiveSec=24h [Install] WantedBy=timers.target EOF
Reload systemd to pick up the new service and enable the timer.
sudo systemctl daemon-reload sudo systemctl start update-containers.timer sudo systemctl enable update-containers.timer
You can check the status of the timer and the service using standard systemd tools.
sudo systemctl status update-containers.timer sudo systemctl status update-containers.service sudo journalctl -u update-containers.service
That’s it! Sit back and let your containers be automatically updated for you. If you want to manually update a container, you could just use version tags and manage them separately.