A Kubernetes operator for Cloudflare.
It manages Cloudflare tunnels, the cloudflared daemons that run them, and DNS records, as Kubernetes resources.
An Ingress controller creates a tunnel for any Ingress in the cloudflare class.
This project is
v1alpha1and the API may change.
You probably want one of these instead:
| Project | Description |
|---|---|
| adyanth/cloudflare-operator | Creates and manages Cloudflare Tunnels and DNS records for HTTP/TCP/UDP Service resources |
| STRRL/cloudflare-tunnel-ingress-controller | A Kubernetes Ingress controller built on Cloudflare Tunnel |
Both are more mature and cover most of what people come here looking for.
This operator exists because I wanted the experience of writing one, and because I wanted my favourite parts of both projects in a single place.
The three model the problem differently.
adyanth's Tunnel and ClusterTunnel bundle the tunnel with the cloudflared Deployment that serves it, and a TunnelBinding attaches Services to a tunnel and creates their DNS records.
STRRL's controller has no CRDs at all: it owns one tunnel for the cluster and derives its configuration from Ingress objects.
Here every Cloudflare object gets its own resource.
A CloudflareTunnel, the Cloudflared that runs it, and a DnsRecord are each reconciled on their own and refer to one another by name.
All resources live in the cloudflare.unmango.dev/v1alpha1 API group.
| Kind | What it does |
|---|---|
CloudflareTunnel |
Creates and reconciles a tunnel through the Cloudflare API, and can create the Cloudflared that runs it |
Cloudflared |
Runs the cloudflared daemon for a tunnel as a DaemonSet or a Deployment |
DnsRecord |
Manages a single DNS record in a zone |
The Ingress controller watches core Ingress objects and creates a CloudflareTunnel for any whose ingressClassName is cloudflare.
It reads its configuration from ingress.cloudflare.unmango.dev/ annotations.
The manager reads its Cloudflare API token from the CLOUDFLARE_API_TOKEN environment variable.
The chart references a Secret holding that token but never creates one, so create it first:
kubectl create namespace cloudflare-operator-system
kubectl create secret generic cloudflare-credentials \
--namespace cloudflare-operator-system \
--from-literal=CLOUDFLARE_API_TOKEN=<token>
helm install cloudflare-operator ./dist/chart \
--namespace cloudflare-operator-system \
--set cloudflare.auth.apiTokenRef.name=cloudflare-credentialsLeaving cloudflare.auth.apiTokenRef unset installs a manager with no credentials.
It still runs and still reconciles Cloudflared, which needs no API access, but every Cloudflare API call fails.
The chart also installs an IngressClass named cloudflare; set ingressClass.enabled=false to skip it.
The CRDs carry helm.sh/resource-policy: keep, so helm uninstall leaves them in the cluster.
helm uninstall cloudflare-operator --namespace cloudflare-operator-systemTo install from source without Helm, apply the kustomize output instead. This path does not wire up the token; set it yourself afterwards.
make install
make deploy
make undeploy
make uninstallapiVersion: cloudflare.unmango.dev/v1alpha1
kind: CloudflareTunnel
metadata:
name: example
spec:
accountId: <cloudflare-account-id>
configSource: cloudflare
---
apiVersion: cloudflare.unmango.dev/v1alpha1
kind: Cloudflared
metadata:
name: example
spec:
config:
tunnelRef:
name: exampleMore examples are in config/samples.
The repository is Nix first.
The dev shell supplies Go, controller-gen, kustomize, golangci-lint, kubebuilder, helm, kind and the envtest binaries, so nothing is downloaded at build or test time.
direnv allow # or prefix commands with `nix develop -c`
make test # unit and envtest suites
make lint
make build # nix build .#
make check # nix flake check
make helm # regenerate dist/chartThe container image is built by Nix rather than a Dockerfile:
nix build .#image # produces a script that streams the image to stdout
make kind-load # stream it straight into a kind clusterSee AGENTS.md for the architecture and the reasoning behind the less obvious choices.
Apache 2.0. See LICENSE.