A Kubernetes Gateway API controller that routes cluster traffic through Cloudflare Tunnels — no public IPs or open firewall ports required.
How it works: The controller watches GatewayClass, Gateway, and HTTPRoute resources. For each Gateway it creates a Cloudflare Tunnel and deploys a cloudflared client. HTTPRoutes are translated into tunnel ingress rules and DNS CNAME records pointing to the tunnel.
Conformance: This controller passes the official Gateway API conformance tests for the GatewayHTTP profile against Gateway API v1.4.1. Conformance tests run on every push in CI.
flowchart LR
Browser -->|HTTPS| Edge[Cloudflare Edge]
Edge -->|Tunnel| CF[cloudflared Pod]
CF --> Svc[K8s Service]
Svc --> App[App Pod]
- Install the Gateway API CRDs (v1+):
kubectl apply -k github.com/kubernetes-sigs/gateway-api//config/crd?ref=v1.4.1- Install the controller:
kubectl apply -k github.com/cnap-tech/cloudflare-kubernetes-gateway//config/default?ref=main-
Find your Cloudflare account ID and create an API token with Cloudflare Tunnel Edit and Zone DNS Edit permissions.
-
Create the credentials Secret:
kubectl create secret -n cloudflare-gateway generic cloudflare \
--from-literal=ACCOUNT_ID=your-account-id \
--from-literal=TOKEN=your-api-token- Create a GatewayClass:
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: cloudflare
spec:
controllerName: github.com/cnap-tech/cloudflare-kubernetes-gateway
parametersRef:
group: ""
kind: Secret
namespace: cloudflare-gateway
name: cloudflare- Create a Gateway and HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway
namespace: cloudflare-gateway
spec:
gatewayClassName: cloudflare
listeners:
- protocol: HTTP
port: 80
name: http
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: example-route
namespace: default
spec:
parentRefs:
- name: gateway
namespace: cloudflare-gateway
hostnames:
- example.com
rules:
- backendRefs:
- name: example-service
port: 80- (Optional) Install Prometheus ServiceMonitors for controller and cloudflared metrics:
kubectl apply -k github.com/cnap-tech/cloudflare-kubernetes-gateway//config/prometheus?ref=main- HTTPRoute hostname and path matching with automatic path specificity sorting
- HTTPRoute Service backendRefs (without filtering or weighting)
- Gateway lifecycle management — tunnel creation, cloudflared deployment, cleanup on deletion
- GatewayClass validation with Cloudflare API token verification
- DNS management — automatic CNAME record creation and cleanup with metadata tags
- Two config modes — remote (Cloudflare dashboard) or local (ConfigMap-based)
- API proxy support — route API calls through a custom base URL
The controller reads configuration from the Secret referenced by the GatewayClass parametersRef. Required and optional keys:
| Key | Required | Default | Description |
|---|---|---|---|
ACCOUNT_ID |
Yes | — | Cloudflare account ID |
TOKEN |
Yes | — | Cloudflare API token |
API_BASE_URL |
No | api.cloudflare.com |
Custom API base URL (for proxy setups) |
CONFIG_MODE |
No | remote |
Tunnel config mode: remote or local |
Remote (default): Tunnel ingress rules are pushed to the Cloudflare API. Routes appear in the Cloudflare dashboard. cloudflared runs with --token and polls for config updates.
Local: Ingress rules are written to a ConfigMap on the cluster. cloudflared reads from a mounted config file. The controller triggers a rolling restart when the config changes. Faster updates with no polling delay — useful for automated or high-frequency deployments.
DNS records created by the controller include metadata for identification and filtering:
- Tags:
managed-by:cnap-gateway,tunnel-id:<id>,gateway:<namespace>/<name> - Comment: Human-readable description with Kubernetes context
Stale DNS records are automatically cleaned up using tag-based filtering when hostnames are removed from routes.
By default, a cloudflared Deployment runs for each Gateway. Additional clients can be deployed (guide) for custom replicas, tolerations, or other parameters — traffic is load-balanced across all connected clients.
To disable the built-in Deployment:
- Create a ConfigMap:
kubectl create configmap -n cloudflare-gateway gateway --from-literal=disableDeployment=true- Reference it from the Gateway:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway
namespace: cloudflare-gateway
spec:
gatewayClassName: cloudflare
listeners:
- protocol: HTTP
port: 80
name: http
infrastructure:
parametersRef:
group: ""
kind: ConfigMap
name: gatewayForked from alodex/cloudflare-kubernetes-gateway.