KFlared manages Cloudflare Tunnels for two paths: it publishes selected Kubernetes Gateway API hostnames through controller-owned tunnels, and it can route a Service ClusterIP privately to enrolled Cloudflare One clients. Workloads and the Traefik data plane remain on private Kubernetes networking.
The first release is deliberately an integration controller, not a Gateway API implementation:
Cloudflare edge
-> remotely managed Cloudflare Tunnel
-> official cloudflared connector Deployment
-> internal Traefik Service
-> Traefik Gateway and HTTPRoutes
-> workload Services
See the KFlared documentation, architecture, and security model before operating the controller.
- Traefik 3.7.10+ using GatewayClass controller
traefik.io/gateway-controller - Gateway API v1.6.1
GatewayandHTTPRoute - one
CloudflareTunnelBinding, one existing Gateway, and one remotely managed tunnel - one named HTTP listener and concrete, non-wildcard HTTPRoute hostnames
- binding, Gateway, and HTTPRoutes in the application namespace; an origin Service may be cross-namespace with a matching ReferenceGrant
- official
cloudflare/cloudflared:2026.8.3, one or more replicas (one by default) - per-binding ExternalDNS
DNSEndpointautomation, enabled by default, with an explicit opt-out for externally managed DNS targets
GRPCRoute, wildcard hostnames, HTTPS origins, direct Service backend routing, shared/imported tunnels, externally managed connectors, and native GatewayClass ownership are deferred.
CloudflareTunnelPrivateRoute is a separate API for private CIDR routing. It targets a referenced Service's current ClusterIP as a single /32 route and does not configure public hostnames or HTTPRoutes. See the private route configuration and architecture. Cloudflare One client enrollment/access policy and the user's Kubernetes credentials remain external prerequisites.
- Kubernetes 1.35-1.37
- Gateway API v1.6.1 CRDs
- Traefik 3.7.10+ with Kubernetes Gateway provider enabled
- a normal internal
ClusterIPTraefik origin Service (dedicated single-port Service recommended) - a Cloudflare account API token with only Cloudflare Tunnel/Connector write access required for the selected account
- optional ExternalDNS and its
externaldns.k8s.io/v1alpha1DNSEndpoint CRD - optional External Secrets Operator and a
ClusterSecretStorewhen using the chart's ExternalSecret integration
The token does not need DNS edit permission. Put it only in kflared; the controller has no cluster-wide Secret permission:
kubectl -n kflared create secret generic cloudflare-api-token \
--from-literal=api-token='<CLOUDFLARE_API_TOKEN>'Helm users can instead opt in to the chart's externalSecrets values. The chart then creates an ExternalSecret targeting cloudflare-api-token; External Secrets Operator and the referenced ClusterSecretStore must already exist.
Kustomize is the canonical manifest source. Install prerequisites first, then build and deploy an image:
Project automation requires Go and PowerShell 7 or newer. Task is pinned in the isolated tools/task module and does not need to be installed globally. Run tasks through the repository-local wrapper, for example ./task.ps1 --list (PowerShell: .\\task.ps1 --list).
./task.ps1 docker-build docker-push IMG=<registry>/kflared:<tag>
./task.ps1 install
./task.ps1 deploy IMG=<registry>/kflared:<tag>The checked-in Kustomize manager manifest and Helm chart default to the kflared controller class. Set spec.controller: kflared on each provider and binding it owns. Override the chart's controllerClass when running multiple KFlared installations in one cluster, and use the matching value in their resources.
Create a provider, label an allowed tenant namespace, and create a binding. Adapt the examples under config/samples to the actual account ID, DNS zones, Gateway, listener, and Traefik Service.
The binding keeps its Gateway and eligible HTTPRoutes in its application namespace. Its originServiceRef may point to a Service in another namespace if that Service namespace contains a matching Gateway API ReferenceGrant. KFlared checks this custom reference during reconciliation; Kubernetes does not automatically enforce it, and the grant does not provide network access. Revoking authorization deprograms the tunnel and removes managed DNS. Since grants cannot authorize a particular port, use a dedicated single-port ClusterIP origin Service. Origin selection is per binding, allowing one provider to serve multiple origins, multiple providers to share an origin, and incremental blue-green migration.
kubectl apply -f config/samples/kflared_v1alpha1_clustercloudflareprovider.yaml
kubectl label namespace my-app kflared.kodeblox.com/cloudflare-provider=default
kubectl -n my-app apply -f config/samples/kflared_v1alpha1_cloudflaretunnelbinding.yamlInspect conditions and DNS requirements:
kubectl get clustercloudflareproviders
kubectl -n my-app get cloudflaretunnelbindings -o yamlspec.dnsAutomationEnabled defaults to true. When enabled but the DNSEndpoint CRD is unavailable, status.dnsRecords is authoritative and DNSAutomationReady=False reports ManualConfigurationRequired. The MVP intentionally cannot acknowledge or verify manually managed DNS, so Ready remains false. Set the field to false when the public hostname must retain another DNS target; KFlared then removes any binding-owned DNSEndpoint, does not prescribe replacement CNAMEs, and treats the intentional opt-out as ready.
The module targets Go 1.27.0, controller-runtime v0.25.0, Gateway API v1.6.1, and cloudflare-go/v7 v7.8.0. On this workstation, invoke the existing versioned executable and do not alter the default Go installation:
go1.27.0 test ./...
go1.27.0 build ./cmdGenerated code and manifests remain Kubebuilder-controlled. Use the pinned controller-gen version from Taskfile.yaml and verify the resulting diff. See testing.
The supported Helm chart is rooted at charts. It combines the current Helm starter structure with the controller resources derived from Kubebuilder's Kustomize output. Its plain CRDs live in Helm's special charts/crds/ directory; pass --include-crds when rendering the complete chart.
Apache License 2.0. See LICENSE.