Deployment overview
Northplane ships as one static binary, northplaned, with the web UI and this documentation embedded. Every deployment variant below runs that same binary. They differ only in who terminates TLS, where the data directory lives, and who performs the rollout.
What you are deploying
Section titled “What you are deploying”| Item | Fact |
|---|---|
| Binary | northplaned (server). The release tarball also contains np (CLI) and np-agent; the container image contains northplaned and np only — the agent is installed on the monitored hosts, not in the image. |
| Listener | listen, default 127.0.0.1:8443 (loopback on purpose). The image sets NORTHPLANE_LISTEN=:8443 so that port mapping works. |
| TLS policy | Plaintext on a non-loopback listener refuses to start unless you set tls.certFile + tls.keyFile, or trustProxy: true (behind a TLS-terminating proxy), or tls.insecure: true (dev only). There is no built-in ACME client — production TLS comes from Caddy. See TLS and reverse proxies. |
| Data directory | dataDir (NORTHPLANE_DATA_DIR): SQLite core.db, monthly events-YYYYMM.db segments, tsdb/, artifacts/, and the fallback secret.key. Default /var/lib/northplane when running as root; the image declares VOLUME /var/lib/northplane. See Storage. |
| Secret key | secretKeyFile (NORTHPLANE_SECRET_KEY_FILE): a 32-byte hex master key for secrets at rest (AES-256-GCM). Generated on first start if the file is missing; the production stacks bind-mount a persistent ./secret.key owned by uid 65532. Lose it and every stored secret is unreadable. See Secrets. |
| Admin bootstrap | On every start the server seeds a break-glass admin from NP_DEFAULT_ADMIN_EMAIL / NP_DEFAULT_ADMIN_PASSWORD (random password logged once when the variable is unset) unless NP_DEFAULT_ADMIN_DISABLED is set or the password is set-but-empty. Because that happens before the listener opens, /setup is closed on a default install; use the interactive /setup flow only with the seeding disabled. See Authentication. |
| Image | ghcr.io/myfoxit/northplane — public, no login needed. Base gcr.io/distroless/static-debian12:nonroot (uid 65532), no shell, EXPOSE 8443, ENTRYPOINT northplaned, CMD serve. Tags: main-<sha12> per green build of main, latest, and semver tags from releases. |
| Configuration | config.yaml (-config, default /etc/northplane/config.yaml as root) or NORTHPLANE_* environment variables; env wins over file. The container stacks use env only. See Configuration. |
Deployment variants
Section titled “Deployment variants”| Variant | What runs | TLS | Ports on the host | Data and key | Rollout | Details |
|---|---|---|---|---|---|---|
Developer loop (make dev) |
Vite HMR on :5173 + northplaned on 127.0.0.1:8443, demo data seeded |
none (loopback) | 5173, 8443 — loopback only | .dev/data, .dev/secret.key |
auto rebuild on source change | Development setup |
| Single binary + systemd | northplaned serve -config /etc/northplane/config.yaml |
your cert pair in config.yaml, or a reverse proxy + trustProxy |
whatever listen says (:8443) |
/var/lib/northplane, /etc/northplane/secret.key |
tarball or install.sh, northplaned init, systemctl |
Installation |
| Docker, single container | ghcr.io/myfoxit/northplane |
mount a cert pair, or NORTHPLANE_TRUST_PROXY=true behind your own proxy, or NORTHPLANE_TLS_INSECURE=true (dev) |
8443 | named volume on /var/lib/northplane |
docker pull + recreate |
Quickstart |
| Compose + bundled Caddy | northplane + caddy:2-alpine — root docker-compose.yml for trials, deploy/docker-compose.yml + .env + secret.key for a production box |
Caddy: Let’s Encrypt for DOMAIN, internal cert for localhost and the bare IP |
80, 443 | volumes northplane-data, caddy-data, caddy-config; ./secret.key |
docker compose pull && docker compose up -d, or the Deploy workflow |
Docker Compose |
| Edge-proxied VM behind a central Caddy | northplane only (deploy/docker-compose.vm.yml), 8443 published on a private bridge; a shared Caddy LXC terminates TLS for all VMs |
central Caddy (Let’s Encrypt, HTTP-01) | 8443 on the VM; 80/443 on the edge | volume northplane-data, ./secret.key |
Deploy workflow over SSH (DNAT port) | Proxmox VM |
| CI-driven production | the two Compose variants above, rolled forward by GitHub Actions on every green build of main |
as above | as above | as above | .github/workflows/deploy.yml |
CI/CD |
Federation is orthogonal to all of these: an edge instance at a customer site is any of the variants above with federation.mode: edge in its config, dialling out to the main instance. See Federation.
Decision guide
Section titled “Decision guide”- Trying it on a laptop —
docker compose up -dwith the rootdocker-compose.ymland openhttps://localhost(self-signed), ornorthplaned serve --demoon loopback. Quickstart. - One on-prem server you own DNS for — single binary behind your existing reverse proxy (
trustProxy: true), or the Compose stack with bundled Caddy (DOMAIN=monitoring.example.net→ Let’s Encrypt). Both keep everything on one box. - A server that must be reachable before DNS exists —
deploy/docker-compose.yml+deploy/Caddyfile: Caddy serveshttps://<ip>with an internal certificate immediately and picks up Let’s Encrypt forDOMAINas soon as the record resolves. - Several SaaS-style VMs on one hypervisor — one central Caddy container per box (TLS, all domains) and the edge-proxied Compose file per VM. This is the production layout of
doktrace.com. - Many customer sites — one main instance plus an edge instance per site; the edge pulls its bundle and reports status, no inbound firewall rule at the customer. Federation, Tenants and sites.
- Rollouts from git — any of the Compose variants plus the Deploy workflow; needs a host provisioned with
deploy/provision-server.sh. CI/CD, Provisioning.
What every production deployment needs
Section titled “What every production deployment needs”- A TLS decision. Either Caddy in front (bundled or central) with
NORTHPLANE_TRUST_PROXY=true, or a certificate pair inconfig.yaml. Nevertls.insecureoutside dev. Only enabletrustProxywhen the proxy strips inboundX-Forwarded-*headers — Northplane honoursX-Forwarded-Protofor Secure cookies and HSTS. - Persistent storage. A volume or directory for
dataDir, and a persistentsecret.key. The production stacks keepsecret.keynext to the compose file (/opt/northplane/secret.key, 0600, uid 65532) and bind-mount it read-only. - The public URL.
NORTHPLANE_BASE_URL(=baseUrl) is used for links in notifications, ack links, the OIDC redirect and the Web Push VAPID subject. Set it to the URL users will open. - An admin. Either the break-glass admin (
NP_DEFAULT_ADMIN_EMAIL+NP_DEFAULT_ADMIN_PASSWORD) or/setupwith seeding disabled. Change the seeded password after the first login. - Reachable ports — only the ones you use (table below). The app container itself publishes nothing but 8443; everything else is opt-in.
- Backups. There is no periodic backup loop in the server (
backup.intervalis parsed but unused); back upsecret.keyand the data volume yourself, or runnorthplaned backupon demand. See the operations runbook. - The image —
docker pull ghcr.io/myfoxit/northplane:latest(public), or build it yourself withdocker build --build-arg VERSION=… ./make docker.
The demo / real switch
Section titled “The demo / real switch”One switch, NORTHPLANE_DEMO (config key demo), decides whether the showcase environment is seeded at startup:
true→ idempotentdemo-*hosts, services, alerts, an escalation chain, an on-call schedule, a BPI tree with an SLA, a dashboard, a scheduled report and two demo users.false→ nothing is seeded; log in as the break-glass admin (or via/setup) and add real hosts.
The production stacks make the switch safe with separate data directories inside the same volume — NORTHPLANE_DATA_DIR=/var/lib/northplane/demo for demo mode and /var/lib/northplane/real for real mode — so flipping never mixes datasets. As a second guard the server refuses to seed demo data on top of a database that already holds real (non-demo) hosts and logs a warning instead. In CI the switch is the repository variable NORTHPLANE_DEMO plus a per-run override on manual dispatch. Details: Demo mode and switching between demo and real data.
| Port | Protocol | Direction | Used by | When it is open |
|---|---|---|---|---|
| 8443 | TCP | inbound | northplaned listener (listen; :8443 in the image) |
always; loopback-only by default on a bare install |
| 80, 443 | TCP | inbound | Caddy — bundled (caddy:2-alpine) or the central LXC; 80 also serves the ACME HTTP-01 challenge |
Compose and edge variants |
| 9162 | UDP | inbound | SNMP trap receiver of an snmp-trap event source (listen: udp://:9162); serve --demo-traps defaults to the same |
only when such a source exists and the port is published |
| 2023 | TCP | inbound | ESPA 4.4.4 event source (listen: tcp://:2023) |
when configured and published |
| 8123 | TCP | inbound | ESPA-X event source (listen: tcp://:8123) |
when configured and published |
| 4573 | TCP | inbound | FastAGI listener for asterisk-inbound sources (listen: tcp://:4573) |
when configured and published |
| 1883 / 8883 | TCP | outbound | MQTT channel and mqtt event source to your broker |
outbound only |
| 5693 | TCP | outbound (server → agent) | the builtin agent check polling an np-agent in listener mode |
optional |
| 25 / 465 / 587, 443 | TCP | outbound | SMTP, Twilio, ntfy, Slack/Teams, webhooks, ticket systems | outbound only |
| 2201 | TCP | inbound (hypervisor) | DNAT to the VM’s sshd for the CI deploy user | doktrace.com topology only |
| 8006 | TCP | inbound (hypervisor) | Proxmox web UI, IP-allowlisted | doktrace.com topology only |
Where to go next
Section titled “Where to go next”- Docker Compose (standalone box) — the recipe with bundled Caddy.
- Proxmox VM behind a central Caddy — the live
doktrace.comtopology. - CI/CD — how a merge to
mainbecomes a deploy. - Provisioning — preparing a new host.
- Operations runbook and Environments.
- Upgrades and the security checklist.