blog
Monitoring docker image upgrades
Running containers also means keeping their images up to date. While it's quite easy to find out which Linux OS packages need upgrading, periodic checks for docker images aren't that easy, so I wrote a small Icinga/Nagios plugin which gives alerts when new images are available.
The script was written for Docker Compose. Since Docker Compose itself
provides no simple way to determine whether a newer image exists, it is
necessary to execute docker pull for the images being monitored.
In order to not mess with the running configuration,
it is therefore required to “pin” the current image in the
corresponding docker-compose.yaml file, which can be
achieved by substituting the tag for the image: with a specific
@sha256: digest:
docker-compose.yaml
services:
db:
image: mariadb@sha256:628f228f0fd5913a2204386…
x-upgrade-data:
image-source-tag: mariadb:12
This will ensure that only the currently chosen image is run upon
(re)starting containers, which wouldn't be the case if for
example a latest tag would be used.
Also, the script needs to know for which tag the docker pull
operation should be
run. This is provided by the image-source-tag in a
x-upgrade-data section.
This might be <image-name>:latest
for the latest image, but could also be a tag like 2.3
or similar, to ensure that we're running a specific minor version
and are not doing a major version upgrade. In the example above,
the docker pull will get the most current mariadb image for version 12
(please check the tag format for your images since the tag syntax is not
fixed).
The script itself is shown below and also directly available from https://github.com/it-sys-ce/docker-compose-images-upgrade.
docker-compose-images-upgrade.sh
#!/bin/bash
# 2026-07-03 - christoph.ender@it-sys-ce.de - Version 1.0, requires "yq".
if [[ $# -ne 1 && $# -ne 4 ]]
then
echo "Syntax:"
echo " ${0} <compose-dir>"
echo " ${0} <compose-dir> <registry> <registry-username> <registry-password>"
exit 1
fi
if [[ $# -eq 4 ]]
then
REGISTRY=${2}
REGISTRY_USERNAME=${3}
REGISTRY_PASSWORD=${4}
docker login \
-u ${REGISTRY_USERNAME} \
-p ${REGISTRY_PASSWORD} \
${REGISTRY} >/dev/null 2>&1
fi
COMPOSE_DIR=${1}
CONTAINERS=$(docker compose \
--project-directory "${COMPOSE_DIR}" ps --format "{{.Service}}\t{{.Name}}")
IMAGE_LIST=
NEW_IMAGE_LIST=
UPDATE_FOUND=0
while read -r CONTAINER_LINE
do
IFS=$'\t' read -r SERVICE_NAME CONTAINER_NAME <<< "${CONTAINER_LINE}"
# Parse image name
IMAGE=$(docker inspect "${CONTAINER_NAME}" \
--format "{{.Config.Image}}" | sed 's/:[^:]*$//' | sed 's/@[^@]*$//')
IMAGE_SOURCE_TAG=$(docker compose --project-directory "${COMPOSE_DIR}" config \
| yq -r .\"services\".\"${SERVICE_NAME}\".\"x-upgrade-data\".\"image-source-tag\")
# Get hash of currently running container image.
RUNNING_HASH=$(docker inspect "${CONTAINER_NAME}" --format "{{.Image}}")
# Update docker images with latest image.
docker pull "${IMAGE_SOURCE_TAG}" --quiet >/dev/null 2>&1
# Get hash of latest image.
LATEST_HASH=$(docker images --no-trunc --quiet ${IMAGE_SOURCE_TAG})
LATEST_DIGEST=$(docker image inspect \
--format '{{index .RepoDigests 0}}' ${LATEST_HASH})
# Check if currently running image is not the latest.
if [[ ${RUNNING_HASH} != ${LATEST_HASH} ]]
then
UPDATE_FOUND=1
# Append to "new image" list.
NEW_IMAGE_LIST="${NEW_IMAGE_LIST:+$NEW_IMAGE_LIST$'\n'}${IMAGE_SOURCE_TAG} - ${LATEST_DIGEST}"
else
# Append to "current image" list.
IMAGE_LIST="${IMAGE_LIST:+$IMAGE_LIST$'\n'}${IMAGE_SOURCE_TAG} - ${LATEST_DIGEST}"
fi
done <<<${CONTAINERS}
if [[ ${UPDATE_FOUND} -eq 0 ]]
then
echo -e "OK - Using latest images in ${COMPOSE_DIR}.\n${IMAGE_LIST}"
exit 0
else
if [[ -n $IMAGE_LIST ]]
then
OUTPUT="\nUp to date images:\n${IMAGE_LIST}"
fi
OUTPUT="${OUTPUT}\nNew images available:\n${NEW_IMAGE_LIST}"
echo -e "WARNING - New image(s) available for ${COMPOSE_DIR}.${OUTPUT}"
exit 1
fi