Skip to content

NKey file references

Michael Utech edited this page Jun 29, 2026 · 1 revision

Any nkey or seed value in the server configuration may be given as a file:// URL instead of an inline string. The server reads the referenced file when it parses the configuration and uses its (whitespace-trimmed) contents as the value.

Why

Seeds are secrets. Inlining them puts the secret in the config file — and wherever that file ends up (version control, an image, a build artifact, a world-readable store path). A file:// reference keeps the config free of secret material: the config carries only the path, and a secret manager (agenix, sops, systemd credentials, a tmpfs drop, …) provides the file at runtime.

Only the seed is actually secret; user/account public keys are not. The feature applies to public-key fields too, which is convenient when one mechanism owns all key material.

Where it applies

Every configuration field that takes an nkey or seed:

  • leafnode remote seed — leafnodes.remotes[].nkey (a.k.a. seed)
  • leafnode authorization — leafnodes.authorization.nkey
  • account identity — accounts.<name>.nkey
  • authorization / client users — authorization.users[].nkey

Syntax

nkey: "file:///absolute/path/to/key"

A plain inline value works exactly as before. Any value that is not a file:// URL is used verbatim.

Behaviour

  • The file is read synchronously at config parse time; its contents are trimmed of surrounding whitespace (a trailing newline is fine).
  • The resolved value is then validated exactly like an inline value — a seed field must contain a valid user seed; a public-key field must contain a valid public key.

Deferred validation when the file is absent

If the referenced file cannot be read when the configuration is parsed, the reference is left unresolved rather than failing, and key validation for that value is skipped. This is for config-check (nats-server -t) in an environment where the secret has not been deployed yet: the check should not require the live secret.

At normal runtime the file is present, so the value resolves and validates as usual. A genuinely missing file at runtime therefore surfaces as a connection/authentication failure (when the key is used), not as a parse error.

Example

A leaf that authenticates with a seed delivered by a secret manager:

leafnodes {
  remotes = [
    {
      urls: [ "nats-leaf://hub.example:7422" ]
      account: "A"
      nkey: "file:///run/secrets/leaf.seed"
    }
  ]
}

The matching public key is registered on the hub as usual (inline, since it is not secret), or likewise via file:// if preferred.

Clone this wiki locally