Server Configuration
Nebula Commander is configured with environment variables. All backend settings use the prefix NEBULA_COMMANDER_. You can configure everything from the docs below.
- Environment variables – Full list: database, JWT, OIDC, CORS, session, SMTP, debug. Use this to configure the backend completely.
- OIDC – Log in with Keycloak or another OIDC provider: issuer URLs (internal vs public), client id/secret, redirect URI, and zero-touch vs manual setup.
Two-tier layout (Docker)
When using Docker:
- Infrastructure –
docker/.env: ports (e.g. frontend, backend, Keycloak), optional JWT_SECRET_FILE path. - Backend –
docker/env.d/backend: every NEBULA_COMMANDER_* variable. Copy from env.d.example/backend and edit.
See Installation: Docker for the copy steps.
NixOS
services.nebula-commander exposes most of these settings as first-class options, including OIDC, publicUrl, corsOrigins, and sessionHttpsOnly — see Installation: NixOS for the full option table. Anything not yet exposed as an option can still be set by extending the service environment directly with the matching NEBULA_COMMANDER_* variable. A separate services.ncclient module is also available for running the device client declaratively.
1 - Environment Variables
All backend settings use the NEBULA_COMMANDER_ prefix. Set them in the environment or in a file (e.g. docker/env.d/backend). Optional values can be omitted; defaults apply.
Application
| Variable | Description | Default |
|---|
NEBULA_COMMANDER_DEBUG | Enable debug mode (enables dev-token endpoint; do not use in production) | false |
NEBULA_COMMANDER_UPDATE_CHECK_ENABLED | Check GitHub for a newer release, shown as a notification in the About screen. Set to false to disable all outbound calls to github.com (air-gapped/privacy-conscious deployments). | true |
Database
| Variable | Description | Default |
|---|
NEBULA_COMMANDER_DATABASE_URL | Database URL (SQLite: use four slashes for absolute path, e.g. sqlite+aiosqlite:////var/lib/nebula-commander/db.sqlite) | sqlite+aiosqlite:////var/lib/nebula-commander/db.sqlite |
NEBULA_COMMANDER_DATABASE_PATH | Override for SQLite path | — |
NEBULA_COMMANDER_CERT_STORE_PATH | Directory for CA and host certificates | /var/lib/nebula-commander/certs |
Encryption at rest
Required. The backend refuses to boot if this is left at a placeholder/unset
value — unlike most settings here, there’s no insecure-but-working default.
| Variable | Description | Default |
|---|
NEBULA_COMMANDER_ENCRYPTION_KEY | Fernet key used to encrypt certificates and private keys at rest. Generate one with python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())". | — (required) |
NEBULA_COMMANDER_ENCRYPTION_KEY_FILE | Path to a file containing the Fernet key (overrides the plain env var when present). On NixOS this is auto-generated into a oneshot service the first time the module runs. | — |
JWT
| Variable | Description | Default |
|---|
NEBULA_COMMANDER_JWT_SECRET_KEY | Secret for signing tokens (generate with e.g. openssl rand -base64 32) | change-this-in-production |
NEBULA_COMMANDER_JWT_SECRET_FILE | Path to file containing JWT secret (overrides secret key when present) | — |
NEBULA_COMMANDER_JWT_ALGORITHM | JWT algorithm | HS256 |
NEBULA_COMMANDER_JWT_EXPIRATION_MINUTES | Token expiration in minutes | 1440 (24 hours) |
Public URL and OIDC
| Variable | Description | Default |
|---|
NEBULA_COMMANDER_PUBLIC_URL | Base URL where users reach the app (FQDN or host:port). Used to derive redirect URI and for redirect validation. | — |
NEBULA_COMMANDER_STANDALONE_ADMIN_BOOTSTRAP | Allow the unauthenticated /api/auth/dev-token admin-bootstrap endpoint when no OIDC provider is configured below. Standalone (no-IdP) deployments must opt in explicitly — this grants unauthenticated system-admin access to anyone who can reach the backend, so only enable it for a genuinely no-IdP deployment. Deployments with OIDC configured are unaffected (always 404). | false |
NEBULA_COMMANDER_OIDC_ISSUER_URL | OIDC issuer URL used by the backend to reach the provider (internal; e.g. http://keycloak:8080/realms/nebula-commander) | — |
NEBULA_COMMANDER_OIDC_PUBLIC_ISSUER_URL | OIDC issuer URL as seen by the browser (FQDN or host:port) | — |
NEBULA_COMMANDER_OIDC_CLIENT_ID | OIDC client ID | — |
NEBULA_COMMANDER_OIDC_CLIENT_SECRET | OIDC client secret | — |
NEBULA_COMMANDER_OIDC_CLIENT_SECRET_FILE | Path to file containing OIDC client secret | — |
NEBULA_COMMANDER_OIDC_REDIRECT_URI | Callback URL (optional; derived as PUBLIC_URL + /api/auth/callback when PUBLIC_URL is set) | — |
NEBULA_COMMANDER_OIDC_SCOPES | OIDC scopes (space-separated) | openid profile email |
NEBULA_COMMANDER_OIDC_ADMIN_ROLE_CLAIM | Top-level claim name used to detect the system-admin role (e.g. roles, groups, or an Auth0-style namespaced claim). Leave unset to use Keycloak’s default resource_access.<client_id>.roles shape. | — |
NEBULA_COMMANDER_OIDC_ADMIN_ROLE_VALUE | Value that must appear in the admin-role claim to grant system-admin | system-admin |
CORS and session
| Variable | Description | Default |
|---|
NEBULA_COMMANDER_CORS_ORIGINS | Allowed CORS origins: * or comma-separated list. Include your public app URL. Using * with credentials is insecure. | http://localhost:3000, http://localhost:5173 |
NEBULA_COMMANDER_SESSION_HTTPS_ONLY | Set session cookie to HTTPS-only (use true in production with HTTPS) | false |
NEBULA_COMMANDER_ALLOWED_REDIRECT_HOSTS | Allowed hosts for OAuth/OIDC redirects (comma-separated). When empty and PUBLIC_URL is set, derived from PUBLIC_URL. | — |
Certificates and device tokens
| Variable | Description | Default |
|---|
NEBULA_COMMANDER_DEFAULT_CERT_EXPIRY_DAYS | Default certificate expiry in days | 365 |
NEBULA_COMMANDER_DEVICE_TOKEN_EXPIRATION_DAYS | Device token (enrollment) expiry in days | 365 |
SMTP (optional)
| Variable | Description | Default |
|---|
NEBULA_COMMANDER_SMTP_ENABLED | Enable sending email (e.g. for invitations) | false |
NEBULA_COMMANDER_SMTP_HOST | SMTP host | localhost |
NEBULA_COMMANDER_SMTP_PORT | SMTP port | 587 |
NEBULA_COMMANDER_SMTP_USERNAME | SMTP username | — |
NEBULA_COMMANDER_SMTP_PASSWORD | SMTP password | — |
NEBULA_COMMANDER_SMTP_PASSWORD_FILE | Path to file containing SMTP password | — |
NEBULA_COMMANDER_SMTP_USE_TLS | Use TLS | true |
NEBULA_COMMANDER_SMTP_FROM_EMAIL | From address | noreply@example.com |
NEBULA_COMMANDER_SMTP_FROM_NAME | From name | Nebula Commander |
Analytics (optional)
Exposed publicly via GET /api/public-config (no auth) so the frontend can inject the corresponding script tags.
| Variable | Description | Default |
|---|
NEBULA_COMMANDER_PLAUSIBLE_DOMAIN | Plausible domain (e.g. example.com); set to enable Plausible analytics | — |
NEBULA_COMMANDER_PLAUSIBLE_SCRIPT_SRC | Custom Plausible script URL (e.g. behind your own proxy), overriding the default script source | — |
NEBULA_COMMANDER_GA_MEASUREMENT_ID | Google Analytics measurement ID (e.g. G-XXXXXXXXXX); set to enable GA | — |
NEBULA_COMMANDER_ANALYTICS_CUSTOM_SCRIPTS | JSON array of custom scripts to inject, each {"src": "https://...", "defer": true} or {"inline": "..."} | — |
Server (advanced)
| Variable | Description | Default |
|---|
NEBULA_COMMANDER_HOST | Bind host | 0.0.0.0 |
NEBULA_COMMANDER_PORT | Bind port | 8081 |
Security notes
- Generate a strong JWT secret for production (e.g.
openssl rand -base64 32). Do not use the default. ENCRYPTION_KEY has no default to leave-unset-and-forget — the backend won’t start without one. Generate it once and keep it: losing it makes every stored certificate/private key unrecoverable.- Prefer
*_FILE options (JWT, encryption key, OIDC secret, SMTP password) over plain env vars when possible. - In production: set
DEBUG=false, use HTTPS for PUBLIC_URL and OIDC, and set CORS_ORIGINS to your actual frontend origin(s). For examples of putting Nebula Commander behind Nginx, Traefik, or Caddy with TLS and HSTS, see Reverse Proxy.
2 - OIDC Authentication
Nebula Commander can use OpenID Connect (OIDC) for login. Keycloak is supported with a pre-configured setup; other OIDC providers (Authentik, Auth0, etc.) work with the same environment variables.
Required variables
Set these in env.d/backend (Docker) or in the backend environment:
NEBULA_COMMANDER_PUBLIC_URL – URL where users reach the app (FQDN or host:port). The redirect URI is derived as PUBLIC_URL + /api/auth/callback if you do not set OIDC_REDIRECT_URI.NEBULA_COMMANDER_OIDC_ISSUER_URL – Issuer URL used by the backend to talk to the provider. Inside Docker use the container name and container port (e.g. http://keycloak:8080/realms/nebula-commander). Do not use the host-mapped port here.NEBULA_COMMANDER_OIDC_PUBLIC_ISSUER_URL – Issuer URL as seen by the browser (FQDN or host:port). Use the same URL users would use to open the provider’s login page.NEBULA_COMMANDER_OIDC_CLIENT_ID – OIDC client ID (e.g. nebula-commander).NEBULA_COMMANDER_OIDC_CLIENT_SECRET – Client secret from the provider. Prefer NEBULA_COMMANDER_OIDC_CLIENT_SECRET_FILE in production.
Optional: NEBULA_COMMANDER_OIDC_REDIRECT_URI – Override only if you need a callback URL different from PUBLIC_URL + /api/auth/callback.
Keycloak with Docker
- Copy example env files and edit
env.d/backend (set PUBLIC_URL, OIDC issuer URLs, client id/secret). - Start with Keycloak:
docker compose -f docker-compose.yml -f docker-compose-keycloak.yml up -d - Create a user in Keycloak Admin and assign client roles under the
nebula-commander client: system-admin, network-owner, or user.
Zero-touch Keycloak
The project provides a custom Keycloak image that imports the realm and theme at startup. Build the image (see Installation: Docker and docker/keycloak), set the same OIDC variables in env.d/backend, and start the stack. No manual realm creation is required; create users and assign roles in the Admin console.
Manual Keycloak setup
If you do not use the zero-touch image, create the realm and client in Keycloak:
- Client type: OpenID Connect, confidential, standard flow.
- Valid redirect URI: your PUBLIC_URL +
/api/auth/callback. - Valid post-logout redirect URIs: PUBLIC_URL +
/*. - Web origins: your PUBLIC_URL.
- Create client roles:
system-admin, network-owner, user. Assign them to users via Role mapping.
Then set the client secret in env.d/backend.
Backend cannot reach Keycloak
The backend talks to Keycloak on the Docker network. Use the container port in NEBULA_COMMANDER_OIDC_ISSUER_URL (e.g. http://keycloak:8080/realms/nebula-commander), not the host-mapped port. Use the host port or FQDN only in NEBULA_COMMANDER_OIDC_PUBLIC_ISSUER_URL and in NEBULA_COMMANDER_PUBLIC_URL.
External OIDC provider
Keycloak is the default, but Nebula Commander’s OIDC integration is fully
provider-agnostic: login, logout, and step-up reauth all use standard OIDC
discovery (.well-known/openid-configuration) to find the provider’s actual
endpoints, rather than assuming Keycloak’s URL conventions. Any standards-compliant
provider — Authentik, Auth0, Okta, and others — works with the same environment
variables.
Use the same variables as above. In the provider, set redirect URI to PUBLIC_URL +
/api/auth/callback, client type Confidential, grant type Authorization Code. Set
OIDC_ISSUER_URL and OIDC_PUBLIC_ISSUER_URL to the provider’s issuer URL (often
the same).
docker-compose.yml includes the Keycloak stack by default. To use an external
provider instead, comment out (or delete) the include: block at the top of
docker-compose.yml:
# include:
# - path: ./docker-compose-keycloak.yml
then docker compose up -d starts only the backend and frontend.
Admin role mapping
Keycloak assigns roles via a client-roles claim shaped like
resource_access.<client_id>.roles, which most other providers don’t emit by
default. If you’re not using Keycloak, set:
NEBULA_COMMANDER_OIDC_ADMIN_ROLE_CLAIM – the top-level claim your provider
emits (e.g. roles, groups, or an Auth0-style namespaced claim like
https://nebula.example.com/roles).NEBULA_COMMANDER_OIDC_ADMIN_ROLE_VALUE – the value within that claim that
grants system-admin (default system-admin).
Leave OIDC_ADMIN_ROLE_CLAIM unset to keep using Keycloak’s default shape — this
is the default, so existing Keycloak deployments need no changes.
Roles and permissions
- system-admin – Manage users; view all networks/nodes with limited data; delete networks/nodes (with reauth); request access grants from owners.
- network-owner – Create and manage networks, nodes, invitations, node requests, and access; set default node parameters and auto-approval.
- user – Access only what network owners grant; request nodes and higher access.
See the repository docker/README for detailed RBAC and permission features.