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
sudoque pertenezca al grupodocker. - 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 imagen | Qué actualizará Watchtower | Recomendación |
|---|---|---|
nginx:latest | Cualquier versión nueva, incluidas las mayores | Evítala en producción |
nginx:1.28 | Parches de la rama 1.28 | Buen equilibrio |
nginx:1.28.0 | Nada, la etiqueta no cambia | Control 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 etiquetacom.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.
Advertenciael acceso al socket de Docker equivale a acceso root en el host. Usa solo la imagen indicada y no añadas otras opciones que expongan su API HTTP a Internet.
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:
| Servicio | URL |
|---|---|
| Discord | discord://TOKEN@WEBHOOK_ID |
| Telegram | telegram://TOKEN_DEL_BOT@telegram?chats=ID_DEL_CHAT |
| Correo SMTP | smtp://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.
Notasi usas un gestor de credenciales (
credsStore) enconfig.json, Watchtower no puede leerlas. En ese caso las credenciales tienen que estar directamente en el fichero.
Solución de problemas
client version 1.25 is too old. Minimum supported API version is 1.44: estás usando la imagen archivadacontainrrr/watchtowercon un Docker Engine reciente. Cambia la imagen anickfedor/watchtower.Scanned=0en cada ejecución: el filtro por etiquetas está activo y ningún contenedor tienecom.centurylinklabs.watchtower.enable=true. Recuerda que añadir una etiqueta en Compose requiere recrear el contenedor condocker 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; usa0 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 ejemplonginx:1.28.0-alpine) y ejecutadocker compose up -d. Para evitarlo, usa etiquetas de rama en lugar delatest.
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.
