France's national railway just published the numbers every self-hosted platform team should stare at. SNCF, the state-owned operator running 4,000 to 5,000 trains a day, rebuilt its on-premises Kubernetes platform from scratch on Cluster API and Talos Linux — and cluster provisioning went from one month to 30 minutes.
Fleet growth went from 14 clusters in four years to 10 clusters in six months. Production incidents between its infrastructure and container layers fell 90%, maintenance effort dropped 66%, and every cluster in the fleet now updates monthly under what the team calls a zero-drift guarantee.
Those figures come from two 2026 case studies — one from the CNCF, one from Sidero Labs — and they describe the same rebuild: OpenStack underneath, Talos Linux as the immutable OS, Cluster API as the reconciliation core, ArgoCD on top. A national-infrastructure operator with safety-critical reliability requirements looked at hand-rolled scripts on owned hardware, declared the foundation unsteady, and replaced it with declarative machine lifecycle management.
That is the exact architecture underneath a modern self-hosted PaaS. Here is what SNCF's rebuild validates, where its OpenStack-plus-Talos stack diverges from a Hetzner-plus-CAPH one, and what a team running a handful of machines should actually copy.
What was broken: the naive first platform
SNCF's Cloud Native Team brokers services for around 400 internal projects — train management, stations, rolling stock, finance — and had operated Kubernetes in public clouds (Azure and AWS) for years. The problem was parity: delivering that same managed-Kubernetes experience inside its own datacenters.
Its first on-premises attempt was, in the CNCF case study's own word, "naive": functional, but built on manual processes and custom tooling. The pain points will be familiar to anyone who has hand-rolled fleet ops:
- Sluggish provisioning. Delivering a new cluster took up to a month.
- Stagnant growth. Only 14 clusters deployed in four years, strangled by operational overhead.
- Day-2 friction. Upgrades, patching, and scaling were constant struggles, producing "snowflake" clusters and configuration drift.
- Feature gap. Node autoscaling — table stakes on AKS and EKS — simply didn't exist on-premises.
An earlier experiment had already failed expensively: a year-long project building on Ubuntu with RKE2 that the team ultimately abandoned. As Senior Staff Engineer Yann Rotilio put it: "We soon concluded that applying incremental fixes on unsteady foundations would prove time-consuming without any guarantee of fixing the underlying issues. We needed to start again entirely."
So they did — with four design principles: treat nodes as unified OS-Kubernetes pairs with coupled versions, manage operations at the node-pool level to prevent drift, shrink the attack surface, and guarantee reliable rollback for upgrades. And they shipped a production-ready platform in four months.
The stack, layer by layer
SNCF calls the new stack "non-toxic and interchangeable" — modular, CNCF-aligned, no proprietary glue. Each layer kills a specific failure mode from the old platform:
| Layer | Choice | Failure mode it kills |
|---|---|---|
| IaaS | OpenStack (Canonical) | Ad-hoc compute/network/storage provisioning |
| OS | Talos Linux (Sidero Labs) | SSH-and-shell drift; every node managed via API only |
| Lifecycle | Cluster API (CAPI) | Snowflake clusters; clusters become reconciled resources |
| Networking | Cilium (eBPF) | Opaque, slow overlay networking without observability |
| Policy | Kyverno | Manual compliance checks across the fleet |
| GitOps | ArgoCD | Click-ops and out-of-band changes |
| Supply chain | ORAS (CAPI providers as OCI artifacts) | Unversioned, unreproducible provider installs |
Two decisions deserve underlining. First, Talos Linux eliminates SSH and manual configuration entirely — there is no shell to drift through, no package manager to desync, no way to "just hotfix this one node." Second, ORAS manages Cluster API's own providers as OCI artifacts, so even the components that provision the fleet are versioned, pullable, and GitOps-reconciled like any workload image. The supply chain rigor applies to the control plane's own bootstrapping layer, not just tenant images.
The headline capability unlocked was node autoscaling in SNCF's own datacenters — previously assumed to be an AKS/EKS exclusive. "Cluster API was a game-changer," said Rotilio. "It gave us node autoscaling in our datacenters — something we thought was only possible with AKS or EKS."
Why reconciliation, not scripts, is the mechanism
The before/after table from the CNCF case study is worth reproducing in full, because it is the artifact the whole argument rests on:
| Metric | Legacy on-premises | New cloud-native platform |
|---|---|---|
| Cluster provisioning | 1 month | 30 minutes |
| Fleet growth | 14 clusters in 4 years | 10 clusters in 6 months |
| Day-2 operations | Manual and challenging | On-demand and automated |
| Configuration drift | High | Zero-drift guarantee |
Two interlocking mechanisms produce that last row. Cluster API's reconciliation loop continuously drives each cluster toward its declared desired state — drift is not detected by audit, it is erased by controller. And Talos's immutability removes the most common drift source altogether: a node with no SSH and no shell cannot accumulate hand edits between reconciliations. Monthly fleet-wide updates then turn "production matches the manifest" from an aspiration into a scheduled, verifiable guarantee.
Note what this implies for the "scripts versus controllers" debate that still runs in platform teams. SNCF had scripts. It had a dedicated team. It had years of Kubernetes experience. What it didn't have was a machine that noticed drift and fixed it without being asked — and no amount of runbook discipline closed that gap. The rebuild's lesson is not "hire a bigger platform team"; it is "stop asking humans to do what a reconcile loop does for free."
Where SNCF's stack diverges from a Hetzner-plus-CAPH fleet
A self-hosted PaaS on rented bare metal is not a national railway with private datacenters, and the honest version of this post names where the analogy bends. Here is the divergence map:
| Dimension | SNCF | Hetzner-plus-CAPH fleet | Verdict |
|---|---|---|---|
| IaaS API | OpenStack (full private cloud: compute, network, storage APIs) | Hetzner Robot API + hcloud (servers, volumes, networks) | Borrow the pattern, swap the provider. CAPO and CAPH are both CAPI providers; the reconcile loop doesn't care. |
| Machine provisioning | Virtualized tenants on owned hardware | Bare-metal servers via rescue/install flow, or cloud VMs | Borrow for VMs; adapt for bare metal. CAPH drives HetznerBareMetalHost through rescue and install — slower than VM provisioning, same declarative contract. |
| OS image | Talos on OpenStack VMs | Talos on Hetzner (supported on bare metal and cloud) | Borrow directly. Immutability pays off at any scale. |
| Autoscaling | CAPI autoscaler against OpenStack capacity | CAPI autoscaler against Robot/hcloud capacity | Borrow. Same controllers, smaller capacity pool — headroom policy matters more when you can't burst infinitely. |
| Fleet update cadence | Monthly, enforced, zero-drift | Whatever you schedule | Borrow the discipline, automate it. SNCF has a platform team; a small operator needs the cadence in CI, not in headcount. |
| GitOps + supply chain | ArgoCD fleet-wide; ORAS for providers | Same tools, fewer clusters | Borrow ArgoCD early; adopt ORAS when provider sprawl hurts. One provider version pinned in git is fine until it isn't. |
The core deliverable of this comparison: the reconciliation architecture transfers completely; only the capacity assumptions change. A Hetzner fleet cannot pretend to have infinite headroom, and bare-metal provisioning will never hit 30 minutes the way VM provisioning does. But "declare desired state, let controllers reconcile drift away" holds at one-fiftieth the fleet size — arguably it matters more there, because there is no spare engineer to absorb the toil that drift creates.
What a small self-hosted PaaS should actually copy
Distilled to a checklist, ordered by payoff:
- Declare machine lifecycle in Cluster API. Stop provisioning nodes with scripts, Terraform one-shots, or install wizards. If a machine isn't a reconciled resource, it's a future snowflake.
- Run an immutable OS image. Talos's no-SSH model is the strongest version, but any image-based, atomically-updated node OS removes the largest drift vector — the human with root.
- Pick a fleet update cadence and enforce it. Monthly worked for a railway; the number matters less than the forcing function. Scheduled reconciliation-as-policy beats "upgrade when someone has time" every time.
- GitOps the providers too. Your CAPI provider versions, your CNI, your policy engine — if it isn't in git with a pinned version, your "declarative" fleet has an imperative basement.
- Replicate the managed-cloud features you miss instead of mourning them. SNCF open-sourced talos-cockpit to bring AKS-style auto-upgrades on-prem. The gap between owned hardware and managed cloud is a short list of controllers, not a law of nature.
SNCF's own roadmap points the same direction: deeper CNCF integration plus KCP and Crossplane to further simplify how infrastructure is consumed. The trajectory is consistently toward more declaration, less procedure.
The managed experience is a standard, not a location
SNCF set out to compete with AKS and EKS from inside its own datacenters, and — in Head of Container and Cloud Native Platforms Thomas Comtet's words — "the metrics speak for themselves." Thirty-minute provisioning. Monthly zero-drift updates. Node autoscaling on owned hardware. The CNCF case study's closing line is the thesis: the managed-cloud experience isn't a location, it's an operational standard achievable anywhere.
For a self-hosted PaaS, that standard has a concrete shape: CAPI-managed machine lifecycle, immutable nodes, GitOps all the way down, and a cadence that makes drift a scheduled non-event. You don't need a railway's budget to run it. You need the discipline to declare what you want and let the loop do the rest.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.



