Skip to content
ConfigurationActions & NotificationsDocker Compose

Docker-Compose

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

logo

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_start hooks declared on the updated service (if any)
  • Remove the previous image (optionally)

Variables

Env varRequiredDescriptionSupported valuesDefault value when missing
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_FILEThe 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}_BACKUPBackup the docker-compose.yml file as .back before updating?true, falsefalse
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_PRUNEIf the old image must be pruned after upgradetrue, falsefalse
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_DRYRUNWhen enabled, only pull the new image ahead of timetrue, falsefalse
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_BACKUPCOUNTNumber of image backups to retain per containerInteger3
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_AUTOREMOVETIMEOUTWait timeout for container auto-removal (ms)Integer10000
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_COMPOSEFILELABELContainer label name used to locate the compose file per containerLabel namedd.compose.file
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_RECONCILIATIONMODECompose/runtime reconciliation mode before lifecycle (warn, block, or off)warn, block, offwarn
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_DIGESTPINNINGWrite digest-pinned compose image references when a digest is availabletrue, falsefalse
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_COMPOSEFILEONCEBatch compose runtime refresh to one grouped pull/up pass per compose stack/filetrue, falsefalse
DD_ACTION_DOCKERCOMPOSE_{trigger_name}_MOUNTPREFIXFALLBACKAllow compose file matching by trailing project-directory/file path when the Docker label/inspect path and trigger FILE differ only by host/container mount prefixtrue, falsefalse

This trigger also supports the common configuration variables, but only supports the batch mode.

Notes:

  • The env var keys for COMPOSEFILEONCE, COMPOSEFILELABEL, RECONCILIATIONMODE, DIGESTPINNING, and MOUNTPREFIXFALLBACK are case-insensitive — both the lowercased form (e.g. composefileonce) and the camelCase form (e.g. composeFileOnce) are accepted.
  • The removed wud.compose.file label is ignored as of v1.6. Use dd.compose.file, or run node dist/index.js config migrate to 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:

  1. Configured compose-file label on the watched container — dd.compose.file by default, or the label named by COMPOSEFILELABEL.
  2. com.docker.compose.project.config_files from the watched container, resolved relative to com.docker.compose.project.working_dir when needed. Multi-file Compose chains are preserved in their declared order and host paths are mapped through Drydock's bind mounts.
  3. Docker inspect fallback — re-reads those standard Compose project labels from the live container when they were absent from the watcher snapshot.
  4. Trigger-level FILE configurationDD_ACTION_DOCKERCOMPOSE_{trigger_name}_FILE is the last-resort default. If it names a directory, Drydock probes compose.yaml, compose.yml, docker-compose.yaml, then docker-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=true

When 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.

This trigger will only work with locally watched containers.
Do not forget to mount the docker-compose.yml file in the drydock container.

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>.image value 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.

Hook labels are ignored unless 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:

  1. Drydock searches every file in the chain to find which one declares the target service's image key.
  2. 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.
  3. 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_add so Drydock can read and write them.
  • Break-glass workaround (less secure): set both DD_RUN_AS_ROOT=true and DD_ALLOW_INSECURE_ROOT=true; setting only DD_RUN_AS_ROOT=true fails 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.yml

COMPOSE_FILES_GID should match the GID that owns the mounted compose files on the host.

Do not mount the compose file as read-only (: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.yml
docker run \
  -v /etc/my-services/docker-compose.yml:/drydock/docker-compose.yml
  -e "DD_ACTION_DOCKERCOMPOSE_EXAMPLE_FILE=/drydock/docker-compose.yml" \
  ...
  codeswhat/drydock
labels:
  dd.compose.file: "/my/path/docker-compose.yaml"