Skip to content

v0.18.6.

Complete release notes · 2026-10-27. Read upgrade instructions and runtime limits before deploying.

Fixed

  • Pull the published candidate image on every independent release smoke runner before a smoke inspects it, and check the complete secret-provider list in upgrade and user-journey smokes.
  • Keep local Docker retirement journals under the writable panel data directory, preventing permission errors from blocking later deployments and canary rollouts.

Added

  • Paketo and Heroku buildpacks (G4.4). Two new build packs, paketo and heroku, build with Cloud Native Buildpacks through the pack CLI.

    • Pinning: the Dockerfile and install.sh install pack 0.40.9 checksum-verified, as they do Nixpacks and Railpack. Each builder image is pinned by digest. Paketo's base builder covers amd64 only; Heroku's covers amd64 and arm64.
    • Panel host only: a service that a node builds is refused when it is saved, at deploy and in build placement. It never quietly falls back to a Dockerfile build. Choose Build on: panel to ship the image to the node.
    • Build env: it goes through a private env file, never argv, and is not written into the image.
    • Untrusted builder: builds run with --trust-builder=false, so the repository's own build code never shares a container with the Docker socket. pack would otherwise trust the Paketo and Heroku builders, run one container with the socket mounted, and let a member's repository take over the host.
    • Caches: each service keeps one pair of cache volumes, removed with the service. pack's default would have left a new pair per deploy.
    • Platform: builds target the panel host's platform.
    • Containerd image store: Docker's containerd image store (the default for new Docker 29 installs) breaks the Heroku builder whenever the build has env. The deploy log names the fix.

    Both builds were run for real against the pinned builders with Docker 29: a Node app built with Paketo, served its page, and its build env was absent from the image config. Heroku built on this host only without build env; with env it hit the containerd fault. No migration. See PRIVATE_REPO_GUIDE.md §6.

  • Dokploy template converter (G4.7). scripts/buildDokployMirror.ts turns a checkout of the Dokploy blueprint catalog (MIT) into a NineDeploy template bundle, next to the Coolify mirror.

    • What it converts: each blueprint's .env is folded into the compose file. Its generators become the matching SERVICE_* magic tokens: ${password:N}, ${base64:N}, ${hash:N}, ${domain}, ${username} and ${email}.
    • What it skips: any blueprint without a faithful equivalent, with the reason: file mounts, more than one routed service, ${uuid} and ${jwt}, among others.
    • Results: 383 of today's 586 blueprints convert. In a first smoke run, n8n, Umami and NocoDB started. The upstream Ghost blueprint did not: it has no depends_on, so Ghost restarts until MySQL is ready.
    • Catalog: nothing joins the bundled catalog until it passes the smoke run.

    The compose safety checks are now shared by both converters. smol-toml is a new dev dependency, used only by the build script. No migration. See TEMPLATE_MIRRORS.md.

  • More secret managers (G3.2). New reference forms, configured under Settings → Integrations → Secret managers:

    • ${{ssm:/name}} reads AWS Parameter Store with the existing AWS credentials.
    • ${{gcp:secret[@version]}} reads GCP Secret Manager with a service account key.
    • ${{azure:name}} reads Azure Key Vault with an app registration.
    • ${{op:vault/item/field}} reads 1Password Connect.
    • ${{exec:name}} runs an operator-defined command and uses its stdout. This covers any manager that has a command-line client, such as Bitwarden, Doppler, op, pass or sops.

    All forms except op also take #jsonKey. Exec runs without a shell and without the panel's environment. It stays off unless the panel was started with NINEDEPLOY_SECRET_EXEC=1.

    Like Vault and AWS references, the new forms stay literal (with a deploy-log warning) until their provider is configured, and the per-workspace allowlist governs them. ninedeploy secrets providers test|delete accept every kind. No migration. See SECRET_MANAGERS.md §3a.

  • Automatic DNS records in Amazon Route 53, OVHcloud, Porkbun and Hetzner DNS (G3.5). The new card is under Settings → Integrations → DNS records: Route 53, OVH, Porkbun, Hetzner. Saving credentials there makes that provider the active one: a domain gets its A, AAAA or CNAME record when it goes live, and the record is deleted with the domain. List zones checks the credentials. Records that already exist are never changed.

    • Route 53 skips private hosted zones and needs no AWS SDK.
    • OVH refreshes the zone after each change.
    • Hetzner uses the Cloud API, since the old DNS API has been retired.

    The credentials are stored encrypted and re-encrypted by master-key rotation. Saving the Cloudflare card switched off no longer turns off a different active provider. No migration. See TRAEFIK_INGRESS.md §2.

  • Five more notification channels (G3.7), all in the notification wizard:

    • Mattermost (incoming webhook) and Google Chat (space webhook; only https://chat.googleapis.com URLs are accepted).
    • Matrix: posts to a room with a bot account's access token. Each event has a fixed transaction id, so a retried delivery never posts twice.
    • PagerDuty (Events API v2) and Opsgenie (Alert API), US or EU region. These two receive only problems: fired alerts, a monitor or service going down, a failed deploy, a missed backup. The matching recovery, or the service's next successful deploy, closes the incident it opened. Every other event is skipped and never pages anyone. A channel test opens a test incident and closes it right away.

    alert.fired and alert.recovered events now carry the rule id, so recovery closes the right incident. No migration.

  • Backups for ClickHouse, Meilisearch and RabbitMQ (G1.8): the three engines that answered "backup not supported" now have manual and scheduled backups, off-site copies, restore and the Check drill on the panel host. ClickHouse backs up every user database with BACKUP … TO File and restores each under a temporary name before promotion. Previous copies are retained for recovery on a failed promotion; a multi-database restore is not atomic and may require manual recovery. Meilisearch takes a dump; a restore stops the database and imports the dump with a one-off container into a new path, swapping it in only once the import is healthy. RabbitMQ backs up its definitions (users, vhosts, queues, exchanges, bindings, policies), not queued messages; a restore waits until everything in the file exists. Each flow was run against the real images. Node databases of these engines still refuse backups, now with a message that says why. No migration. See DATABASES_BACKUPS.md §10.

  • Email templates in the web UI (G1.5): workspace admins can edit, preview and reset the workspace's email templates on the Workspaces page. Account emails such as the password reset are shown as fixed. Until now this needed ninedeploy email-templates or the API.

  • Outgoing IP per project in the web UI (G1.5): Settings → Security has an operator-only Outgoing IP per project card that lists the egress rules and sets or removes one, picking the project and one of the panel host's IPv4 addresses. Traffic from that project's containers then leaves through that address (iptables SNAT). Until now this needed ninedeploy egress or the API.

  • Firebase push (FCM) channels in the web UI (G1.5): the notification wizard creates an FCM channel from a device registration token and a Firebase service account JSON, checking that the JSON has the fields the server needs before continuing. Until now only ninedeploy notifications create-fcm could create one.

  • Shipped-log search in the web UI (G1.5): a service's Activity Logs tab can search the lines its Loki log drain received, over a window from 15 minutes to 7 days, including lines of containers that no longer exist. Until now only ninedeploy logs search and the API could do this.

  • Backup checks in the web UI (G1.5): each backup in a database's Backups tab has a Check button that runs the existing integrity drill, and a Last check column with the newest verdict (passed, failed or unverifiable). Until now drills ran only from the CLI and API.

  • PgBouncer in the web UI (G1.5): a PostgreSQL database on the panel host gets a PgBouncer connection pooling card showing the sidecar's state, port, pool mode and the pooled connection URL to copy. Instance operators can enable it (with an optional port) or disable it there; until now this was only possible through the CLI, SDK and API.

  • Self-hosted GitLab sources (G1.2): a gitlab source can now have a base URL, including a relative path such as https://example.com/gitlab, with the same https and private-address rules as Gitea. The project list, the branch picker and the live token test use that instance; without a base URL a source still means gitlab.com. GitLab branches are now actually listed, including projects in nested groups; until now they always fell back to main/master. Available in the web UI, ninedeploy sources add --base-url and the API. No migration.

  • R2 development: persist plain-Docker node deployment authority in a private journal atomically with actual admission. Preserve queued predecessors, retain the original owner across configuration-snapshot rewrites, refuse old ownership after Stop or a newer deployment, and retain node identity across removal. Migration 0104 preserves existing work and deployment API payloads. Physical prepare/network/retire and scale/wake integration remain open.

  • R2 development: accept a durable node Stop before the first deployment has selected a runtime. Cancel queued, building, deploying, CI-waiting and approval-waiting work atomically with runtime ownership, fence every accepted node, capture immutable managed generations and confirm routing withdrawal and physical Stop before completion. Nullable Stop anchors retain existing journal data and command revision high-water marks through migration 0103. The new protocol requires capable 0.19.0 agents; production deploy/scale/wake integration and full R2 acceptance remain open.

Fixed

  • Docs: ROADMAP.md no longer lists SAML as shipped. Creating a SAML provider has been refused since r354; OIDC and SCIM are the supported SSO paths.

  • R2 development: fence superseded node deployment continuations after delayed successful or rejected agent replies. Keep newer owners and moved services intact, recheck SQL publication/failure writes at execution, retain unresolved effects for the current owner and suppress obsolete outcome callbacks. An in-flight SQL response cannot reverse an already observed ownership loss. Ordered physical mutation integration remains open.

  • Ignore stale pipeline invocations for non-claimable deployment rows before build, routing, hooks or lifecycle outcomes; stop continuations that lose the conditional queued/building claim. Preserve eligible worker handoffs, explicit Stop cleanup and log-writer ownership.

  • Clear an additional node's pending deployment status after its generation-independent Stop completes. Lost routing confirmation retains the captured targets for retry, while a newer runtime selection prevents the old Stop from resuming.

  • Offer Stop during the first plain-Docker node deployment, show the pending verification state, refresh cancelled deployment history and hide Start while no node runtime has been selected.

Source CHANGELOG.md at 6046cfe8