kdaniel
0001 accepted

Deployment: Hetzner VPS + Cloudflare over AWS Amplify

  • deployment
  • cloudflare
  • hetzner
  • infrastructure

Context

The engine builds fully static HTML into dist/<domain>/ via bun run build. In production, the Hono server serves static files using Host-header routing — one process, all domains.

We evaluated AWS Amplify Hosting. Key findings:

Current active domains:

Domain Type
kda.zone personal (apex + www)
wardleymaps.com project (apex + www)
lego-submarine.kda.zone project (subdomain of kda.zone)

Decision

Deploy to a Hetzner VPS with Cloudflare (free tier) as the TLS/CDN proxy layer.

Architecture

Cloudflare (HTTPS, CDN, DDoS) → Hetzner VPS (HTTP :80) → Hono server → dist/

Why not Amplify

Concern Amplify (3 apps) VPS + Cloudflare
Architecture match Split across 3 apps One process, all domains
sites.yaml drives everything No — domains in AWS console Yes — including DNS sync
Sunset redirects Manual Amplify config Engine handles it
Adding a domain New Amplify app + console Edit sites.yaml, run sync
Server ops Zero ~15 min/month
Cost $0-1/month ~$6/month

The $4/month premium buys architectural coherence and operational simplicity for the common path (content and domain changes).

Setup

Server (one-time):

  1. Hetzner CX22 VPS, Ubuntu
  2. SSH on port 2222, key-only, fail2ban
  3. UFW: allow 2222/tcp, 80/tcp, deny everything else
  4. Bun installed, app cloned to /home/deploy/app
  5. bun run build → dist/ populated
  6. Hono server as systemd service on port 80
  7. GitHub webhook listener for auto-deploy on push

Cloudflare (one-time per root domain):

  1. Add domain to Cloudflare, update registrar nameservers
  2. SSL mode: Full (not Strict — origin is HTTP)
  3. Always Use HTTPS: on
  4. A records for each active domain → server IP, proxied (orange cloud)

DNS sync script:

Cost

Item Monthly
Hetzner CX22 ~$4
Cloudflare (free tier) $0
Domain registration ~$2 amortized
Total ~$6/month

Consequences