DrydockDrydock
ConfigurationLifecycle Hooks

Lifecycle Hooks

Run custom commands before or after container updates.

Overview

Lifecycle hooks let you run shell commands before or after a container is updated. Common use cases:

  • Stop dependent services before updating
  • Run database migrations after updating
  • Send custom notifications
  • Create network resources or volumes

Container labels

LabelTypeDefaultDescription
dd.hook.prestringShell command to execute before the update
dd.hook.poststringShell command to execute after the update
dd.hook.pre.abortbooleantrueAbort the update if the pre-hook exits with a non-zero code
dd.hook.timeoutnumber (ms)60000Maximum execution time for each hook
Legacy wud.hook.* labels are still supported for backward compatibility. When used, drydock logs a one-time deprecation warning and records the fallback in dd_legacy_input_total. You can rewrite labels with node dist/index.js config migrate.

Hook enablement

Env varRequiredDescriptionSupported valuesDefault
DD_HOOKS_ENABLEDEnable execution of lifecycle hook labels (dd.hook.* / wud.hook.*)true, falsefalse
Hooks sourced from labels are skipped by default. Set DD_HOOKS_ENABLED=true to opt in.

Hook environment variables

When a hook runs, drydock injects environment variables describing the update:

VariableDescriptionExample
DD_CONTAINER_NAMEContainer namemyapp
DD_CONTAINER_IDContainer IDabc123def456
DD_IMAGE_NAMEImage name (without registry prefix)myorg/myapp
DD_IMAGE_TAGCurrent image tag1.2.3
DD_UPDATE_KINDUpdate typetag or digest
DD_UPDATE_FROMCurrent tag or digest1.2.3
DD_UPDATE_TONew tag or digest1.3.0

Execution details

  • Hooks run inside the drydock container using /bin/sh -c only when DD_HOOKS_ENABLED=true
  • Combined stdout and stderr output is capped at 10 KB
  • An exit code of 0 is treated as success; any non-zero code is a failure
  • If a hook exceeds its timeout, it is killed and treated as a failure (exit code 1)
  • Hooks are NOT executed during self-update

Threat model (label-sourced commands)

Hooks are sourced from container labels and then executed by drydock. Treat hook labels as a code-execution boundary.

  • Trust boundary: Container labels (dd.hook.* / wud.hook.*) cross into the drydock runtime command executor.
  • Trust boundary: Hook execution can cross from drydock into Docker host control when the socket is mounted.
  • High-value assets: Docker socket access, mounted credentials/secrets, and network-reachable internal services.
  • Attacker capability: Anyone who can modify deployed container labels (Compose manifests, swarm specs, API-driven container creation) can inject hook commands.
  • Abuse path: A malicious label can execute arbitrary shell via /bin/sh -c, including secret exfiltration or unauthorized Docker API actions.
  • Abuse path: Expensive hooks can degrade availability (for example long-running or fork-heavy commands), bounded by dd.hook.timeout.
  • Mitigation: Restrict who can modify container specs/labels and protect deployment pipelines.
  • Mitigation: Run drydock with least privilege; mount /var/run/docker.sock only when required.
  • Mitigation: Keep dd.hook.pre.abort=true when you want fail-closed behavior on pre-hook errors.
  • Mitigation: Monitor audit events (hook-configured, hook-pre-*, hook-post-*) for unauthorized hook changes and executions.

Pre-hook abort behavior

When dd.hook.pre.abort is true (the default), a failed pre-hook cancels the update entirely. Set it to false if you want the update to proceed regardless of hook outcome.

Audit events

EventDescription
hook-configuredHook labels were detected for the update lifecycle
hook-pre-successPre-hook completed successfully
hook-pre-failedPre-hook exited non-zero or timed out
hook-post-successPost-hook completed successfully
hook-post-failedPost-hook exited non-zero or timed out

Example

services:
  myapp:
    image: myapp:latest
    labels:
      - dd.watch=true
      - dd.hook.pre=docker exec mydb pg_dump -U postgres mydb > /backup/pre-update.sql
      - dd.hook.post=curl -X POST https://hooks.slack.com/services/xxx -d '{"text":"myapp updated"}'
      - dd.hook.pre.abort=true
      - dd.hook.timeout=120000
Hooks run with the permissions of the drydock process. Ensure the Docker socket is mounted if your hooks need to interact with Docker.

On this page