Warum ich alles auf Hetzner betreibe
- infrastructure
- devops
- vps
- self-hosting
AWS und GCP sind in Ordnung, bis man die Rechnung sieht. Hier ist, warum ich meine Infrastruktur zu Hetzner VPS verlagert habe und nie zurückgeblickt habe.
Ich habe drei Jahre lang DigitalOcean genutzt. Dann eine Weile AWS. Das Muster war immer dasselbe: klein anfangen, Dienste hinzufügen, zusehen, wie die Rechnung über das hinausgeht, was vernünftige Menschen für ein paar Web-Apps und eine Datenbank zahlen sollten. Letztes Jahr habe ich alles zu Hetzner verschoben. Bereut habe ich das kein einziges Mal.
Der Preisunterschied ist kein Rundungsfehler
Ein Hetzner CPX31 bietet 4 vCPUs, 8 GB RAM, 160 GB NVMe und 20 TB ausgehenden Traffic für etwa 15 EUR im Monat. Das nächste AWS-EC2-Äquivalent (t3.large, vergleichbarer Speicher, verwaltetes EBS, Datentransfer) liegt bei echtem Volumen deutlich über 80 EUR, wenn man Egress einrechnet. Das ist kein 10-prozentiger Unterschied, den man abtun kann. Es ist das Fünffache.
Hetzner reserviert die vCPUs exklusiv. Es sind keine überbuchten Soft-Cores, die verschwinden, wenn ein lauter Nachbar hochfährt. Ich habe dauerhaft hohe CPU-Lasten auf CPX-Instanzen gefahren, und die Zahlen halten stand. Man bekommt, was das Datenblatt verspricht.

Egress: die versteckte Steuer, die man nicht mehr zahlt
Jeder große Cloud-Anbieter berechnet Gebühren für ausgehende Daten. AWS ist berüchtigt teuer mit $0,09 pro GB nach dem ersten GB. GCP ist ähnlich. Azure etwas besser, aber bei großem Volumen immer noch schmerzhaft. Hetzner schließt 20 TB ausgehenden Traffic in jeden VPS-Plan ein. Zwanzig Terabyte. Bei den meisten Projekten wird man diese Grenze nie erreichen.
Ich liefere Video-Assets, große PDF-Downloads und API-Antworten, die umfangreich sein können. Bei AWS haben allein diese Traffic-Kosten monatlich $30 bis $60 gekostet. Bei Hetzner sind sie null, weil ich innerhalb des inkludierten Kontingents bleibe. Eine vorgeschaltete CDN (Cloudflare im Free-Tier reicht für die meisten Traffic-Muster) bedeutet, dass der Origin ohnehin kaum rohen Egress sieht.
Egress-Gebühren sind keine Betriebskosten. Sie sind ein Bindungsmechanismus. In dem Moment, in dem man diesen Rahmen akzeptiert, wird der Anbieterwechsel leichter.
NVMe-Storage und Snapshots, die wirklich funktionieren
Der NVMe-gestützte lokale Speicher auf CPX- und CCX-Instanzen ist schnell. Nicht "Cloud-schnell", wo das Datenblatt SSD sagt, die Latenz aber eine andere Geschichte erzählt. Wirklich schnell. Sequentielle Lesegeschwindigkeiten deutlich über 1 GB/s in der Praxis. Für eine Postgres-Datenbank oder einen Next.js-Build-Cache ist dieser Unterschied spürbar.
Snapshots kosten 20 % des Serverpreises pro Monat und sind einen API-Aufruf entfernt. Ich erstelle vor jedem Deployment einen Snapshot. Wenn etwas katastrophal schiefgeht, kann ich in unter zwei Minuten über die Cloud Console oder die REST API zurückrollen. Die API ist sauber und gut dokumentiert, was wichtig ist, wenn man diese Dinge automatisieren möchte.
- Snapshots: per Klick oder API, abgerechnet mit 20 % der monatlichen Serverrate
- Floating IPs: sofort zwischen Servern neu zuweisen, keine DNS-TTL-Probleme
- Private Netzwerke: flaches L2 zwischen den eigenen Servern in einem Rechenzentrum, kostenlos
- Volumes: persistenten Block-Storage anhängen/trennen während der Server läuft
- Firewalls: Stateful-Regeln über dasselbe Panel oder die API verwalten
Der ehrliche Kompromiss: man ist selbst für die Verfügbarkeit verantwortlich
Hetzner abstrahiert den Betrieb nicht so wie AWS. Es gibt kein verwaltetes RDS, kein ECS, kein Lambda. Man bekommt eine Linux-Box und ein sauberes Netzwerk. Was man damit macht, ist das eigene Problem. Das ist je nach Hintergrund entweder beängstigend oder befreiend.
Ich habe das Verfügbarkeitsproblem in drei Schichten gelöst. Erstens Ploi für Server-Provisioning und Deployments. Ploi kommuniziert mit der Hetzner API, startet Server, installiert Nginx, PHP- oder Node-Runtimes, richtet SSL ein und handhabt Zero-Downtime-Deploys über Git-Hooks. Die Oberfläche ist einfach genug, dass ich sie tatsächlich nutze. Zweitens automatisierte Datenbank-Backups in Hetzner Object Storage (S3-kompatibel) alle sechs Stunden. Drittens Cloudflare davor für DDoS-Schutz, Caching und einen globalen Edge, damit sich der Single-Region-VPS für Nutzer in Tokio nicht langsam anfühlt.
Provisioning und Hardening in 60 Zeilen
Ploi übernimmt die Runtime-Einrichtung, aber ich führe auf jedem neuen Server noch ein Hardening-Skript aus, bevor ich ihn übergebe. Hier ist das Herzstück davon. Nichts Cleveres, nur die Grundlagen, die zählen: Passwort-Authentifizierung deaktivieren, einen Nicht-Root-Benutzer einrichten, UFW konfigurieren und einige Kernel-Parameter anpassen.
#!/usr/bin/env bash
# harden.sh — run once as root immediately after provisioning
set -euo pipefail
NEW_USER="deploy"
SSH_PUBKEY="ssh-ed25519 AAAAC3Nza... your-key-here"
# Create deploy user with sudo, no password prompt for deploys
useradd -m -s /bin/bash "$NEW_USER"
usermod -aG sudo "$NEW_USER"
echo "$NEW_USER ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/"$NEW_USER"
# Inject SSH key
mkdir -p /home/"$NEW_USER"/.ssh
echo "$SSH_PUBKEY" > /home/"$NEW_USER"/.ssh/authorized_keys
chmod 700 /home/"$NEW_USER"/.ssh
chmod 600 /home/"$NEW_USER"/.ssh/authorized_keys
chown -R "$NEW_USER":"$NEW_USER" /home/"$NEW_USER"/.ssh
# Harden SSH
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
systemctl restart sshd
# UFW: allow SSH + HTTP + HTTPS only
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
# Kernel hardening: disable IP forwarding unless you need it, enable SYN cookies
cat >> /etc/sysctl.d/99-harden.conf <<EOF
net.ipv4.ip_forward = 0
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.rp_filter = 1
net.core.somaxconn = 65535
EOF
sysctl --system
# Unattended security upgrades
apt-get install -y unattended-upgrades
dpkg-reconfigure -f noninteractive unattended-upgrades
echo "Hardening complete. SSH as $NEW_USER."Danach übernimmt Ploi. Man zeigt es auf die Server-IP, wählt den Runtime-Stack, gibt die Repo-URL und einen Deploy-Key ein, und das erste Deployment ist in etwa drei Minuten abgeschlossen. Nachfolgende Deployments werden durch einen Git-Push ausgelöst und dauern bei den meisten Node-Apps unter 30 Sekunden.

Das mit einem KI-Coding-Agenten betreiben
Ich nutze Claude Code als Coding-Agenten für die meisten meiner Projekte. Eine Sache, die sehr wichtig ist, wenn ein Agent Infrastruktur verwaltet, ist Langzeitgedächtnis. Der Agent erinnert sich nicht an die Server-IPs, die UFW-Regeln oder den Grund, warum man Port 9000 für den Metriken-Endpunkt gewählt hat. Jede Sitzung fängt er von vorne an, sofern man ihm keinen persistenten Kontext gibt.
Meine Lösung ist eine eingecheckte HETZNER.md-Datei im Projektstamm. Der Agent liest diese zu Beginn jeder infrastrukturbezogenen Sitzung und hat das vollständige Bild: Server-Spezifikationen, IP-Adressen, deployete Dienste, Backup-Zeitpläne, bekannte Fallstricke. Wenn sich etwas ändert, weise ich den Agenten an, die Datei zu aktualisieren. Sie wird zur Quelle der Wahrheit, die Kontext-Window-Resets übersteht.
Die Datei ist kurz und meinungsstark. Keine Prosa, nur Fakten, auf die der Agent handeln kann. Hier ist ein echtes Beispiel, wie das aussieht:
# Hetzner Infrastructure Memory
## Servers
| Name | IP | Type | Region | OS |
|-------------|---------------|--------|--------|--------------|
| web-prod | 65.21.xxx.xxx | CPX31 | HEL1 | Ubuntu 24.04 |
| db-prod | 65.21.yyy.yyy | CPX21 | HEL1 | Ubuntu 24.04 |
## Deploy user: `deploy` (no-password sudo)
## SSH key: ed25519, stored in 1Password under "Hetzner deploy key"
## Services on web-prod
- Nginx 1.26, proxy to Node :3000
- PM2 manages the Node process (app name: `jumpinotech`)
- SSL via Let's Encrypt, auto-renew via certbot systemd timer
- Cloudflare proxied: DNS A record points to 65.21.xxx.xxx
## Database (db-prod)
- Postgres 16, port 5432, bound to private network only (10.0.0.x/24)
- Backups: pg_dump every 6h via cron, upload to Hetzner Object Storage bucket `db-backups-prod`
- Restore: `hetzner-restore.sh` in /home/deploy/scripts/
## Firewall (UFW on both servers)
- 22, 80, 443 open on web-prod
- 22, 5432 open on db-prod (5432 restricted to private network only)
## Snapshots
- Taken manually before each major deploy via Hetzner Cloud Console
- Naming convention: `web-prod-YYYY-MM-DD-pre-deploy`
## Known Gotchas
- Private network interface is eth1, not eth0 — use this for DB connection string
- Ploi deploy hook URL changes if you delete and recreate the site; update GitHub webhook
- Object Storage endpoint: `https://fsn1.your-objectstorage.com`
Mit dieser Datei im Repo kann ich eine neue Sitzung öffnen, sagen "prüf das Backup-Skript auf db-prod" und der Agent kennt die IP, den Benutzer, den Skriptpfad und den Storage-Endpunkt, ohne dass ich irgendetwas wiederholen muss. Er liest das Markdown, bildet einen Plan und führt SSH-Befehle oder API-Aufrufe mit dem richtigen Kontext aus. Keine halluzinierten IPs, kein Raten bei Dienstnamen.
Wann Hetzner nicht die richtige Antwort ist
Hetzner-Rechenzentren befinden sich in Deutschland, Finnland und den USA (Ashburn und Hillsboro). Wenn die Compliance-Anforderungen eine Datenresidenz in APAC erfordern oder man eine einstellige Millisekunden-Latenz für Nutzer in Sydney benötigt, kommt ein hybrides Setup oder ein anderer Anbieter in Frage. Für europäische und nordamerikanische Workloads ist es in Ordnung.
Verwaltete Dienste fehlen ebenfalls. Wenn man einen verwalteten Redis-Cluster, verwaltetes Kafka oder eine serverlose Funktions-Runtime braucht, wird man diese von anderswo einbinden oder selbst betreiben. Für mich ist das kein Ausschlusskriterium. Für ein Team ohne Ops-Erfahrung könnte es eines sein. Ploi deckt speziell für Web-Apps viel der Lücke ab, ersetzt aber keine vollständige PaaS.
Für alles andere sind die wirtschaftlichen Argumente schwer zu widerlegen. Dedizierte Kerne, echter Arbeitsspeicher, NVMe-Storage, großzügiger Egress, saubere API und ein europäisches Unternehmen, das Gebühren nicht im Kleingedruckten versteckt. Wenn man Web-Apps, Nebenprojekte oder interne Tools betreibt und aus Gewohnheit noch auf einem Hyperscaler ist, zieht man die letzten drei Rechnungen heraus und macht die Rechnung. Die Zahlen werden die Entscheidung von selbst treffen.