Skip to content

Repository files navigation

HyprNixOS

A NixOS configuration flake for a Hyprland-based desktop, built with flake-parts and import-tree.

Every sub-directory of modules/hosts/ is a host: its name becomes the nixosConfiguration (and hostname), and its private modules live alongside it. Everything else under modules/ is shared across all hosts. CPU/GPU variant files (e.g. modules/system/hardware/cpu/amd.nix) are auto-selected per host from the cpu/gpu options, and each host picks its desktop with the desktop option (hyprland or kde).

Installing on a new host

nix develop github:qweered/hyprnixos#install

It prints its own help on entry. The command takes the host name (the directory under modules/hosts/):

# partition + format the disk and install the whole system (DESTRUCTIVE)
hyprnixos-install new-host

reboot

# from the booted system, every later rebuild is just plain nh:
nh os switch          # aliased to nh-switch

The target disk is not a command-line argument — it comes from the host's filesystems.nix. To retarget, edit disko.devices.disk.<name>.device there before running the install (hyprnixos-install rejects extra arguments so a stale sda/nvme0n1 guess can't silently wipe the wrong disk).

hyprnixos-install follows the standard NixOS install flow in two phases so the system closure never has to fit in the live ISO's RAM:

  1. format + mountdisko partitions/formats the disk (device from filesystems.nix) and mounts it at /mnt. Only the small disko config is evaluated here, so the tmpfs store stays nearly empty.
  2. installnixos-install builds the closure straight onto /mnt (the real disk) with a live nix-output-monitor progress graph, then the temporary swapfile is torn down (including on failure).

Low-RAM machines are handled by that two-phase split, not by swap alone. A NixOS live ISO keeps /nix/store's writable layer in a RAM-backed tmpfs, so building a large desktop closure there runs out of memory ("No space left on device"). Streaming the closure to /mnt in phase 2 sidesteps that entirely. The swapfile is extra insurance for the flake evaluation and any from-source builds, which still touch RAM; it lives only at /mnt/.install-swap during the install and is swapoff'd and deleted afterwards, so it never reaches the installed system (no swap in your declarative config). It defaults to 8G — bump the count= in install-shell.nix if a build still runs out.

A bare host installs from the flake in the current directory (the clone you're in); pass a full <flake#host> to install from elsewhere instead (e.g. hyprnixos-install github:qweered/hyprnixos#new-host). Once you've rebooted, nothing extra is needed — modules/system/programs/nix.nix is now live, so the plain nh os switch (the nh-switch alias) already knows every cache, and the hostname was set from the host directory name at install time, so nh resolves the right configuration without a -H flag.

Notes specific to this setup

  • new-host needs real values before it'll install. The hostname is derived from the directory name, so there's nothing to set for it — but cpu/gpu in modules/hosts/new-host/options.nix are CHANGE ME placeholders, and device in modules/hosts/new-host/filesystems.nix defaults to /dev/vda (a VM disk). Point device at the host's real disk before formatting anything for real — it is the only place the install reads the target from.

  • sops secrets are keyed to the host's SSH key (converted to an age key). A brand-new host isn't a recipient of anything yet, so secrets won't decrypt at activation — enroll it before the first real switch: copy its /etc/ssh/ssh_host_ed25519_key.pub into modules/hosts/<name>/ (or ssh-keyscan -t ed25519 <host> | cut -d' ' -f2-), git add it, and run nix run .#sops-sync. The .pub is committed verbatim so it stays checkable against ssh-keyscan; the age conversion happens at build time.

    Hosts are not trusted by default. Each sops file has its own recipient list: secrets/common.yaml (all hosts), secrets/users/<name>.yaml (only hosts that enable that user), secrets/hosts/<name>.yaml (only that host). A host can never decrypt a secret outside its scopes, and the nix declarations mirror the same boundaries (modules/system/security/sops.nix, the modules/users/<name>.nix profiles, modules/hosts/<name>/). .sops.yaml is generated from the host and user options by nix run .#sops-sync — never edit it by hand; a pre-commit hook rejects drift.

Doing it by hand

If you'd rather not use the shell, run the same steps directly — pass the caches with --option extra-substituters "<urls>" and --option extra-trusted-public-keys "<keys>" (the exact values are in modules/system/programs/nix.nix). The wrappers in modules/flake-parts/install-shell.nix show the full command for each step.

How to use

  • Replace credentials in modules/home/programs/programming/git.nix
  • For new hosts, add a directory under modules/hosts/<name>/
  • For new users, add a profile under modules/users/<name>.nix and enable it on a host with hyprnixos.users.<name>.enable = true
  • Add or edit secrets with sops secrets/<scope>.yaml (decrypts with the GPG admin key from .sops.yaml), then declare them at the matching scope: common ones in modules/system/security/sops.nix, user ones in modules/users/<name>.nix, host ones in modules/hosts/<name>/

Secure Boot (first boot on a new host)

Key creation and enrollment are handled automatically by the Limine bootloader: on the first nixos-rebuild where no keys exist yet, it runs sbctl create-keys, sbctl enroll-keys, and signs the bootloader. This only triggers when the host sets secureBootConfigured = true and /var/lib/sbctl does not already exist.

Only the firmware-side steps remain manual, because they require physical presence and cannot be scripted:

  1. systemctl reboot --firmware-setup
  2. In the firmware, enable Secure Boot Setup Mode (or erase the existing keys). sbctl enroll-keys only succeeds while the Platform Key is cleared. Take care on ThinkPad and Framework 13.
  3. Set secureBootConfigured = true in the host options.
  4. Run nixos-rebuild boot --flake . — the module now creates, enrolls, and signs with no further input.
  5. Reboot into the firmware once more and confirm Secure Boot is enabled.

Attributions

  • Zaney and his ZaneyOS for the solid base for my system
  • Fufexan for the Hyprland, hyprlock, and hypridle stuff

About

No description, website, or topics provided.

Resources

Stars

9 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages