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, nichtav-<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:
- Cloudflare Edge — DDoS-Schutz, TLS
- Cloudflare Access — Email-Allowlist, OTP, optional MFA, optional Geofencing
- Cloudflare Tunnel — kein offener Web-Port am Server (Server hat NUR SSH offen)
- Hetzner Firewall — nur SSH 22 + ICMP von 0.0.0.0/0 (key-only Auth)
- SSH-Hardening — kein Password, kein root-Password-Login, fail2ban, unattended-upgrades
- App-internes Auth — Login auf Tool-Ebene (Stirling:
SECURITY_ENABLELOGIN=true)
Server-Spec
| Feld | Wert |
|---|---|
| Type | cx23 (2 vCPU x86, 4 GB RAM, 40 GB Disk) |
| Standort | nbg1 (DE, DSGVO) |
| Image | ubuntu-24.04 |
| Project | av-tools (interne Tools — pro Kunde nicht dieses Project!) |
Wichtig: Hetzner hat 2026-05 die alte Generation (cx11/cx21/cx22) abgekuendigt. Neue Standard-Mappings:
| Frueher | Jetzt | Aenderung |
|---|---|---|
| cx11 / cx21 | cpx12 | Intel, ~3,79 EUR, gleicher Footprint |
| cx22 | cx23 | Intel, ~5 EUR, gleicher Footprint |
| (neu) | cax11 | ARM, ~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)
- System —
apt update && apt upgrade,ufwinstall (nicht aktiviert, Hetzner-Firewall reicht),fail2ban,unattended-upgrades - SSH-Hardening —
PasswordAuthentication no,PermitRootLogin prohibit-password,KbdInteractiveAuthentication no, sshd reload - Docker — von
download.docker.com(nicht Ubuntu-Variante — neuere Versionen),docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin - cloudflared — aus
pkg.cloudflare.com-Repo, NICHT als Container (Service-Install via apt-Package, Tunnel-Token oder Cert-basiert) - App — Docker-Compose-File unter
/opt/<app>/docker-compose.yml, Container auf127.0.0.1:<port>gebunden (NICHT 0.0.0.0) - 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 CLICloudflared-cert.pem-Scope (wichtig zu wissen):
| Aktion | cert.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
| Posten | EUR/Monat |
|---|---|
| Cloud Server cx23 | ~5,00 |
| Public IPv4 | 0,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.
Related
- av-tools — erste konkrete Instanz (Stirling-PDF)
- mcp-hosting-fargate-tunnel — AWS-Variante fuer Multi-Tenant
- zugriffsmodell — generelle Zugriffs-Doktrin
- hosting-industriekunden — Hetzner-Pattern fuer Kunden-Workloads (anderer Use-Case)