v1.8.0-rc.6
Full Changelog: v1.8.0-rc.5...v1.8.0-rc.6
[1.8.0-rc.6] - 2026-10-23
Warning
Release candidate for opt-in testing. SQLite changes the on-disk store. Stop Drydock and back up the complete store directory before upgrading from v1.7 or earlier. First startup imports existing JSON data into SQLite. Downgrading requires restoring the intended pre-upgrade JSON snapshot and any separate session file; changes made only in SQLite are not carried back. A fresh v1.8 installation has no pre-upgrade JSON snapshot. Start with a copy of your store and test containers.
This candidate includes the v1.8.0-rc.1 features, the v1.8.0-rc.2 fixes, and the v1.8.0-rc.3, v1.8.0-rc.4 and v1.8.0-rc.5 changes, and retains their upgrade warnings. The image no longer bundles Trivy or cosign, and Grype is now the default scanner. DD_SECURITY_BACKEND=auto runs Trivy and cosign in pinned worker containers, so if you set DD_SECURITY_SCANNER=trivy you will see a deprecation warning, and Drydock needs permission to create and attach containers on its Docker socket. Without that permission, use DD_SECURITY_BACKEND=remote with a Trivy server or mount a Trivy binary through DD_SECURITY_TRIVY_COMMAND. The Update Available notification rule now explains when it skips automatic actions (#1281). v1.8 GA is not released; v1.7.0 (GA 2026-10-19) is the latest stable release. RC6 has its own publication-based soak.
Security
- Bump
nodemailerto 10.0.10 (GHSA-39m8-27wv-hr27, used by the SMTP notification trigger) and theip-addressoverride to 10.7.4 (GHSA-rxp7-6hxx-x8q5), and the website'sdompurifyto 3.4.17 (GHSA-c6gr-qvv4-5v3f, GHSA-mrv2-3pqr-j5jr). - Bump the website and docs app to
next16.4.2 for GHSA-p3m5-59mp-jv5r and GHSA-2464-fhr4-w9xw (website only, not in the Drydock image). - OIDC bearer tokens are now opt-in and must be issued for Drydock's client. An
Authorization: Beareraccess token is only accepted whenDD_AUTH_OIDC_{name}_BEARER=true, and then only if itsaudcontains the client ID (or aDD_AUTH_OIDC_{name}_BEARERAUDIENCESentry) or itsazp/client_idequals it, or, for opaque tokens, RFC 7662 introspection returns an active token for this client. Setups that send OIDC access tokens to the API must set the variable. The browser login flow is unchanged (DR-10). See DEPRECATIONS.md. - Remove the vendored Trivy binary and the cosign package from the Docker image. Their Go module graphs produced findings Drydock could not patch independently, so
.grype.yamlignored both binary locations and the image scan never covered them. The file now carries no ignore rules, and every package in the image is gated.
Fixed
- Trivy versions can no longer drift apart. The default Trivy worker had stayed on 0.72.0 while the image bundled 0.73.0; the image no longer bundles Trivy, so the worker default is the only Trivy pin, a test fails if another appears, and the release SBOM uses Syft.
- The Update Available notification rule also gates automatic actions, and Drydock now says so. With that rule turned off, a batch action trigger (such as
dockercompose) skipped every pending update with no log line and no reason in the UI, so updates sat there and never installed. The batch path now logs the rule being off, a group policy holding a container back, a threshold miss and a trigger-condition exclusion, in the same wording as the simple path. The container's update status also gets a soft "Update Available rule is off" reason that points at Settings > Notifications > Update Available; manual updates are unaffected. - The Security view now shows the scanner version, image digest, and vulnerability database or cache update time in each scanner's tooltip, using the asset details the API already returned.
- Show the maturity countdown wherever a held update is listed. A candidate held by
maturityMode: maturenow shows the time left and its local unlock date and time in the container list and card tooltips, in a compact label next to the clock icon, and on the Updates Available dashboard widget, ticking once a minute from one shared clock. Previously only the container detail panels had it. - Show when a notification was last skipped. The container detail notification section already named the reason, and now also shows the local date and time of the last skip for each trigger.
- Key the config
sourcesmap by the setting name for a secret-file entry. A_file(DD_X__FILE) entry was listed underDD_X__FILEat boot and underDD_Xafter a reload, while the docs showedDD_X; both now useDD_X.POST /api/v1/config/validateno longer reports an unchanged_fileentry as changed, and the editors label a field set by an environment variable as environment-owned even when the file also has a_fileentry for it. (CFG-1) - With
watchallon, a slow replacement no longer leaves the renamed old container as a duplicate row that could take the replacement's retained update policy. The old container is skipped while its replacement stands, so the policy stays on the replacement (DR-147). - Send the
update-pending-approvalnotification. The rule existed and could be enabled, but nothing dispatched to it, so enabling it did nothing. It now fires once when an update candidate enters the approval queue (not under update modenotifyor with container actions disabled, where approving is refused), follows the rule's enabled flag, trigger selection, templates and batch modes like the other rules, and links to the queue row whenDD_PUBLIC_URLis set. It is deduplicated on the queue row, so a re-seen pending candidate or a restart doesn't notify again. The rule still ships disabled. - Stop the Closing Keywords Guard from failing every dev to main sync PR. A sync head has main as its first parent, so the guard now checks only the sync commit's own message instead of the whole dev history since the branch cut (DR-100b).
- Check very large GHCR repositories quickly. After a listing hits the 50-page limit once, Drydock lists only the tags pushed after the container's current tag for the next 24 hours instead of re-reading the first 50 pages on every check (REG-PERF-1, follow-up to #1453).
- Keep a candidate that is below an auto action's threshold in the approval queue. With update mode
autoand an action trigger set toAUTO=allwith aminorthreshold, a major update was marked auto-applied and dropped from the queue, yet the trigger never ran it. It now waits for approval, and the container's action policy reads manual instead of auto. - Set the document direction for right-to-left locales. Arabic now sets
dir="rtl"on the page at boot and when you switch language (and back toltrfor the rest), and the app shell, tab bar, notification bell, detail panel and table headers use logical start/end spacing so the layout mirrors instead of staying left-anchored. - Keep anonymous volumes when recreating a container. Containers whose image declares a
VOLUMEwith no named volume or bind mount, such as a defaultpostgresormysqlsetup, came back from an update with a fresh empty volume while the old one, with the data on it, was left orphaned. The docker and dockercompose actions now reattach the existing volume at the same path, asdocker compose updoes. - The Docker Compose action no longer overwrites an image set through a variable (
image: ${IMAGE},image: repo/app:${TAG},${REG}/app:${TAG:-1.0}) with a literal tag. It used to flatten the reference, which silently broke.env-driven setups, and it never updated.env. The update is now refused before any file write or container change, in previews and dry runs too, with an error that names the variable to update. An escaped$$is still rewritten. Writing the new value back to.envis planned for v1.9. - Protect the SQLite store across upgrades and downgrades. Before applying a pending schema migration to an existing
dd.sqlite, Drydock now writes a consistentdd.sqlite.pre-migration-v<version>-<timestamp>snapshot next to it (newest three kept) and stops startup with a clear error if it can't, rather than migrating without a restore point. A database migrated by a newer build is now refused by an older one with an error naming both schema versions and how to restore, instead of being opened. See Storage. - Docker Hub, Quay, Harbor and Azure Container Registry couldn't call the registry webhook at all.
POST /api/v1/webhooks/registryonly accepted an HMAC signature, and none of those four registries can produce Drydock's HMAC (Docker Hub and Quay can't sign or set headers, Harbor and ACR can only send a fixed auth header), so five of the six parsers were unreachable without a signing relay. A provider can now accept a static secret instead: in the URL path (/api/v1/webhooks/registry/<provider>/<secret>), in?secret=, or as a staticAuthorization: Bearer <secret>header on/api/v1/webhooks/registry/<provider>. Set the secret withDD_SERVER_WEBHOOK_STATICSECRET(orDD_SERVER_WEBHOOK_REGISTRIES_<PROVIDER>_SECRET, both with__FILEsupport, 16 characters minimum) and choose the modes per provider withDD_SERVER_WEBHOOK_REGISTRIES_<PROVIDER>_MODES(hmac,path,query,bearer; defaulthmac, so nothing changes until you opt in). Secrets are compared in constant time and never logged (the path segment is masked and the query string dropped), failed attempts get a tighter rate limit than successful ones, and the bare/api/v1/webhooks/registryendpoint stays HMAC-only. URL secrets end up in registry-side logs, so use a dedicated, rotatable value. The webhooks docs now show the exact setup for each registry. - The update-status panel now shows the Manual and Blocked action policy badges, not only Auto, and the full-page associated-triggers rows carry the per-trigger policy tooltip and Auto badge like the other trigger lists.
- Translate the remaining hard-coded UI strings and gate against new ones. Seven English literals are now localized in all 17 locales: the yes/no values in the container preview and compose-write rows, the "Page" and "unknown image" search subtitles, the preview-request fallback error, and a missing
containerComponents.groupedViews.updateFailedkey that rendered its raw key path when an update failed over SSE. New i18n specs fail CI when a key used inui/srcis absent from the English locale files (including dynamict(\prefix.${x}`)` keys) and when a user-facing literal reaches a template expression, toast, dialog or label property outside a documented allowlist. - Translate the remaining hard-coded UI strings and gate against new ones. The "Page" search subtitle is now localized in all 17 locales, along with a missing
containerComponents.groupedViews.updateFailedkey that rendered its raw key path when an update failed over SSE. New i18n specs fail CI when a key used inui/srcis absent from the English locale files (including dynamict(\prefix.${x}`)` keys) and when a user-facing literal reaches a template expression, toast, dialog or label property outside a documented allowlist.
Added
- Decide several queued updates at once. The approval queue gets row checkboxes, a select-all box and a selection bar with Approve selected and Reject selected, backed by the new
POST /api/v1/approvals/bulk(up to 200 ids, withapprove,rejectordeferas the action). Each id is decided exactly as its single-row route would decide it, so a row that would be refused on its own is reported in therejectedlist with the same status and message and the rest still go through, and every decided row writes its own audit entry. Bulk approve requires theX-DD-Confirm-Action: approvals-bulk-approveheader and a confirmation dialog in the UI that lists the hold reasons it overrides and skips hard-blocked rows, and uses the samecontainers:updatescope as the single decisions. - Show recent notification skips per trigger. Open a notification trigger in the Triggers view, or a rule's assigned triggers in the Notifications view, and Recent skips lists the containers it skipped, newest first, with a readable reason and a link to each container. The same data is served by
GET /api/v1/triggers/{type}/{name}/skips(and/{agent}/skipsfor agent triggers) under thereadscope, paginated withlimit(default 50, maximum 200) andoffset, and documented in the OpenAPI spec. It reads the existing in-memory skip record, so there is no new storage. - Prometheus lockout counters
drydock_auth_account_lockout_totalanddrydock_auth_ip_lockout_total, labelled by provider, increment once each time a login lockout starts. The existingdrydock_auth_account_locked_totalanddrydock_auth_ip_locked_totalgauges only show lockouts active at scrape time. The monitoring docs list the new counters and add an alert example and dashboard panel. - Category-specific trigger API routes that match the
DD_ACTION_*/DD_NOTIFICATION_*split:/api/v1/actions(docker,dockercompose,portainer,command) and/api/v1/notification-triggers(every other provider). They list, get and run triggers with the same auth and response shapes as/api/v1/triggers, and a trigger of the other category returns 404./api/v1/notificationsstays the notification rules collection. The UI trigger service now uses the new routes, and the OpenAPI spec and API docs cover them. - Optional dashboard cards and a Recent Activity widget. The dashboard Customize panel now lists Images, Triggers and Watchers stat cards and a Recent Activity widget, all hidden by default so saved layouts look exactly as before. Images counts distinct images and how many have updates, Triggers splits configured actions from notifications, and Watchers shows the count with a red state when an agent-backed watcher is disconnected. Each card links to its view. Recent Activity shows the latest 10 audit entries (time, actor, action, target), links each row to the Audit view filtered to that container, fetches only while shown, and refreshes on container and update events plus a 60 second interval. Preferences schema moves to version 13 to hide the new widgets once for existing layouts.
- The UI now shows a banner when the browser is offline or the Drydock server is unreachable, and clears it and refreshes the open views when the connection returns. While it's up, the dashboard and container views render the last known data with a "last updated" time, and update, start/stop, policy, rollback and delete actions are disabled with a tooltip. A 502, 503 or 504 from a reverse proxy counts as unreachable once a confirming request agrees, so one transient 503 doesn't flash the banner. The saved copy covers only a short list of safe read endpoints, is used only after the server can't be reached, expires after 24 hours, and is wiped on sign-out, sign-in and any 401. Also adds the missing
apple-mobile-web-app-status-bar-stylemeta tag for the installed iOS app.
Deprecated
/api/v1/triggersis deprecated in favor of/api/v1/actionsand/api/v1/notification-triggers. It keeps working and now sendsDeprecation: trueand aLink: <successor>; rel="successor-version"header, and logs a one-time warning per process. Removal is planned no earlier than v1.9.0 (see DEPRECATIONS.md).
Changed
- Delete the container's
updateOperationfield and the SSE bridge that kept it in sync with the operations store (DR-89, slice 5). Every view already reads update progress from the store, and a new architecture test fails on any new use of the field. Behavior is unchanged. - Add an architecture test that fails on toast calls outside the update lifecycle layer (DR-90). Existing feedback toasts are allowlisted per file with a count that can only go down, update outcome toasts can only come from the operations store, and a toast binding can't be handed to a helper. Also drops an unused
useToast()binding fromContainersView. - Containers that share a replaced container's network namespace or volumes now follow it. When Drydock updated a container that others attach to with
network_mode: container:<id>orservice:<name>(the Gluetun pattern) orvolumes_from, those containers kept pointing at the removed container and lost their network, and restarting them failed with "joining network namespace of container: No such container". Drydock now recreates them against the new container, keeping the rest of their configuration. - Update images for services defined in included compose files. Compose records only the files named on the command line in the
config_fileslabel, never the ones pulled in withinclude:, so Drydock could not find a service defined in an included file and skipped the compose file rewrite. Drydock now followsinclude:entries (nested ones too) from the listed files, stays inside the allowed compose roots, and rewrites the file that defines the service. Services that inherit their image throughextendsare still left alone. - Scanners now run outside the image.
DD_SECURITY_BACKENDhas a new default,auto: for each provider Drydock runs a local command whenDD_SECURITY_<PROVIDER>_COMMANDis set or the binary is onPATH, and a hardened, digest-pinned Docker worker otherwise.command,dockerandremotemean what they did before, andremotestays Trivy-only. The release SBOM is now generated with Syft instead of Trivy, in the same SPDX 2.3 format. - Grype leads the docs, quick start and compose examples as the recommended scanner. An empty
DD_SECURITY_SCANNERstill means scanning is disabled. DD_SECURITY_SCANNER=trivyorbothwith no Trivy binary on the machine keeps working through the pinned Trivy worker. Drydock logs one warning per process, counts it in the deprecation banner, and fails the scan with an error naming the three working setups (DD_SECURITY_SCANNER=grype,DD_SECURITY_BACKEND=remotewithDD_SECURITY_TRIVY_SERVER, or a mounted binary set throughDD_SECURITY_TRIVY_COMMAND) if Docker refuses to create or attach the worker. See DEPRECATIONS.md.- cosign signature verification is now a worker provider with the same hardening as the scanner workers: a pinned image, a read-only root filesystem, all capabilities dropped, no Docker socket, and an environment allowlist. It receives the target image's registry credentials the way Grype does, a
DD_SECURITY_COSIGN_KEYfile is passed to it by value, andDD_SECURITY_COSIGN_COMMANDis still honored. The newDD_SECURITY_COSIGN_WORKER_IMAGEoverrides the image, and the Security view andPOST /api/v1/server/security/assets/cosign/{pull,warm}manage it. - The scanner worker pins move to Grype 0.121.0, Syft 1.55.0, Trivy 0.76.1 and cosign 3.1.4, referenced from
ghcr.ioby tag and index digest. Renovate now tracks those constants.