Skip to content

Installation

This guide covers installing the Cloudflare Tunnel Gateway Controller using Helm.

Helm Installation

Helm is the only supported installation method. It handles CRD installation, RBAC setup, and provides a simple upgrade path.

Basic Installation

kubectl create namespace cloudflare-tunnel-system
kubectl create secret generic cloudflare-credentials \
  --namespace cloudflare-tunnel-system \
  --from-literal=api-token="YOUR_API_TOKEN"
kubectl create secret generic cloudflare-tunnel-token \
  --namespace cloudflare-tunnel-system \
  --from-literal=tunnel-token="YOUR_TUNNEL_TOKEN"

helm install cloudflare-tunnel-gateway-controller \
  oci://ghcr.io/lexfrei/charts/cloudflare-tunnel-gateway-controller \
  --namespace cloudflare-tunnel-system \
  --set gatewayClassConfig.create=true \
  --set gatewayClassConfig.tunnelID=YOUR_TUNNEL_ID \
  --set gatewayClassConfig.cloudflareCredentialsSecretRef.name=cloudflare-credentials \
  --set proxy.tunnelTokenSecretRef.name=cloudflare-tunnel-token

Installation with Values File

Create a values.yaml file:

gatewayClassConfig:
  create: true
  tunnelID: "550e8400-e29b-41d4-a716-446655440000"
  cloudflareCredentialsSecretRef:
    name: cloudflare-credentials

# L7 proxy data plane (embeds cloudflared transport in-process).
# tunnelTokenSecretRef.name is REQUIRED — the chart will fail install otherwise.
proxy:
  replicas: 2
  tunnelTokenSecretRef:
    name: cloudflare-tunnel-token

# Controller deployment
replicaCount: 2
resources:
  limits:
    memory: 256Mi
  requests:
    cpu: 100m
    memory: 128Mi
leaderElection:
  enabled: true

Then install:

helm install cloudflare-tunnel-gateway-controller \
  oci://ghcr.io/lexfrei/charts/cloudflare-tunnel-gateway-controller \
  --namespace cloudflare-tunnel-system \
  --create-namespace \
  --values values.yaml

The chart always deploys the in-process L7 proxy alongside the controller (this is the only data plane in v3). For the full list of proxy knobs, see the Helm values reference and the L7 Proxy Guide.

Verify Installation

Check that the controller and proxy pods are running:

kubectl get pods --namespace cloudflare-tunnel-system

Expected output:

NAME                                                            READY   STATUS    RESTARTS   AGE
cloudflare-tunnel-gateway-controller-7d8f9b6c5d-x2j9k           1/1     Running   0          30s
cloudflare-tunnel-gateway-controller-proxy-5c4d8b7f6c-m8n3l     1/1     Running   0          30s
cloudflare-tunnel-gateway-controller-proxy-5c4d8b7f6c-pqr4t     1/1     Running   0          30s

Check GatewayClass:

kubectl get gatewayclass cloudflare-tunnel

Expected output:

NAME               CONTROLLER                          ACCEPTED   AGE
cloudflare-tunnel  cf.k8s.lex.la/tunnel-controller     True       30s

Upgrading

To upgrade to a newer version:

helm upgrade cloudflare-tunnel-gateway-controller \
  oci://ghcr.io/lexfrei/charts/cloudflare-tunnel-gateway-controller \
  --namespace cloudflare-tunnel-system \
  --values values.yaml

Uninstalling

To remove the controller:

helm uninstall cloudflare-tunnel-gateway-controller \
  --namespace cloudflare-tunnel-system

Cleanup

Uninstalling the Helm release will remove the controller and proxy pods. The tunnel configuration in Cloudflare will remain. To fully clean up, delete the tunnel from the Cloudflare dashboard.

If proxy.authTokenSecretRef.name is empty (the default), the controller also generated a config-API bearer token into a Secret named <fullname>-proxy-auth-token, where <fullname> is the Helm release fullname (typically <release>-cloudflare-tunnel-gateway-controller, or just <release> when the release name already contains the chart name). Helm never rendered that Secret, the controller created it directly via the Kubernetes API, so helm uninstall does not remove it either. This is deliberate: reinstalling under the same release name picks the existing token back up instead of generating a new one, so the proxy never has to roll just because you reinstalled. To remove it as part of a full cleanup, first find the exact name your release rendered, then delete it:

helm get manifest <release> | grep -m1 'proxy-auth-secret-ref'

kubectl delete secret <fullname>-proxy-auth-token --namespace cloudflare-tunnel-system

Alternative: External Secrets

For production deployments, consider using external-secrets to manage Cloudflare credentials:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: cloudflare-credentials
  namespace: cloudflare-tunnel-system
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: cloudflare-credentials
  data:
    - secretKey: api-token
      remoteRef:
        key: cloudflare/api-token

Next Steps

After installation, proceed to Quick Start to create your first HTTPRoute.