Skip to main content

The Machine You Can't SSH Into: Why Talos Linux Is the Right Node OS for a Hetzner-Backed PaaS

8 min readDora NodaDora Noda
Share
On this page

Imagine a Linux server with no SSH daemon, no shell, and no package manager. You cannot log into it — not because you lost the key, but because there is deliberately nowhere to log into. Every interaction goes through a mutually authenticated gRPC API.

That machine is a Talos Linux node, built by Sidero Labs exclusively to run Kubernetes. For a self-hosted platform running tenant workloads on rented Hetzner hardware, "you can't get a shell" is not a limitation. It is the whole point.

This post answers one concrete question: what does replacing conventional Ubuntu-plus-kubeadm nodes with Talos Linux actually remove from your attack surface and upgrade toil, what does it cost you in day-to-day debuggability, and does it fit a Cluster API (CAPH) provisioning path on Hetzner? The short version is in the table below; the rest of the post substantiates every row.

Ubuntu + kubeadm nodeTalos Linux node
Remote accessSSH daemon listening, keys to rotate and auditNo SSH, no shell; API-only via talosctl over mTLS gRPC
Software managementapt/yum, drift accumulates per nodeImmutable image; no package manager at all
OS upgrade modelIn-place package upgrades, one snowflake at a timeAtomic A/B image upgrade, automatic revert on failure
Privileged binaries on hostFull distro userland (su, sudo, setuid tools)No su, no interactive users, zero setuid binaries
CVE triageScan a full distro, patch packages, reboot and hopeMinimal surface plus published OpenVEX statements; patch = new image
Node debuggingssh + journalctl + whatever you apt installtalosctl dmesg / logs / pcap; extras baked via Image Factory
Kubernetes installkubeadm + cloud-init userdataDeclarative machine config applied over the API

A 732-byte root exploit that bounces off an OS with no Python

In April 2026, the Copy Fail exploit (CVE-2026-31431) made a striking claim: the same 732-byte Python script roots every Linux distribution shipped since 2017. It corrupts a shared page cache so an unprivileged user can hijack a setuid binary — the classic su-style privilege escalation, weaponized against the page cache every distro shares.

Sidero Labs' field CTO took the claim personally and published a point-by-point teardown of Copy Fail against Talos Linux. The result is the clearest real-world demonstration of what "minimal" buys you:

  • No Python interpreter, so the headline 732-byte script cannot run on the host at all.
  • No su, no interactive users, and zero setuid binaries on the host, so the exploit's core primitive — corrupting the page cache under a setuid target — has nothing to aim at.
  • User workloads are barred from control-plane nodes by default, containing the Kubernetes proof-of-concept variant, which needs a compromised privileged workload (kube-proxy, a CSI driver, a device plugin) co-located with node secrets.

Note what Sidero did not claim: immunity. Copy Fail affected the kernel shipping in Talos, and the fix was a patched kernel in Talos 1.12.7 and 1.13.0 (released April 27, 2026) — which users were told to upgrade to promptly. The honest lesson is defense in depth: the vulnerability was real, the patch was still required, but every layer the exploit needed beyond the kernel bug simply was not there. A conventional node hands an attacker Python, su, and a page cache full of setuid targets; a Talos node hands them a kernel bug with no userspace to leverage it through.

That same minimalism compounds across every CVE, not just the famous ones. Sidero publishes OpenVEX statements (in the siderolabs/talos-vex repository) so scanners like Grype can mechanically distinguish "vulnerability present in some bundled component" from "actually exploitable on Talos." Triage becomes reading a signed statement instead of auditing a full distro userland node by node — a meaningful difference when the alternative is an emergency patch window across your entire fleet.

Upgrades are images, not mutations

The second half of the security story is operational: Talos nodes are upgraded as atomic images, never mutated in place. Each upgrade cordons the node in Kubernetes, reboots it into the new image, and — thanks to an A/B boot scheme — reverts to the previous image automatically if the new one fails to come up. An upgrade either lands cleanly or rolls back; there is no third state where half the packages are new and the node is a unique snowflake.

Two properties of this model matter disproportionately on a small self-hosted fleet:

  • OS upgrades and Kubernetes upgrades are separate operations. You bump the machine image and the Kubernetes version independently, so a kernel patch (like the Copy Fail fix) does not drag a control-plane version migration along with it, and vice versa.
  • Fleet uniformity is structural, not aspirational. With no package manager and a read-only root filesystem, nodes cannot drift from each other between upgrades. The "works on node A, broken on node B" class of debugging session mostly disappears — which, for a team without a dedicated platform group, is a larger time saving than any single feature.
  • Rollouts stay boring at fleet scale. Upgrades are initiated per node through the API (talosctl upgrade, or Sidero's Omni for fleet-wide orchestration), each node cordoned and health-checked in turn. Talos 1.13 even exposes install and upgrade operations through a LifecycleService gRPC API, so Cluster API providers and controllers can drive node lifecycle programmatically instead of shelling out to a CLI.

Sidero's own reference architectures put it bluntly: upgrades either succeed and boot the new release or fail and revert, never leaving a broken state. Compare that with the standard small-fleet ritual of apt upgrade across N machines followed by discovering that two of them held back a kernel package.

The honest costs: debugging without a shell, and the CAPH fit matrix

None of this is free. The no-shell design genuinely changes how you debug a sick node. There is no ssh + journalctl + apt install tcpdump. There is talosctl dmesg, talosctl logs, talosctl pcap — and if you need a diagnostic tool that is not in the image, you bake it in as a system extension through the Image Factory and reboot into the new image. Operators who live in a shell feel the loss viscerally the first time a node misbehaves. There is also a networking gotcha worth knowing up front: the Talos API speaks gRPC with mutual TLS, so it can sit behind a TCP load balancer but not an HTTPS proxy — plan control-plane endpoint access accordingly.

The second cost is provisioning-path specific, and it is the one the original topic draft glossed over, so here it is plainly:

  • Hetzner Cloud + CAPH: works. Build a Talos snapshot image, reference it from your HetznerCluster machine deployments, and drive bootstrap with the Talos bootstrap and control-plane providers (CABPT/CACPPT) instead of kubeadm. Talos has natively supported the Hetzner platform — including VIP support — for years, and community flavors document exactly this shape.
  • Hetzner Robot bare metal + stock CAPH: does not work. CAPH's bare-metal flow provisions via installimage over SSH followed by cloud-init running kubeadm — and Talos has no SSH and no cloud-init. The two are fundamentally incompatible; CAPH maintainers declined Talos bare-metal support, which is why Talos-native alternatives (a dedicated Robot provisioner, Sidero Metal, or rescue-system installs driven by projects like talos-manager) exist.

So the precise recommendation is narrower — and more useful — than "run Talos everywhere": default to Talos for every node your declarative provisioning path can install it on (Hetzner Cloud fleets, Equinix-style Talos-supported bare metal, Sidero Metal shops), and treat Robot bare metal as the exception that needs its own installer, not as a reason to keep the whole fleet on Ubuntu.

Verdict: when the machine you can't log into wins

Choose Talos as the default node OS when your tenants are untrusted or semi-trusted, your ops team is small enough that per-node snowflakes are an existential cost, and your provisioning path speaks its API — which, on Hetzner Cloud under CAPH, it does. Keep Ubuntu (or your hardened image of choice) where you genuinely need host-level customization per node, where compliance paperwork names packages rather than outcomes, or where your only install path is an SSH-and-cloud-init pipeline you cannot replace yet.

The deeper point generalizes beyond one distro. On infrastructure you own, every shell, package manager, and setuid binary on a node is standing attack surface and future drift that you pay to maintain instead of a cloud provider. A hyperscaler's managed node pool absorbs that maintenance invisibly; on your own Hetzner machines, there is nobody to absorb it but you.

An OS that deletes all three is not being difficult. It is doing your platform team's job for them — one reboot-into-an-image at a time.

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