Watchtower es un contenedor que vigila las imágenes de tus otros contenedores: cuando se publica una versión nueva de la misma etiqueta (por ejemplo nginx:1.28), descarga la imagen y recrea el contenedor con la misma configuración. En este tutorial instalarás Watchtower con Docker Compose en Ubuntu 24.04 y lo configurarás de forma prudente: solo actualizará los contenedores que marques, lo hará en una ventana horaria fija, borrará las imágenes antiguas y te avisará de cada cambio.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
  • Un usuario no root con privilegios sudo que pertenezca al grupo docker.
  • Docker Engine y el plugin Docker Compose instalados desde el repositorio oficial de Docker.
  • Opcional: un webhook de Discord o Slack, o una cuenta SMTP, para recibir avisos.

Qué imagen de Watchtower usar

El proyecto original, containrrr/watchtower, dejó de mantenerse y su repositorio está archivado. Además, su cliente usa una versión antigua de la API de Docker que las versiones recientes de Docker Engine ya no aceptan, por lo que falla con un error del tipo client version 1.25 is too old.

En este tutorial se usa la bifurcación mantenida nickfedor/watchtower, que conserva las mismas variables de entorno, opciones y etiquetas. Si ya tienes containrrr/watchtower en marcha, puedes migrar cambiando únicamente el nombre de la imagen.

Cuándo conviene (y cuándo no) actualizar automáticamente

Watchtower solo sigue la etiqueta con la que arrancaste el contenedor. Eso te permite decidir cuánto riesgo asumes:

Etiqueta de la imagenQué actualizará WatchtowerRecomendación
nginx:latestCualquier versión nueva, incluidas las mayoresEvítala en producción
nginx:1.28Parches de la rama 1.28Buen equilibrio
nginx:1.28.0Nada, la etiqueta no cambiaControl manual

Las bases de datos (PostgreSQL, MySQL, Redis con persistencia) son malas candidatas a la actualización automática: una versión mayor puede requerir una migración de datos. Déjalas fuera o en modo solo aviso, como verás en el paso 5.

Paso 1: Arrancar un contenedor de ejemplo

Para tener algo que actualizar, crea un proyecto con Nginx. La etiqueta com.centurylinklabs.watchtower.enable=true marca el contenedor como actualizable; en el paso siguiente configurarás Watchtower para que ignore todo lo que no la lleve.

sudo mkdir -p /opt/web
sudo chown "$USER": /opt/web
nano /opt/web/compose.yaml
services:
  web:
    image: nginx:stable-alpine
    container_name: web
    restart: unless-stopped
    ports:
      - "8080:80"
    labels:
      - "com.centurylinklabs.watchtower.enable=true"

Arráncalo:

cd /opt/web
docker compose up -d

Comprueba que la etiqueta está aplicada:

docker inspect web --format '{{ index .Config.Labels "com.centurylinklabs.watchtower.enable" }}'
true

Paso 2: Instalar Watchtower con Docker Compose

Crea un directorio propio para Watchtower:

sudo mkdir -p /opt/watchtower
sudo chown "$USER": /opt/watchtower
nano /opt/watchtower/compose.yaml
services:
  watchtower:
    image: nickfedor/watchtower:latest
    container_name: watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      TZ: Europe/Madrid
      WATCHTOWER_SCHEDULE: "0 0 4 * * *"
      WATCHTOWER_LABEL_ENABLE: "true"
      WATCHTOWER_CLEANUP: "true"
      WATCHTOWER_TIMEOUT: "30s"

Qué hace cada variable:

  • TZ: zona horaria en la que se interpreta el horario. Cámbiala por la tuya.
  • WATCHTOWER_SCHEDULE: expresión cron de seis campos, el primero son los segundos. 0 0 4 * * * significa todos los días a las 04:00:00.
  • WATCHTOWER_LABEL_ENABLE: solo actualiza contenedores con la etiqueta com.centurylinklabs.watchtower.enable=true. Sin esta opción, Watchtower actualizaría todos los contenedores del servidor.
  • WATCHTOWER_CLEANUP: borra la imagen antigua después de actualizar, para que no se acumulen en disco.
  • WATCHTOWER_TIMEOUT: tiempo que se da a cada contenedor para detenerse limpiamente antes de forzarlo.

Watchtower necesita el socket de Docker para detener y recrear contenedores.

Arranca Watchtower:

cd /opt/watchtower
docker compose up -d

Comprueba en el registro que ha aplicado el horario y el filtro por etiquetas:

docker logs watchtower
level=info msg="Watchtower v1.x.x"
level=info msg="Using no notifications"
level=info msg="Only checking containers with enable label"
level=info msg="Scheduling first run: 2026-09-26 04:00:00 +0200 CEST"

Los textos exactos pueden variar según la versión; lo importante es que aparezcan el filtro por etiqueta y la fecha de la primera ejecución.

Paso 3: Probar una ejecución inmediata

No hace falta esperar a las 04:00 para comprobar que todo funciona. Lanza una ejecución única en un contenedor temporal con --run-once, que revisa los contenedores marcados, actualiza si hay imagen nueva y termina:

docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  nickfedor/watchtower --run-once --label-enable --cleanup
level=info msg="Watchtower v1.x.x"
level=info msg="Running a one time update."
level=info msg="Session done" Failed=0 Scanned=1 Updated=0 notify=no

Scanned=1 indica que ha encontrado el contenedor web. Updated=0 es lo normal si la imagen ya estaba al día. Si aparece Scanned=0, el contenedor no tiene la etiqueta o está parado.

Para ver en detalle qué compara, añade --debug al comando anterior.

Paso 4: Recibir avisos de las actualizaciones

Watchtower envía notificaciones mediante URLs de la librería Shoutrrr, que admite Discord, Telegram, Slack, correo SMTP y otros servicios. Solo necesitas la variable WATCHTOWER_NOTIFICATION_URL.

Algunos formatos habituales:

ServicioURL
Discorddiscord://TOKEN@WEBHOOK_ID
Telegramtelegram://TOKEN_DEL_BOT@telegram?chats=ID_DEL_CHAT
Correo SMTPsmtp://usuario:contraseñ[email protected]_domain:587/?from=watchtower@your_domain&to=admin@your_domain

En un webhook de Discord con la forma https://discord.com/api/webhooks/123456789/AbCdEf, el WEBHOOK_ID es 123456789 y el TOKEN es AbCdEf.

Para no dejar el secreto dentro del compose.yaml, guárdalo en un fichero .env en el mismo directorio:

nano /opt/watchtower/.env
WATCHTOWER_NOTIFICATION_URL=discord://AbCdEf@123456789

Restringe sus permisos:

chmod 600 /opt/watchtower/.env

Añade la variable al bloque environment de /opt/watchtower/compose.yaml:

      WATCHTOWER_NOTIFICATION_URL: ${WATCHTOWER_NOTIFICATION_URL}

Recrea el contenedor:

cd /opt/watchtower
docker compose up -d

Al arrancar, Watchtower envía un mensaje de inicio al canal configurado; si lo recibes, las notificaciones funcionan. A partir de ahí recibirás un aviso cada vez que actualice un contenedor o falle al hacerlo. Si no llega nada, busca errores de notificación con docker logs watchtower.

Paso 5: Vigilar sin actualizar

Para contenedores delicados, como una base de datos, puede interesarte saber que hay una imagen nueva sin que Watchtower la aplique. Para eso existe la etiqueta com.centurylinklabs.watchtower.monitor-only. Un ejemplo con PostgreSQL:

services:
  db:
    image: postgres:17
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
      - "com.centurylinklabs.watchtower.monitor-only=true"

Con estas dos etiquetas, Watchtower revisa el contenedor en cada ejecución y te notifica si hay una imagen nueva, pero no la descarga ni reinicia el contenedor. La actualización la haces tú a mano, cuando tengas una copia de seguridad reciente:

docker compose pull db
docker compose up -d db

Si prefieres que Watchtower no actualice nada en todo el servidor y solo avise, añade WATCHTOWER_MONITOR_ONLY: "true" al bloque environment de Watchtower.

Paso 6: Imágenes de registros privados

Si alguno de tus contenedores usa una imagen de un registro privado (GitHub Container Registry, un Harbor propio, etc.), Watchtower necesita las mismas credenciales que usa Docker. Inicia sesión en el registro como root, que crea /root/.docker/config.json:

sudo docker login ghcr.io

Monta ese fichero en Watchtower añadiendo esta línea a volumes: en /opt/watchtower/compose.yaml:

      - /root/.docker/config.json:/config.json:ro

Aplica el cambio con docker compose up -d y ejecuta de nuevo la prueba del paso 3 con el mismo montaje para verificar que puede consultar el registro.

Solución de problemas

  • client version 1.25 is too old. Minimum supported API version is 1.44: estás usando la imagen archivada containrrr/watchtower con un Docker Engine reciente. Cambia la imagen a nickfedor/watchtower.
  • Scanned=0 en cada ejecución: el filtro por etiquetas está activo y ningún contenedor tiene com.centurylinklabs.watchtower.enable=true. Recuerda que añadir una etiqueta en Compose requiere recrear el contenedor con docker compose up -d.
  • Watchtower nunca se ejecuta: el horario tiene cinco campos en lugar de seis. 0 4 * * * no significa las 04:00 en Watchtower; usa 0 0 4 * * *.
  • Un contenedor se actualizó y ya no arranca: vuelve a la versión anterior fijando una etiqueta concreta en su compose.yaml (por ejemplo nginx:1.28.0-alpine) y ejecuta docker compose up -d. Para evitarlo, usa etiquetas de rama en lugar de latest.

Conclusión

Watchtower mantiene al día los contenedores que marques, a la hora que elijas y avisándote de cada cambio, mientras las bases de datos quedan en modo solo aviso. Como siguientes pasos, programa copias de seguridad de tus volúmenes antes de la ventana de actualización, revisa las etiquetas de imagen de tus servicios para fijar ramas estables y añade comprobaciones de salud (healthcheck) en Compose para detectar pronto un contenedor que no arranque tras actualizarse.