↓ Aller au contenu

WUD : suivre les mises à jour de ses conteneurs Docker

Cryptolab.re
Auteur
Cryptolab.re
Cryptolab est un blog personnel où je documente mes expérimentations techniques : infra, self-hosting, réseau, crypto et projets parfois inutiles, souvent instructifs.
Sommaire

Une fois les versions fixées dans Docker Compose, il reste à savoir quand une nouvelle image est disponible. WUD, pour What’s Up Docker, surveille les conteneurs et peut envoyer une notification dès qu’une mise à jour correspond à vos critères : vous gardez ensuite la main sur le déploiement.

Passer des versions épinglées aux mises à jour suivies
#

La suite logique de « Arrêtez latest dans Docker Compose » tient en une question : comment suivre les nouvelles images sans visiter chaque dépôt toutes les semaines ?

WUD est un projet open source sous licence MIT. Un watcher découvre les conteneurs, les connecteurs de registres recherchent les images candidates et les triggers exécutent une action. Cette action peut être une notification, mais aussi un déploiement automatique. La configuration ci-dessous utilise uniquement un déclencheur ntfy pour les alertes. La documentation présente ces trois composants.

WUD distingue deux changements utiles :

Changement dans le registreCe que WUD peut signaler
Un nouveau tag de version est publiéUne version supérieure, selon les règles de comparaison et les filtres configurés
Le contenu associé au même tag changeUn nouveau digest, si sa surveillance est activée

Une alerte indique qu’une image candidate existe. Pour savoir si elle corrige une CVE ou demande une migration, il faut encore consulter les notes de version du projet.

Installer WUD avec des alertes ntfy
#

Cette configuration nécessite un serveur Linux avec Docker Engine, le plugin docker compose et un utilisateur autorisé à administrer Docker. Elle utilise WUD 9.7.0, publiée le 31 octobre 2026, ainsi qu’un serveur ntfy déjà accessible en HTTPS avec un sujet protégé et un jeton autorisé à y publier.

Créez un répertoire dédié :

mkdir -p ~/stacks/wud
cd ~/stacks/wud
umask 077
mkdir -p secrets
chmod 700 secrets

Avec votre éditeur, créez deux fichiers dans secrets/ : wud_admin_password, contenant le mot de passe du compte administrateur, et ntfy_token, contenant le jeton ntfy. Chaque fichier contient uniquement sa valeur. Gardez ce répertoire hors de Git en ajoutant une entrée secrets/ dans .gitignore.

WUD peut lire ses paramètres depuis des fichiers grâce au suffixe __FILE. Le compte administrateur est nécessaire pour initialiser une instance neuve : sans compte existant, identifiants d’initialisation ou administrateur OIDC configuré, WUD refuse de démarrer.

Enregistrez ce fichier sous compose.yaml. Remplacez https://ntfy.example.net par l’adresse de votre serveur et docker-updates par le nom de votre sujet ntfy :

services:
  wud:
    image: getwud/wud:9.7.0
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      TZ: Europe/Paris
      WUD_AUTH_ADMIN_USER: admin
      WUD_AUTH_ADMIN_PASSWORD__FILE: /run/secrets/wud_admin_password
      WUD_WATCHER_LOCAL_SOCKET: /var/run/docker.sock
      WUD_WATCHER_LOCAL_WATCHBYDEFAULT: "false"
      WUD_WATCHER_LOCAL_WATCHATSTART: "true"
      WUD_WATCHER_LOCAL_CRON: "0 */6 * * *"
      WUD_TRIGGER_NTFY_HOMELAB_URL: "https://ntfy.example.net"
      WUD_TRIGGER_NTFY_HOMELAB_TOPIC: docker-updates
      WUD_TRIGGER_NTFY_HOMELAB_AUTH_TOKEN__FILE: /run/secrets/ntfy_token
      WUD_TRIGGER_NTFY_HOMELAB_AUTO: "true"
      WUD_TRIGGER_NTFY_HOMELAB_ONCE: "true"
      WUD_TRIGGER_NTFY_HOMELAB_THRESHOLD: all
      WUD_TRIGGER_NTFY_HOMELAB_SIMPLETITLE: 'Image disponible pour $${container.name}'
      WUD_TRIGGER_NTFY_HOMELAB_SIMPLEBODY: >-
        $${container.watcher} : $${container.updateKind.localValue}
        -> $${container.updateKind.remoteValue}
        $${container.result && container.result.link ? container.result.link : ''}
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - wud_store:/store
    secrets:
      - wud_admin_password
      - ntfy_token
    labels:
      wud.watch: "true"
      wud.tag.include: '^[0-9]+\.[0-9]+\.[0-9]+$$'
      wud.link.changelog: 'https://github.com/getwud/wud/releases/tag/$${major}.$${minor}.$${patch}'

volumes:
  wud_store:

secrets:
  wud_admin_password:
    file: ./secrets/wud_admin_password
  ntfy_token:
    file: ./secrets/ntfy_token

WATCHBYDEFAULT=false limite le suivi aux conteneurs portant wud.watch=true. WUD se surveille donc lui-même dès le départ. Le watcher Docker vérifie les images au démarrage, puis toutes les six heures, avec un léger décalage aléatoire par défaut. Il observe les conteneurs en cours d’exécution ; ajoutez WUD_WATCHER_LOCAL_WATCHALL: "true" si vous souhaitez inclure ceux qui sont arrêtés.

Le déclencheur ntfy.homelab envoie automatiquement les alertes. AUTO=true concerne ici l’envoi de notifications. Aucun déclencheur de mise à jour Docker ou Docker Compose n’est configuré. ONCE=true évite de renvoyer la même alerte à chaque passage. Le volume /store conserve la base de données de WUD, dont son état et son historique.

Le montage du socket donne à WUD un accès à l’API Docker. Son suffixe :ro ne filtre pas les opérations autorisées par cette API : sur un daemon classique exécuté en root, cet accès permet d’administrer l’hôte. C’est la raison du port limité à localhost et de l’authentification. Docker détaille les pouvoirs associés au contrôle du daemon.

Vérifiez d’abord la syntaxe et la cohérence du fichier :

docker compose config -q

La commande doit terminer sans erreur. Elle valide le modèle Compose, sans vérifier les identifiants ntfy ni la connexion au socket. Lancez ensuite WUD :

docker compose pull wud
docker compose up -d wud
docker compose logs --tail=100 wud

Ouvrez http://127.0.0.1:3000 sur le serveur et connectez-vous avec admin et le mot de passe enregistré. Depuis un poste distant, un tunnel SSH permet de conserver cette écoute locale. Remplacez admin@IP_DU_SERVEUR par votre utilisateur SSH et l’adresse du serveur :

ssh -N -L 3000:127.0.0.1:3000 admin@IP_DU_SERVEUR

L’interface est alors accessible à la même adresse dans le navigateur du poste distant.

Choisir les conteneurs et les versions proposés
#

Les variables WUD_* se placent sur WUD. Les labels wud.* se placent sur les services à surveiller, dans leurs propres fichiers Compose. Le watcher voit les conteneurs du même daemon, même s’ils appartiennent à d’autres projets Compose.

Voici un bloc labels à ajouter à un service Traefik existant, en conservant son image épinglée, ses ports et ses volumes. Fusionnez-le avec les labels déjà présents :

labels:
  wud.watch: "true"
  wud.tag.include: '^v[0-9]+\.[0-9]+\.[0-9]+$$'
  wud.link.changelog: 'https://github.com/traefik/traefik/releases/tag/v$${major}.$${minor}.$${patch}'

Ce filtre retient les tags au format vX.Y.Z, sans suffixe de préversion ni variante d’image. Le lien pointe vers les notes de la version proposée. Les labels de filtrage et de liens permettent de préciser ces règles par conteneur.

Après modification des labels, il faut recréer le conteneur pour que Docker les expose à WUD. Cette opération interrompt brièvement le service. Dans le répertoire de sa stack, si le service s’appelle proxy :

docker compose up -d --no-deps proxy

Pour des tags sans préfixe, au format X.Y.Z, ces expressions définissent les candidats à la comparaison de versions. Les valeurs sont écrites telles qu’elles doivent apparaître dans le fichier Compose, entre apostrophes :

PolitiqueValeur de wud.tag.include
Voir toutes les versions stables à trois nombres'^[0-9]+\.[0-9]+\.[0-9]+$$'
Rester sur la majeure actuellement déployée'^$${major}\.[0-9]+\.[0-9]+$$'
Rester sur la même majeure et la même mineure'^$${major}\.$${minor}\.[0-9]+$$'
Garder une variante portant exactement le suffixe -alpine'^[0-9]+\.[0-9]+\.[0-9]+-alpine$$'

WUD remplace ${major} et ${minor} par les nombres du tag installé. Avec un tag 1.4.2, la troisième règle accepte 1.4.3 et écarte 1.5.0. Ces numéros illustrent le filtre, sans désigner une release particulière. Les tags latest ou stable ne fournissent pas de version numérique pour ces variables.

Les doubles dollars sont nécessaires pour transmettre les variables à WUD : Compose interprète lui aussi les dollars. $$ devient un dollar littéral ; cela vaut aussi pour l’ancre de fin de l’expression régulière. Les apostrophes YAML conservent les antislashs.

Un filtre limité à une majeure masque les majeures suivantes, y compris dans les alertes. Pour un service dont vous voulez suivre toute l’actualité, gardez le filtre sur toutes les versions stables et choisissez la cible au moment du déploiement. wud.tag.exclude permet aussi de retirer des tags d’une sélection plus large, notamment certaines variantes publiées par le projet.

Le seuil du déclencheur intervient ensuite. Pour les changements de tag interprétés en SemVer, THRESHOLD=patch écarte les sauts mineurs et majeurs des notifications ; il ne change pas les images candidates affichées par WUD. Garder THRESHOLD=all permet de recevoir les alertes de versions et de digests retenues par les filtres.

Détecter une reconstruction derrière le même tag
#

Un tag de version peut lui aussi changer de contenu. Pour détecter une reconstruction de l’image sans changement de numéro, ajoutez ce label aux autres labels du service suivi :

labels:
  wud.watch.digest: "true"

WUD compare alors le digest local au digest distant. Cela permet de repérer une image reconstruite avec des paquets de base corrigés. Une différence de digest établit un changement de contenu, pas sa raison. La documentation explique ce suivi.

Cette vérification demande des requêtes supplémentaires au registre. En cas d’erreurs 429 sur Docker Hub, espacez les passages ou configurez l’authentification au registre avec un jeton en lecture seule. Les paramètres sont WUD_REGISTRY_HUB_PUBLIC_LOGIN et WUD_REGISTRY_HUB_PUBLIC_PASSWORD, ce dernier pouvant aussi utiliser le suffixe __FILE. Un docker login effectué sur l’hôte ne remplace pas cette configuration dans WUD.

Vérifier les alertes avant de compter dessus
#

Dans l’interface, vérifiez que WUD apparaît, ainsi que les autres conteneurs sélectionnés après application de leurs labels. Le bouton Watch now lance une vérification sans attendre le prochain passage planifié.

Pour vérifier le canal ntfy sans attendre une nouvelle release, ouvrez Configuration > Triggers, sélectionnez le déclencheur homelab de type ntfy, puis utilisez Test. Abonnez votre client ntfy au sujet configuré, avec un compte autorisé à le lire, et vérifiez la réception du message. Cette fonction de test existe dans l’interface de WUD 9.7.0.

Ce test valide l’envoi. Pour valider toute la chaîne, choisissez ensuite un conteneur pour lequel une version supérieure existe dans le registre, vérifiez qu’elle respecte le filtre, puis lancez Watch now. La version candidate doit apparaître dans WUD et produire une notification lors de sa première détection. Avec ONCE=true, les passages suivants ne renvoient pas cette même alerte. Un conteneur déjà à jour ne provoque pas d’alerte simplement parce que le scan fonctionne.

Si un conteneur manque, cette commande en lecture seule permet de vérifier les labels effectivement présents sur le daemon :

docker ps --filter label=wud.watch=true \
  --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'

Un service absent de cette liste n’a pas le label attendu sur son conteneur en cours d’exécution. S’il est présent mais sans résultat exploitable, consultez les logs de WUD : un refus du socket, une erreur d’authentification au registre ou une limitation de requêtes demandent des corrections différentes. L’absence d’alerte ne suffit donc pas à conclure que toutes les images sont à jour.

Déployer la version choisie dans Compose
#

À réception d’une alerte, consultez les notes de version, puis modifiez explicitement la référence image: dans le dépôt de la stack concernée. Si elle contient @sha256:..., mettez également à jour le digest pour désigner le contenu choisi. Avant une migration de données, conservez une sauvegarde restaurable avec la version précédente.

Depuis le répertoire de cette stack, vérifiez la configuration puis téléchargez l’image du seul service choisi. Ici encore, proxy représente le nom du service dans Compose :

docker compose config -q
docker compose pull proxy

pull télécharge l’image sans démarrer de conteneur. La commande suivante recrée le service avec la nouvelle référence et interrompt ses connexions le temps du remplacement :

docker compose up -d --no-deps proxy

Vérifiez ensuite son état, ses logs et une requête réelle à l’application :

docker compose ps proxy
docker compose logs --tail=100 proxy

--no-deps évite de démarrer les services liés. Si la release exige aussi une évolution de base de données ou d’un autre composant, préparez cette opération dans la procédure de mise à jour.

Pour revenir en arrière, rétablissez la référence précédente dans Compose et recréez le service avec la même commande. Si la mise à jour a modifié le format des données de façon incompatible, revenir à l’ancienne image ne suffit pas : la restauration de la sauvegarde fait alors partie du retour arrière.

Sources
#

Articles connexes

Docker Compose : sortir les secrets de .env avec SOPS et age

··3201 mots·16 mins
Un fichier .env ignoré par Git reste un fichier en clair. Dans une stack Docker Compose, il finit facilement dans une copie de migration, une sauvegarde, une archive de support ou, un jour, l’historique du dépôt. SOPS et age permettent de corriger une partie du problème sans déployer un gestionnaire de secrets centralisé. Le principe est simple : le dépôt contient un fichier chiffré, les clés privées restent hors de Git et le serveur ne déchiffre que les valeurs nécessaires au déploiement. Docker Compose reçoit ensuite ces valeurs sous forme de fichiers montés dans /run/secrets/. Les mots de passe ne passent plus par environment: et ne finissent plus dans la configuration du conteneur. Cette méthode convient bien à un homelab, à quelques VPS ou à une petite infrastructure pilotée par Git. SOPS reste un outil de chiffrement de fichiers, pas un gestionnaire de secrets centralisé, et ne protège pas un hôte déjà compromis.

Docker Compose : arrêtez de mettre latest dans vos fichiers

Le tag latest est confortable. On écrit un compose.yml, on lance docker compose up -d, et le service démarre. Pas besoin de choisir une version de PostgreSQL, Redis, Traefik, Gitea, Vaultwarden ou n’importe quelle application auto-hébergée. Le problème, c’est que latest ne veut pas dire “dernière version stable adaptée à mon environnement”. Ça veut seulement dire : “ce tag pointe vers quelque chose dans le registre au moment où Docker le résout”. Ce quelque chose peut changer sans que votre fichier Compose change. Pour une stack de test, ce n’est pas très grave. Pour une base de données, un reverse proxy exposé, un service d’authentification ou une application avec des volumes persistants, c’est une mauvaise convention d’exploitation. Le vrai sujet n’est pas Docker. C’est la reproductibilité.