Initially, when I setup my desktop I thought about taking the same approach as you, but I too found it aesthetically unpleasing. I wanted, almost the same layout on my Desktop as on my Laptop, but I also wanted the possibility of add one program to one machine without adding it to the other. To achieve this, I decided to base my setup upon a set of generic NixOS modules, somewhat inspired by this thread about multi-system flake configurations.
├── common/ # All common components across machines
│ ├── default-configuration.nix
│ ├── default-home.nix
│ ├── desktop-environments
│ │ ├── cosmic.nix
│ │ └── kde.nix
│ └── vim.nix
├── flake.lock
├── flake.nix
└── hosts # Machine specific configurations
├── desktop
│ ├── configuration.nix
│ └── hardware-configuration.nix
└── t480
├── configuration.nix
└── hardware-configuration.nix
To reduce the need for repeating myself, I defined options, for the NixOS module default-configuration.nix to hold the most commonly used values. Below, an excerpt of the aforementioned file, full contents on my GitHub.
# Please read the docs about stateVersion. Link is in the bottom.
{ config, lib, pkgs, ... }:
with lib;
{
imports = [
./vim.nix
];
options.custom.identity = {
username = mkOption {
type = types.str;
description = "Name of the user account.";
};
hostname = mkOption {
type = types.str;
description = "The network hostname of the machine.";
};
stateVersion = mkOption {
type = types.str;
description = "The first version of NixOS installed on this machine. Read the docs: https://nixos.org/nixos/options.html";
};
};
config = {
environment.systemPackages = with pkgs; [
curl
wget
];
virtualisation.podman = {
enable = true;
dockerCompat = true;
};
networking.hostName = config.custom.identity.hostname;
# ... omitted code ...
# Define a user account. Don't forget to set a password with ‘passwd’.
users.users."${config.custom.identity.username}" = {
isNormalUser = true;
description = "Gustav Hjort Bonnerup";
extraGroups = [ "networkmanager" "wheel" ];
};
# ... omitted code ...
nix.settings.experimental-features = [ "nix-command" "flakes" ];
nix.settings.trusted-users = [ "root" config.custom.identity.username ];
# ... omitted code ...
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = config.custom.identity.stateVersion; # Did you read the comment?
};
}
For my default home manager setup, I likewise define a NixOS module, however in the shorthand format instead, as I pull in the above defined default values from the environment. Again, below is an excerpt, the full contents is available on my GitHub.
{ config, lib, osConfig, pkgs, pkgs-unstable, ...}:
with lib;
{
# ... omitted code ...
home.username = osConfig.custom.identity.username;
home.homeDirectory = "/home/${osConfig.custom.identity.username}";
home.stateVersion = osConfig.custom.identity.stateVersion;
home.packages = with pkgs; [
bc
devenv # Consider deleting at some point
htop
just
kdePackages.okular
libreoffice
nixd
ripgrep
texliveFull
tree
pkgs-unstable.antigravity-cli
];
programs.direnv = {
enable = true;
nix-direnv.enable = true;
};
programs.emacs = {
enable = true;
package = pkgs.emacs;
};
programs.firefox.enable = true;
programs.git = {
enable = true;
settings.user = {
name = "Gustav Hjort Bonnerup";
email = "gustav@hjortbonnerup.dk";
};
ignores = [
"*~"
"*.swp"
".direnv/"
".envrc.local"
];
};
# ... omitted code ...
}
Lastly, when configuring a specific machine, I pull in the default configurations shown above, into a machine specific configuration, e.g. hosts/desktop/configuration.nix, and on top of that add additional configurations for that specific machine. Below, the full contents of my desktop configuration, please not that I have added some clarifying comments, prefixed with NOTE:, that are not in the actual config.
let # NOTE: Global values defined below
username = "ghb";
hostname = "desktop";
stateVersion = "26.05";
in
{ config, pkgs, ... }: {
imports = [ # NOTE: Importing NixOS modules here
./hardware-configuration.nix
../../common/default-configuration.nix
../../common/desktop-environments/kde.nix
];
# Passing variables to modules imported above
custom.identity = {
inherit username hostname stateVersion;
};
boot.kernelModules = [
"nct6683" # Driver for motherboard fans
];
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
hardware.graphics.enable = true;
hardware.graphics.enable32Bit = true;
programs.coolercontrol.enable = true;
# NOTE: Configuring home manager setup
home-manager.users."${username}" = {
imports = [
../../common/default-home.nix
{
home.packages = with pkgs; [
steam
];
}
];
};
}
If considering multiple users on the same machine, the home manager configuration can easily be configured to not, import the values from the global environment, but instead using the values, that are passed to the NixOS module. This can be done by not using the NixOS module shorthand format, as is done for common/default-configuration.nix.
If it can help as inspiration, my entire NixOS setup is available on my GitHub.