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.
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. |
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.
├── .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)
# 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 thatThe 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).
sudo dnf upgrade --refresh -y
sudo dnf autoremove && sudo dnf clean all
sudo rebootFirmware (UEFI/BIOS, SSD, peripherals) through fwupd and the LVFS:
sudo fwupdmgr refresh --force
sudo fwupdmgr get-updates
sudo fwupdmgr updateDo 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.
sudo cp system/dnf.conf /etc/dnf/dnf.confKey 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
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 coreThe 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.
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-freeworldIntel — same for Intel's media driver:
sudo dnf install -y intel-media-driverNVIDIA — 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-driverWait ~5 minutes for akmods to build the kernel module, then:
sudo akmods --force
sudo dracut --forceApply 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.confSkip 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.serviceOn 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 rebootVerify 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 GPUsudo 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 onlyThe 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-eppand Intel CPUsintel_pstate, both in active mode by default. In that mode only theperformanceandpowersavegovernors exist, notschedutilor others.
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.serviceincludepkgs 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_ARGSkfuncs 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 withcat /sys/kernel/sched_ext/state—disabledmeans EEVDF is running instead. The kernel's watchdog ejects a scheduler on arunnable task stallandscx_loaderrestarts it (journalctl -k | grep 'runnable task stall').
sudo dnf install -y zram-generator
sudo cp system/zram-generator.conf /etc/systemd/zram-generator.conf
sudo rebootVerify: 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).
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 |
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.
sudo dnf install -y lm_sensors
sensors | grep -E 'Tctl|Package id' # Verify: Tctl on AMD, Package id 0 on Intelk10temp (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.
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-clientkdeconnect is removed from the zone — add it back if you pair a phone (see Disable Unnecessary Services).
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.shrunssudo timedatectl set-local-rtc 0. Verify withtimedatectl— it must showRTC in local TZ: no. If it showsyes, the two clocks fight; re-runsudo 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:
Then Settings → Time & language → Set time automatically on.
reg add "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\TimeZoneInformation" /v RealTimeIsUniversal /d 1 /t REG_DWORD /f
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:
This also disables Fast Startup, which must be off when sharing an NTFS disk with Linux.
powercfg /h off - Linux —
fedora-setup.shsetsGRUB_SAVEDEFAULT=true(withGRUB_DEFAULT=saved) and regeneratesgrub.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.
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.timerNote: Fedora often pre-configures snapper. If
create-configreturns "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 listTo 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.
# 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.serviceUndo 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.
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.quirksconfigs/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 wireplumbersystemd 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.
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.
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_failMost 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.
sudo dnf install -y clamav clamd clamav-freshclam
sudo systemctl enable --now clamav-freshclam-once.timerThe 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.
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.
cryptsetup (LUKS), gnupg2, openssl.
firewalld (see Firewall Hardening above), mullvad-vpn, tailscale, wireguard-tools,
openvpn. DNS never gets a forced global resolver — see system/resolved-hardening.conf.
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.
mokutil manages enrolled keys (see NVIDIA Drivers + Secure Boot), fwupd applies
firmware updates (fwupdmgr refresh && fwupdmgr update), efibootmgr lists boot entries.
sudo dnf install -y kitty
mkdir -p ~/.config/kitty
cp configs/kitty/kitty.conf ~/.config/kitty/kitty.confKey bindings:
| Shortcut | Action |
|---|---|
Ctrl+Shift+T |
New tab |
Ctrl+Shift+W |
Close tab |
Ctrl+Tab |
Next tab |
Ctrl+Shift+Enter |
New window (split) |
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 -fvIn KDE: System Settings → Fonts → Fixed width → JetBrainsMono Nerd Font
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.tomlFish 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.fishPut 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.
sudo dnf install -y zoxide fzf ripgrep fd-find bat eza git-deltalazygit 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.
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=~/.localconfigs/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.
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)"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 yaziresvg renders yazi's SVG previews and comes from the same COPR.
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 |
mkdir -p ~/Pictures
cp wallpaper/wallpaper.jpg ~/Pictures/wallpaper.jpgSet in KDE: right-click desktop → Configure Desktop → Wallpaper
mkdir -p ~/.local/share/color-schemes
cp configs/kde/DarthVader.colors ~/.local/share/color-schemes/Apply: System Settings → Colors → Darth Vader → Apply
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_HEADIn Kvantum Manager:
- Install/Update Theme → Select
/tmp/layan-kde/Kvantum/Layan→ Install - 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.kvconfigEnable 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.reconfigureRight-click panel → Enter Edit Mode:
- Position: Bottom
- Alignment: Center
- Width: Fit content
- Floating: Panel and applets (gives rounded corners)
- Opacity: Translucent
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.serviceThe 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.
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 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.easyeffectsNTSync 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 ntsyncVerify: ls /dev/ntsync — should exist after modprobe.
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.
sudo dnf install -y gamemode mangohudSteam 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.
Installed via Flatpak (net.davidotek.pupgui2). Use to install GE-Proton for better game compatibility.
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.
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%.
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 400instead.
PROTON_ENABLE_WAYLAND=1 requires GE-Proton — standard Steam Proton ignores it.
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.
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/rpcs3is 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/duckstationwhen 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, runflatpak uninstall org.duckstation.DuckStationwithout--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-emustay. - RetroArch: removes the Mupen64Plus-Next core, which these steps used to name for N64.
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/bioswhen 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.PUPfrom Sony). - PS4 games → keep the game folders outside
~/Emulation/romsand put one.desktopshortcut per game inroms/ps4:[Desktop Entry],Type=Application,Name=<game>andExec="/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/romsto 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.
system/99-dualsense.rulesstops the touchpad acting as a system mouse and the motion sensors registering as a second joystick (inverted axes / dead buttons).- KWin's
gamecontrollerplugin emulates keyboard/mouse from a pad for desktop navigation, which hijacks emulators (D-pad→arrows, Cross→Enter, Circle→ESC). Disabled viakwinrc→[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.
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.shStrata 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 nokeep 0.2's picture reader and image model off, so the Coder stays a text model with nothing new in VRAM;./setup.sh --setupin 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": falsegets the stock model.- Idle unload:
"idle_unload_s": 600instrata-coder-iq1_m.jsonends 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 fromuvx duckduckgo-mcp-server@0.7.0(no account), written whenuvxis 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": trueshortens a request'smax_tokensto 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, anyANTHROPIC_AUTH_TOKEN, a knownANTHROPIC_MODELname) 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.shopens the chat onhttp://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.
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
ollamalinks 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_uvmis 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-uvmbefore the service starts; without it Ollama quietly runs on Vulkan.ollama-setup.sh testandverify-setup.shreport 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 security9.2 → 2.2 beforePrivateTmp,PrivateUsers,RestrictAddressFamiliesandUMaskjoined them) — it resets the$PATHthe installer bakes into the unit and allows only the accelerator devices. Anollamauser that upstream'sinstall.shcreated 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_OVERHEADsets a 3 GiB floor, but only a bakednum_gpuholds the headroom, so the script sweeps GPU layer counts and bakes the largest that still leaves about 3 GB free atnum_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 athttp://127.0.0.1:11434and leave Override Parameters off — on, it sends its ownnum_ctxand overrides the baked cap that holds the VRAM margin.
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 thatvirtio-win-guest-tools.exeinstalls, and the Hyper-V enlightenmentsvirt-install --osinfo win11adds. 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_CPUSandVM_DISK_GIBoverride 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=yesadds you to thelibvirtgroup instead, which polkit lets manageqemu:///systemwithout a password; that is root-equivalent, so it stays opt-in. - Storage: on btrfs the script turns an empty
/var/lib/libvirt/imagesinto 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/homeand 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 forNetKVM\w11\amd64(network): without the network driver, Windows' first-run setup cannot get online. Missed it: at the network screen press Shift+F10 and runpnputil /add-driver E:\NetKVM\w11\amd64\netkvm.inf /install, whereE:is the virtio-win CD (dir E:\listsNetKVM). After the first login, runvirtio-win-guest-tools.exefrom 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.
--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.
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).
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.
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.
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.
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.
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).
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).
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.
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%.
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.
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.
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.
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.
Upgrades to the next Fedora release; see Upgrading Fedora. It downloads and leaves the reboot to sudo dnf system-upgrade reboot.
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.
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}.shConky 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.
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"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.shcreates private read-only root and separate home snapshots plus verified/bootand EFI archives under/.fedora-kde-setup-backups/run-*. AREADYmarker identifies a completed backup;RECOVERY.txt,layout.txtandmounts.txtrecord 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, andscripts/disk-cleanup.shlists 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.shseparately takes a Snapper root snapshot and prints its number.sudo snapper -c root status <n>..0lists what changed since, andsudo 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 withsudo 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.servicefirst, because a deleted unit thatsleep.targetstill requires stops every suspend, andsudo systemctl daemon-reloadafter. The exceptions are/etc/dnf/dnf.confand/etc/tuned/ppd.conf, which replace package defaults. Delete those and runsudo 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.shwithRICE=noremoves it.kernel.kexec_load_disabledandkernel.unprivileged_bpf_disabledstay set until the next boot after/etc/sysctl.d/99-tweaks.confis gone. - Services, firewall, KDE settings: the commands in the guide above are the record;
systemctl unmask,firewall-cmd --permanent --add-service=<name>andkwriteconfig6with the old value reverse them. - Changes with no file to delete:
fedora-setup.shswapsglibc-all-langpacksfor your languages' langpacks and limits RPM translations to them in/etc/rpm/macros.image-language-conf, whichapply-system.shrewrites on every run (reinstallglibc-all-langpacks, delete the file and stop runningapply-system.shto undo it). It disables ABRT (sudo systemctl enable --now abrtd), removes Fedora's Flatpak remote when nothing uses it, and writes these KDE keys withkwriteconfig6: KWinLatencyPolicy, 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.shkeeps 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 throughkwriteconfig6need their own reversal. - GRUB default:
/etc/default/grub.origis the file before the setup edited it; copy it back and runsudo grub2-mkconfig -o /boot/grub2/grub.cfg. - Terminal extras:
fedora-setup.shinstalls 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-setupand# <<< fedora-kde-setupmarkers from~/.bashrc, or restore~/.bashrc.orig. - Ollama (
scripts/ollama-setup.sh):sudo systemctl disable --now ollama, then remove/etc/systemd/system/ollama.serviceand its.ddrop-ins,/usr/local/bin/ollama,/usr/local/lib/ollama,/var/lib/ollama, theollamauser and group, and the GUI withflatpak uninstall com.jeffser.Alpaca. - Strata (
scripts/strata-setup.sh): remove the checkout (~/Prosjekter/Strata), the model files (/mnt/data/Strata-dataor yourSTRATA_DATA), the CUDA repo/etc/yum.repos.d/cuda-fedora44.repo, andsudo dnf remove cuda-toolkit-13-4. - Emulation (
scripts/emulation-setup.sh):flatpak uninstallthe Flatpak emulators andflatpak remote-delete dolphin; removeES-DE_x64.AppImage,DuckStation-x64.AppImageplus its license/source sidecars,rpcs3_linux64.AppImage,Shadps4-sdl.AppImageandxenia_edge_linux.AppImagefrom~/Applications, andes-de.desktopandrpcs3.desktopfrom~/.local/share/applications(sudo dnf remove emulationstation-defor an ES-DE from Terra). Restore the numbered backups of~/ES-DE/custom_systems/es_systems.xmlandes_find_rules.xmlif present. ROMs, BIOS files and saves are left alone: RPCS3's data stays in~/.config/rpcs3and DuckStation's in~/.local/share/duckstation. - Windows VM (
scripts/windows-vm-setup.sh):sudo virsh undefine win11 --nvram --tpm --storage vdaremoves the VM with its disk, NVRAM and TPM state; delete the ISOs from/var/lib/libvirt/imagesyourself. WithVM_LIBVIRT_GROUP=yes, leave the group withsudo gpasswd -d $USER libvirt. The libvirt packages and the/var/lib/libvirt/imagessubvolume stay, since other VMs may use them.
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 releasefedora-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
localectland falls back to fbcon when it cannot start, somok-reenroll.shstill runs from a TTY.setfontandloadkeysno longer apply there. - ptrace: debuggers (
gdb,strace,ltrace,elfutils,systemtap) now pull inyama-ptrace-enable, which setskernel.yama.ptrace_scope=0.99-tweaks.confkeeps it at 1. - RPM signatures are mandatory:
rpm -irefuses an unsigned package or one whose key is not imported. Install local RPMs withdnf install ./file.rpm, which is also how akmods installs its NVIDIA build. - Repo files move to
/usr/share/dnf5/repos.d. Theincludepkgsandexcludelimits the scripts set live in/etc/dnf/repos.override.dand keep applying. An edited packaged repo file comes back as.rpmsave. - Conky 1.24 takes the background's transparency from
own_window_colourand ignores the old ARGB settings, so a Conky config from before the upgrade draws an opaque black box. Copyconfigs/conky/conky.confto~/.config/conky/again and restart theconkyuser service. - Lua 5.5: Conky is rebuilt against it, and
configs/conky/conky.confparses 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.shchecks that the service still runs. - GCC 16: CUDA 13.4's
nvccaccepts it, sostrata-setup.shstill 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 newadsoption, on by default). Do not addads=0to fstab while a 7.2 kernel is the fallback: 7.2 rejects the option andnofailhides the failed mount.verify-setup.shchecks the new kernel's filesystem drivers and NVIDIA module before you boot it.
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| Tool | Source |
|---|---|
| kernel-hardening-checker | github.com/a13xp0p0v/kernel-hardening-checker |
- Fedora Documentation
- WirePlumber ALSA configuration —
monitor.alsa.rulesanddevice.disabled, used by50-audio.conf - Arch Wiki — good reference even on Fedora
- r/Fedora
- r/unixporn
- r/linux_gaming
