Step-by-step guide to upgrading a Kvelden Enclave cluster to a new release from the admin GUI with zero downtime — one node at a time, standbys first and the primary last, with automatic bundle distribution, per-node readiness checks, and automatic rollback of any node that fails.
This page covers a clustered deployment (two or more appliances joined for
high availability). The rolling upgrade takes each node through the new version
one at a time — standbys first, the primary last — so the service stays
available throughout (zero user-visible downtime). For a single appliance,
use the Standalone upgrade instead.
New to how this works? Read System upgrade
first — it explains the signed bundle, the A/B slots, and the automatic safety
net that each node uses during its own step.
You upload the new bundle once. The cluster then upgrades itself node by node:
1
Standbys first, primary last
Standby nodes upgrade before the primary. When it’s the primary’s turn, the
cluster automatically fails over to an already-upgraded standby first, so
a writable node is always serving.
2
One node at a time
Only one node is ever offline (rebooting into the new version) at a time. The
remaining nodes keep a quorum and keep serving through the stable ingress
address (VIP / load balancer / DNS).
3
Automatic distribution
Each node pulls the bundle from the node you uploaded to over the cluster
network — you don’t copy files to each appliance.
4
Per-node safety net
Each node applies the update with the same verified, self-healing engine as a
standalone upgrade (verify → snapshot → install to the standby slot → reboot →
health-gate → commit or auto-rollback). A node that fails halts the rollout
with the other nodes still serving.
The rolling upgrade requires the HA clustering capability (the same one
used to form the cluster). Check Admin → Licensing.
Sign in as the owner, via the cluster address
Open the cluster’s ingress address (the VIP, load balancer, or DNS name) and
sign in as the owner. Manage the upgrade from Admin → Cluster / High
Availability.
Cluster is healthy
On the Cluster board, confirm Healthy, Quorum N/N OK, every node
Up / streaming, and Lag 0. Resolve any degraded node before
upgrading — the preflight will refuse an unhealthy cluster.
Free disk space on every node
Each node needs working room for the pulled bundle plus the new images. The
per-node readiness check verifies this and marks any short node red before
you can start.
Clock synchronized on every node
All nodes must have a correct, synchronized clock (required for leader
election, TLS, and signature validation). Confirm under System →
Diagnostics on each node.
A backup key on every node
Each node takes its own safety snapshot during its step, which requires a
backup key. Set one on every node under Admin → Backup & Restore if
not already configured.
Same bundle, compatible version
Use one enclave-update-<version>.tar.gz for the whole cluster. Only an
adjacent version step is allowed (the current version → the next); the
preflight enforces this.
Go to System → Software Update and upload
enclave-update-<version>.tar.gz with Choose bundle…. You only need to
do this on the node you’re signed in to — the cluster distributes it to the
other nodes automatically.
2
Open the Rolling upgrade panel
Go to Admin → Cluster / High Availability and scroll to Rolling
upgrade. Select the bundle you uploaded from the Update bundle
drop-down.
3
Run Preflight
Select Preflight. The cluster checks it is safe to roll:
The bundle is signed, valid, and a permitted version step.
The cluster has quorum, every node is running/streaming, and
replication lag is 0.
A green confirmation (cluster preflight OK … <from> → <to>) means the
cluster is ready.
4
Review the per-node readiness table
After preflight passes, a per-node readiness table appears with a row per
node and disk / version / health indicators plus reachability. Every node
must be ready (all green). A red row shows the reason — see
Troubleshooting. Start stays disabled until all nodes
are ready.
5
Start the rolling upgrade
Select Start rolling upgrade and confirm your password (step-up). The
rollout begins.
6
Watch it roll, node by node
Each node, standbys first, goes through: distribute (pull the bundle) →
install to the standby slot → reboot & activate → health-gate →
rejoin (streaming, Lag 0). The panel shows the current node and stage.When it’s the primary’s turn, the cluster switches over to an
upgraded standby first (the ingress address follows), then upgrades the old
primary. Throughout, the service stays reachable on the cluster address.
7
Verify the cluster is fully up-level
When the rollout completes, confirm on the Cluster board:
All nodes show the new version.
Healthy, Quorum N/N OK, all nodes streaming, Lag 0.
The primary may have changed (from the switchover) — that’s expected.
Your data is intact via the cluster address and from each node.
Keep the cluster address open in a browser tab during the rollout — it should
stay reachable the whole time. A single node being briefly offline while it
reboots is normal and does not interrupt the service.
The node that failed its health gate automatically rolls back to its
previous version (OS + application together) and rejoins on the old version.
The rollout halts at that point — every other node keeps serving, so you
are never left mid-upgrade with the service down.
The panel shows which node failed and why.
Resolve the cause on that node (usually disk space or a transient health issue),
then run Preflight and Start rolling upgrade again — nodes already on the
new version are skipped, and the rollout continues from where it stopped.
The cluster isn’t in a safe state to roll: a node is down, replication lag is
non-zero, or quorum is not met. Open the Cluster board, bring the cluster
back to Healthy · Quorum N/N · Lag 0, then re-run Preflight.
Preflight failed — bundle rejected
The bundle’s signature/format didn’t validate or it isn’t a permitted version
step (only current → next is allowed). Re-download the correct release bundle
from Kvelden.
A node row shows disk red
That node is short on free space for the bundle + new images. Free space on
it (remove old uploaded bundles under Software Update), then re-run
Preflight.
A node row shows version red
That node’s version is too far from the target — only an adjacent step is
allowed. Make sure every node is on the same current version before rolling.
A node row shows unreachable
The initiating node couldn’t reach that peer over the cluster network.
Confirm the node is Up on the Cluster board and that the nodes can reach
each other on the cluster network, then re-run Preflight.
The rollout halted partway
A node failed and rolled itself back; the rest kept serving. Review the
reason in the panel, fix it on that node, and re-run Preflight → Start —
already-upgraded nodes are skipped.
A node is stuck 'distributing' / 'waiting for the bundle'
Each node now shows its own reason on the board (pulling, waiting on a
peer, or “self-healing”). Distribution self-heals — it retries every source
every few seconds and fails fast on an unresponsive peer, so a single slow node
no longer strands the roll. If a node stays stuck, it is usually a node-local
issue (disk space, or a wedged stack): free space, or restart that node’s stack
(leadership fails over automatically), then the roll resumes. A distribution
that genuinely cannot reach every node halts safely with an actionable message
rather than hanging — nothing has been applied at that point.
The rollout always fails safe: it never auto-rolls-back a partially-upgraded
cluster. A halt holds in a supported mixed state (some nodes new, some old) until
you resume or abort — the old primary keeps serving throughout, so there is no
downtime while you investigate.
{"docsConfig":{"$schema":"https://mintlify.com/docs.json","theme":"mint","name":"Kvelden Docs","colors":{"primary":"#00C9A7","light":"#2dd4bf","dark":"#0d9488"},"favicon":"https://mintcdn.com/quelden/oKCNshq5BG5rVXut/logo/icon.svg?fit=max\u0026auto=format\u0026n=oKCNshq5BG5rVXut\u0026q=85\u0026s=e79b6958bbeea4b05d28b9ddec85a0d4","logo":{"light":"https://mintcdn.com/quelden/oKCNshq5BG5rVXut/logo/kvelden-logo-horizontal-on-light.svg?fit=max\u0026auto=format\u0026n=oKCNshq5BG5rVXut\u0026q=85\u0026s=5f0d1d63aa343c49681026d2af610c8a","dark":"https://mintcdn.com/quelden/oKCNshq5BG5rVXut/logo/kvelden-logo-horizontal-on-dark.svg?fit=max\u0026auto=format\u0026n=oKCNshq5BG5rVXut\u0026q=85\u0026s=7bdd9cf74d62e7f0f2eb68dbd1e8480b","href":"https://kvelden.com"},"navbar":{"links":[{"label":"Website","href":"https://kvelden.com"}],"primary":{"type":"button","label":"Get Enclave","href":"https://kvelden.com/request-access"}},"footer":{"socials":{"linkedin":"https://linkedin.com/company/kvelden","youtube":"https://www.youtube.com/@kvelden","facebook":"https://www.facebook.com/people/Kvelden/61590973313213"}},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"quelden","hasAudioNarration":false,"actualSubdomain":"quelden","customDomains":["docs.kvelden.com"],"gitSource":{"type":"github","owner":"kveldentech","repo":"docs","deployBranch":"main","contentDirectory":"","isPrivate":false,"mountPath":"","label":""},"entitlements":{"AI_CHAT":{"status":"DISABLED"}},"buildId":"6ad50ac8f20f038492fb176e","clientVersion":"0.0.4117","searchFilterCounts":[{"language":"default","tag":"Organization","count":20},{"language":"default","tag":"Platform","count":17},{"language":"default","tag":"Self-Hosted Appliance","count":14},{"language":"default","tag":"Deploy the Appliance","count":11},{"language":"default","tag":"Platform Guide","count":11},{"language":"default","tag":"Security \u0026 Trust","count":8},{"language":"default","tag":"SaaS","count":5},{"language":"default","tag":"Deploy on SaaS","count":5},{"language":"default","tag":"Documentation","count":4},{"language":"default","tag":"Get Started","count":4}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-rbac-roles","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-metadata","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"enclave/appliance/upgrade/cluster","slug":"/enclave/appliance/upgrade/cluster","pageMetadata":{"title":"Cluster in-place upgrade (GUI)","description":"Step-by-step guide to upgrading a Kvelden Enclave cluster to a new release from the admin GUI with zero downtime — one node at a time, standbys first and the primary last, with automatic bundle distribution, per-node readiness checks, and automatic rollback of any node that fails.","href":"/enclave/appliance/upgrade/cluster"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Step-by-step guide to upgrading a Kvelden Enclave cluster to a new release from the admin GUI with zero downtime — one node at a time, standbys first and the primary last, with automatic bundle distribution, per-node readiness checks, and automatic rollback of any node that fails.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,li:`li`,p:`p`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{Accordion,AccordionGroup,Card,Check,Heading,Info,Note,Step,Steps}=_components;if(!Accordion)_missingMdxReference(`Accordion`,true);if(!AccordionGroup)_missingMdxReference(`AccordionGroup`,true);if(!Card)_missingMdxReference(`Card`,true);if(!Check)_missingMdxReference(`Check`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Info)_missingMdxReference(`Info`,true);if(!Note)_missingMdxReference(`Note`,true);if(!Step)_missingMdxReference(`Step`,true);if(!Steps)_missingMdxReference(`Steps`,true);return _jsxs(_Fragment,{children:[_jsx(Info,{children:_jsxs(_components.p,{children:[`This page covers a `,_jsx(_components.strong,{children:`clustered`}),` deployment (two or more appliances joined for\nhigh availability). The rolling upgrade takes each node through the new version\n`,_jsx(_components.strong,{children:`one at a time — standbys first, the primary last — so the service stays\navailable throughout (zero user-visible downtime).`}),` For a single appliance,\nuse the `,_jsx(_components.a,{href:`/enclave/appliance/upgrade/standalone`,children:`Standalone upgrade`}),` instead.`]})}),`\n`,_jsx(Note,{children:_jsxs(_components.p,{children:[`New to how this works? Read `,_jsx(_components.a,{href:`/enclave/appliance/upgrade/overview`,children:`System upgrade`}),`\nfirst — it explains the signed bundle, the A/B slots, and the automatic safety\nnet that each node uses during its own step.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`how-the-rolling-upgrade-works`,children:`How the rolling upgrade works`}),`\n`,_jsxs(_components.p,{children:[`You upload the new bundle `,_jsx(_components.strong,{children:`once`}),`. The cluster then upgrades itself node by node:`]}),`\n`,_jsxs(Steps,{children:[_jsx(Step,{title:`Standbys first, primary last`,children:_jsxs(_components.p,{children:[`Standby nodes upgrade before the primary. When it’s the primary’s turn, the\ncluster `,_jsx(_components.strong,{children:`automatically fails over`}),` to an already-upgraded standby first, so\na writable node is always serving.`]})}),_jsx(Step,{title:`One node at a time`,children:_jsx(_components.p,{children:`Only one node is ever offline (rebooting into the new version) at a time. The\nremaining nodes keep a quorum and keep serving through the stable ingress\naddress (VIP / load balancer / DNS).`})}),_jsx(Step,{title:`Automatic distribution`,children:_jsx(_components.p,{children:`Each node pulls the bundle from the node you uploaded to over the cluster\nnetwork — you don’t copy files to each appliance.`})}),_jsx(Step,{title:`Per-node safety net`,children:_jsxs(_components.p,{children:[`Each node applies the update with the same verified, self-healing engine as a\nstandalone upgrade (verify → snapshot → install to the standby slot → reboot →\nhealth-gate → commit or auto-rollback). A node that fails `,_jsx(_components.strong,{children:`halts the rollout\nwith the other nodes still serving`}),`.`]})})]}),`\n`,_jsx(Heading,{level:`2`,id:`before-you-start`,children:`Before you start`}),`\n`,_jsxs(AccordionGroup,{children:[_jsx(Accordion,{title:`HA clustering license`,id:`ha-clustering-license`,children:_jsxs(_components.p,{children:[`The rolling upgrade requires the `,_jsx(_components.strong,{children:`HA clustering`}),` capability (the same one\nused to form the cluster). Check `,_jsx(_components.strong,{children:`Admin → Licensing`}),`.`]})}),_jsx(Accordion,{title:`Sign in as the owner, via the cluster address`,id:`sign-in-as-the-owner-via-the-cluster-address`,children:_jsxs(_components.p,{children:[`Open the cluster’s ingress address (the VIP, load balancer, or DNS name) and\nsign in as the `,_jsx(_components.strong,{children:`owner`}),`. Manage the upgrade from `,_jsx(_components.strong,{children:`Admin → Cluster / High\nAvailability`}),`.`]})}),_jsx(Accordion,{title:`Cluster is healthy`,id:`cluster-is-healthy`,children:_jsxs(_components.p,{children:[`On the Cluster board, confirm `,_jsx(_components.strong,{children:`Healthy`}),`, `,_jsx(_components.strong,{children:`Quorum N/N OK`}),`, every node\n`,_jsx(_components.strong,{children:`Up / streaming`}),`, and `,_jsx(_components.strong,{children:`Lag 0`}),`. Resolve any degraded node before\nupgrading — the preflight will refuse an unhealthy cluster.`]})}),_jsx(Accordion,{title:`Free disk space on every node`,id:`free-disk-space-on-every-node`,children:_jsx(_components.p,{children:`Each node needs working room for the pulled bundle plus the new images. The\nper-node readiness check verifies this and marks any short node red before\nyou can start.`})}),_jsx(Accordion,{title:`Clock synchronized on every node`,id:`clock-synchronized-on-every-node`,children:_jsxs(_components.p,{children:[`All nodes must have a correct, synchronized clock (required for leader\nelection, TLS, and signature validation). Confirm under `,_jsx(_components.strong,{children:`System →\nDiagnostics`}),` on each node.`]})}),_jsx(Accordion,{title:`A backup key on every node`,id:`a-backup-key-on-every-node`,children:_jsxs(_components.p,{children:[`Each node takes its own safety snapshot during its step, which requires a\nbackup key. Set one on every node under `,_jsx(_components.strong,{children:`Admin → Backup \u0026 Restore`}),` if\nnot already configured.`]})}),_jsx(Accordion,{title:`Same bundle, compatible version`,id:`same-bundle-compatible-version`,children:_jsxs(_components.p,{children:[`Use one `,_jsx(_components.code,{children:`enclave-update-\u003cversion>.tar.gz`}),` for the whole cluster. Only an\nadjacent version step is allowed (the current version → the next); the\npreflight enforces this.`]})})]}),`\n`,_jsx(Heading,{level:`2`,id:`upgrade-steps`,children:`Upgrade steps`}),`\n`,_jsxs(Steps,{children:[_jsx(Step,{title:`Upload the bundle (once)`,children:_jsxs(_components.p,{children:[`Go to `,_jsx(_components.strong,{children:`System → Software Update`}),` and upload\n`,_jsx(_components.code,{children:`enclave-update-\u003cversion>.tar.gz`}),` with `,_jsx(_components.strong,{children:`Choose bundle…`}),`. You only need to\ndo this on the node you’re signed in to — the cluster distributes it to the\nother nodes automatically.`]})}),_jsx(Step,{title:`Open the Rolling upgrade panel`,children:_jsxs(_components.p,{children:[`Go to `,_jsx(_components.strong,{children:`Admin → Cluster / High Availability`}),` and scroll to `,_jsx(_components.strong,{children:`Rolling\nupgrade`}),`. Select the bundle you uploaded from the `,_jsx(_components.strong,{children:`Update bundle`}),`\ndrop-down.`]})}),_jsxs(Step,{title:`Run Preflight`,children:[_jsxs(_components.p,{children:[`Select `,_jsx(_components.strong,{children:`Preflight`}),`. The cluster checks it is safe to roll:`]}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The bundle is `,_jsx(_components.strong,{children:`signed, valid, and a permitted version step`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`The cluster has `,_jsx(_components.strong,{children:`quorum`}),`, every node is `,_jsx(_components.strong,{children:`running/streaming`}),`, and\n`,_jsx(_components.strong,{children:`replication lag is 0`}),`.`]}),`\n`]}),_jsxs(_components.p,{children:[`A green confirmation (`,_jsx(_components.code,{children:`cluster preflight OK … \u003cfrom> → \u003cto>`}),`) means the\ncluster is ready.`]})]}),_jsx(Step,{title:`Review the per-node readiness table`,children:_jsxs(_components.p,{children:[`After preflight passes, a `,_jsx(_components.strong,{children:`per-node readiness`}),` table appears with a row per\nnode and `,_jsx(_components.strong,{children:`disk / version / health`}),` indicators plus reachability. Every node\nmust be `,_jsx(_components.strong,{children:`ready`}),` (all green). A red row shows the reason — see\n`,_jsx(_components.a,{href:`#troubleshooting`,children:`Troubleshooting`}),`. `,_jsx(_components.strong,{children:`Start`}),` stays disabled until all nodes\nare ready.`]})}),_jsx(Step,{title:`Start the rolling upgrade`,children:_jsxs(_components.p,{children:[`Select `,_jsx(_components.strong,{children:`Start rolling upgrade`}),` and confirm your password (step-up). The\nrollout begins.`]})}),_jsxs(Step,{title:`Watch it roll, node by node`,children:[_jsxs(_components.p,{children:[`Each node, standbys first, goes through: `,_jsx(_components.strong,{children:`distribute`}),` (pull the bundle) →\n`,_jsx(_components.strong,{children:`install to the standby slot`}),` → `,_jsx(_components.strong,{children:`reboot \u0026 activate`}),` → `,_jsx(_components.strong,{children:`health-gate`}),` →\n`,_jsx(_components.strong,{children:`rejoin`}),` (streaming, Lag 0). The panel shows the current node and stage.`]}),_jsxs(_components.p,{children:[`When it’s the `,_jsx(_components.strong,{children:`primary’s`}),` turn, the cluster `,_jsx(_components.strong,{children:`switches over`}),` to an\nupgraded standby first (the ingress address follows), then upgrades the old\nprimary. Throughout, the service stays reachable on the cluster address.`]})]}),_jsxs(Step,{title:`Verify the cluster is fully up-level`,children:[_jsx(_components.p,{children:`When the rollout completes, confirm on the Cluster board:`}),_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:_jsx(_components.strong,{children:`All nodes show the new version.`})}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Healthy`}),`, `,_jsx(_components.strong,{children:`Quorum N/N OK`}),`, all nodes `,_jsx(_components.strong,{children:`streaming`}),`, `,_jsx(_components.strong,{children:`Lag 0`}),`.`]}),`\n`,_jsx(_components.li,{children:`The primary may have changed (from the switchover) — that’s expected.`}),`\n`,_jsx(_components.li,{children:`Your data is intact via the cluster address and from each node.`}),`\n`]})]})]}),`\n`,_jsx(Check,{children:_jsx(_components.p,{children:`Keep the cluster address open in a browser tab during the rollout — it should\nstay reachable the whole time. A single node being briefly offline while it\nreboots is normal and does not interrupt the service.`})}),`\n`,_jsx(Heading,{level:`2`,id:`if-a-node-fails`,children:`If a node fails`}),`\n`,_jsxs(_components.p,{children:[`The rollout is `,_jsx(_components.strong,{children:`fail-safe`}),`:`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The node that failed its health gate `,_jsx(_components.strong,{children:`automatically rolls back`}),` to its\nprevious version (OS + application together) and rejoins on the old version.`]}),`\n`,_jsxs(_components.li,{children:[`The `,_jsx(_components.strong,{children:`rollout halts`}),` at that point — every other node keeps serving, so you\nare never left mid-upgrade with the service down.`]}),`\n`,_jsx(_components.li,{children:`The panel shows which node failed and why.`}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`Resolve the cause on that node (usually disk space or a transient health issue),\nthen run `,_jsx(_components.strong,{children:`Preflight`}),` and `,_jsx(_components.strong,{children:`Start rolling upgrade`}),` again — nodes already on the\nnew version are skipped, and the rollout continues from where it stopped.`]}),`\n`,_jsx(Heading,{level:`2`,id:`troubleshooting`,children:`Troubleshooting`}),`\n`,_jsxs(AccordionGroup,{children:[_jsx(Accordion,{title:`Preflight failed — cluster not ready`,id:`preflight-failed-cluster-not-ready`,children:_jsxs(_components.p,{children:[`The cluster isn’t in a safe state to roll: a node is down, replication lag is\nnon-zero, or quorum is not met. Open the Cluster board, bring the cluster\nback to `,_jsx(_components.strong,{children:`Healthy · Quorum N/N · Lag 0`}),`, then re-run Preflight.`]})}),_jsx(Accordion,{title:`Preflight failed — bundle rejected`,id:`preflight-failed-bundle-rejected`,children:_jsx(_components.p,{children:`The bundle’s signature/format didn’t validate or it isn’t a permitted version\nstep (only current → next is allowed). Re-download the correct release bundle\nfrom Kvelden.`})}),_jsx(Accordion,{title:`A node row shows disk red`,id:`a-node-row-shows-disk-red`,children:_jsxs(_components.p,{children:[`That node is short on free space for the bundle + new images. Free space on\nit (remove old uploaded bundles under `,_jsx(_components.strong,{children:`Software Update`}),`), then re-run\nPreflight.`]})}),_jsx(Accordion,{title:`A node row shows version red`,id:`a-node-row-shows-version-red`,children:_jsx(_components.p,{children:`That node’s version is too far from the target — only an adjacent step is\nallowed. Make sure every node is on the same current version before rolling.`})}),_jsx(Accordion,{title:`A node row shows unreachable`,id:`a-node-row-shows-unreachable`,children:_jsxs(_components.p,{children:[`The initiating node couldn’t reach that peer over the cluster network.\nConfirm the node is `,_jsx(_components.strong,{children:`Up`}),` on the Cluster board and that the nodes can reach\neach other on the cluster network, then re-run Preflight.`]})}),_jsx(Accordion,{title:`The rollout halted partway`,id:`the-rollout-halted-partway`,children:_jsxs(_components.p,{children:[`A node failed and rolled itself back; the rest kept serving. Review the\nreason in the panel, fix it on that node, and re-run `,_jsx(_components.strong,{children:`Preflight → Start`}),` —\nalready-upgraded nodes are skipped.`]})}),_jsx(Accordion,{title:`A node is stuck 'distributing' / 'waiting for the bundle'`,id:`a-node-is-stuck-distributing-waiting-for-the-bundle`,children:_jsxs(_components.p,{children:[`Each node now shows its `,_jsx(_components.strong,{children:`own`}),` reason on the board (pulling, waiting on a\npeer, or “self-healing”). Distribution self-heals — it retries every source\nevery few seconds and fails fast on an unresponsive peer, so a single slow node\nno longer strands the roll. If a node stays stuck, it is usually a node-local\nissue (disk space, or a wedged stack): free space, or restart that node’s stack\n(leadership fails over automatically), then the roll resumes. A distribution\nthat genuinely cannot reach every node halts safely with an actionable message\nrather than hanging — nothing has been applied at that point.`]})})]}),`\n`,_jsx(Info,{children:_jsxs(_components.p,{children:[`The rollout `,_jsx(_components.strong,{children:`always fails safe`}),`: it never auto-rolls-back a partially-upgraded\ncluster. A halt holds in a supported mixed state (some nodes new, some old) until\nyou resume or abort — the old primary keeps serving throughout, so there is no\ndowntime while you investigate.`]})}),`\n`,_jsx(Card,{title:`Related`,icon:`layer-group`,href:`/enclave/appliance/clustering`,children:_jsxs(_components.p,{children:[`See `,_jsx(_components.a,{href:`/enclave/appliance/clustering`,children:`High availability \u0026 clustering`}),` for\nhow to form and operate the cluster itself.`]})})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Cluster in-place upgrade (GUI)","description":"Step-by-step guide to upgrading a Kvelden Enclave cluster to a new release from the admin GUI with zero downtime — one node at a time, standbys first and the primary last, with automatic bundle distribution, per-node readiness checks, and automatic rollback of any node that fails.","href":"/enclave/appliance/upgrade/cluster"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"How the rolling upgrade works","slug":"how-the-rolling-upgrade-works","depth":2,"children":[]},{"title":"Before you start","slug":"before-you-start","depth":2,"children":[]},{"title":"Upgrade steps","slug":"upgrade-steps","depth":2,"children":[]},{"title":"If a node fails","slug":"if-a-node-fails","depth":2,"children":[]},{"title":"Troubleshooting","slug":"troubleshooting","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"quelden","siteName":"Kvelden Docs","title":"Cluster in-place upgrade (GUI) - Kvelden Docs","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-09-24T06:02:59.025Z","originalFileLocation":"enclave/appliance/upgrade/cluster.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Step-by-step guide to upgrading a Kvelden Enclave cluster to a new release from the admin GUI with zero downtime — one node at a time, standbys first and the primary last, with automatic bundle distribution, per-node readiness checks, and automatic rollback of any node that fails.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Kvelden Docs\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/quelden/xg8MK2Aw4uo0TQDh/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/quelden/xg8MK2Aw4uo0TQDh/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/quelden/xg8MK2Aw4uo0TQDh/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/quelden/xg8MK2Aw4uo0TQDh/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/quelden/xg8MK2Aw4uo0TQDh/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/quelden/xg8MK2Aw4uo0TQDh/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/quelden/xg8MK2Aw4uo0TQDh/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/quelden/xg8MK2Aw4uo0TQDh/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/quelden/xg8MK2Aw4uo0TQDh/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Cluster in-place upgrade (GUI) - Kvelden Docs\">\u003cmeta data-mint-head property=\"og:description\" content=\"Step-by-step guide to upgrading a Kvelden Enclave cluster to a new release from the admin GUI with zero downtime — one node at a time, standbys first and the primary last, with automatic bundle distribution, per-node readiness checks, and automatic rollback of any node that fails.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Kvelden Docs\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.kvelden.com/enclave/appliance/upgrade/cluster\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://quelden.mintlify.site/_mintlify/api/og/v2?division=System+Upgrade\u0026amp;title=Cluster+in-place+upgrade+%28GUI%29\u0026amp;description=Step-by-step+guide+to+upgrading+a+Kvelden+Enclave+cluster+to+a+new+release+from+the+admin+GUI+with+zero+downtime+%E2%80%94+one+node+at+a+time%2C+standbys+first+and+the+pr\u0026amp;theme=da089d14d9ab8ff60e45ae6e\u0026amp;signature=797e7eb98414413e8b2f68a81820f1299180ad6d9c88821372e7cdf6f364e3ca\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Cluster in-place upgrade (GUI) - Kvelden Docs\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Step-by-step guide to upgrading a Kvelden Enclave cluster to a new release from the admin GUI with zero downtime — one node at a time, standbys first and the primary last, with automatic bundle distribution, per-node readiness checks, and automatic rollback of any node that fails.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://quelden.mintlify.site/_mintlify/api/og/v2?division=System+Upgrade\u0026amp;title=Cluster+in-place+upgrade+%28GUI%29\u0026amp;description=Step-by-step+guide+to+upgrading+a+Kvelden+Enclave+cluster+to+a+new+release+from+the+admin+GUI+with+zero+downtime+%E2%80%94+one+node+at+a+time%2C+standbys+first+and+the+pr\u0026amp;theme=da089d14d9ab8ff60e45ae6e\u0026amp;signature=797e7eb98414413e8b2f68a81820f1299180ad6d9c88821372e7cdf6f364e3ca\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/quelden/xg8MK2Aw4uo0TQDh/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Kvelden Docs\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#00C9A7\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.kvelden.com/enclave/appliance/upgrade/cluster\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/enclave/appliance/upgrade/cluster.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.kvelden.com/#organization\",\"name\":\"Kvelden Docs\",\"url\":\"https://docs.kvelden.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/quelden/oKCNshq5BG5rVXut/logo/kvelden-logo-horizontal-on-light.svg?fit=max\u0026auto=format\u0026n=oKCNshq5BG5rVXut\u0026q=85\u0026s=5f0d1d63aa343c49681026d2af610c8a\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.kvelden.com#website\",\"name\":\"Kvelden Docs\",\"url\":\"https://docs.kvelden.com\",\"publisher\":{\"@id\":\"https://docs.kvelden.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.kvelden.com/enclave/appliance/upgrade/cluster#webpage\",\"url\":\"https://docs.kvelden.com/enclave/appliance/upgrade/cluster\",\"name\":\"Cluster in-place upgrade (GUI)\",\"description\":\"Step-by-step guide to upgrading a Kvelden Enclave cluster to a new release from the admin GUI with zero downtime — one node at a time, standbys first and the primary last, with automatic bundle distribution, per-node readiness checks, and automatic rollback of any node that fails.\",\"dateModified\":\"2026-09-24T06:02:59.025Z\",\"isPartOf\":{\"@id\":\"https://docs.kvelden.com#website\"},\"breadcrumb\":{\"@id\":\"https://docs.kvelden.com/enclave/appliance/upgrade/cluster#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.kvelden.com/enclave/appliance/upgrade/cluster#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"System Upgrade\",\"item\":\"https://docs.kvelden.com/enclave/appliance/upgrade/overview\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Cluster in-place upgrade (GUI)\",\"item\":\"https://docs.kvelden.com/enclave/appliance/upgrade/cluster\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.kvelden.com/enclave/appliance/upgrade/cluster#article\",\"headline\":\"Cluster in-place upgrade (GUI)\",\"name\":\"Cluster in-place upgrade (GUI)\",\"description\":\"Step-by-step guide to upgrading a Kvelden Enclave cluster to a new release from the admin GUI with zero downtime — one node at a time, standbys first and the primary last, with automatic bundle distribution, per-node readiness checks, and automatic rollback of any node that fails.\",\"url\":\"https://docs.kvelden.com/enclave/appliance/upgrade/cluster\",\"mainEntityOfPage\":{\"@id\":\"https://docs.kvelden.com/enclave/appliance/upgrade/cluster#webpage\"},\"dateModified\":\"2026-09-24T06:02:59.025Z\",\"publisher\":{\"@id\":\"https://docs.kvelden.com/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.kvelden.com#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Kvelden Docs\",\"publisher\":{\"@type\":\"Organization\",\"name\":\"Kvelden Docs\"},\"provider\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","agentPointerHref":"/enclave/appliance/upgrade/cluster.md","siteName":"Kvelden Docs","nav":{"global":null,"tabs":[{"tab":"Documentation","pages":[{"title":"Documentation","href":"/enclave/introduction"}]},{"tab":"Self-Hosted Appliance","groups":[{"group":"Deploy the Appliance","pages":[{"title":"Private Virtual Appliance","href":"/enclave/appliance/overview"},{"title":"First-run setup","href":"/enclave/appliance/first-run"},{"title":"Network Configuration","href":"/enclave/appliance/network"},{"title":"Web TLS certificate \u0026 hostname","href":"/enclave/appliance/tls"},{"title":"LDAP / Active Directory sign-in","href":"/enclave/appliance/ldap"},{"title":"Licensing \u0026 Activation","href":"/enclave/appliance/licensing"},{"title":"KMS Configuration","href":"/enclave/appliance/kms"},{"title":"Storage Configuration","href":"/enclave/appliance/storage"},{"title":"Network Ports \u0026 Firewall","href":"/enclave/appliance/ports"},{"title":"High availability \u0026 clustering","href":"/enclave/appliance/clustering"},{"title":"Backup \u0026 Restore","href":"/enclave/appliance/backup-restore"}]},{"group":"System Upgrade","pages":[{"title":"System upgrade","href":"/enclave/appliance/upgrade/overview"},{"title":"Standalone upgrade (GUI)","description":"Step-by-step guide to upgrading a single (non-clustered) Kvelden Enclave appliance to a new release from the admin GUI. Signed and verified, with a brief reboot and automatic rollback if the new version is unhealthy.","href":"/enclave/appliance/upgrade/standalone"},{"title":"Cluster in-place upgrade (GUI)","href":"/enclave/appliance/upgrade/cluster"}]}]},{"tab":"SaaS","pages":[{"title":"SaaS","href":"/enclave/saas/overview"}]},{"tab":"Platform","pages":[{"title":"Platform","href":"/enclave/platform/overview"}]},{"tab":"Organization","pages":[{"title":"Organization","href":"/enclave/organization/overview"}]},{"tab":"Security \u0026 Trust","pages":[{"title":"Security \u0026 Trust","href":"/enclave/security/architecture"}]}]},"basePath":"","title":"Cluster in-place upgrade (GUI) - Kvelden Docs","buildId":"dpl_41S5Z2jG9QnfDRwN1KZrLdBwj7DM","deploymentId":"dpl_41S5Z2jG9QnfDRwN1KZrLdBwj7DM","dynamics":[],"chunks":[]}