Install
Self-hosting runs the Nexus Telemetry Fleet server on hardware you own, in place of the managed service. During the beta it is available by request. Write to us before you start. Support issues the licence key and guides the first start. A fleet server with no licence key does nothing. See Licensing.
Requirements
- A Linux host, virtual host or Docker host that you administer.
- A public DNS name for the dashboard, already pointing at the host.
- Open ports on the host: 80 and 443 for the dashboard with the compose files, 3000 with the package, and 9443 for the collectors’ connections. The sites where the terminals are need no open port, because a collector connects out to the fleet server and accepts no connection.
See Files and repositories for the ports and paths.
Compose route
The production fleet server is defined by 2 files, a compose file and a Caddy
configuration. Download or copy each into an empty directory. Save the Caddy
configuration as Caddyfile, with no extension, beside the compose file.
# Production reference: the fleet server behind Caddy.
#
# Caddy terminates TLS on 443 with a publicly trusted certificate and renews
# it automatically (ACME). The dashboard and enrolment endpoint are reachable
# only through it, port 3000 stays on the compose network and is never
# published. Plain HTTP is the tyre-kicking path in `docker-compose.yml`, for
# a server on your own machine; HTTP in production is unsupported.
#
# 1. cp Caddyfile.example Caddyfile, put your domain in it and in
# NF_STREAM_SAN below (DNS for the name must point at this host, with
# 80 and 443 reachable for the ACME challenge).
# 2. Set NF_STREAM_SAN in the environment BEFORE the first start.
# 3. docker compose -f docker-compose.production.yml up -d
# 4. Create the first user to require dashboard sign-in:
# docker compose -f docker-compose.production.yml exec nf-server \
# nf-server create-operator --username admin --role owner
services:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80" # ACME challenge + redirect to HTTPS
- "443:443" # dashboard + enrolment, publicly trusted certificate
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
nf-server:
# Pulls the published server image (pushed by every release, :latest =
# the current release). To build from this tree instead, swap in:
# build: { context: ., target: server }
image: registry.nexustelemetry.com/nf-server:latest
restart: unless-stopped
# The collector mTLS stream is published directly and must NOT pass
# through Caddy or any other terminating proxy: the collector verifies
# this port against the fleet server's own private chain, pinned at
# enrolment, and will refuse Caddy's publicly trusted certificate.
#
# No dashboard port here, on purpose, 3000 is reachable only on the
# compose network, through Caddy. Never publish it alongside.
ports:
- "9443:9443"
environment:
# The public name collectors dial. Set BEFORE the first start: it is
# baked into the stream certificate when the server first runs.
# Required, from the environment (compose refuses to start without it):
# NF_STREAM_SAN=fleet.example.com docker compose -f docker-compose.production.yml up -d
NF_STREAM_SAN: "${NF_STREAM_SAN:?required, see the comment above}"
# NF_INITIAL_ADMIN_PASSWORD: "change-me" # seed an admin on first run
# NF_LOG_FORMAT: "pretty" # json is the default
volumes:
- nf-data:/var/lib/nexus-fleet
# HEALTHCHECK is defined in the image (GET /health).
stop_grace_period: 20s
volumes:
nf-data:
caddy-data:
caddy-config:# The TLS front door for the fleet dashboard and enrolment
# (docker-compose.production.yml). Caddy obtains and renews the publicly
# trusted certificate itself via ACME, nothing to install or rotate.
# Replace the domain here and in NF_STREAM_SAN, then: cp Caddyfile.example Caddyfile
#
# Only the dashboard goes through here. The collector stream (9443) is
# published directly by the compose file, it rides the fleet server's own
# private chain and must not be proxied.
{
# The certificate authority's contact for this site: expiry warnings and
# account notices go here. Your own address, not ours.
email [email protected]
}
fleet.example.com {
reverse_proxy nf-server:3000
}Set the 3 values below. Do not change anything else in either file.
| Value | Where | Set it to |
|---|---|---|
| The dashboard’s name | Caddyfile, in place of fleet.example.com |
The public DNS name. Caddy requests and renews the certificate for it |
| A contact address | Caddyfile, in place of [email protected] |
An email address someone reads. The certificate authority sends expiry warnings to it |
NF_STREAM_SAN |
A .env file beside the compose file |
The same name as the dashboard. Compose reads the file for every command, and refuses every command without the variable, exec and logs included |
Warning: the DNS name must resolve to this host, with ports 80 and 443
reachable from the internet, before the first start. Caddy answers the
certificate challenge on those ports. If the name does not point here yet,
no certificate is issued, the dashboard does not answer over HTTPS, and the
only record is Caddy’s error in docker compose logs caddy.
echo NF_STREAM_SAN=<your domain> > .env
docker compose -f docker-compose.production.yml up -dCaddy obtains the certificate at the first start. Open https://<your domain>
and activate the fleet server. See
Licensing.
Stream name
NF_STREAM_SAN is the name collectors connect to. It is the same name as the
dashboard’s. The fleet server writes it into its own certificate at the first
run, and the value in the certificate is the one that counts from then on.
Restarting with a different value changes nothing. Correcting a wrong name
means restarting with the corrected value and then issuing the fleet server
a new certificate, which is done from the
Fleet server pane of the Certificates
page. The
certificate also covers localhost, so a trial on one host requires no name.
Warning: the compose file publishes the collector stream port directly from the host. The stream must never pass through Caddy or any other proxy that terminates TLS. See Hardening.
Package route
The nf-server package from the signed repository installs a systemd
service, a service user and a configuration file. See Files and
repositories for adding the
repository.
Debian and Ubuntu
Signed apt repository, updates arrive through apt
sudo apt install nf-serverInstalling the package creates /etc/nexus-fleet/server.env and
/var/lib/nexus-fleet/, and prints the value to set before the first start.
RHEL and Fedora
Signed rpm repository, updates arrive through dnf
sudo dnf install nf-serverInstalling the package creates the same 2 paths. It prints no notice, so
the value to set, NF_STREAM_SAN, is the one below.
Set NF_STREAM_SAN, and NF_LISTEN for a dashboard reached from other
hosts, in /etc/nexus-fleet/server.env before the first start, then enable
the service. See Hardening for TLS on
the dashboard port.
sudo systemctl enable --now nf-serverAt its first run the fleet server generates its certificate authority and the certificate for the stream name.
First user
A new fleet server has no users. Until one exists, the dashboard lets in anyone who can reach it, with no sign-in. Create the first user before anything else.
sudo -u nexus-fleet NF_DATA_DIR=/var/lib/nexus-fleet nf-server create-operator --username admin --role ownerOn the compose route, run the same command inside the container instead.
docker compose -f docker-compose.production.yml exec nf-server nf-server create-operator --username admin --role ownerThe command prints the user’s password once. Store it. The dashboard requires a sign-in from then on. The password is not forced to change at the first sign-in. Change it from the dashboard. See Users and roles for the roles.
Advertised address
Every enrolment command the dashboard generates includes the fleet server’s
address. By default that is the address the browser used to reach the
dashboard, which is wrong when the dashboard is reached through a tunnel. Set
NF_SERVER_URL to the public https:// name and restart. Unlike the stream
name, it can be changed at any time, and it applies to commands generated
from then on.
See Add a collector for enrolling the first collector.