Skip to content

cfgate.iocfgate v0.2.0-alpha.11 · Release documentation

cfgate

cfgate is a Kubernetes operator that manages Cloudflare Tunnels, DNS records, reusable Access policies, and Access application bindings through custom resources. It uses Gateway API, the CNCF standard replacing Ingress, so routing configuration works the same as Envoy, Istio, or Cilium. Clusters running cfgate need no public IP, no ingress controller, and no load balancer. Traffic reaches services through Cloudflare Tunnels: outbound-only connections from the cluster to Cloudflare’s edge.

Gateway API is the Kubernetes successor to Ingress. If you’re coming from Ingress, see the Gateway API Primer.

  • Composable CRDs for tunnels, DNS, and access. CloudflareTunnel, CloudflareDNS, CloudflareAccessPolicy, and CloudflareAccessApplication each manage a distinct piece of Cloudflare infrastructure as Kubernetes resources. Tunnels, DNS records, reusable policies, and app bindings all live in version-controlled YAML instead of the Cloudflare dashboard.
  • Outbound-only tunnel connections. Cloudflare Tunnels establish outbound-only connections from the cluster to Cloudflare’s edge. Services are never exposed via public IP or load balancer.
  • Built on Gateway API. Uses the Gateway API standard, not a proprietary abstraction. Existing community operators use the deprecated Ingress API and lack Access policy management.
  • Independent, composable resources. Each CRD operates independently. Use the resources together or pick the ones you need: a tunnel without DNS sync, DNS without Access, Access policies without app bindings, or the full stack.

How cfgate works

Define a CloudflareTunnel, point a Gateway at it, and attach HTTPRoutes to the Gateway. cfgate reconciles each resource against the Cloudflare API: it creates the tunnel, deploys cloudflared pods, syncs DNS records, syncs reusable Access policies, and binds Access Applications to route host/path targets. Traffic flows from Cloudflare’s edge through the tunnel directly to in-cluster services. The cluster needs no public IP, no ingress controller, and no load balancer.

Kustomize

Terminal window
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.2/standard-install.yaml
kubectl apply -f https://github.com/cfgate/cfgate/releases/latest/download/install.yaml

Helm

Terminal window
helm install cfgate oci://ghcr.io/cfgate/charts/cfgate \
--namespace cfgate-system --create-namespace

Both methods create the cfgate-system namespace. CloudflareTunnel and CloudflareDNS resources typically live here. Routes and services can be in any namespace.

Terminal window
kubectl create secret generic cloudflare-credentials \
-n cfgate-system \
--from-literal=CLOUDFLARE_API_TOKEN=<your-token>
apiVersion: cfgate.io/v1alpha1
kind: CloudflareTunnel
metadata:
name: my-tunnel
namespace: cfgate-system
spec:
tunnel:
name: my-tunnel
cloudflare:
accountId: "<account-id>"
secretRef:
name: cloudflare-credentials
cloudflared:
replicas: 2
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: cfgate
spec:
controllerName: cfgate.io/cloudflare-tunnel-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cloudflare-tunnel
namespace: cfgate-system
annotations:
cfgate.io/tunnel-ref: cfgate-system/my-tunnel
spec:
gatewayClassName: cfgate
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All

GatewayClass declares the controller (cfgate.io/cloudflare-tunnel-controller). Gateway is the runtime instance that binds to a specific CloudflareTunnel via the cfgate.io/tunnel-ref annotation. Both are required.

apiVersion: cfgate.io/v1alpha1
kind: CloudflareDNS
metadata:
name: my-dns
namespace: cfgate-system
spec:
tunnelRef:
name: my-tunnel
zones:
- name: example.com
source:
gatewayRoutes:
enabled: true

The presence of source.gatewayRoutes enables route discovery. With enabled: true and no annotationFilter, cfgate syncs DNS records for all routes attached to the referenced tunnel’s Gateways. Explicit-only CloudflareDNS resources do not watch routes. To sync specific routes only, use the annotationFilter field. See CloudflareDNS reference.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app
namespace: default
spec:
parentRefs:
- name: cloudflare-tunnel
namespace: cfgate-system
hostnames:
- app.example.com
rules:
- backendRefs:
- name: my-service
port: 80

cfgate automatically:

  • Creates a CNAME record app.example.com → {tunnelId}.cfargotunnel.com
  • Adds a cloudflared ingress rule routing app.example.com → http://my-service.default.svc:80
  • Manages ownership TXT records for safe multi-cluster deployments

CloudflareTunnel manages tunnel lifecycle and cloudflared deployment. A single tunnel serves any number of domains across zones. → Full reference

CloudflareDNS syncs DNS records independently from tunnel lifecycle, with multi-zone support and ownership tracking. → Full reference

CloudflareAccessPolicy manages reusable account-level Cloudflare Access policies. → Full reference

CloudflareAccessApplication binds reusable policies to Gateway and HTTPRoute host/path targets. → Full reference

Per-route configuration (origin protocol, TLS settings, timeouts, DNS TTL) is set via annotations on Gateway API HTTPRoute resources. → Full reference

DocumentDescription
Authorization and ownershipAdministrator/tenant boundaries, grants, and alpha.5 → alpha.6 migration
Gateway API PrimerGateway API concepts for Ingress users
CloudflareTunnelFull CRD reference
CloudflareDNSFull CRD reference, annotationFilter, ownership
CloudflareAccessPolicyReusable Access policy reference, rule types, service tokens
CloudflareAccessApplicationGateway API target binding, path rules, policyRefs
AnnotationsComplete annotation reference
Service MeshIstio, Envoy Gateway, and Kiali integration
TroubleshootingDiagnostic steps and solutions
TestingUnit and E2E test strategy
ContributingDevelopment setup and workflow
ChangelogRelease history
ExampleDescription
basicSingle tunnel + gateway + DNS sync
multi-serviceMultiple services, one tunnel, reusable Access policies and app bindings
with-rancherRancher 2.14+ integration
external-targetA/AAAA records via ExternalTarget (no tunnel)

Create a token at Cloudflare Dashboard → API Tokens with:

ScopePermissionUsed By
AccountCloudflare Tunnel: EditCloudflareTunnel
AccountAccess: Apps and Policies: EditCloudflareAccessPolicy, CloudflareAccessApplication
AccountAccess: Service Tokens: EditCloudflareAccessPolicy
AccountAccount Settings: ReadCloudflareTunnel (accountName only)*
ZoneDNS: EditCloudflareDNS

*Only required when using spec.cloudflare.accountName instead of accountId.

  • Kubernetes compatible with the installed Gateway API bundle; the standard bundle below requires Kubernetes 1.30 or later
  • Gateway API CRDs installed; this release pins validation to v1.6.2
  • cluster-admin access for CRD installation

The bundle’s API minimum is not a cfgate-tested version range. See the compatibility and release validation policy for component pins, tested combinations and upgrade requirements.

RepositoryDescription
cfgate/helm-chartHelm chart for cfgate
cfgate/cfgate.ioProject website
Terminal window
brew install mise
mise install
mise tasks

See CONTRIBUTING.md for full development setup, secrets configuration, and contribution guidelines.

See docs/TESTING.md for the unit-only CI coverage model, local-only E2E bootstrap paths, environment variables, and test execution.

Apache 2.0