Why I Moved My Game Servers from Homelab to Hetzner

Summary

Running game servers at home worked — until it didn’t.

What started as a learning project slowly became a real workload with real users. Friends were connecting daily, sessions got longer, and expectations grew. At the same time, my home network was being used normally: streaming, downloads, updates, and general traffic.

Eventually it became clear:

Kubernetes was stable. My storage was stable. My home network was not.


The Real Problem: Home Network Limitations

Game servers are extremely sensitive to:

  • Latency spikes
  • Packet loss
  • Bandwidth contention
  • Router CPU load

Even if the cluster is perfectly configured, none of that matters when the uplink is saturated or the router starts struggling under load.

I noticed jitter when multiple people were connected and someone at home started streaming or downloading. That’s not acceptable for games.

At that point I had two options:

  1. Upgrade my entire home network
  2. Separate public workloads from my private environment

I chose separation.


Why Hetzner - and Why I Kept It Simple

I moved the game servers to a small Hetzner setup:

  • 1 master
  • 1 worker
  • No HA
  • No distributed storage

The Hetzner cluster lives in its own Git repository path and is managed as a completely separate cluster in Flux. It reconciles independently from my home cluster.

That separation is important:

  • Different failure domain
  • Different network
  • Different operational goal

This cluster has exactly one purpose:

Run game servers reliably.

Nothing else.


The Migration Strategy

I didn’t want dirty copies or inconsistent state. So I migrated properly.

1. Scale to Zero

All game server deployments were scaled to:

replicas: 0

This ensured:

  • No open file handles
  • No active writes
  • Clean shutdown

2. Rsync via Temporary Pod

I created a temporary pod that:

  • Mounted the Longhorn volume
  • Used rsync
  • Synced data directly to the Hetzner worker

This allowed controlled data transfer without copying live volumes.

3. Storage Model Change

At home I was using Longhorn.

In Hetzner I switched to simple local storage.

Why?

Because the Hetzner cluster has only one worker.

There is no benefit in running distributed storage on a single-node workload.

For game servers, local disk means:

  • Lower IO overhead
  • No replication latency
  • Fewer components
  • Better performance

If the node fails, I restore from backup.

Simple beats complex when HA is not required.


Why I Removed Longhorn in Hetzner

Longhorn is excellent for:

  • Multi-node clusters
  • Volume replication
  • Real HA setups

But in this case:

  • One worker only
  • No failover node
  • Performance-sensitive workloads

Running Longhorn would only add overhead without providing real benefits.

So I removed it.


Flux and Cluster Separation

The Hetzner cluster is defined as a separate cluster in my GitOps structure.

That means:

  • Separate clusters/hetzner/ directory
  • Independent reconciliation
  • Clear boundaries between environments

Home cluster:

  • Learning
  • Internal services
  • Monitoring
  • Wiki
  • Automation

Hetzner cluster:

  • Public-facing game servers
  • Stable bandwidth
  • Isolated blast radius

This separation improved stability more than any tuning inside Kubernetes ever could.


What I Learned

Moving workloads is good practice.

It forces you to:

  • Think about state
  • Plan migrations
  • Test backups
  • Define clear responsibilities per cluster

The biggest improvement wasn’t performance.

It was clarity.

The home cluster is now for experimentation. The Hetzner cluster is for reliability.

And mixing those two goals was the original mistake.