1 NixOS
Kyle Mendell edited this page 2026-10-27 15:06:39 -05:00

Overpass distributes a Nix Flake containing precompiled binaries for the control plane, node agent, op, opctl, and two NixOS modules. Releases are packaged and published automatically to nix/overpass via GoReleaser.

Add the flake

Add the Overpass repository to your system flake:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    overpass.url = "git+https://forgejo.ofkm.us/nix/overpass.git";
  };

  outputs = { nixpkgs, overpass, ... }: {
    nixosConfigurations.gateway = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        overpass.nixosModules.default
        ./configuration.nix
      ];
    };
  };
}

The flake exposes:

  • packages.<system>.default / overpass — package containing overpass, op, and opctl.
  • Apps named overpass, op, and opctl for nix run.
  • An overlay as overlays.default.
  • NixOS modules: nixosModules.default, nixosModules.overpass, and nixosModules.overpass-node.

Package outputs are provided for x86_64-linux, aarch64-linux, and aarch64-darwin.

Run the control plane

Enable the control-plane module in configuration.nix:

{
  services.overpass = {
    enable = true;
    openFirewall = true;
    settings = {
      cluster_name = "production";
      proxy = {
        public_addr = "overpass.example.com";
        public_url = "https://overpass.example.com";
      };
    };
  };
}

Apply the configuration and inspect the service:

sudo nixos-rebuild switch --flake .#gateway
systemctl status overpass
sudo journalctl -u overpass -b

On its first start, Overpass writes a one-time setup claim code to the journal. Open https://overpass.example.com/setup, enter that code, and create the first administrator. With the default self-signed certificate, the browser will show a certificate warning until you configure a trusted certificate.

The module:

  • Creates the overpass system user and group.
  • Creates /var/lib/overpass with mode 0700.
  • Adds the selected package to environment.systemPackages.
  • Generates the YAML configuration in the Nix store.
  • Runs overpass server as a hardened systemd service.
  • Marks the executable as package-managed so it cannot replace itself.

services.overpass.dataDir always becomes the generated data_dir; a data_dir value inside settings is ignored. The directory contains the SQLite database, certificate-authority keys, TLS keys, and the server-managed config directory at <dataDir>/overpass.d, which holds configuration made through the web UI (settings, roles, SSO connectors, desktops). Include the whole directory in your backups. Anything declared in settings is authoritative and shows as read-only in the web UI; set settings.config_dir to relocate the writable layer.

If you change services.overpass.user or services.overpass.group, create the custom account or group elsewhere in your NixOS configuration. The module only creates the default overpass user and group automatically.

Listener ports and the firewall

Without TLS routing, the default listeners are:

Port Purpose
8443 Web UI, REST API, OIDC, and browser terminal
3023 Client SSH routing
3024 Node reverse tunnels
3025 Login, enrollment, and administrative gRPC

Set services.overpass.openFirewall = true to allow the ports configured in settings.proxy. When proxy.tls_routing = true, the module opens only the configured web listener because clients and nodes share it through TLS ALPN or WebSocket routing.

For a reverse-proxy deployment, configure public_url, public_addr, and any trusted proxy networks explicitly. See Reverse proxy for the single-port routing examples.

Keep secrets outside the Nix store

Values under services.overpass.settings are rendered into a file in the Nix store. Do not put passwords, tokens, or private credentials directly in that attribute set.

Reference environment-variable names from the generated configuration and supply their values with environmentFile:

{
  services.overpass = {
    environmentFile = "/run/secrets/overpass.env";
    settings = {
      auth.oidc = [
        {
          name = "sso";
          issuer_url = "https://id.example.com";
          client_id = "overpass";
          client_secret_env = "OVERPASS_OIDC_CLIENT_SECRET";
        }
      ];
    };
  };
}

The environment file uses systemd KEY=value syntax:

OVERPASS_OIDC_CLIENT_SECRET=replace-with-secret

Create that file with sops-nix, agenix, or another secret manager. The module rejects inline OIDC client_secret values to prevent this common leak.

Enroll a node

Create a one-time join token from the dashboard, or locally on the control plane:

sudo -u overpass opctl --local --data-dir /var/lib/overpass tokens add

Store the token in a root-readable secret file on the node, then configure the node service:

{
  services.overpass-node = {
    enable = true;
    name = "web-1";
    proxy = "overpass.example.com:3025";
    tlsPin = "sha256:BASE64_CERTIFICATE_FINGERPRINT";
    joinTokenFile = "/run/secrets/overpass-join";
    labels = {
      env = "production";
      role = "web";
    };
  };
}

Apply the node configuration and follow enrollment:

sudo nixos-rebuild switch --flake .#web-1
systemctl status overpass-node
sudo journalctl -u overpass-node -b -f

The module passes the token with systemd credentials. Its value does not appear in the generated unit command line or in the Nix store. The service performs an enrollment-only check before starting the long-running agent and reads the token only when no valid identity already exists.

After the first successful enrollment, remove joinTokenFile and delete the one-time token secret. The enrolled identity remains under /var/lib/overpass-node.

TLS to the control plane has three modes: with a publicly trusted certificate, omit both options and the agent does standard PKI validation; insecure = true disables verification and is for bootstrap/testing only; tlsPin remains an optional advanced override for self-signed clusters.

For initial testing against a self-signed endpoint, you can use:

services.overpass-node.insecure = true;

The node needs only outbound access to the control plane; it does not require an inbound firewall rule.

Remote sessions on NixOS

The node runs as root because it must switch each SSH session to the requested local Unix account. It resolves that account's configured login shell and starts the session with these Nix profile directories before the system PATH:

  • $HOME/.nix-profile/bin
  • /etc/profiles/per-user/<username>/bin

Override the system portion with services.overpass-node.sessionPath when the host needs additional commands or wrapper directories.

Module option reference

services.overpass

Option Default Description
enable false Start the Overpass control plane.
package Flake package Package containing overpass, op, and opctl.
user "overpass" Service user. Custom users must already exist.
group "overpass" Service group. Custom groups must already exist.
dataDir "/var/lib/overpass" Persistent database, CA/TLS state, and the writable config directory (overpass.d).
settings { } Attributes serialized to the Overpass YAML configuration. Declared values are authoritative and read-only in the web UI.
environmentFile null Absolute path to a systemd environment file containing secrets.
openFirewall false Allow the configured listener ports through the NixOS firewall.
debug false Run the control plane with debug logging.

See Configuration for the complete set of values accepted under settings.

services.overpass-node

Option Default Description
enable false Start the node agent.
package overpass-node variant Node executable; the web-free overpass-node build by default.
name networking.hostName Name presented to users and the control plane.
proxy Required Control-plane gRPC address in host:port form.
tunnel Learned during enrollment Optional explicit reverse-tunnel address.
dataDir "/var/lib/overpass-node" Persistent node identity directory.
labels { } Labels requested during initial enrollment.
joinTokenFile null Root-readable one-time enrollment-token file.
tlsPin null Control-plane certificate pin in sha256:<base64> form.
insecure false Disable certificate verification for testing or bootstrap.
sessionPath NixOS system paths System PATH appended after user-specific Nix profiles.
debug false Run the node with debug logging.

Update and roll back

Nix-built executables identify themselves as package-managed and never replace files in the Nix store. Update the locked Overpass input and rebuild:

nix flake update overpass
sudo nixos-rebuild switch --flake .#gateway

Apply the same input update to node configurations so the fleet converges on one version. If the fleet target is newer than a deployed Nix package, the Updates page keeps that control plane or node pending until its system is rebuilt.

Use the normal NixOS rollback if a new generation fails:

sudo nixos-rebuild switch --rollback

Troubleshoot

Inspect the effective units and recent logs:

systemctl cat overpass
systemctl cat overpass-node
sudo journalctl -u overpass -b
sudo journalctl -u overpass-node -b

Check the control-plane health endpoint locally:

curl --fail --insecure https://127.0.0.1:8443/healthz

Common failures:

  • A fresh node without joinTokenFile cannot enroll. Add the credential, rebuild, and remove it only after enrollment succeeds.
  • A node that cannot verify the control-plane certificate fails to connect. Use a publicly trusted certificate (set neither option), tlsPin for a self-signed control plane, or insecure = true for bootstrap only.
  • Inline OIDC secrets are rejected because settings is stored in the Nix store. Move them to environmentFile.
  • A custom control-plane user or group must be declared separately and needs write access to dataDir.
  • With openFirewall = false, open the configured ports through your own firewall rules or keep them intentionally private.