My primary laptop.
{
lib,
pkgs,
...
}:
let
<<make-subvolume-mount>>
in
{
networking.hostName = "athena";
nixpkgs.hostPlatform = "aarch64-linux";
system.stateVersion = "25.05";
<<nixos-config>>
_class = "nixos";
}athena is a 2023 16" MacBook Pro with an M2 Max. It used to be a work laptop, but I was allowed to keep it when I left that job. After it was wiped, I installed NixOS on it almost immediately, using the nixos-apple-silicon project.
I lucked out here, as the M2 is currently the most powerful Apple Silicon processor that has decent Linux support. It's a pretty beastly machine aside from the 32GB of RAM, which feels a bit anemic when trying to do big Nix builds.
This is probably the workstation I use the most often these days.
boot.initrd.availableKernelModules = [ "usb_storage" "usbhid" "sdhci_pci" "rtc_macsmc" "spmi_apple_controller" "apple_nvmem_spmi" ]; boot.initrd.kernelModules = [ "dm-snapshot" ];
Most of this came from nixos-generate-config. rtc_macsmc and the two below it are there to get support for the real-time clock as early as possible, since before that, the time will be incorrect. This is particularly important for auto-login, as an invalid time can cause keys in the kernel keyring to be expired prematurely.
hardware.enableRedistributableFirmware = true;
I'm not sure if this enables any relevant firmware for this machine, but it doesn't hurt.
boot.initrd.luks.devices.cryptroot = {
device = "/dev/disk/by-partuuid/8a1cdb50-eaca-460e-8348-47d58050f9f4";
preLVM = true;
};The partition on the disk that is used for Linux is set up as btrfs-on-LVM-on-LUKS. The LUKS container needs to be unlocked on boot, and since it holds the root FS, that needs to be done in stage 1. Since the LUKS container contains an LVM volume group, the preLVM option must be set to true, so unlocking happens before LVM is set up.
mkSubvolumeMount = name: {
device = "/dev/lvm/nixos";
fsType = "btrfs";
options = [
"subvol=${name}"
"compress=zstd"
"relatime"
];
};fileSystems."/" = mkSubvolumeMount "root"; fileSystems."/nix" = mkSubvolumeMount "nix"; fileSystems."/home" = mkSubvolumeMount "home";
This machine uses three btrfs subvolumes for /, /nix, and /home. / and /nix actually get mounted in stage 1, but NixOS has special treatment for these mountpoints and a few others and marks them as neededForBoot automatically.
fileSystems."/boot" = {
device = "/dev/disk/by-partuuid/47ecfec3-26aa-4d4c-a7e2-e76e6fddd09d";
fsType = "vfat";
options = [
"fmask=0022"
"dmask=0022"
];
};
boot.loader.systemd-boot.enable = true;My systems use systemd-boot wherever possible, as it's a simple reliable bootloader for UEFI. athena is my only workstation that can't use Lanzaboote for SecureBoot, because it lacks a traditional TPM. Instead, it has Apple's Secure Enclave, which doesn't currently have Linux support.
Either way, I need the EFI system partition mounted at /boot.
swapDevices = [
{
device = "/var/lib/swapfile";
size = 32 * 1024;
}
];
boot.zswap.enable = true;Everybody needs a little swap. 32G to be specific. And since there's actual swap space on disk, I enable zswap.
mjm.desktop.enable = true; mjm.desktop.niri.enable = true; mjm.desktop.autoLogin.enable = true;
Since it's a workstation, it uses my own desktop module with all of the desktop configuration. I'm using niri on all of my workstations.
I'm also enabling auto-login, which for my machines means I'm unlocking the disk using a PIN of some kind (FIDO2 in this case, since athena doesn't have a TPM) and then using that same PIN to also unlock gnome-keyring after automatically logging in at the display manager.
hardware.asahi.enable = true;
Since this is an Apple Silicon machine, Asahi hardware support needs to be enabled. The Apple silicon support modules are imported in my nixos/base module, but disabled by default.
services.kmscon.config.font-size = 32;
athena has a pretty high resolution display, so I've configured the console to be a bit bigger than the default so it's actually readable.
powerManagement.resumeCommands = ''
${pkgs.tuxvdmtool}/bin/tuxvdmtool disconnect
'';tuxvdmtool is a special tool from Asahi Linux. I think its main purpose is getting a serial console to an Apple Silicon device, but that's not why I have it installed. There's a bug around USB devices and suspend on Asahi Linux, where the devices don't reconnect when waking. This is annoying for the YubiKey that I always have connected to this laptop for SSH. With this tool, I can run "tuxvdmtool disconnect" automatically when waking from suspend, and any USB devices will reconnect.
nix.settings.max-jobs = 4;
By default, Nix will run as many concurrent builds as you have CPU cores. athena has 12 cores, but only 32GiB of RAM. Without extra configuration, that makes it very easy for a Nix build to exhaust RAM by running too many jobs at once, so I've configured it to only run four builds at a time.
Even if I had more RAM, it would probably still be a good idea to limit concurrent Nix builds more than the default. Each build can potentially use all of the cores in the system (unless you configure that limit to be lower), so with the default settings you can end up with pretty high resource contention. You're generally better off reducing these a bit, so that you have some oversubscription (in case you have builds that don't use many resources) but not nearly as much as there is by default.
nix.distributedBuilds = true;
nix.buildMachines = [
{
hostName = "hades.home.mattmoriarity.com";
sshUser = "mjm";
system = "x86_64-linux";
protocol = "ssh-ng";
maxJobs = 6;
supportedFeatures = [
"kvm"
"nixos-test"
"benchmark"
"big-parallel"
];
}
];
boot.binfmt.emulatedSystems = lib.mkForce [ ];athena runs a different architecture (aarch64-linux) than most of the machines in my lab (x86_64-linux) which can make working on changes to those machines kind of annoying. Normally I would use qemu-user binfmt emulation to paper over this, but it's slow and doesn't always work correctly. Instead, I'm using my CI build machine as a remote builder for Nix. This requires the root user on athena to have an SSH key that is trusted by hades.
specialisation.plasma.configuration = {
mjm.desktop.niri.enable = lib.mkForce false;
mjm.desktop.plasma.enable = true;
programs.ssh.startAgent = true;
};I keep around a specialisation of the system that uses Plasma 6 instead of niri. The SSH agent option is because the Niri setup uses gnome-keyring, which includes an SSH agent as part of it. Plasma doesn't do something like this by default, so I need to enable starting the normal SSH agent.
text/gemini;lang=en-USThis content has been proxied by September (UNKNO).