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.

docker-compose.production.ymlDownload
# 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:
CaddyfileDownload
# 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.

bash
echo NF_STREAM_SAN=<your domain> > .env
docker compose -f docker-compose.production.yml up -d

Caddy 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

bash
sudo apt install nf-server

Installing 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

bash
sudo dnf install nf-server

Installing 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.

bash
sudo systemctl enable --now nf-server

At 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.

bash
sudo -u nexus-fleet NF_DATA_DIR=/var/lib/nexus-fleet nf-server create-operator --username admin --role owner

On the compose route, run the same command inside the container instead.

bash
docker compose -f docker-compose.production.yml exec nf-server nf-server create-operator --username admin --role owner

The 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.