Lifecycle Hooks
Watchtower’s lifecycle hooks are a feature that allows monitored containers to execute custom commands at specific points during the container update process. These hooks leverage Docker’s exec API to run commands inside containers.
Overview
Lifecycle hooks enable containers to perform custom actions such as:
- Graceful shutdown procedures
- Database backups or migrations
- Configuration validation
- Notification systems
- Cleanup operations
Hook Types
Watchtower supports four distinct lifecycle hook types that execute at different stages of the update process:
| Hook Type | Description | Execution Timing |
|---|---|---|
| Pre-check | Executed for each filtered container before the update cycle begins | Per container, before scanning containers |
| Pre-update | Executed before stopping the old container | Per container, immediately before stopping |
| Post-update | Executed after starting the new container | Per container, immediately after starting |
| Post-check | Executed for each filtered container after the update cycle completes | Per container, after all updates |
Configuration
Enabling Lifecycle Hooks
Lifecycle hooks are disabled by default. Enable them by using the following on the Watchtower container:
--enable-lifecycle-hooksDefining Hook Commands
Hook commands are defined using Docker labels on the containers being monitored by Watchtower:
sh, jq, etc.) to be installed in the monitored container.LABEL com.centurylinklabs.watchtower.lifecycle.pre-check="echo 'Starting update cycle'"LABEL com.centurylinklabs.watchtower.lifecycle.pre-update="echo 'Preparing container for update'"LABEL com.centurylinklabs.watchtower.lifecycle.post-update="echo 'Container updated successfully'"LABEL com.centurylinklabs.watchtower.lifecycle.post-check="echo 'Update cycle completed'"Advanced Configuration
Custom Timeouts
By default, hook commands timeout after 1 minute. Override this with timeout labels:
LABEL com.centurylinklabs.watchtower.lifecycle.pre-check-timeout="5"LABEL com.centurylinklabs.watchtower.lifecycle.pre-check-timeout="0"Custom User Execution
By default, hooks run as the monitored container’s configured user and group (uid:gid).
Both global and individual, container-specific uid/gid configurations are supported.
--lifecycle-uid 1000--lifecycle-gid 1000Execution Details
Docker API Integration
Lifecycle hooks utilize Docker’s exec API through the following sequence:
- Exec Creation:
ContainerExecCreatecreates an exec instance with the specified command - Exec Start:
ContainerExecStartbegins execution of the command - Output Capture:
ContainerExecAttachcaptures stdout/stderr output - Status Monitoring:
ContainerExecInspectmonitors execution status and exit codes
Environment Variables
Lifecycle hook commands receive container metadata via the WT_CONTAINER environment variable containing a JSON object with the following fields:
| Field | Type | Description | Example |
|---|---|---|---|
name |
string | Container name (may include leading /) |
"/my-app" or "my-app" |
id |
string | Full container ID | "abc123def456..." |
image_name |
string | Container image name with tag | "nginx:latest" |
stop_signal |
string | Container’s configured stop signal | "SIGTERM" |
labels |
object | Watchtower-specific labels only | {"com.centurylinklabs.watchtower.lifecycle.pre-update": "backup.sh"} |
labels object contains only Watchtower-specific labels (those starting with com.centurylinklabs.watchtower.) to keep the JSON payload small and focused on Watchtower configuration.Usage Examples
#!/bin/bash
CONTAINER_NAME=$(echo $WT_CONTAINER | jq -r '.name')
echo "Processing container: $CONTAINER_NAME"Execution Flow
Complete Update Cycle
flowchart TD
START([START])
START --> PRECHECK
PRECHECK[Pre-check hook]
PRECHECK --> SCAN
SCAN[Scan for updates<br/>Check images<br/>Find stale containers]
SCAN --> DECISION{Stale containers<br/>found?}
DECISION -->|No| POSTCHECK
DECISION -->|Yes| LOOPSTART
LOOPSTART[For each stale container]
LOOPSTART --> PREUPDATE
PREUPDATE[Pre-update hook]
PREUPDATE --> STOP[Stop old container]
STOP --> STARTNEW[Start new container]
STARTNEW --> POSTUPDATE[Post-update hook]
POSTUPDATE --> LOOPEND{All containers<br/>processed?}
LOOPEND -->|No| LOOPSTART
LOOPEND -->|Yes| POSTCHECK
POSTCHECK[Post-check hook]
POSTCHECK --> END([END])
classDef hook fill:#406170,stroke:#000,stroke-width:2px;
classDef action fill:#003343,stroke:#000,stroke-width:2px;
classDef decision fill:#003343,stroke:#000,stroke-width:2px;
class PRECHECK,PREUPDATE,POSTUPDATE,POSTCHECK hook
class SCAN,STOP,STARTNEW,LOOPSTART action
class DECISION,LOOPEND decision
Hook Execution Conditions
| Hook Type | Scope | Timing | Conditions | Container State |
|---|---|---|---|---|
| Pre-check | Per filtered container | Beginning of update cycle | Command defined + hooks enabled | Ignored |
| Pre-update | Individual containers being updated | Immediately before stopping | Command defined + hooks enabled + container running + not restarting | Must be running and not restarting |
| Post-update | Individual containers successfully updated | Immediately after starting new container | Command defined + hooks enabled + update successful | New container running |
| Post-check | Per filtered container | End of update cycle | Command defined + hooks enabled | Ignored |
Exit Code Handling
Hook execution results are evaluated based on exit codes, with different behaviors per hook type:
Practical Examples
FROM postgres:13
# Pre-update: Create backup before stopping
LABEL com.centurylinklabs.watchtower.lifecycle.pre-update="/usr/local/bin/backup.sh"
LABEL com.centurylinklabs.watchtower.lifecycle.pre-update-timeout="10"
# Post-update: Run migrations after starting new version
LABEL com.centurylinklabs.watchtower.lifecycle.post-update="/usr/local/bin/migrate.sh"
LABEL com.centurylinklabs.watchtower.lifecycle.post-update-timeout="15"
COPY backup.sh migrate.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/backup.sh /usr/local/bin/migrate.shSynology DSM Graceful Shutdown
There is a well-known issue with Synology devices sending warning notifications when containers are stopped by anything other than the Synology’s Docker service. This can be problematic when using tools like Watchtower that stop and restart containers.
The examples/lifecycle-hooks/synology-stop directory provides examples for implementing graceful shutdowns using Synology’s DSM Web API. This includes both shell script and Go implementations that authenticate with DSM, stop containers gracefully, and handle session management.
See the synology-stop README for detailed setup instructions, environment variables, and deployment examples using docker-compose.
Troubleshooting
Common Issues
Hook Commands Not Executing
- Verify
--enable-lifecycle-hooks/WATCHTOWER_LIFECYCLE_HOOKS=trueis set - Check that labels are correctly formatted
- Ensure container is running (for pre-update hooks)
Timeout Errors
- Increase timeout values using timeout labels
- Set timeout to “0” to disable timeouts
- Check command execution time
Permission Issues
- Use appropriate UID/GID labels
- Ensure user has permissions to execute commands
- Check container’s user configuration
Exit Code Confusion
- Exit code 0: Success, continue
- Exit code 75: Skip this container update
- Other codes: Fail the entire update process
Debugging
Enable debug logging to see hook execution details:
watchtower --debug --enable-lifecycle-hooksLook for log messages containing:
- “Executing pre-check command”
- “Executing pre-update command”
- “Command output captured”
- “Command execution failed”