Skip to content

Changelog.

136 releases and counting. Highlights and upgrade notes from the newest 24; read the full release notes before upgrading.

  1. v0.15.8

    Oct 10, 2026

    Optional pinning of a Git host's SSH keys for deploy-key sources, and `deploys watch` that exits non-zero after a failed deploy.latest
    Full v0.15.8 release and upgrade notes

    Upgrade notes5

    • One small migration, 0073. It adds two columns to sources (a flag that defaults to off, and a nullable text). No row is rewritten, so the upgrade is instant and a rollback to 0.15.7 boots on the migrated file.
    • Nothing changes for an existing source. Pinning is off for every one of them, which keeps today's behaviour: each deploy accepts the host key it is shown and refuses one that changes during that deploy.
    • ninedeploy deploys watch now exits 1 when the deploy ended failed or cancelled. Until now a clean end of the log stream exited 0 whatever the outcome, so deploys watch && next-step carried on after a failed deploy. If a script…
    • Rolling back to 0.15.7 is supported. Pinning stops being enforced (clones are back to the per-job policy) and the pinned keys stay in the table for when you upgrade again. docs/ROLLBACK.md has the note.
    • Verified before release: in-place upgrades from 0.15.7 and from 0.10.45, a rehearsed rollback to 0.15.7, the user-journey smoke (which now also drives the pinning setting), and a clone against a real OpenSSH server: the first clone…

    Added1

    • Pin a Git host's SSH keys (opt-in, per source). Sources → *Pin git host keys*, or ninedeploy sources pin-host-keys <id> on|off. The first clone after switching it on learns the host keys (only after the clone succeeded, which needs…

    Fixed1

    • deploys watch exited 0 after a failed or cancelled deploy (see the upgrade notes).
  2. v0.15.7

    Oct 10, 2026

    175 more one-click templates (130 → 305), every one of them started in an isolated container before it was listed, and password-reset and invitation emails through Resend.
    Full v0.15.7 release and upgrade notes

    Upgrade notes5

    • No migration (0072 is still the latest). The catalog is data that ships with the panel; nothing about an existing service changes.
    • Services you already deployed are untouched. A template is only read when you deploy from it, and a stack that is already running keeps the compose file it was created with.
    • New templates need Docker Compose on the host, like every compose template before them, and are for operators only (compose stacks always were). Five of them mount the Docker socket or run privileged (Portainer, Glances, Authentik,…
    • Rolling back to 0.15.6 is supported. Stacks deployed from the new templates keep running. 0.15.6 does not know those template ids, so it cannot show them in the Hub; redeploying such a stack from its service page still works.
    • Verified before release: in-place upgrades from 0.15.6 and from 0.10.45, a rehearsed rollback to 0.15.6, and the user-journey smoke, which now also lists the catalog and deploys one converted stack through the panel's own compose pipeline.

    Added2

    • 175 templates from the Coolify catalog, each marked verified in the Hub: Actual Budget, AnythingLLM, Authentik, Docmost, Immich, Karakeep, Kimai, Listmonk, Logto, Matrix Synapse, Miniflux, Paperless, Vikunja and many more. Each was…
    • Password-reset and invitation emails can go through a Resend channel. They used the SMTP channel only, so an install with just a Resend channel sent nothing. SMTP is still used first when both exist, so nothing changes for installs that…

    Fixed2

    • The Coolify mirror converter produced stacks Docker Compose refuses or runs wrongly. It now declares the named volumes Coolify creates implicitly ("refers to undefined volume"), drops the Coolify-only exclude_from_hc and…
    • The template smoke now generates SERVICE_* values with the panel's own resolver (a HEX_ token is hex, a URL token is a URL), tears down a failed stack at once, and can test a candidate bundle before it is added to the catalog.
  3. v0.15.6

    Oct 10, 2026

    Two new database engines (KeyDB and Dragonfly), two new notification channels (Microsoft Teams and Resend), and a fix for secrets leaking into pushed Nixpacks images.
    Full v0.15.6 release and upgrade notes

    Upgrade notes5

    • No migration (0072 is still the latest). Database engines and notification channel types are plain text columns.
    • One deploy can now be refused that was not before. A service that builds with Nixpacks, has a push registry set (Service → Settings → Build), and has any secret variable, attached-database credential or vault reference now fails its…
    • KeyDB and Dragonfly databases on nodes need node agent v0.15.6. Creating one on an older agent answers "update the node agent". Everything else in this release is on the panel.
    • Rolling back to 0.15.5 is supported, with two things to know. If a KeyDB or Dragonfly database exists, the 0.15.5 database list answers an error for instance operators and its scheduled backups fail; delete or stop those databases…
    • Verified before release: in-place upgrades from 0.15.5 and from 0.10.45, a rehearsed rollback to 0.15.5, and the user-journey smoke, which now creates a KeyDB and a Dragonfly database for real, enforces the password, takes a backup,…

    Added2

    • KeyDB and Dragonfly databases. Redis-protocol engines that work like Redis in the panel: a generated password, redis:// connection strings, backup and restore, RDB import, the backup drill, public access with TLS, the terminal and…
    • Notification channels: Microsoft Teams (a Workflows or legacy Office webhook, sent as an Adaptive Card) and Resend (the transactional email API). Secrets are encrypted at rest like the other channels, and the test button works for both.

    Fixed3

    • Secrets in pushed Nixpacks images (see the upgrade note above).
    • The backup drill reported valid backups as failed on Docker installs. The drill copies the decrypted backup into a throwaway checker container, and the checker, running as root with every capability dropped, could not read a file owned…
    • The multi-node docs now say that a Nixpacks build on a build server leaves a copy of its build environment in the image on that server until the next deploy.
  4. v0.15.5

    Oct 10, 2026

    Remote nodes, part 4: every multi-node feature is now in the web panel, the CLI, the SDK and MCP. The whole series is verified on two real Docker hosts.
    Full v0.15.5 release and upgrade notes

    Upgrade notes5

    • No migration (0072 from 0.15.2 already has every column this release uses).
    • Update node agents to v0.15.5 to get the clone fixes and the Railpack check below. Everything else in this release is on the panel and works with the agent versions that 0.15.2–0.15.4 required.
    • A successful "Test connection" on a node now refreshes what the panel knows about its agent at once. Before, it took up to 5 minutes after an agent update.
    • Rolling back to 0.15.4 is supported; this release adds no data the older release cannot read. docs/ROLLBACK.md now has one consolidated multi-node runbook for rolling back to 0.15.3, 0.15.2 or 0.15.1.
    • Verified before release: in-place upgrades from 0.15.4 and from 0.10.45, and a rehearsed rollback to 0.15.4; the new two-host smoke: a fresh install, an upgrade from 0.15.1 with an old agent and then an updated one, a rollback to 0.15.1…

    Added7

    • Web panel: Servers: each node shows its agent version and features, with an "update the agent" hint naming what is missing. It also has a build-server toggle with concurrency, Swarm join and leave (with the node opt-in hint), and its…
    • CLI: servers list|roles; services placement|transfers|swarm; swarm status|init|enable|disable|join|leave; volumes create and --server on volume commands; databases create --server; sources allow-on-nodes.
    • SDK: placement, image transfers, Swarm, server roles, node volumes and allowOnNodes. Existing signatures are unchanged and new response fields are optional.
    • MCP: read-only get_service_placement, list_image_transfers, get_service_swarm and get_swarm_status (operator).
    • API: GET /v1/services/:id includes placement. GET /v1/servers includes swarmNodeId and swarmRole. The OpenAPI document describes the node fields, and the schema lists every Swarm and placement error code.
    • and 2 more

    Fixed6

    • A labelled volume attached to a node service was also created on the panel host.
    • Attaching an existing volume by name to a node service checked the panel host. A volume that existed only on the node was refused, and a panel-host volume with the same name was accepted.
    • Node clones from git servers without shallow-clone support (plain HTTP hosting, some self-hosted servers) failed, while the panel's own clone worked. The agent now retries once with a full clone or fetch. Needs agent v0.15.5.
    • Every node deploy printed "network with name ninedeploy already exists".
    • Railpack on a node whose Docker uses the classic image store now stops before building, with the fix in the deploy log: turn on the containerd image store, set NINEDEPLOY_AGENT_BUILDKIT_HOST, or build on the panel or a build server.…
    • and 1 more
  5. v0.15.4

    Oct 9, 2026

    Remote nodes, part 3: Docker Swarm as an opt-in orchestrator. Nothing changes until an operator turns it on.
    Full v0.15.4 release and upgrade notes

    Upgrade notes10

    • No migration (0072 from 0.15.2 already has the columns this release uses).
    • Swarm is off until an operator enables it. The upgrade does not put Docker in Swarm mode or move any service onto a stack. Services with no orchestrator set, or set to container, deploy exactly as before.
    • Joining a node to Swarm needs node agent v0.15.4, and the node's owner must opt in. Set NINEDEPLOY_AGENT_SWARM_MANAGER=<panel advertise address>:2377 in the agent's environment and restart the agent. The agent joins only that manager,…
    • Swarm runs NineDeploy services only on nodes joined through the panel (labelled nd.member=1). A node that joined the swarm another way gets no NineDeploy task.
    • Node owners: a join is also refused while TLS Docker variables (DOCKER_TLS_VERIFY, DOCKER_TLS, DOCKER_CERT_PATH, an https:// DOCKER_HOST) or a non-default Docker context are set on the node.
    • and 5 more

    Added1

    • Docker Swarm (opt-in per service). Turning it on: an operator initialises Swarm on the panel host (an interactive session and a password re-check are required) and enables it. Nodes join and leave through their agents; leave drains the…

    Fixed2

    • The old, unreachable Swarm driver is replaced. Its redeploys changed only the image. It put env on the command line and published ports past Traefik. It wrote state to a directory a Docker install cannot write. It never applied rotated…
    • Deploy-key clones on the panel host no longer accept any SSH host key.
  6. v0.15.3

    Oct 9, 2026

    Remote nodes, part 2: a build server, image transfer between hosts, and managed databases on nodes. Single-host installs keep working as before, apart from the two fixes below.
    Full v0.15.3 release and upgrade notes

    Upgrade notes7

    • No migration (0072 from 0.15.2 already holds every column this release uses).
    • Managed databases on nodes need node agent v0.15.3. Building on the panel or a build server and shipping the image needs v0.15.2. Re-run a node's bootstrap from the Servers page to update it. Older agents keep working as before, and…
    • Two fixes reach existing services on their next deploy: Attached databases are now connected to their service's network. Previously only databases added through a template were, so a database attached any other way could not be reached…
    • Fan-out (copies of a service on other nodes): Copies of a Nixpacks, Railpack or static service now receive the primary's image instead of being rebuilt from a Dockerfile. A copy that needs a command override, the Docker socket or…
    • Attaching a panel-host database to a service on a node now answers 409 instead of creating an attachment that could never deploy.
    • and 2 more

    Added2

    • Build placement. A service can build where it runs (the default, unchanged), on the panel, or on a build server, and then ship the image to its node. Building on the panel keeps private-repository credentials off your nodes. Image…
    • Managed databases on nodes. Operators can place a new database on a node. It runs on its own network, and services on the same node can attach to it. What works there: start, stop, restart, limits, logs, credentials, backups and backup…

    Fixed4

    • Databases attached by hand were never connected to their service's network.
    • A second PgBouncer sidecar failed because every sidecar published port 6432.
    • Fan-out copies silently dropped a service's command override, Docker socket and volumes.
    • Fan-out rebuilt Nixpacks, Railpack and static services from a Dockerfile.
  7. v0.15.2

    Oct 9, 2026

    Remote nodes, part 1: builds, private clones and volumes on nodes. Everything new is off until a node runs the updated agent; nothing changes for single-host installs.
    Full v0.15.2 release and upgrade notes

    Upgrade notes8

    • One additive migration, 0072_multi_node: a new image_transfers table and nullable or defaulted columns on databases, services, servers, sources, backups and deployments. No existing row changes behaviour.
    • The new node features need node agent v0.15.2. Re-run a node's bootstrap from the Servers page to update it. An older agent keeps working exactly as before. Anything that needs the new agent is refused with "update the node agent to…
    • The runtime image now includes openssh-client (about 5–6 MB). Without it, deploy-key clones could not run in Docker installs or on nodes.
    • Railpack on nodes: after the node agent is updated, a Railpack service placed on a node builds with Railpack itself instead of the Dockerfile stopgap. The resulting image can differ.
    • Scheduled backups of a service on a node now back up its volume on that node. Before, they skipped it or copied a stale volume of the same name from the panel host.
    • and 3 more

    Added4

    • Builds on nodes. Nixpacks and Railpack run on the node. The panel works out the build pack on its own checkout with the same rules as a local build, so services that already build on a node send the same build inputs.
    • Private clones on nodes. A personal access token or deploy key is sent only for one job, over the sealed agent transport. A deploy key is kept in memory-backed storage on the node and removed afterwards, even when the job fails. The…
    • Volumes on nodes. Create, list, back up and restore volumes on a node. Backups are encrypted like the panel's; the node's data never touches the panel's disk unencrypted. Docker services on a node can use volume attachments, a command…
    • Node agent. A sealed, checksum-verified data channel for images and volumes. A structured run operation that accepts a validated spec, never a raw command line. Image archives are checked before and after docker load, so an archive…

    Fixed3

    • Compose stacks on a node with attached volumes failed at compose up ("external volume not found"). Missing service volumes are now created first.
    • With a node selected, the volume file manager opened the panel host's volume of the same name. It now refuses.
    • Deploy-key clones failed in Docker installs because the image had no SSH client.
  8. v0.15.1

    Oct 9, 2026

    Security patch: patched releases of two dependencies of the MCP server.
    Full v0.15.1 release and upgrade notes

    Upgrade notes1

    • No migration and no configuration change. Upgrading from 0.15.0 changes nothing except the bundled dependencies.

    Security4

    • @modelcontextprotocol/sdk is now 1.32.1. Versions before 1.31.0 had an OAuth client that could send credentials to an authorization server an attacker steered it to (GHSA-6qxp-vccf-f47h, high).
    • proxy-addr is now 2.0.8. Earlier versions could be fooled by IPv4-mapped addresses when deciding a client's IP (critical). It came in through the MCP SDK's HTTP server.
    • NineDeploy's MCP server talks over stdio and uses neither code path, so these were not reachable in practice. Both now resolve to fixed releases anyway.
    • One audit finding remains: braces, which pm2 depends on. No fixed release exists yet.

    Changed1

    • MCP: a tool whose inputs are all optional now runs when called with no arguments, as the MCP specification allows. Tools with a required input still refuse such a call.
  9. v0.15.0

    Oct 9, 2026

    Operations and API: browser and CLI terminals with a real TTY (also on remote nodes), opt-in traffic analytics, an OpenAPI 3.1 document for every route with generated MCP tools, and project- and environment-level access grants. See docs/TERMINALS.md, docs/TRAEFIK_INGRESS.md, docs/WORKSPACES_RBAC.md and docs/AI_MCP_CLI.md.
    Full v0.15.0 release and upgrade notes

    Upgrade notes9

    • One additive migration, 0071_operations_api: three new tables (terminal_sessions, traffic_rollups, access_grants). No existing row is changed.
    • Traefik is not touched by the upgrade. Traffic analytics is off by default, and with it off Traefik's configuration is byte-identical to 0.14. Turning analytics on recreates Traefik once. On the json-file/local Docker log drivers,…
    • Permissions are unchanged. With no access grants, every access decision is identical to 0.14 (checked over 4839 caller × resource × action cases). Grants only ever raise access.
    • Terminals: container shells are available to operators. Host shells (a root shell on the panel host or a node) are off until an operator enables them under Settings → Security, and each one needs a browser session and a password…
    • Node terminals need node agent v0.15.0 (re-run the node's bootstrap from the Servers page). Older agents get a clear "update the agent" refusal; nothing else about them changes.
    • and 4 more

    Added6

    • Terminals. Shells into services, databases and managed containers, on the panel host and on remote nodes. A real TTY with resize, short-lived single-use tickets, idle and maximum-duration limits, and per-panel and per-user caps. Every…
    • Traffic analytics (opt-in). Requests, status classes and latency per domain and service, read from a Traefik access log that keeps no client IP, path, query or headers. Rolled up per minute (48 h) and per hour (30 days by default). Turn…
    • OpenAPI 3.1. GET /v1/openapi.json (sign-in required) describes every route (435 operations), built from the same zod contracts the server validates with. A test fails if a route has no description or its access level disagrees with…
    • MCP. The server gains search_api and 16 read-only tools generated from the spec, including traffic summaries, access grants and terminal sessions. There are still no write tools.
    • Access grants. A workspace admin can raise a user's role on one project, one environment, or a project+environment pair. A grant can also give a user without a workspace seat (a guest) access to just those resources. Guests never get…
    • and 1 more

    Fixed5

    • The web terminal in Docker installs ran without a TTY (no python3 in the runtime image).
    • The old service shell socket recorded only a start audit entry. It now records the session's end, duration and client too, and operators can terminate it.
    • Terminal resize was never sent to the server.
    • WebSocket frames up to 100 MiB were buffered before authentication. They are now capped at 1 MiB, and a plain subprotocol is preferred over echoing the bearer token.
    • Eight places read a workspace seat directly instead of going through the access check. They now share one access path.
  10. v0.14.0

    Oct 8, 2026

    Network and data access: public database access, editable Traefik configuration with your own certificates, database dump import, and HashiCorp Vault / OpenBao and AWS Secrets Manager. See docs/TRAEFIK_INGRESS.md, docs/DATABASES_BACKUPS.md and docs/SECRET_MANAGERS.md.
    Full v0.14.0 release and upgrade notes

    Upgrade notes8

    • One additive migration, 0070_network_data_access: four new tables (database_public_access, tls_certificates, database_imports, secret_providers). No existing row is changed.
    • Traefik is recreated once on the first 0.14 boot. Traefik now reads its dynamic configuration from a directory (/etc/traefik/dynamic). The current routes are copied there before the recreate, so sites keep serving. If the recreate…
    • Remote nodes start serving their routes. Since remote servers were introduced, a node's Traefik read a file the agent never wrote, so it loaded no routes. Nodes recreate their proxy once after the upgrade and then serve the routes the…
    • Certificate-expiry alerts start working. Expiry dates of Let's Encrypt certificates were never read (Traefik stores them base64-encoded), so the cert-expiry alert could not fire. After the upgrade it fires for certificates that are…
    • Everything else is opt-in. No database is exposed, no custom config or certificate exists, and no secret provider is configured until an operator sets one up.
    • and 3 more

    Added6

    • Public database access. An operator can publish a managed postgres, mysql, mariadb, redis, valkey or mongo database on one host port. It runs through its own Traefik TCP sidecar (nd-dbpub-<slug>); the main Traefik is never touched. An…
    • Custom Traefik dynamic config. Operators can add their own routers, middlewares and services. Every name must start with custom-, so it can't collide with the panel's own routes. Each save is validated, then tried in a throwaway…
    • Uploaded TLS certificates. Operators can upload PEM certificates and keys; the key is stored encrypted. A domain fully covered by an uploaded certificate stops requesting a Let's Encrypt certificate. Uploaded certificates feed the…
    • Database dump import. Sources: resumable 8 MiB chunked uploads (database admin), or an object from a backup destination (operator). Formats: postgres (custom or plain SQL), mysql/mariadb (SQL), mongo (archive) and redis/valkey (RDB),…
    • Secret managers. Env values can reference HashiCorp Vault / OpenBao KV v2 (${{vault:path#field}}) and AWS Secrets Manager (${{aws:id}} or ${{aws:id#key}}). Configure them under Settings → Integrations. Infisical and Doppler keep…
    • and 1 more

    Fixed2

    • Remote-node proxies loaded no routes: the static config pointed at dynamic.yml, while the agent writes dynamic/ninedeploy.yml.
    • Let's Encrypt certificate expiry was never read, so the cert-expiry alert never fired, and the certificate inventory always showed the issuer "Let's Encrypt" and the source acme.json.
  11. v0.13.0

    Oct 8, 2026

    GitHub App integration: short-lived installation tokens instead of long-lived PATs, App webhooks, commit statuses and PR comments, private clones on remote nodes, and Gitea repository listing. See docs/GITHUB_APP.md.
    Full v0.13.0 release and upgrade notes

    Upgrade notes7

    • One additive migration, 0069_github_app: four new tables (github_apps, github_app_installations, service_github_links, github_pr_comments) and a nullable sources.base_url column. No existing row is changed.
    • Nothing changes until an operator registers a GitHub App. PAT, deploy-key and webhook services deploy exactly as before, and their per-service webhooks keep working.
    • Moving an existing service to the App is explicit and reversible: migrate links it while keeping its PAT and webhook, finalize detaches them, and revert goes back.
    • Deploying an App repository to a remote node needs the node agent v0.13.0 or newer over the sealed transport, and git 2.31 or newer on the node. An older agent gets a refusal telling you to update it; nothing is minted. The check runs…
    • Deleting an App in the panel also removes its installation rows and service links. The generated github_app sources stay, and their clones then fail closed until you attach another credential.
    • and 2 more

    Added8

    • GitHub App setup. One-click setup through GitHub's manifest flow, for a personal account or an organization (Sources → GitHub Apps). Manual entry for GitHub Enterprise Server or an existing App. The webhook is registered automatically.…
    • Installations as sources. Each installation becomes a github_app source. Clones use a repository-scoped, read-only installation token that lives in memory for at most an hour. A token is only ever sent to the App's own GitHub host.
    • App webhooks. Pushes and pull requests arrive at POST /v1/hooks/github-app/<key>, signature-checked with replay protection, and are routed by repository id, so a renamed or transferred repository keeps deploying and the service's URL…
    • Service links. The Deploy Wizard links a new service to the App repository it picked; existing services can migrate, finalize and revert from the service's Settings → GitHub card, the API (/v1/services/:id/github), SDK…
    • Commit statuses and PR comments. Opt-in per service: a ninedeploy/<slug> commit status for each deploy, and one preview comment per pull request, edited in place with the URL and outcome. Feedback never fails a deploy.
    • and 3 more

    Fixed2

    • /v1/hooks/<non-numeric id> answers 404 instead of 500.
    • After a session expired, the login redirect now keeps the query string, so returning from GitHub's setup pages still completes the setup.
  12. v0.12.0

    Oct 8, 2026

    Backups, preview environments and disk alerts — the first release of the roadmap.
    Full v0.12.0 release and upgrade notes

    Upgrade notes4

    • Two additive migrations: 0067_database_backup_policies and 0068_preview_env_vars. Both are new tables only. No existing row is changed, and a rollback to 0.11.x ignores both tables.
    • Nothing changes until you configure it: A database without a backup policy keeps the daily backup with 7 kept. Panel self-backup is off. No alert rules are created. PR previews get exactly the environment they got before.
    • The panel now pings approved remote nodes every 30 seconds. SSH-bootstrapped nodes therefore stay "online" on the Servers page while they are reachable. They used to show offline about 5 minutes after bootstrap.
    • Verified before release: in-place upgrades from 0.11.2 and from 0.10.45, plus the feature checks above on the upgraded panel.

    Added4

    • Per-database backup policy. Settings: a cron schedule, local retention (1–365), optional remote retention, and a destination or local-only. Where to configure it: the database's Backups tab, GET/PUT /v1/databases/:id/backup-policy,…
    • Panel self-backup. What is backed up: the panel database (a consistent snapshot), master key, .env and Traefik config. It is written to a backup destination on a schedule, encrypted with a recovery passphrase the operator keeps, and…
    • Preview-only environment. A git-backed service can hold environment values that only its PR previews receive. Previews still never receive the service's secrets. Where to set them: Env tab → Preview deployments,…
    • Alert metrics disk and server_offline. disk is the worst disk-usage percentage across the panel host, Docker's data root and the remote nodes. server_offline is the number of minutes a remote node has gone unseen. Both are…

    Changed2

    • System export and import share one archive implementation (lib/systemArchive.ts) with panel backup. The API and the archive format are unchanged.
    • An alert rule PATCH now validates the merged rule. An existing cert-expiry rule can no longer be scoped to a service.
  13. v0.11.2

    Oct 8, 2026

    Security release: a failed submodule clone could put the Git source's access token into deploy logs, the audit log and notifications.
    Full v0.11.2 release and upgrade notes

    Upgrade notes3

    • Rotate Git source tokens that may have leaked. Before this release, a deploy whose repository had a submodule the token could not clone wrote the token into: the deploy log (live stream and logs/<id>.log); the deploy.failed audit…
    • No database migrations or new configuration fields.
    • The OIDC provider defaultRole field is deprecated. The API still accepts and returns it, but it has had no effect since 0.11.0.

    Security1

    • A failed clone's git output is now redacted before it is logged or stored. git rebuilds a submodule's URL from the tokenized origin and prints it on failure; that text was passed through unchanged (F1013).

    Fixed4

    • A failed deploy clone names its reason in the deploy log and the failure message: authentication failed, HTTP 403, not found or no access, could not resolve/connect, TLS/host-key failure, or missing branch. It adds advice for GitHub…
    • The Deploy Wizard shows why a repository list is short: the classic token lacks repo, the list was capped, a later page failed, or the provider returned an error. The SDK gains sources.reposWithDiagnostics (F1010).
    • Dashboards served from a separate origin can read that diagnostic, because CORS now exposes x-nd-source-error (F1016).
    • ninedeploy sources test prints the token type, scopes and warnings. Provider-supplied text is stripped of terminal control sequences (F1011).

    Deprecated1

    • OIDC provider defaultRole. OIDC users always own their personal workspace; add them to team workspaces with invitations or SCIM. The SSO settings form no longer shows the field (F1014).
  14. v0.11.1

    Oct 8, 2026

    Private repositories: complete repository lists, clone errors that say why, and a token check.
    Full v0.11.1 release and upgrade notes

    Upgrade notes3

    • No database migrations or new configuration fields.
    • POST /v1/insights and POST /v1/services/:id/insights/refresh answer 400 repo_unreachable or branch_not_found when the repository cannot be cloned. The first answered 500 and the refresh route answered 404 before.
    • GET /v1/sources/:id/test returns tokenKind, scopes and warnings for GitHub sources. These fields are additive.

    Fixed2

    • Git source repository lists stopped at the provider's first page (100 repositories, most recently updated first), so older private repositories never appeared. Lists now follow GitHub Link, GitLab X-Next-Page and Bitbucket next…
    • Analysing a repository the selected credential cannot clone answered "Internal Server Error". The answer now names a reason: authentication failed, HTTP 403 (permission denied), not found or no access, could not resolve/connect, or…

    Added2

    • Sources: a "Test token" button shows the login, the GitHub token type, a classic token's scopes, and warnings when private repositories cannot be listed or cloned.
    • The Deploy Wizard explains, next to the repository picker, why a repository may be missing from a GitHub source.
  15. v0.11.0

    Oct 7, 2026

    The evidence audit release: over 300 defects, each reproduced on the previous code before it was fixed, then verified and, where it mattered, pinned by a regression test.
    Full v0.11.0 release and upgrade notes

    Upgrade notes5

    • No database migrations or new configuration fields.
    • API changes: POST /v1/build-cache/store answers HTTP 400 { error } for a missing key, malformed digest or unknown cache (was 200 { ok: false }). It also accepts an optional ref (<repo>@sha256:<64 hex>). Compose services and…
    • CLI: ninedeploy deploys watch exits 1 on any close other than a clean end, including when its 30-minute wait limit is reached. Ctrl-C still exits 0. The source-token environment variable is NINEDEPLOY_SOURCE_TOKEN.
    • One-shot repairs on first boot: Team services and databases still owned by a deactivated user move to the workspace owner (ownership_repair_deactivated_owner_r976). SSO personal workspaces get their owner seat back. Both write audit…
    • Behaviour: The build cache now stores a pullable image reference (new marker and registry-manifest fields). An FCM channel whose device token is rejected as dead is deactivated. Docker inspects that time out (10 s) fail instead of…

    Security7

    • Enabling the host firewall could lock the operator out. ufw rejected most generated rules, including the SSH/HTTP/HTTPS safety rules. A failed safety rule was ignored, and the panel port was never allowed.
    • Tenant-supplied header and basic-auth values were evaluated as Traefik Go templates, which could leak environment secrets. A covering wildcard router could outrank an exact host and skip basic auth or IP allow-lists.
    • Templates could deploy onto another tenant's attached volume. A member could claim another tenant's future PR-preview hostnames, or the www/apex half of an active redirect. A pending domain transfer outlived its initiator's admin seat.
    • An HTTPS token stayed in .git/config of relative submodules and was copied into images. Repository framework detection had a ReDoS on member-controlled files.
    • logs search printed tenant container output raw, so terminal escape sequences (clipboard writes, title changes) reached the operator's terminal.
    • and 2 more

    Fixed10

    • Data safety: Backup retention could delete the last good backup after a failed upload. Restore checked only the primary container while replicas kept running on the volume. Prune, delete and the doctor treated database-adopted volumes…
    • DNS: A Namecheap reply without a host list could wipe the whole zone on the next write. Concurrent record changes in one zone overwrote each other. IPv6 targets were sent as CNAME records; they are now AAAA.
    • Deploys: A template service with a second attached database took its database settings from the wrong one. Compose deploys picked the wrong main container. A pipeline crash left deployments building for up to 45 minutes. Scheduled…
    • Runtime: About 25 Docker calls that could hang a deploy, a health check or a request for 30 minutes are now bounded. A user-stopped service is no longer restarted by the background reconcile. The deploy-log WebSocket closes once a…
    • Build cache: Cache hits now reach docker buildx --cache-from on the inline, registry and S3 backends. Malformed digests are refused instead of overwriting a valid entry.
    • and 5 more

    Tests1

    • Permanent regressions cover the race, ownership, High and Critical fixes: reconcile and stop races, concurrent passkey migration, domain transfer accept, seat hand-over, audit entity formats, deploy-log close codes and the boot repairs.
  16. v0.10.45

    Oct 5, 2026

    Deployment reliability and shared-contract fixes from the evidence-led audit.
    Full v0.10.45 release and upgrade notes

    Upgrade notes2

    • No database migrations or new configuration fields.
    • Existing source builds on remote nodes retain the agent requirements introduced in earlier releases.

    Fixed11

    • Deployment queues, filesystem locks and lifecycle cleanup handle failure paths consistently. Stale auto-update probes no longer update a service whose deployment settings changed while the probe was running.
    • Community template creation uses exclusive writes, preserving an existing template when concurrent installs select the same name. Explicit replacement remains available.
    • Agent connection caching follows the current node host and port. Node proxy sync errors identify the failed operation stage even when a transport error omits its name.
    • Log timestamps preserve fractional nanoseconds. Log search, runtime log selection, deployment history and scheduled-job fixtures include the preceding audit repairs.
    • Manifest import/export preserves explicit redirectWww: false; service creation retains healthPath. SDK manifests and errors, CLI configuration and terminal formatting include the audited contract fixes.
    • and 6 more

    Tests2

    • Permanent regressions cover concurrent template creation, stale auto-update completion, session rotation, deploy-log selection and successive panel-update timers.
    • Shell-based installer and update fixtures use the native Git Bash installation on Windows rather than a WSL launcher.
  17. v0.10.44

    Oct 3, 2026

    Ownership follow-up to 0.10.43 (r710–r711): team services whose creator was removed by SCIM before 0.10.43 deployed without their project's shared environment; the first boot of this release hands them to the workspace owner. Repository analysis now needs the same seat as creating a service.
    Full v0.10.44 release and upgrade notes

    Upgrade notes3

    • One-time ownership hand-over on first boot (r710). Services and managed databases in a workspace whose creator no longer holds a seat there (SCIM suspend or deprovision before 0.10.43) now belong to the workspace owner — what 0.10.43…
    • Repository analysis needs a member seat (r711). POST /v1/insights (the Deploy Wizard's analysis step) answers 403 for accounts with no seat or only viewer seats, the same rule as creating a service. Operators and members are unaffected.
    • No migrations.

    Fixed1

    • Team services lost their project's shared env after a SCIM removal (r710). The deploy pipeline injects a project's shared env only when the service owner is seated in the project's workspace; a creator removed by SCIM before 0.10.43…

    Security1

    • Repository analysis was open to any signed-in account (r711). A seatless or viewer-only account could have the panel clone arbitrary public repositories (bounded and rate-limited, but outside the role model every other write follows).

    Docs1

    • README → Verifying a release: image signatures need cosign v3, or v2.6+ with --new-bundle-format; older cosign reports "no signatures found".
  18. v0.10.43

    Oct 2, 2026

    The verified-release release (r690–r703): releases are signed and their assets checksummed, installs verify what they download, and every API route is now held to an authorization matrix that fails the build when a new route is not classified.
    Full v0.10.43 release and upgrade notes

    Upgrade notes6

    • Creators who lost their seat lose access (r694). A service or database creator counts as its owner only while they hold a seat in a workspace it belongs to. Someone removed from the workspace (for example by SCIM) now gets "not found";…
    • SCIM removal hands ownership over (r695). Suspending or deprovisioning a user through SCIM transfers what they created to the workspace owner, as the panel's own member-removal already did. Reinstating the user restores the seat, not…
    • Installs verify downloads (r702). From this release on, the installer downloads the release's own source archive and checks it against the signed SHA256SUMS; when cosign (v2.4+) is installed it also verifies the signature. A…
    • Self-update verifies the installer (r703). Takes effect from the first update a 0.10.43 panel starts.
    • Revoking something that is already gone, or that is not yours, now answers 404 instead of 200 (r691).
    • and 1 more

    Security3

    • Seatless creators kept owner access (r694, high). Whoever created a team service or database kept full access after losing their seat — secret env export, database credentials, backup downloads, deploy and delete (82 routes).
    • Cross-tenant ids on service create left half-created services (r692); the backup-drill route revealed whether another tenant's backup id exists (r693); owner-scoped deletes answered 200 for ids outside the caller's scope (r691).
    • Authorization matrix (r690). A test boots the real API on a real database, enumerates every route (361), and checks each against two tenants and every role: no cross-tenant access, no write or outside call on refusal, the role floor…

    Release integrity3

    • Signed images (r700). Every published image is signed with Sigstore keyless signing (GitHub OIDC, no stored key) and carries build provenance. The exact cosign verify, cosign verify-blob and gh attestation verify commands are in…
    • Checksummed, signed release assets (r701). Each release carries ninedeploy-vX.Y.Z.tar.gz, install.sh, SHA256SUMS and its signature bundle. The release is created as a draft, its assets are verified the way installers check them,…
    • What this guarantees: the archive, installer and image are byte-for-byte what this repository's release workflow produced. What it does not: the trust root is still GitHub and Sigstore.
  19. v0.10.42

    Oct 2, 2026

    The second-pass audit release (r630–r680): ingress, managed data, PR previews and remote nodes. Two bugs could freeze routing for the whole instance, PR previews still received production credentials, and managed Redis/Valkey have never started since 0.2.2. All fixed, each with a regression test proven to fail on 0.10.41, and every refusal says what to do.
    Full v0.10.42 release and upgrade notes

    Upgrade notes — read before updating11

    • Update your node agents (r660). Agents older than 0.10.42 are refused for remote source builds (repository Dockerfile, repository compose, source fan-out) — the error names the node. Image deploys and inline compose stacks keep working.…
    • SSH host keys are now pinned (r661). The first SSH test or bootstrap after updating trusts and records the node's host key (shown as a warning). A reinstalled host then fails until you enter its new fingerprint on the Servers page.
    • Managed Redis/Valkey start now (r644). Databases created before stayed in error; start them from the database page (or delete and recreate).
    • PR previews lose production credentials (r651). They no longer receive project-shared secrets, vault references, the parent's databases, or a manifest database: attachment; each preview's deploy log lists what was withheld. Give a…
    • Manifest restrictions (r650, r652). A .ninedeploy database: section only attaches databases the service owner administers; notifications: only applies to operator-owned services (existing subscriptions on other services stop…
    • and 6 more

    Security8

    • Hostname check vs render mismatch (r630). A member could store a hostname the checks treated as one host and the proxy rendered as a catch-all — routing every tenant's automatic domain to their own container.
    • PR previews inherited production credentials (r651) through project secrets, vault references and manifest database attachments — reachable by anyone who can push a branch and open a PR.
    • Manifest attached databases the owner could only view (r650), handing their password to the container.
    • Volume routes queued redeploys without the deploy privilege check (r640) — a member could get a privileged operator-owned stack redeployed from a repository they push to. One enqueue helper now serves every user-triggered deploy, pinned…
    • Template databases could adopt a deleted tenant's retained volume (r641); dependency slugs could be squatted (r642).
    • and 3 more

    Fixed5

    • Routing froze for the whole instance when any service enabled sticky sessions (Traefik has no sticky middleware) (r631) or ran more than one replica (mis-indented healthCheck) (r638). A golden test now validates every middleware the…
    • Managed Redis/Valkey never started (r644): --requirepass was passed before the image. The user-journey smoke now creates a managed Postgres and Redis on every release (r680).
    • Deleting a database left its off-site dumps (r646); concurrent restores of one remote backup clobbered each other (r647); volume names could collide with another service's primary volume (r648); the volume list ran a helper container…
    • Bundle import is transactional and claim-checked (r656); repository analysis clones are shallow and bounded (r657); webhook-out no longer follows redirects and /ai/config hides the base URL from non-operators (r658); Doctor flags a…
    • Node checkouts are removed on delete/move (r662); fan-out targets are torn down on service delete (r662); IPv6-safe host normalisation (r663); ssh errors no longer echo the remote command (r664); terminal and container routes refuse…
  20. v0.10.41

    Oct 2, 2026

    Security release — update now. A second-pass audit found that a signed-in user could make the panel read a file on the panel host through a symlink committed to a repository and get its content back — any file the panel process can read: its environment (`NINEDEPLOY_JWT_SECRET`), `master.key`, the panel database, and on bare-metal installs (which run as root) system files. Every release before 0.10.41 is affected.
    Full v0.10.41 release and upgrade notes

    Security3

    • Symlinked repository files were followed on the panel host (r620). Repository analysis and the .ninedeploy loader now read only regular files inside the checkout (no symlinks, FIFOs or devices; O_NOFOLLOW; bounded reads). A detected…
    • Workspace email overrides could send arbitrary mail through the instance (r621) — a regression in 0.10.40, where they were first wired into sending. Overrides now apply only to workspaces an instance operator owns and must keep…
    • Git never runs hooks from a checkout (r622) — core.hooksPath=/dev/null on every clone, fetch and submodule update.

    Upgrade notes3

    • No migrations. Stored analyses are left in place (Doctor needs them to tell you what happened); re-run analysis or redeploy to rewrite them.
    • A .ninedeploy that is a symlink now fails with a message naming it instead of being followed — commit the file itself.
    • Custom invitation emails on workspaces not owned by an instance operator stop being sent (the built-in text goes out); saving one there answers 400 with the reason.
  21. v0.10.40

    Oct 2, 2026

    The first published build of the 0.10.38 changes — read the 0.10.38 section below, including its upgrade notes. Neither the 0.10.38 nor the 0.10.39 tag reached a panel: the smoke-gated release pipeline (r581) stopped each one before any image was pushed, `:latest` moved or a GitHub Release was created.
    Full v0.10.40 release and upgrade notes

    Fixed1

    • Railpack on arm64 (r584). Railpack names its arm64 Linux binary arm64-unknown-linux-musl, not aarch64-… (Nixpacks' spelling). The arm64 image build 404'd, and install.sh on arm64 bare-metal hosts has silently never installed…
  22. v0.10.39

    Oct 2, 2026

    The first published build of the 0.10.38 changes. The 0.10.38 tag never reached a panel: its release checks failed on a test that only broke on Node 26, and the smoke-gated pipeline (r581) pushed no image, moved no `:latest` and created no GitHub Release. Read the 0.10.38 section below — including its upgrade notes — for everything this release contains.
    Full v0.10.39 release and upgrade notes

    Fixed1

    • The r605 pinned-dispatcher test built its Agent from the Node 22/24 undici slot and failed on Node 26 (CI's Node). It now uses the same bundledAgentClass() lookup guardedFetch uses. The production code was already correct, verified…
  23. v0.10.38

    Oct 2, 2026

    The proven-upgrade release (r580–r610): releases are now only offered to installed servers after the published image has been upgraded onto from the previous release and walked through the user journey — and the follow-ups the 0.10.37 audit deliberately left out.
    Full v0.10.38 release and upgrade notes

    Upgrade notes — read before updating6

    • Workspace invitations (r604). For non-operators, adding an email that is not yet a member always sends an invitation, even when an account exists — the colleague accepts it from the link. Instance operators still add registered accounts…
    • Sandbox plugins on Node < 25 (r600). Already-installed sandbox plugins keep loading; installing a new one (or reinstalling) is refused unless you upgrade Node to ≥ 25 or set NINEDEPLOY_ALLOW_SANDBOX_NETWORK=1. Doctor lists sandbox…
    • Email templates (r610). Workspace invitation overrides you saved earlier are now actually sent. Without an override, emails are byte-for-byte what 0.10.37 sent. Password-reset overrides are refused (a workspace admin must not rewrite…
    • Railpack (r582). Ships in the container image now. Railpack builds need a BuildKit daemon: set BUILDKIT_HOST (e.g. docker-container://buildkit next to a moby/buildkit container). Without it, saving or deploying a railpack service…
    • Compose redeploys (r590) build before taking the old stack down, so the previous stack keeps serving during the build and a failed build leaves it running.
    • and 1 more

    Release process2

    • Publication is gated on end-to-end smokes (r581). The release workflow pushes :vX.Y.Z only, then runs smoke:upgrade (previous published release → this tag, on one data volume) and smoke:user-journey against the pushed image;…
    • pnpm smoke:upgrade (r580): boots the previous release on a data volume, seeds an operator, a deployed service with a domain, an orphaned project secret, and a deploy hard-killed mid-build; boots the new release on the same volume and…

    Security7

    • DNS-rebinding window closed (r605). guardedFetch connects only to the addresses it vetted (pinned lookup on Node's bundled undici Agent — verified on Node 22.13, 24 and 26), keeping SNI and the Host header.
    • Email enumeration through member-add and invitations (r604) — registered and unregistered emails now get the same response.
    • Sandbox network on Node < 25 (r600) — new installs refused unless knowingly allowed; plugin list shows networkRestricted.
    • Vault references at write time (r601) on template deploys and bundle import.
    • Preview patterns from .ninedeploy manifests are validated (r602) — and the manifest's documented {n} placeholder is finally understood (it was never substituted).
    • and 2 more

    Fixed8

    • Workspace email templates were never sent (r610) — invitations from both invite paths now render the workspace's override; a wiring guard pins every template to a sender.
    • Compose deploys took the stack down before building (r590).
    • Node-pinned compose services are health-patrolled (r591).
    • Fan-out source builds pull private base images with the bound registry credential (r592).
    • A container started by a deploy interrupted by a panel restart is removed at boot (r593) — containers now carry ninedeploy.deployment / ninedeploy.service labels.
    • and 3 more
  24. v0.10.37

    Oct 2, 2026

    The tenancy release (r500–r576): a whole-repo deep audit found that several per-instance resources (the vault, registry credentials, preview hosts, SCIM, the plugin event bus) were reachable across tenants. All fixed, each with a regression test proven to fail on 0.10.35, and every behaviour change ships with an upgrade path so a 0.10.35 panel updates in place.
    Full v0.10.37 release and upgrade notes

    Upgrade notes — read before updating15

    • Vault allowlist (r510). On first boot the allowlist is seeded from current use (workspaces of non-operator services/projects that already reference the vault), audited as settings.vault_allowlist_seeded and logged. Working deploys…
    • Registry credentials (r512). Each registry source is bound to the hosts its services pull from today (audited as source.registry_hosts_seeded). A credential is no longer sent to any other host.
    • Preview patterns (r511). Stored patterns without both {{pr}} and {{slug}} keep deploying previews but provision no domain (previewDomainSkipped: pattern_requires_pr_and_slug).
    • Passkeys / TOTP (r502) ask for your password (or a sign-in from the last 10 minutes) when enrolling.
    • SSO (r505). A tab loaded before the update will be asked to sign in again once.
    • and 10 more

    Security12

    • Instance vault readable by any member (r510). ${{infisical|doppler:KEY}} resolved with the operator's vault token for every service. Now only operator-owned services and allowlisted workspaces resolve references; non-operators writing…
    • Preview-domain takeover (r511). A member-editable preview pattern could render another tenant's host and steal its traffic. Patterns must contain {{pr}} and {{slug}}, and the rendered host goes through the own-zone claim check.
    • Registry credential exfiltration (r512). Changing image pointed docker login and the auto-update probe at any host. Credentials are now bound to registry hosts, and members cannot repoint such a service to another registry.
    • Auto-update skipped the owner-privilege check (r513); its registry probe was a blind SSRF for member-owned services (r514).
    • Login lockout never tripped (r500). Per-IP locks zeroed the counters the account tier sums. Rewritten with a per-account window; IPv6 bucketed by /64.
    • and 7 more

    Fixed13

    • Railpack double build (r520); railpack refused on container installs, which ship no railpack CLI.
    • Node deploys reported success while the node's proxy kept pointing at the removed container (r521).
    • Deploy hooks and exec jobs on node-pinned services ran on the panel host (r522, r523).
    • A restart mid-deploy blocked local deploys for up to 45 minutes (r524); interrupted deploys are failed at boot.
    • Node-pinned services were never health-patrolled (r525); an unreachable node raises alert.node_unreachable instead of "service down".
    • and 8 more

    Installer, release and docs5

    • latest_tag prefers the published release over a freshly pushed tag, and its warnings no longer leak into the captured tag (r571). Docker installs pin the image tag, keep the previous compose/.env for a real rollback, and wait up to…
    • Self-update answers 409 deploys_in_flight while a deploy is building, unless force: true (r572).
    • bump-version.js validates before writing; :latest only moves forward; per-tag release concurrency (r573).
    • apache2/nginx are only stopped on fresh installs that need :80/:443; Nixpacks checksums keyed by version; provenance comment no longer overclaims (r574).
    • Doctor finding panel_plaintext_exposure; README/ARCHITECTURE counts corrected (r575). CI exercises the real install.sh helpers and runs a non-blocking pnpm audit (r576).