[{"data":1,"prerenderedAt":596},["ShallowReactive",2],{"navigation_docs":3,"-operate-high-availability":136,"-operate-high-availability-surround":591},[4,26,47,68,89,110,123],{"title":5,"path":6,"stem":7,"children":8,"page":25},"Getting Started","\u002Fgetting-started","1.getting-started",[9,13,17,21],{"title":10,"path":11,"stem":12},"What you are running","\u002Fgetting-started\u002Fwhat-you-are-running","1.getting-started\u002F1.what-you-are-running",{"title":14,"path":15,"stem":16},"Architecture","\u002Fgetting-started\u002Farchitecture","1.getting-started\u002F2.architecture",{"title":18,"path":19,"stem":20},"Prerequisites","\u002Fgetting-started\u002Fprerequisites","1.getting-started\u002F3.prerequisites",{"title":22,"path":23,"stem":24},"Quickstart","\u002Fgetting-started\u002Fquickstart","1.getting-started\u002F4.quickstart",false,{"title":27,"path":28,"stem":29,"children":30,"page":25},"Install","\u002Finstall","2.install",[31,35,39,43],{"title":32,"path":33,"stem":34},"Install the chart","\u002Finstall\u002Finstall-the-chart","2.install\u002F1.install-the-chart",{"title":36,"path":37,"stem":38},"Activation","\u002Finstall\u002Factivation","2.install\u002F2.activation",{"title":40,"path":41,"stem":42},"DNS and TLS","\u002Finstall\u002Fdns-and-tls","2.install\u002F3.dns-and-tls",{"title":44,"path":45,"stem":46},"Verify your install","\u002Finstall\u002Fverify-your-install","2.install\u002F4.verify-your-install",{"title":48,"path":49,"stem":50,"children":51,"page":25},"Configure","\u002Fconfigure","3.configure",[52,56,60,64],{"title":53,"path":54,"stem":55},"Data services","\u002Fconfigure\u002Fdata-services","3.configure\u002F1.data-services",{"title":57,"path":58,"stem":59},"Secrets","\u002Fconfigure\u002Fsecrets","3.configure\u002F2.secrets",{"title":61,"path":62,"stem":63},"Registry and air-gapped installs","\u002Fconfigure\u002Fregistry-and-airgap","3.configure\u002F3.registry-and-airgap",{"title":65,"path":66,"stem":67},"Integrations","\u002Fconfigure\u002Fintegrations","3.configure\u002F4.integrations",{"title":69,"path":70,"stem":71,"children":72,"page":25},"Licensing","\u002Flicensing","4.licensing",[73,77,81,85],{"title":74,"path":75,"stem":76},"How licensing works","\u002Flicensing\u002Fhow-licensing-works","4.licensing\u002F1.how-licensing-works",{"title":78,"path":79,"stem":80},"Your key never leaves","\u002Flicensing\u002Fyour-key-never-leaves","4.licensing\u002F2.your-key-never-leaves",{"title":82,"path":83,"stem":84},"What phones home","\u002Flicensing\u002Fwhat-phones-home","4.licensing\u002F3.what-phones-home",{"title":86,"path":87,"stem":88},"Revoke and recover","\u002Flicensing\u002Frevoke-and-recover","4.licensing\u002F4.revoke-and-recover",{"title":90,"path":91,"stem":92,"children":93,"page":25},"Operate","\u002Foperate","5.operate",[94,98,102,106],{"title":95,"path":96,"stem":97},"Upgrade","\u002Foperate\u002Fupgrade","5.operate\u002F1.upgrade",{"title":99,"path":100,"stem":101},"Backup and restore","\u002Foperate\u002Fbackup-and-restore","5.operate\u002F2.backup-and-restore",{"title":103,"path":104,"stem":105},"Observability","\u002Foperate\u002Fobservability","5.operate\u002F3.observability",{"title":107,"path":108,"stem":109},"High availability","\u002Foperate\u002Fhigh-availability","5.operate\u002F4.high-availability",{"title":111,"path":112,"stem":113,"children":114,"page":25},"Troubleshooting","\u002Ftroubleshooting","6.troubleshooting",[115,119],{"title":116,"path":117,"stem":118},"Install failures","\u002Ftroubleshooting\u002Finstall-failures","6.troubleshooting\u002F1.install-failures",{"title":120,"path":121,"stem":122},"Licensing failures","\u002Ftroubleshooting\u002Flicensing-failures","6.troubleshooting\u002F2.licensing-failures",{"title":124,"path":125,"stem":126,"children":127,"page":25},"Reference","\u002Freference","9.reference",[128,132],{"title":129,"path":130,"stem":131},"Helm values","\u002Freference\u002Fhelm-values","9.reference\u002F1.helm-values",{"title":133,"path":134,"stem":135},"Environment variables","\u002Freference\u002Fenvironment","9.reference\u002F2.environment",{"id":137,"title":107,"body":138,"description":584,"extension":585,"links":586,"meta":587,"navigation":588,"path":108,"seo":589,"stem":109,"__hash__":590},"docs\u002F5.operate\u002F4.high-availability.md",{"type":139,"value":140,"toc":574},"minimark",[141,145,149,153,156,271,298,309,313,384,394,403,409,418,425,431,503,510,514,525,532,536,547,550,556,559,563,570],[142,143,107],"h1",{"id":144},"high-availability",[146,147,148],"p",{},"A default install is a single-node stack: one Postgres, one Redis, one Kafka, one of everything else. That is the right shape for a pilot and the wrong shape for production. Here is what to change.",[150,151,53],"h2",{"id":152},"data-services",[146,154,155],{},"Postgres and Kafka run under an operator by default, CloudNativePG for Postgres and Strimzi for Kafka, and there is no mode switch to flip for HA: the operator is already reconciling a real clustered topology, it just defaults to one replica. Raise the counts:",[157,158,163],"pre",{"className":159,"code":160,"language":161,"meta":162,"style":162},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","helm upgrade my-mvno oci:\u002F\u002Fghcr.io\u002Fsolvegio\u002Fcharts\u002Fplatform \\\n  --version \u003Cversion> --namespace my-mvno -f my-values.yaml \\\n  --set postgres.instances=3 \\\n  --set kafka.replicas=3 \\\n  --set kafka.replicationFactor=3 \\\n  --set kafka.minInsync=2\n","bash","",[164,165,166,189,221,236,248,260],"code",{"__ignoreMap":162},[167,168,171,175,179,182,185],"span",{"class":169,"line":170},"line",1,[167,172,174],{"class":173},"sBMFI","helm",[167,176,178],{"class":177},"sfazB"," upgrade",[167,180,181],{"class":177}," my-mvno",[167,183,184],{"class":177}," oci:\u002F\u002Fghcr.io\u002Fsolvegio\u002Fcharts\u002Fplatform",[167,186,188],{"class":187},"sTEyZ"," \\\n",[167,190,192,195,199,202,205,208,211,213,216,219],{"class":169,"line":191},2,[167,193,194],{"class":177},"  --version",[167,196,198],{"class":197},"sMK4o"," \u003C",[167,200,201],{"class":177},"versio",[167,203,204],{"class":187},"n",[167,206,207],{"class":197},">",[167,209,210],{"class":177}," --namespace",[167,212,181],{"class":177},[167,214,215],{"class":177}," -f",[167,217,218],{"class":177}," my-values.yaml",[167,220,188],{"class":187},[167,222,224,227,230,234],{"class":169,"line":223},3,[167,225,226],{"class":177},"  --set",[167,228,229],{"class":177}," postgres.instances=",[167,231,233],{"class":232},"sbssI","3",[167,235,188],{"class":187},[167,237,239,241,244,246],{"class":169,"line":238},4,[167,240,226],{"class":177},[167,242,243],{"class":177}," kafka.replicas=",[167,245,233],{"class":232},[167,247,188],{"class":187},[167,249,251,253,256,258],{"class":169,"line":250},5,[167,252,226],{"class":177},[167,254,255],{"class":177}," kafka.replicationFactor=",[167,257,233],{"class":232},[167,259,188],{"class":187},[167,261,263,265,268],{"class":169,"line":262},6,[167,264,226],{"class":177},[167,266,267],{"class":177}," kafka.minInsync=",[167,269,270],{"class":232},"2\n",[146,272,273,276,277,280,281,284,285,276,288,291,292,294,295,297],{},[164,274,275],{},"postgres.instances"," and ",[164,278,279],{},"kafka.replicas"," both default to ",[164,282,283],{},"1",", which renders and reconciles perfectly well but gives you no redundancy at all. Three Postgres instances survive losing a node. ",[164,286,287],{},"kafka.replicationFactor",[164,289,290],{},"kafka.minInsync"," default to ",[164,293,283],{}," too; raise them alongside ",[164,296,279],{}," or a 3-broker cluster still writes every event to just one broker.",[146,299,300,301,304,305,308],{},"Redis is the exception: it stays a single bundled instance regardless, because there is no Redis operator behind it. If you need the cache itself to survive a node failure, point ",[164,302,303],{},"redis.mode: external"," at a managed Redis instead; that is the only HA path for the cache. Full details for all three, including the credential handling, are in ",[306,307,53],"a",{"href":54},".",[150,310,312],{"id":311},"api-replicas-and-disruption-budgets","API replicas and disruption budgets",[157,314,318],{"className":315,"code":316,"language":317,"meta":162,"style":162},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","highAvailability:\n  enabled: true\n  priorityClassName: \"\"\napi:\n  replicas: 2\nauth:\n  replicas: 2\n","yaml",[164,319,320,329,341,351,358,368,375],{"__ignoreMap":162},[167,321,322,326],{"class":169,"line":170},[167,323,325],{"class":324},"swJcz","highAvailability",[167,327,328],{"class":197},":\n",[167,330,331,334,337],{"class":169,"line":191},[167,332,333],{"class":324},"  enabled",[167,335,336],{"class":197},":",[167,338,340],{"class":339},"sfNiH"," true\n",[167,342,343,346,348],{"class":169,"line":223},[167,344,345],{"class":324},"  priorityClassName",[167,347,336],{"class":197},[167,349,350],{"class":197}," \"\"\n",[167,352,353,356],{"class":169,"line":238},[167,354,355],{"class":324},"api",[167,357,328],{"class":197},[167,359,360,363,365],{"class":169,"line":250},[167,361,362],{"class":324},"  replicas",[167,364,336],{"class":197},[167,366,367],{"class":232}," 2\n",[167,369,370,373],{"class":169,"line":262},[167,371,372],{"class":324},"auth",[167,374,328],{"class":197},[167,376,378,380,382],{"class":169,"line":377},7,[167,379,362],{"class":324},[167,381,336],{"class":197},[167,383,367],{"class":232},[146,385,386,389,390,393],{},[164,387,388],{},"highAvailability.enabled"," renders a PodDisruptionBudget for the API and auth Deployments, a soft zone spread across ",[164,391,392],{},"topology.kubernetes.io\u002Fzone",", and per-node anti-affinity so two replicas do not land on the same machine.",[395,396,398,399,402],"h3",{"id":397},"handle-priorityclassname-before-you-enable-it","Handle ",[164,400,401],{},"priorityClassName"," before you enable it",[146,404,405,406,408],{},"Note the empty ",[164,407,401],{}," above. It is doing real work.",[146,410,411,413,414,417],{},[164,412,388],{}," also stamps ",[164,415,416],{},"priorityClassName: solvegio-platform"," onto the API and auth pods, and no PriorityClass by that name exists in your cluster until you make one. Kubernetes rejects a pod that names a PriorityClass it cannot resolve, so if you turn HA on and leave that value at its default you get zero API pods and zero auth pods. A working install goes to nothing. Decide which of the two you want before the upgrade:",[146,419,420,424],{},[421,422,423],"strong",{},"Set it empty",", as in the snippet. The field is then not rendered at all and your pods schedule at normal priority. This is the right choice unless you have a reason to want otherwise.",[146,426,427,430],{},[421,428,429],{},"Or create the class first."," Worth doing on a shared cluster where you want platform pods to evict lower-priority workloads under node pressure:",[157,432,434],{"className":315,"code":433,"language":317,"meta":162,"style":162},"apiVersion: scheduling.k8s.io\u002Fv1\nkind: PriorityClass\nmetadata:\n  name: solvegio-platform\nvalue: 1000000\nglobalDefault: false\ndescription: Solvegio platform pods\n",[164,435,436,446,456,463,473,483,493],{"__ignoreMap":162},[167,437,438,441,443],{"class":169,"line":170},[167,439,440],{"class":324},"apiVersion",[167,442,336],{"class":197},[167,444,445],{"class":177}," scheduling.k8s.io\u002Fv1\n",[167,447,448,451,453],{"class":169,"line":191},[167,449,450],{"class":324},"kind",[167,452,336],{"class":197},[167,454,455],{"class":177}," PriorityClass\n",[167,457,458,461],{"class":169,"line":223},[167,459,460],{"class":324},"metadata",[167,462,328],{"class":197},[167,464,465,468,470],{"class":169,"line":238},[167,466,467],{"class":324},"  name",[167,469,336],{"class":197},[167,471,472],{"class":177}," solvegio-platform\n",[167,474,475,478,480],{"class":169,"line":250},[167,476,477],{"class":324},"value",[167,479,336],{"class":197},[167,481,482],{"class":232}," 1000000\n",[167,484,485,488,490],{"class":169,"line":262},[167,486,487],{"class":324},"globalDefault",[167,489,336],{"class":197},[167,491,492],{"class":339}," false\n",[167,494,495,498,500],{"class":169,"line":377},[167,496,497],{"class":324},"description",[167,499,336],{"class":197},[167,501,502],{"class":177}," Solvegio platform pods\n",[146,504,505,506,509],{},"Apply that, then leave ",[164,507,508],{},"highAvailability.priorityClassName"," at its default and the pods pick it up.",[395,511,513],{"id":512},"replicas-and-ha-move-together","Replicas and HA move together",[146,515,516,517,520,521,524],{},"Raise the replica counts and enable HA in the same change. The two are coupled: a PodDisruptionBudget with ",[164,518,519],{},"minAvailable: 1"," against a single replica blocks every node drain, because there is no way to evict the only pod without violating the budget. Enabling HA while leaving ",[164,522,523],{},"replicas: 1"," gives you a cluster you cannot upgrade.",[146,526,527,528,531],{},"If you want the API to scale under load rather than sit at a fixed floor, ",[164,529,530],{},"api.autoscaling.enabled"," adds an HPA on top. The replica count then acts as the floor a drain can always fall back to.",[150,533,535],{"id":534},"the-license-agent-runs-one-replica","The license agent runs one replica",[146,537,538,539,542,543,546],{},"The license agent is deliberately a single replica with a ",[164,540,541],{},"Recreate"," rollout strategy. Do not raise ",[164,544,545],{},"replicas"," on it. It is not supported, and it is not an oversight in the chart.",[146,548,549],{},"Two agents running at once means two writers against the same license, and the parent sees that as exactly what it looks like: something has gone wrong with this deployment's licensing. You get a false alarm on an otherwise healthy platform, and somebody has to come and unpick it. There is nothing to gain: a second replica does not renew your lease any faster.",[146,551,552,553,555],{},"The agent is not on the request path, so a single replica is not a single point of failure for your customers. The API reads the lease from a mounted file and keeps serving whether the agent is up or not. Everything the agent does (renewing the lease, rotating the key) happens well ahead of expiry, so the pod can be down for a rollout, a node drain or a short outage with no visible effect. What the agent's absence eventually costs you is the lease itself: if it stays down long enough for the held lease to expire, the platform fails closed. That is the bound to plan against, and ",[306,554,103],{"href":104}," is how you see it coming.",[146,557,558],{},"No PodDisruptionBudget is rendered for the agent, for the same reason. A budget against a single replica would block your node drains.",[150,560,562],{"id":561},"what-is-left-after-all-that","What is left after all that",[146,564,565,566,569],{},"Even fully configured, the failure that takes a self-hosted platform down is almost never a pod. It is the lease running out because nobody was watching the alert. Wire ",[164,567,568],{},"SolvegioLicenseAtRisk"," before you wire anything else.",[571,572,573],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":162,"searchDepth":170,"depth":191,"links":575},[576,577,582,583],{"id":152,"depth":191,"text":53},{"id":311,"depth":191,"text":312,"children":578},[579,581],{"id":397,"depth":223,"text":580},"Handle priorityClassName before you enable it",{"id":512,"depth":223,"text":513},{"id":534,"depth":191,"text":535},{"id":561,"depth":191,"text":562},"Operator-mode data services, multiple API replicas, and the one component that must stay at a single replica.","md",null,{},true,{"title":107,"description":584},"YSF_f7k1EwyNjhZ3kYuU6yN8boIp7IvHw3fsRomVV_A",[592,594],{"title":103,"path":104,"stem":105,"description":593,"children":-1},"The license agent's Prometheus metrics, and the one alert that tells you a licensed platform is about to stop serving.",{"title":116,"path":117,"stem":118,"description":595,"children":-1},"Pods that will not pull, an API that never goes ready, an Ingress with no address, and certificates that never issue.",1783967741116]