Skip to content
NebulaCtrldocs
Concepts

Networking

How traffic moves in NebulaCtrl. Internal service hostnames, environment isolation, domains and certificates, Tailscale exposure, edge clusters and the WireGuard mesh.

Traffic reaches a service from three directions: other services in the same environment, people on the internet or your tailnet, and services in other environments or clusters. NebulaCtrl gives each its own mechanism, and denies everything you haven't allowed.

Internet ──► Ingress (Traefik) ──► svc-web ──► active release of web
                                      ▲
Same environment ── svc-api:8080 ─────┘
Other environment ── only with a mesh grant
Tailnet ──► Tailscale ingress ──► svc-web
Cluster A ◄══ WireGuard mesh ══► Cluster B

Internal hostnames

Each service has a stable in-cluster hostname, svc-SLUG, where SLUG is the service's slug. It points at whichever release is active, so a deployment switches traffic without changing the address. Another service in the same environment reaches it at svc-SLUG:PORT. A name too long for Kubernetes is shortened with a hash suffix.

You rarely write the hostname yourself. The variable function ${{ host(SLUG) }} expands to it, and a database's connection variables, such as DATABASE_URL, already contain it. A Postgres database with readers also has svc-SLUG-read for read-only traffic. See Variables.

An http process is reachable internally on its port and publicly through a domain. A worker process that declares a TCP port is reachable internally only. It's never routable from the internet.

Environment isolation

Every environment runs in its own namespace, with a default-deny network policy. Traffic is allowed only within the same namespace, to DNS, from the cluster's ingress, from the agent for health checks and metrics, and out to public internet addresses. Egress excludes the private ranges 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 and 169.254.0.0/16. Services in different environments can't talk to each other, even in the same project.

A mesh grant opens one path. An admin allows every workload of one environment to reach one process of a service in another environment, on any cluster of the organization. You set it on the target service in Network access with Allow access from environment…. Nothing else crosses an environment boundary.

Domains and certificates

A domain is a hostname routed to one service and process through the cluster's ingress, which is Traefik. You add one with Add domain and choose:

ChoiceOptions
ExposurePublic, reachable on the internet at the hostname. Tailnet, reachable only from your tailnet.
Serve throughThis environment's cluster, or a public edge cluster. Public domains only.
DNSPublic domains only. Manual: you create the records at your DNS provider. Cloudflare: NebulaCtrl creates and maintains the records in your connected Cloudflare zone, optionally with Proxy through Cloudflare.
CertificatePublic domains only. HTTP-01: Traefik in the cluster answers Let's Encrypt on port 80. DNS-01: NebulaCtrl answers Let's Encrypt through Cloudflare DNS, which works when the cluster isn't reachable on port 80. HTTP-01 is the default, except that a Cloudflare-managed domain defaults to DNS-01 when the cluster supports it.

A certificate always lives on the cluster that runs the service. See Domains for the steps.

A cluster is reachable from the internet when one of its nodes has a public IPv4 address. A cluster behind NAT still runs and serves services internally. To give it public domains, add a public gateway to it: a small node with a public IPv4 address that alone serves ports 80 and 443 for the cluster, forwards over the mesh, and runs no workloads. Or serve its domains through an edge cluster.

Edge clusters

An edge cluster is a publicly reachable cluster of your organization that serves a domain whose service runs on another cluster. You choose it as Serve through when you add the domain. TLS passes through the edge untouched, so the certificate stays on the service's own cluster, and the domain must use DNS-01. The edge relays over the mesh to a mesh-only port on the home cluster's ingress. See Edge clusters.

Tailscale

A domain with Tailnet exposure is reachable only from devices on your tailnet. Tailscale supplies the hostname, under your tailnet's ts.net name, and the certificate. The cluster must be connected to your tailnet first, from its settings in Clusters. The Tailscale operator puts one device on your tailnet for each tailnet domain and removes it when the domain is deleted. If the device is still there 10 minutes after no domain reports it, NebulaCtrl removes it, but only a device it recorded for one of your tailnet domains that still carries the operator's tag:k8s. A proxy process can also forward to a device on your tailnet. See Expose a service through Tailscale.

The WireGuard mesh

The mesh is your organization's own WireGuard network. Every node of every cluster in the organization joins it before K3s starts, and all node-to-node traffic runs inside it: K3s, kubelet, etcd and pod traffic. That is why a cluster behind NAT can grow across sites, and why one organization's clusters can reach each other privately.

  • Addresses. Each machine gets one address in 10.254.0.0/16. Each cluster gets a fixed slot in one address plan: a /16 for its pods in 10.192.0.0/12 and one for its services in 10.208.0.0/12. Ranges never overlap between clusters, so traffic needs no address translation. An organization has at most 16 clusters.
  • Port. The mesh listens on UDP port 51821. K3s's own WireGuard backend keeps 51820 inside it.
  • Paths. Nodes that can reach each other connect directly. The oldest live public node, the hub, relays for the rest. If the hub stops fetching its configuration for 90 seconds, the next public node takes over.
  • Peers. A mesh peer is one machine admitted by exactly one install grant, with an address and a public key that are never reused. To replace a machine, remove it from the mesh first: Clusters > Nodes > Remove from mesh.
  • Older clusters. A cluster installed before the mesh existed can't take new machines. Create a new cluster, move its environments there and remove the old one.

On this page