OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 091

nh: a unified workflow for Nix systems, users, and cleanup

Cover nh commands, module options, specialisations, search, environment variables, and upstream contribution guidance.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSNix commands and long-term maintenance ↗
About 16 min readStart reading

Source and license: the translations and adaptations of the upstream README in this article’s “What is nh?”, “Features”, “Current status”, “Usage”, “Environment variables”, “Frequently asked questions”, and “Contributing” sections come from the nix-community/nh project and its contributors. Those upstream materials and their translations, arrangement, and revisions are provided under EUPL-1.2 (full license); that license is not extended to the independent module configuration reference, this site’s added workflow-order guidance, or other content. License verification is based on the upstream README at pinned reference revision 25da175ed916 and its LICENSE. This revision is the reference used for this verification, not a claim about the exact revision originally imported. This article includes translation, selection, and supplementary explanations; this license and modification notice was added on 2026-10-09. The editable source of this article retains the translated and adapted content. “We” in the original text refers to the upstream project and does not imply upstream endorsement of this site.

What is nh?#

Source scope: this article preserves the translation of upstream nh documentation and module-option explanations from the original notes. The source includes development-branch features, such as SPAM offline search and GitHub PR/issue search, which are not guaranteed to be present in every distribution’s stable package. Check the installed version with nh --version, each subcommand’s --help, and the corresponding source.

nh is a modern helper designed to unify and reimplement the commands and interfaces of several tools in the Nix/NixOS ecosystem. Our goal is to provide a consistent, easy-to-understand interface with more features, a better user experience, and greater speed. Beyond being an all-in-one tool that reimplements familiar, frequently used Nix commands, nh is also _pleasant to look at_, integrating related third-party projects you may already know.

For a quick start, begin with “Usage” and “Command examples.”

Features#

  • A unified CLI: a consistent, intuitive interface for Nix, NixOS, Home Manager, and Nix-Darwin workflows.
  • A rich interface: each major feature (os, home, darwin, search, clean) provides fine-grained subcommands and arguments for precise control.
  • Enhanced garbage collection: nh clean extends nix-collect-garbage with gcroots cleanup, profile selection, and time-based retention.
  • Faster Nix search: search Nixpkgs through Elasticsearch for faster responses.
  • Good looks: attractive without compromise. Who does not enjoy a great-looking interface?
    • Build-tree visualization: commands such as nh os use nix-output-monitor (nom) to make the build dependency tree clear at a glance.
    • Change comparisons: integrated, ultrafast derivation diffs before activation or switching.
  • Specialisation support: easily select or ignore NixOS and Home-Manager specialisations through command arguments.
  • Generation management: inspect, roll back, and manage system generations, with support for selecting a specific target generation.
  • Extensible and future-ready: designed to make adding new subcommands and arguments quick and seamless.
    • nh reimplements the CLI tools you know and love, with a stronger emphasis on safety and correctness. Its language and design choices make new features easy to add with virtually no overhead.
  • Excellent documentation: everything nh can do is documented. Everything nh does is documented too. User and developer documentation is always kept up to date.

Design philosophy

nh is a _unified_ CLI, meaning that it aims to bring core platform support together in one convenient tool. Its interface currently supports NixOS as a first-class platform, Home Manager, and Nix-Darwin. We want to give users of these projects a familiar, convenient, and _beautiful_ interface.

nh’s interface may look familiar, but that should not be seen as a weakness: nh is not a wrapper around nixos-rebuild and is not constrained by tools of that kind. In short, we aim not only to _implement existing functionality_, but to _go beyond it_, adding commands and arguments that improve the experience provided by the default tools. That means _infrastructure_ and _sensible defaults_ matter. You can enjoy the tools you are used to while gaining the benefits of a faster, safer language.

Future goals for nh include support for lower-level tools such as nixos-install and nixos-generate-config, making it a Rust alternative to most core tools. Feedback and feature requests are welcome on the Discussions and Issues pages.

Current status#

The release and backport descriptions below come from the notes’ snapshot. Check the pinned Nixpkgs for actual stable package versions and backport availability.

nh is packaged in nixpkgs and is available in both nixpkgs stable and nixpkgs unstable. The original upstream translation describes its backport policy, but this should not be taken as a guarantee that every update reaches stable. See the Installation section for details. If the package is out of date, please submit an update request.

Installation

The latest tagged release is available in Nixpkgs as nh stable and is recommended for most users, since tagged releases generally receive more testing. This repository also provides the latest development version of nh through its flake outputs.

nix shell nixpkgs#nh             # 稳定版
nix shell github:nix-community/nh  # 开发版

You can try nh in a Nix shell right now, with no configuration required!

NixOS

We provide a NixOS module to run nh clean as a system service. Enable it as follows:

{
  programs.nh = {
    enable = true;
    clean.enable = true;
    clean.extraArgs = "--keep-since 4d --keep 3";
    flake = "/etc/nixos";  # NixOS 模块设置 NH_FLAKE
  };
}

programs.nh module configuration reference#

nh (Nix Helper) is a tool designed to simplify Nix and NixOS command-line operations. The programs.nh module lets you declaratively install it, set a default Flake path, and configure automatic system cleanup tasks based on nh.

Option list

programs.nh.enable

  • Type: boolean (Boolean)
  • Default: false
  • Description: whether to enable nh and its associated configuration. When set to true, the system installs the nh package into the global environment.

programs.nh.package

  • Type: Nix package (Package)
  • Default: pkgs.nh
  • Description: the nh package instance to install. The default is usually sufficient; override it here if you need a specific version or a custom build of nh.

programs.nh.flake

  • Type: null or a single-line string (null or string)
  • Default: null
  • Description: the default Flake path for nh. This value is assigned to the NH_FLAKE environment variable and supplies the default Flake reference for commands such as nh os switch.
  • Override mechanism: you can override this default for specific commands by setting the following environment variables:
    • NH_OS_FLAKE: takes precedence for nh os commands.
    • NH_HOME_FLAKE: takes precedence for nh home commands.
    • NH_DARWIN_FLAKE: takes precedence for nh darwin commands.
  • Constraint: this path must point to a directory, not an individual .nix file. An invalid configuration triggers an assertion error during the system build.

programs.nh.clean.enable

  • Type: boolean (Boolean)
  • Default: false
  • Description: whether to enable periodic garbage collection based on nh clean all.
  • Important: if this option is enabled, you must disable the system’s native nix.gc.automatic = true; otherwise, the two scheduled jobs conflict and trigger a system warning at build time.

programs.nh.clean.dates

  • Type: single-line string (singleLineStr)
  • Default: "weekly"
  • Description: how often the cleanup task runs. This string is passed to systemd.time.
  • Format reference: accepts the time formats defined in systemd.time(7), such as "weekly", "daily", or a specific time such as "03:30".

programs.nh.clean.extraArgs

  • Type: single-line string (singleLineStr)
  • Default: ""
  • Description: extra arguments passed to nh clean all when the automatic cleanup service runs.
  • Example: "--keep 5 --keep-since 3d" (keep the most recent 5 generations and all generations from the last 3 days). Consult nh clean all --help for available arguments.

Configuration example

The following shows a standard configuration.nix example that enables and configures nh:

{ config, lib, pkgs, ... }:
{
  # 启用 nh 工具
  programs.nh = {
    enable = true;

    # 指定默认的 Flake 路径,简化日常命令
    flake = "/etc/nixos";

    # 配置 nh 接管系统的自动垃圾回收
    clean = {
      enable = true;
      dates = "weekly";  # 每周执行一次

      # 保留策略:至少保留最近 5 个世代,且近 7 天内的世代全部保留
      extraArgs = "--keep 5 --keep-since 7d";
    };
  };

  # 关键:禁用原生 nix.gc 以避免与 nh 的清理任务冲突
  nix.gc.automatic = lib.mkForce false;
}

Starting with version 4.0, nh fully supports both Nix flakes and traditional NixOS configurations managed through channels or manual dependencies. Treat the new API as mature but still somewhat experimental. Bug reports are welcome!

  • For flakes, use nh os switch /path/to/flake
  • For traditional configurations:
    • nh os switch -f '<nixpkgs/nixos>', or
    • nh os switch -f '<nixpkgs/nixos>' -- -I nixos-config=/path/to/configuration.nix (if you use a non-default path)

Check nh os --help or man 1 nh for other arguments and environment-variable defaults.

Specialisation support

nh can detect which specialisation you are currently running and execute the correct activation script. To do so, tell nh by writing the specialisation’s name to /etc/specialisation:

{config, pkgs, ...}: {
  specialisation."foo".configuration = {
    environment.etc."specialisation".text = "foo";
    # ..其余配置
  };

  specialisation."bar".configuration = {
    environment.etc."specialisation".text = "bar";
    # ..其余配置
  };
}

Home-Manager

Home specialisations are read from ~/.local/share/home-manager/specialisation. Configure this as follows:

{config, pkgs, ...}: {
  specialisation."foo".configuration = {
    xdg.dataFile."home-manager/specialisation".text = "foo";
    # ..其余配置
  };

  specialisation."bar".configuration = {
    xdg.dataFile."home-manager/specialisation".text = "bar";
    # ..其余配置
  };
}

Usage#

One of nh’s core principles is to give users a clear, unified, and intuitive CLI experience. The nh command offers multiple subcommands, each with a rich set of CLI arguments for flexible configuration.

nh supports a variety of arguments, environment variables, and configuration options to provide the best possible user experience. Consult each subcommand’s --help page or man 1 nh for more details and examples. You can also customize it for your system through the relevant platform’s module, such as the NixOS module in Nixpkgs, as described in the installation section.

Under the nh command, you will use two categories of commands:

Global subcommands

Global subcommands enhance core Nix commands. We currently offer better search and better garbage collection through two ready-to-use subcommands.

nh search

We provide ultrafast Nix package and NixOS/Home Manager option search, powered by the search.nixos.org Elasticsearch backend, as well as offline search of local SPAM databases and searches for Nixpkgs pull requests and issues. All are available through nh search! These extended search subcommands belong to the feature snapshot recorded in the notes; if your distribution’s nh search --help does not list them, do not assume they are available.

The notes list the following search modes and convenient shorthand forms:

Invocation Function
nh search <query> Shorthand form; searches packages by default (change the default with NH_DEFAULT_SEARCH)
nh search packages <query> Search Nixpkgs packages through search.nixos.org
nh search options [--scope=<SCOPE>] <query> Search NixOS/Home Manager options (--scope: nixpkgs, home-manager, all)
nh search offline --db <PATH> <query> Search local SPAM databases offline. Obtain a pre-generated database matching your machine from the upstream SPAM project.
nh search prs [--days <DAYS>] <query> Search Nixpkgs pull requests and show branch reachability for merged PRs. Defaults to the last 15 days.
nh search issues [--days <DAYS>] <query> Search Nixpkgs issues, excluding pull requests. Defaults to the last 15 days.

--json is available in every search mode. --limit, --channel, and --platforms are available in the search modes and shorthand forms to which those arguments apply.

nh search prs and nh search issues use GH_TOKEN for GitHub authentication. If GH_TOKEN is unset, nh reads the token from NH_GITHUB_TOKEN_FILE, or from $XDG_STATE_HOME/nh/github-token (falling back to ~/.local/state/nh/github-token). If no token is found in an interactive terminal, nh prompts you for one and saves it to the token file.

nh clean

A reimplementation of nix-collect-garbage that also cleans gcroots, offers options for fine-grained control over what to retain, and shows additional information before cleanup so you can see what will be removed.

In the source version, gcroots cleanup in nh clean inspects your gcroots directory. Removing the corresponding roots leaves build results or direnv caches unprotected; it does not guarantee deletion of all project directories. Check the default switches and cleanup scope with your locally installed nh clean --help.

Use --no-gcroots to skip all gcroot cleanup, or --no-direnv to preserve direnv gcroots while cleaning everything else.

Platform-specific subcommands

Platform-specific subcommands implement CLI tools for NixOS, Home Manager, and Nix-Darwin.

nh os

The nh os subcommand reimplements the Python script nixos-rebuild-ng[^1] from scratch, and additionally provides:

  • Build-tree visualization through nix-output-monitor (nom)
  • Attractive change diffs through dix
  • Confirmation prompts

And other interface improvements, from environment-variable support to extra safety measures. Is that all? No, there is more to look forward to.

nh home

The nh home subcommand reimplements the home-manager script with the same enhancements as nh os.

nh darwin

Last but not least, the nh darwin subcommand is a pure Rust reimplementation of the darwin-rebuild script, with the same enhancements as nh os and nh home.

Command examples

Platform Old command (without nh) New command (with nh)
NixOS nixos-rebuild switch --flake .#workstation nh os switch . -H workstation
Darwin darwin-rebuild switch --flake .#workstation nh darwin switch . -H workstation
Home Manager home-manager switch --flake .#workstation nh home switch . -c user

If NH_FLAKE is set, nh lets you omit the flake path. The NixOS and Home-Manager modules set this automatically when programs.nh.flake is configured.

nh also lets you omit the NixOS/Darwin hostname (-H) and Home Manager configuration name (-c) when the system can discover them automatically. For example, with NH_OS_FLAKE set, you can simply run nh os switch without extra arguments, and it automatically resolves nixosConfigurations.<workstation>.

nh also allows the Nix file path to be omitted for non-flake configurations. You can specify the Nix file and Nix attribute path with the NH_FILE and NH_ATTRP environment variables. If both NH_FILE and NH_ATTRP are set, nh os switch effectively operates on $NH_FILE#$NH_ATTRP.

Environment variables#

Some variables come from development-branch documentation; check support against the installed nh version. Variable names are not credentials, and no token values are included here.

nh supports various environment variables that control command behavior. The following are common variables you may encounter or choose to use:

Global

  • NIX_SSHOPTS, NIX_CONFIG, and NIX_REMOTE are forwarded to all Nix commands run with environment isolation.
  • NIX_SSL_CERT_FILE
    • Specifies a TLS CA certificate file for Nix; it has nothing to do with bootloader installation.
  • NIX_USER_CONF_FILES
    • Specifies the list of Nix configuration files read by the client.
  • NIXOS_INSTALL_BOOTLOADER
    • Relates to reinstalling the bootloader during system activation; do not confuse it with TLS configuration. Check its accepted values and activation-script behavior against the system version.

NH-specific

  • NH_NO_CHECKS

    • When set to a nonempty value, skips startup checks such as Nix version and experimental-feature verification. Useful for generating completions or running in constrained build environments. You can also think of it as "expert mode", offering a small performance gain, provided you know what you are doing.
  • NH_FLAKE

    • The preferred path/reference to the directory containing flake.nix that NH uses for flake-based commands. Its historical predecessor was FLAKE; if the platform-specific NH_*_FLAKE is unset and FLAKE exists, NH migrates it to NH_FLAKE.
  • NH_OS_FLAKE、NH_HOME_FLAKE、NH_DARWIN_FLAKE

    • Flake references for the os, home, and darwin commands, respectively. When present, they take precedence over NH_FLAKE.
  • NH_SUDO_ASKPASS

    • The path to the helper invoked when NH elevates privileges with sudo. If SUDO_ASKPASS is set, NH passes -A to sudo.
  • NH_PRESERVE_ENV

    • Controls whether environment variables marked for preservation are passed to elevated commands. Set to "0" to disable preservation or "1" to force it. If unset, preservation is enabled by default.
  • NH_SHOW_ACTIVATION_LOGS

    • Controls whether activation output is shown. Activation output is hidden by default. Setting this to "1" shows full activation logs to help debug activation failures. Supported on all platforms: NixOS, Home Manager, and Darwin.
  • NH_LOG

    • Sets NH’s logging/tracing filter. Uses the same format as the tracing_subscriber environment filter, for example nh=trace.
  • NH_CONFIG

    • Overrides the NH configuration-file path. If unset, NH uses $XDG_CONFIG_HOME/nh/config.toml, falling back to ~/.config/nh/config.toml.
  • GH_TOKEN

    • The GitHub token used by nh search prs and nh search issues. When set, it takes precedence over any token file.
  • NH_GITHUB_TOKEN_FILE

    • Overrides the GitHub token-file path used by nh search prs and nh search issues when GH_TOKEN is unset. If this variable is unset, NH uses $XDG_STATE_HOME/nh/github-token, falling back to ~/.local/state/nh/github-token. Tokens entered at the interactive prompt are saved to this file with permissions allowing only the user to read it.
  • NH_SEARCH_CHANNEL

    • The default Nixpkgs channel used by nh search packages and nh search options, such as nixos-unstable. Override it for an individual invocation with --channel.
  • NH_SEARCH_JSON

    • When set to a truthy value, nh search outputs results as raw JSON instead of a formatted display. Equivalent to --json.
  • NH_SEARCH_PLATFORM

    • When set to a truthy value, shows the supported platforms for each package result. Equivalent to --platforms.
  • NH_DEFAULT_SEARCH

    • Controls the target of the nh search <query> shorthand form when no subcommand is specified. Accepted values are packages (default), options, and all. Equivalent to --default-search.
  • NH_OFFLINE_DB

    • A colon-separated list of SPAM database file paths for nh search offline. Each path is treated as an independent database. Equivalent to passing --db multiple times. Example: NH_OFFLINE_DB=/var/cache/spam/nixpkgs.db:/var/cache/spam/hm.db.
  • NH_NOM

    • Controls whether nom (nix-output-monitor) is enabled during builds. Equivalent to --no-nom.
  • NH_REMOTE_CLEANUP

    • Whether to try cleaning up remote processes through pkill on interruption. This feature matches nixos-rebuild’s behavior, but is disabled by default because it is inherently fragile. Usually unnecessary unless NH leaves zombie processes behind after an interruption.

Notes

  • NH explicitly propagates any environment variable prefixed with NH_ to subcommands where appropriate, that is, during environment isolation.
  • For backward compatibility, if FLAKE exists and no platform-specific NH_*_FLAKE variable is set, NH sets NH_FLAKE to the value of FLAKE and prints a migration warning. FLAKE will be removed in a future version of NH.

Frequently asked questions (FAQ)#

Q: does nh wrap the CLI tools I normally use?

A: no. All commands invoke Nix directly; they do not use the traditional CLI tools. nh is gradually replacing tools formerly invoked through a shell with native Rust libraries for safer integration and slightly better performance.

Contributing#

Contributions are welcome. Simply clone the repository and run nix develop. We also provide an .envrc for Direnv users; run direnv allow to enter the development environment.

[^1]:nixos-rebuild-ng does not yet have complete feature parity with nixos-rebuild. Although many subcommands have been implemented, some features may still be missing. See #358 for the roadmap.

Structure

The following src/xtask description preserves the code layout in the older translation. The current repository may have been split into directories such as crates and docs; read the current CONTRIBUTING documentation before contributing.

nh is written in Rust and consists of two modules. The core code is in src, divided into submodules. Key modules to know include nh::commands for the command interface, nh::checks for pre-startup checks, and nh::util for shared logic. Platform-specific logic lives in the corresponding platform module, such as nh::nixos or nh::darwin, while general helper functions live in nh::util.

The xtask directory contains the cargo-xtask tasks NH uses for work such as generating manpages.

Submitting changes

After making changes, remember to run just fix to apply the general formatting and lint rules expected by CI.

Before creating your change, you also need to update the changelog with enough information to describe the new behavior.

This may look daunting, but it is not. Even if you _do not_ meet these requirements, you will receive friendly guidance to finish the changes. Friendly contributions are always welcome.

Acknowledgments

nh has a long history and has grown considerably over the years. The original translation recounts the project’s history in NotAShelf’s voice, beginning with thanks to ViperML for his tremendous work as nh’s creator and first maintainer. I recently took over nh, but none of this would have been possible without him.

Next, I want to thank the tools we use under the hood. Upgrade-diff visualization comes from my good friend faukah’s dix crate. Compared with our previous diff tool, nvd, dix is more than twice as fast, a boon for nh’s diff experience. Thank you!

nix-output-monitor is another excellent tool worth mentioning. nh uses it under the hood to display beautiful build trees. Many thanks to nix-output-monitor for giving countless nh users like me such attractive build visualization.

I also want to thank the many Rust crates that power nh under the hood and give it its distinctive UX. Without the wonderful Rust ecosystem, nh would not be where it is today.

Last but not least, I want to thank… you! Thank you to everyone who contributes to nh, discusses it, or criticizes it to drive improvements. nh would not be where it is today without you.

Order of operations: build, activate, and clean#

For system commands, you can start with nh os build to check the build result. nh os test still actually activates services, while nh os switch changes the running system and default configuration. nh clean reduces the generations and caches available for recovery; use it only after confirming retention time, generation count, and gcroots scope. NixOS’s programs.nh.flake sets NH_FLAKE, while platform-specific variables such as NH_OS_FLAKE take precedence; do not conflate them. The upstream module source confirms that enabling both clean and the native nix.gc automatic task produces a warning. Choose one timer.

References & further reading

  1. Upstream nh project ↗
  2. NixOS nh module source ↗
  3. SPAM project ↗
  4. nh README: pinned reference for this license verification ↗
  5. nh: EUPL-1.2 license at the reference revision ↗
  6. Full EUPL-1.2 license ↗
  7. Editable source of this article ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

Nix commands and long-term maintenance

  1. 01A NixOS learning roadmap: foundations, Flakes, user configuration, and beyond
  2. 02Nix commands explained: builds, environments, profiles, and systems
  3. 03nh: a unified workflow for Nix systems, users, and cleanup
  4. 04Garbage collection, store optimization, and rollback generations
  5. 05NixOS configuration organization, maintenance, and troubleshooting
CONTINUE READING / NEXT ARTICLEA NixOS learning roadmap: foundations, Flakes, user configuration, and beyond

Retain six learning modules, language ecosystems, and advanced resources, with clear prerequisites and progression.