Skip to content

Cloudflare

A cloudflared Deployment runs in-cluster, creating an encrypted tunnel to the Cloudflare edge using a remotely-managed (token-based) tunnel, as recommended by the official Cloudflare guide.

Prerequisites

  • Cloudflare account with Zero Trust enabled
  • A domain zone added to Cloudflare
  • cloudflared CLI installed locally
  • Cluster can reach Cloudflare on port 7844

Step 1: Create a Tunnel in the Dashboard

  1. Go to Zero Trust → Networks → Cloudflare Tunnels
  2. Select Create a tunnel
  3. Choose Cloudflared and name it (e.g. k8s-tunnel)
  4. Copy the tunnel token (e.g. eyJhIjoi...)

Step 2: Store the Token

kubectl create ns cloudflare-tunnel
kubectl -n cloudflare-tunnel create secret generic tunnel-token \
  --from-literal=token=<YOUR_TUNNEL_TOKEN>

Step 3: Deploy cloudflared

make deploy-cluster-tunnel

The Deployment runs 2 replicas (for HA — guide: replicas are for HA, not LB). Each replica connects to Cloudflare with the same tunnel token — no need for separate tunnels per pod.

Configuration: cloudflare-k8s/deployment.yaml

  • Routes traffic based on Public hostnames configured in the Cloudflare dashboard
  • Each replica exposes a /ready endpoint on port 2000 for liveness checks
  • ICMP support is enabled via net.ipv4.ping_group_range sysctl

Step 4: Deploy httpbin (Test App)

Deploy a sample httpbin application to verify tunnel connectivity:

kubectl apply -f cloudflare-k8s/httpbin.yaml

This creates a Deployment (2 replicas) and ClusterIP service named httpbin-service in the cloudflare-tunnel namespace.

Step 5: Configure Public Hostnames

In the Cloudflare dashboard under the tunnel, add Public hostnames:

Hostname Service
lab.example.com http://traefik.kube-system.svc:80
*.lab.example.com http://traefik.kube-system.svc:80

For direct testing without Traefik, point a hostname straight at httpbin:

Hostname Service
httpbin.lab.example.com http://httpbin-service.cloudflare-tunnel.svc:80

Traffic flows: User → Cloudflare Edge → cloudflared pod → Service → Pod

Step 6: Verify

kubectl -n cloudflare-tunnel logs deploy/cloudflared
Expected output:
INF Starting tunnel tunnelID=...
INF Version 2025...
INF Initial protocol quic

Test the httpbin endpoint:

curl -s httpbin.lab.example.com/get

Terraform — Tunnel Routes & Zero Trust

The cloudflare-k8s/ Terraform workspace manages private network routes and Gateway rules for the tunnel:

cd cloudflare-k8s
terraform init
terraform apply

Resources created:

Resource Purpose
cloudflare_argo_tunnel Zero Trust tunnel definition
cloudflare_tunnel_route Routes Kubernetes API IPs through the tunnel
cloudflare_teams_list IP list for the API server
cloudflare_teams_rule Bypass HTTP inspection for kubectl traffic

Note

The Terraform-managed tunnel (cloudflare_argo_tunnel) is separate from the dashboard-created tunnel used by the Deployment. Update main.tf to reference the correct tunnel ID if you want Terraform to manage tunnel routes for the dashboard-created tunnel.

Cloudflare Operator (Alternative)

A separate operator that manages DNS records and tunnels declaratively via Custom Resources:

make deploy-cloudflare-operator

Source: adyanth/cloudflare-operator