Skip to content

Wimpy's NixOS, nix-darwin & Home Manager Configurations ❄️

License

Notifications You must be signed in to change notification settings

wimpysworld/nix-config

Repository files navigation

Sith Happens

Wimpy's NixOS, nix-darwin & Home Manager Configurations

Made with 💝 for NixOS & macOS

A Nix Flake managing NixOS, macOS, and Home Manager across all my systems from a single repo. The interesting bit is the architecture: every module is imported everywhere, and each module decides for itself whether to activate based on host metadata. No import lists to maintain, no per-host module selections - just drop a self-gating module in a directory and every relevant host picks it up automatically. If you're starting your own Nix configuration, I'd recommend https://github.com/Misterio77/nix-starter-configs as a foundation. 👍️ This repo is the deep end.

These computers are managed by this Nix flake ❄️

Hostname Board CPU RAM Primary GPU Secondary GPU Role OS State
malgus Framework Desktop Mainboard AMD Ryzen AI Max+ 395 128GB AMD Radeon 8060S 🖥️ ❄️ 🚧
zannah Framework Desktop Mainboard AMD Ryzen AI Max+ 395 128GB AMD Radeon 8060S 🖥️ ❄️ 🚧
sidious ThinkPad P1 Gen 1 Intel Xeon E-2176M 64GB NVIDIA Quadro P2000 Max-Q Intel UHD Graphics P630 💻️🎭️ ❄️
palpatine ⤴️ ⤴️ ⤴️ ⤴️ ⤴️ 💻️🎭️ 🪟
bane Framework 16 AMD Ryzen 7 7940HS 96GB AMD Radeon 780M 💻️ ❄️
tanis ThinkPad Z13 Gen 1 AMD Ryzen 5 PRO 6650U 32GB AMD Radeon 660M 💻️ ❄️
felkor ThinkPad X13 Gen 2 AMD Ryzen 5 PRO 5650U 16GB AMD Radeon Vega 7 💻️ ❄️ 🚧
shaa ThinkPad T14s Gen 1 AMD Ryzen 5 PRO 4650U 16GB AMD Radeon RX Vega 6 💻️ ❄️
atrius ThinkPad T495s AMD Ryzen 7 3700U 16GB AMD Radeon Vega 10 💻️ ❄️ 🚧
momin MacBook M3 Pro 11-core Apple M3 Pro chip 36GB 14-core GPU 💻️ 🍏
steamdeck Steam Deck 64GB LCD Zen 2 4c/8t 16GB 8 RDNA 2 CUs 🎮️ 🐧
crawler QEMU - - VirGL 🐄 ❄️
dagger QEMU - - VirGL 🐄 ❄️
defender Lima - - - 🐄 🐧
fighter Lima - - - 🐄 🐧
revan Z390-DESIGNARE Intel Core i9-9900K 64GB Intel UHD Graphics 630 NVIDIA T1000 ☁️ ❄️ 🚧
malak B360 HD3P-LM Intel Core i7-8700 128GB Intel UHD Graphics 630 - ☁️ ❄️
maul TRX40-DESIGNARE AMD Ryzen Threadripper 3970X 256GB NVIDIA RTX 3090 NVIDIA RTX 2080Ti (22GB) ☁️ ❄️ 🚧

Workstation and server host names are Sith Lords and the virtual machines are named after TIE fighter series. Dual boot systems have the NixOS install named a Sith Lord and the "other" OS named after their public-facing persona.

Key

  • 🎭️ : Dual boot
  • 🖥️ : Desktop
  • 💻️ : Laptop
  • 🎮️ : Games Machine
  • 🐄 : Virtual Machine
  • ☁️ : Server
  • 🧟 : Not in service

As featured on Linux Matters podcast! 🎙️ I am a presenter on Linux Matters and this configuration was featured in Episode 7 - Immutable Desktop Linux for Anyone.

Linux Matters Podcast
Linux Matters Podcast

How It Works 🧠

Most NixOS configurations use selective imports - each host cherry-picks which modules to include. This flake does the opposite.

Every module is imported by every host. Modules decide internally whether to activate, based on typed host metadata. I call this the "broadcast-and-gate" pattern, and it changes how you think about composing NixOS configurations.

Self-gating modules

The _mixins directories contain modules that are imported universally. Each module uses lib.mkIf with metadata from the noughty module system to decide whether it should do anything:

# This module activates itself on workstations. Servers and VMs ignore it.
{ config, lib, ... }:
lib.mkIf config.noughty.host.is.workstation {
  boot.plymouth.enable = true;
}

A module whose condition is false evaluates to nothing - zero cost, zero side effects. The Nix module system handles this natively.

Conditions can be as specific as you need: workstation vs server, laptop vs desktop, GPU vendor, display layout, user identity, freeform tags. Modules can combine conditions too - "only on Martin's workstations that have an NVIDIA GPU" is a one-liner.

What noughty provides

Every host is registered in the system registry (lib/registry-systems.toml, read by flake.nix) with its properties: what kind of system it is, its platform, form factor, GPU vendors, displays, and freeform tags. The noughty module system turns this registry into typed, overridable NixOS options under config.noughty.*, with derived booleans like host.is.workstation, host.is.laptop, and host.gpu.hasNvidia computed automatically.

Why this matters

Adding a new feature = drop a directory containing a self-gating module. No import lists to edit. No other files to touch. Every host that matches the condition picks it up on next build.

Adding a new host = add a registry entry to lib/registry-systems.toml and create a hardware config. That's it. The new host automatically gets the right desktop, GPU drivers, services, shell, everything - because the modules gate themselves based on the host's properties, not its name.

Host-specific directories (nixos/vader/, nixos/sidious/, etc.) contain only hardware: disk layouts and kernel modules. All behaviour lives in the self-gating modules reacting to host properties.

The full noughty option reference, helper functions, and usage patterns are documented in lib/noughty/README.md.

Structure 📦

Directory tree
.
├── common
│  └── default.nix
├── darwin
│  ├── _mixins
│  │  ├── desktop
│  │  └── features
│  ├── momin
│  └── default.nix
├── home-manager
│  ├── _mixins
│  │  ├── desktop
│  │  ├── development
│  │  ├── filesync
│  │  ├── scripts
│  │  ├── services
│  │  ├── terminal
│  │  └── users
│  └── default.nix
├── lib
│  ├── noughty
│  │  └── default.nix
│  ├── default.nix
│  ├── flake-builders.nix
│  ├── noughty-helpers.nix
│  ├── registry-systems.toml
│  └── registry-users.toml
├── nixos
│  ├── _mixins
│  │  ├── console
│  │  ├── desktop
│  │  ├── hardware
│  │  ├── network
│  │  ├── policy
│  │  ├── scripts
│  │  ├── server
│  │  ├── users
│  │  └── virtualisation
│  ├── atrius
│  ├── bane
│  ├── crawler -> dagger
│  ├── dagger
│  ├── felkor
│  ├── malak
│  ├── maul
│  ├── nihilus
│  ├── phasma
│  ├── revan
│  ├── shaa
│  ├── sidious
│  ├── tanis
│  ├── vader
│  └── default.nix
├── overlays
│  └── default.nix
├── pkgs
│  └── default.nix
├── secrets
│  └── secrets.yaml
├── flake.nix
└── justfile
Directory Purpose
common/ Shared configuration imported by both NixOS and nix-darwin: overlays, system packages, environment variables, shell settings
darwin/ macOS system configuration and _mixins for Darwin-specific features
home-manager/ Home Manager configuration and _mixins for user-level programs, dotfiles, and scripts
nixos/ NixOS system configuration, _mixins for self-gating modules, and per-host hardware directories
lib/ The noughty module system, flake-builders.nix for generating configs from the system registry, registry-systems.toml and registry-users.toml for host and user definitions, and pure helper functions
overlays/ Custom overlays: local packages, modified packages, and nixpkgs-unstable access
pkgs/ Custom local packages
secrets/ Encrypted secrets managed by sops-nix

CI/CD workflows in .github are Nix ❄️ supercharged ⚡️ by Determinate Systems: Install Determinate Nix, FlakeHub Cache, Flake Checker, and Update Flake Lock.

The Shell 🐚

A fully "oxidised" 🦀 Modern Unix shell experience is provided by Fish shell 🐟️ with Starship 🚀 and a collection of tools that deliver a contempory UX to my terminal ‍🧑‍💻

The base system has a firewall enabled and also includes OpenSSH, sops-nix for secret management, Tailscale and, of course, a gloriously unrepentant modeless Neovim config that treats modal editing as Stockholm syndrome. (Fight me! 🥊) Useful shell scripts I used to keep in muddle of git repos are now migrated to NixOS scripts and Home Manager scripts to provide a declarative, reproducible and shellcheck validated toolbox 🧰

fastfetch on Phasma

The Desktop 🖥️

Hyprland 💧 and Wayfire 🔥 desktop options are available. The font configuration is common for all desktops using Work Sans and Fira Code. The usual creature comforts you'd expect to find in a Linux Desktop are integrated such as Pipewire, Bluetooth, Avahi, CUPS, SANE and NetworkManager.

Desktops NixOS Home Manager Theme
💧 Hyprland Hyprland NixOS Hyprland Home Manager Catppuccin Mocha
🔥 Wayfire Wayfire NixOS Wayfire Home Manager Catppuccin Mocha

Eye Candy 👀🍬

Hyprland on Shaa

Hyprland on Shaa; a work in progress; soon to be daily driver

Installing 💾

  • Boot off an .iso image created by this flake using just iso (see below) 💿
  • Put the .iso image on a USB drive, I use USBImager
  • Boot the target computer from the USB drive
  • From a trusted workstation, inject tokens to the ISO host:
    just inject-tokens <ip-address>
    This sends the SOPS age keys to the live environment. Both age keys are hard requirements - the install will abort without them. FlakeHub authentication is handled interactively during install via determinate-nixd login. If determinate-nixd is authenticated, the installer uses FlakeHub Cache for a faster install; otherwise it builds locally.
  • SSH into the ISO host and run the installer:
    ssh nixos@<ip-address>
    install-system <hostname> [username]
    The install script uses Disko to partition and format the disks, installs NixOS via nixos-install, copies the flake to ~/Zero/nix-config, and chroots into the new system to activate the Home Manager configuration.
  • Make a cuppa 🫖
  • Reboot 🥾

See install-system documentation for the full reference.

Installing to a remote host 🌍

As Disko is used to declare the disk layout for all my NixOS hosts, each NixOS configuration can be deployed to a remote host using nixos-anywhere. The justfile includes an install recipe that wraps nixos-anywhere to simplify remote deployments. For example, malak is a Hetzner dedicated server. To deploy it, enable the Hetzner Rescue system and then run the following from one of my workstations:

just install malak <ip-address>

Optional parameters: keep_disks="true" preserves existing disk partitions, and vm_test="true" runs a local VM test instead of deploying.

When the deployment is complete, the remote host will be automatically rebooted. The just install recipe handles SOPS age keys (user and host), initrd SSH keys, and per-host SSH host keys automatically, decrypting them from sops secrets and injecting them into the target.

I keep my Home Manager configuration separate from my NixOS configuration, so after the NixOS configuration has been deployed, I SSH in to the remote host and activate the Home Manager configuration:

git clone https://github.com/wimpysworld/nix-config "$HOME/Zero/nix-config"
home-manager switch -b backup --flake "$HOME/Zero/nix-config"

Applying Changes ✨

I clone this repo to ~/Zero/nix-config. NixOS and Home Manager changes are applied separately because I tend to iterate on the Home Manager configuration more frequently than the NixOS configuration.

gh repo clone wimpysworld/nix-config "$HOME/Zero/nix-config"
cd "$HOME/Zero/nix-config"

This flake includes a justfile that provides convenient commands for building and applying configurations.

  • ❄️ NixOS & macOS: Use just host to build and switch the NixOS or nix-darwin configuration.
    • just build-host to only build.
    • just switch-host to only switch.
  • 🏠️ Home Manager: Use just home to build and switch the Home Manager configuration.
    • just build-home to only build.
    • just switch-home to only switch.
  • 🌍️ All:
    • just build to build both NixOS/nix-darwin and Home Manager configurations.
    • just switch to switch to both NixOS/nix-darwin and Home Manager configurations.

The fast path: FlakeHub Cache

Every push to main builds all configurations in CI and publishes them to FlakeHub Cache with full output paths. The apply commands pull these pre-built closures directly using the fh CLI, completely skipping local Nix evaluation and compilation. No flake evaluation, no derivation realisation, just fetch and activate.

  • ⚡️ From FlakeHub Cache:
    • just apply to apply both NixOS/nix-darwin and Home Manager from FlakeHub Cache.
    • just apply-home to apply Home Manager only.
    • just apply-host to apply NixOS/nix-darwin only.

The tradeoff: you get whatever was last published from main. If you have local uncommitted changes, use just host/just home instead.

ISO 📀

Daily ISO builds are available for download from this project's Releases, built automatically via GitHub Actions. If you'd rather build your own, just iso builds the nihilus ISO image from this flake. It includes install-system for automated installation.

Live images will be left in result/iso/ and also copied to ~/Quickemu/nihilus/nixos.iso.

Building without just

If nh, nom, just and home-manager are not available, here are the traditional ways to build and switch.

NixOS

sudo nixos-rebuild boot --flake $HOME/Zero/nix-config
sudo nixos-rebuild switch --flake $HOME/Zero/nix-config
nix build .#nixosConfigurations.{hostname}.config.system.build.toplevel
nix run github:nix-community/nixos-anywhere -- --flake '.#{hostname}' root@{ip-address}

nix-darwin

nix run nix-darwin -- switch --flake ~/Zero/nix-config
nix build .#darwinConfigurations.{hostname}.config.system.build.toplevel

Home Manager

home-manager build --flake $HOME/Zero/nix-config -L
home-manager switch -b backup --flake $HOME/Zero/nix-config
nix run nixpkgs#home-manager -- build --flake "$HOME/Zero/nix-config"
nix run nixpkgs#home-manager -- switch -b backup --flake "$HOME/Zero/nix-config"

ISO

nix build .#nixosConfigurations.nihilus.config.system.build.isoImage

Post-install Checklist

Things I currently need to do manually after installation.

Secrets

  • 1Password - authenticate
  • LastPass - authenticate
  • Run just detsys-login
  • Run gh auth login
  • Keybase - keybase login

Accounts

  • Atuin
    • atuin login -u <username>
    • atuin sync -f
  • Brave - enroll sync
  • Chatterino - authenticate
  • Discord - authenticate
  • GitKraken
    • Authenticate with GitHub
    • Create Personal Profile
    • Create Work Profile
  • Grammarly - authenticate
  • Maelstral - maestral_qt
  • Matrix - authenticate
  • Slack - authenticate
  • Telegram - authenticate
  • VSCode - authenticate with GitHub enable sync
  • Wavebox - authenticate Google and restore profile

System

defender and fighter are Ubuntu virtual machines run under Lima for my Ubuntu MATE development and testing environments.

  • Tools
    • install-cdebug
    • install-chainctl
    • install-wolfi-package-status
    • install-yam
  • On Linux run
    • create-defender
    • create-fighter
  • On macOS run
    • Run Docker Desktop to complete the setup.

Malak

  • Create ntfy user and ACLs sudo ntfy user add --role=admin <username> sudo ntfy access everyone <topic> rw

Themes

Some applications require manual configuration to apply the correct theme.

  • Enable Stylus Sync to Dropbox in Brave to get Catppuccin userstyles and Enable Patch CSP
  • Cider
    • Open Cider
    • Menu → Marketplace → Themes
    • Find Catppuccin and your favorite flavor
    • Click Install
  • Discord OpenAsar
    • Add Catppuccin CSS
/* mocha */
@import url("https://catppuccin.github.io/discord/dist/catppuccin-mocha.theme.css");
@import url("https://catppuccin.github.io/discord/dist/catppuccin-mocha-blue.theme.css");