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

The mqtt trigger lets you send container update notifications to an MQTT broker.
Variables
| Env var | Required | Description | Supported values | Default value when missing |
|---|---|---|---|---|
DD_NOTIFICATION_MQTT_{trigger_name}_URL | 🔴 | The URL of the MQTT broker | Valid 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 use | dd_$random | |
DD_NOTIFICATION_MQTT_{trigger_name}_TOPIC | ⚪ | The base topic where the updates are published to | dd/container | |
DD_NOTIFICATION_MQTT_{trigger_name}_HASS_ENABLED | ⚪ | Enable Home-assistant integration and deliver additional topics | true, false | false |
DD_NOTIFICATION_MQTT_{trigger_name}_HASS_DISCOVERY | ⚪ | Enable Home-assistant integration including discovery. When omitted, defaults to the value of HASS_ENABLED. | true, false | Same as HASS_ENABLED |
DD_NOTIFICATION_MQTT_{trigger_name}_HASS_PREFIX | ⚪ | Base topic for hass entity discovery | homeassistant | |
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, false | true |
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, false | false |
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, false | true |
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, false | false |
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, short | short |
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 trusted | true, false | true |
simple mode.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.dd.display.name and dd.display.icon labels.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.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.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:1883Post 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.pemPost 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/imageExample 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

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=trueCheck that mqtt integration is properly configured

A drydock device is automatically added to the hass registry

Entities are automatically created (per Docker image)

Entities are binary_sensors whose state is true when an update is available.
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

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=trueA few things worth knowing before you turn this on:
HASS_COMMANDSneedsHASS_ENABLED, but notHASS_DISCOVERY. If you've hand-configured your own HAupdateentity pointing at drydock's command topic convention ({topic}/{watcher}/{container}/cmd, or{topic}/agent/{agent}/{watcher}/{container}/cmdfor agent-owned containers), commands work without drydock ever publishing discovery messages itself.{container}is the identity-based segment described above (the Composeproject.servicepair, 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}/+/+/cmdand{topic}/agent/+/+/+/cmdis 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 %}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:
HASS_FILTER_INCLUDE(flattened key include list)HASS_FILTER_EXCLUDE(flattened key exclude list)EXCLUDE(legacy nested dot-path exclude list)HASS_ATTRIBUTESpreset 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_tagExclude 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_idLegacy 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:
| Preset | Behavior |
|---|---|
full | Sends 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.