---
title: Kubernetes
description: Kubernetes in Zero Trust networking.
image: https://edgetunnel-b2h.pages.dev/zt-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://edgetunnel-b2h.pages.dev/cloudflare-one/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Kubernetes

[Kubernetes ↗](https://kubernetes.io/) 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.

Tip

If your server is behind a restrictive firewall, verify it can reach Cloudflare on port `7844` before proceeding. Refer to [Connectivity pre-checks](https://edgetunnel-b2h.pages.dev/cloudflare-one/networks/connectors/cloudflare-tunnel/troubleshoot-tunnels/connectivity-prechecks/).

Locally-managed tunnels

If you are looking to set up a locally-managed tunnel in Kubernetes, refer to the [example code in GitHub ↗](https://github.com/cloudflare/argo-tunnel-examples/tree/master/named-tunnel-k8s).

## Architecture

![Diagram showing how a user connects to Kubernetes services through Cloudflare Tunnel](https://edgetunnel-b2h.pages.dev/_astro/kubernetes-tunnel.C8IQcJlu_h8gOW.webp) 

As shown in the diagram, we recommend setting up `cloudflared` as an adjacent [deployment ↗](https://kubernetes.io/docs/concepts/workloads/controllers/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](https://edgetunnel-b2h.pages.dev/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/) 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.

Note

We do not recommend using `cloudflared` in autoscaling setups because downscaling (removing replicas) will break existing user connections to that replica. Additionally, `cloudflared` does not load balance across replicas; replicas are strictly for high availability. To load balance traffic to your nodes, you can use [Cloudflare Load Balancer](https://edgetunnel-b2h.pages.dev/load-balancing/private-network/) or a third-party load balancer.

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 applications to the Internet and restrict other applications to internal Cloudflare One Client users.

## Prerequisites

To complete the following procedure, you will need:

* [A Google Cloud Project ↗](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating%5Fa%5Fproject)
* [A zone on Cloudflare](https://edgetunnel-b2h.pages.dev/fundamentals/manage-domains/add-site/)

## 1\. Create a GKE cluster

To create a new Kubernetes cluster in Google Cloud:

1. Open [Google Cloud ↗](https://console.cloud.google.com/) and go to **Kubernetes Engine**.
2. In **Clusters**, select **Create**.
3. Name the cluster. In this example, we will name it `cloudflare-tunnel`.
4. (Optional) Choose your desired region and other cluster specifications. For this example, we will use the default specifications.
5. Select **Create**.
6. To connect to the cluster:

  1. Select the three-dot menu.
  2. Select **Connect**.
  3. Select **Run in Cloud Shell** to open a terminal in the browser.
  4. Select **Authorize**.
  5. Press `Enter` to run the pre-populated `gcloud` command.
  6. (Recommended) In the Cloud Shell menu, select **Open Editor** to launch the built-in IDE.
7. In the Cloud Shell terminal, run the following command to check the cluster status:  
```sh  
kubectl get all  
```  
```sh  
NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE  
service/kubernetes   ClusterIP   34.118.224.1   <none>        443/TCP   15m  
```

## 2\. Create pods for the web app

A pod represents an instance of a running process in the cluster. In this example, we will deploy the [httpbin ↗](https://httpbin.org/) application with two pods and make the pods accessible inside the cluster at `httpbin-service:80`.

1. Create a folder for your Kubernetes manifest files:  
```sh  
mkdir tunnel-example  
```
2. Change into the directory:  
```sh  
cd tunnel-example  
```
3. In the `tunnel-example` directory, create a new file called `httpbin.yaml`. This file defines the Kubernetes deployment for the httpbin app.

**httpbin.yaml**  
```yaml  
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  name: httpbin-deployment  
  namespace: default  
spec:  
  replicas: 2  
  selector:  
    matchLabels:  
      app: httpbin  
  template:  
    metadata:  
      labels:  
        app: httpbin  
    spec:  
      containers:
        - name: httpbin  
          image: kennethreitz/httpbin:latest  
          imagePullPolicy: IfNotPresent  
          ports:
            - containerPort: 80  
```
4. Create a new `httpbinsvc.yaml` file. This file defines a Kubernetes service that allows other apps in the cluster (such as `cloudflared`) to access the set of httpbin pods.

**httpbinsvc.yaml**  
```yaml  
apiVersion: v1  
kind: Service  
metadata:  
  name: httpbin-service  
  namespace: default  
spec:  
  type: LoadBalancer  
  selector:  
    app: httpbin  
  ports:
    - port: 80  
      targetPort: 80  
```
5. Use the following command to run the application inside the cluster:  
```sh  
kubectl create -f httpbin.yaml -f httpbinsvc.yaml  
```
6. Check the status of your deployment:  
```sh  
kubectl get all  
```  
```sh  
NAME                                     READY   STATUS    RESTARTS   AGE  
pod/httpbin-deployment-bc6689c5d-b5ftk   1/1     Running   0          79s  
pod/httpbin-deployment-bc6689c5d-cbd9m   1/1     Running   0          79s  
NAME                      TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)        AGE  
service/httpbin-service   LoadBalancer   34.118.225.147   34.75.201.60   80:31967/TCP   79s  
service/kubernetes        ClusterIP      34.118.224.1     <none>         443/TCP        24h  
NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE  
deployment.apps/httpbin-deployment   2/2     2            2           79s  
NAME                                           DESIRED   CURRENT   READY   AGE  
replicaset.apps/httpbin-deployment-bc6689c5d   2         2         2       79s  
```

## 3\. Create a tunnel

To create a Cloudflare Tunnel:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Networking** \> **Tunnels**.
2. Select **Create a tunnel**.
3. Enter a name for your tunnel (for example, `gke-tunnel`).
4. Select **Create Tunnel**.
5. Choose your operating system and 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.
6. 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.

## 4\. Store the tunnel token

`cloudflared` uses a tunnel token to run a remotely-managed Cloudflare Tunnel. You can store the tunnel token in a [Kubernetes secret ↗](https://kubernetes.io/docs/concepts/configuration/secret/).

1. In GKE Cloud Shell, create a `tunnel-token.yaml` file with the following content. Make sure to replace `<YOUR_TUNNEL_TOKEN>` with your tunnel token (`eyJhIjoiNWFiNGU5Z...`).

**tunnel-token.yaml**  
```yaml  
apiVersion: v1  
kind: Secret  
metadata:  
  name: tunnel-token  
stringData:  
  token: <YOUR_TUNNEL_TOKEN>  
```
2. Create the secret:  
```sh  
kubectl create -f tunnel-token.yaml  
```
3. Check the newly created secret:  
```sh  
kubectl get secrets  
```  
```sh  
NAME        TYPE     DATA   AGE  
tunnel-token   Opaque   1      100s  
```

## 5\. Create pods for cloudflared

To run the Cloudflare Tunnel in Kubernetes:

1. Create a Kubernetes deployment for a remotely-managed Cloudflare Tunnel:

**tunnel.yaml**  
```yaml  
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  name: cloudflared-deployment  
  namespace: default  
spec:  
  replicas: 2  
  selector:  
    matchLabels:  
      pod: cloudflared  
  template:  
    metadata:  
      labels:  
        pod: cloudflared  
    spec:  
      securityContext:  
        sysctls:  
          # Allows ICMP traffic (ping, traceroute) to resources behind cloudflared.
          - name: net.ipv4.ping_group_range  
            value: "65532 65532"  
      containers:
        - image: cloudflare/cloudflared:latest  
          name: cloudflared  
          env:  
            # Defines an environment variable for the tunnel token.
            - name: TUNNEL_TOKEN  
              valueFrom:  
                secretKeyRef:  
                  name: tunnel-token  
                  key: token  
          command:  
            # Configures tunnel run parameters
            - cloudflared
            - tunnel
            - --no-autoupdate
            - --loglevel
            - info
            - --metrics
            - 0.0.0.0:2000
            - run  
          livenessProbe:  
            httpGet:  
              # Cloudflared has a /ready endpoint which returns 200 if and only if  
              # it has an active connection to Cloudflare's network.  
              path: /ready  
              port: 2000  
            failureThreshold: 1  
            initialDelaySeconds: 10  
            periodSeconds: 10  
```
2. Deploy `cloudflared` to the cluster:  
```sh  
kubectl create -f tunnel.yaml  
```  
Kubernetes will install the `cloudflared` image on two pods and run the tunnel using the command `cloudflared tunnel --no-autoupdate --loglevel info --metrics 0.0.0.0:2000 run`. `cloudflared` will consume the tunnel token from the `TUNNEL_TOKEN` environment variable.
3. Check the status of your cluster:  
```sh  
kubectl get all  
```  
```sh  
NAME                                          READY   STATUS    RESTARTS   AGE  
pod/cloudflared-deployment-6d5f9f9666-85l5w   1/1     Running   0          21s  
pod/cloudflared-deployment-6d5f9f9666-wb96x   1/1     Running   0          21s  
pod/httpbin-deployment-bc6689c5d-b5ftk        1/1     Running   0          3m36s  
pod/httpbin-deployment-bc6689c5d-cbd9m        1/1     Running   0          3m36s  
NAME                      TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)        AGE  
service/httpbin-service   LoadBalancer   34.118.225.147   34.75.201.60   80:31967/TCP   3m36s  
service/kubernetes        ClusterIP      34.118.224.1     <none>         443/TCP        24h  
NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE  
deployment.apps/cloudflared-deployment   2/2     2            2           22s  
deployment.apps/httpbin-deployment       2/2     2            2           3m37s  
NAME                                                DESIRED   CURRENT   READY   AGE  
replicaset.apps/cloudflared-deployment-6d5f9f9666   2         2         2       22s  
replicaset.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, check the `command` syntax in `tunnel.yaml` and make sure that the [tunnel run parameters](https://edgetunnel-b2h.pages.dev/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/) are in the correct order.

## 6\. Verify tunnel status

To print logs for a `cloudflared` instance:

```sh
kubectl logs pod/cloudflared-deployment-6d5f9f9666-85l5w
```

```sh
2025-06-11T22:00:47Z INF Starting tunnel tunnelID=64c359b6-e111-40ec-a3a9-199c2a656613
2025-06-11T22:00:47Z INF Version 2025.6.0 (Checksum 72f233bb55199093961bf099ad62d491db58819df34b071ab231f622deff33ce)
2025-06-11T22:00:47Z INF GOOS: linux, GOVersion: go1.24.2, GoArch: amd64
2025-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-1e0aa1af8d94
2025-06-11T22:00:47Z DBG Fetched protocol: quic
2025-06-11T22:00:47Z INF Initial protocol quic
...
```

## 7\. Add a tunnel route

Now that the tunnel is up and running, we can route the httpbin service through the tunnel.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Networking** \> **Tunnels** and select your tunnel.
2. On the **Routes** tab, select **Add route** \> **Published application**.
3. Enter a hostname for the application (for example, `httpbin.<your-domain>.com`).
4. Under **Service**, enter `http://httpbin-service`. `httpbin-service` is the name of the Kubernetes service defined in `httpbinsvc.yaml`.
5. Select **Add route**.

## 8\. Test the connection

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](https://edgetunnel-b2h.pages.dev/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) to control who can access the service.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://edgetunnel-b2h.pages.dev/cloudflare-one/networks/connectors/cloudflare-tunnel/deployment-guides/kubernetes/#page","headline":"Kubernetes · Cloudflare One docs","description":"Kubernetes in Zero Trust networking.","url":"https://edgetunnel-b2h.pages.dev/cloudflare-one/networks/connectors/cloudflare-tunnel/deployment-guides/kubernetes/","inLanguage":"en","image":"https://edgetunnel-b2h.pages.dev/zt-preview.png","dateModified":"2026-04-17","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://edgetunnel-b2h.pages.dev/#website","name":"Cloudflare Docs","url":"https://edgetunnel-b2h.pages.dev/"},"keywords":["Kubernetes"]}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/cloudflare-one/","name":"Cloudflare One"}},{"@type":"ListItem","position":3,"item":{"@id":"/cloudflare-one/networks/","name":"Networks"}},{"@type":"ListItem","position":4,"item":{"@id":"/cloudflare-one/networks/connectors/","name":"Connectors"}},{"@type":"ListItem","position":5,"item":{"@id":"/cloudflare-one/networks/connectors/cloudflare-tunnel/","name":"Cloudflare Tunnel"}},{"@type":"ListItem","position":6,"item":{"@id":"/cloudflare-one/networks/connectors/cloudflare-tunnel/deployment-guides/","name":"Environments"}},{"@type":"ListItem","position":7,"item":{"@id":"/cloudflare-one/networks/connectors/cloudflare-tunnel/deployment-guides/kubernetes/","name":"Kubernetes"}}]}
```
