Self-hosting the NexusLink control plane
Requirements
- Linux VM with at least 1 CPU and 2 GB RAM (amd64 or arm64)
- A public DNS name pointing at the VM (e.g.
nexuslink.example.com) - Open ports: TCP 80 and 443 (dashboard, API, Let's Encrypt), UDP 3478 (STUN/TURN)
- Docker with the Compose plugin (
docker compose version) jq,openssl,curl
1. Container images
The installer expects three images: nexuslink-server (management + signal +
relay), dashboard and, optionally, reverse-proxy. Choose one:
Build from this repository (no registry needed):
./infrastructure_files/build-images.sh --with-dashboard
The dashboard is a separate upstream repository; the script clones it, applies
the NexusLink branding with tools/branding/rebrand.py and builds it. To push
multi-arch images to your own registry instead:
export NEXUSLINK_IMAGE_REGISTRY=ghcr.io/your-org NEXUSLINK_IMAGE_TAG=v1.0.0
./infrastructure_files/build-images.sh --with-dashboard --push
Use a registry that already hosts them:
export NEXUSLINK_IMAGE_REGISTRY=ghcr.io/your-org NEXUSLINK_IMAGE_TAG=v1.0.0
Individual images can be overridden with NEXUSLINK_SERVER_IMAGE,
DASHBOARD_IMAGE and NEXUSLINK_PROXY_IMAGE. The installer checks image
availability before asking any questions; set
NEXUSLINK_SKIP_IMAGE_CHECK=true to bypass the check.
2. Run the installer
Prefer the deploy wizard in the manager dashboard (python3 manager.py --web
--open → Deploy & Servers → New deployment): it runs the pre-flight, uploads
the installer files, runs getting-started.sh non-interactively with the
values you enter and verifies the result, all over SSH. The manual equivalent:
export NEXUSLINK_DOMAIN=nexuslink.example.com
./infrastructure_files/getting-started.sh
Non-interactive installs can pre-set every prompt, for example:
export NEXUSLINK_DOMAIN=nexuslink.example.com
export NEXUSLINK_REVERSE_PROXY_TYPE=0 # 0 = built-in Traefik with Let's Encrypt
export NEXUSLINK_LETSENCRYPT_EMAIL=ops@example.com
./infrastructure_files/getting-started.sh
Reverse proxy options offered by the installer: built-in Traefik (default),
your own Traefik, nginx, Nginx Proxy Manager, Caddy, or manual. The script
renders docker-compose.yml, config.yaml (server), dashboard.env and the
proxy configuration into the current directory and starts the stack.
The embedded identity provider (Dex) is enabled by default; the first run prints the URL to create the initial administrator. External IdPs (Entra ID, Okta, Keycloak, Zitadel, Google, …) can be added from the dashboard.
3. Operate
docker compose ps
docker compose logs -f nexuslink-server
docker compose pull && docker compose up -d # upgrade
docker compose down --volumes # remove everything
Data lives in the Docker volumes created by the compose file
(/var/lib/nexuslink inside the server container). Back up the volume and
config.yaml (it contains the encryption keys).
Legacy compose layout
infrastructure_files/docker-compose.yml.tmpl, base.setup.env,
setup.env.example and configure.sh describe the older layout with separate
management, signal and relay containers (images
nexusremotesolutions/management, /signal, /relay). It still works with an
external IdP and is kept for existing deployments; new installs should use
getting-started.sh.
Enterprise / migration scripts
getting-started-enterprise.sh and migrate-to-enterprise.sh were inherited
from upstream and target NetBird's commercial images; they are not usable for
NexusLink without an equivalent image set.