Skip to content
NebulaCtrldocs

Requirements

The operating system, software, database, ports, DNS and outbound access that the NebulaCtrl control plane and its cluster nodes need.

This page lists what the control plane host and the nodes of your clusters need. The installer and the cluster install script check most of it before they change anything.

Control plane host

ItemRequirement
Operating systemLinux. The installer stops on any other system.
CPU architectureamd64 (x86_64) or arm64 (aarch64). Images are published for linux/amd64 and linux/arm64.
Softwarecurl, Docker Engine and the Docker Compose plugin. The installer does not install Docker.
PrivilegesA user that can run docker, or passwordless sudo. Write access to the install directory (/opt/nebula by default), or passwordless sudo to create it.
Free portThe listen port, 8080 on 127.0.0.1 by default. The installer stops if something else holds it.
Console updatessystemd, and root or passwordless sudo, to install the nebula-updater service. Without them the install is complete and you update with the installer.
CPU, memory, diskThe installer enforces no minimum. The bundled PostgreSQL keeps its data in a Docker volume on this host; the control plane keeps seven days of logs and metrics in it.

The control plane container runs as user 65532 with a read-only filesystem, no Linux capabilities and no-new-privileges.

Database

The control plane stores everything in PostgreSQL with the TimescaleDB extension.

ItemRequirement
BundledThe installer starts timescale/timescaledb:2.29.2-pg17 as the postgres service of the nebula Compose project. It is not published on any port.
ExternalAny PostgreSQL you pass with --database-url or NEBULA_EXTERNAL_DATABASE_URL, with TimescaleDB 2.13 or newer and timescaledb in shared_preload_libraries.
MigrationsApplied automatically at every start. Without a usable TimescaleDB the migration stops with a message that names what to install, and changes nothing.

Network: control plane

DirectionPortPurpose
Inbound443/tcp on your reverse proxyBrowsers, cluster agents and Git provider webhooks. The control plane itself listens on the --listen address, published on the host as NEBULA_HOST_LISTEN (default 127.0.0.1:8080).
Internal5432/tcpThe bundled database, on the Compose network only.
Internal9090/tcpPrometheus metrics, on the container's own loopback. Never published.

Clusters dial the control plane. It opens no connection to a cluster, so no inbound firewall rule on the cluster side is needed for the agent link.

DNS, TLS and the reverse proxy

  • Base URL. One public https:// URL (--base-url) that resolves for operators' browsers, for every cluster node and for your Git providers. The OIDC redirect URI is BASE_URL/api/v1/auth/callback.
  • Certificate. Nodes must trust the certificate. The session cookie is marked Secure only when the base URL starts with https://.
  • Proxy behavior. The proxy must pass event streams unbuffered and keep idle connections for at least 15 seconds. Streams stay open for up to 30 minutes.
  • Client addresses. The control plane trusts forwarded client addresses only from loopback and from the proxies listed in --trusted-proxies (NEBULA_TRUSTED_PROXIES). When the proxy reaches the container through the Docker bridge, add that bridge's CIDR. The value cloudflare adds the reviewed Cloudflare ranges.
  • Request size. The control plane refuses request bodies over 1 MiB, except agent log uploads, which may be 8 MiB.

Outbound access: control plane

HostUsed forFlag
Your OIDC issuerDiscovery at start. The control plane does not start while the issuer is unreachable.Required.
get.nebulactrl.devThe installer, the Compose file, the version index and the release feed. The feed request is an anonymous GET at start, every 6 hours and on Check for updates, with no instance id or version.--update-check=false stops the feed. --release-feed-url points it at a mirror. The installer's --compose-file avoids the Compose download.
ghcr.ioThe nebulactrl/nebula image.The installer's --image names a mirror.
codeload.github.comThe community template catalogue, refreshed hourly.--community-templates=false.
Your Git providerAPI calls and clones through your Git connections. api.github.com and github.com for GitHub, or your instance.Only if you connect one.
api.cloudflare.comThe Cloudflare connection.Only if you connect one.
acme-v02.api.letsencrypt.orgDNS-01 certificates the control plane issues.--acme-directory-url.
update.k3s.ioK3s release channels for a cluster's Upgrade tab.--k3s-channels-url.
api.tailscale.comTailscale connections, to clean up devices.Only if you connect one.
events.pagerduty.com, Slack webhook URLsNotification channels.Only if you add a channel.
Object store and registry endpointsCredential checks.Only if you add them.

The control plane refuses to dial a host you name in the console, such as a Git provider, registry, object store or webhook, when it resolves to a loopback, link-local, private or reserved address. The one exception is a private SSH target that you explicitly authorize for a cluster install.

The flags --update-check, --release-feed-url, --community-templates, --acme-directory-url and --k3s-channels-url are nebula serve flags; see Server flags and environment variables. --image and --compose-file are installer flags. The Compose file that the installer downloads passes only some .env settings to the container: NEBULA_DATABASE_URL, NEBULA_MASTER_KEY, NEBULA_PREVIOUS_MASTER_KEYS, NEBULA_BASE_URL, NEBULA_OIDC_ISSUER, NEBULA_OIDC_CLIENT_ID, NEBULA_OIDC_CLIENT_SECRET, NEBULA_OIDC_PROVIDER_NAME, NEBULA_OIDC_SCOPES, NEBULA_OIDC_INSECURE_HTTP, NEBULA_TRUSTED_PROXIES, NEBULA_LOG_LEVEL, NEBULA_AGENT_IMAGE and NEBULA_UPDATER_TOKEN. Any other server setting reaches the container only through a Compose file of your own, installed with --compose-file. A console update downloads the release's Compose file again and replaces yours, so update such an install with the installer.

Cluster nodes

The cluster install script checks these before it changes a node.

ItemRequirement
Operating systemUbuntu 22.04 or newer, or Debian 12 or newer.
CPU architecturex86_64 or aarch64.
Server node (K3s server)At least 2 CPUs and 1,900,000 kB of memory.
Worker or public gateway nodeAt least 1 CPU and 480,000 kB of memory.
PrivilegesRoot, and the ss command (iproute2).
Firewallfirewalld must not be active. The script manages ufw.
Server node, first installPorts 80, 443 and 6443 free. A worker node has no such check.
Existing K3sNone for another cluster. The script refuses and names /usr/local/bin/k3s-uninstall.sh.
AddressAn outbound IPv4 address. A public gateway needs a public IPv4 address.

The script installs curl, ca-certificates, jq, ufw, fail2ban, unattended-upgrades, apt-listchanges, wireguard-tools and nftables with apt. It sets ufw to deny inbound traffic by default and allows the SSH port, 80/tcp, 443/tcp and the mesh port. If root, or the user running sudo, has an authorized_keys file, it also turns off SSH password login.

Node network

DirectionPortPurpose
Inbound80/tcp, 443/tcpIngress and certificate challenges.
InboundSSH port (22/tcp by default)Your access.
Between nodes51821/udpThe WireGuard mesh, interface nebula0. All node-to-node traffic, including K3s on 6443, travels inside it.
Outbound443/tcp to the base URLThe agent link and the install script.

Outbound access: nodes

HostUsed for
Your base URLThe install script, the agent link and mesh registration.
get.k3s.ioThe K3s installer.
ghcr.ioThe agent, the build helper images and CloudNativePG.
quay.iocert-manager images, used with database support.
Docker HubBuild images and the in-cluster registry.
Your own registriesThe images your services run.
pkgs.tailscale.com and raw.githubusercontent.comOnly when you connect a cluster to Tailscale.

Limits

A mesh holds at most 16 clusters per organization.

See also

On this page