How should I structure my NixOS config for multiple machines?

Alternatively:

First of all, yes, you should keep your hardware-configuration.nix in version control, alongside all of the rest of your NixOS configuration. If you're using flakes, this is required, but even if you aren't, it's useful to have your entire configuration in VCS to be able to track history and correct mistakes.

Many folks start using NixOS with a single machine and then find themselves wanting to expand to more. A common desire is to have the One Config to Rule Them All: a single configuration.nix that they can reuse across all of their machines. But if you try to do this, you hit obstacles quickly:

The problem is you're approaching the problem upside-down.

What you want to do instead is:

Example

Say you start with a single machine named bulbasaur.

# configuration.nix
{ pkgs, ... }:
{
  imports = [
    ./hardware-configuration.nix
  ];

  networking.hostName = "bulbasaur";

  services.xserver.videoDrivers = [ "nvidia" ];

  services.desktopManager.plasma6.enable = true;
  services.displayManager.sddm.enable = true;

  environment.systemPackages = [
    pkgs.git
    pkgs.vim
  ];
}

This is working great. Now you have another machine you want to configure: charmander. You want to keep using Plasma there and install git and vim, but charmander doesn't have an NVIDIA card, and it obviously has its own hostname. So lets reorganize a little bit. Here's the final structure:

common.nix
bulbasaur/
  default.nix
  hardware-configuration.nix
charmander/
  default.nix
  hardware-configuration.nix

The default.nix for each machine will serve as the entry point for that machine's NixOS config. And each one will import both common.nix and the machine's hardware-configuration.nix.

# common.nix
{ pkgs, ... }:
{
  services.desktopManager.plasma6.enable = true;
  services.displayManager.sddm.enable = true;

  environment.systemPackages = [
    pkgs.git
    pkgs.vim
  ];
}

# bulbasaur/default.nix
{
  imports = [
    ../common.nix
    ./hardware-configuration.nix
  ];

  networking.hostName = "bulbasaur";

  services.xserver.videoDrivers = [ "nvidia" ];
}

# charmander/default.nix
{
  imports = [
    ../common.nix
    ./hardware-configuration.nix
  ];

  networking.hostName = "charmander";
}

With this structure, you've separated the machine-specific config from the common config. And you could even break this down further: maybe pull the GUI options into a gui.nix if you need to introduce machines that don't use a GUI.

If you're not using flakes, you can symlink the appropriate default.nix file to /etc/nixos/configuration.nix, or you can set the NIXOS_CONFIG environment variable to point at the correct file.

If you are using flakes, you can add multiple outputs under nixosConfigurations:

nixosConfigurations = {
  bulbasaur = nixpkgs.lib.nixosSystem {
    modules = [ ./bulbasaur ];
  };
  charmander = nixpkgs.lib.nixosSystem {
    modules = [ ./charmander ];
  };
};
Proxy Information
Original URL
gemini://midna.dev/nix/faq/nixos-multiple-machines.gmi
Status Code
Success (20)
Meta
text/gemini;lang=en-US
Capsule Response Time
15.001307 milliseconds
Gemini-to-HTML Time
0.287832 milliseconds

This content has been proxied by September (UNKNO).