Hermes NixOS Setup — Tutorial 12: Nix & Flake
Install Hermes Agent on NixOS: run it ad hoc, install it persistently, and wire up the NixOS module or the flake input.
Tutorial 12: Nix & NixOS Setup
Welcome back! In this tutorial, we’re diving into the Nix and NixOS setup for Hermes Agent. If you’re new to Nix, don’t worry — we’ll keep things friendly and practical.
What’s New in This Guide
The Nix support has been significantly updated. Hermes Agent now ships with a Nix flake, a NixOS module, and a Home Manager module. This means you have more flexibility than ever to run Hermes exactly how you like.
Before we jump in, a quick heads-up: Nix and NixOS are considered Tier 2 platforms. That means the flake and NixOS module are maintained on a best-effort basis, and commits to main may break these packages at any point. If you need a fully supported setup, stick with the standard Docker or FHS installation paths.
Choose Your Level
Here’s a quick overview of the options available:
| Level | Who it’s for | What you get |
|---|---|---|
nix run / nix profile install |
Any Nix user (macOS, Linux) | Pre-built binary with all deps — then use the standard CLI workflow |
| Home Manager module | An agent for one person, on any distribution or macOS | Declarative configuration and a user service, without root |
| NixOS module (native) | NixOS server deployments | Declarative config, hardened systemd service, managed secrets |
| NixOS module (container) | Agents that need self-modification | Everything above, plus a persistent Ubuntu container for apt/pip/npm install |
How This Differs from Standard Install
The classic curl | bash installer manages Python, Node, and dependencies itself. The Nix flake replaces all of that. Every Python dependency is a Nix derivation built by uv2nix, and runtime tools like Node.js, git, ripgrep, and ffmpeg are wrapped into the binary’s PATH. There’s no runtime pip, no venv activation, and no npm install.
For non-NixOS users, this only changes the install step. Everything after — hermes setup, hermes gateway install, config editing — works exactly like the standard install.
For NixOS module users, the entire lifecycle is different. Configuration lives in configuration.nix, secrets go through sops-nix or agenix, the service is a systemd unit, and CLI config commands are blocked. You manage Hermes the same way you manage any other NixOS service.
Runtime Pins
PM’s tool lock is also a Nix build input. nix/npm-pinned.nix reads the npm pin, and nix/pm-packages.nix exposes matching archives as pm-NAME derivations:
nix build .#pm-ripgrep
These outputs unpack the pinned archives. They are not the complete Hermes wrapper or a guarantee that each archive runs without platform integration. The application still uses the uv2nix environment and Nix wrapper.
nix/pythonLock.nix reads Python’s major/minor from pm/lock.json. The uv2nix environment, package overrides, plugin packages, and developer shell use that interpreter family. If the pinned nixpkgs lacks that family, evaluation stops instead of selecting a different Python.
Native Nix evaluation and builds remain CI gates. Update through Nix. Do not repair a Nix store path with pip.
Prerequisites
- Nix with flakes enabled — Determinate Nix is recommended (enables flakes by default)
- API keys for the services you want to use (at minimum: an OpenRouter or Anthropic key)
Quick Start (Any Nix User)
No clone needed. Nix fetches, builds, and runs everything:
# Run the desktop app
nix run github:NousResearch/hermes-agent#desktop
# Or install persistently
nix profile install github:NousResearch/hermes-agent#desktop
# Run the TUI
nix run github:NousResearch/hermes-agent -- setup
nix run github:NousResearch/hermes-agent -- --tui
# Or install it in your profile
nix profile install github:NousResearch/hermes-agent
hermes setup
hermes --tui
After nix profile install, hermes, hermes-agent, and hermes-acp are on your PATH. From here, the workflow is identical to the standard installation — hermes setup walks you through provider selection, hermes gateway install sets up a launchd (macOS) or systemd user service, and config lives in ~/.hermes/.
A note on size: The default package includes ALL libraries Hermes might need, adding about 700 MB to the closure. If you only need messaging platforms (Discord, Telegram, Slack), check the #messaging output — it adds just ~33 MB.
Running from a local clone? Here’s the quick version:
git clone https://github.com/NousResearch/hermes-agent.git
cd hermes-agent
nix develop
hermes setup
NixOS Module
The flake exports nixosModules.default — a full NixOS service module that declaratively manages user creation, directories, config generation, secrets, documents, and service lifecycle.
Important: This module needs NixOS. If you want an agent for one person (not a system service), use the Home Manager module instead — it runs on NixOS and any other system Home Manager supports.
Add the Flake Input
# /etc/nixos/flake.nix (or your system flake)
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
hermes-agent.url = "github:NousResearch/hermes-agent";
};
outputs = { nixpkgs, hermes-agent, ... }: {
nixosConfigurations.your-host = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
hermes-agent.nixosModules.default
./configuration.nix
];
};
};
}
Minimal Configuration
# configuration.nix
{ config, ... }: {
services.hermes-agent = {
enable = true;
settings.model.default = "anthropic/claude-sonnet-4";
environmentFiles = [ config.sops.secrets."hermes-env".path ];
addToSystemPackages = true;
};
}
That’s it! nixos-rebuild switch creates the hermes user, generates config.yaml, wires up secrets, and starts the gateway — a long-running service that connects the agent to messaging platforms and listens for incoming messages.
Secrets are required. The environmentFiles line assumes you have sops-nix or agenix configured. The file should contain at least one LLM provider key (e.g., OPENROUTER_API_KEY=sk-or-...). No secrets manager yet? You can use a plain file as a starting point — just make sure it’s not world-readable:
echo "OPENROUTER_API_KEY=sk-or-your-key" | sudo install -m 0600 -o hermes /dev/stdin /var/lib/hermes/env
services.hermes-agent.environmentFiles = [ "/var/lib/hermes/env" ];
Pro tip: Setting addToSystemPackages = true does two things: puts the hermes CLI on your system PATH and sets HERMES_HOME system-wide so the interactive CLI shares state (sessions, skills, cron) with the gateway service. Without it, running hermes in your shell creates a separate ~/.hermes/ directory.
Container-aware CLI
When container.enable = true and addToSystemPackages = true, every hermes command on the host automatically routes into the managed container. Your interactive CLI session then runs inside the same environment as the gateway service, with access to all container-installed packages and tools.
- The routing is transparent:
hermes chat,hermes sessions list,hermes --version, and so on all exec into the container under the hood - All CLI flags are forwarded as-is
- If the container isn’t running, the CLI retries briefly (5s with a spinner for interactive use, 10s silently for scripts) then fails with a clear error — no silent fallback
- For developers working on the hermes codebase, set
HERMES_DEV=1to bypass container routing and run the local checkout directly
Set container.hostUsers to create a ~/.hermes symlink to the service state directory, so the host CLI and the container share sessions, config, and memories:
services.hermes-agent = {
container.enable = true;
container.hostUsers = [ "your-username" ];
addToSystemPackages = true;
};
Users listed in hostUsers are automatically added to the hermes group for file permission access.
Podman users: The NixOS service runs the container as root. Docker users get access via the docker group socket, but Podman’s rootful containers require sudo. Grant passwordless sudo for your container runtime:
security.sudo.extraRules = [{
users = [ "your-username" ];
commands = [{
command = "/run/current-system/sw/bin/podman";
options = [ "NOPASSWD" ];
}];
}];
The CLI auto-detects when sudo is needed and uses it transparently. Without this, you’ll need to run sudo hermes chat manually.
Verify It Works
After nixos-rebuild switch, check that the service is running:
# Check service status
systemctl status hermes-agent
# Watch logs (Ctrl+C to stop)
journalctl -u hermes-agent -f
# If addToSystemPackages is true, test the CLI
hermes --version
hermes config # shows the generated config
Choosing a Deployment Mode
The module supports two modes, controlled by container.enable:
| Native (default) | Container | |
|---|---|---|
| How it runs | Hardened systemd service on the host | Persistent Ubuntu container with /nix/store bind-mounted |
| Security | NoNewPrivileges, ProtectSystem=strict, PrivateTmp |
Container isolation, runs as unprivileged user inside |
| Agent can self-install packages | No — only tools on the Nix-provided PATH | Yes — apt, pip, npm installs persist across restarts |
| Config surface | Same | Same |
| When to choose | Standard deployments, maximum security, reproducibility | Agent needs runtime package installation, mutable environment, experimental tools |
To enable container mode, add one line:
{
services.hermes-agent = {
enable = true;
container.enable = true;
# ... rest of config is identical
};
}
Container mode auto-enables virtualisation.docker.enable via mkDefault. If you use Podman instead, set container.backend = "podman" and virtualisation.docker.enable = false.
Extra Dependency Groups
You can pull in optional dependency groups at build time, for example to enable voice features:
config.services.hermes-agent.package.python.pkgs.buildPythonPackage {
build-system = [ config.services.hermes-agent.package.python.pkgs.setuptools ];
extraDependencyGroups = [ "voice" ];
}
Common groups include:
| Group | What it enables |
|---|---|
voice |
Voice features |
The extraDependencyGroups option is a listOf str that defaults to []. It lists the pyproject.toml optional extras to include in the sealed venv (for example ["voice"]). Resolution is handled by uv, so there are no collisions.
These groups join the core dependency resolution at build time. Conflicting requirements can still fail that resolution. The table lists common groups; pyproject.toml is authoritative for the complete list and platform markers.
Memory providers that live in the plugin catalog rather than in the Hermes tree (e.g. Hindsight) are not extras. Install them like any catalog plugin with hermes plugins install hindsight, or declaratively via extraPlugins pointing at the plugin’s source tree.
You can also add plain Python packages alongside the groups:
extraPythonPackages = [ config.services.hermes-agent.package.python.pkgs.redis ]; # its Python dep
Or override the package directly:
# pkgs.hermes-agent.override { extraDependencyGroups = [ "voice" ]; }
The flake provides an editable Python venv with the dev dependency group, running on the interpreter pm/lock.json pins. HERMES_PYTHON and VIRTUAL_ENV point at it, and it needs no PYTHONPATH. It does not install the full wrapper.
Cron on a native install needs a lingering service user. Scheduled cron jobs run in a transient systemd-run --user --scope, so a gateway restart can’t kill a running job. That requires a systemd user manager for the service uid, which only exists when the uid lingers. With createUser = true the module handles this for you (nixpkgs ≥ 25.05). If you declare the user yourself (createUser = false), set linger = true on it or run sudo loginctl enable-linger <user> once — otherwise cron falls back to unscoped workers, or fails closed under cron.require_restart_safe_scope: true.
That’s the Nix setup in a nutshell. Whether you’re a casual Nix user or running a full NixOS deployment, Hermes Agent now fits right into your workflow. Happy hacking!
📖 Official Docs
This article is based on the official Hermes Agent documentation:Official docs › getting-started/nix-setup