Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CLI

Flat reference for all nixfleet CLI commands and flags.

Global options

FlagEnv varDefaultDescription
--control-plane-urlNIXFLEET_CONTROL_PLANE_URLhttp://localhost:8080Control plane URL
--api-keyNIXFLEET_API_KEY""API key for control plane authentication
--client-certNIXFLEET_CLIENT_CERT""Client certificate for mTLS authentication
--client-keyNIXFLEET_CLIENT_KEY""Client key for mTLS authentication
--ca-certNIXFLEET_CA_CERT""CA certificate for TLS verification (uses system trust store if omitted)
--json-falseOutput structured JSON (on commands that produce tables/detail views)
--config--Path to .nixfleet.toml (default: walk up from cwd)
-v, --verbose-0Verbosity: -v shows INFO milestones + subprocess rolling window + progress bar; -vv shows raw passthrough (debug)

Logging is controlled via RUST_LOG (overrides -v/--verbose when set).

Configuration sources

The CLI reads connection settings from four layers, in priority order (highest wins):

  1. CLI flags (--control-plane-url, --api-key, …)
  2. Environment variables (NIXFLEET_* shown above)
  3. ~/.config/nixfleet/credentials.toml - user-level API keys, keyed by CP URL (auto-saved by nixfleet bootstrap)
  4. .nixfleet.toml - repo-level config, from --config <path> or discovered by walking up from cwd

This means the same CLI commands run with no flags from any fleet repo, inheriting the repo’s connection settings and the user’s bootstrapped credentials. See .nixfleet.toml format below.

mTLS example (with config file):

# One-time setup (creates .nixfleet.toml)
nixfleet init \
  --control-plane-url https://cp-01:8080 \
  --ca-cert modules/_config/fleet-ca.pem \
  --client-cert '/run/agenix/agent-${HOSTNAME}-cert' \
  --client-key '/run/agenix/agent-${HOSTNAME}-key' \
  --cache-url http://cache:5000 \
  --push-to ssh://root@cache

# Bootstrap first admin key (auto-saves to ~/.config/nixfleet/credentials.toml)
nixfleet bootstrap

# Subsequent commands: no flags needed
nixfleet machines list
nixfleet release create
nixfleet deploy --release rel-abc123 --hosts 'web-*' --wait

deploy

Deploy config to fleet hosts.

nixfleet deploy [FLAGS]
FlagTypeDefaultDescription
--release <ID>stringDeploy an existing release (required for rollout mode unless using --push-to / --copy)
--push-to <URL>stringBuild all hosts, push to a Nix binary cache URL, and register a release implicitly (e.g., ssh://root@cache, s3://bucket)
--hookboolfalseUse hook mode: push via [cache.hook] push-cmd instead of nix copy. Requires [cache.hook] in .nixfleet.toml or --hook-push-cmd
--hook-push-cmd <CMD>stringOverride hook push command ({} = store path). Requires --hook
--hook-url <URL>stringOverride hook cache URL for agents to pull from. Requires --hook
--copyboolfalseBuild all hosts, push to each target via nix-copy-closure (no binary cache needed), and register a release implicitly
--hosts <PATTERN>string (comma-separated or repeatable)*Host glob patterns. In SSH mode: hosts to deploy. In rollout mode: target machines directly (alternative to --tags)
--tags <TAG>string (comma-separated or repeatable)Target machines by tag - filters both the release build and rollout targeting (only hosts with a matching services.nixfleet-agent.tags value are built)
--dry-runboolfalseBuild closures and show plan, do not push or register
--sshboolfalseSSH fallback mode: build locally, copy via SSH, run switch-to-configuration (no CP needed)
--target <SSH>stringSSH target override (e.g., root@192.168.1.10). Only valid with --ssh and a single host.
--flake <REF>string.Flake reference
--strategy <STRATEGY>stringall-at-onceRollout strategy: canary, staged, all-at-once
--batch-size <SIZES>string (comma-separated)Batch sizes (e.g., 1,25%,100%)
--failure-threshold <N>string0Max unhealthy machines per batch before pausing/reverting. Accepts absolute count or percentage (e.g. 30%)
--on-failure <ACTION>stringpauseAction on batch failure: pause (stop and wait for rollout resume) or revert (roll back to previous generation)
--health-timeout <SECS>u64300Seconds to wait for health reports per batch
--waitboolfalseStream rollout progress; exits non-zero if rollout pauses or fails
--wait-timeout <SECS>u64300Timeout in seconds for --wait (0 = wait forever)
--cache-url <URL>stringBinary cache URL for agents to fetch closures from (overrides the release’s cache_url)

Modes:

  • SSH mode (--ssh): Builds locally, copies closures via SSH, activates on target. No control plane required. Platform-aware: NixOS hosts use switch-to-configuration switch, Darwin hosts use nix-env --set + activate (auto-detected from the host’s platform).

Note: --ssh deploys directly via nix-copy-closure and activation, bypassing the control plane entirely. Lifecycle state is not checked - a machine in maintenance will still receive the deploy. Use --ssh as an emergency escape hatch when the CP is unavailable, not as a routine deployment method.

Darwin SSH deploy requirements: SSH deploy to Darwin hosts connects as $USER@host (not root@ - macOS disables root SSH login). This requires:

  1. Username match: The operator’s local username must exist on the Darwin target with SSH key access. Override with --target user@host for single-host deploys if usernames differ.
  2. Passwordless sudo: Activation requires root. The target must allow passwordless sudo for nix-env and the activation script:
    # nix-darwin: security.sudo.extraConfig
    s33d ALL=(root) NOPASSWD: /nix/var/nix/profiles/default/bin/nix-env *
    s33d ALL=(root) NOPASSWD: /nix/store/*/activate
    
  3. SSH key access: The operator’s SSH public key must be in the target user’s authorized keys.

For production mixed-fleet deploys, prefer the CP rollout path - the agent runs as root (launchd daemon), pulls from cache, and activates locally with no SSH user/sudo requirements.

  • Rollout mode (requires a release): Creates a rollout on the control plane with the specified strategy. Specify an existing release with --release <ID>, or use --push-to <url> / --hook / --copy to build + push + register implicitly in one command.
  • Hook mode (--hook): Uses [cache.hook] push-cmd from .nixfleet.toml to push closures (e.g., attic push mycache {}). Overrides --push-to and uses [cache.hook] url as the cache URL for agents. Flags --hook-push-cmd and --hook-url override the config values.
  • Targeting: Use --tags <TAG> or --hosts <pattern> to select machines. Both are intersected with the release’s host list (machines not in the release are skipped with a warning).

init

Create a .nixfleet.toml config file in the current directory. Run this once per fleet repo to set the connection and deploy defaults.

nixfleet init [FLAGS]
FlagTypeDefaultDescription
--control-plane-url <URL>string– (required)Control plane URL
--ca-cert <PATH>stringCA certificate path (relative to config file or absolute)
--client-cert <PATH>stringClient certificate path (supports ${HOSTNAME} expansion)
--client-key <PATH>stringClient key path (supports ${HOSTNAME} expansion)
--cache-url <URL>stringDefault binary cache URL for agents
--push-to <URL>stringDefault push destination for release create
--hook-url <URL>stringHook mode cache URL (e.g., http://cache:8081/mycache for Attic)
--hook-push-cmd <CMD>stringHook mode push command ({} = store path, e.g., attic push mycache {})
--strategy <STRATEGY>stringDefault deploy strategy (canary, staged, all-at-once)
--on-failure <ACTION>stringDefault deploy failure action (pause, revert)

After init, run nixfleet bootstrap to create and auto-save the first admin API key.


release create

Build host closures, distribute them, and register a release manifest in the control plane. A release is an immutable mapping of hostnames to built store paths that subsequent rollouts can target.

nixfleet release create [FLAGS]
FlagTypeDefaultDescription
--flake <REF>string.Flake reference
--hosts <PATTERN>string*Host glob pattern or comma-separated list
--push-to <URL>stringPush closures to this Nix cache URL via nix copy --to (e.g., ssh://root@cache, s3://bucket)
--hookboolfalseUse hook mode: push via [cache.hook] push-cmd instead of nix copy
--hook-push-cmd <CMD>stringOverride hook push command ({} = store path). Requires --hook
--hook-url <URL>stringOverride hook cache URL. Requires --hook
--copyboolfalsePush closures directly to each target host via nix-copy-closure (no binary cache)
--cache-url <URL>stringOverride the cache URL recorded in the release (defaults to --push-to URL, or config file)
--eval-onlyboolfalseEvaluate config.system.build.toplevel.outPath without building. Assumes closures are already in the cache (e.g., CI-built). Incompatible with --push-to, --hook, --copy
--dry-runboolfalseBuild and show the manifest without pushing or registering
--allow-dirtyboolfalseSkip the dirty working tree check

Output prints the release ID, host count, and per-host store paths. Use the ID with nixfleet deploy --release <ID>.


release list

List recent releases.

nixfleet release list [FLAGS]
FlagTypeDefaultDescription
--limit <N>u3220Number of releases to show (newest first)
--host <HOSTNAME>stringFilter releases to those containing entries for this hostname

release show

Show a release’s full metadata and per-host entries.

nixfleet release show <ID>
ArgumentTypeDescription
<ID>stringRelease ID

release diff

Diff two releases: added hosts, removed hosts, changed store paths, unchanged.

nixfleet release diff <ID_A> <ID_B>
ArgumentTypeDescription
<ID_A>stringFirst release ID
<ID_B>stringSecond release ID

release delete

Delete a release. Fails with exit code 1 if the release is still referenced by a rollout - the control plane returns 409 in that case to prevent breaking rollout history.

nixfleet release delete <RELEASE_ID>
ArgumentTypeDescription
<RELEASE_ID>stringID of the release to delete

Exit codes:

  • 0 - release deleted (CP returned 204)
  • 1 - release still referenced by a rollout (CP returned 409), release not found (CP returned 404), or another non-2xx status

status

Show fleet status from the control plane.

nixfleet status [FLAGS]
FlagTypeDefaultDescription
--stale-threshold <SECS>u64600Seconds without a report before a machine is marked stale
--watchboolfalseContinuously refresh the display (clears screen, Ctrl+C to exit). Incompatible with --json
--interval <SECS>u642Refresh interval in seconds (requires --watch)

Outputs a table of all machines. Pass --json (global flag) for structured JSON output.


rollback

Rollback a single machine to a previous generation via SSH. Activates the previous generation directly on the target, then notifies the control plane so desired generation stays in sync.

nixfleet rollback --host <HOST> --ssh [FLAGS]
FlagTypeDefaultDescription
--host <HOST>string– (required)Target host name
--generation <PATH>stringStore path to roll back to (default: previous generation from system-1-link)
--targetstring-SSH target override (e.g. root@192.168.1.10)
--darwinboolfalseTarget is a Darwin (macOS) host - uses $USER@host, sudo activate instead of switch-to-configuration

Rollback always operates via SSH. The --ssh flag is accepted for backwards compatibility but hidden from --help. For CP-driven rollback, use --on-failure revert on rollouts, or deploy an older release. After a successful rollback, the CP is notified (best-effort) so nixfleet status shows the machine in sync.

Darwin rollback: Use --darwin for macOS hosts. This runs nix-env --set + activate instead of switch-to-configuration:

nixfleet rollback --host aether --ssh --darwin

host add

Scaffold a new host.

nixfleet host add --hostname <NAME> [FLAGS]
FlagTypeDefaultDescription
--hostname <NAME>string– (required)Host name for the new machine
--org <ORG>stringmy-orgOrganization name
--role <ROLE>stringworkstationHost role (workstation, server, edge, kiosk)
--platform <PLATFORM>stringx86_64-linuxTarget platform
--target <SSH>stringSSH target to fetch hardware config (e.g., root@192.168.1.42)

rollout list

List rollouts.

nixfleet rollout list [FLAGS]
FlagTypeDefaultDescription
--status <STATUS>stringFilter by status (e.g., running, paused, completed)
--sort <FIELD>stringcreatedSort by: created (newest first), status, strategy

rollout status

Show rollout detail with batch breakdown.

nixfleet rollout status <ID> [FLAGS]
Argument/FlagTypeDefaultDescription
<ID>stringRollout ID
--waitboolfalseBlock until rollout completes, fails, is cancelled, or pauses. Exits non-zero on failure or pause
--wait-timeout <SECS>u64300Timeout in seconds for --wait (0 = wait forever)
--watchboolfalseContinuously refresh the display (clears screen, Ctrl+C to exit). Incompatible with --wait and --json
--interval <SECS>u642Refresh interval in seconds (requires --watch)

rollout resume

Resume a paused rollout.

nixfleet rollout resume <ID>
ArgumentTypeDescription
<ID>stringRollout ID

rollout cancel

Cancel a rollout.

nixfleet rollout cancel <ID>
ArgumentTypeDescription
<ID>stringRollout ID

bootstrap

Create the first admin API key. Only works when no keys exist in the control plane.

nixfleet bootstrap [FLAGS]
FlagTypeDefaultDescription
--name <NAME>stringadminName for the admin key
--save-key <KEY>stringSave an existing API key without calling the CP (for setting up additional machines)

Output: Human-friendly info to stderr, raw key to stdout. Scriptable:

API_KEY=$(nixfleet bootstrap)

Returns exit code 1 with an error message if keys already exist (409).

Note: No --api-key needed (chicken-and-egg). mTLS is still required when the CP has --client-ca set.

Multi-machine setup: Bootstrap once on your primary machine, then use --save-key on additional machines to share the same API key without re-bootstrapping:

# On the primary machine:
nixfleet bootstrap

# On additional machines (same fleet):
nixfleet bootstrap --save-key nfk-abc123...

completions

Generate a shell completion script.

nixfleet completions <SHELL>
ArgumentTypeDescription
<SHELL>stringTarget shell: zsh, bash, or fish

Source the output in your shell profile:

# zsh
nixfleet completions zsh > ~/.zsh/completions/_nixfleet

# bash
nixfleet completions bash > /etc/bash_completion.d/nixfleet

# fish
nixfleet completions fish > ~/.config/fish/completions/nixfleet.fish

machines register

Register a machine with the control plane (admin endpoint).

nixfleet machines register <ID> [FLAGS]
Argument/FlagTypeDescription
<ID>stringMachine ID
--tags <TAG>string (comma-separated or repeatable)Initial tags

Agents auto-register on first health report, so manual registration is optional. Use this to pre-register machines before they come online.


machines list

List machines.

nixfleet machines list [FLAGS]
FlagTypeDefaultDescription
--tags <TAG>string (comma-separated or repeatable)Filter by tags (machines matching any listed tag are shown)
--watchboolfalseRefresh the list on an interval (clears screen, Ctrl+C to exit). Incompatible with --json
--interval <SECS>u642Refresh interval in seconds (requires --watch)

machines set-lifecycle

Change a machine’s lifecycle state.

nixfleet machines set-lifecycle <ID> <STATE>
ArgumentTypeDescription
<ID>stringMachine ID
<STATE>stringLifecycle state: active, pending, provisioning, maintenance, decommissioned

Only active machines participate in rollouts. Machines in maintenance or decommissioned state are excluded even when explicitly targeted by hostname. Use maintenance to temporarily remove a machine from fleet operations without deregistering it.


machines clear-desired

Clear a machine’s stale desired generation. Use this when an agent is stuck polling for a generation that will never be fulfilled (e.g., after a cancelled rollout).

nixfleet machines clear-desired <ID>
ArgumentTypeDescription
<ID>stringMachine ID

Exit codes:

  • 0 - desired generation cleared (CP returned 204)
  • 1 - machine not found (CP returned 404), or another non-2xx status

machines notify-deploy

Notify the control plane of an out-of-band deploy (e.g. SSH). Sets the machine’s desired generation to the deployed store path so nixfleet status shows the machine in sync once the agent confirms.

Called automatically by deploy --ssh after a successful switch. Also available manually for other out-of-band deploy workflows.

nixfleet machines notify-deploy <ID> <STORE_PATH>
ArgumentTypeDescription
<ID>stringMachine ID
<STORE_PATH>stringStore path that was deployed

Requires deploy or admin role.


rollout delete

Delete a terminal rollout (completed, cancelled, or failed). The control plane rejects deletion of active rollouts with 409.

nixfleet rollout delete <ID>
ArgumentTypeDescription
<ID>stringRollout ID

Exit codes:

  • 0 - rollout deleted (CP returned 204)
  • 1 - rollout is still active (CP returned 409), rollout not found (CP returned 404), or another non-2xx status

Operation logs

All CLI operations (deploy, release create, rollout commands) write persistent logs to:

~/.local/state/nixfleet/logs/

Each operation creates a JSONL file with timestamped entries covering subprocess invocations (command, stdout, stderr, exit code), tracing events, and host context. Logs are written regardless of verbosity level.


.nixfleet.toml format

Committed to the fleet repo root. Discovered by walking up from the CLI’s current working directory. All fields optional - CLI flags and environment variables always override.

[control-plane]
url = "https://cp.example.com:8080"
ca-cert = "modules/_config/fleet-ca.pem"    # relative to config file location

[tls]
client-cert = "/run/agenix/agent-${HOSTNAME}-cert"
client-key = "/run/agenix/agent-${HOSTNAME}-key"

[cache]
url = "http://cache.example.com:5000"          # default --cache-url for rollouts
push-to = "ssh://root@cache.example.com"       # default --push-to for release create

[cache.hook]                                    # used when --hook is passed
url = "http://cache.example.com:8081/mycache"   # overrides cache.url for the release
push-cmd = "attic push mycache {}"              # {} is replaced with the store path

[deploy]
strategy = "staged"             # default rollout strategy
health-timeout = 300            # default health timeout in seconds
failure-threshold = "0"
on-failure = "pause"

Environment variable expansion: values support ${VAR} expansion. ${HOSTNAME} and ${HOST} fall back to the gethostname() syscall if not set in the environment (so they work from zsh where $HOST is a shell builtin, not exported). This lets the same .nixfleet.toml work across every fleet host when agent cert paths follow a per-hostname convention.

Relative paths (like ca-cert = "modules/_config/fleet-ca.pem") are resolved relative to the .nixfleet.toml location, not the CLI’s working directory.

~/.config/nixfleet/credentials.toml format

User-level, mode 600, not checked into any repo. Written automatically by nixfleet bootstrap and keyed by CP URL to support multiple clusters.

["https://cp.example.com:8080"]
api-key = "nfk-73c713cc..."

["https://cp-staging.example.com:8080"]
api-key = "nfk-abc..."

On impermanent NixOS hosts, add .config/nixfleet to home-manager persistence so the credentials file survives reboots.