1 - ncclient Installation
You can install ncclient in several ways: Docker (preferred for the first lighthouse to use Magic DNS), binaries (Web UI or GitHub Releases), Windows MSI (CLI and tray app), or Pip (PyPI) as a fallback when Docker or binaries are not suitable.
Docker
The Docker client is the preferred method for the first lighthouse in a network so the container can run dnsmasq and you can use Magic DNS (split-horizon DNS) for the network. Other devices (CLI, tray, or additional Docker clients) can then resolve Nebula hostnames via the lighthouse.
Image: ghcr.io/nixrtr/nebula-commander-ncclient:latest, or build from the repo client/docker (Dockerfile in that directory).
Required environment:
NEBULA_COMMANDER_SERVER – Base URL of your Nebula Commander backend (e.g. https://nc.example.com), no trailing slash.
Optional environment:
ENROLL_CODE – One-time enrollment code from the Nebula Commander UI (Nodes → Enroll for the node). Only used when the token file does not exist; after enrollment the token is stored and this is ignored.SERVE_DNS – Set to "true" to run dnsmasq on this node when it is a lighthouse, so the network can use Magic DNS. Omit or set to false if this node is not a lighthouse or you do not need DNS.NEBULA_DNS_POLL_INTERVAL – Seconds between dnsmasq config polls when this node is a lighthouse (default: 60).NEBULA_OUTPUT_DIR – Directory where ncclient writes Nebula config and certs inside the container (default: /data/nebula).NEBULA_DEVICE_TOKEN_FILE – Path to the device token file (default: /data/nebula-commander/token).
Use a persistent volume for /data so the token and Nebula config/certs survive restarts. The compose file uses network_mode: host so Nebula and dnsmasq can bind to the host.
Example (docker-compose):
services:
ncclient:
image: ghcr.io/nixrtr/nebula-commander-ncclient:latest
network_mode: host
restart: unless-stopped
environment:
NEBULA_COMMANDER_SERVER: "https://nc.example.com"
ENROLL_CODE: "XXXXXXXX" # one-time, from UI
SERVE_DNS: "true" # for first lighthouse + Magic DNS
volumes:
- ncclient-data:/data
volumes:
ncclient-data:
driver: local
Steps:
- In Nebula Commander, create a network and add a node for this device. Mark it as a lighthouse if this will be the first lighthouse and you want Magic DNS.
- Create or sign a certificate for the node, then click Enroll and copy the one-time code.
- Set
NEBULA_COMMANDER_SERVER and ENROLL_CODE (and SERVE_DNS: "true" for the first lighthouse), then start the container. - After enrollment, the container fetches config and certs and runs Nebula (and dnsmasq if
SERVE_DNS is set and the node is a lighthouse).
Binaries
If Docker is not an option, use binaries from the Web UI or GitHub Releases.
From Web UI
When your Nebula Commander instance is deployed with client binaries included (for example the frontend image built with DOWNLOAD_BINARIES=1), the Web UI can serve them.
- Open your Nebula Commander URL in a browser and log in.
- Go to the downloads or client section (or open
https://YOUR_SERVER/downloads/ if your instance serves that path). - Download the binary for your platform:
- Linux x86_64:
ncclient-linux-amd64 - Linux ARM64:
ncclient-linux-arm64 - Windows x86_64:
ncclient-windows-amd64.exe - macOS Intel:
ncclient-macos-amd64 - macOS Apple Silicon:
ncclient-macos-arm64
- Place the file in a directory on your PATH (or add that directory to PATH). On Linux and macOS, make it executable:
chmod +x ncclient-linux-amd64 (or the file you downloaded).
If your instance does not serve binaries, use From releases or Pip (PyPI) instead.
From releases
Pre-built binaries are attached to GitHub Releases for each version.
- Open the releases page and choose a version (e.g. the latest).
- Download the file for your platform (same names as in From Web UI).
- Optionally verify with
SHA256SUMS.txt in the same release. - Place the binary in a directory on your PATH (or add that directory to PATH). On Linux and macOS, make it executable:
chmod +x ncclient-linux-amd64 (or the file you downloaded).
Windows Installer
On Windows the MSI installer sets up the ncclient CLI, an unelevated tray control UI, and a background Windows Service that does the actual work as LocalSystem - no UAC prompts for enrolling, starting/stopping, or applying split-horizon DNS.
What the installer includes:
- ncclient – CLI for enrollment and daemon (poll for config/certs, run or restart Nebula).
- ncclient-tray – Unelevated system tray control UI: enroll, settings, and start/stop/restart the background service.
- ncclient-service – The
NebulaCommanderService Windows Service that polls for config/certs and runs Nebula as LocalSystem. Starts automatically at boot; the tray talks to it over a local named pipe.
All three are installed to %ProgramFiles%\Nebula Commander\. The installer can add that directory to PATH, creates Start Menu shortcuts, and registers/starts the service.
Getting the installer:
- Download
NebulaCommander-windows-amd64.msi from the GitHub Releases page for the version you want. - Use
SHA256SUMS.txt in the same release to verify the file.
After install:
- Open the tray app from the Start Menu and use Enroll: paste the server URL and the one-time code from Nebula Commander (Nodes → open the node → Enroll). This is the recommended way to enroll after an MSI install - it writes the token where the service reads it (
%ProgramData%\nebula-commander\) and immediately notifies the service to fetch config. (The CLI’s ncclient enroll writes to a separate per-user location the service does not read from, so avoid it for MSI installs unless you’ve explicitly redirected NEBULA_COMMANDER_CONFIG_DIR.) - The service starts polling automatically once enrolled - nothing else to run. Use the tray’s Start/Stop/Restart Service menu to control it, and Settings to change server URL, poll interval, or enable split-horizon DNS. See Windows Tray in Usage for details.
For building the MSI yourself, see Development: Manual builds.
Pip (PyPI)
Fallback method when Docker or binaries are not suitable (e.g. no Docker, or you need to run from source).
From PyPI:
pip install nebula-commander
Requires Python 3.10+. This installs the ncclient command.
From source (repo clone):
cd nebula-commander
pip install -r client/requirements.txt
Then run as python -m client --server URL enroll --code XXX, or install the client in development mode to get the ncclient command:
cd client
pip install -e .
2 - ncclient Usage
After installing ncclient, enroll the device once, then run the daemon (or install the service on Linux).
Command Line
Enrollment
Enrollment is one-time per device. It stores a device token that ncclient uses to fetch config and certificates.
- In Nebula Commander, open Nodes, select the node for this device, and click Enroll.
- Copy the enrollment code.
- On the device, run:
ncclient enroll --server https://YOUR_NEBULA_COMMANDER_URL --code XXXXXXXX
The device token is saved to ~/.config/nebula-commander/token (or /etc/nebula-commander/token when run as root). On Windows, the token is stored under %USERPROFILE%\.config\nebula-commander\token.
Run (daemon)
After enrollment, run ncclient so it periodically pulls config and certificates and optionally runs or restarts Nebula:
ncclient run --server https://YOUR_NEBULA_COMMANDER_URL
Defaults: poll every 60 seconds, write files to /etc/nebula (or ~/.nebula on Windows), and start or restart Nebula from PATH when config changes.
Options
| Option | Description |
|---|
--output-dir DIR | Where to write config.yaml, ca.crt, host.crt (default: /etc/nebula on Linux/macOS, ~/.nebula on Windows) |
--interval N | Poll interval in seconds (default: 60) |
--token-file PATH | Path to device token file |
--nebula PATH | Path to the nebula binary if it is not on PATH |
--restart-service NAME | Instead of running nebula directly, restart this systemd service (e.g. nebula). Use only one of --nebula or --restart-service. |
--accept-dns | Enable split-horizon DNS: fetch DNS config from the server and apply it so the Nebula domain is resolved via the network’s DNS (lighthouses). On Linux use systemd-resolved, dnsmasq, or similar (run as root); on Windows uses NRPT (run as Administrator). See Split-horizon DNS below. |
Example with nebula in a non-standard location:
ncclient run --server https://nc.example.com --nebula /usr/local/bin/nebula
Example using systemd to run Nebula (ncclient only restarts the service):
ncclient run --server https://nc.example.com --restart-service nebula
Linux: Creating the Nebula TUN device requires root. Run ncclient as root, e.g. sudo ncclient run --server https://....
Split-horizon DNS
When the server has DNS enabled for the network, you can pass --accept-dns so ncclient fetches the DNS config (domain and lighthouse IPs) and configures the host to resolve the Nebula domain via the network’s DNS. On Linux the client tries, in order: systemd-resolved, dnsmasq, NetworkManager, systemd-networkd, then /etc/resolv.conf (best-effort). On Windows it uses NRPT. Run as root (Linux) or Administrator (Windows) to apply. To remove the DNS override on exit, stop ncclient normally (e.g. Ctrl+C); the client clears the rules on exit.
Certificates: If the cert was created via the server (Create certificate in the UI), the bundle includes host.key. If it was signed (Sign flow), the server does not have the key; put your host.key in the same directory as the generated certs (the output dir).
Install service
Linux (quick install)
On Linux you can install a systemd service with one command:
This checks for an existing token at /etc/nebula-commander/token. If missing, it prints the exact ncclient enroll ... command to run first. It then prompts for server URL and options (output dir, interval, nebula path, restart-service), writes /etc/default/ncclient and /etc/systemd/system/ncclient.service, and enables (and optionally starts) the service.
- Use
--no-start to enable without starting. - Use
--non-interactive with NEBULA_COMMANDER_SERVER (and optional env vars) set for scripting.
Run ncclient run under your init system (launchd on macOS, Task Scheduler or NSSM on Windows). Example configs are in the repo under examples/; see examples/README-startup.md for step-by-step setup on macOS and Windows.
Troubleshooting
- No TUN device / cannot ping Nebula IP – On Linux, run with
sudo. For Sign flow, ensure host.key is in the output directory. Check Nebula’s error output (e.g. “failed to get tun device”, “no such file”). - Nebula starts then exits – Often missing
host.key (Sign flow), wrong config path, or on Linux needing root. Check the Nebula lines ncclient prints.
macOS notes
Token: ~/.config/nebula-commander/token (or /etc/nebula-commander/token as root). Default output dir: /etc/nebula; for non-root use --output-dir ~/.nebula. Nebula: use PATH or --nebula /opt/homebrew/bin/nebula (Apple Silicon) or /usr/local/bin/nebula (Intel). Do not use --restart-service; use launchd for background runs.
Windows notes (CLI)
Token: %USERPROFILE%\.config\nebula-commander\token. Default output dir: %USERPROFILE%\.nebula. Use --nebula if nebula.exe is not on PATH. Do not use --restart-service. This plain-CLI token/output location is separate from the shared, service-managed location the tray/service use below - for a GUI and no manual daemon management, use the Windows Tray section below instead.
Windows Tray
On Windows, the tray app is an unelevated control UI for a background Windows Service (NebulaCommanderService). The service does the actual work - polling for config/certs and running Nebula - as LocalSystem, so there is no UAC prompt at any point: not to launch the tray, not to enroll, not to start/stop/restart the daemon, and not to apply split-horizon DNS.
The tray and service are installed together by the MSI installer. The tray is not designed to run standalone without it - the service is only ever registered by the MSI (there is no CLI install/remove subcommand for it), so a standalone tray with no service installed has nothing to control and shows as unreachable.
Usage
- Enroll – Open the tray menu and use Enroll. Enter the server URL and the one-time code from Nebula Commander (Nodes → Enroll for the node). This writes the device token (DPAPI-encrypted, machine-scope) and settings to the shared
%ProgramData%\nebula-commander\ folder the service reads from, then tells the service (over a local named pipe) to poll immediately instead of waiting for the next interval. - Settings – Configure server URL, poll interval, optional path to the Nebula binary, and Accept split-horizon DNS. There is no output-directory field - Nebula’s config, certs, and logs always live under
%ProgramData%\nebula-commander\. When the app is built with bundled Nebula, the default Nebula path points to the bundled nebula.exe. - Start / Stop / Restart Service – Controls the real Windows Service via the Service Control Manager (not an in-process loop). The installer grants Authenticated Users the rights to do this, so it works with no admin prompt.
- Run On Startup – Optional: registers the tray itself (the UI) in the Windows Registry (
HKCU\...\Run) so the icon appears when you sign in. This only affects the tray UI - the service already starts automatically at boot (Start="auto", LocalSystem) regardless of whether anyone is logged in.
Settings are stored in %ProgramData%\nebula-commander\settings.json - shared between the tray and the service, not the per-user %APPDATA% location older versions used.
Run from source
From the nebula-commander repo root:
pip install -r client/windows/requirements.txt
pip install -e client/
python -m client.windows.tray
Or with pythonw to avoid a console window:
pythonw -m client.windows.tray
Running this way still expects a real installed-and-running NebulaCommanderService to control - it won’t do anything useful on a machine without one.
Build (PyInstaller)
To build the standalone tray and service executables (and optionally bundle the Nebula Windows binary):
cd client/windows
pip install -r requirements.txt pyinstaller
python build.py
By default build.py builds both ncclient-tray.exe and ncclient-service.exe (--target both); pass --target tray or --target service to build just one. Output is in client/windows/dist/. See client/windows/README.md and build.py for details.
The Windows MSI installer installs and registers all three: ncclient.exe, ncclient-tray.exe, and ncclient-service.exe.