This guide will help you install the Cloudflare Operator and create your first tunnel.
- Kubernetes cluster v1.28+
kubectlconfigured with cluster access- Cloudflare account with Zero Trust enabled
- Cloudflare API Token
Choose one of the following installation methods:
Single command to install everything:
# All-in-one: CRDs + Namespace + RBAC + Operator
kubectl apply -f https://github.com/StringKe/cloudflare-operator/releases/latest/download/cloudflare-operator-full-no-webhook.yamlFor fine-grained control over installation:
# Step 1: Install CRDs (requires cluster-admin)
kubectl apply -f https://github.com/StringKe/cloudflare-operator/releases/latest/download/cloudflare-operator-crds.yaml
# Step 2: Create namespace
kubectl apply -f https://github.com/StringKe/cloudflare-operator/releases/latest/download/cloudflare-operator-namespace.yaml
# Step 3: Install operator (RBAC + Deployment)
kubectl apply -f https://github.com/StringKe/cloudflare-operator/releases/latest/download/cloudflare-operator-no-webhook.yaml| File | Contents | Use Case |
|---|---|---|
cloudflare-operator-full.yaml |
CRDs + Namespace + RBAC + Operator + Webhook | Full with cert-manager |
cloudflare-operator-full-no-webhook.yaml |
CRDs + Namespace + RBAC + Operator | Full without webhook |
cloudflare-operator-crds.yaml |
CRDs only | Modular installation |
cloudflare-operator-namespace.yaml |
Namespace only | Modular installation |
cloudflare-operator.yaml |
RBAC + Operator + Webhook | Upgrade existing installation |
cloudflare-operator-no-webhook.yaml |
RBAC + Operator | Upgrade without webhook |
# Check operator pod
kubectl get pods -n cloudflare-operator-system
# Check CRDs
kubectl get crds | grep cloudflareExpected output:
NAME CREATED AT
accessapplications.networking.cloudflare-operator.io 2024-01-01T00:00:00Z
accessgroups.networking.cloudflare-operator.io 2024-01-01T00:00:00Z
...
tunnels.networking.cloudflare-operator.io 2024-01-01T00:00:00Z
-
Create a Custom Token with these permissions:
Account:Cloudflare Tunnel:EditZone:DNS:Edit(for your domain)
-
Create the Kubernetes Secret:
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-credentials
namespace: default
type: Opaque
stringData:
CLOUDFLARE_API_TOKEN: "<your-api-token>"kubectl apply -f secret.yaml- Log in to Cloudflare Dashboard
- Select any domain
- Find Account ID in the right sidebar under "API"
apiVersion: networking.cloudflare-operator.io/v1alpha2
kind: Tunnel
metadata:
name: my-first-tunnel
namespace: default
spec:
newTunnel:
name: my-k8s-tunnel
cloudflare:
accountId: "<your-account-id>"
domain: example.com
secret: cloudflare-credentialskubectl apply -f tunnel.yaml# Check tunnel status
kubectl get tunnel my-first-tunnel
# Check cloudflared deployment
kubectl get deployment -l app.kubernetes.io/name=cloudflared
# Check cloudflared logs
kubectl logs -l app.kubernetes.io/name=cloudflaredDeploy a sample application:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world
spec:
replicas: 1
selector:
matchLabels:
app: hello-world
template:
metadata:
labels:
app: hello-world
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: hello-world
spec:
selector:
app: hello-world
ports:
- port: 80Create a TunnelBinding:
apiVersion: networking.cfargotunnel.com/v1alpha1
kind: TunnelBinding
metadata:
name: hello-world-binding
namespace: default
spec:
subjects:
- kind: Service
name: hello-world
spec:
fqdn: hello.example.com
protocol: http
tunnelRef:
kind: Tunnel
name: my-first-tunnelkubectl apply -f binding.yamlAfter a few moments, your application will be accessible at https://hello.example.com.
# Verify DNS record
dig hello.example.com
# Access the application
curl https://hello.example.comUse the deployPatch field to customize the cloudflared deployment. This is a JSON patch applied to the deployment spec.
Set replica count:
apiVersion: networking.cloudflare-operator.io/v1alpha2
kind: Tunnel
metadata:
name: my-tunnel
namespace: default
spec:
newTunnel:
name: my-k8s-tunnel
cloudflare:
accountId: "<your-account-id>"
domain: example.com
secret: cloudflare-credentials
deployPatch: '{"spec":{"replicas":3}}'Set resources and node selector:
apiVersion: networking.cloudflare-operator.io/v1alpha2
kind: Tunnel
metadata:
name: my-tunnel
namespace: default
spec:
newTunnel:
name: my-k8s-tunnel
cloudflare:
accountId: "<your-account-id>"
domain: example.com
secret: cloudflare-credentials
deployPatch: |
{
"spec": {
"replicas": 2,
"template": {
"spec": {
"nodeSelector": {
"node-role.kubernetes.io/edge": "true"
},
"containers": [{
"name": "cloudflared",
"resources": {
"requests": {"cpu": "100m", "memory": "128Mi"},
"limits": {"cpu": "500m", "memory": "512Mi"}
}
}]
}
}
}
}For cluster-wide tunnels (accessible from any namespace), use ClusterTunnel:
apiVersion: networking.cloudflare-operator.io/v1alpha2
kind: ClusterTunnel
metadata:
name: shared-tunnel
spec:
newTunnel:
name: shared-k8s-tunnel
cloudflare:
accountId: "<your-account-id>"
domain: example.com
secret: cloudflare-credentials # Must be in cloudflare-operator-system namespace
deployPatch: '{"spec":{"replicas":2}}'Note: For ClusterTunnel and other cluster-scoped resources, the secret must be in the
cloudflare-operator-systemnamespace.