Appearance
Cloudflare Tunnel
Gets api.chemassist-occ.com to the droplet's nginx container without opening any inbound port on the box besides SSH.
Model: remotely managed
Hostname routing lives in the Cloudflare dashboard, not a local config.yml on the droplet. The cloudflared container only needs a TUNNEL_TOKEN; everything else (which hostname maps to which origin) is configured once in Zero Trust and can be changed without touching the droplet.
Setup
- Cloudflare dashboard → Zero Trust → Networks → Tunnels → Create a tunnel → name it (e.g.
chem-assistant-api) → choose "Docker" as the connector — copy the token it gives you (the long string after--tokenin the sample command). That'sTUNNEL_TOKEN. - Same tunnel → Public Hostname tab → Add a hostname:
- Subdomain:
api, Domain:chemassist-occ.com - Service:
HTTP, URL:nginx:80(the container name inside the compose network, notlocalhost—cloudflaredandnginxshare the compose network, not the host's).
- Subdomain:
- Paste
TUNNEL_TOKENinto/opt/csa/.envand bring thecloudflaredservice up (it's already indocker-compose.prod.yml). - Verify:
curl https://api.chemassist-occ.com/should return the API's health JSON.
Gotchas
- Never run
cloudflared service install <token>on a laptop or any second machine with the same token. It registers a second connector on the same tunnel, and requests round-robin between the droplet and whatever else has the token — confusing intermittent 502s that look like a droplet problem but aren't. cloudflaredhas nodepends_onin the compose file on purpose — gating it on postgres/redis/api being healthy would block bringing up infra-only on a fresh droplet before.envis fully written.- The tunnel terminates TLS at Cloudflare's edge.
nginxinside the compose network serves plain HTTP on port 80 — that's correct, not a missing cert.