Motivation#
After configuring one NixOS computer, the obvious way to set up another is to copy the configuration and adjust the hardware settings.
This works for the initial setup, but shared settings then need maintaining in several copies. For example, adding a shell setting or common tool requires updating each computer’s configuration. Missing one copy introduces a difference between computers.
I put the computers in one flake so the common configuration only needs to be written once. Each computer keeps its own differences. This post uses three example names, laptop, workstation and server; replace the names and paths with your own.
The series began with three computers; the same flake now manages four. The three fictional roles below illustrate repository organization rather than limiting the number of hosts.
If flakes are still unfamiliar, start with the previous post.
Configuration Repository Location#
After installation, the generated configuration is normally under /etc/nixos. Your working repository can live elsewhere, provided you select it when rebuilding:
| |
This build uses the flake in the specified repository. Editing another copy will not affect it unless that copy is imported by the selected flake.
I keep the configuration in a separate Git repository to make editing, reading history and syncing to other computers convenient. Before editing, check the repository path used by the rebuild command. If you edit /etc/nixos but rebuild from another directory, check whether those files are imported by the selected flake.
Configuration File Categories#
A simple layout could look like this:
| |
flake.nix is the entry point, and flake.lock records dependency versions. The other files fall into a few groups.
Common configuration#
Settings needed on all the computers can go in modules/common.nix: command-line tools, language and timezone settings, and common service configuration, for example.
The common module should contain settings needed by all the computers. For example, the laptop’s desktop and power settings may not suit the server, and the workstation’s graphics driver should remain in its host configuration.
Each computer’s configuration#
Give each computer a directory, such as hosts/laptop/. Its configuration.nix contains settings for that computer. The flake below combines it with the common module.
Display settings, lid behavior and special hardware support usually belong here. This makes it easier to see what differs between computers.
Hardware configuration#
The installer-generated hardware-configuration.nix describes filesystems, swap and detected hardware modules. It may contain the local disk’s UUID, so copying another computer’s file is not a good starting point.
If the storage layout changes, generate a candidate on the corresponding computer and compare it with the repository version:
| |
Writing to a temporary file gives you a chance to inspect the result before replacing anything. This assumes the installed target system is running; from an installer USB environment, you need to specify the correct target root.
Configuring Flake Host Outputs#
Each computer needs a nixosConfigurations output. Since the definitions are similar, a function can remove some repetition. For example, this fragment can go inside the flake’s outputs, where the nixpkgs input is available:
| |
mkHost takes a host module and passes it to nixosSystem together with the common module. This is a simplified fragment, not a complete flake.nix. Each host module also needs to import its own hardware configuration.
In this example, mkHost already imports the common module, so the host files do not need to import it again. You can instead import it from each host file if you prefer; the arrangement should be easy to follow.
On the laptop, select the laptop output:
| |
On the server, select server. A shared module affects every host that imports it, so successfully rebuilding one computer does not establish that all the other configurations are valid.
Managing User Configuration with Home Manager#
Home Manager can manage the shell, editor, terminal and other user settings. It can follow the same organization: common preferences in one place, machine-dependent settings such as display layouts kept separately.
When integrated as a NixOS module, Home Manager can apply user configuration through nixos-rebuild, without a separate home-manager switch.
Existing manually created configuration files may cause conflicts during the first Home Manager activation. Back up the files and review how to handle them. If a backup extension is configured, also check whether a backup with the same name already exists. The Home Manager post will explain these operations in detail; here the focus is on organizing user configuration.
Changing and Checking Configuration#
Before editing the laptop’s configuration, check what is already changed:
| |
After editing the relevant files, check and build:
| |
If you added a module, add the new file to Git’s index so the local Git flake can see it. After building, try the configuration with test, then use switch once the result is satisfactory:
| |
Kernel and boot-related changes also need a reboot check. Post 5 explains the commands in more detail.
Dependency updates can be a separate operation: run nix flake update, review flake.lock, then check and build. If a problem appears, this makes it easier to tell whether it came from a configuration edit or a dependency version change.
Build and Runtime Records#
Git records configuration file changes. The actual state of the computer needs a separate record. For example, a configuration may be committed even though its build failed. After switch, the computer may still be running the previous kernel until it reboots. The Git log alone does not establish these details.
I therefore record the source commit, actual generation, activation and reboot status, and remaining problems in build and update notes. They can live under docs/, with a file for each computer for later reference.
A generation link’s timestamp is only a clue; it does not prove that the system ran. These paths are useful when checking:
| |
The historical notes remain useful after old generations are garbage-collected, but a generation mentioned in a note may no longer be available for rollback. Check the available generations separately.
Those notes are useful when I return to the setup later, and when an AI assistant continues the work. The next post explains how to give the assistant the repository’s maintenance instructions.
