I’ve been pursuing a fairly stripped down flake approach with the barest minimum amount of seemingly magical language mess that doesn’t seem to make too much sense or be directly relevant, where possible. I’ve arrived at something that looks like this structure:
flake.nix:
{
description = "nipsy's NixOS configuration";
inputs = {
disko.url = "github:nix-community/disko";
disko.inputs.nixpkgs.follows = "nixpkgs-unstable";
home-manager-stable = {
url = "github:nix-community/home-manager/release-23.11";
inputs.nixpkgs.follows = "nixpkgs-stable";
};
home-manager-unstable = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs-unstable";
};
nixos-hardware.url = "github:nixos/nixos-hardware";
nixpkgs-stable.url = "github:nixos/nixpkgs/release-23.11";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
};
outputs = inputs@{ home-manager-stable, home-manager-unstable, nixos-hardware, nixpkgs-stable, nixpkgs-unstable, ... }: rec {
nixosConfigurations = {
ginaz = nixpkgs-unstable.lib.nixosSystem {
pkgs = pkgs-unstable;
system = "x86_64-linux";
modules = [
./hosts/ginaz
nixos-hardware.nixosModules.lenovo-yoga-7-14ARH7.amdgpu
home-manager-unstable.nixosModules.home-manager {
home-manager.users.nipsy = import ./home/nipsy/ginaz.nix;
}
];
};
richese = nixpkgs-unstable.lib.nixosSystem {
pkgs = pkgs-unstable;
system = "x86_64-linux";
modules = [
./hosts/richese
home-manager-unstable.nixosModules.home-manager {
home-manager.users.nipsy = import ./home/nipsy/richese.nix;
}
];
};
};
pkgs-stable = import nixpkgs-stable {
system = "x86_64-linux";
config.allowUnfree = true;
overlays = [(import ./pkgs)];
};
pkgs-unstable = import nixpkgs-unstable {
system = "x86_64-linux";
config.allowUnfree = true;
overlays = [(import ./pkgs)];
};
};
}
I’m trying to make every input be defined as explicitly as possible without simply assigning something directly to nixpkgs or home-manager. I’m not sure this goal even has much purpose, but it seems to make more sense in my mind by keeping everything clearly delineated.
Then, looking at one of my host configurations, you’ll see how things sort of evolve from there:
% cat hosts/ginaz/default.nix
{ config, pkgs, ... }: {
boot = {
initrd.kernelModules = [ "amdgpu" "zfs" ];
kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;
loader = {
efi.canTouchEfiVariables = true;
systemd-boot.enable = true;
timeout = 3;
};
supportedFilesystems = [ "zfs" ];
zfs.devNodes = "/dev/disk/by-label";
};
imports = [
./hardware-configuration.nix
../common/core
../common/optional/db.nix
../common/optional/dev.nix
../common/optional/games.nix
../common/optional/google-authenticator.nix
../common/optional/multimedia.nix
../common/optional/pipewire.nix
../common/optional/sdr.nix
../common/optional/services/openssh.nix
../common/optional/services/xorg.nix
../common/optional/sound.nix
../common/optional/zfs.nix
../common/users/nipsy
../common/users/root
];
networking = {
hostId = "12345678";
hostName = "ginaz";
networkmanager.enable = true;
nftables.enable = true;
};
services.xserver.videoDrivers = [ "amdgpu" ];
system.stateVersion = "23.11";
}
As you’d expect, the subsequently included modules are all mostly package lists with a smattering of further defined programs and services options, along with some home-manager specific settings of course.
One of the problems I ran into with all of this was when defining my package lists. I initially had been using this construct (this is from my hosts/common/optional/sdr.nix):
environment.systemPackages = builtins.attrValues {
inherit (pkgs)
fldigi
sdrconnect;
};
Defining the list this way via the builtin function seems to be used quite frequently in most people’s repos for doing NixOS stuff. However, I ran into a problem defining things this way, which led to me using this direct list construct elsewhere instead:
environment.systemPackages = with pkgs; [
(pass.withExtensions (ext: with ext; [pass-otp]))
pass
wineWowPackages.stagingFull
];
because neither the embedded whatever is happening with pass extensions in that first line nor the period in the wine package are allowed when trying to use the other approach.
I’m not clear enough on the Nix language here to understand the exact difference between how these two constructs are working compared to each other. I was hoping someone here could shed some light on it. Why do those peculiar package name variations not work in the first form? Is there a way to make them work in the first form? Is there any advantage or disadvantage to using either form?
I’m clearly using a mixture of both strewn throughout these various modules, and that is also working fine seemingly. It would be nice to understand a little more about what is happening here under the covers, so to speak.
And more generally, is this just a terrible approach to trying to provision mixed stable/unstable based hosts via the same flake? It looks to be the cleanest approach I’ve seen thus far, but I’m not sure about any potential sacrifices I’ve made with this particular structure, specifically declaring home-manager in the way I’m doing it here versus the way I’ve seen it more commonly elsewhere (and had been using myself previously) where you have something like:
homeConfigurations = {
"nipsy@ginaz" = lib.homeManagerConfiguration ...
Lot to chew on there. Sorry about that. I’ve tried to wrap my head around as much of this as I can. The skill cliff is very real!