TurboPanel Docs
Deployment

Version compatibility

Each repository carries its own semantic version and publishes its own GitHub Release; a control-plane release names the daemon and UI versions it was tested against. Both wires carry versions and hold the peer against a floor. A peer below the floor stays connected and is flagged. Commands to an old daemon fail with daemon_unsupported. An old control plane is logged and the daemon keeps reconnecting. A missing version is unknown and is allowed. Mixed versions stay connected: that is how a managed upgrade reaches a daemon that is not on the new build yet.

A platform upgrade (Admin → Updates, or an automatic run) does impose an order on self-hosted: this host's daemon, then the control plane, then the other servers, and the the servers step does not start until the first two are on target. That order is the upgrade run. It is not a version floor, and it does not drop a socket. See Upgrade and rollback.

Channels

canary, rc, and release are GitHub Releases on each package's own repository (redirects, no API calls). Daemon trunk is the per-merge CDN drop at dl.trbp.nl/channels/trunk/manifest.json. The control plane and the UI publish only on canary, rc, and release — they have no CDN drop — so a control plane whose channel is trunk has no control-plane package target. Admin → Updates leaves that row's Upgrade disabled and still resolves the daemon from the CDN. See Canary environment.

ChannelWhat it points atDaemonControl plane and UI
canaryThe newest green build of trunkreleases/download/canary/manifest.json on turbopaneldThe same path on turbopanel and ui
rcThe current release candidate — a rolling pre-release tagreleases/download/rc/manifest.jsonThe same path on each repository
releaseThe latest promoted releasereleases/latest/download/manifest.jsonThe same path on each repository
trunkEvery merge to trunk — contributors onlydl.trbp.nl/channels/trunk/manifest.jsonNo package

Promotion is a pointer move — the bytes that soaked on rc are the bytes release serves.

Tested pairings

Control planeDaemonUINotes
0.2.x0.2.x0.2.xCurrent target; not yet released.
0.1.60.1.60.1.5The UI had no 0.1.6 build; 0.1.5 is the newest UI.
0.1.50.1.50.1.5
0.1.40.1.40.1.4
0.1.30.1.30.1.3
0.1.20.1.20.1.2First release through the promote pipeline.
0.1.00.1.00.1.0First release. Every 0.1.0 build understands managed.ingress.reconcile, the dockerNetworkAddressing[] deploy field and the GET /host/docker-networking pull.

Version floors

SideFloorWhat happens below it
Control plane → daemon0.1.0The daemon reports its version on hello. A supported daemon runs commands; an unsupported one keeps its connection but every command fails with daemon_unsupported until it updates. A daemon that reports no version passes as unknown.
Daemon → control plane0.1.0Every daemon REST response, including a non-OK response and the 401 before a session refresh, carries x-turbopanel-version. A socket-only session carries { type: "version", instanceVersion } on attach. A response or frame that omits the version clears the last observation back to unknown, and unknown passes. An unsupported control plane is logged (instance-version:) and the daemon keeps reconnecting. The daemon also refuses to install a control-plane package below this floor. A missing or non-semver target is unknown and is allowed. The control plane accepts instance-update from any connected daemon.
App → control plane0.1.0The control plane stamps x-turbopanel-version on every response and the app sends x-turbopanel-client-version. The native app refuses to connect to an older control plane and says which version it needs; a control plane that sends no header is accepted. The bundled web UI always matches its control plane.

Both floors are 0.1.0, so any 0.1.x peer is in window and a 0.0.x peer is not. The window may trail the current release by several minor versions. Raise both floors in the same change, and record why. The floors are unchanged by managed upgrades. A platform upgrade follows the order above; a daemon and a control plane that are not on the same build stay connected either way.

Wire features

The cell attach frame { type: "version", instanceVersion, features } lists what this control plane can speak. A daemon hello lists what that daemon can speak. Today both sides advertise four features:

  • managed-upgrade-v1: pinned backup and rollback on a platform upgrade.
  • update-progress-v1: fire-and-forget stage reports (preparing, downloading, installing, restarting, verifying, done, failed, rolled-back).
  • sealed-instance-secrets-v1: the daemon opens sealed envelopes for the control plane TLS keys and the tunnel token, so the control plane sends those secrets sealed to that one daemon instead of as plaintext. A daemon that does not list it still gets the legacy plaintext fields until the floor moves; one that lists it but cannot be sealed to is refused, never sent plaintext.
  • managed-health-v1: the on-demand managed-database replica health probe. The request goes only to a daemon that lists it.

A peer checks the list before it treats the other side as able to speak the message. A daemon that does not list managed-upgrade-v1 still receives a plain update, which is how a co-located daemon from before this release is refreshed; the control-plane step refuses to start until that feature is present, because the backup has to exist.

An unknown message is ignored. A well-formed frame whose type this process does not know leaves the socket up — the peer learned a message this build has not. The daemon logs ignored unknown websocket message type and continues. The control plane records the inbound frame as ignored and does not close the connection. Oversized frames, invalid JSON, and a known type that fails field validation are still rejected.

A panel feature that depends on a daemon-rendered artifact has its own minimum, separate from the floor. Unknown is unsupported for a feature. The first entry is instance-cert-sources-per-hostname at daemon 0.1.1: per-hostname certificate sources on the control-plane Caddyfile. Older daemons stay connected and receive the flat public-URL list only. The app reads resolveDaemonCapabilities and does not compare versions itself.

Mixing channels (a canary control plane with release daemons, or the reverse) is unsupported outside active development. A trunk channel updates the daemon from the CDN and has no control-plane package to mix.

Check versions

SurfaceHow
Daemonturbopaneld --version → turbopaneld v0.1.0 <commit> (…)
Control plane (self-hosted)GET /api/health → version and revision.commit (the release job stamps the exact commit)
Control plane (TurboPanel High Availability)GET /api/health on the TurboPanel High Availability origin, or the x-turbopanel-version response header
UIThe About screen; the web export ships as turbopanel-ui-<version>.tar.gz and reports itself in x-turbopanel-client-version
Server pageThe daemon panel shows the running version and commit and names the channel target by version

Breaking changes

Breaking API or enrollment changes are called out in:

Edit on GitHub

Last updated on

On this page