The nebula-mgmt server is the control plane for the mesh. It issues
host certificates, manages networks and operators, distributes config to
agents via /api/v1/agent/updates, and exposes the Web UI + REST API +
CLI from a single Go binary.
This document covers production usage — installation, configuration, day-2 operations, upgrade. The 30-second walkthrough lives in the project README.
- A Linux host (Debian / Ubuntu / RHEL family supported by the packages below; tarball fallback for the rest).
- Persistent storage for
data_dir(default/var/lib/nebula-mgmt) — the SQLite database and all CA material encrypted at rest live here. - The
NEBULA_MGMT_MASTER_KEY(base64 32-byte AES-256 key) must be supplied at startup via:- Environment variable
NEBULA_MGMT_MASTER_KEY(recommended for systemd / Docker), or - The
master_keyfield inserver.yml(base64-encoded). The key is never written to disk by the server; loss of the key means loss of the ability to mint new certificates against existing CAs.
- Environment variable
- ~25 MiB of disk for the binary; runtime memory typically < 100 MiB.
Each tagged release publishes .deb and .rpm packages for amd64 and
arm64 alongside the agent package:
TAG=$(curl -fsSL https://api.github.com/repos/forgekeep/nebula-mesh/releases/latest | grep -m1 tag_name | cut -d'"' -f4)
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
# Debian / Ubuntu
curl -fsSL -O "https://github.com/forgekeep/nebula-mesh/releases/download/${TAG}/nebula-mgmt_${TAG#v}_linux_${ARCH}.deb"
sudo apt install -y "./nebula-mgmt_${TAG#v}_linux_${ARCH}.deb"
# RHEL / Rocky / Alma / Fedora
sudo rpm -i "https://github.com/forgekeep/nebula-mesh/releases/download/${TAG}/nebula-mgmt_${TAG#v}_linux_${ARCH}.rpm"The package installs:
/usr/bin/nebula-mgmt— the server binary;/lib/systemd/system/nebula-mgmt.service— the systemd unit;/etc/nebula-mgmt/server.example.yml— example config (markedconfig|noreplaceso upgrades preserve your edits);/var/lib/nebula-mgmt/— empty data dir, mode0750, owned by the newly-creatednebula-mgmtsystem user;/usr/share/doc/nebula-mgmt/{README,LICENSE,CHANGELOG,server.md}— docs;/usr/share/doc/nebula-mgmt/reverse-proxy/{nginx.conf,Caddyfile,traefik-dynamic.yml}— reverse-proxy snippets you cancpinto the right system directory.
The post-install script does not start or enable the unit; you must complete bootstrap first (see Bootstrap below).
For platforms without a native package (macOS dev install, FreeBSD, non-standard Linux distros) or container deployments, fall back to the prebuilt tarball or the published Docker image — see the Install section in the README.
# 1. Generate a master key (required for CA encryption) and set it in a
# systemd drop-in so it never appears in /etc/nebula-mgmt/server.yml.
openssl rand -base64 32
sudo systemctl edit nebula-mgmt.service
# [Service]
# Environment=NEBULA_MGMT_MASTER_KEY=<base64-32-byte-key>
# 2. Materialise the config from the shipped example and edit.
sudo cp /etc/nebula-mgmt/server.example.yml /etc/nebula-mgmt/server.yml
sudoedit /etc/nebula-mgmt/server.yml
# 3. Run init exactly once. The script creates the initial admin
# operator and provisions the admin-default CA.
sudo -u nebula-mgmt -E nebula-mgmt init --config /etc/nebula-mgmt/server.yml
# 4. Enable + start.
sudo systemctl enable --now nebula-mgmt.service
sudo journalctl -u nebula-mgmt -fThe post-install reminder printed by the package walks operators through these same steps.
The server speaks plain HTTP on the bind address by default. Always front it with a TLS-terminating proxy unless your network is unconditionally trusted.
Three working snippets ship in the package at
/usr/share/doc/nebula-mgmt/reverse-proxy/ (and in the repo under
deploy/reverse-proxy/):
nginx.conf— nginx ≥ 1.18 + certbot.Caddyfile— Caddy 2.x with automatic Let's Encrypt.traefik-dynamic.yml— Traefik v3 file provider.
All three preserve X-Forwarded-For so the management server's per-IP
rate limiter (issue #52) keys on the real client IP — set
rate_limit.trust_proxy_header: true in server.yml to opt into that.
sudo apt install -y "./nebula-mgmt_<new-version>_linux_<arch>.deb"
# rpm equivalent:
# sudo rpm -U "./nebula-mgmt_<new-version>_linux_<arch>.rpm"The deb / rpm post-install runs systemctl daemon-reload; if the unit
was already enabled, systemd auto-restarts the service. /etc/nebula-mgmt/server.yml
and the data dir survive across upgrades.
When upgrading across a minor version, check the
CHANGELOG for migration notes — every database
migration is tracked in internal/store/migrations/, applied
automatically on startup, and recorded in schema_migrations.
sudo apt remove nebula-mgmt # stops + disables the service, keeps data
sudo apt purge nebula-mgmt # additionally drops the system userapt purge never deletes /var/lib/nebula-mgmt (CA + DB) or
/etc/nebula-mgmt/. Clean them up manually if you really mean to
nuke the install:
sudo rm -rf /var/lib/nebula-mgmt /etc/nebula-mgmtThe rpm equivalents are rpm -e nebula-mgmt and the same manual
clean-up afterwards.
The whole server state collapses to a single SQLite file. With the service running:
sudo -u nebula-mgmt sqlite3 /var/lib/nebula-mgmt/nebula.db \
".backup /backups/nebula-$(date +%F).db"The NEBULA_MGMT_MASTER_KEY is load-bearing: the DB on its own
is useless without the matching key. Keep the master key in your secret
manager — it is not stored in the database or configuration.
Networks can contain one or more CIDR prefixes, enabling dual-stack (IPv4 + IPv6) and segmented address schemes. Create via the Web UI or REST API:
curl -X POST "https://mgmt.example.com:8080/api/v1/networks" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "production",
"cidrs": ["10.42.0.0/24", "fd00:42::/64"]
}'Response (201):
{
"id": "net_abc123",
"name": "production",
"cidrs": ["10.42.0.0/24", "fd00:42::/64"],
"ca_id": "ca_xyz789",
"created_at": "2026-05-14T10:30:00Z"
}Hosts are assigned one or more overlay addresses from the parent network's CIDR
prefixes. When multiple addresses are provided, the host's certificate includes
all of them; the configuration generated for the host references all addresses
in static_host_map and lighthouse.hosts.
curl -X POST "https://mgmt.example.com:8080/api/v1/hosts" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"network_id": "net_abc123",
"name": "edge-1",
"nebula_ips": ["10.42.0.10", "fd00:42::10"],
"role": "host"
}'Response (201):
{
"id": "host_def456",
"network_id": "net_abc123",
"name": "edge-1",
"nebula_ips": ["10.42.0.10", "fd00:42::10"],
"role": "host",
"status": "pending",
"groups": [],
"created_at": "2026-05-14T10:31:00Z"
}Field notes:
cidrs(networks): array of CIDR strings. Each CIDR must be unique and non-overlapping within the network. At least one is required.nebula_ips(hosts): array of IP address strings. Each address must fall within one of the parent network's CIDRs. At least one is required. Order is preserved in the issued certificate.- Legacy singular fields (
cidrandnebula_ip) were removed in v0.3.0. Requests using the old field names receive a 400 Bad Request error.
To change a host's addresses (trigger a new certificate issuance), use PATCH:
curl -X PATCH "https://mgmt.example.com:8080/api/v1/hosts/host_def456" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"nebula_ips": ["fd00:42::10", "10.42.0.10"]
}'The new certificate will reflect the reordered addresses on the agent's next poll.
The Mobile Nebula
app (iOS) and
Mobile Nebula
app (Android) allow iOS and Android devices to join a Nebula mesh. Unlike
regular hosts that run the nebula-agent daemon, mobile devices import a
self-contained Nebula configuration file (in YAML format) bundled with
inline certificates and keys.
- In the Web UI, navigate to Hosts → New Host.
- Select Kind: Mobile and choose the device type (Variant: iOS or
Variant: Android). The host's role is automatically set to
host(mobile devices cannot be lighthouses or relays). - Fill in the remaining fields (Name, IP address, Groups) as you would for a standard host.
- Submit. The mobile host is created but is not yet enrolled — no enrollment token is issued.
- Navigate to the mobile host's detail page.
- Click Generate Mobile Bundle. The server will:
- Generate a fresh X25519 keypair
- Mint a certificate signed by your CA
- Create a self-contained Nebula YAML configuration with inline PEM blocks
for
pki.ca,pki.cert, andpki.key
- The result page displays:
- The YAML configuration as a code block
- A QR code encoding the same YAML (for quick import into Mobile Nebula)
- A download link for the YAML file
- Save the YAML immediately. The private key is shown only once; it is not stored on the server. Once you close the page, you cannot recover the key.
- On your iOS or Android device, install Mobile Nebula from the App Store or Play Store.
- Open the app and create a new profile.
- Import the configuration file via one of:
- Scan the QR code shown in the Web UI
- Copy-paste the YAML configuration
- Download the YAML file and share it to the Mobile Nebula app
To rotate a mobile host's certificate (when approaching expiry or after a security incident):
- On the mobile host's detail page, click Regenerate Bundle (Rotate Cert). This generates a new keypair and certificate.
- Follow the steps under Generating the mobile bundle to download and import the new configuration into Mobile Nebula.
Each regeneration creates a fresh certificate. The old certificate remains valid for a short overlap window (to avoid disrupting active connections) and is automatically revoked after 30 days.
Mobile certificates have a 365-day default lifetime, compared to 30 days for agent-managed hosts. This longer lifetime reduces the operational burden of manual bundle regeneration and import on mobile devices.
However, the certificate lifetime is clamped to the remaining validity of your CA. If your CA certificate is close to expiry, the generated mobile certificate will expire sooner. For example, if your CA expires in 180 days, a mobile certificate issued today will expire in 180 days (not 365).
Operational implication: Before your CA certificate expires, rotate it and regenerate all mobile bundles. Failing to do so will leave mobile devices with expired certificates, unable to connect to the mesh.
To revoke (block) a mobile host:
- On the mobile host's detail page, click Block. The host's certificate fingerprint is added to the blocklist.
- Other mesh nodes will receive the updated blocklist via their regular poll interval and refuse handshakes with the blocked fingerprint.
Mobile devices themselves do not poll the management server, so they will not receive the revocation notice. However, the host is effectively isolated because peer nodes reject incoming and outgoing connections. To fully remove the device from the mesh, delete the configuration from the Mobile Nebula app.
Mobile bundles encode the network's current configuration (lighthouse IP addresses, port mappings, etc.). If you change your network topology (for example, promoting a new lighthouse or changing its public IP), previously-generated bundles become stale.
To update mobile devices after a network change:
- Regenerate the bundle (click Regenerate Bundle on the host's detail page).
- Re-import the new YAML into Mobile Nebula.
There is no automatic update mechanism for mobile clients; manual re-import is required each time the underlying network configuration changes.
When a CA approaches its expiry date (≤20% lifetime remaining), the Web UI displays a warning badge alerting the operator. The operator can manually rotate the CA via the UI, REST API, or CLI; a new CA is created as the successor of the old one. Existing host certificates signed by the old CA remain valid until their natural expiry.
Optionally, an opt-in background worker can automatically rotate approaching-expiry CAs.
Web UI: Navigate to /ui/cas or the CA detail page; click the Rotate button.
REST API:
curl -X POST "https://mgmt.example.com:8080/api/v1/cas/<ca-id>/rotate" \
-H "Authorization: Bearer $API_KEY"Response (201):
{
"id": "ca_new123",
"name": "tenant-a",
"predecessor_id": "ca_old789",
"status": "active",
"created_at": "2026-05-15T12:00:00Z"
}CLI:
nebula-mgmt ca rotate --server https://mgmt.example.com:8080 --api-key "$API_KEY" \
--id ca_xyz789After rotation, the database contains two active CAs:
- Old CA (
predecessor_id = NULL): Remainsstatus = active. Any host certificates signed by this CA before rotation continue to verify and function until their natural expiry. - New CA (
predecessor_id = old-ca-id): The successor. Same owner, same lifetime duration as the old CA, new key material.
When an agent polls /api/v1/agent/updates, it receives a trust bundle —
the concatenated PEM certificates of both the old and new CA. The agent writes
the trust bundle to disk atomically; Nebula natively parses multi-cert PEM files,
so certificate verification works seamlessly during the transition.
To enable automatic CA rotation, add the ca_auto_rotate section to server.yml:
ca_auto_rotate:
enabled: true # default: false
interval: 6h # default: 6h — how often to check for expiring CAs
threshold: 0.20 # default: 0.20 — rotate when ≤20% lifetime remainingThe background scanner:
- Runs once per
intervalin a separate goroutine. - Queries for active CAs with ≤
thresholdlifetime remaining. - For each CA, creates a successor via
pki.RotateAndStoreCA. - Records an audit entry
ca.auto_rotatedwith the new CA ID. - Logs the rotation event.
If an error occurs (e.g., transient database failure), the scanner logs and skips to the next check interval without blocking server shutdown.
Default behavior: Auto-rotation is disabled (enabled: false). Operators
retain explicit control over when CAs are rotated.
- ADR 0008 — Hybrid CA rotation design.
- ADR 0007 — Per-operator CA storage (successor to this feature).
Most often a leftover *-wal / *-shm file from a hard crash. Stop
the service, run sqlite3 /var/lib/nebula-mgmt/nebula.db "PRAGMA wal_checkpoint(TRUNCATE);", then start again.
nebula-mgmt init prints the seeded admin's username and one-time
password to stdout. The package log capture in journalctl shows it
only on the first start of the service. If you missed it, run:
sudo -u nebula-mgmt -E nebula-mgmt user create --username admin --role adminto mint a new admin operator (the existing seed user keeps its TOTP binding, if any).
The package sets 0750 nebula-mgmt:nebula-mgmt on the data dir. If a
previous install left it as root-owned, fix it:
sudo chown -R nebula-mgmt:nebula-mgmt /var/lib/nebula-mgmt
sudo chmod 0750 /var/lib/nebula-mgmt