Kubernetes
Kubernetes ↗ is a container orchestration tool that is used to deploy applications onto physical or virtual machines, scale the deployment to meet traffic demands, and push updates without downtime. The Kubernetes cluster, or environment, where the application instances are running is connected internally through a private network. You can install the cloudflared
daemon inside of the Kubernetes cluster in order to connect applications inside of the cluster to Cloudflare.
This guide will cover how to expose a Kubernetes service to the public Internet using a remotely-managed Cloudflare Tunnel. For the purposes of this example, we will deploy a basic web application alongside cloudflared
in Google Kubernetes Engine (GKE). The same principles apply to any other Kubernetes environment (such as minikube
, kubeadm
, or a cloud-based Kubernetes service) where cloudflared
can connect to Cloudflare's network.

As shown in the diagram, we recommend setting up cloudflared
as an adjacent deployment ↗ to the application deployments. Having a separate Kubernetes deployment for cloudflared
allows you to scale cloudflared
independently of the application. In the cloudflared
deployment, you can spin up multiple replicas running the same Cloudflare Tunnel -- there is no need to build a dedicated tunnel for each cloudflared
pod. Each cloudflared
replica / pod can reach all Kubernetes services in the cluster.
Once the cluster is connected to Cloudflare, you can configure Cloudflare Tunnel routes to control how cloudflared
will proxy traffic to services within the cluster. For example, you may wish to publish certain Kubernetes application to the Internet and restrict other applications to internal WARP client users.
To complete the following procedure, you will need:
To create a new Kubernetes cluster in Google Cloud:
- Open Google Cloud ↗ and go to Kubernetes Engine.
- In Clusters, select Create.
- Name the cluster. In this example, we will name it
cloudflare-tunnel
. - (Optional) Choose your desired region and other cluster specifications. For this example, we will use the default specifications.
- Select Create.
- To connect to the cluster:
- Select the three-dot menu.
- Select Connect.
- Select Run in Cloud Shell to open a terminal in the browser.
- Select Authorize.
- Press Enter to run the pre-populated
gcloud
command. - (Recommended) In the Cloud Shell menu, select Open Editor to launch the built-in IDE.
- In the Cloud Shell terminal, run the following command to check the cluster status:
Terminal window kubectl get allNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/kubernetes ClusterIP 34.118.224.1 <none> 443/TCP 15m
A pod represents an instance of a running process in the cluster. In this example, we will deploy the httpbin ↗ application with two pods and make the pods accessible inside the cluster at httpbin-service:80
.
-
Create a folder for your Kubernetes manifest files:
Terminal window mkdir tunnel-example -
Change into the directory:
Terminal window cd tunnel-example -
In the
tunnel-example
directory, create a new file calledhttpbin.yaml
. This file defines the Kubernetes deployment for the httpbin app.httpbin.yaml apiVersion: apps/v1kind: Deploymentmetadata:name: httpbin-deploymentnamespace: defaultspec:replicas: 2selector:matchLabels:app: httpbintemplate:metadata:labels:app: httpbinspec:containers:- name: httpbinimage: kennethreitz/httpbin:latestimagePullPolicy: IfNotPresentports:- containerPort: 80 -
Create a new
httpbinsvc.yaml
file. This file defines a Kubernetes service that allows other apps in the cluster (such ascloudflared
) to access the set of httpbin pods.httpbinsvc.yaml apiVersion: v1kind: Servicemetadata:name: httpbin-servicenamespace: defaultspec:type: LoadBalancerselector:app: httpbinports:- port: 80targetPort: 80 -
Use the following command to run the application inside the cluster:
Terminal window kubectl create -f httpbin.yaml -f httpbinsvc.yaml -
Check the status of your deployment:
Terminal window kubectl get allNAME READY STATUS RESTARTS AGEpod/httpbin-deployment-bc6689c5d-b5ftk 1/1 Running 0 79spod/httpbin-deployment-bc6689c5d-cbd9m 1/1 Running 0 79sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/httpbin-service LoadBalancer 34.118.225.147 34.75.201.60 80:31967/TCP 79sservice/kubernetes ClusterIP 34.118.224.1 <none> 443/TCP 24hNAME READY UP-TO-DATE AVAILABLE AGEdeployment.apps/httpbin-deployment 2/2 2 2 79sNAME DESIRED CURRENT READY AGEreplicaset.apps/httpbin-deployment-bc6689c5d 2 2 2 79s
To create a Cloudflare Tunnel:
-
Open a new browser tab and log in to Zero Trust ↗.
-
Go to Networks > Tunnels.
-
Select Create a tunnel.
-
Choose Cloudflared for the connector type and select Next.
-
Enter a name for your tunnel (for example,
gke-tunnel
). -
Select Save tunnel.
-
Under Choose an environment, select Docker.
Applications must be packaged into a containerized image before you can run it in Kubernetes. Therefore, we will use the
cloudflared
Docker container image to deploy the tunnel in Kubernetes. -
Instead of running the installation command, copy just the token value rather than the whole command. The token value is of the form
eyJhIjoiNWFiNGU5Z...
You will need the token for the Kubernetes manifest file.
Leave the Cloudflare Tunnel browser tab open while we focus on the Kubernetes deployment.
cloudflared
uses a tunnel token to run a remotely-managed Cloudflare Tunnel. You can store the tunnel token in a Kubernetes secret ↗, which requires data to be encoded as a base64-encoded string. The encoding is not meant to protect the token from being read but to allow for the safe handling of binary data within Kubernetes.
-
Convert the tunnel token into base64 format:
Terminal window 'eyJhIjoiNWFiNGU5Z...' | base64ZXlKa...NKOQo= -
In GKE Cloud Shell, create a
tunnel-token.yaml
file with the following content. Make sure to replace<base64_tunnel_token>
with your base64-encoded token value (ZXlKa...NKOQo=
).tunnel-token.yaml apiVersion: v1data:token: <base64_tunnel_token>kind: Secretmetadata:name: tunnel-tokennamespace: defaulttype: Opaque -
Create the secret:
Terminal window kubectl create -f tunnel-token.yaml -
Check the newly created secret:
Terminal window kubectl get secretsNAME TYPE DATA AGEtunnel-token Opaque 1 100s
To run the Cloudflare Tunnel in Kubernetes:
-
Create a Kubernetes deployment for a remotely-managed Cloudflare Tunnel:
tunnel.yaml apiVersion: apps/v1kind: Deploymentmetadata:name: cloudflared-deploymentnamespace: defaultspec:replicas: 2selector:matchLabels:pod: cloudflaredtemplate:metadata:labels:pod: cloudflaredspec:securityContext:sysctls:# Allows ICMP traffic (ping, traceroute) to resources behind cloudflared.- name: net.ipv4.ping_group_rangevalue: "65532 65532"containers:- image: cloudflare/cloudflared:latestname: cloudflaredenv:- name: token_valuevalueFrom:secretKeyRef:name: tunnel-tokenkey: tokencommand:# Parameters must be in the correct order relative to the "run" command.- cloudflared- tunnel- --no-autoupdate- --loglevel- debug- --metrics- 0.0.0.0:2000- runargs:- --token- $(token_value)livenessProbe:httpGet:# Cloudflared has a /ready endpoint which returns 200 if and only if# it has an active connection to Cloudflare's network.path: /readyport: 2000failureThreshold: 1initialDelaySeconds: 10periodSeconds: 10 -
Deploy
cloudflared
to the cluster:Terminal window kubectl create -f tunnel.yamlKubernetes will install the
cloudflared
image on two pods and run the tunnel using the commandcloudflared tunnel --no-autoupdate --loglevel debug --metrics 0.0.0.0:2000 run --token eyJhIjoiNWFiNGU5Z...
-
Check the status of your cluster:
Terminal window kubectl get allNAME READY STATUS RESTARTS AGEpod/cloudflared-deployment-6d5f9f9666-85l5w 1/1 Running 0 21spod/cloudflared-deployment-6d5f9f9666-wb96x 1/1 Running 0 21spod/httpbin-deployment-bc6689c5d-b5ftk 1/1 Running 0 3m36spod/httpbin-deployment-bc6689c5d-cbd9m 1/1 Running 0 3m36sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/httpbin-service LoadBalancer 34.118.225.147 34.75.201.60 80:31967/TCP 3m36sservice/kubernetes ClusterIP 34.118.224.1 <none> 443/TCP 24hNAME READY UP-TO-DATE AVAILABLE AGEdeployment.apps/cloudflared-deployment 2/2 2 2 22sdeployment.apps/httpbin-deployment 2/2 2 2 3m37sNAME DESIRED CURRENT READY AGEreplicaset.apps/cloudflared-deployment-6d5f9f9666 2 2 2 22sreplicaset.apps/httpbin-deployment-bc6689c5d 2 2 2 3m37s
You should see two cloudflared
pods and two httpbin
pods with a Running
status. If your cloudflared
pods keep restarting, make sure that your cloudflared
run parameters are in the correct order. In the example above, --loglevel
, --no-autoupdate
, and --metrics
should come before the run
command, while --token
comes after the run
command.
To print logs for a cloudflared
instance:
kubectl logs pod/cloudflared-deployment-6d5f9f9666-85l5w
2025-06-11T22:00:47Z INF Starting tunnel tunnelID=64c359b6-e111-40ec-a3a9-199c2a6566132025-06-11T22:00:47Z INF Version 2025.6.0 (Checksum 72f233bb55199093961bf099ad62d491db58819df34b071ab231f622deff33ce)2025-06-11T22:00:47Z INF GOOS: linux, GOVersion: go1.24.2, GoArch: amd642025-06-11T22:00:47Z INF Settings: map[loglevel:debug metrics:0.0.0.0:2000 no-autoupdate:true token:*****]2025-06-11T22:00:47Z INF Generated Connector ID: aff7c4a0-85a3-4ac9-8475-1e0aa1af8d942025-06-11T22:00:47Z DBG Fetched protocol: quic2025-06-11T22:00:47Z INF Initial protocol quic...
Now that the tunnel is up and running, we can use the Zero Trust dashboard to route the httpbin service through the tunnel.
-
Switch to the browser tab where you were configuring Cloudflare Tunnel.
-
Go to the Route tunnel step.
-
In the Public hostnames tab, enter a hostname for the application (for example,
httpbin.<your-domain>.com
). -
Under Service, enter
http://httpbin-service
.httpbin-service
is the name of the Kubernetes service defined inhttpbinsvc.yaml
. -
Select Complete setup.
To test, open a new browser tab and go to httpbin.<your-domain>.com
. You should see the httpbin homepage.
You can optionally create an Access application to control who can access the service.
Was this helpful?
- Resources
- API
- New to Cloudflare?
- Products
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- 2025 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark