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
Simulation mode: pulls the new image to verify registry availability, but never rewrites compose files or recreates services (even during manual execution via Home Assistant, UI, or API). Not intended for preparing future manual updates (see PRELOAD instead)
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
When enabled with AUTO=false, automatically downloads updated image layers in the background (using pull-by-digest for floating tags) without rewriting compose files or restarting services. When an operator triggers the manual update later (via Home Assistant, Web UI, or API), stack recreation is instantaneous (< 1s) with zero network wait
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.
β‘ Operating Modes: Auto, Preload & Dry-Runβ
When configuring the Docker Compose trigger alongside the common AUTO setting (WUD_TRIGGER_DOCKERCOMPOSE_{trigger_name}_AUTO), it is essential to distinguish between DRYRUN and PRELOAD:
-
DRYRUN(Simulation / Test mode):
Validates credentials and pulls the new image to verify registry availability, but never modifies Compose files, stops containers, or restarts stack services. This applies even if you manually trigger an update from the Web UI, Home Assistant, or REST API.DRYRUNis strictly intended for testing and validationβit is not meant for preparing a future manual update. -
PRELOAD(Controlled Manual Updates with Zero Downtime):
Designed specifically for production workflows where automated updates are disabled (AUTO=false) to avoid unexpected stack restarts. When an update is detected, WUD downloads all image layers in the background ahead of time without modifyingdocker-compose.ymlor restarting containers. For floating tags (such as:latest), WUD pulls specifically by manifest digest so running containers keep their existing tag references intact. When an operator triggers the update later (via Home Assistant button, Web UI, or API), stack recreation is instantaneous (< 1s) because all layers are already present locally.
Mode Comparisonβ
The table below summarizes the four typical operating modes:
| Mode | AUTO | PRELOAD | DRYRUN | Behavior & Use Case |
|---|---|---|---|---|
| Fully Automated | true | false | false | Hands-off auto-update: Services and Compose files are immediately updated and recreated as soon as an update is detected. |
| Fully Manual | false | false | false | Standard manual update: WUD notifies when an update is available without downloading images in advance. When triggered manually, WUD pulls the image, rewrites the Compose file, and restarts services (downtime includes download time). |
| Manual with Zero-Downtime Preload | false | true | false | Optimized maintenance: WUD pre-caches image layers in the background without touching running services or Compose files. When triggered manually, recreation is instantaneous (< 1s) with zero network wait. |
| Simulation / Test | * | * | true | Test mode only: Pulls images to test network and registry access, but never modifies Compose files or recreates services (even on manual execution). |
Preload can also be opted into per container using Docker labels:
wud.trigger.preload=true(applies to all triggers supporting preload)wud.trigger.dockercompose.<trigger_name>.preload=true(applies to a specific trigger instance)
π 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
Controlled Manual Updates with Background Preloadingβ
Combine AUTO=false and PRELOAD=true so updated image layers are pre-cached in the background without touching Compose files, allowing instantaneous stack updates when triggered manually:
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
# Disable automatic stack recreation
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_AUTO=false
# Preload updated image layers in the background
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_PRELOAD=true
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_BACKUP=true
- WUD_TRIGGER_DOCKERCOMPOSE_LOCAL_PRUNE=true