A Kubernetes operator that automates Towonel self-hosted tunnels — provisioning tunnels, deploying agents, and connecting them, declaratively.
Warning
Agentically generated. This codebase was produced through agentic, spec-driven development: each feature began as a written design and implementation spec, then a coding agent executed the plan under human review. Tests, code review, and CI gates apply as they would for any project, but the authorship pattern is not a single human contributor — keep that in mind when evaluating fit for your environment.
Towonel exposes services behind NAT/CGNAT without inbound ports. This operator drives that from Kubernetes via two resources:
TowonelTunnel— the control plane. Reconciles a Towonel invite (token, authorized hostnames, TCP/UDP port reservations) against the Towonel API and owns the token Secret.TowonelAgent— a placement-specific workload that references a tunnel and renders the agent Deployment plus itshostname → originrouting. Multiple agents can share one tunnel for failover.
You can author those resources directly, or annotate a Service, Gateway, or
HTTPRoute and let the operator materialize them (the dogfooding path). Set
towonel.io/auto-routes: "true" on a Gateway to tunnel its same-namespace HTTPRoutes
without annotating each one — see Gateway auto-routes.
A Gateway can extend auto-routes to other namespaces with an opt-in towonel.io/auto-routes-namespaces allowlist (off by default) — note the security implications of public cross-namespace exposure.
See docs/architecture.md for the why behind the two-resource split.
Prerequisites: a Towonel account + a personal API key, and a Kubernetes
cluster. (Full prerequisites and chart values: docs/installation.md.)
1. Install the operator (Helm, OCI chart on GHCR):
kubectl create namespace towonel-system
kubectl create secret generic towonel-api -n towonel-system --from-literal=token='twk_xxx'
helm install towonel-operator \
oci://ghcr.io/jacaudi/charts/towonel-operator --version <version> \
--namespace towonel-system \
--set credentials.existingSecret=towonel-apiReplace <version> with the chart version you want — use the latest release (currently v1.0.1).
2. Create a tunnel (the control plane — provisions the Towonel invite + token):
apiVersion: towonel.io/v1alpha1
kind: TowonelTunnel
metadata:
name: app
namespace: towonel-system
spec:
region: EU
apiKeySecretRef:
name: towonel-api
key: token
deletionPolicy: Retain3. Expose a service — either annotate it:
apiVersion: v1
kind: Service
metadata:
name: grafana
namespace: monitoring
annotations:
towonel.io/tunnel: enable
towonel.io/tunnel-ref: towonel-system/app
towonel.io/hostname: grafana.example.com
spec:
selector:
app.kubernetes.io/name: grafana
ports:
- name: http
port: 3000
targetPort: 3000…or author a TowonelAgent directly for full control (HTTPS + raw TCP/UDP, failover,
direct-path connectivity) — see docs/examples/.
4. Point DNS. The operator does not manage DNS yet — read the tunnel's status
(authorizedHostnames, portAllocations[].edge.addresses) and create the records in your
DNS provider. See docs/dns.md.
kubectl get towoneltunnel app -n towonel-system -o yaml # inspect statusOperator-managed Towonel agent pods serve Prometheus metrics at /metrics on port 9090. Scraping is opt-in and off by default. Enable a single cluster-wide PodMonitor (selects all agent pods in every namespace) via the chart:
observability:
metrics:
agentPodMonitor:
enabled: true
# Most Prometheus installs only discover PodMonitors carrying a specific label:
additionalLabels:
release: kube-prometheus-stack- Requires the Prometheus Operator
PodMonitorCRD (monitoring.coreos.com/v1). With the toggle on,helm install/upgradefails on clusters without it. additionalLabelsis usually required. kube-prometheus-stack only selects PodMonitors carrying its release label; without it the PodMonitor is silently ignored — enabled but never scraped.- Namespace-scoped Prometheus. The PodMonitor selects pods in all namespaces (
namespaceSelector.any: true), but a Prometheus restricted bypodMonitorNamespaceSelectormust be allowed to read it and the agent namespaces. - Existing agents. The
metricsport reaches already-running agents only on their next config change; to cover them immediately, runkubectl rollout restart deploy -l app.kubernetes.io/name=towonel-agent.
Full docs live in docs/:
- Installation · Architecture
TowonelTunnel·TowonelAgentreferences- Source layer (annotations) · Direct-path connectivity · DNS
- Examples
- Bernd Schorgers (bjw-s) — this operator's Helm chart is built on
the bjw-s
app-templatecommon library, which does the heavy lifting of rendering the controller Deployment, RBAC, service account, and service. Thank you for an excellent, reusable chart! - Erwan Leboucher (eleboucher) — the creator of Towonel itself (codeberg · github). This operator only automates the client side; Towonel — the hub, edges, and agent — is his work. Thank you for creating a self-hosted Cloudflare Tunnel replacement!
MIT