Services

2026 03 20 Caddy Gateway Exposure Design


Expose Assistance Project via Remote Caddy Gateway

Date: 2026-03-20 Status: Deployed

Overview#

Expose all HTTP services in the assistance monorepo through the remote Caddy gateway at proxy using the assistance.prod.assistance.bg delegated subzone.

Dev services are exposed under assistance.dev.assistance.bg (pointing to dev ports on suse-10).

Prod Services (assistance.prod.assistance.bg)#

HostnameBackendProd PortProxy Mode
www.assistance.prod.assistance.bgNext.js marketing site13000svc_proxy
docs.assistance.prod.assistance.bgNext.js docs platform13001svc_proxy
ds.assistance.prod.assistance.bgDesign system app13003svc_proxy
ui.assistance.prod.assistance.bgUI library app13004svc_proxy
learn.assistance.prod.assistance.bgLearning app13007svc_proxy
manager.assistance.prod.assistance.bgSaaS admin dashboard14000svc_proxy
auth.assistance.prod.assistance.bgAuth API (Go)18090svc_proxy
billing.assistance.prod.assistance.bgBilling API (Go)18080svc_proxy
html.assistance.prod.assistance.bgDesign system (alias)13003svc_proxy

Dev Services (assistance.dev.assistance.bg)#

HostnameBackendDev PortProxy Mode
www.assistance.dev.assistance.bgNext.js marketing site3040svc_proxy
docs.assistance.dev.assistance.bgNext.js docs platform3041svc_proxy
ds.assistance.dev.assistance.bgDesign system app3042svc_proxy
ui.assistance.dev.assistance.bgUI library app3043svc_proxy
learn.assistance.dev.assistance.bgLearning app3044svc_proxy
manager.assistance.dev.assistance.bgSaaS admin dashboard3045svc_proxy
auth.assistance.dev.assistance.bgAuth API (Go)3046svc_proxy
billing.assistance.dev.assistance.bgBilling API (Go)3047svc_proxy
html.assistance.dev.assistance.bgDesign system (alias)3042svc_proxy

Excluded (non-HTTP / dev-only)#

  • PostgreSQL (3058 prod / 3048 dev) - TCP database
  • Meilisearch (3059 prod / 3049 dev) - search (internal only)
  • Mailpit SMTP - dev email catcher only

DNS Architecture#

Zone hierarchy under Cloudflare-managed assistance.bg:

1
assistance.bg (Cloudflare — public domain)
2
├── prod.assistance.bg (Linode zone, NS delegated from CF)
3
│ └── assistance.prod.assistance.bg (Linode subzone, NS delegated from prod)
4
│ ├── A @ → 139.162.191.120
5
│ ├── A www → 139.162.191.120
6
│ ├── A docs → 139.162.191.120
7
│ ├── A ds → 139.162.191.120
8
│ ├── A ui → 139.162.191.120
9
│ ├── A learn → 139.162.191.120
10
│ ├── A manager → 139.162.191.120
11
│ ├── A auth → 139.162.191.120
12
│ ├── A billing → 139.162.191.120
13
│ ├── A html → 139.162.191.120
14
│ └── A index → 139.162.191.120
15
├── dev.assistance.bg (Linode zone, NS delegated from CF)
16
│ └── assistance.dev.assistance.bg (Linode subzone, NS delegated from dev)

Caddy Configuration#

All site blocks include:

  • import common_headers - security headers
  • import ip_allowlist - restrict to allowed IPs
  • import zone_index assistance.prod.assistance.bg - zone index at /_zone
  • reverse_proxy to suse-10.lan.assistance.bg:<port> with import svc_proxy

The www block acts as a unified gateway with path-based routing:

  • /api/auth/* → auth-api (strip prefix)
  • /api/billing/* → billing-api (strip prefix)
  • /docs*, /learn*, /design-system*, /ui-library*, /manager* → respective apps
  • /blog* → blog dev server :3063 when using path-based dev access (see below)
  • catch-all → www

Blog at /blog on www (dev)#

The blog app uses Next.js basePath: '/blog'. If Caddy sends /blog* to suse-10.lan.assistance.bg:3063, the path must not be stripped: upstream expects /blog/_next/..., not /_next/.... Hot reload uses WebSockets (e.g. wss://www.assistance.dev.assistance.bg/blog/_next/webpack-hmr). Caddy’s reverse_proxy forwards Upgrade/Connection by default; HTTP 502 on that socket usually means the blog process is not listening on 3063, the matcher strips /blog, or a timeout closes the upgrade. Production blog is normally the separate host blog.assistance.bg (Cloudflare Workers), not a path on www.

Paste-ready Caddy fragment: docs/infrastructure/caddy-www-blog-path.caddy (adjust upstream host if Caddy and Next run on the same machine).

Individual per-service blocks also exist for direct access.

File Structure#

1
192.168.3.20:/home/vchavkov/src/BA/registry/
2
caddy/
3
Caddyfile # All site blocks (Groups 8 + 9 for assistance)
4
zones/
5
assistance.prod.assistance.bg/
6
index.html # Zone index listing all services
7
assistance.dev.assistance.bg/
8
index.html
9
scripts/
10
sync.sh # rsync + validate + reload

Deployment#

  1. Edit Caddyfile on 192.168.3.20:/home/vchavkov/src/BA/registry/caddy/Caddyfile
  2. Run ssh 192.168.3.20 'cd /home/vchavkov/src/BA/registry && bash scripts/sync.sh'

Constraints#

  • All sites under *.prod.assistance.bg and *.dev.assistance.bg MUST have ip_allowlist
  • TTL for all DNS records: 300 seconds (A records), 86400 (NS records)
  • Use service registry API at 192.168.3.20:9070 for DNS (not Linode API directly)
  • Never edit /etc/caddy/Caddyfile on proxy directly
  • Prod ports: 13000-14000, 18080-18090 range (from podman-compose.yml)
  • Dev ports: 3040-3047 range (native processes via mise)