> Markdown version of https://tuna.am/en/blog/kubernetes-operator/. Documentation index: [llms.txt](https://tuna.am/en/llms.txt), full text in one file: [llms-full.txt](https://tuna.am/en/llms-full.txt). Any page is available as Markdown: append `.md` to its URL without the trailing slash.

[Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features)

# Tuna Kubernetes operator — tunnels, domains, and Ingress as cluster resources

November 6 · 5 min

To expose a service from a cluster, you typically need a LoadBalancer, a public IP, and an ingress controller. In the cloud you pay for this, and many clusters don't have a public address at all: an air-gapped environment, kind on a laptop, k3s on a Raspberry Pi in a homelab.

For such clusters, we built a Kubernetes operator. Install it with a Helm chart, write a regular Ingress with the `tuna` class — and the service is accessible from the internet. The cluster only makes outbound connections to Tuna; no inbound ports need to be open.

![Animation: the operator in the cluster opens an outbound tunnel to a Tuna node; a request to my-app.a.2na.am/api comes through the tunnel and goes to Service api, a request to the root goes to Service web](/blog/img/2026-11-06-kubernetes-operator/how.svg)

The cluster opens a tunnel to Tuna on its own, and the operator routes requests by Ingress paths.

> **In brief**
>
> * **A real Ingress controller.** Any Helm chart that can create an Ingress is published through Tuna with a single line in values.
> * **Domains and ports as manifests.** Subdomains, custom domains, and TCP ports are reserved as resources alongside the application and live in git.
> * Installation, key setup, and all resources — in the [documentation](https://tuna.am/en/docs/tunnels/kubernetes-operator.md).

## Before: a pod with `tuna http`

Publishing a cluster through Tuna was possible before: run [`tuna http` in a pod](https://tuna.am/en/docs/tunnels/examples/kubernetes.md). One of our users used this to expose ingress-nginx:

```yaml
spec:
  replicas: 1
  strategy: {type: Recreate}
  template:
    spec:
      containers:
      - name: tuna
        image: yuccastream/tuna:latest
        args: [http, ingress-nginx-controller.ingress-nginx.svc.cluster.local:80, --inspect=false]
        env:
        - {name: TUNA_TOKEN,     valueFrom: {secretKeyRef: {name: app-tuna, key: TUNA_TOKEN}}}
        - {name: TUNA_SUBDOMAIN, valueFrom: {secretKeyRef: {name: app-tuna, key: TUNA_SUBDOMAIN}}}

```

It works, but everything is assembled by hand: the subdomain is reserved in the dashboard, the Secret is created manually, and a separate ingress-nginx sits in front of the tunnel. And `TUNA_TOKEN` is the entire account's token: if it leaks from the Secret, it can be used to start as many tunnels as you want on any address.

## Now: a regular Ingress

With the operator, Tuna is just another ingress controller in the cluster. The chart doesn't need to know anything about Tuna — just specify the class. Here's [podinfo](https://github.com/stefanprodan/podinfo) on a reserved subdomain:

values.yaml

```yaml
ingress:
  enabled: true
  className: tuna
  hosts:
    - host: podinfo.a.2na.am
      paths:
        - path: /
          pathType: Prefix

```

Paths within a single Ingress are routed to different Services: `/api` to the backend, `/` to the frontend. The operator creates one tunnel per host and applies new paths and backends without reconnecting. If the chart doesn't support setting a class, `tuna` can be made the default class.

The subdomain itself is also described as a manifest — a `Domain` resource alongside the application. Custom domains and TCP ports are reserved the same way, and for a standalone service there's a short `Tunnel` resource without Ingress.

## A key for every tunnel

The main thing we designed into the operator is that leaking one secret should not compromise the entire account.

* **Each tunnel has its own ephemeral key.** It can only start a tunnel: one protocol, no more than one tunnel at a time, no API access. Delete the resource — the key is revoked. Keys for resources deleted while the operator was down are revoked automatically.
* **The operator's key cannot start tunnels.** It only needs write access to "Endpoints and domains": reserving addresses and issuing keys for tunnels.
* **Own namespace only.** Tunnel and Ingress can only publish Services from their own namespace. IP addresses, external hosts, and other namespaces are rejected — otherwise the right to create an Ingress in one namespace would grant access to everything the operator pod can see.
* **Pods without extra privileges.** Non-privileged user, read-only root filesystem, no capabilities.

## GitOps-friendly

If the cluster is managed by Argo CD or Flux, the operator stays out of the way:

* a domain or port that already exists in the account is adopted under management rather than failing with an error;
* with `reclaimPolicy: Retain`, the reservation survives resource deletion — rebuild the cluster, and the domain is still there;
* a reservation deleted manually in the dashboard is recreated by the operator.

## How to try it

Create an [API key](https://my.tuna.am/api_keys?lang=en) for the operator and install the chart:

```shell
helm repo add tuna https://code.tuna.am/api/packages/tuna/helm
helm install tuna-operator tuna/tuna-k8s-operator --namespace tuna-system --create-namespace --set credentials.apiKey=<API-key>

```

Which permissions the key needs, how to install the operator via Nelm, and where to store the key besides `--set` — in the [documentation](https://tuna.am/en/docs/tunnels/kubernetes-operator.md).

## What's next

This is the first version: API `v1alpha1`, resource fields may still change. Traffic currently flows through the operator pod, and tunnels reconnect during updates.

Coming soon:

* **Tunnels directly from Service.** A Service of type LoadBalancer with `loadBalancerClass: tuna` will get a tunnel on its own, without Ingress and without a `Tunnel` resource.
* **Secret synchronization.** The operator will sync [Tuna secrets](https://tuna.am/en/docs/secrets.md) into cluster Secrets and keep them updated on changes.
* **Routing on Tuna nodes.** Ingress rules will move to our nodes, traffic will stop flowing through the operator pod, and Gateway API support will follow.

Missing a feature or the operator didn't work? Contact [support](https://tuna.am/en/docs/support.md).

## Leave feedback

If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en).

![](/blog/img/2024-08-22/feedback.gif)

## News

We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything.

## Help

We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways:

* [Email](mailto:info@tuna.am)
* [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy)

Categories: [New features](https://tuna.am/en/blog/tags/features)

[older →🕸️ Tuna Mesh in beta — a private network for your devices](https://tuna.am/en/blog/tuna-mesh-beta.md)
