Keep your containers up to date – Watchtower

Using Watchtower, you can effortlessly update the active version of your containerized application by pulling a fresh image from the Docker Hub or your custom image registry. Watchtower will fetch the updated image, gracefully halt the current container, and then restart it with the same parameters that were applied during its initial deployment. In this tutorial, we’ll approach updating running containers in two ways: by running the Watchtower as a docker container and running it via the shell script.

Running Watchtower in a container

docker run \
--name=watchtower \
--env=WATCHTOWER_MONITOR_ONLY=true \
--env='WATCHTOWER_NOTIFICATION_URL=ntfy://ntfy.sh/your-ntfy-channel' \
--env=WATCHTOWER_NOTIFICATIONS=shoutrrr \
--env=TZ=Europe/Warsaw \
--env=WATCHTOWER_LIFECYCLE_HOOKS=1 \
--volume=/var/run/docker.sock:/var/run/docker.sock \
--workdir=/ \
--restart=unless-stopped \
--detach=true docker.io/containrrr/watchtower:latest

This Docker run command is creating and starting a Docker container for Watchtower. Let’s break down the components of the command:

  1. docker run: Initiates the creation and execution of a Docker container.
  2. --name=watchtower: Assigns the name “watchtower” to the Docker container.
  3. --env=WATCHTOWER_MONITOR_ONLY=true: Sets the environment variable WATCHTOWER_MONITOR_ONLY to true, indicating that Watchtower should only monitor for updates but not perform automatic updates. Set the value to false if you want automatic updates of your containers.
  4. --env='WATCHTOWER_NOTIFICATION_URL=ntfy://ntfy.sh/your-ntfy-channel': Specifies the notification URL for Watchtower updates. In this case, it is set up to use the Ntfy service with a custom channel. Read more about the notification possibilities here: https://containrrr.dev/watchtower/notifications/
  5. --env=WATCHTOWER_NOTIFICATIONS=shoutrrr: Sets the method of notifications to be used when there are updates. Here, it is configured to use the Shoutrrr notification service.
  6. --env=TZ=Europe/Warsaw: Sets the time zone inside the container to Europe/Warsaw. This is important for applications that rely on accurate time information.
  7. --env=WATCHTOWER_LIFECYCLE_HOOKS=1: Enables Watchtower lifecycle hooks, allowing for additional actions to be performed before or after the update process.
  8. --volume=/var/run/docker.sock:/var/run/docker.sock: Mounts the Docker socket from the host into the container. This allows Watchtower to communicate with the Docker daemon on the host and manage other containers.
  9. --workdir=/: Sets the working directory within the container to the root directory.
  10. --restart=unless-stopped: Configures the container to restart automatically unless explicitly stopped.
  11. --detach=true: Runs the container in detached mode, allowing it to operate in the background.
  12. docker.io/containrrr/watchtower:latest: Specifies the Docker image to be used for creating the container. The :latest tag indicates the use of the latest version of the Watchtower image.

WATCHTOWER_LIFECYCLE_HOOKS is an environment variable used with Watchtower, a container management tool designed to automatically update running Docker containers to their latest versions. When the WATCHTOWER_LIFECYCLE_HOOKS variable is set, it enables Watchtower to execute lifecycle hooks during the update process. These hooks allow for additional actions to be performed before or after the update operation.

Lifecycle hooks provide a way to customize the behavior of Watchtower according to specific needs or requirements. By setting the WATCHTOWER_LIFECYCLE_HOOKS variable, you signal to Watchtower that it should consider and execute any defined hooks during the update process.

For example, you might use lifecycle hooks to perform tasks such as:

  1. Pre-update tasks: Execute commands or scripts before the container is updated. This could involve stopping certain services, creating backups, or any other preparation steps.
  2. Post-update tasks: Perform actions after the container has been updated. This might include restarting dependent services, logging the update event, or any other activities needed to ensure a smooth transition.

Running Watchtower in a script (cron)

You can use a helper script I’ve created, that will run Watchower and then send the notification about updated containers to the Apprise endpoint. Apprise allows you to send a notification to almost all of the most popular notification services available to us today such as: email, Telegram, Discord, Slack, Amazon SNS, Gotify, etc.

#!/bin/zsh
#
# Docker Update Check and Notification Script
#
# This script checks for updates in Docker containers using watchtower,
# and sends a notification via Apprise if updates are found. The notification
# includes the number of updated containers and the detailed output from watchtower.
#
# Make sure to set your Apprise notification service URL (APPRISE_URL) before running this script.
#

# Set your Apprise notification service URL
APPRISE_URL="https://your-apprise-url/notify/apprise"

# Specify the watchtower image
WATCHTOWER_IMAGE="docker.io/containrrr/watchtower:latest"

# Function to check for updates and send notification
check_and_notify_updates() {
    local updated_containers
    local hostname=$(hostname)

    # Function to check for updates and update containers
    check_and_update_containers() {
        echo "Checking for updates..."
        updated_containers=$(docker run --name watchtower --rm -v /var/run/docker.sock:/var/run/docker.sock $WATCHTOWER_IMAGE --run-once 2>&1)
        echo "watchtower output: $updated_containers"
    }

    # Function to send notification using curl
    send_notification() {
        local title="$1"
        local body="$2"
        curl -k -X POST -F "title=$title" -F "body=$body" "$APPRISE_URL"
    }

    # Main execution
    check_and_update_containers

    # Extract the number of updated containers from the output
    num_updated_containers=$(echo "$updated_containers" | grep -oP 'Updated=\K\d+')

    # Check if there were updates
    if [ "$num_updated_containers" -gt 0 ]; then
        echo "Updates found. Sending notification..."
        send_notification "[$hostname] Docker: $num_updated_containers containers updated" "Watchtower output:\n$updated_containers"
    else
        echo "No updates found."
    fi
}

# Call the function
check_and_notify_updates

The script can be used in cron, for example. Save the above script in a docker_update.zsh file (I’m assuming your shell is zsh, because why would you still be using bash?) and then execute crontab -e, then add the following line:

0 3 * * * /home/your-user/scripts/docker_update.zsh

This will check and update your containers every night at 3:00 AM. This is how the output of the script looks like, if you configure Apprise to send notifications to your Gmail inbox:

Leave a Reply

Your email address will not be published. Required fields are marked *