Skip to content

About

Hardware-aware post-install for Fedora 45 KDE Plasma 6 on Wayland: NVIDIA, AMD or Intel, desktop or laptop, dual-boot. Low-latency gaming, KDE and terminal rice, security and privacy hardening, suspend and audio fixes, snapper snapshots, emulation, opt-in local LLM and Windows 11 VM, release upgrades and a live verification script. CI-tested.

Topics

Resources

Contributing

Security policy

Stars

14 stars

Watchers

0 watching

Forks

Repository files navigation

Fedora 45 KDE — Full Setup Guide

ShellCheck Secret Scan Trivy Validate License: MIT Fedora KDE Plasma Wayland

Post-installation guide, config files, and scripts for Fedora 45 KDE Plasma 6 on Wayland. Focused on low-latency gaming and a clean rice.

The automated fresh-install setup supports Fedora 45 on x86_64 and refuses other releases and architectures before making changes. Its pre-install recovery requires a Btrfs root and /home on the same Btrfs filesystem, with no separate system-data mounts or nested home subvolumes outside the supported layout. On other layouts it asks whether to continue without that restore point; with --yes it stops. Other hardware paths have fixture coverage; the complete setup still needs validation on each target machine.

Requirements

Check these before running anything, so no script stops halfway:

For You need
Everything Fedora 45 with KDE Plasma 6 on x86_64, in Plasma's default Wayland session: the KDE Edition or another Fedora 45 install with Plasma. Fedora Kinoite and the other atomic variants have no dnf and are not supported. A regular user in the wheel group (the scripts call sudo themselves and refuse to run as root) and an internet connection.
fedora-setup.sh A fully updated system after a reboot (Quick Setup step 1) and at least 20 GB free on /. For the pre-install restore point: a Btrfs root with /home on the same filesystem, Fedora's default layout. On another layout it asks whether to go on without one.
An NVIDIA card with Secure Boot on Being at the machine for the next boot: the akmods signing key is enrolled at the blue MOK Manager screen, with a one-time password you choose. A later BIOS update clears that enrollment; scripts/mok-reenroll.sh restores it.
fedora-upgrade.sh At least 10 GB free on / and 200 MB on /boot; the script checks both before it changes anything.
strata-setup.sh An NVIDIA card, Turing or newer (tested with 12 GB of VRAM). About 86 GB for the model files on a disk outside /home, 5 GB for the CUDA toolkit and 1 GB for the checkout. At least 30 GB of RAM: the Coder model holds about 20 GB while loaded, and from 40 GB of RAM the script picks the full model, which needs about 48 GB.
ollama-setup.sh An NVIDIA card and about 14 GB of disk: Ollama 2.5 GB, the model 8.7 GB, the Alpaca app 2.3 GB. With 12 GB of VRAM the model runs almost entirely on the GPU; a smaller card runs part of it on the CPU.
emulation-setup.sh About 1.7 GB of disk for ES-DE and the emulators: 0.4 GB of AppImages, 1.3 GB of Flatpaks (RetroArch alone 0.8 GB). On a machine without Flatpak apps, up to 6 GB more for the runtimes they share: KDE 6.10 and 6.11 and Freedesktop 26.08 with their GL drivers. Your own BIOS, firmware and game dumps: the script installs the emulators and nothing else. Optionally a second disk for the ROMs.
windows-vm-setup.sh Virtualization switched on in the firmware setup (AMD SVM or Intel VT-x), at least 16 GB of RAM (the VM gets 8 GiB from 24 GB, else 4 GiB), room on / for a VM disk that grows up to 128 GiB, a Windows 11 x64 ISO from Microsoft and a Windows license.

Hardware Support

The scripts detect the hardware and adapt to it, so the same repo sets up a gaming desktop or a laptop, with an NVIDIA, AMD or Intel GPU:

Detected What the setup does
NVIDIA GPU, Turing (RTX 20 / GTX 16) or newer akmod-nvidia from RPM Fusion, NVIDIA Wayland and suspend configs; under Secure Boot it prints the MOK enrollment steps
NVIDIA GPU, Maxwell, Pascal or Volta (GTX 900 / 10) The 580xx driver branch, the last one that supports them. Older cards stay on nouveau
AMD GPU mesa-va-drivers-freeworld for H.264/H.265 hardware video
Intel GPU intel-media-driver for H.264/H.265 hardware video
Desktop tuned latency-performance, scx Gaming mode, workqueue.power_efficient=false
Laptop tuned keeps its battery-aware balanced profile, scx runs in Auto mode. With hybrid graphics the desktop stays on the integrated GPU
Windows in the UEFI boot list GRUB remembers the last-booted OS
btrfs root Snapper snapshots
AMD or Intel CPU CPU temperature from k10temp or coretemp in Conky and sysinfo

Detection lives in scripts/lib/hw.sh, and fedora-setup.sh prints what it found before it changes anything. To override it, copy setup.conf.example to setup.conf (git-ignored) and set FORM_FACTOR, NVIDIA_DRIVER or AUDIO_TRIM, or RICE=no to skip the Darth Vader look; a value outside the documented set stops the scripts before they change anything. Everything targets x86_64. Peripheral quirks (LAMZU mice, DualSense controllers, a few USB receivers) match on USB IDs and do nothing unless the device is plugged in.

Developed and verified on an AMD desktop with a discrete NVIDIA card next to the CPU's integrated GPU, dual-booting Windows. The other hardware paths are covered by tests/hw-detect.sh and the CI package check.


Repository Structure

├── .github/                    # CI workflows (shellcheck, validate, gitleaks, trivy), Dependabot, PR and issue templates
├── configs/
│   ├── conky/conky.conf        # Desktop system stats widget
│   ├── fastfetch/config.jsonc  # fastfetch module layout
│   ├── es-de/es_systems.xml    # ~/ES-DE/custom_systems/ — the installed emulator as each system's default
│   ├── es-de/es_find_rules.xml # ~/ES-DE/custom_systems/ — DuckStation resolves to the pinned AppImage
│   ├── fish/
│   │   ├── config.fish         # Fish shell config (aliases, zoxide, starship)
│   │   └── functions/ya.fish   # Yazi cd-on-exit wrapper
│   ├── kde/
│   │   ├── DarthVader.colors   # KDE color scheme (teal + red on black)
│   │   ├── kvantum/
│   │   │   └── kvantum.kvconfig # Kvantum theme config (LayanDark)
│   │   └── plasma-theme/       # Plasma desktop theme: Breeze with contrast/transparency tweaks (metadata.json + plasmarc)
│   ├── kitty/kitty.conf        # Terminal config — fish shell, Darth Vader palette
│   ├── starship/starship.toml  # Shell prompt
│   ├── wireplumber/
│   │   └── wireplumber.conf.d/
│   │       └── 50-audio.conf   # Opt-in (AUDIO_TRIM): hide onboard AMD, iGPU HDMI and webcam audio
│   ├── systemd/
│   │   └── conky.service       # ~/.config/systemd/user/ — Conky autostart service
│   └── bashrc                  # ~/.bashrc additions (zoxide, aliases, mise, starship); the setup adds the ble.sh lines
├── system/
│   ├── nvidia-wayland.conf     # /etc/environment.d/ — NVIDIA Wayland env vars (NVIDIA only, not on hybrid laptops)
│   ├── nvidia-performance.conf # /etc/modprobe.d/ — NVIDIA kernel options (NVIDIA only)
│   ├── nvidia-580xx-storage.conf # /etc/modprobe.d/ — 580xx: video memory saved to /var/tmp, not /tmp
│   ├── nvidia-vram-check.sh    # /usr/local/bin/ — read-only NVIDIA suspend storage check
│   ├── nvidia-vram-check.service # /etc/systemd/system/ — require sufficient storage before sleep
│   ├── 99-tweaks.conf          # /etc/sysctl.d/ — performance tweaks
│   ├── 99-disable-modules.conf # /etc/modprobe.d/ — blacklist unused protocols (attack surface)
│   ├── dnf.conf                # /etc/dnf/ — DNF settings
│   ├── journald-limits.conf    # /etc/systemd/journald.conf.d/ — journal capped at 1G
│   ├── user-runtime-dir-noquota.conf # user-runtime-dir@.service drop-in — drop the /dev/shm quota at login
│   ├── firefox-policies.json   # /etc/firefox/policies/ — telemetry, Studies, Pocket and sponsored content off
│   ├── zram-generator.conf     # /etc/systemd/ — ZRAM as large as RAM, up to 8GB, zstd
│   ├── tmp-mount-override.conf # /etc/systemd/system/tmp.mount.d/ — drop tmpfs usrquota
│   ├── scx_loader.toml         # /etc/scx_loader/ — scx_lavd Gaming mode on desktops
│   ├── scx_loader-laptop.toml  # /etc/scx_loader/ — scx_lavd Auto mode on laptops
│   ├── resolved-hardening.conf # /etc/systemd/resolved.conf.d/ — DNSSEC, DoT, LLMNR/mDNS off
│   ├── tuned-ppd.conf          # /etc/tuned/ppd.conf — PPD → tuned profile map
│   ├── ntsync.conf             # /etc/modules-load.d/ — load ntsync at boot
│   ├── 70-ntsync.rules         # /etc/udev/rules.d/ — ntsync user access
│   ├── 70-lamzu.rules          # /etc/udev/rules.d/ — LAMZU mouse configurator access (by USB ID)
│   ├── 99-dualsense.rules      # /etc/udev/rules.d/ — DualSense touchpad/motion noise fix
│   ├── 99-disable-wakeup.rules # /etc/udev/rules.d/ — no LAMZU USB device may wake the system
│   ├── libinput-overrides.quirks # /etc/libinput/ — no mouse debouncing for LAMZU mice
│   ├── hugepages.conf          # /etc/tmpfiles.d/ — transparent hugepages
│   ├── kwin-display-fix.sh     # /usr/lib/systemd/system-sleep/ — USB suspend/resume quirks
│   ├── kde-display-sleep.sh    # /usr/local/bin/ — preserve enabled external display state
│   ├── kde-display-sleep.service # /etc/systemd/system/ — KDE IPC before freeze and after thaw
│   ├── usb-autosuspend.service # /etc/systemd/system/ — autosuspend xHCI-blocking USB devices (by USB ID)
│   ├── gamescope-caps.actions  # /etc/dnf/libdnf5-plugins/actions.d/ — re-grant CAP_SYS_NICE after gamescope updates
│   └── plasmalogin-restart.conf # systemd drop-in — auto-restart plasmalogin on crash
├── scripts/
│   ├── lib/hw.sh               # Hardware detection shared by the setup scripts
│   ├── lib/restore.sh          # Pre-install root/home snapshots and verified boot/EFI archives
│   ├── fedora-setup.sh         # Automated post-upgrade setup (run after the initial system upgrade + reboot)
│   ├── apply-system.sh         # Deploy system/ files to their system paths
│   ├── emulation-setup.sh      # ES-DE + one emulator per system, 14 systems — opt-in, ~1.7 GB
│   ├── strata-setup.sh         # Local LLM (Strata) on the NVIDIA card — opt-in, 85 GB+
│   ├── ollama-setup.sh         # Local LLM (Ollama) + Alpaca chat GUI — opt-in, ~9 GB
│   ├── windows-vm-setup.sh     # Windows 11 VM on KVM/libvirt (Secure Boot, TPM, virtio) — opt-in
│   ├── setup-github.sh         # GitHub CLI login + git identity/defaults
│   ├── rice-start.sh           # Restart Conky
│   ├── sysinfo.sh              # System health overview in terminal
│   ├── hwstat.sh               # CPU/GPU readings for Conky and sysinfo (AMD, Intel, NVIDIA)
│   ├── deep-health.sh          # Full hardware audit — firmware, SMART, btrfs scrub, self-test
│   ├── disk-cleanup.sh         # Trim snapper's timeline and clear package and container caches
│   ├── mok-reenroll.sh         # Re-enroll the akmods MOK after a BIOS flash
│   ├── fedora-upgrade.sh       # Upgrade to the next Fedora release (checks, snapshot, download)
│   └── verify-setup.sh         # Check the live system against the setup (after setup or an upgrade)
├── tests/
│   ├── hw-detect.sh            # Detection and hwstat tests against fixture sysfs trees
│   ├── verify-setup.sh         # Read-only verification failure paths with command stubs
│   ├── fedora-setup.sh         # The setup run end to end with every system change stubbed
│   ├── ollama-setup.sh         # Ollama install, hardening drop-in and model tuning with stubs
│   ├── helper-scripts.sh       # Maintenance command failures and upgrade safeguards
│   ├── setup-configs.sh        # Numbered user config backups and repeat installs
│   ├── sleep-hooks.sh          # Display lifecycle, USB quirks and systemd unit ordering
│   ├── nvidia-vram.sh          # GPU capacity, disk storage and failure paths
│   ├── emulation-setup.sh      # Pinned AppImage downloads and ES-DE selection
│   ├── setup-restore.sh        # Recovery gates and boot/EFI archive round trips
│   ├── windows-vm.sh           # Windows VM setup: input checks before sudo, the VM definition
│   ├── fresh-install.sh        # CI: apply-system.sh and the README's copy steps on a fresh Fedora
│   └── check-packages.sh       # CI: literal package names resolve in the scoped repos
├── setup.conf.example          # Per-machine overrides; copy to setup.conf
├── README.md                   # This guide
├── CONTRIBUTING.md             # Checks to run, conventions, repository settings
├── AGENTS.md                   # Boundaries for AI coding agents
├── SECURITY.md                 # How to report a vulnerability
├── LICENSE                     # MIT
├── .editorconfig, .gitattributes, .gitignore, .mailmap   # Editor, line-ending, ignore and author settings
└── wallpaper/
    └── wallpaper.jpg           # Darth Vader — dark, teal glow, red lightsaber (5120x2880)

Quick Setup (New Machine)

# 1. Update the system to a clean kernel/driver baseline first, then reboot
sudo dnf upgrade --refresh -y
sudo reboot

# 2. After the reboot, clone and run the setup (the KDE image does not ship git)
sudo dnf install -y git
git clone https://github.com/26zl/fedora-kde-setup.git ~/fedora-setup
cd ~/fedora-setup
cp setup.conf.example setup.conf   # optional: to override the detection or set RICE=no
bash scripts/fedora-setup.sh       # prints what it will change and asks once; --yes skips that

The setup script automates most of the guide below. A few steps stay manual: the initial system upgrade + reboot (Step 1), the optional service trimming, applying the Layan Kvantum theme, and Secure Boot MOK enrollment + BIOS settings (NVIDIA only).


Step-by-Step Guide

1. System Update

sudo dnf upgrade --refresh -y
sudo dnf autoremove && sudo dnf clean all
sudo reboot

Firmware (UEFI/BIOS, SSD, peripherals) through fwupd and the LVFS:

sudo fwupdmgr refresh --force
sudo fwupdmgr get-updates
sudo fwupdmgr update

Do this before the NVIDIA driver, or with Secure Boot on run sudo bash scripts/mok-reenroll.sh after any UEFI/BIOS update: a BIOS flash can clear the enrolled MOK key, and the signed NVIDIA modules then stop loading. fedora-setup.sh only lists the available updates and leaves applying them to you.

2. DNF Configuration

sudo cp system/dnf.conf /etc/dnf/dnf.conf

Key settings: installonly_limit=2 (max 2 kernels), max_parallel_downloads=10, fastestmirror=True, and skip_if_unavailable=False, Fedora's default: a repo dnf cannot reach stops the run instead of silently going without its updates. --setopt=<repo-id>.skip_if_unavailable=True gets past an outage of one repo

3. RPM Fusion

sudo dnf install -y \
  "https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm" \
  "https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm"
sudo dnf group upgrade -y core

The two release packages come over HTTPS from RPM Fusion's own server and carry its signing keys; every package after that is GPG-verified. Terra's key is fetched the same way (--setopt=terra.gpgkey=), so trust in both repos rests on TLS to their hosts.

4. GPU Drivers

fedora-setup.sh installs what the detected GPUs need. Several can apply at once, e.g. an AMD iGPU next to an NVIDIA card.

AMD — Fedora's Mesa leaves out H.264/H.265. RPM Fusion's build adds them, and libva loads it ahead of Fedora's:

sudo dnf install -y mesa-va-drivers-freeworld

Intel — same for Intel's media driver:

sudo dnf install -y intel-media-driver

NVIDIA — the current branch covers Turing (RTX 20 / GTX 16) and newer. Maxwell, Pascal and Volta (GTX 900 / 10) need the 580xx branch, the last one that supports them; for those cards, use akmod-nvidia-580xx and xorg-x11-drv-nvidia-580xx-cuda instead. Kepler and older cards stay on nouveau.

sudo dnf install -y akmod-nvidia xorg-x11-drv-nvidia-cuda libva-nvidia-driver

Wait ~5 minutes for akmods to build the kernel module, then:

sudo akmods --force
sudo dracut --force

Apply NVIDIA configs:

sudo mkdir -p /etc/environment.d
sudo cp system/nvidia-wayland.conf /etc/environment.d/nvidia-wayland.conf
sudo cp system/nvidia-performance.conf /etc/modprobe.d/nvidia-performance.conf

Skip nvidia-wayland.conf on a laptop with hybrid graphics. It forces GLX, GBM and VA-API onto NVIDIA, but the panel is driven by the integrated GPU. There the desktop stays on the iGPU and single programs run on NVIDIA with switcherooctl launch <program>. apply-system.sh makes that call itself.

Enable power management services:

sudo systemctl enable nvidia-suspend nvidia-resume nvidia-hibernate
sudo systemctl enable nvidia-suspend-then-hibernate
sudo mkdir -p /usr/local/bin
sudo cp system/nvidia-vram-check.sh /usr/local/bin/nvidia-vram-check.sh
sudo chmod 755 /usr/local/bin/nvidia-vram-check.sh
sudo cp system/nvidia-vram-check.service /etc/systemd/system/nvidia-vram-check.service
sudo restorecon /usr/local/bin/nvidia-vram-check.sh /etc/systemd/system/nvidia-vram-check.service
sudo systemctl daemon-reload
sudo systemctl enable nvidia-vram-check.service

On suspend the driver saves the video memory. The open kernel module of the current branch (615.71.09 and later) keeps it in RAM unless NVreg_TemporaryFilePath names a directory; the 580xx branch writes it to /tmp, a tmpfs of half the RAM, which cannot hold a card with more than about 47% of the RAM. On 580xx, apply-system.sh therefore also deploys nvidia-580xx-storage.conf, which moves the file to /var/tmp on disk (copy it to /etc/modprobe.d/ by hand on such a machine). The read-only capacity check runs before sleep and the NVIDIA sleep services: it needs room for the sum of all GPU memory plus 5%, following NVIDIA's sizing guidance, and for a store in RAM or on a tmpfs it counts MemAvailable, so a suspend with a loaded model filling the RAM is refused with a message instead of failing halfway. Inspect refusals with journalctl -u nvidia-vram-check.service.

kde-display-sleep.service captures enabled, connected external displays before sleep.target and restores only that recorded set after user sessions thaw. It only acts with the amdgpu module loaded, whose DisplayPort link re-training on resume is what disconnects a monitor; on other hardware it exits at once. It never enables laptop panels or displays that were disabled before sleep, and ignores captures from another session. The USB-only hook runs inside the frozen interval. Both are deployed by apply-system.sh; the display service is enabled without starting it during installation. This ordering follows the systemd sleep integration example.

Enroll Secure Boot MOK key (only with Secure Boot on; mokutil --sb-state tells):

sudo mokutil --import /etc/pki/akmods/certs/public_key.der
sudo reboot
# Select "Enroll MOK" at the blue MOK Manager screen on reboot

Verify after reboot:

mokutil --sb-state           # Should show: SecureBoot enabled
lsmod | grep nvidia          # Should list nvidia, nvidia_drm, nvidia_modeset, nvidia_uvm
nvidia-smi                   # Should show your GPU

5. CPU Performance (tuned)

sudo dnf install -y tuned
sudo systemctl enable --now tuned
sudo cp system/tuned-ppd.conf /etc/tuned/ppd.conf
sudo tuned-adm profile latency-performance   # desktops only

The tuned-ppd.conf maps KDE's "Performance" power mode to latency-performance instead of the default throughput-performance, so the profile persists correctly at boot. Laptops get balanced instead (apply-system.sh sets it, which also undoes latency-performance left by an earlier desktop run), and tuned-ppd switches that to balanced-battery when unplugged. Performance is still one click away in the battery applet. This relies on tuned-ppd, which the KDE spin ships and fedora-setup.sh installs; without it tuned keeps whatever profile was last set.

Note: Recent AMD CPUs run amd-pstate-epp and Intel CPUs intel_pstate, both in active mode by default. In that mode only the performance and powersave governors exist, not schedutil or others.

6. SCX Scheduler (Gaming)

sudo dnf copr enable -y bieszczaders/kernel-cachyos-addons  # scx-scheds is not in the Fedora repos
sudo dnf config-manager setopt 'copr:copr.fedorainfracloud.org:bieszczaders:kernel-cachyos-addons.includepkgs=scx-scheds,scx-tools'
sudo dnf install -y scx-scheds
sudo mkdir -p /etc/scx_loader
sudo cp system/scx_loader.toml /etc/scx_loader/config.toml
sudo systemctl enable --now scx_loader.service

includepkgs limits the COPR to the two packages used here; unrestricted, a later dnf upgrade can pull its builds of other packages, and scx-scheds would pull the scx-tools-git snapshot instead of the scx-tools release. fedora-setup.sh scopes every COPR it adds the same way.

On a laptop, deploy system/scx_loader-laptop.toml instead: Auto mode, because Gaming mode keeps tasks on the fastest cores at the cost of battery.

scx_lavd runs in Gaming mode and needs scx-scheds 1.1.4 or newer: 1.1.3 starves tasks for 30–43 s until the kernel watchdog ejects it (sched-ext/scx#3791), and 1.1.4 contains the fix, commit 6d31ddd. On an older build (rpm -q scx-scheds), set default_sched = "scx_bpfland" until the COPR catches up.

Loads from kernel 7.2.7-200 on; broken on Fedora 7.1.x–7.2.6. Those builds fail with the running kernel's BTF has malformed scx kfunc prototype(s) — KF_IMPLICIT_ARGS kfuncs only get a loadable BTF prototype from a new enough pahole at kernel build time, and they used pahole 1.30 or older; 7.2.7-200 was built with 1.32. grep CONFIG_PAHOLE_VERSION /boot/config-* shows which pahole built each installed kernel. Same failure: CachyOS COPR #113. Verify with cat /sys/kernel/sched_ext/state — disabled means EEVDF is running instead. The kernel's watchdog ejects a scheduler on a runnable task stall and scx_loader restarts it (journalctl -k | grep 'runnable task stall').

7. ZRAM

sudo dnf install -y zram-generator
sudo cp system/zram-generator.conf /etc/systemd/zram-generator.conf
sudo reboot

Verify: lsblk | grep zram — should show a swap device as large as the RAM, up to 8GB (Fedora's own size rule; this file adds zstd compression).

8. sysctl Tweaks

sudo cp system/99-tweaks.conf /etc/sysctl.d/99-tweaks.conf
sudo sysctl --system
Key Value Purpose
vm.swappiness 180 Favour ZRAM over disk swap
vm.page-cluster 0 No swap readahead; zram reads one page at a time
vm.max_map_count 2147483642 Required for some games (Steam/Proton)
net.core.rmem_max, net.core.wmem_max 16777216 Better network throughput
vm.compaction_proactiveness 0 Disable proactive memory compaction — reduces jitter
vm.page_lock_unfairness 1 Lower page lock contention
fs.inotify.max_user_instances 512 File watchers in editors and Steam
kernel.kptr_restrict 1 Kernel addresses hidden from users; perf and BPF under sudo still resolve symbols
kernel.dmesg_restrict 1 Kernel log for root only
net.core.bpf_jit_harden 2 Hardened BPF JIT
kernel.unprivileged_bpf_disabled 1 No BPF for unprivileged users; stays on until reboot
kernel.yama.ptrace_scope 1 ptrace only on own child processes (see Upgrading Fedora)
kernel.kexec_load_disabled 1 No runtime kernel replacement, which also disables kexec reboots and kdump; stays on until reboot
kernel.oops_limit 100 Panic after 100 oopses instead of letting an exploit retry
vm.mmap_rnd_bits 32 Maximum ASLR entropy for 64-bit programs; 32-bit games keep the default
fs.protected_fifos, fs.protected_regular 2 No writing to other users' FIFOs and files in sticky directories
fs.protected_symlinks, fs.protected_hardlinks 1 Link attacks in shared directories blocked
fs.suid_dumpable 0 No core dumps of setuid programs
dev.tty.ldisc_autoload 0 Users cannot load TTY line disciplines
net.ipv4.tcp_syncookies 1 SYN flood protection
net.ipv4.tcp_rfc1337 1 Drop TIME-WAIT assassination packets
net.ipv4.conf.{all,default}.{accept,secure,send}_redirects, net.ipv6.conf.{all,default}.accept_redirects 0 No ICMP redirects on a workstation
net.ipv4.conf.all.accept_source_route, net.ipv6.conf.all.accept_source_route 0 No source routing
net.ipv6.conf.{all,default}.use_tempaddr 2 Outgoing IPv6 connections use privacy addresses
net.ipv4.conf.{all,default}.log_martians 1 Log packets with impossible source addresses
net.ipv4.conf.{all,default}.rp_filter 2 Loose reverse-path filter; strict breaks Mullvad's and containers' asymmetric routes

Transparent hugepages are configured via system/hugepages.conf (deployed to /etc/tmpfiles.d/):

sudo cp system/hugepages.conf /etc/tmpfiles.d/hugepages.conf
sudo systemd-tmpfiles --create /etc/tmpfiles.d/hugepages.conf
Setting Value Purpose
transparent_hugepage/enabled madvise 2MB pages only for processes that opt in via madvise() — fewer TLB misses without blanket overhead
transparent_hugepage/shmem_enabled advise Hugepages for shared memory (wine/Proton)
transparent_hugepage/khugepaged/defrag 0 Disable background defrag — reduces jitter

9. Kernel Parameters

sudo grubby --update-kernel=ALL --args="nowatchdog audit=1 audit_backlog_limit=8192 skew_tick=1 workqueue.power_efficient=false preempt=full"

apply-system.sh leaves workqueue.power_efficient=false out on laptops, where the power-efficient workqueues save battery.

Parameter Purpose
nowatchdog Disable watchdog timers — reduces interrupts
audit=1 Enable the audit framework — auditd.service has ConditionKernelCommandLine=!audit=0 and will not start without it
audit_backlog_limit=8192 The kernel default is 64, which overflows during boot once audit=1 is set (kauditd hold queue overflow in dmesg) and silently drops audit events
skew_tick=1 Skew timer ticks across cores — reduces lock contention
workqueue.power_efficient=false Disable power-efficient workqueues — prevents cross-core cache misses
preempt=full Full kernel preemption. Fedora builds PREEMPT_DYNAMIC and boots lazy by default; full trades a little throughput for lower worst-case latency. Check the active model with journalctl -k -b | grep 'Dynamic Preempt'

Takes effect on next boot. Verify with cat /proc/cmdline.

10. CPU Temperature Sensor

sudo dnf install -y lm_sensors
sensors | grep -E 'Tctl|Package id'  # Verify: Tctl on AMD, Package id 0 on Intel

k10temp (AMD) and coretemp (Intel) load on their own through the CPU's modalias; no modules-load.d entry is needed. scripts/hwstat.sh cpu-temp reads whichever one is present.

11. Firewall Hardening

The KDE Edition's default zone, FedoraWorkstation, has ports 1025-65535 open, plus the mdns, ssh and samba-client services. An install from the Everything ISO defaults to public instead; without --zone, firewall-cmd changes whichever zone is the default. Harden to only what's needed:

sudo firewall-cmd --permanent --remove-port=1025-65535/udp
sudo firewall-cmd --permanent --remove-port=1025-65535/tcp
sudo firewall-cmd --permanent --remove-service=mdns
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --permanent --remove-service=samba-client
sudo firewall-cmd --permanent --remove-service=kdeconnect
sudo firewall-cmd --permanent --add-service=dhcpv6-client
sudo firewall-cmd --reload
firewall-cmd --list-services  # Verify: dhcpv6-client

kdeconnect is removed from the zone — add it back if you pair a phone (see Disable Unnecessary Services).

12. Dual-Boot (Clock + Sleep/Hibernate)

Only relevant when Windows shares the machine. fedora-setup.sh checks the UEFI boot list for a Windows Boot Manager entry and otherwise only sets the clock to UTC.

Clock — Windows and Linux must read the hardware clock the same way, or the time is off by your UTC offset after switching OS. Use UTC on both sides:

  • Linux → UTC: fedora-setup.sh runs sudo timedatectl set-local-rtc 0. Verify with timedatectl — it must show RTC in local TZ: no. If it shows yes, the two clocks fight; re-run sudo timedatectl set-local-rtc 0. (This is the usual reason the time is still wrong even after the Windows reg edit.)
  • Windows → UTC, PowerShell as Administrator:
    reg add "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\TimeZoneInformation" /v RealTimeIsUniversal /d 1 /t REG_DWORD /f
    Then Settings → Time & language → Set time automatically on.

Sleep / hibernate — after a long sleep, Windows (Modern Standby) auto-hibernates and powers off. The next wake is a full boot, so GRUB appears (Fedora is first in the UEFI boot order) and after its timeout boots Fedora — stranding the hibernated Windows session.

  • Windows — disable hibernate (a desktop never needs it; sleep keeps RAM powered; on a laptop, weigh this against battery drain while asleep), PowerShell as Administrator:
    powercfg /h off
    This also disables Fast Startup, which must be off when sharing an NTFS disk with Linux.
  • Linux — fedora-setup.sh sets GRUB_SAVEDEFAULT=true (with GRUB_DEFAULT=saved) and regenerates grub.cfg, so GRUB remembers the last-booted OS. If GRUB does appear it re-selects Windows and chainloads the Windows Boot Manager, which resumes the hibernated session.

13. Backup — Snapper (BTRFS Snapshots)

Fedora installs on BTRFS by default. Snapper integrates natively with Fedora's subvolume layout. fedora-setup.sh skips this step when the root filesystem is something else.

sudo dnf install -y snapper btrfs-assistant
sudo snapper -c root create-config /
sudo snapper -c home create-config /home
sudo snapper -c home set-config TIMELINE_CREATE=no
sudo snapper -c root set-config TIMELINE_LIMIT_HOURLY=5 TIMELINE_LIMIT_DAILY=7 TIMELINE_LIMIT_WEEKLY=2 TIMELINE_LIMIT_MONTHLY=1 TIMELINE_LIMIT_YEARLY=0
sudo snapper -c home set-config TIMELINE_LIMIT_HOURLY=0 TIMELINE_LIMIT_DAILY=0 TIMELINE_LIMIT_WEEKLY=0 TIMELINE_LIMIT_MONTHLY=0 TIMELINE_LIMIT_YEARLY=0
sudo snapper -c root setup-quota
sudo snapper -c home setup-quota
sudo snapper -c root create --description "Initial clean setup" --cleanup-algorithm number
sudo snapper -c home create --description "Initial home snapshot" --cleanup-algorithm number
sudo systemctl enable --now snapper-timeline.timer snapper-cleanup.timer

Note: Fedora often pre-configures snapper. If create-config returns "subvolume already covered", skip it and go straight to creating a snapshot.

/home changes constantly, so its hourly timeline snapshots are what fill the disk; it keeps only manual and number-cleanup snapshots, and / carries the timeline. The number cleanup needs a btrfs qgroup to weigh how much space snapshots pin, which setup-quota creates. Snapper's defaults keep 10 hourly, 10 daily, 10 monthly and 10 yearly timeline snapshots, and TIMELINE_CREATE=no does not delete the ones already made, so leftover home snapshots can pin hundreds of GB of deleted data. / therefore keeps 5 hourly, 7 daily, 2 weekly and 1 monthly, /home keeps none, and scripts/disk-cleanup.sh applies the limits and removes the rest.

BTRFS Assistant (GUI) is available in the app menu — shows all snapshots and allows one-click restore.

To list snapshots:

sudo snapper -c root list

To restore, boot from a live USB, mount the BTRFS partition, and use btrfs subvolume to swap snapshots — or use BTRFS Assistant.

Snapshots sit on the same disk as the data, so they are not a backup. kopia does encrypted, incremental backups to another disk or a remote and is not part of this setup.

14. Disable Unnecessary Services

# System services. Keep ModemManager on a laptop with a mobile broadband (WWAN)
# modem, and pcscd if you use a smart card or a YubiKey's PIV/OpenPGP applet.
sudo systemctl disable --now ModemManager avahi-daemon.socket avahi-daemon pcscd.socket pcscd

# Unused services holding sockets. gssproxy must be masked, not disabled —
# auth-rpcgss-module.service pulls it back in through WantedBy.
sudo systemctl disable --now cups.service cups.socket cups.path   # no printer
sudo systemctl mask --now gssproxy.service                        # no NFS mounts

# KDE Connect binds 0.0.0.0:1716 plus random UDP ports. Remove the package rather
# than masking it — Plasma re-activates the daemon over D-Bus, and a stub D-Bus
# service makes plasmashell block for 25s per attempt and the panel disappear.
# Keep the two dependencies that are useful on their own.
sudo dnf mark user fuse-sshfs openssh-askpass
sudo dnf remove -y kde-connect

# Baloo file indexer (major CPU offender)
balooctl6 disable
kwriteconfig6 --file baloofilerc --group "Basic Settings" --key "Indexing-Enabled" false

# KDE user services. These are generated from XDG autostart files and have no
# [Install] section, so `disable` silently does nothing — they must be masked.
# Verify with `systemctl --user is-enabled <unit>`: "generated" means still active.
systemctl --user mask --now \
  app-org.kde.discover.notifier@autostart.service \
  app-org.kde.kalendarac@autostart.service \
  app-sealertauto@autostart.service \
  app-org.freedesktop.problems.applet@autostart.service \
  'app-imsettings\x2dstart@autostart.service' \
  app-vboxclient@autostart.service

# Akonadi (KDE PIM — only needed if using KMail/Kontact). It pulls in ~16 processes
# and its own MariaDB. Same reason: mask, not disable.
systemctl --user mask --now akonadi_control.service

# DrKonqi's crash pickup: KDE's own unit stops it after 30 minutes (RuntimeMaxSec), which
# systemd records as a failed unit at every login. Crashes while logged in still get their
# dialog through drkonqi-coredump-launcher.socket; only crashes from before login go unseen.
systemctl --user mask --now drkonqi-coredump-pickup.service

Undo any of these with systemctl --user unmask <unit>.

kalendarac is the one that matters: with Akonadi masked it starts, fails to reach it, and dies with malloc(): unaligned tcache chunk detected — several hundred stack-trace lines in journalctl -b -p err every boot. app-vboxclient comes from virtualbox-guest-additions, which belongs inside a VM, not on the host. imsettings-start fails every login with Current desktop isn't supported by IMSettings; systemd escapes the hyphen in its unit name as \x2d.

15. libinput Debouncing

libinput adds eager debouncing to all mice by default. LAMZU mice such as the Maya X 8K use Hall Effect switches — debouncing is unnecessary and can block fast clicks. The quirk matches LAMZU's USB vendor IDs, so every other mouse keeps the default debouncing; add a section with your mouse's vendor ID (lsusb) to extend it.

sudo mkdir -p /etc/libinput
sudo cp system/libinput-overrides.quirks /etc/libinput/local-overrides.quirks

16. Audio Trim (optional)

configs/wireplumber/wireplumber.conf.d/50-audio.conf hides the onboard AMD audio controller, AMD iGPU HDMI audio and every webcam microphone from PipeWire. That is right when all sound goes through another device, such as a USB headset or the GPU's HDMI/DP output, and wrong otherwise — on many laptops the same AMD controller drives the speakers. It is therefore opt-in: set AUDIO_TRIM=yes in setup.conf, or copy it by hand:

mkdir -p ~/.config/wireplumber/wireplumber.conf.d
cp configs/wireplumber/wireplumber.conf.d/50-audio.conf ~/.config/wireplumber/wireplumber.conf.d/
systemctl --user restart wireplumber

17. tmpfs Quota Fix (/tmp + /dev/shm)

systemd 256+ mounts /tmp and /dev/shm with an automatic per-user quota (~12 GB on 30 GB RAM). Heavy temp use (Chrome, large extractions) hits it and fails with "Disk quota exceeded" long before the tmpfs is full. Pointless on a single-user box.

# /tmp — drop-in removes usrquota (keeps nosuid/nodev/size)
sudo mkdir -p /etc/systemd/system/tmp.mount.d
sudo cp system/tmp-mount-override.conf /etc/systemd/system/tmp.mount.d/override.conf

# /dev/shm — mounted before any unit exists, and remount cannot drop tmpfs quota.
# The limit is (re)applied by systemd-user-runtime-dir at every login, so clear it
# right after, from the unit that sets it.
sudo mkdir -p /etc/systemd/system/user-runtime-dir@.service.d
sudo cp system/user-runtime-dir-noquota.conf \
        /etc/systemd/system/user-runtime-dir@.service.d/noquota.conf
sudo systemctl daemon-reload

/tmp takes effect on reboot; /dev/shm from the next login. An /etc/fstab entry for /dev/shm does not work — verified live, the quota survives it.


Security Tooling

Not installed by fedora-setup.sh — these live on the system rather than in the repo, documented here so the machine's security posture is reproducible.

Auditing and integrity

sudo dnf install -y lynis aide rkhunter
Tool Use
lynis Whole-system audit: sudo lynis audit system
aide File integrity: sudo aide --init for a baseline, sudo aide --check after
rkhunter Rootkit scan: sudo rkhunter --check
auditd Collects the kernel audit events (audit=1, see Kernel Parameters): sudo ausearch -m avc -ts today, sudo aureport --summary

AIDE flags every package update as a change; refresh the baseline with sudo aide --update.

Lynis suggestions that must not be applied here: rp_filter=1 (breaks Tailscale and VPN routing), kernel.modules_disabled=1 (blocks NVIDIA and USB), lower vm.swappiness (180 is set for ZRAM), disabling USB storage.

kernel-hardening-checker is not packaged for Fedora. It is stdlib-only Python, so run it from a pinned commit (v0.6.17.1):

git init -q /tmp/khc && git -C /tmp/khc fetch -q --depth 1 https://github.com/a13xp0p0v/kernel-hardening-checker afc376f2a935994793343cfeb05953583cc30191 && git -C /tmp/khc checkout -q FETCH_HEAD
sudo sysctl -a > /tmp/sysctl.txt
python3 /tmp/khc/bin/kernel-hardening-checker -c /boot/config-$(uname -r) -l /proc/cmdline -s /tmp/sysctl.txt -m show_fail

Most failures are expected. The kconfig checks describe Fedora's kernel build — fixing them means a self-built kernel, which breaks Secure Boot. The per-vulnerability mitigation checks only accept mitigations=auto,nosmt; the default auto is mitigated, which deep-health.sh confirms from sysfs. The zero-cost findings are in 99-tweaks.conf (kernel.unprivileged_bpf_disabled=1, kernel.oops_limit=100, vm.mmap_rnd_bits=32). Findings that must not be applied here: nosmt (halves the threads), ia32_emulation=0 (Steam and 32-bit games), user.max_user_namespaces=0 (Flatpak, Steam pressure-vessel, browser sandboxes), lockdown=confidentiality and kernel.kptr_restrict=2 (perf and bpftrace), kernel.yama.ptrace_scope=3 (gdb, strace), vm.mmap_rnd_compat_bits=16 (32-bit games lose contiguous address space), kernel.warn_limit (a driver's WARN splats would panic the box), and on AMD pti=on and intel_iommu=on, which only apply to Intel CPUs.

dev-sec/ansible-collection-hardening and dev-sec/linux-baseline are not used. They are server baselines: the defaults (rp_filter=1, net.ipv4.ip_forward=0, kernel.kptr_restrict=2, kernel.sysrq=0, a templated auditd.conf) break Mullvad, Tailscale, Docker and bpftrace here, and linux-baseline needs an InSpec runtime to check what lynis already covers.

Malware scanning

sudo dnf install -y clamav clamd clamav-freshclam
sudo systemctl enable --now clamav-freshclam-once.timer

The timer is not enabled by the package, so without it the signature database silently goes stale — it ships once at install time and never updates. It runs daily with Persistent=true, so a missed run is caught up at next boot.

On-access scanning (clamd@scan, clamav-clamonacc) stays off — it costs throughput on every file read. Scan on demand with clamscan -r <path>, and pass --tempdir when scanning large archives so decompression does not fill /tmp.

Sandboxing and mandatory access control

SELinux runs enforcing with the targeted policy — verify with sestatus, and use setroubleshoot to explain denials. Flatpak sandboxes applications through bubblewrap. Firejail is deliberately not used: it is SUID, has had its own privilege-escalation CVEs, and Flatpak already covers the GUI applications here.

Disk and system crypto

cryptsetup (LUKS), gnupg2, openssl.

Firewall and VPN

firewalld (see Firewall Hardening above), mullvad-vpn, tailscale, wireguard-tools, openvpn. DNS never gets a forced global resolver — see system/resolved-hardening.conf.

Browser

system/firefox-policies.json, deployed to /etc/firefox/policies/policies.json, turns off telemetry, Studies, Pocket and sponsored content and turns on tracking protection for every profile. Per-profile hardening beyond that, such as fingerprinting resistance and cache behaviour, is what a Betterfox or arkenfox user.js does; neither is deployed here.

Firmware and Secure Boot

mokutil manages enrolled keys (see NVIDIA Drivers + Secure Boot), fwupd applies firmware updates (fwupdmgr refresh && fwupdmgr update), efibootmgr lists boot entries.


Terminal Setup

Kitty Terminal

sudo dnf install -y kitty
mkdir -p ~/.config/kitty
cp configs/kitty/kitty.conf ~/.config/kitty/kitty.conf

Key bindings:

Shortcut Action
Ctrl+Shift+T New tab
Ctrl+Shift+W Close tab
Ctrl+Tab Next tab
Ctrl+Shift+Enter New window (split)

JetBrainsMono Nerd Font

mkdir -p ~/.local/share/fonts/JetBrainsMono
curl -fsSL "https://github.com/ryanoasis/nerd-fonts/releases/download/v3.5.1/JetBrainsMono.tar.xz" \
  -o /tmp/JetBrainsMono.tar.xz
echo "04d5e8f903693f9dd13e16f867e994834e681eb3c72c0d337a770dcda09010cf  /tmp/JetBrainsMono.tar.xz" | sha256sum -c
tar -xf /tmp/JetBrainsMono.tar.xz -C ~/.local/share/fonts/JetBrainsMono/
fc-cache -fv

In KDE: System Settings → Fonts → Fixed width → JetBrainsMono Nerd Font

Starship Prompt

Packaged in the Terra repo, which fedora-setup.sh adds and restricts with includepkgs to starship:

sudo dnf install -y --repofrompath "terra,https://repos.fyralabs.com/terra$(rpm -E %fedora)" \
  --setopt="terra.gpgkey=https://repos.fyralabs.com/terra$(rpm -E %fedora)/key.asc" terra-release
sudo dnf config-manager setopt 'terra.includepkgs=terra-release,terra-gpg-keys,starship'
sudo dnf install -y starship
cp configs/starship/starship.toml ~/.config/starship.toml

Fish Shell (inside Kitty)

Fish is configured as the shell for Kitty only (shell /usr/bin/fish in kitty.conf), keeping bash as the login shell for KDE/PAM compatibility.

sudo dnf install -y fish
mkdir -p ~/.config/fish/functions
cp configs/fish/config.fish ~/.config/fish/config.fish
cp configs/fish/functions/ya.fish ~/.config/fish/functions/ya.fish

Put your own additions in ~/.config/fish/local.fish, not in config.fish: running fedora-setup.sh again replaces config.fish (the old one is kept as a numbered backup), while local.fish stays. config.fish sources it last, after mise activate, so a version manager initialised there, such as rbenv init - fish | source, keeps its shims ahead of the PATH mise rebuilds.

Shell Tools

sudo dnf install -y zoxide fzf ripgrep fd-find bat eza git-delta

lazygit is not in the Fedora repos and the atim/lazygit COPR stopped at 0.47, so fedora-setup.sh installs the pinned upstream release (v0.66.3, SHA-256 checked) to /usr/local/bin.

ble.sh (bash syntax highlighting)

Adds fish-style syntax coloring and completion in bash. Load order matters — it must be sourced before other config.

Pinned to a master commit; origin must exist because .gitmodules uses a relative URL:

git init -q /tmp/ble.sh && cd /tmp/ble.sh
git remote add origin https://github.com/akinomyoga/ble.sh.git
git fetch -q --depth 1 origin 152942758f567e51fd17bdc7b3783d27b3c476aa && git checkout -q FETCH_HEAD
git submodule update -q --init --recursive --depth 1
make install PREFIX=~/.local

configs/bashrc holds the bashrc additions (aliases, zoxide, mise, starship). fedora-setup.sh handles the deploy: it prepends the ble.sh --noattach loader to line 1, then appends configs/bashrc itself followed by the ble-attach line — ble.sh requires --noattach first and ble-attach last. Both additions sit between # >>> fedora-kde-setup and # <<< fedora-kde-setup markers, and the untouched file is kept once as ~/.bashrc.orig.

Bash-it is not used: ble.sh, Starship and configs/bashrc already cover it, and Kitty runs fish, not bash.

mise (Runtime Version Manager)

sudo dnf copr enable -y jdxcode/mise
sudo dnf config-manager setopt 'copr:copr.fedorainfracloud.org:jdxcode:mise.includepkgs=mise'
sudo dnf install -y mise
eval "$(mise activate bash)"

Yazi (Terminal File Manager)

sudo dnf copr enable -y lihaohong/yazi
sudo dnf config-manager setopt 'copr:copr.fedorainfracloud.org:lihaohong:yazi.includepkgs=yazi,resvg'
sudo dnf install -y yazi

resvg renders yazi's SVG previews and comes from the same COPR.


Desktop Rice (Darth Vader Theme)

Wallpaper

fedora-setup.sh sets up this section except the Layan Kvantum theme and the panel, and also puts the wallpaper on the lock and login screens; the color scheme, application style and desktop wallpaper still need Apply in System Settings. RICE=no in setup.conf skips all of it and keeps the terminal setup and KDE settings. The login screen gets the wallpaper only while System Settings → Login Screen has no other one saved. Choosing Fedora's default there saves nothing, so to keep it, set RICE=no; apply-system.sh then takes the wallpaper back off.

Color palette extracted from wallpaper:

Color Hex Usage
Background #090909 Terminal, Conky bg
Teal (primary) #00c8a8 Accents, clock, labels
Teal (bright) #1de9b6 Directory, highlights
Red #cc2222 Errors, speeds, danger
Gray #b0bec5 Foreground text

Wallpaper

mkdir -p ~/Pictures
cp wallpaper/wallpaper.jpg ~/Pictures/wallpaper.jpg

Set in KDE: right-click desktop → Configure Desktop → Wallpaper

KDE Color Scheme

mkdir -p ~/.local/share/color-schemes
cp configs/kde/DarthVader.colors ~/.local/share/color-schemes/

Apply: System Settings → Colors → Darth Vader → Apply

Kvantum Theme (Application Style)

sudo dnf install -y kvantum
git init -q /tmp/layan-kde && git -C /tmp/layan-kde fetch -q --depth 1 https://github.com/vinceliuice/Layan-kde.git a0b6a4956022276aee33309b2e07d1d0ef3db30c && git -C /tmp/layan-kde checkout -q FETCH_HEAD

In Kvantum Manager:

  1. Install/Update Theme → Select /tmp/layan-kde/Kvantum/Layan → Install
  2. Change/Delete Theme → Select LayanDark → Use this theme

Apply: System Settings → Application Style → kvantum → Apply

mkdir -p ~/.config/Kvantum
cp configs/kde/kvantum/kvantum.kvconfig ~/.config/Kvantum/kvantum.kvconfig

Enable blur: System Settings → Desktop Effects → Blur (or via terminal):

kwriteconfig6 --file kwinrc --group Plugins --key blurEnabled true
dbus-send --session --dest=org.kde.KWin /KWin org.kde.KWin.reconfigure

KDE Panel

Right-click panel → Enter Edit Mode:

  • Position: Bottom
  • Alignment: Center
  • Width: Fit content
  • Floating: Panel and applets (gives rounded corners)
  • Opacity: Translucent

Conky (System Stats Widget)

sudo dnf install -y conky
mkdir -p ~/.config/conky ~/.config/systemd/user
cp configs/conky/conky.conf ~/.config/conky/conky.conf
cp configs/systemd/conky.service ~/.config/systemd/user/conky.service
systemctl --user daemon-reload
systemctl --user enable --now conky.service

The config builds its hardware rows when Conky starts. It shows one row per four CPU cores (up to 16), GPU lines for whatever ~/scripts/hwstat.sh can read, and a line per disk mounted under /mnt. It sits on the second screen when KWin reports more than one enabled output, otherwise on the only one, and adapts to the machine without edits.


Gaming Setup

Steam

Installed natively from RPM Fusion (sudo dnf install steam), not Flatpak. The Flatpak sandbox ships its own NVIDIA driver layer, which broke Vulkan device init (RenderDeviceMgr001) and forced shader reprocessing on a multi-GPU machine. Native uses the host driver directly. Launch options: gamemoderun mangohud %command% — host gamemode and MangoHud work without sandbox extensions. On a multi-GPU system, if a game picks the wrong GPU, pin NVIDIA with __NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only (NVIDIA's offload layer, container-safe). Avoid VK_LOADER_DRIVERS_SELECT — it breaks Steam's pressure-vessel container.

Flatpak

flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
sudo flatpak remote-delete --system fedora   # the spin's own OCI remote; unused, slows Discover refreshes
flatpak install -y flathub \
  net.davidotek.pupgui2 \
  com.heroicgameslauncher.hgl \
  net.lutris.Lutris \
  com.usebottles.bottles \
  org.prismlauncher.PrismLauncher \
  io.github.benjamimgois.goverlay \
  com.github.wwmm.easyeffects

NTSync (Wine/Proton CPU sync)

NTSync is a Linux kernel feature that replaces Wine's user-space synchronization with native kernel primitives — lower CPU overhead and better frame times.

Requires Linux ≥ 6.14 and GE-Proton ≥ 10-10 (install via ProtonUp-Qt). Standard Steam Proton does not support NTSync yet. Proton uses it automatically once /dev/ntsync is available.

sudo cp system/ntsync.conf /etc/modules-load.d/ntsync.conf
sudo cp system/70-ntsync.rules /etc/udev/rules.d/70-ntsync.rules
sudo udevadm control --reload-rules
sudo modprobe ntsync

Verify: ls /dev/ntsync — should exist after modprobe.

Gamescope

sudo dnf install -y gamescope
sudo setcap cap_sys_nice+ep "$(which gamescope)"

CAP_SYS_NICE lets Gamescope use --rt (real-time scheduling) without root. rpm drops file capabilities whenever it replaces the binary, so system/gamescope-caps.actions (a libdnf5-plugin-actions hook) re-applies it after every gamescope install or update. Steam launch option example, with your display's mode filled in: gamescope -W <width> -H <height> -r <refresh> --hdr-enabled -- %command%. From a shell, gcs2 <command> (in configs/bashrc and config.fish) runs a program in gamescope at the primary monitor's current mode, read from kscreen-doctor.

GameMode + MangoHud

sudo dnf install -y gamemode mangohud

Steam launch options: gamemoderun mangohud %command%

This is the universal baseline — use it on every game, native or Proton. The flags in the table below stack in front of it only for Proton games that need them.

ProtonUp-Qt (Proton versions)

Installed via Flatpak (net.davidotek.pupgui2). Use to install GE-Proton for better game compatibility.

KDE Direct Scanout

KDE Plasma can bypass the compositor entirely for fullscreen games, reducing latency. Requires:

  • No color profile applied to the display
  • HDR off
  • Night Light off
  • No custom KWin effects (default set only)

Compositor bypass happens automatically when conditions are met. Inspect it in the KWin debug console: qdbus-qt6 org.kde.KWin /KWin org.kde.KWin.showDebugConsole.

Steam Launch Options

Per-game launch options in Steam (right-click game → Properties → Launch Options):

Use case Launch option
Native Wayland (GE-Proton) PROTON_ENABLE_WAYLAND=1 %command%
DLSS / RTX / Reflex (NVIDIA) PROTON_ENABLE_NVAPI=1 DXVK_ENABLE_NVAPI=1 %command%
Ray tracing (VKD3D) VKD3D_CONFIG=dxr %command%
NVIDIA GPU not detected in game add PROTON_HIDE_NVIDIA_GPU=0
All combined (NVIDIA) PROTON_ENABLE_WAYLAND=1 PROTON_ENABLE_NVAPI=1 DXVK_ENABLE_NVAPI=1 PROTON_HIDE_NVIDIA_GPU=0 VKD3D_CONFIG=dxr %command%

Append gamemoderun mangohud after the env vars to keep GameMode and the overlay, e.g. PROTON_ENABLE_NVAPI=1 DXVK_ENABLE_NVAPI=1 gamemoderun mangohud %command%.

On Proton 9+/GE-Proton 10.x, NVAPI is on by default, so PROTON_ENABLE_NVAPI is mostly a no-op. To force DLSS 4 DLLs on RTX 50-series, use GE-Proton with PROTON_DLSS_UPGRADE=1 %command%.

Display & competitive

Verify the compositor actually runs at the panel's refresh rate with qdbus-qt6 org.kde.KWin /KWin org.kde.KWin.supportInformation | grep 'Refresh Rate' — MaxFPS in kwinrc is an X11 leftover and does not limit Wayland.

  • VRR: System Settings → Display & Monitor → Adaptive Sync → Automatic. On NVIDIA, driver 555.58+ gives Wayland explicit sync (no flicker). Good for desktop and single-player; leave it off for competitive CS2 (adds ~1-3 ms once FPS is well above refresh).
  • HDR: use KWin's native HDR per-display. Avoid gamescope HDR on Plasma 6.5 with NVIDIA — known washed/grey regression. Keep HDR off for CS2 (it's SDR).
  • CS2 NVIDIA Reflex (NVIDIA only): leave Disabled in Video settings — on Linux/NVIDIA it often hurts frametime consistency. Cap frames with +fps_max 400 instead.

PROTON_ENABLE_WAYLAND=1 requires GE-Proton — standard Steam Proton ignores it.


Emulation

ES-DE as the frontend and one emulator per system, each one ES-DE's default for its system. Install with:

bash scripts/emulation-setup.sh
System Emulator Source
PS1 DuckStation Official AppImage
PS2 PCSX2 Flathub
PS3 RPCS3 Official AppImage
PS4 shadPS4, SDL build Official release zip
PSP PPSSPP Flathub
SNES RetroArch with the Snes9x core Flathub
N64 RMG (Rosalie's Mupen GUI) Flathub
DS melonDS Flathub
3DS Azahar Flathub
GameCube / Wii Dolphin The Dolphin team's Flatpak repository
Wii U Cemu Flathub
Dreamcast Flycast Flathub
Xbox 360 Xenia Edge Official AppImage
Frontend ES-DE (EmulationStation Desktop Edition) Official AppImage

Every AppImage and the shadPS4 zip is pinned by version and SHA-256 and checked before anything is installed; nothing downloaded runs during setup. They go to ~/Applications, where ES-DE's own find rules look for them, and ES-DE and RPCS3 get a menu entry under Games. ES-DE comes from es-de.org's own Linux build: the Terra RPM it replaced arrived weeks after each new Fedora release, and Fedora 45 had none. The built-in updaters of ES-DE, DuckStation and RPCS3 can move them to newer builds; running the script again puts the pinned ones back. Xenia Edge has no stable channel, only a release per commit, so its pin is one commit's build. Vita3K (PS Vita) is left out: it publishes only a continuous build that is replaced in place, which no hash can pin. Disk space is in Requirements.

Default emulators

ES-DE starts most of these systems in a RetroArch core unless told otherwise, and only the emulators above are installed: a game would fail with couldn't find emulator core file <x>_libretro.so. configs/es-de/es_systems.xml (deployed to ~/ES-DE/custom_systems/) holds ES-DE 3.5.0's own entries for psx, ps2, ps3, psp, n64, nds, n3ds, gc, wii, dreamcast and xbox360 with the installed emulator moved first, so the file extensions and the alternative emulators stay ES-DE's. ps3 starts game folders (RPCS3 Directory) instead of ES-DE's per-game shortcut files, and xbox360 starts Xenia Edge instead of ES-DE's default, Xenia Canary. snes, wiiu and ps4 keep ES-DE's own defaults: the Snes9x core, Cemu, and a shortcut file per PS4 game. A custom es_find_rules.xml resolves %EMULATOR_DUCKSTATION% to ~/Applications/DuckStation-x64.AppImage alone: ES-DE's bundled rule looks for duckstation, duckstation-qt and org.duckstation.DuckStation on the PATH before any AppImage, so a distro package or an old Flatpak there would win. Existing custom XML is kept as a numbered backup.

Coming from the earlier PCSX2/RPCS3/Dolphin/RetroArch setup or the EOL DuckStation Flatpak, the script also:

  • RPCS3: moves the Flatpak's data (~/.var/app/net.rpcs3.RPCS3/config/rpcs3: firmware, installed games, saves) to ~/.config/rpcs3, where the AppImage keeps it, then removes the Flatpak. It moves only by renaming within one filesystem, only while ~/.config/rpcs3 is absent and only with the Flatpak closed; otherwise it says why and leaves the Flatpak installed. The uninstall keeps the rest of ~/.var/app/net.rpcs3.RPCS3, the shader cache among it.
  • DuckStation: copies the Flatpak's data (~/.var/app/org.duckstation.DuckStation/config/duckstation: BIOS, memory cards, save states, settings) to ~/.local/share/duckstation, where the AppImage keeps it ($XDG_CONFIG_HOME/duckstation when that variable is set), also when the Flatpak is already uninstalled. It copies only while that folder is absent and the Flatpak closed, into a temporary folder renamed into place, so an interrupted copy leaves nothing behind; the originals stay. If the folder already exists, it copies nothing and says how many of the Flatpak's files are missing there. The Flatpak stays installed: once DuckStation shows your saves, run flatpak uninstall org.duckstation.DuckStation without --delete-data.
  • Dolphin: reinstalls a Flathub Dolphin from Dolphin's own repository. The app ID is the same, so settings and saves in ~/.var/app/org.DolphinEmu.dolphin-emu stay.
  • RetroArch: removes the Mupen64Plus-Next core, which these steps used to name for N64.

BIOS / firmware / ROMs (user-provided)

Not shipped — provide your own dumps:

  • ROMs → ~/Emulation/roms/<system>/ (psx, ps2, ps3, ps4, psp, snes, n64, nds, n3ds, gc, wii, wiiu, dreamcast, xbox360). Set ES-DE's ROM directory here.
  • PS1 BIOS → ~/.local/share/duckstation/bios, or $XDG_CONFIG_HOME/duckstation/bios when that variable is set.
  • PS2 BIOS → PCSX2's bios/ folder under ~/.var/app/net.pcsx2.PCSX2/.
  • PS3 firmware → RPCS3 (in the menu) → File → Install Firmware (PS3UPDAT.PUP from Sony).
  • PS4 games → keep the game folders outside ~/Emulation/roms and put one .desktop shortcut per game in roms/ps4: [Desktop Entry], Type=Application, Name=<game> and Exec="/home/<you>/Applications/Shadps4-sdl.AppImage" "/path/to/<game>/eboot.bin". The SDL build has no game list to make shortcuts from; ES-DE's user guide describes this and the two other ways.
  • Wii U games → install them in Cemu with their updates, then Tools → Title Manager → right-click a game → Convert to compressed Wii U archive (.wua), saved to roms/wiiu.
  • SNES core → RetroArch → Online Updater → Core Downloader → Nintendo - SNES / SFC (Snes9x - Current).

ROMs on a separate disk: move ~/Emulation/roms to the disk and symlink it back, then run the script again. It grants the emulator Flatpaks the link's target as well as ~/Emulation.

DuckStation uses the unmodified official v0.1-12442 AppImage. Both the executable and the license are checked against pinned SHA-256 values before replacement. Its separate CC BY-NC-ND 4.0 license includes noncommercial and no-derivatives conditions; this repository's MIT license does not relicense DuckStation. License and source notices are installed alongside it.

Controller (DualSense)

  • system/99-dualsense.rules stops the touchpad acting as a system mouse and the motion sensors registering as a second joystick (inverted axes / dead buttons).
  • KWin's gamecontroller plugin emulates keyboard/mouse from a pad for desktop navigation, which hijacks emulators (D-pad→arrows, Cross→Enter, Circle→ESC). Disabled via kwinrc → [Plugins] gamecontrollerEnabled=false (needs re-login).
  • Each emulator has its own mapping: PCSX2/DuckStation → Settings → Controllers → Automatic Mapping; Dolphin → Wii Remote 1 → Emulated Wii Remote → Configure.
  • Camera feels inverted vs other games? That's the game's own Invert Y-Axis option, not the emulator.

Local LLM (Strata)

Strata runs Qwen3.8-Flash-Next on the NVIDIA card plus system RAM and serves an OpenAI- and Anthropic-compatible API on 127.0.0.1:8080. Opt-in — about 85 GB of model files for the Coder (more for the full model) and a one-time engine compile:

bash scripts/strata-setup.sh

Strata 0.2 is a desktop app, a Terminal and an engine, with Linux AppImages and ready-made engines; the ready-made engine is checked only against a hash fetched from the same GitHub release, so the script keeps compiling the engine from the pinned commit (--build) and adds NVIDIA's CUDA repo for cuda-toolkit-13-4, with NVIDIA's own driver packages excluded as RPM Fusion recommends: they replace RPM Fusion's driver or collide with its files, and the next driver update then fails. It clones Strata pinned to v0.2.12 into ~/Prosjekter/Strata and puts the model files on /mnt/data/Strata-data — not under /home, which snapper snapshots. STRATA_DIR, STRATA_DATA and STRATA_REV, in the environment or in setup.conf, override both paths and the commit; STRATA_REV=latest follows Strata's newest release instead, upstream code this repo has not tested, and verify-setup.sh then says when a newer one is out. Run the script again to update: it fast-forwards a checkout without tracked edits (one with commits of its own is left for review), removes the build folder of the old source, which would turn the compile into a no-op, and rebuilds; sudo is needed only while the CUDA toolkit or its repo is missing. On a card with 12 GB or less it asks for a 64K context instead of Strata's 128K default: with the 3 GiB reserve, 128K leaves the expert cache no room on an RTX 5070, while 64K still holds a coding agent's 36K prompt.

  • Model by RAM: under 40 GiB the Coder (IQ1_M, half the experts — strong at code, answers Norwegian prompts mostly in English); otherwise the full model at the size Strata recommends.
  • Text only: --vision no --images no keep 0.2's picture reader and image model off, so the Coder stays a text model with nothing new in VRAM; ./setup.sh --setup in the checkout turns them on.
  • --vram-reserve-mib 3072: the card also drives the desktop. With Strata's default 700 MiB reserve the lock screen could not allocate framebuffers on wake (kscreenlocker_greet: eglSwapBuffers failed with 0x3003, nvidia-drm: Failed to allocate NVKMS memory for GEM object) and froze. The bigger reserve costs about 20% decode speed (85 → 68 tokens/s on code).
  • --experimental-speed-projection on: despite the name, Strata's refusal-direction vector, projected out of layers 4–44 at run time, so the model answers instead of refusing. On the Coder it refuses 4 of 146 AdvBench and JailbreakBench prompts, against 143 without it; Strata measures a mean KL of 0.063 to the stock model and 0.2–0.4% less speed. A request with "experimental_speed_projection": false gets the stock model.
  • Idle unload: "idle_unload_s": 600 in strata-coder-iq1_m.json ends the engine after 10 minutes without requests, so its RAM and VRAM go back; the next request loads it again in seconds while the files are still in the page cache.
  • Web search: "mcp_servers" gives the chat page a DuckDuckGo search and page-fetch tool from uvx duckduckgo-mcp-server@0.7.0 (no account), written when uvx is installed. The model decides when to call it, so what it reads from the web is untrusted input; API clients do not get the tool unless they send "strata_mcp": true.
  • Agents: "fit_max_tokens": true shortens a request's max_tokens to the room left instead of refusing it (400). The Coder's 32K context is the real limit: Claude Code's system prompt alone is 26–31K, so it connects (ANTHROPIC_BASE_URL=http://127.0.0.1:8080, any ANTHROPIC_AUTH_TOKEN, a known ANTHROPIC_MODEL name) but has room for one-liners only, and Codex 0.163 sends ~36K before the first word and is refused. Both need a larger context chosen in Strata's setup, which takes VRAM from the 3 GiB reserve — not done here.
  • Running it: ~/Prosjekter/Strata/run-coder-iq1_m.sh opens the chat on http://127.0.0.1:8080. Closing the browser tab does not stop the server — Ctrl+C in its terminal does. Loaded, the model holds ~20 GB of RAM and ~9 GB of VRAM until 10 minutes after the last request — stop it before gaming.

Local LLM (Ollama)

Ollama serves gemma-4-12B-it-uncensored-heretic (an uncensored community build of Gemma, from Hugging Face) on 127.0.0.1:11434, with Alpaca as the chat GUI. Opt-in — about 9 GB:

sudo bash scripts/ollama-setup.sh
  • Upstream build: Fedora's ollama links ROCm and pulls ~4 GiB of AMD libraries that are useless next to an NVIDIA card, so the script installs the CUDA build from Ollama's GitHub release, pinned by version and SHA-256. Re-run it after a version bump.
  • CUDA, not Vulkan: nvidia_uvm is often loaded only when the first desktop program asks for CUDA, after the service has started, and the hardened unit cannot load it itself. A second drop-in loads it and creates /dev/nvidia-uvm before the service starts; without it Ollama quietly runs on Vulkan. ollama-setup.sh test and verify-setup.sh report the backend, and an install without it gets it by running the script again.
  • Hardened unit: a drop-in applies the nixpkgs module's sandbox directives (systemd-analyze security 9.2 → 2.2 before PrivateTmp, PrivateUsers, RestrictAddressFamilies and UMask joined them) — it resets the $PATH the installer bakes into the unit and allows only the accelerator devices. An ollama user that upstream's install.sh created keeps its home, /usr/share/ollama, which the drop-in makes writable.
  • VRAM cap: the card also drives the desktop, and a full load leaves under 1 GB free, which freezes the lock screen on wake (the same failure as Strata's, above). OLLAMA_GPU_OVERHEAD sets a 3 GiB floor, but only a baked num_gpu holds the headroom, so the script sweeps GPU layer counts and bakes the largest that still leaves about 3 GB free at num_ctx 8192. Every Ollama version fits the layers differently, which is why a re-run re-tunes.
  • Running it: ollama run gemma4-12b-heretic, or pick the model in Alpaca. In Alpaca, add an Ollama instance at http://127.0.0.1:11434 and leave Override Parameters off — on, it sends its own num_ctx and overrides the baked cap that holds the VRAM margin.

Windows VM

A Windows 11 virtual machine on KVM/libvirt, for Windows-only programs without a reboot. Games whose anti-cheat runs in the kernel refuse virtual machines too, so those stay on the Windows install. Opt-in:

bash scripts/windows-vm-setup.sh                   # libvirt, VM storage and virtio drivers; no VM yet
bash scripts/windows-vm-setup.sh <Win11 ISO>       # the same, then creates the VM and opens its console
bash scripts/windows-vm-setup.sh --find-iso        # the same with the newest finished Windows 11 ISO in ~/Downloads
  • The VM: UEFI with Secure Boot and Microsoft's keys enrolled, an emulated TPM 2.0 (swtpm), the host's CPU model, a virtio disk and network card, a SPICE display with 64 MiB of QXL video memory (Windows can then fill a 4K or ultrawide window; the default 16 MiB leaves black bars), a channel for the QEMU guest agent that virtio-win-guest-tools.exe installs, and the Hyper-V enlightenments virt-install --osinfo win11 adds. It gets 8 GiB of RAM on a machine with 24 GB or more (4 GiB below), half the CPU threads up to 8, and a 128 GiB sparse qcow2 disk; VM_NAME, VM_RAM_MIB, VM_CPUS and VM_DISK_GIB override them. With the guest tools installed, virt-manager's View → Scale Display → Auto resize VM with window makes Windows follow the window size.
  • Password prompts: virt-manager asks for your password to manage the VM. VM_LIBVIRT_GROUP=yes adds you to the libvirt group instead, which polkit lets manage qemu:///system without a password; that is root-equivalent, so it stays opt-in.
  • Storage: on btrfs the script turns an empty /var/lib/libvirt/images into its own subvolume without copy-on-write, so snapper's root snapshots never hold on to a changing VM disk (see Backup — Snapper); fedora-setup.sh's restore point leaves it out for the same reason. The Windows ISO is copied there, because qemu cannot read files under /home and libvirt cannot label files on NTFS.
  • Drivers: the Windows installer has no virtio drivers, so the script downloads the virtio-win ISO, pinned by version and SHA-256, and attaches it as a second CD.
  • Windows Setup stays manual: press a key at "Press any key to boot from CD or DVD"; at "Where do you want to install Windows?" choose Load driver and browse the virtio-win CD for viostor\w11\amd64 (disk), then choose Load driver again for NetKVM\w11\amd64 (network): without the network driver, Windows' first-run setup cannot get online. Missed it: at the network screen press Shift+F10 and run pnputil /add-driver E:\NetKVM\w11\amd64\netkvm.inf /install, where E: is the virtio-win CD (dir E:\ lists NetKVM). After the first login, run virtio-win-guest-tools.exe from the same CD for the display, clipboard and memory balloon drivers.
  • The ISO is "Download Windows 11 Disk Image (ISO)" at microsoft.com/software-download/windows11.

Scripts

--help on the setup and maintenance scripts prints their header and exits without changing anything, and output is plain text when piped or with NO_COLOR set. fedora-setup.sh, apply-system.sh and windows-vm-setup.sh print what they will change and ask once, fedora-upgrade.sh asks before passing --allowerasing and disk-cleanup.sh before trimming snapshots; --yes answers those with yes, and without a terminal the first three refuse to run while the last two take the no. ollama-setup.sh test verifies an install, and hwstat.sh takes the reading to print. Any other argument prints the header and exits 2.

scripts/fedora-setup.sh

Automated setup for a fresh Fedora 45 KDE install. Run after the initial system upgrade + reboot. It prints the detected hardware first, creates a verified recovery point before its first package or configuration change, installs the matching GPU drivers, deploys the system/ files via apply-system.sh, and adds the Darth Vader look unless setup.conf sets RICE=no. Close applications and other package managers first. Secure Boot MOK enrollment and BIOS settings stay manual (see the guide).

scripts/apply-system.sh

Takes a snapper snapshot of / (see Undoing Changes), then deploys every file in system/ to its live path (see the repository structure above) and reloads what can be reloaded without a reboot, including a scx_loader restart when its config changed. Hardware-specific files follow the detection: NVIDIA configs only with an NVIDIA driver, the desktop or laptop variants of tuned, scx and the kernel parameters. A config it deployed earlier that no longer fits the hardware, such as the NVIDIA files after a GPU swap, is removed as long as it is unedited. fedora-setup.sh calls it during setup; run it standalone to re-apply system configs after editing them.

scripts/lib/hw.sh and setup.conf

hw.sh holds the detection: GPUs by PCI vendor and device ID (which also picks the NVIDIA driver branch), desktop or laptop from the SMBIOS chassis type or a system battery, Windows from the UEFI boot list. setup.conf (copied from setup.conf.example) overrides it per machine: FORM_FACTOR, NVIDIA_DRIVER and the opt-in AUDIO_TRIM; RICE=no leaves out the desktop look. tests/hw-detect.sh runs the detection against fixture trees for each kind of machine.

scripts/setup-github.sh

GitHub CLI login and a git identity derived from it: installs gh, logs in through the browser, wires git to gh's credential helper, then sets user.name and user.email from the GitHub account (the noreply address when the account email is hidden), init.defaultBranch main, pull.rebase false and push.autoSetupRemote. Run without sudo; gh keeps its credentials per user.

scripts/emulation-setup.sh

Retro emulation setup — installs ES-DE and one emulator per system (pinned official AppImages, Flathub, and Dolphin's own Flatpak repository), moves an RPCS3 Flatpak's data and copies a DuckStation Flatpak's data to their AppImages, grants the Flatpaks ROM access, and makes each emulator ES-DE's default for its system. BIOS/firmware/ROMs are user-provided (see the printed manual steps). See Emulation.

scripts/strata-setup.sh

Local LLM setup — CUDA toolkit from NVIDIA's repo, Strata pinned to a release or following the newest, the model picked by RAM, a 3 GiB VRAM reserve for the desktop, Strata's refusal vector and, for the Coder, idle unload and DuckDuckGo web search. See Local LLM (Strata).

scripts/ollama-setup.sh

Local LLM via Ollama — the upstream CUDA build with a hardened unit, the uncensored Gemma model tuned to leave the desktop about 3 GB of VRAM, and Alpaca as the GUI. Opt-in; re-run to update Ollama and re-tune. See Local LLM (Ollama).

scripts/windows-vm-setup.sh

Windows 11 VM on KVM/libvirt — packages, libvirt sockets, VM storage outside snapper's snapshots, the pinned virtio-win drivers, and with an ISO the VM itself. --find-iso takes the newest Windows 11 ISO (Win11*.iso or Windows11*.iso) in the download folder or in VM_ISO_DIR from setup.conf, and refuses one the browser is still downloading. Opt-in. See Windows VM.

scripts/sysinfo.sh — alias: sysinfo

Quick system health check in terminal. Shows CPU/GPU temps, load, RAM, disk (root plus anything mounted under /mnt), network, top processes, and warnings if anything exceeds 85%.

scripts/hwstat.sh

One reading per call — cpu-temp, gpu-name, gpu-temp, gpu-load, gpu-vram — for Conky and sysinfo.sh. CPU temperature comes from k10temp (AMD) or coretemp (Intel). The GPU comes from nvidia-smi when the NVIDIA driver runs, otherwise from sysfs (AMD reports everything, Intel only a name). Fields it cannot read stay empty, and Conky leaves those lines out.

scripts/deep-health.sh

Hardware audit: BIOS version and POST time, Secure Boot and MOK state, machine check counters, per-DIMM memory speed, temperatures, every physical NVMe/SATA disk's SMART data and supported short self-test, btrfs error counters and scrub, NTFS state, GPU, failed units. It preserves running self-tests, skips unsupported tests, and continues to later disks after a device failure. USB bridges and virtual disks are outside SMART discovery. Install its tools first (sudo dnf install smartmontools ntfsprogs dmidecode lm_sensors); missing smartctl is reported as a failure. sysinfo.sh is the glance; this is the check after a BIOS flash or when boot feels wrong. A single-disk audit takes around six minutes; each short test waits at most 450 seconds and btrfs scrub time depends on data size. Failed or unreadable health checks exit nonzero.

Firmware time far above normal (25 s or more on a desktop board) can mean the embedded controller is wedged. A reboot and Load Optimized Defaults do not clear it; only a full standby power drain does — PSU switch off, hold the case power button 30 seconds.

scripts/disk-cleanup.sh

Space back from what grows unnoticed: snapper's snapshot timeline (listed first, trimmed only after a y/N), unused flatpak runtimes, PackageKit's and dnf's package caches and, after a y/N of its own, docker's unused images and build cache (volumes stay) and the calling user's unused podman images, locally built ones included. Restore points and container images go only after an interactive yes, and --dry-run lists everything it would remove without changing anything. Installed to ~/scripts/ by the setup; run with sudo. It waits for btrfs to release the deleted snapshots, which takes minutes after a big trim.

scripts/mok-reenroll.sh

Re-enrolls the akmods signing key. A BIOS flash clears UEFI NVRAM and takes the MOK list with it, so locally signed modules such as NVIDIA's fail signature verification. With NVIDIA, nouveau claims the card and Plasma comes up to a black screen. The machine still boots to a TTY — run this from there, reboot, then pick Enroll MOK on the blue screen. Exits early if the key is still enrolled or Secure Boot is off.

scripts/fedora-upgrade.sh

Upgrades to the next Fedora release; see Upgrading Fedora. It downloads and leaves the reboot to sudo dnf system-upgrade reboot.

scripts/verify-setup.sh

Checks the live system against what the setup configures: failed units, kernel arguments, auditd, zram, the sched_ext scheduler, tuned, the NVIDIA driver and suspend backing capacity, sleep services, gamescope's capability, NTSync, the firewall, SELinux, every deployed system/ file and the RPM language list, unmounted fstab entries, the filesystem drivers and NVIDIA module of a newly installed kernel before it boots, the RPM Fusion release packages, pending .rpmnew files and, when set up, the ES-DE AppImage, the CUDA repo exclude and the GPU backend a running Ollama found. Run it after fedora-setup.sh and on the first boot of a new Fedora release. It changes nothing and exits 1 when a check fails.

scripts/rice-start.sh — alias: rice

Restarts Conky.

Install scripts:

mkdir -p ~/scripts
cp scripts/rice-start.sh scripts/sysinfo.sh scripts/hwstat.sh scripts/deep-health.sh scripts/mok-reenroll.sh scripts/disk-cleanup.sh ~/scripts/
chmod +x ~/scripts/{rice-start,sysinfo,hwstat,deep-health,mok-reenroll,disk-cleanup}.sh

Conky and sysinfo.sh expect hwstat.sh next to them in ~/scripts/.

Aliases (rice, sysinfo) are already in configs/fish/config.fish — deployed by fedora-setup.sh.


Verification Checklist

bash scripts/verify-setup.sh runs these checks and more for the detected hardware, compares every deployed system/ file with the repo, and exits non-zero when one fails. By hand, after a full setup:

# Kernel + Secure Boot
uname -r
mokutil --sb-state

# GPU — NVIDIA
nvidia-smi
lsmod | grep nvidia
# GPU — any vendor: the VA-API driver in use
vainfo 2>/dev/null | grep -i 'driver version'

# CPU (desktop; a laptop shows powersave and balanced / balanced-battery)
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor  # performance
tuned-adm active                                             # latency-performance

# SCX scheduler (scx_loader reports active even when nothing is attached)
cat /sys/kernel/sched_ext/state      # enabled
cat /sys/kernel/sched_ext/root/ops   # lavd_...

# ZRAM
lsblk | grep zram
cat /proc/sys/vm/swappiness  # 180

# Firewall
firewall-cmd --list-services  # dhcpv6-client

# Temperature
~/scripts/hwstat.sh cpu-temp

# Rice
pgrep conky && echo "Conky running"

Undoing Changes

Run in a terminal, the scripts that change the system leave their ✓ and ! lines in the journal, so journalctl -t fedora-kde-setup shows what ran and when.

  • Pre-install recovery: before package or configuration changes, fedora-setup.sh creates private read-only root and separate home snapshots plus verified /boot and EFI archives under /.fedora-kde-setup-backups/run-*. A READY marker identifies a completed backup; RECOVERY.txt, layout.txt and mounts.txt record recovery instructions, original layout and RTC policy. Recover from a Fedora live USB after saving newer work elsewhere. A later run reuses a completed restore point instead of adding one; Snapper does not manage them, and scripts/disk-cleanup.sh lists them and removes them on request. They protect the supported root/home/boot layout; external disks, firmware and EFI NVRAM are outside their scope. Filesystem snapshots on the same disk are not protection against disk loss or an atomic application transaction.
  • Later system deployments: apply-system.sh separately takes a Snapper root snapshot and prints its number. sudo snapper -c root status <n>..0 lists what changed since, and sudo snapper -c root undochange <n>..0 <path> reverts a file. Its root snapshot excludes /boot; use the original pre-install archives when reverting that install, or remove later kernel arguments with sudo grubby --update-kernel=ALL --remove-args="<args>".
  • System files: every target listed under Repository Structure is a file the setup adds, so deleting it undoes it once its units are off: run sudo systemctl disable --now nvidia-vram-check.service kde-display-sleep.service usb-autosuspend.service first, because a deleted unit that sleep.target still requires stops every suspend, and sudo systemctl daemon-reload after. The exceptions are /etc/dnf/dnf.conf and /etc/tuned/ppd.conf, which replace package defaults. Delete those and run sudo dnf reinstall <package> (rpm -qf <file> names it); a reinstall alone keeps the edited file because it is %config(noreplace). The login screen wallpaper is one key in /etc/plasmalogin.conf; apply-system.sh with RICE=no removes it. kernel.kexec_load_disabled and kernel.unprivileged_bpf_disabled stay set until the next boot after /etc/sysctl.d/99-tweaks.conf is gone.
  • Services, firewall, KDE settings: the commands in the guide above are the record; systemctl unmask, firewall-cmd --permanent --add-service=<name> and kwriteconfig6 with the old value reverse them.
  • Changes with no file to delete: fedora-setup.sh swaps glibc-all-langpacks for your languages' langpacks and limits RPM translations to them in /etc/rpm/macros.image-language-conf, which apply-system.sh rewrites on every run (reinstall glibc-all-langpacks, delete the file and stop running apply-system.sh to undo it). It disables ABRT (sudo systemctl enable --now abrtd), removes Fedora's Flatpak remote when nothing uses it, and writes these KDE keys with kwriteconfig6: KWin LatencyPolicy, the gamepad, zoom, shake-cursor and blur plugins, the Meta-only launcher shortcut, launch feedback, the splash screen, loginMode, AnimationDurationFactor, Baloo indexing and the lock-screen wallpaper.
  • User configs: fedora-setup.sh keeps replaced config files, including files within theme directories, the Conky unit, optional audio rules and helper scripts, as <file>.~N~ next to each file. It preserves unrelated files inside copied theme directories. KDE settings written through kwriteconfig6 need their own reversal.
  • GRUB default: /etc/default/grub.orig is the file before the setup edited it; copy it back and run sudo grub2-mkconfig -o /boot/grub2/grub.cfg.
  • Terminal extras: fedora-setup.sh installs lazygit to /usr/local/bin, ble.sh to ~/.local/share/blesh, the JetBrains Mono Nerd Font to ~/.local/share/fonts/JetBrainsMono, and the helper scripts to ~/scripts; remove those, then delete the two blocks between the # >>> fedora-kde-setup and # <<< fedora-kde-setup markers from ~/.bashrc, or restore ~/.bashrc.orig.
  • Ollama (scripts/ollama-setup.sh): sudo systemctl disable --now ollama, then remove /etc/systemd/system/ollama.service and its .d drop-ins, /usr/local/bin/ollama, /usr/local/lib/ollama, /var/lib/ollama, the ollama user and group, and the GUI with flatpak uninstall com.jeffser.Alpaca.
  • Strata (scripts/strata-setup.sh): remove the checkout (~/Prosjekter/Strata), the model files (/mnt/data/Strata-data or your STRATA_DATA), the CUDA repo /etc/yum.repos.d/cuda-fedora44.repo, and sudo dnf remove cuda-toolkit-13-4.
  • Emulation (scripts/emulation-setup.sh): flatpak uninstall the Flatpak emulators and flatpak remote-delete dolphin; remove ES-DE_x64.AppImage, DuckStation-x64.AppImage plus its license/source sidecars, rpcs3_linux64.AppImage, Shadps4-sdl.AppImage and xenia_edge_linux.AppImage from ~/Applications, and es-de.desktop and rpcs3.desktop from ~/.local/share/applications (sudo dnf remove emulationstation-de for an ES-DE from Terra). Restore the numbered backups of ~/ES-DE/custom_systems/es_systems.xml and es_find_rules.xml if present. ROMs, BIOS files and saves are left alone: RPCS3's data stays in ~/.config/rpcs3 and DuckStation's in ~/.local/share/duckstation.
  • Windows VM (scripts/windows-vm-setup.sh): sudo virsh undefine win11 --nvram --tpm --storage vda removes the VM with its disk, NVRAM and TPM state; delete the ISOs from /var/lib/libvirt/images yourself. With VM_LIBVIRT_GROUP=yes, leave the group with sudo gpasswd -d $USER libvirt. The libvirt packages and the /var/lib/libvirt/images subvolume stay, since other VMs may use them.

Upgrading Fedora

bash scripts/fedora-upgrade.sh     # to the next release, e.g. 45 -> 46; downloads, does not reboot
sudo dnf system-upgrade reboot     # runs the upgrade offline and boots into the new release
bash scripts/verify-setup.sh       # on the first boot of the new release

fedora-upgrade.sh stops until the new release is out (its directory on dl.fedoraproject.org goes live on release day), refuses to start with less than 10 GB free on / or 200 MB on /boot, or on an NVIDIA machine whose driver is not loaded or whose akmods key is not enrolled under Secure Boot, then brings the current release up to date and asks for a reboot if that installed a newer kernel. Then it takes a snapper snapshot of /, and stops if snapper has a root config but cannot take it, and downloads the upgrade.

The download runs with skip_if_unavailable=True, so a third-party repo that has no build for the new release does not stop it; dnf.conf keeps Fedora's False for everything else. When dnf cannot resolve the upgrade, the script reads dnf's own report before it asks once. It loads the filelists metadata only when dnf asks for it. A package with no build for the new release yet, typically one linked against a library the release replaced, is removed by --allowerasing. A package whose new build cannot be installed because that build's own requirements are unmet is --excluded, so --allowerasing drops the installed copy instead of failing on the new one. When that package is a repository's *-release package, the repository's .repo files go with it: the script says so, prints the commands that reinstall everything it removed, and verify-setup.sh fails until both RPM Fusion release packages match the running release. When every repository is ready on release day, none of this runs. In the first days after a release, RPM Fusion's updates can run ahead of or behind Fedora's; dnf upgrade --exclude=<name> bridges those days. akmods rebuilds the NVIDIA module for the new kernel, at the latest on the first boot before the login screen. Fedora 45's mandatory RPM signatures do not stop it, because akmods installs its local build through dnf --nogpgcheck.

/etc/dnf/dnf.conf and /etc/tuned/ppd.conf are the package-owned files this setup replaces. When verify-setup.sh reports .rpmnew versions of them or other drift, review the package defaults for required upstream changes before re-running scripts/apply-system.sh. Reapplying overwrites those paths with this repository's versions.

What Fedora 45 changes for this setup (from its ChangeSet):

  • kmscon replaces the kernel text console. It takes the keyboard layout from localectl and falls back to fbcon when it cannot start, so mok-reenroll.sh still runs from a TTY. setfont and loadkeys no longer apply there.
  • ptrace: debuggers (gdb, strace, ltrace, elfutils, systemtap) now pull in yama-ptrace-enable, which sets kernel.yama.ptrace_scope=0. 99-tweaks.conf keeps it at 1.
  • RPM signatures are mandatory: rpm -i refuses an unsigned package or one whose key is not imported. Install local RPMs with dnf install ./file.rpm, which is also how akmods installs its NVIDIA build.
  • Repo files move to /usr/share/dnf5/repos.d. The includepkgs and exclude limits the scripts set live in /etc/dnf/repos.override.d and keep applying. An edited packaged repo file comes back as .rpmsave.
  • Conky 1.24 takes the background's transparency from own_window_colour and ignores the old ARGB settings, so a Conky config from before the upgrade draws an opaque black box. Copy configs/conky/conky.conf to ~/.config/conky/ again and restart the conky user service.
  • Lua 5.5: Conky is rebuilt against it, and configs/conky/conky.conf parses and runs unchanged.
  • Kernel crypto userspace API (AF_ALG) gets restricted. bluetoothd uses it, so pair a Bluetooth device once after the upgrade; verify-setup.sh checks that the service still runs.
  • GCC 16: CUDA 13.4's nvcc accepts it, so strata-setup.sh still builds Strata's engine.
  • Python 3.15: tools that uv or pipx installed for 3.14 stop starting until they are reinstalled (uv tool install --reinstall <tool>, pipx reinstall-all); the scripts here use only the standard library.
  • Kernel 7.3 arrives as an update. Fedora still builds ntfs3, and since 7.3 it reads a file name with : on an NTFS disk as an alternate data stream (the new ads option, on by default). Do not add ads=0 to fstab while a 7.2 kernel is the fallback: 7.2 rejects the option and nofail hides the failed mount. verify-setup.sh checks the new kernel's filesystem drivers and NVIDIA module before you boot it.

Sources & Credits

Core Tools

Tool Source
Fedora KDE fedoraproject.org/kde
RPM Fusion rpmfusion.org
NVIDIA drivers rpmfusion.org/Howto/NVIDIA
SCX Schedulers github.com/sched-ext/scx
Flatpak / Flathub flathub.org

Terminal

Tool Source
Kitty sw.kovidgoyal.net/kitty
Fish fishshell.com
ble.sh github.com/akinomyoga/ble.sh
Starship starship.rs
Zoxide github.com/ajeetdsouza/zoxide
Lazygit github.com/jesseduffield/lazygit
Yazi github.com/sxyazi/yazi
mise mise.jdx.dev
fzf github.com/junegunn/fzf
ripgrep github.com/BurntSushi/ripgrep
bat github.com/sharkdp/bat
eza github.com/eza-community/eza
JetBrainsMono NF github.com/ryanoasis/nerd-fonts

Rice / Desktop

Tool Source
Conky github.com/brndnmtthws/conky
Kvantum github.com/tsujan/Kvantum
Layan KDE github.com/vinceliuice/Layan-kde
Panel Colorizer github.com/luisbocanegra/plasma-panel-colorizer
Wallpaper Made for this repo, no third-party source; covered by the MIT license like the rest of it

Gaming

Tool Source
Steam store.steampowered.com
ProtonUp-Qt github.com/DavidoTek/ProtonUp-Qt
Heroic Games Launcher heroicgameslauncher.com
Lutris lutris.net
Bottles usebottles.com
Prism Launcher prismlauncher.org
GOverlay github.com/benjamimgois/goverlay
EasyEffects github.com/wwmm/easyeffects
MangoHud github.com/flightlessmango/MangoHud
GameMode github.com/FeralInteractive/gamemode
Gamescope github.com/ValveSoftware/gamescope

Gaming Guides

Guide Source
Linux Gaming Optimization github.com/theyareonit/linux-gaming-optimization
Linux Gaming Guide (AdelKS) github.com/AdelKS/LinuxGamingGuide
Linux Gaming Wiki linux-gaming.kwindu.eu

Security

Tool Source
kernel-hardening-checker github.com/a13xp0p0v/kernel-hardening-checker

References

About

Hardware-aware post-install for Fedora 45 KDE Plasma 6 on Wayland: NVIDIA, AMD or Intel, desktop or laptop, dual-boot. Low-latency gaming, KDE and terminal rice, security and privacy hardening, suspend and audio fixes, snapper snapshots, emulation, opt-in local LLM and Windows 11 VM, release upgrades and a live verification script. CI-tested.

Topics

Resources

Contributing

Security policy

Stars

14 stars

Watchers

0 watching

Forks

Used by

Contributors

Languages