Unpublished candidate: its Git tag and signed registry image are retained. Release workflow 41254561302 passed the full source gate and six smoke cohorts, then withheld GitHub/npm publication after the public canary smoke's insufficient-traffic assertion.
Fixed
- Respect native Docker health before default HTTP, strict HTTP and TCP readiness can admit a replacement. Probe a non-loopback container address so a localhost-only listener cannot pass routed readiness. Reject command and running verdicts completed after their original deadline.
- After a command healthcheck succeeds, also wait for the configured TCP application port before switching traffic. Portless workers retain command-only readiness. Running mode retains its explicitly selected policy, including UDP workloads. The isolated delayed-listener panel/Traefik acceptance passed all five modes; this is not a guarantee for every production failure timing.
- Keep panel session updates usable if the desktop credential bridge throws synchronously.
Runtime scope
- Prepare immutable node serving and spare replicas, collect fresh node autoscaling metrics and fence scaling against deployment, lifecycle and database topology changes. Public memory autoscaling with PostgreSQL passed its isolated 1→2→1 acceptance. RPS scaling, node sleep/wake, broader fan-out and the full R2 runtime acceptance remain unfinished; this release does not complete R2 or claim 0.19.0 readiness.
- Rehearse upgrades from the latest published image in CI and require continuous normal-deploy traffic across five healthcheck modes in the published-image release matrix.
Added
10 more Dokploy blueprints in the bundled Hub (G4.7): 9Router, Dolibarr, Graylog, Kimai, Meilisearch, Miniflux, OpenObserve, Ponos, PrivatePaste and Typesense. Each started in an isolated smoke run and was then asked, without credentials, for its admin pages, APIs, install and sign-up pages; each set its admin login or API key from a generated value. YOURLS started too but stays out (its install page needs a manual step without a login).
CI jobs on your own runners (R5; docs/CI_RUNNERS.md).
- A service's pipeline is an image and a script. Operators enrol runners, which pull jobs over HTTPS with their own token (
node apps/server/dist/ciRunner.jsin the panel image). - Each job runs in a throwaway container with the source the panel checked out (no
.git, so no credential) and only the service variables the pipeline names. The container gets no Docker socket and no runner or lease token. - Jobs and attempts are kept in the database (migration 0117). A lease decides which attempt counts: a lost runner's job is requeued, its late result is refused, and its leftover containers are removed when it starts again. A panel restart renews live leases.
- Approval, runner labels and capacity are supported. Logs arrive as numbered chunks and are kept for 30 days.
runOnPushruns a job for every push to the service branch (skip markers apply; one job per commit).gateDeploysholds every deploy of the service and of its PR previews inawaiting_ciuntil a job at the deploy's commit passes. The deploy then builds the commit the job pinned; a failed or cancelled job fails it. A job for a pull request from a fork waits for an admin, and a fork preview deploy without a commit fails rather than run whatever the branch holds later. Runner-gated deploys never take the GitHub workflow gate's polling slots. Migration 0118 links a job to its deploy.- The panel has a CI tab on each service (pipeline, jobs, attempts, followed logs) and Settings → CI runners. The SDK has
client.ci; the CLI hasninedeploy ci(runners, pipeline, set, jobs, run--follow, log, approve, cancel). - The attempt log route now answers JSON (
{ data, size, done }) instead of text with headers. - Each job runs on a bridge network of its own, so it cannot reach other containers on the runner machine or another job.
NINEDEPLOY_RUNNER_NETWORK=nonetakes the network away entirely. A starting runner removes job networks left empty for over ten minutes, even under an old token.
- A service's pipeline is an image and a script. Operators enrol runners, which pull jobs over HTTPS with their own token (
A desktop client (R6, docs/DESKTOP.md).
desktop/builds unsigned installers for Windows (NSIS), Debian (deb), Linux (AppImage) and macOS (dmg).- You add a panel by its address, which must answer
/health, and open it in a sandboxed window with its own storage partition. - The panel keeps its refresh token in the OS credential store (DPAPI, Keychain, libsecret), so a sign-in survives closing the window. Without a real store nothing is written.
- Other sites open in the system browser, apart from an SSO sign-in started on the panel.
- Removing an instance revokes only that computer's session on the panel.
- The panel's web UI picks the bridge up by itself; in a browser nothing changes.
- The project keeps its own lockfile, so Electron stays out of the panel's dependency tree. A new Desktop workflow tests, packages and launches it on all three platforms.
- You add a panel by its address, which must answer
Remote MCP over HTTP (R4, first part). The panel serves its MCP tools at
/v1/mcpover the Streamable HTTP transport, for clients that send an API token asAuthorization: Bearer(for exampleclaude mcp add --transport http). Each tool call runs as that token through the API's own authorization, so scopes, roles and revocation apply per call. Off by default: Settings → Security → Remote MCP, orPUT /v1/mcp/policy; an optional switch limits every caller to read-only tools. Clients that sign in instead of sending a header use OAuth 2.1 as the MCP authorization spec describes: discovery documents, dynamic client registration, a consent page in the dashboard (read only or full access), PKCE, rotating refresh tokens (a reused refresh token ends its grant) and revocation. Each grant shows under Settings → Security → API tokens, where deleting it disconnects the client; its tokens work only on /v1/mcp. No migration.The image scan deploy gate now covers Compose stacks on the panel host (G5.1). The stack's images are scanned after pull and build and before the running stack is taken down, so a blocked deploy leaves the previous stack serving. Build-only services are scanned by the image name Compose gives them. No migration.
The restart-loop limit now covers PM2 services too (G1.3, Service → Settings → Restart-loop limit). It counts PM2's restarts per process, leaving out the ones the panel asked for, and stops the service as the Stop button does. PM2's own
max_restartsonly catches crashes within a second of starting; an app that crashes later looped on until now. No migration.Bare-metal installs on openSUSE, SLES and Arch now set up Node themselves (G4.1). On openSUSE and SLES it comes from NodeSource's RPM repository through zypper; every package signature is checked against NodeSource's key, as on dnf hosts. On Arch, Manjaro and EndeavourOS it comes from the distribution's LTS package of that line (
nodejs-lts-kryptonfor Node 24) withnpm, without upgrading anything else. A Node package of another line already installed on Arch must be removed first.Container options on Swarm services (ROADMAP item 10). Init, the open-files limit, added capabilities and shared memory now apply to each Swarm task: the first three as Swarm's own task fields, shared memory as a sized tmpfs at
/dev/shm. GPUs have no Swarm equivalent and stay off; the deploy log says so. A Swarm service with these options set ignored them before; its next deploy applies them. No migration.