Skip to content
ConfigurationActions & NotificationsMQTT

Mqtt

The mqtt trigger lets you send container update notifications to an MQTT broker.

logo

The mqtt trigger lets you send container update notifications to an MQTT broker.

Variables

Env varRequiredDescriptionSupported valuesDefault value when missing
DD_NOTIFICATION_MQTT_{trigger_name}_URL🔴The URL of the MQTT brokerValid mqtt, mqtts, tcp, tls, ws, wss url
DD_NOTIFICATION_MQTT_{trigger_name}_USER⚪The username if broker authentication is enabled
DD_NOTIFICATION_MQTT_{trigger_name}_PASSWORD⚪The password if broker authentication is enabled
DD_NOTIFICATION_MQTT_{trigger_name}_CLIENTID⚪The Mqtt client Id to usedd_$random
DD_NOTIFICATION_MQTT_{trigger_name}_TOPIC⚪The base topic where the updates are published todd/container
DD_NOTIFICATION_MQTT_{trigger_name}_HASS_ENABLED⚪Enable Home-assistant integration and deliver additional topicstrue, falsefalse
DD_NOTIFICATION_MQTT_{trigger_name}_HASS_DISCOVERY⚪Enable Home-assistant integration including discovery. When omitted, defaults to the value of HASS_ENABLED.true, falseSame as HASS_ENABLED
DD_NOTIFICATION_MQTT_{trigger_name}_HASS_PREFIX⚪Base topic for hass entity discoveryhomeassistant
DD_NOTIFICATION_MQTT_{trigger_name}_HASS_AGENTTOPICSEGMENT⚪Insert an agent/<name> segment into Home Assistant topics for containers owned by a remote agent, including watcher running-status topics, so multiple agents using the default watcher name local do not overwrite each other's topics. Controller-local container topics are unchanged. Set to false to temporarily opt back into the pre-v1.7.0 unscoped layout.true, falsetrue
DD_NOTIFICATION_MQTT_{trigger_name}_AGENTTOPICSEGMENT⚪Insert an agent/<name> segment into plain MQTT container state topics for containers owned by a remote agent, so two agents using the same watcher name (typically the default local) stop overwriting each other's topics. Controller-local topics are unchanged. Turning this on also turns on HASS_AGENTTOPICSEGMENT, so the Home Assistant state topic stays in step. Turning it on moves the retained state topic, and the old unscoped topic is not cleared automatically, because a controller-local container of the same name can legitimately own it, so publish an empty retained message to the old topic yourself.true, falsefalse
DD_NOTIFICATION_MQTT_{trigger_name}_HASS_DEVICEPERCONTAINER⚪Give every watched container its own Home Assistant device, nested under the drydock device via via_device, instead of hanging every entity off the single drydock device. Drydock-level entities (global counts, watcher counts, watcher running status) stay on the drydock device either way. Set to false to keep the pre-v1.8.0 single-device layout.true, falsetrue
DD_NOTIFICATION_MQTT_{trigger_name}_HASS_COMMANDS⚪Enable bidirectional commands: adds a command_topic to each container's discovery payload so Home Assistant's update entity shows an Install button, and subscribes drydock to the corresponding command topics so clicking it triggers an update. Requires HASS_ENABLED.true, falsefalse
DD_NOTIFICATION_MQTT_{trigger_name}_HASS_ATTRIBUTES⚪Attribute preset controlling which container fields are included in MQTT payloads. full sends everything; short excludes large fields like SBOM documents, scan vulnerabilities, details, and labels.full, shortshort
DD_NOTIFICATION_MQTT_{trigger_name}_HASS_FILTER_INCLUDE⚪Comma-separated list of flattened MQTT attribute keys to keep (include-mode). When set, only these keys are published.Comma-separated flattened keys
DD_NOTIFICATION_MQTT_{trigger_name}_HASS_FILTER_EXCLUDE⚪Comma-separated list of flattened MQTT attribute keys to remove (exclude-mode). Used when HASS_FILTER_INCLUDE is empty.Comma-separated flattened keys
DD_NOTIFICATION_MQTT_{trigger_name}_EXCLUDE⚪Legacy comma-separated dot-paths to exclude from the nested container object before flattening. Used only when both HASS_FILTER_INCLUDE and HASS_FILTER_EXCLUDE are empty.Comma-separated dot-paths
DD_NOTIFICATION_MQTT_{trigger_name}_TLS_CACHAIN⚪The path to the file containing the server CA chain (when TLS with a private Certificate Authority)Any valid file path
DD_NOTIFICATION_MQTT_{trigger_name}_TLS_CLIENTCERT⚪The path to the file containing the client public certificate (when TLS mutual authentication)Any valid file path
DD_NOTIFICATION_MQTT_{trigger_name}_TLS_CLIENTKEY⚪The path to the file containing the client private key (when TLS mutual authentication)Any valid file path
DD_NOTIFICATION_MQTT_{trigger_name}_TLS_REJECTUNAUTHORIZED⚪Accept or reject when the TLS server certificate cannot be trustedtrue, falsetrue
This trigger also supports the common configuration variables. but only supports the simple mode.
Home Assistant entity stability: When a watched container is recreated during an update, the MQTT HASS sensor (binary_sensor) is preserved without an intermediate unavailable/disappear state. Drydock sets a replacementExpected flag before stopping the old container so the entity remains visible in Home Assistant throughout the update.
You want to customize the name & icon of the Home-Assistant entity? Use the dd.display.name and dd.display.icon labels.
For Home Assistant, the entity_picture is automatically derived from the dd.display.icon label. Icons with sh:, hl:, or si: prefixes map to CDN image URLs. You can also set dd.display.picture to provide a direct URL override.
Multi-agent deployments: Home Assistant MQTT topics include an agent/<name> segment for agent-owned containers by default as of v1.7.0, scoping topics, watcher running-status, sensor counts, and discovery cleanup per agent so multiple agents sharing the default watcher name local no longer collide. This changed the Home Assistant entity IDs for agent containers on upgrade — update any automations or dashboards that reference the old (agent-less) entity IDs. Drydock does not retroactively remove the old discovery entities; they remain retained on the broker and show up as orphaned, frozen-at-last-state duplicates until manually removed. Set DD_NOTIFICATION_MQTT_{trigger_name}_HASS_AGENTTOPICSEGMENT=false to temporarily opt back into the pre-v1.7.0 unscoped layout — see Deprecations. Toggling HASS_AGENTTOPICSEGMENT in either direction moves the retained state topic and the old one is not cleared automatically, because the unscoped topic can legitimately belong to a controller-local container of the same name and drydock cannot tell that case apart from a stale one. If you flip the setting, publish an empty retained message to the old topic yourself to clear it.
v1.8.0-rc.1 uses container identity for topics and unique_id. The state topic segment ({container} below) is an encoded Compose project.service pair when both com.docker.compose.project and com.docker.compose.service labels are present, and the sanitized container name otherwise. Each Compose component doubles literal hyphens and replaces dots with hyphens before joining with a dot. For example, <topic>/<watcher>/<container> becomes dd/container/local/myapp.web, not dd/container/local/myapp_web_1. Home Assistant unique_id is dd_ plus a hash of the identity, independent of the topic. Rename and recreate stability applies to Compose-labeled containers only; standalone containers remain name-identified. Update hand-written subscriptions to the new shape. Discovery resync clears replaced legacy topics for known containers, records successful cleanup per configured route, and retries failed publishes. It does not enumerate or repair every retained broker entity. See Deprecations.

Home Assistant devices

As of v1.8.0 each watched container gets its own Home Assistant device. Its identifier is drydock_ plus the container's unique_id, its name is the container's display name, its model is the image name, and via_device points at the drydock device, so Home Assistant nests the containers under drydock on the integration page. The drydock-level entities stay where they were: global container counts, per-watcher counts, and watcher running status all remain on the drydock device.

Migration is automatic and keeps your history. The discovery config topic and the unique_id are unchanged by this, so the retained payload drydock publishes on startup is a discovery update for the entity that already exists rather than a new entity: entity ids, unique ids, and history are preserved. Existing entities move to their container device after you reload the MQTT integration (Settings, Devices and services, MQTT, three dots, Reload) or restart Home Assistant; new containers appear under their own device immediately. Nothing is orphaned and no entities need to be removed by hand.

Two things do change visibly. Each container's update entity moves off the drydock device onto its own, so anywhere Home Assistant shows a device next to an entity you now see the container. And friendly names get shorter: Home Assistant prefixes every discovered entity's name with its device's name, so the update entity is published unnamed (Home Assistant's marker for the device's main feature) and shows as nginx rather than the drydock nginx you had before, or nginx nginx if it kept its own name. dd.display.name now names the device; dd.display.icon and dd.display.picture still apply to the entity. Automations that address entities by entity id are unaffected. Anything that targets the drydock device (a device trigger, or a device_id-based action) now only reaches the drydock-level entities and has to be re-pointed at the container's device or at the entity directly.

Set DD_NOTIFICATION_MQTT_{trigger_name}_HASS_DEVICEPERCONTAINER=false to keep the pre-v1.8.0 layout, where every entity sits on the single drydock device.

Examples

Post a message to a local mosquitto broker

services:
  drydock:
    image: codeswhat/drydock
    ...
    environment:
      - DD_NOTIFICATION_MQTT_MOSQUITTO_URL=mqtt://localhost:1883

Post a message to a local mosquitto broker with mTLS enabled

services:
  drydock:
    image: codeswhat/drydock
    ...
    environment:
      - DD_NOTIFICATION_MQTT_MOSQUITTO_URL=mqtts://localhost:8883
      - DD_NOTIFICATION_MQTT_MOSQUITTO_TLS_CLIENTKEY=/drydock/mqtt/client-key.pem
      - DD_NOTIFICATION_MQTT_MOSQUITTO_TLS_CLIENTCERT=/drydock/mqtt/client-cert.pem
      - DD_NOTIFICATION_MQTT_MOSQUITTO_TLS_CACHAIN=/drydock/mqtt/ca.pem
    volumes:
      - /mosquitto/tls/client/client-key.pem:/drydock/mqtt/client-key.pem
      - /mosquitto/tls/client/client-cert.pem:/drydock/mqtt/client-cert.pem
      - /mosquitto/tls/ca.pem:/drydock/mqtt/ca.pem

Post a message to a maqiatto broker

services:
  drydock:
    image: codeswhat/drydock
    ...
    environment:
      - DD_NOTIFICATION_MQTT_MAQIATTO_URL=tcp://maqiatto.com:1883
      - DD_NOTIFICATION_MQTT_MAQIATTO_USER=john@doe.com
      - DD_NOTIFICATION_MQTT_MAQIATTO_PASSWORD=mysecretpassword
      - DD_NOTIFICATION_MQTT_MAQIATTO_TOPIC=john@doe.com/drydock/image

Example of sent message

{
  "id": "31a61a8305ef1fc9a71fa4f20a68d7ec88b28e32303bbc4a5f192e851165b816",
  "name": "homeassistant",
  "watcher": "local",
  "image_id": "sha256:d4a6fafb7d4da37495e5c9be3242590be24a87d7edcc4f79761098889c54fca6",
  "image_registry_url": "123456789.dkr.ecr.eu-west-1.amazonaws.com",
  "image_name": "test",
  "image_tag_value": "2021.6.4",
  "image_tag_semver": true,
  "image_digest_watch": false,
  "image_digest_repo": "sha256:ca0edc3fb0b4647963629bdfccbb3ccfa352184b45a9b4145832000c2878dd72",
  "image_architecture": "amd64",
  "image_os": "linux",
  "image_created": "2021-06-12T05:33:38.440Z",
  "display_name": "Home Assistant",
  "display_icon": "sh:homeassistant",
  "result_tag": "2021.6.5",
  "update_available": true
}

Home-Assistant integration

logo

Drydock integrates with Home Assistant via MQTT Discovery.

services:
  drydock:
    image: codeswhat/drydock
    ...
    environment:
      - DD_NOTIFICATION_MQTT_MOSQUITTO_URL=mqtt://localhost:1883
      - DD_NOTIFICATION_MQTT_MOSQUITTO_HASS_ENABLED=true
      - DD_NOTIFICATION_MQTT_MOSQUITTO_HASS_DISCOVERY=true

Check that mqtt integration is properly configured

image

A drydock device is automatically added to the hass registry

image

Entities are automatically created (per Docker image)

image

Entities are binary_sensors whose state is true when an update is available.

Discovery entities follow the same include/exclude scope as this trigger. A discovery entity is only created for a container this mqtt trigger actually covers — dd.notification.include/dd.notification.exclude (and rollback/agent scoping) apply to entity creation, state publishes, and the Install button's commands alike. If a container is excluded, drydock does not create an entity for it, so it never sits stuck on "Unknown" in HA; if a previously-created entity's container becomes excluded later, its discovery config is removed automatically the next time the container is seen.

Entities

image

Entities expose all the details of the container as attributes:

  • Current version
  • New version
  • Registry
  • Architecture
  • OS
  • Size
  • ...

Install button (bidirectional commands)

Set DD_NOTIFICATION_MQTT_{trigger_name}_HASS_COMMANDS=true to let Home Assistant trigger container updates back, not just observe them. When enabled, drydock adds a command_topic to each container's discovery payload ({state_topic}/cmd), so the update entity's Install button becomes clickable in the HA UI. Clicking it publishes the literal payload install to that topic; drydock subscribes to it and runs the same update path the /update/:containerName webhook uses, including trigger resolution, agent routing, and every existing eligibility check (no update available, active operation in progress, rollback blockers, and so on).

services:
  drydock:
    image: codeswhat/drydock
    ...
    environment:
      - DD_NOTIFICATION_MQTT_MOSQUITTO_URL=mqtt://localhost:1883
      - DD_NOTIFICATION_MQTT_MOSQUITTO_HASS_ENABLED=true
      - DD_NOTIFICATION_MQTT_MOSQUITTO_HASS_DISCOVERY=true
      - DD_NOTIFICATION_MQTT_MOSQUITTO_HASS_COMMANDS=true

A few things worth knowing before you turn this on:

  • HASS_COMMANDS needs HASS_ENABLED, but not HASS_DISCOVERY. If you've hand-configured your own HA update entity pointing at drydock's command topic convention ({topic}/{watcher}/{container}/cmd, or {topic}/agent/{agent}/{watcher}/{container}/cmd for agent-owned containers), commands work without drydock ever publishing discovery messages itself. {container} is the identity-based segment described above (the Compose project.service pair, or the sanitized container name), as of v1.8.0.
  • Each container is rate-limited to one accepted command every 30 seconds. Repeated clicks within that window are dropped silently on drydock's side; this isn't configurable yet.
  • Commands only work while drydock is connected to the broker. The MQTT connection is a clean (non-persistent) session, so a click sent while drydock is offline or reconnecting doesn't queue up and replay later, it's just lost.
  • If two containers somehow sanitize down to the same state topic, drydock refuses to guess and drops the command rather than updating the wrong one.
  • Since anyone who can publish to a container's command topic can trigger an update, scope your Home Assistant broker credential tightly: publish-only access to {topic}/+/+/cmd and {topic}/agent/+/+/+/cmd is all it needs.

Update progress

Once an update is running, drydock reports how far along it is on the same state topic the entity already reads, so the Install button shows a live progress bar instead of sitting there until the next watch cycle. The container's state payload carries an update_state object with Home Assistant's own installed_version, in_progress and update_percentage fields, and the entity's value_template hands that object straight to HA.

The percentage is a fixed ladder over the phases an update walks, not a measurement: pulling the image reports 10, the image scan 30, preparing the new container 40, renaming 50, creating 60, stopping the old one 70, starting the new one 80, and the health gate 85 through 95. A rollback reports 90. Drydock has no byte-level progress from the registry pull to work with, so these numbers tell you which step is running, not how much time is left.

When the operation finishes, drydock publishes in_progress: false with a null percentage and the entity returns to its normal state. That happens on every outcome, so a failed or rolled-back update clears the progress bar exactly like a successful one; the installed and newest versions keep whatever the container itself reports. Progress is derived from the live operation each time drydock publishes, so a restart part-way through an update can't leave an entity spinning forever.

This needs HASS_ENABLED, but not HASS_DISCOVERY. If you hand-configured your own update entity, point its value_template at the new object to pick progress up:

value_template: >-
  {% if value_json.update_state is defined %}{{ value_json.update_state | to_json }}{% else %}{{ value_json.image_tag_value }}{% endif %}
Existing payloads keep working. update_state is added to the state payload, nothing was removed or renamed, and the template falls back to the old behaviour for a retained message published by an older drydock. Home Assistant only reads in_progress and update_percentage from the object the value_template renders, so a template that emits a bare version string ignores them.

Attribute filtering

Container MQTT payloads can include large nested fields (SBOM documents, scan vulnerability arrays, environment details, labels) that exceed Home Assistant's json_attributes_topic size limits or clutter entity attributes.

Filtering is applied in this precedence order:

  1. HASS_FILTER_INCLUDE (flattened key include list)
  2. HASS_FILTER_EXCLUDE (flattened key exclude list)
  3. EXCLUDE (legacy nested dot-path exclude list)
  4. HASS_ATTRIBUTES preset fallback

Include mode (HASS_FILTER_INCLUDE)

Set DD_NOTIFICATION_MQTT_{trigger_name}_HASS_FILTER_INCLUDE to keep only selected flattened keys.

environment:
  - DD_NOTIFICATION_MQTT_MOSQUITTO_HASS_FILTER_INCLUDE=name,image_name,update_available,result_tag

Exclude mode (HASS_FILTER_EXCLUDE)

Set DD_NOTIFICATION_MQTT_{trigger_name}_HASS_FILTER_EXCLUDE to remove selected flattened keys.

environment:
  - DD_NOTIFICATION_MQTT_MOSQUITTO_HASS_FILTER_EXCLUDE=security_sbom_documents_0_spdx_version,security_scan_vulnerabilities_0_id

Legacy nested exclude (EXCLUDE)

DD_NOTIFICATION_MQTT_{trigger_name}_EXCLUDE still supports comma-separated nested dot-paths (for example security.sbom.documents,labels). This legacy filter is only used when HASS_FILTER_INCLUDE and HASS_FILTER_EXCLUDE are both empty.

Attribute preset fallback (HASS_ATTRIBUTES)

When no explicit include/exclude list is configured, HASS_ATTRIBUTES is used:

PresetBehavior
fullSends the entire container object — no filtering
short (default)Excludes security.sbom.documents, security.updateSbom.documents, security.scan.vulnerabilities, security.updateScan.vulnerabilities, details, and labels
HASS_FILTER_INCLUDE and HASS_FILTER_EXCLUDE match flattened MQTT keys (snake_case + underscore delimiter), because filtering is applied after flattening to the payload shape Home Assistant receives.