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 dns against Vultr boxes, with a local config.yaml
ingress and a Capistrano deploy model) was removed — it described an
architecture that no longer exists.

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 to kamal-proxy over the loopback.
  • Public hostnames crm/www/api/scan/mcp.warmlyyours.com (.warmlyyours.ws
    for staging) resolve to the tunnel via proxied *.cfargotunnel.com CNAMEs.

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.deb
cloudflared 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

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

On the active node (over the tailnet):

systemctl status cloudflared          # expect active (running), QUIC connected

Tunnel/connector health is also visible in the Cloudflare Zero Trust dashboard
(Networks → Tunnels) — the box should show HEALTHY.

See also