Pattern — Interne Tools auf Hetzner mit Cloudflare-Tunnel

Wenn ein internes Web-Tool (PDF-Tool, Password-Manager, Status-Page, Dev-Wiki, …) selfhosted werden soll und nicht in das AWS-Pattern (Fargate + ECS-Tunnel) passt, weil:

  • Kein Multi-Tenant-Bedarf
  • Einzelne kleine Last (≤ 4 GB RAM, ein Service)
  • Kein-Kunden-Bezug (also av-tools-Project, nicht av-<kunde>)
  • AWS-Fargate-24/7 ist teurer als Hetzner-VM

Hetzner-Pattern nehmen, nicht AWS. Erstes umgesetztes Beispiel: Stirling-PDF (siehe av-tools).

Architektur (Defense-in-Depth)

Internet
   │ HTTPS (TLS termination by Cloudflare)
   ▼
Cloudflare Edge
   │ Cloudflare Access (Email-OTP, Allowlist)
   ▼
Cloudflare Tunnel (cloudflared sidecar auf Server, outbound-only)
   │ HTTP (intern)
   ▼
Hetzner CX23 (Nuernberg)
   │ Caddy oder direkt (Stirling braucht keinen Caddy)
   ▼
Docker-Container, gebunden auf 127.0.0.1:<port>

Sicherheitsschichten:

  1. Cloudflare Edge — DDoS-Schutz, TLS
  2. Cloudflare Access — Email-Allowlist, OTP, optional MFA, optional Geofencing
  3. Cloudflare Tunnel — kein offener Web-Port am Server (Server hat NUR SSH offen)
  4. Hetzner Firewall — nur SSH 22 + ICMP von 0.0.0.0/0 (key-only Auth)
  5. SSH-Hardening — kein Password, kein root-Password-Login, fail2ban, unattended-upgrades
  6. App-internes Auth — Login auf Tool-Ebene (Stirling: SECURITY_ENABLELOGIN=true)

Server-Spec

FeldWert
Typecx23 (2 vCPU x86, 4 GB RAM, 40 GB Disk)
Standortnbg1 (DE, DSGVO)
Imageubuntu-24.04
Projectav-tools (interne Tools — pro Kunde nicht dieses Project!)

Wichtig: Hetzner hat 2026-05 die alte Generation (cx11/cx21/cx22) abgekuendigt. Neue Standard-Mappings:

FrueherJetztAenderung
cx11 / cx21cpx12Intel, ~3,79 EUR, gleicher Footprint
cx22cx23Intel, ~5 EUR, gleicher Footprint
(neu)cax11ARM, ~3,79 EUR — wenn Tool Multi-Arch-Image hat

ARM lohnt sich preislich nur wenn das Tool sauberes Multi-Arch-Docker-Image liefert. Stirling-PDF: ja. Bei Unklarheit: x86 (cx23) sicherer.

Stack-Komponenten (Bootstrap-Reihenfolge)

  1. Systemapt update && apt upgrade, ufw install (nicht aktiviert, Hetzner-Firewall reicht), fail2ban, unattended-upgrades
  2. SSH-HardeningPasswordAuthentication no, PermitRootLogin prohibit-password, KbdInteractiveAuthentication no, sshd reload
  3. Docker — von download.docker.com (nicht Ubuntu-Variante — neuere Versionen), docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  4. cloudflared — aus pkg.cloudflare.com-Repo, NICHT als Container (Service-Install via apt-Package, Tunnel-Token oder Cert-basiert)
  5. App — Docker-Compose-File unter /opt/<app>/docker-compose.yml, Container auf 127.0.0.1:<port> gebunden (NICHT 0.0.0.0)
  6. Tunnel-Konfig/etc/cloudflared/config.yml + Credentials-JSON, systemd-Service

Idempotentes Bootstrap-Script: siehe /root/bootstrap.sh in jeder av-tools-Instanz.

Cloudflare-Setup-Reihenfolge

# 1. Tunnel via lokaler cloudflared CLI anlegen (auf Marvins Mac, nutzt ~/.cloudflared/cert.pem)
cloudflared tunnel create <name>
# 2. Credentials-JSON auf Server kopieren
scp ~/.cloudflared/<uuid>.json <server>:/etc/cloudflared/<uuid>.json
# 3. config.yml auf Server schreiben (tunnel + credentials-file + ingress)
# 4. Service installieren auf Server
ssh <server> 'cloudflared service install && systemctl enable --now cloudflared'
# 5. DNS-Route anlegen (auch via lokaler CLI, schreibt CNAME in CF)
cloudflared tunnel route dns <name> <subdomain>.agenticventures.de
# 6. Cloudflare Access App + Policy → braucht API-Token, nicht ueber CLI

Cloudflared-cert.pem-Scope (wichtig zu wissen):

Aktioncert.pem reicht?
Tunnel create/list/delete
Tunnel credentials erzeugen
DNS-Route (CNAME → Tunnel-UUID) anlegen
Cloudflare Access App anlegen✗ (braucht API-Token)
Beliebige DNS-Records aendern
Zone-Settings

→ Wenn Access-Setup voll automatisch laufen soll: API-Token mit Access: Apps and Policies: Edit + Access: Organizations, Identity Providers and Groups: Edit in op (1Password) als cloudflare-api-token-access. Aktuell noch nicht angelegt — Access wird im Dashboard geklickt.

Kosten

PostenEUR/Monat
Cloud Server cx23~5,00
Public IPv40,60
Traffic (im 20 TB Free-Quota)0
Cloudflare Tunnel + Access (bis 50 User Free)0
Summe pro Tool~5,60

Skalierung: 5 interne Tools = 5 Server = ~28 EUR/Monat. Bei 5+ Tools lohnt es sich, einen shared CX23 mit Caddy als Multi-Vhost + mehreren Docker-Compose-Stacks zu fahren — dann ist die Marginal-Kosten 0 fuer Tool 2..N.

Wann nicht dieses Pattern

  • Multi-Tenant / pro Kunde getrennt → AWS Fargate + Sub-Account-Pattern (siehe mcp-hosting-fargate-tunnel)
  • Heavy Compute / GPU → Hetzner ist nicht der richtige Ort
  • Industriekunde mit strenger DSGVO + On-Prem-Wunsch → eigenes Hetzner-Project pro Kunde, nicht av-tools (siehe hosting-industriekunden)

Naechste Kandidaten fuer dieses Pattern

  • Vaultwarden (Selfhosted Bitwarden) — wenn Marvin 1Password mal ergaenzen oder migrieren will
  • Uptime-Kuma (Status-Page fuer alle eigenen MCPs + AWS-Services)
  • Linkwarden / Wallabag (Read-Later-Pattern fuer Vault-Aufnahme)
  • n8n (Workflow-Automatisierung, falls je relevant)

Wenn einer dieser Kandidaten kommt: dieser Pattern-File ist die Vorlage, das av-tools.md ist das konkrete Beispiel.