dot
vizie's NixOS desktop: Hyprland, CachyOS kernel and gaming tweaks
| 1 | # What CachyOS adds on top of plain Arch: its kernel and the cachyos-settings |
| 2 | # package (sysctl, udev rules, systemd defaults), ported file by file. The CachyOS |
| 3 | # file each part comes from is named next to it, so they can be compared when |
| 4 | # CachyOS changes something. |
| 5 | # |
| 6 | # Not ported, because it does nothing on this machine: the laptop audio power |
| 7 | # saving and touchpad rules, the old-Radeon (SI/CIK) amdgpu options, and |
| 8 | # debuginfod. Snapper root snapshots are replaced by NixOS generations: every |
| 9 | # rebuild is a boot menu entry you can go back to. |
| 10 | { |
| 11 | inputs, |
| 12 | lib, |
| 13 | pkgs, |
| 14 | ... |
| 15 | }: |
| 16 | let |
| 17 | # cachyos-settings: /usr/bin/game-performance. Runs a game with the |
| 18 | # "performance" power profile held, and the screensaver inhibited. Use it as |
| 19 | # a Steam launch option: game-performance %command% |
| 20 | game-performance = pkgs.writeShellApplication { |
| 21 | name = "game-performance"; |
| 22 | runtimeInputs = [ pkgs.power-profiles-daemon pkgs.systemd ]; |
| 23 | text = '' |
| 24 | if ! powerprofilesctl list | grep -q 'performance:'; then |
| 25 | exec "$@" |
| 26 | fi |
| 27 | if [ -n "''${GAME_PERFORMANCE_SCREENSAVER_ON:-}" ]; then |
| 28 | exec powerprofilesctl launch -p performance \ |
| 29 | -r "Launched with CachyOS game-performance utility" -- "$@" |
| 30 | else |
| 31 | exec systemd-inhibit --why "CachyOS game-performance is running" powerprofilesctl launch \ |
| 32 | -p performance -r "Launched with CachyOS game-performance utility" -- "$@" |
| 33 | fi |
| 34 | ''; |
| 35 | }; |
| 36 | |
| 37 | # cachyos-settings: /usr/bin/dlss-swapper. Steam launch option: |
| 38 | # dlss-swapper %command% |
| 39 | dlss-swapper = pkgs.writeShellScriptBin "dlss-swapper" '' |
| 40 | export PROTON_ENABLE_NGX_UPDATER=1 |
| 41 | export DXVK_NVAPI_DRS_NGX_DLSS_RR_OVERRIDE=on |
| 42 | export DXVK_NVAPI_DRS_NGX_DLSS_SR_OVERRIDE=on |
| 43 | export DXVK_NVAPI_DRS_NGX_DLSS_FG_OVERRIDE=on |
| 44 | export DXVK_NVAPI_DRS_NGX_DLSS_RR_OVERRIDE_RENDER_PRESET_SELECTION=render_preset_latest |
| 45 | export DXVK_NVAPI_DRS_NGX_DLSS_SR_OVERRIDE_RENDER_PRESET_SELECTION=render_preset_latest |
| 46 | exec "$@" |
| 47 | ''; |
| 48 | in |
| 49 | { |
| 50 | # --- Kernel ---------------------------------------------------------------- |
| 51 | |
| 52 | # The overlay pins the kernel to the nixpkgs it was built with, so the build |
| 53 | # doesn't change with this system's nixpkgs. |
| 54 | nixpkgs.overlays = [ inputs.nix-cachyos-kernel.overlays.pinned ]; |
| 55 | |
| 56 | # git.granstrm.com builds the kernel, its modules and the NVIDIA driver every |
| 57 | # night the kernel changes (flake.nix's `ci`), and serves them from its |
| 58 | # binary cache. |
| 59 | nix.settings = { |
| 60 | substituters = [ "https://cache.git.granstrm.com" ]; |
| 61 | trusted-public-keys = [ "cache.git.granstrm.com-1:Y6BjEJMQZ02kt2oOJ/99RU+NTDZDsBY8tqIiypq1ong=" ]; |
| 62 | }; |
| 63 | |
| 64 | # Same build as linux-cachyos on CachyOS's znver4 repo: EEVDF scheduler, |
| 65 | # 1000 Hz, full preemption, Clang ThinLTO, tuned for Zen 4 (also right for |
| 66 | # this Zen 5 CPU, as on CachyOS). |
| 67 | boot.kernelPackages = pkgs.cachyosKernels.linuxPackages-cachyos-latest-lto-zen4; |
| 68 | |
| 69 | boot.kernelParams = [ |
| 70 | "nowatchdog" |
| 71 | # cachyos-settings: udev/rules.d/30-zram.rules turns zswap off once zram is |
| 72 | # up; it is always up here, so it is simply off from boot. |
| 73 | "zswap.enabled=0" |
| 74 | ]; |
| 75 | |
| 76 | # modprobe.d/blacklist.conf |
| 77 | boot.blacklistedKernelModules = [ |
| 78 | "iTCO_wdt" |
| 79 | "sp5100_tco" |
| 80 | ]; |
| 81 | |
| 82 | # modules-load.d/ntsync.conf: Wine/Proton's NT synchronization primitives. |
| 83 | boot.kernelModules = [ "ntsync" ]; |
| 84 | |
| 85 | boot.extraModprobeConfig = '' |
| 86 | # modprobe.d/nvidia.conf |
| 87 | options nvidia NVreg_InitializeSystemMemoryAllocations=0 |
| 88 | # cachyos-iw-set-regdomain: the Wi-Fi regulatory domain, from the time zone. |
| 89 | options cfg80211 ieee80211_regdom="NO" |
| 90 | ''; |
| 91 | |
| 92 | # --- sysctl.d/70-cachyos-settings.conf -------------------------------------- |
| 93 | |
| 94 | boot.kernel.sysctl = { |
| 95 | # 150 rather than the file's 100: 30-zram.rules raises it to 150 as soon as |
| 96 | # zram is active, which on CachyOS is always. |
| 97 | "vm.swappiness" = 150; |
| 98 | "vm.vfs_cache_pressure" = 50; |
| 99 | "vm.dirty_bytes" = 268435456; |
| 100 | "vm.page-cluster" = 0; |
| 101 | "vm.dirty_background_bytes" = 67108864; |
| 102 | "vm.dirty_writeback_centisecs" = 1500; |
| 103 | "kernel.nmi_watchdog" = 0; |
| 104 | "kernel.unprivileged_userns_clone" = 1; |
| 105 | "kernel.printk" = "3 3 3 3"; |
| 106 | "kernel.kptr_restrict" = 2; |
| 107 | "net.core.netdev_max_backlog" = 4096; |
| 108 | "fs.file-max" = 2097152; |
| 109 | }; |
| 110 | |
| 111 | # --- udev rules --------------------------------------------------------------- |
| 112 | |
| 113 | services.udev.extraRules = '' |
| 114 | # 60-ioschedulers.rules |
| 115 | ACTION=="add|change", KERNEL=="sd[a-z]*", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq" |
| 116 | ACTION=="add|change", KERNEL=="sd[a-z]*|mmcblk[0-9]*", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline" |
| 117 | ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="kyber" |
| 118 | |
| 119 | # 40-hpet-permissions.rules, 99-cpu-dma-latency.rules |
| 120 | KERNEL=="rtc0", GROUP="audio" |
| 121 | KERNEL=="hpet", GROUP="audio" |
| 122 | DEVPATH=="/devices/virtual/misc/cpu_dma_latency", OWNER="root", GROUP="audio", MODE="0660" |
| 123 | |
| 124 | # 50-sata.rules |
| 125 | ACTION=="add", SUBSYSTEM=="scsi_host", KERNEL=="host*", ATTR{link_power_management_supported}=="1", ATTR{link_power_management_policy}=="*", ATTR{link_power_management_policy}="max_performance" |
| 126 | |
| 127 | # 71-nvidia.rules: runtime power management for the NVIDIA GPU. |
| 128 | ACTION=="add|bind", SUBSYSTEM=="pci", DRIVERS=="nvidia", ATTR{vendor}=="0x10de", ATTR{class}=="0x03[0-9]*", TEST=="power/control", ATTR{power/control}="auto" |
| 129 | ACTION=="remove|unbind", SUBSYSTEM=="pci", DRIVERS=="nvidia", ATTR{vendor}=="0x10de", ATTR{class}=="0x03[0-9]*", TEST=="power/control", ATTR{power/control}="on" |
| 130 | ''; |
| 131 | |
| 132 | # --- tmpfiles.d --------------------------------------------------------------- |
| 133 | |
| 134 | systemd.tmpfiles.rules = [ |
| 135 | # thp.conf, thp-shrinker.conf |
| 136 | "w! /sys/kernel/mm/transparent_hugepage/defrag - - - - defer+madvise" |
| 137 | "w! /sys/kernel/mm/transparent_hugepage/khugepaged/max_ptes_none - - - - 409" |
| 138 | # coredump.conf |
| 139 | "e /var/lib/systemd/coredump - - - 3d" |
| 140 | ]; |
| 141 | |
| 142 | # --- systemd ------------------------------------------------------------------ |
| 143 | |
| 144 | # system.conf.d/00-timeout.conf, 10-limits.conf |
| 145 | systemd.settings.Manager = { |
| 146 | DefaultTimeoutStartSec = "15s"; |
| 147 | DefaultTimeoutStopSec = "10s"; |
| 148 | DefaultLimitNOFILE = "2048:2097152"; |
| 149 | }; |
| 150 | # user.conf.d/00-timeout.conf, 10-limits.conf |
| 151 | systemd.user.settings.Manager = { |
| 152 | DefaultTimeoutStartSec = "15s"; |
| 153 | DefaultTimeoutStopSec = "10s"; |
| 154 | DefaultLimitNOFILE = "1024:1048576"; |
| 155 | }; |
| 156 | |
| 157 | # journald.conf.d/00-journal-size.conf |
| 158 | services.journald.settings.Journal.SystemMaxUse = "50M"; |
| 159 | |
| 160 | # timesyncd.conf.d/10-timesyncd.conf |
| 161 | services.timesyncd = { |
| 162 | servers = [ "time.cloudflare.com" ]; |
| 163 | fallbackServers = [ |
| 164 | "time.google.com" |
| 165 | "0.nixos.pool.ntp.org" |
| 166 | "1.nixos.pool.ntp.org" |
| 167 | ]; |
| 168 | }; |
| 169 | |
| 170 | # system/user@.service.d/delegate.conf: lets user services (and ananicy) |
| 171 | # manage their own CPU, I/O and memory controllers. |
| 172 | systemd.services."user@".serviceConfig.Delegate = "cpu cpuset io memory pids"; |
| 173 | |
| 174 | # system/rtkit-daemon.service.d/override.conf |
| 175 | security.rtkit.enable = true; |
| 176 | systemd.services.rtkit-daemon.serviceConfig.LogLevelMax = "info"; |
| 177 | |
| 178 | # systemd-oomd per-slice defaults (system.slice.d, user-.slice.d, |
| 179 | # session.slice.d). |
| 180 | systemd.oomd.enable = true; |
| 181 | systemd.slices = { |
| 182 | "system".sliceConfig = { |
| 183 | ManagedOOMSwap = "kill"; |
| 184 | ManagedOOMMemoryPressure = "kill"; |
| 185 | ManagedOOMMemoryPressureLimit = "80%"; |
| 186 | }; |
| 187 | "user-".sliceConfig = { |
| 188 | ManagedOOMSwap = "kill"; |
| 189 | ManagedOOMMemoryPressure = "kill"; |
| 190 | ManagedOOMMemoryPressureLimit = "80%"; |
| 191 | }; |
| 192 | }; |
| 193 | systemd.user.slices.session.sliceConfig.ManagedOOMPreference = "omit"; |
| 194 | |
| 195 | # system/pci-latency.service + /usr/bin/pci-latency |
| 196 | systemd.services.pci-latency = { |
| 197 | description = "Adjust latency timers for PCI peripherals"; |
| 198 | wantedBy = [ "multi-user.target" ]; |
| 199 | serviceConfig.Type = "oneshot"; |
| 200 | path = [ pkgs.pciutils ]; |
| 201 | script = '' |
| 202 | setpci -v -s '*:*' latency_timer=20 |
| 203 | setpci -v -s '0:0' latency_timer=0 |
| 204 | setpci -v -d "*:*:04xx" latency_timer=80 |
| 205 | ''; |
| 206 | }; |
| 207 | |
| 208 | # security/limits.d/20-audio.conf |
| 209 | security.pam.loginLimits = [ |
| 210 | { |
| 211 | domain = "@audio"; |
| 212 | item = "rtprio"; |
| 213 | type = "-"; |
| 214 | value = "99"; |
| 215 | } |
| 216 | { |
| 217 | domain = "@audio"; |
| 218 | item = "nice"; |
| 219 | type = "-"; |
| 220 | value = "-11"; |
| 221 | } |
| 222 | ]; |
| 223 | |
| 224 | hardware.wirelessRegulatoryDatabase = true; |
| 225 | |
| 226 | # --- CachyOS services --------------------------------------------------------- |
| 227 | |
| 228 | # ananicy-cpp with CachyOS's rules: renices games, compilers, browsers etc. |
| 229 | services.ananicy = { |
| 230 | enable = true; |
| 231 | package = pkgs.ananicy-cpp; |
| 232 | rulesProvider = pkgs.ananicy-rules-cachyos; |
| 233 | }; |
| 234 | |
| 235 | # bpftune: auto-tunes network and other kernel settings (enabled on CachyOS). |
| 236 | services.bpftune.enable = true; |
| 237 | |
| 238 | # sched-ext schedulers, as CachyOS's scx-scheds. Off there too (scx_loader was |
| 239 | # disabled); to try one: |
| 240 | # services.scx = { enable = true; scheduler = "scx_lavd"; }; |
| 241 | |
| 242 | # power-profiles-daemon, with the profile you had set on CachyOS. |
| 243 | services.power-profiles-daemon.enable = true; |
| 244 | # Started with the daemon itself (wanting it from multi-user.target makes an |
| 245 | # ordering cycle). Skipped where there is no performance profile, e.g. in a VM. |
| 246 | systemd.services.power-profile-performance = { |
| 247 | description = "Select the performance power profile"; |
| 248 | after = [ "power-profiles-daemon.service" ]; |
| 249 | bindsTo = [ "power-profiles-daemon.service" ]; |
| 250 | wantedBy = [ "power-profiles-daemon.service" ]; |
| 251 | serviceConfig.Type = "oneshot"; |
| 252 | path = [ pkgs.power-profiles-daemon ]; |
| 253 | script = '' |
| 254 | if powerprofilesctl list | grep -q 'performance:'; then |
| 255 | powerprofilesctl set performance |
| 256 | fi |
| 257 | ''; |
| 258 | }; |
| 259 | |
| 260 | environment.systemPackages = [ |
| 261 | game-performance |
| 262 | dlss-swapper |
| 263 | ]; |
| 264 | } |