Docker Compose
The docker-compose trigger recreates containers managed by Docker Compose by pulling the latest images and restarting the stack.
⚙️ Configuration Variables
Back up docker-compose.yml to .back before modifying it
Label name on container to override the compose file path
When enabled, only pulls the new image ahead of time without rewriting compose files or restarting
Path or template for the docker-compose.yml file (e.g. /stacks/${container.labels['com.docker.compose.project']}/docker-compose.yml). If omitted, auto-detected from com.docker.compose.project.config_files or com.docker.compose.project.working_dir.
Container path prefix where host compose directories are mounted inside WUD
Host path prefix to replace with container path prefix when using auto-detection or templated paths
Prune obsolete image versions after a successful upgrade
Enable the automatic rollback on HEALTHCHECK failure for the opted-in services of a compose project (see Automatic rollback on healthcheck failure)
Grace period (in milliseconds) a replacement service must stay up when its image defines no HEALTHCHECK before the update is accepted
Sampling interval (in milliseconds) between two health observations
How long (in milliseconds) to wait for a replacement service to report a terminal health status (healthy/unhealthy) when its image defines a HEALTHCHECK
Allow WUD to update the container it runs in itself (see Updating WUD itself)
How long (in milliseconds) to wait for the replacement WUD container to become healthy before rolling back
This trigger supports all common trigger configuration options and runs in batch mode by default.
🔄 Updating WUD itself
Replacing the container WUD runs in is a special case: the trigger has to stop
that container, and stopping it kills the process that still has to remove,
recreate and start it. WUD would go down mid-update and stay down, because a
deliberate docker stop does not trigger a restart policy.
By default WUD refuses to do it and logs an actionable error instead of leaving you with a stopped container:
Refusing to update the container WUD runs in: stopping it would abort the update
and leave the container down. Enable it with
WUD_TRIGGER_DOCKERCOMPOSE_{trigger_name}_SELFUPDATE=true, or exclude WUD from this
trigger with the wud.trigger.exclude label and update it externally.
Opting in
Set WUD_TRIGGER_DOCKERCOMPOSE_{trigger_name}_SELFUPDATE=true. WUD then delegates the
swap to a short-lived helper container instead of doing it in-process. The
helper runs the image WUD is running right now (not the one being installed),
so the code performing the swap is always known to support it, and it:
- creates the replacement under a temporary name, before touching anything;
- stops the current WUD container;
- starts the replacement;
- waits for it to become healthy;
- on success, removes the old container and renames the replacement;
- on failure, removes the replacement and restarts the old container.
The helper mounts the same Docker socket as the watcher and is named
wud-self-update. It is deliberately not removed when it finishes: if a
self-update fails, WUD is not running to record why, so the helper's logs are
the only account of what happened. Check them with docker logs wud-self-update.
The next self-update replaces it, so at most one is ever left behind.
Self-update requires the watcher to talk to Docker over a socket. It is not
supported for watchers configured with WUD_WATCHER_{watcher_name}_HOST.
🪝 Pre & Post Update Hooks
Like the Docker trigger, the Docker Compose trigger supports Pre-update and Post-update hooks.
Quality Gate (Stack Protection)
Before the Compose file is modified and services are recreated, WUD runs all configured pre hooks across the stack services:
- If any pre-hook fails (exit code != 0 or timeout), the entire update is cancelled.
- The
docker-compose.ymlfile is not modified, no backup file is generated, and running containers remain untouched.
Hook Types
| Type | Target | Description |
|---|---|---|
exec (Type A) | target: "self" (default) | Executes a shell command inside the container being updated. |
exec (Type B) | target: "<container_name>" | Executes a shell command inside another container (e.g. running migrations or DB dumps). |
trigger (Type C) | trigger: "<trigger_name>" | Calls another WUD trigger (e.g. Slack, MQTT, Webhook). |
Environment Variables Injected into exec Hooks
When executing commands inside containers, WUD injects contextual environment variables:
| Variable | Description | Example |
|---|---|---|
WUD_CONTAINER_NAME | Name of the target container | nextcloud |
WUD_CONTAINER_ID | Docker ID of the target container | a1b2c3d4e5f6 |
WUD_IMAGE_NAME | Name/repository of the container image | linuxserver/nextcloud |
WUD_IMAGE_REGISTRY | Registry of the container image | docker.io |
WUD_IMAGE_OLD_TAG | Current tag of the container image | 27.0.1 |
WUD_IMAGE_NEW_TAG | New tag being deployed | 27.1.0 |
WUD_IMAGE_OLD | Full old image reference (name:tag or name@digest) | linuxserver/nextcloud:27.0.1 |
WUD_IMAGE_NEW | Full new image reference (name:tag or name@digest) | linuxserver/nextcloud:27.1.0 |
WUD_WATCHER_NAME | Watcher that detected the update | local |
WUD_TRIGGER_NAME | Trigger executing the hook | local |
WUD_HOOK_PHASE | Current hook phase | pre or post |
Configuration via Container Labels
Hooks are defined via labels on compose services:
services:
web:
image: my-app:1.0.0
labels:
# Quality Gate: database migration check before update
- "wud.hook.1.phase=pre"
- "wud.hook.1.type=exec"
- "wud.hook.1.command=npm run db:check"
- "wud.hook.1.timeout=30000"
# Post-update: notify Slack
- "wud.hook.2.phase=post"
- "wud.hook.2.type=trigger"
- "wud.hook.2.trigger=slack"
🩺 Automatic rollback on healthcheck failure
When at least one service of a compose project opts in (ROLLBACK env var or
wud.rollback.enable / .window / .interval / .grace labels, labels win),
the update of the whole project becomes transactional:
- WUD writes an implicit
.backcopy of the compose file before any mutation (regardless of theBACKUPsetting). If that write fails, nothing is changed. - Every service is swapped using a rename-first archive, so the previous container is never destroyed before the replacement is validated.
- Only the opted-in services are health-gated. WUD waits for a terminal
HEALTHCHECKstatus (up toROLLBACKWINDOWms), or — when the image defines noHEALTHCHECK— requires the replacement to stay up forROLLBACKGRACEms. - On success, the archives are removed, the compose file keeps the new versions, and obsolete images are pruned as usual.
- On any failure, the whole project is reverted: every recreated
service is stopped/removed, its archive is restored under the original name
and started again, and the
.backfile is copied back over the compose file only once every service has been restored. The notification report marks the failing service with its verdict and the healthy-but-reverted services withproject-revert.
Example
services:
whatsupdocker:
image: getwud/wud
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/stacks/app/docker-compose.yml:/wud/docker-compose.yml
environment:
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_FILE=/wud/docker-compose.yml
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_ROLLBACK=true
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_ROLLBACKWINDOW=180000
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_ROLLBACKGRACE=15000
web:
image: my/web:2.0.0
labels:
- wud.rollback.enable=true
- wud.rollback.window=120000
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost/health']
interval: 5s
timeout: 3s
retries: 3
🔄 Docker Compose Convergence & Metadata
- WUD updates the Compose YAML on disk and recreates the container via the Docker Engine API.
- WUD synchronizes
com.docker.compose.imagewith the newly pulled image ID. com.docker.compose.config-hashis an internal Docker Compose hash computed exclusively by the Docker Compose Go CLI; subsequent manual or automateddocker compose upcalls will smoothly converge the hash, or users can passdocker compose up -d --no-recreateif they wish to keep the existing running container instance.
🚀 Examples
Auto-Update Compose Services with Mounted Compose File
- Docker Compose
- Docker
- Per-Container Override
services:
whatsupdocker:
image: getwud/wud
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/stacks/app/docker-compose.yml:/wud/docker-compose.yml
environment:
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_FILE=/wud/docker-compose.yml
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_BACKUP=true
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_PRUNE=true
docker run -d \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /opt/stacks/app/docker-compose.yml:/wud/docker-compose.yml \
-e WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_FILE="/wud/docker-compose.yml" \
-e WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_BACKUP=true \
-e WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_PRUNE=true \
getwud/wud
services:
my-app:
image: my-app:1.2.0
labels:
- wud.compose.file=/opt/stacks/app/docker-compose.yml
Auto-Detection with Path Mapping
When WUD manages multiple Docker Compose stacks, you can mount the parent directory containing all stacks and let WUD auto-detect compose files via Docker Compose labels (com.docker.compose.project.config_files or com.docker.compose.project.working_dir). Path mapping translates host paths to container mount paths automatically:
services:
whatsupdocker:
image: getwud/wud
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/stacks:/stacks
environment:
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_PATHMAPPING_HOST=/opt/stacks
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_PATHMAPPING_CONTAINER=/stacks
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_BACKUP=true
Dynamic File Path with Template Strings
You can also use string interpolation in the FILE variable to resolve paths dynamically using container attributes:
services:
whatsupdocker:
image: getwud/wud
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/stacks:/stacks
environment:
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_FILE=/stacks/$${container.labels['com.docker.compose.project']}/docker-compose.yml