From d14196dcded4ccb2e31da02fd40a67d59e43a330 Mon Sep 17 00:00:00 2001 From: Zephrynis Date: Sat, 18 Jul 2026 15:41:54 +0100 Subject: [PATCH] Initial NixOS flake: Hyprland (end-4 illogical-impulse), NVIDIA, gaming, dev tools Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01C5ydBQk68JYw8HXzUDLAyV --- .gitattributes | 1 + README.md | 88 +++++++++++++++++++++++ flake.nix | 40 +++++++++++ home/zephrynis.nix | 47 ++++++++++++ hosts/nixos-pc/default.nix | 39 ++++++++++ hosts/nixos-pc/hardware-configuration.nix | 26 +++++++ modules/core.nix | 47 ++++++++++++ modules/desktop.nix | 31 ++++++++ modules/gaming.nix | 19 +++++ modules/nvidia.nix | 30 ++++++++ 10 files changed, 368 insertions(+) create mode 100644 .gitattributes create mode 100644 README.md create mode 100644 flake.nix create mode 100644 home/zephrynis.nix create mode 100644 hosts/nixos-pc/default.nix create mode 100644 hosts/nixos-pc/hardware-configuration.nix create mode 100644 modules/core.nix create mode 100644 modules/desktop.nix create mode 100644 modules/gaming.nix create mode 100644 modules/nvidia.nix diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ebb692 --- /dev/null +++ b/README.md @@ -0,0 +1,88 @@ +# nixos-pc + +NixOS flake for the desktop PC: Hyprland with [end-4's illogical-impulse](https://github.com/end-4/dots-hyprland) via [soymou/illogical-flake](https://github.com/soymou/illogical-flake), NVIDIA RTX 3080, Steam/gaming, dev tools. + +- Host: `nixos-pc` · User: `zephrynis` · Channel: nixos-unstable +- Boot: systemd-boot on this install's **own ESP** on the 1TB WD SSD; **rEFInd** (on the 2TB NVMe's ESP) remains the top-level OS picker and auto-detects it. + +``` +hosts/nixos-pc/ host wiring, bootloader, user, hardware config +modules/ core, desktop (hyprland/sddm/fonts), nvidia, gaming +home/zephrynis.nix illogical-impulse + git/direnv/vscode/CLI tools +``` + +Rebuild after changes: `sudo nixos-rebuild switch --flake .#nixos-pc` +Update inputs (one at a time is safer): `nix flake update ` + +--- + +## Install procedure + +Target: ~250GB shrunk from the work-Windows partition on the **1TB WD SA500 SATA SSD**. The work Windows on that disk and everything on the 2TB NVMe (other Windows, Arch, rEFInd's ESP) must not be touched. + +### 1. Prep (from the work Windows) + +1. Back up anything important. If BitLocker is enabled on C:, **export the recovery key** and suspend BitLocker before repartitioning. +2. Disable Fast Startup: `powercfg /h off` (admin) — otherwise NTFS is left dirty. +3. Shrink C: by ~256000 MB: Disk Management → right-click C: → Shrink Volume. If it won't shrink enough (unmovable files): temporarily disable pagefile + System Restore on C:, reboot, retry, re-enable after. +4. Push this repo somewhere reachable from the ISO (GitHub) or copy to a USB stick. **Flakes only evaluate git-tracked files** — commit everything. +5. BIOS: disable Secure Boot (stock NixOS bootloader is unsigned). F11 = one-time boot menu. + +### 2. Partition & install (NixOS ISO, UEFI mode) + +```sh +ls /sys/firmware/efi # must exist (UEFI mode) +lsblk -o NAME,SIZE,FSTYPE,LABEL,MODEL # identify disks +``` + +The 1TB WD (WDS100T1R0A) is likely `/dev/sda`. **Do not touch any NTFS partition, and nothing on the NVMe.** In the ~250G free space (parted `unit GiB`, `print free` to see exact bounds): + +```sh +parted /dev/sda -- mkpart ESP fat32 +parted /dev/sda -- set esp on +parted /dev/sda -- mkpart root ext4 100% + +mkfs.fat -F 32 -n NIXBOOT /dev/sdaX # the new 1GiB partition +mkfs.ext4 -L nixos /dev/sdaY # the new large partition + +mount /dev/disk/by-label/nixos /mnt +mkdir -p /mnt/boot +mount -o umask=0077 /dev/disk/by-label/NIXBOOT /mnt/boot +``` + +Get the flake and the real hardware config: + +```sh +git clone /mnt/etc/nixos # or copy from USB +nixos-generate-config --root /mnt +mv /mnt/etc/nixos/hardware-configuration.nix /mnt/etc/nixos/hosts/nixos-pc/hardware-configuration.nix +rm /mnt/etc/nixos/configuration.nix +git -C /mnt/etc/nixos add -A +``` + +Install (needs network — ethernet just works, else `nmtui`): + +```sh +nixos-install --flake /mnt/etc/nixos#nixos-pc # sets root password at the end +nixos-enter --root /mnt -c 'passwd zephrynis' +reboot +``` + +### 3. After first boot + +1. rEFInd should show a new NixOS/systemd-boot entry (it scans all ESPs). Windows and Arch entries must still work. +2. If the firmware now boots straight into systemd-boot instead of rEFInd, fix the order from NixOS: `efibootmgr` to list, `sudo efibootmgr -o XXXX,YYYY,...` with rEFInd first. +3. Optional rEFInd polish (edit `refind.conf` on the NVMe ESP): a `menuentry` stanza for a custom NixOS name/icon, and `dont_scan_files`/`dont_scan_dirs` to hide duplicate auto-detected kernel entries from the NIXBOOT ESP. +4. Checks: `nvidia-smi`; `cat /sys/module/nvidia_drm/parameters/modeset` → `Y`; Hyprland session in SDDM; quickshell bar appears (Super for launcher); Steam runs a Proton title. + - If the `qs` (quickshell) binary is missing, add `inputs.illogical-flake.inputs.quickshell.packages.x86_64-linux.default` to `home.packages` and rebuild. + - Cursor invisible/glitchy: enable Hyprland `cursor:no_hardware_cursors = true`, or the commented `WLR_NO_HARDWARE_CURSORS` fallback in `modules/nvidia.nix`. + +### Follow-up (someday): separate rEFInd entries for the two Windows installs + +Both Windows currently share one Windows Boot Manager/BCD on the NVMe ESP, so rEFInd shows a single Windows entry with a second picker behind it. The new ESP on the 1TB SSD makes splitting easy: + +1. From the **work Windows**, assign the NIXBOOT ESP a letter (e.g. `S:` via `diskpart` → `assign`), then: + `bcdboot C:\Windows /s S: /f UEFI` + This creates an independent Windows Boot Manager + BCD on the 1TB SSD's ESP that boots only the work OS. 1GiB is plenty for both this and NixOS's kernels. +2. rEFInd then shows two distinct Windows Boot Manager entries (one per ESP). +3. Remove the work OS's entry from the old shared BCD (`msconfig` → Boot tab, or `bcdedit /delete `) so each menu boots exactly one Windows. diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..32655f5 --- /dev/null +++ b/flake.nix @@ -0,0 +1,40 @@ +{ + description = "nixos-pc — NixOS + Hyprland (end-4 illogical-impulse)"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # end-4 dots-hyprland (illogical-impulse) home-manager port + illogical-flake = { + url = "github:soymou/illogical-flake"; + inputs.nixpkgs.follows = "nixpkgs"; + # To pin or fork the end-4 dotfiles themselves, override the port's + # `dotfiles` input (this is the only source-override mechanism): + # inputs.dotfiles.url = "git+https://github.com/end-4/dots-hyprland?submodules=1&rev="; + }; + }; + + outputs = { nixpkgs, home-manager, ... }@inputs: { + nixosConfigurations.nixos-pc = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + specialArgs = { inherit inputs; }; + modules = [ + ./hosts/nixos-pc + + home-manager.nixosModules.home-manager + { + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + home-manager.extraSpecialArgs = { inherit inputs; }; + home-manager.backupFileExtension = "hm-bak"; + home-manager.users.zephrynis = import ./home/zephrynis.nix; + } + ]; + }; + }; +} diff --git a/home/zephrynis.nix b/home/zephrynis.nix new file mode 100644 index 0000000..d99033a --- /dev/null +++ b/home/zephrynis.nix @@ -0,0 +1,47 @@ +{ inputs, pkgs, ... }: + +{ + imports = [ inputs.illogical-flake.homeManagerModules.default ]; + + home.username = "zephrynis"; + home.homeDirectory = "/home/zephrynis"; + # Matches the release era of system.stateVersion; never change afterwards. + home.stateVersion = "25.11"; + + programs.illogical-impulse = { + enable = true; + # All default to true — listed for discoverability + dotfiles = { + fish.enable = true; + kitty.enable = true; + starship.enable = true; + }; + # hyprland.plugins = [ ]; # listOf package, loaded via hyprland's plugin mechanism + }; + + programs.git = { + enable = true; + userName = "zephrynis"; + userEmail = "zephrynis.yt@gmail.com"; + }; + + programs.direnv = { + enable = true; + nix-direnv.enable = true; + }; + + programs.vscode.enable = true; + + home.packages = with pkgs; [ + ripgrep + fd + fzf + jq + btop + eza + bat + tree + nixfmt-rfc-style + nil # Nix LSP + ]; +} diff --git a/hosts/nixos-pc/default.nix b/hosts/nixos-pc/default.nix new file mode 100644 index 0000000..7aeee56 --- /dev/null +++ b/hosts/nixos-pc/default.nix @@ -0,0 +1,39 @@ +{ pkgs, ... }: + +{ + imports = [ + ./hardware-configuration.nix + ../../modules/core.nix + ../../modules/desktop.nix + ../../modules/nvidia.nix + ../../modules/gaming.nix + ]; + + networking.hostName = "nixos-pc"; + + # systemd-boot lives on this install's OWN ESP (on the 1TB WD SSD) and only + # serves NixOS generations. rEFInd on the NVMe ESP stays the top-level OS + # picker and auto-detects this as a new entry — do not add Windows/Arch + # entries here. + boot.loader = { + systemd-boot = { + enable = true; + configurationLimit = 10; + }; + efi.canTouchEfiVariables = true; + }; + + users.users.zephrynis = { + isNormalUser = true; + description = "zephrynis"; + extraGroups = [ "wheel" "networkmanager" "video" "input" ]; + shell = pkgs.fish; # end-4 dots are fish-centric + }; + programs.fish.enable = true; + + time.timeZone = "Europe/London"; + i18n.defaultLocale = "en_GB.UTF-8"; + + # Set to the NixOS release of the ISO used at install time; never change afterwards. + system.stateVersion = "25.11"; +} diff --git a/hosts/nixos-pc/hardware-configuration.nix b/hosts/nixos-pc/hardware-configuration.nix new file mode 100644 index 0000000..c5775c7 --- /dev/null +++ b/hosts/nixos-pc/hardware-configuration.nix @@ -0,0 +1,26 @@ +# PLACEHOLDER — on install day, overwrite this file with the one produced by +# nixos-generate-config --root /mnt +# (it lands in /mnt/etc/nixos/hardware-configuration.nix), then `git add` it — +# flakes only evaluate tracked files. The generated file adds the correct +# boot.initrd.availableKernelModules for this machine. +# +# The labels below match the mkfs commands in README.md (mkfs.ext4 -L nixos, +# mkfs.fat -n NIXBOOT), so this stub is bootable even before replacement. +{ ... }: + +{ + fileSystems."/" = { + device = "/dev/disk/by-label/nixos"; + fsType = "ext4"; + }; + + fileSystems."/boot" = { + device = "/dev/disk/by-label/NIXBOOT"; + fsType = "vfat"; + options = [ "fmask=0077" "dmask=0077" ]; + }; + + swapDevices = [ ]; # 128GB RAM — no swap + + hardware.cpu.intel.updateMicrocode = true; +} diff --git a/modules/core.nix b/modules/core.nix new file mode 100644 index 0000000..d3335c1 --- /dev/null +++ b/modules/core.nix @@ -0,0 +1,47 @@ +{ pkgs, ... }: + +{ + nix.settings = { + experimental-features = [ "nix-command" "flakes" ]; + auto-optimise-store = true; + trusted-users = [ "root" "@wheel" ]; + }; + nix.gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 14d"; + }; + + nixpkgs.config.allowUnfree = true; + + networking.networkmanager.enable = true; # required by illogical-flake + + hardware.bluetooth = { + enable = true; + powerOnBoot = true; + }; + + services.pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + }; + security.rtkit.enable = true; + + services.fstrim.enable = true; + + # Read the Windows/data partitions when needed + boot.supportedFilesystems = [ "ntfs" ]; + + environment.systemPackages = with pkgs; [ + wget + curl + file + unzip + p7zip + pciutils + usbutils + efibootmgr # check/fix boot order so rEFInd stays first + ]; +} diff --git a/modules/desktop.nix b/modules/desktop.nix new file mode 100644 index 0000000..5a605da --- /dev/null +++ b/modules/desktop.nix @@ -0,0 +1,31 @@ +{ pkgs, ... }: + +{ + # System-side requirements of illogical-flake (the HM module does the rest) + programs.hyprland.enable = true; + services.geoclue2.enable = true; # QtPositioning — weather / night light widgets + + services.displayManager.sddm = { + enable = true; + wayland.enable = true; + }; + + xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; # file pickers etc. + + security.polkit.enable = true; + programs.dconf.enable = true; + services.gvfs.enable = true; + services.udisks2.enable = true; + services.upower.enable = true; + + fonts.packages = with pkgs; [ + # named by the illogical-flake README + rubik + nerd-fonts.ubuntu + nerd-fonts.jetbrains-mono + material-symbols + noto-fonts + noto-fonts-cjk-sans + noto-fonts-color-emoji + ]; +} diff --git a/modules/gaming.nix b/modules/gaming.nix new file mode 100644 index 0000000..8da0c62 --- /dev/null +++ b/modules/gaming.nix @@ -0,0 +1,19 @@ +{ pkgs, ... }: + +{ + programs.steam = { + enable = true; + remotePlay.openFirewall = true; + gamescopeSession.enable = true; # "Steam (gamescope)" session in SDDM + extraCompatPackages = [ pkgs.proton-ge-bin ]; + }; + + programs.gamemode.enable = true; + programs.gamescope.enable = true; + # programs.gamescope.capSysNice = true; # left off — known to break gamescope under Steam + + environment.systemPackages = with pkgs; [ + mangohud + protonup-qt + ]; +} diff --git a/modules/nvidia.nix b/modules/nvidia.nix new file mode 100644 index 0000000..497032b --- /dev/null +++ b/modules/nvidia.nix @@ -0,0 +1,30 @@ +{ config, ... }: + +{ + services.xserver.videoDrivers = [ "nvidia" ]; + + hardware.graphics = { + enable = true; + enable32Bit = true; # Steam/Proton + }; + + hardware.nvidia = { + modesetting.enable = true; # nvidia-drm.modeset=1 — mandatory for Wayland + open = true; # RTX 3080 (Ampere): NVIDIA-recommended on driver >= 560 + nvidiaSettings = true; + powerManagement.enable = true; # preserves VRAM across suspend — avoids Wayland resume corruption + powerManagement.finegrained = false; # desktop GPU + package = config.boot.kernelPackages.nvidiaPackages.stable; + }; + + boot.kernelParams = [ "nvidia_drm.fbdev=1" ]; + + environment.sessionVariables = { + LIBVA_DRIVER_NAME = "nvidia"; + NVD_BACKEND = "direct"; + ELECTRON_OZONE_PLATFORM_HINT = "auto"; + # Fallbacks — only if symptoms appear (should be unnecessary with driver 560+ / explicit sync): + # WLR_NO_HARDWARE_CURSORS = "1"; # invisible/glitchy cursor + # __GLX_VENDOR_LIBRARY_NAME = "nvidia"; # Xwayland picks wrong GL vendor + }; +}