cloudflared — Cloudflare Tunnel (post-Kamal)
How the Heatwave hosts receive inbound web traffic without opening a single
inbound web port. Companion to
INFRASTRUCTURE_INVENTORY.md — see its
Edge & network protection and Fleet sections for the surrounding edge
facts.
The pre-Kamal version of this file (hand-run
cloudflared tunnel login/create/route dnsagainst Vultr boxes, with a localconfig.yamlingress and a Capistrano deploy model) was removed — it described an architecture that no longer exists.
What it is
Section titled “What it is”cloudflared is the Cloudflare Tunnel connector. It runs as a host systemd
service on the active app box and holds a single outbound-only QUIC
connection out to Cloudflare. Cloudflare routes inbound HTTPS for the public
hostnames back down that connection to kamal-proxy :80 on the host.
Consequences of the outbound model:
- No inbound web port is open on any box — DOCKER-USER iptables drops
public
80/443. The tunnel is the only path in for web traffic. - TLS terminates at Cloudflare. Kamal runs
proxy.ssl: false; the tunnel hands plain HTTP tokamal-proxyover the loopback. - Public hostnames
crm/www/api/scan/mcp.warmlyyours.com(.warmlyyours.wsfor staging) resolve to the tunnel via proxied*.cfargotunnel.comCNAMEs.
Installed by cloud-init, managed remotely (Terraform)
Section titled “Installed by cloud-init, managed remotely (Terraform)”There is no tunnel config file on the host. The tunnel, its ingress rules,
and the DNS records are declared in Cloudflare and managed by Terraform — prod
in infra/terraform/cloudflare-production/,
staging in infra/terraform/cloudflare/.
Both create a cloudflare_zero_trust_tunnel_cloudflared with
config_src = "cloudflare" (remotely-managed) plus a
…_cloudflared_config ingress block (hostname → http://localhost:80, with the
required http_status:404 catch-all) and the proxied DNS records.
The host side is just the connector binary + service, installed by cloud-init
(infra/terraform/latitude/cloud-init.yaml.tftpl):
# cloud-init runcmd (only runs if cloudflared_token is non-empty):dpkg -i cloudflared-linux-amd64.debcloudflared service install <connector-token>The connector token is the tunnel_token output of the Cloudflare module,
piped into the latitude module as var.cloudflared_token:
-var cloudflared_token="$(tofu -chdir=../cloudflare output -raw tunnel_token)"No cloudflared tunnel login / create / route dns is ever run by hand — those
steps are the Terraform resources.
Which node runs it, and why
Section titled “Which node runs it, and why”cloudflared runs only on the active-primary node (today Dallas,
dal-latitude-heatwave-01). It fronts the public hostnames, which point at the
active node, so it is correctly inactive on the standby (Chicago,
chi-latitude-heatwave-02) and comes up only on promotion.
The cloudflare-production module also defines a Chicago tunnel for a
future cutover (“W3”), gated behind var.activate_dns = false: tofu apply
creates that tunnel + ingress and emits its connector token without moving
any traffic. Flipping activate_dns=true repoints the prod CNAMEs to the
Chicago tunnel — the production traffic cutover — and is done only after the
Chicago app stack is deployed and Postgres is promoted (rollback = re-apply
with activate_dns=false).
Verify
Section titled “Verify”On the active node (over the tailnet):
systemctl status cloudflared # expect active (running), QUIC connectedTunnel/connector health is also visible in the Cloudflare Zero Trust dashboard (Networks → Tunnels) — the box should show HEALTHY.
See also
Section titled “See also”INFRASTRUCTURE_INVENTORY.md— full edge/ingress, port-exposure map, and firewall layering.infra/terraform/cloudflare-production/main.tf— the prod tunnel + ingress + DNS + Chicago-cutover gate.