↓ Skip to main content

NixOS (8): Managing User Configuration with Home Manager

·1977 words·10 mins
Jin Li
Author
Jin Li
Fate lies within the lightcone.
NixOS Series - This article is part of a series.
Part 8: This Article

Motivation
#

The previous post introduced installing user software with Home Manager. A personal environment also contains shell aliases, editor settings and terminal themes. When moving to another computer, we usually want to bring these settings along too.

In my earlier shell and Neovim configuration post, I used scripts and configuration files to organize a personal environment. Home Manager can put software and settings into the same Nix configuration, making them easier to record and update together.

Here I introduce configuration organization and file management with Home Manager, particularly handling existing files during the first migration. The examples use the fictional user alice and host laptop, and contain no private configuration.

Prerequisites
#

  • An existing NixOS flake configuration that builds successfully;
  • Basic familiarity with modules and options, as introduced in post 6;
  • Backups of the existing configuration files you plan to manage with Home Manager.

This post mainly uses Home Manager as a NixOS module. The example assumes an existing normal system user called alice; replace the name with your own. Home Manager user configuration does not replace NixOS account configuration.

Introduction to Home Manager
#

Home Manager uses a module system similar to NixOS. It can manage user software, configuration files, environment variables and some user services. For example, enabling its Bash module lets us define aliases through options instead of maintaining a complete .bashrc separately.

There are two common ways to use it:

MethodApplying configurationSuitable use
As a NixOS moduleBuilt and applied through nixos-rebuildSystem and user configuration in one repository
StandaloneUsing home-manager build and home-manager switchIndependently maintained user configuration, including other systems supported by Nix

Both manage user environments, but their inputs and update procedures need to be arranged accordingly. This series already uses one flake for the system, so the example continues with the first method. See the Home Manager NixOS module documentation for details.

Home Manager manages the software and settings declared to it, rather than taking a snapshot of the whole home directory. Documents, browser history, application caches and databases still need separate preservation.

Configuring Home Manager
#

Adding a flake input
#

For the example using NixOS 26.05, add these inputs to the existing flake.nix:

1
2
3
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
inputs.home-manager.url = "github:nix-community/home-manager/release-26.05";
inputs.home-manager.inputs.nixpkgs.follows = "nixpkgs";

follows makes Home Manager follow this flake’s Nixpkgs input. Choose branches appropriate for your configuration. If the inputs already exist, check their current settings rather than adding duplicates or changing the whole system’s branch to copy the example.

Importing the NixOS module
#

Receive the home-manager input in the outputs function, then add its module to the host’s module list. For example, this is the relevant part of the attribute set returned by outputs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
nixosConfigurations.laptop = nixpkgs.lib.nixosSystem {
  system = "x86_64-linux";
  modules = [
    ./hosts/laptop/configuration.nix
    home-manager.nixosModules.home-manager
    {
      home-manager.useGlobalPkgs = true;
      home-manager.useUserPackages = true;
      home-manager.users.alice = import ./home/alice.nix;
    }
  ];
};

This is not a complete flake; retain the existing common modules and other host outputs. With the mkHost function from post 3, the Home Manager module can instead be added to that function’s modules list.

The two options have different purposes:

  • useGlobalPkgs: makes Home Manager use the system’s pkgs, keeping package configuration consistent. Home Manager’s own nixpkgs.* options are then disabled for separately configuring its package set.
  • useUserPackages: provides Home Manager packages through NixOS’s user package mechanism, placing the user profile under /etc/profiles/per-user/<user>/.

These also differ from input follows: follows aligns input sources, while useGlobalPkgs reuses the configured package set. The example specifies both so user and system environments share the package configuration.

Organizing user configuration
#

Following post 3’s directory structure, separate common user settings from the personal entry point:

1
2
3
4
5
home/
├── common.nix
├── alice.nix
└── files/
    └── settings.ini

Create home/alice.nix first:

1
2
3
4
{
  imports = [ ./common.nix ];
  home.stateVersion = "26.05";
}

home.stateVersion selects compatibility behavior and defaults; it does not select software versions. This new example environment uses "26.05". Keep the original value for an existing environment when updating Home Manager. If changing it is necessary, read the relevant release notes first.

This NixOS integration obtains the username and home directory from the system account. Standalone Home Manager normally also needs home.username and home.homeDirectory in the user configuration.

Software and Shell Configuration
#

Start with a few simple settings in home/common.nix:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{ pkgs, ... }:
{
  home.packages = [ pkgs.jq ];

  programs.bash = {
    enable = true;
    shellAliases.ll = "ls -alh";
  };

  programs.fzf.enable = true;

  xdg.enable = true;
  xdg.configFile."nixos-user-demo/settings.ini".text = ''
    [general]
    mode=demo
  '';
}

This installs jq, adds the ll alias to Bash and enables the fzf module. Enabling Bash configuration does not automatically change the system account’s login shell. If you use Zsh, choose its Home Manager module; the account’s default shell is configured through NixOS.

When a programs.* module exists, its options are usually more convenient than managing the whole file as text. For settings the module does not cover, use its additional configuration options or file management.

Common files suit settings shared by users or computers. Screen layouts, device paths and other specific settings can remain in separate user or host modules, without putting everything into a common file.

Configuration File Management
#

home.file and xdg.configFile
#

Home Manager normally links target files in the home directory to configuration in the store. home.file targets are relative to the home directory; xdg.configFile targets are relative to the XDG configuration directory, which defaults to ~/.config.

The example manages ~/.config/nixos-user-demo/settings.ini. This demonstration file lets us observe file management by reading its contents after activation.

To keep a separate source file instead, change that file option to:

1
xdg.configFile."nixos-user-demo/settings.ini".source = ./files/settings.ini;

The path is relative to home/common.nix, corresponding to home/files/settings.ini in the directory structure above. Its contents can be:

1
2
[general]
mode=demo

text and this source are alternatives. Remove the previous text for this target when changing to source. Add new Nix files and source configuration files to Git’s index so the local Git flake can read them.

Editing managed files
#

After activation, normally edit the Nix settings or source files in the repository and apply them again. Editing the home-directory target directly can fail because it points into the read-only store. Some programs may replace the link themselves, leading to a conflict at the next activation.

I therefore do not recommend handing the entire ~/.config directory to Home Manager. It can contain configuration, caches and runtime state together. Manage specific files that you deliberately want to retain.

Do not write passwords, tokens or private keys into text or copy them into the store as ordinary source files. The next post will cover providing secrets at runtime.

Building and Applying Configuration
#

After creating the files and reviewing the changes, build the target system:

1
2
3
4
git add home/alice.nix home/common.nix
git diff --check
nix flake check --no-build --show-trace
nixos-rebuild build --flake .#laptop

With the source approach, also add home/files/settings.ini to the index. Adding the Home Manager input for the first time may require updating flake.lock; inspect and record its diff. Routine changes with inputs already locked can use --no-update-lock-file as in post 5.

A successful build means the configuration can be generated. It does not guarantee that existing home-directory files will not conflict. After checking and handling the conflicts described below, apply the configuration using post 5’s procedure. For example, to activate immediately and set the boot default:

1
sudo nixos-rebuild switch --flake .#laptop

With the default system-service activation used here, inspect the user’s configuration service and files:

1
2
3
4
systemctl status home-manager-alice.service --no-pager
journalctl -u home-manager-alice.service -b --no-pager
readlink -f ~/.config/nixos-user-demo/settings.ini
cat ~/.config/nixos-user-demo/settings.ini

Check files from the corresponding user’s session. Open a new Bash terminal and run type ll and jq --version to check the alias and package. Existing shells may not automatically load new settings; desktop session environment variables may also require logging in again.

Troubleshooting
#

Existing file conflicts
#

A common first-activation problem is a manually created file already occupying the target, such as an old .bashrc. These conflicts are normally checked during activation. Building alone does not inspect the actual home directory for them.

For example, if the demonstration file already exists, inspect it first:

1
2
3
4
target="$HOME/.config/nixos-user-demo/settings.ini"
ls -l -- "$target"
readlink -f -- "$target"
cat -- "$target"

After confirming it belongs to the previous manual configuration, back it up and merge any settings you still need into the repository. Move the conflicting file aside before applying again. The error normally identifies the target, so handle individual files rather than deleting the whole configuration directory.

To have Home Manager automatically back up conflicting files during activation, set this in a NixOS module:

1
home-manager.backupFileExtension = "hm-backup";

The original file is moved to a name ending in .hm-backup. This option belongs to the NixOS integration, rather than home/common.nix. Standalone Home Manager uses the CLI’s -b backup parameter, so do not interchange commands between the two methods.

An existing backup with the same name can still cause activation to fail. Inspect and separately preserve it rather than deleting it whenever an error appears. home.file.*.force can skip protection checks and replace selected targets, but is not a suitable default for the first migration. See the Home Manager file module and NixOS integration options for conflict and backup behavior.

Settings not taking effect
#

If rebuilding does not produce the expected settings, first check that the user module was imported and the rebuild selected the intended host output. Then inspect the Home Manager service and logs, followed by the actual files and current session.

If user activation fails, other system changes may already be applied. Use the logs to establish the actual state. System rollback also does not restore all home-directory data or automatically undo backups, application writes or manually moved files.

Desktop Settings and Mutable Data
#

Not all user settings are read-only files in the store. Some GNOME preferences, for example, are stored in the dconf database, and Home Manager can write them during activation.

On a GNOME system with system dconf support enabled, a Home Manager module can contain:

1
2
3
dconf.settings."org/gnome/desktop/interface" = {
  color-scheme = "prefer-dark";
};

The corresponding programs.dconf.enable = true; belongs in the NixOS system module. These settings are written to the user’s database during activation, but can still be changed in the graphical interface afterward. A later activation may reapply declared values. This differs from managing linked files, and GNOME options do not apply directly to other desktops.

For settings you frequently adjust in an application’s interface, decide which values should remain under configuration management and which the application should save itself. For example, common fonts and fixed shortcuts can be declared, while recently opened files and temporary window state usually do not need to be.

Recommended Usage#

I recommend beginning with a few tools, shell aliases and one simple configuration file. Build and check the actual result before adopting more dotfiles. This makes it easier to identify which setting caused a conflict.

Keep common and specific settings separate. Back up existing files before deciding how to import them. For applications that modify configuration at runtime, check how they save it before choosing a Home Manager module, file links or mutable files.

When asking AI to migrate a personal environment, have it list the target files and explain how existing contents will be preserved. After the change, check the user service, file contents and a new shell or desktop session. This leaves a personal environment that can be maintained further, beyond simply moving files into a repository.

The next post will introduce information visibility in the Nix store and providing passwords, tokens and other secrets to services at runtime.

NixOS Series - This article is part of a series.
Part 8: This Article