hipfire
/docs · beta branch · view source · edit on GitHub

NixOS

Installing and running hipfire on NixOS.

First-class Nix flake support lives at the repo root (flake.nix) with packages and the NixOS module under nix/.

OutputPath / attributeRole
Packagepackages.hipfire (default)Native hipfire CLI + wrapped hipfire-daemon
Kernelspackages.hipfire-kernelsOptional precompile via scripts/compile-kernels.sh
Dev shelldevShells.defaultRust and hipcc/ROCm tools when rocmSupport
ModulenixosModules.defaultservices.hipfire systemd unit(s)
Overlayoverlays.defaultPins rocmPackages from this flake’s nixpkgs + hipfire packages
FieldValue
Flake nixpkgs inputgithub:NixOS/nixpkgs/nixos-unstable
Module default port11435
System model dir option default/var/lib/hipfire/models (see modelDir limitation)
Kernel package default targets[] (JIT only unless overridden)

Truth state: shipped / ref-pinned for the flake/module surface described here. Pin a release tag or commit for production hosts; building github:warpfront/hipfire without a rev follows that input’s default branch and is not a stable release pin. Branch-only inference features are not implied by enabling the module.

Prerequisites

  • NixOS with flakes (examples below use nixos-unstable or a 25.11-class channel for your system flake; hipfire’s own flake input is unstable for ROCm 7.x — see ROCm).
  • AMD GPU with amdgpu loaded; /dev/kfd and /dev/dri/renderD* present.
  • Interactive users in video and render when not using the system service user.
ls /dev/kfd
ls /dev/dri/
# architecture hints:
rocminfo 2>/dev/null | grep -oP 'amdgcn-amd-amdhsa--\K\S+' | sort -u
# or:
grep gfx_target_version /sys/class/kfd/kfd/topology/nodes/*/properties

Quick start (flake packages)

Dev shell

nix develop github:warpfront/hipfire

Tools only — not a checkout. From a local tree:

nix develop
cargo build --release --features deltanet --example daemon -p hipfire-runtime

Build CLI + daemon

nix build github:warpfront/hipfire
./result/bin/hipfire run qwen3.5:9b "Hello"

Stable pin example (replace rev/hash with a real release or commit):

hipfire.url = "github:warpfront/hipfire/<tag-or-commit>";

Precompiled kernels package

nix build github:warpfront/hipfire#hipfire-kernels

Default gpuTargets = [] produces an empty/near-empty kernels tree; the daemon JIT-compiles on first use. Override targets in the NixOS module or your own package override — an empty default avoids baking the wrong arch.

NixOS module

nixosModules.default → services.hipfire.

Minimal system example

# flake.nix (host)
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    hipfire.url = "github:warpfront/hipfire"; # pin rev for production
  };

  outputs = { nixpkgs, hipfire, ... }: {
    nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        hipfire.nixosModules.default
        {
          nixpkgs.overlays = [ hipfire.overlays.default ];
          services.hipfire.enable = true;
          services.hipfire.gpuTargets = [ "gfx1100" ]; # required non-empty
        }
      ];
    };
  };
}

Assertions when enable = true:

  • gpuTargets must be non-empty (Nix cannot probe the GPU at eval time).
  • With rocmSupport = true (default), hardware.graphics.enable must be true (the module defaults it on and registers the ROCm ICD).

Full options example

services.hipfire = {
  enable = true;
  gpuTargets = [ "gfx1100" ]; # required non-empty
  openFirewall = false;  # does NOT change listen address — only skips opening the port when the host firewall is enabled
  # openFirewall = true opens cfg.port with NO auth/TLS on the listener — see SERVE.md

  defaultModel = "qwen3.5:9b";
  # Module writes these into config.toml as stored globals. run/serve sampling
  # send still omits bare globals (CLI flags / per-model / registry card only);
  # see CONFIG.md. Chat uses the global snapshot exception.
  temperature = 0.3;
  topP = 0.8;
  maxTokens = 512;       # module default; CLI global default is 4096 if unset in config
  maxSeq = 32768;
  repeatPenalty = 1.05;
  kvCache = "auto";
  dflashMode = "off";
  idleTimeout = 300;

  # Writes native ~/.hipfire/models.toml per-model overrides.
  perModelSettings = {
    "qwen3.5:27b" = {
      max_seq = 16384;
      kv_cache = "q8";
      dflash_mode = "on";
    };
  };

  extraSettings = {
    # Canonical dotted keys are preferred; legacy snake_case aliases are
    # translated before writing config.toml.
    # Actual loopback-only listen (openFirewall=false alone is not enough):
    host = "127.0.0.1";
  };

  environment = {
    HIPFIRE_NORMALIZE_PROMPT = "1";
  };

  # Exported as HIPFIRE_MODELS_DIR and honored by the native CLI/TUI.
  modelDir = "/var/lib/hipfire/models";
};

Typed options are written to config.toml using the canonical dotted schema (serve.default_model, generation.top_p, serve.idle_timeout_seconds, …). See CONFIG.md for the full key set the CLI understands; module-typed keys are the subset in nix/module.nix.

modelDir and effective paths

services.hipfire.modelDir is exported as HIPFIRE_MODELS_DIR. The native CLI and TUI use it for tag/list/pull/pre-warm discovery:

ModeHOMEEffective CLI models path
System service (userService = false)/var/lib/hipfireconfigured services.hipfire.modelDir
User servicethe login user’s homeconfigured services.hipfire.modelDir

hipfire-setup creates modelDir, and the system service includes it in ReadWritePaths. Absolute model paths remain valid for ad-hoc runs.

Desktop / user service

services.hipfire = {
  enable = true;
  userService = true;
  gpuTargets = [ "gfx1201" ];
  # Must be user-writable. Default /var/lib/hipfire/models is not; setup's
  # mkdir -p fails for an unprivileged user unit.
  modelDir = "/home/yourname/.hipfire/models";  # match effective CLI path
  defaultModel = "";                             # module starts --no-prewarm
};

users.users.yourname.extraGroups = [ "video" "render" ];
# hipfire.service only has After=hipfire-setup — it does NOT Wants/Requires setup.
# Start setup first (or ensure default.target already ran it after login):
systemctl --user start hipfire-setup.service
systemctl --user start hipfire.service
systemctl --user status hipfire.service

User mode skips the dedicated hipfire system user and the oneshot precompile unit; setup still copies config and symlinks daemon/kernels under $HOME/.hipfire. Models for tag discovery must live under $HOME/.hipfire/models (see modelDir limitation).

Pinning source from the module

Precedence: services.hipfire.src > services.hipfire.github.rev > flake package default.

# branch / tag / commit via fetchFromGitHub
services.hipfire = {
  enable = true;
  gpuTargets = [ "gfx1100" ];
  github.rev = "v0.2.1";          # example pin — use a real tag/commit
  github.hash = "";               # first build prints the SRI hash
};

# fork
services.hipfire.github.owner = "my-username";
services.hipfire.github.repo = "hipfire";
services.hipfire.github.rev = "my-branch";
services.hipfire.github.hash = "sha256-…";

# Path source only (module type is nullOr path — not an arbitrary derivation).
# Use a checkout path or a path-coerced fetch result your Nix accepts as path:
services.hipfire.src = ./path/to/hipfire-checkout;
# Prefer github.* when you need fetchFromGitHub (typed owner/repo/rev/hash):
# services.hipfire.github.rev = "abc123…";
# services.hipfire.github.hash = "sha256-…";

Unreleased / working-branch checkouts are branch-implemented relative to whatever you pin — do not describe them as a stable release without an explicit tag or commit hash.

What the system service does

When userService = false (default):

UnitRole
hipfire-setup.serviceOneshot: native config.toml + models.toml, bin/daemon symlink, kernels symlink under /var/lib/hipfire/.hipfire
hipfire-precompile.serviceOneshot: hipfire-daemon --precompile (failure is warned; JIT still works)
hipfire.serviceExecStart = hipfire serve, restart on failure

Environment always includes HIPFIRE_MODELS_DIR=<modelDir>; the native CLI and TUI honor it for discovery and model lifecycle. With rocmSupport, LD_LIBRARY_PATH is set for nixpkgs ROCm. System mode also sets HOME=/var/lib/hipfire and HIPFIRE_KERNEL_CACHE=/var/cache/hipfire/kernels.

Security: hipfire serve has no authentication and no TLS (SERVE.md). Default listen remains 0.0.0.0 unless you set extraSettings.host = "127.0.0.1" (or equivalent). openFirewall = false does not change the listen address — it only avoids opening cfg.port when the host firewall is enabled. Keep the firewall closed unless the host is on a trusted network or an authenticated TLS reverse proxy terminates in front of the port. Prefer probing on loopback (127.0.0.1).

Device policy allows /dev/kfd, DRM char devices, and /dev/accel/accel0.

HTTP contract after start: SERVE.md.

GPU targets

Set only arches you need. Common labels (not an admission list):

ArchTypical hardware
gfx906Vega 20 / MI50
gfx908MI100
gfx1010RX 5700 XT
gfx1030RX 6800 XT class
gfx1100RX 7900 XTX class
gfx1151Strix Halo
gfx1200Radeon AI PRO R9700 class
gfx1201RX 9070 / 9070 XT class
services.hipfire.gpuTargets = [ "gfx1100" "gfx1030" ];

Module options reference

From nix/module.nix (defaults are module defaults, not every CLI default):

OptionTypeDefaultDescription
enableboolfalseEnable the service
openFirewallboolfalseOpen port when the host firewall is enabled; does not change listen address (default still 0.0.0.0)
packagepackagepkgs.hipfirePackage to run
srcpath or nullnullOverride source path (not a package/derivation)
github.ownerstr"warpfront"fetch owner
github.repostr"hipfire"fetch repo
github.revstr or nullnullbranch/tag/commit
github.hashstr""SRI hash for fetch
kernelsPackagepackagepkgs.hipfire-kernelsPrecompiled kernels
gpuTargetslist of str[] (required non-empty when enabled)Kernel compile arches
rocmSupportbooltrueUse nixpkgs ROCm + graphics ICD
portport11435Serve port
defaultModelstr""Empty omits serve.default_model and makes the module start hipfire serve --no-prewarm; a non-empty value is pre-warmed
temperaturefloat0.3Stored in config.toml only; not an effective run/serve send default (see CONFIG.md sampling send)
topPfloat0.8Stored global; same send caveat as temperature
maxTokensint512Written to config.toml
maxSeqint32768KV capacity
repeatPenaltyfloat1.05Stored global; same send caveat as temperature
kvCachestr"auto"KV mode string
dflashModeenum"off"on / off / auto
idleTimeoutint300Idle unload seconds (0 = never)
extraSettingsattrs{}Extra config.toml keys (e.g. "serve.host" = "127.0.0.1"; legacy host is translated)
perModelSettingsattrs of attrs{}Writes native models.toml per-model overrides
environmentattrs of str{}Extra env for the unit
modelDirstr"/var/lib/hipfire/models"Exported as HIPFIRE_MODELS_DIR; native CLI/TUI model discovery uses it
userServiceboolfalsesystemd --user mode
user / groupstr"hipfire"System service identity

ROCm configuration

Default: flake overlay + nixpkgs ROCm

overlays.default re-imports this flake’s nixpkgs (unstable) with rocmSupport so rocmPackages tracks ROCm 7.x. Comment in-tree: nixos-25.11’s ROCm 6.4.3 libamdhip64 can segfault on gfx1151 during weight upload — prefer the overlay on Strix Halo hosts.

LD_LIBRARY_PATH is injected on the daemon/CLI wrappers and the systemd unit when rocmSupport = true.

Bring your own ROCm

services.hipfire = {
  rocmSupport = false;
  environment = {
    LD_LIBRARY_PATH = "/opt/rocm/lib";
  };
};

You must still satisfy GPU device access and any ICD requirements yourself.

Configuration precedence

Same layered model as the CLI:

  1. Engine / daemon defaults
  2. config.toml (module typed options + extraSettings)
  3. Per-model overlay from models.toml (perModelSettings)
  4. Environment (services.hipfire.environment and process env)

Interactive shells outside systemd only see variables you export yourself (env-vars.md).

Production smoke after deploy

Enabling the module starts hipfire serve; it does not by itself certify model quality or GPU numerical correctness.

On a machine with models in the effective CLI path (/var/lib/hipfire/.hipfire/models for the system service, or ~/.hipfire/models for user mode — not necessarily modelDir):

curl -s http://127.0.0.1:11435/health
# GPU serve semantics (manual); pass an absolute model file:
python3 scripts/serve_harness.py --model /var/lib/hipfire/.hipfire/models/<file> --tag qwen3.5:9b

Route selection: VALIDATION.md. No-GPU CI does not replace this.

Troubleshooting

libamdhip64.so not found

With rocmSupport = true, confirm the overlay is applied and rocmPackages.clr builds. With BYO ROCm, check LD_LIBRARY_PATH and the .so path.

Permission denied on /dev/kfd

Add the runtime user to video and render, rebuild, re-login (user sessions). The system service user already gets those groups.

No AMD GPU detected

lsmod | grep amdgpu
hardware.firmware = [ pkgs.linux-firmware ];
# and/or:
hardware.amdgpu.initrd.enable = true;

Ensure hardware.graphics.enable = true when using bundled ROCm.

Kernel pre-compilation fails

hipfire-precompile warns and continues; first request JIT-compiles. Check hipcc --version against the target arch. gfx1151 expects ROCm 7.2+ class tooling (aligned with the container base and flake overlay intent).

  • Serve HTTP: SERVE.md
  • Config keys: CONFIG.md
  • Containers: CONTAINER.md
  • Package implementation: nix/package.nix, nix/kernels.nix, nix/dev-shell.nix, nix/module.nix