Skip to content

Manual Installation

Not Recommended

The recommended installation method is via Helm chart. Use manual installation only if you have specific requirements that prevent using Helm.

Prerequisites

  • Kubernetes cluster with kubectl configured
  • Gateway API CRDs installed
  • Cloudflare account with a pre-created tunnel

Installation Steps

1. Install Gateway API CRDs

kubectl apply --filename https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.2/standard-install.yaml

2. Create Namespace

kubectl create namespace cloudflare-tunnel-system

3. Create Secrets

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

# Tunnel token (consumed by the L7 proxy pod via the chart's proxy.tunnelTokenSecretRef)
kubectl create secret generic cloudflare-tunnel-token \
  --namespace cloudflare-tunnel-system \
  --from-literal=tunnel-token="YOUR_TUNNEL_TOKEN"

4. Deploy the Controller

deploy/controller/deployment.yaml names a controller image and, in the --proxy-image flag, a proxy image. Set both tags to the release you are installing before applying. An image tag is the release version without its leading v: release v3.5.0 publishes tag 3.5.0, and a v-prefixed tag fails with ImagePullBackOff.

kubectl apply --filename deploy/rbac/
kubectl apply --filename deploy/controller/

Manual manifests ship the controller only — the L7 proxy is not included

The in-process L7 proxy is the only data plane, and the controller pushes routing config to it at the --proxy-endpoints URL. The deploy/ manifests intentionally ship only the controller (and the RBAC it needs); they do NOT include the proxy Deployment, Services, or config. Without the proxy running, no HTTPRoute or GRPCRoute takes effect and all traffic is silently non-functional — even though step 3 created the cloudflare-tunnel-token Secret. Use the Helm chart for a complete, working installation, or supply your own proxy Deployment that consumes the tunnel token and serves the config API at the --proxy-endpoints URL. Point that URL at a headless Service with publishNotReadyAddresses: true: without it, DNS never returns a joining proxy pod, which is NotReady until it has its first config, so it never receives that config (see Proxy Endpoints). deploy/controller/deployment.yaml sets --proxy-auth-secret-ref so the controller generates and uses a config-API bearer token automatically; point your own proxy's PROXY_AUTH_TOKEN env at the same Secret (auth-token key) via a pod-level secretKeyRef so it validates pushes against the same token. That variable is required in tunnel mode: the proxy refuses to start without it, or without PROXY_ALLOW_UNAUTHENTICATED_CONFIG_API=1. See Config API Auth Wiring. The manifests also leave config API TLS off, which the chart turns on by default: to match it, add --proxy-config-ca-secret and --proxy-config-tls-secret, switch --proxy-endpoints to https://, grant the controller update on the serving-certificate Secret, and mount that Secret into your proxy with PROXY_CONFIG_TLS_CERT_FILE and PROXY_CONFIG_TLS_KEY_FILE — see Config API TLS.

5. Create GatewayClassConfig, GatewayClass, and Gateway

Edit deploy/samples/gatewayclassconfig.yaml with your tunnel ID:

kubectl apply --filename deploy/samples/gatewayclassconfig.yaml
kubectl apply --filename deploy/samples/gatewayclass.yaml
kubectl apply --filename deploy/samples/gateway.yaml

Manifest Structure

The deploy/ directory contains:

deploy/
├── rbac/
│   ├── service_account.yaml
│   ├── role.yaml
│   └── role_binding.yaml
├── controller/
│   └── deployment.yaml
└── samples/
    ├── secrets.yaml
    ├── gatewayclassconfig.yaml
    ├── gatewayclass.yaml
    └── gateway.yaml

Sample GatewayClassConfig

apiVersion: cf.k8s.lex.la/v1alpha1
kind: GatewayClassConfig
metadata:
  name: cloudflare-tunnel-config
spec:
  tunnelID: "550e8400-e29b-41d4-a716-446655440000"
  cloudflareCredentialsSecretRef:
    name: cloudflare-credentials
  # accountId: "1234567890abcdef"  # Optional, auto-detected

The tunnel token is passed to the L7 proxy pod as the TUNNEL_TOKEN environment variable. In the Helm chart this is wired via proxy.tunnelTokenSecretRef; for a manual proxy deployment, expose the cloudflare-tunnel-token Secret to the proxy container the same way. The token is not part of the GatewayClassConfig CRD spec.

Sample GatewayClass

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: cloudflare-tunnel
spec:
  controllerName: cf.k8s.lex.la/tunnel-controller
  parametersRef:
    group: cf.k8s.lex.la
    kind: GatewayClassConfig
    name: cloudflare-tunnel-config

Sample Gateway

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: cloudflare-tunnel
  namespace: cloudflare-tunnel-system
spec:
  gatewayClassName: cloudflare-tunnel
  listeners:
    - name: https
      port: 443
      protocol: HTTPS
      # Allow HTTPRoutes from all namespaces
      allowedRoutes:
        namespaces:
          from: All

Upgrading

When upgrading manually, apply manifests in order:

  1. Update CRDs (if changed)
  2. Update RBAC resources
  3. Update controller deployment, after setting both image tags in deploy/controller/deployment.yaml to the new release
kubectl apply --filename deploy/rbac/
kubectl apply --filename deploy/controller/

Uninstalling

kubectl delete --filename deploy/samples/gateway.yaml
kubectl delete --filename deploy/samples/gatewayclass.yaml
kubectl delete --filename deploy/samples/gatewayclassconfig.yaml
kubectl delete --filename deploy/controller/
kubectl delete --filename deploy/rbac/
kubectl delete namespace cloudflare-tunnel-system

Comparison with Helm

Aspect Helm Manual
CRD management Automatic Manual
RBAC setup Automatic Manual
Upgrades helm upgrade kubectl apply
Rollback helm rollback Manual
Value validation Schema validation None
Templating Built-in None

When to Use Manual Installation

  • Air-gapped environments without Helm access
  • Strict policy requirements against Helm
  • Custom modifications not supported by chart
  • Educational/debugging purposes

For production deployments, the Helm chart is strongly recommended for its ease of use, upgrade path, and validation features.