What Lifecycle Hooks Are For
A plain Watchtower update is: detect new image → stop container → start new container. That's fine for stateless services, but some containers need a step in between — dump a database before it restarts, warm a cache after it comes back up, or notify an external system mid-update. Lifecycle hooks let you run an arbitrary shell command inside the container at four points in that sequence, without needing a separate orchestration tool.
Enabling Lifecycle Hooks
Hooks are off by default. Turn them on for the whole Watchtower instance with an environment variable or CLI flag:
services:
watchtower:
image: containrrr/watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_LIFECYCLE_HOOKS=true
- WATCHTOWER_CLEANUP=true
This is a global switch. Individual containers only run hooks if they carry the hook labels described below — enabling this flag doesn't run anything by itself.
The Four Hook Labels
| Stage | Label | Runs when |
|---|---|---|
| Pre-check | com.centurylinklabs.watchtower.lifecycle.pre-check | Before Watchtower checks this container for an update |
| Pre-update | com.centurylinklabs.watchtower.lifecycle.pre-update | After an update is found, before the container is stopped |
| Post-update | com.centurylinklabs.watchtower.lifecycle.post-update | After the new container has started |
| Post-check | com.centurylinklabs.watchtower.lifecycle.post-check | After the whole update cycle for this container finishes |
Example: Dump and Restore Around an Update
services:
db-app:
image: myapp:latest
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=true"
- "com.centurylinklabs.watchtower.lifecycle.pre-update=/scripts/dump-data.sh"
- "com.centurylinklabs.watchtower.lifecycle.post-update=/scripts/restore-data.sh"
watchtower:
image: containrrr/watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_LIFECYCLE_HOOKS=true
- WATCHTOWER_LABEL_ENABLE=true
The command in each label must exist and be executable inside the target container's image — Watchtower runs it via docker exec-style execution against that container, not on the host.
Hook Timeouts
Each hook has a default 60-second timeout. If your dump or warm-up script legitimately needs longer, override it per hook, in minutes:
labels:
- "com.centurylinklabs.watchtower.lifecycle.pre-update=/scripts/long-dump.sh"
- "com.centurylinklabs.watchtower.lifecycle.pre-update-timeout=5"
- "com.centurylinklabs.watchtower.lifecycle.post-update=/scripts/warm-cache.sh"
- "com.centurylinklabs.watchtower.lifecycle.post-update-timeout=0"
Setting a timeout label to 0 disables the timeout for that hook entirely — use this carefully, since a hung command with no timeout will block that container's update indefinitely.
com.centurylinklabs.watchtower.enable=false) and updating it manually on a maintenance window instead.Frequently Asked Questions
Set WATCHTOWER_LIFECYCLE_HOOKS=true (or pass --enable-lifecycle-hooks) on the Watchtower container. Without this flag, hook labels on your containers are ignored.
com.centurylinklabs.watchtower.lifecycle.pre-check, .pre-update, .post-update, and .post-check. Each takes a shell command to run inside the target container at that stage.
Hooks have a default 60-second timeout. Override it per hook with the pre-update-timeout / post-update-timeout labels (in minutes), or set to 0 to disable the timeout — do this only if you're confident the command can't hang.