From adec17cd020de323a134b8ca38ccbd7f74c33eb2 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Sat, 14 Mar 2026 19:41:35 +0100 Subject: [PATCH] docs(deployment): add future scaling section for 50+ custom domains Document two strategies for scaling beyond manual Caddyfile management: - Caddy on-demand TLS (simple, no Cloudflare protection) - Cloudflare for SaaS / Custom Hostnames (recommended, full protection) - Infrastructure scaling notes for 1,000+ sites Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/deployment/hetzner-server-setup.md | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/docs/deployment/hetzner-server-setup.md b/docs/deployment/hetzner-server-setup.md index 20a67f5f..0525f999 100644 --- a/docs/deployment/hetzner-server-setup.md +++ b/docs/deployment/hetzner-server-setup.md @@ -968,6 +968,53 @@ curl -I https://newplatform.lu/ curl -I https://teststore.newplatform.lu/ ``` +#### Future: Scaling Custom Domains Beyond 50 + +The manual Caddyfile approach (one block per custom domain + Cloudflare origin cert) works well up to ~50 custom store domains. Beyond that, managing Caddyfile blocks and origin certs becomes tedious. Two scaling strategies: + +##### Option 1: Caddy On-Demand TLS (simple, no Cloudflare protection) + +A single catch-all Caddy block replaces all custom domain blocks. Caddy auto-provisions Let's Encrypt certs on first request and validates domains against the database: + +```caddy +https:// { + tls { + on_demand + ask http://localhost:8001/api/v1/internal/verify-domain + } + reverse_proxy localhost:8001 +} +``` + +The `/verify-domain` endpoint checks the `store_domains` table — returns 200 (provision cert) or 404 (reject). Adding a new custom domain becomes a database insert only, no Caddy or Cloudflare changes needed. + +**Limitation**: Custom domains point directly to the server (no Cloudflare proxy), so they lose DDoS protection, WAF, bot mitigation, and CDN caching. A DDoS attack on any custom domain impacts all sites on the server. + +Let's Encrypt rate limits (50 certs/registered domain/week, 300 new orders/3 hours) are not an issue since each custom domain is unique. Caddy handles 5,000+ certs comfortably in memory (~50-100MB). + +##### Option 2: Cloudflare for SaaS (recommended for production scale) + +[Cloudflare for SaaS](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/) (Custom Hostnames) is how Shopify, Webflow, and major SaaS platforms handle custom domains at scale. Every custom domain gets full Cloudflare protection: + +1. Configure a **fallback origin** in your Cloudflare account (e.g., `customers.omsflow.lu → 91.99.65.229`) +2. Customer sets a CNAME: `wizamart.com → customers.omsflow.lu` +3. Cloudflare proxies `wizamart.com` through your account — DDoS, WAF, bot protection, CDN all included +4. SSL is automatic (no Let's Encrypt, no origin certs to manage) +5. Adding a domain = database insert + one Cloudflare API call (automatable) + +**Cost**: Available on Cloudflare Pro ($20/month) with per-hostname pricing (~$0.10/month each at volume). At 5,000 domains × $0.10 = ~$500/month for full Cloudflare protection on every customer domain. + +**Hybrid approach**: Platform domains (`*.omsflow.lu`, `rewardflow.lu`, etc.) stay on the current Cloudflare setup with origin certs. Only customer custom domains use Cloudflare for SaaS. + +##### Infrastructure scaling at 1,000+ sites + +At this scale, the 4GB Hetzner VPS becomes the bottleneck before Caddy does. Plan for: + +- Horizontal scaling: multiple app servers behind a load balancer +- Dedicated PostgreSQL server +- Dedicated Redis instance +- CDN for static assets (Cloudflare, already in place) + ## Step 15: Gitea Actions Runner !!! warning "ARM64 architecture"