Skip to content

About

Kubernetes operator that declaratively provisions Towonel self-hosted tunnels and agents to expose services behind NAT/CGNAT without inbound ports.

Topics

Resources

Contributing

Stars

8 stars

Watchers

0 watching

Forks

Repository files navigation

towonel-operator

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.

What it does

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 its hostname → origin routing. 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.

Quickstart

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-api

Replace <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: Retain

3. 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 status

Agent environment variables

To pass settings the operator does not model to the Towonel agent — log verbosity, for example — set agentEnv in the chart. The variables are added to every operator-managed agent pod, hand-authored TowonelAgents and auto-created default agents alike:

agentEnv:
  RUST_LOG: info,iroh::socket::transports=trace
  • Operator-wide, plain values. There is no per-agent override and no valueFrom; each entry is a name: value pair.
  • Changing it rolls every agent. All agent Deployments restart with the new environment.
  • Operator-managed names win. A name the operator sets itself (invite token, rendered services, relay URL, connectivity, health address) cannot be overridden: the entry is ignored and each affected agent gets an EnvIgnored Warning event.
  • Bad entries stop the operator. An invalid or duplicate name fails the manager at startup rather than being dropped silently.

Agent metrics (Prometheus)

Operator-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 PodMonitor CRD (monitoring.coreos.com/v1). With the toggle on, helm install/upgrade fails on clusters without it.
  • additionalLabels is 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 by podMonitorNamespaceSelector must be allowed to read it and the agent namespaces.
  • Existing agents. The metrics port reaches already-running agents only on their next config change; to cover them immediately, run kubectl rollout restart deploy -l app.kubernetes.io/name=towonel-agent.
  • Job label. Agent series carry job="towonel-agent" (the PodMonitor sets jobLabel: app.kubernetes.io/name). Before the dashboard was added they carried job="<release-namespace>/towonel-operator-agents"; update any alert or query that matched the old value.

Agent dashboard (Grafana)

The chart bundles the upstream Towonel agent dashboard (streams, errors, throughput, edge sessions, process stats), unmodified. It is opt-in and off by default, and charts the series the agent PodMonitor above scrapes, so enable both:

observability:
  metrics:
    agentPodMonitor:
      enabled: true
  dashboards:
    enabled: true
  • Grafana sidecar (default). Renders a ConfigMap labelled grafana_dashboard: "1", which the kube-prometheus-stack / Grafana chart sidecar loads automatically. If the sidecar only watches Grafana's own namespace, set observability.dashboards.namespace to it.
  • grafana-operator. Set observability.dashboards.grafanaOperator.enabled: true and grafanaOperator.matchLabels to your Grafana instance's labels. The chart then renders a GrafanaDashboard CR (grafana.integreatly.org/v1beta1) referencing the ConfigMap and drops the sidecar label. helm install/upgrade fails without that CRD, or if matchLabels is empty.
  • Scraping agents yourself. Without the bundled PodMonitor, give the agent series job="towonel-agent", or the process panels stay empty.

Documentation

Full docs live in docs/:

Acknowledgements

  • Bernd Schorgers (bjw-s) — this operator's Helm chart is built on the bjw-s app-template common 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!

License

MIT

About

Kubernetes operator that declaratively provisions Towonel self-hosted tunnels and agents to expose services behind NAT/CGNAT without inbound ports.

Topics

Resources

Contributing

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages