Skip to content

NixOS with Home Manager

Overview

This walkthrough wires Home Manager into a flake as a NixOS module: one nixosConfigurations.<host> output evaluates system modules and per-user home modules together, and a single nixos-rebuild switch activates both. That is the opposite of standalone homeConfigurations, where user profiles are built and switched with home-manager switch independently of the OS.

The pattern fits a single-machine or mono-repo setup where dotfiles should stay aligned with the system declaration, share one pkgs via home-manager.useGlobalPkgs, and ride the same generation cadence as NixOS. Pins below use release-26.05 / nixos-26.05 as illustrative stable branches (mid-2026).

Details

Domains composed

Domain Role in this example
Flake (concept) Inputs, lock, and nixosConfigurations output
nixosConfigurations nixpkgs.lib.nixosSystem host wiring
homeConfigurations Contrast only — not used when HM is embedded
Experimental flakes / nix-command Required for nix flake and --flake rebuilds
Users and groups Login accounts declared under users.users
Standalone vs NixOS module Integration mode and trade-offs
nixos-rebuild Activation entry point for this path

Home Manager’s flake integration is experimental; pin the home-manager input to a release branch that matches your Nixpkgs channel and review upstream notes before bumping. See follows and overrides and lockfile.

Repository layout

A minimal mono-repo keeps the host entry thin and colocates the user module next to the machine (alternatives in config repo layout):

.
├── flake.nix
├── flake.lock
└── hosts/
    └── laptop/
        ├── default.nix          # NixOS entry: imports + HM wiring
        ├── hardware-configuration.nix
        └── home.nix             # Home Manager module for one user

hardware-configuration.nix is normally generated by nixos-generate-config; treat it as machine-specific facts, not shared dotfiles.

flake.nix (annotated)

{
  description = "NixOS host with embedded Home Manager";

  inputs = {
    # Pin Nixpkgs to a release branch (illustrative).
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";

    # Match Home Manager’s release branch to the same NixOS release.
    home-manager = {
      url = "github:nix-community/home-manager/release-26.05";
      # Reuse the root nixpkgs checkout in the lock graph.
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { nixpkgs, home-manager, ... }@inputs: {

    nixosConfigurations.laptop = nixpkgs.lib.nixosSystem {
      # Pass flake inputs into every NixOS/HM module (optional but common).
      specialArgs = { inherit inputs; };

      modules = [
        ./hosts/laptop/default.nix
      ];
    };

  };
}

There is no homeConfigurations key for the embedded user — that output is for standalone Home Manager only. The same ./home.nix file can still be reused in a standalone config if you later split modes; only the import path changes.

hosts/laptop/default.nix (system + Home Manager wiring)

{ inputs, ... }:

{
  imports = [
    ./hardware-configuration.nix
    inputs.home-manager.nixosModules.home-manager
  ];

  networking.hostName = "laptop";

  # Account identity is NixOS’s job — Home Manager configures the home dir.
  users.users.alice = {
    isNormalUser = true;
    extraGroups = [ "wheel" "networkmanager" ];
  };

  # Share the system pkgs with Home Manager modules (overlays / nixpkgs.* live here).
  home-manager.useGlobalPkgs = true;
  # Install user packages into the usual NixOS user profile layout.
  home-manager.useUserPackages = true;

  # Attach the user’s Home Manager module (path or inline attrset).
  home-manager.users.alice = import ./home.nix;

  # Minimal system services — expand per role modules as needed.
  services.openssh.enable = true;
  system.stateVersion = "26.05";
}

home-manager.users.<name> must match a users.users.<name> entry. Home Manager does not create Unix accounts.

hosts/laptop/home.nix (user environment)

{ pkgs, ... }:

{
  home.username = "alice";
  home.homeDirectory = "/home/alice";
  home.stateVersion = "26.05"; # Set once; do not change casually.

  home.packages = with pkgs; [
    ripgrep
    jq
  ];

  programs.git = {
    enable = true;
    userName = "Alice Example";
    userEmail = "alice@example.org";
  };

  # programs.* / xdg.configFile patterns: see dotfiles guidance.
}

With useGlobalPkgs = true, pkgs in this module is the system package set. Program modules and file options follow normal Home Manager semantics; see dotfiles patterns and writing HM modules.

Activate

On the target machine (flakes and nix-command enabled):

sudo nixos-rebuild switch --flake .#laptop

That one command builds the system closure and activates Home Manager profiles for every home-manager.users.* entry. Do not run home-manager switch for this integration path — it would maintain a separate generation line and can fight module-mode activation.

Optional checks before switching:

nix flake check          # evaluates nixosConfigurations.*.config.system.build.toplevel
nix build .#nixosConfigurations.laptop.config.system.build.toplevel

Use test or boot instead of switch when you want non-persistent or reboot-deferred activation; see rebuild: switch, boot, test.

Failure modes

Symptom Likely cause Fix
HM option unknown / version skew home-manager input branch does not match nixpkgs Align release branches; run nix flake update intentionally
User does not exist / cannot log in Missing or mismatched users.users.<name> Declare the account in NixOS; match home.username
Duplicate or stale packages Ran home-manager switch alongside module mode Use only nixos-rebuild for this setup
home-manager.users has no effect Forgot home-manager.nixosModules.home-manager import Add the module to imports or modules
Collision on activation Unmanaged file in ~ blocks symlinks Migrate into config or use backup/force sparingly — dotfiles patterns
Two Nixpkgs checkouts / slow builds HM input not following root nixpkgs Set inputs.nixpkgs.follows = "nixpkgs" on the HM input
Flake eval errors in modules Modules need inputs but lack specialArgs Pass specialArgs = { inherit inputs; }; on nixosSystem

For standalone user configs on non-NixOS hosts, or when dotfiles should move independently of the system, use homeConfigurations and home-manager switch --flake .#<name> instead — details in standalone vs NixOS module, not duplicated here.

Examples

Inline user module instead of import ./home.nix:

home-manager.users.alice = { pkgs, ... }: {
  home.stateVersion = "26.05";
  programs.git.enable = true;
};

Shared user module across hosts (person follows the laptop, not the hardware):

# hosts/laptop/default.nix
home-manager.users.alice = import ../../users/alice/home.nix;

Keep host-specific packages in the host entry or a small wrapper import; shared shell/editor config belongs under users/ or modules/ as in config repo layout.

Passing flake inputs into home.nix when modules need custom flakes or sources:

# home.nix
{ inputs, pkgs, ... }:
{
  # inputs available because flake.nix set specialArgs on nixosSystem
}

References

See also