Cloudflare Tunnel (cloudflared)
Secure, outbound-only tunnel between your Kubernetes cluster and Cloudflare’s global network. cloudflared connects to Cloudflare from inside the cluster — no open inbound ports, no public IP required. Traffic routing is managed in the Cloudflare dashboard via Public Hostnames, and each replica opens an independent connection for high availability.
When using Cloudflare Tunnel, traffic routing is configured in the Cloudflare dashboard under Networks → Tunnels →
Public Hostnames — not via Kubernetes Ingress resources. Adding a Kubernetes Ingress for the same service duplicates
routing and is usually unnecessary. Services are referenced by their cluster-internal DNS (e.g.
http://my-service.namespace.svc.cluster.local:80).
Key Features
- Zero-trust networking — outbound-only connections, no inbound firewall rules needed
- HA by default — 2 replicas, each with an independent Cloudflare edge connection
- PodDisruptionBudget — built-in disruption protection enabled by default
- Dashboard-managed routing — Public Hostnames configured in the Cloudflare dashboard
- Prometheus metrics —
/readyand/metricsendpoint on port 2000, enabled by default - ServiceMonitor — optional Prometheus Operator integration
- Existing Secret support — bring your own Secret for the tunnel token
Quick Start
- Go to Cloudflare Zero Trust dashboard → Networks → Tunnels
- Create a new tunnel and copy the token
- Deploy this chart with the token
helm install cloudflared helmforge/cloudflared \
--set tunnel.token='eyJhIjoiY2Y...'
- In the dashboard, add Public Hostnames mapping your domain to services inside the cluster
(e.g.
https://app.example.com → http://myapp.default.svc.cluster.local:80)
Installation
HTTPS repository:
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install cloudflared helmforge/cloudflared -f values.yaml
OCI registry:
helm install cloudflared oci://ghcr.io/helmforgedev/helm/cloudflared -f values.yaml
Deployment Examples
# values.yaml — cloudflared with inline token (not recommended for production)
# Store the token in an existing Secret for production environments.
tunnel:
token: 'eyJhIjoiY2Y...' # From Cloudflare dashboard
replicaCount: 2
pdb:
enabled: true
minAvailable: 1# values.yaml — Production cloudflared with secret-backed token and Prometheus metrics
# Create the secret first: kubectl create secret generic cloudflare-tunnel --from-literal=token='eyJ...'
tunnel:
existingSecret: cloudflare-tunnel
existingSecretKey: token
replicaCount: 2
pdb:
enabled: true
minAvailable: 1
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 128Mi
metrics:
enabled: true
serviceMonitor:
enabled: true
interval: 30s
labels:
prometheus: kube-prometheus# values.yaml — Single replica for dev or resource-constrained environments
tunnel:
existingSecret: cloudflare-tunnel
existingSecretKey: token
replicaCount: 1
pdb:
enabled: false # PDB with minAvailable:1 would block all maintenance on 1-replica deploymentsConfiguration Reference
Core
| Parameter | Type | Default | Description |
|---|---|---|---|
nameOverride | string | "" | Override the chart name. |
fullnameOverride | string | "" | Override the full release name. |
commonLabels | object | {} | Extra labels added to all resources. |
Image
| Parameter | Type | Default | Description |
|---|---|---|---|
image.repository | string | docker.io/cloudflare/cloudflared | cloudflared container image. |
image.tag | string | "2026.3.0" | Image tag. |
image.pullPolicy | string | IfNotPresent | Image pull policy. |
imagePullSecrets | array | [] | Pull secrets for private registries. |
Tunnel
| Parameter | Type | Default | Description |
|---|---|---|---|
tunnel.token | string | "" | Tunnel token from the Cloudflare dashboard. Prefer existingSecret in production. |
tunnel.existingSecret | string | "" | Existing Kubernetes Secret containing the tunnel token. |
tunnel.existingSecretKey | string | token | Key inside the existing Secret for the token value. |
Setting tunnel.token inline exposes the token in helm get values and Helm release history. Use
tunnel.existingSecret with a pre-created Kubernetes Secret. The token grants full control over the tunnel and cannot
be rotated without updating the Cloudflare dashboard.
cloudflared Options
| Parameter | Type | Default | Description |
|---|---|---|---|
cloudflared.logLevel | string | info | Log verbosity: info, debug, warn, error, or fatal. |
cloudflared.noAutoupdate | boolean | true | Disable in-process auto-update. Always true in containers. |
cloudflared.metricsPort | integer | 2000 | Port serving /ready and /metrics. |
cloudflared.extraArgs | array | [] | Extra command-line arguments appended to the cloudflared command. |
cloudflared.extraEnv | array | [] | Extra environment variables for the container. |
Replicas and Availability
| Parameter | Type | Default | Description |
|---|---|---|---|
replicaCount | integer | 2 | Number of cloudflared replicas. Each replica opens an independent tunnel connection. |
Horizontal Pod Autoscaling that scales down replicas terminates active tunnel connections immediately. Clients
connected through those tunnels will experience dropped connections. Use a fixed replicaCount of 2 or more instead
of autoscaling.
PodDisruptionBudget
| Parameter | Type | Default | Description |
|---|---|---|---|
pdb.enabled | boolean | true | Create a PodDisruptionBudget for cloudflared pods. |
pdb.minAvailable | integer | 1 | Minimum available replicas during voluntary cluster disruptions. |
Service
| Parameter | Type | Default | Description |
|---|---|---|---|
service.type | string | ClusterIP | Kubernetes service type. |
service.port | integer | 2000 | Metrics service port. |
service.annotations | object | {} | Annotations for the Service. |
Metrics
| Parameter | Type | Default | Description |
|---|---|---|---|
metrics.enabled | boolean | true | Expose the metrics Service for /ready and /metrics. |
serviceMonitor.enabled | boolean | false | Create a Prometheus Operator ServiceMonitor. |
serviceMonitor.interval | string | 30s | Metrics scrape interval. |
serviceMonitor.labels | object | {} | Extra labels added to the ServiceMonitor. |
Probes
| Parameter | Type | Default | Description |
|---|---|---|---|
probes.startup.enabled | boolean | true | Enable startup probe on /ready. |
probes.startup.initialDelaySeconds | integer | 5 | Startup probe initial delay. |
probes.startup.periodSeconds | integer | 5 | Startup probe period. |
probes.startup.timeoutSeconds | integer | 3 | Startup probe timeout. |
probes.startup.failureThreshold | integer | 12 | Startup probe failure threshold. |
probes.liveness.enabled | boolean | true | Enable liveness probe on /ready. |
probes.liveness.initialDelaySeconds | integer | 0 | Liveness probe initial delay. |
probes.liveness.periodSeconds | integer | 15 | Liveness probe period. |
probes.liveness.timeoutSeconds | integer | 5 | Liveness probe timeout. |
probes.liveness.failureThreshold | integer | 3 | Liveness probe failure threshold. |
probes.readiness.enabled | boolean | true | Enable readiness probe on /ready. |
probes.readiness.initialDelaySeconds | integer | 0 | Readiness probe initial delay. |
probes.readiness.periodSeconds | integer | 10 | Readiness probe period. |
probes.readiness.timeoutSeconds | integer | 5 | Readiness probe timeout. |
probes.readiness.failureThreshold | integer | 3 | Readiness probe failure threshold. |
Resources and Security
| Parameter | Type | Default | Description |
|---|---|---|---|
resources | object | {} | CPU and memory requests and limits. |
podSecurityContext | object | {} | Pod-level security context. |
securityContext | object | {} | Container-level security context. |
Service Account
| Parameter | Type | Default | Description |
|---|---|---|---|
serviceAccount.create | boolean | false | Create a dedicated ServiceAccount. |
serviceAccount.name | string | "" | Override the ServiceAccount name. |
serviceAccount.annotations | object | {} | Annotations for the ServiceAccount. |
Scheduling
| Parameter | Type | Default | Description |
|---|---|---|---|
nodeSelector | object | {} | Node selector for scheduling. |
tolerations | array | [] | Tolerations for scheduling. |
affinity | object | {} | Affinity rules. |
topologySpreadConstraints | array | [] | Topology spread constraints. |
priorityClassName | string | "" | PriorityClass for the pod. |
terminationGracePeriodSeconds | integer | 30 | Termination grace period. |
podLabels | object | {} | Extra labels for the pod. |
podAnnotations | object | {} | Extra annotations for the pod. |
Extra
| Parameter | Type | Default | Description |
|---|---|---|---|
extraVolumes | array | [] | Extra volumes to attach to the pod. |
extraVolumeMounts | array | [] | Extra volume mounts for the container. |
extraManifests | array | [] | Extra Kubernetes manifests deployed alongside the chart. |