Running a highly-available k3s cluster on Hetzner
Every client system we deliver runs on infrastructure we own and operate: a highly-available k3s cluster on Hetzner. This post explains why we made that choice and the decisions that keep it reliable.
Why self-managed, not managed Kubernetes
Managed Kubernetes is excellent — and for a company whose margin depends on a predictable, low monthly infrastructure cost per client, it's also expensive at the scale we operate. Running k3s ourselves on Hetzner's CCX line gives us:
- Predictable cost per node, with no per-cluster control-plane fee.
- Full control over networking, ingress and storage.
- A stack we understand end to end, so we can debug it at 2am.
The trade-off is that HA is our responsibility. Here's how we handle it.
The HA choices that matter
We run a multi-node cluster split between infrastructure and worker nodes, with the control plane replicated. A few decisions carry most of the reliability:
Anti-affinity plus PodDisruptionBudgets means a node drain never takes the last replica of a service down. It sounds obvious; it's the thing people skip.
# Every client-facing Deployment gets this shape.
spec:
replicas: 2
template:
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
app: my-service- Databases run with replication and automated failover, not a single pod.
- Traefik terminates TLS via cert-manager and Let's Encrypt, with the certificate lifecycle fully automated.
- Monitoring (Prometheus + Loki) is not optional — if we can't see it, we can't operate it, and operating it is the whole point.
What this means for clients
When we tell a client "we host, monitor and maintain your system," this is what sits underneath that sentence. The same cluster that runs our own products runs your integration or your custom system — with the same discipline.