Docker-Compose
The dockercompose trigger lets you update docker-compose.yml files & replace existing containers with their updated versions.

The dockercompose trigger lets you update docker-compose.yml files & replace existing containers with their updated versions.
The trigger will:
- Update the related docker-compose.yml file
- Clone the existing container specification
- Pull the new image
- Stop the existing container
- Remove the existing container
- Create the new container
- Start the new container (if the previous one was running)
- Run
post_starthooks declared on the updated service (if any) - Remove the previous image (optionally)
Variables
| Env var | Required | Description | Supported values | Default value when missing |
|---|---|---|---|---|
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_FILE | ⚪ | The docker-compose.yml file location or directory (can also be set per container via the dd.compose.file label). When a directory is given, Drydock probes for compose.yaml, compose.yml, docker-compose.yaml, and docker-compose.yml in order. | ||
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_BACKUP | ⚪ | Backup the docker-compose.yml file as .back before updating? | true, false | false |
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_PRUNE | ⚪ | If the old image must be pruned after upgrade | true, false | false |
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_DRYRUN | ⚪ | When enabled, only pull the new image ahead of time | true, false | false |
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_BACKUPCOUNT | ⚪ | Number of image backups to retain per container | Integer | 3 |
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_AUTOREMOVETIMEOUT | ⚪ | Wait timeout for container auto-removal (ms) | Integer | 10000 |
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_COMPOSEFILELABEL | ⚪ | Container label name used to locate the compose file per container | Label name | dd.compose.file |
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_RECONCILIATIONMODE | ⚪ | Compose/runtime reconciliation mode before lifecycle (warn, block, or off) | warn, block, off | warn |
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_DIGESTPINNING | ⚪ | Write digest-pinned compose image references when a digest is available | true, false | false |
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_COMPOSEFILEONCE | ⚪ | Batch compose runtime refresh to one grouped pull/up pass per compose stack/file | true, false | false |
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_MOUNTPREFIXFALLBACK | ⚪ | Allow compose file matching by trailing project-directory/file path when the Docker label/inspect path and trigger FILE differ only by host/container mount prefix | true, false | false |
This trigger also supports the common configuration variables, but only supports the batch mode.
Notes:
- The env var keys for
COMPOSEFILEONCE,COMPOSEFILELABEL,RECONCILIATIONMODE,DIGESTPINNING, andMOUNTPREFIXFALLBACKare case-insensitive — both the lowercased form (e.g.composefileonce) and the camelCase form (e.g.composeFileOnce) are accepted. - The removed
wud.compose.filelabel is ignored as of v1.6. Usedd.compose.file, or runnode dist/index.js config migrateto rewrite existing compose files.
Auto-detection
For every update, Drydock resolves the active compose-file chain in this order, and the first non-empty source wins:
- Configured compose-file label on the watched container —
dd.compose.fileby default, or the label named byCOMPOSEFILELABEL. com.docker.compose.project.config_filesfrom the watched container, resolved relative tocom.docker.compose.project.working_dirwhen needed. Multi-file Compose chains are preserved in their declared order and host paths are mapped through Drydock's bind mounts.- Docker inspect fallback — re-reads those standard Compose project labels from the live container when they were absent from the watcher snapshot.
- Trigger-level
FILEconfiguration —DD_ACTION_DOCKERCOMPOSE_{trigger_name}_FILEis the last-resort default. If it names a directory, Drydock probescompose.yaml,compose.yml,docker-compose.yaml, thendocker-compose.yml.
Most Docker Compose users do not need to configure the file path at all. As long as containers were started with docker compose up, the config_files label is present and Drydock resolves the path automatically.
The removed wud.compose.file label is not part of this chain and is ignored in v1.6.
Mount-prefix fallback
Some stack managers or bind mounts expose the same compose file under different prefixes inside Drydock than the path recorded on the running container. For example, the container can report /opt/stacks/app/compose.yaml while Drydock has the file mounted as /drydock/stacks/app/compose.yaml.
The default behavior is strict: Drydock only updates a compose file when the discovered path and configured path match through normal label/inspect resolution. If your paths differ only by host/container mount prefix, opt in to trailing path matching:
environment:
- DD_ACTION_DOCKERCOMPOSE_MYSTACK_FILE=/drydock/stacks/app/compose.yaml
- DD_ACTION_DOCKERCOMPOSE_MYSTACK_MOUNTPREFIXFALLBACK=trueWhen enabled, Drydock compares the trailing <project-directory>/<compose-file> path after strict matching fails. Keep this disabled unless those path tails are unique across your mounted stacks; ambiguous layouts should use explicit dd.compose.file labels instead.
The Docker Compose trigger uses the Docker Engine API directly for all runtime operations (pull, stop, recreate). The docker compose / docker-compose CLI binary is not required inside the drydock container.
Tag vs digest updates
For tag-based updates, the trigger patches the compose file with the new tag and then recreates the container. For digest-only updates (same tag, new digest), the compose file is left unchanged and only the container runtime is refreshed (pull new image, stop/remove old container, recreate with new image).
When DIGESTPINNING=true, compose mutations use digest references (image@sha256:...) when a digest is available.
Compose file patch behavior
When a tag-based update requires a compose file change, Drydock patches Compose content in a service-scoped way:
- Only the target
services.<service>.imagevalue is edited. - Comments and surrounding formatting are preserved outside that scalar value.
- Drydock does not use global string replacement for image tags, so matching strings in other fields (for example environment values) are not rewritten.
If a service uses flow-style mapping without an image key (for example nginx: { restart: always }), Drydock will not auto-insert image. Convert that service to block-style YAML first.
Before persisting a compose mutation, Drydock validates the candidate file by parsing the full compose chain in-process. Invalid candidate configs are not written.
Update Concurrency
Services within the same compose stack are serialized by a per-compose-project lock — only one service in a given stack updates at a time. Services in different stacks (or bare containers) can update concurrently. This prevents docker compose up orchestration conflicts without restricting cross-stack parallelism.
A global DD_UPDATE_MAX_CONCURRENT cap (default: unlimited) limits the total simultaneous update lifecycles across all stacks. See Container Actions — Global Concurrency Cap for details.
Reconciliation behavior
Before lifecycle execution, Drydock checks the running container image against the compose-declared image for the target service.
RECONCILIATIONMODE=warn(default): log a reconciliation warning and continue.RECONCILIATIONMODE=block: fail the update before write/runtime steps.RECONCILIATIONMODE=off: skip reconciliation checks.
Failure safety
Before removing the running container, Drydock verifies that the target image can run on the daemon architecture. During recreate, it captures enough of the original container specification to restore the previous container if the replacement fails before the update is finalized. This does not replace a service-level health check, but it prevents common compose update failures from leaving the service absent.
Compose-file-once mode
When COMPOSEFILEONCE=true and multiple services in the same compose stack/file require runtime refresh, Drydock performs one runtime refresh per unique service and skips repeated refreshes for subsequent containers sharing the same service in that batch.
Inherited Docker Trigger Features
The Docker Compose trigger extends the Docker trigger and inherits the following features:
Lifecycle Hooks
Container lifecycle hooks (dd.hook.pre, dd.hook.post, dd.hook.pre.abort, dd.hook.timeout) work identically to the Docker trigger. Hooks execute before and after each container update in the batch. See Docker trigger — Lifecycle Hooks for full details.
DD_HOOKS_ENABLED=true is set on the Drydock container.Auto-Rollback
Auto-rollback labels (dd.rollback.auto, dd.rollback.window, dd.rollback.interval) are supported. When a container becomes unhealthy after a compose-based update, Drydock automatically rolls back to the previous image. See Docker trigger — Auto-Rollback.
Image Backup & Rollback
Image backups are retained per container (controlled by the BACKUPCOUNT variable). You can roll back to any retained backup via the API. See Docker trigger — Image Backup & Rollback.
Security Gate (Update Bouncer)
When Update Bouncer is configured, candidate images are scanned for vulnerabilities before the compose update proceeds. If blocking issues are found, the update is aborted and a security-alert notification is sent.
Self-Update
The Docker Compose trigger supports self-updates — when Drydock detects that its own container has an available update, it performs the same safe self-replacement sequence described in Docker trigger — Self-Update, with the additional step of updating the compose file first.
Multi-file compose chains
When a container was started with multiple compose files (e.g. docker-compose.yml + docker-compose.override.yml), Drydock detects the full file chain from the com.docker.compose.project.config_files label.
During an update:
- Drydock searches every file in the chain to find which one declares the target service's
imagekey. - The last file in the chain that contains the service definition and is writable is selected as the update target. This matches Docker Compose override semantics where later files take precedence.
- All files in the chain are parsed in-process for validation before and after any mutation.
The compose file chain for each container is visible in the container detail view of the UI.
If only one compose file is used, the chain is a single entry and the behavior is identical to the single-file case.
Troubleshooting: permission denied (EACCES)
If logs show an error like:
EACCES: permission denied, access '/drydock/.../docker-compose.yml'the mounted compose file (or parent directory) is not writable by the Drydock process. This trigger needs read and write access to update image tags in the compose file.
Ways to fix:
- Grant read and write access on the mounted path for the user/group used by the Drydock container.
- Add the host group that owns the compose files with
group_addso Drydock can read and write them. - Break-glass workaround (less secure): set both
DD_RUN_AS_ROOT=trueandDD_ALLOW_INSECURE_ROOT=true; setting onlyDD_RUN_AS_ROOT=truefails closed at startup.
Example using group_add:
services:
drydock:
image: codeswhat/drydock
group_add:
- "${COMPOSE_FILES_GID}"
volumes:
- /var/lib/docker/volumes/portainer_data/_data/compose:/drydock
environment:
- DD_ACTION_DOCKERCOMPOSE_EXAMPLE_FILE=/drydock/5/docker-compose.ymlCOMPOSE_FILES_GID should match the GID that owns the mounted compose files on the host.
:ro) — the trigger must write updated image tags back to the file.Examples
services:
drydock:
image: codeswhat/drydock
...
volumes:
- /etc/my-services/docker-compose.yml:/drydock/docker-compose.yml
environment:
- DD_ACTION_DOCKERCOMPOSE_EXAMPLE_FILE=/drydock/docker-compose.ymldocker run \
-v /etc/my-services/docker-compose.yml:/drydock/docker-compose.yml
-e "DD_ACTION_DOCKERCOMPOSE_EXAMPLE_FILE=/drydock/docker-compose.yml" \
...
codeswhat/drydocklabels:
dd.compose.file: "/my/path/docker-compose.yaml"