Skip to main content

France's Railway Runs Kubernetes on Cluster API: What a National Railway's Declarative Rebuild Teaches a Two-Person Fleet Team

10 min readDora NodaDora Noda
Share
On this page

France's national railway went from commissioning one Kubernetes cluster a season to commissioning one every 18 days — on hardware it owns, in datacenters it controls, with a platform team that stopped doing machine lifecycle by hand. The numbers come from SNCF's CNCF case study, published March 25, 2026: cluster provisioning fell from one month to 30 minutes, fleet growth went from 14 clusters in four years to 10 clusters in six months, and every cluster in the fleet is now updated monthly under what the team calls a zero-drift guarantee.

The headline is not that a railway runs Kubernetes. It is how: Cluster API as the declarative heart of the platform, desired state in git, controllers reconciling toward it — the same "managed Kubernetes experience" SNCF previously only got from Azure AKS and AWS EKS, rebuilt on-premises. And the part that matters to everyone who is not a national railway: the standard SNCF converged on scales down. A two-person team running a handful of Hetzner boxes under the Cluster API Provider Hetzner (CAPH) can steal the three lessons that did the work — declarative machine lifecycle, upgrades as a cadence, GitOps as the team shape — while skipping the enterprise ceremony that only pays off at railway scale.

What SNCF actually built, in 200 words

After years of running Kubernetes successfully in public clouds, SNCF tried to reproduce that experience in its own datacenters and got what its engineers frankly call a "naïve" platform: manual processes, custom tooling, a month per cluster, snowflake clusters drifting apart, and no node autoscaling — the feature gap that stung most, because autoscaling was the thing that made the public-cloud experience feel managed. Rather than patch the foundation, the team rebuilt from scratch with a stack it describes as modular and interchangeable:

  • IaaS: OpenStack (Canonical) for compute, networking, and storage primitives.
  • OS: Talos Linux (SideroLabs) — API-driven, immutable, no SSH, so nobody can hand-edit a node back into a snowflake.
  • Lifecycle: Cluster API, with the OpenStack provider, treating clusters as Kubernetes resources managed by reconciliation loops.
  • Networking and policy: Cilium for eBPF networking and observability, Kyverno for policy-as-code across the fleet.
  • Delivery: the ArgoCD setup SNCF already ran for hundreds of public-cloud clusters, extended to the datacenter fleet, with ORAS managing Cluster API providers as OCI artifacts so every primitive flows through GitOps.

The single decision that unlocked the metrics was Cluster API. "Cluster API was a game-changer," said Senior Staff Engineer Yann Rotilio. "It gave us node autoscaling in our datacenters — something we thought was only possible with AKS or EKS." That sentence is the whole transfer argument in miniature: the capability gap between "cloud" and "own hardware" turned out to be a control-plane gap, not a location gap. Close it with the right controllers and the location stops mattering.

Lesson 1: machines are API objects, not tickets

The legacy SNCF platform provisioned machines the way most small teams still do: somebody ran a process — a ticket, a runbook, a script with a memory. Every machine was a small unique event, which is exactly why 14 clusters in four years was the ceiling and why each one drifted. The rebuild replaced the event with an object: a Cluster, MachineDeployment, and provider-specific machine template declared in YAML, with controllers converging reality toward the declaration. Provisioning collapsed from a month to 30 minutes because there is nothing left to do per machine — there is only state to declare.

For a two-person fleet team, this is the cheapest lesson to steal because it requires no scale at all. The small-fleet equivalent of SNCF's OpenStack provider is CAPH: a MachineDeployment plus a HetznerMachineTemplate replaces the Hetzner Cloud console clicks or the bespoke Terraform-plus-cloud-init snow pile. Node autoscaling — the capability SNCF's engineers assumed was AKS/EKS-only — arrives through the same Cluster Autoscaler integration on owned boxes. The mechanism does not care whether the provider underneath is OpenStack or Hetzner Cloud; that interchangeability is the point of the provider model, and it is why SNCF calls its stack non-toxic.

One honest caveat belongs here, because the reconciliation story has a bootstrap paradox: something has to host the controllers. SNCF runs dedicated management infrastructure; a small team does not need to. The established small-fleet pattern is a single cheap VM running the management cluster that hosts CAPI and CAPH and reconciles the workload clusters.

Real operators do exactly this: one production Hetzner setup runs its management cluster as a single-node Talos VM hosting CAPI plus CAPH, with workload nodes in a separate project. Protect that one VM (back up its etcd, keep its kubeconfig in two places) and the paradox is managed for the price of the smallest instance type on the menu.

Lesson 2: upgrades are a cadence, not an event

The metric that impressed me more than the 30-minute provisioning is the quiet one: every Kubernetes cluster in the SNCF fleet is now updated monthly. Not "we upgraded last quarter" — monthly, fleet-wide, as a standing posture, producing the zero-drift guarantee the case study claims.

Drift is what happens when upgrades are events: scheduled, feared, postponed, each postponement forking the fleet a little further until every cluster is a pet with a history. A monthly cadence inverts the dynamic. Each upgrade is small because the last one was recent; the fleet stays near-identical because nothing is ever allowed to get old.

Small teams fail at this for the opposite reason SNCF did — not too many clusters, but too much fear per cluster. When an upgrade means an evening of hand-draining nodes and praying the control plane comes back, you do it twice a year, which guarantees each one is big and scary, which guarantees you keep postponing.

Cluster API breaks the loop structurally: rolling machine replacement is the upgrade mechanism, so "upgrade" means changing a version string and letting the controllers rotate machines. And the cost of each rotation keeps falling upstream — Cluster API v1.12, released January 2026, added in-place updates and chained upgrades, with worker machines skipping intermediate minor versions wherever skew policy allows. SNCF's monthly posture was achieved without those features; a small team adopting CAPI today inherits them for free.

The transferable practice is therefore not "hire a release team" but "declare the cadence and let the controllers keep it." Pin the Kubernetes version in git next to the machine templates, bump it monthly, watch the rollout the way SNCF watches its fleet. The discipline is the same at 200 clusters and at three; only the blast radius differs, and a small blast radius is an argument for the cadence, not against it.

Lesson 3: the team shape is GitOps, not headcount

Notice what SNCF did not do: build a new datacenter team with a new toolchain. It took the ArgoCD implementation already managing hundreds of public-cloud clusters and extended it to the new on-premises fleet, creating one management layer across hybrid environments. Platform-team structure, in this story, is not an org chart — it is a repo layout. The same desired-state declarations, the same review process, the same audit trail, regardless of where the machines physically sit.

That maps down to small teams almost embarrassingly well. A two-person team cannot afford a platform organization, but it can afford a repository: cluster definitions, machine templates, and policy as reviewed pull requests, with ArgoCD or Flux syncing the management cluster. The review that a railway gets from a platform group, a tiny team gets from the PR diff — "this PR bumps three workload clusters to 1.33 and rotates two machine images" reads the same in both worlds. SNCF's ORAS-backed supply chain for provider artifacts is the enterprise version of a habit any small team can copy cheaply: version everything, including the tooling, and let git be the only writer that matters.

The overkill boundary: what not to copy

The second half of the promise is the boundary, because copying SNCF wholesale would be as foolish as ignoring it. Four pieces of the railway's platform earn their keep at railway scale and cost more than they return below it:

  • Management-cluster HA. SNCF's controllers are production infrastructure for national rail operations and are built accordingly. A two-person team with a handful of workload clusters should run the management cluster as simply as possible — one backed-up VM — and spend the saved complexity budget on the workload clusters that actually serve traffic. If the management VM dies, existing workloads keep running; you lose the ability to change things until it is restored, which is an incident, not an outage.
  • Fleet-wide policy-as-code posture. Kyverno enforcing compliance across a railway's fleet answers to regulators and auditors. A small team still benefits from a handful of guardrails (require resource limits, block :latest, enforce an image allowlist), but a compliance framework with exception workflows is ceremony without a regulator to perform it for.
  • The next-gen abstraction layer. SNCF is exploring KCP and Crossplane to simplify how infrastructure is consumed across the organization — "across the organization" doing heavy lifting in that sentence. Until your consumers are internal teams filing requests rather than your own co-founder opening PRs, an extra abstraction tier over CAPI is a second platform to operate in exchange for self-service nobody asked for.
  • The OCI-artifact supply chain. Managing providers as versioned OCI artifacts through ORAS is the right answer when dozens of people touch the platform. At tiny scale, the equivalent discipline is pinning provider versions in the bootstrap config and bumping them deliberately — same principle, one line instead of a pipeline.

The heuristic underneath all four: copy the control loop, not the org chart. Declarative state, reconciliation, git as the writer, monthly cadence — those are properties of the machine system and they pay off at any size. Everything SNCF built to coordinate humans at scale can wait until you have humans at scale.

Steal the standard, skip the ceremony

SNCF set out, in Head of Container and Cloud Native Platforms Thomas Comtet's words, to build "a platform that could compete with AKS or EKS, but running in our own datacenters" — and the metrics say it worked. But the deeper result is the one the case study's closing line states outright: the managed-cloud experience is not a location, it is an operational standard achievable anywhere. Anywhere includes a Hetzner project with a few boxes, a single management VM, and a git repo.

The three transfers, compressed: declare machines instead of provisioning them, upgrade monthly instead of eventually, and let the repo be the platform team. The three skips, compressed: HA management, compliance-grade policy, and abstraction layers you have nobody to abstract for. A railway proved the standard; your job is only to run it at your size.

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.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex