about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/README.md2
-rw-r--r--nixos/doc/manual/configuration/abstractions.section.md2
-rw-r--r--nixos/doc/manual/configuration/gpu-accel.chapter.md28
-rw-r--r--nixos/doc/manual/configuration/x-windows.chapter.md19
-rw-r--r--nixos/doc/manual/development/bootspec.chapter.md17
-rw-r--r--nixos/doc/manual/development/option-declarations.section.md2
-rw-r--r--nixos/doc/manual/development/running-nixos-tests-interactively.section.md2
-rw-r--r--nixos/doc/manual/development/settings-options.section.md21
-rw-r--r--nixos/doc/manual/installation/installing-from-other-distro.section.md4
-rw-r--r--nixos/doc/manual/installation/installing-virtualbox-guest.section.md4
-rw-r--r--nixos/doc/manual/installation/upgrading.chapter.md20
-rw-r--r--nixos/doc/manual/release-notes/release-notes.md1
-rw-r--r--nixos/doc/manual/release-notes/rl-2111.section.md2
-rw-r--r--nixos/doc/manual/release-notes/rl-2205.section.md2
-rw-r--r--nixos/doc/manual/release-notes/rl-2211.section.md2
-rw-r--r--nixos/doc/manual/release-notes/rl-2405.section.md921
-rw-r--r--nixos/doc/manual/release-notes/rl-2411.section.md136
-rw-r--r--nixos/lib/make-disk-image.nix9
-rw-r--r--nixos/lib/systemd-lib.nix27
-rw-r--r--nixos/lib/systemd-network-units.nix27
-rw-r--r--nixos/lib/test-driver/default.nix16
-rw-r--r--nixos/lib/test-driver/pyproject.toml8
-rwxr-xr-xnixos/lib/test-driver/test_driver/__init__.py30
-rw-r--r--nixos/lib/test-driver/test_driver/driver.py45
-rw-r--r--nixos/lib/test-driver/test_driver/logger.py249
-rw-r--r--nixos/lib/test-driver/test_driver/machine.py16
-rw-r--r--nixos/lib/test-driver/test_driver/polling_condition.py11
-rw-r--r--nixos/lib/test-driver/test_driver/vlan.py15
-rw-r--r--nixos/lib/test-script-prepend.py4
-rw-r--r--nixos/lib/testing/driver.nix2
-rw-r--r--nixos/maintainers/scripts/ec2/README.md35
-rw-r--r--nixos/maintainers/scripts/ec2/amazon-image.nix6
-rwxr-xr-xnixos/maintainers/scripts/ec2/create-amis.sh368
-rw-r--r--nixos/maintainers/scripts/lxd/lxd-container-image.nix2
-rw-r--r--nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix3
-rw-r--r--nixos/modules/config/fonts/ghostscript.nix2
-rw-r--r--nixos/modules/config/no-x-libs.nix9
-rw-r--r--nixos/modules/config/nsswitch.nix26
-rw-r--r--nixos/modules/config/swap.nix10
-rw-r--r--nixos/modules/config/terminfo.nix2
-rw-r--r--nixos/modules/config/xdg/portal.nix2
-rw-r--r--nixos/modules/config/xdg/terminal-exec.nix54
-rw-r--r--nixos/modules/hardware/graphics.nix126
-rw-r--r--nixos/modules/hardware/opengl.nix161
-rw-r--r--nixos/modules/hardware/openrazer.nix21
-rw-r--r--nixos/modules/hardware/printers.nix2
-rw-r--r--nixos/modules/hardware/video/amdgpu-pro.nix69
-rw-r--r--nixos/modules/hardware/video/intel-gpu-tools.nix25
-rw-r--r--nixos/modules/hardware/video/nvidia.nix882
-rw-r--r--nixos/modules/hardware/video/virtualbox.nix7
-rw-r--r--nixos/modules/hardware/xone.nix2
-rw-r--r--nixos/modules/image/repart-image.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix4
-rw-r--r--nixos/modules/installer/netboot/netboot.nix4
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix10
-rw-r--r--nixos/modules/misc/locate.nix154
-rw-r--r--nixos/modules/misc/version.nix4
-rw-r--r--nixos/modules/module-list.nix39
-rw-r--r--nixos/modules/profiles/perlless.nix2
-rw-r--r--nixos/modules/programs/_1password-gui.nix16
-rw-r--r--nixos/modules/programs/_1password.nix10
-rw-r--r--nixos/modules/programs/adb.nix10
-rw-r--r--nixos/modules/programs/alvr.nix16
-rw-r--r--nixos/modules/programs/appgate-sdp.nix6
-rw-r--r--nixos/modules/programs/atop.nix50
-rw-r--r--nixos/modules/programs/ausweisapp.nix8
-rw-r--r--nixos/modules/programs/autojump.nix12
-rw-r--r--nixos/modules/programs/bandwhich.nix10
-rw-r--r--nixos/modules/programs/bash-my-aws.nix6
-rw-r--r--nixos/modules/programs/bash/bash-completion.nix18
-rw-r--r--nixos/modules/programs/bash/bash.nix48
-rw-r--r--nixos/modules/programs/bash/blesh.nix9
-rw-r--r--nixos/modules/programs/bash/ls-colors.nix6
-rw-r--r--nixos/modules/programs/bash/undistract-me.nix16
-rw-r--r--nixos/modules/programs/benchexec.nix98
-rw-r--r--nixos/modules/programs/browserpass.nix6
-rw-r--r--nixos/modules/programs/calls.nix6
-rw-r--r--nixos/modules/programs/cdemu.nix24
-rw-r--r--nixos/modules/programs/cfs-zen-tweaks.nix8
-rw-r--r--nixos/modules/programs/chromium.nix44
-rw-r--r--nixos/modules/programs/cnping.nix6
-rw-r--r--nixos/modules/programs/command-not-found/command-not-found.nix12
-rw-r--r--nixos/modules/programs/coolercontrol.nix8
-rw-r--r--nixos/modules/programs/cpu-energy-meter.nix27
-rw-r--r--nixos/modules/programs/criu.nix8
-rw-r--r--nixos/modules/programs/digitalbitbox/default.nix10
-rw-r--r--nixos/modules/programs/dmrconfig.nix12
-rw-r--r--nixos/modules/programs/droidcam.nix4
-rw-r--r--nixos/modules/programs/dublin-traceroute.nix10
-rw-r--r--nixos/modules/programs/ecryptfs.nix6
-rw-r--r--nixos/modules/programs/environment.nix10
-rw-r--r--nixos/modules/programs/extra-container.nix5
-rw-r--r--nixos/modules/programs/feedbackd.nix8
-rw-r--r--nixos/modules/programs/firefox.nix52
-rw-r--r--nixos/modules/programs/firejail.nix40
-rw-r--r--nixos/modules/programs/fish.nix86
-rw-r--r--nixos/modules/programs/flashrom.nix10
-rw-r--r--nixos/modules/programs/flexoptix-app.nix8
-rw-r--r--nixos/modules/programs/freetds.nix12
-rw-r--r--nixos/modules/programs/fuse.nix16
-rw-r--r--nixos/modules/programs/fzf.nix2
-rw-r--r--nixos/modules/programs/gamemode.nix20
-rw-r--r--nixos/modules/programs/gamescope.nix36
-rw-r--r--nixos/modules/programs/gdk-pixbuf.nix (renamed from nixos/modules/services/x11/gdk-pixbuf.nix)12
-rw-r--r--nixos/modules/programs/geary.nix8
-rw-r--r--nixos/modules/programs/git.nix34
-rw-r--r--nixos/modules/programs/gnupg.nix16
-rw-r--r--nixos/modules/programs/gphoto2.nix10
-rw-r--r--nixos/modules/programs/haguichi.nix6
-rw-r--r--nixos/modules/programs/hamster.nix4
-rw-r--r--nixos/modules/programs/htop.nix24
-rw-r--r--nixos/modules/programs/i3lock.nix14
-rw-r--r--nixos/modules/programs/iftop.nix6
-rw-r--r--nixos/modules/programs/iotop.nix6
-rw-r--r--nixos/modules/programs/java.nix12
-rw-r--r--nixos/modules/programs/joycond-cemuhook.nix3
-rw-r--r--nixos/modules/programs/k3b.nix8
-rw-r--r--nixos/modules/programs/k40-whisperer.nix12
-rw-r--r--nixos/modules/programs/kbdlight.nix6
-rw-r--r--nixos/modules/programs/kclock.nix5
-rw-r--r--nixos/modules/programs/kdeconnect.nix8
-rw-r--r--nixos/modules/programs/kubeswitch.nix18
-rw-r--r--nixos/modules/programs/ladybird.nix14
-rw-r--r--nixos/modules/programs/less.nix68
-rw-r--r--nixos/modules/programs/liboping.nix8
-rw-r--r--nixos/modules/programs/light.nix20
-rw-r--r--nixos/modules/programs/mdevctl.nix5
-rw-r--r--nixos/modules/programs/mepo.nix17
-rw-r--r--nixos/modules/programs/mininet.nix6
-rw-r--r--nixos/modules/programs/miriway.nix2
-rw-r--r--nixos/modules/programs/msmtp.nix32
-rw-r--r--nixos/modules/programs/mtr.nix12
-rw-r--r--nixos/modules/programs/nbd.nix6
-rw-r--r--nixos/modules/programs/neovim.nix76
-rw-r--r--nixos/modules/programs/nethoscope.nix10
-rw-r--r--nixos/modules/programs/nncp.nix23
-rw-r--r--nixos/modules/programs/noisetorch.nix8
-rw-r--r--nixos/modules/programs/npm.nix8
-rw-r--r--nixos/modules/programs/oblogout.nix4
-rw-r--r--nixos/modules/programs/openvpn3.nix12
-rw-r--r--nixos/modules/programs/pantheon-tweaks.nix19
-rw-r--r--nixos/modules/programs/plotinus.nix8
-rw-r--r--nixos/modules/programs/pqos-wrapper.nix27
-rw-r--r--nixos/modules/programs/proxychains.nix71
-rw-r--r--nixos/modules/programs/qt5ct.nix4
-rw-r--r--nixos/modules/programs/rust-motd.nix34
-rw-r--r--nixos/modules/programs/screen.nix37
-rw-r--r--nixos/modules/programs/sedutil.nix6
-rw-r--r--nixos/modules/programs/shadow.nix276
-rw-r--r--nixos/modules/programs/sharing.nix5
-rw-r--r--nixos/modules/programs/singularity.nix35
-rw-r--r--nixos/modules/programs/slock.nix10
-rw-r--r--nixos/modules/programs/soundmodem.nix10
-rw-r--r--nixos/modules/programs/spacefm.nix16
-rw-r--r--nixos/modules/programs/ssh.nix136
-rw-r--r--nixos/modules/programs/steam.nix118
-rw-r--r--nixos/modules/programs/streamdeck-ui.nix18
-rw-r--r--nixos/modules/programs/sysdig.nix6
-rw-r--r--nixos/modules/programs/system-config-printer.nix6
-rw-r--r--nixos/modules/programs/systemtap.nix8
-rw-r--r--nixos/modules/programs/thefuck.nix14
-rw-r--r--nixos/modules/programs/thunar.nix14
-rw-r--r--nixos/modules/programs/thunderbird.nix89
-rw-r--r--nixos/modules/programs/traceroute.nix8
-rw-r--r--nixos/modules/programs/turbovnc.nix12
-rw-r--r--nixos/modules/programs/udevil.nix6
-rw-r--r--nixos/modules/programs/usbtop.nix6
-rw-r--r--nixos/modules/programs/vim.nix12
-rw-r--r--nixos/modules/programs/virt-manager.nix18
-rw-r--r--nixos/modules/programs/wavemon.nix8
-rw-r--r--nixos/modules/programs/wayland/hyprland.nix132
-rw-r--r--nixos/modules/programs/wayland/hyprlock.nix25
-rw-r--r--nixos/modules/programs/wayland/lib.nix12
-rw-r--r--nixos/modules/programs/wayland/river.nix72
-rw-r--r--nixos/modules/programs/wayland/sway.nix198
-rw-r--r--nixos/modules/programs/wayland/waybar.nix10
-rw-r--r--nixos/modules/programs/wayland/wayland-session.nix40
-rw-r--r--nixos/modules/programs/weylus.nix16
-rw-r--r--nixos/modules/programs/wireshark.nix10
-rw-r--r--nixos/modules/programs/xastir.nix8
-rw-r--r--nixos/modules/programs/xfconf.nix8
-rw-r--r--nixos/modules/programs/xfs_quota.nix48
-rw-r--r--nixos/modules/programs/xonsh.nix27
-rw-r--r--nixos/modules/programs/xss-lock.nix29
-rw-r--r--nixos/modules/programs/xwayland.nix20
-rw-r--r--nixos/modules/programs/yabar.nix60
-rw-r--r--nixos/modules/programs/yazi.nix63
-rw-r--r--nixos/modules/programs/ydotool.nix92
-rw-r--r--nixos/modules/programs/zmap.nix6
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.nix52
-rw-r--r--nixos/modules/programs/zsh/zsh-autoenv.nix8
-rw-r--r--nixos/modules/programs/zsh/zsh-autosuggestions.nix32
-rw-r--r--nixos/modules/programs/zsh/zsh-syntax-highlighting.nix50
-rw-r--r--nixos/modules/programs/zsh/zsh.nix86
-rw-r--r--nixos/modules/rename.nix8
-rw-r--r--nixos/modules/security/acme/default.nix2
-rw-r--r--nixos/modules/security/ipa.nix16
-rw-r--r--nixos/modules/security/krb5/default.nix18
-rw-r--r--nixos/modules/security/krb5/krb5-conf-format.nix73
-rw-r--r--nixos/modules/security/systemd-confinement.nix36
-rw-r--r--nixos/modules/services/admin/docuum.nix30
-rw-r--r--nixos/modules/services/admin/meshcentral.nix2
-rw-r--r--nixos/modules/services/admin/pgadmin.nix10
-rw-r--r--nixos/modules/services/audio/alsa.nix3
-rw-r--r--nixos/modules/services/audio/mopidy.nix1
-rw-r--r--nixos/modules/services/audio/navidrome.nix191
-rw-r--r--nixos/modules/services/backup/borgbackup.nix34
-rw-r--r--nixos/modules/services/backup/restic.nix19
-rw-r--r--nixos/modules/services/cluster/k3s/default.nix44
-rw-r--r--nixos/modules/services/cluster/kubernetes/default.nix2
-rw-r--r--nixos/modules/services/cluster/rke2/default.nix311
-rw-r--r--nixos/modules/services/continuous-integration/hydra/default.nix2
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/default.nix2
-rw-r--r--nixos/modules/services/databases/postgresql.md21
-rw-r--r--nixos/modules/services/desktop-managers/lomiri.nix16
-rw-r--r--nixos/modules/services/desktop-managers/plasma6.nix74
-rw-r--r--nixos/modules/services/desktops/espanso.nix11
-rw-r--r--nixos/modules/services/desktops/gnome/gnome-keyring.nix57
-rw-r--r--nixos/modules/services/display-managers/default.nix10
-rw-r--r--nixos/modules/services/display-managers/greetd.nix14
-rw-r--r--nixos/modules/services/display-managers/sddm.nix15
-rw-r--r--nixos/modules/services/editors/emacs.nix7
-rw-r--r--nixos/modules/services/games/archisteamfarm.nix9
-rw-r--r--nixos/modules/services/hardware/amdgpu.nix43
-rw-r--r--nixos/modules/services/hardware/amdvlk.nix59
-rw-r--r--nixos/modules/services/hardware/handheld-daemon.nix2
-rw-r--r--nixos/modules/services/hardware/kanata.nix76
-rw-r--r--nixos/modules/services/hardware/nvidia-container-toolkit/default.nix8
-rw-r--r--nixos/modules/services/hardware/nvidia-optimus.nix2
-rw-r--r--nixos/modules/services/hardware/power-profiles-daemon.nix6
-rw-r--r--nixos/modules/services/hardware/thermald.nix13
-rw-r--r--nixos/modules/services/hardware/udev.nix14
-rw-r--r--nixos/modules/services/home-automation/ebusd.nix2
-rw-r--r--nixos/modules/services/home-automation/home-assistant.nix5
-rw-r--r--nixos/modules/services/home-automation/wyoming/faster-whisper.nix3
-rw-r--r--nixos/modules/services/home-automation/wyoming/openwakeword.nix3
-rw-r--r--nixos/modules/services/home-automation/wyoming/piper.nix3
-rw-r--r--nixos/modules/services/logging/journalwatch.nix4
-rw-r--r--nixos/modules/services/mail/mailman.nix2
-rw-r--r--nixos/modules/services/mail/postsrsd.nix9
-rw-r--r--nixos/modules/services/mail/public-inbox.nix2
-rw-r--r--nixos/modules/services/mail/stalwart-mail.nix163
-rw-r--r--nixos/modules/services/matrix/mautrix-signal.nix249
-rw-r--r--nixos/modules/services/matrix/synapse.nix2
-rw-r--r--nixos/modules/services/misc/amazon-ssm-agent.nix8
-rw-r--r--nixos/modules/services/misc/anki-sync-server.md2
-rw-r--r--nixos/modules/services/misc/bcg.nix6
-rw-r--r--nixos/modules/services/misc/devpi-server.nix132
-rw-r--r--nixos/modules/services/misc/forgejo.nix179
-rw-r--r--nixos/modules/services/misc/gitea.nix2
-rw-r--r--nixos/modules/services/misc/gollum.nix2
-rw-r--r--nixos/modules/services/misc/graphical-desktop.nix2
-rw-r--r--nixos/modules/services/misc/invidious-router.nix4
-rw-r--r--nixos/modules/services/misc/jellyfin.nix2
-rw-r--r--nixos/modules/services/misc/llama-cpp.nix1
-rw-r--r--nixos/modules/services/misc/mqtt2influxdb.nix3
-rw-r--r--nixos/modules/services/misc/ollama.nix56
-rw-r--r--nixos/modules/services/misc/open-webui.nix114
-rw-r--r--nixos/modules/services/misc/pghero.nix142
-rw-r--r--nixos/modules/services/misc/plex.nix29
-rw-r--r--nixos/modules/services/misc/portunus.nix23
-rw-r--r--nixos/modules/services/misc/private-gpt.nix121
-rw-r--r--nixos/modules/services/misc/renovate.nix153
-rw-r--r--nixos/modules/services/misc/snapper.nix305
-rw-r--r--nixos/modules/services/misc/sourcehut/service.nix9
-rw-r--r--nixos/modules/services/misc/tandoor-recipes.nix4
-rw-r--r--nixos/modules/services/misc/tzupdate.nix2
-rw-r--r--nixos/modules/services/misc/wastebin.nix2
-rw-r--r--nixos/modules/services/misc/zoneminder.nix2
-rw-r--r--nixos/modules/services/monitoring/alloy.nix80
-rw-r--r--nixos/modules/services/monitoring/arbtt.nix2
-rw-r--r--nixos/modules/services/monitoring/grafana-reporter.nix2
-rw-r--r--nixos/modules/services/monitoring/grafana.nix100
-rw-r--r--nixos/modules/services/monitoring/loki.nix15
-rw-r--r--nixos/modules/services/monitoring/netdata.nix65
-rw-r--r--nixos/modules/services/monitoring/prometheus/alertmanager-webhook-logger.nix70
-rw-r--r--nixos/modules/services/monitoring/prometheus/default.nix4
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix4
-rw-r--r--nixos/modules/services/monitoring/scrutiny.nix4
-rw-r--r--nixos/modules/services/monitoring/zabbix-proxy.nix2
-rw-r--r--nixos/modules/services/network-filesystems/davfs2.nix59
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix6
-rw-r--r--nixos/modules/services/networking/adguardhome.nix11
-rw-r--r--nixos/modules/services/networking/antennas.nix5
-rw-r--r--nixos/modules/services/networking/aria2.nix165
-rw-r--r--nixos/modules/services/networking/clatd.nix1
-rw-r--r--nixos/modules/services/networking/ddclient.nix22
-rw-r--r--nixos/modules/services/networking/frr.nix27
-rw-r--r--nixos/modules/services/networking/git-daemon.nix4
-rw-r--r--nixos/modules/services/networking/hostapd.nix17
-rw-r--r--nixos/modules/services/networking/inadyn.nix2
-rw-r--r--nixos/modules/services/networking/jotta-cli.md4
-rw-r--r--nixos/modules/services/networking/jotta-cli.nix4
-rw-r--r--nixos/modules/services/networking/kea.nix3
-rw-r--r--nixos/modules/services/networking/mihomo.nix1
-rw-r--r--nixos/modules/services/networking/mycelium.nix2
-rw-r--r--nixos/modules/services/networking/netbird.nix1
-rw-r--r--nixos/modules/services/networking/netbird/coturn.nix7
-rw-r--r--nixos/modules/services/networking/netbird/server.nix33
-rw-r--r--nixos/modules/services/networking/networkmanager.nix2
-rw-r--r--nixos/modules/services/networking/oink.nix84
-rw-r--r--nixos/modules/services/networking/pixiecore.nix4
-rw-r--r--nixos/modules/services/networking/radvd.nix13
-rw-r--r--nixos/modules/services/networking/rosenpass.nix10
-rw-r--r--nixos/modules/services/networking/smokeping.nix63
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix73
-rw-r--r--nixos/modules/services/networking/sunshine.nix6
-rw-r--r--nixos/modules/services/networking/tailscale-auth.nix2
-rw-r--r--nixos/modules/services/networking/tailscale.nix23
-rw-r--r--nixos/modules/services/networking/tayga.nix26
-rw-r--r--nixos/modules/services/networking/vsftpd.nix2
-rw-r--r--nixos/modules/services/networking/wireguard.nix10
-rw-r--r--nixos/modules/services/networking/wstunnel.nix563
-rw-r--r--nixos/modules/services/networking/zerotierone.nix8
-rw-r--r--nixos/modules/services/search/qdrant.nix1
-rw-r--r--nixos/modules/services/search/quickwit.nix190
-rw-r--r--nixos/modules/services/security/bitwarden-directory-connector-cli.nix1
-rw-r--r--nixos/modules/services/security/oauth2-proxy-nginx.nix25
-rw-r--r--nixos/modules/services/security/oauth2-proxy.nix32
-rw-r--r--nixos/modules/services/security/sslmate-agent.nix2
-rw-r--r--nixos/modules/services/security/step-ca.nix4
-rw-r--r--nixos/modules/services/security/vaultwarden/default.nix47
-rw-r--r--nixos/modules/services/system/dbus.nix20
-rw-r--r--nixos/modules/services/system/kerberos/default.nix84
-rw-r--r--nixos/modules/services/system/kerberos/heimdal.nix105
-rw-r--r--nixos/modules/services/system/kerberos/kerberos-server.md55
-rw-r--r--nixos/modules/services/system/kerberos/mit.nix78
-rw-r--r--nixos/modules/services/system/nix-daemon.nix2
-rw-r--r--nixos/modules/services/torrent/flood.nix85
-rw-r--r--nixos/modules/services/torrent/transmission.nix5
-rw-r--r--nixos/modules/services/ttys/getty.nix3
-rw-r--r--nixos/modules/services/ttys/kmscon.nix2
-rw-r--r--nixos/modules/services/video/frigate.nix4
-rw-r--r--nixos/modules/services/video/photonvision.nix2
-rw-r--r--nixos/modules/services/wayland/cage.nix2
-rw-r--r--nixos/modules/services/wayland/hypridle.nix26
-rw-r--r--nixos/modules/services/web-apps/akkoma.nix16
-rw-r--r--nixos/modules/services/web-apps/artalk.nix131
-rw-r--r--nixos/modules/services/web-apps/commafeed.nix114
-rw-r--r--nixos/modules/services/web-apps/filesender.md49
-rw-r--r--nixos/modules/services/web-apps/filesender.nix253
-rw-r--r--nixos/modules/services/web-apps/firefly-iii.nix98
-rw-r--r--nixos/modules/services/web-apps/flarum.nix210
-rw-r--r--nixos/modules/services/web-apps/freshrss.nix15
-rw-r--r--nixos/modules/services/web-apps/invoiceplane.nix34
-rw-r--r--nixos/modules/services/web-apps/keycloak.md12
-rw-r--r--nixos/modules/services/web-apps/keycloak.nix35
-rw-r--r--nixos/modules/services/web-apps/mastodon.nix67
-rw-r--r--nixos/modules/services/web-apps/mealie.nix3
-rw-r--r--nixos/modules/services/web-apps/miniflux.nix15
-rw-r--r--nixos/modules/services/web-apps/nextcloud-notify_push.nix3
-rw-r--r--nixos/modules/services/web-apps/nextcloud.md5
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix64
-rw-r--r--nixos/modules/services/web-apps/nextjs-ollama-llm-ui.nix87
-rw-r--r--nixos/modules/services/web-apps/node-red.nix12
-rw-r--r--nixos/modules/services/web-apps/plausible.nix9
-rw-r--r--nixos/modules/services/web-apps/pretalx.nix84
-rw-r--r--nixos/modules/services/web-apps/pretix.nix16
-rw-r--r--nixos/modules/services/web-apps/simplesamlphp.nix128
-rw-r--r--nixos/modules/services/web-apps/slskd.nix2
-rw-r--r--nixos/modules/services/web-apps/your_spotify.nix191
-rw-r--r--nixos/modules/services/web-apps/zitadel.nix2
-rw-r--r--nixos/modules/services/web-servers/bluemap.nix309
-rw-r--r--nixos/modules/services/web-servers/caddy/default.nix5
-rw-r--r--nixos/modules/services/web-servers/garage.nix58
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix11
-rw-r--r--nixos/modules/services/web-servers/nginx/tailscale-auth.nix4
-rw-r--r--nixos/modules/services/web-servers/traefik.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/mate.nix1
-rw-r--r--nixos/modules/services/x11/desktop-managers/pantheon.nix5
-rw-r--r--nixos/modules/services/x11/desktop-managers/phosh.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/clfswm.nix2
-rw-r--r--nixos/modules/services/x11/window-managers/qtile.nix43
-rw-r--r--nixos/modules/services/x11/xserver.nix10
-rwxr-xr-xnixos/modules/system/activation/switch-to-configuration.pl3
-rw-r--r--nixos/modules/system/activation/switchable-system.nix109
-rw-r--r--nixos/modules/system/activation/test.nix4
-rw-r--r--nixos/modules/system/activation/top-level.nix23
-rw-r--r--nixos/modules/system/boot/binfmt.nix2
-rw-r--r--nixos/modules/system/boot/initrd-ssh.nix2
-rw-r--r--nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py3
-rw-r--r--nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix6
-rw-r--r--nixos/modules/system/boot/networkd.nix267
-rw-r--r--nixos/modules/system/boot/resolved.nix143
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh1
-rw-r--r--nixos/modules/system/boot/systemd.nix11
-rw-r--r--nixos/modules/system/boot/systemd/coredump.nix2
-rw-r--r--nixos/modules/system/boot/systemd/initrd.nix4
-rw-r--r--nixos/modules/system/boot/systemd/sysusers.nix21
-rw-r--r--nixos/modules/system/boot/systemd/user.nix12
-rw-r--r--nixos/modules/system/etc/build-composefs-dump.py35
-rw-r--r--nixos/modules/system/etc/etc.nix2
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix34
-rw-r--r--nixos/modules/tasks/network-interfaces-scripted.nix6
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix121
-rw-r--r--nixos/modules/testing/test-instrumentation.nix2
-rw-r--r--nixos/modules/virtualisation/containerd.nix1
-rw-r--r--nixos/modules/virtualisation/containers.nix13
-rw-r--r--nixos/modules/virtualisation/docker.nix4
-rw-r--r--nixos/modules/virtualisation/incus.nix74
-rw-r--r--nixos/modules/virtualisation/lxc-image-metadata.nix4
-rw-r--r--nixos/modules/virtualisation/lxd-agent.nix2
-rw-r--r--nixos/modules/virtualisation/oci-containers.nix11
-rw-r--r--nixos/modules/virtualisation/oci-image.nix2
-rw-r--r--nixos/modules/virtualisation/oci-options.nix6
-rw-r--r--nixos/modules/virtualisation/proxmox-image.nix83
-rw-r--r--nixos/modules/virtualisation/proxmox-lxc.nix11
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix2
-rw-r--r--nixos/modules/virtualisation/spice-usb-redirection.nix2
-rw-r--r--nixos/modules/virtualisation/vagrant-guest.nix3
-rw-r--r--nixos/modules/virtualisation/virtualbox-guest.nix11
-rw-r--r--nixos/modules/virtualisation/virtualbox-host.nix2
-rw-r--r--nixos/release-combined.nix5
-rw-r--r--nixos/release-small.nix2
-rw-r--r--nixos/tests/acme.nix40
-rw-r--r--nixos/tests/activation/etc-overlay-immutable.nix4
-rw-r--r--nixos/tests/all-tests.nix51
-rw-r--r--nixos/tests/alloy.nix32
-rw-r--r--nixos/tests/archi.nix4
-rw-r--r--nixos/tests/aria2.nix43
-rw-r--r--nixos/tests/armagetronad.nix2
-rw-r--r--nixos/tests/artalk.nix28
-rw-r--r--nixos/tests/avahi.nix2
-rw-r--r--nixos/tests/ayatana-indicators.nix9
-rw-r--r--nixos/tests/benchexec.nix54
-rw-r--r--nixos/tests/bittorrent.nix2
-rw-r--r--nixos/tests/cagebreak.nix6
-rw-r--r--nixos/tests/castopod.nix3
-rw-r--r--nixos/tests/centrifugo.nix8
-rw-r--r--nixos/tests/clatd.nix97
-rw-r--r--nixos/tests/commafeed.nix21
-rw-r--r--nixos/tests/containers-bridge.nix2
-rw-r--r--nixos/tests/containers-imperative.nix2
-rw-r--r--nixos/tests/containers-ip.nix2
-rw-r--r--nixos/tests/containers-portforward.nix2
-rw-r--r--nixos/tests/crabfit.nix2
-rw-r--r--nixos/tests/devpi-server.nix35
-rw-r--r--nixos/tests/domination.nix4
-rw-r--r--nixos/tests/elk.nix2
-rw-r--r--nixos/tests/fcitx5/default.nix3
-rw-r--r--nixos/tests/filesender.nix137
-rw-r--r--nixos/tests/firefly-iii.nix99
-rw-r--r--nixos/tests/firefox.nix6
-rw-r--r--nixos/tests/firewall.nix2
-rw-r--r--nixos/tests/fish.nix2
-rw-r--r--nixos/tests/flood.nix27
-rw-r--r--nixos/tests/forgejo.nix8
-rw-r--r--nixos/tests/garage/default.nix1
-rw-r--r--nixos/tests/garage/with-3node-replication.nix8
-rw-r--r--nixos/tests/gnome-extensions.nix5
-rw-r--r--nixos/tests/grafana/provision/default.nix41
-rw-r--r--nixos/tests/greetd-no-shadow.nix49
-rw-r--r--nixos/tests/home-assistant.nix8
-rw-r--r--nixos/tests/incus/container.nix108
-rw-r--r--nixos/tests/incus/default.nix21
-rw-r--r--nixos/tests/incus/incusd-options.nix110
-rw-r--r--nixos/tests/incus/lxd-to-incus.nix12
-rw-r--r--nixos/tests/incus/openvswitch.nix8
-rw-r--r--nixos/tests/incus/preseed.nix63
-rw-r--r--nixos/tests/incus/socket-activated.nix9
-rw-r--r--nixos/tests/incus/storage.nix12
-rw-r--r--nixos/tests/incus/ui.nix7
-rw-r--r--nixos/tests/incus/virtual-machine.nix13
-rw-r--r--nixos/tests/initrd-network.nix2
-rw-r--r--nixos/tests/initrd-secrets.nix2
-rw-r--r--nixos/tests/installer-systemd-stage-1.nix2
-rw-r--r--nixos/tests/installer.nix14
-rw-r--r--nixos/tests/ipv6.nix2
-rw-r--r--nixos/tests/jenkins.nix2
-rw-r--r--nixos/tests/jotta-cli.nix2
-rw-r--r--nixos/tests/k3s/default.nix18
-rw-r--r--nixos/tests/k3s/etcd.nix200
-rw-r--r--nixos/tests/k3s/multi-node.nix226
-rw-r--r--nixos/tests/k3s/single-node.nix131
-rw-r--r--nixos/tests/kea.nix1
-rw-r--r--nixos/tests/keepalived.nix4
-rw-r--r--nixos/tests/kerberos/heimdal.nix2
-rw-r--r--nixos/tests/kerberos/mit.nix2
-rw-r--r--nixos/tests/kernel-generic.nix6
-rw-r--r--nixos/tests/keycloak.nix9
-rw-r--r--nixos/tests/knot.nix4
-rw-r--r--nixos/tests/kubernetes/base.nix2
-rw-r--r--nixos/tests/kubo/default.nix4
-rw-r--r--nixos/tests/kubo/kubo-fuse.nix2
-rw-r--r--nixos/tests/ladybird.nix4
-rw-r--r--nixos/tests/libreswan-nat.nix238
-rw-r--r--nixos/tests/libreswan.nix4
-rw-r--r--nixos/tests/login.nix2
-rw-r--r--nixos/tests/logrotate.nix90
-rw-r--r--nixos/tests/lomiri-filemanager-app.nix48
-rw-r--r--nixos/tests/lomiri.nix143
-rw-r--r--nixos/tests/lvm2/default.nix4
-rw-r--r--nixos/tests/lvm2/systemd-stage-1.nix14
-rw-r--r--nixos/tests/matomo.nix6
-rw-r--r--nixos/tests/mealie.nix2
-rw-r--r--nixos/tests/mediamtx.nix95
-rw-r--r--nixos/tests/misc.nix25
-rw-r--r--nixos/tests/monado.nix2
-rw-r--r--nixos/tests/mpv.nix2
-rw-r--r--nixos/tests/mumble.nix2
-rw-r--r--nixos/tests/munin.nix2
-rw-r--r--nixos/tests/musescore.nix34
-rw-r--r--nixos/tests/mycelium/default.nix3
-rw-r--r--nixos/tests/mysql/common.nix2
-rw-r--r--nixos/tests/mysql/mysql.nix2
-rw-r--r--nixos/tests/nat.nix2
-rw-r--r--nixos/tests/netdata.nix5
-rw-r--r--nixos/tests/nextcloud/basic.nix103
-rw-r--r--nixos/tests/nextcloud/default.nix121
-rw-r--r--nixos/tests/nextcloud/with-mysql-and-memcached.nix58
-rw-r--r--nixos/tests/nextcloud/with-objectstore.nix96
-rw-r--r--nixos/tests/nextcloud/with-postgresql-and-redis.nix84
-rw-r--r--nixos/tests/nfs/simple.nix2
-rw-r--r--nixos/tests/nix/misc.nix64
-rw-r--r--nixos/tests/odoo.nix12
-rw-r--r--nixos/tests/ollama.nix8
-rw-r--r--nixos/tests/open-webui.nix33
-rw-r--r--nixos/tests/openarena.nix2
-rw-r--r--nixos/tests/openssh.nix28
-rw-r--r--nixos/tests/patroni.nix2
-rw-r--r--nixos/tests/pghero.nix63
-rw-r--r--nixos/tests/pgvecto-rs.nix2
-rw-r--r--nixos/tests/phosh.nix2
-rw-r--r--nixos/tests/podman/default.nix29
-rw-r--r--nixos/tests/printing.nix2
-rw-r--r--nixos/tests/private-gpt.nix27
-rw-r--r--nixos/tests/prometheus/alertmanager.nix148
-rw-r--r--nixos/tests/prometheus/config-reload.nix116
-rw-r--r--nixos/tests/prometheus/default.nix13
-rw-r--r--nixos/tests/prometheus/federation.nix213
-rw-r--r--nixos/tests/prometheus/prometheus-pair.nix87
-rw-r--r--nixos/tests/prometheus/pushgateway.nix94
-rw-r--r--nixos/tests/prometheus/remote-write.nix73
-rw-r--r--nixos/tests/proxy.nix2
-rw-r--r--nixos/tests/qtile.nix2
-rw-r--r--nixos/tests/quake3.nix4
-rw-r--r--nixos/tests/quickwit.nix103
-rw-r--r--nixos/tests/rabbitmq.nix2
-rw-r--r--nixos/tests/renovate.nix69
-rw-r--r--nixos/tests/rke2/default.nix13
-rw-r--r--nixos/tests/rke2/multi-node.nix176
-rw-r--r--nixos/tests/rke2/single-node.nix75
-rw-r--r--nixos/tests/rosenpass.nix14
-rw-r--r--nixos/tests/samba.nix2
-rw-r--r--nixos/tests/searx.nix182
-rw-r--r--nixos/tests/seatd.nix2
-rw-r--r--nixos/tests/simple.nix2
-rw-r--r--nixos/tests/smokeping.nix14
-rw-r--r--nixos/tests/snapper.nix5
-rw-r--r--nixos/tests/stalwart-mail.nix20
-rw-r--r--nixos/tests/step-ca.nix23
-rw-r--r--nixos/tests/stub-ld.nix4
-rw-r--r--nixos/tests/switch-test.nix7
-rw-r--r--nixos/tests/systemd-confinement.nix184
-rw-r--r--nixos/tests/systemd-confinement/checkperms.py187
-rw-r--r--nixos/tests/systemd-confinement/default.nix274
-rw-r--r--nixos/tests/systemd-initrd-luks-fido2.nix1
-rw-r--r--nixos/tests/systemd-initrd-modprobe.nix12
-rw-r--r--nixos/tests/systemd-machinectl.nix24
-rw-r--r--nixos/tests/systemd-networkd-dhcpserver-static-leases.nix38
-rw-r--r--nixos/tests/systemd-networkd-dhcpserver.nix6
-rw-r--r--nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix3
-rw-r--r--nixos/tests/systemd-networkd-vrf.nix4
-rw-r--r--nixos/tests/systemd-networkd.nix18
-rw-r--r--nixos/tests/systemd-resolved.nix75
-rw-r--r--nixos/tests/tandoor-recipes-script-name.nix95
-rw-r--r--nixos/tests/tayga.nix23
-rw-r--r--nixos/tests/teleport.nix1
-rw-r--r--nixos/tests/thanos.nix (renamed from nixos/tests/prometheus.nix)58
-rw-r--r--nixos/tests/tigervnc.nix10
-rw-r--r--nixos/tests/timezone.nix2
-rw-r--r--nixos/tests/tinywl.nix2
-rw-r--r--nixos/tests/turbovnc-headless-server.nix11
-rw-r--r--nixos/tests/udisks2.nix3
-rw-r--r--nixos/tests/vaultwarden.nix271
-rw-r--r--nixos/tests/vector.nix37
-rw-r--r--nixos/tests/vector/api.nix39
-rw-r--r--nixos/tests/vector/default.nix12
-rw-r--r--nixos/tests/vector/dnstap.nix118
-rw-r--r--nixos/tests/vector/file-sink.nix49
-rw-r--r--nixos/tests/vector/nginx-clickhouse.nix168
-rw-r--r--nixos/tests/vector/syslog-quickwit.nix155
-rw-r--r--nixos/tests/virtualbox.nix1
-rw-r--r--nixos/tests/web-apps/nextjs-ollama-llm-ui.nix22
-rw-r--r--nixos/tests/web-apps/pretalx.nix9
-rw-r--r--nixos/tests/web-servers/stargazer.nix15
-rw-r--r--nixos/tests/wstunnel.nix96
-rw-r--r--nixos/tests/ydotool.nix184
-rw-r--r--nixos/tests/your_spotify.nix33
592 files changed, 15847 insertions, 6945 deletions
diff --git a/nixos/README.md b/nixos/README.md
index 07e82bf0ad938..5f751e10e20a0 100644
--- a/nixos/README.md
+++ b/nixos/README.md
@@ -48,7 +48,7 @@ Reviewing process:
   - Description, default and example should be provided.
 - Ensure that option changes are backward compatible.
   - `mkRenamedOptionModuleWith` provides a way to make renamed option backward compatible.
-  - Use `lib.versionAtLeast config.system.stateVersion "23.11"` on backward incompatible changes which may corrupt, change or update the state stored on existing setups.
+  - Use `lib.versionAtLeast config.system.stateVersion "24.05"` on backward incompatible changes which may corrupt, change or update the state stored on existing setups.
 - Ensure that removed options are declared with `mkRemovedOptionModule`.
 - Ensure that changes that are not backward compatible are mentioned in release notes.
 - Ensure that documentations affected by the change is updated.
diff --git a/nixos/doc/manual/configuration/abstractions.section.md b/nixos/doc/manual/configuration/abstractions.section.md
index 06356c472ba94..3ff8ac885b653 100644
--- a/nixos/doc/manual/configuration/abstractions.section.md
+++ b/nixos/doc/manual/configuration/abstractions.section.md
@@ -10,14 +10,12 @@ If you find yourself repeating yourself over and over, it’s time to abstract.
         adminAddr = "alice@example.org";
         forceSSL = true;
         enableACME = true;
-        enablePHP = true;
       };
       "wiki.example.org" = {
         documentRoot = "/webroot/wiki.example.org";
         adminAddr = "alice@example.org";
         forceSSL = true;
         enableACME = true;
-        enablePHP = true;
       };
     };
 }
diff --git a/nixos/doc/manual/configuration/gpu-accel.chapter.md b/nixos/doc/manual/configuration/gpu-accel.chapter.md
index 3b98bdd97c681..8afa2807b7b60 100644
--- a/nixos/doc/manual/configuration/gpu-accel.chapter.md
+++ b/nixos/doc/manual/configuration/gpu-accel.chapter.md
@@ -30,7 +30,7 @@ $ export \
 ```
 
 The second mechanism is to add the OpenCL driver package to
-[](#opt-hardware.opengl.extraPackages).
+[](#opt-hardware.graphics.extraPackages).
 This links the ICD file under `/run/opengl-driver`, where it will be visible
 to the ICD loader.
 
@@ -51,12 +51,12 @@ Platform Vendor      Advanced Micro Devices, Inc.
 Modern AMD [Graphics Core
 Next](https://en.wikipedia.org/wiki/Graphics_Core_Next) (GCN) GPUs are
 supported through the rocmPackages.clr.icd package. Adding this package to
-[](#opt-hardware.opengl.extraPackages)
+[](#opt-hardware.graphics.extraPackages)
 enables OpenCL support:
 
 ```nix
 {
-  hardware.opengl.extraPackages = [
+  hardware.graphics.extraPackages = [
     rocmPackages.clr.icd
   ];
 }
@@ -71,13 +71,13 @@ intel-compute-runtime package. The proprietary Intel OpenCL runtime, in
 the intel-ocl package, is an alternative for Gen7 GPUs.
 
 The intel-compute-runtime or intel-ocl package can be added to
-[](#opt-hardware.opengl.extraPackages)
+[](#opt-hardware.graphics.extraPackages)
 to enable OpenCL support. For example, for Gen8 and later GPUs, the following
 configuration can be used:
 
 ```nix
 {
-  hardware.opengl.extraPackages = [
+  hardware.graphics.extraPackages = [
     intel-compute-runtime
   ];
 }
@@ -90,8 +90,8 @@ compute API for GPUs. It is used directly by games or indirectly though
 compatibility layers like
 [DXVK](https://github.com/doitsujin/dxvk/wiki).
 
-By default, if [](#opt-hardware.opengl.driSupport)
-is enabled, mesa is installed and provides Vulkan for supported hardware.
+By default, if [](#opt-hardware.graphics.enable)
+is enabled, Mesa is installed and provides Vulkan for supported hardware.
 
 Similar to OpenCL, Vulkan drivers are loaded through the *Installable
 Client Driver* (ICD) mechanism. ICD files for Vulkan are JSON files that
@@ -110,7 +110,7 @@ $ export \
 ```
 
 The second mechanism is to add the Vulkan driver package to
-[](#opt-hardware.opengl.extraPackages).
+[](#opt-hardware.graphics.extraPackages).
 This links the ICD file under `/run/opengl-driver`, where it will be
 visible to the ICD loader.
 
@@ -140,18 +140,18 @@ Modern AMD [Graphics Core
 Next](https://en.wikipedia.org/wiki/Graphics_Core_Next) (GCN) GPUs are
 supported through either radv, which is part of mesa, or the amdvlk
 package. Adding the amdvlk package to
-[](#opt-hardware.opengl.extraPackages)
+[](#opt-hardware.graphics.extraPackages)
 makes amdvlk the default driver and hides radv and lavapipe from the device list.
 A specific driver can be forced as follows:
 
 ```nix
 {
-  hardware.opengl.extraPackages = [
+  hardware.graphics.extraPackages = [
     pkgs.amdvlk
   ];
 
   # To enable Vulkan support for 32-bit applications, also add:
-  hardware.opengl.extraPackages32 = [
+  hardware.graphics.extraPackages32 = [
     pkgs.driversi686Linux.amdvlk
   ];
 
@@ -171,7 +171,7 @@ graphics hardware acceleration capabilities for video processing.
 
 VA-API drivers are loaded by `libva`. The version in nixpkgs is built to search
 the opengl driver path, so drivers can be installed in
-[](#opt-hardware.opengl.extraPackages).
+[](#opt-hardware.graphics.extraPackages).
 
 VA-API can be tested using:
 
@@ -185,7 +185,7 @@ Modern Intel GPUs use the iHD driver, which can be installed with:
 
 ```nix
 {
-  hardware.opengl.extraPackages = [
+  hardware.graphics.extraPackages = [
     intel-media-driver
   ];
 }
@@ -195,7 +195,7 @@ Older Intel GPUs use the i965 driver, which can be installed with:
 
 ```nix
 {
-  hardware.opengl.extraPackages = [
+  hardware.graphics.extraPackages = [
     intel-vaapi-driver
   ];
 }
diff --git a/nixos/doc/manual/configuration/x-windows.chapter.md b/nixos/doc/manual/configuration/x-windows.chapter.md
index 31752330dd9a8..362ae5d7e5df1 100644
--- a/nixos/doc/manual/configuration/x-windows.chapter.md
+++ b/nixos/doc/manual/configuration/x-windows.chapter.md
@@ -79,7 +79,7 @@ Wine, you should also set the following:
 
 ```nix
 {
-  hardware.opengl.driSupport32Bit = true;
+  hardware.graphics.enable32Bit = true;
 }
 ```
 
@@ -183,23 +183,6 @@ If you have an older card, you may have to use one of the legacy drivers:
 You may need to reboot after enabling this driver to prevent a clash
 with other kernel modules.
 
-## Proprietary AMD drivers {#sec-x11--graphics-cards-amd}
-
-AMD provides a proprietary driver for its graphics cards that is not
-enabled by default because it's not Free Software, is often broken in
-nixpkgs and as of this writing doesn't offer more features or
-performance. If you still want to use it anyway, you need to explicitly
-set:
-
-```nix
-{
-  services.xserver.videoDrivers = [ "amdgpu-pro" ];
-}
-```
-
-You will need to reboot after enabling this driver to prevent a clash
-with other kernel modules.
-
 ## Touchpads {#sec-x11-touchpads}
 
 Support for Synaptics touchpads (found in many laptops such as the Dell
diff --git a/nixos/doc/manual/development/bootspec.chapter.md b/nixos/doc/manual/development/bootspec.chapter.md
index 96c12f24e7f1f..eb307c43088ed 100644
--- a/nixos/doc/manual/development/bootspec.chapter.md
+++ b/nixos/doc/manual/development/bootspec.chapter.md
@@ -1,17 +1,17 @@
-# Experimental feature: Bootspec {#sec-experimental-bootspec}
+# Bootspec {#sec-bootspec}
 
-Bootspec is a experimental feature, introduced in the [RFC-0125 proposal](https://github.com/NixOS/rfcs/pull/125), the reference implementation can be found [there](https://github.com/NixOS/nixpkgs/pull/172237) in order to standardize bootloader support
-and advanced boot workflows such as SecureBoot and potentially more.
+Bootspec is a feature introduced in [RFC-0125](https://github.com/NixOS/rfcs/pull/125) in order to standardize bootloader support and advanced boot workflows such as SecureBoot and potentially more.
+The reference implementation can be found [here](https://github.com/NixOS/nixpkgs/pull/172237).
 
-You can enable the creation of bootspec documents through [`boot.bootspec.enable = true`](options.html#opt-boot.bootspec.enable), which will prompt a warning until [RFC-0125](https://github.com/NixOS/rfcs/pull/125) is officially merged.
+The creation of bootspec documents is enabled by default.
 
-## Schema {#sec-experimental-bootspec-schema}
+## Schema {#sec-bootspec-schema}
 
 The bootspec schema is versioned and validated against [a CUE schema file](https://cuelang.org/) which should considered as the source of truth for your applications.
 
 You will find the current version [here](../../../modules/system/activation/bootspec.cue).
 
-## Extensions mechanism {#sec-experimental-bootspec-extensions}
+## Extensions mechanism {#sec-bootspec-extensions}
 
 Bootspec cannot account for all usecases.
 
@@ -29,8 +29,9 @@ An example for SecureBoot is to get the Nix store path to `/etc/os-release` in o
 
 To reduce incompatibility and prevent names from clashing between applications, it is **highly recommended** to use a unique namespace for your extensions.
 
-## External bootloaders {#sec-experimental-bootspec-external-bootloaders}
+## External bootloaders {#sec-bootspec-external-bootloaders}
 
 It is possible to enable your own bootloader through [`boot.loader.external.installHook`](options.html#opt-boot.loader.external.installHook) which can wrap an existing bootloader.
 
-Currently, there is no good story to compose existing bootloaders to enrich their features, e.g. SecureBoot, etc. It will be necessary to reimplement or reuse existing parts.
+Currently, there is no good story to compose existing bootloaders to enrich their features, e.g. SecureBoot, etc.
+It will be necessary to reimplement or reuse existing parts.
diff --git a/nixos/doc/manual/development/option-declarations.section.md b/nixos/doc/manual/development/option-declarations.section.md
index 325f4d11cb083..ee4540d0cf6fd 100644
--- a/nixos/doc/manual/development/option-declarations.section.md
+++ b/nixos/doc/manual/development/option-declarations.section.md
@@ -173,7 +173,7 @@ lib.mkOption {
 
 ## Extensible Option Types {#sec-option-declarations-eot}
 
-Extensible option types is a feature that allow to extend certain types
+Extensible option types is a feature that allows to extend certain types
 declaration through multiple module files. This feature only work with a
 restricted set of types, namely `enum` and `submodules` and any composed
 forms of them.
diff --git a/nixos/doc/manual/development/running-nixos-tests-interactively.section.md b/nixos/doc/manual/development/running-nixos-tests-interactively.section.md
index 4b8385d7e0d94..422dbf174ad56 100644
--- a/nixos/doc/manual/development/running-nixos-tests-interactively.section.md
+++ b/nixos/doc/manual/development/running-nixos-tests-interactively.section.md
@@ -44,7 +44,7 @@ As an alternative, you can proxy the guest shell to a local TCP server by first
 starting a TCP server in a terminal using the command:
 
 ```ShellSession
-$ socat 'READLINE,PROMPT=$ ' tcp-listen:4444,reuseaddr`
+$ socat 'READLINE,PROMPT=$ ' tcp-listen:4444,reuseaddr
 ```
 
 In the terminal where the test driver is running, connect to this server by
diff --git a/nixos/doc/manual/development/settings-options.section.md b/nixos/doc/manual/development/settings-options.section.md
index 806eee5637907..cedc82d32f89a 100644
--- a/nixos/doc/manual/development/settings-options.section.md
+++ b/nixos/doc/manual/development/settings-options.section.md
@@ -146,6 +146,27 @@ have a predefined type and string generator already declared under
     :   Outputs the given attribute set as an Elixir map, instead of the
         default Elixir keyword list
 
+`pkgs.formats.php { finalVariable }` []{#pkgs-formats-php}
+
+:   A function taking an attribute set with values
+
+    `finalVariable`
+
+    :   The variable that will store generated expression (usually `config`). If set to `null`, generated expression will contain `return`.
+
+    It returns a set with PHP-Config-specific attributes `type`, `lib`, and
+    `generate` as specified [below](#pkgs-formats-result).
+
+    The `lib` attribute contains functions to be used in settings, for
+    generating special PHP values:
+
+    `mkRaw phpCode`
+
+    :   Outputs the given string as raw PHP code
+
+    `mkMixedArray list set`
+
+    :   Creates PHP array that contains both indexed and associative values. For example, `lib.mkMixedArray [ "hello" "world" ] { "nix" = "is-great"; }` returns `['hello', 'world', 'nix' => 'is-great']`
 
 []{#pkgs-formats-result}
 These functions all return an attribute set with these values:
diff --git a/nixos/doc/manual/installation/installing-from-other-distro.section.md b/nixos/doc/manual/installation/installing-from-other-distro.section.md
index 10ac2be4e161f..38f0e5301472b 100644
--- a/nixos/doc/manual/installation/installing-from-other-distro.section.md
+++ b/nixos/doc/manual/installation/installing-from-other-distro.section.md
@@ -42,9 +42,11 @@ The first steps to all these are the same:
     will be safer to use the `nixos-*` channels instead:
 
     ```ShellSession
-    $ nix-channel --add https://nixos.org/channels/nixos-version nixpkgs
+    $ nix-channel --add https://nixos.org/channels/nixos-<version> nixpkgs
     ```
 
+    Where `<version>` corresponds to the latest version available on [channels.nixos.org](https://channels.nixos.org/).
+
     You may want to throw in a `nix-channel --update` for good measure.
 
 1.  Install the NixOS installation tools:
diff --git a/nixos/doc/manual/installation/installing-virtualbox-guest.section.md b/nixos/doc/manual/installation/installing-virtualbox-guest.section.md
index 4b9ae0a9c55f0..415119bd8c898 100644
--- a/nixos/doc/manual/installation/installing-virtualbox-guest.section.md
+++ b/nixos/doc/manual/installation/installing-virtualbox-guest.section.md
@@ -3,8 +3,8 @@
 Installing NixOS into a VirtualBox guest is convenient for users who
 want to try NixOS without installing it on bare metal. If you want to
 use a pre-made VirtualBox appliance, it is available at [the downloads
-page](https://nixos.org/nixos/download.html). If you want to set up a
-VirtualBox guest manually, follow these instructions:
+page](https://nixos.org/download/#nixos-virtualbox). If you want to set
+up a VirtualBox guest manually, follow these instructions:
 
 1.  Add a New Machine in VirtualBox with OS Type "Linux / Other Linux"
 
diff --git a/nixos/doc/manual/installation/upgrading.chapter.md b/nixos/doc/manual/installation/upgrading.chapter.md
index 09338bf8723d2..1a3fc10ec8005 100644
--- a/nixos/doc/manual/installation/upgrading.chapter.md
+++ b/nixos/doc/manual/installation/upgrading.chapter.md
@@ -6,7 +6,7 @@ expressions and associated binaries. The NixOS channels are updated
 automatically from NixOS's Git repository after certain tests have
 passed and all packages have been built. These channels are:
 
--   *Stable channels*, such as [`nixos-23.11`](https://channels.nixos.org/nixos-23.11).
+-   *Stable channels*, such as [`nixos-24.05`](https://channels.nixos.org/nixos-24.05).
     These only get conservative bug fixes and package upgrades. For
     instance, a channel update may cause the Linux kernel on your system
     to be upgraded from 4.19.34 to 4.19.38 (a minor bug fix), but not
@@ -19,7 +19,7 @@ passed and all packages have been built. These channels are:
     radical changes between channel updates. It's not recommended for
     production systems.
 
--   *Small channels*, such as [`nixos-23.11-small`](https://channels.nixos.org/nixos-23.11-small)
+-   *Small channels*, such as [`nixos-24.05-small`](https://channels.nixos.org/nixos-24.05-small)
     or [`nixos-unstable-small`](https://channels.nixos.org/nixos-unstable-small).
     These are identical to the stable and unstable channels described above,
     except that they contain fewer binary packages. This means they get updated
@@ -33,13 +33,13 @@ To see what channels are available, go to <https://channels.nixos.org>.
 contains the channel's latest version and includes ISO images and
 VirtualBox appliances.) Please note that during the release process,
 channels that are not yet released will be present here as well. See the
-Getting NixOS page <https://nixos.org/nixos/download.html> to find the
-newest supported stable release.
+Getting NixOS page <https://nixos.org/download/> to find the newest
+supported stable release.
 
 When you first install NixOS, you're automatically subscribed to the
 NixOS channel that corresponds to your installation source. For
-instance, if you installed from a 23.11 ISO, you will be subscribed to
-the `nixos-23.11` channel. To see which NixOS channel you're subscribed
+instance, if you installed from a 24.05 ISO, you will be subscribed to
+the `nixos-24.05` channel. To see which NixOS channel you're subscribed
 to, run the following as root:
 
 ```ShellSession
@@ -54,16 +54,16 @@ To switch to a different NixOS channel, do
 ```
 
 (Be sure to include the `nixos` parameter at the end.) For instance, to
-use the NixOS 23.11 stable channel:
+use the NixOS 24.05 stable channel:
 
 ```ShellSession
-# nix-channel --add https://channels.nixos.org/nixos-23.11 nixos
+# nix-channel --add https://channels.nixos.org/nixos-24.05 nixos
 ```
 
 If you have a server, you may want to use the "small" channel instead:
 
 ```ShellSession
-# nix-channel --add https://channels.nixos.org/nixos-23.11-small nixos
+# nix-channel --add https://channels.nixos.org/nixos-24.05-small nixos
 ```
 
 And if you want to live on the bleeding edge:
@@ -117,6 +117,6 @@ modules. You can also specify a channel explicitly, e.g.
 
 ```nix
 {
-  system.autoUpgrade.channel = "https://channels.nixos.org/nixos-23.11";
+  system.autoUpgrade.channel = "https://channels.nixos.org/nixos-24.05";
 }
 ```
diff --git a/nixos/doc/manual/release-notes/release-notes.md b/nixos/doc/manual/release-notes/release-notes.md
index 0514a1b0044af..24494ed95ca88 100644
--- a/nixos/doc/manual/release-notes/release-notes.md
+++ b/nixos/doc/manual/release-notes/release-notes.md
@@ -3,6 +3,7 @@
 This section lists the release notes for each stable version of NixOS and current unstable revision.
 
 ```{=include=} sections
+rl-2411.section.md
 rl-2405.section.md
 rl-2311.section.md
 rl-2305.section.md
diff --git a/nixos/doc/manual/release-notes/rl-2111.section.md b/nixos/doc/manual/release-notes/rl-2111.section.md
index 8edf4fd35e4fb..4143f440f2890 100644
--- a/nixos/doc/manual/release-notes/rl-2111.section.md
+++ b/nixos/doc/manual/release-notes/rl-2111.section.md
@@ -146,7 +146,7 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - [touchegg](https://github.com/JoseExposito/touchegg), a multi-touch gesture recognizer. Available as [services.touchegg](#opt-services.touchegg.enable).
 
-- [pantheon-tweaks](https://github.com/pantheon-tweaks/pantheon-tweaks), an unofficial system settings panel for Pantheon. Available as [programs.pantheon-tweaks](#opt-programs.pantheon-tweaks.enable).
+- [pantheon-tweaks](https://github.com/pantheon-tweaks/pantheon-tweaks), an unofficial system settings panel for Pantheon. Available as `programs.pantheon-tweaks`.
 
 - [joycond](https://github.com/DanielOgorchock/joycond), a service that uses `hid-nintendo` to provide nintendo joycond pairing and better nintendo switch pro controller support.
 
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index 3a2c70fb7a31b..dad45f12373e6 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -453,7 +453,7 @@ In addition to numerous new and upgraded packages, this release has the followin
 
     The new names are as follows:
     - `bindAddress`: [`services.keycloak.settings.http-host`](#opt-services.keycloak.settings.http-host)
-    - `forceBackendUrlToFrontendUrl`: [`services.keycloak.settings.hostname-strict-backchannel`](#opt-services.keycloak.settings.hostname-strict-backchannel)
+    - `forceBackendUrlToFrontendUrl`: `services.keycloak.settings.hostname-strict-backchannel`
     - `httpPort`: [`services.keycloak.settings.http-port`](#opt-services.keycloak.settings.http-port)
     - `httpsPort`: [`services.keycloak.settings.https-port`](#opt-services.keycloak.settings.https-port)
 
diff --git a/nixos/doc/manual/release-notes/rl-2211.section.md b/nixos/doc/manual/release-notes/rl-2211.section.md
index 77cb6c9baadbc..4630b0987436c 100644
--- a/nixos/doc/manual/release-notes/rl-2211.section.md
+++ b/nixos/doc/manual/release-notes/rl-2211.section.md
@@ -366,7 +366,7 @@ In addition to numerous new and upgraded packages, this release includes the fol
       __Note:__ secrets from these files will be leaked into the store unless you use a
       [**file**-provider or env-var](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider) for secrets!
 
-    - [services.grafana.provision.notifiers](#opt-services.grafana.provision.notifiers) is not affected by this change because
+    - `services.grafana.provision.notifiers` is not affected by this change because
       this feature is deprecated by Grafana and will probably be removed in Grafana 10.
       It's recommended to use `services.grafana.provision.alerting.contactPoints` instead.
 
diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md
index cd2393514be8c..b1b18b35e9c28 100644
--- a/nixos/doc/manual/release-notes/rl-2405.section.md
+++ b/nixos/doc/manual/release-notes/rl-2405.section.md
@@ -1,4 +1,4 @@
-# Release 24.05 (“Uakari”, 2024.05/??) {#sec-release-24.05}
+# Release 24.05 (“Uakari”, 2024.05/31) {#sec-release-24.05}
 
 Support is planned until the end of December 2024, handing over to 24.11.
 
@@ -6,56 +6,29 @@ Support is planned until the end of December 2024, handing over to 24.11.
 
 In addition to numerous new and upgraded packages, this release has the following highlights:
 
-<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
-
-- `cryptsetup` has been upgraded from 2.6.1 to 2.7.0. Cryptsetup is a critical component enabling LUKS-based (but not only) full disk encryption.
-  Take the time to review [the release notes](https://gitlab.com/cryptsetup/cryptsetup/-/raw/v2.7.0/docs/v2.7.0-ReleaseNotes).
-  One of the highlights is that it is now possible to use hardware OPAL-based encryption of your disk with `cryptsetup`. It has a lot of caveats, see the above notes for the full details.
+<!-- Please keep entries alphabetically sorted. -->
 
-- `screen`'s module has been cleaned, and will now require you to set `programs.screen.enable` in order to populate `screenrc` and add the program to the environment.
-
-- `linuxPackages_testing_bcachefs` is now fully deprecated by `linuxPackages_latest`, and is therefore no longer available.
+- The default kernel package has been updated from 6.1 to 6.6. All supported kernels remain available.
 
-- (TODO not sure what path to use here) The default kernel package has been updated from 6.1 to 6.6. All supported kernels remain available.
+- For each supporting version of the Linux kernel, firmware blobs
+  are compressed with zstd. For firmware blobs this means an increase of 4.4% in size, however
+  a significantly higher decompression speed.
 
 - NixOS now installs a stub ELF loader that prints an informative error message when users attempt to run binaries not made for NixOS.
-   - This can be disabled through the `environment.stub-ld.enable` option.
-   - If you use `programs.nix-ld.enable`, no changes are needed. The stub will be disabled automatically.
+  - This can be disabled through the `environment.stub-ld.enable` option.
+  - If you use `programs.nix-ld.enable`, no changes are needed. The stub will be disabled automatically.
 
 - On flake-based NixOS configurations using `nixpkgs.lib.nixosSystem`, NixOS will automatically set `NIX_PATH` and the system-wide flake registry (`/etc/nix/registry.json`) to point `<nixpkgs>` and the unqualified flake path `nixpkgs` to the version of nixpkgs used to build the system.
 
   This makes `nix run nixpkgs#hello` and `nix-build '<nixpkgs>' -A hello` work out of the box with no added configuration, reusing dependencies already on the system.
 
-  This may be undesirable if nix commands are not going to be run on the built system since it adds nixpkgs to the system closure. For such closure-size-constrained non-interactive systems, this setting should be disabled.
-
-  To disable this, set [nixpkgs.flake.setNixPath](#opt-nixpkgs.flake.setNixPath) and [nixpkgs.flake.setFlakeRegistry](#opt-nixpkgs.flake.setFlakeRegistry) to false.
-
-- `nixVersions.unstable` was removed. Instead the following attributes are provided:
-  - `nixVersions.git` which tracks the latest Nix master and is roughly updated once a week. This is intended to enable people to easily test unreleased changes of Nix to catch regressions earlier.
-  - `nixVersions.latest` which points to the latest Nix version packaged in nixpkgs.
-
-- `julia` environments can now be built with arbitrary packages from the ecosystem using the `.withPackages` function. For example: `julia.withPackages ["Plots"]`.
-
-- `pipewire` and `wireplumber` modules have removed support for using
-`environment.etc."pipewire/..."` and `environment.etc."wireplumber/..."`.
-Use `services.pipewire.extraConfig` or `services.pipewire.configPackages` for PipeWire and
-`services.pipewire.wireplumber.configPackages` for WirePlumber instead."
-
-- `teleport` has been upgraded from major version 14 to major version 15.
-  Refer to upstream [upgrade instructions](https://goteleport.com/docs/management/operations/upgrading/)
-  and release notes for [v15](https://goteleport.com/docs/changelog/#1500-013124).
-
-- `systemd.sysusers.enable` option was added. If enabled, users and
-  groups are created with systemd-sysusers instead of with a custom perl script.
+  This may be undesirable if Nix commands are not going to be run on the built system since it adds nixpkgs to the system closure. For such closure-size-constrained non-interactive systems, this setting should be disabled.
 
-- `virtualisation.docker.enableNvidia` and `virtualisation.podman.enableNvidia` options are deprecated. `hardware.nvidia-container-toolkit.enable` should be used instead. This option will expose GPUs on containers with the `--device` CLI option. This is supported by Docker 25, Podman 3.2.0 and Singularity 4. Any container runtime that supports the CDI specification will take advantage of this feature.
-
-- `system.etc.overlay.enable` option was added. If enabled, `/etc` is
-  mounted via an overlayfs instead of being created by a custom perl script.
+  To disable it, set [nixpkgs.flake.setNixPath](#opt-nixpkgs.flake.setNixPath) and [nixpkgs.flake.setFlakeRegistry](#opt-nixpkgs.flake.setFlakeRegistry) to false.
 
 - NixOS AMIs are now uploaded regularly to a new AWS Account.
   Instructions on how to use them can be found on <https://nixos.github.io/amis>.
-  We are working on integration the data into the NixOS homepage.
+  We are working on integrating the data into the NixOS homepage.
   The list in `nixos/modules/virtualisation/amazon-ec2-amis.nix` will stop
   being updated and will be removed in the future.
 
@@ -69,269 +42,471 @@ Use `services.pipewire.extraConfig` or `services.pipewire.configPackages` for Pi
   }
   ```
 
-- `virtialisation.incus` now defaults to the newly-added `incus-lts` release (v6.0.x). Users who wish to continue using the non-LTS release will need to set `virtualisation.incus.package = pkgs.incus`. Stable release users are encouraged to stay on the LTS release as non-LTS releases will by default not be backported.
+- Cinnamon has been updated to 6.0. Please be aware that the [Wayland session](https://blog.linuxmint.com/?p=4591) is still experimental in this release and could potentially [affect Xorg sessions](https://blog.linuxmint.com/?p=4639). We suggest a reboot when switching between sessions.
 
-- Canonical `lxd` has been upgraded to v5.21.x, an LTS release. The LTS release is now the only supported LXD release. Users are encouraged to [migrate to Incus](https://linuxcontainers.org/incus/docs/main/howto/server_migrate_lxd/) for better support on NixOS.
+- GNOME has been updated to 46 "Kathmandu". Refer to the [release notes](https://release.gnome.org/46/) for more details. Notably this release brings experimental VRR support, default GTK renderer changes and WebDAV support in Online Accounts. This release we have also stopped including the legacy and unsupported Adwaita-Dark theme by default.
 
-- `lua` interpreters default LUA_PATH and LUA_CPATH are not overriden by nixpkgs
-  anymore, we patch LUA_ROOT instead which is more respectful to upstream.
+- Lomiri (formerly known as Unity8) desktop mode, using Mir 2.x to function as a Wayland compositor, is now available and can be installed with `services.desktopManager.lomiri.enable = true`. Note that some core applications, services and indicators have yet to be packaged, and some functions may remain incomplete, but the base experience should be there.
 
-- `plasma6` is now available and can be installed with `services.xserver.desktopManager.plasma6.enable = true;`. Plasma 5 will likely be deprecated in the next release (24.11). Note that Plasma 6 runs as Wayland by default, and the X11 session needs to be explicitly selected if necessary.
+- LXQt has been updated to 2.0, which is based on Qt 6 and features Wayland support for many applications.
 
-- `lomiri` (formerly known as Unity8) desktop mode, using Mir 2.x to function as a Wayland compositor, is now available and can be installed with `services.desktopManager.lomiri.enable = true`. Note that some core applications, services and indicators have yet to be packaged, and some functions may remain incomplete, but the base experience should be there.
+- MATE has been updated to 1.28.
+  - To properly support panel plugins built with Wayland (in-process) support, we are introducing the `services.xserver.desktopManager.mate.extraPanelApplets` option, please use that for installing panel applets.
+  - Similarly, please use the `services.xserver.desktopManager.mate.extraCajaExtensions` option for installing Caja extensions.
+  - To use the Wayland session, enable `services.xserver.desktopManager.mate.enableWaylandSession`. This is opt-in for now as it is in early an stage and introduces a new set of Wayfire closures. Due to [known issues with LightDM](https://github.com/canonical/lightdm/issues/63), we suggest using SDDM as the display manager.
 
-## New Services {#sec-release-24.05-new-services}
+- Plasma 6 is now available and can be installed with `services.desktopManager.plasma6.enable = true;`. Plasma 5 will likely be deprecated in the next release (24.11). Note that Plasma 6 runs as Wayland by default, and the X11 session needs to be explicitly selected if necessary.
 
-<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
+## New Services {#sec-release-24.05-new-services}
 
-- [ownCloud Infinite Scale Stack](https://owncloud.com/infinite-scale-4-0/), a modern and scalable rewrite of ownCloud.
+<!-- Please keep entries alphabetically sorted. -->
 
-- [Handheld Daemon](https://github.com/hhd-dev/hhd), support for gaming handhelds like the Legion Go, ROG Ally, and GPD Win. Available as [services.handheld-daemon](#opt-services.handheld-daemon.enable).
+- [Anki Sync Server](https://docs.ankiweb.net/sync-server.html), the official sync server built into recent versions of Anki. Available as [services.anki-sync-server](#opt-services.anki-sync-server.enable).
+The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been marked deprecated and will be dropped after 24.05 due to lack of maintenance of the ankisyncd software.
 
-- [Guix](https://guix.gnu.org), a functional package manager inspired by Nix. Available as [services.guix](#opt-services.guix.enable).
+- [ALVR](https://github.com/alvr-org/alvr), a VR desktop streamer. Available as [programs.alvr](#opt-programs.alvr.enable).
 
-- [PhotonVision](https://photonvision.org/), a free, fast, and easy-to-use computer vision solution for the FIRST® Robotics Competition.
+- [AppImage](https://appimage.org/), a tool to package desktop applications, now has a `binfmt` option to support running AppImages seamlessly on NixOS. Available as [programs.appimage.binfmt](#opt-programs.appimage.binfmt).
 
-- [clatd](https://github.com/toreanderson/clatd), a a CLAT / SIIT-DC Edge Relay implementation for Linux.
+- [armagetronad](https://wiki.armagetronad.org), a mid-2000s 3D lightcycle game widely played at iD Tech Camps. You can define multiple servers using `services.armagetronad.<server>.enable`.
 
-- [pyLoad](https://pyload.net/), a FOSS download manager written in Python. Available as [services.pyload](#opt-services.pyload.enable)
+- [BenchExec](https://github.com/sosy-lab/benchexec), a framework for reliable benchmarking and resource measurement, available as [programs.benchexec](#opt-programs.benchexec.enable),
+  as well as related programs
+  [CPU Energy Meter](https://github.com/sosy-lab/cpu-energy-meter), available as [programs.cpu-energy-meter](#opt-programs.cpu-energy-meter.enable), and
+  [PQoS Wrapper](https://gitlab.com/sosy-lab/software/pqos-wrapper), available as [programs.pqos-wrapper](#opt-programs.pqos-wrapper.enable).
 
-- [maubot](https://github.com/maubot/maubot), a plugin-based Matrix bot framework. Available as [services.maubot](#opt-services.maubot.enable).
+- [Bluemap](https://bluemap.bluecolored.de/), a 3D minecraft map renderer. Available as [services.bluemap](#opt-services.bluemap.enable).
 
-- [ryzen-monitor-ng](https://github.com/mann1x/ryzen_monitor_ng), a desktop AMD CPU power monitor and controller, similar to Ryzen Master but for Linux. Available as [programs.ryzen-monitor-ng](#opt-programs.ryzen-monitor-ng.enable)
+- [clatd](https://github.com/toreanderson/clatd), a CLAT / SIIT-DC Edge Relay implementation for Linux.
 
-- [ryzen-smu](https://gitlab.com/leogx9r/ryzen_smu), Linux kernel driver to expose the SMU (System Management Unit) for certain AMD Ryzen Processors. Includes the userspace program `monitor_cpu`. Available at [hardward.cpu.amd.ryzen-smu](#opt-hardware.cpu.amd.ryzen-smu.enable)
+- [Clevis](https://github.com/latchset/clevis), a pluggable framework for automated decryption, used to unlock encrypted devices in initrd. Available as [boot.initrd.clevis.enable](#opt-boot.initrd.clevis.enable).
 
-- `systemd`'s `gateway`, `upload`, and `remote` services, which provide ways of sending journals across the network. Enable using [services.journald.gateway](#opt-services.journald.gateway.enable), [services.journald.upload](#opt-services.journald.upload.enable), and [services.journald.remote](#opt-services.journald.remote.enable).
+- [CommaFeed](https://github.com/Athou/commafeed), a Google Reader-inspired self-hosted RSS reader. Available as [services.commafeed](#opt-services.commafeed.enable).
 
-- [GNS3](https://www.gns3.com/), a network software emulator. Available as [services.gns3-server](#opt-services.gns3-server.enable).
+- [davis](https://github.com/tchapi/davis), a simple CardDav and CalDav server inspired by Baïkal. Available as [services.davis](#opt-services.davis.enable).
 
-- [pretalx](https://github.com/pretalx/pretalx), a conference planning tool. Available as [services.pretalx](#opt-services.pretalx.enable).
+- [db-rest](https://github.com/derhuerst/db-rest), a wrapper around Deutsche Bahn's internal API for public transport data. Available as [services.db-rest](#opt-services.db-rest.enable).
 
 - [dnsproxy](https://github.com/AdguardTeam/dnsproxy), a simple DNS proxy with DoH, DoT, DoQ and DNSCrypt support. Available as [services.dnsproxy](#opt-services.dnsproxy.enable).
 
-- [manticoresearch](https://manticoresearch.com), easy to use open source fast database for search. Available as [services.manticore](#opt-services.manticore.enable).
+- [FCast Receiver](https://fcast.org), an open-source alternative to Chromecast and AirPlay. Available as [programs.fcast-receiver](#opt-programs.fcast-receiver.enable).
 
-- [rspamd-trainer](https://gitlab.com/onlime/rspamd-trainer), script triggered by a helper which reads mails from a specific mail inbox and feeds them into rspamd for spam/ham training.
+- [FileSender](https://filesender.org/), a file sharing software. Available as [services.filesender](#opt-services.filesender.enable).
 
-- [ollama](https://ollama.ai), server for running large language models locally.
+- [Firefly-iii](https://www.firefly-iii.org), a free and open source personal finance manager. Available as [services.firefly-iii](#opt-services.firefly-iii.enable).
 
-- [Mihomo](https://github.com/MetaCubeX/mihomo/tree/Alpha), a rule-based proxy in Go. Available as [services.mihomo.enable](#opt-services.mihomo.enable).
+- [Flarum](https://flarum.org/), a delightfully simple discussion platform for your website. Available as [services.flarum](#opt-services.flarum.enable).
+
+- [fritz-exporter](https://github.com/pdreker/fritz_exporter), a Prometheus exporter for extracting metrics from [FRITZ!](https://avm.de/produkte/) devices. Available as [services.prometheus.exporters.fritz](#opt-services.prometheus.exporters.fritz.enable).
+
+- [GNS3](https://www.gns3.com/), a network software emulator. Available as [services.gns3-server](#opt-services.gns3-server.enable).
+
+- [go-camo](https://github.com/cactus/go-camo), a secure image proxy server. Available as [services.go-camo](#opt-services.go-camo.enable).
+
+- [Guix](https://guix.gnu.org), a functional package manager inspired by Nix. Available as [services.guix](#opt-services.guix.enable).
+
+- [Handheld Daemon](https://github.com/hhd-dev/hhd), support for gaming handhelds like the Legion Go, ROG Ally, and GPD Win. Available as [services.handheld-daemon](#opt-services.handheld-daemon.enable).
 
 - [hebbot](https://github.com/haecker-felix/hebbot), a Matrix bot to generate "This Week in X" like blog posts. Available as [services.hebbot](#opt-services.hebbot.enable).
 
-- [Workout-tracker](https://github.com/jovandeginste/workout-tracker), a workout tracking web application for personal use.
+- [inadyn](https://github.com/troglobit/inadyn), a Dynamic DNS client with built-in support for multiple providers. Available as [services.inadyn](#opt-services.inadyn.enable).
 
-- [Python Matter Server](https://github.com/home-assistant-libs/python-matter-server), a
-  Matter Controller Server exposing websocket connections for use with other services, notably Home Assistant.
-  Available as [services.matter-server](#opt-services.matter-server.enable)
+- [intel-gpu-tools](https://drm.pages.freedesktop.org/igt-gpu-tools), tools for development and testing of the Intel DRM driver. Available as [hardware.intel-gpu-tools](#opt-hardware.intel-gpu-tools.enable).
 
-- [db-rest](https://github.com/derhuerst/db-rest), a wrapper around Deutsche Bahn's internal API for public transport data. Available as [services.db-rest](#opt-services.db-rest.enable).
+- [isolate](https://github.com/ioi/isolate), a sandbox for securely executing untrusted programs. Available as [security.isolate](#opt-security.isolate.enable).
 
-- [Anki Sync Server](https://docs.ankiweb.net/sync-server.html), the official sync server built into recent versions of Anki. Available as [services.anki-sync-server](#opt-services.anki-sync-server.enable).
-The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been marked deprecated and will be dropped after 24.05 due to lack of maintenance of the anki-sync-server software.
+- [Jottacloud Command-line Tool](https://docs.jottacloud.com/en/articles/1436834-jottacloud-command-line-tool), a CLI for the [Jottacloud](https://jottacloud.com/) cloud storage provider. Available as [services.jotta-cli](#opt-services.jotta-cli.enable).
 
-- [mautrix-meta](https://github.com/mautrix/meta), a Matrix <-> Facebook and Matrix <-> Instagram hybrid puppeting/relaybot bridge. Available as services.mautrix-meta
+- [keto](https://www.ory.sh/keto/), a permission & access control server, the first open source implementation of [*Zanzibar: Google's Consistent, Global Authorization System*](https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/).
 
-- [Jottacloud Command-line Tool](https://docs.jottacloud.com/en/articles/1436834-jottacloud-command-line-tool), a CLI for the [Jottacloud](https://jottacloud.com/) cloud storage provider. Available as [user.services.jotta-cli](#opt-user.services.jotta-cli.enable).
+- [manticoresearch](https://manticoresearch.com), easy to use open source fast database for search. Available as [services.manticore](#opt-services.manticore.enable).
 
-- [transfer-sh](https://github.com/dutchcoders/transfer.sh), a tool that supports easy and fast file sharing from the command-line. Available as [services.transfer-sh](#opt-services.transfer-sh.enable).
+- [maubot](https://github.com/maubot/maubot), a plugin-based Matrix bot framework. Available as [services.maubot](#opt-services.maubot.enable).
 
-- [FCast Receiver](https://fcast.org), an open-source alternative to Chromecast and AirPlay. Available as [programs.fcast-receiver](#opt-programs.fcast-receiver.enable).
+- [mautrix-meta](https://github.com/mautrix/meta), a Matrix <-> Facebook and Matrix <-> Instagram hybrid puppeting/relaybot bridge. Available as services.mautrix-meta.
+
+- [mautrix-signal](https://github.com/mautrix/signal), a Matrix-Signal puppeting bridge. Available as [services.mautrix-signal](#opt-services.mautrix-signal.enable).
+
+- [Mealie](https://nightly.mealie.io/), a self-hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in NuxtJS for a pleasant user experience for the whole family. Available as [services.mealie](#opt-services.mealie.enable).
 
 - [MollySocket](https://github.com/mollyim/mollysocket) which allows getting Signal notifications via UnifiedPush.
 
-- [Suwayomi Server](https://github.com/Suwayomi/Suwayomi-Server), a free and open source manga reader server that runs extensions built for [Tachiyomi](https://tachiyomi.org). Available as [services.suwayomi-server](#opt-services.suwayomi-server.enable).
+- [microsocks](https://github.com/rofl0r/microsocks), a tiny, portable SOCKS5 server with very moderate resource usage. Available as [services.microsocks](#opt-services.microsocks.enable).
 
-- [Netbird](https://netbird.io), an open-source VPN management platform, now has a self-hosted management server. Available as [services.netbird.server](#opt-services.netbird.server.enable).
+- [Mihomo](https://github.com/MetaCubeX/mihomo/tree/Alpha), a rule-based proxy in Go. Available as [services.mihomo.enable](#opt-services.mihomo.enable).
 
-- [ping_exporter](https://github.com/czerwonk/ping_exporter), a Prometheus exporter for ICMP echo requests. Available as [services.prometheus.exporters.ping](#opt-services.prometheus.exporters.ping.enable).
+- [Monado](https://monado.freedesktop.org/), an open source XR runtime. Available as [services.monado](#opt-services.monado.enable).
 
-- [Prometheus DNSSEC Exporter](https://github.com/chrj/prometheus-dnssec-exporter), check for validity and expiration in DNSSEC signatures and expose metrics for Prometheus. Available as [services.prometheus.exporters.dnssec](#opt-services.prometheus.exporters.dnssec.enable).
+- [Netbird](https://netbird.io), an open-source VPN management platform, now has a self-hosted management server. Available as [services.netbird.server](#opt-services.netbird.server.enable).
 
-- [TigerBeetle](https://tigerbeetle.com/), a distributed financial accounting database designed for mission critical safety and performance. Available as [services.tigerbeetle](#opt-services.tigerbeetle.enable).
+- [nh](https://github.com/viperML/nh), yet another Nix CLI helper. Available as [programs.nh](#opt-programs.nh.enable).
 
-- [go-camo](https://github.com/cactus/go-camo), a secure image proxy server. Available as [services.go-camo](#opt-services.go-camo.enable).
+- [oink](https://github.com/rlado/oink), a dynamic DNS client for Porkbun. Available as [services.oink](#opt-services.oink.enable).
 
-- [Monado](https://monado.freedesktop.org/), an open source XR runtime. Available as [services.monado](#opt-services.monado.enable).
+- [ollama](https://ollama.ai), server for running large language models locally.
 
-- [Pretix](https://pretix.eu/about/en/), an open source ticketing software for events. Available as [services.pretix](#opt-services.pretix.enable).
+- [nextjs-ollama-llm-ui](https://github.com/jakobhoeg/nextjs-ollama-llm-ui), light-weight frontend server to chat with Ollama models through a web app.
 
-- [microsocks](https://github.com/rofl0r/microsocks), a tiny, portable SOCKS5 server with very moderate resource usage. Available as [services.microsocks](#opt-services.microsocks.enable).
+- [ownCloud Infinite Scale Stack](https://owncloud.com/infinite-scale-4-0/), a modern and scalable rewrite of ownCloud.
 
-- [inadyn](https://github.com/troglobit/inadyn), a Dynamic DNS client with built-in support for multiple providers. Available as [services.inadyn](#opt-services.inadyn.enable).
+- [PhotonVision](https://photonvision.org/), a free, fast, and easy-to-use computer vision solution for the FIRST® Robotics Competition.
 
-- [Clevis](https://github.com/latchset/clevis), a pluggable framework for automated decryption, used to unlock encrypted devices in initrd. Available as [boot.initrd.clevis.enable](#opt-boot.initrd.clevis.enable).
+- [ping_exporter](https://github.com/czerwonk/ping_exporter), a Prometheus exporter for ICMP echo requests. Available as [services.prometheus.exporters.ping](#opt-services.prometheus.exporters.ping.enable).
 
-- [fritz-exporter](https://github.com/pdreker/fritz_exporter), a Prometheus exporter for extracting metrics from [FRITZ!](https://avm.de/produkte/) devices. Available as [services.prometheus.exporters.fritz](#opt-services.prometheus.exporters.fritz.enable).
+- [Pretix](https://pretix.eu/about/en/), an open source ticketing software for events. Available as [services.pretix](#opt-services.pretix.enable).
 
-- [armagetronad](https://wiki.armagetronad.org), a mid-2000s 3D lightcycle game widely played at iD Tech Camps. You can define multiple servers using `services.armagetronad.<server>.enable`.
+- [pretalx](https://github.com/pretalx/pretalx), a conference planning tool. Available as [services.pretalx](#opt-services.pretalx.enable).
 
-- [wyoming-satellite](https://github.com/rhasspy/wyoming-satellite), a voice assistant satellite for Home Assistant using the Wyoming protocol. Available as [services.wyoming.satellite](#opt-services.wyoming.satellite.enable).
+- [private-gpt](https://github.com/zylon-ai/private-gpt), a service to interact with your documents using the power of LLMs, 100% privately, no data leaks. Available as [services.private-gpt](#opt-services.private-gpt.enable).
 
-- [TuxClocker](https://github.com/Lurkki14/tuxclocker), a hardware control and monitoring program. Available as [programs.tuxclocker](#opt-programs.tuxclocker.enable).
+- [Prometheus DNSSEC Exporter](https://github.com/chrj/prometheus-dnssec-exporter): check for validity and expiration in DNSSEC signatures and expose metrics for Prometheus. Available as [services.prometheus.exporters.dnssec](#opt-services.prometheus.exporters.dnssec.enable).
 
-- [AppImage](https://appimage.org/), a tool to package desktop applications, now has a `binfmt` option to support running AppImages seamlessly on NixOS. Available as [programs.appimage.binfmt](#opt-programs.appimage.binfmt).
+- [prometheus-nats-exporter](https://github.com/nats-io/prometheus-nats-exporter), a Prometheus exporter for NATS. Available as [services.prometheus.exporters.nats](#opt-services.prometheus.exporters.nats.enable).
 
-- [nh](https://github.com/viperML/nh), yet another Nix CLI helper. Available as [programs.nh](#opt-programs.nh.enable).
+- [pyLoad](https://pyload.net/), a FOSS download manager written in Python. Available as [services.pyload](#opt-services.pyload.enable).
 
-- [ALVR](https://github.com/alvr-org/alvr), a VR desktop streamer. Available as [programs.alvr](#opt-programs.alvr.enable)
+- [Python Matter Server](https://github.com/home-assistant-libs/python-matter-server), a
+  Matter Controller Server exposing websocket connections for use with other services, notably Home Assistant.
+  Available as [services.matter-server](#opt-services.matter-server.enable).
 
 - [RustDesk](https://rustdesk.com), a full-featured open source remote control alternative for self-hosting and security with minimal configuration. Alternative to TeamViewer. Available as [services.rustdesk-server](#opt-services.rustdesk-server.enable).
 
+- [ryzen-monitor-ng](https://github.com/mann1x/ryzen_monitor_ng), a desktop AMD CPU power monitor and controller, similar to Ryzen Master but for Linux. Available as [programs.ryzen-monitor-ng](#opt-programs.ryzen-monitor-ng.enable).
+
+- [ryzen-smu](https://gitlab.com/leogx9r/ryzen_smu), Linux kernel driver to expose the SMU (System Management Unit) for certain AMD Ryzen Processors. Includes the userspace program `monitor_cpu`. Available at [hardward.cpu.amd.ryzen-smu](#opt-hardware.cpu.amd.ryzen-smu.enable).
+
 - [Scrutiny](https://github.com/AnalogJ/scrutiny), a S.M.A.R.T monitoring tool for hard disks with a web frontend. Available as [services.scrutiny](#opt-services.scrutiny.enable).
 
-- [davis](https://github.com/tchapi/davis), a simple CardDav and CalDav server inspired by Baïkal. Available as [services.davis](#opt-services.davis.enable).
+- [SimpleSAMLphp](https://simplesamlphp.org/), an application written in native PHP that deals with authentication (SQL, .htpasswd, YubiKey, LDAP, PAPI, Radius). Available as [services.simplesamlphp](#opt-services.simplesamlphp).
 
-- [Firefly-iii](https://www.firefly-iii.org), a free and open source personal finance manager. Available as [services.firefly-iii](#opt-services.firefly-iii.enable)
+- `systemd`'s `gateway`, `upload`, and `remote` services, which provide ways of sending journals across the network. Enable using [services.journald.gateway](#opt-services.journald.gateway.enable), [services.journald.upload](#opt-services.journald.upload.enable), and [services.journald.remote](#opt-services.journald.remote.enable).
 
 - [systemd-lock-handler](https://git.sr.ht/~whynothugo/systemd-lock-handler/), a bridge between logind D-Bus events and systemd targets. Available as [services.systemd-lock-handler.enable](#opt-services.systemd-lock-handler.enable).
 
+- [rspamd-trainer](https://gitlab.com/onlime/rspamd-trainer), script triggered by a helper which reads mails from a specific mail inbox and feeds them into rspamd for spam/ham training.
+
+- [Sunshine](https://app.lizardbyte.dev/Sunshine), a self-hosted game stream host for Moonlight. Available as [services.sunshine](#opt-services.sunshine.enable).
+
+- [Suwayomi Server](https://github.com/Suwayomi/Suwayomi-Server), a free and open source manga reader server that runs extensions built for [Tachiyomi](https://tachiyomi.org). Available as [services.suwayomi-server](#opt-services.suwayomi-server.enable).
+
+- [TigerBeetle](https://tigerbeetle.com/), a distributed financial accounting database designed for mission critical safety and performance. Available as [services.tigerbeetle](#opt-services.tigerbeetle.enable).
+
+- [transfer-sh](https://github.com/dutchcoders/transfer.sh), a tool that supports easy and fast file sharing from the command-line. Available as [services.transfer-sh](#opt-services.transfer-sh.enable).
+
+- [TuxClocker](https://github.com/Lurkki14/tuxclocker), a hardware control and monitoring program. Available as [programs.tuxclocker](#opt-programs.tuxclocker.enable).
+
+- [Uni-Sync](https://github.com/EightB1ts/uni-sync), a synchronization tool for Lian Li Uni Controllers. Available as [hardware.uni-sync](#opt-hardware.uni-sync.enable).
+
 - [wastebin](https://github.com/matze/wastebin), a pastebin server written in rust. Available as [services.wastebin](#opt-services.wastebin.enable).
 
-- [Mealie](https://nightly.mealie.io/), a self-hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in NuxtJS for a pleasant user experience for the whole family. Available as [services.mealie](#opt-services.mealie.enable)
+- [watchdogd](https://troglobit.com/projects/watchdogd/), a system and process supervisor using watchdog timers. Available as [services.watchdogd](#opt-services.watchdogd.enable).
 
-- [Sunshine](https://app.lizardbyte.dev/Sunshine), a self-hosted game stream host for Moonlight. Available as [services.sunshine](#opt-services.sunshine.enable).
+- [Workout-tracker](https://github.com/jovandeginste/workout-tracker), a workout tracking web application for personal use.
 
-- [Uni-Sync](https://github.com/EightB1ts/uni-sync), a synchronization tool for Lian Li Uni Controllers. Available as [hardware.uni-sync](#opt-hardware.uni-sync.enable)
+- [wyoming-satellite](https://github.com/rhasspy/wyoming-satellite), a voice assistant satellite for Home Assistant using the Wyoming protocol. Available as [services.wyoming.satellite](#opt-services.wyoming.satellite.enable).
 
-- [prometheus-nats-exporter](https://github.com/nats-io/prometheus-nats-exporter), a Prometheus exporter for NATS. Available as [services.prometheus.exporters.nats](#opt-services.prometheus.exporters.nats.enable).
+- [xdg-terminal-exec](https://github.com/Vladimir-csp/xdg-terminal-exec), the proposed Default Terminal Execution Specification.
 
-- [isolate](https://github.com/ioi/isolate), a sandbox for securely executing untrusted programs. Available as [security.isolate](#opt-security.isolate.enable).
+- Convenience options for `amdgpu`, open source driver for Radeon cards, is now available under `hardware.amdgpu`.
+
+- [ydotool](https://github.com/ReimuNotMoe/ydotool), a generic command-line automation tool now has a module. Available as [programs.ydotool](#opt-programs.ydotool.enable).
+
+- [your_spotify](https://github.com/Yooooomi/your_spotify), a self hosted Spotify tracking dashboard. Available as [services.your_spotify](#opt-services.your_spotify.enable)
+
+- [RKE2](https://github.com/rancher/rke2), also known as RKE Government, is Rancher's next-generation Kubernetes distribution. Available as [services.rke2](#opt-services.rke2.enable).
 
 ## Backward Incompatibilities {#sec-release-24.05-incompatibilities}
 
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
 
-- `k3s`: was updated to version [v1.29](https://github.com/k3s-io/k3s/releases/tag/v1.29.1%2Bk3s2), all previous versions (k3s_1_26, k3s_1_27, k3s_1_28) will be removed. See [changelog and upgrade notes](https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.29.md#urgent-upgrade-notes) for more information.
+- `akkoma` now requires explicitly setting the base URL for uploaded media (`settings."Pleroma.Upload".base_url`), as well as for the media proxy if enabled (`settings."Media"`).
+  This is recommended to be a separate (sub)domain to the one Akkoma is hosted at.
+  See [here](https://meta.akkoma.dev/t/akkoma-stable-2024-03-securer-i-barely-know-her/681#explicit-upload-and-media-proxy-domains-5) for more details.
 
-- `himalaya` was updated to v1.0.0-beta.4, which introduces breaking changes. Check out the [release note](https://github.com/soywod/himalaya/releases/tag/v1.0.0-beta.4) for details.
+- `appimageTools.wrapAppImage` now creates the binary at `$out/bin/${pname}` rather than `$out/bin/${pname}-${version}`, which will break downstream workarounds.
 
-- `security.pam.enableSSHAgentAuth` was replaced by the `sshAgentAuth` attrset, and **only**
-  `authorized_keys` files listed in [`sshAgentAuth.authorizedKeysFiles`] are trusted,
-  defaulting to `/etc/ssh/authorized_keys.d/%u`.
-  ::: {.warning}
-  Users of {manpage}`pam_ssh_agent_auth(8)` must take care that the pubkeys they use (for instance with `sudo`)
-  are listed in [`sshAgentAuth.authorizedKeysFiles`].
-  :::
-  ::: {.note}
-  Previously, all `services.openssh.authorizedKeysFiles` were trusted, including `~/.ssh/authorized_keys`,
-  which results in an **insecure** configuration; see [#31611](https://github.com/NixOS/nixpkgs/issues/31611).
-  :::
+- `azure-cli` now has extension support. For example, to install the `aks-preview` extension, use
 
-[`sshAgentAuth.authorizedKeysFiles`]: #opt-security.pam.sshAgentAuth.authorizedKeysFiles
+  ```nix
+  environment.systemPackages = [
+    (azure-cli.withExtensions [ azure-cli.extensions.aks-preview ])
+  ];
+  ```
+  To make the `azure-cli` immutable and prevent clashes in case `azure-cli` is also installed via other package managers, some configuration files were moved into the derivation.
+  This can be disabled by overriding `withImmutableConfig = false` when building `azure-cli`.
 
-- The `power.ups` module now generates `upsd.conf`, `upsd.users` and `upsmon.conf` automatically from a set of new configuration options. This breaks compatibility with existing `power.ups` setups where these files were created manually. Back up these files before upgrading NixOS.
+- `boot.supportedFilesystems` and `boot.initrd.supportedFilesystems` are now attribute sets instead of lists. Assignment from lists as done previously is still supported, but checking whether a filesystem is enabled must now by done using `supportedFilesystems.fs or false` instead of using `lib.elem "fs" supportedFilesystems` as was done previously.
 
-- `programs.nix-ld.libraries` no longer sets `baseLibraries` via the option's default but in config and now merges any additional libraries with the default ones.
-  This means that `lib.mkForce` must be used to clear the list of default libraries.
+- `buildGoModule` now throws an error when `vendorHash` is not specified. `vendorSha256`, deprecated in Nixpkgs 23.11, is now ignored and is no longer a `vendorHash` alias.
+
+- `chromium` and `ungoogled-chromium` had a long-standing issue regarding Widevine DRM handling in nixpkgs fixed.
+  `chromium` now no longer automatically downloads Widevine when encountering DRM protected content.
+  To be able to play DRM protected content in `chromium`, you now have to explicitly opt-in as originally intended using `chromium.override { enableWideVine = true; }`.
+  This override was added almost 10 years ago.
+
+- `craftos-pc` package has been updated to v2.8, which includes [breaking changes](https://github.com/MCJack123/craftos2/releases/tag/v2.8).
+  - Files are now handled in binary mode; this could break programs with embedded UTF-8 characters.
+  - The ROM was updated to match ComputerCraft version v1.109.2.
+  - The bundled Lua was updated to Lua v5.2, which includes breaking changes. See the [Lua manual](https://www.lua.org/manual/5.2/manual.html#8) for more information.
+  - The WebSocket API [was rewritten](https://github.com/MCJack123/craftos2/issues/337), which introduced breaking changes.
+
+- `cryptsetup` has been upgraded from 2.6.1 to 2.7.0. Cryptsetup is a critical component enabling LUKS-based (but not only) full disk encryption.
+  Take the time to review [the release notes](https://gitlab.com/cryptsetup/cryptsetup/-/raw/v2.7.0/docs/v2.7.0-ReleaseNotes).
+  One of the highlights is that it is now possible to use hardware OPAL-based encryption of your disk with `cryptsetup`. It has a lot of caveats, see the above notes for the full details.
+
+- `crystal` package has been updated to 1.11.x, which has some breaking changes.
+  Refer to crystal's changelog for more information. ([v1.10](https://github.com/crystal-lang/crystal/blob/master/CHANGELOG.md#1100-2023-10-09), [v1.11](https://github.com/crystal-lang/crystal/blob/master/CHANGELOG.md#1110-2024-01-08))
+
+- `cudaPackages` package scope has been updated to `cudaPackages_12`.
 
 - `cudaPackages.autoAddOpenGLRunpathHook` and `cudaPackages.autoAddDriverRunpath` have been deprecated for `pkgs.autoAddDriverRunpath`. Functionality has not changed, but the setuphook has been renamed and moved to the top-level package scope.
 
+- `cudaPackages.cudatoolkit` has been deprecated and replaced with a
+  symlink-based wrapper for the splayed redistributable CUDA packages. The
+  wrapper only includes tools and libraries necessary to build common packages
+  such as tensorflow. The original runfile-based `cudatoolkit` is still
+  available as `cudatoolkit-legacy-runfile`.
+
+- `cudaPackages.nsight_systems` now has most vendored third-party libraries removed, though we now only ship it for `cudaPackages_11_8` and later, due to outdated dependencies. Users comfortable with the vendored dependencies may use `overrideAttrs` to amend the `postPatch` phase and the `meta.broken` correspondingly. Alternatively, one could package the deprecated `boost170` locally, as required for `cudaPackages_11_4.nsight_systems`.
+
 - `cudaPackages.autoFixElfFiles` has been deprecated for `pkgs.autoFixElfFiles`. Functionality has not changed, but the setuphook has been renamed and moved to the top-level package scope.
 
-- `appimageTools.wrapAppImage` now creates the binary at `$out/bin/${pname}` rather than `$out/bin/${pname}-${version}`, which will break downstream workarounds.
+- `davfs2`'s `services.davfs2.extraConfig` setting has been deprecated and converted to the free-form type option named `services.davfs2.settings` according to RFC42.
 
-- `pdns` was updated to version [v4.9.x](https://doc.powerdns.com/authoritative/changelog/4.9.html), which introduces breaking changes. Check out the [Upgrade Notes](https://doc.powerdns.com/authoritative/upgrading.html#to-4-9-0) for details.
+- `dwarf-fortress` has been updated to version 50, which is identical to the version on Steam, but without the paid elements like tilepacks.
+  dfhack and Dwarf Therapist still work, and older versions are still packaged in case you'd like to roll back. Note that DF 50 saves will not be compatible with DF 0.47 and earlier.
+  See [Bay 12 Games](http://www.bay12games.com/dwarves/) for more details on what's new in Dwarf Fortress.
 
-- `unrar` was updated to v7. See [changelog](https://www.rarlab.com/unrar7notes.htm) for more information.
+  - Running an earlier version can be achieved through an override: `dwarf-fortress-packages.dwarf-fortress-full.override { dfVersion = "0.47.5"; }`
+
+  - Ruby plugin support has been disabled in DFHack. Many of the Ruby plugins have been converted to Lua, and support was removed upstream due to frequent crashes.
+
+- `erlang-ls` package no longer ships the `els_dap` binary as of v0.51.0.
+
+- `erlang_node_short_name`, `erlang_node_name`: `port` and `options` configuration parameters are gone, and have been replaced with an `environment` parameter.
+    Use the appropriate [environment variables](https://hexdocs.pm/livebook/readme.html#environment-variables) inside `environment` to configure the service instead.
+
+- `firefox-devedition`, `firefox-beta`, `firefox-esr` executable file names for now match their package names, which is consistent with the `firefox-*-bin` packages. The desktop entries are also updated so that you can have multiple editions of firefox in your app launcher.
+
+- `gauge` now supports installing plugins using Nix. For the old imperative approach, switch to `gauge-unwrapped`.
+  You can load plugins from an existing gauge manifest file using `gauge.fromManifest ./path/to/manifest.json` or
+  specify plugins in Nix using `gauge.withPlugins (p: with p; [ js html-report xml-report ])`.
+
+- `gitea` has been updated to 1.21, which introduces several breaking changes, including:
+  - Custom themes and other assets that were previously stored in `custom/public/*` now belong in `custom/public/assets/*`
+  - New instances of Gitea using MySQL now ignore the `[database].CHARSET` config option and always use the `utf8mb4` charset, existing instances should migrate via the `gitea doctor convert` CLI command.
 
 - `git-town` was updated from version 11 to 13. See the [changelog](https://github.com/git-town/git-town/blob/main/CHANGELOG.md#1300-2024-03-22) for breaking changes.
 
+- `gonic` has been updated to v0.16.4. Config now requires `playlists-path` to be set. See the rest of the [v0.16.0 release notes](https://github.com/sentriz/gonic/releases/tag/v0.16.0) for more details.
+
+- `go-ethereum` has been updated to v1.14.3. Geth v1.14.0 introduced a brand new live-tracing feature,
+  which required a number of breaking internal API changes. If you had your own native tracers implemented before this change,
+  the [changelog](https://github.com/ethereum/go-ethereum/blob/master/core/tracing/CHANGELOG.md) contains the necessary steps needed to update your old code for the new APIs.
+  Geth v1.14.0 drops support for running pre-merge networks ([#29169](https://github.com/ethereum/go-ethereum/pull/29169)).
+  It also stops automatically constructing the pending block ([#28623](https://github.com/ethereum/go-ethereum/pull/28623)),
+  removes support for filtering pending logs, switched to using Go v1.22 by default (#28946), which means we've dropped support for Go v1.20.
+  See [the 1.14.0 release notes](https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0) for more details.
+
+- `grafana-loki` has been updated to 3.0.0, which includes [breaking changes](https://github.com/grafana/loki/releases/tag/v3.0.0).
+
+- `gtest` package has been updated past v1.13.0, which requires C++14 or higher.
+
+- `hare` may now be cross-compiled. For that to work, however, `haredoc` needed to stop being built together with it. Thus, the latter is now its own package with the name of `haredoc`.
+
+- `himalaya` has been updated to v1.0.0-beta.4, which introduces breaking changes. Check out the [release note](https://github.com/soywod/himalaya/releases/tag/v1.0.0-beta.4) for details.
+
+- `halloy` has been updated to 2024.5, which introduced a breaking change by switching the config format from YAML to TOML. See <https://github.com/squidowl/halloy/releases/tag/2024.5> for details.
+
+- `hvm` was updated to version 2.
+
+- `icu` no longer includes `install-sh` and `mkinstalldirs` in the shared folder.
+
+- `idris2` was updated to v0.7.0. This version introduces breaking changes. Check out the [changelog](https://github.com/idris-lang/Idris2/blob/v0.7.0/CHANGELOG.md#v070) for details.
+
+- `inetutils` now has a lower priority to avoid shadowing the commonly-used `util-linux`. If one wishes to restore the default priority, simply use `lib.setPrio 5 inetutils` or override with `meta.priority = 5`.
+
+- `jdt-language-server` package now uses upstream's provided python wrapper instead of our own custom wrapper. This results in the following breaking and notable changes:
+
+  - The main binary for the package is now named `jdtls` instead of `jdt-language-server`, equivalent to what most editors expect the binary to be named.
+
+  - JVM arguments should now be provided with the `--jvm-arg` flag instead of setting `JAVA_OPTS`.
+
+  - The `-data` path is no longer required to run the package, and will be set to point to a folder in `$TMP` if missing.
+
+- `julia` environments can now be built with arbitrary packages from the ecosystem using the `.withPackages` function. For example: `julia.withPackages ["Plots"]`.
+
+- `k3s` has been updated to version [v1.30](https://github.com/k3s-io/k3s/releases/tag/v1.30.0%2Bk3s1), previous supported versions are available under release-specific names (e.g. k3s_1_27, k3s_1_28, and k3s_1_29) in order to help you migrate to the latest supported version. See [changelog and upgrade notes](https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.30.md#changelog-since-v1290) for more information.
+
 - `k9s` was updated to v0.31. There have been various breaking changes in the config file format,
   check out the changelog of [v0.29](https://github.com/derailed/k9s/releases/tag/v0.29.0),
   [v0.30](https://github.com/derailed/k9s/releases/tag/v0.30.0) and
   [v0.31](https://github.com/derailed/k9s/releases/tag/v0.31.0) for details. It is recommended
   to back up your current configuration and let k9s recreate the new base configuration.
 
-- NixOS AMIs are now uploaded regularly to a new AWS Account.
-  Instructions on how to use them can be found on <https://nixos.github.io/amis>.
-  We are working on integration the data into the NixOS homepage.
-  The list in `nixos/modules/virtualisation/amazon-ec2-amis.nix` will stop
-  being updated and will be removed in the future.
+- `kanata` package has been updated to v1.6.1, which includes breaking changes.  Check out the changelog of [v1.5.0](https://github.com/jtroo/kanata/releases/tag/v1.5.0) and [v1.6.0](https://github.com/jtroo/kanata/releases/tag/v1.6.0) for details.
 
-- The option `services.postgresql.ensureUsers._.ensurePermissions` has been removed as it is
-  not declarative and is broken with newer postgresql versions. Consider using
-  [](#opt-services.postgresql.ensureUsers._.ensureDBOwnership)
-  instead or a tool that is more suited for managing the data inside a postgresql database.
+- `linuxPackages_testing_bcachefs` is now fully deprecated by `linuxPackages_latest`, and is therefore no longer available.
 
-- `idris2` was updated to v0.7.0. This version introduces breaking changes. Check out the [changelog](https://github.com/idris-lang/Idris2/blob/v0.7.0/CHANGELOG.md#v070) for details.
+- `livebook` package is now built as a `mix release` instead of an `escript`.
+  This means that configuration now has to be done using [environment variables](https://hexdocs.pm/livebook/readme.html#environment-variables) instead of command line arguments.
+  This has the further consequence that the `livebook` service configuration has changed.
+
+- `lua` interpreters default LUA_PATH and LUA_CPATH are not overriden by nixpkgs
+  anymore, we patch LUA_ROOT instead which is more respectful to upstream.
+
+- `luarocks-packages-updater`'s .csv format, used to define lua packages to be updated, has changed: `src` (URL of a git repository) has now become `rockspec` (URL of a rockspec) to remove ambiguity regarding which rockspec to use and simplify implementation.
 
-- `nvtop` family of packages was reorganized into nested attrset. `nvtop` has been renamed to `nvtopPackages.full`, and all `nvtop-{amd,nvidia,intel,msm}` packages are now named as `nvtopPackages.{amd,nvidia,intel,msm}`
+- `mkosi` was updated to v22. Parts of the user interface have changed. Consult the
+  release notes of [v19](https://github.com/systemd/mkosi/releases/tag/v19),
+  [v20](https://github.com/systemd/mkosi/releases/tag/v20),
+  [v21](https://github.com/systemd/mkosi/releases/tag/v21) and
+  [v22](https://github.com/systemd/mkosi/releases/tag/v22) for a list of changes.
+
+- `mongodb-4_4` has been removed as it has reached end of life. Consequently, `unifi7` and `unifi8` now use MongoDB 5.0 by default.
+
+- `mongodb-5_0` and newer requires a cpu with the AVX instruction set to run.
 
 - `neo4j` has been updated to version 5. You may want to read the [release notes for Neo4j 5](https://neo4j.com/release-notes/database/neo4j-5/).
 
-- `services.neo4j.allowUpgrade` was removed and no longer has any effect. Neo4j 5 supports automatic rolling upgrades.
+- `netbox` was updated to v3.7. `services.netbox.package` still defaults
+  to v3.6 if `stateVersion` is earlier than 24.05. Refer to upstream's breaking
+  changes [for
+  v3.7.0](https://github.com/netbox-community/netbox/releases/tag/v3.7.0) and
+  upgrade NetBox by changing `services.netbox.package`. Database migrations
+  will be run automatically.
 
-- `unifiLTS`, `unifi5` and `unifi6` have been removed, as they require MongoDB versions which are end-of-life. All these versions can be upgraded to `unifi7` directly.
+- `network-interfaces.target` system target was removed as it has been deprecated for a long time. Use `network.target` instead.
 
-- `mongodb-4_4` has been removed as it has reached end of life. Consequently, `unifi7` and `unifi8` now use MongoDB 5.0 by default.
+- `networking.iproute2.enable` now does not set `environment.etc."iproute2/rt_tables".text`.
+
+  Setting `environment.etc."iproute2/{CONFIG_FILE_NAME}".text` will override the whole configuration file instead of appending it to the upstream configuration file.
+
+  `CONFIG_FILE_NAME` includes `bpf_pinning`, `ematch_map`, `group`, `nl_protos`, `rt_dsfield`, `rt_protos`, `rt_realms`, `rt_scopes`, and `rt_tables`.
+
+- `nextcloud26` has been removed since it's not maintained anymore by upstream. The latest available version of Nextcloud is now v29 (available as `pkgs.nextcloud29`). The installation logic is as follows:
+  - If [`services.nextcloud.package`](#opt-services.nextcloud.package) is specified explicitly, this package will be installed (**recommended**).
+  - If [`system.stateVersion`](#opt-system.stateVersion) is >=24.05, `pkgs.nextcloud29` will be installed by default.
+  - If [`system.stateVersion`](#opt-system.stateVersion) is >=23.11, `pkgs.nextcloud27` will be installed by default.
+  - Please note that an upgrade from v27 (or older) to v29 directly is not possible. Please upgrade to `nextcloud28` (or earlier) first. Nextcloud prohibits skipping major versions while upgrading. You can upgrade by declaring [`services.nextcloud.package = pkgs.nextcloud28;`](options.html#opt-services.nextcloud.package).
+  - Known warnings after the upgrade are documented in [](#module-services-nextcloud-known-warnings) from now on.
+  - The "Photos" app only displays Media from inside the `Photos` directory. This can be changed manually in the "Photos" tab below "Photos settings".
 
 - `nitter` requires a `guest_accounts.jsonl` to be provided as a path or loaded into the default location at `/var/lib/nitter/guest_accounts.jsonl`. See [Guest Account Branch Deployment](https://github.com/zedeus/nitter/wiki/Guest-Account-Branch-Deployment) for details.
 
-- `boot.supportedFilesystems` and `boot.initrd.supportedFilesystems` are now attribute sets instead of lists. Assignment from lists as done previously is still supported, but checking whether a filesystem is enabled must now by done using `supportedFilesystems.fs or false` instead of using `lib.elem "fs" supportedFilesystems` as was done previously.
+- `nixVersions.unstable` was removed. Instead the following attributes are provided:
+  - `nixVersions.git` which tracks the latest Nix master and is roughly updated once a week. This is intended to enable people to easily test unreleased changes of Nix to catch regressions earlier.
+  - `nixVersions.latest` which points to the latest Nix version packaged in nixpkgs.
 
-- `services.aria2.rpcSecret` has been replaced with `services.aria2.rpcSecretFile`.
-  This was done so that secrets aren't stored in the world-readable nix store.
-  To migrate, you will have to create a file with the same exact string, and change
-  your module options to point to that file. For example, `services.aria2.rpcSecret =
-  "mysecret"` becomes `services.aria2.rpcSecretFile = "/path/to/secret_file"`
-  where the file `secret_file` contains the string `mysecret`.
+- `nomad` has been updated - note that HashiCorp recommends updating one minor version at a time. Please check [their upgrade guide](https://developer.hashicorp.com/nomad/docs/upgrade) for information on safely updating clusters and potential breaking changes.
+
+  - `nomad` is now Nomad 1.7.x.
+
+  - `nomad_1_4` has been removed, as it is now unsupported upstream.
+
+- `nvtop` family of packages was reorganized into a nested attrset. `nvtop` has been renamed to `nvtopPackages.full`, and all `nvtop-{amd,nvidia,intel,msm}` packages are renamed to `nvtopPackages.{amd,nvidia,intel,msm}`.
 
 - `openssh`, `openssh_hpn` and `openssh_gssapi` are now compiled without support for the DSA signature algorithm as it is being deprecated upstream. Users still relying on DSA keys should consider upgrading
   to another signature algorithm. However, for the time being it is possible to restore DSA key support using `override` to set `dsaKeysSupport = true`.
 
-- `buildGoModule` now throws an error when `vendorHash` is not specified. `vendorSha256`, deprecated in Nixpkgs 23.11, is now ignored and is no longer a `vendorHash` alias.
+- `optparse-bash` is now dropped due to upstream inactivity. Alternatives available in Nixpkgs include [`argc`](https://github.com/sigoden/argc), [`argbash`](https://github.com/matejak/argbash), [`bashly`](https://github.com/DannyBen/bashly) and [`gum`](https://github.com/charmbracelet/gum), to name a few.
 
-- `services.invidious.settings.db.user`, the default database username has changed from `kemal` to `invidious`. Setups involving an externally-provisioned database (i.e. `services.invidious.database.createLocally == false`) should adjust their configuration accordingly. The old `kemal` user will not be removed automatically even when the database is provisioned automatically.(https://github.com/NixOS/nixpkgs/pull/265857)
+- `paperless`' `services.paperless.extraConfig` setting has been removed and converted to the free-form type and option named `services.paperless.settings`.
 
-- `writeReferencesToFile` is deprecated in favour of the new trivial build helper `writeClosure`. The latter accepts a list of paths and has an unambiguous name and cleaner implementation.
+- `pdns` was updated to version [v4.9.x](https://doc.powerdns.com/authoritative/changelog/4.9.html), which introduces breaking changes. Check out the [Upgrade Notes](https://doc.powerdns.com/authoritative/upgrading.html#to-4-9-0) for details.
 
-- `inetutils` now has a lower priority to avoid shadowing the commonly used `util-linux`. If one wishes to restore the default priority, simply use `lib.setPrio 5 inetutils` or override with `meta.priority = 5`.
+- `percona-server` now follows [the same two-fold release cycle](https://www.percona.com/blog/lts-and-innovation-releases-for-percona-server-for-mysql/) as Oracle MySQL and provides a *Long-Term-Support (LTS)* in parallel with a continuous-delivery *Innovation* release. `percona-server` defaults to `percona-server_lts`, will be backed by the same release branch throughout the lifetime of this stable NixOS release, and is still available under the versioned attribute `percona-server_8_0`.
+  The `percona-server_innovation` releases however have support periods shorter than the lifetime of this NixOS release and will continuously be updated to newer Percona releases. Note that Oracle considers the *Innovation* releases to be production-grade, but each release might include backwards-incompatible changes, even in its on-disk format.
+  The same release scheme is applied to the supporting `percona-xtrabackup` tool as well.
 
-- `paperless`' `services.paperless.extraConfig` setting has been removed and converted to the free-form type and option named `services.paperless.settings`.
+- `pipewire` and `wireplumber` modules have removed support for using
+`environment.etc."pipewire/..."` and `environment.etc."wireplumber/..."`.
+Use `services.pipewire.extraConfig` or `services.pipewire.configPackages` for PipeWire and
+`services.pipewire.wireplumber.configPackages` for WirePlumber instead.
 
-- `davfs2`' `services.davfs2.extraConfig` setting has been deprecated and converted to the free-form type option named `services.davfs2.settings` according to RFC42.
+- `power.ups` now generates `upsd.conf`, `upsd.users` and `upsmon.conf` automatically from a set of new configuration options. This breaks compatibility with existing `power.ups` setups where these files were created manually. Back up these files before upgrading NixOS.
 
-- `services.homepage-dashboard` now takes its configuration using native Nix expressions, rather than dumping templated configurations into `/var/lib/homepage-dashboard` where they were previously managed manually. There are now new options which allow the configuration of bookmarks, services, widgets and custom CSS/JS natively in Nix.
+- `programs.nix-ld.libraries` no longer sets `baseLibraries` via the option's default but in config and now merges any additional libraries with the default ones.
+  This means that `lib.mkForce` must be used to clear the list of default libraries.
 
-- `hare` may now be cross-compiled. For that to work, however, `haredoc` needed to stop being built together with it. Thus, the latter is now its own package with the name of `haredoc`.
+- `screen`'s module has been cleaned, and will now require you to set `programs.screen.enable` in order to populate `screenrc` and add the program to the environment.
 
-- `network-interfaces.target` system target was removed as it has been deprecated for a long time. Use `network.target` instead.
+- `security.acme.defaults.server` now has a default value instead of `null`.
+  This effectively uses the same server, the Let's Encrypt production server,
+  but makes the default explicit, instead of relying on the Lego default.
 
-- `services.redis.vmOverCommit` now defaults to `true` and no longer enforces Transparent Hugepages (THP) to be disabled. Redis only works with THP configured to `madvise` which is the kernel's default.
+  A side effect of this is that the directory in which account data is stored
+  changes and the ACME module will request a new account and new certificates
+  for all domains.  This may cause issues if you pin an `acccounturl` in a CAA
+  DNS record.  To avoid this, you
+  may set `security.acme.defaults.server = null` to keep the old hashes.
 
-- `azure-cli` now has extension support. For example, to install the `aks-preview` extension, use
+- `security.pam.sshAgentAuth.enable` now requires `services.openssh.authorizedKeysFiles` to be non-empty,
+  which is the case when `services.openssh.enable` is true. Previously, `pam_ssh_agent_auth` silently failed to work.
 
-  ```nix
-  environment.systemPackages = [
-    (azure-cli.withExtensions [ azure-cli.extensions.aks-preview ])
-  ];
-  ```
-  To make the `azure-cli` immutable and prevent clashes in case `azure-cli` is also installed via other package managers, some configuration files were moved into the derivation.
-  This can be disabled by overriding `withImmutableConfig = false` when building `azure-cli`.
+- `security.pam.enableSSHAgentAuth` was replaced by the `sshAgentAuth` attrset, and **only**
+  `authorized_keys` files listed in [`sshAgentAuth.authorizedKeysFiles`] are trusted,
+  defaulting to `/etc/ssh/authorized_keys.d/%u`.
+  ::: {.warning}
+  Users of {manpage}`pam_ssh_agent_auth(8)` must take care that the pubkeys they use (for instance with `sudo`)
+  are listed in [`sshAgentAuth.authorizedKeysFiles`].
+  :::
+  ::: {.note}
+  Previously, all `services.openssh.authorizedKeysFiles` were trusted, including `~/.ssh/authorized_keys`,
+  which results in an **insecure** configuration; see [#31611](https://github.com/NixOS/nixpkgs/issues/31611).
+  :::
+
+[`sshAgentAuth.authorizedKeysFiles`]: #opt-security.pam.sshAgentAuth.authorizedKeysFiles
 
-- `services.frp.settings` now generates the frp configuration file in TOML format as [recommended by upstream](https://github.com/fatedier/frp#configuration-files), instead of the legacy INI format. This has also introduced other changes in the configuration file structure and options.
-  - The `settings.common` section in the configuration is no longer valid and all the options form inside it now goes directly under `settings`.
-  - The `_` separating words in the configuration options is removed so the options are now in camel case. For example: `server_addr` becomes `serverAddr`, `server_port` becomes `serverPort` etc.
-  - Proxies are now defined with a new option `settings.proxies` which takes a list of proxies.
+- `services.archisteamfarm` no longer uses the abbreviation `asf` for its state directory (`/var/lib/asf`), user and group (both `asf`). Instead the long name `archisteamfarm` is used.
+  Configurations with `system.stateVersion` 23.11 or earlier, default to the old stateDirectory until the 24.11 release and must either set the option explicitly or move the data to the new directory.
+
+- `frr` was updated to 10.0, which introduces the default of `enforce-first-as` for BGP. Please disable again if needed.
+
+- `services.aria2.rpcSecret` has been replaced with `services.aria2.rpcSecretFile`.
+  This was done so that secrets aren't stored in the world-readable Nix store.
+  To migrate, you will have to create a file with the same exact string, and change
+  your module options to point to that file. For example, `services.aria2.rpcSecret =
+  "mysecret"` becomes `services.aria2.rpcSecretFile = "/path/to/secret_file"`
+  where the file `secret_file` contains the string `mysecret`.
+
+- `services.avahi.nssmdns` was split into `services.avahi.nssmdns4` and `services.avahi.nssmdns6` which enable the mDNS NSS switches for IPv4 and IPv6 respectively.
+  Since most mDNS responders only register IPv4 addresses, most users want to keep the IPv6 support disabled to avoid long timeouts.
+
+- `services.frp.settings` now generates the frp configuration file in TOML format as [recommended by upstream](https://github.com/fatedier/frp#configuration-files), instead of the legacy INI format. This has also introduced other changes in the configuration file structure and options:
+  - The `settings.common` section in the configuration is no longer valid and all the options form inside it now go directly under `settings`.
+  - Configuration option names have been changed from snake_case to camelCase. For example: `server_addr` becomes `serverAddr`, `server_port` becomes `serverPort` etc.
+  - Proxies are now defined with a new option, `settings.proxies`, which takes a list of proxies.
   - Consult the [upstream documentation](https://github.com/fatedier/frp#example-usage) for more details on the changes.
 
-- `mkosi` was updated to v20. Parts of the user interface have changed. Consult the
-  release notes of [v19](https://github.com/systemd/mkosi/releases/tag/v19) and
-  [v20](https://github.com/systemd/mkosi/releases/tag/v20) for a list of changes.
+- `services.hledger-web.capabilities` options has been replaced by a new option `services.hledger-web.allow`.
 
-- `gonic` has been updated to v0.16.4. Config now requires `playlists-path` to be set. See the rest of the [v0.16.0 release notes](https://github.com/sentriz/gonic/releases/tag/v0.16.0) for more details.
+  - `allow = "view"` means `capabilities = { view = true; }`;
+  - `allow = "add"` means `capabilities = { view = true; add = true; }`;
+  - `allow = "edit"` means `capabilities = { view = true; add = true; edit = true }`;
+  - `allow = "sandstorm"` reads permissions from the `X-Sandstorm-Permissions` request header.
 
-- `services.vikunja` systemd service now uses `vikunja` as dynamic user instead of `vikunja-api`. Database users might need to be changed.
+- `services.homepage-dashboard` now takes its configuration using native Nix expressions, rather than dumping templated configurations into `/var/lib/homepage-dashboard` where they were previously managed manually. There are now new options which allow the configuration of bookmarks, services, widgets and custom CSS/JS natively in Nix.
 
-- `services.vikunja.setupNginx` setting has been removed. Users now need to setup the webserver configuration on their own with a proxy pass to the vikunja service.
+- `services.invidious.settings.db.user`, the default database username has changed from `kemal` to `invidious`. Setups involving an externally-provisioned database (i.e. `services.invidious.database.createLocally == false`) should adjust their configuration accordingly. The old `kemal` user will not be removed automatically even when the database is provisioned automatically.(https://github.com/NixOS/nixpkgs/pull/265857).
 
-- `services.vmagent` module deprecates `dataDir`, `group` and `user` setting in favor of systemd provided CacheDirectory and DynamicUser.
+- `services.oauth2_proxy` was renamed to `services.oauth2-proxy`. Also the corresponding service, user and group were renamed.
 
-- `services.vmagent.remoteWriteUrl` setting has been renamed to `services.vmagent.remoteWrite.url` and now defaults to `null`.
+- `services.smokeping` now has an option `webService`. When enabled, smokeping is now served via nginx instead of thttpd. This change brings the following consequences:
+  - The default port for smokeping is now the nginx default port 80 instead of 8081.
+  - The option `services.smokeping.port` has been removed. To customize the port, use `services.nginx.virtualHosts.smokeping.listen.*.port`.
 
-- `woodpecker-*` packages have been updated to v2 which includes [breaking changes](https://woodpecker-ci.org/docs/next/migrations#200).
+- `services.neo4j.allowUpgrade` was removed and no longer has any effect. Neo4j 5 supports automatic rolling upgrades.
+
+- `services.nextcloud` has the following options moved into [`services.nextcloud.settings`](#opt-services.nextcloud.settings) and renamed to match the name from Nextcloud's `config.php`:
+  - `logLevel` -> [`loglevel`](#opt-services.nextcloud.settings.loglevel),
+  - `logType` -> [`log_type`](#opt-services.nextcloud.settings.log_type),
+  - `defaultPhoneRegion` -> [`default_phone_region`](#opt-services.nextcloud.settings.default_phone_region),
+  - `overwriteProtocol` -> [`overwriteprotocol`](#opt-services.nextcloud.settings.overwriteprotocol),
+  - `skeletonDirectory` -> [`skeletondirectory`](#opt-services.nextcloud.settings.skeletondirectory),
+  - `globalProfiles` -> [`profile.enabled`](#opt-services.nextcloud.settings._profile.enabled_),
+  - `extraTrustedDomains` -> [`trusted_domains`](#opt-services.nextcloud.settings.trusted_domains) and
+  - `trustedProxies` -> [`trusted_proxies`](#opt-services.nextcloud.settings.trusted_proxies).
 
 - `services.nginx` will no longer advertise HTTP/3 availability automatically. This must now be manually added, preferably to each location block.
   Example:
@@ -347,301 +522,202 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
   }
   ```
 
-- `optparse-bash` is now dropped due to upstream inactivity. Alternatives available in Nixpkgs include [`argc`](https://github.com/sigoden/argc), [`argbash`](https://github.com/matejak/argbash), [`bashly`](https://github.com/DannyBen/bashly) and [`gum`](https://github.com/charmbracelet/gum), to name a few.
+- `services.pgbouncer` now has systemd support enabled and will log to journald. The default setting for `services.pgbouncer.logFile` is now `null` to disable logging to a separate log file.
 
-- `kanata` package has been updated to v1.6.0, which includes breaking changes.  Check out the changelog of [v1.5.0](https://github.com/jtroo/kanata/releases/tag/v1.5.0) and [v1.6.0](https://github.com/jtroo/kanata/releases/tag/v1.6.0) for details.
+- `services.postgresql.ensureUsers._.ensurePermissions` has been removed as it is
+  not declarative and is broken with newer postgresql versions. Consider using
+  [](#opt-services.postgresql.ensureUsers._.ensureDBOwnership)
+  instead or a tool that is more suited for managing the data inside a postgresql database.
 
-- `craftos-pc` package has been updated to v2.8, which includes [breaking changes](https://github.com/MCJack123/craftos2/releases/tag/v2.8).
-  - Files are now handled in binary mode; this could break programs with embedded UTF-8 characters.
-  - The ROM was updated to match ComputerCraft version v1.109.2.
-  - The bundled Lua was updated to Lua v5.2, which includes breaking changes. See the [Lua manual](https://www.lua.org/manual/5.2/manual.html#8) for more information.
-  - The WebSocket API [was rewritten](https://github.com/MCJack123/craftos2/issues/337), which introduced breaking changes.
+- `services.redis.vmOverCommit` now defaults to `true` and no longer enforces Transparent Hugepages (THP) to be disabled. Redis only works with THP configured to `madvise` which is the kernel's default.
 
-- `gtest` package has been updated past v1.13.0, which requires C++14 or higher.
+- `services.resolved.fallbackDns`
+  - can now be used to disable the upstream fallback servers entirely by setting it to `[]`
+  - to get previous behaviour of upstream defaults, set it to `null`
+  - default value has changed from `[]` to `null`, in order to preserve default behaviour
 
-- Nextcloud 26 has been removed since it's not maintained anymore by upstream.
+- `services.vikunja` systemd service now uses `vikunja` as dynamic user instead of `vikunja-api`. Database users might need to be changed.
 
-- The latest available version of Nextcloud is v29 (available as `pkgs.nextcloud29`). The installation logic is as follows:
-  - If [`services.nextcloud.package`](#opt-services.nextcloud.package) is specified explicitly, this package will be installed (**recommended**)
-  - If [`system.stateVersion`](#opt-system.stateVersion) is >=24.05, `pkgs.nextcloud29` will be installed by default.
-  - If [`system.stateVersion`](#opt-system.stateVersion) is >=23.11, `pkgs.nextcloud27` will be installed by default.
-  - Please note that an upgrade from v27 (or older) to v29 directly is not possible. Please upgrade to `nextcloud28` (or earlier) first. Nextcloud prohibits skipping major versions while upgrading. You can upgrade by declaring [`services.nextcloud.package = pkgs.nextcloud28;`](options.html#opt-services.nextcloud.package).
-  - Known warnings after the upgrade are documented in [](#module-services-nextcloud-known-warnings).
+- `services.vikunja.setupNginx` setting has been removed. Users now need to set up the webserver configuration on their own with a proxy pass to the vikunja service.
 
-- The vendored third party libraries have been mostly removed from `cudaPackages.nsight_systems`, which we now only ship for `cudaPackages_11_8` and later due to outdated dependencies. Users comfortable with the vendored dependencies may use `overrideAttrs` to amend the `postPatch` phase and the `meta.broken` correspondingly. Alternatively, one could package the deprecated `boost170` locally, as required for `cudaPackages_11_4.nsight_systems`.
+- `services.vmagent` module deprecates `dataDir`, `group` and `user` settings in favor of the systemd-provided CacheDirectory and DynamicUser.
 
-- `cudaPackages` package scope has been updated to `cudaPackages_12`.
+- `services.vmagent.remoteWriteUrl` setting has been renamed to `services.vmagent.remoteWrite.url` and now defaults to `null`.
 
-- The deprecated `cudaPackages.cudatoolkit` has been replaced with a
-  symlink-based wrapper for the splayed redistributable CUDA packages. The
-  wrapper only includes tools and libraries necessary to build common packages
-  like e.g. tensorflow. The original runfile-based `cudatoolkit` is still
-  available as `cudatoolkit-legacy-runfile`.
+- `services.zope2` has been removed, as `zope2` is unmaintained and was relying on Python 2.
 
-- `halloy` package was updated past 2024.5 which introduced a breaking change by switching the config format from YAML to TOML. See https://github.com/squidowl/halloy/releases/tag/2024.5 for details.
+- `spark2014` has been renamed to `gnatprove`. A version of `gnatprove` matching different GNAT versions is available from the different `gnatPackages` sets.
 
-- The `wpaperd` package has a breaking change moving to 1.0.1, previous version 0.3.0 had 2 different configuration files, one for wpaperd and one for the wallpapers. Remove the former and move the latter (`wallpaper.toml`) to `config.toml`.
+- `stalwart-mail` has been updated to v0.5.3, which includes [breaking changes](https://github.com/stalwartlabs/mail-server/blob/v0.5.3/UPGRADING.md).
 
-- Ada packages (libraries and tools) have been moved into the `gnatPackages` scope. `gnatPackages` uses the default GNAT compiler, `gnat12Packages` and `gnat13Packages` use the respective matching compiler version.
+- `system.etc.overlay.enable` option was added. If enabled, `/etc` is
+  mounted via an overlayfs instead of being created by a custom perl script.
 
-- Paths provided as `restartTriggers` and `reloadTriggers` for systemd units will now be copied into the nix store to make the behavior consistent.
-  Previously, `restartTriggers = [ ./config.txt ]`, if defined in a flake, would trigger a restart when any part of the flake changed; and if not defined in a flake, would never trigger a restart even if the contents of `config.txt` changed.
+- `system.forbiddenDependenciesRegex` has been renamed to `system.forbiddenDependenciesRegexes` and now has the type of `listOf string` instead of `string` to accept multiple regexes.
 
-- `spark2014` has been renamed to `gnatprove`. A version of `gnatprove` matching different GNAT versions is available from the different `gnatPackages` sets.
+- `systemd.oomd` module behavior has changed:
 
-- `services.resolved.fallbackDns` can now be used to disable the upstream fallback servers entirely by setting it to an empty list. To get the previous behaviour of the upstream defaults set it to null, the new default, instead.
+  - Raise ManagedOOMMemoryPressureLimit from 50% to 80%. This should make systemd-oomd kill things less often, and fix issues like [this](https://pagure.io/fedora-workstation/issue/358).
+    Reference: [commit](https://src.fedoraproject.org/rpms/systemd/c/806c95e1c70af18f81d499b24cd7acfa4c36ffd6?branch=806c95e1c70af18f81d499b24cd7acfa4c36ffd6).
 
-- `services.hledger-web.capabilities` options has been replaced by a new option `services.hledger-web.allow`.
+  - Remove swap policy. This helps prevent killing processes when user's swap is small.
 
-  - `allow = "view"` means `capabilities = { view = true; }`;
-  - `allow = "add"` means `capabilities = { view = true; add = true; }`;
-  - `allow = "edit"` means `capabilities = { view = true; add = true; edit = true }`;
-  - `allow = "sandstorm"` reads permissions from the `X-Sandstorm-Permissions` request header.
+  - Expand the memory pressure policy to `system.slice`, `user-.slice`, and all user-owned slices. Reference: [commit](https://src.fedoraproject.org/rpms/systemd/c/7665e1796f915dedbf8e014f0a78f4f576d609bb).
 
-- `xxd` has been moved from `vim` default output to its own output to reduce closure size. The canonical way to reference it across all platforms is `unixtools.xxd`.
+  - Rename `systemd.oomd.enableUserServices` to `systemd.oomd.enableUserSlices`.
 
-- `stalwart-mail` package has been updated to v0.5.3, which includes [breaking changes](https://github.com/stalwartlabs/mail-server/blob/v0.5.3/UPGRADING.md).
+- `systemd.sysusers.enable` option was added. If enabled, users and
+  groups are created with systemd-sysusers instead of with a custom perl script.
 
-- `services.zope2` has been removed as `zope2` is unmaintained and was relying on Python2.
+- `teleport` has been upgraded from major version 14 to major version 15.
+  Refer to upstream [upgrade instructions](https://goteleport.com/docs/management/operations/upgrading/)
+  and release notes for [v15](https://goteleport.com/docs/changelog/#1500-013124).
 
-- `services.oauth2_proxy` was renamed to `services.oauth2-proxy`. Also the corresponding service, user and group were renamed.
+- `unifiLTS`, `unifi5` and `unifi6` have been removed, as they require MongoDB versions which are end-of-life. All these versions can be upgraded to `unifi7` directly.
 
-- `services.avahi.nssmdns` got split into `services.avahi.nssmdns4` and `services.avahi.nssmdns6` which enable the mDNS NSS switch for IPv4 and IPv6 respectively.
-  Since most mDNS responders only register IPv4 addresses, most users want to keep the IPv6 support disabled to avoid long timeouts.
+- `unrar` was updated to v7. See [changelog](https://www.rarlab.com/unrar7notes.htm) for more information.
 
-- A warning has been added for services that are
-  `after = [ "network-online.target" ]` but do not depend on it (e.g. using
-  `wants`), because the dependency that `multi-user.target` has on
-  `network-online.target` is planned for removal.
+- `virtualisation.docker.enableNvidia` and `virtualisation.podman.enableNvidia` options are deprecated. `hardware.nvidia-container-toolkit.enable` should be used instead. This option will expose GPUs on containers with the `--device` CLI option. This is supported by Docker 25, Podman 3.2.0 and Singularity 4. Any container runtime that supports the CDI specification will take advantage of this feature.
 
-- `services.pgbouncer` now has systemd support enabled and will log to journald. The default setting for `services.pgbouncer.logFile` is now `null` to disable logging to a separate log file.
+- `virtialisation.incus` now defaults to the newly-added `incus-lts` release (v6.0.x). Users who wish to continue using the non-LTS release will need to set `virtualisation.incus.package = pkgs.incus`. Stable release users are encouraged to stay on the LTS release as non-LTS releases will by default not be backported.
 
-- `services.archisteamfarm` no longer uses the abbreviation `asf` for its state directory (`/var/lib/asf`), user and group (both `asf`). Instead the long name `archisteamfarm` is used.
-  Configurations with `system.stateVersion` 23.11 or earlier, default to the old stateDirectory until the 24.11 release and must either set the option explicitly or move the data to the new directory.
+- `woodpecker-*` packages have been updated to v2 which includes [breaking changes](https://woodpecker-ci.org/docs/next/migrations#200).
+
+- `wpaperd` has been updated to 1.0.1, which has a breaking change: previous version 0.3.0 had 2 different configuration files, one for wpaperd and one for the wallpapers. Remove the former and move the latter (`wallpaper.toml`) to `config.toml`.
+
+- `writeReferencesToFile` is deprecated in favour of the new trivial build helper `writeClosure`. The latter accepts a list of paths and has an unambiguous name and cleaner implementation.
 
 - `xfsprogs` was updated to version 6.6.0, which enables reverse mapping (rmapbt) and large extent counts (nrext64) by default.
    Support for these features was added in kernel 4.9 and 5.19 and nrext64 was deemed stable in kernel 6.5.
    Format your filesystems with `mkfs.xfs -i nrext64=0`, if they need to be readable by GRUB2 before 2.12 or kernels older than 5.19.
 
-- `networking.iproute2.enable` now does not set `environment.etc."iproute2/rt_tables".text`.
+- `xxd` has been moved from `vim` default output to its own output to reduce closure size. The canonical way to reference it across all platforms is `unixtools.xxd`.
 
-  Setting `environment.etc."iproute2/{CONFIG_FILE_NAME}".text` will override the whole configuration file instead of appending it to the upstream configuration file.
+- `youtrack` was bumped to 2023.3. The update is not performed automatically, it requires manual interaction. See the YouTrack section in the manual for details.
 
-  `CONFIG_FILE_NAME` includes `bpf_pinning`, `ematch_map`, `group`, `nl_protos`, `rt_dsfield`, `rt_protos`, `rt_realms`, `rt_scopes`, and `rt_tables`.
-
-- `netbox` was updated to v3.7. `services.netbox.package` still defaults
-  to v3.6 if `stateVersion` is earlier than 24.05. Refer to upstream's breaking
-  changes [for
-  v3.7.0](https://github.com/netbox-community/netbox/releases/tag/v3.7.0) and
-  upgrade NetBox by changing `services.netbox.package`. Database migrations
-  will be run automatically.
+- Ada packages (libraries and tools) have been moved into the `gnatPackages` scope. `gnatPackages` uses the default GNAT compiler, `gnat12Packages` and `gnat13Packages` use the respective matching compiler version.
 
-- `gauge` now supports installing plugins using nix. For the old imperative approach, switch to `gauge-unwrapped`.
-  You can load plugins from an existing gauge manifest file using `gauge.fromManifest ./path/to/manifest.json` or
-  specify plugins in nix using `gauge.withPlugins (p: with p; [ js html-report xml-report ])`.
+- Paths provided as `restartTriggers` and `reloadTriggers` for systemd units will now be copied into the Nix store to make the behavior consistent.
+  Previously, `restartTriggers = [ ./config.txt ]`, if defined in a flake, would trigger a restart when any part of the flake changed; and if not defined in a flake, would never trigger a restart even if the contents of `config.txt` changed.
 
-- `firefox-devedition`, `firefox-beta`, `firefox-esr` executable file names for now match their package names, which is consistent with the `firefox-*-bin` packages. The desktop entries are also updated so that you can have multiple editions of firefox in your app launcher.
+- A warning has been added for services that are
+  `after = [ "network-online.target" ]` but do not depend on it (e.g. using
+  `wants`), because the dependency that `multi-user.target` has on
+  `network-online.target` is planned for removal.
 
 - switch-to-configuration does not directly call systemd-tmpfiles anymore.
   Instead, the new artificial sysinit-reactivation.target is introduced which
   allows to restart multiple services that are ordered before sysinit.target
   and respect the ordering between the services.
 
-- `systemd.oomd` module behavior is changed as:
-
-  - Raise ManagedOOMMemoryPressureLimit from 50% to 80%. This should make systemd-oomd kill things less often, and fix issues like [this](https://pagure.io/fedora-workstation/issue/358).
-    Reference: [commit](https://src.fedoraproject.org/rpms/systemd/c/806c95e1c70af18f81d499b24cd7acfa4c36ffd6?branch=806c95e1c70af18f81d499b24cd7acfa4c36ffd6)
-
-  - Remove swap policy. This helps prevent killing processes when user's swap is small.
-
-  - Expand the memory pressure policy to system.slice, user-.slice, and all user owned slices. Reference: [commit](https://src.fedoraproject.org/rpms/systemd/c/7665e1796f915dedbf8e014f0a78f4f576d609bb)
-
-  - `systemd.oomd.enableUserServices` is renamed to `systemd.oomd.enableUserSlices`.
-
-- `security.pam.enableSSHAgentAuth` now requires `services.openssh.authorizedKeysFiles` to be non-empty,
-  which is the case when `services.openssh.enable` is true. Previously, `pam_ssh_agent_auth` silently failed to work.
-
-- The configuration format for `services.prometheus.exporters.snmp` changed with release 0.23.0.
+- `services.prometheus.exporters.snmp`'s configuration format changed with release 0.23.0.
   The module now includes an optional config check, that is enabled by default, to make the change obvious before any deployment.
   More information about the configuration syntax change is available in the [upstream repository](https://github.com/prometheus/snmp_exporter/blob/b75fc6b839ee3f3ccbee68bee55f1ae99555084a/auth-split-migration.md).
 
-- [watchdogd](https://troglobit.com/projects/watchdogd/), a system and process supervisor using watchdog timers. Available as [services.watchdogd](#opt-services.watchdogd.enable).
-
-- `jdt-language-server` package now uses upstream's provided python wrapper instead of our own custom wrapper. This results in the following breaking and notable changes:
-
-  - The main binary for the package is now named `jdtls` instead of `jdt-language-server`, equivalent to what most editors expect the binary to be named.
-
-  - JVM arguments should now be provided with the `--jvm-arg` flag instead of setting `JAVA_OPTS`.
-
-  - The `-data` path is no longer required to run the package, and will be set to point to a folder in `$TMP` if missing.
-
-- `nomad` has been updated - note that HashiCorp recommends updating one minor version at a time. Please check [their upgrade guide](https://developer.hashicorp.com/nomad/docs/upgrade) for information on safely updating clusters and potential breaking changes.
+## Other Notable Changes {#sec-release-24.05-notable-changes}
 
-  - `nomad` is now Nomad 1.7.x.
+<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
 
-  - `nomad_1_4` has been removed, as it is now unsupported upstream.
+- `addDriverRunpath` has been added to facilitate the deprecation of the old `addOpenGLRunpath` setuphook. This change is motivated by the evolution of the setuphook to include all hardware acceleration.
 
-- Dwarf Fortress has been updated to version 50, and its derivations continue to menace with spikes of Nix and bash. Version 50 is identical to the version on Steam, but without the paid elements like tilepacks.
-  dfhack and Dwarf Therapist still work, and older versions are still packaged in case you'd like to roll back. Note that DF 50 saves will not be compatible with DF 0.47 and earlier.
-  See [Bay 12 Games](http://www.bay12games.com/dwarves/) for more details on what's new in Dwarf Fortress.
+- `appimage`, `appimageTools.wrapAppImage` and `buildFHSEnvBubblewrap` now properly accept `pname` and `version`.
 
-  - Running an earlier version can be achieved through an override: `dwarf-fortress-packages.dwarf-fortress-full.override { dfVersion = "0.47.5"; }`
+- `bacula` now allows to configure `TLS` for encrypted communication.
 
-  - Ruby plugin support has been disabled in DFHack. Many of the Ruby plugins have been converted to Lua, and support was removed upstream due to frequent crashes.
+- `boot.initrd.network.ssh.authorizedKeyFiles` is a new option in the initrd ssh daemon module, for adding authorized keys via list of files.
 
-- `livebook` package is now built as a `mix release` instead of an `escript`.
-  This means that configuration now has to be done using [environment variables](https://hexdocs.pm/livebook/readme.html#environment-variables) instead of command line arguments.
-  This has the further implication that the `livebook` service configuration has changed:
+- `boot.kernel.sysctl."net.core.wmem_max"` changed from a string to an integer because of the addition of a custom merge option (taking the highest value defined to avoid conflicts between 2 services trying to set that value), just as `boot.kernel.sysctl."net.core.rmem_max"` since 22.11.
 
-- `erlang_node_short_name`, `erlang_node_name`, `port` and `options` configuration parameters are gone, and have been replaced with an `environment` parameter.
-    Use the appropriate [environment variables](https://hexdocs.pm/livebook/readme.html#environment-variables) inside `environment` to configure the service instead.
+- `boot.loader.systemd-boot.xbootldrMountPoint` is a new option for setting up a separate [XBOOTLDR partition](https://uapi-group.org/specifications/specs/boot_loader_specification/) to store boot files. Useful on systems with a small EFI System partition that cannot be easily repartitioned.
 
-- `akkoma` now requires explicitly setting the base URL for uploaded media (`settings."Pleroma.Upload".base_url`), as well as for the media proxy if enabled (`settings."Media"`).
-  This is recommended to be a separate (sub)domain to the one Akkoma is hosted at.
-  See [here](https://meta.akkoma.dev/t/akkoma-stable-2024-03-securer-i-barely-know-her/681#explicit-upload-and-media-proxy-domains-5) for more details.
+- `boot.loader.systemd-boot` will now verify that `efiSysMountPoint` (and `xbootldrMountPoint` if configured) are mounted partitions.
 
-- `crystal` package has been updated to 1.11.x, which has some breaking changes.
-  Refer to crystal's changelog for more information. ([v1.10](https://github.com/crystal-lang/crystal/blob/master/CHANGELOG.md#1100-2023-10-09), [v1.11](https://github.com/crystal-lang/crystal/blob/master/CHANGELOG.md#1110-2024-01-08))
+- `buildDubPackage` can now be used to build Programs written in [D](https://dlang.org/) using the `dub` build system and package manager.
+  See the [D section](https://nixos.org/manual/nixpkgs/unstable#dlang) in the manual for more information.
 
-- `erlang-ls` package no longer ships the `els_dap` binary as of v0.51.0.
+- `castopod` has some migration actions to be taken in case of a S3 setup. Some new features may also need some manual migration actions. See [https://code.castopod.org/adaures/castopod/-/releases](https://code.castopod.org/adaures/castopod/-/releases) for more information.
 
-- `icu` no longer includes `install-sh` and `mkinstalldirs` in the shared folder.
+- `documentation.man.mandoc` now, by default, uses `MANPATH` to set the directories where mandoc will search for manual pages.
+  This enables mandoc to find manual pages in Nix profiles. To set the manual search paths via the `mandoc.conf` configuration file like before, use `documentation.man.mandoc.settings.manpath` instead.
 
-## Other Notable Changes {#sec-release-24.05-notable-changes}
+- `drbd` out-of-tree Linux kernel driver has been added in version 9.2.7. With it the DRBD 9.x features can be used instead of the 8.x features provided by the 8.4.11 in-tree driver.
 
-<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
+- `garage` has been updated to v1.x.x. Users should read the [upstream release notes](https://git.deuxfleurs.fr/Deuxfleurs/garage/releases/tag/v1.0.0) and follow the documentation when changing over their `services.garage.package` and performing this manual upgrade.
 
-- `addDriverRunpath` has been added to facilitate the deprecation of the old `addOpenGLRunpath` setuphook. This change is motivated by the evolution of the setuphook to include all hardware acceleration.
+- `hardware.pulseaudio` module now sets permissions of pulse user home directory to 755 when running in systemWide mode. It fixes [issue 114399](https://github.com/NixOS/nixpkgs/issues/114399).
 
-- `cinnamon` has been updated to 6.0. Please beware that the [Wayland session](https://blog.linuxmint.com/?p=4591) is still experimental in this release and could potentially [affect Xorg sessions](https://blog.linuxmint.com/?p=4639). We suggest a reboot when switching between sessions.
+- `kavita` has been updated to 0.8.0, requiring a manual forced library scan on all libraries for migration. Refer to upstream's [release notes](https://github.com/Kareadita/Kavita/releases/tag/v0.8.0) for details.
 
-- (TODO awaiting feedback on code-casing package names) MATE has been updated to 1.28.
-  - To properly support panel plugins built with Wayland (in-process) support, we are introducing `services.xserver.desktopManager.mate.extraPanelApplets` option, please use that for installing panel applets.
-  - Similarly, please use `services.xserver.desktopManager.mate.extraCajaExtensions` option for installing Caja extensions.
-  - To use the Wayland session, enable `services.xserver.desktopManager.mate.enableWaylandSession`. This is opt-in for now as it is in early stage and introduces a new set of Wayfire closure. Due to [known issues with LightDM](https://github.com/canonical/lightdm/issues/63), we suggest using SDDM for display manager.
+- `krb5` module has been rewritten and moved to `security.krb5`, moving all options but `security.krb5.enable` and `security.krb5.package` into `security.krb5.settings`.
 
-- `services.xserver.desktopManager.budgie` installs `gnome.gnome-terminal` by default (instead of `mate.mate-terminal`).
+- `libass` now uses the native CoreText backend on Darwin, which may fix subtitle rendering issues with `mpv`, `ffmpeg`, etc.
 
-- New `boot.loader.systemd-boot.xbootldrMountPoint` allows setting up a separate [XBOOTLDR partition](https://uapi-group.org/specifications/specs/boot_loader_specification/) to store boot files. Useful on systems with a small EFI System partition that cannot be easily repartitioned.
+- `libjxl` version was bumped from 0.8.2 to 0.9.1 [dropped support for the butteraugli API](https://github.com/libjxl/libjxl/pull/2576). You will no longer be able to set `enableButteraugli` on `libaom`.
 
-- `boot.loader.systemd-boot` will now verify that `efiSysMountPoint` (and `xbootldrMountPoint` if configured) are mounted partitions.
+- [`lilypond`](https://lilypond.org/index.html) and [`denemo`](https://www.denemo.org) are now compiled with Guile 3.0.
 
-- `services.postgresql.extraPlugins` changed its type from just a list of packages to also a function that returns such a list.
-  For example a config line like ``services.postgresql.extraPlugins = with pkgs.postgresql_11.pkgs; [ postgis ];`` is recommended to be changed to ``services.postgresql.extraPlugins = ps: with ps; [ postgis ];``;
+- `lxd` has been upgraded to v5.21.x, an LTS release. The LTS release is now the only supported LXD release. Users are encouraged to [migrate to Incus](https://linuxcontainers.org/incus/docs/main/howto/server_migrate_lxd/) for better support on NixOS.
 
 - [`matrix-synapse`](https://element-hq.github.io/synapse/) homeserver module now supports configuring UNIX domain socket [`listeners`](#opt-services.matrix-synapse.settings.listeners) through the `path` option.
   The default replication worker on the main instance has been migrated away from TCP sockets to UNIX domain sockets.
 
-- `boot.initrd.network.ssh.authorizedKeyFiles` is a new option in the initrd ssh daemon module, for adding authorized keys via list of files.
+- `mockgen` has changed to the [go.uber.org/mock](https://github.com/uber-go/mock) fork because [the original repository is no longer maintained](https://github.com/golang/mock#gomock).
 
-- `appimage`, `appimageTools.wrapAppImage` and `buildFHSEnvBubblewrap` now properly accepts `pname` and `version`.
+- `mpich` now requires `withPm` to be a list, e.g. `"hydra:gforker"` becomes `[ "hydra" "gforker" ]`.
 
-- Programs written in [Nim](https://nim-lang.org/) are built with libraries selected by lockfiles.
-  The `nimPackages` and `nim2Packages` sets have been removed.
-  See https://nixos.org/manual/nixpkgs/unstable#nim for more information.
+- `nextcloud-setup.service` no longer changes the group of each file and directory inside `/var/lib/nextcloud/{config,data,store-apps}` if one of these directories has the wrong owner group. This was part of transitioning the group used for `/var/lib/nextcloud`, but isn't necessary anymore.
 
-- [TODO: reword to place an attribute at the front] Programs written in [D](https://dlang.org/) using the `dub` build system and package manager can now be built using `buildDubPackage` utilizing lockfiles provided by the new `dub-to-nix` helper program.
-  See the [D section](https://nixos.org/manual/nixpkgs/unstable#dlang) in the manual for more information.
+- `oils-for-unix`, the oil shell's C++ version is now available. The Python version is still available as `oil`.
+
+- `pkgsExtraHardening`, a new top-level package set, was added. This is a set of packages built with stricter hardening flags - those that have not yet received enough testing to be applied universally, those that are more likely to cause build failures or those that have drawbacks to their use (e.g. performance or required hardware features).
 
 - [`portunus`](https://github.com/majewsky/portunus) has been updated to major version 2.
   This version of Portunus supports strong password hashes, but the legacy hash SHA-256 is also still supported to ensure a smooth migration of existing user accounts.
   After upgrading, follow the instructions on the [upstream release notes](https://github.com/majewsky/portunus/releases/tag/v2.0.0) to upgrade all user accounts to strong password hashes.
   Support for weak password hashes will be removed in NixOS 24.11.
 
-- A stdenv's default set of hardening flags can now be set via its `bintools-wrapper`'s `defaultHardeningFlags` argument. A convenient stdenv adapter, `withDefaultHardeningFlags`, can be used to override an existing stdenv's `defaultHardeningFlags`.
+- `programs.fish.package` now allows you to override the package used in the `fish` module.
 
-- `libass` now uses the native CoreText backend on Darwin, which may fix subtitle rendering issues with `mpv`, `ffmpeg`, etc.
+- `qt6.qtmultimedia` has changed its default backend to `QT_MEDIA_BACKEND=ffmpeg` (previously `gstreamer` on Linux or `darwin` on macOS).
+  The previous native backends remain available but are now minimally maintained. Refer to [upstream documentation](https://doc.qt.io/qt-6/qtmultimedia-index.html#ffmpeg-as-the-default-backend) for further details about each platform.
 
-- [`lilypond`](https://lilypond.org/index.html) and [`denemo`](https://www.denemo.org) are now compiled with Guile 3.0.
+- `services.btrbk` now automatically selects and provides required compression
+  program depending on the configured `stream_compress` option. Since this
+  replaces the need for the `extraPackages` option, this option will be
+  deprecated in future releases.
 
-- `garage` has been updated to v1.x.x. Users should read the [upstream release notes](https://git.deuxfleurs.fr/Deuxfleurs/garage/releases/tag/v1.0.0) and follow the documentation when changing over their `services.garage.package` and performing this manual upgrade.
+- `services.github-runner` module has been removed. To configure a single GitHub Actions Runner refer to `services.github-runners.*`. Note that this will trigger a new runner registration.
 
-- [TODO: reword to place an attribute at the front] The EC2 image module now enables the [Amazon SSM Agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html) by default.
-
-- The following options of the Nextcloud module were moved into [`services.nextcloud.settings`](#opt-services.nextcloud.settings) and renamed to match the name from Nextcloud's `config.php`:
-  - `logLevel` -> [`loglevel`](#opt-services.nextcloud.settings.loglevel),
-  - `logType` -> [`log_type`](#opt-services.nextcloud.settings.log_type),
-  - `defaultPhoneRegion` -> [`default_phone_region`](#opt-services.nextcloud.settings.default_phone_region),
-  - `overwriteProtocol` -> [`overwriteprotocol`](#opt-services.nextcloud.settings.overwriteprotocol),
-  - `skeletonDirectory` -> [`skeletondirectory`](#opt-services.nextcloud.settings.skeletondirectory),
-  - `globalProfiles` -> [`profile.enabled`](#opt-services.nextcloud.settings._profile.enabled_),
-  - `extraTrustedDomains` -> [`trusted_domains`](#opt-services.nextcloud.settings.trusted_domains) and
-  - `trustedProxies` -> [`trusted_proxies`](#opt-services.nextcloud.settings.trusted_proxies).
+- `services.networkmanager.extraConfig` was renamed to `services.networkmanager.settings` and changed to use the ini type instead of using a multiline string.
 
 - `services.nextcloud.config.dbport` option of the Nextcloud module was removed to match upstream.
   The port can be specified in [`services.nextcloud.config.dbhost`](#opt-services.nextcloud.config.dbhost).
 
-- A new abstraction to create both read-only as well as writable overlay file
-  systems was added. Available via
-  [fileSystems.overlay](#opt-fileSystems._name_.overlay.lowerdir). See also the
-  [NixOS docs](#sec-overlayfs).
-
-- `systemd` units can now specify the `Upholds=` and `UpheldBy=` unit dependencies via the aptly
-  named `upholds` and `upheldBy` options. These options get systemd to enforce that the
-  dependencies remain continuosly running for as long as the dependent unit is in a running state.
-
-- `stdenv`: The `--replace` flag in `substitute`, `substituteInPlace`, `substituteAll`, `substituteAllStream`, and `substituteStream` is now deprecated if favor of the new `--replace-fail`, `--replace-warn` and `--replace-quiet`. The deprecated `--replace` equates to `--replace-warn`.
-
-- A new hardening flag, `zerocallusedregs` was made available, corresponding to the gcc/clang option `-fzero-call-used-regs=used-gpr`.
-
-- A new hardening flag, `trivialautovarinit` was made available, corresponding to the gcc/clang option `-ftrivial-auto-var-init=pattern`.
-
-- New options were added to the dnsdist module to enable and configure a DNSCrypt endpoint (see `services.dnsdist.dnscrypt.enable`, etc.).
-  The module can generate the DNSCrypt provider key pair, certificates and also performs their rotation automatically with no downtime.
-
-- `sonarr` version bumped to from 3.0.10 to 4.0.3. Consequently existing config database files will be upgraded automatically, but note that some old apparently-working configs [might actually be corrupt and fail to upgrade cleanly](https://forums.sonarr.tv/t/sonarr-v4-released/33089).
-
-- [TODO: reword to place an attribute at the front] The Yama LSM is now enabled by default in the kernel, which prevents ptracing
-  non-child processes. This means you will not be able to attach gdb to an
-  existing process, but will need to start that process from gdb (so it is a
-  child). Or you can set `boot.kernel.sysctl."kernel.yama.ptrace_scope"` to 0.
-
-- `netbird` module now allows running multiple tunnels in parallel through [`services.netbird.tunnels`](#opt-services.netbird.tunnels).
-
-- [Nginx virtual hosts](#opt-services.nginx.virtualHosts) using `forceSSL` or
-  `globalRedirect` can now have redirect codes other than 301 through `redirectCode`.
-
-- `bacula` now allows to configure `TLS` for encrypted communication.
-
-- `libjxl` version bumped from 0.8.2 to 0.9.1 [dropped support for the butteraugli API](https://github.com/libjxl/libjxl/pull/2576). You will no longer be able to set `enableButteraugli` on `libaom`.
-
-- `mockgen` package source has changed to the [go.uber.org/mock](https://github.com/uber-go/mock) fork because [the original repository is no longer maintained](https://github.com/golang/mock#gomock).
-
-- [](#opt-boot.kernel.sysctl._net.core.wmem_max_) changed from a string to an integer because of the addition of a custom merge option (taking the highest value defined to avoid conflicts between 2 services trying to set that value), just as [](#opt-boot.kernel.sysctl._net.core.rmem_max_) since 22.11.
-
-- [TODO: reword to place an attribute at the front] A new top-level package set, `pkgsExtraHardening` is added. This is a set of packages built with stricter hardening flags - those that have not yet received enough testing to be applied universally, those that are more likely to cause build failures or those that have drawbacks to their use (e.g. performance or required hardware features).
-
-- `services.zfs.zed.enableMail` now uses the global `sendmail` wrapper defined by an email module
-  (such as msmtp or Postfix). It no longer requires using a special ZFS build with email support.
-
-- `castopod` has some migration actions to be taken in case of a S3 setup. Some new features may also need some manual migration actions. See [https://code.castopod.org/adaures/castopod/-/releases](https://code.castopod.org/adaures/castopod/-/releases) for more informations.
-
-- `nextcloud-setup.service` no longer changes the group of each file & directory inside `/var/lib/nextcloud/{config,data,store-apps}` if one of these directories has the wrong owner group. This was part of transitioning the group used for `/var/lib/nextcloud`, but isn't necessary anymore.
-
 - `services.kavita` now uses the free-form option `services.kavita.settings` for the application settings file.
   The options `services.kavita.ipAdresses` and `services.kavita.port` now exist at `services.kavita.settings.IpAddresses`
   and `services.kavita.settings.IpAddresses`. The file at `services.kavita.tokenKeyFile` now needs to contain a secret with
   512+ bits instead of 128+ bits.
 
-- `kavita` has been updated to 0.8.0, requiring a manual forced library scan on all libraries for migration. Refer to upstream's [release notes](https://github.com/Kareadita/Kavita/releases/tag/v0.8.0) for details.
-
-- `krb5` module has been rewritten and moved to `security.krb5`, moving all options but `security.krb5.enable` and `security.krb5.package` into `security.krb5.settings`.
+- `services.netbird` now allows running multiple tunnels in parallel through [`services.netbird.tunnels`](#opt-services.netbird.tunnels).
 
-- `services.soju` now has a wrapper for the `sojuctl` command, pointed at the service config file. It also has the new option `adminSocket.enable`, which creates a unix admin socket at `/run/soju/admin`.
+- `services.nginx.virtualHosts` using `forceSSL` or
+  `globalRedirect` can now have redirect codes other than 301 through `redirectCode`.
 
-- `gitea` upgrade to 1.21 has several breaking changes, including:
-  - Custom themes and other assets that were previously stored in `custom/public/*` now belong in `custom/public/assets/*`
-  - New instances of Gitea using MySQL now ignore the `[database].CHARSET` config option and always use the `utf8mb4` charset, existing instances should migrate via the `gitea doctor convert` CLI command.
+- `services.openssh` now has an option `authorizedKeysInHomedir`, controlling whether `~/.ssh/authorizedKeys` is
+  added to `authorizedKeysFiles`.
+  ::: {.note}
+  This option currently defaults to `true` for NixOS 24.05, preserving the previous behaviour.
+  This is expected to change in NixOS 24.11.
+  :::
+  ::: {.warning}
+  Users should check that their SSH keys are in `users.users.*.openssh`, or that they have another way to access
+  and administer the system, before setting this option to `false`.
+  :::
 
 - `services.paperless` module no longer uses the previously downloaded NLTK data stored in `/var/cache/paperless/nltk`. This directory can be removed.
 
-- `services.teeworlds` module now has a wealth of configuration options, including a new `package` option.
-
-- `hardware.pulseaudio` module now sets permission of pulse user home directory to 755 when running in "systemWide" mode. It fixes [issue 114399](https://github.com/NixOS/nixpkgs/issues/114399).
-
-- `services.networkmanager.extraConfig` was renamed to `services.networkmanager.settings` and was changed to use the ini type instead of using a multiline string.
-
-- `services.github-runner` module has been removed. To configure a single GitHub Actions Runner refer to `services.github-runners.*`. Note that this will trigger a new runner registration.
+- `services.postgresql.extraPlugins`' type has expanded. Previously it was a list of packages, now it can also be a function that returns such a list.
+  For example a config line like ``services.postgresql.extraPlugins = with pkgs.postgresql_11.pkgs; [ postgis ];`` is recommended to be changed to ``services.postgresql.extraPlugins = ps: with ps; [ postgis ];``;
 
 - `services.slskd` has been refactored to include more configuation options in
   the free-form `services.slskd.settings` option, and some defaults (including listen ports)
@@ -649,28 +725,103 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
   disabled by default, and the log rotation timer has been removed.
   The nginx virtualhost option is now of the `vhost-options` type.
 
-- `services.btrbk` now automatically selects and provides required compression
-  program depending on the configured `stream_compress` option. Since this
-  replaces the need for the `extraPackages` option, this option will be
-  deprecated in future releases.
+- `services.soju` now has a wrapper for the `sojuctl` command, pointed at the service config file. It also has the new option `adminSocket.enable`, which creates a unix admin socket at `/run/soju/admin`.
+
+- `services.stalwart-mail` uses the legacy version 0.6.X as default because newer `stalwart-mail` versions require a [manual upgrade process](https://github.com/stalwartlabs/mail-server/blob/main/UPGRADING.md). Change [`services.stalwart-mail.package`](#opt-services.stalwart-mail.package) to `pkgs.stalwart-mail` if you wish to switch to the new version.
 
-- `mpich` package expression now requires `withPm` to be a list, e.g. `"hydra:gforker"` becomes `[ "hydra" "gforker" ]`.
+- `services.teeworlds` module now has a wealth of configuration options, including a new `package` option.
+
+- `services.xserver.desktopManager.budgie` installs `gnome.gnome-terminal` by default (instead of `mate.mate-terminal`).
+
+- `services.zfs.zed.enableMail` now uses the global `sendmail` wrapper defined by an email module
+  (such as msmtp or Postfix). It no longer requires using a special ZFS build with email support.
+
+- `sonarr` version was bumped to from 3.0.10 to 4.0.3. Consequently existing config database files will be upgraded automatically, but note that some old apparently-working configs [might actually be corrupt and fail to upgrade cleanly](https://forums.sonarr.tv/t/sonarr-v4-released/33089).
+
+- `stdenv`: The `--replace` flag in `substitute`, `substituteInPlace`, `substituteAll`, `substituteAllStream`, and `substituteStream` is now deprecated if favor of the new `--replace-fail`, `--replace-warn` and `--replace-quiet`. The deprecated `--replace` equates to `--replace-warn`.
 
 - `systemd`: when merging unit options (of type `unitOption`),
   if at least one definition is a list, all those which aren't are now lifted into a list,
   making it possible to accumulate definitions without resorting to `mkForce`,
   hence to retain the definitions not anticipating that need.
 
-- `youtrack` is bumped to 2023.3. The update is not performed automatically, it requires manual interaction. See the YouTrack section in the manual for details.
+- `systemd` units can now specify the `Upholds=` and `UpheldBy=` unit dependencies via the aptly
+  named `upholds` and `upheldBy` options. These options get systemd to enforce that the
+  dependencies remain continuosly running for as long as the dependent unit is in a running state.
 
-- QtMultimedia has changed its default backend to `QT_MEDIA_BACKEND=ffmpeg` (previously `gstreamer` on Linux or `darwin` on MacOS).
-  The previous native backends remain available but are now minimally maintained. Refer to [upstream documentation](https://doc.qt.io/qt-6/qtmultimedia-index.html#ffmpeg-as-the-default-backend) for further details about each platform.
+- A stdenv's default set of hardening flags can now be set via its `bintools-wrapper`'s `defaultHardeningFlags` argument. A convenient stdenv adapter, `withDefaultHardeningFlags`, can be used to override an existing stdenv's `defaultHardeningFlags`.
 
-- `drbd` out-of-tree Linux kernel driver has been added in version 9.2.7. With it the DRBD 9.x features can be used instead of the 8.x features provided by the 8.4.11 in-tree driver.
+- Programs written in [Nim](https://nim-lang.org/) are built with libraries selected by lockfiles.
+  The `nimPackages` and `nim2Packages` sets have been removed.
+  See https://nixos.org/manual/nixpkgs/unstable#nim for more information.
 
-- [TODO: reword to place an attribute at the front] The oil shell's c++ version is now available as `oils-for-unix`. The python version is still available as `oil`
+- The EC2 image module now enables the [Amazon SSM Agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html) by default.
 
-- `documentation.man.mandoc` now by default uses `MANPATH` to set the directories where mandoc will search for manual pages.
-  This enables mandoc to find manual pages in Nix profiles. To set the manual search paths via the `mandoc.conf` configuration file like before, use `documentation.man.mandoc.settings.manpath` instead.
+- A new abstraction to create both read-only as well as writable overlay file
+  systems was added. Available via
+  [fileSystems.overlay](#opt-fileSystems._name_.overlay.lowerdir). See also the
+  [NixOS docs](#sec-overlayfs).
+
+- A new hardening flag, `zerocallusedregs` was made available, corresponding to the gcc/clang option `-fzero-call-used-regs=used-gpr`.
+
+- A new hardening flag, `trivialautovarinit` was made available, corresponding to the gcc/clang option `-ftrivial-auto-var-init=pattern`.
+
+- `dnsdist` has new options to enable and configure a DNSCrypt endpoint (see `services.dnsdist.dnscrypt.enable`, etc.).
+  The module can generate the DNSCrypt provider key pair and certificates, and also rotates them automatically with no downtime.
+
+- The kernel Yama LSM is now enabled by default, which prevents ptracing
+  non-child processes. This means you will not be able to attach gdb to an
+  existing process, but will need to start that process from gdb (so it is a
+  child). Or you can set `boot.kernel.sysctl."kernel.yama.ptrace_scope"` to 0.
 
-- `grafana-loki` package was updated to 3.0.0 which includes [breaking changes](https://github.com/grafana/loki/releases/tag/v3.0.0)
+- Lisp modules: previously deprecated interface based on `common-lisp.sh` has now been removed.
+
+- The `systemd-confinement` module extension is now compatible with `DynamicUser=true` and thus `ProtectSystem=strict` too.
+
+
+## Nixpkgs Library {#sec-release-24.05-nixpkgs-lib}
+
+### Additions and Improvements {#sec-release-24.05-lib-additions-improvements}
+
+New functions:
+- [`lib.asserts.assertEachOneOf`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.asserts.assertEachOneOf): Check that each value is one of the allowed ones.
+- [`lib.attrsets.longestValidPathPrefix`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.attrsets.longestValidPathPrefix): The longest prefix of an attribute path that refers to an existing attribute in a nesting of attribute sets.
+- [`lib.attrsets.mapCartesianProduct`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.attrsets.mapCartesianProduct): Apply a function to the cartesian product of attribute set value combinations.
+- [`lib.trivial.xor`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.trivial.xor): Boolean "exclusive or"
+- [`lib.lists.ifilter0`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.lists.ifilter0): Filter a list for elements that satisfy a predicate function. The predicate function is called with both the index and value for each element.
+- [`lib.lists.sortOn`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.lists.sortOn): Sort a list based on the default comparison of a derived property.
+- [`lib.path.hasStorePathPrefix`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.path.hasStorePathPrefix): Whether a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path) has a [store path](https://nixos.org/manual/nix/stable/store/store-path.html#store-path) as a prefix.
+- [`lib.filesystem.packagesFromDirectoryRecursive`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.filesystem.packagesFromDirectoryRecursive): Transform a directory tree containing package files suitable for `callPackage` into a matching nested attribute set of derivations.
+- [`lib.fileset.toList`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.fileset.toList): The list of file paths contained in a given file set.
+- [`lib.fileset.maybeMissing`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.fileset.maybeMissing): Create a file set from a path that may or may not exist.
+- [`lib.derivations.optionalDrvAttr`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.derivations.optionalDrvAttr): Conditionally set a derivation attribute.
+- [`lib.strings.makeIncludePath`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.strings.makeIncludePath): Construct an include search path (such as `C_INCLUDE_PATH`) containing the header files for a set of packages or paths.
+
+Improvements:
+- [`lib.fixedPoints.extends`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.fixedPoints.extends): Better documentation
+- [`lib.customisation.makeScope`](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.customisation.makeScope): Better documentation
+- [`lib.derivations.lazyDerivation`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.derivations.lazyDerivation): Now supports multiple outputs with an `outputs` argument
+- [`lib.gvariant`](https://nixos.org/manual/nixpkgs/unstable#sec-functions-library-gvariant): Better error message for integers and attribute set values
+- [`lib.filesets.gitTracked`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.fileset.gitTracked): Now works within store paths
+
+Misc:
+- The [`lib/` directory](https://github.com/NixOS/nixpkgs/tree/release-24.05/lib) is a self-contained flake now, including a working [`lib.trivial.version`](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.trivial.version) (but note that fetching a subtree by itself is not supported in Nix yet)
+- [Various minor performance improvements](https://github.com/NixOS/nixpkgs/issues?q=author%3Aadisbladis+label%3A%226.topic%3A+lib%22+created%3A%3C2024-05-31+)
+
+Module System:
+- New types:
+  - [`types.attrTag`](https://nixos.org/manual/nixos/unstable/#sec-option-types-sums): A tagged union type
+  - `types.nonEmptyListOf`: A non-empty list
+- Improved types:
+  - `types.uniq`/`unique` now check the wrapped type
+
+### Deprecations {#sec-release-24.05-lib-deprecations}
+
+- [`lib.options.mdDoc`](https://nixos.org/manual/nixpkgs/unstable#function-library-lib.options.mdDoc) is obsolete and now emits a warning. The core ecosystem has completely migrated to markdown, so marking markdown as markdown is redundant.
+- `lib.attrsets.zipWithNames` is now a deprecated alias of [`lib.attrsets.zipAttrsWithNames`](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.attrsets.zipAttrsWithNames)
+- `lib.attrsets.cartesianProductOfSets` has been renamed to [`lib.attrsets.cartesianProduct`](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.attrsets.cartesianProduct)
+
+### Internal {#sec-release-24.05-lib-internal}
+- `lib` now has [Readme for contributing](https://github.com/NixOS/nixpkgs/tree/master/lib#readme).
+- Some function's documentation is now written using the [accepted doc comment syntax](https://github.com/NixOS/rfcs/pull/145).
+- `odoo` has been updated from `16.0.20231024` to `17.0.20240507`.
diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md
new file mode 100644
index 0000000000000..49bfdf2e2b323
--- /dev/null
+++ b/nixos/doc/manual/release-notes/rl-2411.section.md
@@ -0,0 +1,136 @@
+# Release 24.11 (“Vicuña”, 2024.11/??) {#sec-release-24.11}
+
+<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
+
+## Highlights {#sec-release-24.11-highlights}
+
+- Convenience options for `amdgpu`, open source driver for Radeon cards, is now available under `hardware.amdgpu`.
+
+- [AMDVLK](https://github.com/GPUOpen-Drivers/AMDVLK), AMD's open source Vulkan driver, is now available to be configured as `hardware.amdgpu.amdvlk` option.
+  This also allows configuring runtime settings of AMDVLK and enabling experimental features.
+
+## New Services {#sec-release-24.11-new-services}
+
+- [Open-WebUI](https://github.com/open-webui/open-webui), a user-friendly WebUI
+  for LLMs. Available as [services.open-webui](#opt-services.open-webui.enable)
+  service.
+
+- [Quickwit](https://quickwit.io), sub-second search & analytics engine on cloud storage. Available as [services.quickwit](options.html#opt-services.quickwit).
+
+- [Flood](https://flood.js.org/), a beautiful WebUI for various torrent clients. Available as [services.flood](options.html#opt-services.flood).
+
+- [Renovate](https://github.com/renovatebot/renovate), a dependency updating tool for various git forges and language ecosystems. Available as [services.renovate](#opt-services.renovate.enable).
+
+## Backward Incompatibilities {#sec-release-24.11-incompatibilities}
+
+- `transmission` package has been aliased with a `trace` warning to `transmission_3`. Since [Transmission 4 has been released last year](https://github.com/transmission/transmission/releases/tag/4.0.0), and Transmission 3 will eventually go away, it was decided perform this warning alias to make people aware of the new version. The `services.transmission.package` defaults to `transmission_3` as well because the upgrade can cause data loss in certain specific usage patterns (examples: [#5153](https://github.com/transmission/transmission/issues/5153), [#6796](https://github.com/transmission/transmission/issues/6796)). Please make sure to back up to your data directory per your usage:
+  - `transmission-gtk`: `~/.config/transmission`
+  - `transmission-daemon` using NixOS module: `${config.services.transmission.home}/.config/transmission-daemon` (defaults to `/var/lib/transmission/.config/transmission-daemon`)
+
+- `androidenv.androidPkgs_9_0` has been removed, and replaced with `androidenv.androidPkgs` for a more complete Android SDK including support for Android 9 and later.
+
+- `wstunnel` has had a major version upgrade that entailed rewriting the program in Rust.
+  The module was updated to accommodate for breaking changes.
+  Breaking changes to the module API were minimised as much as possible,
+  but some were nonetheless inevitable due to changes in the upstream CLI.
+  Certain options were moved from separate CLI arguments into the forward specifications,
+  and those options were also removed from the module's API,
+  please consult the wstunnel man page for more detail.
+  Also be aware that if you have set additional options in `services.wstunnel.{clients,servers}.<name>.extraArgs`,
+  that those might have been removed or modified upstream.
+
+- `clang-tools_<version>` packages have been moved into `llvmPackages_<version>` (i.e. `clang-tools_18` is now `llvmPackages_18.clang-tools`).
+  - For convenience, the top-level `clang-tools` attribute remains and is now bound to `llvmPackages.clang-tools`.
+  - Top-level `clang_tools_<version>` attributes are now aliases; these will be removed in a future release.
+
+- `nginx` package no longer includes `gd` and `geoip` dependencies. For enabling it, override `nginx` package with the optionals `withImageFilter` and `withGeoIP`.
+
+- `openssh` and `openssh_hpn` are now compiled without Kerberos 5 / GSSAPI support in an effort to reduce the attack surface of the components for the majority of users. Users needing this support can
+  use the new `opensshWithKerberos` and `openssh_hpnWithKerberos` flavors (e.g. `programs.ssh.package = pkgs.openssh_gssapi`).
+
+- `security.ipa.ipaHostname` now defaults to the value of `networking.fqdn` if
+  it is set, instead of the previous hardcoded default of
+  `${networking.hostName}.${security.ipa.domain}`.
+
+- `nvimpager` was updated to version 0.13.0, which changes the order of user and
+  nvimpager settings: user commands in `-c` and `--cmd` now override the
+  respective default settings because they are executed later.
+
+- `services.forgejo.mailerPasswordFile` has been deprecated by the drop-in replacement `services.forgejo.secrets.mailer.PASSWD`,
+  which is part of the new free-form `services.forgejo.secrets` option.
+  `services.forgejo.secrets` is a small wrapper over systemd's `LoadCredential=`. It has the same structure (sections/keys) as
+  `services.forgejo.settings` but takes file paths that will be read before service startup instead of some plaintext value.
+
+- `services.ddclient.use` has been deprecated: `ddclient` now supports separate IPv4 and IPv6 configuration. Use `services.ddclient.usev4` and `services.ddclient.usev6` instead.
+
+- `vaultwarden` lost the capability to bind to privileged ports. If you rely on
+   this behavior, override the systemd unit to allow `CAP_NET_BIND_SERVICE` in
+   your local configuration.
+
+- The Invoiceplane module now only accepts the structured `settings` option.
+  `extraConfig` is now removed.
+
+- Legacy package `stalwart-mail_0_6` was dropped, please note the
+  [manual upgrade process](https://github.com/stalwartlabs/mail-server/blob/main/UPGRADING.md)
+  before changing the package to `pkgs.stalwart-mail` in
+  [`services.stalwart-mail.package`](#opt-services.stalwart-mail.package).
+
+- `androidndkPkgs` has been updated to `androidndkPkgs_26`.
+
+- Android NDK version 26 and SDK version 33 are now the default versions used for cross compilation to android.
+
+- `haskell.lib.compose.justStaticExecutables` now disallows references to GHC in the
+  output by default, to alert users to closure size issues caused by
+  [#164630](https://github.com/NixOS/nixpkgs/issues/164630). See ["Packaging
+  Helpers" in the Haskell section of the Nixpkgs
+  manual](https://nixos.org/manual/nixpkgs/unstable/#haskell-packaging-helpers)
+  for information on working around `output '...' is not allowed to refer to
+  the following paths` errors caused by this change.
+
+- The `stalwart-mail` service now runs under the `stalwart-mail` system user
+  instead of a dynamically created one via `DynamicUser`, to avoid automatic
+  ownership changes on its large file store each time the service was started.
+  This change requires to manually move the state directory from
+  `/var/lib/private/stalwart-mail` to `/var/lib/stalwart-mail` and to
+  change the ownership of the directory and its content to `stalwart-mail`.
+
+- The `stalwart-mail` module now uses RocksDB as the default storage backend
+  for `stateVersion` ≥ 24.11. (It was previously using SQLite for structured
+  data and the filesystem for blobs).
+
+- `libe57format` has been updated to `>= 3.0.0`, which contains some backward-incompatible API changes. See the [release note](https://github.com/asmaloney/libE57Format/releases/tag/v3.0.0) for more details.
+
+- `zx` was updated to v8, which introduces several breaking changes.
+  See the [v8 changelog](https://github.com/google/zx/releases/tag/8.0.0) for more information.
+
+- The `portunus` package and service do not support weak password hashes anymore.
+  If you installed Portunus on NixOS 23.11 or earlier, upgrade to NixOS 24.05 first to get support for strong password hashing.
+  Then, follow the instructions on the [upstream release notes](https://github.com/majewsky/portunus/releases/tag/v2.0.0) to upgrade all existing user accounts to strong password hashes.
+  If you need to upgrade to 24.11 without having completed the migration, consider the security implications of weak password hashes on your user accounts, and add the following to your configuration:
+  ```nix
+  services.portunus.package      = pkgs.portunus.override { libxcrypt = pkgs.libxcrypt-legacy; };
+  services.portunus.ldap.package = pkgs.openldap.override { libxcrypt = pkgs.libxcrypt-legacy; };
+  ```
+
+- `keycloak` was updated to version 25, which introduces new hostname related options.
+  See [Upgrading Guide](https://www.keycloak.org/docs/25.0.1/upgrading/#migrating-to-25-0-0) for instructions.
+
+- The `tracy` package no longer works on X11, since it's moved to Wayland
+  support, which is the intended default behavior by Tracy maintainers.
+  X11 users have to switch to the new package `tracy-x11`.
+
+## Other Notable Changes {#sec-release-24.11-notable-changes}
+
+<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
+
+- The `zerocallusedregs` hardening flag is enabled by default on compilers that support it.
+
+- The `stackclashprotection` hardening flag has been added, though disabled by default.
+
+- `hareHook` has been added as the language framework for Hare. From now on, it,
+  not the `hare` package, should be added to `nativeBuildInputs` when building
+  Hare programs.
+
+- To facilitate dependency injection, the `imgui` package now builds a static archive using vcpkg' CMake rules.
+  The derivation now installs "impl" headers selectively instead of by a wildcard.
+  Use `imgui.src` if you just want to access the unpacked sources.
diff --git a/nixos/lib/make-disk-image.nix b/nixos/lib/make-disk-image.nix
index 9bdbf4e0713de..1220bbfd5ed7c 100644
--- a/nixos/lib/make-disk-image.nix
+++ b/nixos/lib/make-disk-image.nix
@@ -603,10 +603,11 @@ let format' = format; in let
       ${lib.optionalString installBootLoader ''
         # In this throwaway resource, we only have /dev/vda, but the actual VM may refer to another disk for bootloader, e.g. /dev/vdb
         # Use this option to create a symlink from vda to any arbitrary device you want.
-        ${optionalString (config.boot.loader.grub.enable && config.boot.loader.grub.device != "/dev/vda") ''
-            mkdir -p $(dirname ${config.boot.loader.grub.device})
-            ln -s /dev/vda ${config.boot.loader.grub.device}
-        ''}
+        ${optionalString (config.boot.loader.grub.enable) (lib.concatMapStringsSep " " (device:
+          lib.optionalString (device != "/dev/vda") ''
+            mkdir -p "$(dirname ${device})"
+            ln -s /dev/vda ${device}
+          '') config.boot.loader.grub.devices)}
 
         # Set up core system link, bootloader (sd-boot, GRUB, uboot, etc.), etc.
 
diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix
index eef49f8c4ef38..dac5cc7b700c8 100644
--- a/nixos/lib/systemd-lib.nix
+++ b/nixos/lib/systemd-lib.nix
@@ -18,6 +18,7 @@ let
     flip
     head
     isInt
+    isFloat
     isList
     isPath
     length
@@ -152,7 +153,7 @@ in rec {
       "Systemd ${group} field `${name}' is outside the range [${toString min},${toString max}]";
 
   assertRangeOrOneOf = name: min: max: values: group: attr:
-    optional (attr ? ${name} && !((min <= attr.${name} && max >= attr.${name}) || elem attr.${name} values))
+    optional (attr ? ${name} && !(((isInt attr.${name} || isFloat attr.${name}) && min <= attr.${name} && max >= attr.${name}) || elem attr.${name} values))
       "Systemd ${group} field `${name}' is not a value in range [${toString min},${toString max}], or one of ${toString values}";
 
   assertMinimum = name: min: group: attr:
@@ -181,6 +182,30 @@ in rec {
   in if errors == [] then true
      else trace (concatStringsSep "\n" errors) false;
 
+  checkUnitConfigWithLegacyKey = legacyKey: group: checks: attrs:
+    let
+      dump = lib.generators.toPretty { }
+        (lib.generators.withRecursion { depthLimit = 2; throwOnDepthLimit = false; } attrs);
+      attrs' =
+        if legacyKey == null
+          then attrs
+        else if ! attrs?${legacyKey}
+          then attrs
+        else if removeAttrs attrs [ legacyKey ] == {}
+          then attrs.${legacyKey}
+        else throw ''
+          The declaration
+
+          ${dump}
+
+          must not mix unit options with the legacy key '${legacyKey}'.
+
+          This can be fixed by moving all settings from within ${legacyKey}
+          one level up.
+        '';
+    in
+    checkUnitConfig group checks attrs';
+
   toOption = x:
     if x == true then "true"
     else if x == false then "false"
diff --git a/nixos/lib/systemd-network-units.nix b/nixos/lib/systemd-network-units.nix
index ae581495772a8..c35309a6d2628 100644
--- a/nixos/lib/systemd-network-units.nix
+++ b/nixos/lib/systemd-network-units.nix
@@ -63,13 +63,13 @@ in {
       ${attrsToSection def.l2tpConfig}
     '' + flip concatMapStrings def.l2tpSessions (x: ''
       [L2TPSession]
-      ${attrsToSection x.l2tpSessionConfig}
+      ${attrsToSection x}
     '') + optionalString (def.wireguardConfig != { }) ''
       [WireGuard]
       ${attrsToSection def.wireguardConfig}
     '' + flip concatMapStrings def.wireguardPeers (x: ''
       [WireGuardPeer]
-      ${attrsToSection x.wireguardPeerConfig}
+      ${attrsToSection x}
     '') + optionalString (def.bondConfig != { }) ''
       [Bond]
       ${attrsToSection def.bondConfig}
@@ -122,13 +122,13 @@ in {
       ${concatStringsSep "\n" (map (s: "Xfrm=${s}") def.xfrm)}
     '' + "\n" + flip concatMapStrings def.addresses (x: ''
       [Address]
-      ${attrsToSection x.addressConfig}
+      ${attrsToSection x}
     '') + flip concatMapStrings def.routingPolicyRules (x: ''
       [RoutingPolicyRule]
-      ${attrsToSection x.routingPolicyRuleConfig}
+      ${attrsToSection x}
     '') + flip concatMapStrings def.routes (x: ''
       [Route]
-      ${attrsToSection x.routeConfig}
+      ${attrsToSection x}
     '') + optionalString (def.dhcpV4Config != { }) ''
       [DHCPv4]
       ${attrsToSection def.dhcpV4Config}
@@ -147,24 +147,27 @@ in {
     '' + optionalString (def.ipv6SendRAConfig != { }) ''
       [IPv6SendRA]
       ${attrsToSection def.ipv6SendRAConfig}
-    '' + flip concatMapStrings def.ipv6Prefixes (x: ''
+    '' + flip concatMapStrings def.ipv6PREF64Prefixes (x: ''
+      [IPv6PREF64Prefix]
+      ${attrsToSection x}
+    '') + flip concatMapStrings def.ipv6Prefixes (x: ''
       [IPv6Prefix]
-      ${attrsToSection x.ipv6PrefixConfig}
+      ${attrsToSection x}
     '') + flip concatMapStrings def.ipv6RoutePrefixes (x: ''
       [IPv6RoutePrefix]
-      ${attrsToSection x.ipv6RoutePrefixConfig}
+      ${attrsToSection x}
     '') + flip concatMapStrings def.dhcpServerStaticLeases (x: ''
       [DHCPServerStaticLease]
-      ${attrsToSection x.dhcpServerStaticLeaseConfig}
+      ${attrsToSection x}
     '') + optionalString (def.bridgeConfig != { }) ''
       [Bridge]
       ${attrsToSection def.bridgeConfig}
     '' + flip concatMapStrings def.bridgeFDBs (x: ''
       [BridgeFDB]
-      ${attrsToSection x.bridgeFDBConfig}
+      ${attrsToSection x}
     '') + flip concatMapStrings def.bridgeMDBs (x: ''
       [BridgeMDB]
-      ${attrsToSection x.bridgeMDBConfig}
+      ${attrsToSection x}
     '') + optionalString (def.lldpConfig != { }) ''
       [LLDP]
       ${attrsToSection def.lldpConfig}
@@ -251,7 +254,7 @@ in {
       ${attrsToSection def.quickFairQueueingConfigClass}
     '' + flip concatMapStrings def.bridgeVLANs (x: ''
       [BridgeVLAN]
-      ${attrsToSection x.bridgeVLANConfig}
+      ${attrsToSection x}
     '') + def.extraConfig;
 
 }
diff --git a/nixos/lib/test-driver/default.nix b/nixos/lib/test-driver/default.nix
index 1acdaacc4e658..26652db6016e6 100644
--- a/nixos/lib/test-driver/default.nix
+++ b/nixos/lib/test-driver/default.nix
@@ -13,17 +13,27 @@
 , extraPythonPackages ? (_ : [])
 , nixosTests
 }:
-
+let
+  fs = lib.fileset;
+in
 python3Packages.buildPythonApplication {
   pname = "nixos-test-driver";
   version = "1.1";
-  src = ./.;
+  src = fs.toSource {
+    root = ./.;
+    fileset = fs.unions [
+      ./pyproject.toml
+      ./test_driver
+      ./extract-docstrings.py
+    ];
+  };
   pyproject = true;
 
   propagatedBuildInputs = [
     coreutils
     netpbm
     python3Packages.colorama
+    python3Packages.junit-xml
     python3Packages.ptpython
     qemu_pkg
     socat
@@ -46,7 +56,7 @@ python3Packages.buildPythonApplication {
     echo -e "\x1b[32m## run mypy\x1b[0m"
     mypy test_driver extract-docstrings.py
     echo -e "\x1b[32m## run ruff\x1b[0m"
-    ruff .
+    ruff check .
     echo -e "\x1b[32m## run black\x1b[0m"
     black --check --diff .
   '';
diff --git a/nixos/lib/test-driver/pyproject.toml b/nixos/lib/test-driver/pyproject.toml
index 17b7130a4bad7..714139bc1b25c 100644
--- a/nixos/lib/test-driver/pyproject.toml
+++ b/nixos/lib/test-driver/pyproject.toml
@@ -19,8 +19,8 @@ test_driver = ["py.typed"]
 [tool.ruff]
 line-length = 88
 
-select = ["E", "F", "I", "U", "N"]
-ignore = ["E501"]
+lint.select = ["E", "F", "I", "U", "N"]
+lint.ignore = ["E501"]
 
 # xxx: we can import https://pypi.org/project/types-colorama/ here
 [[tool.mypy.overrides]]
@@ -31,6 +31,10 @@ ignore_missing_imports = true
 module = "ptpython.*"
 ignore_missing_imports = true
 
+[[tool.mypy.overrides]]
+module = "junit_xml.*"
+ignore_missing_imports = true
+
 [tool.black]
 line-length = 88
 target-version = ['py39']
diff --git a/nixos/lib/test-driver/test_driver/__init__.py b/nixos/lib/test-driver/test_driver/__init__.py
index 9daae1e941a65..42b6d29b76714 100755
--- a/nixos/lib/test-driver/test_driver/__init__.py
+++ b/nixos/lib/test-driver/test_driver/__init__.py
@@ -6,7 +6,12 @@ from pathlib import Path
 import ptpython.repl
 
 from test_driver.driver import Driver
-from test_driver.logger import rootlog
+from test_driver.logger import (
+    CompositeLogger,
+    JunitXMLLogger,
+    TerminalLogger,
+    XMLLogger,
+)
 
 
 class EnvDefault(argparse.Action):
@@ -93,6 +98,11 @@ def main() -> None:
         type=writeable_dir,
     )
     arg_parser.add_argument(
+        "--junit-xml",
+        help="Enable JunitXML report generation to the given path",
+        type=Path,
+    )
+    arg_parser.add_argument(
         "testscript",
         action=EnvDefault,
         envvar="testScript",
@@ -102,14 +112,24 @@ def main() -> None:
 
     args = arg_parser.parse_args()
 
+    output_directory = args.output_directory.resolve()
+    logger = CompositeLogger([TerminalLogger()])
+
+    if "LOGFILE" in os.environ.keys():
+        logger.add_logger(XMLLogger(os.environ["LOGFILE"]))
+
+    if args.junit_xml:
+        logger.add_logger(JunitXMLLogger(output_directory / args.junit_xml))
+
     if not args.keep_vm_state:
-        rootlog.info("Machine state will be reset. To keep it, pass --keep-vm-state")
+        logger.info("Machine state will be reset. To keep it, pass --keep-vm-state")
 
     with Driver(
         args.start_scripts,
         args.vlans,
         args.testscript.read_text(),
-        args.output_directory.resolve(),
+        output_directory,
+        logger,
         args.keep_vm_state,
         args.global_timeout,
     ) as driver:
@@ -125,7 +145,7 @@ def main() -> None:
             tic = time.time()
             driver.run_tests()
             toc = time.time()
-            rootlog.info(f"test script finished in {(toc-tic):.2f}s")
+            logger.info(f"test script finished in {(toc-tic):.2f}s")
 
 
 def generate_driver_symbols() -> None:
@@ -134,7 +154,7 @@ def generate_driver_symbols() -> None:
     in user's test scripts. That list is then used by pyflakes to lint those
     scripts.
     """
-    d = Driver([], [], "", Path())
+    d = Driver([], [], "", Path(), CompositeLogger([]))
     test_symbols = d.test_symbols()
     with open("driver-symbols", "w") as fp:
         fp.write(",".join(test_symbols.keys()))
diff --git a/nixos/lib/test-driver/test_driver/driver.py b/nixos/lib/test-driver/test_driver/driver.py
index f792c04591996..01b64b92e9770 100644
--- a/nixos/lib/test-driver/test_driver/driver.py
+++ b/nixos/lib/test-driver/test_driver/driver.py
@@ -9,7 +9,7 @@ from typing import Any, Callable, ContextManager, Dict, Iterator, List, Optional
 
 from colorama import Fore, Style
 
-from test_driver.logger import rootlog
+from test_driver.logger import AbstractLogger
 from test_driver.machine import Machine, NixStartScript, retry
 from test_driver.polling_condition import PollingCondition
 from test_driver.vlan import VLan
@@ -49,6 +49,7 @@ class Driver:
     polling_conditions: List[PollingCondition]
     global_timeout: int
     race_timer: threading.Timer
+    logger: AbstractLogger
 
     def __init__(
         self,
@@ -56,6 +57,7 @@ class Driver:
         vlans: List[int],
         tests: str,
         out_dir: Path,
+        logger: AbstractLogger,
         keep_vm_state: bool = False,
         global_timeout: int = 24 * 60 * 60 * 7,
     ):
@@ -63,12 +65,13 @@ class Driver:
         self.out_dir = out_dir
         self.global_timeout = global_timeout
         self.race_timer = threading.Timer(global_timeout, self.terminate_test)
+        self.logger = logger
 
         tmp_dir = get_tmp_dir()
 
-        with rootlog.nested("start all VLans"):
+        with self.logger.nested("start all VLans"):
             vlans = list(set(vlans))
-            self.vlans = [VLan(nr, tmp_dir) for nr in vlans]
+            self.vlans = [VLan(nr, tmp_dir, self.logger) for nr in vlans]
 
         def cmd(scripts: List[str]) -> Iterator[NixStartScript]:
             for s in scripts:
@@ -84,6 +87,7 @@ class Driver:
                 tmp_dir=tmp_dir,
                 callbacks=[self.check_polling_conditions],
                 out_dir=self.out_dir,
+                logger=self.logger,
             )
             for cmd in cmd(start_scripts)
         ]
@@ -92,19 +96,18 @@ class Driver:
         return self
 
     def __exit__(self, *_: Any) -> None:
-        with rootlog.nested("cleanup"):
+        with self.logger.nested("cleanup"):
             self.race_timer.cancel()
             for machine in self.machines:
                 machine.release()
 
     def subtest(self, name: str) -> Iterator[None]:
         """Group logs under a given test name"""
-        with rootlog.nested("subtest: " + name):
+        with self.logger.subtest(name):
             try:
                 yield
-                return True
             except Exception as e:
-                rootlog.error(f'Test "{name}" failed with error: "{e}"')
+                self.logger.error(f'Test "{name}" failed with error: "{e}"')
                 raise e
 
     def test_symbols(self) -> Dict[str, Any]:
@@ -118,7 +121,7 @@ class Driver:
             machines=self.machines,
             vlans=self.vlans,
             driver=self,
-            log=rootlog,
+            log=self.logger,
             os=os,
             create_machine=self.create_machine,
             subtest=subtest,
@@ -150,13 +153,13 @@ class Driver:
 
     def test_script(self) -> None:
         """Run the test script"""
-        with rootlog.nested("run the VM test script"):
+        with self.logger.nested("run the VM test script"):
             symbols = self.test_symbols()  # call eagerly
             exec(self.tests, symbols, None)
 
     def run_tests(self) -> None:
         """Run the test script (for non-interactive test runs)"""
-        rootlog.info(
+        self.logger.info(
             f"Test will time out and terminate in {self.global_timeout} seconds"
         )
         self.race_timer.start()
@@ -168,13 +171,13 @@ class Driver:
 
     def start_all(self) -> None:
         """Start all machines"""
-        with rootlog.nested("start all VMs"):
+        with self.logger.nested("start all VMs"):
             for machine in self.machines:
                 machine.start()
 
     def join_all(self) -> None:
         """Wait for all machines to shut down"""
-        with rootlog.nested("wait for all VMs to finish"):
+        with self.logger.nested("wait for all VMs to finish"):
             for machine in self.machines:
                 machine.wait_for_shutdown()
             self.race_timer.cancel()
@@ -182,7 +185,7 @@ class Driver:
     def terminate_test(self) -> None:
         # This will be usually running in another thread than
         # the thread actually executing the test script.
-        with rootlog.nested("timeout reached; test terminating..."):
+        with self.logger.nested("timeout reached; test terminating..."):
             for machine in self.machines:
                 machine.release()
             # As we cannot `sys.exit` from another thread
@@ -227,7 +230,7 @@ class Driver:
                     f"Unsupported arguments passed to create_machine: {args}"
                 )
 
-            rootlog.warning(
+            self.logger.warning(
                 Fore.YELLOW
                 + Style.BRIGHT
                 + "WARNING: Using create_machine with a single dictionary argument is deprecated and will be removed in NixOS 24.11"
@@ -246,13 +249,14 @@ class Driver:
             start_command=cmd,
             name=name,
             keep_vm_state=keep_vm_state,
+            logger=self.logger,
         )
 
     def serial_stdout_on(self) -> None:
-        rootlog._print_serial_logs = True
+        self.logger.print_serial_logs(True)
 
     def serial_stdout_off(self) -> None:
-        rootlog._print_serial_logs = False
+        self.logger.print_serial_logs(False)
 
     def check_polling_conditions(self) -> None:
         for condition in self.polling_conditions:
@@ -271,6 +275,7 @@ class Driver:
             def __init__(self, fun: Callable):
                 self.condition = PollingCondition(
                     fun,
+                    driver.logger,
                     seconds_interval,
                     description,
                 )
@@ -285,15 +290,17 @@ class Driver:
             def wait(self, timeout: int = 900) -> None:
                 def condition(last: bool) -> bool:
                     if last:
-                        rootlog.info(f"Last chance for {self.condition.description}")
+                        driver.logger.info(
+                            f"Last chance for {self.condition.description}"
+                        )
                     ret = self.condition.check(force=True)
                     if not ret and not last:
-                        rootlog.info(
+                        driver.logger.info(
                             f"({self.condition.description} failure not fatal yet)"
                         )
                     return ret
 
-                with rootlog.nested(f"waiting for {self.condition.description}"):
+                with driver.logger.nested(f"waiting for {self.condition.description}"):
                     retry(condition, timeout=timeout)
 
         if fun_ is None:
diff --git a/nixos/lib/test-driver/test_driver/logger.py b/nixos/lib/test-driver/test_driver/logger.py
index 0b0623bddfa1e..484829254b812 100644
--- a/nixos/lib/test-driver/test_driver/logger.py
+++ b/nixos/lib/test-driver/test_driver/logger.py
@@ -1,33 +1,238 @@
+import atexit
 import codecs
 import os
 import sys
 import time
 import unicodedata
-from contextlib import contextmanager
+from abc import ABC, abstractmethod
+from contextlib import ExitStack, contextmanager
+from pathlib import Path
 from queue import Empty, Queue
-from typing import Any, Dict, Iterator
+from typing import Any, Dict, Iterator, List
 from xml.sax.saxutils import XMLGenerator
 from xml.sax.xmlreader import AttributesImpl
 
 from colorama import Fore, Style
+from junit_xml import TestCase, TestSuite
 
 
-class Logger:
-    def __init__(self) -> None:
-        self.logfile = os.environ.get("LOGFILE", "/dev/null")
-        self.logfile_handle = codecs.open(self.logfile, "wb")
-        self.xml = XMLGenerator(self.logfile_handle, encoding="utf-8")
-        self.queue: "Queue[Dict[str, str]]" = Queue()
+class AbstractLogger(ABC):
+    @abstractmethod
+    def log(self, message: str, attributes: Dict[str, str] = {}) -> None:
+        pass
 
-        self.xml.startDocument()
-        self.xml.startElement("logfile", attrs=AttributesImpl({}))
+    @abstractmethod
+    @contextmanager
+    def subtest(self, name: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
+        pass
+
+    @abstractmethod
+    @contextmanager
+    def nested(self, message: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
+        pass
+
+    @abstractmethod
+    def info(self, *args, **kwargs) -> None:  # type: ignore
+        pass
+
+    @abstractmethod
+    def warning(self, *args, **kwargs) -> None:  # type: ignore
+        pass
+
+    @abstractmethod
+    def error(self, *args, **kwargs) -> None:  # type: ignore
+        pass
+
+    @abstractmethod
+    def log_serial(self, message: str, machine: str) -> None:
+        pass
+
+    @abstractmethod
+    def print_serial_logs(self, enable: bool) -> None:
+        pass
+
+
+class JunitXMLLogger(AbstractLogger):
+    class TestCaseState:
+        def __init__(self) -> None:
+            self.stdout = ""
+            self.stderr = ""
+            self.failure = False
+
+    def __init__(self, outfile: Path) -> None:
+        self.tests: dict[str, JunitXMLLogger.TestCaseState] = {
+            "main": self.TestCaseState()
+        }
+        self.currentSubtest = "main"
+        self.outfile: Path = outfile
+        self._print_serial_logs = True
+        atexit.register(self.close)
+
+    def log(self, message: str, attributes: Dict[str, str] = {}) -> None:
+        self.tests[self.currentSubtest].stdout += message + os.linesep
+
+    @contextmanager
+    def subtest(self, name: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
+        old_test = self.currentSubtest
+        self.tests.setdefault(name, self.TestCaseState())
+        self.currentSubtest = name
+
+        yield
+
+        self.currentSubtest = old_test
+
+    @contextmanager
+    def nested(self, message: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
+        self.log(message)
+        yield
+
+    def info(self, *args, **kwargs) -> None:  # type: ignore
+        self.tests[self.currentSubtest].stdout += args[0] + os.linesep
+
+    def warning(self, *args, **kwargs) -> None:  # type: ignore
+        self.tests[self.currentSubtest].stdout += args[0] + os.linesep
+
+    def error(self, *args, **kwargs) -> None:  # type: ignore
+        self.tests[self.currentSubtest].stderr += args[0] + os.linesep
+        self.tests[self.currentSubtest].failure = True
+
+    def log_serial(self, message: str, machine: str) -> None:
+        if not self._print_serial_logs:
+            return
+
+        self.log(f"{machine} # {message}")
+
+    def print_serial_logs(self, enable: bool) -> None:
+        self._print_serial_logs = enable
+
+    def close(self) -> None:
+        with open(self.outfile, "w") as f:
+            test_cases = []
+            for name, test_case_state in self.tests.items():
+                tc = TestCase(
+                    name,
+                    stdout=test_case_state.stdout,
+                    stderr=test_case_state.stderr,
+                )
+                if test_case_state.failure:
+                    tc.add_failure_info("test case failed")
+
+                test_cases.append(tc)
+            ts = TestSuite("NixOS integration test", test_cases)
+            f.write(TestSuite.to_xml_string([ts]))
+
+
+class CompositeLogger(AbstractLogger):
+    def __init__(self, logger_list: List[AbstractLogger]) -> None:
+        self.logger_list = logger_list
+
+    def add_logger(self, logger: AbstractLogger) -> None:
+        self.logger_list.append(logger)
+
+    def log(self, message: str, attributes: Dict[str, str] = {}) -> None:
+        for logger in self.logger_list:
+            logger.log(message, attributes)
+
+    @contextmanager
+    def subtest(self, name: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
+        with ExitStack() as stack:
+            for logger in self.logger_list:
+                stack.enter_context(logger.subtest(name, attributes))
+            yield
+
+    @contextmanager
+    def nested(self, message: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
+        with ExitStack() as stack:
+            for logger in self.logger_list:
+                stack.enter_context(logger.nested(message, attributes))
+            yield
+
+    def info(self, *args, **kwargs) -> None:  # type: ignore
+        for logger in self.logger_list:
+            logger.info(*args, **kwargs)
+
+    def warning(self, *args, **kwargs) -> None:  # type: ignore
+        for logger in self.logger_list:
+            logger.warning(*args, **kwargs)
+
+    def error(self, *args, **kwargs) -> None:  # type: ignore
+        for logger in self.logger_list:
+            logger.error(*args, **kwargs)
+        sys.exit(1)
 
+    def print_serial_logs(self, enable: bool) -> None:
+        for logger in self.logger_list:
+            logger.print_serial_logs(enable)
+
+    def log_serial(self, message: str, machine: str) -> None:
+        for logger in self.logger_list:
+            logger.log_serial(message, machine)
+
+
+class TerminalLogger(AbstractLogger):
+    def __init__(self) -> None:
         self._print_serial_logs = True
 
+    def maybe_prefix(self, message: str, attributes: Dict[str, str]) -> str:
+        if "machine" in attributes:
+            return f"{attributes['machine']}: {message}"
+        return message
+
     @staticmethod
     def _eprint(*args: object, **kwargs: Any) -> None:
         print(*args, file=sys.stderr, **kwargs)
 
+    def log(self, message: str, attributes: Dict[str, str] = {}) -> None:
+        self._eprint(self.maybe_prefix(message, attributes))
+
+    @contextmanager
+    def subtest(self, name: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
+        with self.nested("subtest: " + name, attributes):
+            yield
+
+    @contextmanager
+    def nested(self, message: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
+        self._eprint(
+            self.maybe_prefix(
+                Style.BRIGHT + Fore.GREEN + message + Style.RESET_ALL, attributes
+            )
+        )
+
+        tic = time.time()
+        yield
+        toc = time.time()
+        self.log(f"(finished: {message}, in {toc - tic:.2f} seconds)")
+
+    def info(self, *args, **kwargs) -> None:  # type: ignore
+        self.log(*args, **kwargs)
+
+    def warning(self, *args, **kwargs) -> None:  # type: ignore
+        self.log(*args, **kwargs)
+
+    def error(self, *args, **kwargs) -> None:  # type: ignore
+        self.log(*args, **kwargs)
+
+    def print_serial_logs(self, enable: bool) -> None:
+        self._print_serial_logs = enable
+
+    def log_serial(self, message: str, machine: str) -> None:
+        if not self._print_serial_logs:
+            return
+
+        self._eprint(Style.DIM + f"{machine} # {message}" + Style.RESET_ALL)
+
+
+class XMLLogger(AbstractLogger):
+    def __init__(self, outfile: str) -> None:
+        self.logfile_handle = codecs.open(outfile, "wb")
+        self.xml = XMLGenerator(self.logfile_handle, encoding="utf-8")
+        self.queue: Queue[dict[str, str]] = Queue()
+
+        self._print_serial_logs = True
+
+        self.xml.startDocument()
+        self.xml.startElement("logfile", attrs=AttributesImpl({}))
+
     def close(self) -> None:
         self.xml.endElement("logfile")
         self.xml.endDocument()
@@ -54,17 +259,19 @@ class Logger:
 
     def error(self, *args, **kwargs) -> None:  # type: ignore
         self.log(*args, **kwargs)
-        sys.exit(1)
 
     def log(self, message: str, attributes: Dict[str, str] = {}) -> None:
-        self._eprint(self.maybe_prefix(message, attributes))
         self.drain_log_queue()
         self.log_line(message, attributes)
 
+    def print_serial_logs(self, enable: bool) -> None:
+        self._print_serial_logs = enable
+
     def log_serial(self, message: str, machine: str) -> None:
+        if not self._print_serial_logs:
+            return
+
         self.enqueue({"msg": message, "machine": machine, "type": "serial"})
-        if self._print_serial_logs:
-            self._eprint(Style.DIM + f"{machine} # {message}" + Style.RESET_ALL)
 
     def enqueue(self, item: Dict[str, str]) -> None:
         self.queue.put(item)
@@ -80,13 +287,12 @@ class Logger:
             pass
 
     @contextmanager
-    def nested(self, message: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
-        self._eprint(
-            self.maybe_prefix(
-                Style.BRIGHT + Fore.GREEN + message + Style.RESET_ALL, attributes
-            )
-        )
+    def subtest(self, name: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
+        with self.nested("subtest: " + name, attributes):
+            yield
 
+    @contextmanager
+    def nested(self, message: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
         self.xml.startElement("nest", attrs=AttributesImpl({}))
         self.xml.startElement("head", attrs=AttributesImpl(attributes))
         self.xml.characters(message)
@@ -100,6 +306,3 @@ class Logger:
         self.log(f"(finished: {message}, in {toc - tic:.2f} seconds)")
 
         self.xml.endElement("nest")
-
-
-rootlog = Logger()
diff --git a/nixos/lib/test-driver/test_driver/machine.py b/nixos/lib/test-driver/test_driver/machine.py
index 652cc600fad59..3a1d5bc1be764 100644
--- a/nixos/lib/test-driver/test_driver/machine.py
+++ b/nixos/lib/test-driver/test_driver/machine.py
@@ -17,7 +17,7 @@ from pathlib import Path
 from queue import Queue
 from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
 
-from test_driver.logger import rootlog
+from test_driver.logger import AbstractLogger
 
 from .qmp import QMPSession
 
@@ -270,6 +270,7 @@ class Machine:
         out_dir: Path,
         tmp_dir: Path,
         start_command: StartCommand,
+        logger: AbstractLogger,
         name: str = "machine",
         keep_vm_state: bool = False,
         callbacks: Optional[List[Callable]] = None,
@@ -280,6 +281,7 @@ class Machine:
         self.name = name
         self.start_command = start_command
         self.callbacks = callbacks if callbacks is not None else []
+        self.logger = logger
 
         # set up directories
         self.shared_dir = self.tmp_dir / "shared-xchg"
@@ -307,15 +309,15 @@ class Machine:
         return self.booted and self.connected
 
     def log(self, msg: str) -> None:
-        rootlog.log(msg, {"machine": self.name})
+        self.logger.log(msg, {"machine": self.name})
 
     def log_serial(self, msg: str) -> None:
-        rootlog.log_serial(msg, self.name)
+        self.logger.log_serial(msg, self.name)
 
     def nested(self, msg: str, attrs: Dict[str, str] = {}) -> _GeneratorContextManager:
         my_attrs = {"machine": self.name}
         my_attrs.update(attrs)
-        return rootlog.nested(msg, my_attrs)
+        return self.logger.nested(msg, my_attrs)
 
     def wait_for_monitor_prompt(self) -> str:
         assert self.monitor is not None
@@ -1113,8 +1115,8 @@ class Machine:
 
     def cleanup_statedir(self) -> None:
         shutil.rmtree(self.state_dir)
-        rootlog.log(f"deleting VM state directory {self.state_dir}")
-        rootlog.log("if you want to keep the VM state, pass --keep-vm-state")
+        self.logger.log(f"deleting VM state directory {self.state_dir}")
+        self.logger.log("if you want to keep the VM state, pass --keep-vm-state")
 
     def shutdown(self) -> None:
         """
@@ -1221,7 +1223,7 @@ class Machine:
     def release(self) -> None:
         if self.pid is None:
             return
-        rootlog.info(f"kill machine (pid {self.pid})")
+        self.logger.info(f"kill machine (pid {self.pid})")
         assert self.process
         assert self.shell
         assert self.monitor
diff --git a/nixos/lib/test-driver/test_driver/polling_condition.py b/nixos/lib/test-driver/test_driver/polling_condition.py
index 12cbad69e34e9..1cccaf2c71e74 100644
--- a/nixos/lib/test-driver/test_driver/polling_condition.py
+++ b/nixos/lib/test-driver/test_driver/polling_condition.py
@@ -2,7 +2,7 @@ import time
 from math import isfinite
 from typing import Callable, Optional
 
-from .logger import rootlog
+from test_driver.logger import AbstractLogger
 
 
 class PollingConditionError(Exception):
@@ -13,6 +13,7 @@ class PollingCondition:
     condition: Callable[[], bool]
     seconds_interval: float
     description: Optional[str]
+    logger: AbstractLogger
 
     last_called: float
     entry_count: int
@@ -20,11 +21,13 @@ class PollingCondition:
     def __init__(
         self,
         condition: Callable[[], Optional[bool]],
+        logger: AbstractLogger,
         seconds_interval: float = 2.0,
         description: Optional[str] = None,
     ):
         self.condition = condition  # type: ignore
         self.seconds_interval = seconds_interval
+        self.logger = logger
 
         if description is None:
             if condition.__doc__:
@@ -41,7 +44,7 @@ class PollingCondition:
         if (self.entered or not self.overdue) and not force:
             return True
 
-        with self, rootlog.nested(self.nested_message):
+        with self, self.logger.nested(self.nested_message):
             time_since_last = time.monotonic() - self.last_called
             last_message = (
                 f"Time since last: {time_since_last:.2f}s"
@@ -49,13 +52,13 @@ class PollingCondition:
                 else "(not called yet)"
             )
 
-            rootlog.info(last_message)
+            self.logger.info(last_message)
             try:
                 res = self.condition()  # type: ignore
             except Exception:
                 res = False
             res = res is None or res
-            rootlog.info(self.status_message(res))
+            self.logger.info(self.status_message(res))
             return res
 
     def maybe_raise(self) -> None:
diff --git a/nixos/lib/test-driver/test_driver/vlan.py b/nixos/lib/test-driver/test_driver/vlan.py
index ec9679108e58d..9340fc92ed4c4 100644
--- a/nixos/lib/test-driver/test_driver/vlan.py
+++ b/nixos/lib/test-driver/test_driver/vlan.py
@@ -4,7 +4,7 @@ import pty
 import subprocess
 from pathlib import Path
 
-from test_driver.logger import rootlog
+from test_driver.logger import AbstractLogger
 
 
 class VLan:
@@ -19,17 +19,20 @@ class VLan:
     pid: int
     fd: io.TextIOBase
 
+    logger: AbstractLogger
+
     def __repr__(self) -> str:
         return f"<Vlan Nr. {self.nr}>"
 
-    def __init__(self, nr: int, tmp_dir: Path):
+    def __init__(self, nr: int, tmp_dir: Path, logger: AbstractLogger):
         self.nr = nr
         self.socket_dir = tmp_dir / f"vde{self.nr}.ctl"
+        self.logger = logger
 
         # TODO: don't side-effect environment here
         os.environ[f"QEMU_VDE_SOCKET_{self.nr}"] = str(self.socket_dir)
 
-        rootlog.info("start vlan")
+        self.logger.info("start vlan")
         pty_master, pty_slave = pty.openpty()
 
         # The --hub is required for the scenario determined by
@@ -52,11 +55,11 @@ class VLan:
         assert self.process.stdout is not None
         self.process.stdout.readline()
         if not (self.socket_dir / "ctl").exists():
-            rootlog.error("cannot start vde_switch")
+            self.logger.error("cannot start vde_switch")
 
-        rootlog.info(f"running vlan (pid {self.pid}; ctl {self.socket_dir})")
+        self.logger.info(f"running vlan (pid {self.pid}; ctl {self.socket_dir})")
 
     def __del__(self) -> None:
-        rootlog.info(f"kill vlan (pid {self.pid})")
+        self.logger.info(f"kill vlan (pid {self.pid})")
         self.fd.close()
         self.process.terminate()
diff --git a/nixos/lib/test-script-prepend.py b/nixos/lib/test-script-prepend.py
index 976992ea00158..9d2efdf973031 100644
--- a/nixos/lib/test-script-prepend.py
+++ b/nixos/lib/test-script-prepend.py
@@ -4,7 +4,7 @@
 from test_driver.driver import Driver
 from test_driver.vlan import VLan
 from test_driver.machine import Machine
-from test_driver.logger import Logger
+from test_driver.logger import AbstractLogger
 from typing import Callable, Iterator, ContextManager, Optional, List, Dict, Any, Union
 from typing_extensions import Protocol
 from pathlib import Path
@@ -44,7 +44,7 @@ test_script: Callable[[], None]
 machines: List[Machine]
 vlans: List[VLan]
 driver: Driver
-log: Logger
+log: AbstractLogger
 create_machine: CreateMachineProtocol
 run_tests: Callable[[], None]
 join_all: Callable[[], None]
diff --git a/nixos/lib/testing/driver.nix b/nixos/lib/testing/driver.nix
index 7eb06e023918b..d4f8e0f0c6e38 100644
--- a/nixos/lib/testing/driver.nix
+++ b/nixos/lib/testing/driver.nix
@@ -139,7 +139,7 @@ in
     enableOCR = mkOption {
       description = ''
         Whether to enable Optical Character Recognition functionality for
-        testing graphical programs. See [Machine objects](`ssec-machine-objects`).
+        testing graphical programs. See [`Machine objects`](#ssec-machine-objects).
       '';
       type = types.bool;
       default = false;
diff --git a/nixos/maintainers/scripts/ec2/README.md b/nixos/maintainers/scripts/ec2/README.md
index 1328109d464a3..eb2c9088d5a2c 100644
--- a/nixos/maintainers/scripts/ec2/README.md
+++ b/nixos/maintainers/scripts/ec2/README.md
@@ -1,7 +1,36 @@
 # Amazon images
 
-* The `create-amis.sh` script will be replaced by https://github.com/NixOS/amis which will regularly upload AMIs per NixOS channel bump.
+AMIs are regularly uploaded from Hydra. This automation lives in
+https://github.com/NixOS/amis
 
-* @arianvp is planning to drop zfs support
-* @arianvp is planning to rewrite the image builder to use the repart-based image builder.
 
+## How to upload an AMI for testing
+
+If you want to upload an AMI from changes in a local nixpkgs checkout.
+
+```bash
+nix-build nixos/release.nix -A amazonImage
+
+export AWS_REGION=us-west-2
+export AWS_PROFILE=my-profile
+nix run nixpkgs#upload-ami -- --image-info ./result/nix-support/image-info.json
+```
+
+## How to build your own NixOS config into an AMI
+
+I suggest looking at https://github.com/nix-community/nixos-generators for a user-friendly interface.
+
+```bash
+nixos-generate -c ./my-config.nix -f amazon
+
+export AWS_REGION=us-west-2
+export AWS_PROFILE=my-profile
+nix run github:NixOS/amis#upload-ami -- --image-info ./result/nix-support/image-info.json
+```
+
+## Roadmap
+
+* @arianvp is planning to drop zfs support unless someone else picks it up
+* @arianvp is planning to rewrite the image builder to use the repart-based image builder.
+* @arianvp is planning to perhaps rewrite `upload-ami` to use coldnsap
+* @arianvp is planning to move `upload-ami` tooling into nixpkgs once it has stabilized. And only keep the Github Action in separate repo
diff --git a/nixos/maintainers/scripts/ec2/amazon-image.nix b/nixos/maintainers/scripts/ec2/amazon-image.nix
index 357b86367d984..8b6a9bc52b128 100644
--- a/nixos/maintainers/scripts/ec2/amazon-image.nix
+++ b/nixos/maintainers/scripts/ec2/amazon-image.nix
@@ -71,9 +71,8 @@ in {
       '';
 
     zfsBuilder = import ../../../lib/make-multi-disk-zfs-image.nix {
-      inherit lib config configFile;
+      inherit lib config configFile pkgs;
       inherit (cfg) contents format name;
-      pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
 
       includeChannel = true;
 
@@ -120,10 +119,9 @@ in {
     };
 
     extBuilder = import ../../../lib/make-disk-image.nix {
-      inherit lib config configFile;
+      inherit lib config configFile pkgs;
 
       inherit (cfg) contents format name;
-      pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
 
       fsType = "ext4";
       partitionTableType = if config.ec2.efi then "efi" else "legacy+gpt";
diff --git a/nixos/maintainers/scripts/ec2/create-amis.sh b/nixos/maintainers/scripts/ec2/create-amis.sh
deleted file mode 100755
index d182c5c2a4794..0000000000000
--- a/nixos/maintainers/scripts/ec2/create-amis.sh
+++ /dev/null
@@ -1,368 +0,0 @@
-#!/usr/bin/env nix-shell
-#!nix-shell -p awscli -p jq -p qemu -i bash
-# shellcheck shell=bash
-#
-# Future Deprecation?
-# This entire thing should probably be replaced with a generic terraform config
-
-# Uploads and registers NixOS images built from the
-# <nixos/release.nix> amazonImage attribute. Images are uploaded and
-# registered via a home region, and then copied to other regions.
-
-# The home region requires an s3 bucket, and an IAM role named "vmimport"
-# (by default) with access to the S3 bucket. The name can be
-# configured with the "service_role_name" variable. Configuration of the
-# vmimport role is documented in
-# https://docs.aws.amazon.com/vm-import/latest/userguide/vmimport-image-import.html
-
-# set -x
-set -euo pipefail
-
-var () { true; }
-
-# configuration
-var ${state_dir:=$HOME/amis/ec2-images}
-var ${home_region:=eu-west-1}
-var ${bucket:=nixos-amis}
-var ${service_role_name:=vmimport}
-
-# Output of the command:
-# $ nix-shell -I nixpkgs=. -p awscli --run 'aws ec2 describe-regions --region us-east-1 --all-regions --query "Regions[].{Name:RegionName}" --output text | sort | sed -e s/^/\ \ /'
-var ${regions:=
-  af-south-1
-  ap-east-1
-  ap-northeast-1
-  ap-northeast-2
-  ap-northeast-3
-  ap-south-1
-  ap-south-2
-  ap-southeast-1
-  ap-southeast-2
-  ap-southeast-3
-  ap-southeast-4
-  ca-central-1
-  eu-central-1
-  eu-central-2
-  eu-north-1
-  eu-south-1
-  eu-south-2
-  eu-west-1
-  eu-west-2
-  eu-west-3
-  il-central-1
-  me-central-1
-  me-south-1
-  sa-east-1
-  us-east-1
-  us-east-2
-  us-west-1
-  us-west-2
-}
-
-regions=($regions)
-
-log() {
-    echo "$@" >&2
-}
-
-if [ "$#" -ne 1 ]; then
-    log "Usage: ./upload-amazon-image.sh IMAGE_OUTPUT"
-    exit 1
-fi
-
-# result of the amazon-image from nixos/release.nix
-store_path=$1
-
-if [ ! -e "$store_path" ]; then
-    log "Store path: $store_path does not exist, fetching..."
-    nix-store --realise "$store_path"
-fi
-
-if [ ! -d "$store_path" ]; then
-    log "store_path: $store_path is not a directory. aborting"
-    exit 1
-fi
-
-read_image_info() {
-    if [ ! -e "$store_path/nix-support/image-info.json" ]; then
-        log "Image missing metadata"
-        exit 1
-    fi
-    jq -r "$1" "$store_path/nix-support/image-info.json"
-}
-
-# We handle a single image per invocation, store all attributes in
-# globals for convenience.
-zfs_disks=$(read_image_info .disks)
-is_zfs_image=
-if jq -e .boot <<< "$zfs_disks"; then
-  is_zfs_image=1
-  zfs_boot=".disks.boot"
-fi
-image_label="$(read_image_info .label)${is_zfs_image:+-ZFS}"
-image_system=$(read_image_info .system)
-image_files=( $(read_image_info ".disks.root.file") )
-
-image_logical_bytes=$(read_image_info "${zfs_boot:-.disks.root}.logical_bytes")
-
-if [[ -n "$is_zfs_image" ]]; then
-  image_files+=( $(read_image_info .disks.boot.file) )
-fi
-
-# Derived attributes
-
-image_logical_gigabytes=$(((image_logical_bytes-1)/1024/1024/1024+1)) # Round to the next GB
-
-case "$image_system" in
-    aarch64-linux)
-        amazon_arch=arm64
-        ;;
-    x86_64-linux)
-        amazon_arch=x86_64
-        ;;
-    *)
-        log "Unknown system: $image_system"
-        exit 1
-esac
-
-image_name="NixOS-${image_label}-${image_system}"
-image_description="NixOS ${image_label} ${image_system}"
-
-log "Image Details:"
-log " Name: $image_name"
-log " Description: $image_description"
-log " Size (gigabytes): $image_logical_gigabytes"
-log " System: $image_system"
-log " Amazon Arch: $amazon_arch"
-
-read_state() {
-    local state_key=$1
-    local type=$2
-
-    cat "$state_dir/$state_key.$type" 2>/dev/null || true
-}
-
-write_state() {
-    local state_key=$1
-    local type=$2
-    local val=$3
-
-    mkdir -p "$state_dir"
-    echo "$val" > "$state_dir/$state_key.$type"
-}
-
-wait_for_import() {
-    local region=$1
-    local task_id=$2
-    local state snapshot_id
-    log "Waiting for import task $task_id to be completed"
-    while true; do
-        read -r state message snapshot_id < <(
-            aws ec2 describe-import-snapshot-tasks --region "$region" --import-task-ids "$task_id" | \
-                jq -r '.ImportSnapshotTasks[].SnapshotTaskDetail | "\(.Status) \(.StatusMessage) \(.SnapshotId)"'
-        )
-        log " ... state=$state message=$message snapshot_id=$snapshot_id"
-        case "$state" in
-            active)
-                sleep 10
-                ;;
-            completed)
-                echo "$snapshot_id"
-                return
-                ;;
-            *)
-                log "Unexpected snapshot import state: '${state}'"
-                log "Full response: "
-                aws ec2 describe-import-snapshot-tasks --region "$region" --import-task-ids "$task_id" >&2
-                exit 1
-                ;;
-        esac
-    done
-}
-
-wait_for_image() {
-    local region=$1
-    local ami_id=$2
-    local state
-    log "Waiting for image $ami_id to be available"
-
-    while true; do
-        read -r state < <(
-            aws ec2 describe-images --image-ids "$ami_id" --region "$region" | \
-                jq -r ".Images[].State"
-        )
-        log " ... state=$state"
-        case "$state" in
-            pending)
-                sleep 10
-                ;;
-            available)
-                return
-                ;;
-            *)
-                log "Unexpected AMI state: '${state}'"
-                exit 1
-                ;;
-        esac
-    done
-}
-
-
-make_image_public() {
-    local region=$1
-    local ami_id=$2
-
-    wait_for_image "$region" "$ami_id"
-
-    log "Making image $ami_id public"
-
-    aws ec2 modify-image-attribute \
-        --image-id "$ami_id" --region "$region" --launch-permission 'Add={Group=all}' >&2
-}
-
-upload_image() {
-    local region=$1
-
-    for image_file in "${image_files[@]}"; do
-        local aws_path=${image_file#/}
-
-        if [[ -n "$is_zfs_image" ]]; then
-            local suffix=${image_file%.*}
-            suffix=${suffix##*.}
-        fi
-
-        local state_key="$region.$image_label${suffix:+.${suffix}}.$image_system"
-        local task_id
-        task_id=$(read_state "$state_key" task_id)
-        local snapshot_id
-        snapshot_id=$(read_state "$state_key" snapshot_id)
-        local ami_id
-        ami_id=$(read_state "$state_key" ami_id)
-
-        if [ -z "$task_id" ]; then
-            log "Checking for image on S3"
-            if ! aws s3 ls --region "$region" "s3://${bucket}/${aws_path}" >&2; then
-                log "Image missing from aws, uploading"
-                aws s3 cp --region "$region" "$image_file" "s3://${bucket}/${aws_path}" >&2
-            fi
-
-            log "Importing image from S3 path s3://$bucket/$aws_path"
-
-            task_id=$(aws ec2 import-snapshot --role-name "$service_role_name" --disk-container "{
-              \"Description\": \"nixos-image-${image_label}-${image_system}\",
-              \"Format\": \"vhd\",
-              \"UserBucket\": {
-                  \"S3Bucket\": \"$bucket\",
-                  \"S3Key\": \"$aws_path\"
-              }
-            }" --region "$region" | jq -r '.ImportTaskId')
-
-            write_state "$state_key" task_id "$task_id"
-        fi
-
-        if [ -z "$snapshot_id" ]; then
-            snapshot_id=$(wait_for_import "$region" "$task_id")
-            write_state "$state_key" snapshot_id "$snapshot_id"
-        fi
-    done
-
-    if [ -z "$ami_id" ]; then
-        log "Registering snapshot $snapshot_id as AMI"
-
-        local block_device_mappings=(
-            "DeviceName=/dev/xvda,Ebs={SnapshotId=$snapshot_id,VolumeSize=$image_logical_gigabytes,DeleteOnTermination=true,VolumeType=gp3}"
-        )
-
-        if [[ -n "$is_zfs_image" ]]; then
-            local root_snapshot_id=$(read_state "$region.$image_label.root.$image_system" snapshot_id)
-
-            local root_image_logical_bytes=$(read_image_info ".disks.root.logical_bytes")
-            local root_image_logical_gigabytes=$(((root_image_logical_bytes-1)/1024/1024/1024+1)) # Round to the next GB
-
-            block_device_mappings+=(
-                "DeviceName=/dev/xvdb,Ebs={SnapshotId=$root_snapshot_id,VolumeSize=$root_image_logical_gigabytes,DeleteOnTermination=true,VolumeType=gp3}"
-            )
-        fi
-
-
-        local extra_flags=(
-            --root-device-name /dev/xvda
-            --sriov-net-support simple
-            --ena-support
-            --virtualization-type hvm
-        )
-
-        block_device_mappings+=("DeviceName=/dev/sdb,VirtualName=ephemeral0")
-        block_device_mappings+=("DeviceName=/dev/sdc,VirtualName=ephemeral1")
-        block_device_mappings+=("DeviceName=/dev/sdd,VirtualName=ephemeral2")
-        block_device_mappings+=("DeviceName=/dev/sde,VirtualName=ephemeral3")
-
-        ami_id=$(
-            aws ec2 register-image \
-                --name "$image_name" \
-                --description "$image_description" \
-                --region "$region" \
-                --architecture $amazon_arch \
-                --block-device-mappings "${block_device_mappings[@]}" \
-                --boot-mode $(read_image_info .boot_mode) \
-                "${extra_flags[@]}" \
-                | jq -r '.ImageId'
-              )
-
-        write_state "$state_key" ami_id "$ami_id"
-    fi
-
-    [[ -v PRIVATE ]] || make_image_public "$region" "$ami_id"
-
-    echo "$ami_id"
-}
-
-copy_to_region() {
-    local region=$1
-    local from_region=$2
-    local from_ami_id=$3
-
-    state_key="$region.$image_label.$image_system"
-    ami_id=$(read_state "$state_key" ami_id)
-
-    if [ -z "$ami_id" ]; then
-        log "Copying $from_ami_id to $region"
-        ami_id=$(
-            aws ec2 copy-image \
-                --region "$region" \
-                --source-region "$from_region" \
-                --source-image-id "$from_ami_id" \
-                --name "$image_name" \
-                --description "$image_description" \
-                | jq -r '.ImageId'
-              )
-
-        write_state "$state_key" ami_id "$ami_id"
-    fi
-
-    [[ -v PRIVATE ]] || make_image_public "$region" "$ami_id"
-
-    echo "$ami_id"
-}
-
-upload_all() {
-    home_image_id=$(upload_image "$home_region")
-    jq -n \
-       --arg key "$home_region.$image_system" \
-       --arg value "$home_image_id" \
-       '$ARGS.named'
-
-    for region in "${regions[@]}"; do
-        if [ "$region" = "$home_region" ]; then
-            continue
-        fi
-        copied_image_id=$(copy_to_region "$region" "$home_region" "$home_image_id")
-
-        jq -n \
-           --arg key "$region.$image_system" \
-           --arg value "$copied_image_id" \
-           '$ARGS.named'
-    done
-}
-
-upload_all | jq --slurp from_entries
diff --git a/nixos/maintainers/scripts/lxd/lxd-container-image.nix b/nixos/maintainers/scripts/lxd/lxd-container-image.nix
index 930ab34af3856..8c0e75e84f753 100644
--- a/nixos/maintainers/scripts/lxd/lxd-container-image.nix
+++ b/nixos/maintainers/scripts/lxd/lxd-container-image.nix
@@ -20,7 +20,7 @@
     };
   in ''
     if [ ! -e /etc/nixos/configuration.nix ]; then
-      install -m 644 -D ${config} /etc/nixos/configuration.nix
+      install -m 0644 -D ${config} /etc/nixos/configuration.nix
     fi
   '';
 
diff --git a/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix b/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix
index bbbdd1f3fa173..8a1c9b0d634cd 100644
--- a/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix
+++ b/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix
@@ -20,8 +20,7 @@
     };
   in ''
     if [ ! -e /etc/nixos/configuration.nix ]; then
-      mkdir -p /etc/nixos
-      cp ${config} /etc/nixos/configuration.nix
+      install -m 0644 -D ${config} /etc/nixos/configuration.nix
     fi
   '';
 
diff --git a/nixos/modules/config/fonts/ghostscript.nix b/nixos/modules/config/fonts/ghostscript.nix
index a5508b948990c..5db7c0ac71799 100644
--- a/nixos/modules/config/fonts/ghostscript.nix
+++ b/nixos/modules/config/fonts/ghostscript.nix
@@ -18,6 +18,6 @@ with lib;
   };
 
   config = mkIf config.fonts.enableGhostscriptFonts {
-    fonts.packages = [ "${pkgs.ghostscript}/share/ghostscript/fonts" ];
+    fonts.packages = [ pkgs.ghostscript.fonts ];
   };
 }
diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix
index 1d7976cef36a2..930e57dbde5bb 100644
--- a/nixos/modules/config/no-x-libs.nix
+++ b/nixos/modules/config/no-x-libs.nix
@@ -39,25 +39,27 @@ with lib;
       # dep of graphviz, libXpm is optional for Xpm support
       gd = super.gd.override { withXorg = false; };
       ghostscript = super.ghostscript.override { cupsSupport = false; x11Support = false; };
-      gjs = super.gjs.overrideAttrs { doCheck = false; installTests = false; }; # avoid test dependency on gtk3
+      gjs = (super.gjs.override { installTests = false; }).overrideAttrs { doCheck = false; }; # avoid test dependency on gtk3
       gobject-introspection = super.gobject-introspection.override { x11Support = false; };
       gpg-tui = super.gpg-tui.override { x11Support = false; };
       gpsd = super.gpsd.override { guiSupport = false; };
       graphviz = super.graphviz-nox;
       gst_all_1 = super.gst_all_1 // {
         gst-plugins-bad = super.gst_all_1.gst-plugins-bad.override { guiSupport = false; };
-        gst-plugins-base = super.gst_all_1.gst-plugins-base.override { enableWayland = false; enableX11 = false; };
+        gst-plugins-base = super.gst_all_1.gst-plugins-base.override { enableGl = false; enableWayland = false; enableX11 = false; };
         gst-plugins-good = super.gst_all_1.gst-plugins-good.override { enableWayland = false; enableX11 = false; gtkSupport = false; qt5Support = false; qt6Support = false; };
+        gst-plugins-rs = super.gst_all_1.gst-plugins-rs.override { withGtkPlugins = false; };
       };
       imagemagick = super.imagemagick.override { libX11Support = false; libXtSupport = false; };
       imagemagickBig = super.imagemagickBig.override { libX11Support = false; libXtSupport = false; };
       intel-vaapi-driver = super.intel-vaapi-driver.override { enableGui = false; };
       libdevil = super.libdevil-nox;
       libextractor = super.libextractor.override { gtkSupport = false; };
+      libplacebo = super.libplacebo.override { vulkanSupport = false; };
       libva = super.libva-minimal;
       limesuite = super.limesuite.override { withGui = false; };
       mc = super.mc.override { x11Support = false; };
-      mpv-unwrapped = super.mpv-unwrapped.override { sdl2Support = false; x11Support = false; waylandSupport = false; };
+      mpv-unwrapped = super.mpv-unwrapped.override { drmSupport = false; screenSaverSupport = false; sdl2Support = false; vulkanSupport = false; waylandSupport = false; x11Support = false; };
       msmtp = super.msmtp.override { withKeyring = false; };
       mupdf = super.mupdf.override { enableGL = false; enableX11 = false; };
       neofetch = super.neofetch.override { x11Support = false; };
@@ -70,6 +72,7 @@ with lib;
       networkmanager-vpnc = super.networkmanager-vpnc.override { withGnome = false; };
       pango = super.pango.override { x11Support = false; };
       pinentry-curses = super.pinentry-curses.override { withLibsecret = false; };
+      pinentry-tty = super.pinentry-tty.override { withLibsecret = false; };
       pipewire = super.pipewire.override { vulkanSupport = false; x11Support = false; };
       pythonPackagesExtensions = super.pythonPackagesExtensions ++ [
         (python-final: python-prev: {
diff --git a/nixos/modules/config/nsswitch.nix b/nixos/modules/config/nsswitch.nix
index c7ba9b8eec6a2..fe0402ee9e666 100644
--- a/nixos/modules/config/nsswitch.nix
+++ b/nixos/modules/config/nsswitch.nix
@@ -12,7 +12,7 @@ with lib;
     system.nssModules = mkOption {
       type = types.listOf types.path;
       internal = true;
-      default = [];
+      default = [ ];
       description = ''
         Search path for NSS (Name Service Switch) modules.  This allows
         several DNS resolution methods to be specified via
@@ -35,7 +35,7 @@ with lib;
 
           This option only takes effect if nscd is enabled.
         '';
-        default = [];
+        default = [ ];
       };
 
       group = mkOption {
@@ -47,7 +47,7 @@ with lib;
 
           This option only takes effect if nscd is enabled.
         '';
-        default = [];
+        default = [ ];
       };
 
       shadow = mkOption {
@@ -59,7 +59,19 @@ with lib;
 
           This option only takes effect if nscd is enabled.
         '';
-        default = [];
+        default = [ ];
+      };
+
+      sudoers = mkOption {
+        type = types.listOf types.str;
+        description = ''
+          List of sudoers entries to configure in {file}`/etc/nsswitch.conf`.
+
+          Note that "files" is always prepended.
+
+          This option only takes effect if nscd is enabled.
+        '';
+        default = [ ];
       };
 
       hosts = mkOption {
@@ -71,7 +83,7 @@ with lib;
 
           This option only takes effect if nscd is enabled.
         '';
-        default = [];
+        default = [ ];
       };
 
       services = mkOption {
@@ -83,7 +95,7 @@ with lib;
 
           This option only takes effect if nscd is enabled.
         '';
-        default = [];
+        default = [ ];
       };
     };
   };
@@ -112,6 +124,7 @@ with lib;
       passwd:    ${concatStringsSep " " config.system.nssDatabases.passwd}
       group:     ${concatStringsSep " " config.system.nssDatabases.group}
       shadow:    ${concatStringsSep " " config.system.nssDatabases.shadow}
+      sudoers:   ${concatStringsSep " " config.system.nssDatabases.sudoers}
 
       hosts:     ${concatStringsSep " " config.system.nssDatabases.hosts}
       networks:  files
@@ -126,6 +139,7 @@ with lib;
       passwd = mkBefore [ "files" ];
       group = mkBefore [ "files" ];
       shadow = mkBefore [ "files" ];
+      sudoers = mkBefore [ "files" ];
       hosts = mkMerge [
         (mkOrder 998 [ "files" ])
         (mkOrder 1499 [ "dns" ])
diff --git a/nixos/modules/config/swap.nix b/nixos/modules/config/swap.nix
index a606ebd767598..53aea5d847129 100644
--- a/nixos/modules/config/swap.nix
+++ b/nixos/modules/config/swap.nix
@@ -275,7 +275,6 @@ in
                     chattr +C "$DEVICE" 2>/dev/null || true
 
                     dd if=/dev/zero of="$DEVICE" bs=1M count=${toString sw.size}
-                    chmod 0600 ${sw.device}
                     ${optionalString (!sw.randomEncryption.enable) "mkswap ${sw.realDevice}"}
                   fi
                 ''}
@@ -292,9 +291,12 @@ in
 
             unitConfig.RequiresMountsFor = [ "${dirOf sw.device}" ];
             unitConfig.DefaultDependencies = false; # needed to prevent a cycle
-            serviceConfig.Type = "oneshot";
-            serviceConfig.RemainAfterExit = sw.randomEncryption.enable;
-            serviceConfig.ExecStop = optionalString sw.randomEncryption.enable "${pkgs.cryptsetup}/bin/cryptsetup luksClose ${sw.deviceName}";
+            serviceConfig = {
+              Type = "oneshot";
+              RemainAfterExit = sw.randomEncryption.enable;
+              UMask = "0177";
+              ExecStop = optionalString sw.randomEncryption.enable "${pkgs.cryptsetup}/bin/cryptsetup luksClose ${sw.deviceName}";
+            };
             restartIfChanged = false;
           };
 
diff --git a/nixos/modules/config/terminfo.nix b/nixos/modules/config/terminfo.nix
index 4b58605aa7f1a..b538d749ffcbf 100644
--- a/nixos/modules/config/terminfo.nix
+++ b/nixos/modules/config/terminfo.nix
@@ -31,7 +31,7 @@ with lib;
     # attrNames (filterAttrs
     #  (_: drv: (builtins.tryEval (isDerivation drv && drv ? terminfo)).value)
     #  pkgs)
-    environment.systemPackages = mkIf config.environment.enableAllTerminfo (map (x: x.terminfo) (with pkgs; [
+    environment.systemPackages = mkIf config.environment.enableAllTerminfo (map (x: x.terminfo) (with pkgs.pkgsBuildBuild; [
       alacritty
       contour
       foot
diff --git a/nixos/modules/config/xdg/portal.nix b/nixos/modules/config/xdg/portal.nix
index 2c4d07c4953cb..2368ca04a49ea 100644
--- a/nixos/modules/config/xdg/portal.nix
+++ b/nixos/modules/config/xdg/portal.nix
@@ -96,7 +96,7 @@ in
         Sets which portal backend should be used to provide the implementation
         for the requested interface. For details check {manpage}`portals.conf(5)`.
 
-        Configs will be linked to `/etx/xdg/xdg-desktop-portal/` with the name `$desktop-portals.conf`
+        Configs will be linked to `/etc/xdg/xdg-desktop-portal/` with the name `$desktop-portals.conf`
         for `xdg.portal.config.$desktop` and `portals.conf` for `xdg.portal.config.common`
         as an exception.
       '';
diff --git a/nixos/modules/config/xdg/terminal-exec.nix b/nixos/modules/config/xdg/terminal-exec.nix
new file mode 100644
index 0000000000000..daf2055d2e906
--- /dev/null
+++ b/nixos/modules/config/xdg/terminal-exec.nix
@@ -0,0 +1,54 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  cfg = config.xdg.terminal-exec;
+  inherit (lib) mkIf mkEnableOption mkOption mkPackageOption types;
+in
+{
+  meta.maintainers = with lib.maintainers; [ Cryolitia ];
+
+  ###### interface
+
+  options = {
+    xdg.terminal-exec = {
+      enable = mkEnableOption "xdg-terminal-exec, the [proposed](https://gitlab.freedesktop.org/xdg/xdg-specs/-/merge_requests/46) Default Terminal Execution Specification";
+      package = mkPackageOption pkgs "xdg-terminal-exec" { };
+      settings = mkOption {
+        type = with types; attrsOf (listOf str);
+        default = { };
+        description = ''
+          Configuration options for the Default Terminal Execution Specification.
+
+          The keys are the desktop environments that are matched (case-insensitively) against `$XDG_CURRENT_DESKTOP`,
+          or `default` which is used when the current desktop environment is not found in the configuration.
+          The values are a list of terminals' [desktop file IDs](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s02.html#desktop-file-id) to try in order of decreasing priority.
+        '';
+        example = {
+          default = [ "kitty.desktop" ];
+          GNOME = [ "com.raggesilver.BlackBox.desktop" "org.gnome.Terminal.desktop" ];
+        };
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment = {
+      systemPackages = [ cfg.package ];
+
+      etc = lib.mapAttrs' (
+        desktop: terminals:
+        # map desktop name such as GNOME to `xdg/gnome-xdg-terminals.list`, default to `xdg/xdg-terminals.list`
+        lib.nameValuePair (
+          "xdg/${if desktop == "default" then "" else "${lib.toLower desktop}-"}xdg-terminals.list"
+        ) { text = lib.concatLines terminals; }
+      ) cfg.settings;
+    };
+  };
+}
diff --git a/nixos/modules/hardware/graphics.nix b/nixos/modules/hardware/graphics.nix
new file mode 100644
index 0000000000000..99c122f75c2a1
--- /dev/null
+++ b/nixos/modules/hardware/graphics.nix
@@ -0,0 +1,126 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.hardware.graphics;
+
+  driversEnv = pkgs.buildEnv {
+    name = "graphics-drivers";
+    paths = [ cfg.package ] ++ cfg.extraPackages;
+  };
+
+  driversEnv32 = pkgs.buildEnv {
+    name = "graphics-drivers-32bit";
+    paths = [ cfg.package32 ] ++ cfg.extraPackages32;
+  };
+in
+{
+  imports = [
+    (lib.mkRenamedOptionModule [ "services" "xserver" "vaapiDrivers" ] [ "hardware" "opengl" "extraPackages" ])
+    (lib.mkRemovedOptionModule [ "hardware" "opengl" "s3tcSupport" ] "S3TC support is now always enabled in Mesa.")
+    (lib.mkRemovedOptionModule [ "hardware" "opengl" "driSupport"] "The setting can be removed.")
+
+    (lib.mkRenamedOptionModule [ "hardware" "opengl" "enable"] [ "hardware" "graphics" "enable" ])
+    (lib.mkRenamedOptionModule [ "hardware" "opengl" "driSupport32Bit"] [ "hardware" "graphics" "enable32Bit" ])
+    (lib.mkRenamedOptionModule [ "hardware" "opengl" "package"] [ "hardware" "graphics" "package" ])
+    (lib.mkRenamedOptionModule [ "hardware" "opengl" "package32"] [ "hardware" "graphics" "package32" ])
+    (lib.mkRenamedOptionModule [ "hardware" "opengl" "extraPackages"] [ "hardware" "graphics" "extraPackages" ])
+    (lib.mkRenamedOptionModule [ "hardware" "opengl" "extraPackages32"] [ "hardware" "graphics" "extraPackages32" ])
+  ];
+
+  options.hardware.graphics = {
+    enable = lib.mkOption {
+      description = ''
+        Whether to enable hardware accelerated graphics drivers.
+
+        This is required to allow most graphical applications and
+        environments to use hardware rendering, video encode/decode
+        acceleration, etc.
+
+        This option should be enabled by default by the corresponding modules,
+        so you do not usually have to set it yourself.
+      '';
+      type = lib.types.bool;
+      default = false;
+    };
+
+    enable32Bit = lib.mkOption {
+      description = ''
+        On 64-bit systems, whether to also install 32-bit drivers for
+        32-bit applications (such as Wine).
+      '';
+      type = lib.types.bool;
+      default = false;
+    };
+
+    package = lib.mkOption {
+      description = ''
+        The package that provides the default driver set.
+      '';
+      type = lib.types.package;
+      internal = true;
+    };
+
+    package32 = lib.mkOption {
+      description = ''
+        The package that provides the 32-bit driver set. Used when {option}`enable32Bit` is enabled.
+        set.
+      '';
+      type = lib.types.package;
+      internal = true;
+    };
+
+    extraPackages = lib.mkOption {
+      description = ''
+        Additional packages to add to the default graphics driver lookup path.
+        This can be used to add OpenCL drivers, VA-API/VDPAU drivers, etc.
+
+        ::: {.note}
+        intel-media-driver supports hardware Broadwell (2014) or newer. Older hardware should use the mostly unmaintained intel-vaapi-driver driver.
+        :::
+      '';
+      type = lib.types.listOf lib.types.package;
+      default = [];
+      example = lib.literalExpression "with pkgs; [ intel-media-driver intel-ocl intel-vaapi-driver ]";
+    };
+
+    extraPackages32 = lib.mkOption {
+      description = ''
+        Additional packages to add to 32-bit graphics driver lookup path on 64-bit systems.
+        Used when {option}`enable32Bit` is set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers, etc.
+
+        ::: {.note}
+        intel-media-driver supports hardware Broadwell (2014) or newer. Older hardware should use the mostly unmaintained intel-vaapi-driver driver.
+        :::
+      '';
+      type = lib.types.listOf lib.types.package;
+      default = [];
+      example = lib.literalExpression "with pkgs.pkgsi686Linux; [ intel-media-driver intel-vaapi-driver ]";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = cfg.enable32Bit -> pkgs.stdenv.isx86_64;
+        message = "`hardware.graphics.enable32Bit` only makes sense on a 64-bit system.";
+      }
+      {
+        assertion = cfg.enable32Bit -> (config.boot.kernelPackages.kernel.features.ia32Emulation or false);
+        message = "`hardware.graphics.enable32Bit` requires a kernel that supports 32-bit emulation";
+      }
+    ];
+
+    systemd.tmpfiles.settings.graphics-driver = {
+      "/run/opengl-driver"."L+".argument = toString driversEnv;
+      "/run/opengl-driver-32" =
+        if pkgs.stdenv.isi686 then
+          { "L+".argument = "opengl-driver"; }
+        else if cfg.enable32Bit then
+          { "L+".argument = toString driversEnv32; }
+        else
+          { "r" = {}; };
+    };
+
+    hardware.graphics.package = lib.mkDefault pkgs.mesa.drivers;
+    hardware.graphics.package32 = lib.mkDefault pkgs.pkgsi686Linux.mesa.drivers;
+  };
+}
diff --git a/nixos/modules/hardware/opengl.nix b/nixos/modules/hardware/opengl.nix
deleted file mode 100644
index 25324fd8b0af9..0000000000000
--- a/nixos/modules/hardware/opengl.nix
+++ /dev/null
@@ -1,161 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  cfg = config.hardware.opengl;
-
-  kernelPackages = config.boot.kernelPackages;
-
-  videoDrivers = config.services.xserver.videoDrivers;
-
-  package = pkgs.buildEnv {
-    name = "opengl-drivers";
-    paths = [ cfg.package ] ++ cfg.extraPackages;
-  };
-
-  package32 = pkgs.buildEnv {
-    name = "opengl-drivers-32bit";
-    paths = [ cfg.package32 ] ++ cfg.extraPackages32;
-  };
-
-in
-
-{
-
-  imports = [
-    (mkRenamedOptionModule [ "services" "xserver" "vaapiDrivers" ] [ "hardware" "opengl" "extraPackages" ])
-    (mkRemovedOptionModule [ "hardware" "opengl" "s3tcSupport" ] "S3TC support is now always enabled in Mesa.")
-  ];
-
-  options = {
-
-    hardware.opengl = {
-      enable = mkOption {
-        description = ''
-          Whether to enable OpenGL drivers. This is needed to enable
-          OpenGL support in X11 systems, as well as for Wayland compositors
-          like sway and Weston. It is enabled by default
-          by the corresponding modules, so you do not usually have to
-          set it yourself, only if there is no module for your wayland
-          compositor of choice. See services.xserver.enable and
-          programs.sway.enable.
-        '';
-        type = types.bool;
-        default = false;
-      };
-
-      driSupport = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          Whether to enable accelerated OpenGL rendering through the
-          Direct Rendering Interface (DRI).
-        '';
-      };
-
-      driSupport32Bit = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          On 64-bit systems, whether to support Direct Rendering for
-          32-bit applications (such as Wine).  This is currently only
-          supported for the `nvidia` as well as
-          `Mesa`.
-        '';
-      };
-
-      package = mkOption {
-        type = types.package;
-        internal = true;
-        description = ''
-          The package that provides the OpenGL implementation.
-        '';
-      };
-
-      package32 = mkOption {
-        type = types.package;
-        internal = true;
-        description = ''
-          The package that provides the 32-bit OpenGL implementation on
-          64-bit systems. Used when {option}`driSupport32Bit` is
-          set.
-        '';
-      };
-
-      extraPackages = mkOption {
-        type = types.listOf types.package;
-        default = [];
-        example = literalExpression "with pkgs; [ intel-media-driver intel-ocl intel-vaapi-driver ]";
-        description = ''
-          Additional packages to add to OpenGL drivers.
-          This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc.
-
-          ::: {.note}
-          intel-media-driver supports hardware Broadwell (2014) or newer. Older hardware should use the mostly unmaintained intel-vaapi-driver driver.
-          :::
-        '';
-      };
-
-      extraPackages32 = mkOption {
-        type = types.listOf types.package;
-        default = [];
-        example = literalExpression "with pkgs.pkgsi686Linux; [ intel-media-driver intel-vaapi-driver ]";
-        description = ''
-          Additional packages to add to 32-bit OpenGL drivers on 64-bit systems.
-          Used when {option}`driSupport32Bit` is set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc.
-
-          ::: {.note}
-          intel-media-driver supports hardware Broadwell (2014) or newer. Older hardware should use the mostly unmaintained intel-vaapi-driver driver.
-          :::
-        '';
-      };
-
-      setLdLibraryPath = mkOption {
-        type = types.bool;
-        internal = true;
-        default = false;
-        description = ''
-          Whether the `LD_LIBRARY_PATH` environment variable
-          should be set to the locations of driver libraries. Drivers which
-          rely on overriding libraries should set this to true. Drivers which
-          support `libglvnd` and other dispatch libraries
-          instead of overriding libraries should not set this.
-        '';
-      };
-    };
-
-  };
-
-  config = mkIf cfg.enable {
-    assertions = [
-      { assertion = cfg.driSupport32Bit -> pkgs.stdenv.isx86_64;
-        message = "Option driSupport32Bit only makes sense on a 64-bit system.";
-      }
-      { assertion = cfg.driSupport32Bit -> (config.boot.kernelPackages.kernel.features.ia32Emulation or false);
-        message = "Option driSupport32Bit requires a kernel that supports 32bit emulation";
-      }
-    ];
-
-    systemd.tmpfiles.rules = [
-      "L+ /run/opengl-driver - - - - ${package}"
-      (
-        if pkgs.stdenv.isi686 then
-          "L+ /run/opengl-driver-32 - - - - opengl-driver"
-        else if cfg.driSupport32Bit then
-          "L+ /run/opengl-driver-32 - - - - ${package32}"
-        else
-          "r /run/opengl-driver-32"
-      )
-    ];
-
-    environment.sessionVariables.LD_LIBRARY_PATH = mkIf cfg.setLdLibraryPath
-      ([ "/run/opengl-driver/lib" ] ++ optional cfg.driSupport32Bit "/run/opengl-driver-32/lib");
-
-    hardware.opengl.package = mkDefault pkgs.mesa.drivers;
-    hardware.opengl.package32 = mkDefault pkgs.pkgsi686Linux.mesa.drivers;
-
-    boot.extraModulePackages = optional (elem "virtualbox" videoDrivers) kernelPackages.virtualboxGuestAdditions;
-  };
-}
diff --git a/nixos/modules/hardware/openrazer.nix b/nixos/modules/hardware/openrazer.nix
index 5ba6abfdb3d7e..6f61254a60c1f 100644
--- a/nixos/modules/hardware/openrazer.nix
+++ b/nixos/modules/hardware/openrazer.nix
@@ -19,7 +19,7 @@ let
       [Startup]
       sync_effects_enabled = ${toPyBoolStr cfg.syncEffectsEnabled}
       devices_off_on_screensaver = ${toPyBoolStr cfg.devicesOffOnScreensaver}
-      battery_notifier = ${toPyBoolStr (cfg.mouseBatteryNotifier || cfg.batteryNotifier.enable)}
+      battery_notifier = ${toPyBoolStr cfg.batteryNotifier.enable}
       battery_notifier_freq = ${builtins.toString cfg.batteryNotifier.frequency}
       battery_notifier_percent = ${builtins.toString cfg.batteryNotifier.percentage}
 
@@ -80,14 +80,6 @@ in
         '';
       };
 
-      mouseBatteryNotifier = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          Mouse battery notifier.
-        '';
-      };
-
       batteryNotifier = mkOption {
         description = ''
           Settings for device battery notifications.
@@ -143,14 +135,11 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
-    warnings = flatten [
-      (optional cfg.mouseBatteryNotifier ''
-        The option openrazer.mouseBatteryNotifier is deprecated.
-        Please use openrazer.batteryNotifier instead to enable and configure battery notifications.
-      '')
-    ];
+  imports = [
+    (mkRenamedOptionModule [ "hardware" "openrazer" "mouseBatteryNotifier" ] [ "hardware" "openrazer" "batteryNotifier" "enable" ])
+  ];
 
+  config = mkIf cfg.enable {
     boot.extraModulePackages = [ kernelPackages.openrazer ];
     boot.kernelModules = drivers;
 
diff --git a/nixos/modules/hardware/printers.nix b/nixos/modules/hardware/printers.nix
index de2f84d4831b8..ace900d88586d 100644
--- a/nixos/modules/hardware/printers.nix
+++ b/nixos/modules/hardware/printers.nix
@@ -13,7 +13,7 @@ let
     } // optionalAttrs (p.description != null) {
       D = p.description;
     } // optionalAttrs (p.ppdOptions != {}) {
-      o = mapAttrsToList (name: value: "'${name}'='${value}'") p.ppdOptions;
+      o = mapAttrsToList (name: value: "${name}=${value}") p.ppdOptions;
     });
   in ''
     ${pkgs.cups}/bin/lpadmin ${args} -E
diff --git a/nixos/modules/hardware/video/amdgpu-pro.nix b/nixos/modules/hardware/video/amdgpu-pro.nix
deleted file mode 100644
index 2a86280eec8cb..0000000000000
--- a/nixos/modules/hardware/video/amdgpu-pro.nix
+++ /dev/null
@@ -1,69 +0,0 @@
-# This module provides the proprietary AMDGPU-PRO drivers.
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  drivers = config.services.xserver.videoDrivers;
-
-  enabled = elem "amdgpu-pro" drivers;
-
-  package = config.boot.kernelPackages.amdgpu-pro;
-  package32 = pkgs.pkgsi686Linux.linuxPackages.amdgpu-pro.override { kernel = null; };
-
-  opengl = config.hardware.opengl;
-
-in
-
-{
-
-  config = mkIf enabled {
-    services.xserver.drivers = singleton
-      { name = "amdgpu"; modules = [ package ]; display = true; };
-
-    hardware.opengl.package = package;
-    hardware.opengl.package32 = package32;
-    hardware.opengl.setLdLibraryPath = true;
-
-    boot.extraModulePackages = [ package.kmod ];
-
-    boot.kernelPackages = pkgs.linuxKernel.packagesFor
-      (pkgs.linuxKernel.kernels.linux_5_10.override {
-        structuredExtraConfig = {
-          DEVICE_PRIVATE = kernel.yes;
-          KALLSYMS_ALL = kernel.yes;
-        };
-      });
-
-    hardware.firmware = [ package.fw ];
-
-    systemd.tmpfiles.settings.amdgpu-pro = {
-      "/run/amdgpu"."L+".argument = "${package}/opt/amdgpu";
-      "/run/amdgpu-pro"."L+".argument = "${package}/opt/amdgpu-pro";
-    };
-
-    system.requiredKernelConfig = with config.lib.kernelConfig; [
-      (isYes "DEVICE_PRIVATE")
-      (isYes "KALLSYMS_ALL")
-    ];
-
-    boot.initrd.extraUdevRulesCommands = mkIf (!config.boot.initrd.systemd.enable) ''
-      cp -v ${package}/etc/udev/rules.d/*.rules $out/
-    '';
-    boot.initrd.services.udev.packages = [ package ];
-
-    environment.systemPackages =
-      [ package.vulkan ] ++
-      # this isn't really DRI, but we'll reuse this option for now
-      optional config.hardware.opengl.driSupport32Bit package32.vulkan;
-
-    environment.etc = {
-      "modprobe.d/blacklist-radeon.conf".source = package + "/etc/modprobe.d/blacklist-radeon.conf";
-      amd.source = package + "/etc/amd";
-    };
-
-  };
-
-}
diff --git a/nixos/modules/hardware/video/intel-gpu-tools.nix b/nixos/modules/hardware/video/intel-gpu-tools.nix
new file mode 100644
index 0000000000000..b69fefcae1184
--- /dev/null
+++ b/nixos/modules/hardware/video/intel-gpu-tools.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.hardware.intel-gpu-tools;
+in
+{
+  options = {
+    hardware.intel-gpu-tools = {
+      enable = lib.mkEnableOption "a setcap wrapper for intel-gpu-tools";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    security.wrappers.intel_gpu_top = {
+      owner = "root";
+      group = "root";
+      source = "${pkgs.intel-gpu-tools}/bin/intel_gpu_top";
+      capabilities = "cap_perfmon+ep";
+    };
+  };
+
+  meta = {
+    maintainers = with lib.maintainers; [ kira-bruneau ];
+  };
+}
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index 37d8e53a2e049..0274dfcaa70f9 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -3,12 +3,10 @@
   lib,
   pkgs,
   ...
-}: let
+}:
+let
   nvidiaEnabled = (lib.elem "nvidia" config.services.xserver.videoDrivers);
-  nvidia_x11 =
-    if nvidiaEnabled || cfg.datacenter.enable
-    then cfg.package
-    else null;
+  nvidia_x11 = if nvidiaEnabled || cfg.datacenter.enable then cfg.package else null;
 
   cfg = config.hardware.nvidia;
 
@@ -19,8 +17,9 @@
   primeEnabled = syncCfg.enable || reverseSyncCfg.enable || offloadCfg.enable;
   busIDType = lib.types.strMatching "([[:print:]]+[\:\@][0-9]{1,3}\:[0-9]{1,2}\:[0-9])?";
   ibtSupport = cfg.open || (nvidia_x11.ibtSupport or false);
-  settingsFormat = pkgs.formats.keyValue {};
-in {
+  settingsFormat = pkgs.formats.keyValue { };
+in
+{
   options = {
     hardware.nvidia = {
       datacenter.enable = lib.mkEnableOption ''
@@ -29,50 +28,50 @@ in {
       datacenter.settings = lib.mkOption {
         type = settingsFormat.type;
         default = {
-          LOG_LEVEL=4;
-          LOG_FILE_NAME="/var/log/fabricmanager.log";
-          LOG_APPEND_TO_LOG=1;
-          LOG_FILE_MAX_SIZE=1024;
-          LOG_USE_SYSLOG=0;
-          DAEMONIZE=1;
-          BIND_INTERFACE_IP="127.0.0.1";
-          STARTING_TCP_PORT=16000;
-          FABRIC_MODE=0;
-          FABRIC_MODE_RESTART=0;
-          STATE_FILE_NAME="/var/tmp/fabricmanager.state";
-          FM_CMD_BIND_INTERFACE="127.0.0.1";
-          FM_CMD_PORT_NUMBER=6666;
-          FM_STAY_RESIDENT_ON_FAILURES=0;
-          ACCESS_LINK_FAILURE_MODE=0;
-          TRUNK_LINK_FAILURE_MODE=0;
-          NVSWITCH_FAILURE_MODE=0;
-          ABORT_CUDA_JOBS_ON_FM_EXIT=1;
-          TOPOLOGY_FILE_PATH="${nvidia_x11.fabricmanager}/share/nvidia-fabricmanager/nvidia/nvswitch";
-          DATABASE_PATH="${nvidia_x11.fabricmanager}/share/nvidia-fabricmanager/nvidia/nvswitch";
+          LOG_LEVEL = 4;
+          LOG_FILE_NAME = "/var/log/fabricmanager.log";
+          LOG_APPEND_TO_LOG = 1;
+          LOG_FILE_MAX_SIZE = 1024;
+          LOG_USE_SYSLOG = 0;
+          DAEMONIZE = 1;
+          BIND_INTERFACE_IP = "127.0.0.1";
+          STARTING_TCP_PORT = 16000;
+          FABRIC_MODE = 0;
+          FABRIC_MODE_RESTART = 0;
+          STATE_FILE_NAME = "/var/tmp/fabricmanager.state";
+          FM_CMD_BIND_INTERFACE = "127.0.0.1";
+          FM_CMD_PORT_NUMBER = 6666;
+          FM_STAY_RESIDENT_ON_FAILURES = 0;
+          ACCESS_LINK_FAILURE_MODE = 0;
+          TRUNK_LINK_FAILURE_MODE = 0;
+          NVSWITCH_FAILURE_MODE = 0;
+          ABORT_CUDA_JOBS_ON_FM_EXIT = 1;
+          TOPOLOGY_FILE_PATH = "${nvidia_x11.fabricmanager}/share/nvidia-fabricmanager/nvidia/nvswitch";
+          DATABASE_PATH = "${nvidia_x11.fabricmanager}/share/nvidia-fabricmanager/nvidia/nvswitch";
         };
         defaultText = lib.literalExpression ''
-        {
-          LOG_LEVEL=4;
-          LOG_FILE_NAME="/var/log/fabricmanager.log";
-          LOG_APPEND_TO_LOG=1;
-          LOG_FILE_MAX_SIZE=1024;
-          LOG_USE_SYSLOG=0;
-          DAEMONIZE=1;
-          BIND_INTERFACE_IP="127.0.0.1";
-          STARTING_TCP_PORT=16000;
-          FABRIC_MODE=0;
-          FABRIC_MODE_RESTART=0;
-          STATE_FILE_NAME="/var/tmp/fabricmanager.state";
-          FM_CMD_BIND_INTERFACE="127.0.0.1";
-          FM_CMD_PORT_NUMBER=6666;
-          FM_STAY_RESIDENT_ON_FAILURES=0;
-          ACCESS_LINK_FAILURE_MODE=0;
-          TRUNK_LINK_FAILURE_MODE=0;
-          NVSWITCH_FAILURE_MODE=0;
-          ABORT_CUDA_JOBS_ON_FM_EXIT=1;
-          TOPOLOGY_FILE_PATH="''${nvidia_x11.fabricmanager}/share/nvidia-fabricmanager/nvidia/nvswitch";
-          DATABASE_PATH="''${nvidia_x11.fabricmanager}/share/nvidia-fabricmanager/nvidia/nvswitch";
-        }
+          {
+            LOG_LEVEL=4;
+            LOG_FILE_NAME="/var/log/fabricmanager.log";
+            LOG_APPEND_TO_LOG=1;
+            LOG_FILE_MAX_SIZE=1024;
+            LOG_USE_SYSLOG=0;
+            DAEMONIZE=1;
+            BIND_INTERFACE_IP="127.0.0.1";
+            STARTING_TCP_PORT=16000;
+            FABRIC_MODE=0;
+            FABRIC_MODE_RESTART=0;
+            STATE_FILE_NAME="/var/tmp/fabricmanager.state";
+            FM_CMD_BIND_INTERFACE="127.0.0.1";
+            FM_CMD_PORT_NUMBER=6666;
+            FM_STAY_RESIDENT_ON_FAILURES=0;
+            ACCESS_LINK_FAILURE_MODE=0;
+            TRUNK_LINK_FAILURE_MODE=0;
+            NVSWITCH_FAILURE_MODE=0;
+            ABORT_CUDA_JOBS_ON_FM_EXIT=1;
+            TOPOLOGY_FILE_PATH="''${nvidia_x11.fabricmanager}/share/nvidia-fabricmanager/nvidia/nvswitch";
+            DATABASE_PATH="''${nvidia_x11.fabricmanager}/share/nvidia-fabricmanager/nvidia/nvswitch";
+          }
         '';
         description = ''
           Additional configuration options for fabricmanager.
@@ -207,11 +206,26 @@ in {
         option is supported is used
       '';
 
+      prime.reverseSync.setupCommands.enable =
+        (lib.mkEnableOption ''
+          configure the display manager to be able to use the outputs
+          attached to the NVIDIA GPU.
+          Disable in order to configure the NVIDIA GPU outputs manually using xrandr.
+          Note that this configuration will only be successful when a display manager
+          for which the {option}`services.xserver.displayManager.setupCommands`
+          option is supported is used
+        '')
+        // {
+          default = true;
+        };
+
       nvidiaSettings =
         (lib.mkEnableOption ''
           nvidia-settings, NVIDIA's GUI configuration tool
         '')
-        // {default = true;};
+        // {
+          default = true;
+        };
 
       nvidiaPersistenced = lib.mkEnableOption ''
         nvidia-persistenced a update for NVIDIA GPU headless mode, i.e.
@@ -226,7 +240,8 @@ in {
       '';
 
       package = lib.mkOption {
-        default = config.boot.kernelPackages.nvidiaPackages."${if cfg.datacenter.enable then "dc" else "stable"}";
+        default =
+          config.boot.kernelPackages.nvidiaPackages."${if cfg.datacenter.enable then "dc" else "stable"}";
         defaultText = lib.literalExpression ''
           config.boot.kernelPackages.nvidiaPackages."\$\{if cfg.datacenter.enable then "dc" else "stable"}"
         '';
@@ -242,403 +257,404 @@ in {
     };
   };
 
-  config = let
-    igpuDriver =
-      if pCfg.intelBusId != ""
-      then "modesetting"
-      else "amdgpu";
-    igpuBusId =
-      if pCfg.intelBusId != ""
-      then pCfg.intelBusId
-      else pCfg.amdgpuBusId;
-  in
-    lib.mkIf (nvidia_x11 != null) (lib.mkMerge [
-      # Common
-      ({
-        assertions = [
-          {
-            assertion = !(nvidiaEnabled && cfg.datacenter.enable);
-            message = "You cannot configure both X11 and Data Center drivers at the same time.";
-          }
-        ];
-        boot = {
-          blacklistedKernelModules = ["nouveau" "nvidiafb"];
-
-          # Don't add `nvidia-uvm` to `kernelModules`, because we want
-          # `nvidia-uvm` be loaded only after `udev` rules for `nvidia` kernel
-          # module are applied.
-          #
-          # Instead, we use `softdep` to lazily load `nvidia-uvm` kernel module
-          # after `nvidia` kernel module is loaded and `udev` rules are applied.
-          extraModprobeConfig = ''
-            softdep nvidia post: nvidia-uvm
-          '';
-        };
-        systemd.tmpfiles.rules =
-          lib.optional config.virtualisation.docker.enableNvidia
-            "L+ /run/nvidia-docker/bin - - - - ${nvidia_x11.bin}/origBin";
-        services.udev.extraRules =
-        ''
-          # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
-          KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c 195 255'"
-          KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'for i in $$(cat /proc/driver/nvidia/gpus/*/information | grep Minor | cut -d \  -f 4); do mknod -m 666 /dev/nvidia$${i} c 195 $${i}; done'"
-          KERNEL=="nvidia_modeset", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-modeset c 195 254'"
-          KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm c $$(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0'"
-          KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm-tools c $$(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 1'"
-        '';
-        hardware.opengl = {
-          extraPackages = [
-            nvidia_x11.out
+  config =
+    let
+      igpuDriver = if pCfg.intelBusId != "" then "modesetting" else "amdgpu";
+      igpuBusId = if pCfg.intelBusId != "" then pCfg.intelBusId else pCfg.amdgpuBusId;
+    in
+    lib.mkIf (nvidia_x11 != null) (
+      lib.mkMerge [
+        # Common
+        ({
+          assertions = [
+            {
+              assertion = !(nvidiaEnabled && cfg.datacenter.enable);
+              message = "You cannot configure both X11 and Data Center drivers at the same time.";
+            }
           ];
-          extraPackages32 = [
-            nvidia_x11.lib32
+          boot = {
+            blacklistedKernelModules = [
+              "nouveau"
+              "nvidiafb"
+            ];
+
+            # Don't add `nvidia-uvm` to `kernelModules`, because we want
+            # `nvidia-uvm` be loaded only after `udev` rules for `nvidia` kernel
+            # module are applied.
+            #
+            # Instead, we use `softdep` to lazily load `nvidia-uvm` kernel module
+            # after `nvidia` kernel module is loaded and `udev` rules are applied.
+            extraModprobeConfig = ''
+              softdep nvidia post: nvidia-uvm
+            '';
+          };
+          systemd.tmpfiles.rules = lib.mkIf config.virtualisation.docker.enableNvidia [ "L+ /run/nvidia-docker/bin - - - - ${nvidia_x11.bin}/origBin" ];
+          services.udev.extraRules = ''
+            # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
+            KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c 195 255'"
+            KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'for i in $$(cat /proc/driver/nvidia/gpus/*/information | grep Minor | cut -d \  -f 4); do mknod -m 666 /dev/nvidia$${i} c 195 $${i}; done'"
+            KERNEL=="nvidia_modeset", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-modeset c 195 254'"
+            KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm c $$(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0'"
+            KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm-tools c $$(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 1'"
+          '';
+          hardware.graphics = {
+            extraPackages = [ nvidia_x11.out ];
+            extraPackages32 = [ nvidia_x11.lib32 ];
+          };
+          environment.systemPackages = [ nvidia_x11.bin ];
+        })
+
+        # X11
+        (lib.mkIf nvidiaEnabled {
+          assertions = [
+            {
+              assertion = primeEnabled -> pCfg.intelBusId == "" || pCfg.amdgpuBusId == "";
+              message = "You cannot configure both an Intel iGPU and an AMD APU. Pick the one corresponding to your processor.";
+            }
+
+            {
+              assertion = offloadCfg.enableOffloadCmd -> offloadCfg.enable || reverseSyncCfg.enable;
+              message = "Offload command requires offloading or reverse prime sync to be enabled.";
+            }
+
+            {
+              assertion =
+                primeEnabled -> pCfg.nvidiaBusId != "" && (pCfg.intelBusId != "" || pCfg.amdgpuBusId != "");
+              message = "When NVIDIA PRIME is enabled, the GPU bus IDs must be configured.";
+            }
+
+            {
+              assertion = offloadCfg.enable -> lib.versionAtLeast nvidia_x11.version "435.21";
+              message = "NVIDIA PRIME render offload is currently only supported on versions >= 435.21.";
+            }
+
+            {
+              assertion =
+                (reverseSyncCfg.enable && pCfg.amdgpuBusId != "") -> lib.versionAtLeast nvidia_x11.version "470.0";
+              message = "NVIDIA PRIME render offload for AMD APUs is currently only supported on versions >= 470 beta.";
+            }
+
+            {
+              assertion = !(syncCfg.enable && offloadCfg.enable);
+              message = "PRIME Sync and Offload cannot be both enabled";
+            }
+
+            {
+              assertion = !(syncCfg.enable && reverseSyncCfg.enable);
+              message = "PRIME Sync and PRIME Reverse Sync cannot be both enabled";
+            }
+
+            {
+              assertion = !(syncCfg.enable && cfg.powerManagement.finegrained);
+              message = "Sync precludes powering down the NVIDIA GPU.";
+            }
+
+            {
+              assertion = cfg.powerManagement.finegrained -> offloadCfg.enable;
+              message = "Fine-grained power management requires offload to be enabled.";
+            }
+
+            {
+              assertion = cfg.powerManagement.enable -> lib.versionAtLeast nvidia_x11.version "430.09";
+              message = "Required files for driver based power management only exist on versions >= 430.09.";
+            }
+
+            {
+              assertion = cfg.open -> (cfg.package ? open && cfg.package ? firmware);
+              message = "This version of NVIDIA driver does not provide a corresponding opensource kernel driver";
+            }
+
+            {
+              assertion = cfg.dynamicBoost.enable -> lib.versionAtLeast nvidia_x11.version "510.39.01";
+              message = "NVIDIA's Dynamic Boost feature only exists on versions >= 510.39.01";
+            }
           ];
-        };
-        environment.systemPackages = [
-          nvidia_x11.bin
-        ];
-      })
-      # X11
-      (lib.mkIf nvidiaEnabled {
-        assertions = [
-        {
-          assertion = primeEnabled -> pCfg.intelBusId == "" || pCfg.amdgpuBusId == "";
-          message = "You cannot configure both an Intel iGPU and an AMD APU. Pick the one corresponding to your processor.";
-        }
-
-        {
-          assertion = offloadCfg.enableOffloadCmd -> offloadCfg.enable || reverseSyncCfg.enable;
-          message = "Offload command requires offloading or reverse prime sync to be enabled.";
-        }
-
-        {
-          assertion = primeEnabled -> pCfg.nvidiaBusId != "" && (pCfg.intelBusId != "" || pCfg.amdgpuBusId != "");
-          message = "When NVIDIA PRIME is enabled, the GPU bus IDs must be configured.";
-        }
-
-        {
-          assertion = offloadCfg.enable -> lib.versionAtLeast nvidia_x11.version "435.21";
-          message = "NVIDIA PRIME render offload is currently only supported on versions >= 435.21.";
-        }
-
-        {
-          assertion = (reverseSyncCfg.enable && pCfg.amdgpuBusId != "") -> lib.versionAtLeast nvidia_x11.version "470.0";
-          message = "NVIDIA PRIME render offload for AMD APUs is currently only supported on versions >= 470 beta.";
-        }
-
-        {
-          assertion = !(syncCfg.enable && offloadCfg.enable);
-          message = "PRIME Sync and Offload cannot be both enabled";
-        }
-
-        {
-          assertion = !(syncCfg.enable && reverseSyncCfg.enable);
-          message = "PRIME Sync and PRIME Reverse Sync cannot be both enabled";
-        }
-
-        {
-          assertion = !(syncCfg.enable && cfg.powerManagement.finegrained);
-          message = "Sync precludes powering down the NVIDIA GPU.";
-        }
-
-        {
-          assertion = cfg.powerManagement.finegrained -> offloadCfg.enable;
-          message = "Fine-grained power management requires offload to be enabled.";
-        }
-
-        {
-          assertion = cfg.powerManagement.enable -> lib.versionAtLeast nvidia_x11.version "430.09";
-          message = "Required files for driver based power management only exist on versions >= 430.09.";
-        }
-
-        {
-          assertion = cfg.open -> (cfg.package ? open && cfg.package ? firmware);
-          message = "This version of NVIDIA driver does not provide a corresponding opensource kernel driver";
-        }
-
-        {
-          assertion = cfg.dynamicBoost.enable -> lib.versionAtLeast nvidia_x11.version "510.39.01";
-          message = "NVIDIA's Dynamic Boost feature only exists on versions >= 510.39.01";
-        }];
-
-        # If Optimus/PRIME is enabled, we:
-        # - Specify the configured NVIDIA GPU bus ID in the Device section for the
-        #   "nvidia" driver.
-        # - Add the AllowEmptyInitialConfiguration option to the Screen section for the
-        #   "nvidia" driver, in order to allow the X server to start without any outputs.
-        # - Add a separate Device section for the Intel GPU, using the "modesetting"
-        #   driver and with the configured BusID.
-        # - OR add a separate Device section for the AMD APU, using the "amdgpu"
-        #   driver and with the configures BusID.
-        # - Reference that Device section from the ServerLayout section as an inactive
-        #   device.
-        # - Configure the display manager to run specific `xrandr` commands which will
-        #   configure/enable displays connected to the Intel iGPU / AMD APU.
-
-        # reverse sync implies offloading
-        hardware.nvidia.prime.offload.enable = lib.mkDefault reverseSyncCfg.enable;
-
-        services.xserver.drivers =
-          lib.optional primeEnabled {
-            name = igpuDriver;
-            display = offloadCfg.enable;
-            modules = lib.optional (igpuDriver == "amdgpu") pkgs.xorg.xf86videoamdgpu;
-            deviceSection =
-              ''
-                BusID "${igpuBusId}"
-              ''
-              + lib.optionalString (syncCfg.enable && igpuDriver != "amdgpu") ''
-                Option "AccelMethod" "none"
-              '';
-          }
-          ++ lib.singleton {
-            name = "nvidia";
-            modules = [nvidia_x11.bin];
-            display = !offloadCfg.enable;
-            deviceSection =
-              ''
-                Option "SidebandSocketPath" "/run/nvidia-xdriver/"
-              '' +
-              lib.optionalString primeEnabled
-              ''
-                BusID "${pCfg.nvidiaBusId}"
-              ''
-              + lib.optionalString pCfg.allowExternalGpu ''
-                Option "AllowExternalGpus"
-              '';
-            screenSection =
-              ''
-                Option "RandRRotation" "on"
-              ''
-              + lib.optionalString syncCfg.enable ''
-                Option "AllowEmptyInitialConfiguration"
+
+          # If Optimus/PRIME is enabled, we:
+          # - Specify the configured NVIDIA GPU bus ID in the Device section for the
+          #   "nvidia" driver.
+          # - Add the AllowEmptyInitialConfiguration option to the Screen section for the
+          #   "nvidia" driver, in order to allow the X server to start without any outputs.
+          # - Add a separate Device section for the Intel GPU, using the "modesetting"
+          #   driver and with the configured BusID.
+          # - OR add a separate Device section for the AMD APU, using the "amdgpu"
+          #   driver and with the configures BusID.
+          # - Reference that Device section from the ServerLayout section as an inactive
+          #   device.
+          # - Configure the display manager to run specific `xrandr` commands which will
+          #   configure/enable displays connected to the Intel iGPU / AMD APU.
+
+          # reverse sync implies offloading
+          hardware.nvidia.prime.offload.enable = lib.mkDefault reverseSyncCfg.enable;
+
+          services.xserver.drivers =
+            lib.optional primeEnabled {
+              name = igpuDriver;
+              display = offloadCfg.enable;
+              modules = lib.optional (igpuDriver == "amdgpu") pkgs.xorg.xf86videoamdgpu;
+              deviceSection =
+                ''
+                  BusID "${igpuBusId}"
+                ''
+                + lib.optionalString (syncCfg.enable && igpuDriver != "amdgpu") ''
+                  Option "AccelMethod" "none"
+                '';
+            }
+            ++ lib.singleton {
+              name = "nvidia";
+              modules = [ nvidia_x11.bin ];
+              display = !offloadCfg.enable;
+              deviceSection =
+                ''
+                  Option "SidebandSocketPath" "/run/nvidia-xdriver/"
+                ''
+                + lib.optionalString primeEnabled ''
+                  BusID "${pCfg.nvidiaBusId}"
+                ''
+                + lib.optionalString pCfg.allowExternalGpu ''
+                  Option "AllowExternalGpus"
+                '';
+              screenSection =
+                ''
+                  Option "RandRRotation" "on"
+                ''
+                + lib.optionalString syncCfg.enable ''
+                  Option "AllowEmptyInitialConfiguration"
+                ''
+                + lib.optionalString cfg.forceFullCompositionPipeline ''
+                  Option         "metamodes" "nvidia-auto-select +0+0 {ForceFullCompositionPipeline=On}"
+                  Option         "AllowIndirectGLXProtocol" "off"
+                  Option         "TripleBuffer" "on"
+                '';
+            };
+
+          services.xserver.serverLayoutSection =
+            lib.optionalString syncCfg.enable ''
+              Inactive "Device-${igpuDriver}[0]"
+            ''
+            + lib.optionalString reverseSyncCfg.enable ''
+              Inactive "Device-nvidia[0]"
+            ''
+            + lib.optionalString offloadCfg.enable ''
+              Option "AllowNVIDIAGPUScreens"
+            '';
+
+          services.xserver.displayManager.setupCommands =
+            let
+              gpuProviderName =
+                if igpuDriver == "amdgpu" then
+                  # find the name of the provider if amdgpu
+                  "`${lib.getExe pkgs.xorg.xrandr} --listproviders | ${lib.getExe pkgs.gnugrep} -i AMD | ${lib.getExe pkgs.gnused} -n 's/^.*name://p'`"
+                else
+                  igpuDriver;
+              providerCmdParams =
+                if syncCfg.enable then "\"${gpuProviderName}\" NVIDIA-0" else "NVIDIA-G0 \"${gpuProviderName}\"";
+            in
+            lib.optionalString
+              (syncCfg.enable || (reverseSyncCfg.enable && reverseSyncCfg.setupCommands.enable))
               ''
-              + lib.optionalString cfg.forceFullCompositionPipeline ''
-                Option         "metamodes" "nvidia-auto-select +0+0 {ForceFullCompositionPipeline=On}"
-                Option         "AllowIndirectGLXProtocol" "off"
-                Option         "TripleBuffer" "on"
+                # Added by nvidia configuration module for Optimus/PRIME.
+                ${lib.getExe pkgs.xorg.xrandr} --setprovideroutputsource ${providerCmdParams}
+                ${lib.getExe pkgs.xorg.xrandr} --auto
               '';
-          };
 
-        services.xserver.serverLayoutSection =
-          lib.optionalString syncCfg.enable ''
-            Inactive "Device-${igpuDriver}[0]"
-          ''
-          + lib.optionalString reverseSyncCfg.enable ''
-            Inactive "Device-nvidia[0]"
-          ''
-          + lib.optionalString offloadCfg.enable ''
-            Option "AllowNVIDIAGPUScreens"
-          '';
+          environment.etc = {
+            "nvidia/nvidia-application-profiles-rc" = lib.mkIf nvidia_x11.useProfiles {
+              source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc";
+            };
 
-        services.xserver.displayManager.setupCommands = let
-          gpuProviderName =
-            if igpuDriver == "amdgpu"
-            then
-              # find the name of the provider if amdgpu
-              "`${lib.getExe pkgs.xorg.xrandr} --listproviders | ${lib.getExe pkgs.gnugrep} -i AMD | ${lib.getExe pkgs.gnused} -n 's/^.*name://p'`"
-            else igpuDriver;
-          providerCmdParams =
-            if syncCfg.enable
-            then "\"${gpuProviderName}\" NVIDIA-0"
-            else "NVIDIA-G0 \"${gpuProviderName}\"";
-        in
-          lib.optionalString (syncCfg.enable || reverseSyncCfg.enable) ''
-            # Added by nvidia configuration module for Optimus/PRIME.
-            ${lib.getExe pkgs.xorg.xrandr} --setprovideroutputsource ${providerCmdParams}
-            ${lib.getExe pkgs.xorg.xrandr} --auto
-          '';
+            # 'nvidia_x11' installs it's files to /run/opengl-driver/...
+            "egl/egl_external_platform.d".source = "/run/opengl-driver/share/egl/egl_external_platform.d/";
+          };
 
-        environment.etc = {
-          "nvidia/nvidia-application-profiles-rc" = lib.mkIf nvidia_x11.useProfiles {source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc";};
+          hardware.graphics = {
+            extraPackages = [ pkgs.nvidia-vaapi-driver ];
+            extraPackages32 = [ pkgs.pkgsi686Linux.nvidia-vaapi-driver ];
+          };
 
-          # 'nvidia_x11' installs it's files to /run/opengl-driver/...
-          "egl/egl_external_platform.d".source = "/run/opengl-driver/share/egl/egl_external_platform.d/";
-        };
+          environment.systemPackages =
+            lib.optional cfg.nvidiaSettings nvidia_x11.settings
+            ++ lib.optional cfg.nvidiaPersistenced nvidia_x11.persistenced
+            ++ lib.optional offloadCfg.enableOffloadCmd (
+              pkgs.writeShellScriptBin "nvidia-offload" ''
+                export __NV_PRIME_RENDER_OFFLOAD=1
+                export __NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0
+                export __GLX_VENDOR_LIBRARY_NAME=nvidia
+                export __VK_LAYER_NV_optimus=NVIDIA_only
+                exec "$@"
+              ''
+            );
 
-        hardware.opengl = {
-          extraPackages = [
-            pkgs.nvidia-vaapi-driver
-          ];
-          extraPackages32 = [
-            pkgs.pkgsi686Linux.nvidia-vaapi-driver
-          ];
-        };
-        environment.systemPackages =
-          lib.optional cfg.nvidiaSettings nvidia_x11.settings
-          ++ lib.optional cfg.nvidiaPersistenced nvidia_x11.persistenced
-          ++ lib.optional offloadCfg.enableOffloadCmd
-          (pkgs.writeShellScriptBin "nvidia-offload" ''
-            export __NV_PRIME_RENDER_OFFLOAD=1
-            export __NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0
-            export __GLX_VENDOR_LIBRARY_NAME=nvidia
-            export __VK_LAYER_NV_optimus=NVIDIA_only
-            exec "$@"
-          '');
-
-        systemd.packages = lib.optional cfg.powerManagement.enable nvidia_x11.out;
-
-        systemd.services = let
-          nvidiaService = state: {
-            description = "NVIDIA system ${state} actions";
-            path = [pkgs.kbd];
-            serviceConfig = {
-              Type = "oneshot";
-              ExecStart = "${nvidia_x11.out}/bin/nvidia-sleep.sh '${state}'";
-            };
-            before = ["systemd-${state}.service"];
-            requiredBy = ["systemd-${state}.service"];
-          };
-        in
-          lib.mkMerge [
-            (lib.mkIf cfg.powerManagement.enable {
-              nvidia-suspend = nvidiaService "suspend";
-              nvidia-hibernate = nvidiaService "hibernate";
-              nvidia-resume =
-                (nvidiaService "resume")
-                // {
-                  before = [];
-                  after = ["systemd-suspend.service" "systemd-hibernate.service"];
-                  requiredBy = ["systemd-suspend.service" "systemd-hibernate.service"];
-                };
-            })
-            (lib.mkIf cfg.nvidiaPersistenced {
-              "nvidia-persistenced" = {
-                description = "NVIDIA Persistence Daemon";
-                wantedBy = ["multi-user.target"];
+          systemd.packages = lib.optional cfg.powerManagement.enable nvidia_x11.out;
+
+          systemd.services =
+            let
+              nvidiaService = state: {
+                description = "NVIDIA system ${state} actions";
+                path = [ pkgs.kbd ];
                 serviceConfig = {
-                  Type = "forking";
-                  Restart = "always";
-                  PIDFile = "/var/run/nvidia-persistenced/nvidia-persistenced.pid";
-                  ExecStart = "${lib.getExe nvidia_x11.persistenced} --verbose";
-                  ExecStopPost = "${pkgs.coreutils}/bin/rm -rf /var/run/nvidia-persistenced";
+                  Type = "oneshot";
+                  ExecStart = "${nvidia_x11.out}/bin/nvidia-sleep.sh '${state}'";
                 };
+                before = [ "systemd-${state}.service" ];
+                requiredBy = [ "systemd-${state}.service" ];
               };
-            })
-            (lib.mkIf cfg.dynamicBoost.enable {
-              "nvidia-powerd" = {
-                description = "nvidia-powerd service";
-                path = [
-                  pkgs.util-linux # nvidia-powerd wants lscpu
-                ];
-                wantedBy = ["multi-user.target"];
-                serviceConfig = {
-                  Type = "dbus";
-                  BusName = "nvidia.powerd.server";
-                  ExecStart = "${nvidia_x11.bin}/bin/nvidia-powerd";
+            in
+            lib.mkMerge [
+              (lib.mkIf cfg.powerManagement.enable {
+                nvidia-suspend = nvidiaService "suspend";
+                nvidia-hibernate = nvidiaService "hibernate";
+                nvidia-resume = (nvidiaService "resume") // {
+                  before = [ ];
+                  after = [
+                    "systemd-suspend.service"
+                    "systemd-hibernate.service"
+                  ];
+                  requiredBy = [
+                    "systemd-suspend.service"
+                    "systemd-hibernate.service"
+                  ];
                 };
-              };
-            })
-          ];
-        services.acpid.enable = true;
-
-        services.dbus.packages = lib.optional cfg.dynamicBoost.enable nvidia_x11.bin;
-
-        hardware.firmware = lib.optional cfg.open nvidia_x11.firmware;
-
-        systemd.tmpfiles.rules = [
-          # Remove the following log message:
-          #    (WW) NVIDIA: Failed to bind sideband socket to
-          #    (WW) NVIDIA:     '/var/run/nvidia-xdriver-b4f69129' Permission denied
-          #
-          # https://bbs.archlinux.org/viewtopic.php?pid=1909115#p1909115
-          "d /run/nvidia-xdriver 0770 root users"
-        ] ++ lib.optional (nvidia_x11.persistenced != null && config.virtualisation.docker.enableNvidia)
-          "L+ /run/nvidia-docker/extras/bin/nvidia-persistenced - - - - ${nvidia_x11.persistenced}/origBin/nvidia-persistenced";
-
-        boot = {
-          extraModulePackages =
-            if cfg.open
-            then [nvidia_x11.open]
-            else [nvidia_x11.bin];
-          # nvidia-uvm is required by CUDA applications.
-          kernelModules =
-            lib.optionals config.services.xserver.enable ["nvidia" "nvidia_modeset" "nvidia_drm"];
-
-          # If requested enable modesetting via kernel parameter.
-          kernelParams =
-            lib.optional (offloadCfg.enable || cfg.modesetting.enable) "nvidia-drm.modeset=1"
-            ++ lib.optional cfg.powerManagement.enable "nvidia.NVreg_PreserveVideoMemoryAllocations=1"
-            ++ lib.optional cfg.open "nvidia.NVreg_OpenRmEnableUnsupportedGpus=1"
-            ++ lib.optional (config.boot.kernelPackages.kernel.kernelAtLeast "6.2" && !ibtSupport) "ibt=off";
-
-          # enable finegrained power management
-          extraModprobeConfig = lib.optionalString cfg.powerManagement.finegrained ''
-            options nvidia "NVreg_DynamicPowerManagement=0x02"
-          '';
-        };
-        services.udev.extraRules =
-          lib.optionalString cfg.powerManagement.finegrained (
-          lib.optionalString (lib.versionOlder config.boot.kernelPackages.kernel.version "5.5") ''
-            # Remove NVIDIA USB xHCI Host Controller devices, if present
-            ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{remove}="1"
-
-            # Remove NVIDIA USB Type-C UCSI devices, if present
-            ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{remove}="1"
-
-            # Remove NVIDIA Audio devices, if present
-            ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{remove}="1"
-          ''
-          + ''
-            # Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
-            ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
-            ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"
-
-            # Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
-            ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
-            ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"
-          ''
-        );
-      })
-      # Data Center
-      (lib.mkIf (cfg.datacenter.enable) {
-        boot.extraModulePackages = [
-          nvidia_x11.bin
-        ];
-
-        systemd = {
-          tmpfiles.rules =
-            lib.optional (nvidia_x11.persistenced != null && config.virtualisation.docker.enableNvidia)
-            "L+ /run/nvidia-docker/extras/bin/nvidia-persistenced - - - - ${nvidia_x11.persistenced}/origBin/nvidia-persistenced";
-
-          services = lib.mkMerge [
-            ({
-              nvidia-fabricmanager = {
-                enable = true;
-                description = "Start NVIDIA NVLink Management";
-                wantedBy = [ "multi-user.target" ];
-                unitConfig.After = [ "network-online.target" ];
-                unitConfig.Requires = [ "network-online.target" ];
-                serviceConfig = {
-                  Type = "forking";
-                  TimeoutStartSec = 240;
-                  ExecStart = let
-                    nv-fab-conf = settingsFormat.generate "fabricmanager.conf" cfg.datacenter.settings;
-                    in
+              })
+              (lib.mkIf cfg.nvidiaPersistenced {
+                "nvidia-persistenced" = {
+                  description = "NVIDIA Persistence Daemon";
+                  wantedBy = [ "multi-user.target" ];
+                  serviceConfig = {
+                    Type = "forking";
+                    Restart = "always";
+                    PIDFile = "/var/run/nvidia-persistenced/nvidia-persistenced.pid";
+                    ExecStart = "${lib.getExe nvidia_x11.persistenced} --verbose";
+                    ExecStopPost = "${pkgs.coreutils}/bin/rm -rf /var/run/nvidia-persistenced";
+                  };
+                };
+              })
+              (lib.mkIf cfg.dynamicBoost.enable {
+                "nvidia-powerd" = {
+                  description = "nvidia-powerd service";
+                  path = [
+                    pkgs.util-linux # nvidia-powerd wants lscpu
+                  ];
+                  wantedBy = [ "multi-user.target" ];
+                  serviceConfig = {
+                    Type = "dbus";
+                    BusName = "nvidia.powerd.server";
+                    ExecStart = "${nvidia_x11.bin}/bin/nvidia-powerd";
+                  };
+                };
+              })
+            ];
+
+          services.acpid.enable = true;
+
+          services.dbus.packages = lib.optional cfg.dynamicBoost.enable nvidia_x11.bin;
+
+          hardware.firmware = lib.optional (cfg.open || lib.versionAtLeast nvidia_x11.version "555") nvidia_x11.firmware;
+
+          systemd.tmpfiles.rules =
+            [
+              # Remove the following log message:
+              #    (WW) NVIDIA: Failed to bind sideband socket to
+              #    (WW) NVIDIA:     '/var/run/nvidia-xdriver-b4f69129' Permission denied
+              #
+              # https://bbs.archlinux.org/viewtopic.php?pid=1909115#p1909115
+              "d /run/nvidia-xdriver 0770 root users"
+            ]
+            ++ lib.optional (nvidia_x11.persistenced != null && config.virtualisation.docker.enableNvidia)
+              "L+ /run/nvidia-docker/extras/bin/nvidia-persistenced - - - - ${nvidia_x11.persistenced}/origBin/nvidia-persistenced";
+
+          boot = {
+            extraModulePackages = if cfg.open then [ nvidia_x11.open ] else [ nvidia_x11.bin ];
+            # nvidia-uvm is required by CUDA applications.
+            kernelModules = lib.optionals config.services.xserver.enable [
+              "nvidia"
+              "nvidia_modeset"
+              "nvidia_drm"
+            ];
+
+            # If requested enable modesetting via kernel parameter.
+            kernelParams =
+              lib.optional (offloadCfg.enable || cfg.modesetting.enable) "nvidia-drm.modeset=1"
+              ++ lib.optional cfg.powerManagement.enable "nvidia.NVreg_PreserveVideoMemoryAllocations=1"
+              ++ lib.optional cfg.open "nvidia.NVreg_OpenRmEnableUnsupportedGpus=1"
+              ++ lib.optional (config.boot.kernelPackages.kernel.kernelAtLeast "6.2" && !ibtSupport) "ibt=off";
+
+            # enable finegrained power management
+            extraModprobeConfig = lib.optionalString cfg.powerManagement.finegrained ''
+              options nvidia "NVreg_DynamicPowerManagement=0x02"
+            '';
+          };
+          services.udev.extraRules = lib.optionalString cfg.powerManagement.finegrained (
+            lib.optionalString (lib.versionOlder config.boot.kernelPackages.kernel.version "5.5") ''
+              # Remove NVIDIA USB xHCI Host Controller devices, if present
+              ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{remove}="1"
+
+              # Remove NVIDIA USB Type-C UCSI devices, if present
+              ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{remove}="1"
+
+              # Remove NVIDIA Audio devices, if present
+              ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{remove}="1"
+            ''
+            + ''
+              # Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
+              ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
+              ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"
+
+              # Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
+              ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
+              ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"
+            ''
+          );
+        })
+        # Data Center
+        (lib.mkIf (cfg.datacenter.enable) {
+          boot.extraModulePackages = [ nvidia_x11.bin ];
+
+          systemd = {
+            tmpfiles.rules =
+              lib.optional (nvidia_x11.persistenced != null && config.virtualisation.docker.enableNvidia)
+                "L+ /run/nvidia-docker/extras/bin/nvidia-persistenced - - - - ${nvidia_x11.persistenced}/origBin/nvidia-persistenced";
+
+            services = lib.mkMerge [
+              ({
+                nvidia-fabricmanager = {
+                  enable = true;
+                  description = "Start NVIDIA NVLink Management";
+                  wantedBy = [ "multi-user.target" ];
+                  unitConfig.After = [ "network-online.target" ];
+                  unitConfig.Requires = [ "network-online.target" ];
+                  serviceConfig = {
+                    Type = "forking";
+                    TimeoutStartSec = 240;
+                    ExecStart =
+                      let
+                        nv-fab-conf = settingsFormat.generate "fabricmanager.conf" cfg.datacenter.settings;
+                      in
                       "${lib.getExe nvidia_x11.fabricmanager} -c ${nv-fab-conf}";
-                  LimitCORE="infinity";
+                    LimitCORE = "infinity";
+                  };
                 };
-              };
-            })
-            (lib.mkIf cfg.nvidiaPersistenced {
-              "nvidia-persistenced" = {
-                description = "NVIDIA Persistence Daemon";
-                wantedBy = ["multi-user.target"];
-                serviceConfig = {
-                  Type = "forking";
-                  Restart = "always";
-                  PIDFile = "/var/run/nvidia-persistenced/nvidia-persistenced.pid";
-                  ExecStart = "${lib.getExe nvidia_x11.persistenced} --verbose";
-                  ExecStopPost = "${pkgs.coreutils}/bin/rm -rf /var/run/nvidia-persistenced";
+              })
+              (lib.mkIf cfg.nvidiaPersistenced {
+                "nvidia-persistenced" = {
+                  description = "NVIDIA Persistence Daemon";
+                  wantedBy = [ "multi-user.target" ];
+                  serviceConfig = {
+                    Type = "forking";
+                    Restart = "always";
+                    PIDFile = "/var/run/nvidia-persistenced/nvidia-persistenced.pid";
+                    ExecStart = "${lib.getExe nvidia_x11.persistenced} --verbose";
+                    ExecStopPost = "${pkgs.coreutils}/bin/rm -rf /var/run/nvidia-persistenced";
+                  };
                 };
-              };
-            })
-          ];
-      };
+              })
+            ];
+          };
 
-      environment.systemPackages =
-        lib.optional cfg.datacenter.enable nvidia_x11.fabricmanager
-        ++ lib.optional cfg.nvidiaPersistenced nvidia_x11.persistenced;
-    })
-  ]);
+          environment.systemPackages =
+            lib.optional cfg.datacenter.enable nvidia_x11.fabricmanager
+            ++ lib.optional cfg.nvidiaPersistenced nvidia_x11.persistenced;
+        })
+      ]
+    );
 }
diff --git a/nixos/modules/hardware/video/virtualbox.nix b/nixos/modules/hardware/video/virtualbox.nix
new file mode 100644
index 0000000000000..31ed92b7d148e
--- /dev/null
+++ b/nixos/modules/hardware/video/virtualbox.nix
@@ -0,0 +1,7 @@
+{ lib, config, ... }:
+let
+  inherit (config.boot) kernelPackages;
+  inherit (config.services.xserver) videoDrivers;
+in {
+  boot.extraModulePackages = lib.mkIf (lib.elem "virtualbox" videoDrivers) [ kernelPackages.virtualboxGuestAdditions ];
+}
diff --git a/nixos/modules/hardware/xone.nix b/nixos/modules/hardware/xone.nix
index 89690d8c6fb10..bb3b42399d8e1 100644
--- a/nixos/modules/hardware/xone.nix
+++ b/nixos/modules/hardware/xone.nix
@@ -6,7 +6,7 @@ let
 in
 {
   options.hardware.xone = {
-    enable = mkEnableOption "the xone driver for Xbox One and Xbobx Series X|S accessories";
+    enable = mkEnableOption "the xone driver for Xbox One and Xbox Series X|S accessories";
   };
 
   config = mkIf cfg.enable {
diff --git a/nixos/modules/image/repart-image.nix b/nixos/modules/image/repart-image.nix
index 59d5fc26efe9b..e404067299004 100644
--- a/nixos/modules/image/repart-image.nix
+++ b/nixos/modules/image/repart-image.nix
@@ -10,7 +10,6 @@
 , mypy
 , systemd
 , fakeroot
-, util-linux
 
   # filesystem tools
 , dosfstools
@@ -105,7 +104,6 @@ in
   nativeBuildInputs = [
     systemd
     fakeroot
-    util-linux
   ] ++ lib.optionals (compression.enable) [
     compressionPkg
   ] ++ fileSystemTools;
@@ -148,7 +146,7 @@ in
     runHook preBuild
 
     echo "Building image with systemd-repart..."
-    unshare --map-root-user fakeroot systemd-repart \
+    fakeroot systemd-repart \
       ''${systemdRepartFlags[@]} \
       ${imageFileBasename}.raw \
       | tee repart-output.json
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix
index 61e94ffed8894..d1a42fc7a713b 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix
@@ -23,7 +23,7 @@
 
   environment.systemPackages = with pkgs; [
     # Graphical text editor
-    kate
+    plasma5Packages.kate
   ];
 
   system.activationScripts.installerDesktop = let
@@ -40,7 +40,7 @@
 
     ln -sfT ${manualDesktopFile} ${desktopDir + "nixos-manual.desktop"}
     ln -sfT ${pkgs.gparted}/share/applications/gparted.desktop ${desktopDir + "gparted.desktop"}
-    ln -sfT ${pkgs.konsole}/share/applications/org.kde.konsole.desktop ${desktopDir + "org.kde.konsole.desktop"}
+    ln -sfT ${pkgs.plasma5Packages.konsole}/share/applications/org.kde.konsole.desktop ${desktopDir + "org.kde.konsole.desktop"}
     ln -sfT ${pkgs.calamares-nixos}/share/applications/io.calamares.calamares.desktop ${desktopDir + "io.calamares.calamares.desktop"}
   '';
 
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix
index ce111bcebd5c9..770df5bb997b9 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix
@@ -23,7 +23,7 @@
 
   environment.systemPackages = with pkgs; [
     # Graphical text editor
-    kate
+    plasma5Packages.kate
   ];
 
   system.activationScripts.installerDesktop = let
@@ -40,7 +40,7 @@
 
     ln -sfT ${manualDesktopFile} ${desktopDir + "nixos-manual.desktop"}
     ln -sfT ${pkgs.gparted}/share/applications/gparted.desktop ${desktopDir + "gparted.desktop"}
-    ln -sfT ${pkgs.konsole}/share/applications/org.kde.konsole.desktop ${desktopDir + "org.kde.konsole.desktop"}
+    ln -sfT ${pkgs.plasma5Packages.konsole}/share/applications/org.kde.konsole.desktop ${desktopDir + "org.kde.konsole.desktop"}
   '';
 
 }
diff --git a/nixos/modules/installer/netboot/netboot.nix b/nixos/modules/installer/netboot/netboot.nix
index c88a53393d13f..93f806b75eb11 100644
--- a/nixos/modules/installer/netboot/netboot.nix
+++ b/nixos/modules/installer/netboot/netboot.nix
@@ -37,10 +37,6 @@ with lib;
     # here and it causes a cyclic dependency.
     boot.loader.grub.enable = false;
 
-    # !!! Hack - attributes expected by other modules.
-    environment.systemPackages = [ pkgs.grub2_efi ]
-      ++ (lib.optionals (pkgs.stdenv.hostPlatform.system != "aarch64-linux") [pkgs.grub2 pkgs.syslinux]);
-
     fileSystems."/" = mkImageMediaOverride
       { fsType = "tmpfs";
         options = [ "mode=0755" ];
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index e4241e9654036..9669ec5e37f3c 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,7 +1,7 @@
 {
-  x86_64-linux = "/nix/store/azvn85cras6xv4z5j85fiy406f24r1q0-nix-2.18.1";
-  i686-linux = "/nix/store/9bnwy7f9h0kzdzmcnjjsjg0aak5waj40-nix-2.18.1";
-  aarch64-linux = "/nix/store/hh65xwqm9s040s3cgn9vzcmrxj0sf5ij-nix-2.18.1";
-  x86_64-darwin = "/nix/store/6zi5fqzn9n17wrk8r41rhdw4j7jqqsi3-nix-2.18.1";
-  aarch64-darwin = "/nix/store/0pbq6wzr2f1jgpn5212knyxpwmkjgjah-nix-2.18.1";
+  x86_64-linux = "/nix/store/yrsmzlw2lgbknzwic1gy1gmv3l2w1ax8-nix-2.18.3";
+  i686-linux = "/nix/store/ds9381l9mlwfaclvqnkzn3jl4qb8m3y1-nix-2.18.3";
+  aarch64-linux = "/nix/store/hw1zny3f8520zyskmp1qaybv1ir5ilxh-nix-2.18.3";
+  x86_64-darwin = "/nix/store/z08yc4sl1fr65q53wz6pw30h67qafaln-nix-2.18.3";
+  aarch64-darwin = "/nix/store/p57m7m0wrz8sqxiwinzpwzqzak82zn75-nix-2.18.3";
 }
diff --git a/nixos/modules/misc/locate.nix b/nixos/modules/misc/locate.nix
index 84c711c2b4efa..0e9adefff5e1e 100644
--- a/nixos/modules/misc/locate.nix
+++ b/nixos/modules/misc/locate.nix
@@ -1,24 +1,22 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.services.locate;
-  isMLocate = hasPrefix "mlocate" cfg.package.name;
-  isPLocate = hasPrefix "plocate" cfg.package.name;
+  isMLocate = lib.hasPrefix "mlocate" cfg.package.name;
+  isPLocate = lib.hasPrefix "plocate" cfg.package.name;
   isMorPLocate = isMLocate || isPLocate;
-  isFindutils = hasPrefix "findutils" cfg.package.name;
+  isFindutils = lib.hasPrefix "findutils" cfg.package.name;
 in
 {
   imports = [
-    (mkRenamedOptionModule [ "services" "locate" "period" ] [ "services" "locate" "interval" ])
-    (mkRenamedOptionModule [ "services" "locate" "locate" ] [ "services" "locate" "package" ])
-    (mkRemovedOptionModule [ "services" "locate" "includeStore" ] "Use services.locate.prunePaths")
+    (lib.mkRenamedOptionModule [ "services" "locate" "period" ] [ "services" "locate" "interval" ])
+    (lib.mkRenamedOptionModule [ "services" "locate" "locate" ] [ "services" "locate" "package" ])
+    (lib.mkRemovedOptionModule [ "services" "locate" "includeStore" ] "Use services.locate.prunePaths")
   ];
 
-  options.services.locate = with types; {
-    enable = mkOption {
-      type = bool;
+  options.services.locate = {
+    enable = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         If enabled, NixOS will periodically update the database of
@@ -26,12 +24,12 @@ in
       '';
     };
 
-    package = mkPackageOption pkgs [ "findutils" "locate" ] {
+    package = lib.mkPackageOption pkgs [ "findutils" "locate" ] {
       example = "mlocate";
     };
 
-    interval = mkOption {
-      type = str;
+    interval = lib.mkOption {
+      type = lib.types.str;
       default = "02:15";
       example = "hourly";
       description = ''
@@ -46,24 +44,24 @@ in
       '';
     };
 
-    extraFlags = mkOption {
-      type = listOf str;
+    extraFlags = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
       default = [ ];
       description = ''
         Extra flags to pass to {command}`updatedb`.
       '';
     };
 
-    output = mkOption {
-      type = path;
+    output = lib.mkOption {
+      type = lib.types.path;
       default = "/var/cache/locatedb";
       description = ''
         The database file to build.
       '';
     };
 
-    localuser = mkOption {
-      type = nullOr str;
+    localuser = lib.mkOption {
+      type = lib.types.nullOr lib.types.str;
       default = "nobody";
       description = ''
         The user to search non-network directories as, using
@@ -71,8 +69,8 @@ in
       '';
     };
 
-    pruneFS = mkOption {
-      type = listOf str;
+    pruneFS = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
       default = [
         "afs"
         "anon_inodefs"
@@ -158,8 +156,8 @@ in
       '';
     };
 
-    prunePaths = mkOption {
-      type = listOf path;
+    prunePaths = lib.mkOption {
+      type = lib.types.listOf lib.types.path;
       default = [
         "/tmp"
         "/var/tmp"
@@ -175,10 +173,10 @@ in
       '';
     };
 
-    pruneNames = mkOption {
-      type = listOf str;
+    pruneNames = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
       default = lib.optionals (!isFindutils) [ ".bzr" ".cache" ".git" ".hg" ".svn" ];
-      defaultText = literalMD ''
+      defaultText = lib.literalMD ''
         `[ ".bzr" ".cache" ".git" ".hg" ".svn" ]`, if
         supported by the locate implementation (i.e. mlocate or plocate).
       '';
@@ -187,8 +185,8 @@ in
       '';
     };
 
-    pruneBindMounts = mkOption {
-      type = bool;
+    pruneBindMounts = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         Whether not to index bind mounts
@@ -197,10 +195,10 @@ in
 
   };
 
-  config = mkIf cfg.enable {
-    users.groups = mkMerge [
-      (mkIf isMLocate { mlocate = { }; })
-      (mkIf isPLocate { plocate = { }; })
+  config = lib.mkIf cfg.enable {
+    users.groups = lib.mkMerge [
+      (lib.mkIf isMLocate { mlocate = { }; })
+      (lib.mkIf isPLocate { plocate = { }; })
     ];
 
     security.wrappers =
@@ -211,46 +209,46 @@ in
           setgid = true;
           setuid = false;
         };
-        mlocate = mkIf isMLocate {
+        mlocate = lib.mkIf isMLocate {
           group = "mlocate";
           source = "${cfg.package}/bin/locate";
         };
-        plocate = mkIf isPLocate {
+        plocate = lib.mkIf isPLocate {
           group = "plocate";
           source = "${cfg.package}/bin/plocate";
         };
       in
-      mkIf isMorPLocate {
-        locate = mkMerge [ common mlocate plocate ];
-        plocate = mkIf isPLocate (mkMerge [ common plocate ]);
+      lib.mkIf isMorPLocate {
+        locate = lib.mkMerge [ common mlocate plocate ];
+        plocate = lib.mkIf isPLocate (lib.mkMerge [ common plocate ]);
       };
 
-    environment.systemPackages = [ cfg.package ];
+    environment = {
+      # write /etc/updatedb.conf for manual calls to `updatedb`
+      etc."updatedb.conf".text = ''
+        PRUNEFS="${lib.concatStringsSep " " cfg.pruneFS}"
+        PRUNENAMES="${lib.concatStringsSep " " cfg.pruneNames}"
+        PRUNEPATHS="${lib.concatStringsSep " " cfg.prunePaths}"
+        PRUNE_BIND_MOUNTS="${if cfg.pruneBindMounts then "yes" else "no"}"
+      '';
 
-    environment.variables.LOCATE_PATH = cfg.output;
+      systemPackages = [ cfg.package ];
 
-    environment.etc = {
-      # write /etc/updatedb.conf for manual calls to `updatedb`
-      "updatedb.conf" = {
-        text = ''
-          PRUNEFS="${lib.concatStringsSep " " cfg.pruneFS}"
-          PRUNENAMES="${lib.concatStringsSep " " cfg.pruneNames}"
-          PRUNEPATHS="${lib.concatStringsSep " " cfg.prunePaths}"
-          PRUNE_BIND_MOUNTS="${if cfg.pruneBindMounts then "yes" else "no"}"
-        '';
+      variables = lib.mkIf isFindutils {
+        LOCATE_PATH = cfg.output;
       };
     };
 
-    warnings = optional (isMorPLocate && cfg.localuser != null)
+    warnings = lib.optional (isMorPLocate && cfg.localuser != null)
       "mlocate and plocate do not support the services.locate.localuser option. updatedb will run as root. Silence this warning by setting services.locate.localuser = null."
-    ++ optional (isFindutils && cfg.pruneNames != [ ])
+    ++ lib.optional (isFindutils && cfg.pruneNames != [ ])
       "findutils locate does not support pruning by directory component"
-    ++ optional (isFindutils && cfg.pruneBindMounts)
+    ++ lib.optional (isFindutils && cfg.pruneBindMounts)
       "findutils locate does not support skipping bind mounts";
 
     systemd.services.update-locatedb = {
       description = "Update Locate Database";
-      path = mkIf (!isMorPLocate) [ pkgs.su ];
+      path = lib.mkIf (!isMorPLocate) [ pkgs.su ];
 
       # mlocate's updatedb takes flags via a configuration file or
       # on the command line, but not by environment variable.
@@ -258,42 +256,44 @@ in
         if isMorPLocate then
           let
             toFlags = x:
-              optional (cfg.${x} != [ ])
-                "--${lib.toLower x} '${concatStringsSep " " cfg.${x}}'";
-            args = concatLists (map toFlags [ "pruneFS" "pruneNames" "prunePaths" ]);
+              lib.optional (cfg.${x} != [ ])
+                "--${lib.toLower x} '${lib.concatStringsSep " " cfg.${x}}'";
+            args = lib.concatLists (map toFlags [ "pruneFS" "pruneNames" "prunePaths" ]);
           in
           ''
             exec ${cfg.package}/bin/updatedb \
-              --output ${toString cfg.output} ${concatStringsSep " " args} \
+              --output ${toString cfg.output} ${lib.concatStringsSep " " args} \
               --prune-bind-mounts ${if cfg.pruneBindMounts then "yes" else "no"} \
-              ${concatStringsSep " " cfg.extraFlags}
+              ${lib.concatStringsSep " " cfg.extraFlags}
           ''
         else ''
           exec ${cfg.package}/bin/updatedb \
-            ${optionalString (cfg.localuser != null && !isMorPLocate) "--localuser=${cfg.localuser}"} \
-            --output=${toString cfg.output} ${concatStringsSep " " cfg.extraFlags}
+            ${lib.optionalString (cfg.localuser != null && !isMorPLocate) "--localuser=${cfg.localuser}"} \
+            --output=${toString cfg.output} ${lib.concatStringsSep " " cfg.extraFlags}
         '';
-      environment = optionalAttrs (!isMorPLocate) {
-        PRUNEFS = concatStringsSep " " cfg.pruneFS;
-        PRUNEPATHS = concatStringsSep " " cfg.prunePaths;
-        PRUNENAMES = concatStringsSep " " cfg.pruneNames;
+      environment = lib.optionalAttrs (!isMorPLocate) {
+        PRUNEFS = lib.concatStringsSep " " cfg.pruneFS;
+        PRUNEPATHS = lib.concatStringsSep " " cfg.prunePaths;
+        PRUNENAMES = lib.concatStringsSep " " cfg.pruneNames;
         PRUNE_BIND_MOUNTS = if cfg.pruneBindMounts then "yes" else "no";
       };
-      serviceConfig.Nice = 19;
-      serviceConfig.IOSchedulingClass = "idle";
-      serviceConfig.PrivateTmp = "yes";
-      serviceConfig.PrivateNetwork = "yes";
-      serviceConfig.NoNewPrivileges = "yes";
-      serviceConfig.ReadOnlyPaths = "/";
-      # Use dirOf cfg.output because mlocate creates temporary files next to
-      # the actual database. We could specify and create them as well,
-      # but that would make this quite brittle when they change something.
-      # NOTE: If /var/cache does not exist, this leads to the misleading error message:
-      # update-locatedb.service: Failed at step NAMESPACE spawning …/update-locatedb-start: No such file or directory
-      serviceConfig.ReadWritePaths = dirOf cfg.output;
+      serviceConfig = {
+        Nice = 19;
+        IOSchedulingClass = "idle";
+        PrivateTmp = "yes";
+        PrivateNetwork = "yes";
+        NoNewPrivileges = "yes";
+        ReadOnlyPaths = "/";
+        # Use dirOf cfg.output because mlocate creates temporary files next to
+        # the actual database. We could specify and create them as well,
+        # but that would make this quite brittle when they change something.
+        # NOTE: If /var/cache does not exist, this leads to the misleading error message:
+        # update-locatedb.service: Failed at step NAMESPACE spawning …/update-locatedb-start: No such file or directory
+        ReadWritePaths = dirOf cfg.output;
+      };
     };
 
-    systemd.timers.update-locatedb = mkIf (cfg.interval != "never") {
+    systemd.timers.update-locatedb = lib.mkIf (cfg.interval != "never") {
       description = "Update timer for locate database";
       partOf = [ "update-locatedb.service" ];
       wantedBy = [ "timers.target" ];
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index d582e0c162de3..db917f73a0645 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -121,7 +121,7 @@ in
     image = {
 
       id = lib.mkOption {
-        type = types.nullOr (types.strMatching "^[a-z0-9._-]+$");
+        type = types.nullOr types.str;
         default = null;
         description = ''
           Image identifier.
@@ -135,7 +135,7 @@ in
       };
 
       version = lib.mkOption {
-        type = types.nullOr (types.strMatching "^[a-z0-9._-]+$");
+        type = types.nullOr types.str;
         default = null;
         description = ''
           Image version.
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 111b5c129cb3c..876e40983c1e5 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -45,6 +45,7 @@
   ./config/xdg/portals/lxqt.nix
   ./config/xdg/portals/wlr.nix
   ./config/xdg/sounds.nix
+  ./config/xdg/terminal-exec.nix
   ./config/zram.nix
   ./hardware/acpilight.nix
   ./hardware/all-firmware.nix
@@ -66,6 +67,7 @@
   ./hardware/gkraken.nix
   ./hardware/glasgow.nix
   ./hardware/gpgsmartcards.nix
+  ./hardware/graphics.nix
   ./hardware/hackrf.nix
   ./hardware/i2c.nix
   ./hardware/infiniband.nix
@@ -83,7 +85,6 @@
   ./hardware/new-lg4ff.nix
   ./hardware/nitrokey.nix
   ./hardware/onlykey/default.nix
-  ./hardware/opengl.nix
   ./hardware/openrazer.nix
   ./hardware/opentabletdriver.nix
   ./hardware/pcmcia.nix
@@ -102,13 +103,14 @@
   ./hardware/uni-sync.nix
   ./hardware/usb-modeswitch.nix
   ./hardware/usb-storage.nix
-  ./hardware/video/amdgpu-pro.nix
   ./hardware/video/bumblebee.nix
   ./hardware/video/capture/mwprocapture.nix
   ./hardware/video/displaylink.nix
+  ./hardware/video/intel-gpu-tools.nix
   ./hardware/video/nvidia.nix
   ./hardware/video/switcheroo-control.nix
   ./hardware/video/uvcvideo/default.nix
+  ./hardware/video/virtualbox.nix
   ./hardware/video/webcam/facetimehd.nix
   ./hardware/video/webcam/ipu6.nix
   ./hardware/wooting.nix
@@ -156,6 +158,7 @@
   ./programs/bash/ls-colors.nix
   ./programs/bash/undistract-me.nix
   ./programs/bcc.nix
+  ./programs/benchexec.nix
   ./programs/browserpass.nix
   ./programs/calls.nix
   ./programs/captive-browser.nix
@@ -165,6 +168,7 @@
   ./programs/chromium.nix
   ./programs/clash-verge.nix
   ./programs/cnping.nix
+  ./programs/cpu-energy-meter.nix
   ./programs/command-not-found/command-not-found.nix
   ./programs/coolercontrol.nix
   ./programs/criu.nix
@@ -192,6 +196,7 @@
   ./programs/fzf.nix
   ./programs/gamemode.nix
   ./programs/gamescope.nix
+  ./programs/gdk-pixbuf.nix
   ./programs/geary.nix
   ./programs/git.nix
   ./programs/gnome-disks.nix
@@ -214,6 +219,7 @@
   ./programs/kbdlight.nix
   ./programs/kclock.nix
   ./programs/kdeconnect.nix
+  ./programs/ladybird.nix
   ./programs/lazygit.nix
   ./programs/kubeswitch.nix
   ./programs/less.nix
@@ -245,9 +251,9 @@
   ./programs/oblogout.nix
   ./programs/oddjobd.nix
   ./programs/openvpn3.nix
-  ./programs/pantheon-tweaks.nix
   ./programs/partition-manager.nix
   ./programs/plotinus.nix
+  ./programs/pqos-wrapper.nix
   ./programs/projecteur.nix
   ./programs/proxychains.nix
   ./programs/qdmr.nix
@@ -277,6 +283,7 @@
   ./programs/systemtap.nix
   ./programs/thefuck.nix
   ./programs/thunar.nix
+  ./programs/thunderbird.nix
   ./programs/tmux.nix
   ./programs/traceroute.nix
   ./programs/trippy.nix
@@ -288,6 +295,7 @@
   ./programs/virt-manager.nix
   ./programs/wavemon.nix
   ./programs/wayland/cardboard.nix
+  ./programs/wayland/hyprlock.nix
   ./programs/wayland/hyprland.nix
   ./programs/wayland/labwc.nix
   ./programs/wayland/river.nix
@@ -306,6 +314,7 @@
   ./programs/xwayland.nix
   ./programs/yabar.nix
   ./programs/yazi.nix
+  ./programs/ydotool.nix
   ./programs/yubikey-touch-detector.nix
   ./programs/zmap.nix
   ./programs/zsh/oh-my-zsh.nix
@@ -412,6 +421,7 @@
   ./services/cluster/kubernetes/scheduler.nix
   ./services/cluster/pacemaker/default.nix
   ./services/cluster/patroni/default.nix
+  ./services/cluster/rke2/default.nix
   ./services/cluster/spark/default.nix
   ./services/computing/boinc/client.nix
   ./services/computing/foldingathome/client.nix
@@ -539,6 +549,8 @@
   ./services/games/xonotic.nix
   ./services/hardware/acpid.nix
   ./services/hardware/actkbd.nix
+  ./services/hardware/amdgpu.nix
+  ./services/hardware/amdvlk.nix
   ./services/hardware/argonone.nix
   ./services/hardware/asusd.nix
   ./services/hardware/auto-cpufreq.nix
@@ -664,6 +676,7 @@
   ./services/matrix/maubot.nix
   ./services/matrix/mautrix-facebook.nix
   ./services/matrix/mautrix-meta.nix
+  ./services/matrix/mautrix-signal.nix
   ./services/matrix/mautrix-telegram.nix
   ./services/matrix/mautrix-whatsapp.nix
   ./services/matrix/mjolnir.nix
@@ -696,6 +709,7 @@
   ./services/misc/cpuminer-cryptonight.nix
   ./services/misc/db-rest.nix
   ./services/misc/devmon.nix
+  ./services/misc/devpi-server.nix
   ./services/misc/dictd.nix
   ./services/misc/disnix.nix
   ./services/misc/docker-registry.nix
@@ -760,12 +774,14 @@
   ./services/misc/octoprint.nix
   ./services/misc/ollama.nix
   ./services/misc/ombi.nix
+  ./services/misc/open-webui.nix
   ./services/misc/osrm.nix
   ./services/misc/owncast.nix
   ./services/misc/packagekit.nix
   ./services/misc/paperless.nix
   ./services/misc/parsoid.nix
   ./services/misc/persistent-evdev.nix
+  ./services/misc/pghero.nix
   ./services/misc/pinnwand.nix
   ./services/misc/plex.nix
   ./services/misc/plikd.nix
@@ -773,12 +789,14 @@
   ./services/misc/polaris.nix
   ./services/misc/portunus.nix
   ./services/misc/preload.nix
+  ./services/misc/private-gpt.nix
   ./services/misc/prowlarr.nix
   ./services/misc/pufferpanel.nix
   ./services/misc/pykms.nix
   ./services/misc/radarr.nix
   ./services/misc/readarr.nix
   ./services/misc/redmine.nix
+  ./services/misc/renovate.nix
   ./services/misc/ripple-data-api.nix
   ./services/misc/rippled.nix
   ./services/misc/rmfakecloud.nix
@@ -821,6 +839,7 @@
   ./services/misc/zoneminder.nix
   ./services/misc/zookeeper.nix
   ./services/monitoring/alerta.nix
+  ./services/monitoring/alloy.nix
   ./services/monitoring/apcupsd.nix
   ./services/monitoring/arbtt.nix
   ./services/monitoring/below.nix
@@ -861,6 +880,7 @@
   ./services/monitoring/osquery.nix
   ./services/monitoring/parsedmarc.nix
   ./services/monitoring/prometheus/alertmanager-irc-relay.nix
+  ./services/monitoring/prometheus/alertmanager-webhook-logger.nix
   ./services/monitoring/prometheus/alertmanager.nix
   ./services/monitoring/prometheus/default.nix
   ./services/monitoring/prometheus/exporters.nix
@@ -1100,6 +1120,7 @@
   ./services/networking/ocserv.nix
   ./services/networking/ofono.nix
   ./services/networking/oidentd.nix
+  ./services/networking/oink.nix
   ./services/networking/onedrive.nix
   ./services/networking/openconnect.nix
   ./services/networking/openvpn.nix
@@ -1234,6 +1255,7 @@
   ./services/search/meilisearch.nix
   ./services/search/opensearch.nix
   ./services/search/qdrant.nix
+  ./services/search/quickwit.nix
   ./services/search/sonic-server.nix
   ./services/search/typesense.nix
   ./services/security/aesmd.nix
@@ -1296,6 +1318,7 @@
   ./services/system/zram-generator.nix
   ./services/torrent/deluge.nix
   ./services/torrent/flexget.nix
+  ./services/torrent/flood.nix
   ./services/torrent/magnetico.nix
   ./services/torrent/opentracker.nix
   ./services/torrent/peerflix.nix
@@ -1316,9 +1339,11 @@
   ./services/video/unifi-video.nix
   ./services/video/v4l2-relayd.nix
   ./services/wayland/cage.nix
+  ./services/wayland/hypridle.nix
   ./services/web-apps/akkoma.nix
   ./services/web-apps/alps.nix
   ./services/web-apps/anuko-time-tracker.nix
+  ./services/web-apps/artalk.nix
   ./services/web-apps/atlassian/confluence.nix
   ./services/web-apps/atlassian/crowd.nix
   ./services/web-apps/atlassian/jira.nix
@@ -1332,6 +1357,7 @@
   ./services/web-apps/chatgpt-retrieval-plugin.nix
   ./services/web-apps/cloudlog.nix
   ./services/web-apps/code-server.nix
+  ./services/web-apps/commafeed.nix
   ./services/web-apps/convos.nix
   ./services/web-apps/crabfit.nix
   ./services/web-apps/davis.nix
@@ -1342,7 +1368,9 @@
   ./services/web-apps/dolibarr.nix
   ./services/web-apps/engelsystem.nix
   ./services/web-apps/ethercalc.nix
+  ./services/web-apps/filesender.nix
   ./services/web-apps/firefly-iii.nix
+  ./services/web-apps/flarum.nix
   ./services/web-apps/fluidd.nix
   ./services/web-apps/freshrss.nix
   ./services/web-apps/galene.nix
@@ -1386,6 +1414,7 @@
   ./services/web-apps/netbox.nix
   ./services/web-apps/nextcloud.nix
   ./services/web-apps/nextcloud-notify_push.nix
+  ./services/web-apps/nextjs-ollama-llm-ui.nix
   ./services/web-apps/nexus.nix
   ./services/web-apps/nifi.nix
   ./services/web-apps/node-red.nix
@@ -1414,6 +1443,7 @@
   ./services/web-apps/selfoss.nix
   ./services/web-apps/shiori.nix
   ./services/web-apps/silverbullet.nix
+  ./services/web-apps/simplesamlphp.nix
   ./services/web-apps/slskd.nix
   ./services/web-apps/snipe-it.nix
   ./services/web-apps/sogo.nix
@@ -1425,11 +1455,13 @@
   ./services/web-apps/windmill.nix
   ./services/web-apps/wordpress.nix
   ./services/web-apps/writefreely.nix
+  ./services/web-apps/your_spotify.nix
   ./services/web-apps/youtrack.nix
   ./services/web-apps/zabbix.nix
   ./services/web-apps/zitadel.nix
   ./services/web-servers/agate.nix
   ./services/web-servers/apache-httpd/default.nix
+  ./services/web-servers/bluemap.nix
   ./services/web-servers/caddy/default.nix
   ./services/web-servers/darkhttpd.nix
   ./services/web-servers/fcgiwrap.nix
@@ -1473,7 +1505,6 @@
   ./services/x11/display-managers/xpra.nix
   ./services/x11/extra-layouts.nix
   ./services/x11/fractalart.nix
-  ./services/x11/gdk-pixbuf.nix
   ./services/x11/hardware/cmt.nix
   ./services/x11/hardware/digimend.nix
   ./services/x11/hardware/synaptics.nix
diff --git a/nixos/modules/profiles/perlless.nix b/nixos/modules/profiles/perlless.nix
index 90abd14f077e4..010e4f8f2a28e 100644
--- a/nixos/modules/profiles/perlless.nix
+++ b/nixos/modules/profiles/perlless.nix
@@ -26,6 +26,6 @@
 
   # Check that the system does not contain a Nix store path that contains the
   # string "perl".
-  system.forbiddenDependenciesRegex = "perl";
+  system.forbiddenDependenciesRegexes = ["perl"];
 
 }
diff --git a/nixos/modules/programs/_1password-gui.nix b/nixos/modules/programs/_1password-gui.nix
index b21e8783f660e..04f36cf0237ab 100644
--- a/nixos/modules/programs/_1password-gui.nix
+++ b/nixos/modules/programs/_1password-gui.nix
@@ -1,7 +1,5 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let
 
   cfg = config.programs._1password-gui;
@@ -9,25 +7,25 @@ let
 in
 {
   imports = [
-    (mkRemovedOptionModule [ "programs" "_1password-gui" "gid" ] ''
+    (lib.mkRemovedOptionModule [ "programs" "_1password-gui" "gid" ] ''
       A preallocated GID will be used instead.
     '')
   ];
 
   options = {
     programs._1password-gui = {
-      enable = mkEnableOption "the 1Password GUI application";
+      enable = lib.mkEnableOption "the 1Password GUI application";
 
-      polkitPolicyOwners = mkOption {
-        type = types.listOf types.str;
+      polkitPolicyOwners = lib.mkOption {
+        type = lib.types.listOf lib.types.str;
         default = [ ];
-        example = literalExpression ''["user1" "user2" "user3"]'';
+        example = lib.literalExpression ''["user1" "user2" "user3"]'';
         description = ''
           A list of users who should be able to integrate 1Password with polkit-based authentication mechanisms.
         '';
       };
 
-      package = mkPackageOption pkgs "1Password GUI" {
+      package = lib.mkPackageOption pkgs "1Password GUI" {
         default = [ "_1password-gui" ];
       };
     };
@@ -39,7 +37,7 @@ in
         polkitPolicyOwners = cfg.polkitPolicyOwners;
       };
     in
-    mkIf cfg.enable {
+    lib.mkIf cfg.enable {
       environment.systemPackages = [ package ];
       users.groups.onepassword.gid = config.ids.gids.onepassword;
 
diff --git a/nixos/modules/programs/_1password.nix b/nixos/modules/programs/_1password.nix
index b87e9b776e85b..5dff199341b94 100644
--- a/nixos/modules/programs/_1password.nix
+++ b/nixos/modules/programs/_1password.nix
@@ -1,7 +1,5 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let
 
   cfg = config.programs._1password;
@@ -9,22 +7,22 @@ let
 in
 {
   imports = [
-    (mkRemovedOptionModule [ "programs" "_1password" "gid" ] ''
+    (lib.mkRemovedOptionModule [ "programs" "_1password" "gid" ] ''
       A preallocated GID will be used instead.
     '')
   ];
 
   options = {
     programs._1password = {
-      enable = mkEnableOption "the 1Password CLI tool";
+      enable = lib.mkEnableOption "the 1Password CLI tool";
 
-      package = mkPackageOption pkgs "1Password CLI" {
+      package = lib.mkPackageOption pkgs "1Password CLI" {
         default = [ "_1password" ];
       };
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
     users.groups.onepassword-cli.gid = config.ids.gids.onepassword-cli;
 
diff --git a/nixos/modules/programs/adb.nix b/nixos/modules/programs/adb.nix
index d8c700bc36b65..62ab6ab4137a1 100644
--- a/nixos/modules/programs/adb.nix
+++ b/nixos/modules/programs/adb.nix
@@ -1,16 +1,14 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 {
-  meta.maintainers = [ maintainers.mic92 ];
+  meta.maintainers = [ lib.maintainers.mic92 ];
 
   ###### interface
   options = {
     programs.adb = {
-      enable = mkOption {
+      enable = lib.mkOption {
         default = false;
-        type = types.bool;
+        type = lib.types.bool;
         description = ''
           Whether to configure system to use Android Debug Bridge (adb).
           To grant access to a user, it must be part of adbusers group:
@@ -21,7 +19,7 @@ with lib;
   };
 
   ###### implementation
-  config = mkIf config.programs.adb.enable {
+  config = lib.mkIf config.programs.adb.enable {
     services.udev.packages = [ pkgs.android-udev-rules ];
     environment.systemPackages = [ pkgs.android-tools ];
     users.groups.adbusers = {};
diff --git a/nixos/modules/programs/alvr.nix b/nixos/modules/programs/alvr.nix
index e5de06f1157ad..da66200cf075b 100644
--- a/nixos/modules/programs/alvr.nix
+++ b/nixos/modules/programs/alvr.nix
@@ -1,19 +1,17 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let
   cfg = config.programs.alvr;
 in
 {
   options = {
     programs.alvr = {
-      enable = mkEnableOption "ALVR, the VR desktop streamer";
+      enable = lib.mkEnableOption "ALVR, the VR desktop streamer";
 
-      package = mkPackageOption pkgs "alvr" { };
+      package = lib.mkPackageOption pkgs "alvr" { };
 
-      openFirewall = mkOption {
-        type = types.bool;
+      openFirewall = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to open the default ports in the firewall for the ALVR server.
@@ -22,14 +20,14 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
 
-    networking.firewall = mkIf cfg.openFirewall {
+    networking.firewall = lib.mkIf cfg.openFirewall {
       allowedTCPPorts = [ 9943 9944 ];
       allowedUDPPorts = [ 9943 9944 ];
     };
   };
 
-  meta.maintainers = with maintainers; [ passivelemon ];
+  meta.maintainers = with lib.maintainers; [ passivelemon ];
 }
diff --git a/nixos/modules/programs/appgate-sdp.nix b/nixos/modules/programs/appgate-sdp.nix
index 6d61c87eeb612..f4d4140571a64 100644
--- a/nixos/modules/programs/appgate-sdp.nix
+++ b/nixos/modules/programs/appgate-sdp.nix
@@ -1,15 +1,13 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 {
   options = {
     programs.appgate-sdp = {
-      enable = mkEnableOption "the AppGate SDP VPN client";
+      enable = lib.mkEnableOption "the AppGate SDP VPN client";
     };
   };
 
-  config = mkIf config.programs.appgate-sdp.enable {
+  config = lib.mkIf config.programs.appgate-sdp.enable {
     boot.kernelModules = [ "tun" ];
     environment.systemPackages = [ pkgs.appgate-sdp ];
     services.dbus.packages = [ pkgs.appgate-sdp ];
diff --git a/nixos/modules/programs/atop.nix b/nixos/modules/programs/atop.nix
index 618b641143590..3738f926ca3d8 100644
--- a/nixos/modules/programs/atop.nix
+++ b/nixos/modules/programs/atop.nix
@@ -2,8 +2,6 @@
 
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let cfg = config.programs.atop;
 
 in
@@ -14,31 +12,31 @@ in
 
     programs.atop = rec {
 
-      enable = mkEnableOption "Atop, a tool for monitoring system resources";
+      enable = lib.mkEnableOption "Atop, a tool for monitoring system resources";
 
-      package = mkPackageOption pkgs "atop" { };
+      package = lib.mkPackageOption pkgs "atop" { };
 
       netatop = {
-        enable = mkOption {
-          type = types.bool;
+        enable = lib.mkOption {
+          type = lib.types.bool;
           default = false;
           description = ''
             Whether to install and enable the netatop kernel module.
             Note: this sets the kernel taint flag "O" for loading out-of-tree modules.
           '';
         };
-        package = mkOption {
-          type = types.package;
+        package = lib.mkOption {
+          type = lib.types.package;
           default = config.boot.kernelPackages.netatop;
-          defaultText = literalExpression "config.boot.kernelPackages.netatop";
+          defaultText = lib.literalExpression "config.boot.kernelPackages.netatop";
           description = ''
             Which package to use for netatop.
           '';
         };
       };
 
-      atopgpu.enable = mkOption {
-        type = types.bool;
+      atopgpu.enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to install and enable the atopgpud daemon to get information about
@@ -46,8 +44,8 @@ in
         '';
       };
 
-      setuidWrapper.enable = mkOption {
-        type = types.bool;
+      setuidWrapper.enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to install a setuid wrapper for Atop. This is required to use some of
@@ -56,24 +54,24 @@ in
         '';
       };
 
-      atopService.enable = mkOption {
-        type = types.bool;
+      atopService.enable = lib.mkOption {
+        type = lib.types.bool;
         default = true;
         description = ''
           Whether to enable the atop service responsible for storing statistics for
           long-term analysis.
         '';
       };
-      atopRotateTimer.enable = mkOption {
-        type = types.bool;
+      atopRotateTimer.enable = lib.mkOption {
+        type = lib.types.bool;
         default = true;
         description = ''
           Whether to enable the atop-rotate timer, which restarts the atop service
           daily to make sure the data files are rotate.
         '';
       };
-      atopacctService.enable = mkOption {
-        type = types.bool;
+      atopacctService.enable = lib.mkOption {
+        type = lib.types.bool;
         default = true;
         description = ''
           Whether to enable the atopacct service which manages process accounting.
@@ -81,8 +79,8 @@ in
           two refresh intervals.
         '';
       };
-      settings = mkOption {
-        type = types.attrs;
+      settings = lib.mkOption {
+        type = lib.types.attrs;
         default = { };
         example = {
           flags = "a1f";
@@ -95,7 +93,7 @@ in
     };
   };
 
-  config = mkIf cfg.enable (
+  config = lib.mkIf cfg.enable (
     let
       atop =
         if cfg.atopgpu.enable then
@@ -104,11 +102,11 @@ in
           cfg.package;
     in
     {
-      environment.etc = mkIf (cfg.settings != { }) {
-        atoprc.text = concatStrings
-          (mapAttrsToList
+      environment.etc = lib.mkIf (cfg.settings != { }) {
+        atoprc.text = lib.concatStrings
+          (lib.mapAttrsToList
             (n: v: ''
-              ${n} ${toString v}
+              ${n} ${builtins.toString v}
             '')
             cfg.settings);
       };
diff --git a/nixos/modules/programs/ausweisapp.nix b/nixos/modules/programs/ausweisapp.nix
index 0359e58c554ca..ebd6a3e13bf66 100644
--- a/nixos/modules/programs/ausweisapp.nix
+++ b/nixos/modules/programs/ausweisapp.nix
@@ -1,15 +1,13 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg  = config.programs.ausweisapp;
 in
 {
   options.programs.ausweisapp = {
-    enable = mkEnableOption "AusweisApp";
+    enable = lib.mkEnableOption "AusweisApp";
 
-    openFirewall = mkOption {
+    openFirewall = lib.mkOption {
       description = ''
         Whether to open the required firewall ports for the Smartphone as Card Reader (SaC) functionality of AusweisApp.
       '';
@@ -18,7 +16,7 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = with pkgs; [ ausweisapp ];
     networking.firewall.allowedUDPPorts = lib.optionals cfg.openFirewall [ 24727 ];
   };
diff --git a/nixos/modules/programs/autojump.nix b/nixos/modules/programs/autojump.nix
index ecfc2f658079d..5011d7e142375 100644
--- a/nixos/modules/programs/autojump.nix
+++ b/nixos/modules/programs/autojump.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.autojump;
   prg = config.programs;
@@ -10,8 +8,8 @@ in
   options = {
     programs.autojump = {
 
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to enable autojump.
@@ -22,12 +20,12 @@ in
 
   ###### implementation
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.pathsToLink = [ "/share/autojump" ];
     environment.systemPackages = [ pkgs.autojump ];
 
     programs.bash.interactiveShellInit = "source ${pkgs.autojump}/share/autojump/autojump.bash";
-    programs.zsh.interactiveShellInit = mkIf prg.zsh.enable "source ${pkgs.autojump}/share/autojump/autojump.zsh";
-    programs.fish.interactiveShellInit = mkIf prg.fish.enable "source ${pkgs.autojump}/share/autojump/autojump.fish";
+    programs.zsh.interactiveShellInit = lib.mkIf prg.zsh.enable "source ${pkgs.autojump}/share/autojump/autojump.zsh";
+    programs.fish.interactiveShellInit = lib.mkIf prg.fish.enable "source ${pkgs.autojump}/share/autojump/autojump.fish";
   };
 }
diff --git a/nixos/modules/programs/bandwhich.nix b/nixos/modules/programs/bandwhich.nix
index 2c78584f2d248..e2c55ca5bea4a 100644
--- a/nixos/modules/programs/bandwhich.nix
+++ b/nixos/modules/programs/bandwhich.nix
@@ -1,15 +1,13 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let cfg = config.programs.bandwhich;
 in {
-  meta.maintainers = with maintainers; [ Br1ght0ne ];
+  meta.maintainers = with lib.maintainers; [ Br1ght0ne ];
 
   options = {
     programs.bandwhich = {
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to add bandwhich to the global environment and configure a
@@ -19,7 +17,7 @@ in {
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = with pkgs; [ bandwhich ];
     security.wrappers.bandwhich = {
       owner = "root";
diff --git a/nixos/modules/programs/bash-my-aws.nix b/nixos/modules/programs/bash-my-aws.nix
index 15e429a754976..85618ad98f080 100644
--- a/nixos/modules/programs/bash-my-aws.nix
+++ b/nixos/modules/programs/bash-my-aws.nix
@@ -1,7 +1,5 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let
   prg = config.programs;
   cfg = prg.bash-my-aws;
@@ -13,11 +11,11 @@ in
   {
     options = {
       programs.bash-my-aws = {
-        enable = mkEnableOption "bash-my-aws";
+        enable = lib.mkEnableOption "bash-my-aws";
       };
     };
 
-    config = mkIf cfg.enable {
+    config = lib.mkIf cfg.enable {
       environment.systemPackages = with pkgs; [ bash-my-aws ];
 
       programs.bash.interactiveShellInit = initScript;
diff --git a/nixos/modules/programs/bash/bash-completion.nix b/nixos/modules/programs/bash/bash-completion.nix
index b8e5b1bfa336f..f143361bc9331 100644
--- a/nixos/modules/programs/bash/bash-completion.nix
+++ b/nixos/modules/programs/bash/bash-completion.nix
@@ -1,18 +1,22 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
-  enable = config.programs.bash.enableCompletion;
+  cfg = config.programs.bash;
 in
 {
-  options = {
-    programs.bash.enableCompletion = mkEnableOption "Bash completion for all interactive bash shells" // {
+  options.programs.bash.completion = {
+    enable = lib.mkEnableOption "Bash completion for all interactive bash shells" // {
       default = true;
     };
+
+    package = lib.mkPackageOption pkgs "bash-completion" { };
   };
 
-  config = mkIf enable {
+  imports = [
+    (lib.mkRenamedOptionModule [ "programs" "bash" "enableCompletion" ] [ "programs" "bash" "completion" "enable" ])
+  ];
+
+  config = lib.mkIf cfg.completion.enable {
     programs.bash.promptPluginInit = ''
       # Check whether we're running a version of Bash that has support for
       # programmable completion. If we do, enable all modules installed in
@@ -21,7 +25,7 @@ in
       # $XDG_DATA_DIRS/bash-completion/completions/
       # on demand, so they do not need to be sourced here.
       if shopt -q progcomp &>/dev/null; then
-        . "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh"
+        . "${cfg.completion.package}/etc/profile.d/bash_completion.sh"
         nullglobStatus=$(shopt -p nullglob)
         shopt -s nullglob
         for p in $NIX_PROFILES; do
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
index 21ef8338d8dd8..4c06f0aad9f81 100644
--- a/nixos/modules/programs/bash/bash.nix
+++ b/nixos/modules/programs/bash/bash.nix
@@ -3,24 +3,22 @@
 
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
 
   cfge = config.environment;
 
   cfg = config.programs.bash;
 
-  bashAliases = concatStringsSep "\n" (
-    mapAttrsFlatten (k: v: "alias -- ${k}=${escapeShellArg v}")
-      (filterAttrs (k: v: v != null) cfg.shellAliases)
+  bashAliases = builtins.concatStringsSep "\n" (
+    lib.mapAttrsFlatten (k: v: "alias -- ${k}=${lib.escapeShellArg v}")
+      (lib.filterAttrs (k: v: v != null) cfg.shellAliases)
   );
 
 in
 
 {
   imports = [
-    (mkRemovedOptionModule [ "programs" "bash" "enable" ] "")
+    (lib.mkRemovedOptionModule [ "programs" "bash" "enable" ] "")
   ];
 
   options = {
@@ -28,7 +26,7 @@ in
     programs.bash = {
 
       /*
-      enable = mkOption {
+      enable = lib.mkOption {
         default = true;
         description = ''
           Whenever to configure Bash as an interactive shell.
@@ -38,44 +36,44 @@ in
           set this variable if you have another shell configured
           with NixOS.
         '';
-        type = types.bool;
+        type = lib.types.bool;
       };
       */
 
-      shellAliases = mkOption {
+      shellAliases = lib.mkOption {
         default = {};
         description = ''
           Set of aliases for bash shell, which overrides {option}`environment.shellAliases`.
           See {option}`environment.shellAliases` for an option format description.
         '';
-        type = with types; attrsOf (nullOr (either str path));
+        type = with lib.types; attrsOf (nullOr (either str path));
       };
 
-      shellInit = mkOption {
+      shellInit = lib.mkOption {
         default = "";
         description = ''
           Shell script code called during bash shell initialisation.
         '';
-        type = types.lines;
+        type = lib.types.lines;
       };
 
-      loginShellInit = mkOption {
+      loginShellInit = lib.mkOption {
         default = "";
         description = ''
           Shell script code called during login bash shell initialisation.
         '';
-        type = types.lines;
+        type = lib.types.lines;
       };
 
-      interactiveShellInit = mkOption {
+      interactiveShellInit = lib.mkOption {
         default = "";
         description = ''
           Shell script code called during interactive bash shell initialisation.
         '';
-        type = types.lines;
+        type = lib.types.lines;
       };
 
-      promptInit = mkOption {
+      promptInit = lib.mkOption {
         default = ''
           # Provide a nice prompt if the terminal supports it.
           if [ "$TERM" != "dumb" ] || [ -n "$INSIDE_EMACS" ]; then
@@ -95,15 +93,15 @@ in
         description = ''
           Shell script code used to initialise the bash prompt.
         '';
-        type = types.lines;
+        type = lib.types.lines;
       };
 
-      promptPluginInit = mkOption {
+      promptPluginInit = lib.mkOption {
         default = "";
         description = ''
           Shell script code used to initialise bash prompt plugins.
         '';
-        type = types.lines;
+        type = lib.types.lines;
         internal = true;
       };
 
@@ -111,11 +109,11 @@ in
 
   };
 
-  config = /* mkIf cfg.enable */ {
+  config = /* lib.mkIf cfg.enable */ {
 
     programs.bash = {
 
-      shellAliases = mapAttrs (name: mkDefault) cfge.shellAliases;
+      shellAliases = builtins.mapAttrs (name: lib.mkDefault) cfge.shellAliases;
 
       shellInit = ''
         if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]; then
@@ -196,11 +194,11 @@ in
 
     # Configuration for readline in bash. We use "option default"
     # priority to allow user override using both .text and .source.
-    environment.etc.inputrc.source = mkOptionDefault ./inputrc;
+    environment.etc.inputrc.source = lib.mkOptionDefault ./inputrc;
 
-    users.defaultUserShell = mkDefault pkgs.bashInteractive;
+    users.defaultUserShell = lib.mkDefault pkgs.bashInteractive;
 
-    environment.pathsToLink = optionals cfg.enableCompletion [
+    environment.pathsToLink = lib.optionals cfg.completion.enable [
       "/etc/bash_completion.d"
       "/share/bash-completion"
     ];
diff --git a/nixos/modules/programs/bash/blesh.nix b/nixos/modules/programs/bash/blesh.nix
index ea342b0ce3eec..b5ca83a883bb0 100644
--- a/nixos/modules/programs/bash/blesh.nix
+++ b/nixos/modules/programs/bash/blesh.nix
@@ -1,16 +1,15 @@
 { lib, config, pkgs, ... }:
-with lib;
 let
   cfg = config.programs.bash.blesh;
 in {
   options = {
-    programs.bash.blesh.enable = mkEnableOption "blesh, a full-featured line editor written in pure Bash";
+    programs.bash.blesh.enable = lib.mkEnableOption "blesh, a full-featured line editor written in pure Bash";
   };
 
-  config = mkIf cfg.enable {
-    programs.bash.interactiveShellInit = mkBefore ''
+  config = lib.mkIf cfg.enable {
+    programs.bash.interactiveShellInit = lib.mkBefore ''
       source ${pkgs.blesh}/share/blesh/ble.sh
     '';
   };
-  meta.maintainers = with maintainers; [ laalsaas ];
+  meta.maintainers = with lib.maintainers; [ laalsaas ];
 }
diff --git a/nixos/modules/programs/bash/ls-colors.nix b/nixos/modules/programs/bash/ls-colors.nix
index 254ee14c477d6..3ee00e93d4dae 100644
--- a/nixos/modules/programs/bash/ls-colors.nix
+++ b/nixos/modules/programs/bash/ls-colors.nix
@@ -1,18 +1,16 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   enable = config.programs.bash.enableLsColors;
 in
 {
   options = {
-    programs.bash.enableLsColors = mkEnableOption "extra colors in directory listings" // {
+    programs.bash.enableLsColors = lib.mkEnableOption "extra colors in directory listings" // {
       default = true;
     };
   };
 
-  config = mkIf enable {
+  config = lib.mkIf enable {
     programs.bash.promptPluginInit = ''
       eval "$(${pkgs.coreutils}/bin/dircolors -b)"
     '';
diff --git a/nixos/modules/programs/bash/undistract-me.nix b/nixos/modules/programs/bash/undistract-me.nix
index 0e6465e048a10..af4f3a737dabd 100644
--- a/nixos/modules/programs/bash/undistract-me.nix
+++ b/nixos/modules/programs/bash/undistract-me.nix
@@ -1,36 +1,34 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.bash.undistractMe;
 in
 {
   options = {
     programs.bash.undistractMe = {
-      enable = mkEnableOption "notifications when long-running terminal commands complete";
+      enable = lib.mkEnableOption "notifications when long-running terminal commands complete";
 
-      playSound = mkEnableOption "notification sounds when long-running terminal commands complete";
+      playSound = lib.mkEnableOption "notification sounds when long-running terminal commands complete";
 
-      timeout = mkOption {
+      timeout = lib.mkOption {
         default = 10;
         description = ''
           Number of seconds it would take for a command to be considered long-running.
         '';
-        type = types.int;
+        type = lib.types.int;
       };
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     programs.bash.promptPluginInit = ''
-      export LONG_RUNNING_COMMAND_TIMEOUT=${toString cfg.timeout}
+      export LONG_RUNNING_COMMAND_TIMEOUT=${builtins.toString cfg.timeout}
       export UDM_PLAY_SOUND=${if cfg.playSound then "1" else "0"}
       . "${pkgs.undistract-me}/etc/profile.d/undistract-me.sh"
     '';
   };
 
   meta = {
-    maintainers = with maintainers; [ kira-bruneau ];
+    maintainers = with lib.maintainers; [ kira-bruneau ];
   };
 }
diff --git a/nixos/modules/programs/benchexec.nix b/nixos/modules/programs/benchexec.nix
new file mode 100644
index 0000000000000..652670c117ea3
--- /dev/null
+++ b/nixos/modules/programs/benchexec.nix
@@ -0,0 +1,98 @@
+{ lib
+, pkgs
+, config
+, options
+, ...
+}:
+let
+  cfg = config.programs.benchexec;
+  opt = options.programs.benchexec;
+
+  filterUsers = x:
+    if builtins.isString x then config.users.users ? ${x} else
+    if builtins.isInt    x then x                         else
+    throw "filterUsers expects string (username) or int (UID)";
+
+  uid = x:
+    if builtins.isString x then config.users.users.${x}.uid else
+    if builtins.isInt    x then x                           else
+    throw "uid expects string (username) or int (UID)";
+in
+{
+  options.programs.benchexec = {
+    enable = lib.mkEnableOption "BenchExec";
+    package = lib.options.mkPackageOption pkgs "benchexec" { };
+
+    users = lib.options.mkOption {
+      type = with lib.types; listOf (either str int);
+      description = ''
+        Users that intend to use BenchExec.
+        Provide usernames of users that are configured via {option}`${options.users.users}` as string,
+        and UIDs of "mutable users" as integers.
+        Control group delegation will be configured via systemd.
+        For more information, see <https://github.com/sosy-lab/benchexec/blob/3.18/doc/INSTALL.md#setting-up-cgroups>.
+      '';
+      default = [ ];
+      example = lib.literalExpression ''
+        [
+          "alice" # username of a user configured via ${options.users.users}
+          1007    # UID of a mutable user
+        ]
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    assertions = (map
+      (user: {
+        assertion = config.users.users ? ${user};
+        message = ''
+          The user '${user}' intends to use BenchExec (via `${opt.users}`), but is not configured via `${options.users.users}`.
+        '';
+      })
+      (builtins.filter builtins.isString cfg.users)
+    ) ++ (map
+      (id: {
+        assertion = config.users.mutableUsers;
+        message = ''
+          The user with UID '${id}' intends to use BenchExec (via `${opt.users}`), but mutable users are disabled via `${options.users.mutableUsers}`.
+        '';
+      })
+      (builtins.filter builtins.isInt cfg.users)
+    ) ++ [
+      {
+        assertion = config.systemd.enableUnifiedCgroupHierarchy == true;
+        message = ''
+          The BenchExec module `${opt.enable}` only supports control groups 2 (`${options.systemd.enableUnifiedCgroupHierarchy} = true`).
+        '';
+      }
+    ];
+
+    environment.systemPackages = [ cfg.package ];
+
+    # See <https://github.com/sosy-lab/benchexec/blob/3.18/doc/INSTALL.md#setting-up-cgroups>.
+    systemd.services = builtins.listToAttrs (map
+      (user: {
+        name = "user@${builtins.toString (uid user)}";
+        value = {
+          serviceConfig.Delegate = "yes";
+          overrideStrategy = "asDropin";
+        };
+      })
+      (builtins.filter filterUsers cfg.users));
+
+    # See <https://github.com/sosy-lab/benchexec/blob/3.18/doc/INSTALL.md#requirements>.
+    virtualisation.lxc.lxcfs.enable = lib.mkDefault true;
+
+    # See <https://github.com/sosy-lab/benchexec/blob/3.18/doc/INSTALL.md#requirements>.
+    programs = {
+      cpu-energy-meter.enable = lib.mkDefault true;
+      pqos-wrapper.enable = lib.mkDefault true;
+    };
+
+    # See <https://github.com/sosy-lab/benchexec/blob/3.18/doc/INSTALL.md#kernel-requirements>.
+    security.unprivilegedUsernsClone = true;
+  };
+
+  meta.maintainers = with lib.maintainers; [ lorenzleutgeb ];
+}
diff --git a/nixos/modules/programs/browserpass.nix b/nixos/modules/programs/browserpass.nix
index 2894e237e3d4a..ab6be266ea8d6 100644
--- a/nixos/modules/programs/browserpass.nix
+++ b/nixos/modules/programs/browserpass.nix
@@ -1,12 +1,10 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 {
 
-  options.programs.browserpass.enable = mkEnableOption "Browserpass native messaging host";
+  options.programs.browserpass.enable = lib.mkEnableOption "Browserpass native messaging host";
 
-  config = mkIf config.programs.browserpass.enable {
+  config = lib.mkIf config.programs.browserpass.enable {
     environment.etc = let
       appId = "com.github.browserpass.native.json";
       source = part: "${pkgs.browserpass}/lib/browserpass/${part}/${appId}";
diff --git a/nixos/modules/programs/calls.nix b/nixos/modules/programs/calls.nix
index 0cf05f8a2ea0c..36a4c51ddf438 100644
--- a/nixos/modules/programs/calls.nix
+++ b/nixos/modules/programs/calls.nix
@@ -1,19 +1,17 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.calls;
 in {
   options = {
     programs.calls = {
-      enable = mkEnableOption ''
+      enable = lib.mkEnableOption ''
         GNOME calls: a phone dialer and call handler
       '';
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     programs.dconf.enable = true;
 
     environment.systemPackages = [
diff --git a/nixos/modules/programs/cdemu.nix b/nixos/modules/programs/cdemu.nix
index 3ee8b2d8fcd65..1aac28af1d2ca 100644
--- a/nixos/modules/programs/cdemu.nix
+++ b/nixos/modules/programs/cdemu.nix
@@ -1,36 +1,34 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let cfg = config.programs.cdemu;
 in {
 
   options = {
     programs.cdemu = {
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           {command}`cdemu` for members of
           {option}`programs.cdemu.group`.
         '';
       };
-      group = mkOption {
-        type = types.str;
+      group = lib.mkOption {
+        type = lib.types.str;
         default = "cdrom";
         description = ''
           Group that users must be in to use {command}`cdemu`.
         '';
       };
-      gui = mkOption {
-        type = types.bool;
+      gui = lib.mkOption {
+        type = lib.types.bool;
         default = true;
         description = ''
           Whether to install the {command}`cdemu` GUI (gCDEmu).
         '';
       };
-      image-analyzer = mkOption {
-        type = types.bool;
+      image-analyzer = lib.mkOption {
+        type = lib.types.bool;
         default = true;
         description = ''
           Whether to install the image analyzer.
@@ -39,7 +37,7 @@ in {
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
 
     boot = {
       extraModulePackages = [ config.boot.kernelPackages.vhba ];
@@ -68,8 +66,8 @@ in {
 
     environment.systemPackages =
       [ pkgs.cdemu-daemon pkgs.cdemu-client ]
-      ++ optional cfg.gui pkgs.gcdemu
-      ++ optional cfg.image-analyzer pkgs.image-analyzer;
+      ++ lib.optional cfg.gui pkgs.gcdemu
+      ++ lib.optional cfg.image-analyzer pkgs.image-analyzer;
   };
 
 }
diff --git a/nixos/modules/programs/cfs-zen-tweaks.nix b/nixos/modules/programs/cfs-zen-tweaks.nix
index 28d1ef5992d7d..bc16264039352 100644
--- a/nixos/modules/programs/cfs-zen-tweaks.nix
+++ b/nixos/modules/programs/cfs-zen-tweaks.nix
@@ -2,8 +2,6 @@
 
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let
 
   cfg = config.programs.cfs-zen-tweaks;
@@ -13,14 +11,14 @@ in
 {
 
   meta = {
-    maintainers = with maintainers; [ mkg20001 ];
+    maintainers = with lib.maintainers; [ mkg20001 ];
   };
 
   options = {
-    programs.cfs-zen-tweaks.enable = mkEnableOption "CFS Zen Tweaks";
+    programs.cfs-zen-tweaks.enable = lib.mkEnableOption "CFS Zen Tweaks";
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     systemd.packages = [ pkgs.cfs-zen-tweaks ];
 
     systemd.services.set-cfs-tweaks.wantedBy = [
diff --git a/nixos/modules/programs/chromium.nix b/nixos/modules/programs/chromium.nix
index fa5abe957a904..4d248dbe0945f 100644
--- a/nixos/modules/programs/chromium.nix
+++ b/nixos/modules/programs/chromium.nix
@@ -1,11 +1,9 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.chromium;
 
-  defaultProfile = filterAttrs (k: v: v != null) {
+  defaultProfile = lib.filterAttrs (k: v: v != null) {
     HomepageLocation = cfg.homepageLocation;
     DefaultSearchProviderEnabled = cfg.defaultSearchProviderEnabled;
     DefaultSearchProviderSearchURL = cfg.defaultSearchProviderSearchURL;
@@ -19,14 +17,14 @@ in
 
   options = {
     programs.chromium = {
-      enable = mkEnableOption "{command}`chromium` policies";
+      enable = lib.mkEnableOption "{command}`chromium` policies";
 
-      enablePlasmaBrowserIntegration = mkEnableOption "Native Messaging Host for Plasma Browser Integration";
+      enablePlasmaBrowserIntegration = lib.mkEnableOption "Native Messaging Host for Plasma Browser Integration";
 
-      plasmaBrowserIntegrationPackage = mkPackageOption pkgs [ "plasma5Packages" "plasma-browser-integration" ] { };
+      plasmaBrowserIntegrationPackage = lib.mkPackageOption pkgs [ "plasma5Packages" "plasma-browser-integration" ] { };
 
-      extensions = mkOption {
-        type = with types; nullOr (listOf str);
+      extensions = lib.mkOption {
+        type = with lib.types; nullOr (listOf str);
         description = ''
           List of chromium extensions to install.
           For list of plugins ids see id in url of extensions on
@@ -38,7 +36,7 @@ in
           for additional details.
         '';
         default = null;
-        example = literalExpression ''
+        example = lib.literalExpression ''
           [
             "chlffgpmiacpedhhbkiomidkjlcfhogd" # pushbullet
             "mbniclmhobmnbdlbpiphghaielnnpgdp" # lightshot
@@ -48,36 +46,36 @@ in
         '';
       };
 
-      homepageLocation = mkOption {
-        type = types.nullOr types.str;
+      homepageLocation = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
         description = "Chromium default homepage";
         default = null;
         example = "https://nixos.org";
       };
 
-      defaultSearchProviderEnabled = mkOption {
-        type = types.nullOr types.bool;
+      defaultSearchProviderEnabled = lib.mkOption {
+        type = lib.types.nullOr lib.types.bool;
         description = "Enable the default search provider.";
         default = null;
         example = true;
       };
 
-      defaultSearchProviderSearchURL = mkOption {
-        type = types.nullOr types.str;
+      defaultSearchProviderSearchURL = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
         description = "Chromium default search provider url.";
         default = null;
         example = "https://encrypted.google.com/search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}ie={inputEncoding}";
       };
 
-      defaultSearchProviderSuggestURL = mkOption {
-        type = types.nullOr types.str;
+      defaultSearchProviderSuggestURL = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
         description = "Chromium default search provider url for suggestions.";
         default = null;
         example = "https://encrypted.google.com/complete/search?output=chrome&q={searchTerms}";
       };
 
-      extraOpts = mkOption {
-        type = types.attrs;
+      extraOpts = lib.mkOption {
+        type = lib.types.attrs;
         description = ''
           Extra chromium policy options. A list of available policies
           can be found in the Chrome Enterprise documentation:
@@ -85,7 +83,7 @@ in
           Make sure the selected policy is supported on Linux and your browser version.
         '';
         default = {};
-        example = literalExpression ''
+        example = lib.literalExpression ''
           {
             "BrowserSignin" = 0;
             "SyncDisabled" = true;
@@ -99,8 +97,8 @@ in
         '';
       };
 
-      initialPrefs = mkOption {
-        type = types.attrs;
+      initialPrefs = lib.mkOption {
+        type = lib.types.attrs;
         description = ''
           Initial preferences are used to configure the browser for the first run.
           Unlike {option}`programs.chromium.extraOpts`, initialPrefs can be changed by users in the browser settings.
@@ -108,7 +106,7 @@ in
           <https://www.chromium.org/administrators/configuring-other-preferences/>
         '';
         default = {};
-        example = literalExpression ''
+        example = lib.literalExpression ''
           {
             "first_run_tabs" = [
               "https://nixos.org/"
diff --git a/nixos/modules/programs/cnping.nix b/nixos/modules/programs/cnping.nix
index 77cbf4d82086b..f4b5aa845b5f2 100644
--- a/nixos/modules/programs/cnping.nix
+++ b/nixos/modules/programs/cnping.nix
@@ -1,18 +1,16 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.cnping;
 in
 {
   options = {
     programs.cnping = {
-      enable = mkEnableOption "a setcap wrapper for cnping";
+      enable = lib.mkEnableOption "a setcap wrapper for cnping";
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     security.wrappers.cnping = {
       source = "${pkgs.cnping}/bin/cnping";
       capabilities = "cap_net_raw+ep";
diff --git a/nixos/modules/programs/command-not-found/command-not-found.nix b/nixos/modules/programs/command-not-found/command-not-found.nix
index 4d2a89b515842..a223e811728dc 100644
--- a/nixos/modules/programs/command-not-found/command-not-found.nix
+++ b/nixos/modules/programs/command-not-found/command-not-found.nix
@@ -5,8 +5,6 @@
 
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.command-not-found;
   commandNotFound = pkgs.substituteAll {
@@ -23,8 +21,8 @@ in
 {
   options.programs.command-not-found = {
 
-    enable = mkOption {
-      type = types.bool;
+    enable = lib.mkOption {
+      type = lib.types.bool;
       default = true;
       description = ''
         Whether interactive shells should show which Nix package (if
@@ -32,7 +30,7 @@ in
       '';
     };
 
-    dbPath = mkOption {
+    dbPath = lib.mkOption {
       default = "/nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite" ;
       description = ''
         Absolute path to programs.sqlite.
@@ -40,11 +38,11 @@ in
         By default this file will be provided by your channel
         (nixexprs.tar.xz).
       '';
-      type = types.path;
+      type = lib.types.path;
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     programs.bash.interactiveShellInit =
       ''
         # This function is called whenever a command is not found.
diff --git a/nixos/modules/programs/coolercontrol.nix b/nixos/modules/programs/coolercontrol.nix
index 8c9a39d2eba99..1c64d46ae2b90 100644
--- a/nixos/modules/programs/coolercontrol.nix
+++ b/nixos/modules/programs/coolercontrol.nix
@@ -48,9 +48,11 @@ in
 
     # Nvidia support
     (lib.mkIf cfg.nvidiaSupport {
-      systemd.services.coolercontrold.path = with config.boot.kernelPackages; [
-        nvidia_x11 # nvidia-smi
-        nvidia_x11.settings # nvidia-settings
+      systemd.services.coolercontrold.path = let
+        nvidiaPkg = config.hardware.nvidia.package;
+      in [
+        nvidiaPkg # nvidia-smi
+        nvidiaPkg.settings # nvidia-settings
       ];
     })
   ]);
diff --git a/nixos/modules/programs/cpu-energy-meter.nix b/nixos/modules/programs/cpu-energy-meter.nix
new file mode 100644
index 0000000000000..653ec067492d7
--- /dev/null
+++ b/nixos/modules/programs/cpu-energy-meter.nix
@@ -0,0 +1,27 @@
+{ config
+, lib
+, pkgs
+, ...
+}: {
+  options.programs.cpu-energy-meter = {
+    enable = lib.mkEnableOption "CPU Energy Meter";
+    package = lib.mkPackageOption pkgs "cpu-energy-meter" { };
+  };
+
+  config =
+    let
+      cfg = config.programs.cpu-energy-meter;
+    in
+    lib.mkIf cfg.enable {
+      hardware.cpu.x86.msr.enable = true;
+
+      security.wrappers.${cfg.package.meta.mainProgram} = {
+        owner = "nobody";
+        group = config.hardware.cpu.x86.msr.group;
+        source = lib.getExe cfg.package;
+        capabilities = "cap_sys_rawio=ep";
+      };
+    };
+
+  meta.maintainers = with lib.maintainers; [ lorenzleutgeb ];
+}
diff --git a/nixos/modules/programs/criu.nix b/nixos/modules/programs/criu.nix
index 9414d0b27f0d2..492a158923cb2 100644
--- a/nixos/modules/programs/criu.nix
+++ b/nixos/modules/programs/criu.nix
@@ -1,14 +1,12 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let cfg = config.programs.criu;
 in {
 
   options = {
     programs.criu = {
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Install {command}`criu` along with necessary kernel options.
@@ -16,7 +14,7 @@ in {
       };
     };
   };
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     system.requiredKernelConfig = with config.lib.kernelConfig; [
       (isYes "CHECKPOINT_RESTORE")
     ];
diff --git a/nixos/modules/programs/digitalbitbox/default.nix b/nixos/modules/programs/digitalbitbox/default.nix
index 10b5a88171fcd..06d33966b4a07 100644
--- a/nixos/modules/programs/digitalbitbox/default.nix
+++ b/nixos/modules/programs/digitalbitbox/default.nix
@@ -1,29 +1,27 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.digitalbitbox;
 in
 
 {
   options.programs.digitalbitbox = {
-    enable = mkOption {
-      type = types.bool;
+    enable = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         Installs the Digital Bitbox application and enables the complementary hardware module.
       '';
     };
 
-    package = mkPackageOption pkgs "digitalbitbox" {
+    package = lib.mkPackageOption pkgs "digitalbitbox" {
       extraDescription = ''
         This can be used to install a package with udev rules that differ from the defaults.
       '';
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
     hardware.digitalbitbox = {
       enable = true;
diff --git a/nixos/modules/programs/dmrconfig.nix b/nixos/modules/programs/dmrconfig.nix
index 15338681e642a..0078ca19f41a1 100644
--- a/nixos/modules/programs/dmrconfig.nix
+++ b/nixos/modules/programs/dmrconfig.nix
@@ -1,19 +1,17 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.dmrconfig;
 
 in {
-  meta.maintainers = with maintainers; [ ];
+  meta.maintainers = with lib.maintainers; [ ];
 
   ###### interface
   options = {
     programs.dmrconfig = {
-      enable = mkOption {
+      enable = lib.mkOption {
         default = false;
-        type = types.bool;
+        type = lib.types.bool;
         description = ''
           Whether to configure system to enable use of dmrconfig. This
           enables the required udev rules and installs the program.
@@ -21,12 +19,12 @@ in {
         relatedPackages = [ "dmrconfig" ];
       };
 
-      package = mkPackageOption pkgs "dmrconfig" { };
+      package = lib.mkPackageOption pkgs "dmrconfig" { };
     };
   };
 
   ###### implementation
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
     services.udev.packages = [ cfg.package ];
   };
diff --git a/nixos/modules/programs/droidcam.nix b/nixos/modules/programs/droidcam.nix
index 9843a1f5be252..eef3997e6b809 100644
--- a/nixos/modules/programs/droidcam.nix
+++ b/nixos/modules/programs/droidcam.nix
@@ -1,10 +1,8 @@
 { lib, pkgs, config, ... }:
 
-with lib;
-
 {
   options.programs.droidcam = {
-    enable = mkEnableOption "DroidCam client";
+    enable = lib.mkEnableOption "DroidCam client";
   };
 
   config = lib.mkIf config.programs.droidcam.enable {
diff --git a/nixos/modules/programs/dublin-traceroute.nix b/nixos/modules/programs/dublin-traceroute.nix
index 6ff8a5bdefc39..de9446ad7377c 100644
--- a/nixos/modules/programs/dublin-traceroute.nix
+++ b/nixos/modules/programs/dublin-traceroute.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.dublin-traceroute;
 
@@ -10,22 +8,22 @@ in {
 
   options = {
     programs.dublin-traceroute = {
-      enable = mkEnableOption ''
+      enable = lib.mkEnableOption ''
       dublin-traceroute, add it to the global environment and configure a setcap wrapper for it.
       '';
 
-      package = mkPackageOption pkgs "dublin-traceroute" { };
+      package = lib.mkPackageOption pkgs "dublin-traceroute" { };
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
 
     security.wrappers.dublin-traceroute = {
       owner = "root";
       group = "root";
       capabilities = "cap_net_raw+p";
-      source = getExe cfg.package;
+      source = lib.getExe cfg.package;
     };
   };
 }
diff --git a/nixos/modules/programs/ecryptfs.nix b/nixos/modules/programs/ecryptfs.nix
index ced5eb26fb9a2..8674f7ec80e0a 100644
--- a/nixos/modules/programs/ecryptfs.nix
+++ b/nixos/modules/programs/ecryptfs.nix
@@ -1,16 +1,14 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.ecryptfs;
 
 in {
   options.programs.ecryptfs = {
-    enable = mkEnableOption "ecryptfs setuid mount wrappers";
+    enable = lib.mkEnableOption "ecryptfs setuid mount wrappers";
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     security.wrappers = {
 
       "mount.ecryptfs_private" = {
diff --git a/nixos/modules/programs/environment.nix b/nixos/modules/programs/environment.nix
index 8ac723f42f61a..8a565b7bcac46 100644
--- a/nixos/modules/programs/environment.nix
+++ b/nixos/modules/programs/environment.nix
@@ -4,8 +4,6 @@
 
 { config, lib, ... }:
 
-with lib;
-
 let
 
   cfg = config.environment;
@@ -20,14 +18,14 @@ in
       { NIXPKGS_CONFIG = "/etc/nix/nixpkgs-config.nix";
         # note: many programs exec() this directly, so default options for less must not
         # be specified here; do so in the default value of programs.less.envVariables instead
-        PAGER = mkDefault "less";
-        EDITOR = mkDefault "nano";
+        PAGER = lib.mkDefault "less";
+        EDITOR = lib.mkDefault "nano";
       };
 
     # since we set PAGER to this above, make sure it's installed
     programs.less.enable = true;
 
-    environment.profiles = mkAfter
+    environment.profiles = lib.mkAfter
       [ "/nix/var/nix/profiles/default"
         "/run/current-system/sw"
       ];
@@ -53,7 +51,7 @@ in
     environment.extraInit =
       ''
          export NIX_USER_PROFILE_DIR="/nix/var/nix/profiles/per-user/$USER"
-         export NIX_PROFILES="${concatStringsSep " " (reverseList cfg.profiles)}"
+         export NIX_PROFILES="${builtins.concatStringsSep " " (lib.reverseList cfg.profiles)}"
       '';
 
   };
diff --git a/nixos/modules/programs/extra-container.nix b/nixos/modules/programs/extra-container.nix
index c10ccd7691688..6dcfba7971da2 100644
--- a/nixos/modules/programs/extra-container.nix
+++ b/nixos/modules/programs/extra-container.nix
@@ -1,16 +1,15 @@
 { config, pkgs, lib, ... }:
 
-with lib;
 let
   cfg = config.programs.extra-container;
 in {
   options = {
-    programs.extra-container.enable = mkEnableOption ''
+    programs.extra-container.enable = lib.mkEnableOption ''
       extra-container, a tool for running declarative NixOS containers
       without host system rebuilds
     '';
   };
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ pkgs.extra-container ];
     boot.extraSystemdUnitPaths = [ "/etc/systemd-mutable/system" ];
   };
diff --git a/nixos/modules/programs/feedbackd.nix b/nixos/modules/programs/feedbackd.nix
index 9de604c34a7ea..0c82c7840c8f1 100644
--- a/nixos/modules/programs/feedbackd.nix
+++ b/nixos/modules/programs/feedbackd.nix
@@ -1,21 +1,19 @@
 { pkgs, lib, config, ... }:
 
-with lib;
-
 let
   cfg = config.programs.feedbackd;
 in {
   options = {
     programs.feedbackd = {
-      enable = mkEnableOption ''
+      enable = lib.mkEnableOption ''
         the feedbackd D-BUS service and udev rules.
 
         Your user needs to be in the `feedbackd` group to trigger effects
       '';
-      package = mkPackageOption pkgs "feedbackd" { };
+      package = lib.mkPackageOption pkgs "feedbackd" { };
     };
   };
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
 
     services.dbus.packages = [ cfg.package ];
diff --git a/nixos/modules/programs/firefox.nix b/nixos/modules/programs/firefox.nix
index 39b30be48de9d..7e0dec57d2dac 100644
--- a/nixos/modules/programs/firefox.nix
+++ b/nixos/modules/programs/firefox.nix
@@ -1,7 +1,5 @@
 { pkgs, config, lib, ... }:
 
-with lib;
-
 let
   cfg = config.programs.firefox;
 
@@ -62,13 +60,13 @@ let
 in
 {
   options.programs.firefox = {
-    enable = mkEnableOption "the Firefox web browser";
+    enable = lib.mkEnableOption "the Firefox web browser";
 
-    package = mkOption {
-      type = types.package;
+    package = lib.mkOption {
+      type = lib.types.package;
       default = pkgs.firefox;
       description = "Firefox package to use.";
-      defaultText = literalExpression "pkgs.firefox";
+      defaultText = lib.literalExpression "pkgs.firefox";
       relatedPackages = [
         "firefox"
         "firefox-beta-bin"
@@ -78,13 +76,13 @@ in
       ];
     };
 
-    wrapperConfig = mkOption {
-      type = types.attrs;
+    wrapperConfig = lib.mkOption {
+      type = lib.types.attrs;
       default = {};
       description = "Arguments to pass to Firefox wrapper";
     };
 
-    policies = mkOption {
+    policies = lib.mkOption {
       type = policyFormat.type;
       default = { };
       description = ''
@@ -100,8 +98,8 @@ in
       '';
     };
 
-    preferences = mkOption {
-      type = with types; attrsOf (oneOf [ bool int str ]);
+    preferences = lib.mkOption {
+      type = with lib.types; attrsOf (oneOf [ bool int str ]);
       default = { };
       description = ''
         Preferences to set from `about:config`.
@@ -113,8 +111,8 @@ in
       '';
     };
 
-    preferencesStatus = mkOption {
-      type = types.enum [ "default" "locked" "user" "clear" ];
+    preferencesStatus = lib.mkOption {
+      type = lib.types.enum [ "default" "locked" "user" "clear" ];
       default = "locked";
       description = ''
         The status of `firefox.preferences`.
@@ -127,9 +125,9 @@ in
       '';
     };
 
-    languagePacks = mkOption {
+    languagePacks = lib.mkOption {
       # Available languages can be found in https://releases.mozilla.org/pub/firefox/releases/${cfg.package.version}/linux-x86_64/xpi/
-      type = types.listOf (types.enum ([
+      type = lib.types.listOf (lib.types.enum ([
         "ach"
         "af"
         "an"
@@ -235,8 +233,8 @@ in
       '';
     };
 
-    autoConfig = mkOption {
-      type = types.lines;
+    autoConfig = lib.mkOption {
+      type = lib.types.lines;
       default = "";
       description = ''
         AutoConfig files can be used to set and lock preferences that are not covered
@@ -247,19 +245,19 @@ in
     };
 
     nativeMessagingHosts = ({
-      packages = mkOption {
-        type = types.listOf types.package;
+      packages = lib.mkOption {
+        type = lib.types.listOf lib.types.package;
         default = [];
         description = ''
           Additional packages containing native messaging hosts that should be made available to Firefox extensions.
         '';
       };
-    }) // (mapAttrs (k: v: mkEnableOption "${v.name} support") nmhOptions);
+    }) // (builtins.mapAttrs (k: v: lib.mkEnableOption "${v.name} support") nmhOptions);
   };
 
   config = let
-    forEachEnabledNmh = fn: flatten (mapAttrsToList (k: v: lib.optional cfg.nativeMessagingHosts.${k} (fn k v)) nmhOptions);
-  in mkIf cfg.enable {
+    forEachEnabledNmh = fn: lib.flatten (lib.mapAttrsToList (k: v: lib.optional cfg.nativeMessagingHosts.${k} (fn k v)) nmhOptions);
+  in lib.mkIf cfg.enable {
     warnings = forEachEnabledNmh (k: v:
       "The `programs.firefox.nativeMessagingHosts.${k}` option is deprecated, " +
       "please add `${v.package.pname}` to `programs.firefox.nativeMessagingHosts.packages` instead."
@@ -278,18 +276,18 @@ in
       let
         policiesJSON = policyFormat.generate "firefox-policies.json" { inherit (cfg) policies; };
       in
-      mkIf (cfg.policies != { }) {
+      lib.mkIf (cfg.policies != { }) {
         "firefox/policies/policies.json".source = "${policiesJSON}";
       };
 
     # Preferences are converted into a policy
     programs.firefox.policies = {
       DisableAppUpdate = true;
-      Preferences = (mapAttrs
+      Preferences = (builtins.mapAttrs
         (_: value: { Value = value; Status = cfg.preferencesStatus; })
         cfg.preferences);
-      ExtensionSettings = listToAttrs (map
-        (lang: nameValuePair
+      ExtensionSettings = builtins.listToAttrs (builtins.map
+        (lang: lib.attrsets.nameValuePair
           "langpack-${lang}@firefox.mozilla.org"
           {
             installation_mode = "normal_installed";
@@ -300,5 +298,5 @@ in
     };
   };
 
-  meta.maintainers = with maintainers; [ danth ];
+  meta.maintainers = with lib.maintainers; [ danth ];
 }
diff --git a/nixos/modules/programs/firejail.nix b/nixos/modules/programs/firejail.nix
index 0510cf8c610d4..90da938182748 100644
--- a/nixos/modules/programs/firejail.nix
+++ b/nixos/modules/programs/firejail.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.firejail;
 
@@ -21,13 +19,13 @@ let
         else { executable = value; desktop = null; profile = null; extraArgs = []; };
         args = lib.escapeShellArgs (
           opts.extraArgs
-          ++ (optional (opts.profile != null) "--profile=${toString opts.profile}")
+          ++ (lib.optional (opts.profile != null) "--profile=${builtins.toString opts.profile}")
         );
       in
       ''
         cat <<_EOF >$out/bin/${command}
         #! ${pkgs.runtimeShell} -e
-        exec /run/wrappers/bin/firejail ${args} -- ${toString opts.executable} "\$@"
+        exec /run/wrappers/bin/firejail ${args} -- ${builtins.toString opts.executable} "\$@"
         _EOF
         chmod 0755 $out/bin/${command}
 
@@ -40,30 +38,30 @@ let
 
 in {
   options.programs.firejail = {
-    enable = mkEnableOption "firejail, a sandboxing tool for Linux";
+    enable = lib.mkEnableOption "firejail, a sandboxing tool for Linux";
 
-    wrappedBinaries = mkOption {
-      type = types.attrsOf (types.either types.path (types.submodule {
+    wrappedBinaries = lib.mkOption {
+      type = lib.types.attrsOf (lib.types.either lib.types.path (lib.types.submodule {
         options = {
-          executable = mkOption {
-            type = types.path;
+          executable = lib.mkOption {
+            type = lib.types.path;
             description = "Executable to run sandboxed";
-            example = literalExpression ''"''${lib.getBin pkgs.firefox}/bin/firefox"'';
+            example = lib.literalExpression ''"''${lib.getBin pkgs.firefox}/bin/firefox"'';
           };
-          desktop = mkOption {
-            type = types.nullOr types.path;
+          desktop = lib.mkOption {
+            type = lib.types.nullOr lib.types.path;
             default = null;
             description = ".desktop file to modify. Only necessary if it uses the absolute path to the executable.";
-            example = literalExpression ''"''${pkgs.firefox}/share/applications/firefox.desktop"'';
+            example = lib.literalExpression ''"''${pkgs.firefox}/share/applications/firefox.desktop"'';
           };
-          profile = mkOption {
-            type = types.nullOr types.path;
+          profile = lib.mkOption {
+            type = lib.types.nullOr lib.types.path;
             default = null;
             description = "Profile to use";
-            example = literalExpression ''"''${pkgs.firejail}/etc/firejail/firefox.profile"'';
+            example = lib.literalExpression ''"''${pkgs.firejail}/etc/firejail/firefox.profile"'';
           };
-          extraArgs = mkOption {
-            type = types.listOf types.str;
+          extraArgs = lib.mkOption {
+            type = lib.types.listOf lib.types.str;
             default = [];
             description = "Extra arguments to pass to firejail";
             example = [ "--private=~/.firejail_home" ];
@@ -71,7 +69,7 @@ in {
         };
       }));
       default = {};
-      example = literalExpression ''
+      example = lib.literalExpression ''
         {
           firefox = {
             executable = "''${lib.getBin pkgs.firefox}/bin/firefox";
@@ -89,7 +87,7 @@ in {
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     security.wrappers.firejail =
       { setuid = true;
         owner = "root";
@@ -100,5 +98,5 @@ in {
     environment.systemPackages = [ pkgs.firejail ] ++ [ wrappedBins ];
   };
 
-  meta.maintainers = with maintainers; [ peterhoeg ];
+  meta.maintainers = with lib.maintainers; [ peterhoeg ];
 }
diff --git a/nixos/modules/programs/fish.nix b/nixos/modules/programs/fish.nix
index 2102a07cd0bca..5a6fdb9b5ec5a 100644
--- a/nixos/modules/programs/fish.nix
+++ b/nixos/modules/programs/fish.nix
@@ -1,21 +1,19 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
 
   cfge = config.environment;
 
   cfg = config.programs.fish;
 
-  fishAbbrs = concatStringsSep "\n" (
-    mapAttrsFlatten (k: v: "abbr -ag ${k} ${escapeShellArg v}")
+  fishAbbrs = lib.concatStringsSep "\n" (
+    lib.mapAttrsFlatten (k: v: "abbr -ag ${k} ${lib.escapeShellArg v}")
       cfg.shellAbbrs
   );
 
-  fishAliases = concatStringsSep "\n" (
-    mapAttrsFlatten (k: v: "alias ${k} ${escapeShellArg v}")
-      (filterAttrs (k: v: v != null) cfg.shellAliases)
+  fishAliases = lib.concatStringsSep "\n" (
+    lib.mapAttrsFlatten (k: v: "alias ${k} ${lib.escapeShellArg v}")
+      (lib.filterAttrs (k: v: v != null) cfg.shellAliases)
   );
 
   envShellInit = pkgs.writeText "shellInit" cfge.shellInit;
@@ -47,16 +45,18 @@ in
 
     programs.fish = {
 
-      enable = mkOption {
+      enable = lib.mkOption {
         default = false;
         description = ''
           Whether to configure fish as an interactive shell.
         '';
-        type = types.bool;
+        type = lib.types.bool;
       };
 
-      useBabelfish = mkOption {
-        type = types.bool;
+      package = lib.mkPackageOption pkgs "fish" { };
+
+      useBabelfish = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           If enabled, the configured environment will be translated to native fish using [babelfish](https://github.com/bouk/babelfish).
@@ -64,31 +64,31 @@ in
         '';
       };
 
-      vendor.config.enable = mkOption {
-        type = types.bool;
+      vendor.config.enable = lib.mkOption {
+        type = lib.types.bool;
         default = true;
         description = ''
           Whether fish should source configuration snippets provided by other packages.
         '';
       };
 
-      vendor.completions.enable = mkOption {
-        type = types.bool;
+      vendor.completions.enable = lib.mkOption {
+        type = lib.types.bool;
         default = true;
         description = ''
           Whether fish should use completion files provided by other packages.
         '';
       };
 
-      vendor.functions.enable = mkOption {
-        type = types.bool;
+      vendor.functions.enable = lib.mkOption {
+        type = lib.types.bool;
         default = true;
         description = ''
           Whether fish should autoload fish functions provided by other packages.
         '';
       };
 
-      shellAbbrs = mkOption {
+      shellAbbrs = lib.mkOption {
         default = {};
         example = {
           gco = "git checkout";
@@ -97,63 +97,63 @@ in
         description = ''
           Set of fish abbreviations.
         '';
-        type = with types; attrsOf str;
+        type = with lib.types; attrsOf str;
       };
 
-      shellAliases = mkOption {
+      shellAliases = lib.mkOption {
         default = {};
         description = ''
           Set of aliases for fish shell, which overrides {option}`environment.shellAliases`.
           See {option}`environment.shellAliases` for an option format description.
         '';
-        type = with types; attrsOf (nullOr (either str path));
+        type = with lib.types; attrsOf (nullOr (either str path));
       };
 
-      shellInit = mkOption {
+      shellInit = lib.mkOption {
         default = "";
         description = ''
           Shell script code called during fish shell initialisation.
         '';
-        type = types.lines;
+        type = lib.types.lines;
       };
 
-      loginShellInit = mkOption {
+      loginShellInit = lib.mkOption {
         default = "";
         description = ''
           Shell script code called during fish login shell initialisation.
         '';
-        type = types.lines;
+        type = lib.types.lines;
       };
 
-      interactiveShellInit = mkOption {
+      interactiveShellInit = lib.mkOption {
         default = "";
         description = ''
           Shell script code called during interactive fish shell initialisation.
         '';
-        type = types.lines;
+        type = lib.types.lines;
       };
 
-      promptInit = mkOption {
+      promptInit = lib.mkOption {
         default = "";
         description = ''
           Shell script code used to initialise fish prompt.
         '';
-        type = types.lines;
+        type = lib.types.lines;
       };
 
     };
 
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
 
-    programs.fish.shellAliases = mapAttrs (name: mkDefault) cfge.shellAliases;
+    programs.fish.shellAliases = lib.mapAttrs (name: lib.mkDefault) cfge.shellAliases;
 
     # Required for man completions
     documentation.man.generateCaches = lib.mkDefault true;
 
-    environment = mkMerge [
-      (mkIf cfg.useBabelfish
+    environment = lib.mkMerge [
+      (lib.mkIf cfg.useBabelfish
       {
         etc."fish/setEnvironment.fish".source = babelfishTranslate config.system.build.setEnvironment "setEnvironment";
         etc."fish/shellInit.fish".source = babelfishTranslate envShellInit "shellInit";
@@ -161,7 +161,7 @@ in
         etc."fish/interactiveShellInit.fish".source = babelfishTranslate envInteractiveShellInit "interactiveShellInit";
      })
 
-      (mkIf (!cfg.useBabelfish)
+      (lib.mkIf (!cfg.useBabelfish)
       {
         etc."fish/foreign-env/shellInit".source = envShellInit;
         etc."fish/foreign-env/loginShellInit".source = envLoginShellInit;
@@ -244,8 +244,8 @@ in
           patchedGenerator = pkgs.stdenv.mkDerivation {
             name = "fish_patched-completion-generator";
             srcs = [
-              "${pkgs.fish}/share/fish/tools/create_manpage_completions.py"
-              "${pkgs.fish}/share/fish/tools/deroff.py"
+              "${cfg.package}/share/fish/tools/create_manpage_completions.py"
+              "${cfg.package}/share/fish/tools/deroff.py"
             ];
             unpackCmd = "cp $curSrc $(basename $curSrc)";
             sourceRoot = ".";
@@ -264,7 +264,7 @@ in
                 pathName = substring storeLength (stringLength package - storeLength) package;
               in (package.name or pathName) + "_fish-completions")
             ( { inherit package; } //
-              optionalAttrs (package ? meta.priority) { meta.priority = package.meta.priority; })
+              lib.optionalAttrs (package ? meta.priority) { meta.priority = package.meta.priority; })
             ''
               mkdir -p $out
               if [ -d $package/share/man ]; then
@@ -275,24 +275,24 @@ in
           pkgs.buildEnv {
             name = "system_fish-completions";
             ignoreCollisions = true;
-            paths = map generateCompletions config.environment.systemPackages;
+            paths = builtins.map generateCompletions config.environment.systemPackages;
           };
       }
 
       # include programs that bring their own completions
       {
         pathsToLink = []
-        ++ optional cfg.vendor.config.enable "/share/fish/vendor_conf.d"
-        ++ optional cfg.vendor.completions.enable "/share/fish/vendor_completions.d"
-        ++ optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d";
+        ++ lib.optional cfg.vendor.config.enable "/share/fish/vendor_conf.d"
+        ++ lib.optional cfg.vendor.completions.enable "/share/fish/vendor_completions.d"
+        ++ lib.optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d";
       }
 
-      { systemPackages = [ pkgs.fish ]; }
+      { systemPackages = [ cfg.package ]; }
 
       {
         shells = [
           "/run/current-system/sw/bin/fish"
-          "${pkgs.fish}/bin/fish"
+          (lib.getExe cfg.package)
         ];
       }
     ];
diff --git a/nixos/modules/programs/flashrom.nix b/nixos/modules/programs/flashrom.nix
index 1b9b4493ef201..dd398497c2d0d 100644
--- a/nixos/modules/programs/flashrom.nix
+++ b/nixos/modules/programs/flashrom.nix
@@ -1,14 +1,12 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.flashrom;
 in
 {
   options.programs.flashrom = {
-    enable = mkOption {
-      type = types.bool;
+    enable = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         Installs flashrom and configures udev rules for programmers
@@ -16,10 +14,10 @@ in
         group.
       '';
     };
-    package = mkPackageOption pkgs "flashrom" { };
+    package = lib.mkPackageOption pkgs "flashrom" { };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     services.udev.packages = [ cfg.package ];
     environment.systemPackages = [ cfg.package ];
   };
diff --git a/nixos/modules/programs/flexoptix-app.nix b/nixos/modules/programs/flexoptix-app.nix
index 47a76da125f01..baa9e33882cae 100644
--- a/nixos/modules/programs/flexoptix-app.nix
+++ b/nixos/modules/programs/flexoptix-app.nix
@@ -1,19 +1,17 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let
   cfg = config.programs.flexoptix-app;
 in {
   options = {
     programs.flexoptix-app = {
-      enable = mkEnableOption "FLEXOPTIX app + udev rules";
+      enable = lib.mkEnableOption "FLEXOPTIX app + udev rules";
 
-      package = mkPackageOption pkgs "flexoptix-app" { };
+      package = lib.mkPackageOption pkgs "flexoptix-app" { };
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
     services.udev.packages = [ cfg.package ];
   };
diff --git a/nixos/modules/programs/freetds.nix b/nixos/modules/programs/freetds.nix
index 8b52fc37c5e03..77daaa8fd3985 100644
--- a/nixos/modules/programs/freetds.nix
+++ b/nixos/modules/programs/freetds.nix
@@ -2,8 +2,6 @@
 
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
 
   cfg = config.environment.freetds;
@@ -14,10 +12,10 @@ in
 
   options = {
 
-    environment.freetds = mkOption {
-      type = types.attrsOf types.str;
+    environment.freetds = lib.mkOption {
+      type = lib.types.attrsOf lib.types.str;
       default = {};
-      example = literalExpression ''
+      example = lib.literalExpression ''
         { MYDATABASE = '''
             host = 10.0.2.100
             port = 1433
@@ -40,14 +38,14 @@ in
 
   ###### implementation
 
-  config = mkIf (length (attrNames cfg) > 0) {
+  config = lib.mkIf (builtins.length (builtins.attrNames cfg) > 0) {
 
     environment.variables.FREETDSCONF = "/etc/freetds.conf";
     environment.variables.FREETDS = "/etc/freetds.conf";
     environment.variables.SYBASE = "${pkgs.freetds}";
 
     environment.etc."freetds.conf" = { text =
-      (concatStrings (mapAttrsToList (name: value:
+      (lib.concatStrings (lib.mapAttrsToList (name: value:
         ''
         [${name}]
         ${value}
diff --git a/nixos/modules/programs/fuse.nix b/nixos/modules/programs/fuse.nix
index c15896efbb51a..7083194bd9894 100644
--- a/nixos/modules/programs/fuse.nix
+++ b/nixos/modules/programs/fuse.nix
@@ -1,25 +1,23 @@
 { config, lib, ... }:
 
-with lib;
-
 let
   cfg = config.programs.fuse;
 in {
-  meta.maintainers = with maintainers; [ primeos ];
+  meta.maintainers = with lib.maintainers; [ primeos ];
 
   options.programs.fuse = {
-    mountMax = mkOption {
+    mountMax = lib.mkOption {
       # In the C code it's an "int" (i.e. signed and at least 16 bit), but
       # negative numbers obviously make no sense:
-      type = types.ints.between 0 32767; # 2^15 - 1
+      type = lib.types.ints.between 0 32767; # 2^15 - 1
       default = 1000;
       description = ''
         Set the maximum number of FUSE mounts allowed to non-root users.
       '';
     };
 
-    userAllowOther = mkOption {
-      type = types.bool;
+    userAllowOther = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         Allow non-root users to specify the allow_other or allow_root mount
@@ -30,8 +28,8 @@ in {
 
   config =  {
     environment.etc."fuse.conf".text = ''
-      ${optionalString (!cfg.userAllowOther) "#"}user_allow_other
-      mount_max = ${toString cfg.mountMax}
+      ${lib.optionalString (!cfg.userAllowOther) "#"}user_allow_other
+      mount_max = ${builtins.toString cfg.mountMax}
     '';
   };
 }
diff --git a/nixos/modules/programs/fzf.nix b/nixos/modules/programs/fzf.nix
index 66ad7d418de68..b9258ab1e5052 100644
--- a/nixos/modules/programs/fzf.nix
+++ b/nixos/modules/programs/fzf.nix
@@ -15,7 +15,7 @@ in
     environment.systemPackages = lib.mkIf (cfg.keybindings || cfg.fuzzyCompletion) [ pkgs.fzf ];
 
     programs = {
-      # load after programs.bash.enableCompletion
+      # load after programs.bash.completion.enable
       bash.promptPluginInit = lib.mkAfter (lib.optionalString cfg.fuzzyCompletion ''
         source ${pkgs.fzf}/share/fzf/completion.bash
       '' + lib.optionalString cfg.keybindings ''
diff --git a/nixos/modules/programs/gamemode.nix b/nixos/modules/programs/gamemode.nix
index 878f785074f17..14892f9c6eac8 100644
--- a/nixos/modules/programs/gamemode.nix
+++ b/nixos/modules/programs/gamemode.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.gamemode;
   settingsFormat = pkgs.formats.ini { };
@@ -10,20 +8,20 @@ in
 {
   options = {
     programs.gamemode = {
-      enable = mkEnableOption "GameMode to optimise system performance on demand";
+      enable = lib.mkEnableOption "GameMode to optimise system performance on demand";
 
-      enableRenice = mkEnableOption "CAP_SYS_NICE on gamemoded to support lowering process niceness" // {
+      enableRenice = lib.mkEnableOption "CAP_SYS_NICE on gamemoded to support lowering process niceness" // {
         default = true;
       };
 
-      settings = mkOption {
+      settings = lib.mkOption {
         type = settingsFormat.type;
         default = { };
         description = ''
           System-wide configuration for GameMode (/etc/gamemode.ini).
           See gamemoded(8) man page for available settings.
         '';
-        example = literalExpression ''
+        example = lib.literalExpression ''
           {
             general = {
               renice = 10;
@@ -46,7 +44,7 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment = {
       systemPackages = [ pkgs.gamemode ];
       etc."gamemode.ini".source = configFile;
@@ -54,7 +52,7 @@ in
 
     security = {
       polkit.enable = true;
-      wrappers = mkIf cfg.enableRenice {
+      wrappers = lib.mkIf cfg.enableRenice {
         gamemoded = {
           owner = "root";
           group = "root";
@@ -77,14 +75,14 @@ in
         #
         # This uses a link farm to make sure other wrapped executables
         # aren't included in PATH.
-        environment.PATH = mkForce (pkgs.linkFarm "pkexec" [
+        environment.PATH = lib.mkForce (pkgs.linkFarm "pkexec" [
           {
             name = "pkexec";
             path = "${config.security.wrapperDir}/pkexec";
           }
         ]);
 
-        serviceConfig.ExecStart = mkIf cfg.enableRenice [
+        serviceConfig.ExecStart = lib.mkIf cfg.enableRenice [
           "" # Tell systemd to clear the existing ExecStart list, to prevent appending to it.
           "${config.security.wrapperDir}/gamemoded"
         ];
@@ -95,6 +93,6 @@ in
   };
 
   meta = {
-    maintainers = with maintainers; [ kira-bruneau ];
+    maintainers = with lib.maintainers; [ kira-bruneau ];
   };
 }
diff --git a/nixos/modules/programs/gamescope.nix b/nixos/modules/programs/gamescope.nix
index af9ced4715391..6a0b0a8fbdddd 100644
--- a/nixos/modules/programs/gamescope.nix
+++ b/nixos/modules/programs/gamescope.nix
@@ -3,30 +3,30 @@
 , pkgs
 , ...
 }:
-with lib; let
+let
   cfg = config.programs.gamescope;
 
   gamescope =
     let
       wrapperArgs =
-        optional (cfg.args != [ ])
-          ''--add-flags "${toString cfg.args}"''
-        ++ builtins.attrValues (mapAttrs (var: val: "--set-default ${var} ${val}") cfg.env);
+        lib.optional (cfg.args != [ ])
+          ''--add-flags "${builtins.toString cfg.args}"''
+        ++ builtins.attrValues (builtins.mapAttrs (var: val: "--set-default ${var} ${val}") cfg.env);
     in
     pkgs.runCommand "gamescope" { nativeBuildInputs = [ pkgs.makeBinaryWrapper ]; } ''
       mkdir -p $out/bin
       makeWrapper ${cfg.package}/bin/gamescope $out/bin/gamescope --inherit-argv0 \
-        ${toString wrapperArgs}
+        ${builtins.toString wrapperArgs}
     '';
 in
 {
   options.programs.gamescope = {
-    enable = mkEnableOption "gamescope, the SteamOS session compositing window manager";
+    enable = lib.mkEnableOption "gamescope, the SteamOS session compositing window manager";
 
-    package = mkPackageOption pkgs "gamescope" { };
+    package = lib.mkPackageOption pkgs "gamescope" { };
 
-    capSysNice = mkOption {
-      type = types.bool;
+    capSysNice = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         Add cap_sys_nice capability to the GameScope
@@ -34,8 +34,8 @@ in
       '';
     };
 
-    args = mkOption {
-      type = types.listOf types.str;
+    args = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
       default = [ ];
       example = [ "--rt" "--prefer-vk-device 8086:9bc4" ];
       description = ''
@@ -43,10 +43,10 @@ in
       '';
     };
 
-    env = mkOption {
-      type = types.attrsOf types.str;
+    env = lib.mkOption {
+      type = lib.types.attrsOf lib.types.str;
       default = { };
-      example = literalExpression ''
+      example = lib.literalExpression ''
         # for Prime render offload on Nvidia laptops.
         # Also requires `hardware.nvidia.prime.offload.enable`.
         {
@@ -61,8 +61,8 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
-    security.wrappers = mkIf cfg.capSysNice {
+  config = lib.mkIf cfg.enable {
+    security.wrappers = lib.mkIf cfg.capSysNice {
       gamescope = {
         owner = "root";
         group = "root";
@@ -71,8 +71,8 @@ in
       };
     };
 
-    environment.systemPackages = mkIf (!cfg.capSysNice) [ gamescope ];
+    environment.systemPackages = lib.mkIf (!cfg.capSysNice) [ gamescope ];
   };
 
-  meta.maintainers = with maintainers; [ nrdxp ];
+  meta.maintainers = with lib.maintainers; [ nrdxp ];
 }
diff --git a/nixos/modules/services/x11/gdk-pixbuf.nix b/nixos/modules/programs/gdk-pixbuf.nix
index 9e89d9f96c4af..f96259ccd2c78 100644
--- a/nixos/modules/services/x11/gdk-pixbuf.nix
+++ b/nixos/modules/programs/gdk-pixbuf.nix
@@ -1,16 +1,20 @@
 { config, lib, pkgs, ... }:
 
 let
-  cfg = config.services.xserver.gdk-pixbuf;
+  cfg = config.programs.gdk-pixbuf;
 
   loadersCache = pkgs.gnome._gdkPixbufCacheBuilder_DO_NOT_USE {
-    extraLoaders = lib.unique (cfg.modulePackages);
+    extraLoaders = lib.unique cfg.modulePackages;
   };
 in
 
 {
+  imports = [
+    (lib.mkRenamedOptionModule [ "services" "xserver" "gdk-pixbuf" ] [ "programs" "gdk-pixbuf" ])
+  ];
+
   options = {
-    services.xserver.gdk-pixbuf.modulePackages = lib.mkOption {
+    programs.gdk-pixbuf.modulePackages = lib.mkOption {
       type = lib.types.listOf lib.types.package;
       default = [ ];
       description = "Packages providing GDK-Pixbuf modules, for cache generation.";
@@ -22,7 +26,7 @@ in
   # GDK_PIXBUF_MODULE_FILE to point to it.
   config = lib.mkIf (cfg.modulePackages != []) {
     environment.sessionVariables = {
-      GDK_PIXBUF_MODULE_FILE = "${loadersCache}";
+      GDK_PIXBUF_MODULE_FILE = loadersCache;
     };
   };
 }
diff --git a/nixos/modules/programs/geary.nix b/nixos/modules/programs/geary.nix
index 6103ee7df8591..cfd5bed78d971 100644
--- a/nixos/modules/programs/geary.nix
+++ b/nixos/modules/programs/geary.nix
@@ -1,20 +1,18 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let
   cfg = config.programs.geary;
 
 in {
   meta = {
-    maintainers = teams.gnome.members;
+    maintainers = lib.teams.gnome.members;
   };
 
   options = {
-    programs.geary.enable = mkEnableOption "Geary, a Mail client for GNOME";
+    programs.geary.enable = lib.mkEnableOption "Geary, a Mail client for GNOME";
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ pkgs.gnome.geary ];
     programs.dconf.enable = true;
     services.gnome.gnome-keyring.enable = true;
diff --git a/nixos/modules/programs/git.nix b/nixos/modules/programs/git.nix
index 2a5d52f2d191f..e4f6ce937f04d 100644
--- a/nixos/modules/programs/git.nix
+++ b/nixos/modules/programs/git.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.git;
 in
@@ -9,23 +7,23 @@ in
 {
   options = {
     programs.git = {
-      enable = mkEnableOption "git, a distributed version control system";
+      enable = lib.mkEnableOption "git, a distributed version control system";
 
-      package = mkPackageOption pkgs "git" {
+      package = lib.mkPackageOption pkgs "git" {
         example = "gitFull";
       };
 
-      config = mkOption {
+      config = lib.mkOption {
         type =
-          with types;
+          with lib.types;
           let
             gitini = attrsOf (attrsOf anything);
           in
           either gitini (listOf gitini) // {
             merge = loc: defs:
               let
-                config = foldl'
-                  (acc: { value, ... }@x: acc // (if isList value then {
+                config = builtins.foldl'
+                  (acc: { value, ... }@x: acc // (if builtins.isList value then {
                     ordered = acc.ordered ++ value;
                   } else {
                     unordered = acc.unordered ++ [ x ];
@@ -55,25 +53,25 @@ in
       };
 
       prompt = {
-        enable = mkEnableOption "automatically sourcing git-prompt.sh. This does not change $PS1; it simply provides relevant utility functions";
+        enable = lib.mkEnableOption "automatically sourcing git-prompt.sh. This does not change $PS1; it simply provides relevant utility functions";
       };
 
       lfs = {
-        enable = mkEnableOption "git-lfs (Large File Storage)";
+        enable = lib.mkEnableOption "git-lfs (Large File Storage)";
 
-        package = mkPackageOption pkgs "git-lfs" { };
+        package = lib.mkPackageOption pkgs "git-lfs" { };
       };
     };
   };
 
-  config = mkMerge [
-    (mkIf cfg.enable {
+  config = lib.mkMerge [
+    (lib.mkIf cfg.enable {
       environment.systemPackages = [ cfg.package ];
-      environment.etc.gitconfig = mkIf (cfg.config != [ ]) {
-        text = concatMapStringsSep "\n" generators.toGitINI cfg.config;
+      environment.etc.gitconfig = lib.mkIf (cfg.config != [ ]) {
+        text = lib.concatMapStringsSep "\n" lib.generators.toGitINI cfg.config;
       };
     })
-    (mkIf (cfg.enable && cfg.lfs.enable) {
+    (lib.mkIf (cfg.enable && cfg.lfs.enable) {
       environment.systemPackages = [ cfg.lfs.package ];
       programs.git.config = {
         filter.lfs = {
@@ -84,12 +82,12 @@ in
         };
       };
     })
-    (mkIf (cfg.enable && cfg.prompt.enable) {
+    (lib.mkIf (cfg.enable && cfg.prompt.enable) {
       environment.interactiveShellInit = ''
         source ${cfg.package}/share/bash-completion/completions/git-prompt.sh
       '';
     })
   ];
 
-  meta.maintainers = with maintainers; [ figsoda ];
+  meta.maintainers = with lib.maintainers; [ figsoda ];
 }
diff --git a/nixos/modules/programs/gnupg.nix b/nixos/modules/programs/gnupg.nix
index c755d110170c6..eb983d9ce78a9 100644
--- a/nixos/modules/programs/gnupg.nix
+++ b/nixos/modules/programs/gnupg.nix
@@ -8,22 +8,6 @@ let
   agentSettingsFormat = pkgs.formats.keyValue {
     mkKeyValue = lib.generators.mkKeyValueDefault { } " ";
   };
-
-  xserverCfg = config.services.xserver;
-
-  defaultPinentryFlavor =
-    if xserverCfg.desktopManager.lxqt.enable
-    || xserverCfg.desktopManager.plasma5.enable
-    || xserverCfg.desktopManager.plasma6.enable
-    || xserverCfg.desktopManager.deepin.enable then
-      "qt"
-    else if xserverCfg.desktopManager.xfce.enable then
-      "gtk2"
-    else if xserverCfg.enable || config.programs.sway.enable then
-      "gnome3"
-    else
-      "curses";
-
 in
 {
   imports = [
diff --git a/nixos/modules/programs/gphoto2.nix b/nixos/modules/programs/gphoto2.nix
index d99259b545825..d9f09483f63c1 100644
--- a/nixos/modules/programs/gphoto2.nix
+++ b/nixos/modules/programs/gphoto2.nix
@@ -1,16 +1,14 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 {
-  meta.maintainers = [ maintainers.league ];
+  meta.maintainers = [ lib.maintainers.league ];
 
   ###### interface
   options = {
     programs.gphoto2 = {
-      enable = mkOption {
+      enable = lib.mkOption {
         default = false;
-        type = types.bool;
+        type = lib.types.bool;
         description = ''
           Whether to configure system to use gphoto2.
           To grant digital camera access to a user, the user must
@@ -22,7 +20,7 @@ with lib;
   };
 
   ###### implementation
-  config = mkIf config.programs.gphoto2.enable {
+  config = lib.mkIf config.programs.gphoto2.enable {
     services.udev.packages = [ pkgs.libgphoto2 ];
     environment.systemPackages = [ pkgs.gphoto2 ];
     users.groups.camera = {};
diff --git a/nixos/modules/programs/haguichi.nix b/nixos/modules/programs/haguichi.nix
index 4f48551cf1dac..fd769ac8d0a03 100644
--- a/nixos/modules/programs/haguichi.nix
+++ b/nixos/modules/programs/haguichi.nix
@@ -1,13 +1,11 @@
 { lib, pkgs, config, ... }:
 
-with lib;
-
 {
   options.programs.haguichi = {
-    enable = mkEnableOption "Haguichi, a Linux GUI frontend to the proprietary LogMeIn Hamachi";
+    enable = lib.mkEnableOption "Haguichi, a Linux GUI frontend to the proprietary LogMeIn Hamachi";
   };
 
-  config = mkIf config.programs.haguichi.enable {
+  config = lib.mkIf config.programs.haguichi.enable {
     environment.systemPackages = with pkgs; [ haguichi ];
 
     services.logmein-hamachi.enable = true;
diff --git a/nixos/modules/programs/hamster.nix b/nixos/modules/programs/hamster.nix
index 0bb56ad7ff36a..90cfc0f86a246 100644
--- a/nixos/modules/programs/hamster.nix
+++ b/nixos/modules/programs/hamster.nix
@@ -1,12 +1,10 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 {
   meta.maintainers = pkgs.hamster.meta.maintainers;
 
   options.programs.hamster.enable =
-    mkEnableOption "hamster, a time tracking program";
+    lib.mkEnableOption "hamster, a time tracking program";
 
   config = lib.mkIf config.programs.hamster.enable {
     environment.systemPackages = [ pkgs.hamster ];
diff --git a/nixos/modules/programs/htop.nix b/nixos/modules/programs/htop.nix
index bf3d851081706..1252b41e8b851 100644
--- a/nixos/modules/programs/htop.nix
+++ b/nixos/modules/programs/htop.nix
@@ -1,29 +1,27 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
 
   cfg = config.programs.htop;
 
   fmt = value:
-    if isList value then concatStringsSep " " (map fmt value) else
-    if isString value then value else
-    if isBool value then if value then "1" else "0" else
-    if isInt value then toString value else
-    throw "Unrecognized type ${typeOf value} in htop settings";
+    if builtins.isList value then builtins.concatStringsSep " " (builtins.map fmt value) else
+    if builtins.isString value then value else
+    if builtins.isBool value then if value then "1" else "0" else
+    if builtins.isInt value then builtins.toString value else
+    throw "Unrecognized type ${builtins.typeOf value} in htop settings";
 
 in
 
 {
 
   options.programs.htop = {
-    package = mkPackageOption pkgs "htop" { };
+    package = lib.mkPackageOption pkgs "htop" { };
 
-    enable = mkEnableOption "htop process monitor";
+    enable = lib.mkEnableOption "htop process monitor";
 
-    settings = mkOption {
-      type = with types; attrsOf (oneOf [ str int bool (listOf (oneOf [ str int bool ])) ]);
+    settings = lib.mkOption {
+      type = with lib.types; attrsOf (oneOf [ str int bool (listOf (oneOf [ str int bool ])) ]);
       default = {};
       example = {
         hide_kernel_threads = true;
@@ -38,7 +36,7 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [
       cfg.package
     ];
@@ -46,7 +44,7 @@ in
     environment.etc."htoprc".text = ''
       # Global htop configuration
       # To change set: programs.htop.settings.KEY = VALUE;
-    '' + concatStringsSep "\n" (mapAttrsToList (key: value: "${key}=${fmt value}") cfg.settings);
+    '' + builtins.concatStringsSep "\n" (lib.mapAttrsToList (key: value: "${key}=${fmt value}") cfg.settings);
   };
 
 }
diff --git a/nixos/modules/programs/i3lock.nix b/nixos/modules/programs/i3lock.nix
index 8068ecaf08ca7..ff616144e2834 100644
--- a/nixos/modules/programs/i3lock.nix
+++ b/nixos/modules/programs/i3lock.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
 
   cfg = config.programs.i3lock;
@@ -12,8 +10,8 @@ in {
 
   options = {
     programs.i3lock = {
-      enable = mkEnableOption "i3lock";
-      package = mkPackageOption pkgs "i3lock" {
+      enable = lib.mkEnableOption "i3lock";
+      package = lib.mkPackageOption pkgs "i3lock" {
         example = "i3lock-color";
         extraDescription = ''
           ::: {.note}
@@ -21,8 +19,8 @@ in {
           :::
         '';
       };
-      u2fSupport = mkOption {
-        type        = types.bool;
+      u2fSupport = lib.mkOption {
+        type        = lib.types.bool;
         default     = false;
         example     = true;
         description = ''
@@ -36,11 +34,11 @@ in {
 
   ###### implementation
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
 
     environment.systemPackages = [ cfg.package ];
 
-    security.wrappers.i3lock = mkIf cfg.u2fSupport {
+    security.wrappers.i3lock = lib.mkIf cfg.u2fSupport {
       setuid = true;
       owner = "root";
       group = "root";
diff --git a/nixos/modules/programs/iftop.nix b/nixos/modules/programs/iftop.nix
index c74714a9a6d64..d6e56c8fded69 100644
--- a/nixos/modules/programs/iftop.nix
+++ b/nixos/modules/programs/iftop.nix
@@ -1,14 +1,12 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let
   cfg = config.programs.iftop;
 in {
   options = {
-    programs.iftop.enable = mkEnableOption "iftop + setcap wrapper";
+    programs.iftop.enable = lib.mkEnableOption "iftop + setcap wrapper";
   };
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ pkgs.iftop ];
     security.wrappers.iftop = {
       owner = "root";
diff --git a/nixos/modules/programs/iotop.nix b/nixos/modules/programs/iotop.nix
index b7c1c69f9ddd0..ba8d028f6bb8d 100644
--- a/nixos/modules/programs/iotop.nix
+++ b/nixos/modules/programs/iotop.nix
@@ -1,14 +1,12 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let
   cfg = config.programs.iotop;
 in {
   options = {
-    programs.iotop.enable = mkEnableOption "iotop + setcap wrapper";
+    programs.iotop.enable = lib.mkEnableOption "iotop + setcap wrapper";
   };
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     security.wrappers.iotop = {
       owner = "root";
       group = "root";
diff --git a/nixos/modules/programs/java.nix b/nixos/modules/programs/java.nix
index f201f67b42e46..784add809682e 100644
--- a/nixos/modules/programs/java.nix
+++ b/nixos/modules/programs/java.nix
@@ -3,8 +3,6 @@
 
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.java;
 in
@@ -14,7 +12,7 @@ in
 
     programs.java = {
 
-      enable = mkEnableOption "java" // {
+      enable = lib.mkEnableOption "java" // {
         description = ''
           Install and setup the Java development kit.
 
@@ -30,19 +28,19 @@ in
         '';
       };
 
-      package = mkPackageOption pkgs "jdk" {
+      package = lib.mkPackageOption pkgs "jdk" {
         example = "jre";
       };
 
-      binfmt = mkEnableOption "binfmt to execute java jar's and classes";
+      binfmt = lib.mkEnableOption "binfmt to execute java jar's and classes";
 
     };
 
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
 
-    boot.binfmt.registrations = mkIf cfg.binfmt {
+    boot.binfmt.registrations = lib.mkIf cfg.binfmt {
       java-class = {
         recognitionType = "extension";
         magicOrExtension = "class";
diff --git a/nixos/modules/programs/joycond-cemuhook.nix b/nixos/modules/programs/joycond-cemuhook.nix
index ebb0198ee60c0..6cdd198a7df23 100644
--- a/nixos/modules/programs/joycond-cemuhook.nix
+++ b/nixos/modules/programs/joycond-cemuhook.nix
@@ -1,8 +1,7 @@
 { lib, pkgs, config, ... }:
-with lib;
 {
   options.programs.joycond-cemuhook = {
-    enable = mkEnableOption "joycond-cemuhook, a program to enable support for cemuhook's UDP protocol for joycond devices.";
+    enable = lib.mkEnableOption "joycond-cemuhook, a program to enable support for cemuhook's UDP protocol for joycond devices.";
   };
 
   config = lib.mkIf config.programs.joycond-cemuhook.enable {
diff --git a/nixos/modules/programs/k3b.nix b/nixos/modules/programs/k3b.nix
index 4d6385dab4f07..3e9435d3dc601 100644
--- a/nixos/modules/programs/k3b.nix
+++ b/nixos/modules/programs/k3b.nix
@@ -1,12 +1,10 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 {
   # interface
   options.programs.k3b = {
-    enable = mkOption {
-      type = types.bool;
+    enable = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         Whether to enable k3b, the KDE disk burning application.
@@ -22,7 +20,7 @@ with lib;
   };
 
   # implementation
-  config = mkIf config.programs.k3b.enable {
+  config = lib.mkIf config.programs.k3b.enable {
 
     environment.systemPackages = with pkgs; [
       k3b
diff --git a/nixos/modules/programs/k40-whisperer.nix b/nixos/modules/programs/k40-whisperer.nix
index 156ded6c39fe8..0f29c476cbb79 100644
--- a/nixos/modules/programs/k40-whisperer.nix
+++ b/nixos/modules/programs/k40-whisperer.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.k40-whisperer;
   pkg = cfg.package.override {
@@ -10,20 +8,20 @@ let
 in
 {
   options.programs.k40-whisperer = {
-    enable = mkEnableOption "K40-Whisperer";
+    enable = lib.mkEnableOption "K40-Whisperer";
 
-    group = mkOption {
-      type = types.str;
+    group = lib.mkOption {
+      type = lib.types.str;
       description = ''
         Group assigned to the device when connected.
       '';
       default = "k40";
     };
 
-    package = mkPackageOption pkgs "k40-whisperer" { };
+    package = lib.mkPackageOption pkgs "k40-whisperer" { };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     users.groups.${cfg.group} = {};
 
     environment.systemPackages = [ pkg ];
diff --git a/nixos/modules/programs/kbdlight.nix b/nixos/modules/programs/kbdlight.nix
index 8a2a0057cf2da..934bb214c1166 100644
--- a/nixos/modules/programs/kbdlight.nix
+++ b/nixos/modules/programs/kbdlight.nix
@@ -1,15 +1,13 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.kbdlight;
 
 in
 {
-  options.programs.kbdlight.enable = mkEnableOption "kbdlight";
+  options.programs.kbdlight.enable = lib.mkEnableOption "kbdlight";
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ pkgs.kbdlight ];
     security.wrappers.kbdlight =
       { setuid = true;
diff --git a/nixos/modules/programs/kclock.nix b/nixos/modules/programs/kclock.nix
index c2299a3f1b034..b69f358ec1ff9 100644
--- a/nixos/modules/programs/kclock.nix
+++ b/nixos/modules/programs/kclock.nix
@@ -1,12 +1,11 @@
 { lib, pkgs, config, ... }:
-with lib;
 let
   cfg = config.programs.kclock;
   kclockPkg = pkgs.libsForQt5.kclock;
 in {
-  options.programs.kclock = { enable = mkEnableOption "KClock"; };
+  options.programs.kclock = { enable = lib.mkEnableOption "KClock"; };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     services.dbus.packages = [ kclockPkg ];
     environment.systemPackages = [ kclockPkg ];
   };
diff --git a/nixos/modules/programs/kdeconnect.nix b/nixos/modules/programs/kdeconnect.nix
index 1431281405968..76bba40103084 100644
--- a/nixos/modules/programs/kdeconnect.nix
+++ b/nixos/modules/programs/kdeconnect.nix
@@ -1,8 +1,7 @@
 { config, pkgs, lib, ... }:
-with lib;
 {
   options.programs.kdeconnect = {
-    enable = mkEnableOption ''
+    enable = lib.mkEnableOption ''
       kdeconnect.
 
       Note that it will open the TCP and UDP port from
@@ -11,7 +10,7 @@ with lib;
       `gnomeExtensions.gsconnect` as an alternative
       implementation if you use Gnome
     '';
-    package = mkPackageOption pkgs [ "plasma5Packages" "kdeconnect-kde" ] {
+    package = lib.mkPackageOption pkgs [ "plasma5Packages" "kdeconnect-kde" ] {
       example = "gnomeExtensions.gsconnect";
     };
   };
@@ -19,10 +18,9 @@ with lib;
     let
       cfg = config.programs.kdeconnect;
     in
-      mkIf cfg.enable {
+      lib.mkIf cfg.enable {
         environment.systemPackages = [
           cfg.package
-          pkgs.sshfs
         ];
         networking.firewall = rec {
           allowedTCPPortRanges = [ { from = 1714; to = 1764; } ];
diff --git a/nixos/modules/programs/kubeswitch.nix b/nixos/modules/programs/kubeswitch.nix
index 304df48e3c11a..9348540022f23 100644
--- a/nixos/modules/programs/kubeswitch.nix
+++ b/nixos/modules/programs/kubeswitch.nix
@@ -29,17 +29,13 @@ in
 
   config =
     let
-      shell_files = pkgs.stdenv.mkDerivation rec {
-        name = "kubeswitch-shell-files";
-        phases = [ "installPhase" ];
-        installPhase = ''
-          mkdir -p $out/share
-          for shell in bash zsh; do
-            ${cfg.package}/bin/switcher init $shell | sed 's/switch(/${cfg.commandName}(/' > $out/share/${cfg.commandName}_init.$shell
-            ${cfg.package}/bin/switcher --cmd ${cfg.commandName} completion $shell > $out/share/${cfg.commandName}_completion.$shell
-          done
-        '';
-      };
+      shell_files = pkgs.runCommand "kubeswitch-shell-files" {} ''
+        mkdir -p $out/share
+        for shell in bash zsh; do
+          ${cfg.package}/bin/switcher init $shell | sed 's/switch(/${cfg.commandName}(/' > $out/share/${cfg.commandName}_init.$shell
+          ${cfg.package}/bin/switcher --cmd ${cfg.commandName} completion $shell > $out/share/${cfg.commandName}_completion.$shell
+        done
+      '';
     in
     lib.mkIf cfg.enable {
       environment.systemPackages = [ cfg.package ];
diff --git a/nixos/modules/programs/ladybird.nix b/nixos/modules/programs/ladybird.nix
new file mode 100644
index 0000000000000..43bfe445ef58e
--- /dev/null
+++ b/nixos/modules/programs/ladybird.nix
@@ -0,0 +1,14 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.programs.ladybird;
+in {
+  options = {
+    programs.ladybird.enable = lib.mkEnableOption "the Ladybird web browser";
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.ladybird ];
+    fonts.fontDir.enable = true;
+  };
+}
diff --git a/nixos/modules/programs/less.nix b/nixos/modules/programs/less.nix
index 2cb7620075118..50ea1586f6880 100644
--- a/nixos/modules/programs/less.nix
+++ b/nixos/modules/programs/less.nix
@@ -1,26 +1,24 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
 
   cfg = config.programs.less;
 
   configText = if (cfg.configFile != null) then (builtins.readFile cfg.configFile) else ''
     #command
-    ${concatStringsSep "\n"
-      (mapAttrsToList (command: action: "${command} ${action}") cfg.commands)
+    ${builtins.concatStringsSep "\n"
+      (lib.mapAttrsToList (command: action: "${command} ${action}") cfg.commands)
     }
-    ${optionalString cfg.clearDefaultCommands "#stop"}
+    ${lib.optionalString cfg.clearDefaultCommands "#stop"}
 
     #line-edit
-    ${concatStringsSep "\n"
-      (mapAttrsToList (command: action: "${command} ${action}") cfg.lineEditingKeys)
+    ${builtins.concatStringsSep "\n"
+      (lib.mapAttrsToList (command: action: "${command} ${action}") cfg.lineEditingKeys)
     }
 
     #env
-    ${concatStringsSep "\n"
-      (mapAttrsToList (variable: values: "${variable}=${values}") cfg.envVariables)
+    ${builtins.concatStringsSep "\n"
+      (lib.mapAttrsToList (variable: values: "${variable}=${values}") cfg.envVariables)
     }
   '';
 
@@ -35,12 +33,14 @@ in
 
       # note that environment.nix sets PAGER=less, and
       # therefore also enables this module
-      enable = mkEnableOption "less, a file pager";
+      enable = lib.mkEnableOption "less, a file pager";
+
+      package = lib.mkPackageOption pkgs "less" { };
 
-      configFile = mkOption {
-        type = types.nullOr types.path;
+      configFile = lib.mkOption {
+        type = lib.types.nullOr lib.types.path;
         default = null;
-        example = literalExpression ''"''${pkgs.my-configs}/lesskey"'';
+        example = lib.literalExpression ''"''${pkgs.my-configs}/lesskey"'';
         description = ''
           Path to lesskey configuration file.
 
@@ -50,8 +50,8 @@ in
         '';
       };
 
-      commands = mkOption {
-        type = types.attrsOf types.str;
+      commands = lib.mkOption {
+        type = lib.types.attrsOf lib.types.str;
         default = {};
         example = {
           h = "noaction 5\\e(";
@@ -60,8 +60,8 @@ in
         description = "Defines new command keys.";
       };
 
-      clearDefaultCommands = mkOption {
-        type = types.bool;
+      clearDefaultCommands = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Clear all default commands.
@@ -70,8 +70,8 @@ in
         '';
       };
 
-      lineEditingKeys = mkOption {
-        type = types.attrsOf types.str;
+      lineEditingKeys = lib.mkOption {
+        type = lib.types.attrsOf lib.types.str;
         default = {};
         example = {
           e = "abort";
@@ -79,8 +79,8 @@ in
         description = "Defines new line-editing keys.";
       };
 
-      envVariables = mkOption {
-        type = types.attrsOf types.str;
+      envVariables = lib.mkOption {
+        type = lib.types.attrsOf lib.types.str;
         default = {
           LESS = "-R";
         };
@@ -90,17 +90,17 @@ in
         description = "Defines environment variables.";
       };
 
-      lessopen = mkOption {
-        type = types.nullOr types.str;
+      lessopen = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
         default = "|${pkgs.lesspipe}/bin/lesspipe.sh %s";
-        defaultText = literalExpression ''"|''${pkgs.lesspipe}/bin/lesspipe.sh %s"'';
+        defaultText = lib.literalExpression ''"|''${pkgs.lesspipe}/bin/lesspipe.sh %s"'';
         description = ''
           Before less opens a file, it first gives your input preprocessor a chance to modify the way the contents of the file are displayed.
         '';
       };
 
-      lessclose = mkOption {
-        type = types.nullOr types.str;
+      lessclose = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
         default = null;
         description = ''
           When less closes a file opened in such a way, it will call another program, called the input postprocessor,
@@ -110,26 +110,26 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
 
-    environment.systemPackages = [ pkgs.less ];
+    environment.systemPackages = [ cfg.package ];
 
     environment.variables = {
-      LESSKEYIN_SYSTEM = toString lessKey;
-    } // optionalAttrs (cfg.lessopen != null) {
+      LESSKEYIN_SYSTEM = builtins.toString lessKey;
+    } // lib.optionalAttrs (cfg.lessopen != null) {
       LESSOPEN = cfg.lessopen;
-    } // optionalAttrs (cfg.lessclose != null) {
+    } // lib.optionalAttrs (cfg.lessclose != null) {
       LESSCLOSE = cfg.lessclose;
     };
 
-    warnings = optional (
-      cfg.clearDefaultCommands && (all (x: x != "quit") (attrValues cfg.commands))
+    warnings = lib.optional (
+      cfg.clearDefaultCommands && (builtins.all (x: x != "quit") (builtins.attrValues cfg.commands))
     ) ''
       config.programs.less.clearDefaultCommands clears all default commands of less but there is no alternative binding for exiting.
       Consider adding a binding for 'quit'.
     '';
   };
 
-  meta.maintainers = with maintainers; [ johnazoidberg ];
+  meta.maintainers = with lib.maintainers; [ johnazoidberg ];
 
 }
diff --git a/nixos/modules/programs/liboping.nix b/nixos/modules/programs/liboping.nix
index 4433f9767d6ee..5ff9ad74b1584 100644
--- a/nixos/modules/programs/liboping.nix
+++ b/nixos/modules/programs/liboping.nix
@@ -1,16 +1,14 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.liboping;
 in {
   options.programs.liboping = {
-    enable = mkEnableOption "liboping";
+    enable = lib.mkEnableOption "liboping";
   };
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = with pkgs; [ liboping ];
-    security.wrappers = mkMerge (map (
+    security.wrappers = lib.mkMerge (builtins.map (
       exec: {
         "${exec}" = {
           owner = "root";
diff --git a/nixos/modules/programs/light.nix b/nixos/modules/programs/light.nix
index b1584a1b3d28c..29fcc98a8e0ad 100644
--- a/nixos/modules/programs/light.nix
+++ b/nixos/modules/programs/light.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.light;
 
@@ -10,9 +8,9 @@ in
   options = {
     programs.light = {
 
-      enable = mkOption {
+      enable = lib.mkOption {
         default = false;
-        type = types.bool;
+        type = lib.types.bool;
         description = ''
           Whether to install Light backlight control command
           and udev rules granting access to members of the "video" group.
@@ -20,8 +18,8 @@ in
       };
 
       brightnessKeys = {
-        enable = mkOption {
-          type = types.bool;
+        enable = lib.mkOption {
+          type = lib.types.bool;
           default = false;
           description = ''
             Whether to enable brightness control with keyboard keys.
@@ -38,8 +36,8 @@ in
           '';
         };
 
-        step = mkOption {
-          type = types.int;
+        step = lib.mkOption {
+          type = lib.types.int;
           default = 10;
           description = ''
             The percentage value by which to increase/decrease brightness.
@@ -51,14 +49,14 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ pkgs.light ];
     services.udev.packages = [ pkgs.light ];
-    services.actkbd = mkIf cfg.brightnessKeys.enable {
+    services.actkbd = lib.mkIf cfg.brightnessKeys.enable {
       enable = true;
       bindings = let
         light = "${pkgs.light}/bin/light";
-        step = toString cfg.brightnessKeys.step;
+        step = builtins.toString cfg.brightnessKeys.step;
       in [
         {
           keys = [ 224 ];
diff --git a/nixos/modules/programs/mdevctl.nix b/nixos/modules/programs/mdevctl.nix
index be33835639d2e..a7e7d01dffdfc 100644
--- a/nixos/modules/programs/mdevctl.nix
+++ b/nixos/modules/programs/mdevctl.nix
@@ -1,14 +1,13 @@
 { config, pkgs, lib, ... }:
 
-with lib;
 let
   cfg = config.programs.mdevctl;
 in {
   options.programs.mdevctl = {
-    enable = mkEnableOption "Mediated Device Management";
+    enable = lib.mkEnableOption "Mediated Device Management";
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = with pkgs; [ mdevctl ];
 
     environment.etc."mdevctl.d/scripts.d/notifiers/.keep".text = "";
diff --git a/nixos/modules/programs/mepo.nix b/nixos/modules/programs/mepo.nix
index 22596892ff5dc..783d2ad149626 100644
--- a/nixos/modules/programs/mepo.nix
+++ b/nixos/modules/programs/mepo.nix
@@ -1,15 +1,14 @@
 { pkgs, config, lib, ...}:
-with lib;
 let
   cfg = config.programs.mepo;
 in
 {
   options.programs.mepo = {
-    enable = mkEnableOption "Mepo, a fast, simple and hackable OSM map viewer";
+    enable = lib.mkEnableOption "Mepo, a fast, simple and hackable OSM map viewer";
 
     locationBackends = {
-      gpsd = mkOption {
-        type = types.bool;
+      gpsd = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to enable location detection via gpsd.
@@ -17,21 +16,21 @@ in
         '';
       };
 
-      geoclue = mkOption {
-        type = types.bool;
+      geoclue = lib.mkOption {
+        type = lib.types.bool;
         default = true;
         description = "Whether to enable location detection via geoclue";
       };
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = with pkgs; [
       mepo
     ] ++ lib.optional cfg.locationBackends.geoclue geoclue2-with-demo-agent
     ++ lib.optional cfg.locationBackends.gpsd gpsd;
 
-    services.geoclue2 = mkIf cfg.locationBackends.geoclue {
+    services.geoclue2 = lib.mkIf cfg.locationBackends.geoclue {
       enable = true;
       appConfig.where-am-i = {
         isAllowed = true;
@@ -42,5 +41,5 @@ in
     services.gpsd.enable = cfg.locationBackends.gpsd;
   };
 
-  meta.maintainers = with maintainers; [ laalsaas ];
+  meta.maintainers = with lib.maintainers; [ laalsaas ];
 }
diff --git a/nixos/modules/programs/mininet.nix b/nixos/modules/programs/mininet.nix
index a9190ed989007..ab862b21fe021 100644
--- a/nixos/modules/programs/mininet.nix
+++ b/nixos/modules/programs/mininet.nix
@@ -2,15 +2,13 @@
 # kernel must have NETNS/VETH/SCHED
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.mininet;
 in
 {
-  options.programs.mininet.enable = mkEnableOption "Mininet, an emulator for rapid prototyping of Software Defined Networks";
+  options.programs.mininet.enable = lib.mkEnableOption "Mininet, an emulator for rapid prototyping of Software Defined Networks";
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
 
     virtualisation.vswitch.enable = true;
 
diff --git a/nixos/modules/programs/miriway.nix b/nixos/modules/programs/miriway.nix
index 00c1356ab0836..418bb3dc4f2dd 100644
--- a/nixos/modules/programs/miriway.nix
+++ b/nixos/modules/programs/miriway.nix
@@ -65,7 +65,7 @@ in {
       };
     };
 
-    hardware.opengl.enable = lib.mkDefault true;
+    hardware.graphics.enable = lib.mkDefault true;
     fonts.enableDefaultPackages = lib.mkDefault true;
     programs.dconf.enable = lib.mkDefault true;
     programs.xwayland.enable = lib.mkDefault true;
diff --git a/nixos/modules/programs/msmtp.nix b/nixos/modules/programs/msmtp.nix
index 9c067bdc96957..8a04acb3b7ea6 100644
--- a/nixos/modules/programs/msmtp.nix
+++ b/nixos/modules/programs/msmtp.nix
@@ -1,27 +1,25 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.msmtp;
 
 in {
-  meta.maintainers = with maintainers; [ pacien ];
+  meta.maintainers = with lib.maintainers; [ pacien ];
 
   options = {
     programs.msmtp = {
-      enable = mkEnableOption "msmtp - an SMTP client";
+      enable = lib.mkEnableOption "msmtp - an SMTP client";
 
-      setSendmail = mkOption {
-        type = types.bool;
+      setSendmail = lib.mkOption {
+        type = lib.types.bool;
         default = true;
         description = ''
           Whether to set the system sendmail to msmtp's.
         '';
       };
 
-      defaults = mkOption {
-        type = types.attrs;
+      defaults = lib.mkOption {
+        type = lib.types.attrs;
         default = {};
         example = {
           aliases = "/etc/aliases";
@@ -34,8 +32,8 @@ in {
         '';
       };
 
-      accounts = mkOption {
-        type = with types; attrsOf attrs;
+      accounts = lib.mkOption {
+        type = with lib.types; attrsOf attrs;
         default = {};
         example = {
           "default" = {
@@ -59,8 +57,8 @@ in {
         '';
       };
 
-      extraConfig = mkOption {
-        type = types.lines;
+      extraConfig = lib.mkOption {
+        type = lib.types.lines;
         default = "";
         description = ''
           Extra lines to add to the msmtp configuration verbatim.
@@ -70,10 +68,10 @@ in {
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ pkgs.msmtp ];
 
-    services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail {
+    services.mail.sendmailSetuidWrapper = lib.mkIf cfg.setSendmail {
       program = "sendmail";
       source = "${pkgs.msmtp}/bin/sendmail";
       setuid = false;
@@ -86,10 +84,10 @@ in {
       mkValueString = v:
         if v == true then "on"
         else if v == false then "off"
-        else generators.mkValueStringDefault {} v;
+        else lib.generators.mkValueStringDefault {} v;
       mkKeyValueString = k: v: "${k} ${mkValueString v}";
       mkInnerSectionString =
-        attrs: concatStringsSep "\n" (mapAttrsToList mkKeyValueString attrs);
+        attrs: builtins.concatStringsSep "\n" (lib.mapAttrsToList mkKeyValueString attrs);
       mkAccountString = name: attrs: ''
         account ${name}
         ${mkInnerSectionString attrs}
@@ -98,7 +96,7 @@ in {
       defaults
       ${mkInnerSectionString cfg.defaults}
 
-      ${concatStringsSep "\n" (mapAttrsToList mkAccountString cfg.accounts)}
+      ${builtins.concatStringsSep "\n" (lib.mapAttrsToList mkAccountString cfg.accounts)}
 
       ${cfg.extraConfig}
     '';
diff --git a/nixos/modules/programs/mtr.nix b/nixos/modules/programs/mtr.nix
index 6a767df15f098..1a9deba989663 100644
--- a/nixos/modules/programs/mtr.nix
+++ b/nixos/modules/programs/mtr.nix
@@ -1,15 +1,13 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.mtr;
 
 in {
   options = {
     programs.mtr = {
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to add mtr to the global environment and configure a
@@ -17,12 +15,12 @@ in {
         '';
       };
 
-      package = mkPackageOption pkgs "mtr" { };
+      package = lib.mkPackageOption pkgs "mtr" { };
     };
   };
 
-  config = mkIf cfg.enable {
-    environment.systemPackages = with pkgs; [ cfg.package ];
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
 
     security.wrappers.mtr-packet = {
       owner = "root";
diff --git a/nixos/modules/programs/nbd.nix b/nixos/modules/programs/nbd.nix
index fea9bc1ff71a1..1e319f0273455 100644
--- a/nixos/modules/programs/nbd.nix
+++ b/nixos/modules/programs/nbd.nix
@@ -1,18 +1,16 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.nbd;
 in
 {
   options = {
     programs.nbd = {
-      enable = mkEnableOption "Network Block Device (nbd) support";
+      enable = lib.mkEnableOption "Network Block Device (nbd) support";
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = with pkgs; [ nbd ];
     boot.kernelModules = [ "nbd" ];
   };
diff --git a/nixos/modules/programs/neovim.nix b/nixos/modules/programs/neovim.nix
index 6f6829444a641..8fe6a664b675a 100644
--- a/nixos/modules/programs/neovim.nix
+++ b/nixos/modules/programs/neovim.nix
@@ -1,14 +1,12 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.neovim;
 in
 {
   options.programs.neovim = {
-    enable = mkOption {
-      type = types.bool;
+    enable = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       example = true;
       description = ''
@@ -21,8 +19,8 @@ in
       '';
     };
 
-    defaultEditor = mkOption {
-      type = types.bool;
+    defaultEditor = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         When enabled, installs neovim and configures neovim to be the default editor
@@ -30,44 +28,44 @@ in
       '';
     };
 
-    viAlias = mkOption {
-      type = types.bool;
+    viAlias = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         Symlink {command}`vi` to {command}`nvim` binary.
       '';
     };
 
-    vimAlias = mkOption {
-      type = types.bool;
+    vimAlias = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         Symlink {command}`vim` to {command}`nvim` binary.
       '';
     };
 
-    withRuby = mkOption {
-      type = types.bool;
+    withRuby = lib.mkOption {
+      type = lib.types.bool;
       default = true;
       description = "Enable Ruby provider.";
     };
 
-    withPython3 = mkOption {
-      type = types.bool;
+    withPython3 = lib.mkOption {
+      type = lib.types.bool;
       default = true;
       description = "Enable Python 3 provider.";
     };
 
-    withNodeJs = mkOption {
-      type = types.bool;
+    withNodeJs = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = "Enable Node provider.";
     };
 
-    configure = mkOption {
-      type = types.attrs;
+    configure = lib.mkOption {
+      type = lib.types.attrs;
       default = { };
-      example = literalExpression ''
+      example = lib.literalExpression ''
         {
           customRC = '''
             " here your custom configuration goes!
@@ -86,31 +84,31 @@ in
       '';
     };
 
-    package = mkPackageOption pkgs "neovim-unwrapped" { };
+    package = lib.mkPackageOption pkgs "neovim-unwrapped" { };
 
-    finalPackage = mkOption {
-      type = types.package;
+    finalPackage = lib.mkOption {
+      type = lib.types.package;
       visible = false;
       readOnly = true;
       description = "Resulting customized neovim package.";
     };
 
-    runtime = mkOption {
+    runtime = lib.mkOption {
       default = { };
-      example = literalExpression ''
+      example = lib.literalExpression ''
         { "ftplugin/c.vim".text = "setlocal omnifunc=v:lua.vim.lsp.omnifunc"; }
       '';
       description = ''
         Set of files that have to be linked in {file}`runtime`.
       '';
 
-      type = with types; attrsOf (submodule (
+      type = with lib.types; attrsOf (submodule (
         { name, config, ... }:
         {
           options = {
 
-            enable = mkOption {
-              type = types.bool;
+            enable = lib.mkOption {
+              type = lib.types.bool;
               default = true;
               description = ''
                 Whether this runtime directory should be generated.  This
@@ -118,49 +116,49 @@ in
               '';
             };
 
-            target = mkOption {
-              type = types.str;
+            target = lib.mkOption {
+              type = lib.types.str;
               description = ''
                 Name of symlink.  Defaults to the attribute
                 name.
               '';
             };
 
-            text = mkOption {
+            text = lib.mkOption {
               default = null;
-              type = types.nullOr types.lines;
+              type = lib.types.nullOr lib.types.lines;
               description = "Text of the file.";
             };
 
-            source = mkOption {
+            source = lib.mkOption {
               default = null;
-              type = types.nullOr types.path;
+              type = lib.types.nullOr lib.types.path;
               description = "Path of the source file.";
             };
 
           };
 
-          config.target = mkDefault name;
+          config.target = lib.mkDefault name;
         }
       ));
 
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [
       cfg.finalPackage
     ];
-    environment.variables.EDITOR = mkIf cfg.defaultEditor (mkOverride 900 "nvim");
+    environment.variables.EDITOR = lib.mkIf cfg.defaultEditor (lib.mkOverride 900 "nvim");
 
-    environment.etc = listToAttrs (attrValues (mapAttrs
+    environment.etc = builtins.listToAttrs (builtins.attrValues (builtins.mapAttrs
       (name: value: {
         name = "xdg/nvim/${name}";
-        value = removeAttrs
+        value = builtins.removeAttrs
           (value // {
             target = "xdg/nvim/${value.target}";
           })
-          (optionals (isNull value.source) [ "source" ]);
+          (lib.optionals (builtins.isNull value.source) [ "source" ]);
       })
       cfg.runtime));
 
diff --git a/nixos/modules/programs/nethoscope.nix b/nixos/modules/programs/nethoscope.nix
index 495548e9c6561..7bc1f61b31eae 100644
--- a/nixos/modules/programs/nethoscope.nix
+++ b/nixos/modules/programs/nethoscope.nix
@@ -1,16 +1,14 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let cfg = config.programs.nethoscope;
 in
 {
-  meta.maintainers = with maintainers; [ _0x4A6F ];
+  meta.maintainers = with lib.maintainers; [ _0x4A6F ];
 
   options = {
     programs.nethoscope = {
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to add nethoscope to the global environment and configure a
@@ -20,7 +18,7 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = with pkgs; [ nethoscope ];
     security.wrappers.nethoscope = {
       source = "${pkgs.nethoscope}/bin/nethoscope";
diff --git a/nixos/modules/programs/nncp.nix b/nixos/modules/programs/nncp.nix
index aa2e7c7a6e5b5..3feccef4cf11e 100644
--- a/nixos/modules/programs/nncp.nix
+++ b/nixos/modules/programs/nncp.nix
@@ -1,6 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
 let
   nncpCfgFile = "/run/nncp.hjson";
   programCfg = config.programs.nncp;
@@ -11,10 +10,10 @@ in {
   options.programs.nncp = {
 
     enable =
-      mkEnableOption "NNCP (Node to Node copy) utilities and configuration";
+      lib.mkEnableOption "NNCP (Node to Node copy) utilities and configuration";
 
-    group = mkOption {
-      type = types.str;
+    group = lib.mkOption {
+      type = lib.types.str;
       default = "uucp";
       description = ''
         The group under which NNCP files shall be owned.
@@ -23,10 +22,10 @@ in {
       '';
     };
 
-    package = mkPackageOption pkgs "nncp" { };
+    package = lib.mkPackageOption pkgs "nncp" { };
 
-    secrets = mkOption {
-      type = with types; listOf str;
+    secrets = lib.mkOption {
+      type = with lib.types; listOf str;
       example = [ "/run/keys/nncp.hjson" ];
       description = ''
         A list of paths to NNCP configuration files that should not be
@@ -35,7 +34,7 @@ in {
       '';
     };
 
-    settings = mkOption {
+    settings = lib.mkOption {
       type = settingsFormat.type;
       description = ''
         NNCP configuration, see
@@ -52,7 +51,7 @@ in {
 
   };
 
-  config = mkIf programCfg.enable {
+  config = lib.mkIf programCfg.enable {
 
     environment = {
       systemPackages = [ pkg ];
@@ -60,8 +59,8 @@ in {
     };
 
     programs.nncp.settings = {
-      spool = mkDefault "/var/spool/nncp";
-      log = mkDefault "/var/spool/nncp/log";
+      spool = lib.mkDefault "/var/spool/nncp";
+      log = lib.mkDefault "/var/spool/nncp/log";
     };
 
     systemd.tmpfiles.rules = [
@@ -77,7 +76,7 @@ in {
       script = ''
         umask u=rw
         nncpCfgDir=$(mktemp --directory nncp.XXX)
-        for f in ${jsonCfgFile} ${toString config.programs.nncp.secrets}; do
+        for f in ${jsonCfgFile} ${builtins.toString config.programs.nncp.secrets}; do
           tmpdir=$(mktemp --directory nncp.XXX)
           nncp-cfgdir -cfg $f -dump $tmpdir
           find $tmpdir -size 1c -delete
diff --git a/nixos/modules/programs/noisetorch.nix b/nixos/modules/programs/noisetorch.nix
index 70a0441bd7677..5e37061d9a1d4 100644
--- a/nixos/modules/programs/noisetorch.nix
+++ b/nixos/modules/programs/noisetorch.nix
@@ -1,17 +1,15 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let cfg = config.programs.noisetorch;
 in
 {
   options.programs.noisetorch = {
-    enable = mkEnableOption "noisetorch (+ setcap wrapper), a virtual microphone device with noise suppression";
+    enable = lib.mkEnableOption "noisetorch (+ setcap wrapper), a virtual microphone device with noise suppression";
 
-    package = mkPackageOption pkgs "noisetorch" { };
+    package = lib.mkPackageOption pkgs "noisetorch" { };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     security.wrappers.noisetorch = {
       owner = "root";
       group = "root";
diff --git a/nixos/modules/programs/npm.nix b/nixos/modules/programs/npm.nix
index b379f0165bfed..470188b879b6a 100644
--- a/nixos/modules/programs/npm.nix
+++ b/nixos/modules/programs/npm.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.npm;
 in
@@ -11,13 +9,13 @@ in
 
   options = {
     programs.npm = {
-      enable = mkEnableOption "{command}`npm` global config";
+      enable = lib.mkEnableOption "{command}`npm` global config";
 
-      package = mkPackageOption pkgs [ "nodePackages" "npm" ] {
+      package = lib.mkPackageOption pkgs [ "nodePackages" "npm" ] {
         example = "nodePackages_13_x.npm";
       };
 
-      npmrc = mkOption {
+      npmrc = lib.mkOption {
         type = lib.types.lines;
         description = ''
           The system-wide npm configuration.
diff --git a/nixos/modules/programs/oblogout.nix b/nixos/modules/programs/oblogout.nix
index a039b0623b52c..f09fbdc062423 100644
--- a/nixos/modules/programs/oblogout.nix
+++ b/nixos/modules/programs/oblogout.nix
@@ -1,11 +1,9 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 {
 
   imports = [
-    (mkRemovedOptionModule [ "programs" "oblogout" ] "programs.oblogout has been removed from NixOS. This is because the oblogout repository has been archived upstream.")
+    (lib.mkRemovedOptionModule [ "programs" "oblogout" ] "programs.oblogout has been removed from NixOS. This is because the oblogout repository has been archived upstream.")
   ];
 
 }
diff --git a/nixos/modules/programs/openvpn3.nix b/nixos/modules/programs/openvpn3.nix
index 6415cccecb4ff..10042b44471ff 100644
--- a/nixos/modules/programs/openvpn3.nix
+++ b/nixos/modules/programs/openvpn3.nix
@@ -1,19 +1,17 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.openvpn3;
 in
 {
   options.programs.openvpn3 = {
-    enable = mkEnableOption "the openvpn3 client";
-    package = mkOption {
-      type = types.package;
+    enable = lib.mkEnableOption "the openvpn3 client";
+    package = lib.mkOption {
+      type = lib.types.package;
       default = pkgs.openvpn3.override {
         enableSystemdResolved = config.services.resolved.enable;
       };
-      defaultText = literalExpression ''pkgs.openvpn3.override {
+      defaultText = lib.literalExpression ''pkgs.openvpn3.override {
         enableSystemdResolved = config.services.resolved.enable;
       }'';
       description = ''
@@ -22,7 +20,7 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     services.dbus.packages = [
       cfg.package
     ];
diff --git a/nixos/modules/programs/pantheon-tweaks.nix b/nixos/modules/programs/pantheon-tweaks.nix
deleted file mode 100644
index 0b8a19ea22c01..0000000000000
--- a/nixos/modules/programs/pantheon-tweaks.nix
+++ /dev/null
@@ -1,19 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-{
-  meta = {
-    maintainers = teams.pantheon.members;
-  };
-
-  ###### interface
-  options = {
-    programs.pantheon-tweaks.enable = mkEnableOption "Pantheon Tweaks, an unofficial system settings panel for Pantheon";
-  };
-
-  ###### implementation
-  config = mkIf config.programs.pantheon-tweaks.enable {
-    services.xserver.desktopManager.pantheon.extraSwitchboardPlugs = [ pkgs.pantheon-tweaks ];
-  };
-}
diff --git a/nixos/modules/programs/plotinus.nix b/nixos/modules/programs/plotinus.nix
index 41c75b69a2d2f..835db049d8628 100644
--- a/nixos/modules/programs/plotinus.nix
+++ b/nixos/modules/programs/plotinus.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.plotinus;
 in
@@ -15,21 +13,21 @@ in
 
   options = {
     programs.plotinus = {
-      enable = mkOption {
+      enable = lib.mkOption {
         default = false;
         description = ''
           Whether to enable the Plotinus GTK 3 plugin. Plotinus provides a
           popup (triggered by Ctrl-Shift-P) to search the menus of a
           compatible application.
         '';
-        type = types.bool;
+        type = lib.types.bool;
       };
     };
   };
 
   ###### implementation
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.sessionVariables.XDG_DATA_DIRS = [ "${pkgs.plotinus}/share/gsettings-schemas/${pkgs.plotinus.name}" ];
     environment.variables.GTK3_MODULES = [ "${pkgs.plotinus}/lib/libplotinus.so" ];
   };
diff --git a/nixos/modules/programs/pqos-wrapper.nix b/nixos/modules/programs/pqos-wrapper.nix
new file mode 100644
index 0000000000000..82023e67a2ae2
--- /dev/null
+++ b/nixos/modules/programs/pqos-wrapper.nix
@@ -0,0 +1,27 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+let
+  cfg = config.programs.pqos-wrapper;
+in
+{
+  options.programs.pqos-wrapper = {
+    enable = lib.mkEnableOption "PQoS Wrapper for BenchExec";
+    package = lib.mkPackageOption pkgs "pqos-wrapper" { };
+  };
+
+  config = lib.mkIf cfg.enable {
+    hardware.cpu.x86.msr.enable = true;
+
+    security.wrappers.${cfg.package.meta.mainProgram} = {
+      owner = "nobody";
+      group = config.hardware.cpu.x86.msr.group;
+      source = lib.getExe cfg.package;
+      capabilities = "cap_sys_rawio=eip";
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ lorenzleutgeb ];
+}
diff --git a/nixos/modules/programs/proxychains.nix b/nixos/modules/programs/proxychains.nix
index b15475dac075b..86bbf16a64ce1 100644
--- a/nixos/modules/programs/proxychains.nix
+++ b/nixos/modules/programs/proxychains.nix
@@ -1,15 +1,14 @@
 { config, lib, pkgs, ... }:
-with lib;
 let
 
   cfg = config.programs.proxychains;
 
   configFile = ''
     ${cfg.chain.type}_chain
-    ${optionalString (cfg.chain.type == "random")
+    ${lib.optionalString (cfg.chain.type == "random")
     "chain_len = ${builtins.toString cfg.chain.length}"}
-    ${optionalString cfg.proxyDNS "proxy_dns"}
-    ${optionalString cfg.quietMode "quiet_mode"}
+    ${lib.optionalString cfg.proxyDNS "proxy_dns"}
+    ${lib.optionalString cfg.quietMode "quiet_mode"}
     remote_dns_subnet ${builtins.toString cfg.remoteDNSSubnet}
     tcp_read_time_out ${builtins.toString cfg.tcpReadTimeOut}
     tcp_connect_time_out ${builtins.toString cfg.tcpConnectTimeOut}
@@ -22,20 +21,20 @@ let
 
   proxyOptions = {
     options = {
-      enable = mkEnableOption "this proxy";
+      enable = lib.mkEnableOption "this proxy";
 
-      type = mkOption {
-        type = types.enum [ "http" "socks4" "socks5" ];
+      type = lib.mkOption {
+        type = lib.types.enum [ "http" "socks4" "socks5" ];
         description = "Proxy type.";
       };
 
-      host = mkOption {
-        type = types.str;
+      host = lib.mkOption {
+        type = lib.types.str;
         description = "Proxy host or IP address.";
       };
 
-      port = mkOption {
-        type = types.port;
+      port = lib.mkOption {
+        type = lib.types.port;
         description = "Proxy port";
       };
     };
@@ -49,15 +48,15 @@ in {
 
     programs.proxychains = {
 
-      enable = mkEnableOption "proxychains configuration";
+      enable = lib.mkEnableOption "proxychains configuration";
 
-      package = mkPackageOption pkgs "proxychains" {
+      package = lib.mkPackageOption pkgs "proxychains" {
         example = "proxychains-ng";
       };
 
       chain = {
-        type = mkOption {
-          type = types.enum [ "dynamic" "strict" "random" ];
+        type = lib.mkOption {
+          type = lib.types.enum [ "dynamic" "strict" "random" ];
           default = "strict";
           description = ''
             `dynamic` - Each connection will be done via chained proxies
@@ -75,8 +74,8 @@ in {
             (or proxy chain, see {option}`programs.proxychains.chain.length`) from the list.
           '';
         };
-        length = mkOption {
-          type = types.nullOr types.int;
+        length = lib.mkOption {
+          type = lib.types.nullOr lib.types.int;
           default = null;
           description = ''
             Chain length for random chain.
@@ -84,47 +83,47 @@ in {
         };
       };
 
-      proxyDNS = mkOption {
-        type = types.bool;
+      proxyDNS = lib.mkOption {
+        type = lib.types.bool;
         default = true;
         description = "Proxy DNS requests - no leak for DNS data.";
       };
 
-      quietMode = mkEnableOption "Quiet mode (no output from the library)";
+      quietMode = lib.mkEnableOption "Quiet mode (no output from the library)";
 
-      remoteDNSSubnet = mkOption {
-        type = types.enum [ 10 127 224 ];
+      remoteDNSSubnet = lib.mkOption {
+        type = lib.types.enum [ 10 127 224 ];
         default = 224;
         description = ''
           Set the class A subnet number to use for the internal remote DNS mapping, uses the reserved 224.x.x.x range by default.
         '';
       };
 
-      tcpReadTimeOut = mkOption {
-        type = types.int;
+      tcpReadTimeOut = lib.mkOption {
+        type = lib.types.int;
         default = 15000;
         description = "Connection read time-out in milliseconds.";
       };
 
-      tcpConnectTimeOut = mkOption {
-        type = types.int;
+      tcpConnectTimeOut = lib.mkOption {
+        type = lib.types.int;
         default = 8000;
         description = "Connection time-out in milliseconds.";
       };
 
-      localnet = mkOption {
-        type = types.str;
+      localnet = lib.mkOption {
+        type = lib.types.str;
         default = "127.0.0.0/255.0.0.0";
         description = "By default enable localnet for loopback address ranges.";
       };
 
-      proxies = mkOption {
-        type = types.attrsOf (types.submodule proxyOptions);
+      proxies = lib.mkOption {
+        type = lib.types.attrsOf (lib.types.submodule proxyOptions);
         description = ''
           Proxies to be used by proxychains.
         '';
 
-        example = literalExpression ''
+        example = lib.literalExpression ''
           { myproxy =
             { type = "socks4";
               host = "127.0.0.1";
@@ -140,11 +139,11 @@ in {
 
   ###### implementation
 
-  meta.maintainers = with maintainers; [ sorki ];
+  meta.maintainers = with lib.maintainers; [ sorki ];
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
 
-    assertions = singleton {
+    assertions = lib.singleton {
       assertion = cfg.chain.type != "random" && cfg.chain.length == null;
       message = ''
         Option `programs.proxychains.chain.length`
@@ -152,9 +151,9 @@ in {
       '';
     };
 
-    programs.proxychains.proxies = mkIf config.services.tor.client.enable
+    programs.proxychains.proxies = lib.mkIf config.services.tor.client.enable
       {
-        torproxy = mkDefault {
+        torproxy = lib.mkDefault {
           enable = true;
           type = "socks4";
           host = "127.0.0.1";
diff --git a/nixos/modules/programs/qt5ct.nix b/nixos/modules/programs/qt5ct.nix
index 3ff47b355915b..bc7b28b9c6e92 100644
--- a/nixos/modules/programs/qt5ct.nix
+++ b/nixos/modules/programs/qt5ct.nix
@@ -1,9 +1,7 @@
 { lib, ... }:
 
-with lib;
-
 {
   imports = [
-    (mkRemovedOptionModule [ "programs" "qt5ct" "enable" ] "Use qt5.platformTheme = \"qt5ct\" instead.")
+    (lib.mkRemovedOptionModule [ "programs" "qt5ct" "enable" ] "Use qt5.platformTheme = \"qt5ct\" instead.")
   ];
 }
diff --git a/nixos/modules/programs/rust-motd.nix b/nixos/modules/programs/rust-motd.nix
index 93240fcdd85ee..301b7cebb7f8a 100644
--- a/nixos/modules/programs/rust-motd.nix
+++ b/nixos/modules/programs/rust-motd.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.rust-motd;
   format = pkgs.formats.toml { };
@@ -24,10 +22,10 @@ let
     '';
 in {
   options.programs.rust-motd = {
-    enable = mkEnableOption "rust-motd, a Message Of The Day (MOTD) generator";
-    enableMotdInSSHD = mkOption {
+    enable = lib.mkEnableOption "rust-motd, a Message Of The Day (MOTD) generator";
+    enableMotdInSSHD = lib.mkOption {
       default = true;
-      type = types.bool;
+      type = lib.types.bool;
       description = ''
         Whether to let `openssh` print the
         result when entering a new `ssh`-session.
@@ -36,18 +34,18 @@ in {
         the latter option is incompatible with this module.
       '';
     };
-    refreshInterval = mkOption {
+    refreshInterval = lib.mkOption {
       default = "*:0/5";
-      type = types.str;
+      type = lib.types.str;
       description = ''
         Interval in which the {manpage}`motd(5)` file is refreshed.
         For possible formats, please refer to {manpage}`systemd.time(7)`.
       '';
     };
-    order = mkOption {
-      type = types.listOf types.str;
-      default = attrNames cfg.settings;
-      defaultText = literalExpression "attrNames cfg.settings";
+    order = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      default = builtins.attrNames cfg.settings;
+      defaultText = lib.literalExpression "attrNames cfg.settings";
       description = ''
         The order of the sections in [](#opt-programs.rust-motd.settings).
         By default they are ordered alphabetically.
@@ -79,8 +77,8 @@ in {
         makes sure that `uptime` is placed before `banner` in the motd.
       '';
     };
-    settings = mkOption {
-      type = types.attrsOf format.type;
+    settings = lib.mkOption {
+      type = lib.types.attrsOf format.type;
       description = ''
         Settings on what to generate. Please read the
         [upstream documentation](https://github.com/rust-motd/rust-motd/blob/main/README.md#configuration)
@@ -88,14 +86,14 @@ in {
       '';
     };
   };
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     assertions = [
       { assertion = config.users.motd == null;
         message = ''
           `programs.rust-motd` is incompatible with `users.motd`!
         '';
       }
-      { assertion = sort (a: b: a < b) cfg.order == attrNames cfg.settings;
+      { assertion = builtins.sort (a: b: a < b) cfg.order == builtins.attrNames cfg.settings;
         message = ''
           Please ensure that every section from `programs.rust-motd.settings` is present in
           `programs.rust-motd.order`.
@@ -138,12 +136,12 @@ in {
       wantedBy = [ "timers.target" ];
       timerConfig.OnCalendar = cfg.refreshInterval;
     };
-    security.pam.services.sshd.text = mkIf cfg.enableMotdInSSHD (mkDefault (mkAfter ''
+    security.pam.services.sshd.text = lib.mkIf cfg.enableMotdInSSHD (lib.mkDefault (lib.mkAfter ''
       session optional ${pkgs.pam}/lib/security/pam_motd.so motd=/var/lib/rust-motd/motd
     ''));
-    services.openssh.extraConfig = mkIf (cfg.settings ? last_login && cfg.settings.last_login != {}) ''
+    services.openssh.extraConfig = lib.mkIf (cfg.settings ? last_login && cfg.settings.last_login != {}) ''
       PrintLastLog no
     '';
   };
-  meta.maintainers = with maintainers; [ ma27 ];
+  meta.maintainers = with lib.maintainers; [ ma27 ];
 }
diff --git a/nixos/modules/programs/screen.nix b/nixos/modules/programs/screen.nix
index 01af5b4c9597a..4f3cd9fcf9a56 100644
--- a/nixos/modules/programs/screen.nix
+++ b/nixos/modules/programs/screen.nix
@@ -12,7 +12,8 @@ in
       package = lib.mkPackageOptionMD pkgs "screen" { };
 
       screenrc = lib.mkOption {
-        type = with lib.types; nullOr lines;
+        type = lib.types.lines;
+        default = "";
         example = ''
           defscrollback 10000
           startup_message off
@@ -22,20 +23,22 @@ in
     };
   };
 
-  config = {
-    # TODO: Added in 24.05, remove before 24.11
-    assertions = [
-      {
-        assertion = cfg.screenrc != null -> cfg.enable;
-        message = "`programs.screen.screenrc` has been configured, but `programs.screen.enable` is not true";
-      }
-    ];
-  } // lib.mkIf cfg.enable {
-    environment.etc.screenrc = {
-      enable = cfg.screenrc != null;
-      text = cfg.screenrc;
-    };
-    environment.systemPackages = [ cfg.package ];
-    security.pam.services.screen = {};
-  };
+  config = lib.mkMerge [
+    {
+      # TODO: Added in 24.05, remove before 24.11
+      assertions = [
+        {
+          assertion = cfg.screenrc != "" -> cfg.enable;
+          message = "`programs.screen.screenrc` has been configured, but `programs.screen.enable` is not true";
+        }
+      ];
+    }
+    (lib.mkIf cfg.enable {
+      environment.etc.screenrc = {
+        text = cfg.screenrc;
+      };
+      environment.systemPackages = [ cfg.package ];
+      security.pam.services.screen = {};
+    })
+  ];
 }
diff --git a/nixos/modules/programs/sedutil.nix b/nixos/modules/programs/sedutil.nix
index c62ca24eaa012..978aaa5c82d5a 100644
--- a/nixos/modules/programs/sedutil.nix
+++ b/nixos/modules/programs/sedutil.nix
@@ -1,14 +1,12 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.sedutil;
 
 in {
-  options.programs.sedutil.enable = mkEnableOption "sedutil, to manage self encrypting drives that conform to the Trusted Computing Group OPAL 2.0 SSC specification";
+  options.programs.sedutil.enable = lib.mkEnableOption "sedutil, to manage self encrypting drives that conform to the Trusted Computing Group OPAL 2.0 SSC specification";
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     boot.kernelParams = [
       "libata.allow_tpm=1"
     ];
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
index 2d20644ec51ed..ef5bad69e934e 100644
--- a/nixos/modules/programs/shadow.nix
+++ b/nixos/modules/programs/shadow.nix
@@ -1,29 +1,39 @@
 # Configuration for the pwdutils suite of tools: passwd, useradd, etc.
 { config, lib, utils, pkgs, ... }:
-with lib;
 let
   cfg = config.security.loginDefs;
 in
 {
-  options = with types; {
+  options = {
+
+    security.shadow.enable = lib.mkEnableOption "" // {
+      default = true;
+      description = ''
+        Enable the shadow authentication suite, which provides critical programs such as su, login, passwd.
+
+        Note: This is currently experimental. Only disable this if you're
+        confident that you can recover your system if it breaks.
+      '';
+    };
+
     security.loginDefs = {
-      package = mkPackageOption pkgs "shadow" { };
+      package = lib.mkPackageOption pkgs "shadow" { };
 
-      chfnRestrict = mkOption {
+      chfnRestrict = lib.mkOption {
         description = ''
           Use chfn SUID to allow non-root users to change their account GECOS information.
         '';
-        type = nullOr str;
+        type = lib.types.nullOr lib.types.str;
         default = null;
       };
 
-      settings = mkOption {
+      settings = lib.mkOption {
         description = ''
           Config options for the /etc/login.defs file, that defines
           the site-specific configuration for the shadow password suite.
           See login.defs(5) man page for available options.
         '';
-        type = submodule {
+        type = lib.types.submodule {
           freeformType = (pkgs.formats.keyValue { }).type;
           /* There are three different sources for user/group id ranges, each of which gets
              used by different programs:
@@ -35,88 +45,88 @@ in
                by systemd for features like ConditionUser=@system and systemd-sysusers
               */
           options = {
-            DEFAULT_HOME = mkOption {
+            DEFAULT_HOME = lib.mkOption {
               description = "Indicate if login is allowed if we can't cd to the home directory.";
               default = "yes";
-              type = enum [ "yes" "no" ];
+              type = lib.types.enum [ "yes" "no" ];
             };
 
-            ENCRYPT_METHOD = mkOption {
+            ENCRYPT_METHOD = lib.mkOption {
               description = "This defines the system default encryption algorithm for encrypting passwords.";
               # The default crypt() method, keep in sync with the PAM default
               default = "YESCRYPT";
-              type = enum [ "YESCRYPT" "SHA512" "SHA256" "MD5" "DES"];
+              type = lib.types.enum [ "YESCRYPT" "SHA512" "SHA256" "MD5" "DES"];
             };
 
-            SYS_UID_MIN = mkOption {
+            SYS_UID_MIN = lib.mkOption {
               description = "Range of user IDs used for the creation of system users by useradd or newusers.";
               default = 400;
-              type = int;
+              type = lib.types.int;
             };
 
-            SYS_UID_MAX = mkOption {
+            SYS_UID_MAX = lib.mkOption {
               description = "Range of user IDs used for the creation of system users by useradd or newusers.";
               default = 999;
-              type = int;
+              type = lib.types.int;
             };
 
-            UID_MIN = mkOption {
+            UID_MIN = lib.mkOption {
               description = "Range of user IDs used for the creation of regular users by useradd or newusers.";
               default = 1000;
-              type = int;
+              type = lib.types.int;
             };
 
-            UID_MAX = mkOption {
+            UID_MAX = lib.mkOption {
               description = "Range of user IDs used for the creation of regular users by useradd or newusers.";
               default = 29999;
-              type = int;
+              type = lib.types.int;
             };
 
-            SYS_GID_MIN = mkOption {
+            SYS_GID_MIN = lib.mkOption {
               description = "Range of group IDs used for the creation of system groups by useradd, groupadd, or newusers";
               default = 400;
-              type = int;
+              type = lib.types.int;
             };
 
-            SYS_GID_MAX = mkOption {
+            SYS_GID_MAX = lib.mkOption {
               description = "Range of group IDs used for the creation of system groups by useradd, groupadd, or newusers";
               default = 999;
-              type = int;
+              type = lib.types.int;
             };
 
-            GID_MIN = mkOption {
+            GID_MIN = lib.mkOption {
               description = "Range of group IDs used for the creation of regular groups by useradd, groupadd, or newusers.";
               default = 1000;
-              type = int;
+              type = lib.types.int;
             };
 
-            GID_MAX = mkOption {
+            GID_MAX = lib.mkOption {
               description = "Range of group IDs used for the creation of regular groups by useradd, groupadd, or newusers.";
               default = 29999;
-              type = int;
+              type = lib.types.int;
             };
 
-            TTYGROUP = mkOption {
+            TTYGROUP = lib.mkOption {
               description = ''
                 The terminal permissions: the login tty will be owned by the TTYGROUP group,
                 and the permissions will be set to TTYPERM'';
               default = "tty";
-              type = str;
+              type = lib.types.str;
             };
 
-            TTYPERM = mkOption {
+            TTYPERM = lib.mkOption {
               description = ''
                 The terminal permissions: the login tty will be owned by the TTYGROUP group,
                 and the permissions will be set to TTYPERM'';
               default = "0620";
-              type = str;
+              type = lib.types.str;
             };
 
             # Ensure privacy for newly created home directories.
-            UMASK = mkOption {
+            UMASK = lib.mkOption {
               description = "The file mode creation mask is initialized to this value.";
               default = "077";
-              type = str;
+              type = lib.types.str;
             };
           };
         };
@@ -124,7 +134,7 @@ in
       };
     };
 
-    users.defaultUserShell = mkOption {
+    users.defaultUserShell = lib.mkOption {
       description = ''
         This option defines the default shell assigned to user
         accounts. This can be either a full system path or a shell package.
@@ -132,108 +142,116 @@ in
         This must not be a store path, since the path is
         used outside the store (in particular in /etc/passwd).
       '';
-      example = literalExpression "pkgs.zsh";
-      type = either path shellPackage;
+      example = lib.literalExpression "pkgs.zsh";
+      type = lib.types.either lib.types.path lib.types.shellPackage;
     };
   };
 
   ###### implementation
 
-  config = {
-    assertions = [
-      {
-        assertion = cfg.settings.SYS_UID_MIN <= cfg.settings.SYS_UID_MAX;
-        message = "SYS_UID_MIN must be less than or equal to SYS_UID_MAX";
-      }
-      {
-        assertion = cfg.settings.UID_MIN <= cfg.settings.UID_MAX;
-        message = "UID_MIN must be less than or equal to UID_MAX";
-      }
-      {
-        assertion = cfg.settings.SYS_GID_MIN <= cfg.settings.SYS_GID_MAX;
-        message = "SYS_GID_MIN must be less than or equal to SYS_GID_MAX";
-      }
-      {
-        assertion = cfg.settings.GID_MIN <= cfg.settings.GID_MAX;
-        message = "GID_MIN must be less than or equal to GID_MAX";
-      }
-    ];
-
-    security.loginDefs.settings.CHFN_RESTRICT =
-      mkIf (cfg.chfnRestrict != null) cfg.chfnRestrict;
-
-    environment.systemPackages = optional config.users.mutableUsers cfg.package
-      ++ optional (types.shellPackage.check config.users.defaultUserShell) config.users.defaultUserShell
-      ++ optional (cfg.chfnRestrict != null) pkgs.util-linux;
-
-    environment.etc =
-      # Create custom toKeyValue generator
-      # see https://man7.org/linux/man-pages/man5/login.defs.5.html for config specification
-      let
-        toKeyValue = generators.toKeyValue {
-          mkKeyValue = generators.mkKeyValueDefault { } " ";
-        };
-      in
-      {
-        # /etc/login.defs: global configuration for pwdutils.
-        # You cannot login without it!
-        "login.defs".source = pkgs.writeText "login.defs" (toKeyValue cfg.settings);
-
-        # /etc/default/useradd: configuration for useradd.
-        "default/useradd".source = pkgs.writeText "useradd" ''
-          GROUP=100
-          HOME=/home
-          SHELL=${utils.toShellPath config.users.defaultUserShell}
-        '';
-      };
+  config = lib.mkMerge [
+    {
+      assertions = [
+        {
+          assertion = config.security.shadow.enable || config.services.greetd.enable;
+          message = "You must enable at least one VT login method, either security.shadow.enable or services.greetd.enable";
+        }
+      ];
+    }
+    (lib.mkIf config.security.shadow.enable {
+      assertions = [
+        {
+          assertion = cfg.settings.SYS_UID_MIN <= cfg.settings.SYS_UID_MAX;
+          message = "SYS_UID_MIN must be less than or equal to SYS_UID_MAX";
+        }
+        {
+          assertion = cfg.settings.UID_MIN <= cfg.settings.UID_MAX;
+          message = "UID_MIN must be less than or equal to UID_MAX";
+        }
+        {
+          assertion = cfg.settings.SYS_GID_MIN <= cfg.settings.SYS_GID_MAX;
+          message = "SYS_GID_MIN must be less than or equal to SYS_GID_MAX";
+        }
+        {
+          assertion = cfg.settings.GID_MIN <= cfg.settings.GID_MAX;
+          message = "GID_MIN must be less than or equal to GID_MAX";
+        }
+      ];
 
-    security.pam.services = {
-      chsh = { rootOK = true; };
-      chfn = { rootOK = true; };
-      su = {
-        rootOK = true;
-        forwardXAuth = true;
-        logFailures = true;
-      };
-      passwd = { };
-      # Note: useradd, groupadd etc. aren't setuid root, so it
-      # doesn't really matter what the PAM config says as long as it
-      # lets root in.
-      useradd.rootOK = true;
-      usermod.rootOK = true;
-      userdel.rootOK = true;
-      groupadd.rootOK = true;
-      groupmod.rootOK = true;
-      groupmems.rootOK = true;
-      groupdel.rootOK = true;
-      login = {
-        startSession = true;
-        allowNullPassword = true;
-        showMotd = true;
-        updateWtmp = true;
-      };
-      chpasswd = { rootOK = true; };
-    };
+      security.loginDefs.settings.CHFN_RESTRICT = lib.mkIf (cfg.chfnRestrict != null) cfg.chfnRestrict;
+
+      environment.systemPackages = lib.optional config.users.mutableUsers cfg.package
+        ++ lib.optional (lib.types.shellPackage.check config.users.defaultUserShell) config.users.defaultUserShell
+        ++ lib.optional (cfg.chfnRestrict != null) pkgs.util-linux;
+
+      environment.etc =
+        # Create custom toKeyValue generator
+        # see https://man7.org/linux/man-pages/man5/login.defs.5.html for config specification
+        let
+          toKeyValue = lib.generators.toKeyValue {
+            mkKeyValue = lib.generators.mkKeyValueDefault { } " ";
+          };
+        in {
+          # /etc/login.defs: global configuration for pwdutils.
+          # You cannot login without it!
+          "login.defs".source = pkgs.writeText "login.defs" (toKeyValue cfg.settings);
+
+          # /etc/default/useradd: configuration for useradd.
+          "default/useradd".source = pkgs.writeText "useradd" ''
+            GROUP=100
+            HOME=/home
+            SHELL=${utils.toShellPath config.users.defaultUserShell}
+          '';
+        };
 
-    security.wrappers =
-      let
-        mkSetuidRoot = source: {
-          setuid = true;
-          owner = "root";
-          group = "root";
-          inherit source;
+      security.pam.services = {
+        chsh.rootOK = true;
+        chfn.rootOK = true;
+        su = {
+          rootOK = true;
+          forwardXAuth = true;
+          logFailures = true;
         };
-      in
-      {
-        su = mkSetuidRoot "${cfg.package.su}/bin/su";
-        sg = mkSetuidRoot "${cfg.package.out}/bin/sg";
-        newgrp = mkSetuidRoot "${cfg.package.out}/bin/newgrp";
-        newuidmap = mkSetuidRoot "${cfg.package.out}/bin/newuidmap";
-        newgidmap = mkSetuidRoot "${cfg.package.out}/bin/newgidmap";
-      }
-      // optionalAttrs config.users.mutableUsers {
-        chsh = mkSetuidRoot "${cfg.package.out}/bin/chsh";
-        passwd = mkSetuidRoot "${cfg.package.out}/bin/passwd";
+        passwd = { };
+        # Note: useradd, groupadd etc. aren't setuid root, so it
+        # doesn't really matter what the PAM config says as long as it
+        # lets root in.
+        useradd.rootOK = true;
+        usermod.rootOK = true;
+        userdel.rootOK = true;
+        groupadd.rootOK = true;
+        groupmod.rootOK = true;
+        groupmems.rootOK = true;
+        groupdel.rootOK = true;
+        login = {
+          startSession = true;
+          allowNullPassword = true;
+          showMotd = true;
+          updateWtmp = true;
+        };
+        chpasswd.rootOK = true;
       };
-  };
+
+      security.wrappers =
+        let
+          mkSetuidRoot = source: {
+            setuid = true;
+            owner = "root";
+            group = "root";
+            inherit source;
+          };
+        in
+          {
+            su = mkSetuidRoot "${cfg.package.su}/bin/su";
+            sg = mkSetuidRoot "${cfg.package.out}/bin/sg";
+            newgrp = mkSetuidRoot "${cfg.package.out}/bin/newgrp";
+            newuidmap = mkSetuidRoot "${cfg.package.out}/bin/newuidmap";
+            newgidmap = mkSetuidRoot "${cfg.package.out}/bin/newgidmap";
+          }
+          // lib.optionalAttrs config.users.mutableUsers {
+            chsh = mkSetuidRoot "${cfg.package.out}/bin/chsh";
+            passwd = mkSetuidRoot "${cfg.package.out}/bin/passwd";
+          };
+    })
+  ];
 }
diff --git a/nixos/modules/programs/sharing.nix b/nixos/modules/programs/sharing.nix
index 211dc9815166e..0fe8100bbc569 100644
--- a/nixos/modules/programs/sharing.nix
+++ b/nixos/modules/programs/sharing.nix
@@ -1,8 +1,7 @@
 { config, pkgs, lib, ... }:
-with lib;
 {
   options.programs.sharing = {
-    enable = mkEnableOption ''
+    enable = lib.mkEnableOption ''
       sharing, a CLI tool for sharing files.
 
       Note that it will opens the 7478 port for TCP in the firewall, which is needed for it to function properly
@@ -12,7 +11,7 @@ with lib;
     let
       cfg = config.programs.sharing;
     in
-      mkIf cfg.enable {
+      lib.mkIf cfg.enable {
         environment.systemPackages = [ pkgs.sharing ];
         networking.firewall.allowedTCPPorts = [ 7478 ];
       };
diff --git a/nixos/modules/programs/singularity.nix b/nixos/modules/programs/singularity.nix
index f4c0a6fe487e6..bc989ad2dbaf5 100644
--- a/nixos/modules/programs/singularity.nix
+++ b/nixos/modules/programs/singularity.nix
@@ -5,21 +5,20 @@
   ...
 }:
 
-with lib;
 let
   cfg = config.programs.singularity;
 in
 {
 
   options.programs.singularity = {
-    enable = mkEnableOption "singularity" // {
+    enable = lib.mkEnableOption "singularity" // {
       description = ''
         Whether to install Singularity/Apptainer with system-level overriding such as SUID support.
       '';
     };
-    package = mkPackageOption pkgs "singularity" { example = "apptainer"; };
-    packageOverriden = mkOption {
-      type = types.nullOr types.package;
+    package = lib.mkPackageOption pkgs "singularity" { example = "apptainer"; };
+    packageOverriden = lib.mkOption {
+      type = lib.types.nullOr lib.types.package;
       default = null;
       description = ''
         This option provides access to the overridden result of `programs.singularity.package`.
@@ -42,8 +41,8 @@ in
         Use `lib.mkForce` to forcefully specify the overridden package.
       '';
     };
-    enableExternalLocalStateDir = mkOption {
-      type = types.bool;
+    enableExternalLocalStateDir = lib.mkOption {
+      type = lib.types.bool;
       default = true;
       example = false;
       description = ''
@@ -54,22 +53,22 @@ in
         `/var/lib/''${projectName}/mnt/session`.
       '';
     };
-    enableFakeroot = mkOption {
-      type = types.bool;
+    enableFakeroot = lib.mkOption {
+      type = lib.types.bool;
       default = true;
       example = false;
       description = ''
         Whether to enable the `--fakeroot` support of Singularity/Apptainer.
       '';
     };
-    enableSuid = mkOption {
-      type = types.bool;
+    enableSuid = lib.mkOption {
+      type = lib.types.bool;
       # SingularityCE requires SETUID for most things. Apptainer prefers user
       # namespaces, e.g. `apptainer exec --nv` would fail if built
       # `--with-suid`:
       # > `FATAL: nvidia-container-cli not allowed in setuid mode`
       default = cfg.package.projectName != "apptainer";
-      defaultText = literalExpression ''config.services.singularity.package.projectName != "apptainer"'';
+      defaultText = lib.literalExpression ''config.services.singularity.package.projectName != "apptainer"'';
       example = false;
       description = ''
         Whether to enable the SUID support of Singularity/Apptainer.
@@ -77,28 +76,28 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     programs.singularity.packageOverriden = (
       cfg.package.override (
-        optionalAttrs cfg.enableExternalLocalStateDir { externalLocalStateDir = "/var/lib"; }
-        // optionalAttrs cfg.enableFakeroot {
+        lib.optionalAttrs cfg.enableExternalLocalStateDir { externalLocalStateDir = "/var/lib"; }
+        // lib.optionalAttrs cfg.enableFakeroot {
           newuidmapPath = "/run/wrappers/bin/newuidmap";
           newgidmapPath = "/run/wrappers/bin/newgidmap";
         }
-        // optionalAttrs cfg.enableSuid {
+        // lib.optionalAttrs cfg.enableSuid {
           enableSuid = true;
           starterSuidPath = "/run/wrappers/bin/${cfg.package.projectName}-suid";
         }
       )
     );
     environment.systemPackages = [ cfg.packageOverriden ];
-    security.wrappers."${cfg.packageOverriden.projectName}-suid" = mkIf cfg.enableSuid {
+    security.wrappers."${cfg.packageOverriden.projectName}-suid" = lib.mkIf cfg.enableSuid {
       setuid = true;
       owner = "root";
       group = "root";
       source = "${cfg.packageOverriden}/libexec/${cfg.packageOverriden.projectName}/bin/starter-suid.orig";
     };
-    systemd.tmpfiles.rules = mkIf cfg.enableExternalLocalStateDir [
+    systemd.tmpfiles.rules = lib.mkIf cfg.enableExternalLocalStateDir [
       "d /var/lib/${cfg.packageOverriden.projectName}/mnt/session 0770 root root -"
     ];
   };
diff --git a/nixos/modules/programs/slock.nix b/nixos/modules/programs/slock.nix
index f39b4d5e9280e..ce24f662f218c 100644
--- a/nixos/modules/programs/slock.nix
+++ b/nixos/modules/programs/slock.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.slock;
 
@@ -9,18 +7,18 @@ in
 {
   options = {
     programs.slock = {
-      enable = mkOption {
+      enable = lib.mkOption {
         default = false;
-        type = types.bool;
+        type = lib.types.bool;
         description = ''
           Whether to install slock screen locker with setuid wrapper.
         '';
       };
-      package = mkPackageOption pkgs "slock" {};
+      package = lib.mkPackageOption pkgs "slock" {};
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
     security.wrappers.slock =
       { setuid = true;
diff --git a/nixos/modules/programs/soundmodem.nix b/nixos/modules/programs/soundmodem.nix
index ab992c63c6088..5f57e24a4524a 100644
--- a/nixos/modules/programs/soundmodem.nix
+++ b/nixos/modules/programs/soundmodem.nix
@@ -1,26 +1,24 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.soundmodem;
 in
 {
   options = {
     programs.soundmodem = {
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to add Soundmodem to the global environment and configure a
           wrapper for 'soundmodemconfig' for users in the 'soundmodem' group.
         '';
       };
-      package = mkPackageOption pkgs "soundmodem" { };
+      package = lib.mkPackageOption pkgs "soundmodem" { };
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
     users.groups.soundmodem = { };
 
diff --git a/nixos/modules/programs/spacefm.nix b/nixos/modules/programs/spacefm.nix
index fec14fca48e1e..73d48cf6a3a83 100644
--- a/nixos/modules/programs/spacefm.nix
+++ b/nixos/modules/programs/spacefm.nix
@@ -2,8 +2,6 @@
 
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let cfg = config.programs.spacefm;
 
 in
@@ -14,21 +12,21 @@ in
 
     programs.spacefm = {
 
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to install SpaceFM and create {file}`/etc/spacefm/spacefm.conf`.
         '';
       };
 
-      settings = mkOption {
-        type = types.attrs;
+      settings = lib.mkOption {
+        type = lib.types.attrs;
         default = {
           tmp_dir = "/tmp";
           terminal_su = "${pkgs.sudo}/bin/sudo";
         };
-        defaultText = literalExpression ''
+        defaultText = lib.literalExpression ''
           {
             tmp_dir = "/tmp";
             terminal_su = "''${pkgs.sudo}/bin/sudo";
@@ -46,10 +44,10 @@ in
 
   ###### implementation
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ pkgs.spaceFM ];
 
     environment.etc."spacefm/spacefm.conf".text =
-      concatStrings (mapAttrsToList (n: v: "${n}=${toString v}\n") cfg.settings);
+      lib.concatStrings (lib.mapAttrsToList (n: v: "${n}=${builtins.toString v}\n") cfg.settings);
   };
 }
diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix
index 2d25c7a936623..0692dd46f7d04 100644
--- a/nixos/modules/programs/ssh.nix
+++ b/nixos/modules/programs/ssh.nix
@@ -2,8 +2,6 @@
 
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
 
   cfg  = config.programs.ssh;
@@ -17,16 +15,16 @@ let
       exec ${cfg.askPassword} "$@"
     '';
 
-  knownHosts = attrValues cfg.knownHosts;
+  knownHosts = builtins.attrValues cfg.knownHosts;
 
-  knownHostsText = (flip (concatMapStringsSep "\n") knownHosts
+  knownHostsText = (lib.flip (lib.concatMapStringsSep "\n") knownHosts
     (h: assert h.hostNames != [];
-      optionalString h.certAuthority "@cert-authority " + concatStringsSep "," h.hostNames + " "
-      + (if h.publicKey != null then h.publicKey else readFile h.publicKeyFile)
+      lib.optionalString h.certAuthority "@cert-authority " + builtins.concatStringsSep "," h.hostNames + " "
+      + (if h.publicKey != null then h.publicKey else builtins.readFile h.publicKeyFile)
     )) + "\n";
 
   knownHostsFiles = [ "/etc/ssh/ssh_known_hosts" ]
-    ++ map pkgs.copyPathToStore cfg.knownHostsFiles;
+    ++ builtins.map pkgs.copyPathToStore cfg.knownHostsFiles;
 
 in
 {
@@ -36,21 +34,21 @@ in
 
     programs.ssh = {
 
-      enableAskPassword = mkOption {
-        type = types.bool;
+      enableAskPassword = lib.mkOption {
+        type = lib.types.bool;
         default = config.services.xserver.enable;
-        defaultText = literalExpression "config.services.xserver.enable";
+        defaultText = lib.literalExpression "config.services.xserver.enable";
         description = "Whether to configure SSH_ASKPASS in the environment.";
       };
 
-      askPassword = mkOption {
-        type = types.str;
+      askPassword = lib.mkOption {
+        type = lib.types.str;
         default = "${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass";
-        defaultText = literalExpression ''"''${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass"'';
+        defaultText = lib.literalExpression ''"''${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass"'';
         description = "Program used by SSH to ask for passwords.";
       };
 
-      forwardX11 = mkOption {
+      forwardX11 = lib.mkOption {
         type = with lib.types; nullOr bool;
         default = false;
         description = ''
@@ -65,25 +63,25 @@ in
         '';
       };
 
-      setXAuthLocation = mkOption {
-        type = types.bool;
+      setXAuthLocation = lib.mkOption {
+        type = lib.types.bool;
         description = ''
           Whether to set the path to {command}`xauth` for X11-forwarded connections.
           This causes a dependency on X11 packages.
         '';
       };
 
-      pubkeyAcceptedKeyTypes = mkOption {
-        type = types.listOf types.str;
+      pubkeyAcceptedKeyTypes = lib.mkOption {
+        type = lib.types.listOf lib.types.str;
         default = [];
         example = [ "ssh-ed25519" "ssh-rsa" ];
         description = ''
-          Specifies the key types that will be used for public key authentication.
+          Specifies the key lib.types that will be used for public key authentication.
         '';
       };
 
-      hostKeyAlgorithms = mkOption {
-        type = types.listOf types.str;
+      hostKeyAlgorithms = lib.mkOption {
+        type = lib.types.listOf lib.types.str;
         default = [];
         example = [ "ssh-ed25519" "ssh-rsa" ];
         description = ''
@@ -91,8 +89,8 @@ in
         '';
       };
 
-      extraConfig = mkOption {
-        type = types.lines;
+      extraConfig = lib.mkOption {
+        type = lib.types.lines;
         default = "";
         description = ''
           Extra configuration text prepended to {file}`ssh_config`. Other generated
@@ -102,8 +100,8 @@ in
         '';
       };
 
-      startAgent = mkOption {
-        type = types.bool;
+      startAgent = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to start the OpenSSH agent when you log in.  The OpenSSH agent
@@ -113,8 +111,8 @@ in
         '';
       };
 
-      agentTimeout = mkOption {
-        type = types.nullOr types.str;
+      agentTimeout = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
         default = null;
         example = "1h";
         description = ''
@@ -122,34 +120,34 @@ in
         '';
       };
 
-      agentPKCS11Whitelist = mkOption {
-        type = types.nullOr types.str;
+      agentPKCS11Whitelist = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
         default = null;
-        example = literalExpression ''"''${pkgs.opensc}/lib/opensc-pkcs11.so"'';
+        example = lib.literalExpression ''"''${pkgs.opensc}/lib/opensc-pkcs11.so"'';
         description = ''
           A pattern-list of acceptable paths for PKCS#11 shared libraries
           that may be used with the -s option to ssh-add.
         '';
       };
 
-      package = mkPackageOption pkgs "openssh" { };
+      package = lib.mkPackageOption pkgs "openssh" { };
 
-      knownHosts = mkOption {
+      knownHosts = lib.mkOption {
         default = {};
-        type = types.attrsOf (types.submodule ({ name, config, options, ... }: {
+        type = lib.types.attrsOf (lib.types.submodule ({ name, config, options, ... }: {
           options = {
-            certAuthority = mkOption {
-              type = types.bool;
+            certAuthority = lib.mkOption {
+              type = lib.types.bool;
               default = false;
               description = ''
                 This public key is an SSH certificate authority, rather than an
                 individual host's key.
               '';
             };
-            hostNames = mkOption {
-              type = types.listOf types.str;
+            hostNames = lib.mkOption {
+              type = lib.types.listOf lib.types.str;
               default = [ name ] ++ config.extraHostNames;
-              defaultText = literalExpression "[ ${name} ] ++ config.${options.extraHostNames}";
+              defaultText = lib.literalExpression "[ ${name} ] ++ config.${options.extraHostNames}";
               description = ''
                 A list of host names and/or IP numbers used for accessing
                 the host's ssh service. This list includes the name of the
@@ -160,8 +158,8 @@ in
                 `hostNames` list.
               '';
             };
-            extraHostNames = mkOption {
-              type = types.listOf types.str;
+            extraHostNames = lib.mkOption {
+              type = lib.types.listOf lib.types.str;
               default = [];
               description = ''
                 A list of additional host names and/or IP numbers used for
@@ -169,9 +167,9 @@ in
                 `hostNames` is set explicitly.
               '';
             };
-            publicKey = mkOption {
+            publicKey = lib.mkOption {
               default = null;
-              type = types.nullOr types.str;
+              type = lib.types.nullOr lib.types.str;
               example = "ecdsa-sha2-nistp521 AAAAE2VjZHN...UEPg==";
               description = ''
                 The public key data for the host. You can fetch a public key
@@ -180,9 +178,9 @@ in
                 the key type and the key itself.
               '';
             };
-            publicKeyFile = mkOption {
+            publicKeyFile = lib.mkOption {
               default = null;
-              type = types.nullOr types.path;
+              type = lib.types.nullOr lib.types.path;
               description = ''
                 The path to the public key file for the host. The public
                 key file is read at build time and saved in the Nix store.
@@ -204,7 +202,7 @@ in
           `extraHostNames` to add additional host names without
           disabling this default.
         '';
-        example = literalExpression ''
+        example = lib.literalExpression ''
           {
             myhost = {
               extraHostNames = [ "myhost.mydomain.com" "10.10.1.4" ];
@@ -219,16 +217,16 @@ in
         '';
       };
 
-      knownHostsFiles = mkOption {
+      knownHostsFiles = lib.mkOption {
         default = [];
-        type = with types; listOf path;
+        type = with lib.types; listOf path;
         description = ''
           Files containing SSH host keys to set as global known hosts.
           `/etc/ssh/ssh_known_hosts` (which is
           generated by {option}`programs.ssh.knownHosts`) is
           always included.
         '';
-        example = literalExpression ''
+        example = lib.literalExpression ''
           [
             ./known_hosts
             (writeText "github.keys" '''
@@ -240,8 +238,8 @@ in
         '';
       };
 
-      kexAlgorithms = mkOption {
-        type = types.nullOr (types.listOf types.str);
+      kexAlgorithms = lib.mkOption {
+        type = lib.types.nullOr (lib.types.listOf lib.types.str);
         default = null;
         example = [ "curve25519-sha256@libssh.org" "diffie-hellman-group-exchange-sha256" ];
         description = ''
@@ -249,8 +247,8 @@ in
         '';
       };
 
-      ciphers = mkOption {
-        type = types.nullOr (types.listOf types.str);
+      ciphers = lib.mkOption {
+        type = lib.types.nullOr (lib.types.listOf lib.types.str);
         default = null;
         example = [ "chacha20-poly1305@openssh.com" "aes256-gcm@openssh.com" ];
         description = ''
@@ -258,8 +256,8 @@ in
         '';
       };
 
-      macs = mkOption {
-        type = types.nullOr (types.listOf types.str);
+      macs = lib.mkOption {
+        type = lib.types.nullOr (lib.types.listOf lib.types.str);
         default = null;
         example = [ "hmac-sha2-512-etm@openssh.com" "hmac-sha1" ];
         description = ''
@@ -274,13 +272,13 @@ in
   config = {
 
     programs.ssh.setXAuthLocation =
-      mkDefault (config.services.xserver.enable || config.programs.ssh.forwardX11 == true || config.services.openssh.settings.X11Forwarding);
+      lib.mkDefault (config.services.xserver.enable || config.programs.ssh.forwardX11 == true || config.services.openssh.settings.X11Forwarding);
 
     assertions =
       [ { assertion = cfg.forwardX11 == true -> cfg.setXAuthLocation;
           message = "cannot enable X11 forwarding without setting XAuth location";
         }
-      ] ++ flip mapAttrsToList cfg.knownHosts (name: data: {
+      ] ++ lib.flip lib.mapAttrsToList cfg.knownHosts (name: data: {
         assertion = (data.publicKey == null && data.publicKeyFile != null) ||
                     (data.publicKey != null && data.publicKeyFile == null);
         message = "knownHost ${name} must contain either a publicKey or publicKeyFile";
@@ -296,22 +294,22 @@ in
         # Generated options from other settings
         Host *
         AddressFamily ${if config.networking.enableIPv6 then "any" else "inet"}
-        GlobalKnownHostsFile ${concatStringsSep " " knownHostsFiles}
+        GlobalKnownHostsFile ${builtins.concatStringsSep " " knownHostsFiles}
 
-        ${optionalString cfg.setXAuthLocation "XAuthLocation ${pkgs.xorg.xauth}/bin/xauth"}
+        ${lib.optionalString cfg.setXAuthLocation "XAuthLocation ${pkgs.xorg.xauth}/bin/xauth"}
         ${lib.optionalString (cfg.forwardX11 != null) "ForwardX11 ${if cfg.forwardX11 then "yes" else "no"}"}
 
-        ${optionalString (cfg.pubkeyAcceptedKeyTypes != []) "PubkeyAcceptedKeyTypes ${concatStringsSep "," cfg.pubkeyAcceptedKeyTypes}"}
-        ${optionalString (cfg.hostKeyAlgorithms != []) "HostKeyAlgorithms ${concatStringsSep "," cfg.hostKeyAlgorithms}"}
-        ${optionalString (cfg.kexAlgorithms != null) "KexAlgorithms ${concatStringsSep "," cfg.kexAlgorithms}"}
-        ${optionalString (cfg.ciphers != null) "Ciphers ${concatStringsSep "," cfg.ciphers}"}
-        ${optionalString (cfg.macs != null) "MACs ${concatStringsSep "," cfg.macs}"}
+        ${lib.optionalString (cfg.pubkeyAcceptedKeyTypes != []) "PubkeyAcceptedKeyTypes ${builtins.concatStringsSep "," cfg.pubkeyAcceptedKeyTypes}"}
+        ${lib.optionalString (cfg.hostKeyAlgorithms != []) "HostKeyAlgorithms ${builtins.concatStringsSep "," cfg.hostKeyAlgorithms}"}
+        ${lib.optionalString (cfg.kexAlgorithms != null) "KexAlgorithms ${builtins.concatStringsSep "," cfg.kexAlgorithms}"}
+        ${lib.optionalString (cfg.ciphers != null) "Ciphers ${builtins.concatStringsSep "," cfg.ciphers}"}
+        ${lib.optionalString (cfg.macs != null) "MACs ${builtins.concatStringsSep "," cfg.macs}"}
       '';
 
     environment.etc."ssh/ssh_known_hosts".text = knownHostsText;
 
     # FIXME: this should really be socket-activated for über-awesomeness.
-    systemd.user.services.ssh-agent = mkIf cfg.startAgent
+    systemd.user.services.ssh-agent = lib.mkIf cfg.startAgent
       { description = "SSH Agent";
         wantedBy = [ "default.target" ];
         unitConfig.ConditionUser = "!@system";
@@ -319,8 +317,8 @@ in
           { ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent";
             ExecStart =
                 "${cfg.package}/bin/ssh-agent " +
-                optionalString (cfg.agentTimeout != null) ("-t ${cfg.agentTimeout} ") +
-                optionalString (cfg.agentPKCS11Whitelist != null) ("-P ${cfg.agentPKCS11Whitelist} ") +
+                lib.optionalString (cfg.agentTimeout != null) ("-t ${cfg.agentTimeout} ") +
+                lib.optionalString (cfg.agentPKCS11Whitelist != null) ("-P ${cfg.agentPKCS11Whitelist} ") +
                 "-a %t/ssh-agent";
             StandardOutput = "null";
             Type = "forking";
@@ -330,18 +328,18 @@ in
         # Allow ssh-agent to ask for confirmation. This requires the
         # unit to know about the user's $DISPLAY (via ‘systemctl
         # import-environment’).
-        environment.SSH_ASKPASS = optionalString cfg.enableAskPassword askPasswordWrapper;
+        environment.SSH_ASKPASS = lib.optionalString cfg.enableAskPassword askPasswordWrapper;
         environment.DISPLAY = "fake"; # required to make ssh-agent start $SSH_ASKPASS
       };
 
-    environment.extraInit = optionalString cfg.startAgent
+    environment.extraInit = lib.optionalString cfg.startAgent
       ''
         if [ -z "$SSH_AUTH_SOCK" -a -n "$XDG_RUNTIME_DIR" ]; then
           export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent"
         fi
       '';
 
-    environment.variables.SSH_ASKPASS = optionalString cfg.enableAskPassword cfg.askPassword;
+    environment.variables.SSH_ASKPASS = lib.optionalString cfg.enableAskPassword cfg.askPassword;
 
   };
 }
diff --git a/nixos/modules/programs/steam.nix b/nixos/modules/programs/steam.nix
index 58aa0aa25b082..2ee464dc22d37 100644
--- a/nixos/modules/programs/steam.nix
+++ b/nixos/modules/programs/steam.nix
@@ -1,17 +1,17 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.steam;
   gamescopeCfg = config.programs.gamescope;
 
+  extraCompatPaths = lib.makeSearchPathOutput "steamcompattool" "" cfg.extraCompatPackages;
+
   steam-gamescope = let
     exports = builtins.attrValues (builtins.mapAttrs (n: v: "export ${n}=${v}") cfg.gamescopeSession.env);
   in
     pkgs.writeShellScriptBin "steam-gamescope" ''
       ${builtins.concatStringsSep "\n" exports}
-      gamescope --steam ${toString cfg.gamescopeSession.args} -- steam -tenfoot -pipewire-dmabuf
+      gamescope --steam ${builtins.toString cfg.gamescopeSession.args} -- steam -tenfoot -pipewire-dmabuf
     '';
 
   gamescopeSessionFile =
@@ -24,13 +24,13 @@ let
     '').overrideAttrs (_: { passthru.providedSessions = [ "steam" ]; });
 in {
   options.programs.steam = {
-    enable = mkEnableOption "steam";
+    enable = lib.mkEnableOption "steam";
 
-    package = mkOption {
-      type = types.package;
+    package = lib.mkOption {
+      type = lib.types.package;
       default = pkgs.steam;
-      defaultText = literalExpression "pkgs.steam";
-      example = literalExpression ''
+      defaultText = lib.literalExpression "pkgs.steam";
+      example = lib.literalExpression ''
         pkgs.steam-small.override {
           extraEnv = {
             MANGOHUD = true;
@@ -44,18 +44,19 @@ in {
       '';
       apply = steam: steam.override (prev: {
         extraEnv = (lib.optionalAttrs (cfg.extraCompatPackages != [ ]) {
-          STEAM_EXTRA_COMPAT_TOOLS_PATHS = makeSearchPathOutput "steamcompattool" "" cfg.extraCompatPackages;
-        }) // (optionalAttrs cfg.extest.enable {
+          STEAM_EXTRA_COMPAT_TOOLS_PATHS = extraCompatPaths;
+        }) // (lib.optionalAttrs cfg.extest.enable {
           LD_PRELOAD = "${pkgs.pkgsi686Linux.extest}/lib/libextest.so";
         }) // (prev.extraEnv or {});
         extraLibraries = pkgs: let
           prevLibs = if prev ? extraLibraries then prev.extraLibraries pkgs else [ ];
-          additionalLibs = with config.hardware.opengl;
+          additionalLibs = with config.hardware.graphics;
             if pkgs.stdenv.hostPlatform.is64bit
             then [ package ] ++ extraPackages
             else [ package32 ] ++ extraPackages32;
         in prevLibs ++ additionalLibs;
-      } // optionalAttrs (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice)
+        extraPkgs = p: (cfg.extraPackages ++ lib.optionals (prev ? extraPkgs) (prev.extraPkgs p));
+      } // lib.optionalAttrs (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice)
       {
         buildFHSEnv = pkgs.buildFHSEnv.override {
           # use the setuid wrapped bubblewrap
@@ -71,10 +72,23 @@ in {
       '';
     };
 
-    extraCompatPackages = mkOption {
-      type = types.listOf types.package;
+    extraPackages = lib.mkOption {
+      type = lib.types.listOf lib.types.package;
+      default = [ ];
+      example = lib.literalExpression ''
+        with pkgs; [
+          gamescope
+        ]
+      '';
+      description = ''
+        Additional packages to add to the Steam environment.
+      '';
+    };
+
+    extraCompatPackages = lib.mkOption {
+      type = lib.types.listOf lib.types.package;
       default = [ ];
-      example = literalExpression ''
+      example = lib.literalExpression ''
         with pkgs; [
           proton-ge-bin
         ]
@@ -88,46 +102,59 @@ in {
       '';
     };
 
-    remotePlay.openFirewall = mkOption {
-      type = types.bool;
+    fontPackages = lib.mkOption {
+      type = lib.types.listOf lib.types.package;
+      # `fonts.packages` is a list of paths now, filter out which are not packages
+      default = builtins.filter lib.types.package.check config.fonts.packages;
+      defaultText = lib.literalExpression "builtins.filter lib.types.package.check config.fonts.packages";
+      example = lib.literalExpression "with pkgs; [ source-han-sans ]";
+      description = ''
+        Font packages to use in Steam.
+
+        Defaults to system fonts, but could be overridden to use other fonts — useful for users who would like to customize CJK fonts used in Steam. According to the [upstream issue](https://github.com/ValveSoftware/steam-for-linux/issues/10422#issuecomment-1944396010), Steam only follows the per-user fontconfig configuration.
+      '';
+    };
+
+    remotePlay.openFirewall = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         Open ports in the firewall for Steam Remote Play.
       '';
     };
 
-    dedicatedServer.openFirewall = mkOption {
-      type = types.bool;
+    dedicatedServer.openFirewall = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         Open ports in the firewall for Source Dedicated Server.
       '';
     };
 
-    localNetworkGameTransfers.openFirewall = mkOption {
-      type = types.bool;
+    localNetworkGameTransfers.openFirewall = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         Open ports in the firewall for Steam Local Network Game Transfers.
       '';
     };
 
-    gamescopeSession = mkOption {
+    gamescopeSession = lib.mkOption {
       description = "Run a GameScope driven Steam session from your display-manager";
       default = {};
-      type = types.submodule {
+      type = lib.types.submodule {
         options = {
-          enable = mkEnableOption "GameScope Session";
-          args = mkOption {
-            type = types.listOf types.str;
+          enable = lib.mkEnableOption "GameScope Session";
+          args = lib.mkOption {
+            type = lib.types.listOf lib.types.str;
             default = [ ];
             description = ''
               Arguments to be passed to GameScope for the session.
             '';
           };
 
-          env = mkOption {
-            type = types.attrsOf types.str;
+          env = lib.mkOption {
+            type = lib.types.attrsOf lib.types.str;
             default = { };
             description = ''
               Environmental variables to be passed to GameScope for the session.
@@ -137,20 +164,24 @@ in {
       };
     };
 
-    extest.enable = mkEnableOption ''
+    extest.enable = lib.mkEnableOption ''
       Load the extest library into Steam, to translate X11 input events to
       uinput events (e.g. for using Steam Input on Wayland)
     '';
+
+    protontricks = {
+      enable = lib.mkEnableOption "protontricks, a simple wrapper for running Winetricks commands for Proton-enabled games";
+      package = lib.mkPackageOption pkgs "protontricks" { };
+    };
   };
 
-  config = mkIf cfg.enable {
-    hardware.opengl = { # this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
+  config = lib.mkIf cfg.enable {
+    hardware.graphics = { # this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
       enable = true;
-      driSupport = true;
-      driSupport32Bit = true;
+      enable32Bit = true;
     };
 
-    security.wrappers = mkIf (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice) {
+    security.wrappers = lib.mkIf (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice) {
       # needed or steam fails
       bwrap = {
         owner = "root";
@@ -160,8 +191,10 @@ in {
       };
     };
 
-    programs.gamescope.enable = mkDefault cfg.gamescopeSession.enable;
-    services.displayManager.sessionPackages = mkIf cfg.gamescopeSession.enable [ gamescopeSessionFile ];
+    programs.steam.extraPackages = cfg.fontPackages;
+
+    programs.gamescope.enable = lib.mkDefault cfg.gamescopeSession.enable;
+    services.displayManager.sessionPackages = lib.mkIf cfg.gamescopeSession.enable [ gamescopeSessionFile ];
 
     # optionally enable 32bit pulseaudio support if pulseaudio is enabled
     hardware.pulseaudio.support32Bit = config.hardware.pulseaudio.enable;
@@ -171,28 +204,29 @@ in {
     environment.systemPackages = [
       cfg.package
       cfg.package.run
-    ] ++ lib.optional cfg.gamescopeSession.enable steam-gamescope;
+    ] ++ lib.optional cfg.gamescopeSession.enable steam-gamescope
+    ++ lib.optional cfg.protontricks.enable (cfg.protontricks.package.override { inherit extraCompatPaths; });
 
     networking.firewall = lib.mkMerge [
-      (mkIf (cfg.remotePlay.openFirewall || cfg.localNetworkGameTransfers.openFirewall) {
+      (lib.mkIf (cfg.remotePlay.openFirewall || cfg.localNetworkGameTransfers.openFirewall) {
         allowedUDPPorts = [ 27036 ]; # Peer discovery
       })
 
-      (mkIf cfg.remotePlay.openFirewall {
+      (lib.mkIf cfg.remotePlay.openFirewall {
         allowedTCPPorts = [ 27036 ];
         allowedUDPPortRanges = [ { from = 27031; to = 27035; } ];
       })
 
-      (mkIf cfg.dedicatedServer.openFirewall {
+      (lib.mkIf cfg.dedicatedServer.openFirewall {
         allowedTCPPorts = [ 27015 ]; # SRCDS Rcon port
         allowedUDPPorts = [ 27015 ]; # Gameplay traffic
       })
 
-      (mkIf cfg.localNetworkGameTransfers.openFirewall {
+      (lib.mkIf cfg.localNetworkGameTransfers.openFirewall {
         allowedTCPPorts = [ 27040 ]; # Data transfers
       })
     ];
   };
 
-  meta.maintainers = teams.steam;
+  meta.maintainers = lib.teams.steam.members;
 }
diff --git a/nixos/modules/programs/streamdeck-ui.nix b/nixos/modules/programs/streamdeck-ui.nix
index 6bec2abdfbec6..a1366c42181c2 100644
--- a/nixos/modules/programs/streamdeck-ui.nix
+++ b/nixos/modules/programs/streamdeck-ui.nix
@@ -1,34 +1,32 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.streamdeck-ui;
 in
 {
   options.programs.streamdeck-ui = {
-    enable = mkEnableOption "streamdeck-ui";
+    enable = lib.mkEnableOption "streamdeck-ui";
 
-    autoStart = mkOption {
+    autoStart = lib.mkOption {
       default = true;
-      type = types.bool;
+      type = lib.types.bool;
       description = "Whether streamdeck-ui should be started automatically.";
     };
 
-    package = mkPackageOption pkgs "streamdeck-ui" {
+    package = lib.mkPackageOption pkgs "streamdeck-ui" {
       default = [ "streamdeck-ui" ];
     };
 
   };
 
-  config = mkIf cfg.enable {
-    environment.systemPackages = with pkgs; [
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [
       cfg.package
-      (mkIf cfg.autoStart (makeAutostartItem { name = "streamdeck-ui-noui"; package = cfg.package; }))
+      (lib.mkIf cfg.autoStart (pkgs.makeAutostartItem { name = "streamdeck-ui-noui"; package = cfg.package; }))
     ];
 
     services.udev.packages = [ cfg.package ];
   };
 
-  meta.maintainers = with maintainers; [ majiir ];
+  meta.maintainers = with lib.maintainers; [ majiir ];
 }
diff --git a/nixos/modules/programs/sysdig.nix b/nixos/modules/programs/sysdig.nix
index cf2cbab5cf6e5..47b95ef64e97f 100644
--- a/nixos/modules/programs/sysdig.nix
+++ b/nixos/modules/programs/sysdig.nix
@@ -1,13 +1,11 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.sysdig;
 in {
-  options.programs.sysdig.enable = mkEnableOption "sysdig, a tracing tool";
+  options.programs.sysdig.enable = lib.mkEnableOption "sysdig, a tracing tool";
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ pkgs.sysdig ];
     boot.extraModulePackages = [ config.boot.kernelPackages.sysdig ];
   };
diff --git a/nixos/modules/programs/system-config-printer.nix b/nixos/modules/programs/system-config-printer.nix
index 34592dd7064b4..68b7897d64c24 100644
--- a/nixos/modules/programs/system-config-printer.nix
+++ b/nixos/modules/programs/system-config-printer.nix
@@ -1,7 +1,5 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 {
 
   ###### interface
@@ -10,7 +8,7 @@ with lib;
 
     programs.system-config-printer = {
 
-      enable = mkEnableOption "system-config-printer, a Graphical user interface for CUPS administration";
+      enable = lib.mkEnableOption "system-config-printer, a Graphical user interface for CUPS administration";
 
     };
 
@@ -19,7 +17,7 @@ with lib;
 
   ###### implementation
 
-  config = mkIf config.programs.system-config-printer.enable {
+  config = lib.mkIf config.programs.system-config-printer.enable {
 
     environment.systemPackages = [
       pkgs.system-config-printer
diff --git a/nixos/modules/programs/systemtap.nix b/nixos/modules/programs/systemtap.nix
index d23bd13fdd85d..e61e255e52217 100644
--- a/nixos/modules/programs/systemtap.nix
+++ b/nixos/modules/programs/systemtap.nix
@@ -1,14 +1,12 @@
 { config, lib, ... }:
 
-with lib;
-
 let cfg = config.programs.systemtap;
 in {
 
   options = {
     programs.systemtap = {
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Install {command}`systemtap` along with necessary kernel options.
@@ -16,7 +14,7 @@ in {
       };
     };
   };
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     system.requiredKernelConfig = with config.lib.kernelConfig; [
       (isYes "DEBUG")
     ];
diff --git a/nixos/modules/programs/thefuck.nix b/nixos/modules/programs/thefuck.nix
index ba2e39c013aeb..0e65352a1f216 100644
--- a/nixos/modules/programs/thefuck.nix
+++ b/nixos/modules/programs/thefuck.nix
@@ -1,7 +1,5 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let
   prg = config.programs;
   cfg = prg.thefuck;
@@ -16,11 +14,11 @@ in
   {
     options = {
       programs.thefuck = {
-        enable = mkEnableOption "thefuck, an app which corrects your previous console command";
+        enable = lib.mkEnableOption "thefuck, an app which corrects your previous console command";
 
-        alias = mkOption {
+        alias = lib.mkOption {
           default = "fuck";
-          type = types.str;
+          type = lib.types.str;
 
           description = ''
             `thefuck` needs an alias to be configured.
@@ -30,11 +28,11 @@ in
       };
     };
 
-    config = mkIf cfg.enable {
+    config = lib.mkIf cfg.enable {
       environment.systemPackages = with pkgs; [ thefuck ];
 
       programs.bash.interactiveShellInit = bashAndZshInitScript;
-      programs.zsh.interactiveShellInit = mkIf prg.zsh.enable bashAndZshInitScript;
-      programs.fish.interactiveShellInit = mkIf prg.fish.enable fishInitScript;
+      programs.zsh.interactiveShellInit = lib.mkIf prg.zsh.enable bashAndZshInitScript;
+      programs.fish.interactiveShellInit = lib.mkIf prg.fish.enable fishInitScript;
     };
   }
diff --git a/nixos/modules/programs/thunar.nix b/nixos/modules/programs/thunar.nix
index 5ea2982dd93cf..76fcc9d8298f4 100644
--- a/nixos/modules/programs/thunar.nix
+++ b/nixos/modules/programs/thunar.nix
@@ -1,29 +1,27 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let cfg = config.programs.thunar;
 
 in {
   meta = {
-    maintainers = teams.xfce.members;
+    maintainers = lib.teams.xfce.members;
   };
 
   options = {
     programs.thunar = {
-      enable = mkEnableOption "Thunar, the Xfce file manager";
+      enable = lib.mkEnableOption "Thunar, the Xfce file manager";
 
-      plugins = mkOption {
+      plugins = lib.mkOption {
         default = [];
-        type = types.listOf types.package;
+        type = lib.types.listOf lib.types.package;
         description = "List of thunar plugins to install.";
-        example = literalExpression "with pkgs.xfce; [ thunar-archive-plugin thunar-volman ]";
+        example = lib.literalExpression "with pkgs.xfce; [ thunar-archive-plugin thunar-volman ]";
       };
 
     };
   };
 
-  config = mkIf cfg.enable (
+  config = lib.mkIf cfg.enable (
     let package = pkgs.xfce.thunar.override { thunarPlugins = cfg.plugins; };
 
     in {
diff --git a/nixos/modules/programs/thunderbird.nix b/nixos/modules/programs/thunderbird.nix
new file mode 100644
index 0000000000000..b15c1df609439
--- /dev/null
+++ b/nixos/modules/programs/thunderbird.nix
@@ -0,0 +1,89 @@
+{
+  pkgs,
+  config,
+  lib,
+  ...
+}:
+let
+  cfg = config.programs.thunderbird;
+  policyFormat = pkgs.formats.json { };
+  policyDoc = "https://github.com/thunderbird/policy-templates";
+in
+{
+  options.programs.thunderbird = {
+    enable = lib.mkEnableOption "Thunderbird mail client";
+
+    package = lib.mkPackageOption pkgs "thunderbird" { };
+
+    policies = lib.mkOption {
+      type = policyFormat.type;
+      default = { };
+      description = ''
+        Group policies to install.
+
+        See [Thunderbird's documentation](${policyDoc})
+        for a list of available options.
+
+        This can be used to install extensions declaratively! Check out the
+        documentation of the `ExtensionSettings` policy for details.
+
+      '';
+    };
+
+    preferences = lib.mkOption {
+      type =
+        with lib.types;
+        attrsOf (oneOf [
+          bool
+          int
+          str
+        ]);
+      default = { };
+      description = ''
+        Preferences to set from `about:config`.
+
+        Some of these might be able to be configured more ergonomically
+        using policies.
+      '';
+    };
+
+    preferencesStatus = lib.mkOption {
+      type = lib.types.enum [
+        "default"
+        "locked"
+        "user"
+        "clear"
+      ];
+      default = "locked";
+      description = ''
+        The status of `thunderbird.preferences`.
+
+        `status` can assume the following values:
+        - `"default"`: Preferences appear as default.
+        - `"locked"`: Preferences appear as default and can't be changed.
+        - `"user"`: Preferences appear as changed.
+        - `"clear"`: Value has no effect. Resets to factory defaults on each startup.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    environment.etc =
+      let
+        policiesJSON = policyFormat.generate "thunderbird-policies.json" { inherit (cfg) policies; };
+      in
+      lib.mkIf (cfg.policies != { }) { "thunderbird/policies/policies.json".source = policiesJSON; };
+
+    programs.thunderbird.policies = {
+      DisableAppUpdate = true;
+      Preferences = builtins.mapAttrs (_: value: {
+        Value = value;
+        Status = cfg.preferencesStatus;
+      }) cfg.preferences;
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ nydragon ];
+}
diff --git a/nixos/modules/programs/traceroute.nix b/nixos/modules/programs/traceroute.nix
index 6e04057ac5034..0864dbe79db6b 100644
--- a/nixos/modules/programs/traceroute.nix
+++ b/nixos/modules/programs/traceroute.nix
@@ -1,14 +1,12 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.traceroute;
 in {
   options = {
     programs.traceroute = {
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to configure a setcap wrapper for traceroute.
@@ -17,7 +15,7 @@ in {
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     security.wrappers.traceroute = {
       owner = "root";
       group = "root";
diff --git a/nixos/modules/programs/turbovnc.nix b/nixos/modules/programs/turbovnc.nix
index fbb3a7bf22e99..c7ab18a2e2886 100644
--- a/nixos/modules/programs/turbovnc.nix
+++ b/nixos/modules/programs/turbovnc.nix
@@ -2,8 +2,6 @@
 
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.turbovnc;
 in
@@ -12,14 +10,14 @@ in
 
     programs.turbovnc = {
 
-      ensureHeadlessSoftwareOpenGL = mkOption {
-        type = types.bool;
+      ensureHeadlessSoftwareOpenGL = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to set up NixOS such that TurboVNC's built-in software OpenGL
           implementation works.
 
-          This will enable {option}`hardware.opengl.enable` so that OpenGL
+          This will enable {option}`hardware.graphics.enable` so that OpenGL
           programs can find Mesa's llvmpipe drivers.
 
           Setting this option to `false` does not mean that software
@@ -36,7 +34,7 @@ in
 
   };
 
-  config = mkIf cfg.ensureHeadlessSoftwareOpenGL {
+  config = lib.mkIf cfg.ensureHeadlessSoftwareOpenGL {
 
     # TurboVNC has builtin support for Mesa llvmpipe's `swrast`
     # software rendering to implement GLX (OpenGL on Xorg).
@@ -48,7 +46,7 @@ in
     # can find the llvmpipe `swrast.so` software rendering DRI lib via `libglvnd`.
     # This comment exists to explain why `hardware.` is involved,
     # even though 100% software rendering is used.
-    hardware.opengl.enable = true;
+    hardware.graphics.enable = true;
 
   };
 }
diff --git a/nixos/modules/programs/udevil.nix b/nixos/modules/programs/udevil.nix
index 44b9dd9234b38..e4c0daea72c1b 100644
--- a/nixos/modules/programs/udevil.nix
+++ b/nixos/modules/programs/udevil.nix
@@ -1,14 +1,12 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.udevil;
 
 in {
-  options.programs.udevil.enable = mkEnableOption "udevil, to mount filesystems without password";
+  options.programs.udevil.enable = lib.mkEnableOption "udevil, to mount filesystems without password";
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     security.wrappers.udevil =
       { setuid = true;
         owner = "root";
diff --git a/nixos/modules/programs/usbtop.nix b/nixos/modules/programs/usbtop.nix
index 4f13ce5f6262d..8b77b2bf51c43 100644
--- a/nixos/modules/programs/usbtop.nix
+++ b/nixos/modules/programs/usbtop.nix
@@ -1,15 +1,13 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let
   cfg = config.programs.usbtop;
 in {
   options = {
-    programs.usbtop.enable = mkEnableOption "usbtop and required kernel module, to show estimated USB bandwidth";
+    programs.usbtop.enable = lib.mkEnableOption "usbtop and required kernel module, to show estimated USB bandwidth";
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = with pkgs; [
       usbtop
     ];
diff --git a/nixos/modules/programs/vim.nix b/nixos/modules/programs/vim.nix
index eb3499fd243f5..8232340ddebbf 100644
--- a/nixos/modules/programs/vim.nix
+++ b/nixos/modules/programs/vim.nix
@@ -1,13 +1,11 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.vim;
 in {
   options.programs.vim = {
-    defaultEditor = mkOption {
-      type = types.bool;
+    defaultEditor = lib.mkOption {
+      type = lib.types.bool;
       default = false;
       description = ''
         When enabled, installs vim and configures vim to be the default editor
@@ -15,13 +13,13 @@ in {
       '';
     };
 
-    package = mkPackageOption pkgs "vim" {
+    package = lib.mkPackageOption pkgs "vim" {
       example = "vim-full";
     };
   };
 
-  config = mkIf cfg.defaultEditor {
+  config = lib.mkIf cfg.defaultEditor {
     environment.systemPackages = [ cfg.package ];
-    environment.variables = { EDITOR = mkOverride 900 "vim"; };
+    environment.variables = { EDITOR = lib.mkOverride 900 "vim"; };
   };
 }
diff --git a/nixos/modules/programs/virt-manager.nix b/nixos/modules/programs/virt-manager.nix
index 095db7586a034..9b5fa22268ae9 100644
--- a/nixos/modules/programs/virt-manager.nix
+++ b/nixos/modules/programs/virt-manager.nix
@@ -2,15 +2,27 @@
 
 let
   cfg = config.programs.virt-manager;
-in {
+in
+{
   options.programs.virt-manager = {
     enable = lib.mkEnableOption "virt-manager, an UI for managing virtual machines in libvirt";
 
-    package = lib.mkPackageOption pkgs "virt-manager" {};
+    package = lib.mkPackageOption pkgs "virt-manager" { };
   };
 
   config = lib.mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
-    programs.dconf.enable = true;
+    programs.dconf = {
+      profiles.user.databases = [
+        {
+          settings = {
+            "org/virt-manager/virt-manager/connections" = {
+              autoconnect = [ "qemu:///system" ];
+              uris = [ "qemu:///system" ];
+            };
+          };
+        }
+      ];
+    };
   };
 }
diff --git a/nixos/modules/programs/wavemon.nix b/nixos/modules/programs/wavemon.nix
index e5ccacba75d4a..86bc7cc097954 100644
--- a/nixos/modules/programs/wavemon.nix
+++ b/nixos/modules/programs/wavemon.nix
@@ -1,14 +1,12 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.wavemon;
 in {
   options = {
     programs.wavemon = {
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to add wavemon to the global environment and configure a
@@ -18,7 +16,7 @@ in {
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = with pkgs; [ wavemon ];
     security.wrappers.wavemon = {
       owner = "root";
diff --git a/nixos/modules/programs/wayland/hyprland.nix b/nixos/modules/programs/wayland/hyprland.nix
index e648eaa1b68e4..575adc79cf10b 100644
--- a/nixos/modules/programs/wayland/hyprland.nix
+++ b/nixos/modules/programs/wayland/hyprland.nix
@@ -1,57 +1,43 @@
-{ config
-, lib
-, pkgs
-, ...
-}:
-with lib; let
+{ config, lib, pkgs, ... }:
+
+let
   cfg = config.programs.hyprland;
 
-  finalPortalPackage = cfg.portalPackage.override {
-    hyprland = cfg.finalPackage;
-  };
+  wayland-lib = import ./lib.nix { inherit lib; };
 in
 {
   options.programs.hyprland = {
-    enable = mkEnableOption null // {
-      description = ''
-        Whether to enable Hyprland, the dynamic tiling Wayland compositor that doesn't sacrifice on its looks.
-
-        You can manually launch Hyprland by executing {command}`Hyprland` on a TTY.
-
-        A configuration file will be generated in {file}`~/.config/hypr/hyprland.conf`.
-        See <https://wiki.hyprland.org> for more information.
+    enable = lib.mkEnableOption ''
+      Hyprland, the dynamic tiling Wayland compositor that doesn't sacrifice on its looks.
+      You can manually launch Hyprland by executing {command}`Hyprland` on a TTY.
+      A configuration file will be generated in {file}`~/.config/hypr/hyprland.conf`.
+      See <https://wiki.hyprland.org> for more information'';
+
+    package = lib.mkPackageOption pkgs "hyprland" {
+      extraDescription = ''
+        If the package is not overridable with `enableXWayland`, then the module option
+        {option}`xwayland` will have no effect.
       '';
-    };
-
-    package = mkPackageOption pkgs "hyprland" { };
-
-    finalPackage = mkOption {
-      type = types.package;
-      readOnly = true;
-      default = cfg.package.override {
+    } // {
+      apply = p: wayland-lib.genFinalPackage p {
         enableXWayland = cfg.xwayland.enable;
       };
-      defaultText = literalExpression
-        "`programs.hyprland.package` with applied configuration";
-      description = ''
-        The Hyprland package after applying configuration.
-      '';
     };
 
-    portalPackage = mkPackageOption pkgs "xdg-desktop-portal-hyprland" { };
-
-    xwayland.enable = mkEnableOption ("XWayland") // { default = true; };
-
-    envVars.enable = mkEnableOption null // {
-      default = true;
-      example = false;
-      description = ''
-        Set environment variables for Hyprland to work properly.
-        Enabled by default.
+    portalPackage = lib.mkPackageOption pkgs "xdg-desktop-portal-hyprland" {
+      extraDescription = ''
+        If the package is not overridable with `hyprland`, then the Hyprland package
+        used by the portal may differ from the one set in the module option {option}`package`.
       '';
+    } // {
+      apply = p: wayland-lib.genFinalPackage p {
+        hyprland = cfg.package;
+      };
     };
 
-    systemd.setPath.enable = mkEnableOption null // {
+    xwayland.enable = lib.mkEnableOption "XWayland" // { default = true; };
+
+    systemd.setPath.enable = lib.mkEnableOption null // {
       default = true;
       example = false;
       description = ''
@@ -62,55 +48,47 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
-    environment.systemPackages = [ cfg.finalPackage ];
-
-    fonts.enableDefaultPackages = mkDefault true;
-    hardware.opengl.enable = mkDefault true;
+  config = lib.mkIf cfg.enable (lib.mkMerge [
+    {
+      environment.systemPackages = [ cfg.package ];
 
-    programs = {
-      dconf.enable = mkDefault true;
-      xwayland.enable = mkDefault cfg.xwayland.enable;
-    };
-
-    security.polkit.enable = true;
+      # To make a Hyprland session available if a display manager like SDDM is enabled:
+      services.displayManager.sessionPackages = [ cfg.package ];
 
-    services.displayManager.sessionPackages = [ cfg.finalPackage ];
-
-    xdg.portal = {
-      enable = mkDefault true;
-      extraPortals = [ finalPortalPackage ];
-      configPackages = mkDefault [ cfg.finalPackage ];
-    };
+      xdg.portal = {
+        enable = true;
+        extraPortals = [ cfg.portalPackage ];
+        configPackages = lib.mkDefault [ cfg.package ];
+      };
 
-    environment.sessionVariables = mkIf cfg.envVars.enable {
-      XDG_CURRENT_DESKTOP = "Hyprland";
-      XDG_SESSION_DESKTOP = "Hyprland";
-      XDG_SESSION_TYPE = "wayland";
-      GDK_BACKEND = "wayland,x11";
-      QT_QPA_PLATFORM = "wayland;xcb";
-      _JAVA_AWT_WM_NONREPARENTING = "1"; # Fix for Java applications on tiling window managers
-    };
+      systemd = lib.mkIf cfg.systemd.setPath.enable {
+        user.extraConfig = ''
+          DefaultEnvironment="PATH=/run/wrappers/bin:/etc/profiles/per-user/%u/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:$PATH"
+        '';
+      };
+    }
 
-    systemd = mkIf cfg.systemd.setPath.enable {
-      user.extraConfig = ''
-        DefaultEnvironment="PATH=$PATH:/run/current-system/sw/bin:/etc/profiles/per-user/%u/bin:/run/wrappers/bin"
-      '';
-    };
-  };
+    (import ./wayland-session.nix {
+      inherit lib pkgs;
+      enableXWayland = cfg.xwayland.enable;
+      enableWlrPortal = lib.mkDefault false; # Hyprland has its own portal, wlr is not needed
+    })
+  ]);
 
-  imports = with lib; [
-    (mkRemovedOptionModule
+  imports = [
+    (lib.mkRemovedOptionModule
       [ "programs" "hyprland" "xwayland" "hidpi" ]
       "XWayland patches are deprecated. Refer to https://wiki.hyprland.org/Configuring/XWayland"
     )
-    (mkRemovedOptionModule
+    (lib.mkRemovedOptionModule
       [ "programs" "hyprland" "enableNvidiaPatches" ]
       "Nvidia patches are no longer needed"
     )
-    (mkRemovedOptionModule
+    (lib.mkRemovedOptionModule
       [ "programs" "hyprland" "nvidiaPatches" ]
       "Nvidia patches are no longer needed"
     )
   ];
+
+  meta.maintainers = with lib.maintainers; [ fufexan ];
 }
diff --git a/nixos/modules/programs/wayland/hyprlock.nix b/nixos/modules/programs/wayland/hyprlock.nix
new file mode 100644
index 0000000000000..6c60765e80cc7
--- /dev/null
+++ b/nixos/modules/programs/wayland/hyprlock.nix
@@ -0,0 +1,25 @@
+{ lib, pkgs, config, ... }:
+
+let
+  cfg = config.programs.hyprlock;
+in
+{
+  options.programs.hyprlock = {
+    enable = lib.mkEnableOption "hyprlock, Hyprland's GPU-accelerated screen locking utility";
+    package = lib.mkPackageOption pkgs "hyprlock" { };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [
+      cfg.package
+    ];
+
+    # Hyprlock needs Hypridle systemd service to be running to detect idle time
+    services.hypridle.enable = true;
+
+    # Hyprlock needs PAM access to authenticate, else it fallbacks to su
+    security.pam.services.hyprlock = {};
+  };
+
+  meta.maintainers = with lib.maintainers; [ johnrtitor ];
+}
diff --git a/nixos/modules/programs/wayland/lib.nix b/nixos/modules/programs/wayland/lib.nix
new file mode 100644
index 0000000000000..0f275d3f18c56
--- /dev/null
+++ b/nixos/modules/programs/wayland/lib.nix
@@ -0,0 +1,12 @@
+{ lib }:
+
+{
+  genFinalPackage = pkg: args:
+    let
+      expectedArgs = with lib;
+        lib.naturalSort (lib.attrNames args);
+      existingArgs = with lib;
+        naturalSort (intersectLists expectedArgs (attrNames (functionArgs pkg.override)));
+    in
+      if existingArgs != expectedArgs then pkg else pkg.override args;
+}
diff --git a/nixos/modules/programs/wayland/river.nix b/nixos/modules/programs/wayland/river.nix
index d0e309646b0ef..6391f00e2f626 100644
--- a/nixos/modules/programs/wayland/river.nix
+++ b/nixos/modules/programs/wayland/river.nix
@@ -1,37 +1,40 @@
-{
-  config,
-  pkgs,
-  lib,
-  ...
-}:
-with lib; let
+{ config, lib, pkgs, ... }:
+
+let
   cfg = config.programs.river;
-in {
+
+  wayland-lib = import ./lib.nix { inherit lib; };
+in
+{
   options.programs.river = {
-    enable = mkEnableOption "river, a dynamic tiling Wayland compositor";
+    enable = lib.mkEnableOption "river, a dynamic tiling Wayland compositor";
 
-    package = mkPackageOption pkgs "river" {
+    package = lib.mkPackageOption pkgs "river" {
       nullable = true;
       extraDescription = ''
+        If the package is not overridable with `xwaylandSupport`, then the module option
+        {option}`xwayland` will have no effect.
+
         Set to `null` to not add any River package to your path.
         This should be done if you want to use the Home Manager River module to install River.
       '';
+    } // {
+      apply = p: if p == null then null else
+        wayland-lib.genFinalPackage p {
+          xwaylandSupport = cfg.xwayland.enable;
+        };
     };
 
-    extraPackages = mkOption {
-      type = with types; listOf package;
-      default = with pkgs; [
-        swaylock
-        foot
-        dmenu
-      ];
-      defaultText = literalExpression ''
+    xwayland.enable = lib.mkEnableOption "XWayland" // { default = true; };
+
+    extraPackages = lib.mkOption {
+      type = with lib.types; listOf package;
+      default = with pkgs; [ swaylock foot dmenu ];
+      defaultText = lib.literalExpression ''
         with pkgs; [ swaylock foot dmenu ];
       '';
-      example = literalExpression ''
-        with pkgs; [
-          termite rofi light
-        ]
+      example = lib.literalExpression ''
+        with pkgs; [ termite rofi light ]
       '';
       description = ''
         Extra packages to be installed system wide. See
@@ -41,19 +44,22 @@ in {
     };
   };
 
-  config =
-    mkIf cfg.enable (mkMerge [
-      {
-        environment.systemPackages = optional (cfg.package != null) cfg.package ++ cfg.extraPackages;
+  config = lib.mkIf cfg.enable (lib.mkMerge [
+    {
+      environment.systemPackages = lib.optional (cfg.package != null) cfg.package ++ cfg.extraPackages;
+
+      # To make a river session available if a display manager like SDDM is enabled:
+      services.displayManager.sessionPackages = lib.optional (cfg.package != null) cfg.package;
 
-        # To make a river session available if a display manager like SDDM is enabled:
-        services.displayManager.sessionPackages = optionals (cfg.package != null) [ cfg.package ];
+      # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050913
+      xdg.portal.config.river.default = lib.mkDefault [ "wlr" "gtk" ];
+    }
 
-        # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050913
-        xdg.portal.config.river.default = mkDefault [ "wlr" "gtk" ];
-      }
-      (import ./wayland-session.nix { inherit lib pkgs; })
-    ]);
+    (import ./wayland-session.nix {
+      inherit lib pkgs;
+      enableXWayland = cfg.xwayland.enable;
+    })
+  ]);
 
   meta.maintainers = with lib.maintainers; [ GaetanLepage ];
 }
diff --git a/nixos/modules/programs/wayland/sway.nix b/nixos/modules/programs/wayland/sway.nix
index 348e1db7cdc1c..e3e32aa7a56ab 100644
--- a/nixos/modules/programs/wayland/sway.nix
+++ b/nixos/modules/programs/wayland/sway.nix
@@ -1,88 +1,53 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
+{ config, lib, pkgs, ... }:
 
 let
   cfg = config.programs.sway;
 
-  wrapperOptions = types.submodule {
-    options =
-      let
-        mkWrapperFeature  = default: description: mkOption {
-          type = types.bool;
-          inherit default;
-          example = !default;
-          description = "Whether to make use of the ${description}";
-        };
-      in {
-        base = mkWrapperFeature true ''
-          base wrapper to execute extra session commands and prepend a
-          dbus-run-session to the sway command.
-        '';
-        gtk = mkWrapperFeature false ''
-          wrapGAppsHook wrapper to execute sway with required environment
-          variables for GTK applications.
-        '';
-    };
-  };
-
-  genFinalPackage = pkg:
-    let
-      expectedArgs = lib.naturalSort [
-        "extraSessionCommands"
-        "extraOptions"
-        "withBaseWrapper"
-        "withGtkWrapper"
-        "isNixOS"
-      ];
-      existedArgs = with lib;
-        naturalSort
-        (intersectLists expectedArgs (attrNames (functionArgs pkg.override)));
-    in if existedArgs != expectedArgs then
-      pkg
-    else
-      pkg.override {
-        extraSessionCommands = cfg.extraSessionCommands;
-        extraOptions = cfg.extraOptions;
-        withBaseWrapper = cfg.wrapperFeatures.base;
-        withGtkWrapper = cfg.wrapperFeatures.gtk;
-        isNixOS = true;
-      };
-in {
+  wayland-lib = import ./lib.nix { inherit lib; };
+in
+{
   options.programs.sway = {
-    enable = mkEnableOption ''
+    enable = lib.mkEnableOption ''
       Sway, the i3-compatible tiling Wayland compositor. You can manually launch
       Sway by executing "exec sway" on a TTY. Copy /etc/sway/config to
       ~/.config/sway/config to modify the default configuration. See
       <https://github.com/swaywm/sway/wiki> and
       "man 5 sway" for more information'';
 
-    package = mkOption {
-      type = with types; nullOr package;
-      default = pkgs.sway;
-      apply = p: if p == null then null else genFinalPackage p;
-      defaultText = literalExpression "pkgs.sway";
-      description = ''
-        Sway package to use. If the package does not contain the override arguments
-        `extraSessionCommands`, `extraOptions`, `withBaseWrapper`, `withGtkWrapper`,
-        `isNixOS`, then the module options {option}`wrapperFeatures`,
-        {option}`wrapperFeatures` and {option}`wrapperFeatures` will have no effect.
-        Set to `null` to not add any Sway package to your path. This should be done if
-        you want to use the Home Manager Sway module to install Sway.
+    package = lib.mkPackageOption pkgs "sway" {
+      nullable = true;
+      extraDescription = ''
+        If the package is not overridable with `extraSessionCommands`, `extraOptions`,
+        `withBaseWrapper`, `withGtkWrapper`, `enableXWayland` and `isNixOS`,
+        then the module options {option}`wrapperFeatures`, {option}`extraSessionCommands`,
+        {option}`extraOptions` and {option}`xwayland` will have no effect.
+
+        Set to `null` to not add any Sway package to your path.
+        This should be done if you want to use the Home Manager Sway module to install Sway.
       '';
+    } // {
+      apply = p: if p == null then null else
+        wayland-lib.genFinalPackage p {
+          extraSessionCommands = cfg.extraSessionCommands;
+          extraOptions = cfg.extraOptions;
+          withBaseWrapper = cfg.wrapperFeatures.base;
+          withGtkWrapper = cfg.wrapperFeatures.gtk;
+          enableXWayland = cfg.xwayland.enable;
+          isNixOS = true;
+        };
     };
 
-    wrapperFeatures = mkOption {
-      type = wrapperOptions;
-      default = { };
-      example = { gtk = true; };
-      description = ''
-        Attribute set of features to enable in the wrapper.
-      '';
+    wrapperFeatures = {
+      base = lib.mkEnableOption ''
+        the base wrapper to execute extra session commands and prepend a
+        dbus-run-session to the sway command'' // { default = true; };
+      gtk = lib.mkEnableOption ''
+        the wrapGAppsHook wrapper to execute sway with required environment
+        variables for GTK applications'';
     };
 
-    extraSessionCommands = mkOption {
-      type = types.lines;
+    extraSessionCommands = lib.mkOption {
+      type = lib.types.lines;
       default = "";
       example = ''
         # SDL:
@@ -102,8 +67,8 @@ in {
       '';
     };
 
-    extraOptions = mkOption {
-      type = types.listOf types.str;
+    extraOptions = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
       default = [];
       example = [
         "--verbose"
@@ -116,19 +81,16 @@ in {
       '';
     };
 
-    extraPackages = mkOption {
-      type = with types; listOf package;
-      default = with pkgs; [
-        swaylock swayidle foot dmenu wmenu
-      ];
-      defaultText = literalExpression ''
+    xwayland.enable = lib.mkEnableOption "XWayland" // { default = true; };
+
+    extraPackages = lib.mkOption {
+      type = with lib.types; listOf package;
+      default = with pkgs; [ swaylock swayidle foot dmenu wmenu ];
+      defaultText = lib.literalExpression ''
         with pkgs; [ swaylock swayidle foot dmenu wmenu ];
       '';
-      example = literalExpression ''
-        with pkgs; [
-          i3status i3status-rust
-          termite rofi light
-        ]
+      example = lib.literalExpression ''
+        with pkgs; [ i3status i3status-rust termite rofi light ]
       '';
       description = ''
         Extra packages to be installed system wide. See
@@ -137,46 +99,50 @@ in {
         for a list of useful software.
       '';
     };
-
   };
 
-  config = mkIf cfg.enable
-    (mkMerge [
-      {
-        assertions = [
-          {
-            assertion = cfg.extraSessionCommands != "" -> cfg.wrapperFeatures.base;
-            message = ''
-              The extraSessionCommands for Sway will not be run if
-              wrapperFeatures.base is disabled.
-            '';
-          }
-        ];
-
-        environment = {
-          systemPackages = optional (cfg.package != null) cfg.package ++ cfg.extraPackages;
-          # Needed for the default wallpaper:
-          pathsToLink = optionals (cfg.package != null) [ "/share/backgrounds/sway" ];
-          etc = {
-            "sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" ''
-              # Import the most important environment variables into the D-Bus and systemd
-              # user environments (e.g. required for screen sharing and Pinentry prompts):
-              exec dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
-            '';
-          } // optionalAttrs (cfg.package != null) {
-            "sway/config".source = mkOptionDefault "${cfg.package}/etc/sway/config";
-          };
+  config = lib.mkIf cfg.enable (lib.mkMerge [
+    {
+      assertions = [
+        {
+          assertion = cfg.extraSessionCommands != "" -> cfg.wrapperFeatures.base;
+          message = ''
+            The extraSessionCommands for Sway will not be run if wrapperFeatures.base is disabled.
+          '';
+        }
+      ];
+
+      environment = {
+        systemPackages = lib.optional (cfg.package != null) cfg.package ++ cfg.extraPackages;
+
+        # Needed for the default wallpaper:
+        pathsToLink = lib.optional (cfg.package != null) "/share/backgrounds/sway";
+
+        etc = {
+          "sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" ''
+            # Import the most important environment variables into the D-Bus and systemd
+            # user environments (e.g. required for screen sharing and Pinentry prompts):
+            exec dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
+          '';
+        } // lib.optionalAttrs (cfg.package != null) {
+          "sway/config".source = lib.mkOptionDefault "${cfg.package}/etc/sway/config";
         };
+      };
+
+      programs.gnupg.agent.pinentryPackage = lib.mkDefault pkgs.pinentry-gnome3;
 
-        programs.gnupg.agent.pinentryPackage = lib.mkDefault pkgs.pinentry-gnome3;
+      # To make a Sway session available if a display manager like SDDM is enabled:
+      services.displayManager.sessionPackages = lib.optional (cfg.package != null) cfg.package;
 
-        # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050913
-        xdg.portal.config.sway.default = mkDefault [ "wlr" "gtk" ];
+      # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050913
+      xdg.portal.config.sway.default = lib.mkDefault [ "wlr" "gtk" ];
+    }
 
-        # To make a Sway session available if a display manager like SDDM is enabled:
-        services.displayManager.sessionPackages = optionals (cfg.package != null) [ cfg.package ]; }
-      (import ./wayland-session.nix { inherit lib pkgs; })
-    ]);
+    (import ./wayland-session.nix {
+      inherit lib pkgs;
+      enableXWayland = cfg.xwayland.enable;
+    })
+  ]);
 
   meta.maintainers = with lib.maintainers; [ primeos colemickens ];
 }
diff --git a/nixos/modules/programs/wayland/waybar.nix b/nixos/modules/programs/wayland/waybar.nix
index ffe889504cd39..ab811994be073 100644
--- a/nixos/modules/programs/wayland/waybar.nix
+++ b/nixos/modules/programs/wayland/waybar.nix
@@ -1,17 +1,15 @@
 { lib, pkgs, config, ... }:
 
-with lib;
-
 let
   cfg = config.programs.waybar;
 in
 {
   options.programs.waybar = {
-    enable = mkEnableOption "waybar, a highly customizable Wayland bar for Sway and Wlroots based compositors";
-    package = mkPackageOption pkgs "waybar" { };
+    enable = lib.mkEnableOption "waybar, a highly customizable Wayland bar for Sway and Wlroots based compositors";
+    package = lib.mkPackageOption pkgs "waybar" { };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
     systemd.user.services.waybar = {
       description = "Waybar as systemd service";
@@ -21,5 +19,5 @@ in
     };
   };
 
-  meta.maintainers = [ maintainers.FlorianFranzen ];
+  meta.maintainers = [ lib.maintainers.FlorianFranzen ];
 }
diff --git a/nixos/modules/programs/wayland/wayland-session.nix b/nixos/modules/programs/wayland/wayland-session.nix
index da117ceae0ad6..09fb2a5f14b2e 100644
--- a/nixos/modules/programs/wayland/wayland-session.nix
+++ b/nixos/modules/programs/wayland/wayland-session.nix
@@ -1,23 +1,27 @@
-{ lib, pkgs, ... }: with lib; {
-    security = {
-      polkit.enable = true;
-      pam.services.swaylock = {};
-    };
+{
+  lib,
+  pkgs,
+  enableXWayland ? true,
+  enableWlrPortal ? true,
+}:
 
-    hardware.opengl.enable = mkDefault true;
-    fonts.enableDefaultPackages = mkDefault true;
+{
+  security = {
+    polkit.enable = true;
+    pam.services.swaylock = {};
+  };
 
-    programs = {
-      dconf.enable = mkDefault true;
-      xwayland.enable = mkDefault true;
-    };
+  hardware.graphics.enable = lib.mkDefault true;
+  fonts.enableDefaultPackages = lib.mkDefault true;
 
-    xdg.portal = {
-      enable = mkDefault true;
+  programs = {
+    dconf.enable = lib.mkDefault true;
+    xwayland.enable = lib.mkDefault enableXWayland;
+  };
 
-      extraPortals = [
-        # For screen sharing
-        pkgs.xdg-desktop-portal-wlr
-      ];
-    };
+  xdg.portal.wlr.enable = enableWlrPortal;
+
+  # Window manager only sessions (unlike DEs) don't handle XDG
+  # autostart files, so force them to run the service
+  services.xserver.desktopManager.runXdgAutostartIfNone = lib.mkDefault true;
 }
diff --git a/nixos/modules/programs/weylus.nix b/nixos/modules/programs/weylus.nix
index a47dccb95cd98..d76e2f81b2c94 100644
--- a/nixos/modules/programs/weylus.nix
+++ b/nixos/modules/programs/weylus.nix
@@ -1,15 +1,13 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.weylus;
 in
 {
-  options.programs.weylus = with types; {
-    enable = mkEnableOption "weylus, which turns your smart phone into a graphic tablet/touch screen for your computer";
+  options.programs.weylus = with lib.types; {
+    enable = lib.mkEnableOption "weylus, which turns your smart phone into a graphic tablet/touch screen for your computer";
 
-    openFirewall = mkOption {
+    openFirewall = lib.mkOption {
       type = bool;
       default = false;
       description = ''
@@ -17,7 +15,7 @@ in
       '';
     };
 
-     users = mkOption {
+     users = lib.mkOption {
       type = listOf str;
       default = [ ];
       description = ''
@@ -26,10 +24,10 @@ in
       '';
     };
 
-    package = mkPackageOption pkgs "weylus" { };
+    package = lib.mkPackageOption pkgs "weylus" { };
   };
-  config = mkIf cfg.enable {
-    networking.firewall = mkIf cfg.openFirewall {
+  config = lib.mkIf cfg.enable {
+    networking.firewall = lib.mkIf cfg.openFirewall {
       allowedTCPPorts = [ 1701 9001 ];
     };
 
diff --git a/nixos/modules/programs/wireshark.nix b/nixos/modules/programs/wireshark.nix
index 2d947154e8224..f5673e5940fea 100644
--- a/nixos/modules/programs/wireshark.nix
+++ b/nixos/modules/programs/wireshark.nix
@@ -1,28 +1,26 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.wireshark;
   wireshark = cfg.package;
 in {
   options = {
     programs.wireshark = {
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether to add Wireshark to the global environment and configure a
           setcap wrapper for 'dumpcap' for users in the 'wireshark' group.
         '';
       };
-      package = mkPackageOption pkgs "wireshark-cli" {
+      package = lib.mkPackageOption pkgs "wireshark-cli" {
         example = "wireshark";
       };
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ wireshark ];
     users.groups.wireshark = {};
 
diff --git a/nixos/modules/programs/xastir.nix b/nixos/modules/programs/xastir.nix
index d9c687289ec2c..96201eb5455d0 100644
--- a/nixos/modules/programs/xastir.nix
+++ b/nixos/modules/programs/xastir.nix
@@ -1,17 +1,15 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.xastir;
 in {
-  meta.maintainers = with maintainers; [ melling ];
+  meta.maintainers = with lib.maintainers; [ melling ];
 
   options.programs.xastir = {
-    enable = mkEnableOption "Xastir Graphical APRS client";
+    enable = lib.mkEnableOption "Xastir Graphical APRS client";
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = with pkgs; [ xastir ];
     security.wrappers.xastir = {
       source = "${pkgs.xastir}/bin/xastir";
diff --git a/nixos/modules/programs/xfconf.nix b/nixos/modules/programs/xfconf.nix
index 8e854b40e513d..f2fda3b692d37 100644
--- a/nixos/modules/programs/xfconf.nix
+++ b/nixos/modules/programs/xfconf.nix
@@ -1,21 +1,19 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let cfg = config.programs.xfconf;
 
 in {
   meta = {
-    maintainers = teams.xfce.members;
+    maintainers = lib.teams.xfce.members;
   };
 
   options = {
     programs.xfconf = {
-      enable = mkEnableOption "Xfconf, the Xfce configuration storage system";
+      enable = lib.mkEnableOption "Xfconf, the Xfce configuration storage system";
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [
       pkgs.xfce.xfconf
     ];
diff --git a/nixos/modules/programs/xfs_quota.nix b/nixos/modules/programs/xfs_quota.nix
index 8f70cc2d94163..5ca05f4dc297e 100644
--- a/nixos/modules/programs/xfs_quota.nix
+++ b/nixos/modules/programs/xfs_quota.nix
@@ -2,15 +2,13 @@
 
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
 
   cfg = config.programs.xfs_quota;
 
-  limitOptions = opts: concatStringsSep " " [
-    (optionalString (opts.sizeSoftLimit != null) "bsoft=${opts.sizeSoftLimit}")
-    (optionalString (opts.sizeHardLimit != null) "bhard=${opts.sizeHardLimit}")
+  limitOptions = opts: builtins.concatStringsSep " " [
+    (lib.optionalString (opts.sizeSoftLimit != null) "bsoft=${opts.sizeSoftLimit}")
+    (lib.optionalString (opts.sizeHardLimit != null) "bhard=${opts.sizeHardLimit}")
   ];
 
 in
@@ -22,35 +20,35 @@ in
   options = {
 
     programs.xfs_quota = {
-      projects = mkOption {
+      projects = lib.mkOption {
         default = {};
-        type = types.attrsOf (types.submodule {
+        type = lib.types.attrsOf (lib.types.submodule {
           options = {
-            id = mkOption {
-              type = types.int;
+            id = lib.mkOption {
+              type = lib.types.int;
               description = "Project ID.";
             };
 
-            fileSystem = mkOption {
-              type = types.str;
+            fileSystem = lib.mkOption {
+              type = lib.types.str;
               description = "XFS filesystem hosting the xfs_quota project.";
               default = "/";
             };
 
-            path = mkOption {
-              type = types.str;
+            path = lib.mkOption {
+              type = lib.types.str;
               description = "Project directory.";
             };
 
-            sizeSoftLimit = mkOption {
-              type = types.nullOr types.str;
+            sizeSoftLimit = lib.mkOption {
+              type = lib.types.nullOr lib.types.str;
               default = null;
               example = "30g";
               description = "Soft limit of the project size";
             };
 
-            sizeHardLimit = mkOption {
-              type = types.nullOr types.str;
+            sizeHardLimit = lib.mkOption {
+              type = lib.types.nullOr lib.types.str;
               default = null;
               example = "50g";
               description = "Hard limit of the project size.";
@@ -75,18 +73,18 @@ in
 
   ###### implementation
 
-  config = mkIf (cfg.projects != {}) {
+  config = lib.mkIf (cfg.projects != {}) {
 
     environment.etc.projects.source = pkgs.writeText "etc-project"
-      (concatStringsSep "\n" (mapAttrsToList
-        (name: opts: "${toString opts.id}:${opts.path}") cfg.projects));
+      (builtins.concatStringsSep "\n" (lib.mapAttrsToList
+        (name: opts: "${builtins.toString opts.id}:${opts.path}") cfg.projects));
 
     environment.etc.projid.source = pkgs.writeText "etc-projid"
-      (concatStringsSep "\n" (mapAttrsToList
-        (name: opts: "${name}:${toString opts.id}") cfg.projects));
+      (builtins.concatStringsSep "\n" (lib.mapAttrsToList
+        (name: opts: "${name}:${builtins.toString opts.id}") cfg.projects));
 
-    systemd.services = mapAttrs' (name: opts:
-      nameValuePair "xfs_quota-${name}" {
+    systemd.services = lib.mapAttrs' (name: opts:
+      lib.nameValuePair "xfs_quota-${name}" {
         description = "Setup xfs_quota for project ${name}";
         script = ''
           ${pkgs.xfsprogs.bin}/bin/xfs_quota -x -c 'project -s ${name}' ${opts.fileSystem}
@@ -94,7 +92,7 @@ in
         '';
 
         wantedBy = [ "multi-user.target" ];
-        after = [ ((replaceStrings [ "/" ] [ "-" ] opts.fileSystem) + ".mount") ];
+        after = [ ((builtins.replaceStrings [ "/" ] [ "-" ] opts.fileSystem) + ".mount") ];
 
         restartTriggers = [ config.environment.etc.projects.source ];
 
diff --git a/nixos/modules/programs/xonsh.nix b/nixos/modules/programs/xonsh.nix
index fefe6b456c960..6bf18d4ebd89c 100644
--- a/nixos/modules/programs/xonsh.nix
+++ b/nixos/modules/programs/xonsh.nix
@@ -2,8 +2,6 @@
 
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
 
   cfg = config.programs.xonsh;
@@ -16,29 +14,29 @@ in
 
     programs.xonsh = {
 
-      enable = mkOption {
+      enable = lib.mkOption {
         default = false;
         description = ''
           Whether to configure xonsh as an interactive shell.
         '';
-        type = types.bool;
+        type = lib.types.bool;
       };
 
-      package = mkPackageOption pkgs "xonsh" {
-        example = "xonsh.override { extraPackages = ps: [ ps.requests ]; }";
+      package = lib.mkPackageOption pkgs "xonsh" {
+        example = "xonsh.wrapper.override { extraPackages = ps: [ ps.requests ]; }";
       };
 
-      config = mkOption {
+      config = lib.mkOption {
         default = "";
         description = "Control file to customize your shell behavior.";
-        type = types.lines;
+        type = lib.types.lines;
       };
 
     };
 
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
 
     environment.etc."xonsh/xonshrc".text = ''
       # /etc/xonsh/xonshrc: DO NOT EDIT -- this file has been generated automatically.
@@ -63,17 +61,14 @@ in
               aliases['ls'] = _ls_alias
           del _ls_alias
 
-
       ${cfg.config}
     '';
 
     environment.systemPackages = [ cfg.package ];
 
-    environment.shells =
-      [ "/run/current-system/sw/bin/xonsh"
-        "${cfg.package}/bin/xonsh"
-      ];
-
+    environment.shells = [
+      "/run/current-system/sw/bin/xonsh"
+      "${lib.getExe cfg.package}"
+    ];
   };
-
 }
diff --git a/nixos/modules/programs/xss-lock.nix b/nixos/modules/programs/xss-lock.nix
index 1bb73905599f8..b818c52e1442d 100644
--- a/nixos/modules/programs/xss-lock.nix
+++ b/nixos/modules/programs/xss-lock.nix
@@ -1,26 +1,24 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let
   cfg = config.programs.xss-lock;
 in
 {
   options.programs.xss-lock = {
-    enable = mkEnableOption "xss-lock";
+    enable = lib.mkEnableOption "xss-lock";
 
-    lockerCommand = mkOption {
+    lockerCommand = lib.mkOption {
       default = "${pkgs.i3lock}/bin/i3lock";
-      defaultText = literalExpression ''"''${pkgs.i3lock}/bin/i3lock"'';
-      example = literalExpression ''"''${pkgs.i3lock-fancy}/bin/i3lock-fancy"'';
-      type = types.separatedString " ";
+      defaultText = lib.literalExpression ''"''${pkgs.i3lock}/bin/i3lock"'';
+      example = lib.literalExpression ''"''${pkgs.i3lock-fancy}/bin/i3lock-fancy"'';
+      type = lib.types.separatedString " ";
       description = "Locker to be used with xsslock";
     };
 
-    extraOptions = mkOption {
+    extraOptions = lib.mkOption {
       default = [ ];
       example = [ "--ignore-sleep" ];
-      type = types.listOf types.str;
+      type = lib.types.listOf lib.types.str;
       description = ''
         Additional command-line arguments to pass to
         {command}`xss-lock`.
@@ -28,19 +26,24 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     systemd.user.services.xss-lock = {
       description = "XSS Lock Daemon";
       wantedBy = [ "graphical-session.target" ];
       partOf = [ "graphical-session.target" ];
-      serviceConfig.ExecStart = with lib;
-        strings.concatStringsSep " " ([
+      serviceConfig.ExecStart =
+        builtins.concatStringsSep " " ([
             "${pkgs.xss-lock}/bin/xss-lock" "--session \${XDG_SESSION_ID}"
-          ] ++ (map escapeShellArg cfg.extraOptions) ++ [
+          ] ++ (builtins.map lib.escapeShellArg cfg.extraOptions) ++ [
             "--"
             cfg.lockerCommand
         ]);
       serviceConfig.Restart = "always";
     };
+
+    warnings = lib.mkIf (config.services.xserver.displayManager.startx.enable) [
+      "xss-lock service only works if a displayManager is set; it doesn't work when services.xserver.displayManager.startx.enable = true"
+    ];
+
   };
 }
diff --git a/nixos/modules/programs/xwayland.nix b/nixos/modules/programs/xwayland.nix
index 3a8080fa4c4d8..3df3dbf3783f3 100644
--- a/nixos/modules/programs/xwayland.nix
+++ b/nixos/modules/programs/xwayland.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.xwayland;
 
@@ -10,13 +8,13 @@ in
 {
   options.programs.xwayland = {
 
-    enable = mkEnableOption "Xwayland (an X server for interfacing X11 apps with the Wayland protocol)";
+    enable = lib.mkEnableOption "Xwayland (an X server for interfacing X11 apps with the Wayland protocol)";
 
-    defaultFontPath = mkOption {
-      type = types.str;
-      default = optionalString config.fonts.fontDir.enable
+    defaultFontPath = lib.mkOption {
+      type = lib.types.str;
+      default = lib.optionalString config.fonts.fontDir.enable
         "/run/current-system/sw/share/X11/fonts";
-      defaultText = literalExpression ''
+      defaultText = lib.literalExpression ''
         optionalString config.fonts.fontDir.enable "/run/current-system/sw/share/X11/fonts"
       '';
       description = ''
@@ -24,12 +22,12 @@ in
       '';
     };
 
-    package = mkOption {
-      type = types.path;
+    package = lib.mkOption {
+      type = lib.types.path;
       default = pkgs.xwayland.override (oldArgs: {
         inherit (cfg) defaultFontPath;
       });
-      defaultText = literalExpression ''
+      defaultText = lib.literalExpression ''
         pkgs.xwayland.override (oldArgs: {
           inherit (config.programs.xwayland) defaultFontPath;
         })
@@ -39,7 +37,7 @@ in
 
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
 
     # Needed by some applications for fonts and default settings
     environment.pathsToLink = [ "/share/X11" ];
diff --git a/nixos/modules/programs/yabar.nix b/nixos/modules/programs/yabar.nix
index 6e117506a2dc4..0457f8e76655b 100644
--- a/nixos/modules/programs/yabar.nix
+++ b/nixos/modules/programs/yabar.nix
@@ -1,18 +1,16 @@
 { lib, pkgs, config, ... }:
 
-with lib;
-
 let
   cfg = config.programs.yabar;
 
-  mapExtra = v: lib.concatStringsSep "\n" (mapAttrsToList (
-    key: val: "${key} = ${if (isString val) then "\"${val}\"" else "${builtins.toString val}"};"
+  mapExtra = v: lib.concatStringsSep "\n" (lib.mapAttrsToList (
+    key: val: "${key} = ${if (builtins.isString val) then "\"${val}\"" else "${builtins.toString val}"};"
   ) v);
 
-  listKeys = r: concatStringsSep "," (map (n: "\"${n}\"") (attrNames r));
+  listKeys = r: builtins.concatStringsSep "," (builtins.map (n: "\"${n}\"") (builtins.attrNames r));
 
   configFile = let
-    bars = mapAttrsToList (
+    bars = lib.mapAttrsToList (
       name: cfg: ''
         ${name}: {
           font: "${cfg.font}";
@@ -22,7 +20,7 @@ let
 
           block-list: [${listKeys cfg.indicators}]
 
-          ${concatStringsSep "\n" (mapAttrsToList (
+          ${builtins.concatStringsSep "\n" (lib.mapAttrsToList (
             name: cfg: ''
               ${name}: {
                 exec: "${cfg.exec}";
@@ -36,21 +34,21 @@ let
     ) cfg.bars;
   in pkgs.writeText "yabar.conf" ''
     bar-list = [${listKeys cfg.bars}];
-    ${concatStringsSep "\n" bars}
+    ${builtins.concatStringsSep "\n" bars}
   '';
 in
   {
     options.programs.yabar = {
-      enable = mkEnableOption "yabar, a status bar for X window managers";
+      enable = lib.mkEnableOption "yabar, a status bar for X window managers";
 
-      package = mkOption {
+      package = lib.mkOption {
         default = pkgs.yabar-unstable;
-        defaultText = literalExpression "pkgs.yabar-unstable";
-        example = literalExpression "pkgs.yabar";
-        type = types.package;
+        defaultText = lib.literalExpression "pkgs.yabar-unstable";
+        example = lib.literalExpression "pkgs.yabar";
+        type = lib.types.package;
 
         # `yabar-stable` segfaults under certain conditions.
-        apply = x: if x == pkgs.yabar-unstable then x else flip warn x ''
+        apply = x: if x == pkgs.yabar-unstable then x else lib.flip lib.warn x ''
           It's not recommended to use `yabar' with `programs.yabar', the (old) stable release
           tends to segfault under certain circumstances:
 
@@ -70,63 +68,63 @@ in
         '';
       };
 
-      bars = mkOption {
+      bars = lib.mkOption {
         default = {};
-        type = types.attrsOf(types.submodule {
+        type = lib.types.attrsOf(lib.types.submodule {
           options = {
-            font = mkOption {
+            font = lib.mkOption {
               default = "sans bold 9";
               example = "Droid Sans, FontAwesome Bold 9";
-              type = types.str;
+              type = lib.types.str;
 
               description = ''
                 The font that will be used to draw the status bar.
               '';
             };
 
-            position = mkOption {
+            position = lib.mkOption {
               default = "top";
               example = "bottom";
-              type = types.enum [ "top" "bottom" ];
+              type = lib.types.enum [ "top" "bottom" ];
 
               description = ''
                 The position where the bar will be rendered.
               '';
             };
 
-            extra = mkOption {
+            extra = lib.mkOption {
               default = {};
-              type = types.attrsOf types.str;
+              type = lib.types.attrsOf lib.types.str;
 
               description = ''
                 An attribute set which contains further attributes of a bar.
               '';
             };
 
-            indicators = mkOption {
+            indicators = lib.mkOption {
               default = {};
-              type = types.attrsOf(types.submodule {
-                options.exec = mkOption {
+              type = lib.types.attrsOf(lib.types.submodule {
+                options.exec = lib.mkOption {
                   example = "YABAR_DATE";
-                  type = types.str;
+                  type = lib.types.str;
                   description = ''
                      The type of the indicator to be executed.
                   '';
                 };
 
-                options.align = mkOption {
+                options.align = lib.mkOption {
                   default = "left";
                   example = "right";
-                  type = types.enum [ "left" "center" "right" ];
+                  type = lib.types.enum [ "left" "center" "right" ];
 
                   description = ''
                     Whether to align the indicator at the left or right of the bar.
                   '';
                 };
 
-                options.extra = mkOption {
+                options.extra = lib.mkOption {
                   default = {};
-                  type = types.attrsOf (types.either types.str types.int);
+                  type = lib.types.attrsOf (lib.types.either lib.types.str lib.types.int);
 
                   description = ''
                     An attribute set which contains further attributes of a indicator.
@@ -147,7 +145,7 @@ in
       };
     };
 
-    config = mkIf cfg.enable {
+    config = lib.mkIf cfg.enable {
       systemd.user.services.yabar = {
         description = "yabar service";
         wantedBy = [ "graphical-session.target" ];
diff --git a/nixos/modules/programs/yazi.nix b/nixos/modules/programs/yazi.nix
index 5905f2afb946d..d9f38d8d81185 100644
--- a/nixos/modules/programs/yazi.nix
+++ b/nixos/modules/programs/yazi.nix
@@ -5,7 +5,7 @@ let
 
   settingsFormat = pkgs.formats.toml { };
 
-  names = [ "yazi" "theme" "keymap" ];
+  files = [ "yazi" "theme" "keymap" ];
 in
 {
   options.programs.yazi = {
@@ -15,7 +15,7 @@ in
 
     settings = lib.mkOption {
       type = with lib.types; submodule {
-        options = lib.listToAttrs (map
+        options = (lib.listToAttrs (map
           (name: lib.nameValuePair name (lib.mkOption {
             inherit (settingsFormat) type;
             default = { };
@@ -25,26 +25,65 @@ in
               See https://yazi-rs.github.io/docs/configuration/${name}/ for documentation.
             '';
           }))
-          names);
+          files));
       };
       default = { };
       description = ''
         Configuration included in `$YAZI_CONFIG_HOME`.
       '';
     };
+
+    initLua = lib.mkOption {
+      type = with lib.types; nullOr path;
+      default = null;
+      description = ''
+        The init.lua for Yazi itself.
+      '';
+      example = lib.literalExpression "./init.lua";
+    };
+
+    plugins = lib.mkOption {
+      type = with lib.types; attrsOf (oneOf [ path package ]);
+      default = { };
+      description = ''
+        Lua plugins.
+
+        See https://yazi-rs.github.io/docs/plugins/overview/ for documentation.
+      '';
+      example = lib.literalExpression ''
+        {
+          foo = ./foo;
+          bar = pkgs.bar;
+        }
+      '';
+    };
+
+    flavors = lib.mkOption {
+      type = with lib.types; attrsOf (oneOf [ path package ]);
+      default = { };
+      description = ''
+        Pre-made themes.
+
+        See https://yazi-rs.github.io/docs/flavors/overview/ for documentation.
+      '';
+      example = lib.literalExpression ''
+        {
+          foo = ./foo;
+          bar = pkgs.bar;
+        }
+      '';
+    };
+
   };
 
   config = lib.mkIf cfg.enable {
-    environment = {
-      systemPackages = [ cfg.package ];
-      variables.YAZI_CONFIG_HOME = "/etc/yazi/";
-      etc = lib.attrsets.mergeAttrsList (map
-        (name: lib.optionalAttrs (cfg.settings.${name} != { }) {
-          "yazi/${name}.toml".source = settingsFormat.generate "${name}.toml" cfg.settings.${name};
-        })
-        names);
-    };
+    environment.systemPackages = [
+      (cfg.package.override {
+        inherit (cfg) settings initLua plugins flavors;
+      })
+    ];
   };
+
   meta = {
     maintainers = with lib.maintainers; [ linsui ];
   };
diff --git a/nixos/modules/programs/ydotool.nix b/nixos/modules/programs/ydotool.nix
new file mode 100644
index 0000000000000..643a5d369f3fc
--- /dev/null
+++ b/nixos/modules/programs/ydotool.nix
@@ -0,0 +1,92 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  cfg = config.programs.ydotool;
+in
+{
+  meta = {
+    maintainers = with lib.maintainers; [ quantenzitrone ];
+  };
+
+  options.programs.ydotool = {
+    enable = lib.mkEnableOption ''
+      ydotoold system service and {command}`ydotool` for members of
+      {option}`programs.ydotool.group`.
+    '';
+    group = lib.mkOption {
+      type = lib.types.str;
+      default = "ydotool";
+      description = ''
+        Group which users must be in to use {command}`ydotool`.
+      '';
+    };
+  };
+
+  config = let
+    runtimeDirectory = "ydotoold";
+  in lib.mkIf cfg.enable {
+    users.groups."${config.programs.ydotool.group}" = { };
+
+    systemd.services.ydotoold = {
+      description = "ydotoold - backend for ydotool";
+      wantedBy = [ "multi-user.target" ];
+      partOf = [ "multi-user.target" ];
+      serviceConfig = {
+        Group = config.programs.ydotool.group;
+        RuntimeDirectory = runtimeDirectory;
+        RuntimeDirectoryMode = "0750";
+        ExecStart = "${lib.getExe' pkgs.ydotool "ydotoold"} --socket-path=${config.environment.variables.YDOTOOL_SOCKET} --socket-perm=0660";
+
+        # hardening
+
+        ## allow access to uinput
+        DeviceAllow = [ "/dev/uinput" ];
+        DevicePolicy = "closed";
+
+        ## allow creation of unix sockets
+        RestrictAddressFamilies = [ "AF_UNIX" ];
+
+        CapabilityBoundingSet = "";
+        IPAddressDeny = "any";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        PrivateNetwork = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        ProtectUser = true;
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "~@privileged"
+          "~@resources"
+        ];
+        UMask = "0077";
+
+        # -> systemd-analyze security score 0.7 SAFE 😀
+      };
+    };
+
+    environment.variables = {
+      YDOTOOL_SOCKET = "/run/${runtimeDirectory}/socket";
+    };
+    environment.systemPackages = with pkgs; [ ydotool ];
+  };
+}
diff --git a/nixos/modules/programs/zmap.nix b/nixos/modules/programs/zmap.nix
index 827d9bedca13e..4f31d42c4add7 100644
--- a/nixos/modules/programs/zmap.nix
+++ b/nixos/modules/programs/zmap.nix
@@ -1,15 +1,13 @@
 { pkgs, config, lib, ... }:
 
-with lib;
-
 let
   cfg = config.programs.zmap;
 in {
   options.programs.zmap = {
-    enable = mkEnableOption "ZMap, a network scanner designed for Internet-wide network surveys";
+    enable = lib.mkEnableOption "ZMap, a network scanner designed for Internet-wide network surveys";
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ pkgs.zmap ];
 
     environment.etc."zmap/blacklist.conf".source = "${pkgs.zmap}/etc/zmap/blacklist.conf";
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.nix b/nixos/modules/programs/zsh/oh-my-zsh.nix
index f2a5a7560e409..2120cf1af07e1 100644
--- a/nixos/modules/programs/zsh/oh-my-zsh.nix
+++ b/nixos/modules/programs/zsh/oh-my-zsh.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
 
   cfg = config.programs.zsh.ohMyZsh;
@@ -20,7 +18,7 @@ let
 
   custom =
     if cfg.custom != null then cfg.custom
-    else if length cfg.customPkgs == 0 then null
+    else if builtins.length cfg.customPkgs == 0 then null
     else pkgs.linkFarm "oh-my-zsh-custom" [
       (mkLinkFarmEntry' "themes")
       (mkLinkFarmEntry "completions" "site-functions")
@@ -30,60 +28,60 @@ let
 in
   {
     imports = [
-      (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "enable" ] [ "programs" "zsh" "ohMyZsh" "enable" ])
-      (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "theme" ] [ "programs" "zsh" "ohMyZsh" "theme" ])
-      (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "custom" ] [ "programs" "zsh" "ohMyZsh" "custom" ])
-      (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "plugins" ] [ "programs" "zsh" "ohMyZsh" "plugins" ])
+      (lib.mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "enable" ] [ "programs" "zsh" "ohMyZsh" "enable" ])
+      (lib.mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "theme" ] [ "programs" "zsh" "ohMyZsh" "theme" ])
+      (lib.mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "custom" ] [ "programs" "zsh" "ohMyZsh" "custom" ])
+      (lib.mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "plugins" ] [ "programs" "zsh" "ohMyZsh" "plugins" ])
     ];
 
     options = {
       programs.zsh.ohMyZsh = {
-        enable = mkOption {
-          type = types.bool;
+        enable = lib.mkOption {
+          type = lib.types.bool;
           default = false;
           description = ''
             Enable oh-my-zsh.
           '';
         };
 
-        package = mkPackageOption pkgs "oh-my-zsh" { };
+        package = lib.mkPackageOption pkgs "oh-my-zsh" { };
 
-        plugins = mkOption {
+        plugins = lib.mkOption {
           default = [];
-          type = types.listOf(types.str);
+          type = lib.types.listOf(lib.types.str);
           description = ''
             List of oh-my-zsh plugins
           '';
         };
 
-        custom = mkOption {
+        custom = lib.mkOption {
           default = null;
-          type = with types; nullOr str;
+          type = with lib.types; nullOr str;
           description = ''
             Path to a custom oh-my-zsh package to override config of oh-my-zsh.
             (Can't be used along with `customPkgs`).
           '';
         };
 
-        customPkgs = mkOption {
+        customPkgs = lib.mkOption {
           default = [];
-          type = types.listOf types.package;
+          type = lib.types.listOf lib.types.package;
           description = ''
             List of custom packages that should be loaded into `oh-my-zsh`.
           '';
         };
 
-        theme = mkOption {
+        theme = lib.mkOption {
           default = "";
-          type = types.str;
+          type = lib.types.str;
           description = ''
             Name of the theme to be used by oh-my-zsh.
           '';
         };
 
-        cacheDir = mkOption {
+        cacheDir = lib.mkOption {
           default = "$HOME/.cache/oh-my-zsh";
-          type = types.str;
+          type = lib.types.str;
           description = ''
             Cache directory to be used by `oh-my-zsh`.
             Without this option it would default to the read-only nix store.
@@ -92,10 +90,10 @@ in
       };
     };
 
-    config = mkIf cfg.enable {
+    config = lib.mkIf cfg.enable {
 
       # Prevent zsh from overwriting oh-my-zsh's prompt
-      programs.zsh.promptInit = mkDefault "";
+      programs.zsh.promptInit = lib.mkDefault "";
 
       environment.systemPackages = [ cfg.package ];
 
@@ -103,19 +101,19 @@ in
         # oh-my-zsh configuration generated by NixOS
         export ZSH=${cfg.package}/share/oh-my-zsh
 
-        ${optionalString (length(cfg.plugins) > 0)
-          "plugins=(${concatStringsSep " " cfg.plugins})"
+        ${lib.optionalString (builtins.length(cfg.plugins) > 0)
+          "plugins=(${builtins.concatStringsSep " " cfg.plugins})"
         }
 
-        ${optionalString (custom != null)
+        ${lib.optionalString (custom != null)
           "ZSH_CUSTOM=\"${custom}\""
         }
 
-        ${optionalString (stringLength(cfg.theme) > 0)
+        ${lib.optionalString (builtins.stringLength(cfg.theme) > 0)
           "ZSH_THEME=\"${cfg.theme}\""
         }
 
-        ${optionalString (cfg.cacheDir != null) ''
+        ${lib.optionalString (cfg.cacheDir != null) ''
           if [[ ! -d "${cfg.cacheDir}" ]]; then
             mkdir -p "${cfg.cacheDir}"
           fi
diff --git a/nixos/modules/programs/zsh/zsh-autoenv.nix b/nixos/modules/programs/zsh/zsh-autoenv.nix
index f07fb5c24d7b3..8e0c19f1afea0 100644
--- a/nixos/modules/programs/zsh/zsh-autoenv.nix
+++ b/nixos/modules/programs/zsh/zsh-autoenv.nix
@@ -1,18 +1,16 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.zsh.zsh-autoenv;
 in {
   options = {
     programs.zsh.zsh-autoenv = {
-      enable = mkEnableOption "zsh-autoenv";
-      package = mkPackageOption pkgs "zsh-autoenv" { };
+      enable = lib.mkEnableOption "zsh-autoenv";
+      package = lib.mkPackageOption pkgs "zsh-autoenv" { };
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     programs.zsh.interactiveShellInit = ''
       source ${cfg.package}/share/zsh-autoenv/autoenv.zsh
     '';
diff --git a/nixos/modules/programs/zsh/zsh-autosuggestions.nix b/nixos/modules/programs/zsh/zsh-autosuggestions.nix
index 2e53e907d547a..e046c21025002 100644
--- a/nixos/modules/programs/zsh/zsh-autosuggestions.nix
+++ b/nixos/modules/programs/zsh/zsh-autosuggestions.nix
@@ -1,28 +1,26 @@
 { config, pkgs, lib, ... }:
 
-with lib;
-
 let
   cfg = config.programs.zsh.autosuggestions;
 in
 {
   imports = [
-    (mkRenamedOptionModule [ "programs" "zsh" "enableAutosuggestions" ] [ "programs" "zsh" "autosuggestions" "enable" ])
+    (lib.mkRenamedOptionModule [ "programs" "zsh" "enableAutosuggestions" ] [ "programs" "zsh" "autosuggestions" "enable" ])
   ];
 
   options.programs.zsh.autosuggestions = {
 
-    enable = mkEnableOption "zsh-autosuggestions";
+    enable = lib.mkEnableOption "zsh-autosuggestions";
 
-    highlightStyle = mkOption {
-      type = types.str;
+    highlightStyle = lib.mkOption {
+      type = lib.types.str;
       default = "fg=8"; # https://github.com/zsh-users/zsh-autosuggestions/tree/v0.4.3#suggestion-highlight-style
       description = "Highlight style for suggestions ({fore,back}ground color)";
       example = "fg=cyan";
     };
 
-    strategy = mkOption {
-      type = types.listOf (types.enum [ "history" "completion" "match_prev_cmd" ]);
+    strategy = lib.mkOption {
+      type = lib.types.listOf (lib.types.enum [ "history" "completion" "match_prev_cmd" ]);
       default = [ "history" ];
       description = ''
         `ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated.
@@ -37,18 +35,18 @@ in
       '';
     };
 
-    async = mkOption {
-      type = types.bool;
+    async = lib.mkOption {
+      type = lib.types.bool;
       default = true;
       description = "Whether to fetch suggestions asynchronously";
       example = false;
     };
 
-    extraConfig = mkOption {
-      type = with types; attrsOf str;
+    extraConfig = lib.mkOption {
+      type = lib.types.attrsOf lib.types.str;
       default = {};
       description = "Attribute set with additional configuration values";
-      example = literalExpression ''
+      example = lib.literalExpression ''
         {
           "ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" = "20";
         }
@@ -57,16 +55,16 @@ in
 
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
 
     programs.zsh.interactiveShellInit = ''
       source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
 
       export ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="${cfg.highlightStyle}"
-      export ZSH_AUTOSUGGEST_STRATEGY=(${concatStringsSep " " cfg.strategy})
-      ${optionalString (!cfg.async) "unset ZSH_AUTOSUGGEST_USE_ASYNC"}
+      export ZSH_AUTOSUGGEST_STRATEGY=(${builtins.concatStringsSep " " cfg.strategy})
+      ${lib.optionalString (!cfg.async) "unset ZSH_AUTOSUGGEST_USE_ASYNC"}
 
-      ${concatStringsSep "\n" (mapAttrsToList (key: value: ''export ${key}="${value}"'') cfg.extraConfig)}
+      ${builtins.concatStringsSep "\n" (lib.mapAttrsToList (key: value: ''export ${key}="${value}"'') cfg.extraConfig)}
     '';
 
   };
diff --git a/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix b/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix
index 46bc4fcb87f4f..3f70c14048c75 100644
--- a/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix
+++ b/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix
@@ -1,27 +1,25 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.programs.zsh.syntaxHighlighting;
 in
 {
   imports = [
-    (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
-    (mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "enable" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
-    (mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "highlighters" ] [ "programs" "zsh" "syntaxHighlighting" "highlighters" ])
-    (mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "patterns" ] [ "programs" "zsh" "syntaxHighlighting" "patterns" ])
+    (lib.mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
+    (lib.mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "enable" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
+    (lib.mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "highlighters" ] [ "programs" "zsh" "syntaxHighlighting" "highlighters" ])
+    (lib.mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "patterns" ] [ "programs" "zsh" "syntaxHighlighting" "patterns" ])
   ];
 
   options = {
     programs.zsh.syntaxHighlighting = {
-      enable = mkEnableOption "zsh-syntax-highlighting";
+      enable = lib.mkEnableOption "zsh-syntax-highlighting";
 
-      highlighters = mkOption {
+      highlighters = lib.mkOption {
         default = [ "main" ];
 
         # https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md
-        type = types.listOf(types.enum([
+        type = lib.types.listOf(lib.types.enum([
           "main"
           "brackets"
           "pattern"
@@ -39,11 +37,11 @@ in
         '';
       };
 
-      patterns = mkOption {
+      patterns = lib.mkOption {
         default = {};
-        type = types.attrsOf types.str;
+        type = lib.types.attrsOf lib.types.str;
 
-        example = literalExpression ''
+        example = lib.literalExpression ''
           {
             "rm -rf *" = "fg=white,bold,bg=red";
           }
@@ -56,11 +54,11 @@ in
           https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters/pattern.md
         '';
       };
-      styles = mkOption {
+      styles = lib.mkOption {
         default = {};
-        type = types.attrsOf types.str;
+        type = lib.types.attrsOf lib.types.str;
 
-        example = literalExpression ''
+        example = lib.literalExpression ''
           {
             "alias" = "fg=magenta,bold";
           }
@@ -76,30 +74,30 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
-    environment.systemPackages = with pkgs; [ zsh-syntax-highlighting ];
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.zsh-syntax-highlighting ];
 
     assertions = [
       {
-        assertion = length(attrNames cfg.patterns) > 0 -> elem "pattern" cfg.highlighters;
+        assertion = builtins.length(builtins.attrNames cfg.patterns) > 0 -> builtins.elem "pattern" cfg.highlighters;
         message = ''
           When highlighting patterns, "pattern" needs to be included in the list of highlighters.
         '';
       }
     ];
 
-    programs.zsh.interactiveShellInit = with pkgs;
+    programs.zsh.interactiveShellInit =
       lib.mkAfter (lib.concatStringsSep "\n" ([
-        "source ${zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
-      ] ++ optional (length(cfg.highlighters) > 0)
-        "ZSH_HIGHLIGHT_HIGHLIGHTERS=(${concatStringsSep " " cfg.highlighters})"
-        ++ optionals (length(attrNames cfg.patterns) > 0)
-          (mapAttrsToList (
+        "source ${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
+      ] ++ lib.optional (builtins.length(cfg.highlighters) > 0)
+        "ZSH_HIGHLIGHT_HIGHLIGHTERS=(${builtins.concatStringsSep " " cfg.highlighters})"
+        ++ lib.optionals (builtins.length(builtins.attrNames cfg.patterns) > 0)
+          (lib.mapAttrsToList (
             pattern: design:
             "ZSH_HIGHLIGHT_PATTERNS+=('${pattern}' '${design}')"
           ) cfg.patterns)
-        ++ optionals (length(attrNames cfg.styles) > 0)
-          (mapAttrsToList (
+        ++ lib.optionals (builtins.length(builtins.attrNames cfg.styles) > 0)
+          (lib.mapAttrsToList (
             styles: design:
             "ZSH_HIGHLIGHT_STYLES[${styles}]='${design}'"
           ) cfg.styles)
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index d7e300b50136a..35d2cf4610563 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -2,8 +2,6 @@
 
 { config, lib, options, pkgs, ... }:
 
-with lib;
-
 let
 
   cfge = config.environment;
@@ -11,9 +9,9 @@ let
   cfg = config.programs.zsh;
   opt = options.programs.zsh;
 
-  zshAliases = concatStringsSep "\n" (
-    mapAttrsFlatten (k: v: "alias -- ${k}=${escapeShellArg v}")
-      (filterAttrs (k: v: v != null) cfg.shellAliases)
+  zshAliases = builtins.concatStringsSep "\n" (
+    lib.mapAttrsFlatten (k: v: "alias -- ${k}=${lib.escapeShellArg v}")
+      (lib.filterAttrs (k: v: v != null) cfg.shellAliases)
   );
 
   zshStartupNotes = ''
@@ -42,7 +40,7 @@ in
 
     programs.zsh = {
 
-      enable = mkOption {
+      enable = lib.mkOption {
         default = false;
         description = ''
           Whether to configure zsh as an interactive shell. To enable zsh for
@@ -50,43 +48,43 @@ in
           option for that user. To enable zsh system-wide use the
           {option}`users.defaultUserShell` option.
         '';
-        type = types.bool;
+        type = lib.types.bool;
       };
 
-      shellAliases = mkOption {
+      shellAliases = lib.mkOption {
         default = { };
         description = ''
           Set of aliases for zsh shell, which overrides {option}`environment.shellAliases`.
           See {option}`environment.shellAliases` for an option format description.
         '';
-        type = with types; attrsOf (nullOr (either str path));
+        type = with lib.types; attrsOf (nullOr (either str path));
       };
 
-      shellInit = mkOption {
+      shellInit = lib.mkOption {
         default = "";
         description = ''
           Shell script code called during zsh shell initialisation.
         '';
-        type = types.lines;
+        type = lib.types.lines;
       };
 
-      loginShellInit = mkOption {
+      loginShellInit = lib.mkOption {
         default = "";
         description = ''
           Shell script code called during zsh login shell initialisation.
         '';
-        type = types.lines;
+        type = lib.types.lines;
       };
 
-      interactiveShellInit = mkOption {
+      interactiveShellInit = lib.mkOption {
         default = "";
         description = ''
           Shell script code called during interactive zsh shell initialisation.
         '';
-        type = types.lines;
+        type = lib.types.lines;
       };
 
-      promptInit = mkOption {
+      promptInit = lib.mkOption {
         default = ''
           # Note that to manually override this in ~/.zshrc you should run `prompt off`
           # before setting your PS1 and etc. Otherwise this will likely to interact with
@@ -97,27 +95,27 @@ in
         description = ''
           Shell script code used to initialise the zsh prompt.
         '';
-        type = types.lines;
+        type = lib.types.lines;
       };
 
-      histSize = mkOption {
+      histSize = lib.mkOption {
         default = 2000;
         description = ''
           Change history size.
         '';
-        type = types.int;
+        type = lib.types.int;
       };
 
-      histFile = mkOption {
+      histFile = lib.mkOption {
         default = "$HOME/.zsh_history";
         description = ''
           Change history file.
         '';
-        type = types.str;
+        type = lib.types.str;
       };
 
-      setOptions = mkOption {
-        type = types.listOf types.str;
+      setOptions = lib.mkOption {
+        type = lib.types.listOf lib.types.str;
         default = [
           "HIST_IGNORE_DUPS"
           "SHARE_HISTORY"
@@ -130,25 +128,25 @@ in
         '';
       };
 
-      enableCompletion = mkOption {
+      enableCompletion = lib.mkOption {
         default = true;
         description = ''
           Enable zsh completion for all interactive zsh shells.
         '';
-        type = types.bool;
+        type = lib.types.bool;
       };
 
-      enableBashCompletion = mkOption {
+      enableBashCompletion = lib.mkOption {
         default = false;
         description = ''
           Enable compatibility with bash's programmable completion system.
         '';
-        type = types.bool;
+        type = lib.types.bool;
       };
 
-      enableGlobalCompInit = mkOption {
+      enableGlobalCompInit = lib.mkOption {
         default = cfg.enableCompletion;
-        defaultText = literalExpression "config.${opt.enableCompletion}";
+        defaultText = lib.literalExpression "config.${opt.enableCompletion}";
         description = ''
           Enable execution of compinit call for all interactive zsh shells.
 
@@ -156,24 +154,24 @@ in
           `fpath` and a custom `compinit`
           call in the local config is required.
         '';
-        type = types.bool;
+        type = lib.types.bool;
       };
 
-      enableLsColors = mkOption {
+      enableLsColors = lib.mkOption {
         default = true;
         description = ''
           Enable extra colors in directory listings (used by `ls` and `tree`).
         '';
-        type = types.bool;
+        type = lib.types.bool;
       };
 
     };
 
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
 
-    programs.zsh.shellAliases = mapAttrs (name: mkDefault) cfge.shellAliases;
+    programs.zsh.shellAliases = builtins.mapAttrs (name: lib.mkDefault) cfge.shellAliases;
 
     environment.etc.zshenv.text =
       ''
@@ -239,9 +237,9 @@ in
         if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi
         __ETC_ZSHRC_SOURCED=1
 
-        ${optionalString (cfg.setOptions != []) ''
+        ${lib.optionalString (cfg.setOptions != []) ''
           # Set zsh options.
-          setopt ${concatStringsSep " " cfg.setOptions}
+          setopt ${builtins.concatStringsSep " " cfg.setOptions}
         ''}
 
         # Alternative method of determining short and full hostname.
@@ -249,19 +247,19 @@ in
 
         # Setup command line history.
         # Don't export these, otherwise other shells (bash) will try to use same HISTFILE.
-        SAVEHIST=${toString cfg.histSize}
-        HISTSIZE=${toString cfg.histSize}
+        SAVEHIST=${builtins.toString cfg.histSize}
+        HISTSIZE=${builtins.toString cfg.histSize}
         HISTFILE=${cfg.histFile}
 
         # Configure sane keyboard defaults.
         . /etc/zinputrc
 
-        ${optionalString cfg.enableGlobalCompInit ''
+        ${lib.optionalString cfg.enableGlobalCompInit ''
           # Enable autocompletion.
           autoload -U compinit && compinit
         ''}
 
-        ${optionalString cfg.enableBashCompletion ''
+        ${lib.optionalString cfg.enableBashCompletion ''
           # Enable compatibility with bash's completion system.
           autoload -U bashcompinit && bashcompinit
         ''}
@@ -271,7 +269,7 @@ in
 
         ${cfg.interactiveShellInit}
 
-        ${optionalString cfg.enableLsColors ''
+        ${lib.optionalString cfg.enableLsColors ''
           # Extra colors for directory listings.
           eval "$(${pkgs.coreutils}/bin/dircolors -b)"
         ''}
@@ -302,11 +300,11 @@ in
     environment.etc.zinputrc.text = builtins.readFile ./zinputrc;
 
     environment.systemPackages = [ pkgs.zsh ]
-      ++ optional cfg.enableCompletion pkgs.nix-zsh-completions;
+      ++ lib.optional cfg.enableCompletion pkgs.nix-zsh-completions;
 
-    environment.pathsToLink = optional cfg.enableCompletion "/share/zsh";
+    environment.pathsToLink = lib.optional cfg.enableCompletion "/share/zsh";
 
-    #users.defaultUserShell = mkDefault "/run/current-system/sw/bin/zsh";
+    #users.defaultUserShell = lib.mkDefault "/run/current-system/sw/bin/zsh";
 
     environment.shells =
       [
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 01985995a651d..d4661a19188c8 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -40,12 +40,16 @@ in
     (mkRemovedOptionModule [ "networking" "vpnc" ] "Use environment.etc.\"vpnc/service.conf\" instead.")
     (mkRemovedOptionModule [ "networking" "wicd" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "programs" "gnome-documents" ] "The corresponding package was removed from nixpkgs.")
+    (mkRemovedOptionModule [ "programs" "pantheon-tweaks" ] ''
+      pantheon-tweaks is no longer a switchboard plugin but an independent app,
+      adding the package to environment.systemPackages is sufficient.
+    '')
     (mkRemovedOptionModule [ "programs" "tilp2" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "programs" "way-cooler" ] ("way-cooler is abandoned by its author: " +
       "https://way-cooler.org/blog/2020/01/09/way-cooler-post-mortem.html"))
     (mkRemovedOptionModule [ "security" "hideProcessInformation" ] ''
-        The hidepid module was removed, since the underlying machinery
-        is broken when using cgroups-v2.
+      The hidepid module was removed, since the underlying machinery
+      is broken when using cgroups-v2.
     '')
     (mkRemovedOptionModule [ "services" "baget" "enable" ] "The baget module was removed due to the upstream package being unmaintained.")
     (mkRemovedOptionModule [ "services" "beegfs" ] "The BeeGFS module has been removed")
diff --git a/nixos/modules/security/acme/default.nix b/nixos/modules/security/acme/default.nix
index 5ffafdc37fefb..83581d02840ed 100644
--- a/nixos/modules/security/acme/default.nix
+++ b/nixos/modules/security/acme/default.nix
@@ -545,7 +545,7 @@ let
       };
 
       server = mkOption {
-        type = types.str;
+        type = types.nullOr types.str;
         inherit (defaultAndText "server" "https://acme-v02.api.letsencrypt.org/directory") default defaultText;
         example = "https://acme-staging-v02.api.letsencrypt.org/directory";
         description = ''
diff --git a/nixos/modules/security/ipa.nix b/nixos/modules/security/ipa.nix
index 543b1abfa672c..e746ca75724a1 100644
--- a/nixos/modules/security/ipa.nix
+++ b/nixos/modules/security/ipa.nix
@@ -85,6 +85,18 @@ in {
         description = "Whether to cache credentials.";
       };
 
+      ipaHostname = mkOption {
+        type = types.str;
+        example = "myworkstation.example.com";
+        default = if config.networking.domain != null then config.networking.fqdn
+                  else "${config.networking.hostName}.${cfg.domain}";
+        defaultText = literalExpression ''
+          if config.networking.domain != null then config.networking.fqdn
+          else "''${networking.hostName}.''${security.ipa.domain}"
+        '';
+        description = "Fully-qualified hostname used to identify this host in the IPA domain.";
+      };
+
       ifpAllowedUids = mkOption {
         type = types.listOf types.str;
         default = ["root"];
@@ -218,7 +230,7 @@ in {
 
       ipa_domain = ${cfg.domain}
       ipa_server = _srv_, ${cfg.server}
-      ipa_hostname = ${config.networking.hostName}.${cfg.domain}
+      ipa_hostname = ${cfg.ipaHostname}
 
       cache_credentials = ${pyBool cfg.cacheCredentials}
       krb5_store_password_if_offline = ${pyBool cfg.offlinePasswords}
@@ -232,7 +244,6 @@ in {
       ldap_user_extra_attrs = mail:mail, sn:sn, givenname:givenname, telephoneNumber:telephoneNumber, lock:nsaccountlock
 
       [sssd]
-      debug_level = 65510
       services = nss, sudo, pam, ssh, ifp
       domains = ${cfg.domain}
 
@@ -244,7 +255,6 @@ in {
       pam_verbosity = 3
 
       [sudo]
-      debug_level = 65510
 
       [autofs]
 
diff --git a/nixos/modules/security/krb5/default.nix b/nixos/modules/security/krb5/default.nix
index 78426c07cbc98..6714c41d8a07c 100644
--- a/nixos/modules/security/krb5/default.nix
+++ b/nixos/modules/security/krb5/default.nix
@@ -77,8 +77,22 @@ in {
     };
   };
 
-  config = mkIf cfg.enable {
-    environment = {
+  config = {
+    assertions = mkIf (cfg.enable || config.services.kerberos_server.enable) [(let
+      implementation = cfg.package.passthru.implementation or "<NOT SET>";
+    in {
+      assertion = lib.elem implementation [ "krb5" "heimdal" ];
+      message = ''
+        `security.krb5.package` must be one of:
+
+          - krb5
+          - heimdal
+
+        Currently chosen implementation: ${implementation}
+      '';
+    })];
+
+    environment = mkIf cfg.enable {
       systemPackages = [ cfg.package ];
       etc."krb5.conf".source = format.generate "krb5.conf" cfg.settings;
     };
diff --git a/nixos/modules/security/krb5/krb5-conf-format.nix b/nixos/modules/security/krb5/krb5-conf-format.nix
index 5a6bbed9fd188..3e5e64ae0cb04 100644
--- a/nixos/modules/security/krb5/krb5-conf-format.nix
+++ b/nixos/modules/security/krb5/krb5-conf-format.nix
@@ -7,17 +7,61 @@
 let
   inherit (lib) boolToString concatMapStringsSep concatStringsSep filter
     isAttrs isBool isList mapAttrsToList mkOption singleton splitString;
-  inherit (lib.types) attrsOf bool coercedTo either int listOf oneOf path
-    str submodule;
+  inherit (lib.types) attrsOf bool coercedTo either enum int listOf oneOf
+    path str submodule;
 in
-{ }: {
-  type = let
-    section = attrsOf relation;
-    relation = either (attrsOf value) value;
+{
+  enableKdcACLEntries ? false
+}: rec {
+  sectionType = let
+    relation = oneOf [
+      (listOf (attrsOf value))
+      (attrsOf value)
+      value
+    ];
     value = either (listOf atom) atom;
     atom = oneOf [int str bool];
+  in attrsOf relation;
+
+  type = let
+    aclEntry = submodule {
+      options = {
+        principal = mkOption {
+          type = str;
+          description = "Which principal the rule applies to";
+        };
+        access = mkOption {
+          type = either
+            (listOf (enum ["add" "cpw" "delete" "get" "list" "modify"]))
+            (enum ["all"]);
+          default = "all";
+          description = "The changes the principal is allowed to make.";
+        };
+        target = mkOption {
+          type = str;
+          default = "*";
+          description = "The principals that 'access' applies to.";
+        };
+      };
+    };
+
+    realm = submodule ({ name, ... }: {
+      freeformType = sectionType;
+      options = {
+        acl = mkOption {
+          type = listOf aclEntry;
+          default = [
+            { principal = "*/admin"; access = "all"; }
+            { principal = "admin"; access = "all"; }
+          ];
+          description = ''
+            The privileges granted to a user.
+          '';
+        };
+      };
+    });
   in submodule {
-    freeformType = attrsOf section;
+    freeformType = attrsOf sectionType;
     options = {
       include = mkOption {
         default = [ ];
@@ -40,7 +84,17 @@ in
         '';
         type = coercedTo path singleton (listOf path);
       };
-    };
+
+    }
+    //
+    (lib.optionalAttrs enableKdcACLEntries {
+      realms = mkOption {
+        type = attrsOf realm;
+        description = ''
+          The realm(s) to serve keys for.
+        '';
+      };
+    });
   };
 
   generate = let
@@ -71,6 +125,9 @@ in
         ${name} = {
         ${indent (concatStringsSep "\n" (mapAttrsToList formatValue relation))}
         }''
+      else if isList relation
+      then
+        concatMapStringsSep "\n" (formatRelation name) relation
       else formatValue name relation;
 
     formatValue = name: value:
diff --git a/nixos/modules/security/systemd-confinement.nix b/nixos/modules/security/systemd-confinement.nix
index 0304749b8d109..041c900338864 100644
--- a/nixos/modules/security/systemd-confinement.nix
+++ b/nixos/modules/security/systemd-confinement.nix
@@ -79,13 +79,20 @@ in {
         description = ''
           The value `full-apivfs` (the default) sets up
           private {file}`/dev`, {file}`/proc`,
-          {file}`/sys` and {file}`/tmp` file systems in a separate user
-          name space.
+          {file}`/sys`, {file}`/tmp` and {file}`/var/tmp` file systems
+          in a separate user name space.
 
           If this is set to `chroot-only`, only the file
           system name space is set up along with the call to
           {manpage}`chroot(2)`.
 
+          In all cases, unless `serviceConfig.PrivateTmp=true` is set,
+          both {file}`/tmp` and {file}`/var/tmp` paths are added to `InaccessiblePaths=`.
+          This is to overcome options like `DynamicUser=true`
+          implying `PrivateTmp=true` without letting it being turned off.
+          Beware however that giving processes the `CAP_SYS_ADMIN` and `@mount` privileges
+          can let them undo the effects of `InaccessiblePaths=`.
+
           ::: {.note}
           This doesn't cover network namespaces and is solely for
           file system level isolation.
@@ -98,8 +105,12 @@ in {
         wantsAPIVFS = lib.mkDefault (config.confinement.mode == "full-apivfs");
       in lib.mkIf config.confinement.enable {
         serviceConfig = {
-          RootDirectory = "/var/empty";
-          TemporaryFileSystem = "/";
+          ReadOnlyPaths = [ "+/" ];
+          RuntimeDirectory = [ "confinement/${mkPathSafeName name}" ];
+          RootDirectory = "/run/confinement/${mkPathSafeName name}";
+          InaccessiblePaths = [
+            "-+/run/confinement/${mkPathSafeName name}"
+          ];
           PrivateMounts = lib.mkDefault true;
 
           # https://github.com/NixOS/nixpkgs/issues/14645 is a future attempt
@@ -148,16 +159,6 @@ in {
               + " Please either define a separate service or find a way to run"
               + " commands other than ExecStart within the chroot.";
     }
-    { assertion = !cfg.serviceConfig.DynamicUser or false;
-      message = "${whatOpt "DynamicUser"}. Please create a dedicated user via"
-              + " the 'users.users' option instead as this combination is"
-              + " currently not supported.";
-    }
-    { assertion = cfg.serviceConfig ? ProtectSystem -> cfg.serviceConfig.ProtectSystem == false;
-      message = "${whatOpt "ProtectSystem"}. ProtectSystem is not compatible"
-              + " with service confinement as it fails to remount /usr within"
-              + " our chroot. Please disable the option.";
-    }
   ]) config.systemd.services);
 
   config.systemd.packages = lib.concatLists (lib.mapAttrsToList (name: cfg: let
@@ -183,6 +184,13 @@ in {
         echo "BindReadOnlyPaths=$realprog:/bin/sh" >> "$serviceFile"
       ''}
 
+      # If DynamicUser= is enabled, PrivateTmp=true is implied (and cannot be turned off).
+      # so disable them unless PrivateTmp=true is explicitely set.
+      ${lib.optionalString (!cfg.serviceConfig.PrivateTmp) ''
+        echo "InaccessiblePaths=-+/tmp" >> "$serviceFile"
+        echo "InaccessiblePaths=-+/var/tmp" >> "$serviceFile"
+      ''}
+
       while read storePath; do
         if [ -L "$storePath" ]; then
           # Currently, systemd can't cope with symlinks in Bind(ReadOnly)Paths,
diff --git a/nixos/modules/services/admin/docuum.nix b/nixos/modules/services/admin/docuum.nix
index 6f6cd4e027337..51a21740b276a 100644
--- a/nixos/modules/services/admin/docuum.nix
+++ b/nixos/modules/services/admin/docuum.nix
@@ -2,7 +2,7 @@
 
 let
   cfg = config.services.docuum;
-  inherit (lib) mkIf mkEnableOption mkOption getExe types;
+  inherit (lib) mkIf mkEnableOption mkOption getExe types optionals concatMap;
 in
 {
   options.services.docuum = {
@@ -14,6 +14,27 @@ in
       default = "10 GB";
       example = "50%";
     };
+
+    minAge = mkOption {
+      description = "Sets the minimum age of images to be considered for deletion.";
+      type = types.nullOr types.str;
+      default = null;
+      example = "1d";
+    };
+
+    keep = mkOption {
+      description = "Prevents deletion of images for which repository:tag matches the specified regex.";
+      type = types.listOf types.str;
+      default = [];
+      example = [ "^my-image" ];
+    };
+
+    deletionChunkSize = mkOption {
+      description = "Removes specified quantity of images at a time.";
+      type = types.int;
+      default = 1;
+      example = 10;
+    };
   };
 
   config = mkIf cfg.enable {
@@ -35,10 +56,13 @@ in
         DynamicUser = true;
         StateDirectory = "docuum";
         SupplementaryGroups = [ "docker" ];
-        ExecStart = utils.escapeSystemdExecArgs [
+        ExecStart = utils.escapeSystemdExecArgs ([
           (getExe pkgs.docuum)
           "--threshold" cfg.threshold
-        ];
+          "--deletion-chunk-size" cfg.deletionChunkSize
+        ] ++ (concatMap (keep: [ "--keep" keep ]) cfg.keep)
+          ++ (optionals (cfg.minAge != null) [ "--min-age" cfg.minAge ])
+        );
       };
     };
   };
diff --git a/nixos/modules/services/admin/meshcentral.nix b/nixos/modules/services/admin/meshcentral.nix
index 25779e01123ec..6e0801e1c0894 100644
--- a/nixos/modules/services/admin/meshcentral.nix
+++ b/nixos/modules/services/admin/meshcentral.nix
@@ -42,5 +42,5 @@ in with lib; {
       };
     };
   };
-  meta.maintainers = [ maintainers.lheckemann ];
+  meta.maintainers = [ ];
 }
diff --git a/nixos/modules/services/admin/pgadmin.nix b/nixos/modules/services/admin/pgadmin.nix
index ead0c3c6c9a34..b3dd3c78874c2 100644
--- a/nixos/modules/services/admin/pgadmin.nix
+++ b/nixos/modules/services/admin/pgadmin.nix
@@ -152,7 +152,8 @@ in
         # Check here for password length to prevent pgadmin from starting
         # and presenting a hard to find error message
         # see https://github.com/NixOS/nixpkgs/issues/270624
-        PW_LENGTH=$(wc -m < ${escapeShellArg cfg.initialPasswordFile})
+        PW_FILE="$CREDENTIALS_DIRECTORY/initial_password"
+        PW_LENGTH=$(wc -m < "$PW_FILE")
         if [ $PW_LENGTH -lt ${toString cfg.minimumPasswordLength} ]; then
             echo "Password must be at least ${toString cfg.minimumPasswordLength} characters long"
             exit 1
@@ -162,7 +163,7 @@ in
           echo ${escapeShellArg cfg.initialEmail}
 
           # file might not contain newline. echo hack fixes that.
-          PW=$(cat ${escapeShellArg cfg.initialPasswordFile})
+          PW=$(cat "$PW_FILE")
 
           # Password:
           echo "$PW"
@@ -181,6 +182,8 @@ in
         LogsDirectory = "pgadmin";
         StateDirectory = "pgadmin";
         ExecStart = "${cfg.package}/bin/pgadmin4";
+        LoadCredential = [ "initial_password:${cfg.initialPasswordFile}" ]
+          ++ optional cfg.emailServer.enable "email_password:${cfg.emailServer.passwordFile}";
       };
     };
 
@@ -193,7 +196,8 @@ in
 
     environment.etc."pgadmin/config_system.py" = {
       text = lib.optionalString cfg.emailServer.enable ''
-        with open("${cfg.emailServer.passwordFile}") as f:
+        import os
+        with open(os.path.join(os.environ['CREDENTIALS_DIRECTORY'], 'email_password')) as f:
           pw = f.read()
         MAIL_PASSWORD = pw
       '' + formatPy cfg.settings;
diff --git a/nixos/modules/services/audio/alsa.nix b/nixos/modules/services/audio/alsa.nix
index e53da4b64e7bc..b002cb1274ac3 100644
--- a/nixos/modules/services/audio/alsa.nix
+++ b/nixos/modules/services/audio/alsa.nix
@@ -106,7 +106,8 @@ in
         serviceConfig = {
           Type = "oneshot";
           RemainAfterExit = true;
-          ExecStart = "${pkgs.coreutils}/bin/mkdir -p /var/lib/alsa";
+          ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /var/lib/alsa";
+          ExecStart = "${alsa-utils}/sbin/alsactl restore --ignore";
           ExecStop = "${alsa-utils}/sbin/alsactl store --ignore";
         };
       };
diff --git a/nixos/modules/services/audio/mopidy.nix b/nixos/modules/services/audio/mopidy.nix
index 1d6c45b64a16c..198ca74359dc1 100644
--- a/nixos/modules/services/audio/mopidy.nix
+++ b/nixos/modules/services/audio/mopidy.nix
@@ -78,6 +78,7 @@ in {
     systemd.services.mopidy = {
       wantedBy = [ "multi-user.target" ];
       after = [ "network-online.target" "sound.target" ];
+      wants = [ "network-online.target" ];
       description = "mopidy music player daemon";
       serviceConfig = {
         ExecStart = "${mopidyEnv}/bin/mopidy --config ${concatStringsSep ":" ([mopidyConf] ++ cfg.extraConfigFiles)}";
diff --git a/nixos/modules/services/audio/navidrome.nix b/nixos/modules/services/audio/navidrome.nix
index a5a7e805e3d61..06d2d174a4df3 100644
--- a/nixos/modules/services/audio/navidrome.nix
+++ b/nixos/modules/services/audio/navidrome.nix
@@ -1,11 +1,27 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
 
 let
+  inherit (lib)
+    mkEnableOption
+    mkPackageOption
+    mkOption
+    maintainers
+    ;
+  inherit (lib.types)
+    bool
+    port
+    str
+    submodule
+    ;
   cfg = config.services.navidrome;
-  settingsFormat = pkgs.formats.json {};
-in {
+  settingsFormat = pkgs.formats.json { };
+in
+{
   options = {
     services.navidrome = {
 
@@ -13,72 +29,133 @@ in {
 
       package = mkPackageOption pkgs "navidrome" { };
 
-      settings = mkOption rec {
-        type = settingsFormat.type;
-        apply = recursiveUpdate default;
-        default = {
-          Address = "127.0.0.1";
-          Port = 4533;
+      settings = mkOption {
+        type = submodule {
+          freeformType = settingsFormat.type;
+
+          options = {
+            Address = mkOption {
+              default = "127.0.0.1";
+              description = "Address to run Navidrome on.";
+              type = str;
+            };
+
+            Port = mkOption {
+              default = 4533;
+              description = "Port to run Navidrome on.";
+              type = port;
+            };
+          };
         };
+        default = { };
         example = {
           MusicFolder = "/mnt/music";
         };
-        description = ''
-          Configuration for Navidrome, see <https://www.navidrome.org/docs/usage/configuration-options/> for supported values.
-        '';
+        description = "Configuration for Navidrome, see <https://www.navidrome.org/docs/usage/configuration-options/> for supported values.";
+      };
+
+      user = mkOption {
+        type = str;
+        default = "navidrome";
+        description = "User under which Navidrome runs.";
+      };
+
+      group = mkOption {
+        type = str;
+        default = "navidrome";
+        description = "Group under which Navidrome runs.";
       };
 
       openFirewall = mkOption {
-        type = types.bool;
+        type = bool;
         default = false;
         description = "Whether to open the TCP port in the firewall";
       };
     };
   };
 
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [cfg.settings.Port];
+  config =
+    let
+      inherit (lib) mkIf optional getExe;
+      WorkingDirectory = "/var/lib/navidrome";
+    in
+    mkIf cfg.enable {
+      systemd = {
+        tmpfiles.settings.navidromeDirs = {
+          "${cfg.settings.DataFolder or WorkingDirectory}"."d" = {
+            mode = "700";
+            inherit (cfg) user group;
+          };
+          "${cfg.settings.CacheFolder or (WorkingDirectory + "/cache")}"."d" = {
+            mode = "700";
+            inherit (cfg) user group;
+          };
+        };
+        services.navidrome = {
+          description = "Navidrome Media Server";
+          after = [ "network.target" ];
+          wantedBy = [ "multi-user.target" ];
+          serviceConfig = {
+            ExecStart = ''
+              ${getExe cfg.package} --configfile ${settingsFormat.generate "navidrome.json" cfg.settings}
+            '';
+            User = cfg.user;
+            Group = cfg.group;
+            StateDirectory = "navidrome";
+            inherit WorkingDirectory;
+            RuntimeDirectory = "navidrome";
+            RootDirectory = "/run/navidrome";
+            ReadWritePaths = "";
+            BindPaths =
+              optional (cfg.settings ? DataFolder) cfg.settings.DataFolder
+              ++ optional (cfg.settings ? CacheFolder) cfg.settings.CacheFolder;
+            BindReadOnlyPaths = [
+              # navidrome uses online services to download additional album metadata / covers
+              "${
+                config.environment.etc."ssl/certs/ca-certificates.crt".source
+              }:/etc/ssl/certs/ca-certificates.crt"
+              builtins.storeDir
+              "/etc"
+            ] ++ optional (cfg.settings ? MusicFolder) cfg.settings.MusicFolder;
+            CapabilityBoundingSet = "";
+            RestrictAddressFamilies = [
+              "AF_UNIX"
+              "AF_INET"
+              "AF_INET6"
+            ];
+            RestrictNamespaces = true;
+            PrivateDevices = true;
+            PrivateUsers = true;
+            ProtectClock = true;
+            ProtectControlGroups = true;
+            ProtectHome = true;
+            ProtectKernelLogs = true;
+            ProtectKernelModules = true;
+            ProtectKernelTunables = true;
+            SystemCallArchitectures = "native";
+            SystemCallFilter = [
+              "@system-service"
+              "~@privileged"
+            ];
+            RestrictRealtime = true;
+            LockPersonality = true;
+            MemoryDenyWriteExecute = true;
+            UMask = "0066";
+            ProtectHostname = true;
+          };
+        };
+      };
 
-    systemd.services.navidrome = {
-      description = "Navidrome Media Server";
-      after = [ "network.target" ];
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        ExecStart = ''
-          ${cfg.package}/bin/navidrome --configfile ${settingsFormat.generate "navidrome.json" cfg.settings}
-        '';
-        DynamicUser = true;
-        StateDirectory = "navidrome";
-        WorkingDirectory = "/var/lib/navidrome";
-        RuntimeDirectory = "navidrome";
-        RootDirectory = "/run/navidrome";
-        ReadWritePaths = "";
-        BindPaths = lib.optional (cfg.settings ? DataFolder) cfg.settings.DataFolder;
-        BindReadOnlyPaths = [
-          # navidrome uses online services to download additional album metadata / covers
-          "${config.environment.etc."ssl/certs/ca-certificates.crt".source}:/etc/ssl/certs/ca-certificates.crt"
-          builtins.storeDir
-          "/etc"
-        ] ++ lib.optional (cfg.settings ? MusicFolder) cfg.settings.MusicFolder;
-        CapabilityBoundingSet = "";
-        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
-        RestrictNamespaces = true;
-        PrivateDevices = true;
-        PrivateUsers = true;
-        ProtectClock = true;
-        ProtectControlGroups = true;
-        ProtectHome = true;
-        ProtectKernelLogs = true;
-        ProtectKernelModules = true;
-        ProtectKernelTunables = true;
-        SystemCallArchitectures = "native";
-        SystemCallFilter = [ "@system-service" "~@privileged" ];
-        RestrictRealtime = true;
-        LockPersonality = true;
-        MemoryDenyWriteExecute = true;
-        UMask = "0066";
-        ProtectHostname = true;
+      users.users = mkIf (cfg.user == "navidrome") {
+        navidrome = {
+          inherit (cfg) group;
+          isSystemUser = true;
+        };
       };
+
+      users.groups = mkIf (cfg.group == "navidrome") { navidrome = { }; };
+
+      networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.Port ];
     };
-  };
+  meta.maintainers = with maintainers; [ fsnkty ];
 }
diff --git a/nixos/modules/services/backup/borgbackup.nix b/nixos/modules/services/backup/borgbackup.nix
index 570f8931bd9e9..a3c0715c9e607 100644
--- a/nixos/modules/services/backup/borgbackup.nix
+++ b/nixos/modules/services/backup/borgbackup.nix
@@ -33,13 +33,24 @@ let
     }
     trap on_exit EXIT
 
+    borgWrapper () {
+      local result
+      borg "$@" && result=$? || result=$?
+      if [[ -z "${toString cfg.failOnWarnings}" ]] && [[ "$result" == 1 ]]; then
+        echo "ignoring warning return value 1"
+        return 0
+      else
+        return "$result"
+      fi
+    }
+
     archiveName="${optionalString (cfg.archiveBaseName != null) (cfg.archiveBaseName + "-")}$(date ${cfg.dateFormat})"
     archiveSuffix="${optionalString cfg.appendFailedSuffix ".failed"}"
     ${cfg.preHook}
   '' + optionalString cfg.doInit ''
     # Run borg init if the repo doesn't exist yet
-    if ! borg list $extraArgs > /dev/null; then
-      borg init $extraArgs \
+    if ! borgWrapper list $extraArgs > /dev/null; then
+      borgWrapper init $extraArgs \
         --encryption ${cfg.encryption.mode} \
         $extraInitArgs
       ${cfg.postInit}
@@ -48,7 +59,7 @@ let
     (
       set -o pipefail
       ${optionalString (cfg.dumpCommand != null) ''${escapeShellArg cfg.dumpCommand} | \''}
-      borg create $extraArgs \
+      borgWrapper create $extraArgs \
         --compression ${cfg.compression} \
         --exclude-from ${mkExcludeFile cfg} \
         --patterns-from ${mkPatternsFile cfg} \
@@ -57,16 +68,16 @@ let
         ${if cfg.paths == null then "-" else escapeShellArgs cfg.paths}
     )
   '' + optionalString cfg.appendFailedSuffix ''
-    borg rename $extraArgs \
+    borgWrapper rename $extraArgs \
       "::$archiveName$archiveSuffix" "$archiveName"
   '' + ''
     ${cfg.postCreate}
   '' + optionalString (cfg.prune.keep != { }) ''
-    borg prune $extraArgs \
+    borgWrapper prune $extraArgs \
       ${mkKeepArgs cfg} \
       ${optionalString (cfg.prune.prefix != null) "--glob-archives ${escapeShellArg "${cfg.prune.prefix}*"}"} \
       $extraPruneArgs
-    borg compact $extraArgs $extraCompactArgs
+    borgWrapper compact $extraArgs $extraCompactArgs
     ${cfg.postPrune}
   '');
 
@@ -350,7 +361,7 @@ in {
             type = types.bool;
             example = true;
             description = ''
-              Set the `persistentTimer` option for the
+              Set the `Persistent` option for the
               {manpage}`systemd.timer(5)`
               which triggers the backup immediately if the last trigger
               was missed (e.g. if the system was powered down).
@@ -488,6 +499,15 @@ in {
             default = true;
           };
 
+          failOnWarnings = mkOption {
+            type = types.bool;
+            description = ''
+              Fail the whole backup job if any borg command returns a warning
+              (exit code 1), for example because a file changed during backup.
+            '';
+            default = true;
+          };
+
           doInit = mkOption {
             type = types.bool;
             description = ''
diff --git a/nixos/modules/services/backup/restic.nix b/nixos/modules/services/backup/restic.nix
index 8b56636c79695..8be2649189b95 100644
--- a/nixos/modules/services/backup/restic.nix
+++ b/nixos/modules/services/backup/restic.nix
@@ -11,7 +11,7 @@ in
     description = ''
       Periodic backups to create with Restic.
     '';
-    type = types.attrsOf (types.submodule ({ config, name, ... }: {
+    type = types.attrsOf (types.submodule ({ name, ... }: {
       options = {
         passwordFile = mkOption {
           type = types.str;
@@ -206,12 +206,19 @@ in
           ];
         };
 
+        runCheck = mkOption {
+          type = types.bool;
+          default = (builtins.length config.services.restic.backups.${name}.checkOpts > 0);
+          defaultText = literalExpression ''builtins.length config.services.backups.${name}.checkOpts > 0'';
+          description = "Whether to run the `check` command with the provided `checkOpts` options.";
+          example = true;
+        };
+
         checkOpts = mkOption {
           type = types.listOf types.str;
           default = [ ];
           description = ''
-            A list of options for 'restic check', which is run after
-            pruning.
+            A list of options for 'restic check'.
           '';
           example = [
             "--with-cache"
@@ -298,7 +305,9 @@ in
             doBackup = (backup.dynamicFilesFrom != null) || (backup.paths != null && backup.paths != []);
             pruneCmd = optionals (builtins.length backup.pruneOpts > 0) [
               (resticCmd + " forget --prune " + (concatStringsSep " " backup.pruneOpts))
-              (resticCmd + " check " + (concatStringsSep " " backup.checkOpts))
+            ];
+            checkCmd = optionals backup.runCheck [
+                (resticCmd + " check " + (concatStringsSep " " backup.checkOpts))
             ];
             # Helper functions for rclone remotes
             rcloneRemoteName = builtins.elemAt (splitString ":" backup.repository) 1;
@@ -331,7 +340,7 @@ in
             serviceConfig = {
               Type = "oneshot";
               ExecStart = (optionals doBackup [ "${resticCmd} backup ${concatStringsSep " " (backup.extraBackupArgs ++ excludeFlags)} --files-from=${filesFromTmpFile}" ])
-                ++ pruneCmd;
+                ++ pruneCmd ++ checkCmd;
               User = backup.user;
               RuntimeDirectory = "restic-backups-${name}";
               CacheDirectory = "restic-backups-${name}";
diff --git a/nixos/modules/services/cluster/k3s/default.nix b/nixos/modules/services/cluster/k3s/default.nix
index 040cf7640de16..4d18d378d7944 100644
--- a/nixos/modules/services/cluster/k3s/default.nix
+++ b/nixos/modules/services/cluster/k3s/default.nix
@@ -1,15 +1,25 @@
-{ config, lib, pkgs, ... }:
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
 
 with lib;
 let
   cfg = config.services.k3s;
-  removeOption = config: instruction:
-    lib.mkRemovedOptionModule ([ "services" "k3s" ] ++ config) instruction;
+  removeOption =
+    config: instruction:
+    lib.mkRemovedOptionModule (
+      [
+        "services"
+        "k3s"
+      ]
+      ++ config
+    ) instruction;
 in
 {
-  imports = [
-    (removeOption [ "docker" ] "k3s docker option is no longer supported.")
-  ];
+  imports = [ (removeOption [ "docker" ] "k3s docker option is no longer supported.") ];
 
   # interface
   options.services.k3s = {
@@ -33,7 +43,10 @@ in
         - `serverAddr` is required.
       '';
       default = "server";
-      type = types.enum [ "server" "agent" ];
+      type = types.enum [
+        "server"
+        "agent"
+      ];
     };
 
     serverAddr = mkOption {
@@ -125,7 +138,8 @@ in
         message = "serverAddr or configPath (with 'server' key) should be set if role is 'agent'";
       }
       {
-        assertion = cfg.role == "agent" -> cfg.configPath != null || cfg.tokenFile != null || cfg.token != "";
+        assertion =
+          cfg.role == "agent" -> cfg.configPath != null || cfg.tokenFile != null || cfg.token != "";
         message = "token or tokenFile or configPath (with 'token' or 'token-file' keys) should be set if role is 'agent'";
       }
       {
@@ -142,8 +156,14 @@ in
 
     systemd.services.k3s = {
       description = "k3s service";
-      after = [ "firewall.service" "network-online.target" ];
-      wants = [ "firewall.service" "network-online.target" ];
+      after = [
+        "firewall.service"
+        "network-online.target"
+      ];
+      wants = [
+        "firewall.service"
+        "network-online.target"
+      ];
       wantedBy = [ "multi-user.target" ];
       path = optional config.boot.zfs.enabled config.boot.zfs.package;
       serviceConfig = {
@@ -159,9 +179,7 @@ in
         TasksMax = "infinity";
         EnvironmentFile = cfg.environmentFile;
         ExecStart = concatStringsSep " \\\n " (
-          [
-            "${cfg.package}/bin/k3s ${cfg.role}"
-          ]
+          [ "${cfg.package}/bin/k3s ${cfg.role}" ]
           ++ (optional cfg.clusterInit "--cluster-init")
           ++ (optional cfg.disableAgent "--disable-agent")
           ++ (optional (cfg.serverAddr != "") "--server ${cfg.serverAddr}")
diff --git a/nixos/modules/services/cluster/kubernetes/default.nix b/nixos/modules/services/cluster/kubernetes/default.nix
index 89bbedf4d0401..01760ffbc72da 100644
--- a/nixos/modules/services/cluster/kubernetes/default.nix
+++ b/nixos/modules/services/cluster/kubernetes/default.nix
@@ -261,7 +261,7 @@ in {
           name = "service-account";
           CN = "system:service-account-signer";
           action = ''
-            systemctl reload \
+            systemctl restart \
               kube-apiserver.service \
               kube-controller-manager.service
           '';
diff --git a/nixos/modules/services/cluster/rke2/default.nix b/nixos/modules/services/cluster/rke2/default.nix
new file mode 100644
index 0000000000000..9ddbd299fdf8d
--- /dev/null
+++ b/nixos/modules/services/cluster/rke2/default.nix
@@ -0,0 +1,311 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.rke2;
+in
+{
+  imports = [ ];
+
+  options.services.rke2 = {
+    enable = mkEnableOption "rke2";
+
+    package = mkPackageOption pkgs "rke2" { };
+
+    role = mkOption {
+      type = types.enum [ "server" "agent" ];
+      description = ''
+        Whether rke2 should run as a server or agent.
+
+        If it's a server:
+
+        - By default it also runs workloads as an agent.
+        - any optionals is allowed.
+
+        If it's an agent:
+
+        - `serverAddr` is required.
+        - `token` or `tokenFile` is required.
+        - `agentToken` or `agentTokenFile` or `disable` or `cni` are not allowed.
+      '';
+      default = "server";
+    };
+
+    configPath = mkOption {
+      type = types.path;
+      description = "Load configuration from FILE.";
+      default = "/etc/rancher/rke2/config.yaml";
+    };
+
+    debug = mkOption {
+      type = types.bool;
+      description = "Turn on debug logs.";
+      default = false;
+    };
+
+    dataDir = mkOption {
+      type = types.path;
+      description = "The folder to hold state in.";
+      default = "/var/lib/rancher/rke2";
+    };
+
+    token = mkOption {
+      type = types.str;
+      description = ''
+        Shared secret used to join a server or agent to a cluster.
+
+        > WARNING: This option will expose store your token unencrypted world-readable in the nix store.
+        If this is undesired use the `tokenFile` option instead.
+      '';
+      default = "";
+    };
+
+    tokenFile = mkOption {
+      type = types.nullOr types.path;
+      description = "File path containing rke2 token to use when connecting to the server.";
+      default = null;
+    };
+
+    disable = mkOption {
+      type = types.listOf types.str;
+      description = "Do not deploy packaged components and delete any deployed components.";
+      default = [ ];
+    };
+
+    nodeName = mkOption {
+      type = types.nullOr types.str;
+      description = "Node name.";
+      default = null;
+    };
+
+    nodeLabel = mkOption {
+      type = types.listOf types.str;
+      description = "Registering and starting kubelet with set of labels.";
+      default = [ ];
+    };
+
+    nodeTaint = mkOption {
+      type = types.listOf types.str;
+      description = "Registering kubelet with set of taints.";
+      default = [ ];
+    };
+
+    nodeIP = mkOption {
+      type = types.nullOr types.str;
+      description = "IPv4/IPv6 addresses to advertise for node.";
+      default = null;
+    };
+
+    agentToken = mkOption {
+      type = types.str;
+      description = ''
+        Shared secret used to join agents to the cluster, but not servers.
+
+        > **WARNING**: This option will expose store your token unencrypted world-readable in the nix store.
+        If this is undesired use the `agentTokenFile` option instead.
+      '';
+      default = "";
+    };
+
+    agentTokenFile = mkOption {
+      type = types.nullOr types.path;
+      description = "File path containing rke2 agent token to use when connecting to the server.";
+      default = null;
+    };
+
+    serverAddr = mkOption {
+      type = types.str;
+      description = "The rke2 server to connect to, used to join a cluster.";
+      example = "https://10.0.0.10:6443";
+      default = "";
+    };
+
+    selinux = mkOption {
+      type = types.bool;
+      description = "Enable SELinux in containerd.";
+      default = false;
+    };
+
+    cni = mkOption {
+      type = types.enum [ "none" "canal" "cilium" "calico" "flannel" ];
+      description = ''
+        CNI Plugins to deploy, one of `none`, `calico`, `canal`, `cilium` or `flannel`.
+
+        All CNI plugins get installed via a helm chart after the main components are up and running
+        and can be [customized by modifying the helm chart options](https://docs.rke2.io/helm).
+
+        [Learn more about RKE2 and CNI plugins](https://docs.rke2.io/networking/basic_network_options)
+
+        > **WARNING**: Flannel support in RKE2 is currently experimental.
+      '';
+      default = "canal";
+    };
+
+    cisHardening = mkOption {
+      type = types.bool;
+      description = ''
+        Enable CIS Hardening for RKE2.
+
+        It will set the configurations and controls required to address Kubernetes benchmark controls
+        from the Center for Internet Security (CIS).
+
+        Learn more about [CIS Hardening for RKE2](https://docs.rke2.io/security/hardening_guide).
+
+        > **NOTICE**:
+        >
+        > You may need restart the `systemd-sysctl` muaually by:
+        >
+        > ```shell
+        > sudo systemctl restart systemd-sysctl
+        > ```
+      '';
+      default = false;
+    };
+
+    extraFlags = mkOption {
+      type = types.listOf types.str;
+      description = ''
+        Extra flags to pass to the rke2 service/agent.
+
+        Here you can find all the available flags:
+
+        - [Server Configuration Reference](https://docs.rke2.io/reference/server_config)
+        - [Agent Configuration Reference](https://docs.rke2.io/reference/linux_agent_config)
+      '';
+      example = [ "--disable-kube-proxy" "--cluster-cidr=10.24.0.0/16" ];
+      default = [ ];
+    };
+
+    environmentVars = mkOption {
+      type = types.attrsOf types.str;
+      description = ''
+        Environment variables for configuring the rke2 service/agent.
+
+        Here you can find all the available environment variables:
+
+        - [Server Configuration Reference](https://docs.rke2.io/reference/server_config)
+        - [Agent Configuration Reference](https://docs.rke2.io/reference/linux_agent_config)
+
+        Besides the options above, you can also active environment variables by edit/create those files:
+
+        - `/etc/default/rke2`
+        - `/etc/sysconfig/rke2`
+        - `/usr/local/lib/systemd/system/rke2.env`
+      '';
+      # See: https://github.com/rancher/rke2/blob/master/bundle/lib/systemd/system/rke2-server.env#L1
+      default = {
+        HOME = "/root";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = cfg.role == "agent" -> (builtins.pathExists cfg.configPath || cfg.serverAddr != "");
+        message = "serverAddr or configPath (with 'server' key) should be set if role is 'agent'";
+      }
+      {
+        assertion = cfg.role == "agent" -> (builtins.pathExists cfg.configPath || cfg.tokenFile != null || cfg.token != "");
+        message = "token or tokenFile or configPath (with 'token' or 'token-file' keys) should be set if role is 'agent'";
+      }
+      {
+        assertion = cfg.role == "agent" -> ! (cfg.agentTokenFile != null || cfg.agentToken != "");
+        message = "agentToken or agentTokenFile should be set if role is 'agent'";
+      }
+      {
+        assertion = cfg.role == "agent" -> ! (cfg.disable != [ ]);
+        message = "disable should not be set if role is 'agent'";
+      }
+      {
+        assertion = cfg.role == "agent" -> ! (cfg.cni != "canal");
+        message = "cni should not be set if role is 'agent'";
+      }
+    ];
+
+    environment.systemPackages = [ config.services.rke2.package ];
+    # To configure NetworkManager to ignore calico/flannel related network interfaces.
+    # See: https://docs.rke2.io/known_issues#networkmanager
+    environment.etc."NetworkManager/conf.d/rke2-canal.conf" = {
+      enable = config.networking.networkmanager.enable;
+      text = ''
+        [keyfile]
+        unmanaged-devices=interface-name:cali*;interface-name:flannel*
+      '';
+    };
+    # See: https://docs.rke2.io/security/hardening_guide#set-kernel-parameters
+    boot.kernel.sysctl = mkIf cfg.cisHardening {
+      "vm.panic_on_oom" = 0;
+      "vm.overcommit_memory" = 1;
+      "kernel.panic" = 10;
+      "kernel.panic_on_oops" = 1;
+    };
+
+    systemd.services.rke2 = {
+      description = "Rancher Kubernetes Engine v2";
+      documentation = [ "https://github.com/rancher/rke2#readme" ];
+      after = [ "network-online.target" ];
+      wants = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = if cfg.role == "agent" then "exec" else "notify";
+        EnvironmentFile = [
+          "-/etc/default/%N"
+          "-/etc/sysconfig/%N"
+          "-/usr/local/lib/systemd/system/%N.env"
+        ];
+        Environment = mapAttrsToList (k: v: "${k}=${v}") cfg.environmentVars;
+        KillMode = "process";
+        Delegate = "yes";
+        LimitNOFILE = 1048576;
+        LimitNPROC = "infinity";
+        LimitCORE = "infinity";
+        TasksMax = "infinity";
+        TimeoutStartSec = 0;
+        Restart = "always";
+        RestartSec = "5s";
+        ExecStartPre = [
+          # There is a conflict between RKE2 and `nm-cloud-setup.service`. This service add a routing table that
+          # interfere with the CNI plugin's configuration. This script checks if the service is enabled and if so,
+          # failed the RKE2 start.
+          # See: https://github.com/rancher/rke2/issues/1053
+          (pkgs.writeScript "check-nm-cloud-setup.sh" ''
+            #! ${pkgs.runtimeShell}
+            set -x
+            ! /run/current-system/systemd/bin/systemctl is-enabled --quiet nm-cloud-setup.service
+          '')
+          "-${pkgs.kmod}/bin/modprobe br_netfilter"
+          "-${pkgs.kmod}/bin/modprobe overlay"
+        ];
+        ExecStart = "${cfg.package}/bin/rke2 '${cfg.role}' ${escapeShellArgs (
+             (optional (cfg.configPath != "/etc/rancher/rke2/config.yaml") "--config=${cfg.configPath}")
+          ++ (optional cfg.debug "--debug")
+          ++ (optional (cfg.dataDir != "/var/lib/rancher/rke2") "--data-dir=${cfg.dataDir}")
+          ++ (optional (cfg.token != "") "--token=${cfg.token}")
+          ++ (optional (cfg.tokenFile != null) "--token-file=${cfg.tokenFile}")
+          ++ (optionals (cfg.role == "server" && cfg.disable != [ ]) (map (d: "--disable=${d}") cfg.disable))
+          ++ (optional (cfg.nodeName != null) "--node-name=${cfg.nodeName}")
+          ++ (optionals (cfg.nodeLabel != [ ]) (map (l: "--node-label=${l}") cfg.nodeLabel))
+          ++ (optionals (cfg.nodeTaint != [ ]) (map (t: "--node-taint=${t}") cfg.nodeTaint))
+          ++ (optional (cfg.nodeIP != null) "--node-ip=${cfg.nodeIP}")
+          ++ (optional (cfg.role == "server" && cfg.agentToken != "") "--agent-token=${cfg.agentToken}")
+          ++ (optional (cfg.role == "server" && cfg.agentTokenFile != null) "--agent-token-file=${cfg.agentTokenFile}")
+          ++ (optional (cfg.serverAddr != "") "--server=${cfg.serverAddr}")
+          ++ (optional cfg.selinux "--selinux")
+          ++ (optional (cfg.role == "server" && cfg.cni != "canal") "--cni=${cfg.cni}")
+          ++ (optional cfg.cisHardening "--profile=${if cfg.package.version >= "1.25" then "cis-1.23" else "cis-1.6"}")
+          ++ cfg.extraFlags
+        )}";
+        ExecStopPost = let
+          killProcess = pkgs.writeScript "kill-process.sh" ''
+            #! ${pkgs.runtimeShell}
+            /run/current-system/systemd/bin/systemd-cgls /system.slice/$1 | \
+            ${pkgs.gnugrep}/bin/grep -Eo '[0-9]+ (containerd|kubelet)' | \
+            ${pkgs.gawk}/bin/awk '{print $1}' | \
+            ${pkgs.findutils}/bin/xargs -r ${pkgs.util-linux}/bin/kill
+          '';
+        in "-${killProcess} %n";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix
index 23f07eb64b92d..b516c3d6192cb 100644
--- a/nixos/modules/services/continuous-integration/hydra/default.nix
+++ b/nixos/modules/services/continuous-integration/hydra/default.nix
@@ -335,7 +335,7 @@ in
           mkdir -m 0700 -p ${baseDir}/queue-runner
           mkdir -m 0750 -p ${baseDir}/build-logs
           mkdir -m 0750 -p ${baseDir}/runcommand-logs
-          chown hydra-queue-runner.hydra \
+          chown hydra-queue-runner:hydra \
             ${baseDir}/queue-runner \
             ${baseDir}/build-logs \
             ${baseDir}/runcommand-logs
diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix
index 7b671ba9ed9de..a23120739b7fa 100644
--- a/nixos/modules/services/continuous-integration/jenkins/default.nix
+++ b/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -237,6 +237,8 @@ in {
       serviceConfig = {
         User = cfg.user;
         StateDirectory = mkIf (hasPrefix "/var/lib/jenkins" cfg.home) "jenkins";
+        # For (possible) socket use
+        RuntimeDirectory = "jenkins";
       };
     };
   };
diff --git a/nixos/modules/services/databases/postgresql.md b/nixos/modules/services/databases/postgresql.md
index 8a587832cd8c0..e76f127335c7b 100644
--- a/nixos/modules/services/databases/postgresql.md
+++ b/nixos/modules/services/databases/postgresql.md
@@ -244,6 +244,27 @@ The upgrade process is:
      $ ./delete_old_cluster.sh
      ```
 
+## Versioning and End-of-Life {#module-services-postgres-versioning}
+
+PostgreSQL's versioning policy is described [here](https://www.postgresql.org/support/versioning/). TLDR:
+
+- Each major version is supported for 5 years.
+- Every three months there will be a new minor release, containing bug and security fixes.
+- For criticial/security fixes there could be more minor releases inbetween. This happens *very* infrequently.
+- After five years, a final minor version is released. This usually happens in early November.
+- After that a version is considered end-of-life (EOL).
+- Around February each year is the first time an EOL-release will not have received regular updates anymore.
+
+Technically, we'd not want to have EOL'ed packages in a stable NixOS release, which is to be supported until one month after the previous release. Thus, with NixOS' release schedule in May and November, the oldest PostgreSQL version in nixpkgs would have to be supported until December. It could be argued that a soon-to-be-EOL-ed version should thus be removed in May for the .05 release already. But since new security vulnerabilities are first disclosed in Februrary of the following year, we agreed on keeping the oldest PostgreSQL major version around one more cycle in [#310580](https://github.com/NixOS/nixpkgs/pull/310580#discussion_r1597284693).
+
+Thus:
+- In September/October the new major version will be released and added to nixos-unstable.
+- In November the last minor version for the oldest major will be released.
+- Both the current stable .05 release and nixos-unstable should be updated to the latest minor.
+- In November, before branch-off for the .11 release, the EOL-ed major will be removed from nixos-unstable.
+
+This leaves a small gap of a couple of weeks after the latest minor release and the end of our support window for the .05 release, in which there could be an emergency release to other major versions of PostgreSQL - but not the oldest major we have in that branch. In that case: If we can't trivially patch the issue, we will mark the package/version as insecure **immediately**.
+
 ## Options {#module-services-postgres-options}
 
 A complete list of options for the PostgreSQL module may be found [here](#opt-services.postgresql.enable).
diff --git a/nixos/modules/services/desktop-managers/lomiri.nix b/nixos/modules/services/desktop-managers/lomiri.nix
index e11867b691071..0b871aa38183e 100644
--- a/nixos/modules/services/desktop-managers/lomiri.nix
+++ b/nixos/modules/services/desktop-managers/lomiri.nix
@@ -22,6 +22,7 @@ in {
         libusermetrics
         lomiri
         lomiri-download-manager
+        lomiri-filemanager-app
         lomiri-schemas # exposes some required dbus interfaces
         lomiri-session # wrappers to properly launch the session
         lomiri-sounds
@@ -34,10 +35,15 @@ in {
         morph-browser
         qtmir # not having its desktop file for Xwayland available causes any X11 application to crash the session
         suru-icon-theme
-        telephony-service
+        # telephony-service # currently broken: https://github.com/NixOS/nixpkgs/pull/314043
       ]);
+      variables = {
+        # To override the keyboard layouts in Lomiri
+        NIXOS_XKB_LAYOUTS = config.services.xserver.xkb.layout;
+      };
     };
 
+    hardware.pulseaudio.enable = lib.mkDefault true;
     networking.networkmanager.enable = lib.mkDefault true;
 
     systemd.packages = with pkgs.lomiri; [
@@ -57,7 +63,7 @@ in {
     ];
 
     # Copy-pasted basic stuff
-    hardware.opengl.enable = lib.mkDefault true;
+    hardware.graphics.enable = lib.mkDefault true;
     fonts.enableDefaultPackages = lib.mkDefault true;
     programs.dconf.enable = lib.mkDefault true;
 
@@ -71,10 +77,14 @@ in {
       enable = true;
       packages = (with pkgs; [
         ayatana-indicator-datetime
+        ayatana-indicator-display
         ayatana-indicator-messages
+        ayatana-indicator-power
         ayatana-indicator-session
+      ] ++ lib.optionals (config.hardware.pulseaudio.enable || config.services.pipewire.pulse.enable) [
+        ayatana-indicator-sound
       ]) ++ (with pkgs.lomiri; [
-        telephony-service
+        # telephony-service # currently broken: https://github.com/NixOS/nixpkgs/pull/314043
       ] ++ lib.optionals config.networking.networkmanager.enable [
         lomiri-indicator-network
       ]);
diff --git a/nixos/modules/services/desktop-managers/plasma6.nix b/nixos/modules/services/desktop-managers/plasma6.nix
index 08507b4d370a7..796e24286f9e4 100644
--- a/nixos/modules/services/desktop-managers/plasma6.nix
+++ b/nixos/modules/services/desktop-managers/plasma6.nix
@@ -60,10 +60,8 @@ in {
     qt.enable = true;
     environment.systemPackages = with kdePackages; let
       requiredPackages = [
-        # Hack? To make everything run on Wayland
-        qtwayland
-        # Needed to render SVG icons
-        qtsvg
+        qtwayland # Hack? To make everything run on Wayland
+        qtsvg # Needed to render SVG icons
 
         # Frameworks with globally loadable bits
         frameworkintegration # provides Qt plugin
@@ -75,6 +73,9 @@ in {
         kiconthemes # provides Qt plugins
         kimageformats # provides Qt plugins
         kio # provides helper service + a bunch of other stuff
+        kio-admin # managing files as admin
+        kio-extras # stuff for MTP, AFC, etc
+        kio-fuse # fuse interface for KIO
         kpackage # provides kpackagetool tool
         kservice # provides kbuildsycoca6 tool
         kwallet # provides helper service
@@ -87,30 +88,26 @@ in {
         # Core Plasma parts
         kwin
         pkgs.xwayland
-
         kscreen
         libkscreen
-
         kscreenlocker
-
         kactivitymanagerd
         kde-cli-tools
-        kglobalacceld
+        kglobalacceld # keyboard shortcut daemon
         kwrited # wall message proxy, not to be confused with kwrite
-
-        milou
-        polkit-kde-agent-1
-
+        baloo # system indexer
+        milou # search engine atop baloo
+        kdegraphics-thumbnailers # pdf etc thumbnailer
+        polkit-kde-agent-1 # polkit auth ui
         plasma-desktop
         plasma-workspace
-
-        # Crash handler
-        drkonqi
+        drkonqi # crash handler
+        kde-inotify-survey # warns the user on low inotifywatch limits
 
         # Application integration
         libplasma # provides Kirigami platform theme
         plasma-integration # provides Qt platform theme
-        kde-gtk-config
+        kde-gtk-config # syncs KDE settings to GTK
 
         # Artwork + themes
         breeze
@@ -124,44 +121,32 @@ in {
 
         # misc Plasma extras
         kdeplasma-addons
-
         pkgs.xdg-user-dirs # recommended upstream
 
         # Plasma utilities
         kmenuedit
-
         kinfocenter
         plasma-systemmonitor
         ksystemstats
         libksysguard
-
-        spectacle
         systemsettings
         kcmutils
-
-        # Gear
-        baloo
-        dolphin
-        dolphin-plugins
-        ffmpegthumbs
-        kdegraphics-thumbnailers
-        kde-inotify-survey
-        kio-admin
-        kio-extras
-        kio-fuse
       ];
       optionalPackages = [
         plasma-browser-integration
         konsole
         (lib.getBin qttools) # Expose qdbus in PATH
-
         ark
         elisa
         gwenview
         okular
         kate
         khelpcenter
-        print-manager
+        dolphin
+        dolphin-plugins
+        spectacle
+        ffmpegthumbs
+        krdp
       ];
     in
       requiredPackages
@@ -183,12 +168,13 @@ in {
         )
         kio-extras-kf5
       ]
-      # Optional hardware support features
+      # Optional and hardware support features
       ++ lib.optionals config.hardware.bluetooth.enable [bluedevil bluez-qt pkgs.openobex pkgs.obexftp]
       ++ lib.optional config.networking.networkmanager.enable plasma-nm
       ++ lib.optional config.hardware.pulseaudio.enable plasma-pa
       ++ lib.optional config.services.pipewire.pulse.enable plasma-pa
       ++ lib.optional config.powerManagement.enable powerdevil
+      ++ lib.optional config.services.printing.enable print-manager
       ++ lib.optional config.services.colord.enable colord-kde
       ++ lib.optional config.services.hardware.bolt.enable plasma-thunderbolt
       ++ lib.optional config.services.samba.enable kdenetwork-filesharing
@@ -217,7 +203,7 @@ in {
     environment.sessionVariables.KPACKAGE_DEP_RESOLVERS_PATH = "${kdePackages.frameworkintegration.out}/libexec/kf6/kpackagehandlers";
 
     # Enable GTK applications to load SVG icons
-    services.xserver.gdk-pixbuf.modulePackages = [pkgs.librsvg];
+    programs.gdk-pixbuf.modulePackages = [pkgs.librsvg];
 
     fonts.packages = [cfg.notoPackage pkgs.hack-font];
     fonts.fontconfig.defaultFonts = {
@@ -278,14 +264,26 @@ in {
         enable = true;
         package = kdePackages.kwallet-pam;
       };
-      kde.kwallet = {
-        enable = true;
-        package = kdePackages.kwallet-pam;
+      kde = {
+        allowNullPassword = true;
+        kwallet = {
+          enable = true;
+          package = kdePackages.kwallet-pam;
+        };
       };
       kde-fingerprint = lib.mkIf config.services.fprintd.enable { fprintAuth = true; };
       kde-smartcard = lib.mkIf config.security.pam.p11.enable { p11Auth = true; };
     };
 
+    security.wrappers = {
+      kwin_wayland = {
+        owner = "root";
+        group = "root";
+        capabilities = "cap_sys_nice+ep";
+        source = "${lib.getBin pkgs.kdePackages.kwin}/bin/kwin_wayland";
+      };
+    };
+
     programs.dconf.enable = true;
 
     programs.firefox.nativeMessagingHosts.packages = [kdePackages.plasma-browser-integration];
diff --git a/nixos/modules/services/desktops/espanso.nix b/nixos/modules/services/desktops/espanso.nix
index 4ef6724dda0a0..a6b8a078247b1 100644
--- a/nixos/modules/services/desktops/espanso.nix
+++ b/nixos/modules/services/desktops/espanso.nix
@@ -6,19 +6,24 @@ in {
   meta = { maintainers = with lib.maintainers; [ numkem ]; };
 
   options = {
-    services.espanso = { enable = options.mkEnableOption "Espanso"; };
+    services.espanso = {
+      enable = mkEnableOption "Espanso";
+      package = mkPackageOption pkgs "espanso" {
+        example = "pkgs.espanso-wayland";
+      };
+    };
   };
 
   config = mkIf cfg.enable {
     systemd.user.services.espanso = {
       description = "Espanso daemon";
       serviceConfig = {
-        ExecStart = "${pkgs.espanso}/bin/espanso daemon";
+        ExecStart = "${lib.getExe cfg.package} daemon";
         Restart = "on-failure";
       };
       wantedBy = [ "default.target" ];
     };
 
-    environment.systemPackages = [ pkgs.espanso ];
+    environment.systemPackages = [ cfg.package ];
   };
 }
diff --git a/nixos/modules/services/desktops/gnome/gnome-keyring.nix b/nixos/modules/services/desktops/gnome/gnome-keyring.nix
index 79bce0ade2fc5..02b198fd81cb9 100644
--- a/nixos/modules/services/desktops/gnome/gnome-keyring.nix
+++ b/nixos/modules/services/desktops/gnome/gnome-keyring.nix
@@ -1,45 +1,52 @@
 # GNOME Keyring daemon.
 
-{ config, pkgs, lib, ... }:
-
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
+let
+  cfg = config.services.gnome.gnome-keyring;
+in
 {
 
   meta = {
     maintainers = lib.teams.gnome.members;
   };
 
-  ###### interface
-
   options = {
-
     services.gnome.gnome-keyring = {
-
-      enable = lib.mkOption {
-        type = lib.types.bool;
-        default = false;
-        description = ''
-          Whether to enable GNOME Keyring daemon, a service designed to
-          take care of the user's security credentials,
-          such as user names and passwords.
-        '';
-      };
-
+      enable = lib.mkEnableOption ''
+        GNOME Keyring daemon, a service designed to
+        take care of the user's security credentials,
+        such as user names and passwords
+      '';
     };
-
   };
 
-
-  ###### implementation
-
-  config = lib.mkIf config.services.gnome.gnome-keyring.enable {
-
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ pkgs.gnome.gnome-keyring ];
 
-    services.dbus.packages = [ pkgs.gnome.gnome-keyring pkgs.gcr ];
+    services.dbus.packages = [
+      pkgs.gnome.gnome-keyring
+      pkgs.gcr
+    ];
 
     xdg.portal.extraPortals = [ pkgs.gnome.gnome-keyring ];
 
-    security.pam.services.login.enableGnomeKeyring = true;
+    security.pam.services = lib.mkMerge [
+      {
+        login.enableGnomeKeyring = true;
+      }
+      (lib.mkIf config.services.xserver.displayManager.gdm.enable {
+        gdm-password.enableGnomeKeyring = true;
+        gdm-autologin.enableGnomeKeyring = true;
+      })
+      (lib.mkIf (config.services.xserver.displayManager.gdm.enable && config.services.fprintd.enable) {
+        gdm-fingerprint.enableGnomeKeyring = true;
+      })
+    ];
 
     security.wrappers.gnome-keyring-daemon = {
       owner = "root";
@@ -47,7 +54,5 @@
       capabilities = "cap_ipc_lock=ep";
       source = "${pkgs.gnome.gnome-keyring}/bin/gnome-keyring-daemon";
     };
-
   };
-
 }
diff --git a/nixos/modules/services/display-managers/default.nix b/nixos/modules/services/display-managers/default.nix
index 005ae8f1c8a58..9a7bd6c84b15b 100644
--- a/nixos/modules/services/display-managers/default.nix
+++ b/nixos/modules/services/display-managers/default.nix
@@ -113,10 +113,10 @@ in
         type = lib.types.nullOr lib.types.str // {
           description = "session name";
           check = d:
-            lib.assertMsg (d != null -> (lib.types.str.check d && lib.elem d config.services.displayManager.sessionData.sessionNames)) ''
+            lib.assertMsg (d != null -> (lib.types.str.check d && lib.elem d cfg.sessionData.sessionNames)) ''
                 Default graphical session, '${d}', not found.
                 Valid names for 'services.displayManager.defaultSession' are:
-                  ${lib.concatStringsSep "\n  " cfg.displayManager.sessionData.sessionNames}
+                  ${lib.concatStringsSep "\n  " cfg.sessionData.sessionNames}
               '';
         };
         default = null;
@@ -187,7 +187,7 @@ in
 
     services.displayManager.sessionData = {
       desktops = installedSessions;
-      sessionNames = lib.concatMap (p: p.providedSessions) config.services.displayManager.sessionPackages;
+      sessionNames = lib.concatMap (p: p.providedSessions) cfg.sessionPackages;
       # We do not want to force users to set defaultSession when they have only single DE.
       autologinSession =
         if cfg.defaultSession != null then
@@ -212,9 +212,7 @@ in
       after = [ "acpid.service" "systemd-logind.service" "systemd-user-sessions.service" ];
       restartIfChanged = false;
 
-      environment = lib.optionalAttrs config.hardware.opengl.setLdLibraryPath {
-        LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.addOpenGLRunpath.driverLink ];
-      } // cfg.environment;
+      environment = cfg.environment;
 
       preStart = cfg.preStart;
       script = lib.mkIf (config.systemd.services.display-manager.enable == true) cfg.execCmd;
diff --git a/nixos/modules/services/display-managers/greetd.nix b/nixos/modules/services/display-managers/greetd.nix
index c07b225fc4d95..118a3e1df378c 100644
--- a/nixos/modules/services/display-managers/greetd.nix
+++ b/nixos/modules/services/display-managers/greetd.nix
@@ -27,6 +27,17 @@ in
       '';
     };
 
+    greeterManagesPlymouth = mkOption {
+      type = types.bool;
+      internal = true;
+      default = false;
+      description = ''
+        Don't configure the greetd service to wait for Plymouth to exit.
+
+        Enable this if the greeter you're using can manage Plymouth itself to provide a smoother handoff.
+      '';
+    };
+
     vt = mkOption {
       type = types.int;
       default = 1;
@@ -72,8 +83,9 @@ in
         ];
         After = [
           "systemd-user-sessions.service"
-          "plymouth-quit-wait.service"
           "getty@${tty}.service"
+        ] ++ lib.optionals (!cfg.greeterManagesPlymouth) [
+          "plymouth-quit-wait.service"
         ];
         Conflicts = [
           "getty@${tty}.service"
diff --git a/nixos/modules/services/display-managers/sddm.nix b/nixos/modules/services/display-managers/sddm.nix
index a6bfa213fe380..54356f7bb6171 100644
--- a/nixos/modules/services/display-managers/sddm.nix
+++ b/nixos/modules/services/display-managers/sddm.nix
@@ -66,7 +66,14 @@ let
       HideShells = "/run/current-system/sw/bin/nologin";
     };
 
-    X11 = optionalAttrs xcfg.enable {
+    Wayland = {
+      EnableHiDPI = cfg.enableHidpi;
+      SessionDir = "${dmcfg.sessionData.desktops}/share/wayland-sessions";
+      CompositorCommand = lib.optionalString cfg.wayland.enable cfg.wayland.compositorCommand;
+    };
+
+  } // optionalAttrs xcfg.enable {
+    X11 = {
       MinimumVT = if xcfg.tty != null then xcfg.tty else 7;
       ServerPath = toString xserverWrapper;
       XephyrPath = "${pkgs.xorg.xorgserver.out}/bin/Xephyr";
@@ -77,12 +84,6 @@ let
       DisplayStopCommand = toString Xstop;
       EnableHiDPI = cfg.enableHidpi;
     };
-
-    Wayland = {
-      EnableHiDPI = cfg.enableHidpi;
-      SessionDir = "${dmcfg.sessionData.desktops}/share/wayland-sessions";
-      CompositorCommand = lib.optionalString cfg.wayland.enable cfg.wayland.compositorCommand;
-    };
   } // optionalAttrs dmcfg.autoLogin.enable {
     Autologin = {
       User = dmcfg.autoLogin.user;
diff --git a/nixos/modules/services/editors/emacs.nix b/nixos/modules/services/editors/emacs.nix
index 35f257cee1e3a..98d8506e67278 100644
--- a/nixos/modules/services/editors/emacs.nix
+++ b/nixos/modules/services/editors/emacs.nix
@@ -6,8 +6,7 @@ let
 
   cfg = config.services.emacs;
 
-  editorScript = pkgs.writeScriptBin "emacseditor" ''
-    #!${pkgs.runtimeShell}
+  editorScript = pkgs.writeShellScriptBin "emacseditor" ''
     if [ -z "$1" ]; then
       exec ${cfg.package}/bin/emacsclient --create-frame --alternate-editor ${cfg.package}/bin/emacs
     else
@@ -70,8 +69,8 @@ in
       description = "Emacs: the extensible, self-documenting text editor";
 
       serviceConfig = {
-        Type = "forking";
-        ExecStart = "${pkgs.bash}/bin/bash -c 'source ${config.system.build.setEnvironment}; exec ${cfg.package}/bin/emacs --daemon'";
+        Type = "notify";
+        ExecStart = "${pkgs.runtimeShell} -c 'source ${config.system.build.setEnvironment}; exec ${cfg.package}/bin/emacs --fg-daemon'";
         ExecStop = "${cfg.package}/bin/emacsclient --eval (kill-emacs)";
         Restart = "always";
       };
diff --git a/nixos/modules/services/games/archisteamfarm.nix b/nixos/modules/services/games/archisteamfarm.nix
index 33898f8387e99..7062332db34ab 100644
--- a/nixos/modules/services/games/archisteamfarm.nix
+++ b/nixos/modules/services/games/archisteamfarm.nix
@@ -164,8 +164,11 @@ in
   };
 
   config = lib.mkIf cfg.enable {
-    # TODO: drop with 24.11
-    services.archisteamfarm.dataDir = lib.mkIf (lib.versionAtLeast config.system.stateVersion "24.05") (lib.mkDefault "/var/lib/asf");
+    services.archisteamfarm = {
+      # TODO: drop with 24.11
+      dataDir = lib.mkIf (lib.versionAtLeast config.system.stateVersion "24.05") (lib.mkDefault "/var/lib/asf");
+      settings.IPC = lib.mkIf (!cfg.web-ui.enable) false;
+    };
 
     users = {
       users.archisteamfarm = {
@@ -193,7 +196,7 @@ in
             Group = "archisteamfarm";
             WorkingDirectory = cfg.dataDir;
             Type = "simple";
-            ExecStart = "${lib.getExe cfg.package} --no-restart --process-required --service --system-required --path ${cfg.dataDir}";
+            ExecStart = "${lib.getExe cfg.package} --no-restart --service --system-required --path ${cfg.dataDir}";
             Restart = "always";
 
             # copied from the default systemd service at
diff --git a/nixos/modules/services/hardware/amdgpu.nix b/nixos/modules/services/hardware/amdgpu.nix
new file mode 100644
index 0000000000000..1952be08a17cf
--- /dev/null
+++ b/nixos/modules/services/hardware/amdgpu.nix
@@ -0,0 +1,43 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.hardware.amdgpu;
+in {
+  options.hardware.amdgpu = {
+    legacySupport.enable = lib.mkEnableOption ''
+      using `amdgpu` kernel driver instead of `radeon` for Southern Islands
+      (Radeon HD 7000) series and Sea Islands (Radeon HD 8000)
+      series cards. Note: this removes support for analog video outputs,
+      which is only available in the `radeon` driver
+    '';
+    initrd.enable = lib.mkEnableOption ''
+      loading `amdgpu` kernelModule in stage 1.
+      Can fix lower resolution in boot screen during initramfs phase
+    '';
+    opencl.enable = lib.mkEnableOption ''OpenCL support using ROCM runtime library'';
+    # cfg.amdvlk option is defined in ./amdvlk.nix module
+  };
+
+  config = {
+    boot.kernelParams = lib.optionals cfg.legacySupport.enable [
+      "amdgpu.si_support=1"
+      "amdgpu.cik_support=1"
+      "radeon.si_support=0"
+      "radeon.cik_support=0"
+    ];
+
+    boot.initrd.kernelModules = lib.optionals cfg.initrd.enable [ "amdgpu" ];
+
+    hardware.graphics = lib.mkIf cfg.opencl.enable {
+      enable = lib.mkDefault true;
+      extraPackages = [
+        pkgs.rocmPackages.clr
+        pkgs.rocmPackages.clr.icd
+      ];
+    };
+  };
+
+  meta = {
+    maintainers = with lib.maintainers; [ johnrtitor ];
+  };
+}
diff --git a/nixos/modules/services/hardware/amdvlk.nix b/nixos/modules/services/hardware/amdvlk.nix
new file mode 100644
index 0000000000000..32d6fb3be21dc
--- /dev/null
+++ b/nixos/modules/services/hardware/amdvlk.nix
@@ -0,0 +1,59 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.hardware.amdgpu.amdvlk;
+in {
+  options.hardware.amdgpu.amdvlk = {
+    enable = lib.mkEnableOption "AMDVLK Vulkan driver";
+
+    package = lib.mkPackageOption pkgs "amdvlk" { };
+
+    supportExperimental.enable = lib.mkEnableOption "Experimental features support";
+
+    support32Bit.enable = lib.mkEnableOption "32-bit driver support";
+    support32Bit.package = lib.mkPackageOption pkgs [ "driversi686Linux" "amdvlk" ] { };
+
+    settings = lib.mkOption {
+      type = with lib.types; attrsOf (either str int);
+      default = { };
+      example = {
+        AllowVkPipelineCachingToDisk = 1;
+        ShaderCacheMode = 1;
+        IFH = 0;
+        EnableVmAlwaysValid = 1;
+        IdleAfterSubmitGpuMask = 1;
+      };
+      description = ''
+        Runtime settings for AMDVLK to be configured {file}`/etc/amd/amdVulkanSettings.cfg`.
+        See [AMDVLK GitHub page](https://github.com/GPUOpen-Drivers/AMDVLK?tab=readme-ov-file#runtime-settings).
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    hardware.graphics = {
+      enable = true;
+      extraPackages = [ cfg.package ];
+      extraPackages32 = [ cfg.support32Bit.package ];
+    };
+
+    services.xserver.videoDrivers = [ "amdgpu" ];
+
+    environment.sessionVariables = lib.mkIf cfg.supportExperimental.enable {
+      AMDVLK_ENABLE_DEVELOPING_EXT = "all";
+    };
+
+    environment.etc = lib.mkIf (cfg.settings != { }) {
+      "amd/amdVulkanSettings.cfg".text = lib.concatStrings
+        (lib.mapAttrsToList
+          (n: v: ''
+            ${n},${builtins.toString v}
+          '')
+          cfg.settings);
+    };
+  };
+
+  meta = {
+    maintainers = with lib.maintainers; [ johnrtitor ];
+  };
+}
diff --git a/nixos/modules/services/hardware/handheld-daemon.nix b/nixos/modules/services/hardware/handheld-daemon.nix
index 6c9d5aa3e22c8..a5ba1856d015b 100644
--- a/nixos/modules/services/hardware/handheld-daemon.nix
+++ b/nixos/modules/services/hardware/handheld-daemon.nix
@@ -8,7 +8,7 @@ with lib; let
 in
 {
   options.services.handheld-daemon = {
-    enable = mkEnableOption "Enable Handheld Daemon";
+    enable = mkEnableOption "Handheld Daemon";
     package = mkPackageOption pkgs "handheld-daemon" { };
 
     user = mkOption {
diff --git a/nixos/modules/services/hardware/kanata.nix b/nixos/modules/services/hardware/kanata.nix
index 333b2d2a88a55..60fb33881f256 100644
--- a/nixos/modules/services/hardware/kanata.nix
+++ b/nixos/modules/services/hardware/kanata.nix
@@ -5,7 +5,9 @@ with lib;
 let
   cfg = config.services.kanata;
 
-  keyboard = {
+  upstreamDoc = "See [the upstream documentation](https://github.com/jtroo/kanata/blob/main/docs/config.adoc) and [example config files](https://github.com/jtroo/kanata/tree/main/cfg_samples) for more information.";
+
+  keyboard = { name, config, ... }: {
     options = {
       devices = mkOption {
         type = types.listOf types.str;
@@ -22,28 +24,16 @@ let
         type = types.lines;
         example = ''
           (defsrc
-            grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
-            tab  q    w    e    r    t    y    u    i    o    p    [    ]    \
-            caps a    s    d    f    g    h    j    k    l    ;    '    ret
-            lsft z    x    c    v    b    n    m    ,    .    /    rsft
-            lctl lmet lalt           spc            ralt rmet rctl)
-
-          (deflayer qwerty
-            grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
-            tab  q    w    e    r    t    y    u    i    o    p    [    ]    \
-            @cap a    s    d    f    g    h    j    k    l    ;    '    ret
-            lsft z    x    c    v    b    n    m    ,    .    /    rsft
-            lctl lmet lalt           spc            ralt rmet rctl)
-
-          (defalias
-            ;; tap within 100ms for capslk, hold more than 100ms for lctl
-            cap (tap-hold 100 100 caps lctl))
+            caps)
+
+          (deflayermap (default-layer)
+            ;; tap caps lock as caps lock, hold caps lock as left control
+            caps (tap-hold 100 100 caps lctl))
         '';
         description = ''
           Configuration other than `defcfg`.
 
-          See [example config files](https://github.com/jtroo/kanata)
-          for more information.
+          ${upstreamDoc}
         '';
       };
       extraDefCfg = mkOption {
@@ -55,8 +45,22 @@ let
           from the devices option) and
           `linux-continue-if-no-devs-found` (hardcoded to be yes).
 
-          See [example config files](https://github.com/jtroo/kanata)
-          for more information.
+          ${upstreamDoc}
+        '';
+      };
+      configFile = mkOption {
+        type = types.path;
+        default = mkConfig name config;
+        defaultText =
+          "A config file generated by values from other kanata module options.";
+        description = ''
+          The config file.
+
+          By default, it is generated by values from other kanata
+          module options.
+
+          You can also set it to your own full config file which
+          overrides all other kanata module options.  ${upstreamDoc}
         '';
       };
       extraArgs = mkOption {
@@ -86,14 +90,24 @@ let
     in
     optionalString ((length devices) > 0) "linux-dev (${devicesString})";
 
-  mkConfig = name: keyboard: pkgs.writeText "${mkName name}-config.kdb" ''
-    (defcfg
-      ${keyboard.extraDefCfg}
-      ${mkDevices keyboard.devices}
-      linux-continue-if-no-devs-found yes)
-
-    ${keyboard.config}
-  '';
+  mkConfig = name: keyboard: pkgs.writeTextFile {
+    name = "${mkName name}-config.kdb";
+    text = ''
+      (defcfg
+        ${keyboard.extraDefCfg}
+        ${mkDevices keyboard.devices}
+        linux-continue-if-no-devs-found yes)
+
+      ${keyboard.config}
+    '';
+    # Only the config file generated by this module is checked.  A
+    # user-provided one is not checked because it may not be available
+    # at build time.  I think this is a good balance between module
+    # complexity and functionality.
+    checkPhase = ''
+      ${getExe cfg.package} --cfg "$target" --check --debug
+    '';
+  };
 
   mkService = name: keyboard: nameValuePair (mkName name) {
     wantedBy = [ "multi-user.target" ];
@@ -101,7 +115,7 @@ let
       Type = "notify";
       ExecStart = ''
         ${getExe cfg.package} \
-          --cfg ${mkConfig name keyboard} \
+          --cfg ${keyboard.configFile} \
           --symlink-path ''${RUNTIME_DIRECTORY}/${name} \
           ${optionalString (keyboard.port != null) "--port ${toString keyboard.port}"} \
           ${utils.escapeSystemdExecArgs keyboard.extraArgs}
@@ -153,7 +167,7 @@ in
   options.services.kanata = {
     enable = mkEnableOption "kanata, a tool to improve keyboard comfort and usability with advanced customization";
     package = mkPackageOption pkgs "kanata" {
-      example = "kanata-with-cmd";
+      example = [ "kanata-with-cmd" ];
       extraDescription = ''
         ::: {.note}
         If {option}`danger-enable-cmd` is enabled in any of the keyboards, the
diff --git a/nixos/modules/services/hardware/nvidia-container-toolkit/default.nix b/nixos/modules/services/hardware/nvidia-container-toolkit/default.nix
index 6c6bc667e6498..bd12667a56474 100644
--- a/nixos/modules/services/hardware/nvidia-container-toolkit/default.nix
+++ b/nixos/modules/services/hardware/nvidia-container-toolkit/default.nix
@@ -69,14 +69,18 @@
     virtualisation.docker.daemon.settings = lib.mkIf
       (config.hardware.nvidia-container-toolkit.enable &&
        (lib.versionAtLeast config.virtualisation.docker.package.version "25")) {
-      features.cdi = true;
-    };
+         features.cdi = true;
+       };
 
     hardware.nvidia-container-toolkit.mounts = let
       nvidia-driver = config.hardware.nvidia.package;
     in (lib.mkMerge [
       [{ hostPath = pkgs.addDriverRunpath.driverLink;
          containerPath = pkgs.addDriverRunpath.driverLink; }
+       { hostPath = "${lib.getLib nvidia-driver}/etc";
+         containerPath = "${lib.getLib nvidia-driver}/etc"; }
+       { hostPath = "${lib.getLib nvidia-driver}/share";
+         containerPath = "${lib.getLib nvidia-driver}/share"; }
        { hostPath = "${lib.getLib pkgs.glibc}/lib";
          containerPath = "${lib.getLib pkgs.glibc}/lib"; }
        { hostPath = "${lib.getLib pkgs.glibc}/lib64";
diff --git a/nixos/modules/services/hardware/nvidia-optimus.nix b/nixos/modules/services/hardware/nvidia-optimus.nix
index d53175052c74a..307bc78098d14 100644
--- a/nixos/modules/services/hardware/nvidia-optimus.nix
+++ b/nixos/modules/services/hardware/nvidia-optimus.nix
@@ -23,7 +23,7 @@ let kernel = config.boot.kernelPackages; in
   ###### implementation
 
   config = lib.mkIf config.hardware.nvidiaOptimus.disable {
-    boot.blacklistedKernelModules = ["nouveau" "nvidia" "nvidiafb" "nvidia-drm"];
+    boot.blacklistedKernelModules = ["nouveau" "nvidia" "nvidiafb" "nvidia-drm" "nvidia-modeset"];
     boot.kernelModules = [ "bbswitch" ];
     boot.extraModulePackages = [ kernel.bbswitch ];
 
diff --git a/nixos/modules/services/hardware/power-profiles-daemon.nix b/nixos/modules/services/hardware/power-profiles-daemon.nix
index 05e5b7a00b420..7651c65b9f181 100644
--- a/nixos/modules/services/hardware/power-profiles-daemon.nix
+++ b/nixos/modules/services/hardware/power-profiles-daemon.nix
@@ -39,6 +39,12 @@ in
           which conflicts with services.tlp.enable = true;
         '';
       }
+      { assertion = !config.services.auto-cpufreq.enable;
+        message = ''
+          You have set services.power-profiles-daemon.enable = true;
+          which conflicts with services.auto-cpufreq.enable = true;
+        '';
+      }
     ];
 
     environment.systemPackages = [ cfg.package ];
diff --git a/nixos/modules/services/hardware/thermald.nix b/nixos/modules/services/hardware/thermald.nix
index 4f9202d13d903..fb7cf3735a7ea 100644
--- a/nixos/modules/services/hardware/thermald.nix
+++ b/nixos/modules/services/hardware/thermald.nix
@@ -28,7 +28,13 @@ in
       configFile = mkOption {
         type = types.nullOr types.path;
         default = null;
-        description = "the thermald manual configuration file.";
+        description = ''
+          The thermald manual configuration file.
+
+          Leave unspecified to run with the `--adaptive` flag instead which will have thermald use your computer's DPTF adaptive tables.
+
+          See `man thermald` for more information.
+        '';
       };
 
       package = mkPackageOption pkgs "thermald" { };
@@ -49,9 +55,8 @@ in
             --no-daemon \
             ${optionalString cfg.debug "--loglevel=debug"} \
             ${optionalString cfg.ignoreCpuidCheck "--ignore-cpuid-check"} \
-            ${optionalString (cfg.configFile != null) "--config-file ${cfg.configFile}"} \
-            --dbus-enable \
-            --adaptive
+            ${if cfg.configFile != null then "--config-file ${cfg.configFile}" else "--adaptive"} \
+            --dbus-enable
         '';
       };
     };
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
index 3db6616442816..62603d20e2d30 100644
--- a/nixos/modules/services/hardware/udev.nix
+++ b/nixos/modules/services/hardware/udev.nix
@@ -167,10 +167,16 @@ let
       mv etc/udev/hwdb.bin $out
     '';
 
-  compressFirmware = firmware: if (config.boot.kernelPackages.kernelAtLeast "5.3" && (firmware.compressFirmware or true)) then
-    pkgs.compressFirmwareXz firmware
-  else
-    id firmware;
+  compressFirmware = firmware:
+    let
+      inherit (config.boot.kernelPackages) kernelAtLeast;
+    in
+      if ! (firmware.compressFirmware or true) then
+        firmware
+      else
+        if kernelAtLeast "5.19" then pkgs.compressFirmwareZstd firmware
+        else if kernelAtLeast "5.3" then pkgs.compressFirmwareXz firmware
+        else firmware;
 
   # Udev has a 512-character limit for ENV{PATH}, so create a symlink
   # tree to work around this.
diff --git a/nixos/modules/services/home-automation/ebusd.nix b/nixos/modules/services/home-automation/ebusd.nix
index ac9ec06639c13..f5c5479e8eaff 100644
--- a/nixos/modules/services/home-automation/ebusd.nix
+++ b/nixos/modules/services/home-automation/ebusd.nix
@@ -138,7 +138,7 @@ in
       after = [ "network.target" ];
       serviceConfig = {
         ExecStart = let
-          args = cli.toGNUCommandLineShell { } (foldr (a: b: a // b) { } [
+          args = cli.toGNUCommandLineShell { optionValueSeparator = "="; } (foldr (a: b: a // b) { } [
             {
               inherit (cfg) device port configpath scanconfig readonly;
               foreground = true;
diff --git a/nixos/modules/services/home-automation/home-assistant.nix b/nixos/modules/services/home-automation/home-assistant.nix
index d94adfb4aa1c7..c58a31539ed81 100644
--- a/nixos/modules/services/home-automation/home-assistant.nix
+++ b/nixos/modules/services/home-automation/home-assistant.nix
@@ -518,8 +518,9 @@ in {
           # recreate symlinks for desired components
           declare -a components=(${escapeShellArgs cfg.customComponents})
           for component in "''${components[@]}"; do
-            path="$(dirname $(find "$component" -name "manifest.json"))"
-            ln -fns "$path" "${cfg.configDir}/custom_components/"
+            readarray -t manifests < <(find "$component" -name manifest.json)
+            readarray -t paths < <(dirname "''${manifests[@]}")
+            ln -fns "''${paths[@]}" "${cfg.configDir}/custom_components/"
           done
         '';
       in
diff --git a/nixos/modules/services/home-automation/wyoming/faster-whisper.nix b/nixos/modules/services/home-automation/wyoming/faster-whisper.nix
index d0fca6a41c7b6..45664103665f7 100644
--- a/nixos/modules/services/home-automation/wyoming/faster-whisper.nix
+++ b/nixos/modules/services/home-automation/wyoming/faster-whisper.nix
@@ -113,6 +113,9 @@ in
       nameValuePair "wyoming-faster-whisper-${server}" {
         inherit (options) enable;
         description = "Wyoming faster-whisper server instance ${server}";
+        wants = [
+          "network-online.target"
+        ];
         after = [
           "network-online.target"
         ];
diff --git a/nixos/modules/services/home-automation/wyoming/openwakeword.nix b/nixos/modules/services/home-automation/wyoming/openwakeword.nix
index 856a4ef7366d0..f9848970bf734 100644
--- a/nixos/modules/services/home-automation/wyoming/openwakeword.nix
+++ b/nixos/modules/services/home-automation/wyoming/openwakeword.nix
@@ -108,6 +108,9 @@ in
   config = mkIf cfg.enable {
     systemd.services."wyoming-openwakeword" = {
       description = "Wyoming openWakeWord server";
+      wants = [
+        "network-online.target"
+      ];
       after = [
         "network-online.target"
       ];
diff --git a/nixos/modules/services/home-automation/wyoming/piper.nix b/nixos/modules/services/home-automation/wyoming/piper.nix
index 5b5f898d7ca35..a26fe8e84f609 100644
--- a/nixos/modules/services/home-automation/wyoming/piper.nix
+++ b/nixos/modules/services/home-automation/wyoming/piper.nix
@@ -117,6 +117,9 @@ in
       nameValuePair "wyoming-piper-${server}" {
         inherit (options) enable;
         description = "Wyoming Piper server instance ${server}";
+        wants = [
+          "network-online.target"
+        ];
         after = [
           "network-online.target"
         ];
diff --git a/nixos/modules/services/logging/journalwatch.nix b/nixos/modules/services/logging/journalwatch.nix
index 71b29d57b7eb7..48fd992ffb65a 100644
--- a/nixos/modules/services/logging/journalwatch.nix
+++ b/nixos/modules/services/logging/journalwatch.nix
@@ -56,6 +56,8 @@ in {
         '';
       };
 
+      package = mkPackageOption pkgs "journalwatch" { };
+
       priority = mkOption {
         type = types.int;
         default = 6;
@@ -240,7 +242,7 @@ in {
         # requires a relative directory name to create beneath /var/lib
         StateDirectory = user;
         StateDirectoryMode = "0750";
-        ExecStart = "${pkgs.python3Packages.journalwatch}/bin/journalwatch mail";
+        ExecStart = "${getExe cfg.package} mail";
         # lowest CPU and IO priority, but both still in best-effort class to prevent starvation
         Nice=19;
         IOSchedulingPriority=7;
diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix
index 180c9800d7345..ab10206fea42e 100644
--- a/nixos/modules/services/mail/mailman.nix
+++ b/nixos/modules/services/mail/mailman.nix
@@ -646,7 +646,7 @@ in {
   };
 
   meta = {
-    maintainers = with lib.maintainers; [ lheckemann qyliss ];
+    maintainers = with lib.maintainers; [ qyliss ];
     doc = ./mailman.md;
   };
 
diff --git a/nixos/modules/services/mail/postsrsd.nix b/nixos/modules/services/mail/postsrsd.nix
index 2ebc675ab10af..92f01dd4101e8 100644
--- a/nixos/modules/services/mail/postsrsd.nix
+++ b/nixos/modules/services/mail/postsrsd.nix
@@ -120,14 +120,9 @@ in {
         if [ ! -e "${cfg.secretsFile}" ]; then
           echo "WARNING: secrets file not found, autogenerating!"
           DIR="$(dirname "${cfg.secretsFile}")"
-          if [ ! -d "$DIR" ]; then
-            mkdir -p -m750 "$DIR"
-            chown "${cfg.user}:${cfg.group}" "$DIR"
-          fi
-          dd if=/dev/random bs=18 count=1 | base64 > "${cfg.secretsFile}"
-          chmod 600 "${cfg.secretsFile}"
+          install -m 750 -o ${cfg.user} -g ${cfg.group} -d "$DIR"
+          install -m 600 -o ${cfg.user} -g ${cfg.group} <(dd if=/dev/random bs=18 count=1 | base64) "${cfg.secretsFile}"
         fi
-        chown "${cfg.user}:${cfg.group}" "${cfg.secretsFile}"
       '';
     };
 
diff --git a/nixos/modules/services/mail/public-inbox.nix b/nixos/modules/services/mail/public-inbox.nix
index 14a2ab48fa250..98063e0331bd8 100644
--- a/nixos/modules/services/mail/public-inbox.nix
+++ b/nixos/modules/services/mail/public-inbox.nix
@@ -455,7 +455,7 @@ in
           after = [ "public-inbox-init.service" "public-inbox-watch.service" ];
           requires = [ "public-inbox-init.service" ];
           serviceConfig = {
-            BindPathsReadOnly =
+            BindReadOnlyPaths =
               map (c: c.dir) (lib.attrValues cfg.settings.coderepo);
             ExecStart = escapeShellArgs (
               [ "${cfg.package}/bin/public-inbox-httpd" ] ++
diff --git a/nixos/modules/services/mail/stalwart-mail.nix b/nixos/modules/services/mail/stalwart-mail.nix
index 9cc919fd117d6..776243a68af53 100644
--- a/nixos/modules/services/mail/stalwart-mail.nix
+++ b/nixos/modules/services/mail/stalwart-mail.nix
@@ -7,10 +7,12 @@ let
   configFormat = pkgs.formats.toml { };
   configFile = configFormat.generate "stalwart-mail.toml" cfg.settings;
   dataDir = "/var/lib/stalwart-mail";
+  useLegacyStorage = versionOlder config.system.stateVersion "24.11";
 
 in {
   options.services.stalwart-mail = {
     enable = mkEnableOption "the Stalwart all-in-one email server";
+
     package = mkPackageOption pkgs "stalwart-mail" { };
 
     settings = mkOption {
@@ -26,78 +28,111 @@ in {
   };
 
   config = mkIf cfg.enable {
+
     # Default config: all local
     services.stalwart-mail.settings = {
-      global.tracing.method = mkDefault "stdout";
-      global.tracing.level = mkDefault "info";
-      queue.path = mkDefault "${dataDir}/queue";
-      report.path = mkDefault "${dataDir}/reports";
-      store.db.type = mkDefault "sqlite";
-      store.db.path = mkDefault "${dataDir}/data/index.sqlite3";
-      store.blob.type = mkDefault "fs";
-      store.blob.path = mkDefault "${dataDir}/data/blobs";
+      tracer.stdout = {
+        type = mkDefault "stdout";
+        level = mkDefault "info";
+        ansi = mkDefault false;  # no colour markers to journald
+        enable = mkDefault true;
+      };
+      store = if useLegacyStorage then {
+        # structured data in SQLite, blobs on filesystem
+        db.type = mkDefault "sqlite";
+        db.path = mkDefault "${dataDir}/data/index.sqlite3";
+        fs.type = mkDefault "fs";
+        fs.path = mkDefault "${dataDir}/data/blobs";
+      } else {
+        # everything in RocksDB
+        db.type = mkDefault "rocksdb";
+        db.path = mkDefault "${dataDir}/db";
+        db.compression = mkDefault "lz4";
+      };
       storage.data = mkDefault "db";
       storage.fts = mkDefault "db";
-      storage.blob = mkDefault "blob";
+      storage.lookup = mkDefault "db";
+      storage.blob = mkDefault (if useLegacyStorage then "fs" else "db");
+      directory.internal.type = mkDefault "internal";
+      directory.internal.store = mkDefault "db";
+      storage.directory = mkDefault "internal";
       resolver.type = mkDefault "system";
-      resolver.public-suffix = mkDefault ["https://publicsuffix.org/list/public_suffix_list.dat"];
+      resolver.public-suffix = lib.mkDefault [
+        "file://${pkgs.publicsuffix-list}/share/publicsuffix/public_suffix_list.dat"
+      ];
+      config.resource = {
+        spam-filter = lib.mkDefault "file://${cfg.package}/etc/stalwart/spamfilter.toml";
+      };
     };
 
-    systemd.services.stalwart-mail = {
-      wantedBy = [ "multi-user.target" ];
-      after = [ "local-fs.target" "network.target" ];
+    # This service stores a potentially large amount of data.
+    # Running it as a dynamic user would force chown to be run everytime the
+    # service is restarted on a potentially large number of files.
+    # That would cause unnecessary and unwanted delays.
+    users = {
+      groups.stalwart-mail = { };
+      users.stalwart-mail = {
+        isSystemUser = true;
+        group = "stalwart-mail";
+      };
+    };
 
-      preStart = ''
-        mkdir -p ${dataDir}/{queue,reports,data/blobs}
-      '';
+    systemd = {
+      packages = [ cfg.package ];
+      services.stalwart-mail = {
+        wantedBy = [ "multi-user.target" ];
+        after = [ "local-fs.target" "network.target" ];
+
+        preStart = if useLegacyStorage then ''
+          mkdir -p ${dataDir}/data/blobs
+        '' else ''
+          mkdir -p ${dataDir}/db
+        '';
+
+        serviceConfig = {
+          ExecStart = [
+            ""
+            "${cfg.package}/bin/stalwart-mail --config=${configFile}"
+          ];
+
+          StandardOutput = "journal";
+          StandardError = "journal";
+
+          StateDirectory = "stalwart-mail";
+
+          # Bind standard privileged ports
+          AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
+          CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
 
-      serviceConfig = {
-        ExecStart =
-          "${cfg.package}/bin/stalwart-mail --config=${configFile}";
-
-        # Base from template resources/systemd/stalwart-mail.service
-        Type = "simple";
-        LimitNOFILE = 65536;
-        KillMode = "process";
-        KillSignal = "SIGINT";
-        Restart = "on-failure";
-        RestartSec = 5;
-        StandardOutput = "journal";
-        StandardError = "journal";
-        SyslogIdentifier = "stalwart-mail";
-
-        DynamicUser = true;
-        User = "stalwart-mail";
-        StateDirectory = "stalwart-mail";
-
-        # Bind standard privileged ports
-        AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
-        CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
-
-        # Hardening
-        DeviceAllow = [ "" ];
-        LockPersonality = true;
-        MemoryDenyWriteExecute = true;
-        PrivateDevices = true;
-        PrivateUsers = false;  # incompatible with CAP_NET_BIND_SERVICE
-        ProcSubset = "pid";
-        PrivateTmp = true;
-        ProtectClock = true;
-        ProtectControlGroups = true;
-        ProtectHome = true;
-        ProtectHostname = true;
-        ProtectKernelLogs = true;
-        ProtectKernelModules = true;
-        ProtectKernelTunables = true;
-        ProtectProc = "invisible";
-        ProtectSystem = "strict";
-        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
-        RestrictNamespaces = true;
-        RestrictRealtime = true;
-        RestrictSUIDSGID = true;
-        SystemCallArchitectures = "native";
-        SystemCallFilter = [ "@system-service" "~@privileged" ];
-        UMask = "0077";
+          # Hardening
+          DeviceAllow = [ "" ];
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          PrivateDevices = true;
+          PrivateUsers = false;  # incompatible with CAP_NET_BIND_SERVICE
+          ProcSubset = "pid";
+          PrivateTmp = true;
+          ProtectClock = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectProc = "invisible";
+          ProtectSystem = "strict";
+          RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          SystemCallArchitectures = "native";
+          SystemCallFilter = [ "@system-service" "~@privileged" ];
+          UMask = "0077";
+        };
+        unitConfig.ConditionPathExists = [
+          ""
+          "${configFile}"
+        ];
       };
     };
 
@@ -106,6 +141,6 @@ in {
   };
 
   meta = {
-    maintainers = with maintainers; [ happysalada pacien ];
+    maintainers = with maintainers; [ happysalada pacien onny ];
   };
 }
diff --git a/nixos/modules/services/matrix/mautrix-signal.nix b/nixos/modules/services/matrix/mautrix-signal.nix
new file mode 100644
index 0000000000000..faca10551abb6
--- /dev/null
+++ b/nixos/modules/services/matrix/mautrix-signal.nix
@@ -0,0 +1,249 @@
+{ lib
+, config
+, pkgs
+, ...
+}:
+let
+  cfg = config.services.mautrix-signal;
+  dataDir = "/var/lib/mautrix-signal";
+  registrationFile = "${dataDir}/signal-registration.yaml";
+  settingsFile = "${dataDir}/config.yaml";
+  settingsFileUnsubstituted = settingsFormat.generate "mautrix-signal-config-unsubstituted.json" cfg.settings;
+  settingsFormat = pkgs.formats.json { };
+  appservicePort = 29328;
+
+  # to be used with a list of lib.mkIf values
+  optOneOf = lib.lists.findFirst (value: value.condition) (lib.mkIf false null);
+  mkDefaults = lib.mapAttrsRecursive (n: v: lib.mkDefault v);
+  defaultConfig = {
+    homeserver.address = "http://localhost:8448";
+    appservice = {
+      hostname = "[::]";
+      port = appservicePort;
+      database.type = "sqlite3";
+      database.uri = "file:${dataDir}/mautrix-signal.db?_txlock=immediate";
+      id = "signal";
+      bot = {
+        username = "signalbot";
+        displayname = "Signal Bridge Bot";
+      };
+      as_token = "";
+      hs_token = "";
+    };
+    bridge = {
+      username_template = "signal_{{.}}";
+      displayname_template = "{{or .ProfileName .PhoneNumber \"Unknown user\"}}";
+      double_puppet_server_map = { };
+      login_shared_secret_map = { };
+      command_prefix = "!signal";
+      permissions."*" = "relay";
+      relay.enabled = true;
+    };
+    logging = {
+      min_level = "info";
+      writers = lib.singleton {
+        type = "stdout";
+        format = "pretty-colored";
+        time_format = " ";
+      };
+    };
+  };
+
+in
+{
+  options.services.mautrix-signal = {
+    enable = lib.mkEnableOption "mautrix-signal, a Matrix-Signal puppeting bridge.";
+
+    settings = lib.mkOption {
+      apply = lib.recursiveUpdate defaultConfig;
+      type = settingsFormat.type;
+      default = defaultConfig;
+      description = ''
+        {file}`config.yaml` configuration as a Nix attribute set.
+        Configuration options should match those described in
+        [example-config.yaml](https://github.com/mautrix/signal/blob/master/example-config.yaml).
+        Secret tokens should be specified using {option}`environmentFile`
+        instead of this world-readable attribute set.
+      '';
+      example = {
+        appservice = {
+          database = {
+            type = "postgres";
+            uri = "postgresql:///mautrix_signal?host=/run/postgresql";
+          };
+          id = "signal";
+          ephemeral_events = false;
+        };
+        bridge = {
+          history_sync = {
+            request_full_sync = true;
+          };
+          private_chat_portal_meta = true;
+          mute_bridging = true;
+          encryption = {
+            allow = true;
+            default = true;
+            require = true;
+          };
+          provisioning = {
+            shared_secret = "disable";
+          };
+          permissions = {
+            "example.com" = "user";
+          };
+        };
+      };
+    };
+
+    environmentFile = lib.mkOption {
+      type = lib.types.nullOr lib.types.path;
+      default = null;
+      description = ''
+        File containing environment variables to be passed to the mautrix-signal service.
+        If an environment variable `MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET` is set,
+        then its value will be used in the configuration file for the option
+        `login_shared_secret_map` without leaking it to the store, using the configured
+        `homeserver.domain` as key.
+        See [here](https://github.com/mautrix/signal/blob/main/example-config.yaml)
+        for the documentation of `login_shared_secret_map`.
+      '';
+    };
+
+    serviceDependencies = lib.mkOption {
+      type = with lib.types; listOf str;
+      default = (lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit)
+        ++ (lib.optional config.services.matrix-conduit.enable "conduit.service");
+      defaultText = lib.literalExpression ''
+        (optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit)
+        ++ (optional config.services.matrix-conduit.enable "conduit.service")
+      '';
+      description = ''
+        List of systemd units to require and wait for when starting the application service.
+      '';
+    };
+
+    registerToSynapse = lib.mkOption {
+      type = lib.types.bool;
+      default = config.services.matrix-synapse.enable;
+      defaultText = lib.literalExpression ''
+        config.services.matrix-synapse.enable
+      '';
+      description = ''
+        Whether to add the bridge's app service registration file to
+        `services.matrix-synapse.settings.app_service_config_files`.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    users.users.mautrix-signal = {
+      isSystemUser = true;
+      group = "mautrix-signal";
+      home = dataDir;
+      description = "Mautrix-Signal bridge user";
+    };
+
+    users.groups.mautrix-signal = { };
+
+    services.matrix-synapse = lib.mkIf cfg.registerToSynapse {
+      settings.app_service_config_files = [ registrationFile ];
+    };
+    systemd.services.matrix-synapse = lib.mkIf cfg.registerToSynapse {
+      serviceConfig.SupplementaryGroups = [ "mautrix-signal" ];
+    };
+
+    # Note: this is defined here to avoid the docs depending on `config`
+    services.mautrix-signal.settings.homeserver = optOneOf (with config.services; [
+      (lib.mkIf matrix-synapse.enable (mkDefaults {
+        domain = matrix-synapse.settings.server_name;
+      }))
+      (lib.mkIf matrix-conduit.enable (mkDefaults {
+        domain = matrix-conduit.settings.global.server_name;
+        address = "http://localhost:${toString matrix-conduit.settings.global.port}";
+      }))
+    ]);
+
+    systemd.services.mautrix-signal = {
+      description = "mautrix-signal, a Matrix-Signal puppeting bridge.";
+
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
+      after = [ "network-online.target" ] ++ cfg.serviceDependencies;
+      # ffmpeg is required for conversion of voice messages
+      path = [ pkgs.ffmpeg-headless ];
+
+      preStart = ''
+        # substitute the settings file by environment variables
+        # in this case read from EnvironmentFile
+        test -f '${settingsFile}' && rm -f '${settingsFile}'
+        old_umask=$(umask)
+        umask 0177
+        ${pkgs.envsubst}/bin/envsubst \
+          -o '${settingsFile}' \
+          -i '${settingsFileUnsubstituted}'
+        umask $old_umask
+
+        # generate the appservice's registration file if absent
+        if [ ! -f '${registrationFile}' ]; then
+          ${pkgs.mautrix-signal}/bin/mautrix-signal \
+            --generate-registration \
+            --config='${settingsFile}' \
+            --registration='${registrationFile}'
+        fi
+        chmod 640 ${registrationFile}
+
+        umask 0177
+        # 1. Overwrite registration tokens in config
+        # 2. If environment variable MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET
+        #    is set, set it as the login shared secret value for the configured
+        #    homeserver domain.
+        ${pkgs.yq}/bin/yq -s '.[0].appservice.as_token = .[1].as_token
+          | .[0].appservice.hs_token = .[1].hs_token
+          | .[0]
+          | if env.MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET then .bridge.login_shared_secret_map.[.homeserver.domain] = env.MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET else . end' \
+          '${settingsFile}' '${registrationFile}' > '${settingsFile}.tmp'
+        mv '${settingsFile}.tmp' '${settingsFile}'
+        umask $old_umask
+      '';
+
+      serviceConfig = {
+        User = "mautrix-signal";
+        Group = "mautrix-signal";
+        EnvironmentFile = cfg.environmentFile;
+        StateDirectory = baseNameOf dataDir;
+        WorkingDirectory = dataDir;
+        ExecStart = ''
+          ${pkgs.mautrix-signal}/bin/mautrix-signal \
+          --config='${settingsFile}' \
+          --registration='${registrationFile}'
+        '';
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectSystem = "strict";
+        Restart = "on-failure";
+        RestartSec = "30s";
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallErrorNumber = "EPERM";
+        SystemCallFilter = [ "@system-service" ];
+        Type = "simple";
+        UMask = 0027;
+      };
+      restartTriggers = [ settingsFileUnsubstituted ];
+    };
+  };
+  meta.maintainers = with lib.maintainers; [ niklaskorz ];
+}
diff --git a/nixos/modules/services/matrix/synapse.nix b/nixos/modules/services/matrix/synapse.nix
index bc88fb53012b7..6d2e6201d66d3 100644
--- a/nixos/modules/services/matrix/synapse.nix
+++ b/nixos/modules/services/matrix/synapse.nix
@@ -1121,7 +1121,7 @@ in {
           The client listener on matrix-synapse is configured to use UNIX domain sockets.
           This configuration is incompatible with the `register_new_matrix_user` script.
 
-          Disable  `services.mastrix-synapse.enableRegistrationScript` to continue.
+          Disable  `services.matrix-synapse.enableRegistrationScript` to continue.
         '';
       }
     ]
diff --git a/nixos/modules/services/misc/amazon-ssm-agent.nix b/nixos/modules/services/misc/amazon-ssm-agent.nix
index 9ab4a7f96d087..0da10621d0a09 100644
--- a/nixos/modules/services/misc/amazon-ssm-agent.nix
+++ b/nixos/modules/services/misc/amazon-ssm-agent.nix
@@ -28,13 +28,7 @@ in {
 
   options.services.amazon-ssm-agent = {
     enable = mkEnableOption "Amazon SSM agent";
-
-    package = mkOption {
-      type = types.path;
-      description = "The Amazon SSM agent package to use";
-      default = pkgs.amazon-ssm-agent.override { overrideEtc = false; };
-      defaultText = literalExpression "pkgs.amazon-ssm-agent.override { overrideEtc = false; }";
-    };
+    package = mkPackageOption pkgs "amazon-ssm-agent" {};
   };
 
   config = mkIf cfg.enable {
diff --git a/nixos/modules/services/misc/anki-sync-server.md b/nixos/modules/services/misc/anki-sync-server.md
index f58d3d8ad0dab..5482a4aa0e5ff 100644
--- a/nixos/modules/services/misc/anki-sync-server.md
+++ b/nixos/modules/services/misc/anki-sync-server.md
@@ -52,7 +52,7 @@ following options:
 
 ```nix
 {
-  services.anki-sync-server.host = "0.0.0.0";
+  services.anki-sync-server.address = "0.0.0.0";
   services.anki-sync-server.openFirewall = true;
 }
 ```
diff --git a/nixos/modules/services/misc/bcg.nix b/nixos/modules/services/misc/bcg.nix
index 626a67f66d08b..63c441833d958 100644
--- a/nixos/modules/services/misc/bcg.nix
+++ b/nixos/modules/services/misc/bcg.nix
@@ -149,20 +149,20 @@ in
     systemd.services.bcg = let
       envConfig = cfg.environmentFiles != [];
       finalConfig = if envConfig
-                    then "$RUNTIME_DIRECTORY/bcg.config.yaml"
+                    then "\${RUNTIME_DIRECTORY}/bcg.config.yaml"
                     else configFile;
     in {
       description = "BigClown Gateway";
       wantedBy = [ "multi-user.target" ];
       wants = [ "network-online.target" ] ++ lib.optional config.services.mosquitto.enable "mosquitto.service";
       after = [ "network-online.target" ];
-      preStart = ''
+      preStart = mkIf envConfig ''
         umask 077
         ${pkgs.envsubst}/bin/envsubst -i "${configFile}" -o "${finalConfig}"
         '';
       serviceConfig = {
         EnvironmentFile = cfg.environmentFiles;
-        ExecStart="${cfg.package}/bin/bcg -c ${finalConfig} -v ${cfg.verbose}";
+        ExecStart = "${cfg.package}/bin/bcg -c ${finalConfig} -v ${cfg.verbose}";
         RuntimeDirectory = "bcg";
       };
     };
diff --git a/nixos/modules/services/misc/devpi-server.nix b/nixos/modules/services/misc/devpi-server.nix
new file mode 100644
index 0000000000000..92c0c6206c8b3
--- /dev/null
+++ b/nixos/modules/services/misc/devpi-server.nix
@@ -0,0 +1,132 @@
+{
+  pkgs,
+  lib,
+  config,
+  ...
+}:
+with lib;
+let
+  cfg = config.services.devpi-server;
+
+  secretsFileName = "devpi-secret-file";
+
+  stateDirName = "devpi";
+
+  runtimeDir = "/run/${stateDirName}";
+  serverDir = "/var/lib/${stateDirName}";
+in
+{
+  options.services.devpi-server = {
+    enable = mkEnableOption "Devpi Server";
+
+    package = mkPackageOption pkgs "devpi-server" { };
+
+    primaryUrl = mkOption {
+      type = types.str;
+      description = "Url for the primary node. Required option for replica nodes.";
+    };
+
+    replica = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Run node as a replica.
+        Requires the secretFile option and the primaryUrl to be enabled.
+      '';
+    };
+
+    secretFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        Path to a shared secret file used for synchronization,
+        Required for all nodes in a replica/primary setup.
+      '';
+    };
+
+    host = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = ''
+        domain/ip address to listen on
+      '';
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 3141;
+      description = "The port on which Devpi Server will listen.";
+    };
+
+    openFirewall = mkEnableOption "opening the default ports in the firewall for Devpi Server";
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.services.devpi-server = {
+      enable = true;
+      description = "devpi PyPI-compatible server";
+      documentation = [ "https://devpi.net/docs/devpi/devpi/stable/+d/index.html" ];
+      wants = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" ];
+      # Since at least devpi-server 6.10.0, devpi requires the secrets file to
+      # have 0600 permissions.
+      preStart =
+        ''
+          ${optionalString (!isNull cfg.secretFile)
+            "install -Dm 0600 \${CREDENTIALS_DIRECTORY}/devpi-secret ${runtimeDir}/${secretsFileName}"
+          }
+
+          if [ -f ${serverDir}/.nodeinfo ]; then
+            # already initialized the package index, exit gracefully
+            exit 0
+          fi
+          ${cfg.package}/bin/devpi-init --serverdir ${serverDir} ''
+        + strings.optionalString cfg.replica "--role=replica --master-url=${cfg.primaryUrl}";
+
+      serviceConfig = {
+        LoadCredential = lib.mkIf (! isNull cfg.secretFile) [
+          "devpi-secret:${cfg.secretFile}"
+        ];
+        Restart = "always";
+        ExecStart =
+          let
+            args =
+              [
+                "--request-timeout=5"
+                "--serverdir=${serverDir}"
+                "--host=${cfg.host}"
+                "--port=${builtins.toString cfg.port}"
+              ]
+              ++ lib.optionals (! isNull cfg.secretFile) [
+                "--secretfile=${runtimeDir}/${secretsFileName}"
+              ]
+              ++ (
+                if cfg.replica then
+                  [
+                    "--role=replica"
+                    "--master-url=${cfg.primaryUrl}"
+                  ]
+                else
+                  [ "--role=master" ]
+              );
+          in
+          "${cfg.package}/bin/devpi-server ${concatStringsSep " " args}";
+        DynamicUser = true;
+        StateDirectory = stateDirName;
+        RuntimeDirectory = stateDirName;
+        PrivateDevices = true;
+        PrivateTmp = true;
+        ProtectHome = true;
+        ProtectSystem = "strict";
+      };
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.port ];
+    };
+
+    meta.maintainers = [ cafkafk ];
+  };
+}
diff --git a/nixos/modules/services/misc/forgejo.nix b/nixos/modules/services/misc/forgejo.nix
index babed2d5acd48..9a102918f35e3 100644
--- a/nixos/modules/services/misc/forgejo.nix
+++ b/nixos/modules/services/misc/forgejo.nix
@@ -12,6 +12,15 @@ let
   usePostgresql = cfg.database.type == "postgres";
   useSqlite = cfg.database.type == "sqlite3";
 
+  secrets = let
+    mkSecret = section: values: lib.mapAttrsToList (key: value: {
+      env = envEscape "FORGEJO__${section}__${key}__FILE";
+      path = value;
+    }) values;
+    # https://codeberg.org/forgejo/forgejo/src/tag/v7.0.2/contrib/environment-to-ini/environment-to-ini.go
+    envEscape = string: lib.replaceStrings [ "." "-" ] [ "_0X2E_" "_0X2D_" ] (lib.strings.toUpper string);
+  in lib.flatten (lib.mapAttrsToList mkSecret cfg.secrets);
+
   inherit (lib)
     literalExpression
     mkChangedOptionModule
@@ -34,6 +43,7 @@ in
     (mkRenamedOptionModule [ "services" "forgejo" "appName" ] [ "services" "forgejo" "settings" "DEFAULT" "APP_NAME" ])
     (mkRemovedOptionModule [ "services" "forgejo" "extraConfig" ] "services.forgejo.extraConfig has been removed. Please use the freeform services.forgejo.settings option instead")
     (mkRemovedOptionModule [ "services" "forgejo" "database" "password" ] "services.forgejo.database.password has been removed. Please use services.forgejo.database.passwordFile instead")
+    (mkRenamedOptionModule [ "services" "forgejo" "mailerPasswordFile" ] [ "services" "forgejo" "secrets" "mailer" "PASSWD" ])
 
     # copied from services.gitea; remove at some point
     (mkRenamedOptionModule [ "services" "forgejo" "cookieSecure" ] [ "services" "forgejo" "settings" "session" "COOKIE_SECURE" ])
@@ -224,13 +234,6 @@ in
         description = "Path to the git repositories.";
       };
 
-      mailerPasswordFile = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "/run/keys/forgejo-mailpw";
-        description = "Path to a file containing the SMTP password.";
-      };
-
       settings = mkOption {
         default = { };
         description = ''
@@ -347,6 +350,44 @@ in
           };
         };
       };
+
+      secrets = mkOption {
+        default = { };
+        description = ''
+          This is a small wrapper over systemd's `LoadCredential`.
+
+          It takes the same sections and keys as {option}`services.forgejo.settings`,
+          but the value of each key is a path instead of a string or bool.
+
+          The path is then loaded as credential, exported as environment variable
+          and then feed through
+          <https://codeberg.org/forgejo/forgejo/src/branch/forgejo/contrib/environment-to-ini/environment-to-ini.go>.
+
+          It does the required environment variable escaping for you.
+
+          ::: {.note}
+          Keys specified here take priority over the ones in {option}`services.forgejo.settings`!
+          :::
+        '';
+        example = literalExpression ''
+        {
+          metrics = {
+            TOKEN = "/run/keys/forgejo-metrics-token";
+          };
+          camo = {
+            HMAC_KEY = "/run/keys/forgejo-camo-hmac";
+          };
+          service = {
+            HCAPTCHA_SECRET = "/run/keys/forgejo-hcaptcha-secret";
+            HCAPTCHA_SITEKEY = "/run/keys/forgejo-hcaptcha-sitekey";
+          };
+        }
+        '';
+        type = types.submodule {
+          freeformType = with types; attrsOf (attrsOf path);
+          options = { };
+        };
+      };
     };
   };
 
@@ -381,7 +422,6 @@ in
           HOST = if cfg.database.socket != null then cfg.database.socket else cfg.database.host + ":" + toString cfg.database.port;
           NAME = cfg.database.name;
           USER = cfg.database.user;
-          PASSWD = "#dbpass#";
         })
         (mkIf useSqlite {
           PATH = cfg.database.path;
@@ -397,7 +437,6 @@ in
 
       server = mkIf cfg.lfs.enable {
         LFS_START_SERVER = true;
-        LFS_JWT_SECRET = "#lfsjwtsecret#";
       };
 
       session = {
@@ -405,21 +444,30 @@ in
       };
 
       security = {
-        SECRET_KEY = "#secretkey#";
-        INTERNAL_TOKEN = "#internaltoken#";
         INSTALL_LOCK = true;
       };
 
-      mailer = mkIf (cfg.mailerPasswordFile != null) {
-        PASSWD = "#mailerpass#";
+      lfs = mkIf cfg.lfs.enable {
+        PATH = cfg.lfs.contentDir;
+      };
+    };
+
+    services.forgejo.secrets = {
+      security = {
+        SECRET_KEY = "${cfg.customDir}/conf/secret_key";
+        INTERNAL_TOKEN = "${cfg.customDir}/conf/internal_token";
       };
 
       oauth2 = {
-        JWT_SECRET = "#oauth2jwtsecret#";
+        JWT_SECRET = "${cfg.customDir}/conf/oauth2_jwt_secret";
       };
 
-      lfs = mkIf cfg.lfs.enable {
-        PATH = cfg.lfs.contentDir;
+      database = mkIf (cfg.database.passwordFile != null) {
+        PASSWD = cfg.database.passwordFile;
+      };
+
+      server = mkIf cfg.lfs.enable {
+        LFS_JWT_SECRET = "${cfg.customDir}/conf/lfs_jwt_secret";
       };
     };
 
@@ -476,6 +524,37 @@ in
       "z '${cfg.lfs.contentDir}' 0750 ${cfg.user} ${cfg.group} - -"
     ];
 
+    systemd.services.forgejo-secrets = mkIf (!cfg.useWizard) {
+      description = "Forgejo secret bootstrap helper";
+      script = ''
+        if [ ! -s '${cfg.secrets.security.SECRET_KEY}' ]; then
+            ${exe} generate secret SECRET_KEY > '${cfg.secrets.security.SECRET_KEY}'
+        fi
+
+        if [ ! -s '${cfg.secrets.oauth2.JWT_SECRET}' ]; then
+            ${exe} generate secret JWT_SECRET > '${cfg.secrets.oauth2.JWT_SECRET}'
+        fi
+
+        ${optionalString cfg.lfs.enable ''
+        if [ ! -s '${cfg.secrets.server.LFS_JWT_SECRET}' ]; then
+            ${exe} generate secret LFS_JWT_SECRET > '${cfg.secrets.server.LFS_JWT_SECRET}'
+        fi
+        ''}
+
+        if [ ! -s '${cfg.secrets.security.INTERNAL_TOKEN}' ]; then
+            ${exe} generate secret INTERNAL_TOKEN > '${cfg.secrets.security.INTERNAL_TOKEN}'
+        fi
+      '';
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        User = cfg.user;
+        Group = cfg.group;
+        ReadWritePaths = [ cfg.customDir ];
+        UMask = "0077";
+      };
+    };
+
     systemd.services.forgejo = {
       description = "Forgejo (Beyond coding. We forge.)";
       after = [
@@ -484,11 +563,15 @@ in
         "postgresql.service"
       ] ++ optionals useMysql [
         "mysql.service"
+      ] ++ optionals (!cfg.useWizard) [
+        "forgejo-secrets.service"
       ];
       requires = optionals (cfg.database.createDatabase && usePostgresql) [
         "postgresql.service"
       ] ++ optionals (cfg.database.createDatabase && useMysql) [
         "mysql.service"
+      ] ++ optionals (!cfg.useWizard) [
+        "forgejo-secrets.service"
       ];
       wantedBy = [ "multi-user.target" ];
       path = [ cfg.package pkgs.git pkgs.gnupg ];
@@ -501,61 +584,15 @@ in
       # lfs_jwt_secret.
       # We have to consider this to stay compatible with older installations.
       preStart =
-        let
-          runConfig = "${cfg.customDir}/conf/app.ini";
-          secretKey = "${cfg.customDir}/conf/secret_key";
-          oauth2JwtSecret = "${cfg.customDir}/conf/oauth2_jwt_secret";
-          oldLfsJwtSecret = "${cfg.customDir}/conf/jwt_secret"; # old file for LFS_JWT_SECRET
-          lfsJwtSecret = "${cfg.customDir}/conf/lfs_jwt_secret"; # new file for LFS_JWT_SECRET
-          internalToken = "${cfg.customDir}/conf/internal_token";
-          replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret";
-        in
         ''
-          # copy custom configuration and generate random secrets if needed
-          ${lib.optionalString (!cfg.useWizard) ''
+          ${optionalString (!cfg.useWizard) ''
             function forgejo_setup {
-              cp -f '${format.generate "app.ini" cfg.settings}' '${runConfig}'
-
-              if [ ! -s '${secretKey}' ]; then
-                  ${exe} generate secret SECRET_KEY > '${secretKey}'
-              fi
-
-              # Migrate LFS_JWT_SECRET filename
-              if [[ -s '${oldLfsJwtSecret}' && ! -s '${lfsJwtSecret}' ]]; then
-                  mv '${oldLfsJwtSecret}' '${lfsJwtSecret}'
-              fi
-
-              if [ ! -s '${oauth2JwtSecret}' ]; then
-                  ${exe} generate secret JWT_SECRET > '${oauth2JwtSecret}'
-              fi
-
-              ${optionalString cfg.lfs.enable ''
-              if [ ! -s '${lfsJwtSecret}' ]; then
-                  ${exe} generate secret LFS_JWT_SECRET > '${lfsJwtSecret}'
-              fi
-              ''}
-
-              if [ ! -s '${internalToken}' ]; then
-                  ${exe} generate secret INTERNAL_TOKEN > '${internalToken}'
-              fi
-
-              chmod u+w '${runConfig}'
-              ${replaceSecretBin} '#secretkey#' '${secretKey}' '${runConfig}'
-              ${replaceSecretBin} '#oauth2jwtsecret#' '${oauth2JwtSecret}' '${runConfig}'
-              ${replaceSecretBin} '#internaltoken#' '${internalToken}' '${runConfig}'
-
-              ${optionalString cfg.lfs.enable ''
-                ${replaceSecretBin} '#lfsjwtsecret#' '${lfsJwtSecret}' '${runConfig}'
-              ''}
-
-              ${optionalString (cfg.database.passwordFile != null) ''
-                ${replaceSecretBin} '#dbpass#' '${cfg.database.passwordFile}' '${runConfig}'
-              ''}
-
-              ${optionalString (cfg.mailerPasswordFile != null) ''
-                ${replaceSecretBin} '#mailerpass#' '${cfg.mailerPasswordFile}' '${runConfig}'
-              ''}
-              chmod u-w '${runConfig}'
+              config='${cfg.customDir}/conf/app.ini'
+              cp -f '${format.generate "app.ini" cfg.settings}' "$config"
+
+              chmod u+w "$config"
+              ${lib.getExe' cfg.package "environment-to-ini"} --config "$config"
+              chmod u-w "$config"
             }
             (umask 027; forgejo_setup)
           ''}
@@ -616,6 +653,8 @@ in
         # System Call Filtering
         SystemCallArchitectures = "native";
         SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid" "setrlimit" ];
+        # cfg.secrets
+        LoadCredential = map (e: "${e.env}:${e.path}") secrets;
       };
 
       environment = {
@@ -625,7 +664,7 @@ in
         # is resolved.
         GITEA_WORK_DIR = cfg.stateDir;
         GITEA_CUSTOM = cfg.customDir;
-      };
+      } // lib.listToAttrs (map (e: lib.nameValuePair e.env "%d/${e.env}") secrets);
     };
 
     services.openssh.settings.AcceptEnv = mkIf (!cfg.settings.START_SSH_SERVER or false) "GIT_PROTOCOL";
diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix
index a8526688b074f..d43250c882683 100644
--- a/nixos/modules/services/misc/gitea.nix
+++ b/nixos/modules/services/misc/gitea.nix
@@ -722,5 +722,5 @@ in
       timerConfig.OnCalendar = cfg.dump.interval;
     };
   };
-  meta.maintainers = with lib.maintainers; [ srhb ma27 pyrox0 ];
+  meta.maintainers = with lib.maintainers; [ ma27 techknowlogick SuperSandro2000 ];
 }
diff --git a/nixos/modules/services/misc/gollum.nix b/nixos/modules/services/misc/gollum.nix
index 3966ef036bec4..f320e78a91060 100644
--- a/nixos/modules/services/misc/gollum.nix
+++ b/nixos/modules/services/misc/gollum.nix
@@ -110,7 +110,7 @@ in
     users.groups."${cfg.group}" = { };
 
     systemd.tmpfiles.rules = [
-      "d '${cfg.stateDir}' - ${config.users.users.gollum.name} ${config.users.groups.gollum.name} - -"
+      "d '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -"
     ];
 
     systemd.services.gollum = {
diff --git a/nixos/modules/services/misc/graphical-desktop.nix b/nixos/modules/services/misc/graphical-desktop.nix
index a88c02e610bf4..c8fe0d921c6ad 100644
--- a/nixos/modules/services/misc/graphical-desktop.nix
+++ b/nixos/modules/services/misc/graphical-desktop.nix
@@ -38,7 +38,7 @@ in
 
     fonts.enableDefaultPackages = lib.mkDefault true;
 
-    hardware.opengl.enable = lib.mkDefault true;
+    hardware.graphics.enable = lib.mkDefault true;
 
     programs.gnupg.agent.pinentryPackage = lib.mkOverride 1100 pkgs.pinentry-gnome3;
 
diff --git a/nixos/modules/services/misc/invidious-router.nix b/nixos/modules/services/misc/invidious-router.nix
index 33da7e96b5235..7a90c6ab9ddc0 100644
--- a/nixos/modules/services/misc/invidious-router.nix
+++ b/nixos/modules/services/misc/invidious-router.nix
@@ -8,10 +8,10 @@
   settingsFormat = pkgs.formats.yaml {};
   configFile = settingsFormat.generate "config.yaml" cfg.settings;
 in {
-  meta.maintainers = [lib.maintainers.s1ls];
+  meta.maintainers = [lib.maintainers.sils];
 
   options.services.invidious-router = {
-    enable = lib.mkEnableOption "Enables the invidious-router service";
+    enable = lib.mkEnableOption "the invidious-router service";
     port = lib.mkOption {
       type = lib.types.port;
       default = 8050;
diff --git a/nixos/modules/services/misc/jellyfin.nix b/nixos/modules/services/misc/jellyfin.nix
index a1d3910bd93b0..a006090878422 100644
--- a/nixos/modules/services/misc/jellyfin.nix
+++ b/nixos/modules/services/misc/jellyfin.nix
@@ -160,5 +160,5 @@ in
 
   };
 
-  meta.maintainers = with maintainers; [ minijackson nu-nu-ko ];
+  meta.maintainers = with maintainers; [ minijackson fsnkty ];
 }
diff --git a/nixos/modules/services/misc/llama-cpp.nix b/nixos/modules/services/misc/llama-cpp.nix
index c73cff027e224..2fa1a7b28e3b1 100644
--- a/nixos/modules/services/misc/llama-cpp.nix
+++ b/nixos/modules/services/misc/llama-cpp.nix
@@ -92,7 +92,6 @@ in {
         SystemCallFilter = [
           "@system-service"
           "~@privileged"
-          "~@resources"
         ];
         SystemCallErrorNumber = "EPERM";
         ProtectProc = "invisible";
diff --git a/nixos/modules/services/misc/mqtt2influxdb.nix b/nixos/modules/services/misc/mqtt2influxdb.nix
index a2d6a2b34a239..925139b449b8e 100644
--- a/nixos/modules/services/misc/mqtt2influxdb.nix
+++ b/nixos/modules/services/misc/mqtt2influxdb.nix
@@ -125,6 +125,7 @@ in {
   options = {
     services.mqtt2influxdb = {
       enable = mkEnableOption "BigClown MQTT to InfluxDB bridge.";
+      package = mkPackageOption pkgs ["python3Packages" "mqtt2influxdb"] {};
       environmentFiles = mkOption {
         type = types.listOf types.path;
         default = [];
@@ -245,7 +246,7 @@ in {
       '';
       serviceConfig = {
         EnvironmentFile = cfg.environmentFiles;
-        ExecStart = "${cfg.package}/bin/mqtt2influxdb -dc ${finalConfig}";
+        ExecStart = "${lib.getExe cfg.package} -dc ${finalConfig}";
         RuntimeDirectory = "mqtt2influxdb";
       };
     };
diff --git a/nixos/modules/services/misc/ollama.nix b/nixos/modules/services/misc/ollama.nix
index c0341984aa351..1467c3f93bc85 100644
--- a/nixos/modules/services/misc/ollama.nix
+++ b/nixos/modules/services/misc/ollama.nix
@@ -11,6 +11,11 @@ let
   };
 in
 {
+  imports = [
+    (lib.mkRemovedOptionModule [ "services" "ollama" "listenAddress" ]
+      "Use `services.ollama.host` and `services.ollama.port` instead.")
+  ];
+
   options = {
     services.ollama = {
       enable = lib.mkEnableOption "ollama server for local large language models";
@@ -64,12 +69,20 @@ in
           See also `services.ollama.sandbox`.
         '';
       };
-      listenAddress = lib.mkOption {
+      host = lib.mkOption {
         type = types.str;
-        default = "127.0.0.1:11434";
-        example = "0.0.0.0:11111";
+        default = "127.0.0.1";
+        example = "0.0.0.0";
+        description = ''
+          The host address which the ollama server HTTP interface listens to.
+        '';
+      };
+      port = lib.mkOption {
+        type = types.port;
+        default = 11434;
+        example = 11111;
         description = ''
-          The address which the ollama server HTTP interface binds and listens to.
+          Which port the ollama server listens to.
         '';
       };
       acceleration = lib.mkOption {
@@ -80,14 +93,30 @@ in
           What interface to use for hardware acceleration.
 
           - `null`: default behavior
-            if `nixpkgs.config.rocmSupport` is enabled, uses `"rocm"`
-            if `nixpkgs.config.cudaSupport` is enabled, uses `"cuda"`
-            otherwise defaults to `false`
+            - if `nixpkgs.config.rocmSupport` is enabled, uses `"rocm"`
+            - if `nixpkgs.config.cudaSupport` is enabled, uses `"cuda"`
+            - otherwise defaults to `false`
           - `false`: disable GPU, only use CPU
           - `"rocm"`: supported by most modern AMD GPUs
+            - may require overriding gpu type with `services.ollama.rocmOverrideGfx`
+              if rocm doesn't detect your AMD gpu
           - `"cuda"`: supported by most modern NVIDIA GPUs
         '';
       };
+      rocmOverrideGfx = lib.mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "10.3.0";
+        description = ''
+          Override what rocm will detect your gpu model as.
+          For example, make rocm treat your RX 5700 XT (or any other model)
+          as an RX 6900 XT using a value of `"10.3.0"` (gfx 1030).
+
+          This sets the value of `HSA_OVERRIDE_GFX_VERSION`. See [ollama's docs](
+          https://github.com/ollama/ollama/blob/main/docs/gpu.md#amd-radeon
+          ) for details.
+        '';
+      };
       environmentVariables = lib.mkOption {
         type = types.attrsOf types.str;
         default = { };
@@ -103,6 +132,14 @@ in
           Since `ollama run` is mostly a shell around the ollama server, this is usually sufficient.
         '';
       };
+      openFirewall = lib.mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to open the firewall for ollama.
+          This adds `services.ollama.port` to `networking.firewall.allowedTCPPorts`.
+        '';
+      };
     };
   };
 
@@ -114,7 +151,8 @@ in
       environment = cfg.environmentVariables // {
         HOME = cfg.home;
         OLLAMA_MODELS = cfg.models;
-        OLLAMA_HOST = cfg.listenAddress;
+        OLLAMA_HOST = "${cfg.host}:${toString cfg.port}";
+        HSA_OVERRIDE_GFX_VERSION = lib.mkIf (cfg.rocmOverrideGfx != null) cfg.rocmOverrideGfx;
       };
       serviceConfig = {
         ExecStart = "${lib.getExe ollamaPackage} serve";
@@ -125,6 +163,8 @@ in
       };
     };
 
+    networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; };
+
     environment.systemPackages = [ ollamaPackage ];
   };
 
diff --git a/nixos/modules/services/misc/open-webui.nix b/nixos/modules/services/misc/open-webui.nix
new file mode 100644
index 0000000000000..b4016d03f675f
--- /dev/null
+++ b/nixos/modules/services/misc/open-webui.nix
@@ -0,0 +1,114 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+
+  cfg = config.services.open-webui;
+in
+{
+  options = {
+    services.open-webui = {
+      enable = lib.mkEnableOption "Open-WebUI server";
+      package = lib.mkPackageOption pkgs "open-webui" { };
+
+      stateDir = lib.mkOption {
+        type = types.path;
+        default = "/var/lib/open-webui";
+        example = "/home/foo";
+        description = "State directory of Open-WebUI.";
+      };
+
+      host = lib.mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        example = "0.0.0.0";
+        description = ''
+          The host address which the Open-WebUI server HTTP interface listens to.
+        '';
+      };
+
+      port = lib.mkOption {
+        type = types.port;
+        default = 8080;
+        example = 11111;
+        description = ''
+          Which port the Open-WebUI server listens to.
+        '';
+      };
+
+      environment = lib.mkOption {
+        type = types.attrsOf types.str;
+        default = {
+          SCARF_NO_ANALYTICS = "True";
+          DO_NOT_TRACK = "True";
+          ANONYMIZED_TELEMETRY = "False";
+        };
+        example = ''
+          {
+            OLLAMA_API_BASE_URL = "http://127.0.0.1:11434";
+            # Disable authentication
+            WEBUI_AUTH = "False";
+          }
+        '';
+        description = "Extra environment variables for Open-WebUI";
+      };
+
+      openFirewall = lib.mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to open the firewall for Open-WebUI.
+          This adds `services.open-webui.port` to `networking.firewall.allowedTCPPorts`.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.open-webui = {
+      description = "User-friendly WebUI for LLMs";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      environment = {
+        STATIC_DIR = ".";
+        DATA_DIR = ".";
+        HF_HOME = ".";
+        SENTENCE_TRANSFORMERS_HOME = ".";
+      } // cfg.environment;
+
+      serviceConfig = {
+        ExecStart = "${lib.getExe cfg.package} serve --host ${cfg.host} --port ${toString cfg.port}";
+        WorkingDirectory = cfg.stateDir;
+        StateDirectory = "open-webui";
+        RuntimeDirectory = "open-webui";
+        RuntimeDirectoryMode = "0755";
+        PrivateTmp = true;
+        DynamicUser = true;
+        DevicePolicy = "closed";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = false; # onnxruntime/capi/onnxruntime_pybind11_state.so: cannot enable executable stack as shared object requires: Permission Denied
+        PrivateUsers = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectControlGroups = true;
+        ProcSubset = "all"; # Error in cpuinfo: failed to parse processor information from /proc/cpuinfo
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        SystemCallArchitectures = "native";
+        UMask = "0077";
+      };
+    };
+
+    networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; };
+  };
+
+  meta.maintainers = with lib.maintainers; [ shivaraj-bh ];
+}
diff --git a/nixos/modules/services/misc/pghero.nix b/nixos/modules/services/misc/pghero.nix
new file mode 100644
index 0000000000000..39515f10c8e1d
--- /dev/null
+++ b/nixos/modules/services/misc/pghero.nix
@@ -0,0 +1,142 @@
+{ config, pkgs, lib, utils, ... }:
+let
+  cfg = config.services.pghero;
+  settingsFormat = pkgs.formats.yaml { };
+  settingsFile = settingsFormat.generate "pghero.yaml" cfg.settings;
+in
+{
+  options.services.pghero = {
+    enable = lib.mkEnableOption "PgHero service";
+    package = lib.mkPackageOption pkgs "pghero" { };
+
+    listenAddress = lib.mkOption {
+      type = lib.types.str;
+      example = "[::1]:3000";
+      description = ''
+        `hostname:port` to listen for HTTP traffic.
+
+        This is bound using the systemd socket activation.
+      '';
+    };
+
+    extraArgs = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      default = [ ];
+      description = ''
+        Additional command-line arguments for the systemd service.
+
+        Refer to the [Puma web server documentation] for available arguments.
+
+        [Puma web server documentation]: https://puma.io/puma#configuration
+      '';
+    };
+
+    settings = lib.mkOption {
+      type = settingsFormat.type;
+      default = { };
+      example = {
+        databases = {
+          primary = {
+            url = "<%= ENV['PRIMARY_DATABASE_URL'] %>";
+          };
+        };
+      };
+      description = ''
+        PgHero configuration. Refer to the [PgHero documentation] for more
+        details.
+
+        [PgHero documentation]: https://github.com/ankane/pghero/blob/master/guides/Linux.md#multiple-databases
+      '';
+    };
+
+    environment = lib.mkOption {
+      type = lib.types.attrsOf lib.types.str;
+      default = { };
+      description = ''
+        Environment variables to set for the service. Secrets should be
+        specified using {option}`environmentFile`.
+      '';
+    };
+
+    environmentFiles = lib.mkOption {
+      type = lib.types.listOf lib.types.path;
+      default = [ ];
+      description = ''
+        File to load environment variables from. Loaded variables override
+        values set in {option}`environment`.
+      '';
+    };
+
+    extraGroups = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      default = [ ];
+      example = [ "tlskeys" ];
+      description = ''
+        Additional groups for the systemd service.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.sockets.pghero = {
+      unitConfig.Description = "PgHero HTTP socket";
+      wantedBy = [ "sockets.target" ];
+      listenStreams = [ cfg.listenAddress ];
+    };
+
+    systemd.services.pghero = {
+      description = "PgHero performance dashboard for PostgreSQL";
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "pghero.socket" ];
+      after = [ "pghero.socket" "network.target" ];
+
+      environment = {
+        RAILS_ENV = "production";
+        PGHERO_CONFIG_PATH = settingsFile;
+      } // cfg.environment;
+
+      serviceConfig = {
+        Type = "notify";
+        WatchdogSec = "10";
+
+        ExecStart = utils.escapeSystemdExecArgs ([
+          (lib.getExe cfg.package)
+          "--bind-to-activated-sockets"
+          "only"
+        ] ++ cfg.extraArgs);
+        Restart = "always";
+
+        WorkingDirectory = "${cfg.package}/share/pghero";
+
+        EnvironmentFile = cfg.environmentFiles;
+        SupplementaryGroups = cfg.extraGroups;
+
+        DynamicUser = true;
+        UMask = "0077";
+
+        ProtectHome = true;
+        ProtectProc = "invisible";
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectHostname = true;
+        ProtectControlGroups = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        PrivateUsers = true;
+        PrivateDevices = true;
+        RestrictRealtime = true;
+        RestrictNamespaces = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
+        DeviceAllow = [ "" ];
+        DevicePolicy = "closed";
+        CapabilityBoundingSet = [ "" ];
+        MemoryDenyWriteExecute = true;
+        LockPersonality = true;
+        SystemCallArchitectures = "native";
+        SystemCallErrorNumber = "EPERM";
+        SystemCallFilter = [ "@system-service" ];
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/plex.nix b/nixos/modules/services/misc/plex.nix
index fcd8ebbac6edd..212abda5d1e0b 100644
--- a/nixos/modules/services/misc/plex.nix
+++ b/nixos/modules/services/misc/plex.nix
@@ -93,6 +93,17 @@ in
         '';
       };
 
+      accelerationDevices = mkOption {
+        type = types.listOf types.str;
+        default = ["*"];
+        example = [ "/dev/dri/renderD128" ];
+        description = ''
+          A list of device paths to hardware acceleration devices that Plex should
+          have access to. This is useful when transcoding media files.
+          The special value `"*"` will allow all devices.
+        '';
+      };
+
       package = mkPackageOption pkgs "plex" {
         extraDescription = ''
           Plex subscribers may wish to use their own package here,
@@ -133,6 +144,24 @@ in
         KillSignal = "SIGQUIT";
         PIDFile = "${cfg.dataDir}/Plex Media Server/plexmediaserver.pid";
         Restart = "on-failure";
+
+        # Hardening
+        NoNewPrivileges = true;
+        PrivateTmp = true;
+        PrivateDevices = cfg.accelerationDevices == [];
+        DeviceAllow = mkIf (cfg.accelerationDevices != [] && !lib.elem "*" cfg.accelerationDevices) cfg.accelerationDevices;
+        ProtectSystem = true;
+        ProtectHome = true;
+        ProtectControlGroups = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK"];
+        # This could be made to work if the namespaces needed were known
+        # RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        MemoryDenyWriteExecute = true;
+        LockPersonality = true;
       };
 
       environment = {
diff --git a/nixos/modules/services/misc/portunus.nix b/nixos/modules/services/misc/portunus.nix
index ab78479c96cd3..c7abb2cfa2a3e 100644
--- a/nixos/modules/services/misc/portunus.nix
+++ b/nixos/modules/services/misc/portunus.nix
@@ -98,6 +98,10 @@ in
 
           The OIDC secret must be set as the `DEX_CLIENT_''${id}` environment variable
           in the [](#opt-services.dex.environmentFile) setting.
+
+          ::: {.note}
+          Make sure the id only contains characters that are allowed in an environment variable name, e.g. no -.
+          :::
         '';
       };
 
@@ -111,10 +115,7 @@ in
     ldap = {
       package = mkOption {
         type = types.package;
-        # needs openldap built with a libxcrypt that support crypt sha256 until users have had time to migrate to newer hashes
-        # Ref: <https://github.com/majewsky/portunus/issues/2>
-        # TODO: remove in NixOS 24.11 (cf. same note on pkgs/servers/portunus/default.nix)
-        default = pkgs.openldap.override { libxcrypt = pkgs.libxcrypt-legacy; };
+        default = pkgs.openldap;
         defaultText = lib.literalExpression "pkgs.openldap.override { libxcrypt = pkgs.libxcrypt-legacy; }";
         description = "The OpenLDAP package to use.";
       };
@@ -231,12 +232,14 @@ in
     };
 
     systemd.services = {
-      dex.serviceConfig = mkIf cfg.dex.enable {
-        # `dex.service` is super locked down out of the box, but we need some
-        # place to write the SQLite database. This creates $STATE_DIRECTORY below
-        # /var/lib/private because DynamicUser=true, but it gets symlinked into
-        # /var/lib/dex inside the unit
-        StateDirectory = "dex";
+      dex = mkIf cfg.dex.enable {
+        serviceConfig = {
+          # `dex.service` is super locked down out of the box, but we need some
+          # place to write the SQLite database. This creates $STATE_DIRECTORY below
+          # /var/lib/private because DynamicUser=true, but it gets symlinked into
+          # /var/lib/dex inside the unit
+          StateDirectory = "dex";
+        };
       };
 
       portunus = {
diff --git a/nixos/modules/services/misc/private-gpt.nix b/nixos/modules/services/misc/private-gpt.nix
new file mode 100644
index 0000000000000..ad9b6f5ffa80f
--- /dev/null
+++ b/nixos/modules/services/misc/private-gpt.nix
@@ -0,0 +1,121 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+let
+  inherit (lib) types;
+
+  format = pkgs.formats.yaml { };
+  cfg = config.services.private-gpt;
+in
+{
+  options = {
+    services.private-gpt = {
+      enable = lib.mkEnableOption "private-gpt for local large language models";
+      package = lib.mkPackageOption pkgs "private-gpt" { };
+
+      stateDir = lib.mkOption {
+        type = types.path;
+        default = "/var/lib/private-gpt";
+        description = "State directory of private-gpt.";
+      };
+
+      settings = lib.mkOption {
+        type = format.type;
+        default = {
+          llm = {
+            mode = "ollama";
+            tokenizer = "";
+          };
+          embedding = {
+            mode = "ollama";
+          };
+          ollama = {
+            llm_model = "llama3";
+            embedding_model = "nomic-embed-text";
+            api_base = "http://localhost:11434";
+            embedding_api_base = "http://localhost:11434";
+            keep_alive = "5m";
+            tfs_z = 1;
+            top_k = 40;
+            top_p = 0.9;
+            repeat_last_n = 64;
+            repeat_penalty = 1.2;
+            request_timeout = 120;
+          };
+          vectorstore = {
+            database = "qdrant";
+          };
+          qdrant = {
+            path = "/var/lib/private-gpt/vectorstore/qdrant";
+          };
+          data = {
+            local_data_folder = "/var/lib/private-gpt";
+          };
+          openai = { };
+          azopenai = { };
+        };
+        description = ''
+          settings-local.yaml for private-gpt
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.private-gpt = {
+      description = "Interact with your documents using the power of GPT, 100% privately, no data leaks";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      preStart =
+        let
+          config = format.generate "settings-local.yaml" (cfg.settings // { server.env_name = "local"; });
+        in
+        ''
+          mkdir -p ${cfg.stateDir}/{settings,huggingface,matplotlib,tiktoken_cache}
+          cp ${cfg.package.cl100k_base.tiktoken} ${cfg.stateDir}/tiktoken_cache/9b5ad71b2ce5302211f9c61530b329a4922fc6a4
+          cp ${pkgs.python3Packages.private-gpt}/${pkgs.python3.sitePackages}/private_gpt/settings.yaml ${cfg.stateDir}/settings/settings.yaml
+          cp "${config}" "${cfg.stateDir}/settings/settings-local.yaml"
+          chmod 600 "${cfg.stateDir}/settings/settings-local.yaml"
+        '';
+
+      environment = {
+        PGPT_PROFILES = "local";
+        PGPT_SETTINGS_FOLDER = "${cfg.stateDir}/settings";
+        HF_HOME = "${cfg.stateDir}/huggingface";
+        TRANSFORMERS_OFFLINE = "1";
+        HF_DATASETS_OFFLINE = "1";
+        MPLCONFIGDIR = "${cfg.stateDir}/matplotlib";
+      };
+
+      serviceConfig = {
+        ExecStart = lib.getExe cfg.package;
+        WorkingDirectory = cfg.stateDir;
+        StateDirectory = "private-gpt";
+        RuntimeDirectory = "private-gpt";
+        RuntimeDirectoryMode = "0755";
+        PrivateTmp = true;
+        DynamicUser = true;
+        DevicePolicy = "closed";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        PrivateUsers = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectControlGroups = true;
+        ProcSubset = "pid";
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        SystemCallArchitectures = "native";
+        UMask = "0077";
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ ];
+}
diff --git a/nixos/modules/services/misc/renovate.nix b/nixos/modules/services/misc/renovate.nix
new file mode 100644
index 0000000000000..25a719c91cbd8
--- /dev/null
+++ b/nixos/modules/services/misc/renovate.nix
@@ -0,0 +1,153 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib)
+    mkEnableOption
+    mkPackageOption
+    mkOption
+    types
+    mkIf
+    ;
+  json = pkgs.formats.json { };
+  cfg = config.services.renovate;
+  generateValidatedConfig =
+    name: value:
+    pkgs.callPackage (
+      { runCommand, jq }:
+      runCommand name
+        {
+          nativeBuildInputs = [
+            jq
+            cfg.package
+          ];
+          value = builtins.toJSON value;
+          passAsFile = [ "value" ];
+          preferLocalBuild = true;
+        }
+        ''
+          jq . "$valuePath"> $out
+          renovate-config-validator $out
+        ''
+    ) { };
+  generateConfig = if cfg.validateSettings then generateValidatedConfig else json.generate;
+in
+{
+  meta.maintainers = with lib.maintainers; [ marie natsukium ];
+
+  options.services.renovate = {
+    enable = mkEnableOption "renovate";
+    package = mkPackageOption pkgs "renovate" { };
+    schedule = mkOption {
+      type = with types; nullOr str;
+      description = "How often to run renovate. See {manpage}`systemd.time(7)` for the format.";
+      example = "*:0/10";
+      default = null;
+    };
+    credentials = mkOption {
+      type = with types; attrsOf path;
+      description = ''
+        Allows configuring environment variable credentials for renovate, read from files.
+        This should always be used for passing confidential data to renovate.
+      '';
+      example = {
+        RENOVATE_TOKEN = "/etc/renovate/token";
+      };
+      default = { };
+    };
+    runtimePackages = mkOption {
+      type = with types; listOf package;
+      description = "Packages available to renovate.";
+      default = [ ];
+    };
+    validateSettings = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Weither to run renovate's config validator on the built configuration.";
+    };
+    settings = mkOption {
+      type = json.type;
+      default = { };
+      example = {
+        platform = "gitea";
+        endpoint = "https://git.example.com";
+        gitAuthor = "Renovate <renovate@example.com>";
+      };
+      description = ''
+        Renovate's global configuration.
+        If you want to pass secrets to renovate, please use {option}`services.renovate.credentials` for that.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.renovate.settings = {
+      cacheDir = "/var/cache/renovate";
+      baseDir = "/var/lib/renovate";
+    };
+
+    systemd.services.renovate = {
+      description = "Renovate dependency updater";
+      documentation = [ "https://docs.renovatebot.com/" ];
+      after = [ "network.target" ];
+      startAt = lib.optional (cfg.schedule != null) cfg.schedule;
+      path = [
+        config.systemd.package
+        pkgs.git
+      ] ++ cfg.runtimePackages;
+
+      serviceConfig = {
+        Type = "oneshot";
+        User = "renovate";
+        Group = "renovate";
+        DynamicUser = true;
+        LoadCredential = lib.mapAttrsToList (name: value: "SECRET-${name}:${value}") cfg.credentials;
+        RemainAfterExit = false;
+        Restart = "on-failure";
+        CacheDirectory = "renovate";
+        StateDirectory = "renovate";
+
+        # Hardening
+        CapabilityBoundingSet = [ "" ];
+        DeviceAllow = [ "" ];
+        LockPersonality = true;
+        PrivateDevices = true;
+        PrivateUsers = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        SystemCallArchitectures = "native";
+        UMask = "0077";
+      };
+
+      script = ''
+        ${lib.concatStringsSep "\n" (
+          builtins.map (name: "export ${name}=$(systemd-creds cat 'SECRET-${name}')") (
+            lib.attrNames cfg.credentials
+          )
+        )}
+        exec ${lib.escapeShellArg (lib.getExe cfg.package)}
+      '';
+
+      environment = {
+        RENOVATE_CONFIG_FILE = generateConfig "renovate-config.json" cfg.settings;
+        HOME = "/var/lib/renovate";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/snapper.nix b/nixos/modules/services/misc/snapper.nix
index 3a3ed1b5c0f56..1b16ef7958ad2 100644
--- a/nixos/modules/services/misc/snapper.nix
+++ b/nixos/modules/services/misc/snapper.nix
@@ -1,16 +1,32 @@
-{ config, pkgs, lib, ... }:
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
 
 with lib;
 
 let
   cfg = config.services.snapper;
 
-  mkValue = v:
-    if isList v then "\"${concatMapStringsSep " " (escape [ "\\" " " ]) v}\""
-    else if v == true then "yes"
-    else if v == false then "no"
-    else if isString v then "\"${v}\""
-    else builtins.toJSON v;
+  mkValue =
+    v:
+    if isList v then
+      "\"${
+        concatMapStringsSep " " (escape [
+          "\\"
+          " "
+        ]) v
+      }\""
+    else if v == true then
+      "yes"
+    else if v == false then
+      "no"
+    else if isString v then
+      "\"${v}\""
+    else
+      builtins.toJSON v;
 
   mkKeyValue = k: v: "${k}=${mkValue v}";
 
@@ -43,7 +59,7 @@ let
 
     ALLOW_GROUPS = mkOption {
       type = types.listOf safeStr;
-      default = [];
+      default = [ ];
       description = ''
         List of groups allowed to operate with the config.
 
@@ -53,7 +69,7 @@ let
 
     ALLOW_USERS = mkOption {
       type = types.listOf safeStr;
-      default = [];
+      default = [ ];
       example = [ "alice" ];
       description = ''
         List of users allowed to operate with the config. "root" is always
@@ -78,6 +94,54 @@ let
         Defines whether hourly snapshots should be created.
       '';
     };
+
+    TIMELINE_LIMIT_HOURLY = mkOption {
+      type = types.str;
+      default = "10";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_DAILY = mkOption {
+      type = types.str;
+      default = "10";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_WEEKLY = mkOption {
+      type = types.str;
+      default = "0";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_MONTHLY = mkOption {
+      type = types.str;
+      default = "10";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_QUARTERLY = mkOption {
+      type = types.str;
+      default = "0";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_YEARLY = mkOption {
+      type = types.str;
+      default = "10";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
   };
 in
 
@@ -103,6 +167,18 @@ in
       '';
     };
 
+    persistentTimer = mkOption {
+      default = false;
+      type = types.bool;
+      example = true;
+      description = ''
+        Set the `Persistent` option for the
+        {manpage}`systemd.timer(5)`
+        which triggers the snapshot immediately if the last trigger
+        was missed (e.g. if the system was powered down).
+      '';
+    };
+
     cleanupInterval = mkOption {
       type = types.str;
       default = "1d";
@@ -140,105 +216,129 @@ in
         is valid here, even if NixOS doesn't document it.
       '';
 
-      type = types.attrsOf (types.submodule {
-        freeformType = types.attrsOf (types.oneOf [ (types.listOf safeStr) types.bool safeStr types.number ]);
-
-        options = configOptions;
-      });
+      type = types.attrsOf (
+        types.submodule {
+          freeformType = types.attrsOf (
+            types.oneOf [
+              (types.listOf safeStr)
+              types.bool
+              safeStr
+              types.number
+            ]
+          );
+
+          options = configOptions;
+        }
+      );
     };
   };
 
-  config = mkIf (cfg.configs != {}) (let
-    documentation = [ "man:snapper(8)" "man:snapper-configs(5)" ];
-  in {
-
-    environment = {
-
-      systemPackages = [ pkgs.snapper ];
-
-      # Note: snapper/config-templates/default is only needed for create-config
-      #       which is not the NixOS way to configure.
-      etc = {
-
-        "sysconfig/snapper".text = ''
-          SNAPPER_CONFIGS="${lib.concatStringsSep " " (builtins.attrNames cfg.configs)}"
-        '';
-
-      }
-      // (mapAttrs' (name: subvolume: nameValuePair "snapper/configs/${name}" ({
-        text = lib.generators.toKeyValue { inherit mkKeyValue; } (filterAttrs (k: v: v != defaultOf k) subvolume);
-      })) cfg.configs)
-      // (lib.optionalAttrs (cfg.filters != null) {
-        "snapper/filters/default.txt".text = cfg.filters;
-      });
-
-    };
+  config = mkIf (cfg.configs != { }) (
+    let
+      documentation = [
+        "man:snapper(8)"
+        "man:snapper-configs(5)"
+      ];
+    in
+    {
+      environment = {
+
+        systemPackages = [ pkgs.snapper ];
+
+        # Note: snapper/config-templates/default is only needed for create-config
+        #       which is not the NixOS way to configure.
+        etc =
+          {
+
+            "sysconfig/snapper".text = ''
+              SNAPPER_CONFIGS="${lib.concatStringsSep " " (builtins.attrNames cfg.configs)}"
+            '';
+          }
+          // (mapAttrs' (
+            name: subvolume:
+            nameValuePair "snapper/configs/${name}" ({
+              text = lib.generators.toKeyValue { inherit mkKeyValue; } (
+                filterAttrs (k: v: v != defaultOf k) subvolume
+              );
+            })
+          ) cfg.configs)
+          // (lib.optionalAttrs (cfg.filters != null) { "snapper/filters/default.txt".text = cfg.filters; });
+      };
 
-    services.dbus.packages = [ pkgs.snapper ];
+      services.dbus.packages = [ pkgs.snapper ];
+
+      systemd.services.snapperd = {
+        description = "DBus interface for snapper";
+        inherit documentation;
+        serviceConfig = {
+          Type = "dbus";
+          BusName = "org.opensuse.Snapper";
+          ExecStart = "${pkgs.snapper}/bin/snapperd";
+          CapabilityBoundingSet = "CAP_DAC_OVERRIDE CAP_FOWNER CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_SYS_ADMIN CAP_SYS_MODULE CAP_IPC_LOCK CAP_SYS_NICE";
+          LockPersonality = true;
+          NoNewPrivileges = false;
+          PrivateNetwork = true;
+          ProtectHostname = true;
+          RestrictAddressFamilies = "AF_UNIX";
+          RestrictRealtime = true;
+        };
+      };
 
-    systemd.services.snapperd = {
-      description = "DBus interface for snapper";
-      inherit documentation;
-      serviceConfig = {
-        Type = "dbus";
-        BusName = "org.opensuse.Snapper";
-        ExecStart = "${pkgs.snapper}/bin/snapperd";
-        CapabilityBoundingSet = "CAP_DAC_OVERRIDE CAP_FOWNER CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_SYS_ADMIN CAP_SYS_MODULE CAP_IPC_LOCK CAP_SYS_NICE";
-        LockPersonality = true;
-        NoNewPrivileges = false;
-        PrivateNetwork = true;
-        ProtectHostname = true;
-        RestrictAddressFamilies = "AF_UNIX";
-        RestrictRealtime = true;
+      systemd.services.snapper-timeline = {
+        description = "Timeline of Snapper Snapshots";
+        inherit documentation;
+        requires = [ "local-fs.target" ];
+        serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --timeline";
       };
-    };
 
-    systemd.services.snapper-timeline = {
-      description = "Timeline of Snapper Snapshots";
-      inherit documentation;
-      requires = [ "local-fs.target" ];
-      serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --timeline";
-      startAt = cfg.snapshotInterval;
-    };
+      systemd.timers.snapper-timeline = {
+        wantedBy = [ "timers.target" ];
+        timerConfig = {
+          Persistent = cfg.persistentTimer;
+          OnCalendar = cfg.snapshotInterval;
+        };
+      };
 
-    systemd.services.snapper-cleanup = {
-      description = "Cleanup of Snapper Snapshots";
-      inherit documentation;
-      serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --cleanup";
-    };
+      systemd.services.snapper-cleanup = {
+        description = "Cleanup of Snapper Snapshots";
+        inherit documentation;
+        serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --cleanup";
+      };
 
-    systemd.timers.snapper-cleanup = {
-      description = "Cleanup of Snapper Snapshots";
-      inherit documentation;
-      wantedBy = [ "timers.target" ];
-      requires = [ "local-fs.target" ];
-      timerConfig.OnBootSec = "10m";
-      timerConfig.OnUnitActiveSec = cfg.cleanupInterval;
-    };
+      systemd.timers.snapper-cleanup = {
+        description = "Cleanup of Snapper Snapshots";
+        inherit documentation;
+        wantedBy = [ "timers.target" ];
+        requires = [ "local-fs.target" ];
+        timerConfig.OnBootSec = "10m";
+        timerConfig.OnUnitActiveSec = cfg.cleanupInterval;
+      };
 
-    systemd.services.snapper-boot = lib.optionalAttrs cfg.snapshotRootOnBoot {
-      description = "Take snapper snapshot of root on boot";
-      inherit documentation;
-      serviceConfig.ExecStart = "${pkgs.snapper}/bin/snapper --config root create --cleanup-algorithm number --description boot";
-      serviceConfig.Type = "oneshot";
-      requires = [ "local-fs.target" ];
-      wantedBy = [ "multi-user.target" ];
-      unitConfig.ConditionPathExists = "/etc/snapper/configs/root";
-    };
+      systemd.services.snapper-boot = lib.mkIf cfg.snapshotRootOnBoot {
+        description = "Take snapper snapshot of root on boot";
+        inherit documentation;
+        serviceConfig.ExecStart = "${pkgs.snapper}/bin/snapper --config root create --cleanup-algorithm number --description boot";
+        serviceConfig.Type = "oneshot";
+        requires = [ "local-fs.target" ];
+        wantedBy = [ "multi-user.target" ];
+        unitConfig.ConditionPathExists = "/etc/snapper/configs/root";
+      };
 
-    assertions =
-      concatMap
-        (name:
-          let
-            sub = cfg.configs.${name};
-          in
-          [ { assertion = !(sub ? extraConfig);
-              message = ''
-                The option definition `services.snapper.configs.${name}.extraConfig' no longer has any effect; please remove it.
-                The contents of this option should be migrated to attributes on `services.snapper.configs.${name}'.
-              '';
-            }
-          ] ++
+      assertions = concatMap (
+        name:
+        let
+          sub = cfg.configs.${name};
+        in
+        [
+          {
+            assertion = !(sub ? extraConfig);
+            message = ''
+              The option definition `services.snapper.configs.${name}.extraConfig' no longer has any effect; please remove it.
+              The contents of this option should be migrated to attributes on `services.snapper.configs.${name}'.
+            '';
+          }
+        ]
+        ++
           map
             (attr: {
               assertion = !(hasAttr attr sub);
@@ -246,8 +346,11 @@ in
                 The option definition `services.snapper.configs.${name}.${attr}' has been renamed to `services.snapper.configs.${name}.${toUpper attr}'.
               '';
             })
-            [ "fstype" "subvolume" ]
-        )
-        (attrNames cfg.configs);
-  });
+            [
+              "fstype"
+              "subvolume"
+            ]
+      ) (attrNames cfg.configs);
+    }
+  );
 }
diff --git a/nixos/modules/services/misc/sourcehut/service.nix b/nixos/modules/services/misc/sourcehut/service.nix
index ce5a0e78627c1..3507a49ea13a8 100644
--- a/nixos/modules/services/misc/sourcehut/service.nix
+++ b/nixos/modules/services/misc/sourcehut/service.nix
@@ -324,7 +324,8 @@ in
               };
               preStart =
                 let
-                  version = pkgs.sourcehut.${srvsrht}.version;
+                  package = pkgs.sourcehut.${srvsrht};
+                  version = package.version;
                   stateDir = "/var/lib/sourcehut/${srvsrht}";
                 in
                 mkBefore ''
@@ -336,14 +337,14 @@ in
                   if test ! -e ${stateDir}/db; then
                     # Setup the initial database.
                     # Note that it stamps the alembic head afterward
-                    ${cfg.python}/bin/${srvsrht}-initdb
+                    ${package}/bin/${srvsrht}-initdb
                     echo ${version} >${stateDir}/db
                   fi
 
                   ${optionalString cfg.settings.${iniKey}.migrate-on-upgrade ''
                     if [ "$(cat ${stateDir}/db)" != "${version}" ]; then
                       # Manage schema migrations using alembic
-                      ${cfg.python}/bin/${srvsrht}-migrate -a upgrade head
+                      ${package}/bin/${srvsrht}-migrate -a upgrade head
                       echo ${version} >${stateDir}/db
                     fi
                   ''}
@@ -389,7 +390,7 @@ in
               after = [ "network.target" "${srvsrht}.service" ];
               serviceConfig = {
                 Type = "oneshot";
-                ExecStart = "${cfg.python}/bin/${timerName}";
+                ExecStart = "${pkgs.sourcehut.${srvsrht}}/bin/${timerName}";
               };
             }
             (timer.service or { })
diff --git a/nixos/modules/services/misc/tandoor-recipes.nix b/nixos/modules/services/misc/tandoor-recipes.nix
index a2210f3d7db5a..1c903d280378e 100644
--- a/nixos/modules/services/misc/tandoor-recipes.nix
+++ b/nixos/modules/services/misc/tandoor-recipes.nix
@@ -22,7 +22,7 @@ let
     ${lib.toShellVars env}
     eval "$(${config.systemd.package}/bin/systemctl show -pUID,GID,MainPID tandoor-recipes.service)"
     exec ${pkgs.util-linux}/bin/nsenter \
-      -t $MainPID -m -S $UID -G $GID \
+      -t $MainPID -m -S $UID -G $GID --wdns=${env.MEDIA_ROOT} \
       ${pkg}/bin/tandoor-recipes "$@"
   '';
 in
@@ -88,7 +88,7 @@ in
         Group = "tandoor_recipes";
         DynamicUser = true;
         StateDirectory = "tandoor-recipes";
-        WorkingDirectory = "/var/lib/tandoor-recipes";
+        WorkingDirectory = env.MEDIA_ROOT;
         RuntimeDirectory = "tandoor-recipes";
 
         BindReadOnlyPaths = [
diff --git a/nixos/modules/services/misc/tzupdate.nix b/nixos/modules/services/misc/tzupdate.nix
index eac1e1112a5ab..be63bb179e423 100644
--- a/nixos/modules/services/misc/tzupdate.nix
+++ b/nixos/modules/services/misc/tzupdate.nix
@@ -41,5 +41,5 @@ in {
     };
   };
 
-  meta.maintainers = [ maintainers.michaelpj ];
+  meta.maintainers = [ ];
 }
diff --git a/nixos/modules/services/misc/wastebin.nix b/nixos/modules/services/misc/wastebin.nix
index 3d0af2862683d..f24bf94fa52bb 100644
--- a/nixos/modules/services/misc/wastebin.nix
+++ b/nixos/modules/services/misc/wastebin.nix
@@ -10,7 +10,7 @@ in
 
   options.services.wastebin = {
 
-    enable = mkEnableOption "Wastenbin pastebin service";
+    enable = mkEnableOption "Wastebin, a pastebin service";
 
     package = mkPackageOption pkgs "wastebin" { };
 
diff --git a/nixos/modules/services/misc/zoneminder.nix b/nixos/modules/services/misc/zoneminder.nix
index 84c3a6710c0db..d09cd87febfff 100644
--- a/nixos/modules/services/misc/zoneminder.nix
+++ b/nixos/modules/services/misc/zoneminder.nix
@@ -350,7 +350,7 @@ in {
           RestartSec = "10s";
           CacheDirectory = dirs cacheDirs;
           RuntimeDirectory = dirName;
-          ReadWriteDirectories = lib.mkIf useCustomDir [ cfg.storageDir ];
+          ReadWritePaths = lib.mkIf useCustomDir [ cfg.storageDir ];
           StateDirectory = dirs (lib.optionals (!useCustomDir) libDirs);
           LogsDirectory = dirName;
           PrivateTmp = true;
diff --git a/nixos/modules/services/monitoring/alloy.nix b/nixos/modules/services/monitoring/alloy.nix
new file mode 100644
index 0000000000000..abe8fcd7e1beb
--- /dev/null
+++ b/nixos/modules/services/monitoring/alloy.nix
@@ -0,0 +1,80 @@
+{ lib, pkgs, config, ... }:
+with lib;
+let
+  cfg = config.services.alloy;
+in
+{
+  meta = {
+    maintainers = with maintainers; [ flokli hbjydev ];
+  };
+
+  options.services.alloy = {
+    enable = mkEnableOption "Grafana Alloy";
+
+    package = mkPackageOption pkgs "grafana-alloy" { };
+
+    configPath = mkOption {
+      type = lib.types.path;
+      default = "/etc/alloy";
+      description = ''
+        Alloy configuration file/directory path.
+
+        We default to `/etc/alloy` here, and expect the user to configure a
+        configuration file via `environment.etc."alloy/config.alloy"`.
+
+        This allows config reload, contrary to specifying a store path.
+        A `reloadTrigger` for `config.alloy` is configured.
+
+        Other `*.alloy` files in the same directory (ignoring subdirs) are also
+        honored, but it's necessary to manually extend
+        `systemd.services.alloy.reloadTriggers` to enable config reload
+        during nixos-rebuild switch.
+
+        This can also point to another directory containing `*.alloy` files, or
+        a single Alloy file in the Nix store (at the cost of reload).
+
+        Component names must be unique across all Alloy configuration files, and
+        configuration blocks must not be repeated.
+
+        Alloy will continue to run if subsequent reloads of the configuration
+        file fail, potentially marking components as unhealthy depending on
+        the nature of the failure. When this happens, Alloy will continue
+        functioning in the last valid state.
+      '';
+    };
+
+    extraFlags = mkOption {
+      type = with lib.types; listOf str;
+      default = [ ];
+      example = [ "--server.http.listen-addr=127.0.0.1:12346" "--disable-reporting" ];
+      description = ''
+        Extra command-line flags passed to {command}`alloy run`.
+
+        See <https://grafana.com/docs/alloy/latest/reference/cli/run/>
+      '';
+    };
+  };
+
+
+  config = mkIf cfg.enable {
+    systemd.services.alloy = {
+      wantedBy = [ "multi-user.target" ];
+      reloadTriggers = [ config.environment.etc."alloy/config.alloy".source or null ];
+      serviceConfig = {
+        Restart = "always";
+        DynamicUser = true;
+        RestartSec = 2;
+        SupplementaryGroups = [
+          # allow to read the systemd journal for loki log forwarding
+          "systemd-journal"
+        ];
+        ExecStart = "${lib.getExe cfg.package} run ${cfg.configPath} ${escapeShellArgs cfg.extraFlags}";
+        ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
+        ConfigurationDirectory = "alloy";
+        StateDirectory = "alloy";
+        WorkingDirectory = "%S/alloy";
+        Type = "simple";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/arbtt.nix b/nixos/modules/services/monitoring/arbtt.nix
index 6dad6bdec3284..cf9a236c079c0 100644
--- a/nixos/modules/services/monitoring/arbtt.nix
+++ b/nixos/modules/services/monitoring/arbtt.nix
@@ -45,5 +45,5 @@ in {
     };
   };
 
-  meta.maintainers = [ maintainers.michaelpj ];
+  meta.maintainers = [ ];
 }
diff --git a/nixos/modules/services/monitoring/grafana-reporter.nix b/nixos/modules/services/monitoring/grafana-reporter.nix
index 340ab7abd19b7..528041cab37af 100644
--- a/nixos/modules/services/monitoring/grafana-reporter.nix
+++ b/nixos/modules/services/monitoring/grafana-reporter.nix
@@ -60,7 +60,7 @@ in {
           "-templates ${cfg.templateDir}"
         ];
       in {
-        ExecStart = "${pkgs.grafana_reporter}/bin/grafana-reporter ${args}";
+        ExecStart = "${pkgs.grafana-reporter}/bin/grafana-reporter ${args}";
       };
     };
   };
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index 9d453c5394824..32919950adc1e 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -47,13 +47,6 @@ let
   datasourceFileOrDir = mkProvisionCfg "datasource" "datasources" cfg.provision.datasources;
   dashboardFileOrDir = mkProvisionCfg "dashboard" "providers" cfg.provision.dashboards;
 
-  notifierConfiguration = {
-    apiVersion = 1;
-    notifiers = cfg.provision.notifiers;
-  };
-
-  notifierFileOrDir = pkgs.writeText "notifier.yaml" (builtins.toJSON notifierConfiguration);
-
   generateAlertingProvisioningYaml = x:
     if (cfg.provision.alerting."${x}".path == null)
     then provisioningSettingsFormat.generate "${x}.yaml" cfg.provision.alerting."${x}".settings
@@ -74,10 +67,9 @@ let
     fi
   '';
   provisionConfDir = pkgs.runCommand "grafana-provisioning" { nativeBuildInputs = [ pkgs.xorg.lndir ]; } ''
-    mkdir -p $out/{alerting,datasources,dashboards,notifiers,plugins}
+    mkdir -p $out/{alerting,datasources,dashboards,plugins}
     ${ln { src = datasourceFileOrDir;    dir = "datasources"; filename = "datasource"; }}
     ${ln { src = dashboardFileOrDir;     dir = "dashboards";  filename = "dashboard"; }}
-    ${ln { src = notifierFileOrDir;      dir = "notifiers";   filename = "notifier"; }}
     ${ln { src = rulesFileOrDir;         dir = "alerting";    filename = "rules"; }}
     ${ln { src = contactPointsFileOrDir; dir = "alerting";    filename = "contactPoints"; }}
     ${ln { src = policiesFileOrDir;      dir = "alerting";    filename = "policies"; }}
@@ -161,73 +153,13 @@ let
       };
     };
   };
-
-  grafanaTypes.notifierConfig = types.submodule {
-    options = {
-      name = mkOption {
-        type = types.str;
-        default = "default";
-        description = "Notifier name.";
-      };
-      type = mkOption {
-        type = types.enum [ "dingding" "discord" "email" "googlechat" "hipchat" "kafka" "line" "teams" "opsgenie" "pagerduty" "prometheus-alertmanager" "pushover" "sensu" "sensugo" "slack" "telegram" "threema" "victorops" "webhook" ];
-        description = "Notifier type.";
-      };
-      uid = mkOption {
-        type = types.str;
-        description = "Unique notifier identifier.";
-      };
-      org_id = mkOption {
-        type = types.int;
-        default = 1;
-        description = "Organization ID.";
-      };
-      org_name = mkOption {
-        type = types.str;
-        default = "Main Org.";
-        description = "Organization name.";
-      };
-      is_default = mkOption {
-        type = types.bool;
-        description = "Is the default notifier.";
-        default = false;
-      };
-      send_reminder = mkOption {
-        type = types.bool;
-        default = true;
-        description = "Should the notifier be sent reminder notifications while alerts continue to fire.";
-      };
-      frequency = mkOption {
-        type = types.str;
-        default = "5m";
-        description = "How frequently should the notifier be sent reminders.";
-      };
-      disable_resolve_message = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Turn off the message that sends when an alert returns to OK.";
-      };
-      settings = mkOption {
-        type = types.nullOr types.attrs;
-        default = null;
-        description = "Settings for the notifier type.";
-      };
-      secure_settings = mkOption {
-        type = types.nullOr types.attrs;
-        default = null;
-        description = ''
-          Secure settings for the notifier type. Please note that the contents of this option
-          will end up in a world-readable Nix store. Use the file provider
-          pointing at a reasonably secured file in the local filesystem
-          to work around that. Look at the documentation for details:
-          <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider>
-        '';
-      };
-    };
-  };
 in
 {
   imports = [
+    (mkRemovedOptionModule [ "services" "grafana" "provision" "notifiers" ] ''
+      Notifiers (services.grafana.provision.notifiers) were removed in Grafana 11.
+    '')
+
     (mkRenamedOptionModule [ "services" "grafana" "protocol" ] [ "services" "grafana" "settings" "server" "protocol" ])
     (mkRenamedOptionModule [ "services" "grafana" "addr" ] [ "services" "grafana" "settings" "server" "http_addr" ])
     (mkRenamedOptionModule [ "services" "grafana" "port" ] [ "services" "grafana" "settings" "server" "http_port" ])
@@ -1256,15 +1188,6 @@ in
         };
       };
 
-
-      notifiers = mkOption {
-        description = "Grafana notifier configuration.";
-        default = [ ];
-        type = types.listOf grafanaTypes.notifierConfig;
-        apply = x: map _filter x;
-      };
-
-
       alerting = {
         rules = {
           path = mkOption {
@@ -1746,12 +1669,6 @@ in
             Use file provider or an env-var instead.
           '';
 
-        # Warn about deprecated notifiers.
-        deprecatedNotifiers = optional (cfg.provision.notifiers != [ ]) ''
-          Notifiers are deprecated upstream and will be removed in Grafana 11.
-          Use `services.grafana.provision.alerting.contactPoints` instead.
-        '';
-
         # Ensure that `secureJsonData` of datasources provisioned via `datasources.settings`
         # only uses file/env providers.
         secureJsonDataWithoutFileProvider = optional
@@ -1770,15 +1687,10 @@ in
             Declarations in the `secureJsonData`-block of a datasource will be leaked to the
             Nix store unless a file-provider or an env-var is used!
           '';
-
-        notifierSecureSettingsWithoutFileProvider = optional
-          (any (x: x.secure_settings != null) cfg.provision.notifiers)
-          "Notifier secure settings will be stored as plaintext in the Nix store! Use file provider instead.";
       in
       passwordWithoutFileProvider
-      ++ deprecatedNotifiers
       ++ secureJsonDataWithoutFileProvider
-      ++ notifierSecureSettingsWithoutFileProvider;
+      ;
 
     environment.systemPackages = [ cfg.package ];
 
diff --git a/nixos/modules/services/monitoring/loki.nix b/nixos/modules/services/monitoring/loki.nix
index ba63f95e7f1a8..307119ecbf8ba 100644
--- a/nixos/modules/services/monitoring/loki.nix
+++ b/nixos/modules/services/monitoring/loki.nix
@@ -94,11 +94,24 @@ in {
     systemd.services.loki = {
       description = "Loki Service Daemon";
       wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
 
       serviceConfig = let
         conf = if cfg.configFile == null
-               then prettyJSON cfg.configuration
+               then
+                 # Config validation may fail when using extraFlags = [ "-config.expand-env=true" ].
+                 # To work around this, we simply skip it when extraFlags is not empty.
+                 if cfg.extraFlags == []
+                 then validateConfig (prettyJSON cfg.configuration)
+                 else prettyJSON cfg.configuration
                else cfg.configFile;
+        validateConfig = file:
+        pkgs.runCommand "validate-loki-conf" {
+          nativeBuildInputs = [ cfg.package ];
+        } ''
+            loki -verify-config -config.file "${file}"
+            ln -s "${file}" "$out"
+          '';
       in
       {
         ExecStart = "${cfg.package}/bin/loki --config.file=${conf} ${escapeShellArgs cfg.extraFlags}";
diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix
index 90e00e91deed2..8f89408bdea59 100644
--- a/nixos/modules/services/monitoring/netdata.nix
+++ b/nixos/modules/services/monitoring/netdata.nix
@@ -13,6 +13,9 @@ let
     ln -s /run/wrappers/bin/slabinfo.plugin $out/libexec/netdata/plugins.d/slabinfo.plugin
     ln -s /run/wrappers/bin/freeipmi.plugin $out/libexec/netdata/plugins.d/freeipmi.plugin
     ln -s /run/wrappers/bin/systemd-journal.plugin $out/libexec/netdata/plugins.d/systemd-journal.plugin
+    ln -s /run/wrappers/bin/logs-management.plugin $out/libexec/netdata/plugins.d/logs-management.plugin
+    ln -s /run/wrappers/bin/network-viewer.plugin $out/libexec/netdata/plugins.d/network-viewer.plugin
+    ln -s /run/wrappers/bin/debugfs.plugin $out/libexec/netdata/plugins.d/debugfs.plugin
   '';
 
   plugins = [
@@ -47,6 +50,7 @@ let
 
   defaultUser = "netdata";
 
+  isThereAnyWireGuardTunnels = config.networking.wireguard.enable || lib.any (c: lib.hasAttrByPath [ "netdevConfig" "Kind" ] c && c.netdevConfig.Kind == "wireguard") (builtins.attrValues config.systemd.network.netdevs);
 in {
   options = {
     services.netdata = {
@@ -86,6 +90,14 @@ in {
             Whether to enable python-based plugins
           '';
         };
+        recommendedPythonPackages = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Whether to enable a set of recommended Python plugins
+            by installing extra Python packages.
+          '';
+        };
         extraPackages = mkOption {
           type = types.functionTo (types.listOf types.package);
           default = ps: [];
@@ -198,13 +210,26 @@ in {
         }
       ];
 
+    # Includes a set of recommended Python plugins in exchange of imperfect disk consumption.
+    services.netdata.python.extraPackages = lib.mkIf cfg.python.recommendedPythonPackages (ps: [
+      ps.requests
+      ps.pandas
+      ps.numpy
+      ps.psycopg2
+      ps.python-ldap
+      ps.netdata-pandas
+      ps.changefinder
+    ]);
+
     services.netdata.configDir.".opt-out-from-anonymous-statistics" = mkIf (!cfg.enableAnalyticsReporting) (pkgs.writeText ".opt-out-from-anonymous-statistics" "");
     environment.etc."netdata/netdata.conf".source = configFile;
     environment.etc."netdata/conf.d".source = configDirectory;
 
     systemd.services.netdata = {
       description = "Real time performance monitoring";
-      after = [ "network.target" ];
+      after = [ "network.target" "suid-sgid-wrappers.service" ];
+      # No wrapper means no "useful" netdata.
+      requires = [ "suid-sgid-wrappers.service" ];
       wantedBy = [ "multi-user.target" ];
       path = (with pkgs; [
           curl
@@ -213,10 +238,16 @@ in {
           which
           procps
           bash
+          nvme-cli # for go.d
+          iw # for charts.d
+          apcupsd # for charts.d
+          # TODO: firehol # for FireQoS -- this requires more NixOS module support.
           util-linux # provides logger command; required for syslog health alarms
       ])
         ++ lib.optional cfg.python.enable (pkgs.python3.withPackages cfg.python.extraPackages)
-        ++ lib.optional config.virtualisation.libvirtd.enable (config.virtualisation.libvirtd.package);
+        ++ lib.optional config.virtualisation.libvirtd.enable config.virtualisation.libvirtd.package
+        ++ lib.optional config.virtualisation.docker.enable config.virtualisation.docker.package
+        ++ lib.optionals config.virtualisation.podman.enable [ pkgs.jq config.virtualisation.podman.package ];
       environment = {
         PYTHONPATH = "${cfg.package}/libexec/netdata/python.d/python_modules";
         NETDATA_PIPENAME = "/run/netdata/ipc";
@@ -256,6 +287,8 @@ in {
         # Configuration directory and mode
         ConfigurationDirectory = "netdata";
         ConfigurationDirectoryMode = "0755";
+        # AmbientCapabilities
+        AmbientCapabilities = lib.optional isThereAnyWireGuardTunnels "CAP_NET_ADMIN";
         # Capabilities
         CapabilityBoundingSet = [
           "CAP_DAC_OVERRIDE"      # is required for freeipmi and slabinfo plugins
@@ -269,7 +302,7 @@ in {
           "CAP_SYS_CHROOT"        # is required for cgroups plugin
           "CAP_SETUID"            # is required for cgroups and cgroups-network plugins
           "CAP_SYSLOG"            # is required for systemd-journal plugin
-        ];
+        ] ++ lib.optional isThereAnyWireGuardTunnels "CAP_NET_ADMIN";
         # Sandboxing
         ProtectSystem = "full";
         ProtectHome = "read-only";
@@ -308,6 +341,14 @@ in {
         permissions = "u+rx,g+x,o-rwx";
       };
 
+      "debugfs.plugin" = {
+        source = "${cfg.package}/libexec/netdata/plugins.d/debugfs.plugin.org";
+        capabilities = "cap_dac_read_search+ep";
+        owner = cfg.user;
+        group = cfg.group;
+        permissions = "u+rx,g+x,o-rwx";
+      };
+
       "cgroup-network" = {
         source = "${cfg.package}/libexec/netdata/plugins.d/cgroup-network.org";
         capabilities = "cap_setuid+ep";
@@ -332,6 +373,14 @@ in {
         permissions = "u+rx,g+x,o-rwx";
       };
 
+      "logs-management.plugin" = {
+        source = "${cfg.package}/libexec/netdata/plugins.d/logs-management.plugin.org";
+        capabilities = "cap_dac_read_search,cap_syslog+ep";
+        owner = cfg.user;
+        group = cfg.group;
+        permissions = "u+rx,g+x,o-rwx";
+      };
+
       "slabinfo.plugin" = {
         source = "${cfg.package}/libexec/netdata/plugins.d/slabinfo.plugin.org";
         capabilities = "cap_dac_override+ep";
@@ -348,6 +397,14 @@ in {
         group = cfg.group;
         permissions = "u+rx,g+x,o-rwx";
       };
+    } // optionalAttrs (cfg.package.withNetworkViewer) {
+      "network-viewer.plugin" = {
+        source = "${cfg.package}/libexec/netdata/plugins.d/network-viewer.plugin.org";
+        capabilities = "cap_sys_admin,cap_dac_read_search,cap_sys_ptrace+ep";
+        owner = cfg.user;
+        group = cfg.group;
+        permissions = "u+rx,g+x,o-rwx";
+      };
     };
 
     security.pam.loginLimits = [
@@ -359,6 +416,8 @@ in {
       ${defaultUser} = {
         group = defaultUser;
         isSystemUser = true;
+        extraGroups = lib.optional config.virtualisation.docker.enable "docker"
+          ++ lib.optional config.virtualisation.podman.enable "podman";
       };
     };
 
diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager-webhook-logger.nix b/nixos/modules/services/monitoring/prometheus/alertmanager-webhook-logger.nix
new file mode 100644
index 0000000000000..b4307a76e1b02
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/alertmanager-webhook-logger.nix
@@ -0,0 +1,70 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.alertmanagerWebhookLogger;
+in
+{
+  options.services.prometheus.alertmanagerWebhookLogger = {
+    enable = mkEnableOption "Alertmanager Webhook Logger";
+
+    package = mkPackageOption pkgs "alertmanager-webhook-logger" { };
+
+    extraFlags = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = "Extra command line options to pass to alertmanager-webhook-logger.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.alertmanager-webhook-logger = {
+      description = "Alertmanager Webhook Logger";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" ];
+      wants = [ "network-online.target" ];
+
+      serviceConfig = {
+        ExecStart = ''
+          ${cfg.package}/bin/alertmanager-webhook-logger \
+          ${escapeShellArgs cfg.extraFlags}
+        '';
+
+        DynamicUser = true;
+        NoNewPrivileges = true;
+
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        ProtectHome = "tmpfs";
+
+        PrivateTmp = true;
+        PrivateDevices = true;
+        PrivateIPC = true;
+
+        ProtectHostname = true;
+        ProtectClock = true;
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectKernelLogs = true;
+        ProtectControlGroups = true;
+
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+
+        SystemCallFilter = [
+          "@system-service"
+          "~@cpu-emulation"
+          "~@privileged"
+          "~@reboot"
+          "~@setuid"
+          "~@swap"
+        ];
+      };
+    };
+  };
+
+  meta.maintainers = [ maintainers.jpds ];
+}
diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix
index 7e707a13b7903..4de80acfa9a8b 100644
--- a/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixos/modules/services/monitoring/prometheus/default.nix
@@ -181,6 +181,10 @@ let
         communicating with external systems (federation, remote
         storage, Alertmanager).
       '';
+
+      query_log_file = mkOpt types.str ''
+        Path to the file prometheus should write its query log to.
+      '';
     };
   };
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix b/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix
index e44140b1f51a0..e1b7dc91a0d77 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix
@@ -7,6 +7,8 @@ in
 {
   port = 9332;
   extraOpts = {
+    package = lib.mkPackageOption pkgs "prometheus-bitcoin-exporter" { };
+
     rpcUser = mkOption {
       type = types.str;
       default = "bitcoinrpc";
@@ -65,7 +67,7 @@ in
   serviceOpts = {
     script = ''
       export BITCOIN_RPC_PASSWORD=$(cat ${cfg.rpcPasswordFile})
-      exec ${pkgs.prometheus-bitcoin-exporter}/bin/bitcoind-monitor.py
+      exec ${cfg.package}/bin/bitcoind-monitor.py
     '';
 
     environment = {
diff --git a/nixos/modules/services/monitoring/scrutiny.nix b/nixos/modules/services/monitoring/scrutiny.nix
index 031f5a30cada6..c649d333e401a 100644
--- a/nixos/modules/services/monitoring/scrutiny.nix
+++ b/nixos/modules/services/monitoring/scrutiny.nix
@@ -140,8 +140,8 @@ in
 
             options.api.endpoint = mkOption {
               type = str;
-              default = "http://localhost:${toString cfg.settings.web.listen.port}";
-              defaultText = literalExpression ''"http://localhost:''${config.services.scrutiny.settings.web.listen.port}"'';
+              default = "http://${cfg.settings.web.listen.host}:${toString cfg.settings.web.listen.port}";
+              defaultText = literalExpression ''"http://''${config.services.scrutiny.settings.web.listen.host}:''${config.services.scrutiny.settings.web.listen.port}"'';
               description = "Scrutiny app API endpoint for sending metrics to.";
             };
 
diff --git a/nixos/modules/services/monitoring/zabbix-proxy.nix b/nixos/modules/services/monitoring/zabbix-proxy.nix
index 7fa471b6404a8..dec403df85ea8 100644
--- a/nixos/modules/services/monitoring/zabbix-proxy.nix
+++ b/nixos/modules/services/monitoring/zabbix-proxy.nix
@@ -103,7 +103,7 @@ in
 
         port = mkOption {
           type = types.port;
-          default = if cfg.database.type == "mysql" then mysql.port else pgsql.services.port;
+          default = if cfg.database.type == "mysql" then mysql.port else pgsql.settings.port;
           defaultText = literalExpression ''
             if config.${opt.database.type} == "mysql"
             then config.${options.services.mysql.port}
diff --git a/nixos/modules/services/network-filesystems/davfs2.nix b/nixos/modules/services/network-filesystems/davfs2.nix
index 23c04658031fb..49a363476c975 100644
--- a/nixos/modules/services/network-filesystems/davfs2.nix
+++ b/nixos/modules/services/network-filesystems/davfs2.nix
@@ -4,7 +4,7 @@ let
   inherit (lib.attrsets) optionalAttrs;
   inherit (lib.generators) toINIWithGlobalSection;
   inherit (lib.lists) optional;
-  inherit (lib.modules) mkIf;
+  inherit (lib.modules) mkIf mkRemovedOptionModule;
   inherit (lib.options) literalExpression mkEnableOption mkOption;
   inherit (lib.strings) escape;
   inherit (lib.types) attrsOf bool int lines oneOf str submodule;
@@ -20,17 +20,20 @@ let
     else toString value;
 
   configFile = pkgs.writeText "davfs2.conf" (
-    if (cfg.settings != { }) then
-      (toINIWithGlobalSection {
-        mkSectionName = escapeString;
-        mkKeyValue = k: v: "${k} ${formatValue v}";
-      } cfg.settings)
-    else
-      cfg.extraConfig
-  );
+    toINIWithGlobalSection {
+      mkSectionName = escapeString;
+      mkKeyValue = k: v: "${k} ${formatValue v}";
+    } cfg.settings);
 in
 {
 
+  imports = [
+    (mkRemovedOptionModule [ "services" "davfs2" "extraConfig" ] ''
+      The option extraConfig got removed, please migrate to
+      services.davfs2.settings instead.
+    '')
+  ];
+
   options.services.davfs2 = {
     enable = mkEnableOption "davfs2";
 
@@ -53,29 +56,6 @@ in
       '';
     };
 
-    extraConfig = mkOption {
-      type = lines;
-      default = "";
-      example = ''
-        proxy foo.bar:8080
-        use_locks 0
-
-        [/media/dav]
-        use_locks 1
-
-        [/home/otto/mywebspace]
-        gui_optimize 1
-      '';
-      description = ''
-        Extra lines appended to the configuration of davfs2.
-        See {manpage}`davfs2.conf(5)` for available settings.
-
-        **Note**: Please pass structured settings via
-        {option}`settings` instead, this option
-        will get deprecated in the future.
-      ''  ;
-    };
-
     settings = mkOption {
       type = submodule {
         freeformType = let
@@ -109,21 +89,6 @@ in
 
   config = mkIf cfg.enable {
 
-    assertions = [
-      {
-        assertion = cfg.extraConfig != "" -> cfg.settings == { };
-        message = ''
-          services.davfs2.extraConfig and services.davfs2.settings cannot be used together.
-          Please prefer using services.davfs2.settings.
-        '';
-      }
-    ];
-
-    warnings = optional (cfg.extraConfig != "") ''
-      services.davfs2.extraConfig will be deprecated in future releases;
-      please use services.davfs2.settings instead.
-    '';
-
     environment.systemPackages = [ pkgs.davfs2 ];
     environment.etc."davfs2/davfs2.conf".source = configFile;
 
diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix
index 66ef3f14ed700..c70d0cf7beac3 100644
--- a/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -201,14 +201,10 @@ in
               message   = "If samba.nsswins is enabled, then samba.enableWinbindd must also be enabled";
             }
           ];
-        # Always provide a smb.conf to shut up programs like smbclient and smbspool.
-        environment.etc."samba/smb.conf".source = mkOptionDefault (
-          if cfg.enable then configFile
-          else pkgs.writeText "smb-dummy.conf" "# Samba is disabled."
-        );
       }
 
       (mkIf cfg.enable {
+        environment.etc."samba/smb.conf".source = configFile;
 
         system.nssModules = optional cfg.nsswins samba;
         system.nssDatabases.hosts = optional cfg.nsswins "wins";
diff --git a/nixos/modules/services/networking/adguardhome.nix b/nixos/modules/services/networking/adguardhome.nix
index df9927351edc3..5be3e0bea224a 100644
--- a/nixos/modules/services/networking/adguardhome.nix
+++ b/nixos/modules/services/networking/adguardhome.nix
@@ -140,7 +140,7 @@ in {
       {
         assertion = cfg.settings != null
           -> !(hasAttrByPath [ "bind_port" ] cfg.settings);
-        message = "AdGuard option `settings.bind_host' has been superseded by `services.adguardhome.port'";
+        message = "AdGuard option `settings.bind_port' has been superseded by `services.adguardhome.port'";
       }
       {
         assertion = settings != null -> cfg.mutableSettings
@@ -167,8 +167,13 @@ in {
       preStart = optionalString (settings != null) ''
         if    [ -e "$STATE_DIRECTORY/AdGuardHome.yaml" ] \
            && [ "${toString cfg.mutableSettings}" = "1" ]; then
+          # First run a schema_version update on the existing configuration
+          # This ensures that both the new config and the existing one have the same schema_version
+          # Note: --check-config has the side effect of modifying the file at rest!
+          ${lib.getExe cfg.package} -c "$STATE_DIRECTORY/AdGuardHome.yaml" --check-config
+
           # Writing directly to AdGuardHome.yaml results in empty file
-          ${pkgs.yaml-merge}/bin/yaml-merge "$STATE_DIRECTORY/AdGuardHome.yaml" "${configFile}" > "$STATE_DIRECTORY/AdGuardHome.yaml.tmp"
+          ${lib.getExe pkgs.yaml-merge} "$STATE_DIRECTORY/AdGuardHome.yaml" "${configFile}" > "$STATE_DIRECTORY/AdGuardHome.yaml.tmp"
           mv "$STATE_DIRECTORY/AdGuardHome.yaml.tmp" "$STATE_DIRECTORY/AdGuardHome.yaml"
         else
           cp --force "${configFile}" "$STATE_DIRECTORY/AdGuardHome.yaml"
@@ -178,7 +183,7 @@ in {
 
       serviceConfig = {
         DynamicUser = true;
-        ExecStart = "${cfg.package}/bin/adguardhome ${args}";
+        ExecStart = "${lib.getExe cfg.package} ${args}";
         AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]
           ++ optionals cfg.allowDHCP [ "CAP_NET_RAW" ];
         Restart = "always";
diff --git a/nixos/modules/services/networking/antennas.nix b/nixos/modules/services/networking/antennas.nix
index ef98af22f20f2..a37df953fc923 100644
--- a/nixos/modules/services/networking/antennas.nix
+++ b/nixos/modules/services/networking/antennas.nix
@@ -50,10 +50,7 @@ in
       };
 
       serviceConfig = {
-         ExecStart = "${pkgs.antennas}/bin/antennas";
-
-        # Antennas expects all resources like html and config to be relative to it's working directory
-        WorkingDirectory = "${pkgs.antennas}/libexec/antennas/deps/antennas/";
+        ExecStart = "${pkgs.antennas}/bin/antennas";
 
         # Hardening
         CapabilityBoundingSet = [ "" ];
diff --git a/nixos/modules/services/networking/aria2.nix b/nixos/modules/services/networking/aria2.nix
index f32f5682c9801..f0d5c5c8a21e3 100644
--- a/nixos/modules/services/networking/aria2.nix
+++ b/nixos/modules/services/networking/aria2.nix
@@ -1,98 +1,132 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.services.aria2;
 
   homeDir = "/var/lib/aria2";
-
-  settingsDir = "${homeDir}";
-  sessionFile = "${homeDir}/aria2.session";
-  downloadDir = "${homeDir}/Downloads";
-
-  rangesToStringList = map (x: builtins.toString x.from +"-"+ builtins.toString x.to);
-
-  settingsFile = pkgs.writeText "aria2.conf"
-  ''
-    dir=${cfg.downloadDir}
-    listen-port=${concatStringsSep "," (rangesToStringList cfg.listenPortRange)}
-    rpc-listen-port=${toString cfg.rpcListenPort}
-  '';
-
+  defaultRpcListenPort = 6800;
+  defaultDir = "${homeDir}/Downloads";
+
+  portRangesToString = ranges: lib.concatStringsSep "," (map
+    (x:
+      if x.from == x.to
+      then builtins.toString x.from
+      else builtins.toString x.from + "-" + builtins.toString x.to
+    )
+    ranges);
+
+  customToKeyValue = lib.generators.toKeyValue {
+    mkKeyValue = lib.generators.mkKeyValueDefault
+      {
+        mkValueString = v:
+          if builtins.isList v then portRangesToString v
+          else lib.generators.mkValueStringDefault { } v;
+      } "=";
+  };
 in
 {
   imports = [
-    (mkRemovedOptionModule [ "services" "aria2" "rpcSecret" ] "Use services.aria2.rpcSecretFile instead")
+    (lib.mkRemovedOptionModule [ "services" "aria2" "rpcSecret" ] "Use services.aria2.rpcSecretFile instead")
+    (lib.mkRemovedOptionModule [ "services" "aria2" "extraArguments" ] "Use services.aria2.settings instead")
+    (lib.mkRenamedOptionModule [ "services" "aria2" "downloadDir" ] [ "services" "aria2" "settings" "dir" ])
+    (lib.mkRenamedOptionModule [ "services" "aria2" "listenPortRange" ] [ "services" "aria2" "settings" "listen-port" ])
+    (lib.mkRenamedOptionModule [ "services" "aria2" "rpcListenPort" ] [ "services" "aria2" "settings" "rpc-listen-port" ])
   ];
 
   options = {
     services.aria2 = {
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
           Whether or not to enable the headless Aria2 daemon service.
 
-          Aria2 daemon can be controlled via the RPC interface using
-          one of many WebUI (http://localhost:6800/ by default).
+          Aria2 daemon can be controlled via the RPC interface using one of many
+          WebUIs (http://localhost:${toString defaultRpcListenPort}/ by default).
 
-          Targets are downloaded to ${downloadDir} by default and are
-          accessible to users in the "aria2" group.
+          Targets are downloaded to `${defaultDir}` by default and are
+          accessible to users in the `aria2` group.
         '';
       };
-      openPorts = mkOption {
-        type = types.bool;
+      openPorts = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = ''
-          Open listen and RPC ports found in listenPortRange and rpcListenPort
-          options in the firewall.
-        '';
-      };
-      downloadDir = mkOption {
-        type = types.path;
-        default = downloadDir;
-        description = ''
-          Directory to store downloaded files.
-        '';
-      };
-      listenPortRange = mkOption {
-        type = types.listOf types.attrs;
-        default = [ { from = 6881; to = 6999; } ];
-        description = ''
-          Set UDP listening port range used by DHT(IPv4, IPv6) and UDP tracker.
+          Open listen and RPC ports found in `settings.listen-port` and
+          `settings.rpc-listen-port` options in the firewall.
         '';
       };
-      rpcListenPort = mkOption {
-        type = types.int;
-        default = 6800;
-        description = "Specify a port number for JSON-RPC/XML-RPC server to listen to. Possible Values: 1024-65535";
-      };
-      rpcSecretFile = mkOption {
-        type = types.path;
+      rpcSecretFile = lib.mkOption {
+        type = lib.types.path;
         example = "/run/secrets/aria2-rpc-token.txt";
         description = ''
           A file containing the RPC secret authorization token.
           Read https://aria2.github.io/manual/en/html/aria2c.html#rpc-auth to know how this option value is used.
         '';
       };
-      extraArguments = mkOption {
-        type = types.separatedString " ";
-        example = "--rpc-listen-all --remote-time=true";
-        default = "";
+      settings = lib.mkOption {
         description = ''
-          Additional arguments to be passed to Aria2.
+          Generates the `aria2.conf` file. Refer to [the documentation][0] for
+          all possible settings.
+
+          [0]: https://aria2.github.io/manual/en/html/aria2c.html#synopsis
         '';
+        default = { };
+        type = lib.types.submodule {
+          freeformType = with lib.types; attrsOf (oneOf [ bool int float singleLineStr ]);
+          options = {
+            save-session = lib.mkOption {
+              type = lib.types.singleLineStr;
+              default = "${homeDir}/aria2.session";
+              description = "Save error/unfinished downloads to FILE on exit.";
+            };
+            dir = lib.mkOption {
+              type = lib.types.singleLineStr;
+              default = defaultDir;
+              description = "Directory to store downloaded files.";
+            };
+            conf-path = lib.mkOption {
+              type = lib.types.singleLineStr;
+              default = "${homeDir}/aria2.conf";
+              description = "Configuration file path.";
+            };
+            enable-rpc = lib.mkOption {
+              type = lib.types.bool;
+              default = true;
+              description = "Enable JSON-RPC/XML-RPC server.";
+            };
+            listen-port = lib.mkOption {
+              type = with lib.types; listOf (attrsOf port);
+              default = [{ from = 6881; to = 6999; }];
+              description = "Set UDP listening port range used by DHT(IPv4, IPv6) and UDP tracker.";
+            };
+            rpc-listen-port = lib.mkOption {
+              type = lib.types.port;
+              default = defaultRpcListenPort;
+              description = "Specify a port number for JSON-RPC/XML-RPC server to listen to. Possible Values: 1024-65535";
+            };
+          };
+        };
       };
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = cfg.settings.enable-rpc;
+        message = "RPC has to be enabled, the default module option takes care of that.";
+      }
+      {
+        assertion = !(cfg.settings ? rpc-secret);
+        message = "Set the RPC secret through services.aria2.rpcSecretFile so it will not end up in the world-readable nix store.";
+      }
+    ];
 
     # Need to open ports for proper functioning
-    networking.firewall = mkIf cfg.openPorts {
-      allowedUDPPortRanges = config.services.aria2.listenPortRange;
-      allowedTCPPorts = [ config.services.aria2.rpcListenPort ];
+    networking.firewall = lib.mkIf cfg.openPorts {
+      allowedUDPPortRanges = config.services.aria2.settings.listen-port;
+      allowedTCPPorts = [ config.services.aria2.settings.rpc-listen-port ];
     };
 
     users.users.aria2 = {
@@ -107,7 +141,7 @@ in
 
     systemd.tmpfiles.rules = [
       "d '${homeDir}' 0770 aria2 aria2 - -"
-      "d '${config.services.aria2.downloadDir}' 0770 aria2 aria2 - -"
+      "d '${config.services.aria2.settings.dir}' 0770 aria2 aria2 - -"
     ];
 
     systemd.services.aria2 = {
@@ -115,22 +149,25 @@ in
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       preStart = ''
-        if [[ ! -e "${sessionFile}" ]]
+        if [[ ! -e "${cfg.settings.save-session}" ]]
         then
-          touch "${sessionFile}"
+          touch "${cfg.settings.save-session}"
         fi
-        cp -f "${settingsFile}" "${settingsDir}/aria2.conf"
-        echo "rpc-secret=$(cat "$CREDENTIALS_DIRECTORY/rpcSecretFile")" >> "${settingsDir}/aria2.conf"
+        cp -f "${pkgs.writeText "aria2.conf" (customToKeyValue cfg.settings)}" "${cfg.settings.conf-path}"
+        chmod +w "${cfg.settings.conf-path}"
+        echo "rpc-secret=$(cat "$CREDENTIALS_DIRECTORY/rpcSecretFile")" >> "${cfg.settings.conf-path}"
       '';
 
       serviceConfig = {
         Restart = "on-abort";
-        ExecStart = "${pkgs.aria2}/bin/aria2c --enable-rpc --conf-path=${settingsDir}/aria2.conf ${config.services.aria2.extraArguments} --save-session=${sessionFile}";
+        ExecStart = "${pkgs.aria2}/bin/aria2c --conf-path=${cfg.settings.conf-path}";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
         User = "aria2";
         Group = "aria2";
-        LoadCredential="rpcSecretFile:${cfg.rpcSecretFile}";
+        LoadCredential = "rpcSecretFile:${cfg.rpcSecretFile}";
       };
     };
   };
+
+  meta.maintainers = [ lib.maintainers.timhae ];
 }
diff --git a/nixos/modules/services/networking/clatd.nix b/nixos/modules/services/networking/clatd.nix
index 76e0c130ca466..de6cde4e979c0 100644
--- a/nixos/modules/services/networking/clatd.nix
+++ b/nixos/modules/services/networking/clatd.nix
@@ -43,7 +43,6 @@ in
 
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/clatd -c ${configFile}";
-        startLimitIntervalSec = 0;
 
         # Hardening
         CapabilityBoundingSet = [
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
index b912550e1155e..272a50eb92de8 100644
--- a/nixos/modules/services/networking/ddclient.nix
+++ b/nixos/modules/services/networking/ddclient.nix
@@ -11,7 +11,9 @@ let
     # This file can be used as a template for configFile or is automatically generated by Nix options.
     cache=${dataDir}/ddclient.cache
     foreground=YES
-    use=${cfg.use}
+    ${lib.optionalString (cfg.use != "") "use=${cfg.use}"}
+    ${lib.optionalString (cfg.use == "" && cfg.usev4 != "") "usev4=${cfg.usev4}"}
+    ${lib.optionalString (cfg.use == "" && cfg.usev6 != "") "usev6=${cfg.usev6}"}
     login=${cfg.username}
     password=${if cfg.protocol == "nsupdate" then "/run/${RuntimeDirectory}/ddclient.key" else "@password_placeholder@"}
     protocol=${cfg.protocol}
@@ -163,12 +165,26 @@ with lib;
       };
 
       use = mkOption {
-        default = "web, web=checkip.dyndns.com/, web-skip='Current IP Address: '";
+        default = "";
         type = str;
         description = ''
           Method to determine the IP address to send to the dynamic DNS provider.
         '';
       };
+      usev4 = mkOption {
+        default = "webv4, webv4=checkip.dyndns.com/, webv4-skip='Current IP Address: '";
+        type = str;
+        description = ''
+          Method to determine the IPv4 address to send to the dynamic DNS provider. Only used if `use` is not set.
+        '';
+      };
+      usev6 = mkOption {
+        default = "webv6, webv6=checkipv6.dyndns.com/, webv6-skip='Current IP Address: '";
+        type = str;
+        description = ''
+          Method to determine the IPv6 address to send to the dynamic DNS provider. Only used if `use` is not set.
+        '';
+      };
 
       verbose = mkOption {
         default = false;
@@ -204,6 +220,8 @@ with lib;
   ###### implementation
 
   config = mkIf config.services.ddclient.enable {
+    warnings = lib.optional (cfg.use != "") "Setting `use` is deprecated, ddclient now supports `usev4` and `usev6` for separate IPv4/IPv6 configuration.";
+
     systemd.services.ddclient = {
       description = "Dynamic DNS Client";
       wantedBy = [ "multi-user.target" ];
diff --git a/nixos/modules/services/networking/frr.nix b/nixos/modules/services/networking/frr.nix
index 7f611ce7b1c7d..df2b4035d2f07 100644
--- a/nixos/modules/services/networking/frr.nix
+++ b/nixos/modules/services/networking/frr.nix
@@ -23,10 +23,9 @@ let
     "pbr"
     "bfd"
     "fabric"
-    "mgmt"
   ];
 
-  allServices = services ++ [ "zebra" ];
+  allServices = services ++ [ "zebra" "mgmt" ];
 
   isEnabled = service: cfg.${service}.enable;
 
@@ -137,6 +136,20 @@ in
             '';
           };
         };
+        mgmt = (serviceOptions "mgmt") // {
+          enable = mkOption {
+            type = types.bool;
+            default = isEnabled "static";
+            defaultText = lib.literalExpression "config.services.frr.static.enable";
+            description = ''
+              Whether to enable the Configuration management daemon.
+
+              The Configuration management daemon is automatically
+              enabled if needed, at the moment this is when staticd
+              is enabled.
+            '';
+          };
+        };
       };
     }
     { options.services.frr = (genAttrs services serviceOptions); }
@@ -164,7 +177,7 @@ in
 
     environment.etc = let
       mkEtcLink = service: {
-        name = "frr/${service}.conf";
+        name = "frr/${daemonName service}.conf";
         value.source = configFile service;
       };
     in
@@ -196,18 +209,18 @@ in
               unitConfig.Documentation = if service == "zebra" then "man:zebra(8)"
                 else "man:${daemon}(8) man:zebra(8)";
 
-              restartTriggers = [
+              restartTriggers = mkIf (service != "mgmt") [
                 (configFile service)
               ];
-              reloadIfChanged = true;
+              reloadIfChanged = (service != "mgmt");
 
               serviceConfig = {
                 PIDFile = "frr/${daemon}.pid";
-                ExecStart = "${pkgs.frr}/libexec/frr/${daemon} -f /etc/frr/${service}.conf"
+                ExecStart = "${pkgs.frr}/libexec/frr/${daemon}"
                   + optionalString (scfg.vtyListenAddress != "") " -A ${scfg.vtyListenAddress}"
                   + optionalString (scfg.vtyListenPort != null) " -P ${toString scfg.vtyListenPort}"
                   + " " + (concatStringsSep " " scfg.extraOptions);
-                ExecReload = "${pkgs.python3.interpreter} ${pkgs.frr}/libexec/frr/frr-reload.py --reload --daemon ${daemonName service} --bindir ${pkgs.frr}/bin --rundir /run/frr /etc/frr/${service}.conf";
+                ExecReload = mkIf (service != "mgmt") "${pkgs.python3.interpreter} ${pkgs.frr}/libexec/frr/frr-reload.py --reload --daemon ${daemon} --bindir ${pkgs.frr}/bin --rundir /run/frr /etc/frr/${daemon}.conf";
                 Restart = "on-abnormal";
               };
             });
diff --git a/nixos/modules/services/networking/git-daemon.nix b/nixos/modules/services/networking/git-daemon.nix
index 6be72505c216e..522e6b14f868f 100644
--- a/nixos/modules/services/networking/git-daemon.nix
+++ b/nixos/modules/services/networking/git-daemon.nix
@@ -27,6 +27,8 @@ in
         '';
       };
 
+      package = mkPackageOption pkgs "git" { };
+
       basePath = mkOption {
         type = types.str;
         default = "";
@@ -119,7 +121,7 @@ in
     systemd.services.git-daemon = {
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      script = "${pkgs.git}/bin/git daemon --reuseaddr "
+      script = "${getExe cfg.package} daemon --reuseaddr "
         + (optionalString (cfg.basePath != "") "--base-path=${cfg.basePath} ")
         + (optionalString (cfg.listenAddress != "") "--listen=${cfg.listenAddress} ")
         + "--port=${toString cfg.port} --user=${cfg.user} --group=${cfg.group} ${cfg.options} "
diff --git a/nixos/modules/services/networking/hostapd.nix b/nixos/modules/services/networking/hostapd.nix
index 1bef5a1f0a9e8..b678656f2e046 100644
--- a/nixos/modules/services/networking/hostapd.nix
+++ b/nixos/modules/services/networking/hostapd.nix
@@ -687,7 +687,7 @@ in {
                   authentication = {
                     mode = mkOption {
                       default = "wpa3-sae";
-                      type = types.enum ["none" "wpa2-sha256" "wpa3-sae-transition" "wpa3-sae"];
+                      type = types.enum ["none" "wpa2-sha1" "wpa2-sha256" "wpa3-sae-transition" "wpa3-sae"];
                       description = ''
                         Selects the authentication mode for this AP.
 
@@ -695,7 +695,9 @@ in {
                           and create an open AP. Use {option}`settings` together with this option if you
                           want to configure the authentication manually. Any password options will still be
                           effective, if set.
-                        - {var}`"wpa2-sha256"`: WPA2-Personal using SHA256 (IEEE 802.11i/RSN). Passwords are set
+                        - {var}`"wpa2-sha1"`: Not recommended. WPA2-Personal using HMAC-SHA1. Passwords are set
+                          using {option}`wpaPassword` or preferably by {option}`wpaPasswordFile` or {option}`wpaPskFile`.
+                        - {var}`"wpa2-sha256"`: WPA2-Personal using HMAC-SHA256 (IEEE 802.11i/RSN). Passwords are set
                           using {option}`wpaPassword` or preferably by {option}`wpaPasswordFile` or {option}`wpaPskFile`.
                         - {var}`"wpa3-sae-transition"`: Use WPA3-Personal (SAE) if possible, otherwise fallback
                           to WPA2-SHA256. Only use if necessary and switch to the newer WPA3-SAE when possible.
@@ -812,7 +814,7 @@ in {
                         Warning: These entries will get put into a world-readable file in
                         the Nix store! Using {option}`saePasswordFile` instead is recommended.
 
-                        Not used when {option}`mode` is {var}`"wpa2-sha256"`.
+                        Not used when {option}`mode` is {var}`"wpa2-sha1"` or {var}`"wpa2-sha256"`.
                       '';
                       type = types.listOf (types.submodule {
                         options = {
@@ -884,7 +886,7 @@ in {
                         parameters doesn't matter:
                         `<password>[|mac=<peer mac>][|vlanid=<VLAN ID>][|pk=<m:ECPrivateKey-base64>][|id=<identifier>]`
 
-                        Not used when {option}`mode` is {var}`"wpa2-sha256"`.
+                        Not used when {option}`mode` is {var}`"wpa2-sha1"` or {var}`"wpa2-sha256"`.
                       '';
                     };
 
@@ -959,6 +961,9 @@ in {
                   } // optionalAttrs (bssCfg.authentication.mode == "wpa3-sae-transition") {
                     wpa = 2;
                     wpa_key_mgmt = "WPA-PSK-SHA256 SAE";
+                  } // optionalAttrs (bssCfg.authentication.mode == "wpa2-sha1") {
+                    wpa = 2;
+                    wpa_key_mgmt = "WPA-PSK";
                   } // optionalAttrs (bssCfg.authentication.mode == "wpa2-sha256") {
                     wpa = 2;
                     wpa_key_mgmt = "WPA-PSK-SHA256";
@@ -1186,8 +1191,8 @@ in {
                   message = ''hostapd radio ${radio} bss ${bss}: uses WPA3-SAE in transition mode requires defining both a wpa password option and a sae password option'';
                 }
                 {
-                  assertion = auth.mode == "wpa2-sha256" -> countWpaPasswordDefinitions == 1;
-                  message = ''hostapd radio ${radio} bss ${bss}: uses WPA2-SHA256 which requires defining a wpa password option'';
+                  assertion = (auth.mode == "wpa2-sha1" || auth.mode == "wpa2-sha256") -> countWpaPasswordDefinitions == 1;
+                  message = ''hostapd radio ${radio} bss ${bss}: uses WPA2-PSK which requires defining a wpa password option'';
                 }
               ])
               radioCfg.networks))
diff --git a/nixos/modules/services/networking/inadyn.nix b/nixos/modules/services/networking/inadyn.nix
index baa4302096c2c..7022673538c8a 100644
--- a/nixos/modules/services/networking/inadyn.nix
+++ b/nixos/modules/services/networking/inadyn.nix
@@ -202,7 +202,7 @@ in
         startAt = cfg.interval;
         serviceConfig = {
           Type = "oneshot";
-          ExecStart = ''${lib.getExe pkgs.inadyn} -f ${configFile} --cache-dir ''${CACHE_DIRECTORY}/inadyn -1 --foreground -l ${cfg.logLevel}'';
+          ExecStart = ''${lib.getExe pkgs.inadyn} -f ${configFile} --cache-dir ''${CACHE_DIRECTORY} -1 --foreground -l ${cfg.logLevel}'';
           LoadCredential = "config:${configFile}";
           CacheDirectory = "inadyn";
 
diff --git a/nixos/modules/services/networking/jotta-cli.md b/nixos/modules/services/networking/jotta-cli.md
index fee002a4e6046..335e5c8e38563 100644
--- a/nixos/modules/services/networking/jotta-cli.md
+++ b/nixos/modules/services/networking/jotta-cli.md
@@ -6,7 +6,7 @@ The [Jottacloud Command-line Tool](https://docs.jottacloud.com/en/articles/14368
 
 ```nix
 {
-  user.services.jotta-cli.enable = true;
+  services.jotta-cli.enable = true;
 }
 ```
 
@@ -15,7 +15,7 @@ This adds `jotta-cli` to `environment.systemPackages` and starts a user service
 ## Example Configuration {#module-services-jotta-cli-example-configuration}
 
 ```nix
-user.services.jotta-cli = {
+services.jotta-cli = {
   enable = true;
   options = [ "slow" ];
   package = pkgs.jotta-cli;
diff --git a/nixos/modules/services/networking/jotta-cli.nix b/nixos/modules/services/networking/jotta-cli.nix
index c7e6dad5453ca..e0fa1ef332fe6 100644
--- a/nixos/modules/services/networking/jotta-cli.nix
+++ b/nixos/modules/services/networking/jotta-cli.nix
@@ -2,10 +2,10 @@
 
 with lib;
 
-let cfg = config.user.services.jotta-cli;
+let cfg = config.services.jotta-cli;
 in {
   options = {
-    user.services.jotta-cli = {
+    services.jotta-cli = {
 
       enable = mkEnableOption "Jottacloud Command-line Tool";
 
diff --git a/nixos/modules/services/networking/kea.nix b/nixos/modules/services/networking/kea.nix
index 66173c145d16a..11add600b66fb 100644
--- a/nixos/modules/services/networking/kea.nix
+++ b/nixos/modules/services/networking/kea.nix
@@ -278,6 +278,9 @@ in
         "https://kea.readthedocs.io/en/kea-${package.version}/arm/agent.html"
       ];
 
+      wants = [
+        "network-online.target"
+      ];
       after = [
         "network-online.target"
         "time-sync.target"
diff --git a/nixos/modules/services/networking/mihomo.nix b/nixos/modules/services/networking/mihomo.nix
index 312530caeaade..d4bb10496279d 100644
--- a/nixos/modules/services/networking/mihomo.nix
+++ b/nixos/modules/services/networking/mihomo.nix
@@ -25,6 +25,7 @@ in
     webui = lib.mkOption {
       default = null;
       type = lib.types.nullOr lib.types.path;
+      example = lib.literalExpression "pkgs.metacubexd";
       description = ''
         Local web interface to use.
 
diff --git a/nixos/modules/services/networking/mycelium.nix b/nixos/modules/services/networking/mycelium.nix
index 9487a5daafee0..0d0b2945af4c1 100644
--- a/nixos/modules/services/networking/mycelium.nix
+++ b/nixos/modules/services/networking/mycelium.nix
@@ -60,6 +60,8 @@ in
     networking.firewall.allowedTCPPorts = lib.optionals cfg.openFirewall [ 9651 ];
     networking.firewall.allowedUDPPorts = lib.optionals cfg.openFirewall [ 9650 9651 ];
 
+    environment.systemPackages = [ cfg.package ];
+
     systemd.services.mycelium = {
       description = "Mycelium network";
       after = [ "network.target" ];
diff --git a/nixos/modules/services/networking/netbird.nix b/nixos/modules/services/networking/netbird.nix
index 7add377896cab..e68c39946fe3b 100644
--- a/nixos/modules/services/networking/netbird.nix
+++ b/nixos/modules/services/networking/netbird.nix
@@ -37,7 +37,6 @@ in
 {
   meta.maintainers = with maintainers; [
     misuzu
-    thubrecht
   ];
   meta.doc = ./netbird.md;
 
diff --git a/nixos/modules/services/networking/netbird/coturn.nix b/nixos/modules/services/networking/netbird/coturn.nix
index 746d70a07250d..29ff1e8fc15ee 100644
--- a/nixos/modules/services/networking/netbird/coturn.nix
+++ b/nixos/modules/services/networking/netbird/coturn.nix
@@ -60,6 +60,7 @@ in
       default = null;
       description = ''
         The password of the user used by netbird to connect to the coturn server.
+        Be advised this will be world readable in the nix store.
       '';
     };
 
@@ -142,7 +143,11 @@ in
           ];
         });
 
-      security.acme.certs.${cfg.domain}.postRun = optionalString cfg.useAcmeCertificates "systemctl restart coturn.service";
+      security.acme.certs = mkIf cfg.useAcmeCertificates {
+        ${cfg.domain}.postRun = ''
+          systemctl restart coturn.service
+        '';
+      };
 
       networking.firewall = {
         allowedUDPPorts = cfg.openPorts;
diff --git a/nixos/modules/services/networking/netbird/server.nix b/nixos/modules/services/networking/netbird/server.nix
index a4de0fda6a134..e3de286a04fa4 100644
--- a/nixos/modules/services/networking/netbird/server.nix
+++ b/nixos/modules/services/networking/netbird/server.nix
@@ -2,6 +2,7 @@
 
 let
   inherit (lib)
+    mkDefault
     mkEnableOption
     mkIf
     mkOption
@@ -15,7 +16,7 @@ in
 
 {
   meta = {
-    maintainers = with lib.maintainers; [ thubrecht ];
+    maintainers = with lib.maintainers; [patrickdag];
     doc = ./server.md;
   };
 
@@ -41,26 +42,46 @@ in
   config = mkIf cfg.enable {
     services.netbird.server = {
       dashboard = {
-        inherit (cfg) enable domain enableNginx;
+        domain = mkDefault cfg.domain;
+        enable = mkDefault cfg.enable;
+        enableNginx = mkDefault cfg.enableNginx;
 
         managementServer = "https://${cfg.domain}";
       };
 
       management =
         {
-          inherit (cfg) enable domain enableNginx;
+          domain = mkDefault cfg.domain;
+          enable = mkDefault cfg.enable;
+          enableNginx = mkDefault cfg.enableNginx;
         }
-        // (optionalAttrs cfg.coturn.enable {
+        // (optionalAttrs cfg.coturn.enable rec {
           turnDomain = cfg.domain;
           turnPort = config.services.coturn.tls-listening-port;
+          # We cannot merge a list of attrsets so we have to redefine the whole list
+          settings = {
+            TURNConfig.Turns = mkDefault [
+              {
+                Proto = "udp";
+                URI = "turn:${turnDomain}:${builtins.toString turnPort}";
+                Username = "netbird";
+                Password =
+                  if (cfg.coturn.password != null)
+                  then cfg.coturn.password
+                  else {_secret = cfg.coturn.passwordFile;};
+              }
+            ];
+          };
         });
 
       signal = {
-        inherit (cfg) enable domain enableNginx;
+        domain = mkDefault cfg.domain;
+        enable = mkDefault cfg.enable;
+        enableNginx = mkDefault cfg.enableNginx;
       };
 
       coturn = {
-        inherit (cfg) domain;
+        domain = mkDefault cfg.domain;
       };
     };
   };
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index e33bbb2af178f..b7143cf520f96 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -470,7 +470,7 @@ in
       -      [main]
       -      no-auto-default=*
       -    '''
-      +    extraConfig.main.no-auto-default = "*";
+      +    settings.main.no-auto-default = "*";
          };
       ```
     ''
diff --git a/nixos/modules/services/networking/oink.nix b/nixos/modules/services/networking/oink.nix
new file mode 100644
index 0000000000000..cd0fdf172331d
--- /dev/null
+++ b/nixos/modules/services/networking/oink.nix
@@ -0,0 +1,84 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.oink;
+  makeOinkConfig = attrs: (pkgs.formats.json { }).generate
+    "oink.json" (mapAttrs' (k: v: nameValuePair (toLower k) v) attrs);
+  oinkConfig = makeOinkConfig {
+    global = cfg.settings;
+    domains = cfg.domains;
+  };
+in
+{
+  options.services.oink = {
+    enable = mkEnableOption "Oink, a dynamic DNS client for Porkbun";
+    package = mkPackageOption pkgs "oink" { };
+    settings = {
+      apiKey = mkOption {
+        type = types.str;
+        description = "API key to use when modifying DNS records.";
+      };
+      secretApiKey = mkOption {
+        type = types.str;
+        description = "Secret API key to use when modifying DNS records.";
+      };
+      interval = mkOption {
+        # https://github.com/rlado/oink/blob/v1.1.1/src/main.go#L364
+        type = types.ints.between 60 172800; # 48 hours
+        default = 900;
+        description = "Seconds to wait before sending another request.";
+      };
+      ttl = mkOption {
+        type = types.ints.between 600 172800;
+        default = 600;
+        description = ''
+          The TTL ("Time to Live") value to set for your DNS records.
+
+          The TTL controls how long in seconds your records will be cached
+          for. A smaller value will allow the record to update quicker.
+        '';
+      };
+    };
+    domains = mkOption {
+      type = with types; listOf (attrsOf anything);
+      default = [];
+      example = [
+        {
+          domain = "nixos.org";
+          subdomain = "";
+          ttl = 1200;
+        }
+        {
+          domain = "nixos.org";
+          subdomain = "hydra";
+        }
+      ];
+      description = ''
+        List of attribute sets containing configuration for each domain.
+
+        Each attribute set must have two attributes, one named *domain*
+        and another named *subdomain*. The domain attribute must specify
+        the root domain that you want to configure, and the subdomain
+        attribute must specify its subdomain if any. If you want to
+        configure the root domain rather than a subdomain, leave the
+        subdomain attribute as an empty string.
+
+        Additionally, you can use attributes from *services.oink.settings*
+        to override settings per-domain.
+
+        Every domain listed here *must* have API access enabled in
+        Porkbun's control panel.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.oink = {
+      description = "Dynamic DNS client for Porkbun";
+      wantedBy = [ "multi-user.target" ];
+      script = "${cfg.package}/bin/oink -c ${oinkConfig}";
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/pixiecore.nix b/nixos/modules/services/networking/pixiecore.nix
index cfdb8014136ed..111cb7e355040 100644
--- a/nixos/modules/services/networking/pixiecore.nix
+++ b/nixos/modules/services/networking/pixiecore.nix
@@ -82,8 +82,8 @@ in
 
       apiServer = mkOption {
         type = types.str;
-        example = "localhost:8080";
-        description = "host:port to connect to the API. Ignored unless mode is set to 'api'";
+        example = "http://localhost:8080";
+        description = "URI to connect to the API. Ignored unless mode is set to 'api'";
       };
 
       extraArguments = mkOption {
diff --git a/nixos/modules/services/networking/radvd.nix b/nixos/modules/services/networking/radvd.nix
index 4e3e501d2f593..0143324a78155 100644
--- a/nixos/modules/services/networking/radvd.nix
+++ b/nixos/modules/services/networking/radvd.nix
@@ -33,6 +33,17 @@ in
 
     package = mkPackageOption pkgs "radvd" { };
 
+    debugLevel = mkOption {
+      type = types.int;
+      default = 0;
+      example = 5;
+      description = ''
+          The debugging level is an integer in the range from 1 to 5,
+          from quiet to very verbose. A debugging level of 0 completely
+          turns off debugging.
+        '';
+    };
+
     config = mkOption {
       type = types.lines;
       example =
@@ -67,7 +78,7 @@ in
         wantedBy = [ "multi-user.target" ];
         after = [ "network.target" ];
         serviceConfig =
-          { ExecStart = "@${cfg.package}/bin/radvd radvd -n -u radvd -C ${confFile}";
+          { ExecStart = "@${cfg.package}/bin/radvd radvd -n -u radvd -d ${toString cfg.debugLevel} -C ${confFile}";
             Restart = "always";
           };
       };
diff --git a/nixos/modules/services/networking/rosenpass.nix b/nixos/modules/services/networking/rosenpass.nix
index 373a6c7690799..92ecc1cb31a36 100644
--- a/nixos/modules/services/networking/rosenpass.nix
+++ b/nixos/modules/services/networking/rosenpass.nix
@@ -130,8 +130,8 @@ in
             relevant = config.systemd.network.enable;
             root = config.systemd.network.netdevs;
             peer = (x: x.wireguardPeers);
-            key = (x: if x.wireguardPeerConfig ? PublicKey then x.wireguardPeerConfig.PublicKey else null);
-            description = "${options.systemd.network.netdevs}.\"<name>\".wireguardPeers.*.wireguardPeerConfig.PublicKey";
+            key = x: x.PublicKey or null;
+            description = "${options.systemd.network.netdevs}.\"<name>\".wireguardPeers.*.PublicKey";
           }
           {
             relevant = config.networking.wireguard.enable;
@@ -225,8 +225,10 @@ in
         # See <https://www.freedesktop.org/software/systemd/man/systemd.unit.html#Specifiers>
         environment.CONFIG = "%t/${serviceConfig.RuntimeDirectory}/config.toml";
 
-        preStart = "${getExe pkgs.envsubst} -i ${config} -o \"$CONFIG\"";
-        script = "rosenpass exchange-config \"$CONFIG\"";
+        script = ''
+          ${getExe pkgs.envsubst} -i ${config} -o "$CONFIG"
+          rosenpass exchange-config "$CONFIG"
+        '';
       };
   };
 }
diff --git a/nixos/modules/services/networking/smokeping.nix b/nixos/modules/services/networking/smokeping.nix
index 38d6e4452c97b..3fb3eac45cc82 100644
--- a/nixos/modules/services/networking/smokeping.nix
+++ b/nixos/modules/services/networking/smokeping.nix
@@ -47,6 +47,13 @@ let
 in
 
 {
+  imports = [
+    (mkRemovedOptionModule [ "services" "smokeping" "port" ] ''
+      The smokeping web service is now served by nginx.
+      In order to change the port, you need to change the nginx configuration under `services.nginx.virtualHosts.smokeping.listen.*.port`.
+    '')
+  ];
+
   options = {
     services.smokeping = {
       enable = mkEnableOption "smokeping service";
@@ -71,8 +78,8 @@ in
       };
       cgiUrl = mkOption {
         type = types.str;
-        default = "http://${cfg.hostName}:${toString cfg.port}/smokeping.cgi";
-        defaultText = literalExpression ''"http://''${hostName}:''${toString port}/smokeping.cgi"'';
+        default = "http://${cfg.hostName}/smokeping.cgi";
+        defaultText = literalExpression ''"http://''${hostName}/smokeping.cgi"'';
         example = "https://somewhere.example.com/smokeping.cgi";
         description = "URL to the smokeping cgi.";
       };
@@ -177,11 +184,6 @@ in
           which makes it bind to all interfaces.
         '';
       };
-      port = mkOption {
-        type = types.port;
-        default = 8081;
-        description = "TCP port to use for the web server.";
-      };
       presentationConfig = mkOption {
         type = types.lines;
         default = ''
@@ -312,17 +314,8 @@ in
       description = "smokeping daemon user";
       home = smokepingHome;
       createHome = true;
-      # When `cfg.webService` is enabled, `thttpd` makes SmokePing available
-      # under `${cfg.host}:${cfg.port}/smokeping.fcgi` as per the `ln -s` below.
-      # We also want that going to `${cfg.host}:${cfg.port}` without `smokeping.fcgi`
-      # makes it easy for the user to find SmokePing.
-      # However `thttpd` does not seem to support easy redirections from `/` to `smokeping.fcgi`
-      # and only allows directory listings or `/` -> `index.html` resolution if the directory
-      # has `chmod 755` (see https://acme.com/software/thttpd/thttpd_man.html#PERMISSIONS,
-      # " directories should be 755 if you want to allow indexing").
-      # Otherwise it shows `403 Forbidden` on `/`.
-      # Thus, we need to make `smokepingHome` (which is given to `thttpd -d` below) `755`.
-      homeMode = "755";
+      # When `cfg.webService` is enabled, `nginx` requires read permissions on the home directory.
+      homeMode = "711";
     };
     users.groups.${cfg.user} = { };
     systemd.services.smokeping = {
@@ -342,21 +335,25 @@ in
         ${cfg.package}/bin/smokeping --static --config=${configPath}
       '';
     };
-    systemd.services.thttpd = mkIf cfg.webService {
-      requiredBy = [ "multi-user.target" ];
-      requires = [ "smokeping.service" ];
-      path = with pkgs; [ bash rrdtool smokeping thttpd ];
-      serviceConfig = {
-        Restart = "always";
-        ExecStart = lib.concatStringsSep " " (lib.concatLists [
-          [ "${pkgs.thttpd}/bin/thttpd" ]
-          [ "-u ${cfg.user}" ]
-          [ ''-c "**.fcgi"'' ]
-          [ "-d ${smokepingHome}" ]
-          (lib.optional (cfg.host != null) "-h ${cfg.host}")
-          [ "-p ${builtins.toString cfg.port}" ]
-          [ "-D -nos" ]
-        ]);
+
+    # use nginx to serve the smokeping web service
+    services.fcgiwrap.enable = mkIf cfg.webService true;
+    services.nginx = mkIf cfg.webService {
+      enable = true;
+      virtualHosts."smokeping" = {
+        serverName = mkDefault cfg.host;
+        locations."/" = {
+          root = smokepingHome;
+          index = "smokeping.fcgi";
+        };
+        locations."/smokeping.fcgi" = {
+          extraConfig = ''
+            include ${config.services.nginx.package}/conf/fastcgi_params;
+            fastcgi_pass unix:${config.services.fcgiwrap.socketAddress};
+            fastcgi_param SCRIPT_FILENAME ${smokepingHome}/smokeping.fcgi;
+            fastcgi_param DOCUMENT_ROOT ${smokepingHome};
+          '';
+        };
       };
     };
   };
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index c62bccd462d33..1e4e34a4f1675 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -5,11 +5,11 @@ with lib;
 let
 
   # The splicing information needed for nativeBuildInputs isn't available
-  # on the derivations likely to be used as `cfgc.package`.
+  # on the derivations likely to be used as `cfg.package`.
   # This middle-ground solution ensures *an* sshd can do their basic validation
   # on the configuration.
   validationPackage = if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform
-    then cfgc.package
+    then cfg.package
     else pkgs.buildPackages.openssh;
 
   # dont use the "=" operator
@@ -169,6 +169,13 @@ in
         '';
       };
 
+      package = mkOption {
+        type = types.package;
+        default = config.programs.ssh.package;
+        defaultText = literalExpression "programs.ssh.package";
+        description = "OpenSSH package to use for sshd.";
+      };
+
       startWhenNeeded = mkOption {
         type = types.bool;
         default = false;
@@ -296,6 +303,17 @@ in
         '';
       };
 
+      authorizedKeysInHomedir = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Enables the use of the `~/.ssh/authorized_keys` file.
+
+          Otherwise, the only files trusted by default are those in `/etc/ssh/authorized_keys.d`,
+          *i.e.* SSH keys from [](#opt-users.users._name_.openssh.authorizedKeys.keys).
+        '';
+      };
+
       authorizedKeysCommand = mkOption {
         type = types.str;
         default = "none";
@@ -331,7 +349,7 @@ in
           freeformType = settingsFormat.type;
           options = {
             AuthorizedPrincipalsFile = mkOption {
-              type = types.str;
+              type = types.nullOr types.str;
               default = "none"; # upstream default
               description = ''
                 Specifies a file that lists principal names that are accepted for certificate authentication. The default
@@ -339,16 +357,18 @@ in
               '';
             };
             LogLevel = mkOption {
-              type = types.enum [ "QUIET" "FATAL" "ERROR" "INFO" "VERBOSE" "DEBUG" "DEBUG1" "DEBUG2" "DEBUG3" ];
+              type = types.nullOr (types.enum [ "QUIET" "FATAL" "ERROR" "INFO" "VERBOSE" "DEBUG" "DEBUG1" "DEBUG2" "DEBUG3" ]);
               default = "INFO"; # upstream default
               description = ''
                 Gives the verbosity level that is used when logging messages from sshd(8). Logging with a DEBUG level
                 violates the privacy of users and is not recommended.
               '';
             };
-            UsePAM = mkEnableOption "PAM authentication" // { default = true; };
+            UsePAM =
+              mkEnableOption "PAM authentication"
+              // { default = true; type = types.nullOr types.bool; };
             UseDns = mkOption {
-              type = types.bool;
+              type = types.nullOr types.bool;
               # apply if cfg.useDns then "yes" else "no"
               default = false;
               description = ''
@@ -359,14 +379,14 @@ in
               '';
             };
             X11Forwarding = mkOption {
-              type = types.bool;
+              type = types.nullOr types.bool;
               default = false;
               description = ''
                 Whether to allow X11 connections to be forwarded.
               '';
             };
             PasswordAuthentication = mkOption {
-              type = types.bool;
+              type = types.nullOr types.bool;
               default = true;
               description = ''
                 Specifies whether password authentication is allowed.
@@ -374,20 +394,20 @@ in
             };
             PermitRootLogin = mkOption {
               default = "prohibit-password";
-              type = types.enum ["yes" "without-password" "prohibit-password" "forced-commands-only" "no"];
+              type = types.nullOr (types.enum ["yes" "without-password" "prohibit-password" "forced-commands-only" "no"]);
               description = ''
                 Whether the root user can login using ssh.
               '';
             };
             KbdInteractiveAuthentication = mkOption {
-              type = types.bool;
+              type = types.nullOr types.bool;
               default = true;
               description = ''
                 Specifies whether keyboard-interactive authentication is allowed.
               '';
             };
             GatewayPorts = mkOption {
-              type = types.str;
+              type = types.nullOr types.str;
               default = "no";
               description = ''
                 Specifies whether remote hosts are allowed to connect to
@@ -396,7 +416,7 @@ in
               '';
             };
             KexAlgorithms = mkOption {
-              type = types.listOf types.str;
+              type = types.nullOr (types.listOf types.str);
               default = [
                 "sntrup761x25519-sha512@openssh.com"
                 "curve25519-sha256"
@@ -413,7 +433,7 @@ in
               '';
             };
             Macs = mkOption {
-              type = types.listOf types.str;
+              type = types.nullOr (types.listOf types.str);
               default = [
                 "hmac-sha2-512-etm@openssh.com"
                 "hmac-sha2-256-etm@openssh.com"
@@ -429,14 +449,14 @@ in
               '';
             };
             StrictModes = mkOption {
-              type = types.bool;
+              type = types.nullOr (types.bool);
               default = true;
               description = ''
                 Whether sshd should check file modes and ownership of directories
               '';
             };
             Ciphers = mkOption {
-              type = types.listOf types.str;
+              type = types.nullOr (types.listOf types.str);
               default = [
                 "chacha20-poly1305@openssh.com"
                 "aes256-gcm@openssh.com"
@@ -491,7 +511,9 @@ in
               '';
             };
             # Disabled by default, since pam_motd handles this.
-            PrintMotd = mkEnableOption "printing /etc/motd when a user logs in interactively";
+            PrintMotd =
+              mkEnableOption "printing /etc/motd when a user logs in interactively"
+              // { type = types.nullOr types.bool; };
           };
         });
       };
@@ -533,8 +555,8 @@ in
       };
     users.groups.sshd = {};
 
-    services.openssh.moduliFile = mkDefault "${cfgc.package}/etc/ssh/moduli";
-    services.openssh.sftpServerExecutable = mkDefault "${cfgc.package}/libexec/sftp-server";
+    services.openssh.moduliFile = mkDefault "${cfg.package}/etc/ssh/moduli";
+    services.openssh.sftpServerExecutable = mkDefault "${cfg.package}/libexec/sftp-server";
 
     environment.etc = authKeysFiles // authPrincipalsFiles //
       { "ssh/moduli".source = cfg.moduliFile;
@@ -548,7 +570,7 @@ in
             wantedBy = optional (!cfg.startWhenNeeded) "multi-user.target";
             after = [ "network.target" ];
             stopIfChanged = false;
-            path = [ cfgc.package pkgs.gawk ];
+            path = [ cfg.package pkgs.gawk ];
             environment.LD_LIBRARY_PATH = nssModulesPath;
 
             restartTriggers = optionals (!cfg.startWhenNeeded) [
@@ -582,7 +604,7 @@ in
             serviceConfig =
               { ExecStart =
                   (optionalString cfg.startWhenNeeded "-") +
-                  "${cfgc.package}/bin/sshd " + (optionalString cfg.startWhenNeeded "-i ") +
+                  "${cfg.package}/bin/sshd " + (optionalString cfg.startWhenNeeded "-i ") +
                   "-D " +  # don't detach into a daemon process
                   "-f /etc/ssh/sshd_config";
                 KillMode = "process";
@@ -628,14 +650,17 @@ in
     security.pam.services.sshd = lib.mkIf cfg.settings.UsePAM
       { startSession = true;
         showMotd = true;
-        unixAuth = cfg.settings.PasswordAuthentication;
+        unixAuth =
+          if cfg.settings.PasswordAuthentication == true
+          then true
+          else false;
       };
 
     # These values are merged with the ones defined externally, see:
     # https://github.com/NixOS/nixpkgs/pull/10155
     # https://github.com/NixOS/nixpkgs/pull/41745
     services.openssh.authorizedKeysFiles =
-      [ "%h/.ssh/authorized_keys" "/etc/ssh/authorized_keys.d/%u" ];
+      lib.optional cfg.authorizedKeysInHomedir "%h/.ssh/authorized_keys" ++ [ "/etc/ssh/authorized_keys.d/%u" ];
 
     services.openssh.settings.AuthorizedPrincipalsFile = mkIf (authPrincipalsFiles != {}) "/etc/ssh/authorized_principals.d/%u";
 
@@ -690,6 +715,10 @@ in
 
     assertions = [{ assertion = if cfg.settings.X11Forwarding then cfgc.setXAuthLocation else true;
                     message = "cannot enable X11 forwarding without setting xauth location";}
+                  { assertion = (builtins.match "(.*\n)?(\t )*[Kk][Ee][Rr][Bb][Ee][Rr][Oo][Ss][Aa][Uu][Tt][Hh][Ee][Nn][Tt][Ii][Cc][Aa][Tt][Ii][Oo][Nn][ |\t|=|\"]+yes.*" "${configFile}\n${cfg.extraConfig}") != null -> cfgc.package.withKerberos;
+                    message = "cannot enable Kerberos authentication without using a package with Kerberos support";}
+                  { assertion = (builtins.match "(.*\n)?(\t )*[Gg][Ss][Ss][Aa][Pp][Ii][Aa][Uu][Tt][Hh][Ee][Nn][Tt][Ii][Cc][Aa][Tt][Ii][Oo][Nn][ |\t|=|\"]+yes.*" "${configFile}\n${cfg.extraConfig}") != null -> cfgc.package.withKerberos;
+                    message = "cannot enable GSSAPI authentication without using a package with Kerberos support";}
                   (let
                     duplicates =
                       # Filter out the groups with more than 1 element
diff --git a/nixos/modules/services/networking/sunshine.nix b/nixos/modules/services/networking/sunshine.nix
index 0749eaee95d8a..ec78db1f3f8e9 100644
--- a/nixos/modules/services/networking/sunshine.nix
+++ b/nixos/modules/services/networking/sunshine.nix
@@ -1,6 +1,6 @@
 { config, lib, pkgs, utils, ... }:
 let
-  inherit (lib) mkEnableOption mkPackageOption mkOption mkIf mkDefault types optionals getExe;
+  inherit (lib) mkEnableOption mkPackageOption mkOption literalExpression mkIf mkDefault types optionals getExe;
   inherit (utils) escapeSystemdExecArgs;
   cfg = config.services.sunshine;
 
@@ -46,7 +46,7 @@ in
 
         See https://docs.lizardbyte.dev/projects/sunshine/en/latest/about/advanced_usage.html#configuration for syntax.
       '';
-      example = ''
+      example = literalExpression ''
         {
           sunshine_name = "nixos";
         }
@@ -67,7 +67,7 @@ in
       description = ''
         Configuration for applications to be exposed to Moonlight. If this is set, no configuration is possible from the web UI, and must be by the `settings` option.
       '';
-      example = ''
+      example = literalExpression ''
         {
           env = {
             PATH = "$(PATH):$(HOME)/.local/bin";
diff --git a/nixos/modules/services/networking/tailscale-auth.nix b/nixos/modules/services/networking/tailscale-auth.nix
index c3a515212e782..f21d1f108911c 100644
--- a/nixos/modules/services/networking/tailscale-auth.nix
+++ b/nixos/modules/services/networking/tailscale-auth.nix
@@ -14,7 +14,7 @@ let
 in
 {
   options.services.tailscaleAuth = {
-    enable = mkEnableOption "Enable tailscale.nginx-auth, to authenticate users via tailscale.";
+    enable = mkEnableOption "tailscale.nginx-auth, to authenticate users via tailscale";
 
     package = mkPackageOption pkgs "tailscale-nginx-auth" {};
 
diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix
index a79e47d8491b8..a690dc610e825 100644
--- a/nixos/modules/services/networking/tailscale.nix
+++ b/nixos/modules/services/networking/tailscale.nix
@@ -61,12 +61,21 @@ in {
     };
 
     extraUpFlags = mkOption {
-      description = "Extra flags to pass to {command}`tailscale up`.";
+      description = ''
+        Extra flags to pass to {command}`tailscale up`. Only applied if `authKeyFile` is specified.";
+      '';
       type = types.listOf types.str;
       default = [];
       example = ["--ssh"];
     };
 
+    extraSetFlags = mkOption {
+      description = "Extra flags to pass to {command}`tailscale set`.";
+      type = types.listOf types.str;
+      default = [];
+      example = ["--advertise-exit-node"];
+    };
+
     extraDaemonFlags = mkOption {
       description = "Extra flags to pass to {command}`tailscaled`.";
       type = types.listOf types.str;
@@ -120,6 +129,18 @@ in {
       '';
     };
 
+    systemd.services.tailscaled-set = mkIf (cfg.extraSetFlags != []) {
+      after = ["tailscaled.service"];
+      wants = ["tailscaled.service"];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "oneshot";
+      };
+      script = ''
+        ${cfg.package}/bin/tailscale set ${escapeShellArgs cfg.extraSetFlags}
+      '';
+    };
+
     boot.kernel.sysctl = mkIf (cfg.useRoutingFeatures == "server" || cfg.useRoutingFeatures == "both") {
       "net.ipv4.conf.all.forwarding" = mkOverride 97 true;
       "net.ipv6.conf.all.forwarding" = mkOverride 97 true;
diff --git a/nixos/modules/services/networking/tayga.nix b/nixos/modules/services/networking/tayga.nix
index 1a0df33fe883d..9f118b243e90c 100644
--- a/nixos/modules/services/networking/tayga.nix
+++ b/nixos/modules/services/networking/tayga.nix
@@ -16,6 +16,8 @@ let
     prefix ${strAddr cfg.ipv6.pool}
     dynamic-pool ${strAddr cfg.ipv4.pool}
     data-dir ${cfg.dataDir}
+
+    ${concatStringsSep "\n" (mapAttrsToList (ipv4: ipv6: "map " + ipv4 + " " + ipv6) cfg.mappings)}
   '';
 
   addrOpts = v:
@@ -103,18 +105,38 @@ in
       dataDir = mkOption {
         type = types.path;
         default = "/var/lib/tayga";
-        description = "Directory for persistent data";
+        description = "Directory for persistent data.";
       };
 
       tunDevice = mkOption {
         type = types.str;
         default = "nat64";
-        description = "Name of the nat64 tun device";
+        description = "Name of the nat64 tun device.";
+      };
+
+      mappings = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        description = "Static IPv4 -> IPv6 host mappings.";
+        example = literalExpression ''
+          {
+            "192.168.5.42" = "2001:db8:1:4444::1";
+            "192.168.5.43" = "2001:db8:1:4444::2";
+            "192.168.255.2" = "2001:db8:1:569::143";
+          }
+        '';
       };
     };
   };
 
   config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = allUnique (attrValues cfg.mappings);
+        message = "Neither the IPv4 nor the IPv6 addresses must be entered twice in the mappings.";
+      }
+    ];
+
     networking.interfaces."${cfg.tunDevice}" = {
       virtual = true;
       virtualType = "tun";
diff --git a/nixos/modules/services/networking/vsftpd.nix b/nixos/modules/services/networking/vsftpd.nix
index 25f950600b91c..07b93e92a7509 100644
--- a/nixos/modules/services/networking/vsftpd.nix
+++ b/nixos/modules/services/networking/vsftpd.nix
@@ -278,7 +278,7 @@ in
       }
       {
         assertion = (cfg.enableVirtualUsers -> cfg.userDbPath != null)
-                 && (cfg.enableVirtualUsers -> cfg.localUsers != null);
+                 && (cfg.enableVirtualUsers -> cfg.localUsers);
         message = "vsftpd: If enableVirtualUsers is true, you need to setup both the userDbPath and localUsers options.";
       }];
 
diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix
index 3f68af3a86c96..81abae2c9303d 100644
--- a/nixos/modules/services/networking/wireguard.nix
+++ b/nixos/modules/services/networking/wireguard.nix
@@ -80,6 +80,15 @@ let
         description = "Commands called at the end of the interface setup.";
       };
 
+      preShutdown = mkOption {
+        example = literalExpression ''"''${pkgs.iproute2}/bin/ip netns del foo"'';
+        default = "";
+        type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines;
+        description = ''
+          Commands called before shutting down the interface.
+        '';
+      };
+
       postShutdown = mkOption {
         example = literalExpression ''"''${pkgs.openresolv}/bin/resolvconf -d wg0"'';
         default = "";
@@ -497,6 +506,7 @@ let
         '';
 
         postStop = ''
+          ${values.preShutdown}
           ${ipPostMove} link del dev "${name}"
           ${values.postShutdown}
         '';
diff --git a/nixos/modules/services/networking/wstunnel.nix b/nixos/modules/services/networking/wstunnel.nix
index efb65aead116a..bd7536351955a 100644
--- a/nixos/modules/services/networking/wstunnel.nix
+++ b/nixos/modules/services/networking/wstunnel.nix
@@ -1,95 +1,94 @@
-{ config, lib, options, pkgs, utils, ... }:
-with lib;
+{ config
+, lib
+, pkgs
+, ...
+}:
+
 let
   cfg = config.services.wstunnel;
-  attrsToArgs = attrs: utils.escapeSystemdExecArgs (
-    mapAttrsToList
-    (name: value: if value == true then "--${name}" else "--${name}=${value}")
-    attrs
-  );
+
+  hostPortToString = { host, port }: "${host}:${toString port}";
+
   hostPortSubmodule = {
     options = {
-      host = mkOption {
+      host = lib.mkOption {
         description = "The hostname.";
-        type = types.str;
+        type = lib.types.str;
       };
-      port = mkOption {
+      port = lib.mkOption {
         description = "The port.";
-        type = types.port;
-      };
-    };
-  };
-  localRemoteSubmodule = {
-    options = {
-      local = mkOption {
-        description = "Local address and port to listen on.";
-        type = types.submodule hostPortSubmodule;
-        example = {
-          host = "127.0.0.1";
-          port = 51820;
-        };
-      };
-      remote = mkOption {
-        description = "Address and port on remote to forward traffic to.";
-        type = types.submodule hostPortSubmodule;
-        example = {
-          host = "127.0.0.1";
-          port = 51820;
-        };
+        type = lib.types.port;
       };
     };
   };
-  hostPortToString = { host, port }: "${host}:${builtins.toString port}";
-  localRemoteToString = { local, remote }: utils.escapeSystemdExecArg "${hostPortToString local}:${hostPortToString remote}";
+
   commonOptions = {
-    enable = mkOption {
-      description = "Whether to enable this `wstunnel` instance.";
-      type = types.bool;
+    enable = lib.mkEnableOption "this `wstunnel` instance." // {
       default = true;
     };
 
-    package = mkPackageOption pkgs "wstunnel" {};
+    package = lib.mkPackageOption pkgs "wstunnel" { };
 
-    autoStart = mkOption {
-      description = "Whether this tunnel server should be started automatically.";
-      type = types.bool;
-      default = true;
-    };
+    autoStart =
+      lib.mkEnableOption "starting this wstunnel instance automatically." // {
+        default = true;
+      };
 
-    extraArgs = mkOption {
-      description = "Extra command line arguments to pass to `wstunnel`. Attributes of the form `argName = true;` will be translated to `--argName`, and `argName = \"value\"` to `--argName=value`.";
-      type = with types; attrsOf (either str bool);
-      default = {};
+    extraArgs = lib.mkOption {
+      description = ''
+        Extra command line arguments to pass to `wstunnel`.
+        Attributes of the form `argName = true;` will be translated to `--argName`,
+        and `argName = \"value\"` to `--argName value`.
+      '';
+      type = with lib.types; attrsOf (either str bool);
+      default = { };
       example = {
         "someNewOption" = true;
         "someNewOptionWithValue" = "someValue";
       };
     };
 
-    verboseLogging = mkOption {
-      description = "Enable verbose logging.";
-      type = types.bool;
-      default = false;
+    loggingLevel = lib.mkOption {
+      description = ''
+        Passed to --log-lvl
+
+        Control the log verbosity. i.e: TRACE, DEBUG, INFO, WARN, ERROR, OFF
+        For more details, checkout [EnvFilter](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax)
+      '';
+      type = lib.types.nullOr lib.types.str;
+      example = "INFO";
+      default = null;
     };
 
-    environmentFile = mkOption {
-      description = "Environment file to be passed to the systemd service. Useful for passing secrets to the service to prevent them from being world-readable in the Nix store. Note however that the secrets are passed to `wstunnel` through the command line, which makes them locally readable for all users of the system at runtime.";
-      type = types.nullOr types.path;
+    environmentFile = lib.mkOption {
+      description = ''
+        Environment file to be passed to the systemd service.
+        Useful for passing secrets to the service to prevent them from being
+        world-readable in the Nix store.
+        Note however that the secrets are passed to `wstunnel` through
+        the command line, which makes them locally readable for all users of
+        the system at runtime.
+      '';
+      type = lib.types.nullOr lib.types.path;
       default = null;
       example = "/var/lib/secrets/wstunnelSecrets";
     };
   };
 
-  serverSubmodule = { config, ...}: {
+  serverSubmodule = { config, ... }: {
     options = commonOptions // {
-      listen = mkOption {
-        description = "Address and port to listen on. Setting the port to a value below 1024 will also give the process the required `CAP_NET_BIND_SERVICE` capability.";
-        type = types.submodule hostPortSubmodule;
+      listen = lib.mkOption {
+        description = ''
+          Address and port to listen on.
+          Setting the port to a value below 1024 will also give the process
+          the required `CAP_NET_BIND_SERVICE` capability.
+        '';
+        type = lib.types.submodule hostPortSubmodule;
         default = {
           host = "0.0.0.0";
           port = if config.enableHTTPS then 443 else 80;
         };
-        defaultText = literalExpression ''
+        defaultText = lib.literalExpression ''
           {
             host = "0.0.0.0";
             port = if enableHTTPS then 443 else 80;
@@ -97,250 +96,252 @@ let
         '';
       };
 
-      restrictTo = mkOption {
-        description = "Accepted traffic will be forwarded only to this service. Set to `null` to allow forwarding to arbitrary addresses.";
-        type = types.nullOr (types.submodule hostPortSubmodule);
-        example = {
+      restrictTo = lib.mkOption {
+        description = ''
+          Accepted traffic will be forwarded only to this service.
+        '';
+        type = lib.types.listOf (lib.types.submodule hostPortSubmodule);
+        default = [ ];
+        example = [{
           host = "127.0.0.1";
           port = 51820;
-        };
+        }];
       };
 
-      enableHTTPS = mkOption {
+      enableHTTPS = lib.mkOption {
         description = "Use HTTPS for the tunnel server.";
-        type = types.bool;
+        type = lib.types.bool;
         default = true;
       };
 
-      tlsCertificate = mkOption {
-        description = "TLS certificate to use instead of the hardcoded one in case of HTTPS connections. Use together with `tlsKey`.";
-        type = types.nullOr types.path;
+      tlsCertificate = lib.mkOption {
+        description = ''
+          TLS certificate to use instead of the hardcoded one in case of HTTPS connections.
+          Use together with `tlsKey`.
+        '';
+        type = lib.types.nullOr lib.types.path;
         default = null;
         example = "/var/lib/secrets/cert.pem";
       };
 
-      tlsKey = mkOption {
-        description = "TLS key to use instead of the hardcoded on in case of HTTPS connections. Use together with `tlsCertificate`.";
-        type = types.nullOr types.path;
+      tlsKey = lib.mkOption {
+        description = ''
+          TLS key to use instead of the hardcoded on in case of HTTPS connections.
+          Use together with `tlsCertificate`.
+        '';
+        type = lib.types.nullOr lib.types.path;
         default = null;
         example = "/var/lib/secrets/key.pem";
       };
 
-      useACMEHost = mkOption {
-        description = "Use a certificate generated by the NixOS ACME module for the given host. Note that this will not generate a new certificate - you will need to do so with `security.acme.certs`.";
-        type = types.nullOr types.str;
+      useACMEHost = lib.mkOption {
+        description = ''
+          Use a certificate generated by the NixOS ACME module for the given host.
+          Note that this will not generate a new certificate - you will need to do so with `security.acme.certs`.
+        '';
+        type = lib.types.nullOr lib.types.str;
         default = null;
         example = "example.com";
       };
     };
   };
+
   clientSubmodule = { config, ... }: {
     options = commonOptions // {
-      connectTo = mkOption {
+      connectTo = lib.mkOption {
         description = "Server address and port to connect to.";
-        type = types.submodule hostPortSubmodule;
-        example = {
-          host = "example.com";
-        };
-      };
-
-      enableHTTPS = mkOption {
-        description = "Enable HTTPS when connecting to the server.";
-        type = types.bool;
-        default = true;
-      };
-
-      localToRemote = mkOption {
-        description = "Local hosts and ports to listen on, plus the hosts and ports on remote to forward traffic to. Setting a local port to a value less than 1024 will additionally give the process the required CAP_NET_BIND_SERVICE capability.";
-        type = types.listOf (types.submodule localRemoteSubmodule);
-        default = [];
-        example = [ {
-          local = {
-            host = "127.0.0.1";
-            port = 8080;
-          };
-          remote = {
-            host = "127.0.0.1";
-            port = 8080;
-          };
-        } ];
+        type = lib.types.str;
+        example = "https://wstunnel.server.com:8443";
       };
 
-      dynamicToRemote = mkOption {
-        description = "Host and port for the SOCKS5 proxy to dynamically forward traffic to. Leave this at `null` to disable the SOCKS5 proxy. Setting the port to a value less than 1024 will additionally give the service the required CAP_NET_BIND_SERVICE capability.";
-        type = types.nullOr (types.submodule hostPortSubmodule);
-        default = null;
-        example = {
-          host = "127.0.0.1";
-          port = 1080;
-        };
+      localToRemote = lib.mkOption {
+        description = ''Listen on local and forwards traffic from remote.'';
+        type = lib.types.listOf (lib.types.str);
+        default = [ ];
+        example = [
+          "tcp://1212:google.com:443"
+          "unix:///tmp/wstunnel.sock:g.com:443"
+        ];
       };
 
-      udp = mkOption {
-        description = "Whether to forward UDP instead of TCP traffic.";
-        type = types.bool;
-        default = false;
+      remoteToLocal = lib.mkOption {
+        description = "Listen on remote and forwards traffic from local. Only tcp is supported";
+        type = lib.types.listOf lib.types.str;
+        default = [ ];
+        example = [
+          "tcp://1212:google.com:443"
+          "unix://wstunnel.sock:g.com:443"
+        ];
       };
 
-      udpTimeout = mkOption {
-        description = "When using UDP forwarding, timeout in seconds after which the tunnel connection is closed. `-1` means no timeout.";
-        type = types.int;
-        default = 30;
-      };
+      addNetBind = lib.mkEnableOption "Whether add CAP_NET_BIND_SERVICE to the tunnel service, this should be enabled if you want to bind port < 1024";
 
-      httpProxy = mkOption {
+      httpProxy = lib.mkOption {
         description = ''
           Proxy to use to connect to the wstunnel server (`USER:PASS@HOST:PORT`).
 
           ::: {.warning}
-          Passwords specified here will be world-readable in the Nix store! To pass a password to the service, point the `environmentFile` option to a file containing `PROXY_PASSWORD=<your-password-here>` and set this option to `<user>:$PROXY_PASSWORD@<host>:<port>`. Note however that this will also locally leak the passwords at runtime via e.g. /proc/<pid>/cmdline.
-
+          Passwords specified here will be world-readable in the Nix store!
+          To pass a password to the service, point the `environmentFile` option
+          to a file containing `PROXY_PASSWORD=<your-password-here>` and set
+          this option to `<user>:$PROXY_PASSWORD@<host>:<port>`.
+          Note however that this will also locally leak the passwords at
+          runtime via e.g. /proc/<pid>/cmdline.
           :::
         '';
-        type = types.nullOr types.str;
+        type = lib.types.nullOr lib.types.str;
         default = null;
       };
 
-      soMark = mkOption {
-        description = "Mark network packets with the SO_MARK sockoption with the specified value. Setting this option will also enable the required `CAP_NET_ADMIN` capability for the systemd service.";
-        type = types.nullOr types.int;
+      soMark = lib.mkOption {
+        description = ''
+          Mark network packets with the SO_MARK sockoption with the specified value.
+          Setting this option will also enable the required `CAP_NET_ADMIN` capability
+          for the systemd service.
+        '';
+        type = lib.types.nullOr lib.types.ints.unsigned;
         default = null;
       };
 
-      upgradePathPrefix = mkOption {
-        description = "Use a specific HTTP path prefix that will show up in the upgrade request to the `wstunnel` server. Useful when running `wstunnel` behind a reverse proxy.";
-        type = types.nullOr types.str;
+      upgradePathPrefix = lib.mkOption {
+        description = ''
+          Use a specific HTTP path prefix that will show up in the upgrade
+          request to the `wstunnel` server.
+          Useful when running `wstunnel` behind a reverse proxy.
+        '';
+        type = lib.types.nullOr lib.types.str;
         default = null;
         example = "wstunnel";
       };
 
-      hostHeader = mkOption {
-        description = "Use this as the HTTP host header instead of the real hostname. Useful for circumventing hostname-based firewalls.";
-        type = types.nullOr types.str;
-        default = null;
-      };
-
-      tlsSNI = mkOption {
+      tlsSNI = lib.mkOption {
         description = "Use this as the SNI while connecting via TLS. Useful for circumventing hostname-based firewalls.";
-        type = types.nullOr types.str;
+        type = lib.types.nullOr lib.types.str;
         default = null;
       };
 
-      tlsVerifyCertificate = mkOption {
+      tlsVerifyCertificate = lib.mkOption {
         description = "Whether to verify the TLS certificate of the server. It might be useful to set this to `false` when working with the `tlsSNI` option.";
-        type = types.bool;
+        type = lib.types.bool;
         default = true;
       };
 
       # The original argument name `websocketPingFrequency` is a misnomer, as the frequency is the inverse of the interval.
-      websocketPingInterval = mkOption {
-        description = "Do a heartbeat ping every N seconds to keep up the websocket connection.";
-        type = types.nullOr types.ints.unsigned;
+      websocketPingInterval = lib.mkOption {
+        description = "Frequency at which the client will send websocket ping to the server.";
+        type = lib.types.nullOr lib.types.ints.unsigned;
         default = null;
       };
 
-      upgradeCredentials = mkOption {
+      upgradeCredentials = lib.mkOption {
         description = ''
-          Use these credentials to authenticate during the HTTP upgrade request (Basic authorization type, `USER:[PASS]`).
+          Use these credentials to authenticate during the HTTP upgrade request
+          (Basic authorization type, `USER:[PASS]`).
 
           ::: {.warning}
-          Passwords specified here will be world-readable in the Nix store! To pass a password to the service, point the `environmentFile` option to a file containing `HTTP_PASSWORD=<your-password-here>` and set this option to `<user>:$HTTP_PASSWORD`. Note however that this will also locally leak the passwords at runtime via e.g. /proc/<pid>/cmdline.
+          Passwords specified here will be world-readable in the Nix store!
+          To pass a password to the service, point the `environmentFile` option
+          to a file containing `HTTP_PASSWORD=<your-password-here>` and set this
+          option to `<user>:$HTTP_PASSWORD`.
+          Note however that this will also locally leak the passwords at runtime
+          via e.g. /proc/<pid>/cmdline.
           :::
         '';
-        type = types.nullOr types.str;
+        type = lib.types.nullOr lib.types.str;
         default = null;
       };
 
-      customHeaders = mkOption {
+      customHeaders = lib.mkOption {
         description = "Custom HTTP headers to send during the upgrade request.";
-        type = types.attrsOf types.str;
-        default = {};
+        type = lib.types.attrsOf lib.types.str;
+        default = { };
         example = {
           "X-Some-Header" = "some-value";
         };
       };
     };
   };
+
   generateServerUnit = name: serverCfg: {
     name = "wstunnel-server-${name}";
-    value = {
-      description = "wstunnel server - ${name}";
-      requires = [ "network.target" "network-online.target" ];
-      after = [ "network.target" "network-online.target" ];
-      wantedBy = optional serverCfg.autoStart "multi-user.target";
-
-      serviceConfig = let
-        certConfig = config.security.acme.certs."${serverCfg.useACMEHost}";
-      in {
-        Type = "simple";
-        ExecStart = with serverCfg; let
-          resolvedTlsCertificate = if useACMEHost != null
-            then "${certConfig.directory}/fullchain.pem"
-            else tlsCertificate;
-          resolvedTlsKey = if useACMEHost != null
-            then "${certConfig.directory}/key.pem"
-            else tlsKey;
-        in ''
-          ${package}/bin/wstunnel \
-            --server \
-            ${optionalString (restrictTo != null)     "--restrictTo=${utils.escapeSystemdExecArg (hostPortToString restrictTo)}"} \
-            ${optionalString (resolvedTlsCertificate != null) "--tlsCertificate=${utils.escapeSystemdExecArg resolvedTlsCertificate}"} \
-            ${optionalString (resolvedTlsKey != null)         "--tlsKey=${utils.escapeSystemdExecArg resolvedTlsKey}"} \
-            ${optionalString verboseLogging "--verbose"} \
-            ${attrsToArgs extraArgs} \
-            ${utils.escapeSystemdExecArg "${if enableHTTPS then "wss" else "ws"}://${hostPortToString listen}"}
-        '';
-        EnvironmentFile = optional (serverCfg.environmentFile != null) serverCfg.environmentFile;
-        DynamicUser = true;
-        SupplementaryGroups = optional (serverCfg.useACMEHost != null) certConfig.group;
-        PrivateTmp = true;
-        AmbientCapabilities = optionals (serverCfg.listen.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
-        NoNewPrivileges = true;
-        RestrictNamespaces = "uts ipc pid user cgroup";
-        ProtectSystem = "strict";
-        ProtectHome = true;
-        ProtectKernelTunables = true;
-        ProtectKernelModules = true;
-        ProtectControlGroups = true;
-        PrivateDevices = true;
-        RestrictSUIDSGID = true;
+    value =
+      let
+        certConfig = config.security.acme.certs.${serverCfg.useACMEHost};
+      in
+      {
+        description = "wstunnel server - ${name}";
+        requires = [ "network.target" "network-online.target" ];
+        after = [ "network.target" "network-online.target" ];
+        wantedBy = lib.optional serverCfg.autoStart "multi-user.target";
+
+        environment.RUST_LOG = serverCfg.loggingLevel;
+
+        serviceConfig = {
+          Type = "exec";
+          EnvironmentFile =
+            lib.optional (serverCfg.environmentFile != null) serverCfg.environmentFile;
+          DynamicUser = true;
+          SupplementaryGroups =
+            lib.optional (serverCfg.useACMEHost != null) certConfig.group;
+          PrivateTmp = true;
+          AmbientCapabilities =
+            lib.optionals (serverCfg.listen.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
+          NoNewPrivileges = true;
+          RestrictNamespaces = "uts ipc pid user cgroup";
+          ProtectSystem = "strict";
+          ProtectHome = true;
+          ProtectKernelTunables = true;
+          ProtectKernelModules = true;
+          ProtectControlGroups = true;
+          PrivateDevices = true;
+          RestrictSUIDSGID = true;
+
+          Restart = "on-failure";
+          RestartSec = 2;
+          RestartSteps = 20;
+          RestartMaxDelaySec = "5min";
+        };
 
+        script = with serverCfg; ''
+          ${lib.getExe package} \
+            server \
+            ${lib.cli.toGNUCommandLineShell { } (
+              lib.recursiveUpdate
+              {
+                restrict-to = map hostPortToString restrictTo;
+                tls-certificate = if useACMEHost != null
+                                  then "${certConfig.directory}/fullchain.pem"
+                                  else "${tlsCertificate}";
+                tls-private-key = if useACMEHost != null
+                                  then "${certConfig.directory}/key.pem"
+                                  else "${tlsKey}";
+              }
+              extraArgs
+            )} \
+            ${lib.escapeShellArg "${if enableHTTPS then "wss" else "ws"}://${hostPortToString listen}"}
+        '';
       };
-    };
   };
+
   generateClientUnit = name: clientCfg: {
     name = "wstunnel-client-${name}";
     value = {
       description = "wstunnel client - ${name}";
       requires = [ "network.target" "network-online.target" ];
       after = [ "network.target" "network-online.target" ];
-      wantedBy = optional clientCfg.autoStart "multi-user.target";
+      wantedBy = lib.optional clientCfg.autoStart "multi-user.target";
+
+      environment.RUST_LOG = clientCfg.loggingLevel;
 
       serviceConfig = {
-        Type = "simple";
-        ExecStart = with clientCfg; ''
-          ${package}/bin/wstunnel \
-            ${concatStringsSep " " (builtins.map (x:          "--localToRemote=${localRemoteToString x}") localToRemote)} \
-            ${concatStringsSep " " (mapAttrsToList (n: v:     "--customHeaders=\"${n}: ${v}\"") customHeaders)} \
-            ${optionalString (dynamicToRemote != null)        "--dynamicToRemote=${utils.escapeSystemdExecArg (hostPortToString dynamicToRemote)}"} \
-            ${optionalString udp                              "--udp"} \
-            ${optionalString (httpProxy != null)              "--httpProxy=${httpProxy}"} \
-            ${optionalString (soMark != null)                 "--soMark=${toString soMark}"} \
-            ${optionalString (upgradePathPrefix != null)      "--upgradePathPrefix=${upgradePathPrefix}"} \
-            ${optionalString (hostHeader != null)             "--hostHeader=${hostHeader}"} \
-            ${optionalString (tlsSNI != null)                 "--tlsSNI=${tlsSNI}"} \
-            ${optionalString tlsVerifyCertificate             "--tlsVerifyCertificate"} \
-            ${optionalString (websocketPingInterval != null)  "--websocketPingFrequency=${toString websocketPingInterval}"} \
-            ${optionalString (upgradeCredentials != null)     "--upgradeCredentials=${upgradeCredentials}"} \
-            --udpTimeoutSec=${toString udpTimeout} \
-            ${optionalString verboseLogging "--verbose"} \
-            ${attrsToArgs extraArgs} \
-            ${utils.escapeSystemdExecArg "${if enableHTTPS then "wss" else "ws"}://${hostPortToString connectTo}"}
-        '';
-        EnvironmentFile = optional (clientCfg.environmentFile != null) clientCfg.environmentFile;
+        Type = "exec";
+        EnvironmentFile =
+          lib.optional (clientCfg.environmentFile != null) clientCfg.environmentFile;
         DynamicUser = true;
         PrivateTmp = true;
-        AmbientCapabilities = (optionals (clientCfg.soMark != null) [ "CAP_NET_ADMIN" ]) ++ (optionals ((clientCfg.dynamicToRemote.port or 1024) < 1024 || (any (x: x.local.port < 1024) clientCfg.localToRemote)) [ "CAP_NET_BIND_SERVICE" ]);
+        AmbientCapabilities =
+          (lib.optionals clientCfg.addNetBind [ "CAP_NET_BIND_SERVICE" ]) ++
+          (lib.optionals (clientCfg.soMark != null) [ "CAP_NET_ADMIN" ]);
         NoNewPrivileges = true;
         RestrictNamespaces = "uts ipc pid user cgroup";
         ProtectSystem = "strict";
@@ -350,80 +351,118 @@ let
         ProtectControlGroups = true;
         PrivateDevices = true;
         RestrictSUIDSGID = true;
+
+        Restart = "on-failure";
+        RestartSec = 2;
+        RestartSteps = 20;
+        RestartMaxDelaySec = "5min";
       };
+
+      script = with clientCfg; ''
+        ${lib.getExe package} \
+          client \
+          ${lib.cli.toGNUCommandLineShell { } (
+            lib.recursiveUpdate
+            {
+              local-to-remote = localToRemote;
+              remote-to-local = remoteToLocal;
+              http-headers = lib.mapAttrsToList (n: v: "${n}:${v}") customHeaders;
+              http-proxy = httpProxy;
+              socket-so-mark = soMark;
+              http-upgrade-path-prefix = upgradePathPrefix;
+              tls-sni-override = tlsSNI;
+              tls-verify-certificate = tlsVerifyCertificate;
+              websocket-ping-frequency-sec = websocketPingInterval;
+              http-upgrade-credentials = upgradeCredentials;
+            }
+            extraArgs
+          )} \
+          ${lib.escapeShellArg connectTo}
+      '';
     };
   };
-in {
+in
+{
   options.services.wstunnel = {
-    enable = mkEnableOption "wstunnel";
+    enable = lib.mkEnableOption "wstunnel";
 
-    servers = mkOption {
+    servers = lib.mkOption {
       description = "`wstunnel` servers to set up.";
-      type = types.attrsOf (types.submodule serverSubmodule);
-      default = {};
+      type = lib.types.attrsOf (lib.types.submodule serverSubmodule);
+      default = { };
       example = {
         "wg-tunnel" = {
-          listen.port = 8080;
+          listen = {
+            host = "0.0.0.0";
+            port = 8080;
+          };
           enableHTTPS = true;
           tlsCertificate = "/var/lib/secrets/fullchain.pem";
           tlsKey = "/var/lib/secrets/key.pem";
-          restrictTo = {
+          restrictTo = [{
             host = "127.0.0.1";
             port = 51820;
-          };
+          }];
         };
       };
     };
 
-    clients = mkOption {
+    clients = lib.mkOption {
       description = "`wstunnel` clients to set up.";
-      type = types.attrsOf (types.submodule clientSubmodule);
-      default = {};
+      type = lib.types.attrsOf (lib.types.submodule clientSubmodule);
+      default = { };
       example = {
         "wg-tunnel" = {
-          connectTo = {
-            host = "example.com";
-            port = 8080;
-          };
-          enableHTTPS = true;
-          localToRemote = {
-            local = {
-              host = "127.0.0.1";
-              port = 51820;
-            };
-            remote = {
-              host = "127.0.0.1";
-              port = 51820;
-            };
-          };
-          udp = true;
+          connectTo = "wss://wstunnel.server.com:8443";
+          localToRemote = [
+            "tcp://1212:google.com:443"
+            "tcp://2:n.lan:4?proxy_protocol"
+          ];
+          remoteToLocal = [
+            "socks5://[::1]:1212"
+            "unix://wstunnel.sock:g.com:443"
+          ];
         };
       };
     };
   };
 
-  config = mkIf cfg.enable {
-    systemd.services = (mapAttrs' generateServerUnit (filterAttrs (n: v: v.enable) cfg.servers)) // (mapAttrs' generateClientUnit (filterAttrs (n: v: v.enable) cfg.clients));
-
-    assertions = (mapAttrsToList (name: serverCfg: {
-      assertion = !(serverCfg.useACMEHost != null && (serverCfg.tlsCertificate != null || serverCfg.tlsKey != null));
-      message = ''
-        Options services.wstunnel.servers."${name}".useACMEHost and services.wstunnel.servers."${name}".{tlsCertificate, tlsKey} are mutually exclusive.
-      '';
-    }) cfg.servers) ++
-    (mapAttrsToList (name: serverCfg: {
-      assertion = !((serverCfg.tlsCertificate != null || serverCfg.tlsKey != null) && !(serverCfg.tlsCertificate != null && serverCfg.tlsKey != null));
-      message = ''
-        services.wstunnel.servers."${name}".tlsCertificate and services.wstunnel.servers."${name}".tlsKey need to be set together.
-      '';
-    }) cfg.servers) ++
-    (mapAttrsToList (name: clientCfg: {
-      assertion = !(clientCfg.localToRemote == [] && clientCfg.dynamicToRemote == null);
-      message = ''
-        Either one of services.wstunnel.clients."${name}".localToRemote or services.wstunnel.clients."${name}".dynamicToRemote must be set.
-      '';
-    }) cfg.clients);
+  config = lib.mkIf cfg.enable {
+    systemd.services =
+      (lib.mapAttrs' generateServerUnit (lib.filterAttrs (n: v: v.enable) cfg.servers)) //
+      (lib.mapAttrs' generateClientUnit (lib.filterAttrs (n: v: v.enable) cfg.clients));
+
+    assertions =
+      (lib.mapAttrsToList
+        (name: serverCfg: {
+          assertion =
+            !(serverCfg.useACMEHost != null && serverCfg.tlsCertificate != null);
+          message = ''
+            Options services.wstunnel.servers."${name}".useACMEHost and services.wstunnel.servers."${name}".{tlsCertificate, tlsKey} are mutually exclusive.
+          '';
+        })
+        cfg.servers) ++
+
+      (lib.mapAttrsToList
+        (name: serverCfg: {
+          assertion =
+            (serverCfg.tlsCertificate == null && serverCfg.tlsKey == null) ||
+            (serverCfg.tlsCertificate != null && serverCfg.tlsKey != null);
+          message = ''
+            services.wstunnel.servers."${name}".tlsCertificate and services.wstunnel.servers."${name}".tlsKey need to be set together.
+          '';
+        })
+        cfg.servers) ++
+
+      (lib.mapAttrsToList
+        (name: clientCfg: {
+          assertion = !(clientCfg.localToRemote == [ ] && clientCfg.remoteToLocal == [ ]);
+          message = ''
+            Either one of services.wstunnel.clients."${name}".localToRemote or services.wstunnel.clients."${name}".remoteToLocal must be set.
+          '';
+        })
+        cfg.clients);
   };
 
-  meta.maintainers = with maintainers; [ alyaeanyx ];
+  meta.maintainers = with lib.maintainers; [ alyaeanyx rvdp neverbehave ];
 }
diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix
index 86c1efc629a98..68c04118fdd58 100644
--- a/nixos/modules/services/networking/zerotierone.nix
+++ b/nixos/modules/services/networking/zerotierone.nix
@@ -4,7 +4,9 @@ with lib;
 
 let
   cfg = config.services.zerotierone;
-  localConfFile = pkgs.writeText "zt-local.conf" (builtins.toJSON cfg.localConf);
+
+  settingsFormat = pkgs.formats.json {};
+  localConfFile = settingsFormat.generate "zt-local.conf" cfg.localConf;
   localConfFilePath = "/var/lib/zerotier-one/local.conf";
 in
 {
@@ -41,7 +43,7 @@ in
     example = {
       settings.allowTcpFallbackRelay = false;
     };
-    type = types.nullOr types.attrs;
+    type = settingsFormat.type;
   };
 
   config = mkIf cfg.enable {
@@ -60,7 +62,7 @@ in
         chown -R root:root /var/lib/zerotier-one
       '' + (concatMapStrings (netId: ''
         touch "/var/lib/zerotier-one/networks.d/${netId}.conf"
-      '') cfg.joinNetworks) + optionalString (cfg.localConf != null) ''
+      '') cfg.joinNetworks) + optionalString (cfg.localConf != {}) ''
         if [ -L "${localConfFilePath}" ]
         then
           rm ${localConfFilePath}
diff --git a/nixos/modules/services/search/qdrant.nix b/nixos/modules/services/search/qdrant.nix
index f28178a5f1751..41a4e9b41f6d9 100644
--- a/nixos/modules/services/search/qdrant.nix
+++ b/nixos/modules/services/search/qdrant.nix
@@ -60,6 +60,7 @@ in {
 
   config = mkIf cfg.enable {
     services.qdrant.settings = {
+      service.static_content_dir = mkDefault pkgs.qdrant-web-ui;
       storage.storage_path = mkDefault "/var/lib/qdrant/storage";
       storage.snapshots_path = mkDefault "/var/lib/qdrant/snapshots";
       # The following default values are the same as in the default config,
diff --git a/nixos/modules/services/search/quickwit.nix b/nixos/modules/services/search/quickwit.nix
new file mode 100644
index 0000000000000..6b2db935cf0bf
--- /dev/null
+++ b/nixos/modules/services/search/quickwit.nix
@@ -0,0 +1,190 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.quickwit;
+
+  settingsFormat = pkgs.formats.yaml {};
+  quickwitYml = settingsFormat.generate "quickwit.yml" cfg.settings;
+
+  usingDefaultDataDir = cfg.dataDir == "/var/lib/quickwit";
+  usingDefaultUserAndGroup = cfg.user == "quickwit" && cfg.group == "quickwit";
+in
+{
+
+  options.services.quickwit = {
+    enable = mkEnableOption "Quickwit";
+
+    package = lib.mkPackageOption pkgs "Quickwit" {
+      default = [ "quickwit" ];
+    };
+
+    settings = lib.mkOption {
+      type = lib.types.submodule {
+        freeformType = settingsFormat.type;
+
+        options."rest" = lib.mkOption {
+          default = {};
+          description = ''
+            Rest server configuration for Quickwit
+          '';
+
+          type = lib.types.submodule {
+            freeformType = settingsFormat.type;
+
+            options."listen_port" = lib.mkOption {
+              type = lib.types.port;
+              default = 7280;
+              description = ''
+                The port to listen on for HTTP REST traffic.
+              '';
+            };
+          };
+        };
+
+        options."grpc_listen_port" = lib.mkOption {
+          type = lib.types.port;
+          default = 7281;
+          description = ''
+            The port to listen on for gRPC traffic.
+          '';
+        };
+
+        options."listen_address" = lib.mkOption {
+          type = lib.types.str;
+          default = "127.0.0.1";
+          description = ''
+            Listen address of Quickwit.
+          '';
+        };
+
+        options."version" = lib.mkOption {
+          type = lib.types.float;
+          default = 0.7;
+          description = ''
+            Configuration file version.
+          '';
+        };
+      };
+
+      default = {};
+
+      description = ''
+        Quickwit configuration.
+      '';
+    };
+
+    dataDir = lib.mkOption {
+      type = lib.types.path;
+      default = "/var/lib/quickwit";
+      apply = converge (removeSuffix "/");
+      description = ''
+        Data directory for Quickwit. If you change this, you need to
+        manually create the directory. You also need to create the
+        `quickwit` user and group, or change
+        [](#opt-services.quickwit.user) and
+        [](#opt-services.quickwit.group) to existing ones with
+        access to the directory.
+      '';
+    };
+
+    user = lib.mkOption {
+      type = lib.types.str;
+      default = "quickwit";
+      description = ''
+        The user Quickwit runs as. Should be left at default unless
+        you have very specific needs.
+      '';
+    };
+
+    group = lib.mkOption {
+      type = lib.types.str;
+      default = "quickwit";
+      description = ''
+        The group quickwit runs as. Should be left at default unless
+        you have very specific needs.
+      '';
+    };
+
+    extraFlags = lib.mkOption {
+      description = "Extra command line options to pass to Quickwit.";
+      default = [ ];
+      type = lib.types.listOf lib.types.str;
+    };
+
+    restartIfChanged = lib.mkOption {
+      type = lib.types.bool;
+      description = ''
+        Automatically restart the service on config change.
+        This can be set to false to defer restarts on a server or cluster.
+        Please consider the security implications of inadvertently running an older version,
+        and the possibility of unexpected behavior caused by inconsistent versions across a cluster when disabling this option.
+      '';
+      default = true;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.quickwit = {
+      description = "Quickwit";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      inherit (cfg) restartIfChanged;
+      environment = {
+        QW_DATA_DIR = cfg.dataDir;
+      };
+      serviceConfig = {
+        ExecStart = ''
+          ${cfg.package}/bin/quickwit run --config ${quickwitYml} \
+          ${escapeShellArgs cfg.extraFlags}
+        '';
+        User = cfg.user;
+        Group = cfg.group;
+        Restart = "on-failure";
+        DynamicUser = usingDefaultUserAndGroup && usingDefaultDataDir;
+        CapabilityBoundingSet = [ "" ];
+        DevicePolicy = "closed";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectControlGroups = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        ReadWritePaths = [
+          "/var/lib/quickwit"
+        ];
+        RestrictAddressFamilies = [
+          "AF_NETLINK"
+          "AF_INET"
+          "AF_INET6"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          # 1. allow a reasonable set of syscalls
+          "@system-service @resources"
+          # 2. and deny unreasonable ones
+          "~@privileged"
+          # 3. then allow the required subset within denied groups
+          "@chown"
+        ];
+      } // (optionalAttrs (usingDefaultDataDir) {
+        StateDirectory = "quickwit";
+        StateDirectoryMode = "0700";
+      });
+    };
+
+    environment.systemPackages = [ cfg.package ];
+  };
+}
diff --git a/nixos/modules/services/security/bitwarden-directory-connector-cli.nix b/nixos/modules/services/security/bitwarden-directory-connector-cli.nix
index d21322caf4c33..fef4a88648979 100644
--- a/nixos/modules/services/security/bitwarden-directory-connector-cli.nix
+++ b/nixos/modules/services/security/bitwarden-directory-connector-cli.nix
@@ -260,6 +260,7 @@ in {
         description = "Sync timer for Bitwarden Directory Connector";
         wantedBy = ["timers.target"];
         after = ["network-online.target"];
+        wants = ["network-online.target"];
         timerConfig = {
           OnCalendar = cfg.interval;
           Unit = "bitwarden-directory-connector-cli.service";
diff --git a/nixos/modules/services/security/oauth2-proxy-nginx.nix b/nixos/modules/services/security/oauth2-proxy-nginx.nix
index c05bd304752d1..2dffeb993803f 100644
--- a/nixos/modules/services/security/oauth2-proxy-nginx.nix
+++ b/nixos/modules/services/security/oauth2-proxy-nginx.nix
@@ -64,25 +64,35 @@ in
     };
   };
 
-  config.services.oauth2-proxy = lib.mkIf (cfg.virtualHosts != [] && (lib.hasPrefix "127.0.0.1:" cfg.proxy)) {
+  config.services.oauth2-proxy = lib.mkIf (cfg.virtualHosts != {} && (lib.hasPrefix "127.0.0.1:" cfg.proxy)) {
     enable = true;
   };
 
-  config.services.nginx = lib.mkIf (cfg.virtualHosts != [] && config.services.oauth2-proxy.enable) (lib.mkMerge ([
+  config.services.nginx = lib.mkIf (cfg.virtualHosts != {} && config.services.oauth2-proxy.enable) (lib.mkMerge ([
     {
       virtualHosts.${cfg.domain}.locations."/oauth2/" = {
         proxyPass = cfg.proxy;
         extraConfig = ''
+          auth_request off;
           proxy_set_header X-Scheme                $scheme;
           proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
         '';
       };
     }
-  ] ++ lib.optional (cfg.virtualHosts != []) {
+  ] ++ lib.optional (cfg.virtualHosts != {}) {
     recommendedProxySettings = true; # needed because duplicate headers
   } ++ (lib.mapAttrsToList (vhost: conf: {
     virtualHosts.${vhost} = {
       locations = {
+        "/".extraConfig = ''
+          # pass information via X-User and X-Email headers to backend, requires running with --set-xauthrequest flag
+          proxy_set_header X-User  $user;
+          proxy_set_header X-Email $email;
+
+          # if you enabled --cookie-refresh, this is needed for it to work with auth_request
+          add_header Set-Cookie $auth_cookie;
+        '';
+
         "/oauth2/auth" = let
           maybeQueryArg = name: value:
             if value == null then null
@@ -102,6 +112,7 @@ in
             proxy_pass_request_body           off;
           '';
         };
+
         "@redirectToAuth2ProxyLogin" = {
           return = "307 https://${cfg.domain}/oauth2/start?rd=$scheme://$host$request_uri";
           extraConfig = ''
@@ -114,16 +125,10 @@ in
         auth_request /oauth2/auth;
         error_page 401 = @redirectToAuth2ProxyLogin;
 
-        # pass information via X-User and X-Email headers to backend,
-        # requires running with --set-xauthrequest flag
+        # set variables being used in locations."/".extraConfig
         auth_request_set $user   $upstream_http_x_auth_request_user;
         auth_request_set $email  $upstream_http_x_auth_request_email;
-        proxy_set_header X-User  $user;
-        proxy_set_header X-Email $email;
-
-        # if you enabled --cookie-refresh, this is needed for it to work with auth_request
         auth_request_set $auth_cookie $upstream_http_set_cookie;
-        add_header Set-Cookie $auth_cookie;
       '';
     };
   }) cfg.virtualHosts)));
diff --git a/nixos/modules/services/security/oauth2-proxy.nix b/nixos/modules/services/security/oauth2-proxy.nix
index 075e64b743b1e..a897f04ea6333 100644
--- a/nixos/modules/services/security/oauth2-proxy.nix
+++ b/nixos/modules/services/security/oauth2-proxy.nix
@@ -17,7 +17,7 @@ let
       inherit (cfg.github) org team;
     }; };
 
-    google = cfg: { google = with cfg.google; optionalAttrs (groups != []) {
+    google = cfg: { google = with cfg.google; lib.optionalAttrs (groups != []) {
       admin-email = adminEmail;
       service-account = serviceAccountJSON;
       group = groups;
@@ -577,20 +577,22 @@ in
 
     users.groups.oauth2-proxy = {};
 
-    systemd.services.oauth2-proxy = {
-      description = "OAuth2 Proxy";
-      path = [ cfg.package ];
-      wantedBy = [ "multi-user.target" ];
-      wants = [ "network-online.target" ];
-      after = [ "network-online.target" ];
-
-      serviceConfig = {
-        User = "oauth2-proxy";
-        Restart = "always";
-        ExecStart = "${cfg.package}/bin/oauth2-proxy ${configString}";
-        EnvironmentFile = lib.mkIf (cfg.keyFile != null) cfg.keyFile;
+    systemd.services.oauth2-proxy =
+      let needsKeycloak = lib.elem cfg.provider ["keycloak" "keycloak-oidc"]
+                          && config.services.keycloak.enable;
+      in {
+        description = "OAuth2 Proxy";
+        path = [ cfg.package ];
+        wantedBy = [ "multi-user.target" ];
+        wants = [ "network-online.target" ] ++ lib.optionals needsKeycloak [ "keycloak.service" ];
+        after = [ "network-online.target" ] ++ lib.optionals needsKeycloak [ "keycloak.service" ];
+        restartTriggers = [ cfg.keyFile ];
+        serviceConfig = {
+          User = "oauth2-proxy";
+          Restart = "always";
+          ExecStart = "${lib.getExe cfg.package} ${configString}";
+          EnvironmentFile = lib.mkIf (cfg.keyFile != null) cfg.keyFile;
+        };
       };
-    };
-
   };
 }
diff --git a/nixos/modules/services/security/sslmate-agent.nix b/nixos/modules/services/security/sslmate-agent.nix
index c850eb22a0311..57cb955a39dd9 100644
--- a/nixos/modules/services/security/sslmate-agent.nix
+++ b/nixos/modules/services/security/sslmate-agent.nix
@@ -6,7 +6,7 @@ let
   cfg = config.services.sslmate-agent;
 
 in {
-  meta.maintainers = with maintainers; [ wolfangaukang ];
+  meta.maintainers = [ ];
 
   options = {
     services.sslmate-agent = {
diff --git a/nixos/modules/services/security/step-ca.nix b/nixos/modules/services/security/step-ca.nix
index c708cb2b8910d..43bc402e7818b 100644
--- a/nixos/modules/services/security/step-ca.nix
+++ b/nixos/modules/services/security/step-ca.nix
@@ -4,7 +4,7 @@ let
   settingsFormat = (pkgs.formats.json { });
 in
 {
-  meta.maintainers = with lib.maintainers; [ mohe2015 ];
+  meta.maintainers = with lib.maintainers; [ ];
 
   options = {
     services.step-ca = {
@@ -107,7 +107,7 @@ in
           UMask = "0077";
           Environment = "HOME=%S/step-ca";
           WorkingDirectory = ""; # override upstream
-          ReadWriteDirectories = ""; # override upstream
+          ReadWritePaths = ""; # override upstream
 
           # LocalCredential handles file permission problems arising from the use of DynamicUser.
           LoadCredential = "intermediate_password:${cfg.intermediatePasswordFile}";
diff --git a/nixos/modules/services/security/vaultwarden/default.nix b/nixos/modules/services/security/vaultwarden/default.nix
index 33957be437b30..41f7de5d80fab 100644
--- a/nixos/modules/services/security/vaultwarden/default.nix
+++ b/nixos/modules/services/security/vaultwarden/default.nix
@@ -5,6 +5,8 @@ let
   user = config.users.users.vaultwarden.name;
   group = config.users.groups.vaultwarden.name;
 
+  StateDirectory = if lib.versionOlder config.system.stateVersion "24.11" then "bitwarden_rs" else "vaultwarden";
+
   # Convert name from camel case (e.g. disable2FARemember) to upper case snake case (e.g. DISABLE_2FA_REMEMBER).
   nameToEnvVar = name:
     let
@@ -23,7 +25,7 @@ let
       configEnv = lib.concatMapAttrs (name: value: lib.optionalAttrs (value != null) {
         ${nameToEnvVar name} = if lib.isBool value then lib.boolToString value else toString value;
       }) cfg.config;
-    in { DATA_FOLDER = "/var/lib/bitwarden_rs"; } // lib.optionalAttrs (!(configEnv ? WEB_VAULT_ENABLED) || configEnv.WEB_VAULT_ENABLED == "true") {
+    in { DATA_FOLDER = "/var/lib/${StateDirectory}"; } // lib.optionalAttrs (!(configEnv ? WEB_VAULT_ENABLED) || configEnv.WEB_VAULT_ENABLED == "true") {
       WEB_VAULT_FOLDER = "${cfg.webVaultPackage}/share/vaultwarden/vault";
     } // configEnv;
 
@@ -176,16 +178,45 @@ in {
         User = user;
         Group = group;
         EnvironmentFile = [ configFile ] ++ lib.optional (cfg.environmentFile != null) cfg.environmentFile;
-        ExecStart = "${vaultwarden}/bin/vaultwarden";
+        ExecStart = lib.getExe vaultwarden;
         LimitNOFILE = "1048576";
-        PrivateTmp = "true";
-        PrivateDevices = "true";
-        ProtectHome = "true";
+        CapabilityBoundingSet = [ "" ];
+        DeviceAllow = [ "" ];
+        DevicePolicy = "closed";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "noaccess";
         ProtectSystem = "strict";
-        AmbientCapabilities = "CAP_NET_BIND_SERVICE";
-        StateDirectory = "bitwarden_rs";
+        RemoveIPC = true;
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+          "AF_UNIX"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        inherit StateDirectory;
         StateDirectoryMode = "0700";
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "~@privileged"
+        ];
         Restart = "always";
+        UMask = "0077";
       };
       wantedBy = [ "multi-user.target" ];
     };
@@ -193,7 +224,7 @@ in {
     systemd.services.backup-vaultwarden = lib.mkIf (cfg.backupDir != null) {
       description = "Backup vaultwarden";
       environment = {
-        DATA_FOLDER = "/var/lib/bitwarden_rs";
+        DATA_FOLDER = "/var/lib/${StateDirectory}";
         BACKUP_FOLDER = cfg.backupDir;
       };
       path = with pkgs; [ sqlite ];
diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix
index 8dba0aca64337..d84136125f934 100644
--- a/nixos/modules/services/system/dbus.nix
+++ b/nixos/modules/services/system/dbus.nix
@@ -128,10 +128,14 @@ in
         contents."/etc/dbus-1".source = pkgs.makeDBusConf {
           inherit (cfg) apparmor;
           suidHelper = "/bin/false";
-          serviceDirectories = [ pkgs.dbus ];
+          serviceDirectories = [ pkgs.dbus config.boot.initrd.systemd.package ];
         };
         packages = [ pkgs.dbus ];
-        storePaths = [ "${pkgs.dbus}/bin/dbus-daemon" ];
+        storePaths = [
+          "${pkgs.dbus}/bin/dbus-daemon"
+          "${config.boot.initrd.systemd.package}/share/dbus-1/system-services"
+          "${config.boot.initrd.systemd.package}/share/dbus-1/system.d"
+        ];
         targets.sockets.wants = [ "dbus.socket" ];
       };
     })
@@ -147,6 +151,10 @@ in
       };
 
       systemd.services.dbus = {
+        aliases = [
+          # hack aiding to prevent dbus from restarting when switching from dbus-broker back to dbus
+          "dbus-broker.service"
+        ];
         # Don't restart dbus-daemon. Bad things tend to happen if we do.
         reloadIfChanged = true;
         restartTriggers = [
@@ -158,6 +166,10 @@ in
       };
 
       systemd.user.services.dbus = {
+        aliases = [
+          # hack aiding to prevent dbus from restarting when switching from dbus-broker back to dbus
+          "dbus-broker.service"
+        ];
         # Don't restart dbus-daemon. Bad things tend to happen if we do.
         reloadIfChanged = true;
         restartTriggers = [
@@ -184,6 +196,8 @@ in
       # https://github.com/NixOS/nixpkgs/issues/108643
       systemd.services.dbus-broker = {
         aliases = [
+          # allow other services to just depend on dbus,
+          # but also a hack aiding to prevent dbus from restarting when switching from dbus-broker back to dbus
           "dbus.service"
         ];
         unitConfig = {
@@ -203,6 +217,8 @@ in
 
       systemd.user.services.dbus-broker = {
         aliases = [
+          # allow other services to just depend on dbus,
+          # but also a hack aiding to prevent dbus from restarting when switching from dbus-broker back to dbus
           "dbus.service"
         ];
         # Don't restart dbus. Bad things tend to happen if we do.
diff --git a/nixos/modules/services/system/kerberos/default.nix b/nixos/modules/services/system/kerberos/default.nix
index 7fe970c9609a9..34c7c6c84f865 100644
--- a/nixos/modules/services/system/kerberos/default.nix
+++ b/nixos/modules/services/system/kerberos/default.nix
@@ -1,75 +1,59 @@
-{config, lib, ...}:
+{ config, pkgs, lib, ... }:
 
 let
-  inherit (lib) mkOption mkIf types length attrNames;
+  inherit (lib) mkOption types;
   cfg = config.services.kerberos_server;
-  kerberos = config.security.krb5.package;
+  inherit (config.security.krb5) package;
 
-  aclEntry = {
-    options = {
-      principal = mkOption {
-        type = types.str;
-        description = "Which principal the rule applies to";
-      };
-      access = mkOption {
-        type = types.either
-          (types.listOf (types.enum ["add" "cpw" "delete" "get" "list" "modify"]))
-          (types.enum ["all"]);
-        default = "all";
-        description = "The changes the principal is allowed to make.";
-      };
-      target = mkOption {
-        type = types.str;
-        default = "*";
-        description = "The principals that 'access' applies to.";
-      };
-    };
-  };
-
-  realm = {
-    options = {
-      acl = mkOption {
-        type = types.listOf (types.submodule aclEntry);
-        default = [
-          { principal = "*/admin"; access = "all"; }
-          { principal = "admin"; access = "all"; }
-        ];
-        description = ''
-          The privileges granted to a user.
-        '';
-      };
-    };
-  };
+  format = import ../../../security/krb5/krb5-conf-format.nix { inherit pkgs lib; } { enableKdcACLEntries = true; };
 in
 
 {
   imports = [
+    (lib.mkRenamedOptionModule [ "services" "kerberos_server" "realms" ] [ "services" "kerberos_server" "settings" "realms" ])
+
     ./mit.nix
     ./heimdal.nix
   ];
 
-  ###### interface
   options = {
     services.kerberos_server = {
       enable = lib.mkEnableOption "the kerberos authentication server";
 
-      realms = mkOption {
-        type = types.attrsOf (types.submodule realm);
+      settings = mkOption {
+        type = format.type;
         description = ''
-          The realm(s) to serve keys for.
+          Settings for the kerberos server of choice.
+
+          See the following documentation:
+          - Heimdal: {manpage}`kdc.conf(5)`
+          - MIT Kerberos: <https://web.mit.edu/kerberos/krb5-1.21/doc/admin/conf_files/kdc_conf.html>
         '';
+        default = { };
       };
     };
   };
 
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ package ];
+    assertions = [
+      {
+        assertion = cfg.settings.realms != { };
+        message = "The server needs at least one realm";
+      }
+      {
+        assertion = lib.length (lib.attrNames cfg.settings.realms) <= 1;
+        message = "Only one realm per server is currently supported.";
+      }
+    ];
+
+    systemd.slices.system-kerberos-server = { };
+    systemd.targets.kerberos-server = {
+      wantedBy = [ "multi-user.target" ];
+    };
+  };
 
-  ###### implementation
-
-  config = mkIf cfg.enable {
-    environment.systemPackages = [ kerberos ];
-    assertions = [{
-      assertion = length (attrNames cfg.realms) <= 1;
-      message = "Only one realm per server is currently supported.";
-    }];
+  meta = {
+    doc = ./kerberos-server.md;
   };
 }
diff --git a/nixos/modules/services/system/kerberos/heimdal.nix b/nixos/modules/services/system/kerberos/heimdal.nix
index ecafc92766704..cec4dd276e6b9 100644
--- a/nixos/modules/services/system/kerberos/heimdal.nix
+++ b/nixos/modules/services/system/kerberos/heimdal.nix
@@ -1,68 +1,87 @@
 { pkgs, config, lib, ... } :
 
 let
-  inherit (lib) mkIf concatStringsSep concatMapStrings toList mapAttrs
-    mapAttrsToList;
+  inherit (lib)  mapAttrs;
   cfg = config.services.kerberos_server;
-  kerberos = config.security.krb5.package;
-  stateDir = "/var/heimdal";
-  aclFiles = mapAttrs
-    (name: {acl, ...}: pkgs.writeText "${name}.acl" (concatMapStrings ((
-      {principal, access, target, ...} :
-      "${principal}\t${concatStringsSep "," (toList access)}\t${target}\n"
-    )) acl)) cfg.realms;
+  package = config.security.krb5.package;
 
-  kdcConfigs = mapAttrsToList (name: value: ''
-    database = {
-      dbname = ${stateDir}/heimdal
-      acl_file = ${value}
-    }
-  '') aclFiles;
-  kdcConfFile = pkgs.writeText "kdc.conf" ''
-    [kdc]
-    ${concatStringsSep "\n" kdcConfigs}
-  '';
+  aclConfigs = lib.pipe cfg.settings.realms [
+    (mapAttrs (name: { acl, ... }: lib.concatMapStringsSep "\n" (
+      { principal, access, target, ... }:
+      "${principal}\t${lib.concatStringsSep "," (lib.toList access)}\t${target}"
+    ) acl))
+    (lib.mapAttrsToList (name: text:
+      {
+        dbname = "/var/lib/heimdal/heimdal";
+        acl_file = pkgs.writeText "${name}.acl" text;
+      }
+    ))
+  ];
+
+  finalConfig = cfg.settings // {
+    realms = mapAttrs (_: v: removeAttrs v [ "acl" ]) (cfg.settings.realms or { });
+    kdc = (cfg.settings.kdc or { }) // {
+      database = aclConfigs;
+    };
+  };
+
+  format = import ../../../security/krb5/krb5-conf-format.nix { inherit pkgs lib; } { enableKdcACLEntries = true; };
+
+  kdcConfFile = format.generate "kdc.conf" finalConfig;
 in
 
 {
-  # No documentation about correct triggers, so guessing at them.
+  config = lib.mkIf (cfg.enable && package.passthru.implementation == "heimdal") {
+    environment.etc."heimdal-kdc/kdc.conf".source = kdcConfFile;
+
+    systemd.tmpfiles.settings."10-heimdal" = let
+      databases = lib.pipe finalConfig.kdc.database [
+        (map (dbAttrs: dbAttrs.dbname or null))
+        (lib.filter (x: x != null))
+        lib.unique
+      ];
+    in lib.genAttrs databases (_: {
+      d = {
+        user = "root";
+        group = "root";
+        mode = "0700";
+      };
+    });
 
-  config = mkIf (cfg.enable && kerberos == pkgs.heimdal) {
     systemd.services.kadmind = {
       description = "Kerberos Administration Daemon";
-      wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        mkdir -m 0755 -p ${stateDir}
-      '';
-      serviceConfig.ExecStart =
-        "${kerberos}/libexec/kadmind --config-file=/etc/heimdal-kdc/kdc.conf";
+      partOf = [ "kerberos-server.target" ];
+      wantedBy = [ "kerberos-server.target" ];
+      serviceConfig = {
+        ExecStart = "${package}/libexec/kadmind --config-file=/etc/heimdal-kdc/kdc.conf";
+        Slice = "system-kerberos-server.slice";
+        StateDirectory = "heimdal";
+      };
       restartTriggers = [ kdcConfFile ];
     };
 
     systemd.services.kdc = {
       description = "Key Distribution Center daemon";
-      wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        mkdir -m 0755 -p ${stateDir}
-      '';
-      serviceConfig.ExecStart =
-        "${kerberos}/libexec/kdc --config-file=/etc/heimdal-kdc/kdc.conf";
+      partOf = [ "kerberos-server.target" ];
+      wantedBy = [ "kerberos-server.target" ];
+      serviceConfig = {
+        ExecStart = "${package}/libexec/kdc --config-file=/etc/heimdal-kdc/kdc.conf";
+        Slice = "system-kerberos-server.slice";
+        StateDirectory = "heimdal";
+      };
       restartTriggers = [ kdcConfFile ];
     };
 
     systemd.services.kpasswdd = {
       description = "Kerberos Password Changing daemon";
-      wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        mkdir -m 0755 -p ${stateDir}
-      '';
-      serviceConfig.ExecStart = "${kerberos}/libexec/kpasswdd";
+      partOf = [ "kerberos-server.target" ];
+      wantedBy = [ "kerberos-server.target" ];
+      serviceConfig = {
+        ExecStart = "${package}/libexec/kpasswdd";
+        Slice = "system-kerberos-server.slice";
+        StateDirectory = "heimdal";
+      };
       restartTriggers = [ kdcConfFile ];
     };
-
-    environment.etc = {
-      # Can be set via the --config-file option to KDC
-      "heimdal-kdc/kdc.conf".source = kdcConfFile;
-    };
   };
 }
diff --git a/nixos/modules/services/system/kerberos/kerberos-server.md b/nixos/modules/services/system/kerberos/kerberos-server.md
new file mode 100644
index 0000000000000..80c71be1541e4
--- /dev/null
+++ b/nixos/modules/services/system/kerberos/kerberos-server.md
@@ -0,0 +1,55 @@
+# kerberos_server {#module-services-kerberos-server}
+
+Kerberos is a computer-network authentication protocol that works on the basis of tickets to allow nodes communicating over a non-secure network to prove their identity to one another in a secure manner.
+
+This module provides both the MIT and Heimdal implementations of the a Kerberos server.
+
+## Usage {#module-services-kerberos-server-usage}
+
+To enable a Kerberos server:
+
+```nix
+{
+  security.krb5 = {
+    # Here you can choose between the MIT and Heimdal implementations.
+    package = pkgs.krb5;
+    # package = pkgs.heimdal;
+
+    # Optionally set up a client on the same machine as the server
+    enable = true;
+    settings = {
+      libdefaults.default_realm = "EXAMPLE.COM";
+      realms."EXAMPLE.COM" = {
+        kdc = "kerberos.example.com";
+        admin_server = "kerberos.example.com";
+      };
+    };
+  }
+
+  services.kerberos-server = {
+    enable = true;
+    settings = {
+      realms."EXAMPLE.COM" = {
+        acl = [{ principal = "adminuser"; access=  ["add" "cpw"]; }];
+      };
+    };
+  };
+}
+```
+
+## Notes {#module-services-kerberos-server-notes}
+
+- The Heimdal documentation will sometimes assume that state is stored in `/var/heimdal`, but this module uses `/var/lib/heimdal` instead.
+- Due to the heimdal implementation being chosen through `security.krb5.package`, it is not possible to have a system with one implementation of the client and another of the server.
+- While `services.kerberos_server.settings` has a common freeform type between the two implementations, the actual settings that can be set can vary between the two implementations. To figure out what settings are available, you should consult the upstream documentation for the implementation you are using.
+
+## Upstream Documentation {#module-services-kerberos-server-upstream-documentation}
+
+- MIT Kerberos homepage: https://web.mit.edu/kerberos
+- MIT Kerberos docs: https://web.mit.edu/kerberos/krb5-latest/doc/index.html
+
+- Heimdal Kerberos GitHub wiki: https://github.com/heimdal/heimdal/wiki
+- Heimdal kerberos doc manpages (Debian unstable): https://manpages.debian.org/unstable/heimdal-docs/index.html
+- Heimdal Kerberos kdc manpages (Debian unstable): https://manpages.debian.org/unstable/heimdal-kdc/index.html
+
+Note the version number in the URLs, it may be different for the latest version.
diff --git a/nixos/modules/services/system/kerberos/mit.nix b/nixos/modules/services/system/kerberos/mit.nix
index a654bd1fe7e1b..9ce58986e27af 100644
--- a/nixos/modules/services/system/kerberos/mit.nix
+++ b/nixos/modules/services/system/kerberos/mit.nix
@@ -1,31 +1,37 @@
 { pkgs, config, lib, ... } :
 
 let
-  inherit (lib) mkIf concatStrings concatStringsSep concatMapStrings toList
-    mapAttrs mapAttrsToList;
+  inherit (lib) mapAttrs;
   cfg = config.services.kerberos_server;
-  kerberos = config.security.krb5.package;
-  stateDir = "/var/lib/krb5kdc";
+  package = config.security.krb5.package;
   PIDFile = "/run/kdc.pid";
+
+  format = import ../../../security/krb5/krb5-conf-format.nix { inherit pkgs lib; } { enableKdcACLEntries = true; };
+
   aclMap = {
     add = "a"; cpw = "c"; delete = "d"; get = "i"; list = "l"; modify = "m";
     all = "*";
   };
-  aclFiles = mapAttrs
-    (name: {acl, ...}: (pkgs.writeText "${name}.acl" (concatMapStrings (
-      {principal, access, target, ...} :
-      let access_code = map (a: aclMap.${a}) (toList access); in
-      "${principal} ${concatStrings access_code} ${target}\n"
-    ) acl))) cfg.realms;
-  kdcConfigs = mapAttrsToList (name: value: ''
-    ${name} = {
-      acl_file = ${value}
-    }
-  '') aclFiles;
-  kdcConfFile = pkgs.writeText "kdc.conf" ''
-    [realms]
-    ${concatStringsSep "\n" kdcConfigs}
-  '';
+
+  aclConfigs = lib.pipe cfg.settings.realms [
+    (mapAttrs (name: { acl, ... }: lib.concatMapStringsSep "\n" (
+      { principal, access, target, ... }: let
+        access_code = map (a: aclMap.${a}) (lib.toList access);
+      in "${principal} ${lib.concatStrings access_code} ${target}"
+    ) acl))
+
+    (lib.concatMapAttrs (name: text: {
+      ${name} = {
+        acl_file = pkgs.writeText "${name}.acl" text;
+      };
+    }))
+  ];
+
+  finalConfig = cfg.settings // {
+    realms = mapAttrs (n: v: (removeAttrs v [ "acl" ]) // aclConfigs.${n}) (cfg.settings.realms or { });
+  };
+
+  kdcConfFile = format.generate "kdc.conf" finalConfig;
   env = {
     # What Debian uses, could possibly link directly to Nix store?
     KRB5_KDC_PROFILE = "/etc/krb5kdc/kdc.conf";
@@ -33,36 +39,38 @@ let
 in
 
 {
-  config = mkIf (cfg.enable && kerberos == pkgs.krb5) {
+  config = lib.mkIf (cfg.enable && package.passthru.implementation == "krb5") {
+    environment = {
+      etc."krb5kdc/kdc.conf".source = kdcConfFile;
+      variables = env;
+    };
+
     systemd.services.kadmind = {
       description = "Kerberos Administration Daemon";
-      wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        mkdir -m 0755 -p ${stateDir}
-      '';
-      serviceConfig.ExecStart = "${kerberos}/bin/kadmind -nofork";
+      partOf = [ "kerberos-server.target" ];
+      wantedBy = [ "kerberos-server.target" ];
+      serviceConfig = {
+        ExecStart = "${package}/bin/kadmind -nofork";
+        Slice = "system-kerberos-server.slice";
+        StateDirectory = "krb5kdc";
+      };
       restartTriggers = [ kdcConfFile ];
       environment = env;
     };
 
     systemd.services.kdc = {
       description = "Key Distribution Center daemon";
-      wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        mkdir -m 0755 -p ${stateDir}
-      '';
+      partOf = [ "kerberos-server.target" ];
+      wantedBy = [ "kerberos-server.target" ];
       serviceConfig = {
         Type = "forking";
         PIDFile = PIDFile;
-        ExecStart = "${kerberos}/bin/krb5kdc -P ${PIDFile}";
+        ExecStart = "${package}/bin/krb5kdc -P ${PIDFile}";
+        Slice = "system-kerberos-server.slice";
+        StateDirectory = "krb5kdc";
       };
       restartTriggers = [ kdcConfFile ];
       environment = env;
     };
-
-    environment.etc = {
-      "krb5kdc/kdc.conf".source = kdcConfFile;
-    };
-    environment.variables = env;
   };
 }
diff --git a/nixos/modules/services/system/nix-daemon.nix b/nixos/modules/services/system/nix-daemon.nix
index 0a5b0e2fcb80a..3d44bdac34bf6 100644
--- a/nixos/modules/services/system/nix-daemon.nix
+++ b/nixos/modules/services/system/nix-daemon.nix
@@ -164,7 +164,7 @@ in
         nixPackage
         pkgs.nix-info
       ]
-      ++ optional (config.programs.bash.enableCompletion) pkgs.nix-bash-completions;
+      ++ optional (config.programs.bash.completion.enable) pkgs.nix-bash-completions;
 
     systemd.packages = [ nixPackage ];
 
diff --git a/nixos/modules/services/torrent/flood.nix b/nixos/modules/services/torrent/flood.nix
new file mode 100644
index 0000000000000..213f4ef046483
--- /dev/null
+++ b/nixos/modules/services/torrent/flood.nix
@@ -0,0 +1,85 @@
+{ config, lib, pkgs, utils, ... }:
+
+let
+  cfg = config.services.flood;
+in
+{
+  meta.maintainers = with lib.maintainers; [ thiagokokada ];
+
+  options.services.flood = {
+    enable = lib.mkEnableOption "flood";
+    package = lib.mkPackageOption pkgs "flood" { };
+    openFirewall = lib.mkEnableOption "" // {
+      description = "Whether to open the firewall for the port in {option}`services.flood.port`.";
+    };
+    port = lib.mkOption {
+      type = lib.types.int;
+      description = "Port to bind webserver.";
+      default = 3000;
+      example = 3001;
+    };
+    host = lib.mkOption {
+      type = lib.types.str;
+      description = "Host to bind webserver.";
+      default = "localhost";
+      example = "::";
+    };
+    extraArgs = lib.mkOption {
+      type = with lib.types; listOf str;
+      description = "Extra arguments passed to `flood`.";
+      default = [ ];
+      example = [ "--baseuri=/" ];
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.flood = {
+      description = "A modern web UI for various torrent clients.";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      unitConfig = {
+        Documentation = "https://github.com/jesec/flood/wiki";
+      };
+      serviceConfig = {
+        Restart = "on-failure";
+        RestartSec = "3s";
+        ExecStart = utils.escapeSystemdExecArgs ([
+          (lib.getExe cfg.package)
+          "--host"
+          cfg.host
+          "--port"
+          (toString cfg.port)
+          "--rundir=/var/lib/flood"
+        ] ++ cfg.extraArgs);
+
+        CapabilityBoundingSet = [ "" ];
+        DynamicUser = true;
+        LockPersonality = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateTmp = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        StateDirectory = "flood";
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [ "@system-service" "@pkey" "~@privileged" ];
+      };
+    };
+
+    networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [
+      cfg.port
+    ];
+  };
+}
+
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index 52b472631dcfe..ceef0db78b094 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -174,7 +174,10 @@ in
         };
       };
 
-      package = mkPackageOption pkgs "transmission" {};
+      package = mkPackageOption pkgs "transmission" {
+        default = "transmission_3";
+        example = "pkgs.transmission_4";
+      };
 
       downloadDirPermissions = mkOption {
         type = with types; nullOr str;
diff --git a/nixos/modules/services/ttys/getty.nix b/nixos/modules/services/ttys/getty.nix
index 011016dd5fd14..e88bb4628635e 100644
--- a/nixos/modules/services/ttys/getty.nix
+++ b/nixos/modules/services/ttys/getty.nix
@@ -101,7 +101,7 @@ in
   config = {
     # Note: this is set here rather than up there so that changing
     # nixos.label would not rebuild manual pages
-    services.getty.greetingLine = mkDefault ''<<< Welcome to NixOS ${config.system.nixos.label} (\m) - \l >>>'';
+    services.getty.greetingLine = mkDefault ''<<< Welcome to ${config.system.nixos.distroName} ${config.system.nixos.label} (\m) - \l >>>'';
     services.getty.helpLine = mkIf (config.documentation.nixos.enable && config.documentation.doc.enable) "\nRun 'nixos-help' for the NixOS manual.";
 
     systemd.services."getty@" =
@@ -158,4 +158,5 @@ in
 
   };
 
+  meta.maintainers = with maintainers; [ RossComputerGuy ];
 }
diff --git a/nixos/modules/services/ttys/kmscon.nix b/nixos/modules/services/ttys/kmscon.nix
index 74314e1e76e40..031c5bbb383e1 100644
--- a/nixos/modules/services/ttys/kmscon.nix
+++ b/nixos/modules/services/ttys/kmscon.nix
@@ -107,7 +107,7 @@ in {
         fonts = optional (cfg.fonts != null) "font-name=${lib.concatMapStringsSep ", " (f: f.name) cfg.fonts}";
       in lib.concatStringsSep "\n" (render ++ fonts);
 
-    hardware.opengl.enable = mkIf cfg.hwRender true;
+    hardware.graphics.enable = mkIf cfg.hwRender true;
 
     fonts = mkIf (cfg.fonts != null) {
       fontconfig.enable = true;
diff --git a/nixos/modules/services/video/frigate.nix b/nixos/modules/services/video/frigate.nix
index 0e6bde447c033..c3ec4a3c76c34 100644
--- a/nixos/modules/services/video/frigate.nix
+++ b/nixos/modules/services/video/frigate.nix
@@ -427,10 +427,6 @@ in
         PrivateTmp = true;
         CacheDirectory = "frigate";
         CacheDirectoryMode = "0750";
-
-        BindPaths = [
-          "/migrations:${cfg.package}/share/frigate/migrations:ro"
-        ];
       };
     };
   };
diff --git a/nixos/modules/services/video/photonvision.nix b/nixos/modules/services/video/photonvision.nix
index d4568258db7d2..e2b27b3cc4104 100644
--- a/nixos/modules/services/video/photonvision.nix
+++ b/nixos/modules/services/video/photonvision.nix
@@ -6,7 +6,7 @@ in
 {
   options = {
     services.photonvision = {
-      enable = lib.mkEnableOption "Enable PhotonVision";
+      enable = lib.mkEnableOption "PhotonVision";
 
       package = lib.mkPackageOption pkgs "photonvision" {};
 
diff --git a/nixos/modules/services/wayland/cage.nix b/nixos/modules/services/wayland/cage.nix
index 91949f197cfed..870ae58f8646e 100644
--- a/nixos/modules/services/wayland/cage.nix
+++ b/nixos/modules/services/wayland/cage.nix
@@ -101,7 +101,7 @@ in {
       session required ${config.systemd.package}/lib/security/pam_systemd.so
     '';
 
-    hardware.opengl.enable = mkDefault true;
+    hardware.graphics.enable = mkDefault true;
 
     systemd.targets.graphical.wants = [ "cage-tty1.service" ];
 
diff --git a/nixos/modules/services/wayland/hypridle.nix b/nixos/modules/services/wayland/hypridle.nix
new file mode 100644
index 0000000000000..5442802df9871
--- /dev/null
+++ b/nixos/modules/services/wayland/hypridle.nix
@@ -0,0 +1,26 @@
+{ lib, pkgs, config, ... }:
+
+let
+  cfg = config.services.hypridle;
+in
+{
+  options.services.hypridle = {
+    enable = lib.mkEnableOption "hypridle, Hyprland's idle daemon";
+    package = lib.mkPackageOption pkgs "hypridle" { };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [
+      cfg.package
+    ];
+
+    systemd.user.services.hypridle = {
+      description = "Hypridle idle daemon";
+      wantedBy = [ "graphical-session.target" ];
+      partOf = [ "graphical-session.target" ];
+      script = lib.getExe cfg.package;
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ johnrtitor ];
+}
diff --git a/nixos/modules/services/web-apps/akkoma.nix b/nixos/modules/services/web-apps/akkoma.nix
index 7c9bf6c465164..8ba3c7eaa1e6a 100644
--- a/nixos/modules/services/web-apps/akkoma.nix
+++ b/nixos/modules/services/web-apps/akkoma.nix
@@ -119,7 +119,7 @@ let
         -o ${escapeShellArg cfg.user } \
         -g ${escapeShellArg cfg.group} \
         <(hexdump -n 16 -e '"%02x"' /dev/urandom) \
-        "$RUNTIME_DIRECTORY/cookie"
+        "''${RUNTIME_DIRECTORY%%:*}/cookie"
     '';
   };
 
@@ -131,7 +131,7 @@ let
         -o ${escapeShellArg cfg.user} \
         -g ${escapeShellArg cfg.group} \
         ${escapeShellArg cfg.dist.cookie._secret} \
-        "$RUNTIME_DIRECTORY/cookie"
+        "''${RUNTIME_DIRECTORY%%:*}/cookie"
     '';
   };
 
@@ -181,7 +181,7 @@ let
     name = "akkoma-config";
     runtimeInputs = with pkgs; [ coreutils replace-secret ];
     text = ''
-      cd "$RUNTIME_DIRECTORY"
+      cd "''${RUNTIME_DIRECTORY%%:*}"
       tmp="$(mktemp config.exs.XXXXXXXXXX)"
       trap 'rm -f "$tmp"' EXIT TERM
 
@@ -279,7 +279,7 @@ let
         cd "${cfg.package}"
 
         RUNTIME_DIRECTORY="''${RUNTIME_DIRECTORY:-/run/akkoma}"
-        AKKOMA_CONFIG_PATH="$RUNTIME_DIRECTORY/config.exs" \
+        AKKOMA_CONFIG_PATH="''${RUNTIME_DIRECTORY%%:*}/config.exs" \
         ERL_EPMD_ADDRESS="${cfg.dist.address}" \
         ERL_EPMD_PORT="${toString cfg.dist.epmdPort}" \
         ERL_FLAGS=${lib.escapeShellArg (lib.escapeShellArgs ([
@@ -287,7 +287,7 @@ let
           "-kernel" "inet_dist_listen_min" (toString cfg.dist.portMin)
           "-kernel" "inet_dist_listen_max" (toString cfg.dist.portMax)
         ] ++ cfg.dist.extraFlags))} \
-        RELEASE_COOKIE="$(<"$RUNTIME_DIRECTORY/cookie")" \
+        RELEASE_COOKIE="$(<"''${RUNTIME_DIRECTORY%%:*}/cookie")" \
         RELEASE_NAME="akkoma" \
           exec "${cfg.package}/bin/$(basename "$0")" "$@"
       '';
@@ -984,7 +984,7 @@ in {
         RemainAfterExit = true;
         UMask = "0077";
 
-        RuntimeDirectory = "akkoma";
+        RuntimeDirectory = mkBefore "akkoma";
 
         ExecStart = mkMerge [
           (mkIf (cfg.dist.cookie == null) [ genScript ])
@@ -1072,7 +1072,7 @@ in {
 
         ProtectProc = "noaccess";
         ProcSubset = "pid";
-        ProtectSystem = mkIf (!isConfined) "strict";
+        ProtectSystem = "strict";
         ProtectHome = true;
         PrivateTmp = true;
         PrivateDevices = true;
@@ -1136,6 +1136,6 @@ in {
     };
   };
 
-  meta.maintainers = with maintainers; [ mvs tcmal ];
+  meta.maintainers = with maintainers; [ mvs ];
   meta.doc = ./akkoma.md;
 }
diff --git a/nixos/modules/services/web-apps/artalk.nix b/nixos/modules/services/web-apps/artalk.nix
new file mode 100644
index 0000000000000..d3d06f1521b6a
--- /dev/null
+++ b/nixos/modules/services/web-apps/artalk.nix
@@ -0,0 +1,131 @@
+{
+  config,
+  lib,
+  pkgs,
+  utils,
+  ...
+}:
+let
+  cfg = config.services.artalk;
+  settingsFormat = pkgs.formats.json { };
+in
+{
+
+  meta = {
+    maintainers = with lib.maintainers; [ moraxyc ];
+  };
+
+  options = {
+    services.artalk = {
+      enable = lib.mkEnableOption "artalk, a comment system";
+      configFile = lib.mkOption {
+        type = lib.types.str;
+        default = "/etc/artalk/config.yml";
+        description = "Artalk config file path. If it is not exist, Artalk will generate one.";
+      };
+      allowModify = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = "allow Artalk store the settings to config file persistently";
+      };
+      workdir = lib.mkOption {
+        type = lib.types.str;
+        default = "/var/lib/artalk";
+        description = "Artalk working directory";
+      };
+      user = lib.mkOption {
+        type = lib.types.str;
+        default = "artalk";
+        description = "Artalk user name.";
+      };
+
+      group = lib.mkOption {
+        type = lib.types.str;
+        default = "artalk";
+        description = "Artalk group name.";
+      };
+
+      package = lib.mkPackageOption pkgs "artalk" { };
+      settings = lib.mkOption {
+        type = lib.types.submodule {
+          freeformType = settingsFormat.type;
+          options = {
+            host = lib.mkOption {
+              type = lib.types.str;
+              default = "0.0.0.0";
+              description = ''
+                Artalk server listen host
+              '';
+            };
+            port = lib.mkOption {
+              type = lib.types.port;
+              default = 23366;
+              description = ''
+                Artalk server listen port
+              '';
+            };
+          };
+        };
+        default = { };
+        description = ''
+          The artalk configuration.
+
+          If you set allowModify to true, Artalk will be able to store the settings in the config file persistently. This section's content will update in the config file after the service restarts.
+
+          Options containing secret data should be set to an attribute set
+          containing the attribute `_secret` - a string pointing to a file
+          containing the value the option should be set to.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    users.users.artalk = lib.optionalAttrs (cfg.user == "artalk") {
+      description = "artalk user";
+      isSystemUser = true;
+      group = cfg.group;
+    };
+    users.groups.artalk = lib.optionalAttrs (cfg.group == "artalk") { };
+
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.services.artalk = {
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      preStart =
+        ''
+          umask 0077
+          ${utils.genJqSecretsReplacementSnippet cfg.settings "/run/artalk/new"}
+        ''
+        + (
+          if cfg.allowModify then
+            ''
+              [ -e "${cfg.configFile}" ] || ${lib.getExe cfg.package} gen config "${cfg.configFile}"
+              cat "${cfg.configFile}" | ${lib.getExe pkgs.yj} > "/run/artalk/old"
+              ${lib.getExe pkgs.jq} -s '.[0] * .[1]' "/run/artalk/old" "/run/artalk/new" > "/run/artalk/result"
+              cat "/run/artalk/result" | ${lib.getExe pkgs.yj} -r > "${cfg.configFile}"
+              rm /run/artalk/{old,new,result}
+            ''
+          else
+            ''
+              cat /run/artalk/new | ${lib.getExe pkgs.yj} -r > "${cfg.configFile}"
+              rm /run/artalk/new
+            ''
+        );
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        Type = "simple";
+        ExecStart = "${lib.getExe cfg.package} server --config ${cfg.configFile} --workdir ${cfg.workdir} --host ${cfg.settings.host} --port ${builtins.toString cfg.settings.port}";
+        Restart = "on-failure";
+        RestartSec = "5s";
+        ConfigurationDirectory = [ "artalk" ];
+        StateDirectory = [ "artalk" ];
+        RuntimeDirectory = [ "artalk" ];
+        AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
+        ProtectHome = "yes";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/commafeed.nix b/nixos/modules/services/web-apps/commafeed.nix
new file mode 100644
index 0000000000000..354e3625bb999
--- /dev/null
+++ b/nixos/modules/services/web-apps/commafeed.nix
@@ -0,0 +1,114 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  cfg = config.services.commafeed;
+in
+{
+  options.services.commafeed = {
+    enable = lib.mkEnableOption "CommaFeed";
+
+    package = lib.mkPackageOption pkgs "commafeed" { };
+
+    user = lib.mkOption {
+      type = lib.types.str;
+      description = "User under which CommaFeed runs.";
+      default = "commafeed";
+    };
+
+    group = lib.mkOption {
+      type = lib.types.str;
+      description = "Group under which CommaFeed runs.";
+      default = "commafeed";
+    };
+
+    stateDir = lib.mkOption {
+      type = lib.types.path;
+      description = "Directory holding all state for CommaFeed to run.";
+      default = "/var/lib/commafeed";
+    };
+
+    environment = lib.mkOption {
+      type = lib.types.attrsOf (
+        lib.types.oneOf [
+          lib.types.bool
+          lib.types.int
+          lib.types.str
+        ]
+      );
+      description = ''
+        Extra environment variables passed to CommaFeed, refer to
+        <https://github.com/Athou/commafeed/blob/master/commafeed-server/config.yml.example>
+        for supported values. The default user is `admin` and the default password is `admin`.
+        Correct configuration for H2 database is already provided.
+      '';
+      default = { };
+      example = {
+        CF_SERVER_APPLICATIONCONNECTORS_0_TYPE = "http";
+        CF_SERVER_APPLICATIONCONNECTORS_0_PORT = 9090;
+      };
+    };
+
+    environmentFile = lib.mkOption {
+      type = lib.types.nullOr lib.types.path;
+      description = ''
+        Environment file as defined in {manpage}`systemd.exec(5)`.
+      '';
+      default = null;
+      example = "/var/lib/commafeed/commafeed.env";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.commafeed = {
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      environment = lib.mapAttrs (
+        _: v: if lib.isBool v then lib.boolToString v else toString v
+      ) cfg.environment;
+      serviceConfig = {
+        ExecStart = "${lib.getExe cfg.package} server ${cfg.package}/share/config.yml";
+        User = cfg.user;
+        Group = cfg.group;
+        StateDirectory = baseNameOf cfg.stateDir;
+        WorkingDirectory = cfg.stateDir;
+        # Hardening
+        CapabilityBoundingSet = [ "" ];
+        DevicePolicy = "closed";
+        DynamicUser = true;
+        LockPersonality = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateUsers = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = true;
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "~@privileged"
+        ];
+        UMask = "0077";
+      } // lib.optionalAttrs (cfg.environmentFile != null) { EnvironmentFile = cfg.environmentFile; };
+    };
+  };
+
+  meta.maintainers = [ lib.maintainers.raroh73 ];
+}
diff --git a/nixos/modules/services/web-apps/filesender.md b/nixos/modules/services/web-apps/filesender.md
new file mode 100644
index 0000000000000..44d066761b9a4
--- /dev/null
+++ b/nixos/modules/services/web-apps/filesender.md
@@ -0,0 +1,49 @@
+# FileSender {#module-services-filesender}
+
+[FileSender](https://filesender.org/software/) is a software that makes it easy to send and receive big files.
+
+## Quickstart {#module-services-filesender-quickstart}
+
+FileSender uses [SimpleSAMLphp](https://simplesamlphp.org/) for authentication, which needs to be configured separately.
+
+Minimal working instance of FileSender that uses password-authentication would look like this:
+
+```nix
+{
+  networking.firewall.allowedTCPPorts = [ 80 443 ];
+  services.filesender = {
+    enable = true;
+    localDomain = "filesender.example.com";
+    configureNginx = true;
+    database.createLocally = true;
+
+    settings = {
+      auth_sp_saml_authentication_source = "default";
+      auth_sp_saml_uid_attribute = "uid";
+      storage_filesystem_path = "<STORAGE PATH FOR UPLOADED FILES>";
+      admin = "admin";
+      admin_email = "admin@example.com";
+      email_reply_to = "noreply@example.com";
+    };
+  };
+  services.simplesamlphp.filesender = {
+    settings = {
+      "module.enable".exampleauth = true;
+    };
+    authSources = {
+      admin = [ "core:AdminPassword" ];
+      default = format.lib.mkMixedArray [ "exampleauth:UserPass" ] {
+        "admin:admin123" = {
+          uid = [ "admin" ];
+          cn = [ "admin" ];
+          mail = [ "admin@example.com" ];
+        };
+      };
+    };
+  };
+}
+```
+
+::: {.warning}
+Example above uses hardcoded clear-text password, in production you should use other authentication method like LDAP. You can check supported authentication methods [in SimpleSAMLphp documentation](https://simplesamlphp.org/docs/stable/simplesamlphp-idp.html).
+:::
diff --git a/nixos/modules/services/web-apps/filesender.nix b/nixos/modules/services/web-apps/filesender.nix
new file mode 100644
index 0000000000000..bc8d465643f2f
--- /dev/null
+++ b/nixos/modules/services/web-apps/filesender.nix
@@ -0,0 +1,253 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  format = pkgs.formats.php { finalVariable = "config"; };
+
+  cfg = config.services.filesender;
+  simpleSamlCfg = config.services.simplesamlphp.filesender;
+  fpm = config.services.phpfpm.pools.filesender;
+
+  filesenderConfigDirectory = pkgs.runCommand "filesender-config" { } ''
+    mkdir $out
+    cp ${format.generate "config.php" cfg.settings} $out/config.php
+  '';
+in
+{
+  meta = {
+    maintainers = with lib.maintainers; [ nhnn ];
+    doc = ./filesender.md;
+  };
+
+  options.services.filesender = with lib; {
+    enable = mkEnableOption "FileSender";
+    package = mkPackageOption pkgs "filesender" { };
+    user = mkOption {
+      description = "User under which filesender runs.";
+      type = types.str;
+      default = "filesender";
+    };
+    database = {
+      createLocally = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Create the PostgreSQL database and database user locally.
+        '';
+      };
+      hostname = mkOption {
+        type = types.str;
+        default = "/run/postgresql";
+        description = "Database hostname.";
+      };
+      port = mkOption {
+        type = types.port;
+        default = 5432;
+        description = "Database port.";
+      };
+      name = mkOption {
+        type = types.str;
+        default = "filesender";
+        description = "Database name.";
+      };
+      user = mkOption {
+        type = types.str;
+        default = "filesender";
+        description = "Database user.";
+      };
+      passwordFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/run/keys/filesender-dbpassword";
+        description = ''
+          A file containing the password corresponding to
+          [](#opt-services.filesender.database.user).
+        '';
+      };
+    };
+    settings = mkOption {
+      type = types.submodule {
+        freeformType = format.type;
+        options = {
+          site_url = mkOption {
+            type = types.str;
+            description = "Site URL. Used in emails, to build URLs for logging in, logging out, build URL for upload endpoint for web workers, to include scripts etc.";
+          };
+          admin = mkOption {
+            type = types.commas;
+            description = ''
+              UIDs (as per the configured saml_uid_attribute) of FileSender administrators.
+              Accounts with these UIDs can access the Admin page through the web UI.
+            '';
+          };
+          admin_email = mkOption {
+            type = types.commas;
+            description = ''
+              Email address of FileSender administrator(s).
+              Emails regarding disk full etc. are sent here.
+              You should use a role-address here.
+            '';
+          };
+          storage_filesystem_path = mkOption {
+            type = types.nullOr types.str;
+            description = "When using storage type filesystem this is the absolute path to the file system where uploaded files are stored until they expire. Your FileSender storage root.";
+          };
+          log_facilities = mkOption {
+            type = format.type;
+            default = [ { type = "error_log"; } ];
+            description = "Defines where FileSender logging is sent. You can sent logging to a file, to syslog or to the default PHP log facility (as configured through your webserver's PHP module). The directive takes an array of one or more logging targets. Logging can be sent to multiple targets simultaneously. Each logging target is a list containing the name of the logging target and a number of attributes which vary per log target. See below for the exact definiation of each log target.";
+          };
+        };
+      };
+      default = { };
+      description = ''
+        Configuration options used by FileSender.
+        See [](https://docs.filesender.org/filesender/v2.0/admin/configuration/)
+        for available options.
+      '';
+    };
+    configureNginx = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Configure nginx as a reverse proxy for FileSender.";
+    };
+    localDomain = mkOption {
+      type = types.str;
+      example = "filesender.example.org";
+      description = "The domain serving your FileSender instance.";
+    };
+    poolSettings = mkOption {
+      type =
+        with types;
+        attrsOf (oneOf [
+          str
+          int
+          bool
+        ]);
+      default = {
+        "pm" = "dynamic";
+        "pm.max_children" = "32";
+        "pm.start_servers" = "2";
+        "pm.min_spare_servers" = "2";
+        "pm.max_spare_servers" = "4";
+        "pm.max_requests" = "500";
+      };
+      description = ''
+        Options for FileSender's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives.
+      '';
+    };
+  };
+  config = lib.mkIf cfg.enable {
+    services.simplesamlphp.filesender = {
+      phpfpmPool = "filesender";
+      localDomain = cfg.localDomain;
+      settings.baseurlpath = lib.mkDefault "https://${cfg.localDomain}/saml";
+    };
+
+    services.phpfpm = {
+      pools.filesender = {
+        user = cfg.user;
+        group = config.services.nginx.group;
+        phpEnv = {
+          FILESENDER_CONFIG_DIR = toString filesenderConfigDirectory;
+          SIMPLESAMLPHP_CONFIG_DIR = toString simpleSamlCfg.configDir;
+        };
+        settings = {
+          "listen.owner" = config.services.nginx.user;
+          "listen.group" = config.services.nginx.group;
+        } // cfg.poolSettings;
+      };
+    };
+
+    services.nginx = lib.mkIf cfg.configureNginx {
+      enable = true;
+      virtualHosts.${cfg.localDomain} = {
+        root = "${cfg.package}/www";
+        extraConfig = ''
+          index index.php;
+        '';
+        locations = {
+          "/".extraConfig = ''
+            try_files $uri $uri/ /index.php?args;
+          '';
+          "~ [^/]\\.php(/|$)" = {
+            extraConfig = ''
+              fastcgi_split_path_info  ^(.+\.php)(/.+)$;
+              fastcgi_pass  unix:${fpm.socket};
+              include ${pkgs.nginx}/conf/fastcgi.conf;
+              fastcgi_intercept_errors on;
+              fastcgi_param PATH_INFO       $fastcgi_path_info;
+              fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+            '';
+          };
+          "~ /\\.".extraConfig = "deny all;";
+        };
+      };
+    };
+
+    services.postgresql = lib.mkIf cfg.database.createLocally {
+      enable = true;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        {
+          name = cfg.database.user;
+          ensureDBOwnership = true;
+        }
+      ];
+    };
+
+    services.filesender.settings = lib.mkMerge [
+      (lib.mkIf cfg.database.createLocally {
+        db_host = "/run/postgresql";
+        db_port = "5432";
+        db_password = "."; # FileSender requires it even when on UNIX socket auth.
+      })
+      (lib.mkIf (!cfg.database.createLocally) {
+        db_host = cfg.database.hostname;
+        db_port = toString cfg.database.port;
+        db_password = format.lib.mkRaw "file_get_contents('${cfg.database.passwordFile}')";
+      })
+      {
+        site_url = lib.mkDefault "https://${cfg.localDomain}";
+        db_type = "pgsql";
+        db_username = cfg.database.user;
+        db_database = cfg.database.name;
+        "auth_sp_saml_simplesamlphp_url" = "/saml";
+        "auth_sp_saml_simplesamlphp_location" = "${simpleSamlCfg.libDir}";
+      }
+    ];
+
+    systemd.services.filesender-initdb = {
+      description = "Init filesender DB";
+
+      wantedBy = [
+        "multi-user.target"
+        "phpfpm-filesender.service"
+      ];
+      after = [ "postgresql.service" ];
+
+      restartIfChanged = true;
+
+      serviceConfig = {
+        Environment = [
+          "FILESENDER_CONFIG_DIR=${toString filesenderConfigDirectory}"
+          "SIMPLESAMLPHP_CONFIG_DIR=${toString simpleSamlCfg.configDir}"
+        ];
+        Type = "oneshot";
+        Group = config.services.nginx.group;
+        User = "filesender";
+        ExecStart = "${fpm.phpPackage}/bin/php ${cfg.package}/scripts/upgrade/database.php";
+      };
+    };
+
+    users.extraUsers.filesender = lib.mkIf (cfg.user == "filesender") {
+      home = "/var/lib/filesender";
+      group = config.services.nginx.group;
+      createHome = true;
+      isSystemUser = true;
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/firefly-iii.nix b/nixos/modules/services/web-apps/firefly-iii.nix
index b0024ce09c38e..338f049093202 100644
--- a/nixos/modules/services/web-apps/firefly-iii.nix
+++ b/nixos/modules/services/web-apps/firefly-iii.nix
@@ -3,8 +3,8 @@
 let
   inherit (lib) optionalString mkDefault mkIf mkOption mkEnableOption literalExpression;
   inherit (lib.types) nullOr attrsOf oneOf str int bool path package enum submodule;
-  inherit (lib.strings) concatMapStringsSep removePrefix toShellVars removeSuffix hasSuffix;
-  inherit (lib.attrsets) attrValues genAttrs filterAttrs mapAttrs' nameValuePair;
+  inherit (lib.strings) concatLines removePrefix toShellVars removeSuffix hasSuffix;
+  inherit (lib.attrsets) mapAttrsToList attrValues genAttrs filterAttrs mapAttrs' nameValuePair;
   inherit (builtins) isInt isString toString typeOf;
 
   cfg = config.services.firefly-iii;
@@ -21,18 +21,10 @@ let
     (filterAttrs (n: v: hasSuffix "_FILE" n) cfg.settings);
   env-nonfile-values = filterAttrs (n: v: ! hasSuffix "_FILE" n) cfg.settings;
 
-  envfile = pkgs.writeText "firefly-iii-env" ''
-    ${toShellVars env-file-values}
-    ${toShellVars env-nonfile-values}
-  '';
-
   fileenv-func = ''
-    cp --no-preserve=mode ${envfile} /tmp/firefly-iii-env
-    ${concatMapStringsSep "\n"
-      (n: "${pkgs.replace-secret}/bin/replace-secret ${n} ${n} /tmp/firefly-iii-env")
-      (attrValues env-file-values)}
     set -a
-    . /tmp/firefly-iii-env
+    ${toShellVars env-nonfile-values}
+    ${concatLines (mapAttrsToList (n: v: "${n}=\"$(< ${v})\"") env-file-values)}
     set +a
   '';
 
@@ -41,22 +33,21 @@ let
 
     ${optionalString (cfg.settings.DB_CONNECTION == "sqlite")
       "touch ${cfg.dataDir}/storage/database/database.sqlite"}
-    ${artisan} migrate --seed --no-interaction --force
-    ${artisan} firefly-iii:decrypt-all
+    ${artisan} cache:clear
+    ${artisan} package:discover
     ${artisan} firefly-iii:upgrade-database
-    ${artisan} firefly-iii:correct-database
-    ${artisan} firefly-iii:report-integrity
     ${artisan} firefly-iii:laravel-passport-keys
-    ${artisan} cache:clear
-
-    mv /tmp/firefly-iii-env /run/phpfpm/firefly-iii-env
+    ${artisan} view:cache
+    ${artisan} route:cache
+    ${artisan} config:cache
   '';
 
   commonServiceConfig = {
     Type = "oneshot";
     User = user;
     Group = group;
-    StateDirectory = "${removePrefix "/var/lib/" cfg.dataDir}";
+    StateDirectory = "firefly-iii";
+    ReadWritePaths = [cfg.dataDir];
     WorkingDirectory = cfg.package;
     PrivateTmp = true;
     PrivateDevices = true;
@@ -146,6 +137,7 @@ in {
 
     virtualHost = mkOption {
       type = str;
+      default = "localhost";
       description = ''
         The hostname at which you wish firefly-iii to be served. If you have
         enabled nginx using `services.firefly-iii.enableNginx` then this will
@@ -170,14 +162,15 @@ in {
     };
 
     settings = mkOption {
+      default = {};
       description = ''
         Options for firefly-iii configuration. Refer to
         <https://github.com/firefly-iii/firefly-iii/blob/main/.env.example> for
         details on supported values. All <option>_FILE values supported by
         upstream are supported here.
 
-        APP_URL will be set by `services.firefly-iii.virtualHost`, do not
-        redefine it here.
+        APP_URL will be the same as `services.firefly-iii.virtualHost` if the
+        former is unset in `services.firefly-iii.settings`.
       '';
       example = literalExpression ''
         {
@@ -192,7 +185,6 @@ in {
           DB_PASSWORD_FILE = "/var/secrets/firefly-iii-mysql-password.txt;
         }
       '';
-      default = {};
       type = submodule {
         freeformType = attrsOf (oneOf [str int bool]);
         options = {
@@ -216,9 +208,9 @@ in {
           };
           DB_PORT = mkOption {
             type = nullOr int;
-            default = if cfg.settings.DB_CONNECTION == "sqlite" then null
+            default = if cfg.settings.DB_CONNECTION == "pgsql" then 5432
                       else if cfg.settings.DB_CONNECTION == "mysql" then 3306
-                      else 5432;
+                      else null;
             defaultText = ''
               `null` if DB_CONNECTION is "sqlite", `3306` if "mysql", `5432` if "pgsql"
             '';
@@ -227,6 +219,21 @@ in {
               this value to be filled.
             '';
           };
+          DB_HOST = mkOption {
+            type = str;
+            default = if cfg.settings.DB_CONNECTION == "pgsql" then "/run/postgresql"
+                      else "localhost";
+            defaultText = ''
+              "localhost" if DB_CONNECTION is "sqlite" or "mysql", "/run/postgresql" if "pgsql".
+            '';
+            description = ''
+              The machine which hosts your database. This is left at the
+              default value for "mysql" because we use the "DB_SOCKET" option
+              to connect to a unix socket instead. "pgsql" requires that the
+              unix socket location be specified here instead of at "DB_SOCKET".
+              This option does not affect "sqlite".
+            '';
+          };
           APP_KEY_FILE = mkOption {
             type = path;
             description = ''
@@ -235,6 +242,20 @@ in {
               /dev/urandom | base64)" > /path/to/key-file`.
             '';
           };
+          APP_URL = mkOption {
+            type = str;
+            default = if cfg.virtualHost == "localhost" then "http://${cfg.virtualHost}"
+                      else "https://${cfg.virtualHost}";
+            defaultText = ''
+              http(s)://''${config.services.firefly-iii.virtualHost}
+            '';
+            description = ''
+              The APP_URL used by firefly-iii internally. Please make sure this
+              URL matches the external URL of your Firefly III installation. It
+              is used to validate specific requests and to generate URLs in
+              emails.
+            '';
+          };
         };
       };
     };
@@ -242,12 +263,6 @@ in {
 
   config = mkIf cfg.enable {
 
-    services.firefly-iii = {
-      settings = {
-        APP_URL = cfg.virtualHost;
-      };
-    };
-
     services.phpfpm.pools.firefly-iii = {
       inherit user group;
       phpPackage = cfg.package.phpPackage;
@@ -262,29 +277,25 @@ in {
       } // cfg.poolConfig;
     };
 
-    systemd.services.phpfpm-firefly-iii.serviceConfig = {
-      EnvironmentFile = "/run/phpfpm/firefly-iii-env";
-      ExecStartPost = "${pkgs.coreutils}/bin/rm /run/phpfpm/firefly-iii-env";
-    };
-
     systemd.services.firefly-iii-setup = {
+      after = [ "postgresql.service" "mysql.service" ];
       requiredBy = [ "phpfpm-firefly-iii.service" ];
       before = [ "phpfpm-firefly-iii.service" ];
       serviceConfig = {
         ExecStart = firefly-iii-maintenance;
-        RuntimeDirectory = "phpfpm";
-        RuntimeDirectoryPreserve = true;
+        RemainAfterExit = true;
       } // commonServiceConfig;
       unitConfig.JoinsNamespaceOf = "phpfpm-firefly-iii.service";
+      restartTriggers = [ cfg.package ];
     };
 
     systemd.services.firefly-iii-cron = {
+      after = [ "firefly-iii-setup.service" "postgresql.service" "mysql.service" ];
+      wants = [ "firefly-iii-setup.service" ];
       description = "Daily Firefly III cron job";
-      script = ''
-        ${fileenv-func}
-        ${artisan} firefly-iii:cron
-      '';
-      serviceConfig = commonServiceConfig;
+      serviceConfig = {
+        ExecStart = "${artisan} firefly-iii:cron";
+      } // commonServiceConfig;
     };
 
     systemd.timers.firefly-iii-cron = {
@@ -295,6 +306,7 @@ in {
         Persistent = true;
       };
       wantedBy = [ "timers.target" ];
+      restartTriggers = [ cfg.package ];
     };
 
     services.nginx = mkIf cfg.enableNginx {
diff --git a/nixos/modules/services/web-apps/flarum.nix b/nixos/modules/services/web-apps/flarum.nix
new file mode 100644
index 0000000000000..a967c3b121bd6
--- /dev/null
+++ b/nixos/modules/services/web-apps/flarum.nix
@@ -0,0 +1,210 @@
+{ pkgs, lib, config, ... }:
+
+with lib;
+
+let
+  cfg = config.services.flarum;
+
+  flarumInstallConfig = pkgs.writeText "config.json" (builtins.toJSON {
+    debug = false;
+    offline = false;
+
+    baseUrl = cfg.baseUrl;
+    databaseConfiguration = cfg.database;
+    adminUser = {
+      username = cfg.adminUser;
+      password = cfg.initialAdminPassword;
+      email = cfg.adminEmail;
+    };
+    settings = {
+      forum_title = cfg.forumTitle;
+    };
+  });
+in {
+  options.services.flarum = {
+    enable = mkEnableOption "Flarum discussion platform";
+
+    package = mkPackageOption pkgs "flarum" { };
+
+    forumTitle = mkOption {
+      type = types.str;
+      default = "A Flarum Forum on NixOS";
+      description = "Title of the forum.";
+    };
+
+    domain = mkOption {
+      type = types.str;
+      default = "localhost";
+      example = "forum.example.com";
+      description = "Domain to serve on.";
+    };
+
+    baseUrl = mkOption {
+      type = types.str;
+      default = "http://localhost";
+      example = "https://forum.example.com";
+      description = "Change `domain` instead.";
+    };
+
+    adminUser = mkOption {
+      type = types.str;
+      default = "flarum";
+      description = "Username for first web application administrator";
+    };
+
+    adminEmail = mkOption {
+      type = types.str;
+      default = "admin@example.com";
+      description = "Email for first web application administrator";
+    };
+
+    initialAdminPassword = mkOption {
+      type = types.str;
+      default = "flarum";
+      description = "Initial password for the adminUser";
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = "flarum";
+      description = "System user to run Flarum";
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = "flarum";
+      description = "System group to run Flarum";
+    };
+
+    stateDir = mkOption {
+      type = types.path;
+      default = "/var/lib/flarum";
+      description = "Home directory for writable storage";
+    };
+
+    database = mkOption rec {
+      type = with types; attrsOf (oneOf [str bool int]);
+      description = "MySQL database parameters";
+      default = {
+        # the database driver; i.e. MySQL; MariaDB...
+        driver = "mysql";
+        # the host of the connection; localhost in most cases unless using an external service
+        host = "localhost";
+        # the name of the database in the instance
+        database = "flarum";
+        # database username
+        username = "flarum";
+        # database password
+        password = "";
+        # the prefix for the tables; useful if you are sharing the same database with another service
+        prefix = "";
+        # the port of the connection; defaults to 3306 with MySQL
+        port = 3306;
+        strict = false;
+      };
+    };
+
+    createDatabaseLocally = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Create the database and database user locally, and run installation.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users.${cfg.user} = {
+      isSystemUser = true;
+      home = cfg.stateDir;
+      createHome = true;
+      group = cfg.group;
+    };
+    users.groups.${cfg.group} = {};
+
+    services.phpfpm.pools.flarum = {
+      user = cfg.user;
+      settings = {
+        "listen.owner" = config.services.nginx.user;
+        "listen.group" = config.services.nginx.group;
+        "listen.mode" = "0600";
+        "pm" = mkDefault "dynamic";
+        "pm.max_children" = mkDefault 10;
+        "pm.max_requests" = mkDefault 500;
+        "pm.start_servers" = mkDefault 2;
+        "pm.min_spare_servers" = mkDefault 1;
+        "pm.max_spare_servers" = mkDefault 3;
+      };
+      phpOptions = ''
+        error_log = syslog
+        log_errors = on
+      '';
+    };
+
+    services.nginx = {
+      enable = true;
+      virtualHosts."${cfg.domain}" = {
+        root = "${cfg.stateDir}/public";
+        locations."~ \.php$".extraConfig = ''
+          fastcgi_pass unix:${config.services.phpfpm.pools.flarum.socket};
+          fastcgi_index site.php;
+        '';
+        extraConfig = ''
+          index index.php;
+          include ${cfg.package}/share/php/flarum/.nginx.conf;
+        '';
+      };
+    };
+
+    services.mysql = mkIf cfg.enable {
+      enable = true;
+      package = pkgs.mysql;
+      ensureDatabases = [cfg.database.database];
+      ensureUsers = [
+        {
+          name = cfg.database.username;
+          ensurePermissions = {
+            "${cfg.database.database}.*" = "ALL PRIVILEGES";
+          };
+        }
+      ];
+    };
+
+    assertions = [
+      {
+        assertion = !cfg.createDatabaseLocally || cfg.database.driver == "mysql";
+        message = "Flarum can only be automatically installed in MySQL/MariaDB.";
+      }
+    ];
+
+    systemd.services.flarum-install = {
+      description = "Flarum installation";
+      requiredBy = ["phpfpm-flarum.service"];
+      before = ["phpfpm-flarum.service"];
+      requires = ["mysql.service"];
+      after = ["mysql.service"];
+      serviceConfig = {
+        Type = "oneshot";
+        User = cfg.user;
+        Group = cfg.group;
+      };
+      path = [config.services.phpfpm.phpPackage];
+      script = ''
+        mkdir -p ${cfg.stateDir}/{extensions,public/assets/avatars}
+        mkdir -p ${cfg.stateDir}/storage/{cache,formatter,sessions,views}
+        cd ${cfg.stateDir}
+        cp -f ${cfg.package}/share/php/flarum/{extend.php,site.php,flarum} .
+        ln -sf ${cfg.package}/share/php/flarum/vendor .
+        ln -sf ${cfg.package}/share/php/flarum/public/index.php public/
+        chmod a+x . public
+        chmod +x site.php extend.php flarum
+      '' + optionalString (cfg.createDatabaseLocally && cfg.database.driver == "mysql") ''
+        if [ ! -f config.php ]; then
+            php flarum install --file=${flarumInstallConfig}
+        fi
+        php flarum migrate
+        php flarum cache:clear
+      '';
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ fsagbuya jasonodoom ];
+}
diff --git a/nixos/modules/services/web-apps/freshrss.nix b/nixos/modules/services/web-apps/freshrss.nix
index 77c5ecb246171..021101fecaa48 100644
--- a/nixos/modules/services/web-apps/freshrss.nix
+++ b/nixos/modules/services/web-apps/freshrss.nix
@@ -10,7 +10,7 @@ in
   meta.maintainers = with maintainers; [ etu stunkymonkey mattchrist ];
 
   options.services.freshrss = {
-    enable = mkEnableOption "FreshRSS feed reader";
+    enable = mkEnableOption "FreshRSS RSS aggregator and reader with php-fpm backend.";
 
     package = mkPackageOption pkgs "freshrss" { };
 
@@ -108,7 +108,7 @@ in
       type = types.str;
       default = poolName;
       description = ''
-        Name of the phpfpm pool to use and setup. If not specified, a pool will be created
+        Name of the php-fpm pool to use and setup. If not specified, a pool will be created
         with default values.
       '';
     };
@@ -255,13 +255,10 @@ in
         {
           description = "Set up the state directory for FreshRSS before use";
           wantedBy = [ "multi-user.target" ];
-          serviceConfig = defaultServiceConfig //{
-            Type = "oneshot";
-            User = "freshrss";
-            Group = "freshrss";
-            StateDirectory = "freshrss";
-            WorkingDirectory = cfg.package;
+          serviceConfig = defaultServiceConfig // {
+            RemainAfterExit = true;
           };
+          restartIfChanged = true;
           environment = {
             DATA_PATH = cfg.dataDir;
           };
@@ -299,7 +296,7 @@ in
         environment = {
           DATA_PATH = cfg.dataDir;
         };
-        serviceConfig = defaultServiceConfig //{
+        serviceConfig = defaultServiceConfig // {
           ExecStart = "${cfg.package}/app/actualize_script.php";
         };
       };
diff --git a/nixos/modules/services/web-apps/invoiceplane.nix b/nixos/modules/services/web-apps/invoiceplane.nix
index 4d0e25958e35a..9a9f180b21021 100644
--- a/nixos/modules/services/web-apps/invoiceplane.nix
+++ b/nixos/modules/services/web-apps/invoiceplane.nix
@@ -39,20 +39,17 @@ let
 
   extraConfig = hostName: cfg: let
     settings = mapAttrsToList (k: v: "${k}=${mkPhpValue v}") cfg.settings;
-  in pkgs.writeText "extraConfig.php" ''
-    ${concatStringsSep "\n" settings}
-    ${toString cfg.extraConfig}
-  '';
+  in pkgs.writeText "extraConfig.php" (concatStringsSep "\n" settings);
 
   pkg = hostName: cfg: pkgs.stdenv.mkDerivation rec {
     pname = "invoiceplane-${hostName}";
     version = src.version;
     src = pkgs.invoiceplane;
 
-    postPhase = ''
+    postPatch = ''
       # Patch index.php file to load additional config file
       substituteInPlace index.php \
-        --replace "require('vendor/autoload.php');" "require('vendor/autoload.php'); \$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, 'extraConfig.php'); \$dotenv->load();";
+        --replace-fail "require('vendor/autoload.php');" "require('vendor/autoload.php'); \$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, 'extraConfig.php'); \$dotenv->load();";
     '';
 
     installPhase = ''
@@ -182,25 +179,6 @@ let
           '';
         };
 
-        extraConfig = mkOption {
-          type = types.nullOr types.lines;
-          default = null;
-          example = ''
-            SETUP_COMPLETED=true
-            DISABLE_SETUP=true
-            IP_URL=https://invoice.example.com
-          '';
-          description = ''
-            InvoicePlane configuration. Refer to
-            <https://github.com/InvoicePlane/InvoicePlane/blob/master/ipconfig.php.example>
-            for details on supported values.
-
-            **Note**: Please pass structured settings via
-            `services.invoiceplane.sites.${name}.settings` instead, this option
-            will get deprecated in the future.
-          '';
-        };
-
         settings = mkOption {
           type = types.attrsOf types.anything;
           default = {};
@@ -269,12 +247,6 @@ in
   # implementation
   config = mkIf (eachSite != {}) (mkMerge [{
 
-    warnings = flatten (mapAttrsToList (hostName: cfg: [
-      (optional (cfg.extraConfig != null) ''
-        services.invoiceplane.sites."${hostName}".extraConfig will be deprecated in future releases, please use the settings option now.
-      '')
-    ]) eachSite);
-
     assertions = flatten (mapAttrsToList (hostName: cfg: [
       { assertion = cfg.database.createLocally -> cfg.database.user == user;
         message = ''services.invoiceplane.sites."${hostName}".database.user must be ${user} if the database is to be automatically provisioned'';
diff --git a/nixos/modules/services/web-apps/keycloak.md b/nixos/modules/services/web-apps/keycloak.md
index 020bee4003489..4036885ce151c 100644
--- a/nixos/modules/services/web-apps/keycloak.md
+++ b/nixos/modules/services/web-apps/keycloak.md
@@ -68,13 +68,11 @@ to `/auth`. See the option description
 for more details.
 :::
 
-[](#opt-services.keycloak.settings.hostname-strict-backchannel)
-determines whether Keycloak should force all requests to go
-through the frontend URL. By default,
-Keycloak allows backend requests to
-instead use its local hostname or IP address and may also
-advertise it to clients through its OpenID Connect Discovery
-endpoint.
+[](#opt-services.keycloak.settings.hostname-backchannel-dynamic)
+Keycloak has the capability to offer a separate URL for backchannel requests,
+enabling internal communication while maintaining the use of a public URL
+for frontchannel requests. Moreover, the backchannel is dynamically
+resolved based on incoming headers endpoint.
 
 For more information on hostname configuration, see the [Hostname
 section of the Keycloak Server Installation and Configuration
diff --git a/nixos/modules/services/web-apps/keycloak.nix b/nixos/modules/services/web-apps/keycloak.nix
index cf1282b3d4cf4..36bae2575974e 100644
--- a/nixos/modules/services/web-apps/keycloak.nix
+++ b/nixos/modules/services/web-apps/keycloak.nix
@@ -328,8 +328,7 @@ in
             };
 
             hostname = mkOption {
-              type = nullOr str;
-              default = null;
+              type = str;
               example = "keycloak.example.com";
               description = ''
                 The hostname part of the public URL used as base for
@@ -340,16 +339,13 @@ in
               '';
             };
 
-            hostname-strict-backchannel = mkOption {
+            hostname-backchannel-dynamic = mkOption {
               type = bool;
               default = false;
               example = true;
               description = ''
-                Whether Keycloak should force all requests to go
-                through the frontend URL. By default, Keycloak allows
-                backend requests to instead use its local hostname or
-                IP address and may also advertise it to clients
-                through its OpenID Connect Discovery endpoint.
+                Enables dynamic resolving of backchannel URLs,
+                including hostname, scheme, port and context path.
 
                 See <https://www.keycloak.org/server/hostname>
                 for more information about hostname configuration.
@@ -466,7 +462,8 @@ in
       confFile = pkgs.writeText "keycloak.conf" (keycloakConfig filteredConfig);
       keycloakBuild = cfg.package.override {
         inherit confFile;
-        plugins = cfg.package.enabledPlugins ++ cfg.plugins;
+        plugins = cfg.package.enabledPlugins ++ cfg.plugins ++
+                  (with cfg.package.plugins; [quarkus-systemd-notify quarkus-systemd-notify-deployment]);
       };
     in
     mkIf cfg.enable
@@ -481,12 +478,20 @@ in
             message = "Setting up a local PostgreSQL db for Keycloak requires `standard_conforming_strings` turned on to work reliably";
           }
           {
-            assertion = cfg.settings.hostname != null || cfg.settings.hostname-url or null != null;
-            message = "Setting the Keycloak hostname is required, see `services.keycloak.settings.hostname`";
+            assertion = cfg.settings.hostname-url or null == null;
+            message = ''
+              The option `services.keycloak.settings.hostname-url' has been removed.
+              Set `services.keycloak.settings.hostname' instead.
+              See [New Hostname options](https://www.keycloak.org/docs/25.0.0/upgrading/#new-hostname-options) for details.
+            '';
           }
           {
-            assertion = !(cfg.settings.hostname != null && cfg.settings.hostname-url or null != null);
-            message = "`services.keycloak.settings.hostname` and `services.keycloak.settings.hostname-url` are mutually exclusive";
+            assertion = cfg.settings.hostname-strict-backchannel or null == null;
+            message = ''
+              The option `services.keycloak.settings.hostname-strict-backchannel' has been removed.
+              Set `services.keycloak.settings.hostname-backchannel-dynamic' instead.
+              See [New Hostname options](https://www.keycloak.org/docs/25.0.0/upgrading/#new-hostname-options) for details.
+            '';
           }
         ];
 
@@ -638,6 +643,8 @@ in
               RuntimeDirectory = "keycloak";
               RuntimeDirectoryMode = "0700";
               AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+              Type = "notify";  # Requires quarkus-systemd-notify plugin
+              NotifyAccess = "all";
             };
             script = ''
               set -o errexit -o pipefail -o nounset -o errtrace
@@ -663,7 +670,7 @@ in
             '' + ''
               export KEYCLOAK_ADMIN=admin
               export KEYCLOAK_ADMIN_PASSWORD=${escapeShellArg cfg.initialAdminPassword}
-              kc.sh start --optimized
+              kc.sh --verbose start --optimized
             '';
           };
 
diff --git a/nixos/modules/services/web-apps/mastodon.nix b/nixos/modules/services/web-apps/mastodon.nix
index 570f2770fb291..daebd6441cb5d 100644
--- a/nixos/modules/services/web-apps/mastodon.nix
+++ b/nixos/modules/services/web-apps/mastodon.nix
@@ -20,8 +20,6 @@ let
 
     DB_USER = cfg.database.user;
 
-    REDIS_HOST = cfg.redis.host;
-    REDIS_PORT = toString(cfg.redis.port);
     DB_HOST = cfg.database.host;
     DB_NAME = cfg.database.name;
     LOCAL_DOMAIN = cfg.localDomain;
@@ -34,6 +32,8 @@ let
 
     TRUSTED_PROXY_IP = cfg.trustedProxy;
   }
+  // lib.optionalAttrs (cfg.redis.host != null) { REDIS_HOST = cfg.redis.host; }
+  // lib.optionalAttrs (cfg.redis.port != null) { REDIS_PORT = toString(cfg.redis.port); }
   // lib.optionalAttrs (cfg.redis.createLocally && cfg.redis.enableUnixSocket) { REDIS_URL = "unix://${config.services.redis.servers.mastodon.unixSocket}"; }
   // lib.optionalAttrs (cfg.database.host != "/run/postgresql" && cfg.database.port != null) { DB_PORT = toString cfg.database.port; }
   // lib.optionalAttrs cfg.smtp.authenticate { SMTP_LOGIN  = cfg.smtp.user; }
@@ -90,6 +90,11 @@ let
     SystemCallArchitectures = "native";
   };
 
+  # Services that all Mastodon units After= and Requires= on
+  commonServices = lib.optional redisActuallyCreateLocally "redis-mastodon.service"
+    ++ lib.optional databaseActuallyCreateLocally "postgresql.service"
+    ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service";
+
   envFile = pkgs.writeText "mastodon.env" (lib.concatMapStrings (s: s + "\n") (
     (lib.concatLists (lib.mapAttrsToList (name: value:
       lib.optional (value != null) ''${name}="${toString value}"''
@@ -117,14 +122,8 @@ let
       jobClassLabel = toString ([""] ++ processCfg.jobClasses);
       threads = toString (if processCfg.threads == null then cfg.sidekiqThreads else processCfg.threads);
     in {
-      after = [ "network.target" "mastodon-init-dirs.service" ]
-        ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service"
-        ++ lib.optional databaseActuallyCreateLocally "postgresql.service"
-        ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service";
-      requires = [ "mastodon-init-dirs.service" ]
-        ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service"
-        ++ lib.optional databaseActuallyCreateLocally "postgresql.service"
-        ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service";
+      after = [ "network.target" "mastodon-init-dirs.service" ] ++ commonServices;
+      requires = [ "mastodon-init-dirs.service" ] ++ commonServices;
       description = "Mastodon sidekiq${jobClassLabel}";
       wantedBy = [ "mastodon.target" ];
       environment = env // {
@@ -149,14 +148,8 @@ let
       (map (i: {
         name = "mastodon-streaming-${toString i}";
         value = {
-          after = [ "network.target" "mastodon-init-dirs.service" ]
-            ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service"
-            ++ lib.optional databaseActuallyCreateLocally "postgresql.service"
-            ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service";
-          requires = [ "mastodon-init-dirs.service" ]
-            ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service"
-            ++ lib.optional databaseActuallyCreateLocally "postgresql.service"
-            ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service";
+          after = [ "network.target" "mastodon-init-dirs.service" ] ++ commonServices;
+          requires = [ "mastodon-init-dirs.service" ] ++ commonServices;
           wantedBy = [ "mastodon.target" "mastodon-streaming.target" ];
           description = "Mastodon streaming ${toString i}";
           environment = env // { SOCKET = "/run/mastodon-streaming/streaming-${toString i}.socket"; };
@@ -401,14 +394,20 @@ in {
 
         host = lib.mkOption {
           description = "Redis host.";
-          type = lib.types.str;
-          default = "127.0.0.1";
+          type = lib.types.nullOr lib.types.str;
+          default = if cfg.redis.createLocally && !cfg.redis.enableUnixSocket then "127.0.0.1" else null;
+          defaultText = lib.literalExpression ''
+            if config.${opt.redis.createLocally} && !config.${opt.redis.enableUnixSocket} then "127.0.0.1" else null
+          '';
         };
 
         port = lib.mkOption {
           description = "Redis port.";
-          type = lib.types.port;
-          default = 31637;
+          type = lib.types.nullOr lib.types.port;
+          default = if cfg.redis.createLocally && !cfg.redis.enableUnixSocket then 31637 else null;
+          defaultText = lib.literalExpression ''
+            if config.${opt.redis.createLocally} && !config.${opt.redis.enableUnixSocket} then 31637 else null
+          '';
         };
 
         passwordFile = lib.mkOption {
@@ -632,6 +631,20 @@ in {
   config = lib.mkIf cfg.enable (lib.mkMerge [{
     assertions = [
       {
+        assertion = !redisActuallyCreateLocally -> (cfg.redis.host != "127.0.0.1" && cfg.redis.port != null);
+        message = ''
+          `services.mastodon.redis.host` and `services.mastodon.redis.port` need to be set if
+            `services.mastodon.redis.createLocally` is not enabled.
+        '';
+      }
+      {
+        assertion = redisActuallyCreateLocally -> (!cfg.redis.enableUnixSocket || (cfg.redis.host == null && cfg.redis.port == null));
+        message = ''
+          `services.mastodon.redis.enableUnixSocket` needs to be disabled if
+            `services.mastodon.redis.host` and `services.mastodon.redis.port` is used.
+        '';
+      }
+      {
         assertion = redisActuallyCreateLocally -> (!cfg.redis.enableUnixSocket || cfg.redis.passwordFile == null);
         message = ''
           <option>services.mastodon.redis.enableUnixSocket</option> needs to be disabled if
@@ -783,14 +796,8 @@ in {
     };
 
     systemd.services.mastodon-web = {
-      after = [ "network.target" "mastodon-init-dirs.service" ]
-        ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service"
-        ++ lib.optional databaseActuallyCreateLocally "postgresql.service"
-        ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service";
-      requires = [ "mastodon-init-dirs.service" ]
-        ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service"
-        ++ lib.optional databaseActuallyCreateLocally "postgresql.service"
-        ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service";
+      after = [ "network.target" "mastodon-init-dirs.service" ] ++ commonServices;
+      requires = [ "mastodon-init-dirs.service" ] ++ commonServices;
       wantedBy = [ "mastodon.target" ];
       description = "Mastodon web";
       environment = env // (if cfg.enableUnixSocket
diff --git a/nixos/modules/services/web-apps/mealie.nix b/nixos/modules/services/web-apps/mealie.nix
index 8f68828e7a0be..2484b2489c0d0 100644
--- a/nixos/modules/services/web-apps/mealie.nix
+++ b/nixos/modules/services/web-apps/mealie.nix
@@ -28,8 +28,6 @@ in
         Configuration of the Mealie service.
 
         See [the mealie documentation](https://nightly.mealie.io/documentation/getting-started/installation/backend-config/) for available options and default values.
-
-        In addition to the official documentation, you can set {env}`MEALIE_LOG_FILE`.
       '';
       example = {
         ALLOW_SIGNUP = "false";
@@ -61,6 +59,7 @@ in
         PRODUCTION = "true";
         ALEMBIC_CONFIG_FILE="${pkg}/config/alembic.ini";
         API_PORT = toString cfg.port;
+        BASE_URL = "http://localhost:${toString cfg.port}";
         DATA_DIR = "/var/lib/mealie";
         CRF_MODEL_PATH = "/var/lib/mealie/model.crfmodel";
       } // (builtins.mapAttrs (_: val: toString val) cfg.settings);
diff --git a/nixos/modules/services/web-apps/miniflux.nix b/nixos/modules/services/web-apps/miniflux.nix
index d65d6db3cdaaa..61243a63c582e 100644
--- a/nixos/modules/services/web-apps/miniflux.nix
+++ b/nixos/modules/services/web-apps/miniflux.nix
@@ -1,7 +1,7 @@
 { config, lib, pkgs, ... }:
 
-with lib;
 let
+  inherit (lib) mkEnableOption mkPackageOption mkOption types literalExpression mkIf mkDefault;
   cfg = config.services.miniflux;
 
   defaultAddress = "localhost:8080";
@@ -20,8 +20,8 @@ in
 
       package = mkPackageOption pkgs "miniflux" { };
 
-      createDatabaseLocally = lib.mkOption {
-        type = lib.types.bool;
+      createDatabaseLocally = mkOption {
+        type = types.bool;
         default = true;
         description = ''
           Whether a PostgreSQL database should be automatically created and
@@ -66,6 +66,7 @@ in
       DATABASE_URL = lib.mkIf cfg.createDatabaseLocally "user=miniflux host=/run/postgresql dbname=miniflux";
       RUN_MIGRATIONS = 1;
       CREATE_ADMIN = 1;
+      WATCHDOG = 1;
     };
 
     services.postgresql = lib.mkIf cfg.createDatabaseLocally {
@@ -96,12 +97,18 @@ in
         ++ lib.optionals cfg.createDatabaseLocally [ "postgresql.service" "miniflux-dbsetup.service" ];
 
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/miniflux";
+        Type = "notify";
+        ExecStart = lib.getExe cfg.package;
         User = "miniflux";
         DynamicUser = true;
         RuntimeDirectory = "miniflux";
         RuntimeDirectoryMode = "0750";
         EnvironmentFile = cfg.adminCredentialsFile;
+        WatchdogSec = 60;
+        WatchdogSignal = "SIGKILL";
+        Restart = "always";
+        RestartSec = 5;
+
         # Hardening
         CapabilityBoundingSet = [ "" ];
         DeviceAllow = [ "" ];
diff --git a/nixos/modules/services/web-apps/nextcloud-notify_push.nix b/nixos/modules/services/web-apps/nextcloud-notify_push.nix
index d6d17158a5590..4da5aff0c83e2 100644
--- a/nixos/modules/services/web-apps/nextcloud-notify_push.nix
+++ b/nixos/modules/services/web-apps/nextcloud-notify_push.nix
@@ -90,7 +90,7 @@ in
         export DATABASE_PASSWORD="$(<"${cfg.dbpassFile}")"
       '' + ''
         export DATABASE_URL="${dbUrl}"
-        ${cfg.package}/bin/notify_push '${cfgN.datadir}/config/config.php'
+        exec ${cfg.package}/bin/notify_push '${cfgN.datadir}/config/config.php'
       '';
       serviceConfig = {
         User = "nextcloud";
@@ -98,6 +98,7 @@ in
         RuntimeDirectory = [ "nextcloud-notify_push" ];
         Restart = "on-failure";
         RestartSec = "5s";
+        Type = "notify";
       };
     };
 
diff --git a/nixos/modules/services/web-apps/nextcloud.md b/nixos/modules/services/web-apps/nextcloud.md
index ec860d307b381..0b615deae44be 100644
--- a/nixos/modules/services/web-apps/nextcloud.md
+++ b/nixos/modules/services/web-apps/nextcloud.md
@@ -205,11 +205,6 @@ it complains loudly now. So nothing actionable here by default. Alternatively yo
 * set [](#opt-services.nextcloud.settings.log_type) to "file" to be able to view logs
   from the admin panel.
 
-### Your web server is not properly set up to resolve `.well-known` URLs, failed on: `/.well-known/caldav` {#module-services-nextcloud-warning-wellknown-caldav}
-
-This warning appearing seems to be an upstream issue and is being sorted out
-in [nextcloud/server#45033](https://github.com/nextcloud/server/issues/45033).
-
 ## Maintainer information {#module-services-nextcloud-maintainer-info}
 
 As stated in the previous paragraph, we must provide a clean upgrade-path for Nextcloud
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index 21f76938f20c5..f4560ed64bb4f 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -80,6 +80,12 @@ let
     mkKeyValue = generators.mkKeyValueDefault {} " = ";
   };
 
+  phpCli = concatStringsSep " " ([
+    "${getExe phpPackage}"
+  ] ++ optionals (cfg.cli.memoryLimit != null) [
+    "-dmemory_limit=${cfg.cli.memoryLimit}"
+  ]);
+
   occ = pkgs.writeScriptBin "nextcloud-occ" ''
     #! ${pkgs.runtimeShell}
     cd ${webroot}
@@ -89,7 +95,7 @@ let
     fi
     export NEXTCLOUD_CONFIG_DIR="${datadir}/config"
     $sudo \
-      ${phpPackage}/bin/php \
+      ${phpCli} \
       occ "$@"
   '';
 
@@ -196,6 +202,9 @@ let
 in {
 
   imports = [
+    (mkRenamedOptionModule
+      [ "services" "nextcloud" "cron" "memoryLimit" ]
+      [ "services" "nextcloud" "cli" "memoryLimit" ])
     (mkRemovedOptionModule [ "services" "nextcloud" "enableBrokenCiphersForSSE" ] ''
       This option has no effect since there's no supported Nextcloud version packaged here
       using OpenSSL for RC4 SSE.
@@ -446,7 +455,13 @@ in {
       dbtableprefix = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = "Table prefix in Nextcloud's database.";
+        description = ''
+          Table prefix in Nextcloud's database.
+
+          __Note:__ since Nextcloud 20 it's not an option anymore to create a database
+          schema with a custom table prefix. This option only exists for backwards compatibility
+          with installations that were originally provisioned with Nextcloud <20.
+        '';
       };
       adminuser = mkOption {
         type = types.str;
@@ -642,7 +657,6 @@ in {
       type = types.package;
       default = occ;
       defaultText = literalMD "generated script";
-      internal = true;
       description = ''
         The nextcloud-occ program preconfigured to target this Nextcloud instance.
       '';
@@ -793,11 +807,21 @@ in {
         '';
       };
     };
+
+    cli.memoryLimit = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "1G";
+      description = ''
+        The `memory_limit` of PHP is equal to [](#opt-services.nextcloud.maxUploadSize).
+        The value can be customized for `nextcloud-cron.service` using this option.
+      '';
+    };
   };
 
   config = mkIf cfg.enable (mkMerge [
     { warnings = let
-        latest = 28;
+        latest = 29;
         upgradeWarning = major: nixos:
           ''
             A legacy Nextcloud install (from before NixOS ${nixos}) may be installed.
@@ -816,6 +840,13 @@ in {
           Using config.services.nextcloud.poolConfig is deprecated and will become unsupported in a future release.
           Please migrate your configuration to config.services.nextcloud.poolSettings.
         '')
+        ++ (optional (cfg.config.dbtableprefix != null) ''
+          Using `services.nextcloud.config.dbtableprefix` is deprecated. Fresh installations with this
+          option set are not allowed anymore since v20.
+
+          If you have an existing installation with a custom table prefix, make sure it is
+          set correctly in `config.php` and remove the option from your NixOS config.
+        '')
         ++ (optional (versionOlder cfg.package.version "25") (upgradeWarning 24 "22.11"))
         ++ (optional (versionOlder cfg.package.version "26") (upgradeWarning 25 "23.05"))
         ++ (optional (versionOlder cfg.package.version "27") (upgradeWarning 26 "23.11"))
@@ -939,6 +970,7 @@ in {
 
         in {
           wantedBy = [ "multi-user.target" ];
+          wants = [ "nextcloud-update-db.service" ];
           before = [ "phpfpm-nextcloud.service" ];
           after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
           requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
@@ -997,10 +1029,10 @@ in {
           after = [ "nextcloud-setup.service" ];
           environment.NEXTCLOUD_CONFIG_DIR = "${datadir}/config";
           serviceConfig = {
-            Type = "oneshot";
+            Type = "exec";
             User = "nextcloud";
-            ExecCondition = "${lib.getExe phpPackage} -f ${webroot}/occ status -e";
-            ExecStart = "${lib.getExe phpPackage} -f ${webroot}/cron.php";
+            ExecCondition = "${phpCli} -f ${webroot}/occ status -e";
+            ExecStart = "${phpCli} -f ${webroot}/cron.php";
             KillMode = "process";
           };
         };
@@ -1013,6 +1045,20 @@ in {
           };
           startAt = cfg.autoUpdateApps.startAt;
         };
+        nextcloud-update-db = {
+          after = [ "nextcloud-setup.service" ];
+          environment.NEXTCLOUD_CONFIG_DIR = "${datadir}/config";
+          script = ''
+            ${occ}/bin/nextcloud-occ db:add-missing-columns
+            ${occ}/bin/nextcloud-occ db:add-missing-indices
+            ${occ}/bin/nextcloud-occ db:add-missing-primary-keys
+          '';
+          serviceConfig = {
+            Type = "exec";
+            User = "nextcloud";
+            ExecCondition = "${phpCli} -f ${webroot}/occ status -e";
+          };
+        };
       };
 
       services.phpfpm = {
@@ -1105,10 +1151,10 @@ in {
             extraConfig = ''
               absolute_redirect off;
               location = /.well-known/carddav {
-                return 301 /remote.php/dav;
+                return 301 /remote.php/dav/;
               }
               location = /.well-known/caldav {
-                return 301 /remote.php/dav;
+                return 301 /remote.php/dav/;
               }
               location ~ ^/\.well-known/(?!acme-challenge|pki-validation) {
                 return 301 /index.php$request_uri;
diff --git a/nixos/modules/services/web-apps/nextjs-ollama-llm-ui.nix b/nixos/modules/services/web-apps/nextjs-ollama-llm-ui.nix
new file mode 100644
index 0000000000000..9bd2cf310c0af
--- /dev/null
+++ b/nixos/modules/services/web-apps/nextjs-ollama-llm-ui.nix
@@ -0,0 +1,87 @@
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
+let
+  cfg = config.services.nextjs-ollama-llm-ui;
+  # we have to override the URL to a Ollama service here, because it gets baked into the web app.
+  nextjs-ollama-llm-ui = cfg.package.override { inherit (cfg) ollamaUrl; };
+in
+{
+  options = {
+    services.nextjs-ollama-llm-ui = {
+      enable = lib.mkEnableOption ''
+        Simple Ollama web UI service; an easy to use web frontend for a Ollama backend service.
+        Run state-of-the-art AI large language models (LLM) similar to ChatGPT locally with privacy
+        on your personal computer.
+        This service is stateless and doesn't store any data on the server; all data is kept
+        locally in your web browser.
+        See https://github.com/jakobhoeg/nextjs-ollama-llm-ui.
+
+        Required: You need the Ollama backend service running by having
+        "services.nextjs-ollama-llm-ui.ollamaUrl" point to the correct url.
+        You can host such a backend service with NixOS through "services.ollama".
+      '';
+      package = lib.mkPackageOption pkgs "nextjs-ollama-llm-ui" { };
+
+      hostname = lib.mkOption {
+        type = lib.types.str;
+        default = "127.0.0.1";
+        example = "ui.example.org";
+        description = ''
+          The hostname under which the Ollama UI interface should be accessible.
+          By default it uses localhost/127.0.0.1 to be accessible only from the local machine.
+          Change to "0.0.0.0" to make it directly accessible from the local network.
+
+          Note: You should keep it at 127.0.0.1 and only serve to the local
+          network or internet from a (home) server behind a reverse-proxy and secured encryption.
+          See https://wiki.nixos.org/wiki/Nginx for instructions on how to set up a reverse-proxy.
+        '';
+      };
+
+      port = lib.mkOption {
+        type = lib.types.port;
+        default = 3000;
+        example = 3000;
+        description = ''
+          The port under which the Ollama UI interface should be accessible.
+        '';
+      };
+
+      ollamaUrl = lib.mkOption {
+        type = lib.types.str;
+        default = "127.0.0.1:11434";
+        example = "https://ollama.example.org";
+        description = ''
+          The address (including host and port) under which we can access the Ollama backend server.
+          !Note that if the the UI service is running under a domain "https://ui.example.org",
+          the Ollama backend service must allow "CORS" requests from this domain, e.g. by adding
+          "services.ollama.environment.OLLAMA_ORIGINS = [ ... "https://ui.example.org" ];"!
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services = {
+
+      nextjs-ollama-llm-ui = {
+        wantedBy = [ "multi-user.target" ];
+        description = "Nextjs Ollama LLM Ui.";
+        after = [ "network.target" ];
+        environment = {
+          HOSTNAME = cfg.hostname;
+          PORT = toString cfg.port;
+          NEXT_PUBLIC_OLLAMA_URL = cfg.ollamaUrl;
+        };
+        serviceConfig = {
+          ExecStart = "${lib.getExe nextjs-ollama-llm-ui}";
+          DynamicUser = true;
+        };
+      };
+    };
+  };
+  meta.maintainers = with lib.maintainers; [ malteneuss ];
+}
diff --git a/nixos/modules/services/web-apps/node-red.nix b/nixos/modules/services/web-apps/node-red.nix
index 7c8a2a6687b9c..4c095ea79bbde 100644
--- a/nixos/modules/services/web-apps/node-red.nix
+++ b/nixos/modules/services/web-apps/node-red.nix
@@ -5,15 +5,6 @@ with lib;
 let
   cfg = config.services.node-red;
   defaultUser = "node-red";
-  finalPackage = if cfg.withNpmAndGcc then node-red_withNpmAndGcc else cfg.package;
-  node-red_withNpmAndGcc = pkgs.runCommand "node-red" {
-    nativeBuildInputs = [ pkgs.makeWrapper ];
-  }
-  ''
-    mkdir -p $out/bin
-    makeWrapper ${pkgs.nodePackages.node-red}/bin/node-red $out/bin/node-red \
-      --set PATH '${lib.makeBinPath [ pkgs.nodePackages.npm pkgs.gcc ]}:$PATH' \
-  '';
 in
 {
   options.services.node-red = {
@@ -127,11 +118,12 @@ in
       environment = {
         HOME = cfg.userDir;
       };
+      path = lib.optionals cfg.withNpmAndGcc [ pkgs.nodePackages.npm pkgs.gcc ];
       serviceConfig = mkMerge [
         {
           User = cfg.user;
           Group = cfg.group;
-          ExecStart = "${finalPackage}/bin/node-red ${pkgs.lib.optionalString cfg.safe "--safe"} --settings ${cfg.configFile} --port ${toString cfg.port} --userDir ${cfg.userDir} ${concatStringsSep " " (mapAttrsToList (name: value: "-D ${name}=${value}") cfg.define)}";
+          ExecStart = "${cfg.package}/bin/node-red ${pkgs.lib.optionalString cfg.safe "--safe"} --settings ${cfg.configFile} --port ${toString cfg.port} --userDir ${cfg.userDir} ${concatStringsSep " " (mapAttrsToList (name: value: "-D ${name}=${value}") cfg.define)}";
           PrivateTmp = true;
           Restart = "always";
           WorkingDirectory = cfg.userDir;
diff --git a/nixos/modules/services/web-apps/plausible.nix b/nixos/modules/services/web-apps/plausible.nix
index 8e49e591f75cd..1f909bbd67a36 100644
--- a/nixos/modules/services/web-apps/plausible.nix
+++ b/nixos/modules/services/web-apps/plausible.nix
@@ -276,8 +276,11 @@ in {
             ${lib.optionalString (cfg.mail.smtp.passwordFile != null)
               ''export SMTP_USER_PWD="$(< $CREDENTIALS_DIRECTORY/SMTP_USER_PWD )"''}
 
-            # setup
-            ${cfg.package}/createdb.sh
+            ${lib.optionalString cfg.database.postgres.setup ''
+              # setup
+              ${cfg.package}/createdb.sh
+            ''}
+
             ${cfg.package}/migrate.sh
             export IP_GEOLOCATION_DB=${pkgs.dbip-country-lite}/share/dbip/dbip-country-lite.mmdb
             ${cfg.package}/bin/plausible eval "(Plausible.Release.prepare() ; Plausible.Auth.create_user(\"$ADMIN_USER_NAME\", \"$ADMIN_USER_EMAIL\", \"$ADMIN_USER_PWD\"))"
@@ -326,6 +329,6 @@ in {
     ];
   };
 
-  meta.maintainers = with maintainers; [ ];
+  meta.maintainers = with maintainers; [ xanderio ];
   meta.doc = ./plausible.md;
 }
diff --git a/nixos/modules/services/web-apps/pretalx.nix b/nixos/modules/services/web-apps/pretalx.nix
index b062a8b7eeeac..1411d11982c87 100644
--- a/nixos/modules/services/web-apps/pretalx.nix
+++ b/nixos/modules/services/web-apps/pretalx.nix
@@ -11,20 +11,25 @@ let
 
   configFile = format.generate "pretalx.cfg" cfg.settings;
 
-  extras = cfg.package.optional-dependencies.redis
-    ++ lib.optionals (cfg.settings.database.backend == "mysql") cfg.package.optional-dependencies.mysql
-    ++ lib.optionals (cfg.settings.database.backend == "postgresql") cfg.package.optional-dependencies.postgres;
-
-  pythonEnv = cfg.package.python.buildEnv.override {
-    extraLibs = [ (cfg.package.python.pkgs.toPythonModule cfg.package) ]
-      ++ (with cfg.package.python.pkgs; [ gunicorn ]
-      ++ lib.optional cfg.celery.enable celery) ++ extras;
+  finalPackage = cfg.package.override {
+    inherit (cfg) plugins;
+  };
+
+  pythonEnv = finalPackage.python.buildEnv.override {
+    extraLibs = with finalPackage.python.pkgs; [
+      (toPythonModule finalPackage)
+      gunicorn
+    ]
+    ++ finalPackage.optional-dependencies.redis
+    ++ lib.optionals cfg.celery.enable [ celery ]
+    ++ lib.optionals (cfg.settings.database.backend == "mysql") finalPackage.optional-dependencies.mysql
+    ++ lib.optionals (cfg.settings.database.backend == "postgresql") finalPackage.optional-dependencies.postgres;
   };
 in
 
 {
   meta = with lib; {
-    maintainers = teams.c3d2.members;
+    maintainers = with maintainers; [ hexa] ++ teams.c3d2.members;
   };
 
   options.services.pretalx = {
@@ -44,6 +49,20 @@ in
       description = "User under which pretalx should run.";
     };
 
+    plugins = lib.mkOption {
+      type = with lib.types; listOf package;
+      default = [];
+      example = lib.literalExpression ''
+        with config.services.pretalx.package.plugins; [
+          pages
+          youtube
+        ];
+      '';
+      description = ''
+        Pretalx plugins to install into the Python environment.
+      '';
+    };
+
     gunicorn.extraArgs = lib.mkOption {
       type = with lib.types; listOf str;
       default = [
@@ -329,10 +348,47 @@ in
         serviceConfig = {
           User = "pretalx";
           Group = "pretalx";
-          StateDirectory = [ "pretalx" "pretalx/media" ];
+          StateDirectory = [
+            "pretalx"
+            "pretalx/media"
+          ];
+          StateDirectoryMode = "0750";
           LogsDirectory = "pretalx";
           WorkingDirectory = cfg.settings.filesystem.data;
           SupplementaryGroups = [ "redis-pretalx" ];
+          AmbientCapabilities = "";
+          CapabilityBoundingSet = [ "" ];
+          DevicePolicy = "closed";
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
+          PrivateDevices = true;
+          PrivateTmp = true;
+          ProcSubset = "pid";
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectProc = "invisible";
+          ProtectSystem = "strict";
+          RemoveIPC = true;
+          RestrictAddressFamilies = [
+            "AF_INET"
+            "AF_INET6"
+            "AF_UNIX"
+          ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          SystemCallArchitectures = "native";
+          SystemCallFilter = [
+            "@system-service"
+            "~@privileged"
+            "@chown"
+          ];
+          UMask = "0027";
         };
       };
     in {
@@ -395,6 +451,8 @@ in
         wantedBy = [ "multi-user.target" ];
         serviceConfig.ExecStart = "${lib.getExe' pythonEnv "celery"} -A pretalx.celery_app worker ${cfg.celery.extraArgs}";
       });
+
+      nginx.serviceConfig.SupplementaryGroups = lib.mkIf cfg.nginx.enable [ "pretalx" ];
     };
 
     systemd.sockets.pretalx-web.socketConfig = {
@@ -403,11 +461,9 @@ in
     };
 
     users = {
-      groups."${cfg.group}" = {};
-      users."${cfg.user}" = {
+      groups.${cfg.group} = {};
+      users.${cfg.user} = {
         isSystemUser = true;
-        createHome = true;
-        home = cfg.settings.filesystem.data;
         inherit (cfg) group;
       };
     };
diff --git a/nixos/modules/services/web-apps/pretix.nix b/nixos/modules/services/web-apps/pretix.nix
index 22ee9769aa923..9786b61160260 100644
--- a/nixos/modules/services/web-apps/pretix.nix
+++ b/nixos/modules/services/web-apps/pretix.nix
@@ -310,7 +310,7 @@ in
               type = types.str;
               default = "redis+socket://${config.services.redis.servers.pretix.unixSocket}?virtual_host=1";
               defaultText = literalExpression ''
-                optionalString config.services.pretix.celery.enable "redis+socket://''${config.services.redis.servers.pretix.unixSocket}?virtual_host=1"
+                redis+socket://''${config.services.redis.servers.pretix.unixSocket}?virtual_host=1
               '';
               description = ''
                 URI to the celery backend used for the asynchronous job queue.
@@ -321,7 +321,7 @@ in
               type = types.str;
               default = "redis+socket://${config.services.redis.servers.pretix.unixSocket}?virtual_host=2";
               defaultText = literalExpression ''
-                optionalString config.services.pretix.celery.enable "redis+socket://''${config.services.redis.servers.pretix.unixSocket}?virtual_host=2"
+                redis+socket://''${config.services.redis.servers.pretix.unixSocket}?virtual_host=2
               '';
               description = ''
                 URI to the celery broker used for the asynchronous job queue.
@@ -468,7 +468,7 @@ in
           StateDirectory = [
             "pretix"
           ];
-          StateDirectoryMode = "0755";
+          StateDirectoryMode = "0750";
           CacheDirectory = "pretix";
           LogsDirectory = "pretix";
           WorkingDirectory = cfg.settings.pretix.datadir;
@@ -507,7 +507,7 @@ in
             "~@privileged"
             "@chown"
           ];
-          UMask = "0022";
+          UMask = "0027";
         };
       };
     in {
@@ -561,6 +561,8 @@ in
         wantedBy = [ "multi-user.target" ];
         serviceConfig.ExecStart = "${getExe' pythonEnv "celery"} -A pretix.celery_app worker ${cfg.celery.extraArgs}";
       };
+
+      nginx.serviceConfig.SupplementaryGroups = mkIf cfg.nginx.enable [ "pretix" ];
     };
 
     systemd.sockets.pretix-web.socketConfig = {
@@ -569,11 +571,9 @@ in
     };
 
     users = {
-      groups."${cfg.group}" = {};
-      users."${cfg.user}" = {
+      groups.${cfg.group} = {};
+      users.${cfg.user} = {
         isSystemUser = true;
-        createHome = true;
-        home = cfg.settings.pretix.datadir;
         inherit (cfg) group;
       };
     };
diff --git a/nixos/modules/services/web-apps/simplesamlphp.nix b/nixos/modules/services/web-apps/simplesamlphp.nix
new file mode 100644
index 0000000000000..e970266fc17dd
--- /dev/null
+++ b/nixos/modules/services/web-apps/simplesamlphp.nix
@@ -0,0 +1,128 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  cfg = config.services.simplesamlphp;
+
+  format = pkgs.formats.php { finalVariable = "config"; };
+
+  generateConfig =
+    opts:
+    pkgs.runCommand "simplesamlphp-config" { } ''
+      mkdir $out
+      cp ${format.generate "config.php" opts.settings} $out/config.php
+      cp ${format.generate "authsources.php" opts.authSources} $out/authsources.php
+    '';
+in
+{
+  meta = {
+    maintainers = with lib.maintainers; [ nhnn ];
+  };
+
+  options.services.simplesamlphp =
+    with lib;
+    mkOption {
+      type = types.attrsOf (
+        types.submodule (
+          { config, ... }:
+          {
+            options = {
+              package = mkPackageOption pkgs "simplesamlphp" { };
+              configureNginx = mkOption {
+                type = types.bool;
+                default = true;
+                description = "Configure nginx as a reverse proxy for SimpleSAMLphp.";
+              };
+              phpfpmPool = mkOption {
+                type = types.str;
+                description = "The PHP-FPM pool that serves SimpleSAMLphp instance.";
+              };
+              localDomain = mkOption {
+                type = types.str;
+                description = "The domain serving your SimpleSAMLphp instance. This option modifies only /saml route.";
+              };
+              settings = mkOption {
+                type = types.submodule {
+                  freeformType = format.type;
+                  options = {
+                    baseurlpath = mkOption {
+                      type = types.str;
+                      example = "https://filesender.example.com/saml/";
+                      description = "URL where SimpleSAMLphp can be reached.";
+                    };
+                  };
+                };
+                default = { };
+                description = ''
+                  Configuration options used by SimpleSAMLphp.
+                  See [](https://simplesamlphp.org/docs/stable/simplesamlphp-install)
+                  for available options.
+                '';
+              };
+
+              authSources = mkOption {
+                type = format.type;
+                default = { };
+                description = ''
+                  Auth sources options used by SimpleSAMLphp.
+                '';
+              };
+
+              libDir = mkOption {
+                type = types.str;
+                readOnly = true;
+                description = ''
+                  Path to the SimpleSAMLphp library directory.
+                '';
+              };
+              configDir = mkOption {
+                type = types.str;
+                readOnly = true;
+                description = ''
+                  Path to the SimpleSAMLphp config directory.
+                '';
+              };
+            };
+            config = {
+              libDir = "${config.package}/share/php/simplesamlphp/";
+              configDir = "${generateConfig config}";
+            };
+          }
+        )
+      );
+      default = { };
+      description = "Instances of SimpleSAMLphp. This module is designed to work with already existing PHP-FPM pool and NGINX virtualHost.";
+    };
+
+  config = {
+    services.phpfpm.pools = lib.mapAttrs' (
+      phpfpmName: opts:
+      lib.nameValuePair opts.phpfpmPool { phpEnv.SIMPLESAMLPHP_CONFIG_DIR = "${generateConfig opts}"; }
+    ) cfg;
+
+    services.nginx.virtualHosts = lib.mapAttrs' (
+      phpfpmName: opts:
+      lib.nameValuePair opts.localDomain (
+        lib.mkIf opts.configureNginx {
+          locations."^~ /saml/" = {
+            alias = "${opts.package}/share/php/simplesamlphp/www/";
+            extraConfig = ''
+                location ~ ^(?<prefix>/saml)(?<phpfile>.+?\.php)(?<pathinfo>/.*)?$ {
+                  include ${pkgs.nginx}/conf/fastcgi.conf;
+                  fastcgi_split_path_info  ^(.+\.php)(/.+)$;
+                  fastcgi_pass  unix:${config.services.phpfpm.pools.${phpfpmName}.socket};
+                  fastcgi_intercept_errors on;
+                  fastcgi_param SCRIPT_FILENAME $document_root$phpfile;
+                  fastcgi_param SCRIPT_NAME /saml$phpfile;
+                  fastcgi_param PATH_INFO $pathinfo if_not_empty;
+              }
+            '';
+          };
+        }
+      )
+    ) cfg;
+  };
+}
diff --git a/nixos/modules/services/web-apps/slskd.nix b/nixos/modules/services/web-apps/slskd.nix
index 15a5fd1177adf..6254fe294eeed 100644
--- a/nixos/modules/services/web-apps/slskd.nix
+++ b/nixos/modules/services/web-apps/slskd.nix
@@ -5,7 +5,7 @@ let
   defaultUser = "slskd";
 in {
   options.services.slskd = with lib; with types; {
-    enable = mkEnableOption "enable slskd";
+    enable = mkEnableOption "slskd";
 
     package = mkPackageOptionMD pkgs "slskd" { };
 
diff --git a/nixos/modules/services/web-apps/your_spotify.nix b/nixos/modules/services/web-apps/your_spotify.nix
new file mode 100644
index 0000000000000..3eb2ffef4f933
--- /dev/null
+++ b/nixos/modules/services/web-apps/your_spotify.nix
@@ -0,0 +1,191 @@
+{
+  pkgs,
+  config,
+  lib,
+  ...
+}: let
+  inherit
+    (lib)
+    boolToString
+    concatMapAttrs
+    concatStrings
+    isBool
+    mapAttrsToList
+    mkEnableOption
+    mkIf
+    mkOption
+    mkPackageOption
+    optionalAttrs
+    types
+    mkDefault
+    ;
+  cfg = config.services.your_spotify;
+
+  configEnv = concatMapAttrs (name: value:
+    optionalAttrs (value != null) {
+      ${name} =
+        if isBool value
+        then boolToString value
+        else toString value;
+    })
+  cfg.settings;
+
+  configFile = pkgs.writeText "your_spotify.env" (concatStrings (mapAttrsToList (name: value: "${name}=${value}\n") configEnv));
+in {
+  options.services.your_spotify = let
+    inherit (types) nullOr port str path package;
+  in {
+    enable = mkEnableOption "your_spotify";
+
+    enableLocalDB = mkEnableOption "a local mongodb instance";
+    nginxVirtualHost = mkOption {
+      type = nullOr str;
+      default = null;
+      description = ''
+        If set creates an nginx virtual host for the client.
+        In most cases this should be the CLIENT_ENDPOINT without
+        protocol prefix.
+      '';
+    };
+
+    package = mkPackageOption pkgs "your_spotify" {};
+
+    clientPackage = mkOption {
+      type = package;
+      description = "Client package to use.";
+    };
+
+    spotifySecretFile = mkOption {
+      type = path;
+      description = ''
+        A file containing the secret key of your Spotify application.
+        Refer to: [Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-spotify-application).
+      '';
+    };
+
+    settings = mkOption {
+      description = ''
+        Your Spotify Configuration. Refer to [Your Spotify](https://github.com/Yooooomi/your_spotify) for definitions and values.
+      '';
+      example = lib.literalExpression ''
+        {
+          CLIENT_ENDPOINT = "https://example.com";
+          API_ENDPOINT = "https://api.example.com";
+          SPOTIFY_PUBLIC = "spotify_client_id";
+        }
+      '';
+      type = types.submodule {
+        freeformType = types.attrsOf types.str;
+        options = {
+          CLIENT_ENDPOINT = mkOption {
+            type = str;
+            description = ''
+              The endpoint of your web application.
+              Has to include a protocol Prefix (e.g. `http://`)
+            '';
+            example = "https://your_spotify.example.org";
+          };
+          API_ENDPOINT = mkOption {
+            type = str;
+            description = ''
+              The endpoint of your server
+              This api has to be reachable from the device you use the website from not from the server.
+              This means that for example you may need two nginx virtual hosts if you want to expose this on the
+              internet.
+              Has to include a protocol Prefix (e.g. `http://`)
+            '';
+            example = "https://localhost:3000";
+          };
+          SPOTIFY_PUBLIC = mkOption {
+            type = str;
+            description = ''
+              The public client ID of your Spotify application.
+              Refer to: [Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-spotify-application)
+            '';
+          };
+          MONGO_ENDPOINT = mkOption {
+            type = str;
+            description = ''The endpoint of the Mongo database.'';
+            default = "mongodb://localhost:27017/your_spotify";
+          };
+          PORT = mkOption {
+            type = port;
+            description = "The port of the api server";
+            default = 3000;
+          };
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.your_spotify.clientPackage = mkDefault (cfg.package.client.override {apiEndpoint = cfg.settings.API_ENDPOINT;});
+    systemd.services.your_spotify = {
+      after = ["network.target"];
+      script = ''
+        export SPOTIFY_SECRET=$(< "$CREDENTIALS_DIRECTORY/SPOTIFY_SECRET")
+        ${lib.getExe' cfg.package "your_spotify_migrate"}
+        exec ${lib.getExe cfg.package}
+      '';
+      serviceConfig = {
+        User = "your_spotify";
+        Group = "your_spotify";
+        DynamicUser = true;
+        EnvironmentFile = [configFile];
+        StateDirectory = "your_spotify";
+        LimitNOFILE = "1048576";
+        PrivateTmp = true;
+        PrivateDevices = true;
+        StateDirectoryMode = "0700";
+        Restart = "always";
+
+        LoadCredential = ["SPOTIFY_SECRET:${cfg.spotifySecretFile}"];
+
+        # Hardening
+        CapabilityBoundingSet = "";
+        LockPersonality = true;
+        #MemoryDenyWriteExecute = true; # Leads to coredump because V8 does JIT
+        PrivateUsers = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProcSubset = "pid";
+        ProtectSystem = "strict";
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+          "AF_NETLINK"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "@pkey"
+        ];
+        UMask = "0077";
+      };
+      wantedBy = ["multi-user.target"];
+    };
+    services.nginx = mkIf (cfg.nginxVirtualHost != null) {
+      enable = true;
+      virtualHosts.${cfg.nginxVirtualHost} = {
+        root = cfg.clientPackage;
+        locations."/".extraConfig = ''
+          add_header Content-Security-Policy "frame-ancestors 'none';" ;
+          add_header X-Content-Type-Options "nosniff" ;
+          try_files = $uri $uri/ /index.html ;
+        '';
+      };
+    };
+    services.mongodb = mkIf cfg.enableLocalDB {
+      enable = true;
+    };
+  };
+  meta.maintainers = with lib.maintainers; [patrickdag];
+}
diff --git a/nixos/modules/services/web-apps/zitadel.nix b/nixos/modules/services/web-apps/zitadel.nix
index 99b0a0bc56f67..ed7fae8d9dda0 100644
--- a/nixos/modules/services/web-apps/zitadel.nix
+++ b/nixos/modules/services/web-apps/zitadel.nix
@@ -219,5 +219,5 @@ in
     users.groups.zitadel = lib.mkIf (cfg.group == "zitadel") { };
   };
 
-  meta.maintainers = with lib.maintainers; [ Sorixelle ];
+  meta.maintainers = [ ];
 }
diff --git a/nixos/modules/services/web-servers/bluemap.nix b/nixos/modules/services/web-servers/bluemap.nix
new file mode 100644
index 0000000000000..731468fd9a0ec
--- /dev/null
+++ b/nixos/modules/services/web-servers/bluemap.nix
@@ -0,0 +1,309 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.services.bluemap;
+  format = pkgs.formats.hocon { };
+
+  coreConfig = format.generate "core.conf" cfg.coreSettings;
+  webappConfig = format.generate "webapp.conf" cfg.webappSettings;
+  webserverConfig = format.generate "webserver.conf" cfg.webserverSettings;
+
+  mapsFolder = pkgs.linkFarm "maps"
+    (lib.attrsets.mapAttrs' (name: value:
+      lib.nameValuePair "${name}.conf"
+        (format.generate "${name}.conf" value))
+      cfg.maps);
+
+  storageFolder = pkgs.linkFarm "storage"
+    (lib.attrsets.mapAttrs' (name: value:
+      lib.nameValuePair "${name}.conf"
+        (format.generate "${name}.conf" value))
+      cfg.storage);
+
+  configFolder = pkgs.linkFarm "bluemap-config" {
+    "maps" = mapsFolder;
+    "storages" = storageFolder;
+    "core.conf" = coreConfig;
+    "webapp.conf" = webappConfig;
+    "webserver.conf" = webserverConfig;
+    "resourcepacks" = pkgs.linkFarm "resourcepacks" cfg.resourcepacks;
+  };
+
+  inherit (lib) mkOption;
+in {
+  options.services.bluemap = {
+    enable = lib.mkEnableOption "bluemap";
+
+    eula = mkOption {
+      type = lib.types.bool;
+      description = ''
+        By changing this option to true you confirm that you own a copy of minecraft Java Edition,
+        and that you agree to minecrafts EULA.
+      '';
+      default = false;
+    };
+
+    defaultWorld = mkOption {
+      type = lib.types.path;
+      description = ''
+        The world used by the default map ruleset.
+        If you configure your own maps you do not need to set this.
+      '';
+      example = lib.literalExpression "\${config.services.minecraft.dataDir}/world";
+    };
+
+    enableRender = mkOption {
+      type = lib.types.bool;
+      description = "Enable rendering";
+      default = true;
+    };
+
+    webRoot = mkOption {
+      type = lib.types.path;
+      default = "/var/lib/bluemap/web";
+      description = "The directory for saving and serving the webapp and the maps";
+    };
+
+    enableNginx = mkOption {
+      type = lib.types.bool;
+      default = true;
+      description = "Enable configuring a virtualHost for serving the bluemap webapp";
+    };
+
+    host = mkOption {
+      type = lib.types.str;
+      description = "Domain on which nginx will serve the bluemap webapp";
+    };
+
+    onCalendar = mkOption {
+      type = lib.types.str;
+      description = ''
+        How often to trigger rendering the map,
+        in the format of a systemd timer onCalendar configuration.
+        See {manpage}`systemd.timer(5)`.
+      '';
+      default = "*-*-* 03:10:00";
+    };
+
+    coreSettings = mkOption {
+      type = lib.types.submodule {
+        freeformType = format.type;
+        options = {
+          data = mkOption {
+            type = lib.types.path;
+            description = "Folder for where bluemap stores its data";
+            default = "/var/lib/bluemap";
+          };
+          metrics = lib.mkEnableOption "Sending usage metrics containing the version of bluemap in use";
+        };
+      };
+      description = "Settings for the core.conf file, [see upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/core.conf).";
+    };
+
+    webappSettings = mkOption {
+      type = lib.types.submodule {
+        freeformType = format.type;
+      };
+      default = {
+        enabled = true;
+        webroot = cfg.webRoot;
+      };
+      defaultText = lib.literalExpression ''
+        {
+          enabled = true;
+          webroot = config.services.bluemap.webRoot;
+        }
+      '';
+      description = "Settings for the webapp.conf file, see [upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/webapp.conf).";
+    };
+
+    webserverSettings = mkOption {
+      type = lib.types.submodule {
+        freeformType = format.type;
+        options = {
+          enabled = mkOption {
+            type = lib.types.bool;
+            description = ''
+              Enable bluemap's built-in webserver.
+              Disabled by default in nixos for use of nginx directly.
+            '';
+            default = false;
+          };
+        };
+      };
+      default = { };
+      description = ''
+        Settings for the webserver.conf file, usually not required.
+        [See upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/webserver.conf).
+      '';
+    };
+
+    maps = mkOption {
+      type = lib.types.attrsOf (lib.types.submodule {
+        freeformType = format.type;
+        options = {
+          world = lib.mkOption {
+            type = lib.types.path;
+            description = "Path to world folder containing the dimension to render";
+          };
+        };
+      });
+      default = {
+        "overworld" = {
+          world = "${cfg.defaultWorld}";
+          ambient-light = 0.1;
+          cave-detection-ocean-floor = -5;
+        };
+
+        "nether" = {
+          world = "${cfg.defaultWorld}/DIM-1";
+          sorting = 100;
+          sky-color = "#290000";
+          void-color = "#150000";
+          ambient-light = 0.6;
+          world-sky-light = 0;
+          remove-caves-below-y = -10000;
+          cave-detection-ocean-floor = -5;
+          cave-detection-uses-block-light = true;
+          max-y = 90;
+        };
+
+        "end" = {
+          world = "${cfg.defaultWorld}/DIM1";
+          sorting = 200;
+          sky-color = "#080010";
+          void-color = "#080010";
+          ambient-light = 0.6;
+          world-sky-light = 0;
+          remove-caves-below-y = -10000;
+          cave-detection-ocean-floor = -5;
+        };
+      };
+      defaultText = lib.literalExpression ''
+        {
+          "overworld" = {
+            world = "''${cfg.defaultWorld}";
+            ambient-light = 0.1;
+            cave-detection-ocean-floor = -5;
+          };
+
+          "nether" = {
+            world = "''${cfg.defaultWorld}/DIM-1";
+            sorting = 100;
+            sky-color = "#290000";
+            void-color = "#150000";
+            ambient-light = 0.6;
+            world-sky-light = 0;
+            remove-caves-below-y = -10000;
+            cave-detection-ocean-floor = -5;
+            cave-detection-uses-block-light = true;
+            max-y = 90;
+          };
+
+          "end" = {
+            world = "''${cfg.defaultWorld}/DIM1";
+            sorting = 200;
+            sky-color = "#080010";
+            void-color = "#080010";
+            ambient-light = 0.6;
+            world-sky-light = 0;
+            remove-caves-below-y = -10000;
+            cave-detection-ocean-floor = -5;
+          };
+        };
+      '';
+      description = ''
+        Settings for files in `maps/`.
+        If you define anything here you must define everything yourself.
+        See the default for an example with good options for the different world types.
+        For valid values [consult upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/maps/map.conf).
+      '';
+    };
+
+    storage = mkOption {
+      type = lib.types.attrsOf (lib.types.submodule {
+        freeformType = format.type;
+        options = {
+          storage-type = mkOption {
+            type = lib.types.enum [ "FILE" "SQL" ];
+            description = "Type of storage config";
+            default = "FILE";
+          };
+        };
+      });
+      description = ''
+        Where the rendered map will be stored.
+        Unless you are doing something advanced you should probably leave this alone and configure webRoot instead.
+        [See upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/tree/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/storages)
+      '';
+      default = {
+        "file" = {
+          root = "${cfg.webRoot}/maps";
+        };
+      };
+      defaultText = lib.literalExpression ''
+        {
+          "file" = {
+            root = "''${config.services.bluemap.webRoot}/maps";
+          };
+        }
+      '';
+    };
+
+    resourcepacks = mkOption {
+      type = lib.types.attrsOf lib.types.pathInStore;
+      default = { };
+      description = "A set of resourcepacks to use, loaded in alphabetical order";
+    };
+  };
+
+
+  config = lib.mkIf cfg.enable {
+    assertions =
+      [ { assertion = config.services.bluemap.eula;
+          message = ''
+            You have enabled bluemap but have not accepted minecraft's EULA.
+            You can achieve this through setting `services.bluemap.eula = true`
+          '';
+        }
+      ];
+
+    services.bluemap.coreSettings.accept-download = cfg.eula;
+
+    systemd.services."render-bluemap-maps" = lib.mkIf cfg.enableRender {
+      serviceConfig = {
+        Type = "oneshot";
+        Group = "nginx";
+        UMask = "026";
+      };
+      script = ''
+        ${lib.getExe pkgs.bluemap} -c ${configFolder} -gs -r
+      '';
+    };
+
+    systemd.timers."render-bluemap-maps" = lib.mkIf cfg.enableRender {
+      wantedBy = [ "timers.target" ];
+      timerConfig = {
+        OnCalendar = cfg.onCalendar;
+        Persistent = true;
+        Unit = "render-bluemap-maps.service";
+      };
+    };
+
+    services.nginx.virtualHosts = lib.mkIf cfg.enableNginx {
+      "${cfg.host}" = {
+        root = config.services.bluemap.webRoot;
+        locations = {
+          "~* ^/maps/[^/]*/tiles/[^/]*.json$".extraConfig = ''
+            error_page 404 =200 /assets/emptyTile.json;
+            gzip_static always;
+          '';
+          "~* ^/maps/[^/]*/tiles/[^/]*.png$".tryFiles = "$uri =204";
+        };
+      };
+    };
+  };
+
+  meta = {
+    maintainers = with lib.maintainers; [ dandellion h7x4 ];
+  };
+}
diff --git a/nixos/modules/services/web-servers/caddy/default.nix b/nixos/modules/services/web-servers/caddy/default.nix
index 08ce50bff62c0..064a0c71b586b 100644
--- a/nixos/modules/services/web-servers/caddy/default.nix
+++ b/nixos/modules/services/web-servers/caddy/default.nix
@@ -360,14 +360,15 @@ in
       serviceConfig = let
         runOptions = ''--config ${configPath} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"}'';
       in {
+        # Override the `ExecStart` line from upstream's systemd unit file by our own:
         # https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
         # If the empty string is assigned to this option, the list of commands to start is reset, prior assignments of this option will have no effect.
         ExecStart = [ "" ''${cfg.package}/bin/caddy run ${runOptions} ${optionalString cfg.resume "--resume"}'' ];
         # Validating the configuration before applying it ensures we’ll get a proper error that will be reported when switching to the configuration
-        ExecReload = [ "" ''${cfg.package}/bin/caddy reload ${runOptions} --force'' ];
+        ExecReload = [ "" ] ++ lib.optional cfg.enableReload "${lib.getExe cfg.package} reload ${runOptions} --force";
         User = cfg.user;
         Group = cfg.group;
-        ReadWriteDirectories = cfg.dataDir;
+        ReadWritePaths = [ cfg.dataDir ];
         StateDirectory = mkIf (cfg.dataDir == "/var/lib/caddy") [ "caddy" ];
         LogsDirectory = mkIf (cfg.logDir == "/var/log/caddy") [ "caddy" ];
         Restart = "on-failure";
diff --git a/nixos/modules/services/web-servers/garage.nix b/nixos/modules/services/web-servers/garage.nix
index 39ea8f21b126f..7cf71ff6ff06f 100644
--- a/nixos/modules/services/web-servers/garage.nix
+++ b/nixos/modules/services/web-servers/garage.nix
@@ -10,7 +10,7 @@ in
 {
   meta = {
     doc = ./garage.md;
-    maintainers = with pkgs.lib.maintainers; [ raitobezarius ];
+    maintainers = [ ];
   };
 
   options.services.garage = {
@@ -49,15 +49,15 @@ in
 
           data_dir = mkOption {
             default = "/var/lib/garage/data";
-            type = types.path;
-            description = "The main data storage, put this on your large storage (e.g. high capacity HDD)";
-          };
-
-          replication_mode = mkOption {
-            default = "none";
-            type = types.enum ([ "none" "1" "2" "3" "2-dangerous" "3-dangerous" "3-degraded" 1 2 3 ]);
-            apply = v: toString v;
-            description = "Garage replication mode, defaults to none, see: <https://garagehq.deuxfleurs.fr/documentation/reference-manual/configuration/#replication-mode> for reference.";
+            example = [ {
+              path = "/var/lib/garage/data";
+              capacity = "2T";
+            } ];
+            type = with types; either path (listOf attrs);
+            description = ''
+              The directory in which Garage will store the data blocks of objects. This folder can be placed on an HDD.
+              Since v0.9.0, Garage supports multiple data directories, refer to https://garagehq.deuxfleurs.fr/documentation/reference-manual/configuration/#data_dir for the exact format.
+            '';
           };
         };
       };
@@ -71,6 +71,44 @@ in
   };
 
   config = mkIf cfg.enable {
+
+    assertions = [
+      # We removed our module-level default for replication_mode. If a user upgraded
+      # to garage 1.0.0 while relying on the module-level default, they would be left
+      # with a config which evaluates and builds, but then garage refuses to start
+      # because either replication_factor or replication_mode is required.
+      # The replication_factor option also was `toString`'ed before, which is
+      # now not possible anymore, so we prompt the user to change it to a string
+      # if present.
+      # These assertions can be removed in NixOS 24.11, when all users have been
+      # warned once.
+      {
+        assertion = (cfg.settings ? replication_factor || cfg.settings ? replication_mode) || lib.versionOlder cfg.package.version "1.0.0";
+        message = ''
+          Garage 1.0.0 requires an explicit replication factor to be set.
+          Please set replication_factor to 1 explicitly to preserve the previous behavior.
+          https://git.deuxfleurs.fr/Deuxfleurs/garage/src/tag/v1.0.0/doc/book/reference-manual/configuration.md#replication_factor
+
+        '';
+      }
+      {
+        assertion = lib.isString (cfg.settings.replication_mode or "");
+        message = ''
+          The explicit `replication_mode` option in `services.garage.settings`
+          has been removed and is now handled by the freeform settings in order
+          to allow it being completely absent (for Garage 1.x).
+          That module option previously `toString`'ed the value it's configured
+          with, which is now no longer possible.
+
+          You're still using a non-string here, please manually set it to
+          a string, or migrate to the separate setting keys introduced in 1.x.
+
+          Refer to https://garagehq.deuxfleurs.fr/documentation/working-documents/migration-1/
+          for the migration guide.
+        '';
+      }
+    ];
+
     environment.etc."garage.toml" = {
       source = configFile;
     };
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 08fab09e1e559..f9720c3629353 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -142,7 +142,11 @@ let
       default_type application/octet-stream;
   '';
 
-  configFile = pkgs.writers.writeNginxConfig "nginx.conf" ''
+  configFile = (
+      if cfg.validateConfigFile
+      then pkgs.writers.writeNginxConfig
+      else pkgs.writeText
+    ) "nginx.conf" ''
     pid /run/nginx/nginx.pid;
     error_log ${cfg.logError};
     daemon off;
@@ -352,7 +356,7 @@ let
 
         # The acme-challenge location doesn't need to be added if we are not using any automated
         # certificate provisioning and can also be omitted when we use a certificate obtained via a DNS-01 challenge
-        acmeName = if vhost.useACMEHost != null then vhost.useACMEHost else vhostName;
+        acmeName = if vhost.useACMEHost != null then vhost.useACMEHost else vhost.serverName;
         acmeLocation = optionalString ((vhost.enableACME || vhost.useACMEHost != null) && config.security.acme.certs.${acmeName}.dnsProvider == null)
           # Rule for legitimate ACME Challenge requests (like /.well-known/acme-challenge/xxxxxxxxx)
           # We use ^~ here, so that we don't check any regexes (which could
@@ -1082,6 +1086,9 @@ in
         '';
         description = "Declarative vhost config";
       };
+      validateConfigFile = lib.mkEnableOption ''
+        Validate configuration with pkgs.writeNginxConfig.
+      '' // { default = true; };
     };
   };
 
diff --git a/nixos/modules/services/web-servers/nginx/tailscale-auth.nix b/nixos/modules/services/web-servers/nginx/tailscale-auth.nix
index ca272268f5724..de1d708cbb423 100644
--- a/nixos/modules/services/web-servers/nginx/tailscale-auth.nix
+++ b/nixos/modules/services/web-servers/nginx/tailscale-auth.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, ... }:
+{ config, lib, ... }:
 
 let
   inherit (lib)
@@ -22,7 +22,7 @@ in
   ];
 
   options.services.nginx.tailscaleAuth = {
-    enable = mkEnableOption "Enable tailscale.nginx-auth, to authenticate nginx users via tailscale.";
+    enable = mkEnableOption "tailscale.nginx-auth, to authenticate nginx users via tailscale";
 
     expectedTailnet = mkOption {
       default = "";
diff --git a/nixos/modules/services/web-servers/traefik.nix b/nixos/modules/services/web-servers/traefik.nix
index 9c53455bcf3d7..1a65ce21112ee 100644
--- a/nixos/modules/services/web-servers/traefik.nix
+++ b/nixos/modules/services/web-servers/traefik.nix
@@ -170,7 +170,7 @@ in {
         PrivateDevices = true;
         ProtectHome = true;
         ProtectSystem = "full";
-        ReadWriteDirectories = cfg.dataDir;
+        ReadWritePaths = [ cfg.dataDir ];
         RuntimeDirectory = "traefik";
       };
     };
diff --git a/nixos/modules/services/x11/desktop-managers/mate.nix b/nixos/modules/services/x11/desktop-managers/mate.nix
index beae07b70dbfe..19235be4aa8d5 100644
--- a/nixos/modules/services/x11/desktop-managers/mate.nix
+++ b/nixos/modules/services/x11/desktop-managers/mate.nix
@@ -84,6 +84,7 @@ in
       programs.system-config-printer.enable = (mkIf config.services.printing.enable (mkDefault true));
 
       services.gnome.at-spi2-core.enable = true;
+      services.gnome.glib-networking.enable = true;
       services.gnome.gnome-keyring.enable = true;
       services.udev.packages = [ pkgs.mate.mate-settings-daemon ];
       services.gvfs.enable = true;
diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixos/modules/services/x11/desktop-managers/pantheon.nix
index 008bc65eb6a4f..0e9a06706d4f6 100644
--- a/nixos/modules/services/x11/desktop-managers/pantheon.nix
+++ b/nixos/modules/services/x11/desktop-managers/pantheon.nix
@@ -269,11 +269,6 @@ in
       programs.bash.vteIntegration = mkDefault true;
       programs.zsh.vteIntegration = mkDefault true;
 
-      # Use native GTK file chooser on Qt apps. This is because Qt does not know Pantheon.
-      # https://invent.kde.org/qt/qt/qtbase/-/blob/6.6/src/gui/platform/unix/qgenericunixthemes.cpp#L1312
-      # https://github.com/elementary/default-settings/blob/7.0.2/profile.d/qt-qpa-platformtheme.sh
-      environment.variables.QT_QPA_PLATFORMTHEME = mkDefault "gtk3";
-
       # Default Fonts
       fonts.packages = with pkgs; [
         inter
diff --git a/nixos/modules/services/x11/desktop-managers/phosh.nix b/nixos/modules/services/x11/desktop-managers/phosh.nix
index e8494b2c017c9..12b39f927c012 100644
--- a/nixos/modules/services/x11/desktop-managers/phosh.nix
+++ b/nixos/modules/services/x11/desktop-managers/phosh.nix
@@ -216,7 +216,7 @@ in
 
     security.pam.services.phosh = {};
 
-    hardware.opengl.enable = mkDefault true;
+    hardware.graphics.enable = mkDefault true;
 
     services.gnome.core-shell.enable = true;
     services.gnome.core-os-services.enable = true;
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index 7d80b9b2641c3..53d3b91bfa17c 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -327,7 +327,7 @@ in
       };
 
       # Enable GTK applications to load SVG icons
-      services.xserver.gdk-pixbuf.modulePackages = [ pkgs.librsvg ];
+      programs.gdk-pixbuf.modulePackages = [ pkgs.librsvg ];
 
       fonts.packages = with pkgs; [ cfg.notoPackage hack-font ];
       fonts.fontconfig.defaultFonts = {
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 85d0d199de3f2..69a83ecb72065 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -116,7 +116,7 @@ in
     ] # TODO: NetworkManager doesn't belong here
       ++ optional config.networking.networkmanager.enable networkmanagerapplet
       ++ optional config.powerManagement.enable xfce4-power-manager
-      ++ optionals config.hardware.pulseaudio.enable [
+      ++ optionals (config.hardware.pulseaudio.enable || config.services.pipewire.pulse.enable) [
         pavucontrol
         # volume up/down keys support:
         # xfce4-pulseaudio-plugin includes all the functionalities of xfce4-volumed-pulse
@@ -153,7 +153,7 @@ in
     }];
 
     services.xserver.updateDbusEnvironment = true;
-    services.xserver.gdk-pixbuf.modulePackages = [ pkgs.librsvg ];
+    programs.gdk-pixbuf.modulePackages = [ pkgs.librsvg ];
 
     # Enable helpful DBus services.
     services.udisks2.enable = true;
diff --git a/nixos/modules/services/x11/window-managers/clfswm.nix b/nixos/modules/services/x11/window-managers/clfswm.nix
index 5500c77a038be..09b49962f2ad7 100644
--- a/nixos/modules/services/x11/window-managers/clfswm.nix
+++ b/nixos/modules/services/x11/window-managers/clfswm.nix
@@ -10,7 +10,7 @@ in
   options = {
     services.xserver.windowManager.clfswm = {
       enable = mkEnableOption "clfswm";
-      package = mkPackageOption pkgs [ "lispPackages" "clfswm" ] { };
+      package = mkPackageOption pkgs [ "sbclPackages" "clfswm" ] { };
     };
   };
 
diff --git a/nixos/modules/services/x11/window-managers/qtile.nix b/nixos/modules/services/x11/window-managers/qtile.nix
index 78152283a0a58..4603ca3fb50f0 100644
--- a/nixos/modules/services/x11/window-managers/qtile.nix
+++ b/nixos/modules/services/x11/window-managers/qtile.nix
@@ -4,10 +4,13 @@ with lib;
 
 let
   cfg = config.services.xserver.windowManager.qtile;
-  pyEnv = pkgs.python3.withPackages (p: [ (cfg.package.unwrapped or cfg.package) ] ++ (cfg.extraPackages p));
 in
 
 {
+  imports = [
+    (mkRemovedOptionModule [ "services" "xserver" "windowManager" "qtile" "backend" ] "The qtile package now provides separate display sessions for both X11 and Wayland.")
+  ];
+
   options.services.xserver.windowManager.qtile = {
     enable = mkEnableOption "qtile";
 
@@ -23,14 +26,6 @@ in
       '';
     };
 
-    backend = mkOption {
-      type = types.enum [ "x11" "wayland" ];
-      default = "x11";
-      description = ''
-          Backend to use in qtile: `x11` or `wayland`.
-      '';
-    };
-
     extraPackages = mkOption {
         type = types.functionTo (types.listOf types.package);
         default = _: [];
@@ -48,24 +43,24 @@ in
           ];
         '';
       };
+
+    finalPackage = mkOption {
+      type = types.package;
+      visible = false;
+      readOnly = true;
+      description = "The resulting Qtile package, bundled with extra packages";
+    };
   };
 
   config = mkIf cfg.enable {
-    services.xserver.windowManager.session = [{
-      name = "qtile";
-      start = ''
-        ${pyEnv}/bin/qtile start -b ${cfg.backend} \
-        ${optionalString (cfg.configFile != null)
-        "--config \"${cfg.configFile}\""} &
-        waitPID=$!
-      '';
-    }];
+    services = {
+      xserver.windowManager.qtile.finalPackage = pkgs.python3.pkgs.qtile.override { extraPackages = cfg.extraPackages pkgs.python3.pkgs; };
+      displayManager.sessionPackages = [ cfg.finalPackage ];
+    };
 
-    environment.systemPackages = [
-      # pkgs.qtile is currently a buildenv of qtile and its dependencies.
-      # For userland commands, we want the underlying package so that
-      # packages such as python don't bleed into userland and overwrite intended behavior.
-      (cfg.package.unwrapped or cfg.package)
-    ];
+    environment = {
+      etc."xdg/qtile/config.py" = mkIf (cfg.configFile != null) { source = cfg.configFile; };
+      systemPackages = [ cfg.finalPackage ];
+    };
   };
 }
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index e13c273746701..57e83399eded6 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -302,7 +302,7 @@ in
         default = [ "modesetting" "fbdev" ];
         example = [
           "nvidia"
-          "amdgpu-pro"
+          "amdgpu"
         ];
         # TODO(@oxij): think how to easily add the rest, like those nvidia things
         relatedPackages = concatLists
@@ -716,10 +716,7 @@ in
 
         restartIfChanged = false;
 
-        environment =
-          optionalAttrs config.hardware.opengl.setLdLibraryPath
-            { LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.addOpenGLRunpath.driverLink ]; }
-          // config.services.displayManager.environment;
+        environment = config.services.displayManager.environment;
 
         preStart =
           ''
@@ -728,9 +725,6 @@ in
             rm -f /tmp/.X0-lock
           '';
 
-        # TODO: move declaring the systemd service to its own mkIf
-        script = mkIf (config.systemd.services.display-manager.enable == true) "${config.services.displayManager.execCmd}";
-
         # Stop restarting if the display manager stops (crashes) 2 times
         # in one minute. Starting X typically takes 3-4s.
         startLimitIntervalSec = 30;
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index ba45231465fb4..cabc1dcc2d65a 100755
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -472,6 +472,9 @@ sub handle_modified_unit { ## no critic(Subroutines::ProhibitManyArgs, Subroutin
             $units_to_reload->{$unit} = 1;
             record_unit($reload_list_file, $unit);
         }
+        elsif ($unit eq "dbus.service" || $unit eq "dbus-broker.service") {
+            # dbus service should only ever be reloaded, not started/stoped/restarted as that would break the system.
+        }
         elsif (!parse_systemd_bool(\%new_unit_info, "Service", "X-RestartIfChanged", 1) || parse_systemd_bool(\%new_unit_info, "Unit", "RefuseManualStop", 0) || parse_systemd_bool(\%new_unit_info, "Unit", "X-OnlyManualStart", 0)) {
             $units_to_skip->{$unit} = 1;
         } else {
diff --git a/nixos/modules/system/activation/switchable-system.nix b/nixos/modules/system/activation/switchable-system.nix
index d5bd8cc1dc115..d70fefd0920b4 100644
--- a/nixos/modules/system/activation/switchable-system.nix
+++ b/nixos/modules/system/activation/switchable-system.nix
@@ -4,52 +4,93 @@ let
 
   perlWrapped = pkgs.perl.withPackages (p: with p; [ ConfigIniFiles FileSlurp ]);
 
+  description = extra: ''
+    Whether to include the capability to switch configurations.
+
+    Disabling this makes the system unable to be reconfigured via `nixos-rebuild`.
+
+    ${extra}
+  '';
+
 in
 
 {
 
-  options = {
-    system.switch.enable = lib.mkOption {
+  options.system.switch = {
+    enable = lib.mkOption {
       type = lib.types.bool;
       default = true;
-      description = ''
-        Whether to include the capability to switch configurations.
-
-        Disabling this makes the system unable to be reconfigured via `nixos-rebuild`.
-
+      description = description ''
         This is good for image based appliances where updates are handled
         outside the image. Reducing features makes the image lighter and
         slightly more secure.
       '';
     };
-  };
 
-  config = lib.mkIf config.system.switch.enable {
-    system.activatableSystemBuilderCommands = ''
-      mkdir $out/bin
-      substitute ${./switch-to-configuration.pl} $out/bin/switch-to-configuration \
-        --subst-var out \
-        --subst-var-by toplevel ''${!toplevelVar} \
-        --subst-var-by coreutils "${pkgs.coreutils}" \
-        --subst-var-by distroId ${lib.escapeShellArg config.system.nixos.distroId} \
-        --subst-var-by installBootLoader ${lib.escapeShellArg config.system.build.installBootLoader} \
-        --subst-var-by localeArchive "${config.i18n.glibcLocales}/lib/locale/locale-archive" \
-        --subst-var-by perl "${perlWrapped}" \
-        --subst-var-by shell "${pkgs.bash}/bin/sh" \
-        --subst-var-by su "${pkgs.shadow.su}/bin/su" \
-        --subst-var-by systemd "${config.systemd.package}" \
-        --subst-var-by utillinux "${pkgs.util-linux}" \
-        ;
-
-      chmod +x $out/bin/switch-to-configuration
-      ${lib.optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
-        if ! output=$(${perlWrapped}/bin/perl -c $out/bin/switch-to-configuration 2>&1); then
-          echo "switch-to-configuration syntax is not valid:"
-          echo "$output"
-          exit 1
-        fi
-      ''}
-    '';
+    enableNg = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = description ''
+        Whether to use `switch-to-configuration-ng`, an experimental
+        re-implementation of `switch-to-configuration` with the goal of
+        replacing the original.
+      '';
+    };
   };
 
+  config = lib.mkMerge [
+    {
+      assertions = [{
+        assertion = with config.system.switch; enable -> !enableNg;
+        message = "Only one of system.switch.enable and system.switch.enableNg may be enabled at a time";
+      }];
+    }
+    (lib.mkIf config.system.switch.enable {
+      system.activatableSystemBuilderCommands = ''
+        mkdir $out/bin
+        substitute ${./switch-to-configuration.pl} $out/bin/switch-to-configuration \
+          --subst-var out \
+          --subst-var-by toplevel ''${!toplevelVar} \
+          --subst-var-by coreutils "${pkgs.coreutils}" \
+          --subst-var-by distroId ${lib.escapeShellArg config.system.nixos.distroId} \
+          --subst-var-by installBootLoader ${lib.escapeShellArg config.system.build.installBootLoader} \
+          --subst-var-by localeArchive "${config.i18n.glibcLocales}/lib/locale/locale-archive" \
+          --subst-var-by perl "${perlWrapped}" \
+          --subst-var-by shell "${pkgs.bash}/bin/sh" \
+          --subst-var-by su "${pkgs.shadow.su}/bin/su" \
+          --subst-var-by systemd "${config.systemd.package}" \
+          --subst-var-by utillinux "${pkgs.util-linux}" \
+          ;
+
+        chmod +x $out/bin/switch-to-configuration
+        ${lib.optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
+          if ! output=$(${perlWrapped}/bin/perl -c $out/bin/switch-to-configuration 2>&1); then
+            echo "switch-to-configuration syntax is not valid:"
+            echo "$output"
+            exit 1
+          fi
+        ''}
+      '';
+    })
+    (lib.mkIf config.system.switch.enableNg {
+      # Use a subshell so we can source makeWrapper's setup hook without
+      # affecting the rest of activatableSystemBuilderCommands.
+      system.activatableSystemBuilderCommands = ''
+        (
+          source ${pkgs.buildPackages.makeWrapper}/nix-support/setup-hook
+
+          mkdir $out/bin
+          ln -sf ${lib.getExe pkgs.switch-to-configuration-ng} $out/bin/switch-to-configuration
+          wrapProgram $out/bin/switch-to-configuration \
+            --set OUT $out \
+            --set TOPLEVEL ''${!toplevelVar} \
+            --set DISTRO_ID ${lib.escapeShellArg config.system.nixos.distroId} \
+            --set INSTALL_BOOTLOADER ${lib.escapeShellArg config.system.build.installBootLoader} \
+            --set LOCALE_ARCHIVE ${config.i18n.glibcLocales}/lib/locale/locale-archive \
+            --set SYSTEMD ${config.systemd.package}
+        )
+      '';
+    })
+  ];
+
 }
diff --git a/nixos/modules/system/activation/test.nix b/nixos/modules/system/activation/test.nix
index 8cf000451c6e3..fd251d5289579 100644
--- a/nixos/modules/system/activation/test.nix
+++ b/nixos/modules/system/activation/test.nix
@@ -5,7 +5,7 @@
 }:
 let
   node-forbiddenDependencies-fail = nixos ({ ... }: {
-    system.forbiddenDependenciesRegex = "-dev$";
+    system.forbiddenDependenciesRegexes = ["-dev$"];
     environment.etc."dev-dependency" = {
       text = "${expect.dev}";
     };
@@ -14,7 +14,7 @@ let
     boot.loader.grub.enable = false;
   });
   node-forbiddenDependencies-succeed = nixos ({ ... }: {
-    system.forbiddenDependenciesRegex = "-dev$";
+    system.forbiddenDependenciesRegexes = ["-dev$"];
     system.extraDependencies = [ expect.dev ];
     documentation.enable = false;
     fileSystems."/".device = "ignore-root-device";
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 4cf3012646fa3..ed0ece19f2fa2 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -86,6 +86,7 @@ in
     ../build.nix
     (mkRemovedOptionModule [ "nesting" "clone" ] "Use `specialisation.«name» = { inheritParentConfig = true; configuration = { ... }; }` instead.")
     (mkRemovedOptionModule [ "nesting" "children" ] "Use `specialisation.«name».configuration = { ... }` instead.")
+    (mkRenamedOptionModule [ "system" "forbiddenDependenciesRegex" ] [ "system" "forbiddenDependenciesRegexes" ])
   ];
 
   options = {
@@ -160,12 +161,12 @@ in
       '';
     };
 
-    system.forbiddenDependenciesRegex = mkOption {
-      default = "";
-      example = "-dev$";
-      type = types.str;
+    system.forbiddenDependenciesRegexes = mkOption {
+      default = [];
+      example = ["-dev$"];
+      type = types.listOf types.str;
       description = ''
-        A POSIX Extended Regular Expression that matches store paths that
+        POSIX Extended Regular Expressions that match store paths that
         should not appear in the system closure, with the exception of {option}`system.extraDependencies`, which is not checked.
       '';
     };
@@ -289,15 +290,14 @@ in
             "$out/configuration.nix"
         '' +
       optionalString
-        (config.system.forbiddenDependenciesRegex != "")
-        ''
-          if [[ $forbiddenDependenciesRegex != "" && -n $closureInfo ]]; then
-            if forbiddenPaths="$(grep -E -- "$forbiddenDependenciesRegex" $closureInfo/store-paths)"; then
+        (config.system.forbiddenDependenciesRegexes != []) (lib.concatStringsSep "\n" (map (regex: ''
+          if [[ ${regex} != "" && -n $closureInfo ]]; then
+            if forbiddenPaths="$(grep -E -- "${regex}" $closureInfo/store-paths)"; then
               echo -e "System closure $out contains the following disallowed paths:\n$forbiddenPaths"
               exit 1
             fi
           fi
-        '';
+        '') config.system.forbiddenDependenciesRegexes));
 
     system.systemBuilderArgs = {
 
@@ -319,8 +319,7 @@ in
       # option, as opposed to `system.extraDependencies`.
       passedChecks = concatStringsSep " " config.system.checks;
     }
-    // lib.optionalAttrs (config.system.forbiddenDependenciesRegex != "") {
-      inherit (config.system) forbiddenDependenciesRegex;
+    // lib.optionalAttrs (config.system.forbiddenDependenciesRegexes != []) {
       closureInfo = pkgs.closureInfo { rootPaths = [
         # override to avoid  infinite recursion (and to allow using extraDependencies to add forbidden dependencies)
         (config.system.build.toplevel.overrideAttrs (_: { extraDependencies = []; closureInfo = null; }))
diff --git a/nixos/modules/system/boot/binfmt.nix b/nixos/modules/system/boot/binfmt.nix
index 3605ce56910ed..1d702442f7f66 100644
--- a/nixos/modules/system/boot/binfmt.nix
+++ b/nixos/modules/system/boot/binfmt.nix
@@ -280,7 +280,7 @@ in {
   };
 
   config = {
-    boot.binfmt.registrations = builtins.listToAttrs (map (system: {
+    boot.binfmt.registrations = builtins.listToAttrs (map (system: assert system != pkgs.stdenv.hostPlatform.system; {
       name = system;
       value = { config, ... }: let
         interpreter = getEmulator system;
diff --git a/nixos/modules/system/boot/initrd-ssh.nix b/nixos/modules/system/boot/initrd-ssh.nix
index 9ce5a85b4f073..d1cd601c2d9b1 100644
--- a/nixos/modules/system/boot/initrd-ssh.nix
+++ b/nixos/modules/system/boot/initrd-ssh.nix
@@ -82,7 +82,7 @@ in
       type = types.bool;
       default = false;
       description = ''
-        Allow leaving {option}`config.boot.initrd.network.ssh` empty,
+        Allow leaving {option}`config.boot.initrd.network.ssh.hostKeys` empty,
         to deploy ssh host keys out of band.
       '';
     };
diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
index 03bff1dee5b9d..694d34d1c059a 100644
--- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
+++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
@@ -196,8 +196,7 @@ def get_generations(profile: str | None = None) -> list[SystemIdentifier]:
         f"{NIX}/bin/nix-env",
         "--list-generations",
         "-p",
-        "/nix/var/nix/profiles/%s" % ("system-profiles/" + profile if profile else "system"),
-        "--option", "build-users-group", ""],
+        "/nix/var/nix/profiles/%s" % ("system-profiles/" + profile if profile else "system")],
         universal_newlines=True)
     gen_lines = gen_list.split('\n')
     gen_lines.pop()
diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
index cee8663f0040e..e73048dc2ecbe 100644
--- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
+++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
@@ -323,15 +323,15 @@ in {
     assertions = [
       {
         assertion = (hasPrefix "/" efi.efiSysMountPoint);
-        message = "The ESP mount point '${efi.efiSysMountPoint}' must be an absolute path";
+        message = "The ESP mount point '${toString efi.efiSysMountPoint}' must be an absolute path";
       }
       {
         assertion = cfg.xbootldrMountPoint == null || (hasPrefix "/" cfg.xbootldrMountPoint);
-        message = "The XBOOTLDR mount point '${cfg.xbootldrMountPoint}' must be an absolute path";
+        message = "The XBOOTLDR mount point '${toString cfg.xbootldrMountPoint}' must be an absolute path";
       }
       {
         assertion = cfg.xbootldrMountPoint != efi.efiSysMountPoint;
-        message = "The XBOOTLDR mount point '${cfg.xbootldrMountPoint}' cannot be the same as the ESP mount point '${efi.efiSysMountPoint}'";
+        message = "The XBOOTLDR mount point '${toString cfg.xbootldrMountPoint}' cannot be the same as the ESP mount point '${toString efi.efiSysMountPoint}'";
       }
       {
         assertion = (config.boot.kernelPackages.kernel.features or { efiBootStub = true; }) ? efiBootStub;
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index bb899c8d89994..761bbe6e03d4a 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -17,11 +17,13 @@ let
           "ManageForeignRoutingPolicyRules"
           "ManageForeignRoutes"
           "RouteTable"
+          "IPv6PrivacyExtensions"
         ])
         (assertValueOneOf "SpeedMeter" boolValues)
         (assertInt "SpeedMeterIntervalSec")
         (assertValueOneOf "ManageForeignRoutingPolicyRules" boolValues)
         (assertValueOneOf "ManageForeignRoutes" boolValues)
+        (assertValueOneOf "IPv6PrivacyExtensions" (boolValues ++ ["prefer-public" "kernel"]))
       ];
 
       sectionDHCPv4 = checkUnitConfig "DHCPv4" [
@@ -384,7 +386,7 @@ let
         (assertValueOneOf "UDP6ZeroChecksumRx" boolValues)
       ];
 
-      sectionL2TPSession = checkUnitConfig "L2TPSession" [
+      sectionL2TPSession = checkUnitConfigWithLegacyKey "l2tpSessionConfig" "L2TPSession" [
         (assertOnlyFields [
           "Name"
           "SessionId"
@@ -419,7 +421,7 @@ let
       # NOTE The PresharedKey directive is missing on purpose here, please
       # do not add it to this list. The nix store is world-readable,let's
       # refrain ourselves from providing a footgun.
-      sectionWireGuardPeer = checkUnitConfig "WireGuardPeer" [
+      sectionWireGuardPeer = checkUnitConfigWithLegacyKey "wireguardPeerConfig" "WireGuardPeer" [
         (assertOnlyFields [
           "PublicKey"
           "PresharedKeyFile"
@@ -710,7 +712,7 @@ let
         (assertValueOneOf "KeepConfiguration" (boolValues ++ ["static" "dhcp-on-stop" "dhcp"]))
       ];
 
-      sectionAddress = checkUnitConfig "Address" [
+      sectionAddress = checkUnitConfigWithLegacyKey "addressConfig" "Address" [
         (assertOnlyFields [
           "Address"
           "Peer"
@@ -735,7 +737,7 @@ let
         (assertValueOneOf "AutoJoin" boolValues)
       ];
 
-      sectionRoutingPolicyRule = checkUnitConfig "RoutingPolicyRule" [
+      sectionRoutingPolicyRule = checkUnitConfigWithLegacyKey "routingPolicyRuleConfig" "RoutingPolicyRule" [
         (assertOnlyFields [
           "TypeOfService"
           "From"
@@ -770,7 +772,7 @@ let
         (assertRange "SuppressInterfaceGroup" 0 2147483647)
       ];
 
-      sectionRoute = checkUnitConfig "Route" [
+      sectionRoute = checkUnitConfigWithLegacyKey "routeConfig" "Route" [
         (assertOnlyFields [
           "Gateway"
           "GatewayOnLink"
@@ -950,6 +952,7 @@ let
           "UseGateway"
           "UseRoutePrefix"
           "Token"
+          "UsePREF64"
         ])
         (assertValueOneOf "UseDNS" boolValues)
         (assertValueOneOf "UseDomains" (boolValues ++ ["route"]))
@@ -960,6 +963,7 @@ let
         (assertValueOneOf "UseMTU" boolValues)
         (assertValueOneOf "UseGateway" boolValues)
         (assertValueOneOf "UseRoutePrefix" boolValues)
+        (assertValueOneOf "UsePREF64" boolValues)
       ];
 
       sectionDHCPServer = checkUnitConfig "DHCPServer" [
@@ -1031,7 +1035,15 @@ let
         (assertValueOneOf "EmitDomains" boolValues)
       ];
 
-      sectionIPv6Prefix = checkUnitConfig "IPv6Prefix" [
+      sectionIPv6PREF64Prefix = checkUnitConfigWithLegacyKey "ipv6PREF64PrefixConfig" "IPv6PREF64Prefix" [
+        (assertOnlyFields [
+          "Prefix"
+          "LifetimeSec"
+        ])
+        (assertInt "LifetimeSec")
+      ];
+
+      sectionIPv6Prefix = checkUnitConfigWithLegacyKey "ipv6PrefixConfig" "IPv6Prefix" [
         (assertOnlyFields [
           "AddressAutoconfiguration"
           "OnLink"
@@ -1046,7 +1058,7 @@ let
         (assertValueOneOf "Assign" boolValues)
       ];
 
-      sectionIPv6RoutePrefix = checkUnitConfig "IPv6RoutePrefix" [
+      sectionIPv6RoutePrefix = checkUnitConfigWithLegacyKey "ipv6RoutePrefixConfig" "IPv6RoutePrefix" [
         (assertOnlyFields [
           "Route"
           "LifetimeSec"
@@ -1055,7 +1067,7 @@ let
         (assertInt "LifetimeSec")
       ];
 
-      sectionDHCPServerStaticLease = checkUnitConfig "DHCPServerStaticLease" [
+      sectionDHCPServerStaticLease = checkUnitConfigWithLegacyKey "dhcpServerStaticLeaseConfig" "DHCPServerStaticLease" [
         (assertOnlyFields [
           "MACAddress"
           "Address"
@@ -1102,7 +1114,7 @@ let
         (assertRange "Priority" 0 63)
       ];
 
-      sectionBridgeFDB = checkUnitConfig "BridgeFDB" [
+      sectionBridgeFDB = checkUnitConfigWithLegacyKey "bridgeFDBConfig" "BridgeFDB" [
         (assertOnlyFields [
           "MACAddress"
           "Destination"
@@ -1119,7 +1131,7 @@ let
         (assertValueOneOf "AssociatedWith" [ "use" "self" "master" "router" ])
       ];
 
-      sectionBridgeMDB = checkUnitConfig "BridgeMDB" [
+      sectionBridgeMDB = checkUnitConfigWithLegacyKey "bridgeMDBConfig" "BridgeMDB" [
         (assertOnlyFields [
           "MulticastGroupAddress"
           "VLANId"
@@ -1522,7 +1534,7 @@ let
         (assertRange "Weight" 1 1023)
       ];
 
-      sectionBridgeVLAN = checkUnitConfig "BridgeVLAN" [
+      sectionBridgeVLAN = checkUnitConfigWithLegacyKey "bridgeVLANConfig" "BridgeVLAN" [
         (assertOnlyFields [
           "VLAN"
           "EgressUntagged"
@@ -1625,34 +1637,21 @@ let
 
   };
 
-
-  l2tpSessionOptions = {
-    options = {
-      l2tpSessionConfig = mkOption {
-        default = {};
-        type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionL2TPSession;
-        description = ''
-          Each attribute in this set specifies an option in the
-          `[L2TPSession]` section of the unit.  See
-          {manpage}`systemd.netdev(5)` for details.
-        '';
-      };
-    };
-  };
-
-  wireguardPeerOptions = {
-    options = {
-      wireguardPeerConfig = mkOption {
-        default = {};
-        type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionWireGuardPeer;
-        description = ''
-          Each attribute in this set specifies an option in the
-          `[WireGuardPeer]` section of the unit.  See
-          {manpage}`systemd.netdev(5)` for details.
-        '';
-      };
+  mkSubsectionType = oldKey: checkF:
+    let
+      type = types.addCheck (types.attrsOf unitOption) checkF;
+    in type // {
+      merge = loc: defs:
+        let
+          final = type.merge loc defs;
+        in
+        if final?${oldKey}
+          then warn
+            "Using '${oldKey}' is deprecated! Move all attributes inside one level up and remove it."
+            final.${oldKey}
+        else
+          final;
     };
-  };
 
   netdevOptions = commonNetworkOptions // {
 
@@ -1803,12 +1802,12 @@ let
 
     l2tpSessions = mkOption {
       default = [];
-      example = [ { l2tpSessionConfig={
+      example = [ {
         SessionId = 25;
         PeerSessionId = 26;
         Name = "l2tp-sess";
-      };}];
-      type = with types; listOf (submodule l2tpSessionOptions);
+      }];
+      type = types.listOf (mkSubsectionType "l2tpSessionConfig" check.netdev.sectionL2TPSession);
       description = ''
         Each item in this array specifies an option in the
         `[L2TPSession]` section of the unit. See
@@ -1836,14 +1835,14 @@ let
 
     wireguardPeers = mkOption {
       default = [];
-      example = [ { wireguardPeerConfig={
+      example = [ {
         Endpoint = "192.168.1.1:51820";
         PublicKey = "27s0OvaBBdHoJYkH9osZpjpgSOVNw+RaKfboT/Sfq0g=";
         PresharedKeyFile = "/etc/wireguard/psk.key";
         AllowedIPs = [ "10.0.0.1/32" ];
         PersistentKeepalive = 15;
-      };}];
-      type = with types; listOf (submodule wireguardPeerOptions);
+      } ];
+      type = types.listOf (mkSubsectionType "wireguardPeerConfig" check.netdev.sectionWireGuardPeer);
       description = ''
         Each item in this array specifies an option in the
         `[WireGuardPeer]` section of the unit. See
@@ -1915,143 +1914,6 @@ let
 
   };
 
-  addressOptions = {
-    options = {
-      addressConfig = mkOption {
-        example = { Address = "192.168.0.100/24"; };
-        type = types.addCheck (types.attrsOf unitOption) check.network.sectionAddress;
-        description = ''
-          Each attribute in this set specifies an option in the
-          `[Address]` section of the unit.  See
-          {manpage}`systemd.network(5)` for details.
-        '';
-      };
-    };
-  };
-
-  routingPolicyRulesOptions = {
-    options = {
-      routingPolicyRuleConfig = mkOption {
-        default = { };
-        example = { Table = 10; IncomingInterface = "eth1"; Family = "both"; };
-        type = types.addCheck (types.attrsOf unitOption) check.network.sectionRoutingPolicyRule;
-        description = ''
-          Each attribute in this set specifies an option in the
-          `[RoutingPolicyRule]` section of the unit.  See
-          {manpage}`systemd.network(5)` for details.
-        '';
-      };
-    };
-  };
-
-  routeOptions = {
-    options = {
-      routeConfig = mkOption {
-        default = {};
-        example = { Gateway = "192.168.0.1"; };
-        type = types.addCheck (types.attrsOf unitOption) check.network.sectionRoute;
-        description = ''
-          Each attribute in this set specifies an option in the
-          `[Route]` section of the unit.  See
-          {manpage}`systemd.network(5)` for details.
-        '';
-      };
-    };
-  };
-
-  ipv6PrefixOptions = {
-    options = {
-      ipv6PrefixConfig = mkOption {
-        default = {};
-        example = { Prefix = "fd00::/64"; };
-        type = types.addCheck (types.attrsOf unitOption) check.network.sectionIPv6Prefix;
-        description = ''
-          Each attribute in this set specifies an option in the
-          `[IPv6Prefix]` section of the unit.  See
-          {manpage}`systemd.network(5)` for details.
-        '';
-      };
-    };
-  };
-
-  ipv6RoutePrefixOptions = {
-    options = {
-      ipv6RoutePrefixConfig = mkOption {
-        default = {};
-        example = { Route = "fd00::/64"; };
-        type = types.addCheck (types.attrsOf unitOption) check.network.sectionIPv6RoutePrefix;
-        description = ''
-          Each attribute in this set specifies an option in the
-          `[IPv6RoutePrefix]` section of the unit.  See
-          {manpage}`systemd.network(5)` for details.
-        '';
-      };
-    };
-  };
-
-  dhcpServerStaticLeaseOptions = {
-    options = {
-      dhcpServerStaticLeaseConfig = mkOption {
-        default = {};
-        example = { MACAddress = "65:43:4a:5b:d8:5f"; Address = "192.168.1.42"; };
-        type = types.addCheck (types.attrsOf unitOption) check.network.sectionDHCPServerStaticLease;
-        description = ''
-          Each attribute in this set specifies an option in the
-          `[DHCPServerStaticLease]` section of the unit.  See
-          {manpage}`systemd.network(5)` for details.
-
-          Make sure to configure the corresponding client interface to use
-          `ClientIdentifier=mac`.
-        '';
-      };
-    };
-  };
-
-  bridgeFDBOptions = {
-    options = {
-      bridgeFDBConfig = mkOption {
-        default = {};
-        example = { MACAddress = "65:43:4a:5b:d8:5f"; Destination = "192.168.1.42"; VNI = 20; };
-        type = types.addCheck (types.attrsOf unitOption) check.network.sectionBridgeFDB;
-        description = ''
-          Each attribute in this set specifies an option in the
-          `[BridgeFDB]` section of the unit.  See
-          {manpage}`systemd.network(5)` for details.
-        '';
-      };
-    };
-  };
-
-  bridgeMDBOptions = {
-    options = {
-      bridgeMDBConfig = mkOption {
-        default = {};
-        example = { MulticastGroupAddress = "ff02::1:2:3:4"; VLANId = 10; };
-        type = types.addCheck (types.attrsOf unitOption) check.network.sectionBridgeMDB;
-        description = ''
-          Each attribute in this set specifies an option in the
-          `[BridgeMDB]` section of the unit.  See
-          {manpage}`systemd.network(5)` for details.
-        '';
-      };
-    };
-  };
-
-  bridgeVLANOptions = {
-    options = {
-      bridgeVLANConfig = mkOption {
-        default = {};
-        example = { VLAN = 20; };
-        type = types.addCheck (types.attrsOf unitOption) check.network.sectionBridgeVLAN;
-        description = ''
-          Each attribute in this set specifies an option in the
-          `[BridgeVLAN]` section of the unit.  See
-          {manpage}`systemd.network(5)` for details.
-        '';
-      };
-    };
-  };
-
   networkOptions = commonNetworkOptions // {
 
     linkConfig = mkOption {
@@ -2161,10 +2023,20 @@ let
       '';
     };
 
+    ipv6PREF64Prefixes = mkOption {
+      default = [];
+      example = [ { Prefix = "64:ff9b::/96"; } ];
+      type = types.listOf (mkSubsectionType "ipv6PREF64PrefixConfig" check.network.sectionIPv6PREF64Prefix);
+      description = ''
+        A list of IPv6PREF64Prefix sections to be added to the unit. See
+        {manpage}`systemd.network(5)` for details.
+      '';
+    };
+
     dhcpServerStaticLeases = mkOption {
       default = [];
-      example = [ { dhcpServerStaticLeaseConfig = { MACAddress = "65:43:4a:5b:d8:5f"; Address = "192.168.1.42"; }; } ];
-      type = with types; listOf (submodule dhcpServerStaticLeaseOptions);
+      example = [ { MACAddress = "65:43:4a:5b:d8:5f"; Address = "192.168.1.42"; } ];
+      type = types.listOf (mkSubsectionType "dhcpServerStaticLeaseConfig" check.network.sectionDHCPServerStaticLease);
       description = ''
         A list of DHCPServerStaticLease sections to be added to the unit.  See
         {manpage}`systemd.network(5)` for details.
@@ -2173,8 +2045,8 @@ let
 
     ipv6Prefixes = mkOption {
       default = [];
-      example = [ { ipv6PrefixConfig = { AddressAutoconfiguration = true; OnLink = true; }; } ];
-      type = with types; listOf (submodule ipv6PrefixOptions);
+      example = [ { AddressAutoconfiguration = true; OnLink = true; } ];
+      type = types.listOf (mkSubsectionType "ipv6PrefixConfig" check.network.sectionIPv6Prefix);
       description = ''
         A list of ipv6Prefix sections to be added to the unit.  See
         {manpage}`systemd.network(5)` for details.
@@ -2183,8 +2055,8 @@ let
 
     ipv6RoutePrefixes = mkOption {
       default = [];
-      example = [ { ipv6RoutePrefixConfig = { Route = "fd00::/64"; LifetimeSec = 3600; }; } ];
-      type = with types; listOf (submodule ipv6RoutePrefixOptions);
+      example = [ { Route = "fd00::/64"; LifetimeSec = 3600; } ];
+      type = types.listOf (mkSubsectionType "ipv6RoutePrefixConfig" check.network.sectionIPv6RoutePrefix);
       description = ''
         A list of ipv6RoutePrefix sections to be added to the unit.  See
         {manpage}`systemd.network(5)` for details.
@@ -2204,8 +2076,8 @@ let
 
     bridgeFDBs = mkOption {
       default = [];
-      example = [ { bridgeFDBConfig = { MACAddress = "90:e2:ba:43:fc:71"; Destination = "192.168.100.4"; VNI = 3600; }; } ];
-      type = with types; listOf (submodule bridgeFDBOptions);
+      example = [ { MACAddress = "90:e2:ba:43:fc:71"; Destination = "192.168.100.4"; VNI = 3600; } ];
+      type = types.listOf (mkSubsectionType "bridgeFDBConfig" check.network.sectionBridgeFDB);
       description = ''
         A list of BridgeFDB sections to be added to the unit.  See
         {manpage}`systemd.network(5)` for details.
@@ -2214,8 +2086,8 @@ let
 
     bridgeMDBs = mkOption {
       default = [];
-      example = [ { bridgeMDBConfig = { MulticastGroupAddress = "ff02::1:2:3:4"; VLANId = 10; } ; } ];
-      type = with types; listOf (submodule bridgeMDBOptions);
+      example = [ { MulticastGroupAddress = "ff02::1:2:3:4"; VLANId = 10; } ];
+      type = types.listOf (mkSubsectionType "bridgeMDBConfig" check.network.sectionBridgeMDB);
       description = ''
         A list of BridgeMDB sections to be added to the unit.  See
         {manpage}`systemd.network(5)` for details.
@@ -2532,8 +2404,8 @@ let
 
     bridgeVLANs = mkOption {
       default = [];
-      example = [ { bridgeVLANConfig = { VLAN = "10-20"; }; } ];
-      type = with types; listOf (submodule bridgeVLANOptions);
+      example = [ { VLAN = "10-20"; } ];
+      type = types.listOf (mkSubsectionType "bridgeVLANConfig" check.network.sectionBridgeVLAN);
       description = ''
         A list of BridgeVLAN sections to be added to the unit.  See
         {manpage}`systemd.network(5)` for details.
@@ -2683,7 +2555,8 @@ let
 
     addresses = mkOption {
       default = [ ];
-      type = with types; listOf (submodule addressOptions);
+      example = [ { Address = "192.168.0.100/24"; } ];
+      type = types.listOf (mkSubsectionType "addressConfig" check.network.sectionAddress);
       description = ''
         A list of address sections to be added to the unit.  See
         {manpage}`systemd.network(5)` for details.
@@ -2692,7 +2565,8 @@ let
 
     routingPolicyRules = mkOption {
       default = [ ];
-      type = with types; listOf (submodule routingPolicyRulesOptions);
+      example = [ { Table = 10; IncomingInterface = "eth1"; Family = "both"; } ];
+      type = types.listOf (mkSubsectionType "routingPolicyRuleConfig" check.network.sectionRoutingPolicyRule);
       description = ''
         A list of routing policy rules sections to be added to the unit.  See
         {manpage}`systemd.network(5)` for details.
@@ -2701,7 +2575,8 @@ let
 
     routes = mkOption {
       default = [ ];
-      type = with types; listOf (submodule routeOptions);
+      example = [ { Gateway = "192.168.0.1"; } ];
+      type = types.listOf (mkSubsectionType "routeConfig" check.network.sectionRoute);
       description = ''
         A list of route sections to be added to the unit.  See
         {manpage}`systemd.network(5)` for details.
diff --git a/nixos/modules/system/boot/resolved.nix b/nixos/modules/system/boot/resolved.nix
index 64a15179438fe..b658a7a2dc05e 100644
--- a/nixos/modules/system/boot/resolved.nix
+++ b/nixos/modules/system/boot/resolved.nix
@@ -7,6 +7,20 @@ let
   dnsmasqResolve = config.services.dnsmasq.enable &&
                    config.services.dnsmasq.resolveLocalQueries;
 
+  resolvedConf = ''
+    [Resolve]
+    ${optionalString (config.networking.nameservers != [])
+      "DNS=${concatStringsSep " " config.networking.nameservers}"}
+    ${optionalString (cfg.fallbackDns != null)
+      "FallbackDNS=${concatStringsSep " " cfg.fallbackDns}"}
+    ${optionalString (cfg.domains != [])
+      "Domains=${concatStringsSep " " cfg.domains}"}
+    LLMNR=${cfg.llmnr}
+    DNSSEC=${cfg.dnssec}
+    DNSOverTLS=${cfg.dnsovertls}
+    ${config.services.resolved.extraConfig}
+  '';
+
 in
 {
 
@@ -126,60 +140,87 @@ in
       '';
     };
 
-  };
-
-  config = mkIf cfg.enable {
-
-    assertions = [
-      { assertion = !config.networking.useHostResolvConf;
-        message = "Using host resolv.conf is not supported with systemd-resolved";
-      }
-    ];
-
-    users.users.systemd-resolve.group = "systemd-resolve";
-
-    # add resolve to nss hosts database if enabled and nscd enabled
-    # system.nssModules is configured in nixos/modules/system/boot/systemd.nix
-    # added with order 501 to allow modules to go before with mkBefore
-    system.nssDatabases.hosts = (mkOrder 501 ["resolve [!UNAVAIL=return]"]);
-
-    systemd.additionalUpstreamSystemUnits = [
-      "systemd-resolved.service"
-    ];
-
-    systemd.services.systemd-resolved = {
-      wantedBy = [ "multi-user.target" ];
-      aliases = [ "dbus-org.freedesktop.resolve1.service" ];
-      restartTriggers = [ config.environment.etc."systemd/resolved.conf".source ];
-    };
-
-    environment.etc = {
-      "systemd/resolved.conf".text = ''
-        [Resolve]
-        ${optionalString (config.networking.nameservers != [])
-          "DNS=${concatStringsSep " " config.networking.nameservers}"}
-        ${optionalString (cfg.fallbackDns != null)
-          "FallbackDNS=${concatStringsSep " " cfg.fallbackDns}"}
-        ${optionalString (cfg.domains != [])
-          "Domains=${concatStringsSep " " cfg.domains}"}
-        LLMNR=${cfg.llmnr}
-        DNSSEC=${cfg.dnssec}
-        DNSOverTLS=${cfg.dnsovertls}
-        ${config.services.resolved.extraConfig}
+    boot.initrd.services.resolved.enable = mkOption {
+      default = config.boot.initrd.systemd.network.enable;
+      defaultText = "config.boot.initrd.systemd.network.enable";
+      description = ''
+        Whether to enable resolved for stage 1 networking.
+        Uses the toplevel 'services.resolved' options for 'resolved.conf'
       '';
-
-      # symlink the dynamic stub resolver of resolv.conf as recommended by upstream:
-      # https://www.freedesktop.org/software/systemd/man/systemd-resolved.html#/etc/resolv.conf
-      "resolv.conf".source = "/run/systemd/resolve/stub-resolv.conf";
-    } // optionalAttrs dnsmasqResolve {
-      "dnsmasq-resolv.conf".source = "/run/systemd/resolve/resolv.conf";
     };
 
-    # If networkmanager is enabled, ask it to interface with resolved.
-    networking.networkmanager.dns = "systemd-resolved";
-
-    networking.resolvconf.package = pkgs.systemd;
-
   };
 
+  config = mkMerge [
+    (mkIf cfg.enable {
+
+      assertions = [
+        { assertion = !config.networking.useHostResolvConf;
+          message = "Using host resolv.conf is not supported with systemd-resolved";
+        }
+      ];
+
+      users.users.systemd-resolve.group = "systemd-resolve";
+
+      # add resolve to nss hosts database if enabled and nscd enabled
+      # system.nssModules is configured in nixos/modules/system/boot/systemd.nix
+      # added with order 501 to allow modules to go before with mkBefore
+      system.nssDatabases.hosts = (mkOrder 501 ["resolve [!UNAVAIL=return]"]);
+
+      systemd.additionalUpstreamSystemUnits = [
+        "systemd-resolved.service"
+      ];
+
+      systemd.services.systemd-resolved = {
+        wantedBy = [ "sysinit.target" ];
+        aliases = [ "dbus-org.freedesktop.resolve1.service" ];
+        restartTriggers = [ config.environment.etc."systemd/resolved.conf".source ];
+      };
+
+      environment.etc = {
+        "systemd/resolved.conf".text = resolvedConf;
+
+        # symlink the dynamic stub resolver of resolv.conf as recommended by upstream:
+        # https://www.freedesktop.org/software/systemd/man/systemd-resolved.html#/etc/resolv.conf
+        "resolv.conf".source = "/run/systemd/resolve/stub-resolv.conf";
+      } // optionalAttrs dnsmasqResolve {
+        "dnsmasq-resolv.conf".source = "/run/systemd/resolve/resolv.conf";
+      };
+
+      # If networkmanager is enabled, ask it to interface with resolved.
+      networking.networkmanager.dns = "systemd-resolved";
+
+      networking.resolvconf.package = pkgs.systemd;
+
+    })
+
+    (mkIf config.boot.initrd.services.resolved.enable {
+
+      assertions = [
+        {
+          assertion = config.boot.initrd.systemd.enable;
+          message = "'boot.initrd.services.resolved.enable' can only be enabled with systemd stage 1.";
+        }
+      ];
+
+      boot.initrd.systemd = {
+        contents = {
+          "/etc/tmpfiles.d/resolv.conf".text =
+            "L /etc/resolv.conf - - - - /run/systemd/resolve/stub-resolv.conf";
+          "/etc/systemd/resolved.conf".text = resolvedConf;
+        };
+
+        additionalUpstreamUnits = ["systemd-resolved.service"];
+        users.systemd-resolve = {};
+        groups.systemd-resolve = {};
+        storePaths = ["${config.boot.initrd.systemd.package}/lib/systemd/systemd-resolved"];
+        services.systemd-resolved = {
+          wantedBy = ["sysinit.target"];
+          aliases = [ "dbus-org.freedesktop.resolve1.service" ];
+        };
+      };
+
+    })
+  ];
+
 }
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index 59cf1a47fb7f9..23e9df2189e78 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -576,6 +576,7 @@ while read -u 3 mountPoint; do
       mount -t "$fsType" /dev/root /tmp-iso
       mountFS tmpfs /iso size="$fsSize" tmpfs
 
+      echo "copying ISO contents to RAM..."
       cp -r /tmp-iso/* /mnt-root/iso/
 
       umount /tmp-iso
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index c82924763d5e8..14a4ab596b52c 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -503,8 +503,8 @@ in
     environment.systemPackages = [ cfg.package ];
 
     environment.etc = let
-      # generate contents for /etc/systemd/system-${type} from attrset of links and packages
-      hooks = type: links: pkgs.runCommand "system-${type}" {
+      # generate contents for /etc/systemd/${dir} from attrset of links and packages
+      hooks = dir: links: pkgs.runCommand "${dir}" {
           preferLocalBuild = true;
           packages = cfg.packages;
       } ''
@@ -512,7 +512,7 @@ in
         mkdir -p $out
         for package in $packages
         do
-          for hook in $package/lib/systemd/system-${type}/*
+          for hook in $package/lib/systemd/${dir}/*
           do
             ln -s $hook $out/
           done
@@ -562,8 +562,9 @@ in
         ${cfg.sleep.extraConfig}
       '';
 
-      "systemd/system-generators" = { source = hooks "generators" cfg.generators; };
-      "systemd/system-shutdown" = { source = hooks "shutdown" cfg.shutdown; };
+      "systemd/user-generators" = { source = hooks "user-generators" cfg.user.generators; };
+      "systemd/system-generators" = { source = hooks "system-generators" cfg.generators; };
+      "systemd/system-shutdown" = { source = hooks "system-shutdown" cfg.shutdown; };
     });
 
     services.dbus.enable = true;
diff --git a/nixos/modules/system/boot/systemd/coredump.nix b/nixos/modules/system/boot/systemd/coredump.nix
index 1f29f6686d0d0..ccf5d449b94a3 100644
--- a/nixos/modules/system/boot/systemd/coredump.nix
+++ b/nixos/modules/system/boot/systemd/coredump.nix
@@ -53,7 +53,7 @@ in {
           pkgs.substitute {
             src = "${systemd}/example/sysctl.d/50-coredump.conf";
             substitutions = [
-              "--replace"
+              "--replace-fail"
               "${systemd}"
               "${pkgs.symlinkJoin { name = "systemd"; paths = [ systemd ]; }}"
             ];
diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix
index cc32b2a15e7ce..6107a2594baf8 100644
--- a/nixos/modules/system/boot/systemd/initrd.nix
+++ b/nixos/modules/system/boot/systemd/initrd.nix
@@ -473,8 +473,8 @@ in {
         "${cfg.package.util-linux}/bin/umount"
         "${cfg.package.util-linux}/bin/sulogin"
 
-        # required for script services
-        "${pkgs.runtimeShell}"
+        # required for script services, and some tools like xfs still want the sh symlink
+        "${pkgs.bash}/bin"
 
         # so NSS can look up usernames
         "${pkgs.glibc}/lib/libnss_files.so.2"
diff --git a/nixos/modules/system/boot/systemd/sysusers.nix b/nixos/modules/system/boot/systemd/sysusers.nix
index de70009705973..476251e140456 100644
--- a/nixos/modules/system/boot/systemd/sysusers.nix
+++ b/nixos/modules/system/boot/systemd/sysusers.nix
@@ -12,7 +12,7 @@ let
     ${lib.concatLines (lib.mapAttrsToList
       (username: opts:
         let
-          uid = if opts.uid == null then "-" else toString opts.uid;
+          uid = if opts.uid == null then "/var/lib/nixos/uid/${username}" else toString opts.uid;
         in
           ''u ${username} ${uid}:${opts.group} "${opts.description}" ${opts.home} ${utils.toShellPath opts.shell}''
       )
@@ -21,7 +21,7 @@ let
 
     # Groups
     ${lib.concatLines (lib.mapAttrsToList
-      (groupname: opts: ''g ${groupname} ${if opts.gid == null then "-" else toString opts.gid}'') userCfg.groups)
+      (groupname: opts: ''g ${groupname} ${if opts.gid == null then "/var/lib/nixos/gid/${groupname}" else toString opts.gid}'') userCfg.groups)
     }
 
     # Group membership
@@ -106,6 +106,23 @@ in
             };
           })
           (lib.filterAttrs (_username: opts: opts.home != "/var/empty") userCfg.users);
+
+        # Create uid/gid marker files for those without an explicit id
+        tmpfiles.settings.nixos-uid = lib.mapAttrs'
+          (username: opts: lib.nameValuePair "/var/lib/nixos/uid/${username}" {
+            f = {
+              user = username;
+            };
+          })
+          (lib.filterAttrs (_username: opts: opts.uid == null) userCfg.users);
+
+        tmpfiles.settings.nixos-gid = lib.mapAttrs'
+          (groupname: opts: lib.nameValuePair "/var/lib/nixos/gid/${groupname}" {
+            f = {
+              group = groupname;
+            };
+          })
+          (lib.filterAttrs (_groupname: opts: opts.gid == null) userCfg.groups);
       })
 
       (lib.mkIf config.users.mutableUsers {
diff --git a/nixos/modules/system/boot/systemd/user.nix b/nixos/modules/system/boot/systemd/user.nix
index 2685cf7e283a2..53fca631678c1 100644
--- a/nixos/modules/system/boot/systemd/user.nix
+++ b/nixos/modules/system/boot/systemd/user.nix
@@ -144,6 +144,18 @@ in {
       };
     };
 
+    systemd.user.generators = mkOption {
+      type = types.attrsOf types.path;
+      default = {};
+      example = { systemd-gpt-auto-generator = "/dev/null"; };
+      description = ''
+        Definition of systemd generators; see {manpage}`systemd.generator(5)`.
+
+        For each `NAME = VALUE` pair of the attrSet, a link is generated from
+        `/etc/systemd/user-generators/NAME` to `VALUE`.
+      '';
+    };
+
     systemd.additionalUpstreamUserUnits = mkOption {
       default = [];
       type = types.listOf types.str;
diff --git a/nixos/modules/system/etc/build-composefs-dump.py b/nixos/modules/system/etc/build-composefs-dump.py
index bba454dd888d6..fe739a621ec4d 100644
--- a/nixos/modules/system/etc/build-composefs-dump.py
+++ b/nixos/modules/system/etc/build-composefs-dump.py
@@ -175,7 +175,7 @@ def main() -> None:
                 paths[glob_target] = composefs_path
                 add_leading_directories(glob_target, attrs, paths)
         else:  # Without globbing
-            if mode == "symlink":
+            if mode == "symlink" or mode == "direct-symlink":
                 composefs_path = ComposefsPath(
                     attrs,
                     # A high approximation of the size of a symlink
@@ -184,24 +184,23 @@ def main() -> None:
                     mode="0777",
                     payload=source,
                 )
+            elif os.path.isdir(source):
+                composefs_path = ComposefsPath(
+                    attrs,
+                    size=4096,
+                    filetype=FileType.directory,
+                    mode=mode,
+                    payload=source,
+                )
             else:
-                if os.path.isdir(source):
-                    composefs_path = ComposefsPath(
-                        attrs,
-                        size=4096,
-                        filetype=FileType.directory,
-                        mode=mode,
-                        payload=source,
-                    )
-                else:
-                    composefs_path = ComposefsPath(
-                        attrs,
-                        size=os.stat(source).st_size,
-                        filetype=FileType.file,
-                        mode=mode,
-                        # payload needs to be relative path in this case
-                        payload=target.lstrip("/"),
-                    )
+                composefs_path = ComposefsPath(
+                    attrs,
+                    size=os.stat(source).st_size,
+                    filetype=FileType.file,
+                    mode=mode,
+                    # payload needs to be relative path in this case
+                    payload=target.lstrip("/"),
+                )
             paths[target] = composefs_path
             add_leading_directories(target, attrs, paths)
 
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
index 9fded1e1c9742..80ca69e495e9d 100644
--- a/nixos/modules/system/etc/etc.nix
+++ b/nixos/modules/system/etc/etc.nix
@@ -62,7 +62,7 @@ let
     ]) etc'}
   '';
 
-  etcHardlinks = filter (f: f.mode != "symlink") etc';
+  etcHardlinks = filter (f: f.mode != "symlink" && f.mode != "direct-symlink") etc';
 
   build-composefs-dump = pkgs.runCommand "build-composefs-dump.py"
     {
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 2c749d45d7a15..b75817a011cbd 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -441,21 +441,45 @@ in
           {manpage}`systemd.time(7)`.
         '';
       };
+
+      randomizedDelaySec = mkOption {
+        default = "6h";
+        type = types.str;
+        example = "12h";
+        description = ''
+          Add a randomized delay before each ZFS trim.
+          The delay will be chosen between zero and this value.
+          This value must be a time span in the format specified by
+          {manpage}`systemd.time(7)`
+        '';
+      };
     };
 
     services.zfs.autoScrub = {
       enable = mkEnableOption "periodic scrubbing of ZFS pools";
 
       interval = mkOption {
-        default = "Sun, 02:00";
+        default = "monthly";
         type = types.str;
-        example = "daily";
+        example = "quarterly";
         description = ''
           Systemd calendar expression when to scrub ZFS pools. See
           {manpage}`systemd.time(7)`.
         '';
       };
 
+      randomizedDelaySec = mkOption {
+        default = "6h";
+        type = types.str;
+        example = "12h";
+        description = ''
+          Add a randomized delay before each ZFS autoscrub.
+          The delay will be chosen between zero and this value.
+          This value must be a time span in the format specified by
+          {manpage}`systemd.time(7)`
+        '';
+      };
+
       pools = mkOption {
         default = [];
         type = types.listOf types.str;
@@ -862,6 +886,7 @@ in
         timerConfig = {
           OnCalendar = cfgScrub.interval;
           Persistent = "yes";
+          RandomizedDelaySec = cfgScrub.randomizedDelaySec;
         };
       };
     })
@@ -879,7 +904,10 @@ in
         serviceConfig.ExecStart = "${pkgs.runtimeShell} -c 'for pool in $(zpool list -H -o name); do zpool trim $pool;  done || true' ";
       };
 
-      systemd.timers.zpool-trim.timerConfig.Persistent = "yes";
+      systemd.timers.zpool-trim.timerConfig = {
+        Persistent = "yes";
+        RandomizedDelaySec = cfgTrim.randomizedDelaySec;
+      };
     })
   ];
 }
diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
index 2f2d282fbefb4..bbf2d337aac64 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -203,10 +203,10 @@ let
                   ''
                     echo "${cidr}" >> $state
                     echo -n "adding address ${cidr}... "
-                    if out=$(ip addr add "${cidr}" dev "${i.name}" 2>&1); then
+                    if out=$(ip addr replace "${cidr}" dev "${i.name}" 2>&1); then
                       echo "done"
-                    elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
-                      echo "'ip addr add "${cidr}" dev "${i.name}"' failed: $out"
+                    else
+                      echo "'ip addr replace "${cidr}" dev "${i.name}"' failed: $out"
                       exit 1
                     fi
                   ''
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index 2009c9a7e6e28..c1241d11de879 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -32,13 +32,13 @@ let
     optionalAttrs (gateway != null && gateway.interface != null) {
       networks."40-${gateway.interface}" = {
         matchConfig.Name = gateway.interface;
-        routes = [{
-          routeConfig = {
+        routes = [
+          ({
             Gateway = gateway.address;
           } // optionalAttrs (gateway.metric != null) {
             Metric = gateway.metric;
-          };
-        }];
+          })
+        ];
       };
     }
   ));
@@ -95,65 +95,64 @@ let
       address = forEach (interfaceIps i)
         (ip: "${ip.address}/${toString ip.prefixLength}");
       routes = forEach (interfaceRoutes i)
-        (route: {
+        (route: mkMerge [
           # Most of these route options have not been tested.
           # Please fix or report any mistakes you may find.
-          routeConfig =
-            optionalAttrs (route.address != null && route.prefixLength != null) {
-              Destination = "${route.address}/${toString route.prefixLength}";
-            } //
-            optionalAttrs (route.options ? fastopen_no_cookie) {
-              FastOpenNoCookie = route.options.fastopen_no_cookie;
-            } //
-            optionalAttrs (route.via != null) {
-              Gateway = route.via;
-            } //
-            optionalAttrs (route.type != null) {
-              Type = route.type;
-            } //
-            optionalAttrs (route.options ? onlink) {
-              GatewayOnLink = true;
-            } //
-            optionalAttrs (route.options ? initrwnd) {
-              InitialAdvertisedReceiveWindow = route.options.initrwnd;
-            } //
-            optionalAttrs (route.options ? initcwnd) {
-              InitialCongestionWindow = route.options.initcwnd;
-            } //
-            optionalAttrs (route.options ? pref) {
-              IPv6Preference = route.options.pref;
-            } //
-            optionalAttrs (route.options ? mtu) {
-              MTUBytes = route.options.mtu;
-            } //
-            optionalAttrs (route.options ? metric) {
-              Metric = route.options.metric;
-            } //
-            optionalAttrs (route.options ? src) {
-              PreferredSource = route.options.src;
-            } //
-            optionalAttrs (route.options ? protocol) {
-              Protocol = route.options.protocol;
-            } //
-            optionalAttrs (route.options ? quickack) {
-              QuickAck = route.options.quickack;
-            } //
-            optionalAttrs (route.options ? scope) {
-              Scope = route.options.scope;
-            } //
-            optionalAttrs (route.options ? from) {
-              Source = route.options.from;
-            } //
-            optionalAttrs (route.options ? table) {
-              Table = route.options.table;
-            } //
-            optionalAttrs (route.options ? advmss) {
-              TCPAdvertisedMaximumSegmentSize = route.options.advmss;
-            } //
-            optionalAttrs (route.options ? ttl-propagate) {
-              TTLPropagate = route.options.ttl-propagate == "enabled";
-            };
-        });
+          (mkIf (route.address != null && route.prefixLength != null) {
+            Destination = "${route.address}/${toString route.prefixLength}";
+          })
+          (mkIf (route.options ? fastopen_no_cookie) {
+            FastOpenNoCookie = route.options.fastopen_no_cookie;
+          })
+          (mkIf (route.via != null) {
+            Gateway = route.via;
+          })
+          (mkIf (route.type != null) {
+            Type = route.type;
+          })
+          (mkIf (route.options ? onlink) {
+            GatewayOnLink = true;
+          })
+          (mkIf (route.options ? initrwnd) {
+            InitialAdvertisedReceiveWindow = route.options.initrwnd;
+          })
+          (mkIf (route.options ? initcwnd) {
+            InitialCongestionWindow = route.options.initcwnd;
+          })
+          (mkIf (route.options ? pref) {
+            IPv6Preference = route.options.pref;
+          })
+          (mkIf (route.options ? mtu) {
+            MTUBytes = route.options.mtu;
+          })
+          (mkIf (route.options ? metric) {
+            Metric = route.options.metric;
+          })
+          (mkIf (route.options ? src) {
+            PreferredSource = route.options.src;
+          })
+          (mkIf (route.options ? protocol) {
+            Protocol = route.options.protocol;
+          })
+          (mkIf (route.options ? quickack) {
+            QuickAck = route.options.quickack;
+          })
+          (mkIf (route.options ? scope) {
+            Scope = route.options.scope;
+          })
+          (mkIf (route.options ? from) {
+            Source = route.options.from;
+          })
+          (mkIf (route.options ? table) {
+            Table = route.options.table;
+          })
+          (mkIf (route.options ? advmss) {
+            TCPAdvertisedMaximumSegmentSize = route.options.advmss;
+          })
+          (mkIf (route.options ? ttl-propagate) {
+            TTLPropagate = route.options.ttl-propagate == "enabled";
+          })
+        ]);
       networkConfig.IPv6PrivacyExtensions = "kernel";
       linkConfig = optionalAttrs (i.macAddress != null) {
         MACAddress = i.macAddress;
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index 28abbe66adafb..2b365bc555855 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -218,6 +218,8 @@ in
 
     services.displayManager.logToJournal = true;
 
+    services.logrotate.enable = mkOverride 150 false;
+
     # Make sure we use the Guest Agent from the QEMU package for testing
     # to reduce the closure size required for the tests.
     services.qemuGuest.package = pkgs.qemu_test.ga;
diff --git a/nixos/modules/virtualisation/containerd.nix b/nixos/modules/virtualisation/containerd.nix
index ea89a994b172a..73fb9f3b55d2f 100644
--- a/nixos/modules/virtualisation/containerd.nix
+++ b/nixos/modules/virtualisation/containerd.nix
@@ -84,7 +84,6 @@ in
         # "limits" defined below are adopted from upstream: https://github.com/containerd/containerd/blob/master/containerd.service
         LimitNPROC = "infinity";
         LimitCORE = "infinity";
-        LimitNOFILE = "infinity";
         TasksMax = "infinity";
         OOMScoreAdjust = "-999";
 
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index 65620dd3935b8..c3639f660dfe3 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -53,13 +53,6 @@ in
 
     storage.settings = mkOption {
       type = toml.type;
-      default = {
-        storage = {
-          driver = "overlay";
-          graphroot = "/var/lib/containers/storage";
-          runroot = "/run/containers/storage";
-        };
-      };
       description = "storage.conf configuration";
     };
 
@@ -124,6 +117,12 @@ in
       };
     };
 
+    virtualisation.containers.storage.settings.storage = {
+      driver = lib.mkDefault "overlay";
+      graphroot = lib.mkDefault "/var/lib/containers/storage";
+      runroot = lib.mkDefault "/run/containers/storage";
+    };
+
     environment.etc = {
       "containers/containers.conf".source =
         toml.generate "containers.conf" cfg.containersConf.settings;
diff --git a/nixos/modules/virtualisation/docker.nix b/nixos/modules/virtualisation/docker.nix
index bcc649dcbec0a..8a0894ed85c3d 100644
--- a/nixos/modules/virtualisation/docker.nix
+++ b/nixos/modules/virtualisation/docker.nix
@@ -244,8 +244,8 @@ in
       };
 
       assertions = [
-        { assertion = cfg.enableNvidia && pkgs.stdenv.isx86_64 -> config.hardware.opengl.driSupport32Bit or false;
-          message = "Option enableNvidia on x86_64 requires 32bit support libraries";
+        { assertion = cfg.enableNvidia && pkgs.stdenv.isx86_64 -> config.hardware.graphics.enable32Bit or false;
+          message = "Option enableNvidia on x86_64 requires 32-bit support libraries";
         }];
 
       virtualisation.docker.daemon.settings = {
diff --git a/nixos/modules/virtualisation/incus.nix b/nixos/modules/virtualisation/incus.nix
index 4d04853d20a56..87568390bd3b8 100644
--- a/nixos/modules/virtualisation/incus.nix
+++ b/nixos/modules/virtualisation/incus.nix
@@ -105,6 +105,37 @@ let
       path = "${pkgs.OVMFFull.fd}/FV/${ovmf-prefix}_VARS.fd";
     }
   ];
+
+  environment = lib.mkMerge [
+    {
+      INCUS_LXC_TEMPLATE_CONFIG = "${pkgs.lxcfs}/share/lxc/config";
+      INCUS_OVMF_PATH = ovmf;
+      INCUS_USBIDS_PATH = "${pkgs.hwdata}/share/hwdata/usb.ids";
+      PATH = lib.mkForce serverBinPath;
+    }
+    (lib.mkIf (cfg.ui.enable) { "INCUS_UI" = cfg.ui.package; })
+  ];
+
+  incus-startup = pkgs.writeShellScript "incus-startup" ''
+    case "$1" in
+        start)
+          systemctl is-active incus.service -q && exit 0
+          exec incusd activateifneeded
+        ;;
+
+        stop)
+          systemctl is-active incus.service -q || exit 0
+          exec incusd shutdown
+        ;;
+
+        *)
+          echo "unknown argument \`$1'" >&2
+          exit 1
+        ;;
+    esac
+
+    exit 0
+  '';
 in
 {
   meta = {
@@ -137,6 +168,14 @@ in
         description = "The incus client package to use. This package is added to PATH.";
       };
 
+      softDaemonRestart = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = ''
+          Allow for incus.service to be stopped without affecting running instances.
+        '';
+      };
+
       preseed = lib.mkOption {
         type = lib.types.nullOr (lib.types.submodule { freeformType = preseedFormat.type; });
 
@@ -282,6 +321,8 @@ in
     systemd.services.incus = {
       description = "Incus Container and Virtual Machine Management Daemon";
 
+      inherit environment;
+
       wantedBy = lib.mkIf (!cfg.socketActivation) [ "multi-user.target" ];
       after = [
         "network-online.target"
@@ -296,20 +337,10 @@ in
 
       wants = [ "network-online.target" ];
 
-      environment = lib.mkMerge [
-        {
-          INCUS_LXC_TEMPLATE_CONFIG = "${pkgs.lxcfs}/share/lxc/config";
-          INCUS_OVMF_PATH = ovmf;
-          INCUS_USBIDS_PATH = "${pkgs.hwdata}/share/hwdata/usb.ids";
-          PATH = lib.mkForce serverBinPath;
-        }
-        (lib.mkIf (cfg.ui.enable) { "INCUS_UI" = cfg.ui.package; })
-      ];
-
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/incusd --group incus-admin";
         ExecStartPost = "${cfg.package}/bin/incusd waitready --timeout=${cfg.startTimeout}";
-        ExecStop = "${cfg.package}/bin/incus admin shutdown";
+        ExecStop = lib.optionalString (!cfg.softDaemonRestart) "${cfg.package}/bin/incus admin shutdown";
 
         KillMode = "process"; # when stopping, leave the containers alone
         Delegate = "yes";
@@ -324,6 +355,27 @@ in
       };
     };
 
+    systemd.services.incus-startup = lib.mkIf cfg.softDaemonRestart {
+      description = "Incus Instances Startup/Shutdown";
+
+      inherit environment;
+
+      after = [
+        "incus.service"
+        "incus.socket"
+      ];
+      requires = [ "incus.socket" ];
+
+      serviceConfig = {
+        ExecStart = "${incus-startup} start";
+        ExecStop = "${incus-startup} stop";
+        RemainAfterExit = true;
+        TimeoutStartSec = "600s";
+        TimeoutStopSec = "600s";
+        Type = "oneshot";
+      };
+    };
+
     systemd.sockets.incus = {
       description = "Incus UNIX socket";
       wantedBy = [ "sockets.target" ];
diff --git a/nixos/modules/virtualisation/lxc-image-metadata.nix b/nixos/modules/virtualisation/lxc-image-metadata.nix
index 2c0568b4c4682..38d955798f3e0 100644
--- a/nixos/modules/virtualisation/lxc-image-metadata.nix
+++ b/nixos/modules/virtualisation/lxc-image-metadata.nix
@@ -87,10 +87,10 @@ in {
       contents = [
         {
           source = toYAML "metadata.yaml" {
-            architecture = builtins.elemAt (builtins.match "^([a-z0-9_]+).+" (toString pkgs.system)) 0;
+            architecture = builtins.elemAt (builtins.match "^([a-z0-9_]+).+" (toString pkgs.stdenv.hostPlatform.system)) 0;
             creation_date = 1;
             properties = {
-              description = "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} ${pkgs.system}";
+              description = "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} ${pkgs.stdenv.hostPlatform.system}";
               os = "${config.system.nixos.distroId}";
               release = "${config.system.nixos.codeName}";
             };
diff --git a/nixos/modules/virtualisation/lxd-agent.nix b/nixos/modules/virtualisation/lxd-agent.nix
index 8d536e18a34e4..d319371478481 100644
--- a/nixos/modules/virtualisation/lxd-agent.nix
+++ b/nixos/modules/virtualisation/lxd-agent.nix
@@ -50,7 +50,7 @@ in {
   };
 
   options = {
-    virtualisation.lxd.agent.enable = lib.mkEnableOption "Enable LXD agent";
+    virtualisation.lxd.agent.enable = lib.mkEnableOption "LXD agent";
   };
 
   config = lib.mkIf cfg.enable {
diff --git a/nixos/modules/virtualisation/oci-containers.nix b/nixos/modules/virtualisation/oci-containers.nix
index 4308d410c69c7..f4fa934231798 100644
--- a/nixos/modules/virtualisation/oci-containers.nix
+++ b/nixos/modules/virtualisation/oci-containers.nix
@@ -221,6 +221,13 @@ let
           example = "hello-world";
         };
 
+        preRunExtraOptions = mkOption {
+          type = with types; listOf str;
+          default = [];
+          description = "Extra options for {command}`${defaultBackend}` that go before the `run` argument.";
+          example = [ "--runtime" "runsc" ];
+        };
+
         extraOptions = mkOption {
           type = with types; listOf str;
           default = [];
@@ -284,7 +291,9 @@ let
       else throw "Unhandled backend: ${cfg.backend}";
 
     script = concatStringsSep " \\\n  " ([
-      "exec ${cfg.backend} run"
+      "exec ${cfg.backend} "
+    ]  ++ map escapeShellArg container.preRunExtraOptions ++ [
+      "run"
       "--rm"
       "--name=${escapedName}"
       "--log-driver=${container.log-driver}"
diff --git a/nixos/modules/virtualisation/oci-image.nix b/nixos/modules/virtualisation/oci-image.nix
index d4af5016dd71c..1e2b90bfd46e2 100644
--- a/nixos/modules/virtualisation/oci-image.nix
+++ b/nixos/modules/virtualisation/oci-image.nix
@@ -9,10 +9,10 @@ in
   config = {
     system.build.OCIImage = import ../../lib/make-disk-image.nix {
       inherit config lib pkgs;
+      inherit (cfg) diskSize;
       name = "oci-image";
       configFile = ./oci-config-user.nix;
       format = "qcow2";
-      diskSize = 8192;
       partitionTableType = if cfg.efi then "efi" else "legacy";
     };
 
diff --git a/nixos/modules/virtualisation/oci-options.nix b/nixos/modules/virtualisation/oci-options.nix
index 0dfedc6a530c8..76f3475a42817 100644
--- a/nixos/modules/virtualisation/oci-options.nix
+++ b/nixos/modules/virtualisation/oci-options.nix
@@ -9,6 +9,12 @@
           Whether the OCI instance is using EFI.
         '';
       };
+      diskSize = lib.mkOption {
+        type = lib.types.int;
+        default = 8192;
+        description = "Size of the disk image created in MB.";
+        example = "diskSize = 12 * 1024; # 12GiB";
+      };
     };
   };
 }
diff --git a/nixos/modules/virtualisation/proxmox-image.nix b/nixos/modules/virtualisation/proxmox-image.nix
index 6349bcef99e6b..01ad86c08cd78 100644
--- a/nixos/modules/virtualisation/proxmox-image.nix
+++ b/nixos/modules/virtualisation/proxmox-image.nix
@@ -16,7 +16,7 @@ with lib;
       };
       scsihw = mkOption {
         type = types.str;
-        default = "virtio-scsi-pci";
+        default = "virtio-scsi-single";
         example = "lsi";
         description = ''
           SCSI controller type. Must be one of the supported values given in
@@ -158,6 +158,31 @@ with lib;
         any specific VMID.
       '';
     };
+    cloudInit = {
+      enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether the VM should accept cloud init configurations from PVE.
+        '';
+      };
+      defaultStorage = mkOption {
+        default = "local-lvm";
+        example = "tank";
+        type = types.str;
+        description = ''
+          Default storage name for cloud init drive.
+        '';
+      };
+      device = mkOption {
+        default = "ide2";
+        example = "scsi0";
+        type = types.str;
+        description = ''
+          Bus/device to which the cloud init drive is attached.
+        '';
+      };
+    };
   };
 
   config = let
@@ -216,37 +241,21 @@ with lib;
           seccompSupport = false;
           guestAgentSupport = false;
         }).overrideAttrs ( super: rec {
-
-          version = "7.2.1";
+          # Check https://github.com/proxmox/pve-qemu/tree/master for the version
+          # of qemu and patch to use
+          version = "8.1.5";
           src = pkgs.fetchurl {
-            url= "https://download.qemu.org/qemu-${version}.tar.xz";
-            sha256 = "sha256-jIVpms+dekOl/immTN1WNwsMLRrQdLr3CYqCTReq1zs=";
+            url = "https://download.qemu.org/qemu-${version}.tar.xz";
+            hash = "sha256-l2Ox7+xP1JeWtQgNCINRLXDLY4nq1lxmHMNoalIjKJY=";
           };
           patches = [
             # Proxmox' VMA tool is published as a particular patch upon QEMU
-            (pkgs.fetchpatch {
-              url =
-                let
-                  rev = "abb04bb6272c1202ca9face0827917552b9d06f6";
-                  path = "debian/patches/pve/0027-PVE-Backup-add-vma-backup-format-code.patch";
-                in "https://git.proxmox.com/?p=pve-qemu.git;a=blob_plain;hb=${rev};f=${path}";
-              hash = "sha256-3d0HHdvaExCry6zcULnziYnWIAnn24vECkI4sjj2BMg=";
-            })
-
-            # Proxmox' VMA tool uses O_DIRECT which fails on tmpfs
-            # Filed to upstream issue tracker: https://bugzilla.proxmox.com/show_bug.cgi?id=4710
-            (pkgs.writeText "inline.patch" ''
-                --- a/vma-writer.c   2023-05-01 15:11:13.361341177 +0200
-                +++ b/vma-writer.c   2023-05-01 15:10:51.785293129 +0200
-                @@ -306,7 +306,7 @@
-                             /* try to use O_NONBLOCK */
-                             fcntl(vmaw->fd, F_SETFL, fcntl(vmaw->fd, F_GETFL)|O_NONBLOCK);
-                         } else  {
-                -            oflags = O_NONBLOCK|O_DIRECT|O_WRONLY|O_EXCL;
-                +            oflags = O_NONBLOCK|O_WRONLY|O_EXCL;
-                             vmaw->fd = qemu_create(filename, oflags, 0644, errp);
-                         }
-            '')
+            "${pkgs.fetchFromGitHub {
+              owner = "proxmox";
+              repo = "pve-qemu";
+              rev = "71dd2d48f9122e60e4c0a8480122a27aab15dc70";
+              hash = "sha256-Q8AxNv4geDdlbVIWphRO5P3ESo0SGgvUpVPmPJzubJM=";
+            }}/debian/patches/pve/0027-PVE-Backup-add-vma-backup-format-code.patch"
           ];
 
           buildInputs = super.buildInputs ++ [ pkgs.libuuid ];
@@ -262,7 +271,7 @@ with lib;
         mv "vzdump-qemu-${cfg.filenameSuffix}.vma.zst" $out/
 
         mkdir -p $out/nix-support
-        echo "file vma $out/vzdump-qemu-${cfg.filenameSuffix}.vma.zst" >> $out/nix-support/hydra-build-products
+        echo "file vma $out/vzdump-qemu-${cfg.filenameSuffix}.vma.zst" > $out/nix-support/hydra-build-products
       '';
       inherit (cfg.qemuConf) additionalSpace diskSize bootSize;
       format = "raw";
@@ -298,6 +307,20 @@ with lib;
       fsType = "vfat";
     };
 
-    services.qemuGuest.enable = lib.mkDefault true;
+    networking = mkIf cfg.cloudInit.enable {
+      hostName = mkForce "";
+      useDHCP = false;
+    };
+
+    services = {
+      cloud-init = mkIf cfg.cloudInit.enable {
+        enable = true;
+        network.enable = true;
+      };
+      sshd.enable = mkDefault true;
+      qemuGuest.enable = true;
+    };
+
+    proxmox.qemuExtraConf.${cfg.cloudInit.device} = "${cfg.cloudInit.defaultStorage}:vm-9999-cloudinit,media=cdrom";
   };
 }
diff --git a/nixos/modules/virtualisation/proxmox-lxc.nix b/nixos/modules/virtualisation/proxmox-lxc.nix
index 9b9f99e5b8172..ff1c0972166cf 100644
--- a/nixos/modules/virtualisation/proxmox-lxc.nix
+++ b/nixos/modules/virtualisation/proxmox-lxc.nix
@@ -55,6 +55,8 @@ with lib;
         loader.initScript.enable = true;
       };
 
+      console.enable = true;
+
       networking = mkIf (!cfg.manageNetwork) {
         useDHCP = false;
         useHostResolvConf = false;
@@ -68,8 +70,13 @@ with lib;
         startWhenNeeded = mkDefault true;
       };
 
-      systemd.mounts = mkIf (!cfg.privileged)
-        [{ where = "/sys/kernel/debug"; enable = false; }];
+      systemd = {
+        mounts = mkIf (!cfg.privileged) [{
+          enable = false;
+          where = "/sys/kernel/debug";
+        }];
+        services."getty@".unitConfig.ConditionPathExists = [ "" "/dev/%I" ];
+      };
 
     };
 }
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index c30f4577fdd86..3b81cfaf2b8ca 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -912,7 +912,7 @@ in
           "ppc64-linux" = "tpm-spapr";
           "armv7-linux" = "tpm-tis-device";
           "aarch64-linux" = "tpm-tis-device";
-        }.${pkgs.hostPlatform.system} or (throw "Unsupported system for TPM2 emulation in QEMU"));
+        }.${pkgs.stdenv.hostPlatform.system} or (throw "Unsupported system for TPM2 emulation in QEMU"));
         defaultText = ''
           Based on the guest platform Linux system:
 
diff --git a/nixos/modules/virtualisation/spice-usb-redirection.nix b/nixos/modules/virtualisation/spice-usb-redirection.nix
index 255327f2622c9..1631a91ccf863 100644
--- a/nixos/modules/virtualisation/spice-usb-redirection.nix
+++ b/nixos/modules/virtualisation/spice-usb-redirection.nix
@@ -22,5 +22,5 @@
     };
   };
 
-  meta.maintainers = [ lib.maintainers.lheckemann ];
+  meta.maintainers = [ ];
 }
diff --git a/nixos/modules/virtualisation/vagrant-guest.nix b/nixos/modules/virtualisation/vagrant-guest.nix
index 2fad376086e34..120a2a2324d21 100644
--- a/nixos/modules/virtualisation/vagrant-guest.nix
+++ b/nixos/modules/virtualisation/vagrant-guest.nix
@@ -11,8 +11,7 @@ let
     #!${pkgs.runtimeShell}
     if [ ! -e ~/.ssh/authorized_keys ]; then
       mkdir -m 0700 -p ~/.ssh
-      echo "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key" >> ~/.ssh/authorized_keys
-      chmod 0600 ~/.ssh/authorized_keys
+      install -m 0600 <(echo "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key") ~/.ssh/authorized_keys
     fi
   '';
 in
diff --git a/nixos/modules/virtualisation/virtualbox-guest.nix b/nixos/modules/virtualisation/virtualbox-guest.nix
index 649ff3abb9ae9..b4933cffa2c0c 100644
--- a/nixos/modules/virtualisation/virtualbox-guest.nix
+++ b/nixos/modules/virtualisation/virtualbox-guest.nix
@@ -31,7 +31,9 @@ let
   };
 in
 {
-  ###### interface
+  imports = [
+    (mkRenamedOptionModule [ "virtualisation" "virtualbox" "guest" "draganddrop" ] [ "virtualisation" "virtualbox" "guest" "dragAndDrop" ])
+  ];
 
   options.virtualisation.virtualbox.guest = {
     enable = mkOption {
@@ -52,7 +54,7 @@ in
       description = "Whether to enable seamless mode. When activated windows from the guest appear next to the windows of the host.";
     };
 
-    draganddrop = mkOption {
+    dragAndDrop = mkOption {
       default = true;
       type = types.bool;
       description = "Whether to enable drag and drop support.";
@@ -111,5 +113,10 @@ in
         systemd.user.services.virtualboxClientSeamless = mkVirtualBoxUserService "--seamless";
       }
     )
+    (
+      mkIf cfg.dragAndDrop {
+        systemd.user.services.virtualboxClientDragAndDrop = mkVirtualBoxUserService "--draganddrop";
+      }
+    )
   ]);
 }
diff --git a/nixos/modules/virtualisation/virtualbox-host.nix b/nixos/modules/virtualisation/virtualbox-host.nix
index 609799995c527..a34fe132ba7e1 100644
--- a/nixos/modules/virtualisation/virtualbox-host.nix
+++ b/nixos/modules/virtualisation/virtualbox-host.nix
@@ -134,7 +134,7 @@ in
     assertions = [
       {
         assertion = !cfg.addNetworkInterface;
-        message = "VirtualBox KVM only supports standard NAT networking for VMs. Please turn off virtualisation.virtualbox.host.addNetworkInferface.";
+        message = "VirtualBox KVM only supports standard NAT networking for VMs. Please turn off virtualisation.virtualbox.host.addNetworkInterface.";
       }
 
       {
diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix
index d1773da9afa6f..00ab7efb03b4c 100644
--- a/nixos/release-combined.nix
+++ b/nixos/release-combined.nix
@@ -43,14 +43,14 @@ in rec {
       name = "nixos-${nixos.channel.version}";
       meta = {
         description = "Release-critical builds for the NixOS channel";
-        maintainers = with pkgs.lib.maintainers; [ eelco ];
+        maintainers = with pkgs.lib.maintainers; [ ];
       };
       constituents = pkgs.lib.concatLists [
         [ "nixos.channel" ]
         (onFullSupported "nixos.dummy")
         (onAllSupported "nixos.iso_minimal")
         (onSystems ["x86_64-linux" "aarch64-linux"] "nixos.amazonImage")
-        (onFullSupported "nixos.iso_plasma5")
+        (onFullSupported "nixos.iso_plasma6")
         (onFullSupported "nixos.iso_gnome")
         (onFullSupported "nixos.manual")
         (onSystems ["x86_64-linux"] "nixos.ova")
@@ -102,6 +102,7 @@ in rec {
         (onSystems ["x86_64-linux"] "nixos.tests.installer.swraid")
         (onSystems ["x86_64-linux"] "nixos.tests.installer.zfsroot")
         (onSystems ["x86_64-linux"] "nixos.tests.nixos-rebuild-specialisations")
+        (onFullSupported "nixos.tests.nix-misc.default")
         (onFullSupported "nixos.tests.ipv6")
         (onFullSupported "nixos.tests.keymap.azerty")
         (onFullSupported "nixos.tests.keymap.colemak")
diff --git a/nixos/release-small.nix b/nixos/release-small.nix
index 091c2b1f305be..98e36b0669e79 100644
--- a/nixos/release-small.nix
+++ b/nixos/release-small.nix
@@ -98,7 +98,7 @@ in rec {
     name = "nixos-${nixos.channel.version}";
     meta = {
       description = "Release-critical builds for the NixOS channel";
-      maintainers = [ lib.maintainers.eelco ];
+      maintainers = [ ];
     };
     constituents = lib.flatten [
       [
diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix
index d63a77fcdd23c..379496583d25d 100644
--- a/nixos/tests/acme.nix
+++ b/nixos/tests/acme.nix
@@ -99,7 +99,14 @@
               serverAliases = [ "${server}-wildcard-alias.example.test" ];
               useACMEHost = "example.test";
             };
-          };
+          } // (lib.optionalAttrs (server == "nginx") {
+            # The nginx module supports using a different key than the hostname
+            different-key = vhostBaseData // {
+              serverName = "${server}-different-key.example.test";
+              serverAliases = [ "${server}-different-key-alias.example.test" ];
+              enableACME = true;
+            };
+          });
         };
 
         # Used to determine if service reload was triggered
@@ -385,8 +392,6 @@ in {
   testScript = { nodes, ... }:
     let
       caDomain = nodes.acme.test-support.acme.caDomain;
-      newServerSystem = nodes.webserver.config.system.build.toplevel;
-      switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test";
     in
     # Note, wait_for_unit does not work for oneshot services that do not have RemainAfterExit=true,
     # this is because a oneshot goes from inactive => activating => inactive, and never
@@ -538,6 +543,12 @@ in {
           check_fullchain(webserver, "http.example.test")
           check_issuer(webserver, "http.example.test", "pebble")
 
+      # Perform account hash test
+      with subtest("Assert that account hash didn't unexpected change"):
+          hash = webserver.succeed("ls /var/lib/acme/.lego/accounts/")
+          print("Account hash: " + hash)
+          assert hash.strip() == "d590213ed52603e9128d"
+
       # Perform renewal test
       with subtest("Can renew certificates when they expire"):
           hash = webserver.succeed("sha256sum /var/lib/acme/http.example.test/cert.pem")
@@ -653,20 +664,20 @@ in {
           webserver.succeed("systemctl restart caddy.service")
           check_connection_key_bits(client, "a.example.test", "384")
 
-      domains = ["http", "dns", "wildcard"]
-      for server, logsrc in [
-          ("nginx", "journalctl -n 30 -u nginx.service"),
-          ("httpd", "tail -n 30 /var/log/httpd/*.log"),
+      common_domains = ["http", "dns", "wildcard"]
+      for server, logsrc, domains in [
+          ("nginx", "journalctl -n 30 -u nginx.service", common_domains + ["different-key"]),
+          ("httpd", "tail -n 30 /var/log/httpd/*.log", common_domains),
       ]:
           wait_for_server = lambda: webserver.wait_for_unit(f"{server}.service")
           with subtest(f"Works with {server}"):
               try:
                   switch_to(webserver, server)
-                  # Skip wildcard domain for this check ([:-1])
-                  for domain in domains[:-1]:
-                      webserver.wait_for_unit(
-                          f"acme-finished-{server}-{domain}.example.test.target"
-                      )
+                  for domain in domains:
+                      if domain != "wildcard":
+                          webserver.wait_for_unit(
+                              f"acme-finished-{server}-{domain}.example.test.target"
+                          )
               except Exception as err:
                   _, output = webserver.execute(
                       f"{logsrc} && ls -al /var/lib/acme/acme-challenge"
@@ -676,8 +687,9 @@ in {
 
               wait_for_server()
 
-              for domain in domains[:-1]:
-                  check_issuer(webserver, f"{server}-{domain}.example.test", "pebble")
+              for domain in domains:
+                  if domain != "wildcard":
+                      check_issuer(webserver, f"{server}-{domain}.example.test", "pebble")
               for domain in domains:
                   check_connection(client, f"{server}-{domain}.example.test")
                   check_connection(client, f"{server}-{domain}-alias.example.test")
diff --git a/nixos/tests/activation/etc-overlay-immutable.nix b/nixos/tests/activation/etc-overlay-immutable.nix
index f347f9cf8efe2..f0abf70d350ff 100644
--- a/nixos/tests/activation/etc-overlay-immutable.nix
+++ b/nixos/tests/activation/etc-overlay-immutable.nix
@@ -13,6 +13,7 @@
     users.mutableUsers = false;
     boot.initrd.systemd.enable = true;
     boot.kernelPackages = pkgs.linuxPackages_latest;
+    time.timeZone = "Utc";
 
     specialisation.new-generation.configuration = {
       environment.etc."newgen".text = "newgen";
@@ -23,6 +24,9 @@
     with subtest("/etc is mounted as an overlay"):
       machine.succeed("findmnt --kernel --type overlay /etc")
 
+    with subtest("direct symlinks point to the target without indirection"):
+      assert machine.succeed("readlink -n /etc/localtime") == "/etc/zoneinfo/Utc"
+
     with subtest("switching to the same generation"):
       machine.succeed("/run/current-system/bin/switch-to-configuration test")
 
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index d4da32c44990f..1db9f702e2416 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -115,6 +115,7 @@ in {
   akkoma = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./akkoma.nix {};
   akkoma-confined = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./akkoma.nix { confined = true; };
   alice-lg = handleTest ./alice-lg.nix {};
+  alloy = handleTest ./alloy.nix {};
   allTerminfo = handleTest ./all-terminfo.nix {};
   alps = handleTest ./alps.nix {};
   amazon-init-shell = handleTest ./amazon-init-shell.nix {};
@@ -129,7 +130,9 @@ in {
   appliance-repart-image = runTest ./appliance-repart-image.nix;
   apparmor = handleTest ./apparmor.nix {};
   archi = handleTest ./archi.nix {};
+  aria2 = handleTest ./aria2.nix {};
   armagetronad = handleTest ./armagetronad.nix {};
+  artalk = handleTest ./artalk.nix {};
   atd = handleTest ./atd.nix {};
   atop = handleTest ./atop.nix {};
   atuin = handleTest ./atuin.nix {};
@@ -144,6 +147,7 @@ in {
   bcachefs = handleTestOn ["x86_64-linux" "aarch64-linux"] ./bcachefs.nix {};
   beanstalkd = handleTest ./beanstalkd.nix {};
   bees = handleTest ./bees.nix {};
+  benchexec = handleTest ./benchexec.nix {};
   binary-cache = handleTest ./binary-cache.nix {};
   bind = handleTest ./bind.nix {};
   bird = handleTest ./bird.nix {};
@@ -204,6 +208,7 @@ in {
   code-server = handleTest ./code-server.nix {};
   coder = handleTest ./coder.nix {};
   collectd = handleTest ./collectd.nix {};
+  commafeed = handleTest ./commafeed.nix {};
   connman = handleTest ./connman.nix {};
   consul = handleTest ./consul.nix {};
   consul-template = handleTest ./consul-template.nix {};
@@ -243,6 +248,7 @@ in {
   deepin = handleTest ./deepin.nix {};
   deluge = handleTest ./deluge.nix {};
   dendrite = handleTest ./matrix/dendrite.nix {};
+  devpi-server = handleTest ./devpi-server.nix {};
   dex-oidc = handleTest ./dex-oidc.nix {};
   dhparams = handleTest ./dhparams.nix {};
   disable-installer-tools = handleTest ./disable-installer-tools.nix {};
@@ -308,6 +314,7 @@ in {
   fenics = handleTest ./fenics.nix {};
   ferm = handleTest ./ferm.nix {};
   ferretdb = handleTest ./ferretdb.nix {};
+  filesender = handleTest ./filesender.nix {};
   filesystems-overlayfs = runTest ./filesystems-overlayfs.nix;
   firefly-iii = handleTest ./firefly-iii.nix {};
   firefox = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox; };
@@ -321,6 +328,7 @@ in {
   firewall-nftables = handleTest ./firewall.nix { nftables = true; };
   fish = handleTest ./fish.nix {};
   flannel = handleTestOn ["x86_64-linux"] ./flannel.nix {};
+  flood = handleTest ./flood.nix {};
   floorp = handleTest ./firefox.nix { firefoxPackage = pkgs.floorp; };
   fluentd = handleTest ./fluentd.nix {};
   fluidd = handleTest ./fluidd.nix {};
@@ -374,6 +382,7 @@ in {
   grafana-agent = handleTest ./grafana-agent.nix {};
   graphite = handleTest ./graphite.nix {};
   graylog = handleTest ./graylog.nix {};
+  greetd-no-shadow = handleTest ./greetd-no-shadow.nix {};
   grocy = handleTest ./grocy.nix {};
   grow-partition = runTest ./grow-partition.nix;
   grub = handleTest ./grub.nix {};
@@ -405,6 +414,7 @@ in {
   pyload = handleTest ./pyload.nix {};
   oci-containers = handleTestOn ["aarch64-linux" "x86_64-linux"] ./oci-containers.nix {};
   odoo = handleTest ./odoo.nix {};
+  odoo16 = handleTest ./odoo.nix { package = pkgs.odoo16; };
   odoo15 = handleTest ./odoo.nix { package = pkgs.odoo15; };
   # 9pnet_virtio used to mount /nix partition doesn't support
   # hibernation. This test happens to work on x86_64-linux but
@@ -424,7 +434,8 @@ in {
   icingaweb2 = handleTest ./icingaweb2.nix {};
   iftop = handleTest ./iftop.nix {};
   incron = handleTest ./incron.nix {};
-  incus = pkgs.recurseIntoAttrs (handleTest ./incus { inherit handleTestOn; });
+  incus = pkgs.recurseIntoAttrs (handleTest ./incus { inherit handleTestOn; inherit (pkgs) incus; });
+  incus-lts = pkgs.recurseIntoAttrs (handleTest ./incus { inherit handleTestOn; });
   influxdb = handleTest ./influxdb.nix {};
   influxdb2 = handleTest ./influxdb2.nix {};
   initrd-network-openvpn = handleTestOn [ "x86_64-linux" "i686-linux" ] ./initrd-network-openvpn {};
@@ -489,7 +500,8 @@ in {
   libreddit = handleTest ./libreddit.nix {};
   librenms = handleTest ./librenms.nix {};
   libresprite = handleTest ./libresprite.nix {};
-  libreswan = handleTest ./libreswan.nix {};
+  libreswan = runTest ./libreswan.nix;
+  libreswan-nat = runTest ./libreswan-nat.nix;
   librewolf = handleTest ./firefox.nix { firefoxPackage = pkgs.librewolf; };
   libuiohook = handleTest ./libuiohook.nix {};
   libvirtd = handleTest ./libvirtd.nix {};
@@ -510,6 +522,7 @@ in {
   lxd-image-server = handleTest ./lxd-image-server.nix {};
   #logstash = handleTest ./logstash.nix {};
   lomiri = handleTest ./lomiri.nix {};
+  lomiri-filemanager-app = runTest ./lomiri-filemanager-app.nix;
   lomiri-system-settings = handleTest ./lomiri-system-settings.nix {};
   lorri = handleTest ./lorri/default.nix {};
   maddy = discoverTests (import ./maddy { inherit handleTest; });
@@ -585,7 +598,7 @@ in {
   mysql-backup = handleTest ./mysql/mysql-backup.nix {};
   mysql-replication = handleTest ./mysql/mysql-replication.nix {};
   n8n = handleTest ./n8n.nix {};
-  nagios = handleTest ./nagios.nix {};
+  nagios = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./nagios.nix {};
   nar-serve = handleTest ./nar-serve.nix {};
   nat.firewall = handleTest ./nat.nix { withFirewall = true; };
   nat.standalone = handleTest ./nat.nix { withFirewall = false; };
@@ -610,6 +623,7 @@ in {
   # TODO: put in networking.nix after the test becomes more complete
   networkingProxy = handleTest ./networking-proxy.nix {};
   nextcloud = handleTest ./nextcloud {};
+  nextjs-ollama-llm-ui = runTest ./web-apps/nextjs-ollama-llm-ui.nix;
   nexus = handleTest ./nexus.nix {};
   # TODO: Test nfsv3 + Kerberos
   nfs3 = handleTest ./nfs { version = 3; };
@@ -636,6 +650,7 @@ in {
   nitter = handleTest ./nitter.nix {};
   nix-config = handleTest ./nix-config.nix {};
   nix-ld = handleTest ./nix-ld.nix {};
+  nix-misc = handleTest ./nix/misc.nix {};
   nix-serve = handleTest ./nix-serve.nix {};
   nix-serve-ssh = handleTest ./nix-serve-ssh.nix {};
   nixops = handleTest ./nixops/default.nix {};
@@ -684,6 +699,7 @@ in {
   outline = handleTest ./outline.nix {};
   image-contents = handleTest ./image-contents.nix {};
   openvscode-server = handleTest ./openvscode-server.nix {};
+  open-webui = runTest ./open-webui.nix;
   orangefs = handleTest ./orangefs.nix {};
   os-prober = handleTestOn ["x86_64-linux"] ./os-prober.nix {};
   osquery = handleTestOn ["x86_64-linux"] ./osquery.nix {};
@@ -710,6 +726,7 @@ in {
   pg_anonymizer = handleTest ./pg_anonymizer.nix {};
   pgadmin4 = handleTest ./pgadmin4.nix {};
   pgbouncer = handleTest ./pgbouncer.nix {};
+  pghero = runTest ./pghero.nix;
   pgjwt = handleTest ./pgjwt.nix {};
   pgmanage = handleTest ./pgmanage.nix {};
   pgvecto-rs = handleTest ./pgvecto-rs.nix {};
@@ -756,8 +773,9 @@ in {
   pretix = runTest ./web-apps/pretix.nix;
   printing-socket = handleTest ./printing.nix { socket = true; };
   printing-service = handleTest ./printing.nix { socket = false; };
+  private-gpt = handleTest ./private-gpt.nix {};
   privoxy = handleTest ./privoxy.nix {};
-  prometheus = handleTest ./prometheus.nix {};
+  prometheus = handleTest ./prometheus {};
   prometheus-exporters = handleTest ./prometheus-exporters.nix {};
   prosody = handleTest ./xmpp/prosody.nix {};
   prosody-mysql = handleTest ./xmpp/prosody-mysql.nix {};
@@ -775,9 +793,10 @@ in {
   qgis = handleTest ./qgis.nix { qgisPackage = pkgs.qgis; };
   qgis-ltr = handleTest ./qgis.nix { qgisPackage = pkgs.qgis-ltr; };
   qownnotes = handleTest ./qownnotes.nix {};
-  qtile = handleTest ./qtile.nix {};
+  qtile = handleTestOn ["x86_64-linux" "aarch64-linux"] ./qtile.nix {};
   quake3 = handleTest ./quake3.nix {};
   quicktun = handleTest ./quicktun.nix {};
+  quickwit = handleTest ./quickwit.nix {};
   quorum = handleTest ./quorum.nix {};
   rabbitmq = handleTest ./rabbitmq.nix {};
   radarr = handleTest ./radarr.nix {};
@@ -788,10 +807,12 @@ in {
   redis = handleTest ./redis.nix {};
   redlib = handleTest ./redlib.nix {};
   redmine = handleTest ./redmine.nix {};
+  renovate = handleTest ./renovate.nix {};
   restartByActivationScript = handleTest ./restart-by-activation-script.nix {};
   restic-rest-server = handleTest ./restic-rest-server.nix {};
   restic = handleTest ./restic.nix {};
   retroarch = handleTest ./retroarch.nix {};
+  rke2 = handleTestOn ["aarch64-linux" "x86_64-linux"] ./rke2 {};
   rkvm = handleTest ./rkvm {};
   robustirc-bridge = handleTest ./robustirc-bridge.nix {};
   roundcube = handleTest ./roundcube.nix {};
@@ -815,7 +836,7 @@ in {
   scrutiny = handleTest ./scrutiny.nix {};
   sddm = handleTest ./sddm.nix {};
   seafile = handleTest ./seafile.nix {};
-  searx = handleTest ./searx.nix {};
+  searx = runTest ./searx.nix;
   seatd = handleTest ./seatd.nix {};
   service-runner = handleTest ./service-runner.nix {};
   sftpgo = runTest ./sftpgo.nix;
@@ -870,7 +891,8 @@ in {
   swap-random-encryption = handleTest ./swap-random-encryption.nix {};
   sway = handleTest ./sway.nix {};
   swayfx = handleTest ./swayfx.nix {};
-  switchTest = handleTest ./switch-test.nix {};
+  switchTest = handleTest ./switch-test.nix { ng = false; };
+  switchTestNg = handleTest ./switch-test.nix { ng = true; };
   sympa = handleTest ./sympa.nix {};
   syncthing = handleTest ./syncthing.nix {};
   syncthing-no-settings = handleTest ./syncthing-no-settings.nix {};
@@ -883,7 +905,7 @@ in {
   systemd-binfmt = handleTestOn ["x86_64-linux"] ./systemd-binfmt.nix {};
   systemd-boot = handleTest ./systemd-boot.nix {};
   systemd-bpf = handleTest ./systemd-bpf.nix {};
-  systemd-confinement = handleTest ./systemd-confinement.nix {};
+  systemd-confinement = handleTest ./systemd-confinement {};
   systemd-coredump = handleTest ./systemd-coredump.nix {};
   systemd-cryptenroll = handleTest ./systemd-cryptenroll.nix {};
   systemd-credentials-tpm2 = handleTest ./systemd-credentials-tpm2.nix {};
@@ -922,6 +944,7 @@ in {
   systemd-oomd = handleTest ./systemd-oomd.nix {};
   systemd-portabled = handleTest ./systemd-portabled.nix {};
   systemd-repart = handleTest ./systemd-repart.nix {};
+  systemd-resolved = handleTest ./systemd-resolved.nix {};
   systemd-shutdown = handleTest ./systemd-shutdown.nix {};
   systemd-sysupdate = runTest ./systemd-sysupdate.nix;
   systemd-sysusers-mutable = runTest ./systemd-sysusers-mutable.nix;
@@ -935,6 +958,7 @@ in {
   systemd-homed = handleTest ./systemd-homed.nix {};
   systemtap = handleTest ./systemtap.nix {};
   tandoor-recipes = handleTest ./tandoor-recipes.nix {};
+  tandoor-recipes-script-name = handleTest ./tandoor-recipes-script-name.nix {};
   tang = handleTest ./tang.nix {};
   taskserver = handleTest ./taskserver.nix {};
   tayga = handleTest ./tayga.nix {};
@@ -944,6 +968,7 @@ in {
   teleport = handleTest ./teleport.nix {};
   thelounge = handleTest ./thelounge.nix {};
   terminal-emulators = handleTest ./terminal-emulators.nix {};
+  thanos = handleTest ./thanos.nix {};
   tiddlywiki = handleTest ./tiddlywiki.nix {};
   tigervnc = handleTest ./tigervnc.nix {};
   timescaledb = handleTest ./timescaledb.nix {};
@@ -958,7 +983,7 @@ in {
   traefik = handleTestOn ["aarch64-linux" "x86_64-linux"] ./traefik.nix {};
   trafficserver = handleTest ./trafficserver.nix {};
   transfer-sh = handleTest ./transfer-sh.nix {};
-  transmission = handleTest ./transmission.nix { transmission = pkgs.transmission; };
+  transmission_3 = handleTest ./transmission.nix { transmission = pkgs.transmission_3; };
   transmission_4 = handleTest ./transmission.nix { transmission = pkgs.transmission_4; };
   # tracee requires bpf
   tracee = handleTestOn ["x86_64-linux"] ./tracee.nix {};
@@ -994,12 +1019,13 @@ in {
   v2ray = handleTest ./v2ray.nix {};
   varnish60 = handleTest ./varnish.nix { package = pkgs.varnish60; };
   varnish74 = handleTest ./varnish.nix { package = pkgs.varnish74; };
+  varnish75 = handleTest ./varnish.nix { package = pkgs.varnish75; };
   vault = handleTest ./vault.nix {};
   vault-agent = handleTest ./vault-agent.nix {};
   vault-dev = handleTest ./vault-dev.nix {};
   vault-postgresql = handleTest ./vault-postgresql.nix {};
-  vaultwarden = handleTest ./vaultwarden.nix {};
-  vector = handleTest ./vector.nix {};
+  vaultwarden = discoverTests (import ./vaultwarden.nix);
+  vector = handleTest ./vector {};
   vengi-tools = handleTest ./vengi-tools.nix {};
   victoriametrics = handleTest ./victoriametrics.nix {};
   vikunja = handleTest ./vikunja.nix {};
@@ -1022,6 +1048,7 @@ in {
   wordpress = handleTest ./wordpress.nix {};
   wrappers = handleTest ./wrappers.nix {};
   writefreely = handleTest ./web-apps/writefreely.nix {};
+  wstunnel = runTest ./wstunnel.nix;
   xandikos = handleTest ./xandikos.nix {};
   xautolock = handleTest ./xautolock.nix {};
   xfce = handleTest ./xfce.nix {};
@@ -1035,7 +1062,9 @@ in {
   xterm = handleTest ./xterm.nix {};
   xxh = handleTest ./xxh.nix {};
   yabar = handleTest ./yabar.nix {};
+  ydotool = handleTest ./ydotool.nix {};
   yggdrasil = handleTest ./yggdrasil.nix {};
+  your_spotify = handleTest ./your_spotify.nix {};
   zammad = handleTest ./zammad.nix {};
   zeronet-conservancy = handleTest ./zeronet-conservancy.nix {};
   zfs = handleTest ./zfs.nix {};
diff --git a/nixos/tests/alloy.nix b/nixos/tests/alloy.nix
new file mode 100644
index 0000000000000..d87492127d5bb
--- /dev/null
+++ b/nixos/tests/alloy.nix
@@ -0,0 +1,32 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }:
+
+  let
+    nodes = {
+      machine = {
+        services.alloy = {
+          enable = true;
+        };
+        environment.etc."alloy/config.alloy".text = "";
+      };
+    };
+  in
+  {
+    name = "alloy";
+
+    meta = with lib.maintainers; {
+      maintainers = [ flokli hbjydev ];
+    };
+
+    inherit nodes;
+
+    testScript = ''
+      start_all()
+
+      machine.wait_for_unit("alloy.service")
+      machine.wait_for_open_port(12345)
+      machine.succeed(
+          "curl -sSfN http://127.0.0.1:12345/-/healthy"
+      )
+      machine.shutdown()
+    '';
+  })
diff --git a/nixos/tests/archi.nix b/nixos/tests/archi.nix
index 59f2e940c0050..a8cb1c503d4f7 100644
--- a/nixos/tests/archi.nix
+++ b/nixos/tests/archi.nix
@@ -24,7 +24,9 @@ import ./make-test-python.nix ({ lib, ... }: {
          machine.wait_for_window("Archi")
 
          # wait till main UI is open
-         machine.wait_for_text("Welcome to Archi")
+         # since OCR seems to be buggy wait_for_text was replaced by sleep, issue: #302965
+         # machine.wait_for_text("Welcome to Archi")
+         machine.sleep(20)
 
          machine.screenshot("welcome-screen")
   '';
diff --git a/nixos/tests/aria2.nix b/nixos/tests/aria2.nix
new file mode 100644
index 0000000000000..48fe2094b5dcf
--- /dev/null
+++ b/nixos/tests/aria2.nix
@@ -0,0 +1,43 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+let
+  rpcSecret = "supersecret";
+  rpc-listen-port = 6800;
+  curlBody = {
+    jsonrpc = 2.0;
+    id = 1;
+    method = "aria2.getVersion";
+    params = [ "token:${rpcSecret}" ];
+  };
+in
+rec {
+  name = "aria2";
+
+  nodes.machine = {
+    environment.etc."aria2Rpc".text = rpcSecret;
+    services.aria2 = {
+      enable = true;
+      rpcSecretFile = "/etc/aria2Rpc";
+      settings = {
+        inherit rpc-listen-port;
+        allow-overwrite = false;
+        check-integrity = true;
+        console-log-level = "warn";
+        listen-port = [{ from = 20000; to = 20010; } { from = 22222; to = 22222; }];
+        max-concurrent-downloads = 50;
+        seed-ratio = 1.2;
+        summary-interval = 0;
+      };
+    };
+  };
+
+  testScript = ''
+    machine.start()
+    machine.wait_for_unit("aria2.service")
+    curl_cmd = 'curl --fail-with-body -X POST -H "Content-Type: application/json" \
+                -d \'${builtins.toJSON curlBody}\' http://localhost:${toString rpc-listen-port}/jsonrpc'
+    print(machine.wait_until_succeeds(curl_cmd, timeout=10))
+    machine.shutdown()
+  '';
+
+  meta.maintainers = [ pkgs.lib.maintainers.timhae ];
+})
diff --git a/nixos/tests/armagetronad.nix b/nixos/tests/armagetronad.nix
index d59827354b771..ca93ce8fb6c5d 100644
--- a/nixos/tests/armagetronad.nix
+++ b/nixos/tests/armagetronad.nix
@@ -12,7 +12,7 @@ let
     { pkgs, ... }:
 
     { imports = [ ./common/user-account.nix ./common/x11.nix ];
-      hardware.opengl.driSupport = true;
+      hardware.graphics.enable = true;
       virtualisation.memorySize = 256;
       environment = {
         systemPackages = [ pkgs.armagetronad ];
diff --git a/nixos/tests/artalk.nix b/nixos/tests/artalk.nix
new file mode 100644
index 0000000000000..1338e5cd380c6
--- /dev/null
+++ b/nixos/tests/artalk.nix
@@ -0,0 +1,28 @@
+import ./make-test-python.nix (
+  { lib, pkgs, ... }:
+  {
+
+    name = "artalk";
+
+    meta = {
+      maintainers = with lib.maintainers; [ moraxyc ];
+    };
+
+    nodes.machine =
+      { pkgs, ... }:
+      {
+        environment.systemPackages = [ pkgs.curl ];
+        services.artalk = {
+          enable = true;
+        };
+      };
+
+    testScript = ''
+      machine.wait_for_unit("artalk.service")
+
+      machine.wait_for_open_port(23366)
+
+      machine.succeed("curl --fail --max-time 10 http://127.0.0.1:23366/")
+    '';
+  }
+)
diff --git a/nixos/tests/avahi.nix b/nixos/tests/avahi.nix
index d8f4d13340fbc..4ae2f919f2f7d 100644
--- a/nixos/tests/avahi.nix
+++ b/nixos/tests/avahi.nix
@@ -9,7 +9,7 @@
 import ./make-test-python.nix {
   name = "avahi";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ eelco ];
+    maintainers = [ ];
   };
 
   nodes = let
diff --git a/nixos/tests/ayatana-indicators.nix b/nixos/tests/ayatana-indicators.nix
index 5709ad2a1af69..ccb1e059a6973 100644
--- a/nixos/tests/ayatana-indicators.nix
+++ b/nixos/tests/ayatana-indicators.nix
@@ -28,11 +28,14 @@ in {
       enable = true;
       packages = with pkgs; [
         ayatana-indicator-datetime
+        ayatana-indicator-display
         ayatana-indicator-messages
+        ayatana-indicator-power
         ayatana-indicator-session
+        ayatana-indicator-sound
       ] ++ (with pkgs.lomiri; [
         lomiri-indicator-network
-        telephony-service
+        # telephony-service # currently broken: https://github.com/NixOS/nixpkgs/pull/314043
       ]);
     };
 
@@ -40,6 +43,8 @@ in {
 
     services.accounts-daemon.enable = true; # messages
 
+    hardware.pulseaudio.enable = true; # sound
+
     # Lomiri-ish setup for Lomiri indicators
     # TODO move into a Lomiri module, once the package set is far enough for the DE to start
 
@@ -91,7 +96,7 @@ in {
 
     # Now check if all indicators were brought up successfully, and kill them for later
   '' + (runCommandOverAyatanaIndicators (service: let serviceExec = builtins.replaceStrings [ "." ] [ "-" ] service; in ''
-    machine.succeed("pgrep -u ${user} -f ${serviceExec}")
+    machine.wait_until_succeeds("pgrep -u ${user} -f ${serviceExec}")
     machine.succeed("pkill -f ${serviceExec}")
   '')) + ''
 
diff --git a/nixos/tests/benchexec.nix b/nixos/tests/benchexec.nix
new file mode 100644
index 0000000000000..3fc9ebc2c35f5
--- /dev/null
+++ b/nixos/tests/benchexec.nix
@@ -0,0 +1,54 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }:
+let
+  user = "alice";
+in
+{
+  name = "benchexec";
+
+  nodes.benchexec = {
+    imports = [ ./common/user-account.nix ];
+
+    programs.benchexec = {
+      enable = true;
+      users = [ user ];
+    };
+  };
+
+  testScript = { ... }:
+    let
+      runexec = lib.getExe' pkgs.benchexec "runexec";
+      echo = builtins.toString pkgs.benchexec;
+      test = lib.getExe (pkgs.writeShellApplication rec {
+        name = "test";
+        meta.mainProgram = name;
+        text = "echo '${echo}'";
+      });
+      wd = "/tmp";
+      stdout = "${wd}/runexec.out";
+      stderr = "${wd}/runexec.err";
+    in
+    ''
+      start_all()
+      machine.wait_for_unit("multi-user.target")
+      benchexec.succeed(''''\
+          systemd-run \
+            --property='StandardOutput=file:${stdout}' \
+            --property='StandardError=file:${stderr}' \
+            --unit=runexec --wait --user --machine='${user}@' \
+            --working-directory ${wd} \
+          '${runexec}' \
+            --debug \
+            --read-only-dir / \
+            --hidden-dir /home \
+            '${test}' \
+      '''')
+      benchexec.succeed("grep -s '${echo}' ${wd}/output.log")
+      benchexec.succeed("test \"$(grep -Ec '((start|wall|cpu)time|memory)=' ${stdout})\" = 4")
+      benchexec.succeed("! grep -E '(WARNING|ERROR)' ${stderr}")
+    '';
+
+  interactive.nodes.benchexec.services.kmscon = {
+    enable = true;
+    fonts = [{ name = "Fira Code"; package = pkgs.fira-code; }];
+  };
+})
diff --git a/nixos/tests/bittorrent.nix b/nixos/tests/bittorrent.nix
index 473b05d4c98e8..b5f5982743a13 100644
--- a/nixos/tests/bittorrent.nix
+++ b/nixos/tests/bittorrent.nix
@@ -36,7 +36,7 @@ in
 {
   name = "bittorrent";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ domenkozar eelco rob bobvanderlinden ];
+    maintainers = [ domenkozar rob bobvanderlinden ];
   };
 
   nodes = {
diff --git a/nixos/tests/cagebreak.nix b/nixos/tests/cagebreak.nix
index 1fef7cb57cfc5..4d7664c1505f3 100644
--- a/nixos/tests/cagebreak.nix
+++ b/nixos/tests/cagebreak.nix
@@ -14,9 +14,7 @@ in
   };
 
   nodes.machine = { config, ... }:
-  let
-    alice = config.users.users.alice;
-  in {
+  {
     # Automatically login on tty1 as a normal user:
     imports = [ ./common/user-account.nix ];
     services.getty.autologinUser = "alice";
@@ -31,7 +29,7 @@ in
       fi
     '';
 
-    hardware.opengl.enable = true;
+    hardware.graphics.enable = true;
     programs.xwayland.enable = true;
     security.polkit.enable = true;
     environment.systemPackages = [ pkgs.cagebreak pkgs.wayland-utils ];
diff --git a/nixos/tests/castopod.nix b/nixos/tests/castopod.nix
index 3257cd3d363c7..57e035354d23e 100644
--- a/nixos/tests/castopod.nix
+++ b/nixos/tests/castopod.nix
@@ -98,6 +98,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
             driver = Firefox(options=options, service=service)
             driver = Firefox(options=options)
             driver.implicitly_wait(30)
+            driver.set_page_load_timeout(60)
 
             # install ##########################################################
 
@@ -207,7 +208,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
             text = ''
               out=/tmp/podcast.mp3
               sox -n -r 48000 -t wav - synth ${targetPodcastDuration} sine 440 `
-              `| lame --noreplaygain -cbr -q 9 -b 320 - $out
+              `| lame --noreplaygain --cbr -q 9 -b 320 - $out
               FILESIZE="$(stat -c%s $out)"
               [ "$FILESIZE" -gt 0 ]
               [ "$FILESIZE" -le "${toString targetPodcastSize}" ]
diff --git a/nixos/tests/centrifugo.nix b/nixos/tests/centrifugo.nix
index 45c2904f5585f..8e940f74caa4b 100644
--- a/nixos/tests/centrifugo.nix
+++ b/nixos/tests/centrifugo.nix
@@ -24,12 +24,10 @@ in
             engine = "redis";
             # Connect to local Redis shard via Unix socket.
             redis_address =
-              let
-                otherNodes = lib.take index nodes ++ lib.drop (index + 1) nodes;
-              in
-              map (name: "${name}:${toString redisPort}") otherNodes ++ [
+              let toRedisAddresses = map (name: "${name}:${toString redisPort}"); in
+              toRedisAddresses (lib.take index nodes) ++ [
                 "unix://${config.services.redis.servers.centrifugo.unixSocket}"
-              ];
+              ] ++ toRedisAddresses (lib.drop (index + 1) nodes);
             usage_stats_disable = true;
             api_insecure = true;
           };
diff --git a/nixos/tests/clatd.nix b/nixos/tests/clatd.nix
index 00021d87ba5f4..d0d504851ce4e 100644
--- a/nixos/tests/clatd.nix
+++ b/nixos/tests/clatd.nix
@@ -6,8 +6,8 @@
 # Client | clat    Address: 192.0.0.1/32  (configured via clatd)
 #        |         Route:   default
 #        |
-#        | eth1    Address: 2001:db8::2/64
-#        |  |      Route:   default via 2001:db8::1
+#        | eth1    Address: Assigned via SLAAC within 2001:db8::/64
+#        |  |      Route:   default via IPv6LL address
 #        +--|---
 #           | VLAN 3
 #        +--|---
@@ -31,7 +31,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
 {
   name = "clatd";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ hax404 ];
+    maintainers = [ hax404 jmbaur ];
   };
 
   nodes = {
@@ -59,25 +59,26 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
             "100.64.0.2/24"
           ];
           routes = [
-            { routeConfig = { Destination = "192.0.2.0/24"; Gateway = "100.64.0.1"; }; }
+            { Destination = "192.0.2.0/24"; Gateway = "100.64.0.1"; }
           ];
         };
       };
     };
 
     # The router is configured with static IPv4 addresses towards the server
-    # and IPv6 addresses towards the client. For NAT64, the Well-Known prefix
-    # 64:ff9b::/96 is used. NAT64 is done with TAYGA which provides the
-    # tun-interface nat64 and does the translation over it. The IPv6 packets
-    # are sent to this interfaces and received as IPv4 packets and vice versa.
-    # As TAYGA only translates IPv6 addresses to dedicated IPv4 addresses, it
-    # needs a pool of IPv4 addresses which must be at least as big as the
-    # expected amount of clients. In this test, the packets from the pool are
-    # directly routed towards the client. In normal cases, there would be a
-    # second source NAT44 to map all clients behind one IPv4 address.
+    # and IPv6 addresses towards the client. DNS64 is exposed towards the
+    # client so clatd is able to auto-discover the PLAT prefix. For NAT64, the
+    # Well-Known prefix 64:ff9b::/96 is used. NAT64 is done with TAYGA which
+    # provides the tun-interface nat64 and does the translation over it. The
+    # IPv6 packets are sent to this interfaces and received as IPv4 packets and
+    # vice versa. As TAYGA only translates IPv6 addresses to dedicated IPv4
+    # addresses, it needs a pool of IPv4 addresses which must be at least as
+    # big as the expected amount of clients. In this test, the packets from the
+    # pool are directly routed towards the client. In normal cases, there would
+    # be a second source NAT44 to map all clients behind one IPv4 address.
     router = {
       boot.kernel.sysctl = {
-        "net.ipv4.ip_forward" = 1;
+        "net.ipv4.conf.all.forwarding" = 1;
         "net.ipv6.conf.all.forwarding" = 1;
       };
 
@@ -102,6 +103,36 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
         };
       };
 
+      systemd.network.networks."40-eth2" = {
+        networkConfig.IPv6SendRA = true;
+        ipv6Prefixes = [ { Prefix = "2001:db8::/64"; } ];
+        ipv6PREF64Prefixes = [ { Prefix = "64:ff9b::/96"; } ];
+        ipv6SendRAConfig = {
+          EmitDNS = true;
+          DNS = "_link_local";
+        };
+      };
+
+      services.resolved.extraConfig = ''
+        DNSStubListener=no
+      '';
+
+      networking.extraHosts = ''
+        192.0.0.171 ipv4only.arpa
+        192.0.0.170 ipv4only.arpa
+      '';
+
+      services.coredns = {
+        enable = true;
+        config = ''
+          .:53 {
+            bind ::
+            hosts /etc/hosts
+            dns64 64:ff9b::/96
+          }
+        '';
+      };
+
       services.tayga = {
         enable = true;
         ipv4 = {
@@ -127,10 +158,10 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
       };
     };
 
-    # The client is configured with static IPv6 addresses. It has also a static
-    # default route towards the router. To reach the IPv4-only server, the
-    # client starts the clat daemon which starts and configures the local
-    # IPv4 -> IPv6 translation via Tayga.
+    # The client uses SLAAC to assign IPv6 addresses. To reach the IPv4-only
+    # server, the client starts the clat daemon which starts and configures the
+    # local IPv4 -> IPv6 translation via Tayga after discovering the PLAT
+    # prefix via DNS64.
     client = {
       virtualisation.vlans = [
         3 # towards router
@@ -145,25 +176,36 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
         enable = true;
         networks."vlan1" = {
           matchConfig.Name = "eth1";
-          address = [
-            "2001:db8::2/64"
-          ];
-          routes = [
-            { routeConfig = { Destination = "::/0"; Gateway = "2001:db8::1"; }; }
-          ];
+
+          # NOTE: clatd does not actually use the PREF64 prefix discovered by
+          # systemd-networkd (nor does systemd-networkd do anything with it,
+          # yet), but we set this to confirm it works. See the test script
+          # below.
+          ipv6AcceptRAConfig.UsePREF64 = true;
         };
       };
 
       services.clatd = {
         enable = true;
-        settings.plat-prefix = "64:ff9b::/96";
+        # NOTE: Perl's Net::DNS resolver does not seem to work well querying
+        # for AAAA records to systemd-resolved's default IPv4 bind address
+        # (127.0.0.53), so we add an IPv6 listener address to systemd-resolved
+        # and tell clatd to use that instead.
+        settings.dns64-servers = "::1";
       };
 
+      # Allow clatd to find dns server. See comment above.
+      services.resolved.extraConfig = ''
+        DNSStubListenerExtra=::1
+      '';
+
       environment.systemPackages = [ pkgs.mtr ];
     };
   };
 
   testScript = ''
+    import json
+
     start_all()
 
     # wait for all machines to start up
@@ -178,6 +220,11 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
         'journalctl -u clatd -e | grep -q "Starting up TAYGA, using config file"'
       )
 
+    with subtest("networkd exports PREF64 prefix"):
+      assert json.loads(client.succeed("networkctl status eth1 --json=short"))[
+          "NDisc"
+      ]["PREF64"][0]["Prefix"] == [0x0, 0x64, 0xFF, 0x9B] + ([0] * 12)
+
     with subtest("Test ICMP"):
       client.wait_until_succeeds("ping -c 3 100.64.0.2 >&2")
 
diff --git a/nixos/tests/commafeed.nix b/nixos/tests/commafeed.nix
new file mode 100644
index 0000000000000..7b65720818a9b
--- /dev/null
+++ b/nixos/tests/commafeed.nix
@@ -0,0 +1,21 @@
+import ./make-test-python.nix (
+  { lib, ... }:
+  {
+    name = "commafeed";
+
+    nodes.server = {
+      services.commafeed = {
+        enable = true;
+      };
+    };
+
+    testScript = ''
+      server.start()
+      server.wait_for_unit("commafeed.service")
+      server.wait_for_open_port(8082)
+      server.succeed("curl --fail --silent http://localhost:8082")
+    '';
+
+    meta.maintainers = [ lib.maintainers.raroh73 ];
+  }
+)
diff --git a/nixos/tests/containers-bridge.nix b/nixos/tests/containers-bridge.nix
index d2e16299edaad..3001db33ba5a3 100644
--- a/nixos/tests/containers-bridge.nix
+++ b/nixos/tests/containers-bridge.nix
@@ -8,7 +8,7 @@ in
 import ./make-test-python.nix ({ pkgs, lib, ... }: {
   name = "containers-bridge";
   meta = {
-    maintainers = with lib.maintainers; [ aristid aszlig eelco kampfschlaefer ];
+    maintainers = with lib.maintainers; [ aristid aszlig kampfschlaefer ];
   };
 
   nodes.machine =
diff --git a/nixos/tests/containers-imperative.nix b/nixos/tests/containers-imperative.nix
index fff00e4f73a85..ea1046b40354e 100644
--- a/nixos/tests/containers-imperative.nix
+++ b/nixos/tests/containers-imperative.nix
@@ -1,7 +1,7 @@
 import ./make-test-python.nix ({ pkgs, lib, ... }: {
   name = "containers-imperative";
   meta = {
-    maintainers = with lib.maintainers; [ aristid aszlig eelco kampfschlaefer ];
+    maintainers = with lib.maintainers; [ aristid aszlig kampfschlaefer ];
   };
 
   nodes.machine =
diff --git a/nixos/tests/containers-ip.nix b/nixos/tests/containers-ip.nix
index ecff99a3f0c25..034e5d660415a 100644
--- a/nixos/tests/containers-ip.nix
+++ b/nixos/tests/containers-ip.nix
@@ -14,7 +14,7 @@ let
 in import ./make-test-python.nix ({ pkgs, lib, ... }: {
   name = "containers-ipv4-ipv6";
   meta = {
-    maintainers = with lib.maintainers; [ aristid aszlig eelco kampfschlaefer ];
+    maintainers = with lib.maintainers; [ aristid aszlig kampfschlaefer ];
   };
 
   nodes.machine =
diff --git a/nixos/tests/containers-portforward.nix b/nixos/tests/containers-portforward.nix
index b8c7aabc5a50b..1a9880fe93133 100644
--- a/nixos/tests/containers-portforward.nix
+++ b/nixos/tests/containers-portforward.nix
@@ -8,7 +8,7 @@ in
 import ./make-test-python.nix ({ pkgs, lib, ... }: {
   name = "containers-portforward";
   meta = {
-    maintainers = with lib.maintainers; [ aristid aszlig eelco kampfschlaefer ianwookim ];
+    maintainers = with lib.maintainers; [ aristid aszlig kampfschlaefer ianwookim ];
   };
 
   nodes.machine =
diff --git a/nixos/tests/crabfit.nix b/nixos/tests/crabfit.nix
index 0cd0741f6fa4b..0daf47d52f25d 100644
--- a/nixos/tests/crabfit.nix
+++ b/nixos/tests/crabfit.nix
@@ -4,7 +4,7 @@ import ./make-test-python.nix (
   {
     name = "crabfit";
 
-    meta.maintainers = with lib.maintainers; [ thubrecht ];
+    meta.maintainers = with lib.maintainers; [ ];
 
     nodes = {
       machine =
diff --git a/nixos/tests/devpi-server.nix b/nixos/tests/devpi-server.nix
new file mode 100644
index 0000000000000..2a16d49724dbc
--- /dev/null
+++ b/nixos/tests/devpi-server.nix
@@ -0,0 +1,35 @@
+import ./make-test-python.nix ({pkgs, ...}: let
+  server-port = 3141;
+in {
+  name = "devpi-server";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [cafkafk];
+  };
+
+  nodes = {
+    devpi = {...}: {
+      services.devpi-server = {
+        enable = true;
+        host = "0.0.0.0";
+        port = server-port;
+        openFirewall = true;
+        secretFile = pkgs.writeText "devpi-secret" "v263P+V3YGDYUyfYL/RBURw+tCPMDw94R/iCuBNJrDhaYrZYjpA6XPFVDDH8ViN20j77y2PHoMM/U0opNkVQ2g==";
+      };
+    };
+
+    client1 = {...}: {
+      environment.systemPackages = with pkgs; [
+        devpi-client
+        jq
+      ];
+    };
+  };
+
+  testScript = ''
+    start_all()
+    devpi.wait_for_unit("devpi-server.service")
+    devpi.wait_for_open_port(${builtins.toString server-port})
+
+    client1.succeed("devpi getjson http://devpi:${builtins.toString server-port}")
+  '';
+})
diff --git a/nixos/tests/domination.nix b/nixos/tests/domination.nix
index 409a7f3029c42..9e4badd2e369d 100644
--- a/nixos/tests/domination.nix
+++ b/nixos/tests/domination.nix
@@ -10,6 +10,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     ];
 
     services.xserver.enable = true;
+    sound.enable = true;
     environment.systemPackages = [ pkgs.domination ];
   };
 
@@ -18,6 +19,9 @@ import ./make-test-python.nix ({ pkgs, ... }: {
   testScript =
     ''
       machine.wait_for_x()
+      # Add a dummy sound card, or an error reporting popup will appear,
+      # covering the main window and preventing OCR
+      machine.execute("modprobe snd-dummy")
       machine.execute("domination >&2 &")
       machine.wait_for_window("Menu")
       machine.wait_for_text(r"(New Game|Start Server|Load Game|Help Manual|Join Game|About|Play Online)")
diff --git a/nixos/tests/elk.nix b/nixos/tests/elk.nix
index b5a8cb532ae0a..87c82877fe109 100644
--- a/nixos/tests/elk.nix
+++ b/nixos/tests/elk.nix
@@ -16,7 +16,7 @@ let
     import ./make-test-python.nix ({
     inherit name;
     meta = with pkgs.lib.maintainers; {
-      maintainers = [ eelco offline basvandijk ];
+      maintainers = [ offline basvandijk ];
     };
     nodes = {
       one =
diff --git a/nixos/tests/fcitx5/default.nix b/nixos/tests/fcitx5/default.nix
index c113f2e2c052c..feea621f6b5b2 100644
--- a/nixos/tests/fcitx5/default.nix
+++ b/nixos/tests/fcitx5/default.nix
@@ -89,10 +89,13 @@ rec {
             machine.succeed("xauth merge ${xauth}")
             machine.sleep(5)
 
+            machine.wait_until_succeeds("pgrep fcitx5")
             machine.succeed("su - ${user.name} -c 'kill $(pgrep fcitx5)'")
             machine.sleep(1)
 
             machine.succeed("su - ${user.name} -c 'alacritty >&2 &'")
+            machine.wait_for_window("alice@machine")
+
             machine.succeed("su - ${user.name} -c 'fcitx5 >&2 &'")
             machine.sleep(10)
 
diff --git a/nixos/tests/filesender.nix b/nixos/tests/filesender.nix
new file mode 100644
index 0000000000000..9274ddbf7e90e
--- /dev/null
+++ b/nixos/tests/filesender.nix
@@ -0,0 +1,137 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "filesender";
+  meta = {
+    maintainers = with lib.maintainers; [ nhnn ];
+    broken = pkgs.stdenv.isAarch64; # selenium.common.exceptions.WebDriverException: Message: Unsupported platform/architecture combination: linux/aarch64
+  };
+
+  nodes.filesender = { ... }: let
+    format = pkgs.formats.php { };
+  in {
+    networking.firewall.allowedTCPPorts = [ 80 ];
+
+    services.filesender.enable = true;
+    services.filesender.localDomain = "filesender";
+    services.filesender.settings = {
+      auth_sp_saml_authentication_source = "default";
+      auth_sp_saml_uid_attribute = "uid";
+      storage_filesystem_path = "/tmp";
+      site_url = "http://filesender";
+      force_ssl = false;
+      admin = "";
+      admin_email = "admin@localhost";
+      email_reply_to = "noreply@localhost";
+    };
+    services.simplesamlphp.filesender = {
+      settings = {
+        baseurlpath = "http://filesender/saml";
+        "module.enable".exampleauth = true;
+      };
+      authSources = {
+        admin = [ "core:AdminPassword" ];
+        default = format.lib.mkMixedArray [ "exampleauth:UserPass" ] {
+          "user:password" = {
+            uid = [ "user" ];
+            cn = [ "user" ];
+            mail = [ "user@nixos.org" ];
+          };
+        };
+      };
+    };
+  };
+
+  nodes.client =
+    { pkgs
+    , nodes
+    , ...
+    }:
+    let
+      filesenderIP = (builtins.head (nodes.filesender.networking.interfaces.eth1.ipv4.addresses)).address;
+    in
+    {
+      networking.hosts.${filesenderIP} = [ "filesender" ];
+
+      environment.systemPackages =
+        let
+          username = "user";
+          password = "password";
+          browser-test =
+            pkgs.writers.writePython3Bin "browser-test"
+              {
+                libraries = [ pkgs.python3Packages.selenium ];
+                flakeIgnore = [ "E124" "E501" ];
+              } ''
+              from selenium.webdriver.common.by import By
+              from selenium.webdriver import Firefox
+              from selenium.webdriver.firefox.options import Options
+              from selenium.webdriver.firefox.firefox_profile import FirefoxProfile
+              from selenium.webdriver.firefox.service import Service
+              from selenium.webdriver.support.ui import WebDriverWait
+              from selenium.webdriver.support import expected_conditions as EC
+              from subprocess import STDOUT
+              import string
+              import random
+              import logging
+              import time
+              selenium_logger = logging.getLogger("selenium")
+              selenium_logger.setLevel(logging.DEBUG)
+              selenium_logger.addHandler(logging.StreamHandler())
+              profile = FirefoxProfile()
+              profile.set_preference("browser.download.folderList", 2)
+              profile.set_preference("browser.download.manager.showWhenStarting", False)
+              profile.set_preference("browser.download.dir", "/tmp/firefox")
+              profile.set_preference("browser.helperApps.neverAsk.saveToDisk", "text/plain;text/txt")
+              options = Options()
+              options.profile = profile
+              options.add_argument('--headless')
+              service = Service(log_output=STDOUT)
+              driver = Firefox(options=options)
+              driver.set_window_size(1024, 768)
+              driver.implicitly_wait(30)
+              driver.get('http://filesender/')
+              wait = WebDriverWait(driver, 20)
+              wait.until(EC.title_contains("FileSender"))
+              driver.find_element(By.ID, "btn_logon").click()
+              wait.until(EC.title_contains("Enter your username and password"))
+              driver.find_element(By.ID, 'username').send_keys(
+                  '${username}'
+              )
+              driver.find_element(By.ID, 'password').send_keys(
+                  '${password}'
+              )
+              driver.find_element(By.ID, "submit_button").click()
+              wait.until(EC.title_contains("FileSender"))
+              wait.until(EC.presence_of_element_located((By.ID, "topmenu_logoff")))
+              test_string = "".join(random.choices(string.ascii_uppercase + string.digits, k=20))
+              with open("/tmp/test_file.txt", "w") as file:
+                  file.write(test_string)
+              driver.find_element(By.ID, "files").send_keys("/tmp/test_file.txt")
+              time.sleep(2)
+              driver.find_element(By.CSS_SELECTOR, '.start').click()
+              wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".download_link")))
+              download_link = driver.find_element(By.CSS_SELECTOR, '.download_link > textarea').get_attribute('value').strip()
+              driver.get(download_link)
+              wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".download")))
+              driver.find_element(By.CSS_SELECTOR, '.download').click()
+              wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".ui-dialog-buttonset > button:nth-child(2)")))
+              driver.find_element(By.CSS_SELECTOR, ".ui-dialog-buttonset > button:nth-child(2)").click()
+              driver.close()
+              driver.quit()
+            '';
+        in
+        [
+          pkgs.firefox-unwrapped
+          pkgs.geckodriver
+          browser-test
+        ];
+    };
+
+  testScript = ''
+    start_all()
+    filesender.wait_for_file("/run/phpfpm/filesender.sock")
+    filesender.wait_for_open_port(80)
+    if "If you have received an invitation to access this site as a guest" not in client.wait_until_succeeds("curl -sS -f http://filesender"):
+      raise Exception("filesender returned invalid html")
+    client.succeed("browser-test")
+  '';
+})
diff --git a/nixos/tests/firefly-iii.nix b/nixos/tests/firefly-iii.nix
index c93d799320a48..f8e4ca4bfe2b4 100644
--- a/nixos/tests/firefly-iii.nix
+++ b/nixos/tests/firefly-iii.nix
@@ -1,14 +1,19 @@
-import ./make-test-python.nix ({ lib, pkgs, ... }: {
+import ./make-test-python.nix ({ lib, ... }:
+
+let
+  db-pass = "Test2Test2";
+  app-key = "TestTestTestTestTestTestTestTest";
+in
+{
   name = "firefly-iii";
   meta.maintainers = [ lib.maintainers.savyajha ];
 
-  nodes.machine = { config, ... }: {
+  nodes.fireflySqlite = { config, ... }: {
     environment.etc = {
-      "firefly-iii-appkey".text = "TestTestTestTestTestTestTestTest";
+      "firefly-iii-appkey".text = app-key;
     };
     services.firefly-iii = {
       enable = true;
-      virtualHost = "http://localhost";
       enableNginx = true;
       settings = {
         APP_KEY_FILE = "/etc/firefly-iii-appkey";
@@ -18,9 +23,89 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
     };
   };
 
+  nodes.fireflyPostgresql = { config, pkgs, ... }: {
+    environment.etc = {
+      "firefly-iii-appkey".text = app-key;
+      "postgres-pass".text = db-pass;
+    };
+    services.firefly-iii = {
+      enable = true;
+      enableNginx = true;
+      settings = {
+        APP_KEY_FILE = "/etc/firefly-iii-appkey";
+        LOG_CHANNEL = "stdout";
+        SITE_OWNER = "mail@example.com";
+        DB_CONNECTION = "pgsql";
+        DB_DATABASE = "firefly";
+        DB_USERNAME = "firefly";
+        DB_PASSWORD_FILE = "/etc/postgres-pass";
+        PGSQL_SCHEMA = "firefly";
+      };
+    };
+
+    services.postgresql = {
+      enable = true;
+      package = pkgs.postgresql_16;
+      authentication = ''
+        local all postgres peer
+        local firefly firefly password
+      '';
+      initialScript = pkgs.writeText "firefly-init.sql" ''
+        CREATE USER "firefly" WITH LOGIN PASSWORD '${db-pass}';
+        CREATE DATABASE "firefly" WITH OWNER "firefly";
+        \c firefly
+        CREATE SCHEMA AUTHORIZATION firefly;
+      '';
+    };
+  };
+
+  nodes.fireflyMysql = { config, pkgs, ... }: {
+    environment.etc = {
+      "firefly-iii-appkey".text = app-key;
+      "mysql-pass".text = db-pass;
+    };
+    services.firefly-iii = {
+      enable = true;
+      enableNginx = true;
+      settings = {
+        APP_KEY_FILE = "/etc/firefly-iii-appkey";
+        LOG_CHANNEL = "stdout";
+        SITE_OWNER = "mail@example.com";
+        DB_CONNECTION = "mysql";
+        DB_DATABASE = "firefly";
+        DB_USERNAME = "firefly";
+        DB_PASSWORD_FILE = "/etc/mysql-pass";
+        DB_SOCKET = "/run/mysqld/mysqld.sock";
+      };
+    };
+
+    services.mysql = {
+      enable = true;
+      package = pkgs.mariadb;
+      initialScript = pkgs.writeText "firefly-init.sql" ''
+        create database firefly DEFAULT CHARACTER SET utf8mb4;
+        create user 'firefly'@'localhost' identified by '${db-pass}';
+        grant all on firefly.* to 'firefly'@'localhost';
+      '';
+      settings.mysqld.character-set-server = "utf8mb4";
+    };
+  };
+
   testScript = ''
-    machine.wait_for_unit("phpfpm-firefly-iii.service")
-    machine.wait_for_unit("nginx.service")
-    machine.succeed("curl -fvvv -Ls http://localhost/ | grep 'Firefly III'")
+    fireflySqlite.wait_for_unit("phpfpm-firefly-iii.service")
+    fireflySqlite.wait_for_unit("nginx.service")
+    fireflySqlite.succeed("curl -fvvv -Ls http://localhost/ | grep 'Firefly III'")
+    fireflySqlite.succeed("curl -fvvv -Ls http://localhost/v1/js/app.js")
+    fireflySqlite.succeed("systemctl start firefly-iii-cron.service")
+    fireflyPostgresql.wait_for_unit("phpfpm-firefly-iii.service")
+    fireflyPostgresql.wait_for_unit("nginx.service")
+    fireflyPostgresql.wait_for_unit("postgresql.service")
+    fireflyPostgresql.succeed("curl -fvvv -Ls http://localhost/ | grep 'Firefly III'")
+    fireflyPostgresql.succeed("systemctl start firefly-iii-cron.service")
+    fireflyMysql.wait_for_unit("phpfpm-firefly-iii.service")
+    fireflyMysql.wait_for_unit("nginx.service")
+    fireflyMysql.wait_for_unit("mysql.service")
+    fireflyMysql.succeed("curl -fvvv -Ls http://localhost/ | grep 'Firefly III'")
+    fireflyMysql.succeed("systemctl start firefly-iii-cron.service")
   '';
 })
diff --git a/nixos/tests/firefox.nix b/nixos/tests/firefox.nix
index fbea95dc75235..6418e029f80d9 100644
--- a/nixos/tests/firefox.nix
+++ b/nixos/tests/firefox.nix
@@ -1,9 +1,9 @@
-import ./make-test-python.nix ({ pkgs, firefoxPackage, ... }:
+import ./make-test-python.nix ({ lib, pkgs, firefoxPackage, ... }:
 {
   name = firefoxPackage.pname;
 
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ eelco shlevy ];
+    maintainers = [ shlevy ];
   };
 
   nodes.machine =
@@ -55,7 +55,7 @@ import ./make-test-python.nix ({ pkgs, firefoxPackage, ... }:
     };
 
   testScript = let
-    exe = firefoxPackage.unwrapped.binaryName;
+    exe = lib.getExe firefoxPackage;
   in ''
       from contextlib import contextmanager
 
diff --git a/nixos/tests/firewall.nix b/nixos/tests/firewall.nix
index dd7551f143a5e..34e8bda60eef5 100644
--- a/nixos/tests/firewall.nix
+++ b/nixos/tests/firewall.nix
@@ -3,7 +3,7 @@
 import ./make-test-python.nix ( { pkgs, nftables, ... } : {
   name = "firewall" + pkgs.lib.optionalString nftables "-nftables";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ eelco ];
+    maintainers = [ ];
   };
 
   nodes =
diff --git a/nixos/tests/fish.nix b/nixos/tests/fish.nix
index 3d9b13c6af70a..c9a1bef51478e 100644
--- a/nixos/tests/fish.nix
+++ b/nixos/tests/fish.nix
@@ -10,6 +10,8 @@ import ./make-test-python.nix ({ pkgs, ... }: {
         coreutils
         procps # kill collides with coreutils' to test https://github.com/NixOS/nixpkgs/issues/56432
       ];
+      # TODO: remove if/when #267880 is merged and this is a default
+      services.logrotate.enable = false;
     };
 
   testScript =
diff --git a/nixos/tests/flood.nix b/nixos/tests/flood.nix
new file mode 100644
index 0000000000000..075d37e62835f
--- /dev/null
+++ b/nixos/tests/flood.nix
@@ -0,0 +1,27 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+let
+  port = 3001;
+in
+{
+  name = "flood";
+  meta = {
+    maintainers = with pkgs.lib.maintainers; [ thiagokokada ];
+  };
+
+  nodes.machine = { pkgs, ... }: {
+    services.flood = {
+      inherit port;
+      enable = true;
+      openFirewall = true;
+      extraArgs = [ "--baseuri=/" ];
+    };
+  };
+
+  testScript = /* python */ ''
+    machine.start()
+    machine.wait_for_unit("flood.service")
+    machine.wait_for_open_port(${toString port})
+
+    machine.succeed("curl --fail http://localhost:${toString port}")
+  '';
+})
diff --git a/nixos/tests/forgejo.nix b/nixos/tests/forgejo.nix
index 827fae2790c6e..c5bf8e32524cb 100644
--- a/nixos/tests/forgejo.nix
+++ b/nixos/tests/forgejo.nix
@@ -41,6 +41,8 @@ let
     hash = "sha256-h2/UIp8IjPo3eE4Gzx52Fb7pcgG/Ww7u31w5fdKVMos=";
   };
 
+  metricSecret = "fakesecret";
+
   supportedDbTypes = [ "mysql" "postgres" "sqlite3" ];
   makeForgejoTest = type: nameValuePair type (makeTest {
     name = "forgejo-${type}";
@@ -59,6 +61,8 @@ let
             ENABLE_PUSH_CREATE_USER = true;
             DEFAULT_PUSH_CREATE_PRIVATE = false;
           };
+          settings.metrics.ENABLED = true;
+          secrets.metrics.TOKEN = pkgs.writeText "metrics_secret" metricSecret;
         };
         environment.systemPackages = [ config.services.forgejo.package pkgs.gnupg pkgs.jq pkgs.file pkgs.htmlq ];
         services.openssh.enable = true;
@@ -192,6 +196,10 @@ let
             timeout=10
         )
 
+        with subtest("Testing /metrics endpoint with token from cfg.secrets"):
+            server.fail("curl --fail http://localhost:3000/metrics")
+            server.succeed('curl --fail http://localhost:3000/metrics -H "Authorization: Bearer ${metricSecret}"')
+
         with subtest("Testing runner registration and action workflow"):
             server.succeed(
                 "su -l forgejo -c 'GITEA_WORK_DIR=/var/lib/forgejo gitea actions generate-runner-token' | sed 's/^/TOKEN=/' | tee /var/lib/forgejo/runner_token"
diff --git a/nixos/tests/garage/default.nix b/nixos/tests/garage/default.nix
index a42236e9a5bbe..b7f9bb4b865bd 100644
--- a/nixos/tests/garage/default.nix
+++ b/nixos/tests/garage/default.nix
@@ -51,4 +51,5 @@ in
   [
     "0_8"
     "0_9"
+    "1_x"
   ]
diff --git a/nixos/tests/garage/with-3node-replication.nix b/nixos/tests/garage/with-3node-replication.nix
index d4387b198d976..266a1082893f7 100644
--- a/nixos/tests/garage/with-3node-replication.nix
+++ b/nixos/tests/garage/with-3node-replication.nix
@@ -7,10 +7,10 @@ args@{ mkNode, ver, ... }:
   };
 
   nodes = {
-    node1 = mkNode { replicationMode = 3; publicV6Address = "fc00:1::1"; };
-    node2 = mkNode { replicationMode = 3; publicV6Address = "fc00:1::2"; };
-    node3 = mkNode { replicationMode = 3; publicV6Address = "fc00:1::3"; };
-    node4 = mkNode { replicationMode = 3; publicV6Address = "fc00:1::4"; };
+    node1 = mkNode { replicationMode = "3"; publicV6Address = "fc00:1::1"; };
+    node2 = mkNode { replicationMode = "3"; publicV6Address = "fc00:1::2"; };
+    node3 = mkNode { replicationMode = "3"; publicV6Address = "fc00:1::3"; };
+    node4 = mkNode { replicationMode = "3"; publicV6Address = "fc00:1::4"; };
   };
 
   testScript = ''
diff --git a/nixos/tests/gnome-extensions.nix b/nixos/tests/gnome-extensions.nix
index 51ccabd7e6a65..332cf44b0ed4f 100644
--- a/nixos/tests/gnome-extensions.nix
+++ b/nixos/tests/gnome-extensions.nix
@@ -84,7 +84,6 @@ import ./make-test-python.nix (
       "dash-to-dock"
       "dash-to-panel"
       "ddterm"
-      "emoji-selector"
       "gsconnect"
       "system-monitor-next"
       "desktop-icons-ng-ding"
@@ -138,11 +137,11 @@ import ./make-test-python.nix (
             # Enable and optionally disable
 
             machine.succeed(f"${run "gnome-extensions enable {extension}"}")
-            checkState("ENABLED", extension)
+            checkState("ACTIVE", extension)
 
             if disable:
                 machine.succeed(f"${run "gnome-extensions disable {extension}"}")
-                checkState("DISABLED", extension)
+                checkState("INACTIVE", extension)
     ''
     + lib.concatLines (map (e: ''checkExtension("${e}", False)'') alwaysOnExtensions)
     + lib.concatLines (map (e: ''checkExtension("${e}", True)'') testExtensions)
diff --git a/nixos/tests/grafana/provision/default.nix b/nixos/tests/grafana/provision/default.nix
index d33d16ce12099..f9dd8b2961ac7 100644
--- a/nixos/tests/grafana/provision/default.nix
+++ b/nixos/tests/grafana/provision/default.nix
@@ -1,7 +1,7 @@
 import ../../make-test-python.nix ({ lib, pkgs, ... }:
 
 let
-  inherit (lib) mkMerge nameValuePair maintainers;
+  inherit (lib) mkMerge maintainers;
 
   baseGrafanaConf = {
     services.grafana = {
@@ -33,35 +33,6 @@ let
   };
 
   extraNodeConfs = {
-    provisionLegacyNotifiers = {
-      services.grafana.provision = {
-        datasources.settings = {
-          apiVersion = 1;
-          datasources = [{
-            name = "Test Datasource";
-            type = "testdata";
-            access = "proxy";
-            uid = "test_datasource";
-          }];
-        };
-        dashboards.settings = {
-          apiVersion = 1;
-          providers = [{
-            name = "default";
-            options.path = "/var/lib/grafana/dashboards";
-          }];
-        };
-        notifiers = [{
-          uid = "test_notifiers";
-          name = "Test Notifiers";
-          type = "email";
-          settings = {
-            singleEmail = true;
-            addresses = "test@test.com";
-          };
-        }];
-      };
-    };
     provisionNix = {
       services.grafana.provision = {
         datasources.settings = {
@@ -242,15 +213,5 @@ in {
             machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/v1/provisioning/mute-timings | grep Test\ Mute\ Timing"
             )
-
-    with subtest("Successful notifiers provision"):
-        provisionLegacyNotifiers.wait_for_unit("grafana.service")
-        provisionLegacyNotifiers.wait_for_open_port(3000)
-        print(provisionLegacyNotifiers.succeed(
-            "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/alert-notifications/uid/test_notifiers"
-        ))
-        provisionLegacyNotifiers.succeed(
-            "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/alert-notifications/uid/test_notifiers | grep Test\ Notifiers"
-        )
   '';
 })
diff --git a/nixos/tests/greetd-no-shadow.nix b/nixos/tests/greetd-no-shadow.nix
new file mode 100644
index 0000000000000..382218ffa948f
--- /dev/null
+++ b/nixos/tests/greetd-no-shadow.nix
@@ -0,0 +1,49 @@
+import ./make-test-python.nix ({ pkgs, latestKernel ? false, ... }:
+{
+  name = "greetd-no-shadow";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ ];
+  };
+
+  nodes.machine =
+    { pkgs, lib, ... }: {
+
+      users.users.alice = {
+        isNormalUser = true;
+        group = "alice";
+        password = "foobar";
+      };
+      users.groups.alice = {};
+
+      # This means login(1) breaks, so we must use greetd/agreety instead.
+      security.shadow.enable = false;
+
+      services.greetd = {
+        enable = true;
+        settings = {
+          default_session = {
+            command = "${pkgs.greetd.greetd}/bin/agreety --cmd bash";
+          };
+        };
+      };
+    };
+
+  testScript = ''
+      machine.start()
+
+      machine.wait_for_unit("multi-user.target")
+      machine.wait_until_succeeds("pgrep -f 'agretty.*tty1'")
+      machine.screenshot("postboot")
+
+      with subtest("Log in as alice on a virtual console"):
+          machine.wait_until_tty_matches("1", "login: ")
+          machine.send_chars("alice\n")
+          machine.wait_until_tty_matches("1", "login: alice")
+          machine.wait_until_succeeds("pgrep login")
+          machine.wait_until_tty_matches("1", "Password: ")
+          machine.send_chars("foobar\n")
+          machine.wait_until_succeeds("pgrep -u alice bash")
+          machine.send_chars("touch done\n")
+          machine.wait_for_file("/home/alice/done")
+  '';
+})
diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix
index 05fb2fa1e06aa..47902fa4e1340 100644
--- a/nixos/tests/home-assistant.nix
+++ b/nixos/tests/home-assistant.nix
@@ -44,6 +44,8 @@ in {
       # test loading custom components
       customComponents = with pkgs.home-assistant-custom-components; [
         prometheus_sensor
+        # tests loading multiple components from a single package
+        spook
       ];
 
       # test loading lovelace modules
@@ -179,7 +181,8 @@ in {
 
     with subtest("Check that custom components get installed"):
         hass.succeed("test -f ${configDir}/custom_components/prometheus_sensor/manifest.json")
-        hass.wait_until_succeeds("journalctl -u home-assistant.service | grep -q 'We found a custom integration prometheus_sensor which has not been tested by Home Assistant'")
+        for integration in ("prometheus_sensor", "spook", "spook_inverse"):
+            hass.wait_until_succeeds(f"journalctl -u home-assistant.service | grep -q 'We found a custom integration {integration} which has not been tested by Home Assistant'")
 
     with subtest("Check that lovelace modules are referenced and fetchable"):
         hass.succeed("grep -q 'mini-graph-card-bundle.js' '${configDir}/configuration.yaml'")
@@ -228,7 +231,8 @@ in {
         cursor = get_journal_cursor()
         hass.succeed("${system}/specialisation/removeCustomThings/bin/switch-to-configuration test")
         hass.fail("grep -q 'mini-graph-card-bundle.js' '${configDir}/ui-lovelace.yaml'")
-        hass.fail("test -f ${configDir}/custom_components/prometheus_sensor/manifest.json")
+        for integration in ("prometheus_sensor", "spook", "spook_inverse"):
+            hass.fail(f"test -f ${configDir}/custom_components/{integration}/manifest.json")
         wait_for_homeassistant(cursor)
 
     with subtest("Check that no errors were logged"):
diff --git a/nixos/tests/incus/container.nix b/nixos/tests/incus/container.nix
index a71c5355046a5..10262cf2132b8 100644
--- a/nixos/tests/incus/container.nix
+++ b/nixos/tests/incus/container.nix
@@ -1,4 +1,4 @@
-import ../make-test-python.nix ({ pkgs, lib, extra ? {}, name ? "incus-container", ... } :
+import ../make-test-python.nix ({ pkgs, lib, extra ? {}, name ? "incus-container", incus ? pkgs.incus-lts, ... } :
 
 let
   releases = import ../../release.nix {
@@ -28,7 +28,10 @@ in
       memorySize = 1024;
       diskSize = 4096;
 
-      incus.enable = true;
+      incus = {
+        enable = true;
+        package = incus;
+      };
     };
     networking.nftables.enable = true;
   };
@@ -70,51 +73,60 @@ in
         machine.succeed("incus exec container mount | grep 'lxcfs on /proc/cpuinfo type fuse.lxcfs'")
         machine.succeed("incus exec container mount | grep 'lxcfs on /proc/meminfo type fuse.lxcfs'")
 
-    with subtest("Container CPU limits can be managed"):
-        set_container("limits.cpu 1")
-        cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip()
-        assert cpuinfo == "1", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 1, got: {cpuinfo}"
-
-        set_container("limits.cpu 2")
-        cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip()
-        assert cpuinfo == "2", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 2, got: {cpuinfo}"
-
-    with subtest("Container memory limits can be managed"):
-        set_container("limits.memory 64MB")
-        meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip()
-        meminfo_bytes = " ".join(meminfo.split(' ')[-2:])
-        assert meminfo_bytes == "62500 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '62500 kB', got: '{meminfo_bytes}'"
-
-        set_container("limits.memory 128MB")
-        meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip()
-        meminfo_bytes = " ".join(meminfo.split(' ')[-2:])
-        assert meminfo_bytes == "125000 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '125000 kB', got: '{meminfo_bytes}'"
-
-    with subtest("lxc-container generator configures plain container"):
-        # reuse the existing container to save some time
-        machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
-        check_sysctl("container")
-
-    with subtest("lxc-container generator configures nested container"):
-        machine.execute("incus delete --force container")
-        machine.succeed("incus launch nixos container --config security.nesting=true")
-        with machine.nested("Waiting for instance to start and be usable"):
-          retry(instance_is_up)
-
-        machine.fail("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
-        target = machine.succeed("incus exec container readlink -- -f /run/systemd/system/systemd-binfmt.service").strip()
-        assert target == "/dev/null", "lxc generator did not correctly mask /run/systemd/system/systemd-binfmt.service"
-
-        check_sysctl("container")
-
-    with subtest("lxc-container generator configures privileged container"):
-        machine.execute("incus delete --force container")
-        machine.succeed("incus launch nixos container --config security.privileged=true")
-        with machine.nested("Waiting for instance to start and be usable"):
-          retry(instance_is_up)
-
-        machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
-
-        check_sysctl("container")
+    with subtest("resource limits"):
+        with subtest("Container CPU limits can be managed"):
+            set_container("limits.cpu 1")
+            cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip()
+            assert cpuinfo == "1", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 1, got: {cpuinfo}"
+
+            set_container("limits.cpu 2")
+            cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip()
+            assert cpuinfo == "2", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 2, got: {cpuinfo}"
+
+        with subtest("Container memory limits can be managed"):
+            set_container("limits.memory 64MB")
+            meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip()
+            meminfo_bytes = " ".join(meminfo.split(' ')[-2:])
+            assert meminfo_bytes == "62500 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '62500 kB', got: '{meminfo_bytes}'"
+
+            set_container("limits.memory 128MB")
+            meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip()
+            meminfo_bytes = " ".join(meminfo.split(' ')[-2:])
+            assert meminfo_bytes == "125000 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '125000 kB', got: '{meminfo_bytes}'"
+
+    with subtest("lxc-generator"):
+        with subtest("lxc-container generator configures plain container"):
+            # reuse the existing container to save some time
+            machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
+            check_sysctl("container")
+
+        with subtest("lxc-container generator configures nested container"):
+            machine.execute("incus delete --force container")
+            machine.succeed("incus launch nixos container --config security.nesting=true")
+            with machine.nested("Waiting for instance to start and be usable"):
+              retry(instance_is_up)
+
+            machine.fail("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
+            target = machine.succeed("incus exec container readlink -- -f /run/systemd/system/systemd-binfmt.service").strip()
+            assert target == "/dev/null", "lxc generator did not correctly mask /run/systemd/system/systemd-binfmt.service"
+
+            check_sysctl("container")
+
+        with subtest("lxc-container generator configures privileged container"):
+            machine.execute("incus delete --force container")
+            machine.succeed("incus launch nixos container --config security.privileged=true")
+            with machine.nested("Waiting for instance to start and be usable"):
+              retry(instance_is_up)
+
+            machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
+
+            check_sysctl("container")
+
+    with subtest("softDaemonRestart"):
+        with subtest("Instance remains running when softDaemonRestart is enabled and services is stopped"):
+            pid = machine.succeed("incus info container | grep 'PID'").split(":")[1].strip()
+            machine.succeed(f"ps {pid}")
+            machine.succeed("systemctl stop incus")
+            machine.succeed(f"ps {pid}")
   '';
 })
diff --git a/nixos/tests/incus/default.nix b/nixos/tests/incus/default.nix
index b850c4fba018d..c33bf1600f27a 100644
--- a/nixos/tests/incus/default.nix
+++ b/nixos/tests/incus/default.nix
@@ -3,24 +3,27 @@
   config ? { },
   pkgs ? import ../../.. { inherit system config; },
   handleTestOn,
+  incus ? pkgs.incus-lts,
 }:
 {
   container-legacy-init = import ./container.nix {
     name = "container-legacy-init";
-    inherit system pkgs;
+    inherit incus system pkgs;
   };
   container-systemd-init = import ./container.nix {
     name = "container-systemd-init";
-    inherit system pkgs;
+    inherit incus system pkgs;
     extra = {
       boot.initrd.systemd.enable = true;
     };
   };
-  lxd-to-incus = import ./lxd-to-incus.nix { inherit system pkgs; };
-  openvswitch = import ./openvswitch.nix { inherit system pkgs; };
-  preseed = import ./preseed.nix { inherit system pkgs; };
-  socket-activated = import ./socket-activated.nix { inherit system pkgs; };
-  storage = import ./storage.nix { inherit system pkgs; };
-  ui = import ./ui.nix { inherit system pkgs; };
-  virtual-machine = handleTestOn [ "x86_64-linux" ] ./virtual-machine.nix { inherit system pkgs; };
+  incusd-options = import ./incusd-options.nix { inherit incus system pkgs; };
+  lxd-to-incus = import ./lxd-to-incus.nix { inherit incus system pkgs; };
+  openvswitch = import ./openvswitch.nix { inherit incus system pkgs; };
+  socket-activated = import ./socket-activated.nix { inherit incus system pkgs; };
+  storage = import ./storage.nix { inherit incus system pkgs; };
+  ui = import ./ui.nix { inherit incus system pkgs; };
+  virtual-machine = handleTestOn [ "x86_64-linux" ] ./virtual-machine.nix {
+    inherit incus system pkgs;
+  };
 }
diff --git a/nixos/tests/incus/incusd-options.nix b/nixos/tests/incus/incusd-options.nix
new file mode 100644
index 0000000000000..7b3a4d726e38e
--- /dev/null
+++ b/nixos/tests/incus/incusd-options.nix
@@ -0,0 +1,110 @@
+# this is a set of tests for non-default options. typically the default options
+# will be handled by the other tests
+import ../make-test-python.nix (
+  {
+    pkgs,
+    lib,
+    incus ? pkgs.incus-lts,
+    ...
+  }:
+
+  let
+    releases = import ../../release.nix {
+      configuration = {
+        # Building documentation makes the test unnecessarily take a longer time:
+        documentation.enable = lib.mkForce false;
+      };
+    };
+
+    container-image-metadata = releases.lxdContainerMeta.${pkgs.stdenv.hostPlatform.system};
+    container-image-rootfs = releases.lxdContainerImage.${pkgs.stdenv.hostPlatform.system};
+  in
+  {
+    name = "incusd-options";
+
+    meta = {
+      maintainers = lib.teams.lxc.members;
+    };
+
+    nodes.machine = {
+      virtualisation = {
+        cores = 2;
+        memorySize = 1024;
+        diskSize = 4096;
+
+        incus = {
+          enable = true;
+          package = incus;
+          softDaemonRestart = false;
+
+          preseed = {
+            networks = [
+              {
+                name = "nixostestbr0";
+                type = "bridge";
+                config = {
+                  "ipv4.address" = "10.0.100.1/24";
+                  "ipv4.nat" = "true";
+                };
+              }
+            ];
+            profiles = [
+              {
+                name = "default";
+                devices = {
+                  eth0 = {
+                    name = "eth0";
+                    network = "nixostestbr0";
+                    type = "nic";
+                  };
+                  root = {
+                    path = "/";
+                    pool = "nixostest_pool";
+                    size = "35GiB";
+                    type = "disk";
+                  };
+                };
+              }
+            ];
+            storage_pools = [
+              {
+                name = "nixostest_pool";
+                driver = "dir";
+              }
+            ];
+          };
+        };
+      };
+      networking.nftables.enable = true;
+    };
+
+    testScript = ''
+      def instance_is_up(_) -> bool:
+          status, _ = machine.execute("incus exec container --disable-stdin --force-interactive /run/current-system/sw/bin/systemctl -- is-system-running")
+          return status == 0
+
+      machine.wait_for_unit("incus.service")
+      machine.wait_for_unit("incus-preseed.service")
+
+      with subtest("Container image can be imported"):
+          machine.succeed("incus image import ${container-image-metadata}/*/*.tar.xz ${container-image-rootfs}/*/*.tar.xz --alias nixos")
+
+      with subtest("Container can be launched and managed"):
+          machine.succeed("incus launch nixos container")
+          with machine.nested("Waiting for instance to start and be usable"):
+            retry(instance_is_up)
+          machine.succeed("echo true | incus exec container /run/current-system/sw/bin/bash -")
+
+      with subtest("Verify preseed resources created"):
+          machine.succeed("incus profile show default")
+          machine.succeed("incus network info nixostestbr0")
+          machine.succeed("incus storage show nixostest_pool")
+
+      with subtest("Instance is stopped when softDaemonRestart is disabled and services is stopped"):
+          pid = machine.succeed("incus info container | grep 'PID'").split(":")[1].strip()
+          machine.succeed(f"ps {pid}")
+          machine.succeed("systemctl stop incus")
+          machine.fail(f"ps {pid}")
+    '';
+  }
+)
diff --git a/nixos/tests/incus/lxd-to-incus.nix b/nixos/tests/incus/lxd-to-incus.nix
index e93b76591eca4..66f78cbd33b40 100644
--- a/nixos/tests/incus/lxd-to-incus.nix
+++ b/nixos/tests/incus/lxd-to-incus.nix
@@ -1,6 +1,11 @@
 import ../make-test-python.nix (
 
-  { pkgs, lib, ... }:
+  {
+    pkgs,
+    lib,
+    incus ? pkgs.incus-lts,
+    ...
+  }:
 
   let
     releases = import ../../release.nix { configuration.documentation.enable = lib.mkForce false; };
@@ -65,7 +70,10 @@ import ../make-test-python.nix (
             ];
           };
 
-          incus.enable = true;
+          incus = {
+            enable = true;
+            package = incus;
+          };
         };
         networking.nftables.enable = true;
       };
diff --git a/nixos/tests/incus/openvswitch.nix b/nixos/tests/incus/openvswitch.nix
index 5d4aef031ad0a..1cead99080e7a 100644
--- a/nixos/tests/incus/openvswitch.nix
+++ b/nixos/tests/incus/openvswitch.nix
@@ -1,4 +1,4 @@
-import ../make-test-python.nix ({ pkgs, lib, ... } :
+import ../make-test-python.nix ({ pkgs, lib, incus ? pkgs.incus-lts, ... } :
 
 {
   name = "incus-openvswitch";
@@ -9,7 +9,11 @@ import ../make-test-python.nix ({ pkgs, lib, ... } :
 
   nodes.machine = { lib, ... }: {
     virtualisation = {
-      incus.enable = true;
+      incus = {
+        enable = true;
+        package = incus;
+      };
+
       vswitch.enable = true;
       incus.preseed = {
         networks = [
diff --git a/nixos/tests/incus/preseed.nix b/nixos/tests/incus/preseed.nix
deleted file mode 100644
index f2d928115f3ec..0000000000000
--- a/nixos/tests/incus/preseed.nix
+++ /dev/null
@@ -1,63 +0,0 @@
-import ../make-test-python.nix ({ pkgs, lib, ... } :
-
-{
-  name = "incus-preseed";
-
-  meta = {
-    maintainers = lib.teams.lxc.members;
-  };
-
-  nodes.machine = { lib, ... }: {
-    virtualisation = {
-      incus.enable = true;
-
-      incus.preseed = {
-        networks = [
-          {
-            name = "nixostestbr0";
-            type = "bridge";
-            config = {
-              "ipv4.address" = "10.0.100.1/24";
-              "ipv4.nat" = "true";
-            };
-          }
-        ];
-        profiles = [
-          {
-            name = "nixostest_default";
-            devices = {
-              eth0 = {
-                name = "eth0";
-                network = "nixostestbr0";
-                type = "nic";
-              };
-              root = {
-                path = "/";
-                pool = "default";
-                size = "35GiB";
-                type = "disk";
-              };
-            };
-          }
-        ];
-        storage_pools = [
-          {
-            name = "nixostest_pool";
-            driver = "dir";
-          }
-        ];
-      };
-    };
-    networking.nftables.enable = true;
-  };
-
-  testScript = ''
-    machine.wait_for_unit("incus.service")
-    machine.wait_for_unit("incus-preseed.service")
-
-    with subtest("Verify preseed resources created"):
-      machine.succeed("incus profile show nixostest_default")
-      machine.succeed("incus network info nixostestbr0")
-      machine.succeed("incus storage show nixostest_pool")
-  '';
-})
diff --git a/nixos/tests/incus/socket-activated.nix b/nixos/tests/incus/socket-activated.nix
index 59caf1090fbd8..55c5496396e91 100644
--- a/nixos/tests/incus/socket-activated.nix
+++ b/nixos/tests/incus/socket-activated.nix
@@ -1,4 +1,4 @@
-import ../make-test-python.nix ({ pkgs, lib, ... } :
+import ../make-test-python.nix ({ pkgs, lib, incus ? pkgs.incus-lts, ... } :
 
 {
   name = "incus-socket-activated";
@@ -9,8 +9,11 @@ import ../make-test-python.nix ({ pkgs, lib, ... } :
 
   nodes.machine = { lib, ... }: {
     virtualisation = {
-      incus.enable = true;
-      incus.socketActivation = true;
+      incus = {
+        enable = true;
+        package = incus;
+        socketActivation = true;
+      };
     };
     networking.nftables.enable = true;
   };
diff --git a/nixos/tests/incus/storage.nix b/nixos/tests/incus/storage.nix
index 190f4f7451c20..05ea6ba996eb2 100644
--- a/nixos/tests/incus/storage.nix
+++ b/nixos/tests/incus/storage.nix
@@ -1,5 +1,10 @@
 import ../make-test-python.nix (
-  { pkgs, lib, ... }:
+  {
+    pkgs,
+    lib,
+    incus ? pkgs.incus-lts,
+    ...
+  }:
 
   {
     name = "incus-storage";
@@ -19,7 +24,10 @@ import ../make-test-python.nix (
 
         virtualisation = {
           emptyDiskImages = [ 2048 ];
-          incus.enable = true;
+          incus = {
+            enable = true;
+            package = incus;
+          };
         };
       };
 
diff --git a/nixos/tests/incus/ui.nix b/nixos/tests/incus/ui.nix
index 837eb14844cea..a255d6fabe839 100644
--- a/nixos/tests/incus/ui.nix
+++ b/nixos/tests/incus/ui.nix
@@ -1,4 +1,4 @@
-import ../make-test-python.nix ({ pkgs, lib, ... }: {
+import ../make-test-python.nix ({ pkgs, lib, incus ? pkgs.incus-lts, ... }: {
   name = "incus-ui";
 
   meta = {
@@ -7,7 +7,10 @@ import ../make-test-python.nix ({ pkgs, lib, ... }: {
 
   nodes.machine = { lib, ... }: {
     virtualisation = {
-      incus.enable = true;
+      incus = {
+        enable = true;
+        package = incus;
+      };
       incus.ui.enable = true;
     };
     networking.nftables.enable = true;
diff --git a/nixos/tests/incus/virtual-machine.nix b/nixos/tests/incus/virtual-machine.nix
index eebbbd113ed16..70e54191d3304 100644
--- a/nixos/tests/incus/virtual-machine.nix
+++ b/nixos/tests/incus/virtual-machine.nix
@@ -1,4 +1,4 @@
-import ../make-test-python.nix ({ pkgs, lib, ... }:
+import ../make-test-python.nix ({ pkgs, lib, incus ? pkgs.incus-lts, ... }:
 
 let
   releases = import ../../release.nix {
@@ -33,7 +33,10 @@ in
       # Provide a TPM to test vTPM support for guests
       tpm.enable = true;
 
-      incus.enable = true;
+      incus = {
+        enable = true;
+        package = incus;
+      };
     };
     networking.nftables.enable = true;
   };
@@ -75,5 +78,11 @@ in
         machine.succeed("incus config set ${instance-name} limits.cpu=2")
         count = int(machine.succeed("incus exec ${instance-name} -- nproc").strip())
         assert count == 2, f"Wrong number of CPUs reported, want: 2, got: {count}"
+
+    with subtest("Instance remains running when softDaemonRestart is enabled and services is stopped"):
+        pid = machine.succeed("incus info ${instance-name} | grep 'PID'").split(":")[1].strip()
+        machine.succeed(f"ps {pid}")
+        machine.succeed("systemctl stop incus")
+        machine.succeed(f"ps {pid}")
   '';
 })
diff --git a/nixos/tests/initrd-network.nix b/nixos/tests/initrd-network.nix
index f2483b7393de4..abbc3d0fce822 100644
--- a/nixos/tests/initrd-network.nix
+++ b/nixos/tests/initrd-network.nix
@@ -1,7 +1,7 @@
 import ./make-test-python.nix ({ pkgs, lib, ...} : {
   name = "initrd-network";
 
-  meta.maintainers = [ pkgs.lib.maintainers.eelco ];
+  meta.maintainers = [ ];
 
   nodes.machine = { ... }: {
     imports = [ ../modules/profiles/minimal.nix ];
diff --git a/nixos/tests/initrd-secrets.nix b/nixos/tests/initrd-secrets.nix
index 0f3f83b0904e3..dbbdd83588491 100644
--- a/nixos/tests/initrd-secrets.nix
+++ b/nixos/tests/initrd-secrets.nix
@@ -9,7 +9,7 @@ let
   testWithCompressor = compressor: testing.makeTest {
     name = "initrd-secrets-${compressor}";
 
-    meta.maintainers = [ lib.maintainers.lheckemann ];
+    meta.maintainers = [ ];
 
     nodes.machine = { ... }: {
       virtualisation.useBootLoader = true;
diff --git a/nixos/tests/installer-systemd-stage-1.nix b/nixos/tests/installer-systemd-stage-1.nix
index 1dd55dada042a..00205f9417718 100644
--- a/nixos/tests/installer-systemd-stage-1.nix
+++ b/nixos/tests/installer-systemd-stage-1.nix
@@ -19,7 +19,7 @@
     luksroot
     luksroot-format1
     luksroot-format2
-    # lvm
+    lvm
     separateBoot
     separateBootFat
     separateBootZfs
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 7e835041eb39f..3f57a64333dda 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -249,12 +249,11 @@ let
       with subtest("Check whether nixos-rebuild works"):
           target.succeed("nixos-rebuild switch >&2")
 
-      # FIXME: Nix 2.4 broke nixos-option, someone has to fix it.
-      # with subtest("Test nixos-option"):
-      #     kernel_modules = target.succeed("nixos-option boot.initrd.kernelModules")
-      #     assert "virtio_console" in kernel_modules
-      #     assert "List of modules" in kernel_modules
-      #     assert "qemu-guest.nix" in kernel_modules
+      with subtest("Test nixos-option"):
+          kernel_modules = target.succeed("nixos-option boot.initrd.kernelModules")
+          assert "virtio_console" in kernel_modules
+          assert "List of modules" in kernel_modules
+          assert "qemu-guest.nix" in kernel_modules
 
       target.shutdown()
 
@@ -975,6 +974,9 @@ in {
           "mount LABEL=nixos /mnt",
       )
     '';
+    extraConfig = optionalString systemdStage1 ''
+      boot.initrd.services.lvm.enable = true;
+    '';
   };
 
   # Boot off an encrypted root partition with the default LUKS header format
diff --git a/nixos/tests/ipv6.nix b/nixos/tests/ipv6.nix
index 75faa6f602010..7f91457fa5ea8 100644
--- a/nixos/tests/ipv6.nix
+++ b/nixos/tests/ipv6.nix
@@ -4,7 +4,7 @@
 import ./make-test-python.nix ({ pkgs, lib, ...} : {
   name = "ipv6";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ eelco ];
+    maintainers = [ ];
   };
 
   nodes =
diff --git a/nixos/tests/jenkins.nix b/nixos/tests/jenkins.nix
index a8f6210006547..d7394c866c143 100644
--- a/nixos/tests/jenkins.nix
+++ b/nixos/tests/jenkins.nix
@@ -7,7 +7,7 @@
 import ./make-test-python.nix ({ pkgs, ...} : {
   name = "jenkins";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ bjornfor coconnor domenkozar eelco ];
+    maintainers = [ bjornfor coconnor domenkozar ];
   };
 
   nodes = {
diff --git a/nixos/tests/jotta-cli.nix b/nixos/tests/jotta-cli.nix
index 5eefe65c1d385..0df23ee2cba5c 100644
--- a/nixos/tests/jotta-cli.nix
+++ b/nixos/tests/jotta-cli.nix
@@ -4,7 +4,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
   meta.maintainers = with pkgs.lib.maintainers; [ evenbrenden ];
 
   nodes.machine = { pkgs, ... }: {
-    user.services.jotta-cli.enable = true;
+    services.jotta-cli.enable = true;
     imports = [ ./common/user-account.nix ];
   };
 
diff --git a/nixos/tests/k3s/default.nix b/nixos/tests/k3s/default.nix
index 512dc06ee77ec..297b05a4e4a74 100644
--- a/nixos/tests/k3s/default.nix
+++ b/nixos/tests/k3s/default.nix
@@ -1,16 +1,20 @@
-{ system ? builtins.currentSystem
-, pkgs ? import ../../.. { inherit system; }
-, lib ? pkgs.lib
+{
+  system ? builtins.currentSystem,
+  pkgs ? import ../../.. { inherit system; },
+  lib ? pkgs.lib,
 }:
 let
   allK3s = lib.filterAttrs (n: _: lib.strings.hasPrefix "k3s_" n) pkgs;
 in
 {
   # Testing K3s with Etcd backend
-  etcd = lib.mapAttrs (_: k3s: import ./etcd.nix {
-    inherit system pkgs k3s;
-    inherit (pkgs) etcd;
-  }) allK3s;
+  etcd = lib.mapAttrs (
+    _: k3s:
+    import ./etcd.nix {
+      inherit system pkgs k3s;
+      inherit (pkgs) etcd;
+    }
+  ) allK3s;
   # Run a single node k3s cluster and verify a pod can run
   single-node = lib.mapAttrs (_: k3s: import ./single-node.nix { inherit system pkgs k3s; }) allK3s;
   # Run a multi-node k3s cluster and verify pod networking works across nodes
diff --git a/nixos/tests/k3s/etcd.nix b/nixos/tests/k3s/etcd.nix
index d6e9a294adb13..ac0aa90472516 100644
--- a/nixos/tests/k3s/etcd.nix
+++ b/nixos/tests/k3s/etcd.nix
@@ -1,100 +1,130 @@
-import ../make-test-python.nix ({ pkgs, lib, k3s, etcd, ... }:
-
-{
-  name = "${k3s.name}-etcd";
-
-  nodes = {
-
-    etcd = { ... }: {
-      services.etcd = {
-        enable = true;
-        openFirewall = true;
-        listenClientUrls = [ "http://192.168.1.1:2379" "http://127.0.0.1:2379" ];
-        listenPeerUrls = [ "http://192.168.1.1:2380" ];
-        initialAdvertisePeerUrls = [ "http://192.168.1.1:2380" ];
-        initialCluster = [ "etcd=http://192.168.1.1:2380" ];
-      };
-      networking = {
-        useDHCP = false;
-        defaultGateway = "192.168.1.1";
-        interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
-          { address = "192.168.1.1"; prefixLength = 24; }
-        ];
-      };
-    };
+import ../make-test-python.nix (
+  {
+    pkgs,
+    lib,
+    k3s,
+    etcd,
+    ...
+  }:
+
+  {
+    name = "${k3s.name}-etcd";
+
+    nodes = {
+
+      etcd =
+        { ... }:
+        {
+          services.etcd = {
+            enable = true;
+            openFirewall = true;
+            listenClientUrls = [
+              "http://192.168.1.1:2379"
+              "http://127.0.0.1:2379"
+            ];
+            listenPeerUrls = [ "http://192.168.1.1:2380" ];
+            initialAdvertisePeerUrls = [ "http://192.168.1.1:2380" ];
+            initialCluster = [ "etcd=http://192.168.1.1:2380" ];
+          };
+          networking = {
+            useDHCP = false;
+            defaultGateway = "192.168.1.1";
+            interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
+              {
+                address = "192.168.1.1";
+                prefixLength = 24;
+              }
+            ];
+          };
+        };
 
-    k3s = { pkgs, ... }: {
-      environment.systemPackages = with pkgs; [ jq ];
-      # k3s uses enough resources the default vm fails.
-      virtualisation.memorySize = 1536;
-      virtualisation.diskSize = 4096;
-
-      services.k3s = {
-        enable = true;
-        role = "server";
-        extraFlags = builtins.toString [
-          "--datastore-endpoint=\"http://192.168.1.1:2379\""
-          "--disable" "coredns"
-          "--disable" "local-storage"
-          "--disable" "metrics-server"
-          "--disable" "servicelb"
-          "--disable" "traefik"
-          "--node-ip" "192.168.1.2"
-        ];
-      };
-
-      networking = {
-        firewall = {
-          allowedTCPPorts = [ 2379 2380 6443 ];
-          allowedUDPPorts = [ 8472 ];
+      k3s =
+        { pkgs, ... }:
+        {
+          environment.systemPackages = with pkgs; [ jq ];
+          # k3s uses enough resources the default vm fails.
+          virtualisation.memorySize = 1536;
+          virtualisation.diskSize = 4096;
+
+          services.k3s = {
+            enable = true;
+            role = "server";
+            extraFlags = builtins.toString [
+              "--datastore-endpoint=\"http://192.168.1.1:2379\""
+              "--disable"
+              "coredns"
+              "--disable"
+              "local-storage"
+              "--disable"
+              "metrics-server"
+              "--disable"
+              "servicelb"
+              "--disable"
+              "traefik"
+              "--node-ip"
+              "192.168.1.2"
+            ];
+          };
+
+          networking = {
+            firewall = {
+              allowedTCPPorts = [
+                2379
+                2380
+                6443
+              ];
+              allowedUDPPorts = [ 8472 ];
+            };
+            useDHCP = false;
+            defaultGateway = "192.168.1.2";
+            interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
+              {
+                address = "192.168.1.2";
+                prefixLength = 24;
+              }
+            ];
+          };
         };
-        useDHCP = false;
-        defaultGateway = "192.168.1.2";
-        interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
-          { address = "192.168.1.2"; prefixLength = 24; }
-        ];
-      };
     };
 
-  };
-
-  testScript = ''
-    with subtest("should start etcd"):
-        etcd.start()
-        etcd.wait_for_unit("etcd.service")
+    testScript = ''
+      with subtest("should start etcd"):
+          etcd.start()
+          etcd.wait_for_unit("etcd.service")
 
-    with subtest("should wait for etcdctl endpoint status to succeed"):
-        etcd.wait_until_succeeds("etcdctl endpoint status")
+      with subtest("should wait for etcdctl endpoint status to succeed"):
+          etcd.wait_until_succeeds("etcdctl endpoint status")
 
-    with subtest("should start k3s"):
-        k3s.start()
-        k3s.wait_for_unit("k3s")
+      with subtest("should start k3s"):
+          k3s.start()
+          k3s.wait_for_unit("k3s")
 
-    with subtest("should test if kubectl works"):
-        k3s.wait_until_succeeds("k3s kubectl get node")
+      with subtest("should test if kubectl works"):
+          k3s.wait_until_succeeds("k3s kubectl get node")
 
-    with subtest("should wait for service account to show up; takes a sec"):
-        k3s.wait_until_succeeds("k3s kubectl get serviceaccount default")
+      with subtest("should wait for service account to show up; takes a sec"):
+          k3s.wait_until_succeeds("k3s kubectl get serviceaccount default")
 
-    with subtest("should create a sample secret object"):
-        k3s.succeed("k3s kubectl create secret generic nixossecret --from-literal thesecret=abacadabra")
+      with subtest("should create a sample secret object"):
+          k3s.succeed("k3s kubectl create secret generic nixossecret --from-literal thesecret=abacadabra")
 
-    with subtest("should check if secret is correct"):
-        k3s.wait_until_succeeds("[[ $(kubectl get secrets nixossecret -o json | jq -r .data.thesecret | base64 -d) == abacadabra ]]")
+      with subtest("should check if secret is correct"):
+          k3s.wait_until_succeeds("[[ $(kubectl get secrets nixossecret -o json | jq -r .data.thesecret | base64 -d) == abacadabra ]]")
 
-    with subtest("should have a secret in database"):
-        etcd.wait_until_succeeds("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]")
+      with subtest("should have a secret in database"):
+          etcd.wait_until_succeeds("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]")
 
-    with subtest("should delete the secret"):
-        k3s.succeed("k3s kubectl delete secret nixossecret")
+      with subtest("should delete the secret"):
+          k3s.succeed("k3s kubectl delete secret nixossecret")
 
-    with subtest("should not have a secret in database"):
-        etcd.wait_until_fails("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]")
+      with subtest("should not have a secret in database"):
+          etcd.wait_until_fails("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]")
 
-    with subtest("should shutdown k3s and etcd"):
-        k3s.shutdown()
-        etcd.shutdown()
-  '';
+      with subtest("should shutdown k3s and etcd"):
+          k3s.shutdown()
+          etcd.shutdown()
+    '';
 
-  meta.maintainers = etcd.meta.maintainers ++ k3s.meta.maintainers;
-})
+    meta.maintainers = etcd.meta.maintainers ++ k3s.meta.maintainers;
+  }
+)
diff --git a/nixos/tests/k3s/multi-node.nix b/nixos/tests/k3s/multi-node.nix
index 20279f3ca4b93..b618d2aff34c4 100644
--- a/nixos/tests/k3s/multi-node.nix
+++ b/nixos/tests/k3s/multi-node.nix
@@ -1,14 +1,30 @@
-import ../make-test-python.nix ({ pkgs, lib, k3s, ... }:
+import ../make-test-python.nix (
+  {
+    pkgs,
+    lib,
+    k3s,
+    ...
+  }:
   let
     imageEnv = pkgs.buildEnv {
       name = "k3s-pause-image-env";
-      paths = with pkgs; [ tini bashInteractive coreutils socat ];
+      paths = with pkgs; [
+        tini
+        bashInteractive
+        coreutils
+        socat
+      ];
     };
     pauseImage = pkgs.dockerTools.streamLayeredImage {
       name = "test.local/pause";
       tag = "local";
       contents = imageEnv;
-      config.Entrypoint = [ "/bin/tini" "--" "/bin/sleep" "inf" ];
+      config.Entrypoint = [
+        "/bin/tini"
+        "--"
+        "/bin/sleep"
+        "inf"
+      ];
     };
     # A daemonset that responds 'server' on port 8000
     networkTestDaemonset = pkgs.writeText "test.yml" ''
@@ -42,90 +58,135 @@ import ../make-test-python.nix ({ pkgs, lib, k3s, ... }:
     name = "${k3s.name}-multi-node";
 
     nodes = {
-      server = { pkgs, ... }: {
-        environment.systemPackages = with pkgs; [ gzip jq ];
-        # k3s uses enough resources the default vm fails.
-        virtualisation.memorySize = 1536;
-        virtualisation.diskSize = 4096;
-
-        services.k3s = {
-          inherit tokenFile;
-          enable = true;
-          role = "server";
-          package = k3s;
-          clusterInit = true;
-          extraFlags = builtins.toString [
-            "--disable" "coredns"
-            "--disable" "local-storage"
-            "--disable" "metrics-server"
-            "--disable" "servicelb"
-            "--disable" "traefik"
-            "--node-ip" "192.168.1.1"
-            "--pause-image" "test.local/pause:local"
+      server =
+        { pkgs, ... }:
+        {
+          environment.systemPackages = with pkgs; [
+            gzip
+            jq
+          ];
+          # k3s uses enough resources the default vm fails.
+          virtualisation.memorySize = 1536;
+          virtualisation.diskSize = 4096;
+
+          services.k3s = {
+            inherit tokenFile;
+            enable = true;
+            role = "server";
+            package = k3s;
+            clusterInit = true;
+            extraFlags = builtins.toString [
+              "--disable"
+              "coredns"
+              "--disable"
+              "local-storage"
+              "--disable"
+              "metrics-server"
+              "--disable"
+              "servicelb"
+              "--disable"
+              "traefik"
+              "--node-ip"
+              "192.168.1.1"
+              "--pause-image"
+              "test.local/pause:local"
+            ];
+          };
+          networking.firewall.allowedTCPPorts = [
+            2379
+            2380
+            6443
+          ];
+          networking.firewall.allowedUDPPorts = [ 8472 ];
+          networking.firewall.trustedInterfaces = [ "flannel.1" ];
+          networking.useDHCP = false;
+          networking.defaultGateway = "192.168.1.1";
+          networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
+            {
+              address = "192.168.1.1";
+              prefixLength = 24;
+            }
           ];
         };
-        networking.firewall.allowedTCPPorts = [ 2379 2380 6443 ];
-        networking.firewall.allowedUDPPorts = [ 8472 ];
-        networking.firewall.trustedInterfaces = [ "flannel.1" ];
-        networking.useDHCP = false;
-        networking.defaultGateway = "192.168.1.1";
-        networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
-          { address = "192.168.1.1"; prefixLength = 24; }
-        ];
-      };
-
-      server2 = { pkgs, ... }: {
-        environment.systemPackages = with pkgs; [ gzip jq ];
-        virtualisation.memorySize = 1536;
-        virtualisation.diskSize = 4096;
-
-        services.k3s = {
-          inherit tokenFile;
-          enable = true;
-          serverAddr = "https://192.168.1.1:6443";
-          clusterInit = false;
-          extraFlags = builtins.toString [
-            "--disable" "coredns"
-            "--disable" "local-storage"
-            "--disable" "metrics-server"
-            "--disable" "servicelb"
-            "--disable" "traefik"
-            "--node-ip" "192.168.1.3"
-            "--pause-image" "test.local/pause:local"
+
+      server2 =
+        { pkgs, ... }:
+        {
+          environment.systemPackages = with pkgs; [
+            gzip
+            jq
+          ];
+          virtualisation.memorySize = 1536;
+          virtualisation.diskSize = 4096;
+
+          services.k3s = {
+            inherit tokenFile;
+            enable = true;
+            serverAddr = "https://192.168.1.1:6443";
+            clusterInit = false;
+            extraFlags = builtins.toString [
+              "--disable"
+              "coredns"
+              "--disable"
+              "local-storage"
+              "--disable"
+              "metrics-server"
+              "--disable"
+              "servicelb"
+              "--disable"
+              "traefik"
+              "--node-ip"
+              "192.168.1.3"
+              "--pause-image"
+              "test.local/pause:local"
+            ];
+          };
+          networking.firewall.allowedTCPPorts = [
+            2379
+            2380
+            6443
+          ];
+          networking.firewall.allowedUDPPorts = [ 8472 ];
+          networking.firewall.trustedInterfaces = [ "flannel.1" ];
+          networking.useDHCP = false;
+          networking.defaultGateway = "192.168.1.3";
+          networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
+            {
+              address = "192.168.1.3";
+              prefixLength = 24;
+            }
           ];
         };
-        networking.firewall.allowedTCPPorts = [ 2379 2380 6443 ];
-        networking.firewall.allowedUDPPorts = [ 8472 ];
-        networking.firewall.trustedInterfaces = [ "flannel.1" ];
-        networking.useDHCP = false;
-        networking.defaultGateway = "192.168.1.3";
-        networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
-          { address = "192.168.1.3"; prefixLength = 24; }
-        ];
-      };
-
-      agent = { pkgs, ... }: {
-        virtualisation.memorySize = 1024;
-        virtualisation.diskSize = 2048;
-        services.k3s = {
-          inherit tokenFile;
-          enable = true;
-          role = "agent";
-          serverAddr = "https://192.168.1.3:6443";
-          extraFlags = lib.concatStringsSep " " [
-            "--pause-image" "test.local/pause:local"
-            "--node-ip" "192.168.1.2"
+
+      agent =
+        { pkgs, ... }:
+        {
+          virtualisation.memorySize = 1024;
+          virtualisation.diskSize = 2048;
+          services.k3s = {
+            inherit tokenFile;
+            enable = true;
+            role = "agent";
+            serverAddr = "https://192.168.1.3:6443";
+            extraFlags = lib.concatStringsSep " " [
+              "--pause-image"
+              "test.local/pause:local"
+              "--node-ip"
+              "192.168.1.2"
+            ];
+          };
+          networking.firewall.allowedTCPPorts = [ 6443 ];
+          networking.firewall.allowedUDPPorts = [ 8472 ];
+          networking.firewall.trustedInterfaces = [ "flannel.1" ];
+          networking.useDHCP = false;
+          networking.defaultGateway = "192.168.1.2";
+          networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
+            {
+              address = "192.168.1.2";
+              prefixLength = 24;
+            }
           ];
         };
-        networking.firewall.allowedTCPPorts = [ 6443 ];
-        networking.firewall.allowedUDPPorts = [ 8472 ];
-        networking.firewall.trustedInterfaces = [ "flannel.1" ];
-        networking.useDHCP = false;
-        networking.defaultGateway = "192.168.1.2";
-        networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
-          { address = "192.168.1.2"; prefixLength = 24; }
-        ];
-      };
     };
 
     meta.maintainers = k3s.meta.maintainers;
@@ -178,4 +239,5 @@ import ../make-test-python.nix ({ pkgs, lib, k3s, ... }:
       for m in machines:
           m.shutdown()
     '';
-  })
+  }
+)
diff --git a/nixos/tests/k3s/single-node.nix b/nixos/tests/k3s/single-node.nix
index fd64a050e61ef..80d80a55ddf41 100644
--- a/nixos/tests/k3s/single-node.nix
+++ b/nixos/tests/k3s/single-node.nix
@@ -1,14 +1,29 @@
-import ../make-test-python.nix ({ pkgs, lib, k3s, ... }:
+import ../make-test-python.nix (
+  {
+    pkgs,
+    lib,
+    k3s,
+    ...
+  }:
   let
     imageEnv = pkgs.buildEnv {
       name = "k3s-pause-image-env";
-      paths = with pkgs; [ tini (hiPrio coreutils) busybox ];
+      paths = with pkgs; [
+        tini
+        (hiPrio coreutils)
+        busybox
+      ];
     };
     pauseImage = pkgs.dockerTools.streamLayeredImage {
       name = "test.local/pause";
       tag = "local";
       contents = imageEnv;
-      config.Entrypoint = [ "/bin/tini" "--" "/bin/sleep" "inf" ];
+      config.Entrypoint = [
+        "/bin/tini"
+        "--"
+        "/bin/sleep"
+        "inf"
+      ];
     };
     testPodYaml = pkgs.writeText "test.yml" ''
       apiVersion: v1
@@ -27,57 +42,83 @@ import ../make-test-python.nix ({ pkgs, lib, k3s, ... }:
     name = "${k3s.name}-single-node";
     meta.maintainers = k3s.meta.maintainers;
 
-    nodes.machine = { pkgs, ... }: {
-      environment.systemPackages = with pkgs; [ k3s gzip ];
+    nodes.machine =
+      { pkgs, ... }:
+      {
+        environment.systemPackages = with pkgs; [
+          k3s
+          gzip
+        ];
 
-      # k3s uses enough resources the default vm fails.
-      virtualisation.memorySize = 1536;
-      virtualisation.diskSize = 4096;
+        # k3s uses enough resources the default vm fails.
+        virtualisation.memorySize = 1536;
+        virtualisation.diskSize = 4096;
 
-      services.k3s.enable = true;
-      services.k3s.role = "server";
-      services.k3s.package = k3s;
-      # Slightly reduce resource usage
-      services.k3s.extraFlags = builtins.toString [
-        "--disable" "coredns"
-        "--disable" "local-storage"
-        "--disable" "metrics-server"
-        "--disable" "servicelb"
-        "--disable" "traefik"
-        "--pause-image" "test.local/pause:local"
-      ];
+        services.k3s.enable = true;
+        services.k3s.role = "server";
+        services.k3s.package = k3s;
+        # Slightly reduce resource usage
+        services.k3s.extraFlags = builtins.toString [
+          "--disable"
+          "coredns"
+          "--disable"
+          "local-storage"
+          "--disable"
+          "metrics-server"
+          "--disable"
+          "servicelb"
+          "--disable"
+          "traefik"
+          "--pause-image"
+          "test.local/pause:local"
+        ];
 
-      users.users = {
-        noprivs = {
-          isNormalUser = true;
-          description = "Can't access k3s by default";
-          password = "*";
+        users.users = {
+          noprivs = {
+            isNormalUser = true;
+            description = "Can't access k3s by default";
+            password = "*";
+          };
         };
       };
-    };
 
-    testScript = ''
-      start_all()
+    testScript =
+      ''
+        start_all()
 
-      machine.wait_for_unit("k3s")
-      machine.succeed("kubectl cluster-info")
-      machine.fail("sudo -u noprivs kubectl cluster-info")
+        machine.wait_for_unit("k3s")
+        machine.succeed("kubectl cluster-info")
+        machine.fail("sudo -u noprivs kubectl cluster-info")
       '' # Fix-Me: Tests fail for 'aarch64-linux' as: "CONFIG_CGROUP_FREEZER: missing (fail)"
-      + lib.optionalString (!pkgs.stdenv.isAarch64) ''machine.succeed("k3s check-config")'' + ''
+      + lib.optionalString (!pkgs.stdenv.isAarch64) ''machine.succeed("k3s check-config")''
+      + ''
 
-      machine.succeed(
-          "${pauseImage} | ctr image import -"
-      )
+        machine.succeed(
+            "${pauseImage} | ctr image import -"
+        )
 
-      # Also wait for our service account to show up; it takes a sec
-      machine.wait_until_succeeds("kubectl get serviceaccount default")
-      machine.succeed("kubectl apply -f ${testPodYaml}")
-      machine.succeed("kubectl wait --for 'condition=Ready' pod/test")
-      machine.succeed("kubectl delete -f ${testPodYaml}")
+        # Also wait for our service account to show up; it takes a sec
+        machine.wait_until_succeeds("kubectl get serviceaccount default")
+        machine.succeed("kubectl apply -f ${testPodYaml}")
+        machine.succeed("kubectl wait --for 'condition=Ready' pod/test")
+        machine.succeed("kubectl delete -f ${testPodYaml}")
 
-      # regression test for #176445
-      machine.fail("journalctl -o cat -u k3s.service | grep 'ipset utility not found'")
+        # regression test for #176445
+        machine.fail("journalctl -o cat -u k3s.service | grep 'ipset utility not found'")
 
-      machine.shutdown()
-    '';
-  })
+        with subtest("Run k3s-killall"):
+            # Call the killall script with a clean path to assert that
+            # all required commands are wrapped
+            output = machine.succeed("PATH= ${k3s}/bin/k3s-killall.sh 2>&1 | tee /dev/stderr")
+            assert "command not found" not in output, "killall script contains unknown command"
+
+            # Check that killall cleaned up properly
+            machine.fail("systemctl is-active k3s.service")
+            machine.fail("systemctl list-units | grep containerd")
+            machine.fail("ip link show | awk -F': ' '{print $2}' | grep -e flannel -e cni0")
+            machine.fail("ip netns show | grep cni-")
+
+        machine.shutdown()
+      '';
+  }
+)
diff --git a/nixos/tests/kea.nix b/nixos/tests/kea.nix
index 98a8e93a07609..653e280ec8b7e 100644
--- a/nixos/tests/kea.nix
+++ b/nixos/tests/kea.nix
@@ -57,6 +57,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...}: {
           };
 
           subnet4 = [ {
+            id = 1;
             subnet = "10.0.0.0/29";
             pools = [ {
               pool = "10.0.0.3 - 10.0.0.3";
diff --git a/nixos/tests/keepalived.nix b/nixos/tests/keepalived.nix
index 16564511d85dc..052b36266d037 100644
--- a/nixos/tests/keepalived.nix
+++ b/nixos/tests/keepalived.nix
@@ -4,8 +4,8 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
 
   nodes = {
     node1 = { pkgs, ... }: {
-      networking.firewall.extraCommands = "iptables -A INPUT -p vrrp -j ACCEPT";
       services.keepalived.enable = true;
+      services.keepalived.openFirewall = true;
       services.keepalived.vrrpInstances.test = {
         interface = "eth1";
         state = "MASTER";
@@ -16,8 +16,8 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
       environment.systemPackages = [ pkgs.tcpdump ];
     };
     node2 = { pkgs, ... }: {
-      networking.firewall.extraCommands = "iptables -A INPUT -p vrrp -j ACCEPT";
       services.keepalived.enable = true;
+      services.keepalived.openFirewall = true;
       services.keepalived.vrrpInstances.test = {
         interface = "eth1";
         state = "MASTER";
diff --git a/nixos/tests/kerberos/heimdal.nix b/nixos/tests/kerberos/heimdal.nix
index 393289f7a92ca..098080a84592e 100644
--- a/nixos/tests/kerberos/heimdal.nix
+++ b/nixos/tests/kerberos/heimdal.nix
@@ -4,7 +4,7 @@ import ../make-test-python.nix ({pkgs, ...}: {
   nodes.machine = { config, libs, pkgs, ...}:
   { services.kerberos_server =
     { enable = true;
-      realms = {
+      settings.realms = {
         "FOO.BAR".acl = [{principal = "admin"; access = ["add" "cpw"];}];
       };
     };
diff --git a/nixos/tests/kerberos/mit.nix b/nixos/tests/kerberos/mit.nix
index 1191d047abbf0..172261f95fe6b 100644
--- a/nixos/tests/kerberos/mit.nix
+++ b/nixos/tests/kerberos/mit.nix
@@ -4,7 +4,7 @@ import ../make-test-python.nix ({pkgs, ...}: {
   nodes.machine = { config, libs, pkgs, ...}:
   { services.kerberos_server =
     { enable = true;
-      realms = {
+      settings.realms = {
         "FOO.BAR".acl = [{principal = "admin"; access = ["add" "cpw"];}];
       };
     };
diff --git a/nixos/tests/kernel-generic.nix b/nixos/tests/kernel-generic.nix
index 5f0e7b3e37cd7..e22c7d735a238 100644
--- a/nixos/tests/kernel-generic.nix
+++ b/nixos/tests/kernel-generic.nix
@@ -31,10 +31,13 @@ let
       linux_5_15_hardened
       linux_6_1_hardened
       linux_6_6_hardened
+      linux_6_8_hardened
+      linux_6_9_hardened
       linux_rt_5_4
       linux_rt_5_10
       linux_rt_5_15
       linux_rt_6_1
+      linux_rt_6_6
       linux_libre
 
       linux_testing;
@@ -44,6 +47,9 @@ in mapAttrs (_: lP: testsForLinuxPackages lP) kernels // {
   passthru = {
     inherit testsForLinuxPackages;
 
+    # Useful for development testing of all Kernel configs without building full Kernel
+    configfiles = mapAttrs (_: lP: lP.kernel.configfile) kernels;
+
     testsForKernel = kernel: testsForLinuxPackages (pkgs.linuxPackagesFor kernel);
   };
 }
diff --git a/nixos/tests/keycloak.nix b/nixos/tests/keycloak.nix
index 67b412c80961d..259f1340a22d7 100644
--- a/nixos/tests/keycloak.nix
+++ b/nixos/tests/keycloak.nix
@@ -44,7 +44,6 @@ let
             };
             plugins = with config.services.keycloak.package.plugins; [
               keycloak-discord
-              keycloak-metrics-spi
             ];
           };
           environment.systemPackages = with pkgs; [
@@ -122,14 +121,6 @@ let
                    | jq -r '"Authorization: bearer " + .access_token' >admin_auth_header
           """)
 
-          # Register the metrics SPI
-          keycloak.succeed(
-              """${pkgs.jre}/bin/keytool -import -alias snakeoil -file ${certs.ca.cert} -storepass aaaaaa -keystore cacert.jks -noprompt""",
-              """KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh config credentials --server '${frontendUrl}' --realm master --user admin --password "$(<${adminPasswordFile})" """,
-              """KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh update events/config -s 'eventsEnabled=true' -s 'adminEventsEnabled=true' -s 'eventsListeners+=metrics-listener'""",
-              """curl -sSf '${frontendUrl}/realms/master/metrics' | grep '^keycloak_admin_event_UPDATE'"""
-          )
-
           # Publish the realm, including a test OIDC client and user
           keycloak.succeed(
               "curl -sSf -H @admin_auth_header -X POST -H 'Content-Type: application/json' -d @${realmDataJson} '${frontendUrl}/admin/realms/'"
diff --git a/nixos/tests/knot.nix b/nixos/tests/knot.nix
index eec94a22f2fa7..4441fed6ef507 100644
--- a/nixos/tests/knot.nix
+++ b/nixos/tests/knot.nix
@@ -190,6 +190,10 @@ in {
     primary.wait_for_unit("knot.service")
     secondary.wait_for_unit("knot.service")
 
+    for zone in ("example.com.", "sub.example.com."):
+        secondary.wait_until_succeeds(
+          f"knotc zone-status {zone} | grep -q 'serial: 2019031302'"
+        )
 
     def test(host, query_type, query, pattern):
         out = client.succeed(f"khost -t {query_type} {query} {host}").strip()
diff --git a/nixos/tests/kubernetes/base.nix b/nixos/tests/kubernetes/base.nix
index ba7b2d9b1d2de..13a2bc03831de 100644
--- a/nixos/tests/kubernetes/base.nix
+++ b/nixos/tests/kubernetes/base.nix
@@ -47,7 +47,7 @@ let
                   '') (attrValues nodes);
                 };
               };
-              programs.bash.enableCompletion = true;
+              programs.bash.completion.enable = true;
               environment.systemPackages = [ wrapKubectl ];
               services.flannel.iface = "eth1";
               services.kubernetes = {
diff --git a/nixos/tests/kubo/default.nix b/nixos/tests/kubo/default.nix
index d8c0c69dc1fbd..629922fc366db 100644
--- a/nixos/tests/kubo/default.nix
+++ b/nixos/tests/kubo/default.nix
@@ -1,7 +1,5 @@
 { recurseIntoAttrs, runTest }:
 recurseIntoAttrs {
   kubo = runTest ./kubo.nix;
-  # The FUSE functionality is completely broken since Kubo v0.24.0
-  # See https://github.com/ipfs/kubo/issues/10242
-  # kubo-fuse = runTest ./kubo-fuse.nix;
+  kubo-fuse = runTest ./kubo-fuse.nix;
 }
diff --git a/nixos/tests/kubo/kubo-fuse.nix b/nixos/tests/kubo/kubo-fuse.nix
index 71a5bf61649f6..c8c273fc0dfc7 100644
--- a/nixos/tests/kubo/kubo-fuse.nix
+++ b/nixos/tests/kubo/kubo-fuse.nix
@@ -23,7 +23,7 @@
 
     with subtest("FUSE mountpoint"):
         machine.fail("echo a | su bob -l -c 'ipfs add --quieter'")
-        # The FUSE mount functionality is broken as of v0.13.0 and v0.17.0.
+        # The FUSE mount functionality is broken as of v0.13.0. This is still the case with v0.29.0.
         # See https://github.com/ipfs/kubo/issues/9044.
         # Workaround: using CID Version 1 avoids that.
         ipfs_hash = machine.succeed(
diff --git a/nixos/tests/ladybird.nix b/nixos/tests/ladybird.nix
index 8ed0f47887c7d..85c23353a668a 100644
--- a/nixos/tests/ladybird.nix
+++ b/nixos/tests/ladybird.nix
@@ -10,9 +10,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     ];
 
     services.xserver.enable = true;
-    environment.systemPackages = [
-      pkgs.ladybird
-    ];
+    programs.ladybird.enable = true;
   };
 
   enableOCR = true;
diff --git a/nixos/tests/libreswan-nat.nix b/nixos/tests/libreswan-nat.nix
new file mode 100644
index 0000000000000..973e304f9e3a3
--- /dev/null
+++ b/nixos/tests/libreswan-nat.nix
@@ -0,0 +1,238 @@
+# This test sets up an IPsec VPN server that allows a client behind an IPv4 NAT
+# router to access the IPv6 internet. We check that the client initially can't
+# ping an IPv6 hosts and its connection to the server can be eavesdropped by
+# the router, but once the IPsec tunnel is enstablished it can talk to an
+# IPv6-only host and the connection is secure.
+#
+# Notes:
+#   - the VPN is implemented using policy-based routing.
+#   - the client is assigned an IPv6 address from the same /64 subnet
+#     of the server, without DHCPv6 or SLAAC.
+#   - the server acts as NDP proxy for the client, so that the latter
+#     becomes reachable at its assigned IPv6 via the server.
+#   - the client falls back to TCP if UDP is blocked
+
+{ lib, pkgs, ... }:
+
+let
+
+  # Common network setup
+  baseNetwork = {
+    # shared hosts file
+    networking.extraHosts = lib.mkVMOverride ''
+      203.0.113.1 router
+      203.0.113.2 server
+      2001:db8::2 inner
+      192.168.1.1 client
+    '';
+    # open a port for testing
+    networking.firewall.allowedUDPPorts = [ 1234 ];
+  };
+
+  # Common IPsec configuration
+  baseTunnel = {
+    services.libreswan.enable = true;
+    environment.etc."ipsec.d/tunnel.secrets" =
+      { text = ''@server %any : PSK "j1JbIi9WY07rxwcNQ6nbyThKCf9DGxWOyokXIQcAQUnafsNTUJxfsxwk9WYK8fHj"'';
+        mode = "600";
+      };
+  };
+
+  # Helpers to add a static IP address on an interface
+  setAddress4 = iface: addr: {
+    networking.interfaces.${iface}.ipv4.addresses =
+      lib.mkVMOverride [ { address = addr; prefixLength = 24; } ];
+  };
+  setAddress6 = iface: addr: {
+    networking.interfaces.${iface}.ipv6.addresses =
+      lib.mkVMOverride [ { address = addr; prefixLength = 64; } ];
+  };
+
+in
+
+{
+  name = "libreswan-nat";
+  meta = with lib.maintainers; {
+    maintainers = [ rnhmjoj ];
+  };
+
+  nodes.router = { pkgs, ... }: lib.mkMerge [
+    baseNetwork
+    (setAddress4 "eth1" "203.0.113.1")
+    (setAddress4 "eth2" "192.168.1.1")
+    {
+      virtualisation.vlans = [ 1 2 ];
+      environment.systemPackages = [ pkgs.tcpdump ];
+      networking.nat = {
+        enable = true;
+        externalInterface = "eth1";
+        internalInterfaces = [ "eth2" ];
+      };
+      networking.firewall.trustedInterfaces = [ "eth2" ];
+    }
+  ];
+
+  nodes.inner = lib.mkMerge [
+    baseNetwork
+    (setAddress6 "eth1" "2001:db8::2")
+    { virtualisation.vlans = [ 3 ]; }
+  ];
+
+  nodes.server = lib.mkMerge [
+    baseNetwork
+    baseTunnel
+    (setAddress4 "eth1" "203.0.113.2")
+    (setAddress6 "eth2" "2001:db8::1")
+    {
+      virtualisation.vlans = [ 1 3 ];
+      networking.firewall.allowedUDPPorts = [ 500 4500 ];
+      networking.firewall.allowedTCPPorts = [ 993 ];
+
+      # see https://github.com/NixOS/nixpkgs/pull/310857
+      networking.firewall.checkReversePath = false;
+
+      boot.kernel.sysctl = {
+        # enable forwarding packets
+        "net.ipv6.conf.all.forwarding" = 1;
+        "net.ipv4.conf.all.forwarding" = 1;
+        # enable NDP proxy for VPN clients
+        "net.ipv6.conf.all.proxy_ndp" = 1;
+      };
+
+      services.libreswan.configSetup = "listen-tcp=yes";
+      services.libreswan.connections.tunnel = ''
+        # server
+        left=203.0.113.2
+        leftid=@server
+        leftsubnet=::/0
+        leftupdown=${pkgs.writeScript "updown" ''
+          # act as NDP proxy for VPN clients
+          if test "$PLUTO_VERB" = up-client-v6; then
+            ip neigh add proxy "$PLUTO_PEER_CLIENT_NET" dev eth2
+          fi
+          if test "$PLUTO_VERB" = down-client-v6; then
+            ip neigh del proxy "$PLUTO_PEER_CLIENT_NET" dev eth2
+          fi
+        ''}
+
+        # clients
+        right=%any
+        rightaddresspool=2001:db8:0:0:c::/97
+        modecfgdns=2001:db8::1
+
+        # clean up vanished clients
+        dpddelay=30
+
+        auto=add
+        keyexchange=ikev2
+        rekey=no
+        narrowing=yes
+        fragmentation=yes
+        authby=secret
+
+        leftikeport=993
+        retransmit-timeout=10s
+      '';
+    }
+  ];
+
+  nodes.client = lib.mkMerge [
+    baseNetwork
+    baseTunnel
+    (setAddress4 "eth1" "192.168.1.2")
+    {
+      virtualisation.vlans = [ 2 ];
+      networking.defaultGateway = {
+        address = "192.168.1.1";
+        interface = "eth1";
+      };
+      services.libreswan.connections.tunnel = ''
+        # client
+        left=%defaultroute
+        leftid=@client
+        leftmodecfgclient=yes
+        leftsubnet=::/0
+
+        # server
+        right=203.0.113.2
+        rightid=@server
+        rightsubnet=::/0
+
+        auto=add
+        narrowing=yes
+        rekey=yes
+        fragmentation=yes
+        authby=secret
+
+        # fallback when UDP is blocked
+        enable-tcp=fallback
+        tcp-remoteport=993
+        retransmit-timeout=5s
+      '';
+    }
+  ];
+
+  testScript =
+    ''
+      def client_to_host(machine, msg: str):
+          """
+          Sends a message from client to server
+          """
+          machine.execute("nc -lu :: 1234 >/tmp/msg &")
+          client.sleep(1)
+          client.succeed(f"echo '{msg}' | nc -uw 0 {machine.name} 1234")
+          client.sleep(1)
+          machine.succeed(f"grep '{msg}' /tmp/msg")
+
+
+      def eavesdrop():
+          """
+          Starts eavesdropping on the router
+          """
+          match = "udp port 1234"
+          router.execute(f"tcpdump -i eth1 -c 1 -Avv {match} >/tmp/log &")
+
+
+      start_all()
+
+      with subtest("Network is up"):
+          client.wait_until_succeeds("ping -c1 server")
+          client.succeed("systemctl restart ipsec")
+          server.succeed("systemctl restart ipsec")
+
+      with subtest("Router can eavesdrop cleartext traffic"):
+          eavesdrop()
+          client_to_host(server, "I secretly love turnip")
+          router.sleep(1)
+          router.succeed("grep turnip /tmp/log")
+
+      with subtest("Libreswan is ready"):
+          client.wait_for_unit("ipsec")
+          server.wait_for_unit("ipsec")
+          client.succeed("ipsec checkconfig")
+          server.succeed("ipsec checkconfig")
+
+      with subtest("Client can't ping VPN host"):
+          client.fail("ping -c1 inner")
+
+      with subtest("Client can start the tunnel"):
+          client.succeed("ipsec start tunnel")
+          client.succeed("ip -6 addr show lo | grep -q 2001:db8:0:0:c")
+
+      with subtest("Client can ping VPN host"):
+          client.wait_until_succeeds("ping -c1 2001:db8::1")
+          client.succeed("ping -c1 inner")
+
+      with subtest("Eve no longer can eavesdrop"):
+          eavesdrop()
+          client_to_host(inner, "Just kidding, I actually like rhubarb")
+          router.sleep(1)
+          router.fail("grep rhubarb /tmp/log")
+
+      with subtest("TCP fallback is available"):
+          server.succeed("iptables -I nixos-fw -p udp -j DROP")
+          client.succeed("ipsec restart")
+          client.execute("ipsec start tunnel")
+          client.wait_until_succeeds("ping -c1 inner")
+    '';
+}
diff --git a/nixos/tests/libreswan.nix b/nixos/tests/libreswan.nix
index c798a04645bc0..6dd9a845d19af 100644
--- a/nixos/tests/libreswan.nix
+++ b/nixos/tests/libreswan.nix
@@ -3,7 +3,7 @@
 # Eve can eavesdrop the plaintext traffic between Alice and Bob, but once they
 # enable the secure tunnel Eve's spying becomes ineffective.
 
-import ./make-test-python.nix ({ lib, pkgs, ... }:
+{ lib, pkgs, ... }:
 
 let
 
@@ -133,4 +133,4 @@ in
           eve.sleep(1)
           eve.fail("grep rhubarb /tmp/log")
     '';
-})
+}
diff --git a/nixos/tests/login.nix b/nixos/tests/login.nix
index 67f5764a0a162..bcaee03175ad3 100644
--- a/nixos/tests/login.nix
+++ b/nixos/tests/login.nix
@@ -3,7 +3,7 @@ import ./make-test-python.nix ({ pkgs, latestKernel ? false, ... }:
 {
   name = "login";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ eelco ];
+    maintainers = [ ];
   };
 
   nodes.machine =
diff --git a/nixos/tests/logrotate.nix b/nixos/tests/logrotate.nix
index bcbe89c259ae5..f9c5e90609709 100644
--- a/nixos/tests/logrotate.nix
+++ b/nixos/tests/logrotate.nix
@@ -16,52 +16,60 @@ import ./make-test-python.nix ({ pkgs, ... }: rec {
   };
 
   nodes = {
-    defaultMachine = { ... }: { };
+    defaultMachine = { ... }: {
+      services.logrotate.enable = true;
+    };
     failingMachine = { ... }: {
-      services.logrotate.configFile = pkgs.writeText "logrotate.conf" ''
-        # self-written config file
-        su notarealuser notagroupeither
-      '';
+      services.logrotate = {
+        enable = true;
+        configFile = pkgs.writeText "logrotate.conf" ''
+          # self-written config file
+          su notarealuser notagroupeither
+        '';
+      };
     };
     machine = { config, ... }: {
       imports = [ importTest ];
 
-      services.logrotate.settings = {
-        # remove default frequency header and add another
-        header = {
-          frequency = null;
-          delaycompress = true;
-        };
-        # extra global setting... affecting nothing
-        last_line = {
-          global = true;
-          priority = 2000;
-          shred = true;
-        };
-        # using mail somewhere should add --mail to logrotate invocation
-        sendmail = {
-          mail = "user@domain.tld";
-        };
-        # postrotate should be suffixed by 'endscript'
-        postrotate = {
-          postrotate = "touch /dev/null";
-        };
-        # check checkConfig works as expected: there is nothing to check here
-        # except that the file build passes
-        checkConf = {
-          su = "root utmp";
-          createolddir = "0750 root utmp";
-          create = "root utmp";
-          "create " = "0750 root utmp";
-        };
-        # multiple paths should be aggregated
-        multipath = {
-          files = [ "file1" "file2" ];
-        };
-        # overriding imported path should keep existing attributes
-        # (e.g. olddir is still set)
-        import = {
-          notifempty = true;
+      services.logrotate = {
+        enable = true;
+        settings = {
+          # remove default frequency header and add another
+          header = {
+            frequency = null;
+            delaycompress = true;
+          };
+          # extra global setting... affecting nothing
+          last_line = {
+            global = true;
+            priority = 2000;
+            shred = true;
+          };
+          # using mail somewhere should add --mail to logrotate invocation
+          sendmail = {
+            mail = "user@domain.tld";
+          };
+          # postrotate should be suffixed by 'endscript'
+          postrotate = {
+            postrotate = "touch /dev/null";
+          };
+          # check checkConfig works as expected: there is nothing to check here
+          # except that the file build passes
+          checkConf = {
+            su = "root utmp";
+            createolddir = "0750 root utmp";
+            create = "root utmp";
+            "create " = "0750 root utmp";
+          };
+          # multiple paths should be aggregated
+          multipath = {
+            files = [ "file1" "file2" ];
+          };
+          # overriding imported path should keep existing attributes
+          # (e.g. olddir is still set)
+          import = {
+            notifempty = true;
+          };
         };
       };
     };
diff --git a/nixos/tests/lomiri-filemanager-app.nix b/nixos/tests/lomiri-filemanager-app.nix
new file mode 100644
index 0000000000000..de42c9d150894
--- /dev/null
+++ b/nixos/tests/lomiri-filemanager-app.nix
@@ -0,0 +1,48 @@
+{ pkgs, lib, ... }:
+{
+  name = "lomiri-filemanager-app-standalone";
+  meta.maintainers = lib.teams.lomiri.members;
+
+  nodes.machine =
+    { config, pkgs, ... }:
+    {
+      imports = [ ./common/x11.nix ];
+
+      services.xserver.enable = true;
+
+      environment = {
+        systemPackages = with pkgs.lomiri; [
+          suru-icon-theme
+          lomiri-filemanager-app
+        ];
+        variables = {
+          UITK_ICON_THEME = "suru";
+        };
+      };
+
+      i18n.supportedLocales = [ "all" ];
+
+      fonts.packages = with pkgs; [
+        # Intended font & helps with OCR
+        ubuntu_font_family
+      ];
+    };
+
+  enableOCR = true;
+
+  testScript = ''
+    machine.wait_for_x()
+
+    with subtest("lomiri filemanager launches"):
+        machine.execute("lomiri-filemanager-app >&2 &")
+        machine.wait_for_text(r"(filemanager.ubports|alice|items|directories|files|folder)")
+        machine.screenshot("lomiri-filemanager_open")
+
+    machine.succeed("pkill -f lomiri-filemanager-app")
+
+    with subtest("lomiri filemanager localisation works"):
+        machine.execute("env LANG=de_DE.UTF-8 lomiri-filemanager-app >&2 &")
+        machine.wait_for_text(r"(Elemente|Verzeichnisse|Dateien|Ordner)")
+        machine.screenshot("lomiri-filemanager_localised")
+  '';
+}
diff --git a/nixos/tests/lomiri.nix b/nixos/tests/lomiri.nix
index 9d6337e9977cb..e9134a202cd17 100644
--- a/nixos/tests/lomiri.nix
+++ b/nixos/tests/lomiri.nix
@@ -19,9 +19,13 @@ in {
       inherit description password;
     };
 
+    # To control mouse via scripting
+    programs.ydotool.enable = true;
+
     services.desktopManager.lomiri.enable = lib.mkForce true;
     services.displayManager.defaultSession = lib.mkForce "lomiri";
 
+    # Help with OCR
     fonts.packages = [ pkgs.inconsolata ];
 
     environment = {
@@ -114,17 +118,9 @@ in {
   enableOCR = true;
 
   testScript = { nodes, ... }: ''
-    def open_starter():
-        """
-        Open the starter, and ensure it's opened.
-        """
-        machine.send_key("meta_l-a")
-        # Look for any of the default apps
-        machine.wait_for_text(r"(Search|System|Settings|Morph|Browser|Terminal|Alacritty)")
-
     def toggle_maximise():
         """
-        Send the keybind to maximise the current window.
+        Maximise the current window.
         """
         machine.send_key("ctrl-meta_l-up")
 
@@ -135,13 +131,43 @@ in {
         machine.send_key("esc")
         machine.sleep(5)
 
+    def mouse_click(xpos, ypos):
+        """
+        Move the mouse to a screen location and hit left-click.
+        """
+
+        # Need to reset to top-left, --absolute doesn't work?
+        machine.execute("ydotool mousemove -- -10000 -10000")
+        machine.sleep(2)
+
+        # Move
+        machine.execute(f"ydotool mousemove -- {xpos} {ypos}")
+        machine.sleep(2)
+
+        # Click (C0 - left button: down & up)
+        machine.execute("ydotool click 0xC0")
+        machine.sleep(2)
+
+    def open_starter():
+        """
+        Open the starter, and ensure it's opened.
+        """
+
+        # Using the keybind has a chance of instantly closing the menu again? Just click the button
+        mouse_click(20, 30)
+
+        # Look for Search box & GUI-less content-hub examples, highest chances of avoiding false positives
+        machine.wait_for_text(r"(Search|Export|Import|Share)")
+
     start_all()
     machine.wait_for_unit("multi-user.target")
 
     # Lomiri in greeter mode should work & be able to start a session
     with subtest("lomiri greeter works"):
         machine.wait_for_unit("display-manager.service")
-        # Start page shows current tie
+        machine.wait_until_succeeds("pgrep -u lightdm -f 'lomiri --mode=greeter'")
+
+        # Start page shows current time
         machine.wait_for_text(r"(AM|PM)")
         machine.screenshot("lomiri_greeter_launched")
 
@@ -152,7 +178,6 @@ in {
 
         # Login
         machine.send_chars("${password}\n")
-        # Best way I can think of to differenciate "Lomiri in LightDM greeter mode" from "Lomiri in user shell mode"
         machine.wait_until_succeeds("pgrep -u ${user} -f 'lomiri --mode=full-shell'")
 
     # The session should start, and not be stuck in i.e. a crash loop
@@ -196,6 +221,17 @@ in {
         machine.screenshot("alacritty_opens")
         machine.send_key("alt-f4")
 
+    # Morph is how we go online
+    with subtest("morph browser works"):
+        open_starter()
+        machine.send_chars("Morph\n")
+        machine.wait_for_text(r"(Bookmarks|address|site|visited any)")
+        machine.screenshot("morph_open")
+
+        # morph-browser has a separate VM test, there isn't anything new we could test here
+
+        # Keep it running, we're using it to check content-hub communication from LSS
+
     # LSS provides DE settings
     with subtest("system settings open"):
         open_starter()
@@ -231,64 +267,75 @@ in {
         machine.wait_for_text("Morph") # or Gallery, but Morph is already packaged
         machine.screenshot("settings_content-hub_peers")
 
-        # Sadly, it doesn't seem possible to actually select a peer and attempt a content-hub data exchange with just the keyboard
+        # Select Morph as content source
+        mouse_click(370, 100)
 
-        machine.send_key("alt-f4")
+        # Expect Morph to be brought into the foreground, with its Downloads page open
+        machine.wait_for_text("No downloads")
 
-    # Morph is how we go online
-    with subtest("morph browser works"):
-        open_starter()
-        machine.send_chars("Morph\n")
-        machine.wait_for_text(r"(Bookmarks|address|site|visited any)")
-        machine.screenshot("morph_open")
+        # If content-hub encounters a problem, it may have crashed the original application issuing the request.
+        # Check that it's still alive
+        machine.succeed("pgrep -u ${user} -f lomiri-system-settings")
 
-        # morph-browser has a separate VM test, there isn't anything new we could test here
+        machine.screenshot("content-hub_exchange")
 
-        machine.send_key("alt-f4")
+        # Testing any more would require more applications & setup, the fact that it's already being attempted is a good sign
+        machine.send_key("esc")
+
+        machine.send_key("alt-f4") # LSS
+        machine.sleep(2) # focus is slow to switch to second window, closing it *really* helps with OCR afterwards
+        machine.send_key("alt-f4") # Morph
 
     # The ayatana indicators are an important part of the experience, and they hold the only graphical way of exiting the session.
-    # Reaching them via the intended way requires wayland mouse control, but ydotool lacks a module for its daemon:
-    # https://github.com/NixOS/nixpkgs/issues/183659
-    # Luckily, there's a test app that also displays their contents, but it's abit inconsistent. Hopefully this is *good-enough*.
+    # There's a test app we could use that also displays their contents, but it's abit inconsistent.
     with subtest("ayatana indicators work"):
-        open_starter()
-        machine.send_chars("Indicators\n")
-        machine.wait_for_text(r"(Indicators|Client|List|network|datetime|session)")
+        mouse_click(735, 0) # the cog in the top-right, for the session indicator
+        machine.wait_for_text(r"(Notifications|Rotation|Battery|Sound|Time|Date|System)")
         machine.screenshot("indicators_open")
 
-        # Element tab order within the indicator menus is not fully deterministic
-        # Only check that the indicators are listed & their items load
+        # Indicator order within the menus *should* be fixed based on per-indicator order setting
+        # Session is the one we clicked, but the last we should test (logout). Go as far left as we can test.
+        machine.send_key("left")
+        machine.send_key("left")
+        machine.send_key("left")
+        machine.send_key("left")
+        machine.send_key("left")
+        # Notifications are usually empty, nothing to check there
+
+        with subtest("ayatana indicator display works"):
+            # We start on this, don't go right
+            machine.wait_for_text("Lock")
+            machine.screenshot("indicators_display")
 
         with subtest("lomiri indicator network works"):
-            # Select indicator-network
-            machine.send_key("tab")
-            # Don't go further down, first entry
-            machine.send_key("ret")
+            machine.send_key("right")
             machine.wait_for_text(r"(Flight|Wi-Fi)")
             machine.screenshot("indicators_network")
 
-        machine.send_key("shift-tab")
-        machine.send_key("ret")
-        machine.wait_for_text(r"(Indicators|Client|List|network|datetime|session)")
+        with subtest("ayatana indicator sound works"):
+            machine.send_key("right")
+            machine.wait_for_text(r"(Silent|Volume)")
+            machine.screenshot("indicators_sound")
+
+        with subtest("ayatana indicator power works"):
+            machine.send_key("right")
+            machine.wait_for_text(r"(Charge|Battery settings)")
+            machine.screenshot("indicators_power")
 
         with subtest("ayatana indicator datetime works"):
-            # Select ayatana-indicator-datetime
-            machine.send_key("tab")
-            machine.send_key("down")
-            machine.send_key("ret")
+            machine.send_key("right")
             machine.wait_for_text("Time and Date Settings")
             machine.screenshot("indicators_timedate")
 
-        machine.send_key("shift-tab")
-        machine.send_key("ret")
-        machine.wait_for_text(r"(Indicators|Client|List|network|datetime|session)")
-
         with subtest("ayatana indicator session works"):
-            # Select ayatana-indicator-session
-            machine.send_key("tab")
-            machine.send_key("down")
-            machine.send_key("ret")
+            machine.send_key("right")
             machine.wait_for_text("Log Out")
             machine.screenshot("indicators_session")
+
+            # We should be able to log out and return to the greeter
+            mouse_click(720, 280) # "Log Out"
+            mouse_click(400, 240) # confirm logout
+            machine.wait_until_fails("pgrep -u ${user} -f 'lomiri --mode=full-shell'")
+            machine.wait_until_succeeds("pgrep -u lightdm -f 'lomiri --mode=greeter'")
   '';
 })
diff --git a/nixos/tests/lvm2/default.nix b/nixos/tests/lvm2/default.nix
index 84f24cbc38593..ba1bdb5de5454 100644
--- a/nixos/tests/lvm2/default.nix
+++ b/nixos/tests/lvm2/default.nix
@@ -10,7 +10,7 @@ let
   tests = let callTest = p: lib.flip (import p) { inherit system pkgs; }; in {
     thinpool = { test = callTest ./thinpool.nix; kernelFilter = lib.id; };
     # we would like to test all versions, but the kernel module currently does not compile against the other versions
-    vdo = { test = callTest ./vdo.nix; kernelFilter = lib.filter (v: v == "5.15"); };
+    vdo = { test = callTest ./vdo.nix; kernelFilter = lib.filter (v: v == "6.1"); };
 
 
     # systemd in stage 1
@@ -26,7 +26,7 @@ let
     };
     vdo-sd-stage-1 = {
       test = callTest ./systemd-stage-1.nix;
-      kernelFilter = lib.filter (v: v == "5.15");
+      kernelFilter = lib.filter (v: v == "6.1");
       flavour = "vdo";
     };
   };
diff --git a/nixos/tests/lvm2/systemd-stage-1.nix b/nixos/tests/lvm2/systemd-stage-1.nix
index 7f106e1b0dd64..fe57a615a9555 100644
--- a/nixos/tests/lvm2/systemd-stage-1.nix
+++ b/nixos/tests/lvm2/systemd-stage-1.nix
@@ -81,7 +81,17 @@ in import ../make-test-python.nix ({ pkgs, lib, ... }: {
       kernelPackages = lib.mkIf (kernelPackages != null) kernelPackages;
     };
 
-    specialisation.boot-lvm.configuration.virtualisation.rootDevice = "/dev/test_vg/test_lv";
+    specialisation.boot-lvm.configuration.virtualisation = {
+      useDefaultFilesystems = false;
+      fileSystems = {
+        "/" = {
+          device = "/dev/test_vg/test_lv";
+          fsType = "xfs";
+        };
+      };
+
+      rootDevice = "/dev/test_vg/test_lv";
+    };
   };
 
   testScript = ''
@@ -99,7 +109,7 @@ in import ../make-test-python.nix ({ pkgs, lib, ... }: {
 
     # Ensure we have successfully booted from LVM
     assert "(initrd)" in machine.succeed("systemd-analyze")  # booted with systemd in stage 1
-    assert "/dev/mapper/test_vg-test_lv on / type ext4" in machine.succeed("mount")
+    assert "/dev/mapper/test_vg-test_lv on / type xfs" in machine.succeed("mount")
     assert "hello" in machine.succeed("cat /test")
     ${extraCheck}
   '';
diff --git a/nixos/tests/matomo.nix b/nixos/tests/matomo.nix
index 130f3dd8485a3..cf54f71b738fc 100644
--- a/nixos/tests/matomo.nix
+++ b/nixos/tests/matomo.nix
@@ -41,14 +41,14 @@ let
 in {
   matomo = matomoTest pkgs.matomo // {
     name = "matomo";
-    meta.maintainers = with maintainers; [ florianjacob kiwi mmilata twey boozedog ];
+    meta.maintainers = with maintainers; [ florianjacob mmilata twey boozedog ];
   };
   matomo-beta = matomoTest pkgs.matomo-beta // {
     name = "matomo-beta";
-    meta.maintainers = with maintainers; [ florianjacob kiwi mmilata twey boozedog ];
+    meta.maintainers = with maintainers; [ florianjacob mmilata twey boozedog ];
   };
   matomo_5 = matomoTest pkgs.matomo_5 // {
     name = "matomo-5";
-    meta.maintainers = with maintainers; [ florianjacob kiwi mmilata twey boozedog ] ++ lib.teams.flyingcircus.members;
+    meta.maintainers = with maintainers; [ florianjacob mmilata twey boozedog ] ++ lib.teams.flyingcircus.members;
   };
 }
diff --git a/nixos/tests/mealie.nix b/nixos/tests/mealie.nix
index 88f749c712948..810d47ecd2ec4 100644
--- a/nixos/tests/mealie.nix
+++ b/nixos/tests/mealie.nix
@@ -3,7 +3,7 @@ import ./make-test-python.nix ({ pkgs, ...} :
 {
   name = "mealie";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ litchipi ];
+    maintainers = [ litchipi anoa ];
   };
 
   nodes = {
diff --git a/nixos/tests/mediamtx.nix b/nixos/tests/mediamtx.nix
index 8cacd02631d95..f40c4a8cb5832 100644
--- a/nixos/tests/mediamtx.nix
+++ b/nixos/tests/mediamtx.nix
@@ -1,57 +1,60 @@
-import ./make-test-python.nix ({ pkgs, lib, ...} :
+import ./make-test-python.nix (
+  { pkgs, lib, ... }:
 
-{
-  name = "mediamtx";
-  meta.maintainers = with lib.maintainers; [ fpletz ];
+  {
+    name = "mediamtx";
+    meta.maintainers = with lib.maintainers; [ fpletz ];
 
-  nodes = {
-    machine = { config, ... }: {
-      services.mediamtx = {
-        enable = true;
-        settings = {
-          metrics = true;
-          paths.all.source = "publisher";
+    nodes = {
+      machine = {
+        services.mediamtx = {
+          enable = true;
+          settings = {
+            metrics = true;
+            paths.all.source = "publisher";
+          };
         };
-      };
 
-      systemd.services.rtmp-publish = {
-        description = "Publish an RTMP stream to mediamtx";
-        after = [ "mediamtx.service" ];
-        bindsTo = [ "mediamtx.service" ];
-        wantedBy = [ "multi-user.target" ];
-        serviceConfig = {
-          DynamicUser = true;
-          Restart = "on-failure";
-          RestartSec = "1s";
-          TimeoutStartSec = "10s";
-          ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -re -f lavfi -i smptebars=size=800x600:rate=10 -c libx264 -f flv rtmp://localhost:1935/test";
+        systemd.services.rtmp-publish = {
+          description = "Publish an RTMP stream to mediamtx";
+          after = [ "mediamtx.service" ];
+          bindsTo = [ "mediamtx.service" ];
+          wantedBy = [ "multi-user.target" ];
+          serviceConfig = {
+            DynamicUser = true;
+            Restart = "on-failure";
+            RestartSec = "1s";
+            TimeoutStartSec = "10s";
+            ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -re -f lavfi -i smptebars=size=800x600:rate=10 -c libx264 -f flv rtmp://localhost:1935/test";
+          };
         };
-      };
 
-      systemd.services.rtmp-receive = {
-        description = "Receive an RTMP stream from mediamtx";
-        after = [ "rtmp-publish.service" ];
-        bindsTo = [ "rtmp-publish.service" ];
-        wantedBy = [ "multi-user.target" ];
-        serviceConfig = {
-          DynamicUser = true;
-          Restart = "on-failure";
-          RestartSec = "1s";
-          TimeoutStartSec = "10s";
-          ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -y -re -i rtmp://localhost:1935/test -f flv /dev/null";
+        systemd.services.rtmp-receive = {
+          description = "Receive an RTMP stream from mediamtx";
+          after = [ "rtmp-publish.service" ];
+          bindsTo = [ "rtmp-publish.service" ];
+          wantedBy = [ "multi-user.target" ];
+          serviceConfig = {
+            DynamicUser = true;
+            Restart = "on-failure";
+            RestartSec = "1s";
+            TimeoutStartSec = "10s";
+            ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -y -re -i rtmp://localhost:1935/test -f flv /dev/null";
+          };
         };
       };
     };
-  };
 
-  testScript = ''
-    start_all()
+    testScript = ''
+      start_all()
 
-    machine.wait_for_unit("mediamtx.service")
-    machine.wait_for_unit("rtmp-publish.service")
-    machine.wait_for_unit("rtmp-receive.service")
-    machine.wait_for_open_port(9998)
-    machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"publish\".*1$'")
-    machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"read\".*1$'")
-  '';
-})
+      machine.wait_for_unit("mediamtx.service")
+      machine.wait_for_unit("rtmp-publish.service")
+      machine.sleep(10)
+      machine.wait_for_unit("rtmp-receive.service")
+      machine.wait_for_open_port(9998)
+      machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"publish\".*1$'")
+      machine.succeed("curl http://localhost:9998/metrics | grep '^rtmp_conns.*state=\"read\".*1$'")
+    '';
+  }
+)
diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix
index e7842debba7a2..33b3ca2c11c2e 100644
--- a/nixos/tests/misc.nix
+++ b/nixos/tests/misc.nix
@@ -44,28 +44,6 @@ in {
 
   testScript =
     ''
-      import json
-
-
-      def get_path_info(path):
-          result = machine.succeed(f"nix --option experimental-features nix-command path-info --json {path}")
-          parsed = json.loads(result)
-          return parsed
-
-
-      with subtest("nix-db"):
-          info = get_path_info("${foo}")
-          print(info)
-
-          if (
-              info[0]["narHash"]
-              != "sha256-BdMdnb/0eWy3EddjE83rdgzWWpQjfWPAj3zDIFMD3Ck="
-          ):
-              raise Exception("narHash not set")
-
-          if info[0]["narSize"] != 128:
-              raise Exception("narSize not set")
-
       with subtest("nixos-version"):
           machine.succeed("[ `nixos-version | wc -w` = 2 ]")
 
@@ -149,9 +127,6 @@ in {
       with subtest("shell-vars"):
           machine.succeed('[ -n "$NIX_PATH" ]')
 
-      with subtest("nix-db"):
-          machine.succeed("nix-store -qR /run/current-system | grep nixos-")
-
       with subtest("Test sysctl"):
           machine.wait_for_unit("systemd-sysctl.service")
           assert "1" == machine.succeed("sysctl -ne vm.swappiness").strip()
diff --git a/nixos/tests/monado.nix b/nixos/tests/monado.nix
index 8368950951e73..6f0d27ee42454 100644
--- a/nixos/tests/monado.nix
+++ b/nixos/tests/monado.nix
@@ -5,7 +5,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     { pkgs, ... }:
 
     {
-      hardware.opengl.enable = true;
+      hardware.graphics.enable = true;
       users.users.alice = {
         isNormalUser = true;
         uid = 1000;
diff --git a/nixos/tests/mpv.nix b/nixos/tests/mpv.nix
index 32a81cbe2495e..c2e151c224760 100644
--- a/nixos/tests/mpv.nix
+++ b/nixos/tests/mpv.nix
@@ -12,7 +12,7 @@ in
     {
       environment.systemPackages = [
         pkgs.curl
-        (pkgs.wrapMpv pkgs.mpv-unwrapped {
+        (pkgs.mpv.override {
           scripts = [ pkgs.mpvScripts.simple-mpv-webui ];
         })
       ];
diff --git a/nixos/tests/mumble.nix b/nixos/tests/mumble.nix
index 8eee454721a13..12fa00b79bbf8 100644
--- a/nixos/tests/mumble.nix
+++ b/nixos/tests/mumble.nix
@@ -15,7 +15,7 @@ in
 {
   name = "mumble";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ thoughtpolice eelco ];
+    maintainers = [ thoughtpolice ];
   };
 
   nodes = {
diff --git a/nixos/tests/munin.nix b/nixos/tests/munin.nix
index e371b2dffa6b8..7b7bf6f41c046 100644
--- a/nixos/tests/munin.nix
+++ b/nixos/tests/munin.nix
@@ -4,7 +4,7 @@
 import ./make-test-python.nix ({ pkgs, ...} : {
   name = "munin";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ domenkozar eelco ];
+    maintainers = [ domenkozar ];
   };
 
   nodes = {
diff --git a/nixos/tests/musescore.nix b/nixos/tests/musescore.nix
index 0720631ed284b..559c91ed8e550 100644
--- a/nixos/tests/musescore.nix
+++ b/nixos/tests/musescore.nix
@@ -43,13 +43,10 @@ in
     )
 
     # Start MuseScore window
-    machine.execute("DISPLAY=:0.0 mscore >&2 &")
+    machine.execute("env XDG_RUNTIME_DIR=$PWD DISPLAY=:0.0 mscore >&2 &")
 
     # Wait until MuseScore has launched
-    machine.wait_for_window("MuseScore 4")
-
-    # Wait until the window has completely initialised
-    machine.wait_for_text("MuseScore 4")
+    machine.wait_for_window("MuseScore Studio")
 
     machine.screenshot("MuseScore0")
 
@@ -75,29 +72,22 @@ in
     machine.screenshot("MuseScore2")
 
     # Go to the export dialogue and create a PDF
-    machine.send_key("alt-f")
-    machine.sleep(1)
-    machine.send_key("e")
-
-    # Wait until the export dialogue appears.
-    machine.wait_for_text("Export")
+    machine.send_key("ctrl-p")
 
-    machine.screenshot("MuseScore3")
+    # Wait until the Print dialogue appears.
+    machine.wait_for_window("Print")
 
-    machine.send_key("shift-tab")
-    machine.sleep(1)
-    machine.send_key("ret")
+    machine.screenshot("MuseScore4")
+    machine.send_key("alt-p")
     machine.sleep(1)
-    machine.send_key("ret")
 
-    machine.screenshot("MuseScore4")
+    machine.screenshot("MuseScore5")
 
     # Wait until PDF is exported
-    machine.wait_for_file('"/root/Documents/MuseScore4/Scores/Untitled score.pdf"')
+    machine.wait_for_file('"/root/Untitled score.pdf"')
 
-    # Check that it contains the title of the score
-    machine.succeed('pdfgrep "Untitled score" "/root/Documents/MuseScore4/Scores/Untitled score.pdf"')
-
-    machine.screenshot("MuseScore5")
+    ## Check that it contains the title of the score
+    machine.succeed('pdfgrep "Untitled score" "/root/Untitled score.pdf"')
+    machine.copy_from_vm("/root/Untitled score.pdf")
   '';
 })
diff --git a/nixos/tests/mycelium/default.nix b/nixos/tests/mycelium/default.nix
index 9174c49d70869..956a822a21860 100644
--- a/nixos/tests/mycelium/default.nix
+++ b/nixos/tests/mycelium/default.nix
@@ -51,6 +51,9 @@ in
       peer1.wait_for_unit("mycelium.service")
       peer2.wait_for_unit("mycelium.service")
 
+      peer1.succeed("mycelium peers list | grep 192.168.1.12")
+      peer2.succeed("mycelium peers list | grep 192.168.1.11")
+
       peer1.succeed("ping -c5 ${peer2-ip}")
       peer2.succeed("ping -c5 ${peer1-ip}")
     '';
diff --git a/nixos/tests/mysql/common.nix b/nixos/tests/mysql/common.nix
index 1cf52347f4c74..ad54b0e00c1b3 100644
--- a/nixos/tests/mysql/common.nix
+++ b/nixos/tests/mysql/common.nix
@@ -4,7 +4,7 @@
     inherit (pkgs) mysql80;
   };
   perconaPackages = {
-    inherit (pkgs) percona-server_8_0;
+    inherit (pkgs) percona-server_lts percona-server_innovation;
   };
   mkTestName = pkg: "mariadb_${builtins.replaceStrings ["."] [""] (lib.versions.majorMinor pkg.version)}";
 }
diff --git a/nixos/tests/mysql/mysql.nix b/nixos/tests/mysql/mysql.nix
index 0a61f9d38fe2e..093da4f46aa10 100644
--- a/nixos/tests/mysql/mysql.nix
+++ b/nixos/tests/mysql/mysql.nix
@@ -146,6 +146,6 @@ in
   }) mariadbPackages)
   // (lib.mapAttrs (_: package: makeMySQLTest {
     inherit package;
-    name = "percona_8_0";
+    name = builtins.replaceStrings ["-"] ["_"] package.pname;
     hasMroonga = false; useSocketAuth = false;
   }) perconaPackages)
diff --git a/nixos/tests/nat.nix b/nixos/tests/nat.nix
index 0b617cea7774c..8b682a8b3aa7c 100644
--- a/nixos/tests/nat.nix
+++ b/nixos/tests/nat.nix
@@ -22,7 +22,7 @@ import ./make-test-python.nix ({ pkgs, lib, withFirewall, nftables ? false, ...
     name = "nat" + (lib.optionalString nftables "Nftables")
                  + (if withFirewall then "WithFirewall" else "Standalone");
     meta = with pkgs.lib.maintainers; {
-      maintainers = [ eelco rob ];
+      maintainers = [ rob ];
     };
 
     nodes =
diff --git a/nixos/tests/netdata.nix b/nixos/tests/netdata.nix
index e3438f63404e7..df4d342905c68 100644
--- a/nixos/tests/netdata.nix
+++ b/nixos/tests/netdata.nix
@@ -11,7 +11,10 @@ import ./make-test-python.nix ({ pkgs, ...} : {
       { pkgs, ... }:
         {
           environment.systemPackages = with pkgs; [ curl jq netdata ];
-          services.netdata.enable = true;
+          services.netdata = {
+            enable = true;
+            python.recommendedPythonPackages = true;
+          };
         };
     };
 
diff --git a/nixos/tests/nextcloud/basic.nix b/nixos/tests/nextcloud/basic.nix
index 428fe0aa10db9..bea08e3231104 100644
--- a/nixos/tests/nextcloud/basic.nix
+++ b/nixos/tests/nextcloud/basic.nix
@@ -1,27 +1,27 @@
-args@{ pkgs, nextcloudVersion ? 22, ... }:
+{ name, pkgs, testBase, system,... }:
 
-(import ../make-test-python.nix ({ pkgs, ...}: let
-  adminpass = "notproduction";
-  adminuser = "root";
-in {
-  name = "nextcloud-basic";
+with import ../../lib/testing-python.nix { inherit system pkgs; };
+runTest ({ config, ... }: {
+  inherit name;
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ globin eqyiel ];
+    maintainers = [ globin eqyiel ma27 ];
   };
 
-  nodes = rec {
+  imports = [ testBase ];
+
+  nodes = {
     # The only thing the client needs to do is download a file.
     client = { ... }: {
       services.davfs2.enable = true;
       systemd.tmpfiles.settings.nextcloud = {
         "/tmp/davfs2-secrets"."f+" = {
           mode = "0600";
-          argument = "http://nextcloud/remote.php/dav/files/${adminuser} ${adminuser} ${adminpass}";
+          argument = "http://nextcloud/remote.php/dav/files/${config.adminuser} ${config.adminuser} ${config.adminpass}";
         };
       };
       virtualisation.fileSystems = {
         "/mnt/dav" = {
-          device = "http://nextcloud/remote.php/dav/files/${adminuser}";
+          device = "http://nextcloud/remote.php/dav/files/${config.adminuser}";
           fsType = "davfs";
           options = let
             davfs2Conf = (pkgs.writeText "davfs2.conf" "secrets /tmp/davfs2-secrets");
@@ -30,11 +30,7 @@ in {
       };
     };
 
-    nextcloud = { config, pkgs, ... }: let
-      cfg = config;
-    in {
-      networking.firewall.allowedTCPPorts = [ 80 ];
-
+    nextcloud = { config, pkgs, ... }: {
       systemd.tmpfiles.rules = [
         "d /var/lib/nextcloud-data 0750 nextcloud nginx - -"
       ];
@@ -42,14 +38,6 @@ in {
       services.nextcloud = {
         enable = true;
         datadir = "/var/lib/nextcloud-data";
-        hostName = "nextcloud";
-        database.createLocally = true;
-        config = {
-          # Don't inherit adminuser since "root" is supposed to be the default
-          adminpassFile = "${pkgs.writeText "adminpass" adminpass}"; # Don't try this at home!
-          dbtableprefix = "nixos_";
-        };
-        package = pkgs.${"nextcloud" + (toString nextcloudVersion)};
         autoUpdateApps = {
           enable = true;
           startAt = "20:00";
@@ -57,64 +45,31 @@ in {
         phpExtraExtensions = all: [ all.bz2 ];
       };
 
-      environment.systemPackages = [ cfg.services.nextcloud.occ ];
+      specialisation.withoutMagick.configuration = {
+        services.nextcloud.enableImagemagick = false;
+      };
     };
-
-    nextcloudWithoutMagick = args@{ config, pkgs, lib, ... }:
-      lib.mkMerge
-      [ (nextcloud args)
-        { services.nextcloud.enableImagemagick = false; } ];
   };
 
-  testScript = { nodes, ... }: let
-    withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
-      #!${pkgs.runtimeShell}
-      export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
-      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/dav/files/${adminuser}"
-      export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
-      export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
-      export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})"
-      "''${@}"
-    '';
-    copySharedFile = pkgs.writeScript "copy-shared-file" ''
-      #!${pkgs.runtimeShell}
-      echo 'hi' | ${withRcloneEnv} ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file
-    '';
-
-    diffSharedFile = pkgs.writeScript "diff-shared-file" ''
-      #!${pkgs.runtimeShell}
-      diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file)
-    '';
-
+  test-helpers.extraTests = { nodes, ... }: let
     findInClosure = what: drv: pkgs.runCommand "find-in-closure" { exportReferencesGraph = [ "graph" drv ]; inherit what; } ''
       test -e graph
       grep "$what" graph >$out || true
     '';
-    nextcloudUsesImagick = findInClosure "imagick" nodes.nextcloud.system.build.vm;
-    nextcloudWithoutDoesntUseIt = findInClosure "imagick" nodes.nextcloudWithoutMagick.system.build.vm;
+    nexcloudWithImagick = findInClosure "imagick" nodes.nextcloud.system.build.vm;
+    nextcloudWithoutImagick = findInClosure "imagick" nodes.nextcloud.specialisation.withoutMagick.configuration.system.build.vm;
   in ''
-    assert open("${nextcloudUsesImagick}").read() != ""
-    assert open("${nextcloudWithoutDoesntUseIt}").read() == ""
+    with subtest("File is in proper nextcloud home"):
+        nextcloud.succeed("test -f ${nodes.nextcloud.services.nextcloud.datadir}/data/root/files/test-shared-file")
+
+    with subtest("Closure checks"):
+        assert open("${nexcloudWithImagick}").read() != ""
+        assert open("${nextcloudWithoutImagick}").read() == ""
+
+    with subtest("Davfs2"):
+        assert "hi" in client.succeed("cat /mnt/dav/test-shared-file")
 
-    nextcloud.start()
-    client.start()
-    nextcloud.wait_for_unit("multi-user.target")
-    # This is just to ensure the nextcloud-occ program is working
-    nextcloud.succeed("nextcloud-occ status")
-    nextcloud.succeed("curl -sSf http://nextcloud/login")
-    # Ensure that no OpenSSL 1.1 is used.
-    nextcloud.succeed(
-        "${nodes.nextcloud.services.phpfpm.pools.nextcloud.phpPackage}/bin/php -i | grep 'OpenSSL Library Version' | awk -F'=>' '{ print $2 }' | awk '{ print $2 }' | grep -v 1.1"
-    )
-    nextcloud.succeed(
-        "${withRcloneEnv} ${copySharedFile}"
-    )
-    client.wait_for_unit("multi-user.target")
-    nextcloud.succeed("test -f /var/lib/nextcloud-data/data/root/files/test-shared-file")
-    client.succeed(
-        "${withRcloneEnv} ${diffSharedFile}"
-    )
-    assert "hi" in client.succeed("cat /mnt/dav/test-shared-file")
-    nextcloud.succeed("grep -vE '^HBEGIN:oc_encryption_module' /var/lib/nextcloud-data/data/root/files/test-shared-file")
+    with subtest("Ensure SSE is disabled by default"):
+        nextcloud.succeed("grep -vE '^HBEGIN:oc_encryption_module' /var/lib/nextcloud-data/data/root/files/test-shared-file")
   '';
-})) args
+})
diff --git a/nixos/tests/nextcloud/default.nix b/nixos/tests/nextcloud/default.nix
index d024adffd9f06..33aa227d2b032 100644
--- a/nixos/tests/nextcloud/default.nix
+++ b/nixos/tests/nextcloud/default.nix
@@ -5,21 +5,108 @@
 
 with pkgs.lib;
 
-foldl
-  (matrix: ver: matrix // {
-    "basic${toString ver}" = import ./basic.nix { inherit system pkgs; nextcloudVersion = ver; };
-    "with-postgresql-and-redis${toString ver}" = import ./with-postgresql-and-redis.nix {
-      inherit system pkgs;
-      nextcloudVersion = ver;
-    };
-    "with-mysql-and-memcached${toString ver}" = import ./with-mysql-and-memcached.nix {
-      inherit system pkgs;
-      nextcloudVersion = ver;
-    };
-    "with-declarative-redis-and-secrets${toString ver}" = import ./with-declarative-redis-and-secrets.nix {
-      inherit system pkgs;
-      nextcloudVersion = ver;
+let
+  baseModule = { config, ... }: {
+    imports = [
+      {
+        options.test-helpers = {
+          rclone = mkOption { type = types.str; };
+          upload-sample = mkOption { type = types.str; };
+          check-sample = mkOption { type = types.str; };
+          init = mkOption { type = types.str; default = ""; };
+          extraTests = mkOption { type = types.either types.str (types.functionTo types.str); default = ""; };
+        };
+        options.adminuser = mkOption { type = types.str; };
+        options.adminpass = mkOption { type = types.str; };
+      }
+    ];
+
+    adminuser = "root";
+    adminpass = "hunter2";
+
+    test-helpers.rclone = "${pkgs.writeShellScript "rclone" ''
+      set -euo pipefail
+      export PATH="${pkgs.rclone}/bin:$PATH"
+      export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
+      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/dav/files/${config.adminuser}"
+      export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
+      export RCLONE_CONFIG_NEXTCLOUD_USER="${config.adminuser}"
+      export RCLONE_CONFIG_NEXTCLOUD_PASS="$(rclone obscure ${config.adminpass})"
+      exec "$@"
+    ''}";
+    test-helpers.upload-sample = "${pkgs.writeShellScript "rclone-upload" ''
+      <<<'hi' rclone rcat nextcloud:test-shared-file
+    ''}";
+    test-helpers.check-sample = "${pkgs.writeShellScript "check-sample" ''
+      set -e
+      diff <(echo 'hi') <(rclone cat nextcloud:test-shared-file)
+    ''}";
+
+    nodes = {
+      client = { ... }: {};
+      nextcloud = {
+        networking.firewall.allowedTCPPorts = [ 80 ];
+        services.nextcloud = {
+          enable = true;
+          hostName = "nextcloud";
+          https = false;
+          database.createLocally = true;
+          config = {
+            adminpassFile = "${pkgs.writeText "adminpass" config.adminpass}"; # Don't try this at home!
+          };
+        };
+      };
     };
-  })
-{ }
-  [ 27 28 29 ]
+
+    testScript = args@{ nodes, ... }: let
+      inherit (config) test-helpers;
+    in mkBefore ''
+      nextcloud.start()
+      client.start()
+      nextcloud.wait_for_unit("multi-user.target")
+
+      ${test-helpers.init}
+
+      with subtest("Ensure nextcloud-occ is working"):
+          nextcloud.succeed("nextcloud-occ status")
+          nextcloud.succeed("curl -sSf http://nextcloud/login")
+
+      with subtest("Upload/Download test"):
+          nextcloud.succeed(
+              "${test-helpers.rclone} ${test-helpers.upload-sample}"
+          )
+          client.wait_for_unit("multi-user.target")
+          client.succeed(
+              "${test-helpers.rclone} ${test-helpers.check-sample}"
+          )
+
+      ${if builtins.isFunction test-helpers.extraTests then test-helpers.extraTests args else test-helpers.extraTests}
+    '';
+  };
+
+  genTests = version:
+    let
+      testBase.imports = [
+        baseModule
+        {
+          nodes.nextcloud = { pkgs, ... }: {
+            services.nextcloud.package = pkgs.${"nextcloud${toString version}"};
+          };
+        }
+      ];
+
+      callNextcloudTest = path:
+        let
+          name = "${removeSuffix ".nix" (baseNameOf path)}${toString version}";
+        in nameValuePair name (import path {
+          inherit system pkgs testBase;
+          name = "nextcloud-${name}";
+        });
+    in map callNextcloudTest [
+      ./basic.nix
+      ./with-mysql-and-memcached.nix
+      ./with-postgresql-and-redis.nix
+      ./with-objectstore.nix
+    ];
+in
+listToAttrs (concatMap genTests [ 27 28 29 ])
diff --git a/nixos/tests/nextcloud/with-mysql-and-memcached.nix b/nixos/tests/nextcloud/with-mysql-and-memcached.nix
index 035a7fdcb0c80..07a3e56fae4af 100644
--- a/nixos/tests/nextcloud/with-mysql-and-memcached.nix
+++ b/nixos/tests/nextcloud/with-mysql-and-memcached.nix
@@ -1,79 +1,37 @@
-args@{ pkgs, nextcloudVersion ? 22, ... }:
+{ pkgs, testBase, system, ... }:
 
-(import ../make-test-python.nix ({ pkgs, ...}: let
-  adminpass = "hunter2";
-  adminuser = "root";
-in {
+with import ../../lib/testing-python.nix { inherit system pkgs; };
+runTest ({ config, ... }: {
   name = "nextcloud-with-mysql-and-memcached";
   meta = with pkgs.lib.maintainers; {
     maintainers = [ eqyiel ];
   };
 
-  nodes = {
-    # The only thing the client needs to do is download a file.
-    client = { ... }: {};
+  imports = [ testBase ];
 
+  nodes = {
     nextcloud = { config, pkgs, ... }: {
-      networking.firewall.allowedTCPPorts = [ 80 ];
-
       services.nextcloud = {
-        enable = true;
-        hostName = "nextcloud";
-        https = true;
-        package = pkgs.${"nextcloud" + (toString nextcloudVersion)};
         caching = {
           apcu = true;
           redis = false;
           memcached = true;
         };
-        database.createLocally = true;
-        config = {
-          dbtype = "mysql";
-          # Don't inherit adminuser since "root" is supposed to be the default
-          adminpassFile = "${pkgs.writeText "adminpass" adminpass}"; # Don't try this at home!
-        };
+        config.dbtype = "mysql";
       };
 
       services.memcached.enable = true;
     };
   };
 
-  testScript = let
+  test-helpers.init = let
     configureMemcached = pkgs.writeScript "configure-memcached" ''
-      #!${pkgs.runtimeShell}
       nextcloud-occ config:system:set memcached_servers 0 0 --value 127.0.0.1 --type string
       nextcloud-occ config:system:set memcached_servers 0 1 --value 11211 --type integer
       nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\APCu' --type string
       nextcloud-occ config:system:set memcache.distributed --value '\OC\Memcache\Memcached' --type string
     '';
-    withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
-      #!${pkgs.runtimeShell}
-      export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
-      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/dav/files/${adminuser}"
-      export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
-      export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
-      export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})"
-    '';
-    copySharedFile = pkgs.writeScript "copy-shared-file" ''
-      #!${pkgs.runtimeShell}
-      echo 'hi' | ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file
-    '';
-
-    diffSharedFile = pkgs.writeScript "diff-shared-file" ''
-      #!${pkgs.runtimeShell}
-      diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file)
-    '';
   in ''
-    start_all()
-    nextcloud.wait_for_unit("multi-user.target")
     nextcloud.succeed("${configureMemcached}")
-    nextcloud.succeed("curl -sSf http://nextcloud/login")
-    nextcloud.succeed(
-        "${withRcloneEnv} ${copySharedFile}"
-    )
-    client.wait_for_unit("multi-user.target")
-    client.succeed(
-        "${withRcloneEnv} ${diffSharedFile}"
-    )
   '';
-})) args
+})
diff --git a/nixos/tests/nextcloud/with-objectstore.nix b/nixos/tests/nextcloud/with-objectstore.nix
new file mode 100644
index 0000000000000..fc26760b8babd
--- /dev/null
+++ b/nixos/tests/nextcloud/with-objectstore.nix
@@ -0,0 +1,96 @@
+{ name, pkgs, testBase, system, ... }:
+
+with import ../../lib/testing-python.nix { inherit system pkgs; };
+runTest ({ config, lib, ... }: let
+  accessKey = "BKIKJAA5BMMU2RHO6IBB";
+  secretKey = "V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12";
+
+  rootCredentialsFile = pkgs.writeText "minio-credentials-full" ''
+    MINIO_ROOT_USER=${accessKey}
+    MINIO_ROOT_PASSWORD=${secretKey}
+  '';
+in {
+  inherit name;
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ onny ma27 ];
+  };
+
+  imports = [ testBase ];
+
+  nodes = {
+    nextcloud = { config, pkgs, ... }: {
+      networking.firewall.allowedTCPPorts = [ 9000 ];
+      environment.systemPackages = [ pkgs.minio-client ];
+
+      services.nextcloud.config.objectstore.s3 = {
+        enable = true;
+        bucket = "nextcloud";
+        autocreate = true;
+        key = accessKey;
+        secretFile = "${pkgs.writeText "secretKey" secretKey}";
+        hostname = "nextcloud";
+        useSsl = false;
+        port = 9000;
+        usePathStyle = true;
+        region = "us-east-1";
+      };
+
+      services.minio = {
+        enable = true;
+        listenAddress = "0.0.0.0:9000";
+        consoleAddress = "0.0.0.0:9001";
+        inherit rootCredentialsFile;
+      };
+    };
+  };
+
+  test-helpers.init = ''
+    nextcloud.wait_for_open_port(9000)
+  '';
+
+  test-helpers.extraTests = { nodes, ... }: ''
+    with subtest("File is not on the filesystem"):
+        nextcloud.succeed("test ! -e ${nodes.nextcloud.services.nextcloud.home}/data/root/files/test-shared-file")
+
+    with subtest("Check if file is in S3"):
+        nextcloud.succeed(
+            "mc config host add minio http://localhost:9000 ${accessKey} ${secretKey} --api s3v4"
+        )
+        files = nextcloud.succeed('mc ls minio/nextcloud|sort').strip().split('\n')
+
+        # Cannot assert an exact number here, nc27 writes more stuff initially into S3.
+        # For now let's assume it's always the most recently added file.
+        assert len(files) > 0, f"""
+          Expected to have at least one object in minio/nextcloud. But `mc ls` gave output:
+
+          '{files}'
+        """
+
+        import re
+        ptrn = re.compile("^\[[A-Z0-9 :-]+\] +(?P<details>[A-Za-z0-9 :]+)$")
+        match = ptrn.match(files[-1].strip())
+        assert match, "Cannot match mc client output!"
+        size, type_, file = tuple(match.group('details').split(' '))
+
+        assert size == "3B", f"""
+          Expected size of uploaded file to be 3 bytes, got {size}
+        """
+
+        assert type_ == 'STANDARD', f"""
+          Expected type of bucket entry to be a file, i.e. 'STANDARD'. Got {type_}
+        """
+
+        assert file.startswith('urn:oid'), """
+          Expected filename to start with 'urn:oid', instead got '{file}.
+        """
+
+    with subtest("Test download from S3"):
+        client.succeed(
+            "env AWS_ACCESS_KEY_ID=${accessKey} AWS_SECRET_ACCESS_KEY=${secretKey} "
+            + f"${lib.getExe pkgs.awscli2} s3 cp s3://nextcloud/{file} test --endpoint-url http://nextcloud:9000 "
+            + "--region us-east-1"
+        )
+
+        client.succeed("test hi = $(cat test)")
+  '';
+})
diff --git a/nixos/tests/nextcloud/with-postgresql-and-redis.nix b/nixos/tests/nextcloud/with-postgresql-and-redis.nix
index 06afc589403dd..24c17f70932d3 100644
--- a/nixos/tests/nextcloud/with-postgresql-and-redis.nix
+++ b/nixos/tests/nextcloud/with-postgresql-and-redis.nix
@@ -1,45 +1,30 @@
-args@{ pkgs, nextcloudVersion ? 22, ... }:
+{ name, pkgs, testBase, system, ... }:
 
-(import ../make-test-python.nix ({ pkgs, ...}: let
-  adminpass = "hunter2";
-  adminuser = "custom-admin-username";
-in {
-  name = "nextcloud-with-postgresql-and-redis";
+with import ../../lib/testing-python.nix { inherit system pkgs; };
+runTest ({ config, ... }: {
+  inherit name;
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ eqyiel ];
+    maintainers = [ eqyiel ma27 ];
   };
 
-  nodes = {
-    # The only thing the client needs to do is download a file.
-    client = { ... }: {};
+  imports = [ testBase ];
 
+  nodes = {
     nextcloud = { config, pkgs, lib, ... }: {
-      networking.firewall.allowedTCPPorts = [ 80 ];
-
       services.nextcloud = {
-        enable = true;
-        hostName = "nextcloud";
-        package = pkgs.${"nextcloud" + (toString nextcloudVersion)};
         caching = {
           apcu = false;
           redis = true;
           memcached = false;
         };
-        database.createLocally = true;
-        config = {
-          dbtype = "pgsql";
-          inherit adminuser;
-          adminpassFile = toString (pkgs.writeText "admin-pass-file" ''
-            ${adminpass}
-          '');
-        };
+        config.dbtype = "pgsql";
         notify_push = {
           enable = true;
           logLevel = "debug";
         };
         extraAppsEnable = true;
-        extraApps = {
-          inherit (pkgs."nextcloud${lib.versions.major config.services.nextcloud.package.version}Packages".apps) notify_push notes;
+        extraApps = with config.services.nextcloud.package.packages.apps; {
+          inherit notify_push notes;
         };
         settings.trusted_proxies = [ "::1" ];
       };
@@ -49,50 +34,27 @@ in {
     };
   };
 
-  testScript = let
+  test-helpers.init = let
     configureRedis = pkgs.writeScript "configure-redis" ''
-      #!${pkgs.runtimeShell}
       nextcloud-occ config:system:set redis 'host' --value 'localhost' --type string
       nextcloud-occ config:system:set redis 'port' --value 6379 --type integer
       nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\Redis' --type string
       nextcloud-occ config:system:set memcache.locking --value '\OC\Memcache\Redis' --type string
     '';
-    withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
-      #!${pkgs.runtimeShell}
-      export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
-      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/dav/files/${adminuser}"
-      export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
-      export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
-      export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})"
-      "''${@}"
-    '';
-    copySharedFile = pkgs.writeScript "copy-shared-file" ''
-      #!${pkgs.runtimeShell}
-      echo 'hi' | ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file
-    '';
-
-    diffSharedFile = pkgs.writeScript "diff-shared-file" ''
-      #!${pkgs.runtimeShell}
-      diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file)
-    '';
   in ''
-    start_all()
-    nextcloud.wait_for_unit("multi-user.target")
     nextcloud.succeed("${configureRedis}")
-    nextcloud.succeed("curl -sSf http://nextcloud/login")
-    nextcloud.succeed(
-        "${withRcloneEnv} ${copySharedFile}"
-    )
-    client.wait_for_unit("multi-user.target")
-    client.execute("${pkgs.lib.getExe pkgs.nextcloud-notify_push.passthru.test_client} http://nextcloud ${adminuser} ${adminpass} >&2 &")
-    client.succeed(
-        "${withRcloneEnv} ${diffSharedFile}"
-    )
-    nextcloud.wait_until_succeeds("journalctl -u nextcloud-notify_push | grep -q \"Sending ping to ${adminuser}\"")
+  '';
+
+  test-helpers.extraTests = ''
+    with subtest("notify-push"):
+        client.execute("${pkgs.lib.getExe pkgs.nextcloud-notify_push.passthru.test_client} http://nextcloud ${config.adminuser} ${config.adminpass} >&2 &")
+        nextcloud.wait_until_succeeds("journalctl -u nextcloud-notify_push | grep -q \"Sending ping to ${config.adminuser}\"")
 
-    # redis cache should not be empty
-    nextcloud.fail('test "[]" = "$(redis-cli --json KEYS "*")"')
+    with subtest("Redis is used for caching"):
+        # redis cache should not be empty
+        nextcloud.fail('test "[]" = "$(redis-cli --json KEYS "*")"')
 
-    nextcloud.fail("curl -f http://nextcloud/nix-apps/notes/lib/AppInfo/Application.php")
+    with subtest("No code is returned when requesting PHP files (regression test)"):
+        nextcloud.fail("curl -f http://nextcloud/nix-apps/notes/lib/AppInfo/Application.php")
   '';
-})) args
+})
diff --git a/nixos/tests/nfs/simple.nix b/nixos/tests/nfs/simple.nix
index 026da9563bc03..077c1d4109356 100644
--- a/nixos/tests/nfs/simple.nix
+++ b/nixos/tests/nfs/simple.nix
@@ -20,7 +20,7 @@ in
 {
   name = "nfs";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ eelco ];
+    maintainers = [ ];
   };
 
   nodes =
diff --git a/nixos/tests/nix/misc.nix b/nixos/tests/nix/misc.nix
new file mode 100644
index 0000000000000..6a22ffe0d901f
--- /dev/null
+++ b/nixos/tests/nix/misc.nix
@@ -0,0 +1,64 @@
+# Miscellaneous small tests that don't warrant their own VM run.
+{ pkgs, ... }:
+
+let
+  inherit (pkgs) lib;
+  tests = {
+    default = testsForPackage { nixPackage = pkgs.nix; };
+    lix = testsForPackage { nixPackage = pkgs.lix; };
+  };
+
+  testsForPackage = args: lib.recurseIntoAttrs {
+    # If the attribute is not named 'test'
+    # You will break all the universe on the release-*.nix side of things.
+    # `discoverTests` relies on `test` existence to perform a `callTest`.
+    test = testMiscFeatures args;
+    passthru.override = args': testsForPackage (args // args');
+  };
+
+  testMiscFeatures = { nixPackage, ... }: pkgs.testers.nixosTest (
+  let
+    foo = pkgs.writeText "foo" "Hello World";
+  in {
+    name = "${nixPackage.pname}-misc";
+    meta.maintainers = with lib.maintainers; [ raitobezarius artturin ];
+
+    nodes.machine =
+      { lib, ... }:
+      {
+        system.extraDependencies = [ foo ];
+
+        nix.package = nixPackage;
+      };
+
+    testScript =
+      ''
+        import json
+
+        def get_path_info(path):
+            result = machine.succeed(f"nix --option experimental-features nix-command path-info --json {path}")
+            parsed = json.loads(result)
+            return parsed
+
+        with subtest("nix-db"):
+            out = "${foo}"
+            info = get_path_info(out)
+            print(info)
+
+            pathinfo = info[0] if isinstance(info, list) else info[out]
+
+            if (
+                pathinfo["narHash"]
+                != "sha256-BdMdnb/0eWy3EddjE83rdgzWWpQjfWPAj3zDIFMD3Ck="
+            ):
+                raise Exception("narHash not set")
+
+            if pathinfo["narSize"] != 128:
+                raise Exception("narSize not set")
+
+        with subtest("nix-db"):
+            machine.succeed("nix-store -qR /run/current-system | grep nixos-")
+      '';
+  });
+  in
+  tests
diff --git a/nixos/tests/odoo.nix b/nixos/tests/odoo.nix
index 00ae4a2137d10..45ec7b7d7a6b7 100644
--- a/nixos/tests/odoo.nix
+++ b/nixos/tests/odoo.nix
@@ -14,6 +14,18 @@ import ./make-test-python.nix ({ pkgs, lib, package ? pkgs.odoo, ...} : {
         package = package;
         domain = "localhost";
       };
+
+      # odoo does not automatically initialize its database,
+      # even if passing what _should_ be the equivalent of these options:
+      #  settings = {
+      #    options = {
+      #      database = "odoo";
+      #      init = "base";
+      #    };
+      #  };
+      systemd.services.odoo.preStart = ''
+        HOME=$STATE_DIRECTORY ${package}/bin/odoo -d odoo -i base --stop-after-init --without-demo all
+      '';
     };
   };
 
diff --git a/nixos/tests/ollama.nix b/nixos/tests/ollama.nix
index 4b21f445cdbd3..30e475553eb1a 100644
--- a/nixos/tests/ollama.nix
+++ b/nixos/tests/ollama.nix
@@ -1,10 +1,10 @@
 import ./make-test-python.nix ({ pkgs, lib, ... }:
 let
-  mainPort = "11434";
-  altPort = "11435";
+  mainPort = 11434;
+  altPort = 11435;
 
   curlRequest = port: request:
-    "curl http://127.0.0.1:${port}/api/generate -d '${builtins.toJSON request}'";
+    "curl http://127.0.0.1:${toString port}/api/generate -d '${builtins.toJSON request}'";
 
   prompt = {
     model = "tinydolphin";
@@ -38,7 +38,7 @@ in
 
     altAddress = { ... }: {
       services.ollama.enable = true;
-      services.ollama.listenAddress = "127.0.0.1:${altPort}";
+      services.ollama.port = altPort;
     };
   };
 
diff --git a/nixos/tests/open-webui.nix b/nixos/tests/open-webui.nix
new file mode 100644
index 0000000000000..f10ef1ad98fb0
--- /dev/null
+++ b/nixos/tests/open-webui.nix
@@ -0,0 +1,33 @@
+{ lib, ... }:
+let
+  mainPort = "8080";
+in
+{
+  name = "open-webui";
+  meta = with lib.maintainers; {
+    maintainers = [ shivaraj-bh ];
+  };
+
+  nodes = {
+    machine =
+      { ... }:
+      {
+        services.open-webui = {
+          enable = true;
+          environment = {
+            # Requires network connection
+            RAG_EMBEDDING_MODEL = "";
+          };
+        };
+      };
+  };
+
+  testScript = ''
+    machine.start()
+
+    machine.wait_for_unit("open-webui.service")
+    machine.wait_for_open_port(${mainPort})
+
+    machine.succeed("curl http://127.0.0.1:${mainPort}")
+  '';
+}
diff --git a/nixos/tests/openarena.nix b/nixos/tests/openarena.nix
index 63dc1b9a68570..4dfe71a9a1e95 100644
--- a/nixos/tests/openarena.nix
+++ b/nixos/tests/openarena.nix
@@ -5,7 +5,7 @@ let
     { pkgs, ... }:
 
     { imports = [ ./common/x11.nix ];
-      hardware.opengl.driSupport = true;
+      hardware.graphics.enable = true;
       environment.systemPackages = [ pkgs.openarena ];
     };
 
diff --git a/nixos/tests/openssh.nix b/nixos/tests/openssh.nix
index 2684b6f45e84e..d420c482ca7f2 100644
--- a/nixos/tests/openssh.nix
+++ b/nixos/tests/openssh.nix
@@ -5,7 +5,7 @@ let inherit (import ./ssh-keys.nix pkgs)
 in {
   name = "openssh";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ aszlig eelco ];
+    maintainers = [ aszlig ];
   };
 
   nodes = {
@@ -111,21 +111,23 @@ in {
     server-no-openssl =
       { ... }:
       {
-        programs.ssh.package = pkgs.opensshPackages.openssh.override {
-          linkOpenssl = false;
-        };
         services.openssh = {
           enable = true;
+          package = pkgs.opensshPackages.openssh.override {
+            linkOpenssl = false;
+          };
           hostKeys = [
             { type = "ed25519"; path = "/etc/ssh/ssh_host_ed25519_key"; }
           ];
           settings = {
-            # Must not specify the OpenSSL provided algorithms.
-            Ciphers = [ "chacha20-poly1305@openssh.com" ];
-            KexAlgorithms = [
-              "curve25519-sha256"
-              "curve25519-sha256@libssh.org"
-            ];
+            # Since this test is against an OpenSSH-without-OpenSSL,
+            # we have to override NixOS's defaults ciphers (which require OpenSSL)
+            # and instead set these to null, which will mean OpenSSH uses its defaults.
+            # Expectedly, OpenSSH's defaults don't require OpenSSL when it's compiled
+            # without OpenSSL.
+            Ciphers = null;
+            KexAlgorithms = null;
+            Macs = null;
           };
         };
         users.users.root.openssh.authorizedKeys.keys = [
@@ -136,11 +138,11 @@ in {
     server-no-pam =
       { pkgs, ... }:
       {
-        programs.ssh.package = pkgs.opensshPackages.openssh.override {
-          withPAM = false;
-        };
         services.openssh = {
           enable = true;
+          package = pkgs.opensshPackages.openssh.override {
+            withPAM = false;
+          };
           settings = {
             UsePAM = false;
           };
diff --git a/nixos/tests/patroni.nix b/nixos/tests/patroni.nix
index 1f15cd59677ad..68fce4051553e 100644
--- a/nixos/tests/patroni.nix
+++ b/nixos/tests/patroni.nix
@@ -155,7 +155,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
               print(node.succeed("patronictl list cluster1"))
               node.wait_until_succeeds(f"[ $(patronictl list -f json cluster1 | jq 'length') == {expected_replicas + 1} ]")
               node.wait_until_succeeds("[ $(patronictl list -f json cluster1 | jq 'map(select(.Role | test(\"^Leader$\"))) | map(select(.State | test(\"^running$\"))) | length') == 1 ]")
-              node.wait_until_succeeds(f"[ $(patronictl list -f json cluster1 | jq 'map(select(.Role | test(\"^Replica$\"))) | map(select(.State | test(\"^running$\"))) | length') == {expected_replicas} ]")
+              node.wait_until_succeeds(f"[ $(patronictl list -f json cluster1 | jq 'map(select(.Role | test(\"^Replica$\"))) | map(select(.State | test(\"^streaming$\"))) | length') == {expected_replicas} ]")
               print(node.succeed("patronictl list cluster1"))
           client.wait_until_succeeds("psql -h 127.0.0.1 -U postgres --command='select 1;'")
 
diff --git a/nixos/tests/pghero.nix b/nixos/tests/pghero.nix
new file mode 100644
index 0000000000000..bce32da008862
--- /dev/null
+++ b/nixos/tests/pghero.nix
@@ -0,0 +1,63 @@
+let
+  pgheroPort = 1337;
+  pgheroUser = "pghero";
+  pgheroPass = "pghero";
+in
+{ lib, ... }: {
+  name = "pghero";
+  meta.maintainers = [ lib.maintainers.tie ];
+
+  nodes.machine = { config, ... }: {
+    services.postgresql = {
+      enable = true;
+      # This test uses default peer authentication (socket and its directory is
+      # world-readably by default), so we essentially test that we can connect
+      # with DynamicUser= set.
+      ensureUsers = [{
+        name = "pghero";
+        ensureClauses.superuser = true;
+      }];
+    };
+    services.pghero = {
+      enable = true;
+      listenAddress = "[::]:${toString pgheroPort}";
+      settings = {
+        databases = {
+          postgres.url = "<%= ENV['POSTGRES_DATABASE_URL'] %>";
+          nulldb.url = "nulldb:///";
+        };
+      };
+      environment = {
+        PGHERO_USERNAME = pgheroUser;
+        PGHERO_PASSWORD = pgheroPass;
+        POSTGRES_DATABASE_URL = "postgresql:///postgres?host=/run/postgresql";
+      };
+    };
+  };
+
+  testScript = ''
+    pgheroPort = ${toString pgheroPort}
+    pgheroUser = "${pgheroUser}"
+    pgheroPass = "${pgheroPass}"
+
+    pgheroUnauthorizedURL = f"http://localhost:{pgheroPort}"
+    pgheroBaseURL = f"http://{pgheroUser}:{pgheroPass}@localhost:{pgheroPort}"
+
+    def expect_http_code(node, code, url):
+        http_code = node.succeed(f"curl -s -o /dev/null -w '%{{http_code}}' '{url}'")
+        assert http_code.split("\n")[-1].strip() == code, \
+          f"expected HTTP status code {code} but got {http_code}"
+
+    machine.wait_for_unit("postgresql.service")
+    machine.wait_for_unit("pghero.service")
+
+    with subtest("requires HTTP Basic Auth credentials"):
+      expect_http_code(machine, "401", pgheroUnauthorizedURL)
+
+    with subtest("works with some databases being unavailable"):
+      expect_http_code(machine, "500", pgheroBaseURL + "/nulldb")
+
+    with subtest("connects to the PostgreSQL database"):
+      expect_http_code(machine, "200", pgheroBaseURL + "/postgres")
+  '';
+}
diff --git a/nixos/tests/pgvecto-rs.nix b/nixos/tests/pgvecto-rs.nix
index cd871dab6a0f1..8d9d6c0b88f51 100644
--- a/nixos/tests/pgvecto-rs.nix
+++ b/nixos/tests/pgvecto-rs.nix
@@ -66,7 +66,7 @@ let
     '';
 
   };
-  applicablePostgresqlVersions = filterAttrs (_: value: versionAtLeast value.version "12") postgresql-versions;
+  applicablePostgresqlVersions = filterAttrs (_: value: versionAtLeast value.version "14") postgresql-versions;
 in
 mapAttrs'
   (name: package: {
diff --git a/nixos/tests/phosh.nix b/nixos/tests/phosh.nix
index d505f0ffc5245..64d6889aaf741 100644
--- a/nixos/tests/phosh.nix
+++ b/nixos/tests/phosh.nix
@@ -3,7 +3,7 @@ import ./make-test-python.nix ({ pkgs, ...}: let
 in {
   name = "phosh";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ tomfitzhenry zhaofengli ];
+    maintainers = [ zhaofengli ];
   };
 
   nodes = {
diff --git a/nixos/tests/podman/default.nix b/nixos/tests/podman/default.nix
index 3eea45832f0a6..f57523cf58886 100644
--- a/nixos/tests/podman/default.nix
+++ b/nixos/tests/podman/default.nix
@@ -1,5 +1,22 @@
 import ../make-test-python.nix (
-  { pkgs, lib, ... }: {
+  { pkgs, lib, ... }:
+  let
+    quadletContainerFile = pkgs.writeText "quadlet.container" ''
+      [Unit]
+      Description=A test quadlet container
+
+      [Container]
+      Image=localhost/scratchimg:latest
+      Exec=bash -c 'trap exit SIGTERM SIGINT; while true; do sleep 1; done'
+      ContainerName=quadlet
+      Volume=/nix/store:/nix/store
+      Volume=/run/current-system/sw/bin:/bin
+
+      [Install]
+      WantedBy=default.target
+    '';
+  in
+  {
     name = "podman";
     meta = {
       maintainers = lib.teams.podman.members;
@@ -174,6 +191,16 @@ import ../make-test-python.nix (
       with subtest("A podman non-member can not use the docker cli"):
           docker.fail(su_cmd("docker version", user="mallory"))
 
+      with subtest("A rootless quadlet container service is created"):
+          dir = "/home/alice/.config/containers/systemd"
+          rootless.succeed(su_cmd("tar cv --files-from /dev/null | podman import - scratchimg"))
+          rootless.succeed(su_cmd(f"mkdir -p {dir}"))
+          rootless.succeed(su_cmd(f"cp -f ${quadletContainerFile} {dir}/quadlet.container"))
+          rootless.systemctl("daemon-reload", "alice")
+          rootless.systemctl("start quadlet", "alice")
+          rootless.wait_until_succeeds(su_cmd("podman ps | grep quadlet"), timeout=20)
+          rootless.systemctl("stop quadlet", "alice")
+
       # TODO: add docker-compose test
 
     '';
diff --git a/nixos/tests/printing.nix b/nixos/tests/printing.nix
index 29c5d810f215a..b413996c67db8 100644
--- a/nixos/tests/printing.nix
+++ b/nixos/tests/printing.nix
@@ -9,7 +9,7 @@ import ./make-test-python.nix (
 {
   name = "printing";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ domenkozar eelco matthewbauer ];
+    maintainers = [ domenkozar matthewbauer ];
   };
 
   nodes.server = { ... }: {
diff --git a/nixos/tests/private-gpt.nix b/nixos/tests/private-gpt.nix
new file mode 100644
index 0000000000000..1c90101d29575
--- /dev/null
+++ b/nixos/tests/private-gpt.nix
@@ -0,0 +1,27 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }:
+let
+  mainPort = "8001";
+in
+{
+  name = "private-gpt";
+  meta = with lib.maintainers; {
+    maintainers = [ ];
+  };
+
+  nodes = {
+    machine = { ... }: {
+      services.private-gpt = {
+        enable = true;
+      };
+    };
+  };
+
+  testScript = ''
+    machine.start()
+
+    machine.wait_for_unit("private-gpt.service")
+    machine.wait_for_open_port(${mainPort})
+
+    machine.succeed("curl http://127.0.0.1:${mainPort}")
+  '';
+})
diff --git a/nixos/tests/prometheus/alertmanager.nix b/nixos/tests/prometheus/alertmanager.nix
new file mode 100644
index 0000000000000..feda8d8fc2bcc
--- /dev/null
+++ b/nixos/tests/prometheus/alertmanager.nix
@@ -0,0 +1,148 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "prometheus-alertmanager";
+
+  nodes = {
+    prometheus = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        alertmanagers = [
+          {
+            scheme = "http";
+            static_configs = [
+              {
+                targets = [
+                  "alertmanager:${toString config.services.prometheus.alertmanager.port}"
+                ];
+              }
+            ];
+          }
+        ];
+
+        rules = [
+          ''
+            groups:
+              - name: test
+                rules:
+                  - alert: InstanceDown
+                    expr: up == 0
+                    for: 5s
+                    labels:
+                      severity: page
+                    annotations:
+                      summary: "Instance {{ $labels.instance }} down"
+          ''
+        ];
+
+        scrapeConfigs = [
+          {
+            job_name = "alertmanager";
+            static_configs = [
+              {
+                targets = [
+                  "alertmanager:${toString config.services.prometheus.alertmanager.port}"
+                ];
+              }
+            ];
+          }
+          {
+            job_name = "node";
+            static_configs = [
+              {
+                targets = [
+                  "node:${toString config.services.prometheus.exporters.node.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    alertmanager = { config, pkgs, ... }: {
+      services.prometheus.alertmanager = {
+        enable = true;
+        openFirewall = true;
+
+        configuration = {
+          global = {
+            resolve_timeout = "1m";
+          };
+
+          route = {
+            # Root route node
+            receiver = "test";
+            group_by = ["..."];
+            continue = false;
+            group_wait = "1s";
+            group_interval = "15s";
+            repeat_interval = "24h";
+          };
+
+          receivers = [
+            {
+              name = "test";
+              webhook_configs = [
+                {
+                  url = "http://logger:6725";
+                  send_resolved = true;
+                  max_alerts = 0;
+                }
+              ];
+            }
+          ];
+        };
+      };
+    };
+
+    logger = { config, pkgs, ... }: {
+      networking.firewall.allowedTCPPorts = [ 6725 ];
+
+      services.prometheus.alertmanagerWebhookLogger.enable = true;
+    };
+  };
+
+  testScript = ''
+    alertmanager.wait_for_unit("alertmanager")
+    alertmanager.wait_for_open_port(9093)
+    alertmanager.wait_until_succeeds("curl -s http://127.0.0.1:9093/-/ready")
+    #alertmanager.wait_until_succeeds("journalctl -o cat -u alertmanager.service | grep 'version=${pkgs.prometheus-alertmanager.version}'")
+
+    logger.wait_for_unit("alertmanager-webhook-logger")
+    logger.wait_for_open_port(6725)
+
+    prometheus.wait_for_unit("prometheus")
+    prometheus.wait_for_open_port(9090)
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"alertmanager\"\}==1)' | "
+      + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(alertmanager_build_info)%20by%20(version)' | "
+      + "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus-alertmanager.version}\"'"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"node\"\}!=1)' | "
+      + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=alertmanager_notifications_total\{integration=\"webhook\"\}' | "
+      + "jq '.data.result[0].value[1]' | grep -v '\"0\"'"
+    )
+
+    logger.wait_until_succeeds(
+      "journalctl -o cat -u alertmanager-webhook-logger.service | grep '\"alertname\":\"InstanceDown\"'"
+    )
+  '';
+})
diff --git a/nixos/tests/prometheus/config-reload.nix b/nixos/tests/prometheus/config-reload.nix
new file mode 100644
index 0000000000000..786668c624ea9
--- /dev/null
+++ b/nixos/tests/prometheus/config-reload.nix
@@ -0,0 +1,116 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "prometheus-config-reload";
+
+  nodes = {
+    prometheus = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        enableReload = true;
+        globalConfig.scrape_interval = "2s";
+        scrapeConfigs = [
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "prometheus:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+
+      specialisation = {
+        "prometheus-config-change" = {
+          configuration = {
+            environment.systemPackages = [ pkgs.yq ];
+
+            # This configuration just adds a new prometheus job
+            # to scrape the node_exporter metrics of the s3 machine.
+            services.prometheus = {
+              scrapeConfigs = [
+                {
+                  job_name = "node";
+                  static_configs = [
+                    {
+                      targets = [ "node:${toString config.services.prometheus.exporters.node.port}" ];
+                    }
+                  ];
+                }
+              ];
+            };
+          };
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    prometheus.wait_for_unit("prometheus")
+    prometheus.wait_for_open_port(9090)
+
+    # Check if switching to a NixOS configuration that changes the prometheus
+    # configuration reloads (instead of restarts) prometheus before the switch
+    # finishes successfully:
+    with subtest("config change reloads prometheus"):
+      import json
+      # We check if prometheus has finished reloading by looking for the message
+      # "Completed loading of configuration file" in the journal between the start
+      # and finish of switching to the new NixOS configuration.
+      #
+      # To mark the start we record the journal cursor before starting the switch:
+      cursor_before_switching = json.loads(
+          prometheus.succeed("journalctl -n1 -o json --output-fields=__CURSOR")
+      )["__CURSOR"]
+
+      # Now we switch:
+      prometheus_config_change = prometheus.succeed(
+          "readlink /run/current-system/specialisation/prometheus-config-change"
+      ).strip()
+      prometheus.succeed(prometheus_config_change + "/bin/switch-to-configuration test")
+
+      # Next we retrieve all logs since the start of switching:
+      logs_after_starting_switching = prometheus.succeed(
+          """
+            journalctl --after-cursor='{cursor_before_switching}' -o json --output-fields=MESSAGE
+          """.format(
+              cursor_before_switching=cursor_before_switching
+          )
+      )
+
+      # Finally we check if the message "Completed loading of configuration file"
+      # occurs before the "finished switching to system configuration" message:
+      finished_switching_msg = (
+          "finished switching to system configuration " + prometheus_config_change
+      )
+      reloaded_before_switching_finished = False
+      finished_switching = False
+      for log_line in logs_after_starting_switching.split("\n"):
+          msg = json.loads(log_line)["MESSAGE"]
+          if "Completed loading of configuration file" in msg:
+              reloaded_before_switching_finished = True
+          if msg == finished_switching_msg:
+              finished_switching = True
+              break
+
+      assert reloaded_before_switching_finished
+      assert finished_switching
+
+      # Check if the reloaded config includes the new node job:
+      prometheus.succeed(
+        """
+          curl -sf http://127.0.0.1:9090/api/v1/status/config \
+            | jq -r .data.yaml \
+            | yq '.scrape_configs | any(.job_name == "node")' \
+            | grep true
+        """
+      )
+  '';
+})
diff --git a/nixos/tests/prometheus/default.nix b/nixos/tests/prometheus/default.nix
new file mode 100644
index 0000000000000..133922a453c05
--- /dev/null
+++ b/nixos/tests/prometheus/default.nix
@@ -0,0 +1,13 @@
+{ system ? builtins.currentSystem
+, config ? { }
+, pkgs ? import ../../.. { inherit system config; }
+}:
+
+{
+  alertmanager = import ./alertmanager.nix { inherit system pkgs; };
+  config-reload = import ./config-reload.nix { inherit system pkgs; };
+  federation = import ./federation.nix { inherit system pkgs; };
+  prometheus-pair = import ./prometheus-pair.nix { inherit system pkgs; };
+  pushgateway = import ./pushgateway.nix { inherit system pkgs; };
+  remote-write = import ./remote-write.nix { inherit system pkgs; };
+}
diff --git a/nixos/tests/prometheus/federation.nix b/nixos/tests/prometheus/federation.nix
new file mode 100644
index 0000000000000..0f05166c8f5da
--- /dev/null
+++ b/nixos/tests/prometheus/federation.nix
@@ -0,0 +1,213 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "prometheus-federation";
+
+  nodes = {
+    global1 = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        scrapeConfigs = [
+          {
+            job_name = "federate";
+            honor_labels = true;
+            metrics_path = "/federate";
+
+            params = {
+              "match[]" = [
+                "{job=\"node\"}"
+                "{job=\"prometheus\"}"
+              ];
+            };
+
+            static_configs = [
+              {
+                targets = [
+                  "prometheus1:${toString config.services.prometheus.port}"
+                  "prometheus2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "global1:${toString config.services.prometheus.port}"
+                  "global2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    global2 = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        scrapeConfigs = [
+          {
+            job_name = "federate";
+            honor_labels = true;
+            metrics_path = "/federate";
+
+            params = {
+              "match[]" = [
+                "{job=\"node\"}"
+                "{job=\"prometheus\"}"
+              ];
+            };
+
+            static_configs = [
+              {
+                targets = [
+                  "prometheus1:${toString config.services.prometheus.port}"
+                  "prometheus2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "global1:${toString config.services.prometheus.port}"
+                  "global2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    prometheus1 = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        scrapeConfigs = [
+          {
+            job_name = "node";
+            static_configs = [
+              {
+                targets = [
+                  "node1:${toString config.services.prometheus.exporters.node.port}"
+                ];
+              }
+            ];
+          }
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "prometheus1:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    prometheus2 = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        scrapeConfigs = [
+          {
+            job_name = "node";
+            static_configs = [
+              {
+                targets = [
+                  "node2:${toString config.services.prometheus.exporters.node.port}"
+                ];
+              }
+            ];
+          }
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "prometheus2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    node1 = { config, pkgs, ... }: {
+      services.prometheus.exporters.node = {
+        enable = true;
+        openFirewall = true;
+      };
+    };
+
+    node2 = { config, pkgs, ... }: {
+      services.prometheus.exporters.node = {
+        enable = true;
+        openFirewall = true;
+      };
+    };
+  };
+
+  testScript = ''
+    for machine in node1, node2:
+      machine.wait_for_unit("prometheus-node-exporter")
+      machine.wait_for_open_port(9100)
+
+    for machine in prometheus1, prometheus2, global1, global2:
+      machine.wait_for_unit("prometheus")
+      machine.wait_for_open_port(9090)
+
+    # Verify both servers got the same data from the exporter
+    for machine in prometheus1, prometheus2:
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"node\"\})' | "
+        + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+      )
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(prometheus_build_info)' | "
+        + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+      )
+
+    for machine in global1, global2:
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"node\"\})' | "
+        + "jq '.data.result[0].value[1]' | grep '\"2\"'"
+      )
+
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(prometheus_build_info)' | "
+        + "jq '.data.result[0].value[1]' | grep '\"4\"'"
+      )
+  '';
+})
diff --git a/nixos/tests/prometheus/prometheus-pair.nix b/nixos/tests/prometheus/prometheus-pair.nix
new file mode 100644
index 0000000000000..3ac70ca0403ec
--- /dev/null
+++ b/nixos/tests/prometheus/prometheus-pair.nix
@@ -0,0 +1,87 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "prometheus-pair";
+
+  nodes = {
+    prometheus1 = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+        scrapeConfigs = [
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "prometheus1:${toString config.services.prometheus.port}"
+                  "prometheus2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    prometheus2 = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+        scrapeConfigs = [
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "prometheus1:${toString config.services.prometheus.port}"
+                  "prometheus2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+  };
+
+  testScript = ''
+    for machine in prometheus1, prometheus2:
+      machine.wait_for_unit("prometheus")
+      machine.wait_for_open_port(9090)
+      machine.wait_until_succeeds("journalctl -o cat -u prometheus.service | grep 'version=${pkgs.prometheus.version}'")
+      machine.wait_until_succeeds("curl -sSf http://localhost:9090/-/healthy")
+
+    # Prometheii ready - run some queries
+    for machine in prometheus1, prometheus2:
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=prometheus_build_info\{instance=\"prometheus1:9090\",version=\"${pkgs.prometheus.version}\"\}' | "
+        + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+      )
+
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=prometheus_build_info\{instance=\"prometheus1:9090\"\}' | "
+        + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+      )
+
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(prometheus_build_info)%20by%20(version)' | "
+        + "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus.version}\"'"
+      )
+
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(prometheus_build_info)%20by%20(version)' | "
+        + "jq '.data.result[0].value[1]' | grep '\"2\"'"
+      )
+
+    prometheus1.log(prometheus1.succeed("systemd-analyze security prometheus.service | grep -v '✓'"))
+  '';
+})
diff --git a/nixos/tests/prometheus/pushgateway.nix b/nixos/tests/prometheus/pushgateway.nix
new file mode 100644
index 0000000000000..7904c8bf45b04
--- /dev/null
+++ b/nixos/tests/prometheus/pushgateway.nix
@@ -0,0 +1,94 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "prometheus-pushgateway";
+
+  nodes = {
+    prometheus = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        scrapeConfigs = [
+          {
+            job_name = "pushgateway";
+            static_configs = [
+              {
+                targets = [
+                  "pushgateway:9091"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    pushgateway = { config, pkgs, ... }: {
+      networking.firewall.allowedTCPPorts = [ 9091 ];
+
+      services.prometheus.pushgateway = {
+        enable = true;
+      };
+    };
+
+    client = { config, pkgs, ... }: {
+    };
+  };
+
+  testScript = ''
+    pushgateway.wait_for_unit("pushgateway")
+    pushgateway.wait_for_open_port(9091)
+    pushgateway.wait_until_succeeds("curl -s http://127.0.0.1:9091/-/ready")
+    pushgateway.wait_until_succeeds("journalctl -o cat -u pushgateway.service | grep 'version=${pkgs.prometheus-pushgateway.version}'")
+
+    prometheus.wait_for_unit("prometheus")
+    prometheus.wait_for_open_port(9090)
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"pushgateway\"\})' | "
+      + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(pushgateway_build_info)%20by%20(version)' | "
+      + "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus-pushgateway.version}\"'"
+    )
+
+    client.wait_for_unit("network-online.target")
+
+    # Add a metric and check in Prometheus
+    client.wait_until_succeeds(
+      "echo 'some_metric 3.14' | curl --data-binary @- http://pushgateway:9091/metrics/job/some_job"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=some_metric' | "
+      + "jq '.data.result[0].value[1]' | grep '\"3.14\"'"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=absent(some_metric)' | "
+      + "jq '.data.result[0].value[1]' | grep 'null'"
+    )
+
+    # Delete the metric, check not in Prometheus
+    client.wait_until_succeeds(
+      "curl -X DELETE http://pushgateway:9091/metrics/job/some_job"
+    )
+
+    prometheus.wait_until_fails(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=some_metric' | "
+      + "jq '.data.result[0].value[1]' | grep '\"3.14\"'"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=absent(some_metric)' | "
+      + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+    )
+  '';
+})
diff --git a/nixos/tests/prometheus/remote-write.nix b/nixos/tests/prometheus/remote-write.nix
new file mode 100644
index 0000000000000..24092b9fb88da
--- /dev/null
+++ b/nixos/tests/prometheus/remote-write.nix
@@ -0,0 +1,73 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "prometheus-remote-write";
+
+  nodes = {
+    receiver = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        extraFlags = [ "--web.enable-remote-write-receiver" ];
+      };
+    };
+
+    prometheus = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        remoteWrite = [
+          {
+            url = "http://receiver:9090/api/v1/write";
+          }
+        ];
+
+        scrapeConfigs = [
+          {
+            job_name = "node";
+            static_configs = [
+              {
+                targets = [
+                  "node:${toString config.services.prometheus.exporters.node.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    node = { config, pkgs, ... }: {
+      services.prometheus.exporters.node = {
+        enable = true;
+        openFirewall = true;
+      };
+    };
+  };
+
+  testScript = ''
+    node.wait_for_unit("prometheus-node-exporter")
+    node.wait_for_open_port(9100)
+
+    for machine in prometheus, receiver:
+      machine.wait_for_unit("prometheus")
+      machine.wait_for_open_port(9090)
+
+    # Verify both servers got the same data from the exporter
+    for machine in prometheus, receiver:
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=node_exporter_build_info\{instance=\"node:9100\"\}' | "
+        + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+      )
+  '';
+})
diff --git a/nixos/tests/proxy.nix b/nixos/tests/proxy.nix
index f8a3d576903e3..ce7131b09a8ab 100644
--- a/nixos/tests/proxy.nix
+++ b/nixos/tests/proxy.nix
@@ -12,7 +12,7 @@ let
 in {
   name = "proxy";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ eelco ];
+    maintainers = [ ];
   };
 
   nodes = {
diff --git a/nixos/tests/qtile.nix b/nixos/tests/qtile.nix
index b4d8f9d421144..96afaa342c524 100644
--- a/nixos/tests/qtile.nix
+++ b/nixos/tests/qtile.nix
@@ -10,7 +10,7 @@ import ./make-test-python.nix ({ lib, ...} : {
     test-support.displayManager.auto.user = "alice";
 
     services.xserver.windowManager.qtile.enable = true;
-    services.displayManager.defaultSession = lib.mkForce "none+qtile";
+    services.displayManager.defaultSession = lib.mkForce "qtile";
 
     environment.systemPackages = [ pkgs.kitty ];
   };
diff --git a/nixos/tests/quake3.nix b/nixos/tests/quake3.nix
index 2d8c5207001cb..ff4025e56f4c4 100644
--- a/nixos/tests/quake3.nix
+++ b/nixos/tests/quake3.nix
@@ -21,7 +21,7 @@ let
     { pkgs, ... }:
 
     { imports = [ ./common/x11.nix ];
-      hardware.opengl.driSupport = true;
+      hardware.graphics.enable = true;
       environment.systemPackages = [ pkgs.quake3demo ];
       nixpkgs.config.packageOverrides = overrides;
       nixpkgs.config.allowUnfreePredicate = unfreePredicate;
@@ -32,7 +32,7 @@ in
 rec {
   name = "quake3";
   meta = with lib.maintainers; {
-    maintainers = [ domenkozar eelco ];
+    maintainers = [ domenkozar ];
   };
 
   # TODO: lcov doesn't work atm
diff --git a/nixos/tests/quickwit.nix b/nixos/tests/quickwit.nix
new file mode 100644
index 0000000000000..7e617c63d7973
--- /dev/null
+++ b/nixos/tests/quickwit.nix
@@ -0,0 +1,103 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }:
+
+let
+  # Define an example Quickwit index schema,
+  # and some `exampleDocs` below, to test if ingesting
+  # and querying works as expected.
+  index_yaml = ''
+    version: 0.7
+    index_id: example_server_logs
+    doc_mapping:
+      mode: dynamic
+      field_mappings:
+        - name: datetime
+          type: datetime
+          fast: true
+          input_formats:
+            - iso8601
+          output_format: iso8601
+          fast_precision: seconds
+          fast: true
+        - name: git
+          type: text
+          tokenizer: raw
+        - name: hostname
+          type: text
+          tokenizer: raw
+        - name: level
+          type: text
+          tokenizer: raw
+        - name: message
+          type: text
+        - name: location
+          type: text
+        - name: source
+          type: text
+      timestamp_field: datetime
+
+    search_settings:
+      default_search_fields: [message]
+
+    indexing_settings:
+      commit_timeout_secs: 10
+  '';
+
+  exampleDocs = ''
+    {"datetime":"2024-05-03T02:36:41.017674444Z","git":"e6e1f087ce12065e44ed3b87b50784e6f9bcc2f9","hostname":"machine-1","level":"Info","message":"Processing request done","location":"path/to/server.c:6442:32","source":""}
+    {"datetime":"2024-05-04T02:36:41.017674444Z","git":"e6e1f087ce12065e44ed3b87b50784e6f9bcc2f9","hostname":"machine-1","level":"Info","message":"Got exception processing request: HTTP 404","location":"path/to/server.c:6444:32","source":""}
+    {"datetime":"2024-05-05T02:36:41.017674444Z","git":"e6e1f087ce12065e44ed3b87b50784e6f9bcc2f9","hostname":"machine-1","level":"Info","message":"Got exception processing request: HTTP 404","location":"path/to/server.c:6444:32","source":""}
+    {"datetime":"2024-05-06T02:36:41.017674444Z","git":"e6e1f087ce12065e44ed3b87b50784e6f9bcc2f9","hostname":"machine-2","level":"Info","message":"Got exception processing request: HTTP 404","location":"path/to/server.c:6444:32","source":""}
+  '';
+in
+{
+  name = "quickwit";
+  meta.maintainers = [ pkgs.lib.maintainers.happysalada ];
+
+  nodes = {
+    quickwit = { config, pkgs, ... }: {
+      services.quickwit.enable = true;
+    };
+  };
+
+  testScript =
+  ''
+    quickwit.wait_for_unit("quickwit")
+    quickwit.wait_for_open_port(7280)
+    quickwit.wait_for_open_port(7281)
+
+    quickwit.wait_until_succeeds(
+      "journalctl -o cat -u quickwit.service | grep 'version: ${pkgs.quickwit.version}'"
+    )
+
+    quickwit.wait_until_succeeds(
+      "journalctl -o cat -u quickwit.service | grep 'transitioned to ready state'"
+    )
+
+    with subtest("verify UI installed"):
+      machine.succeed("curl -sSf http://127.0.0.1:7280/ui/")
+
+    with subtest("injest and query data"):
+      import json
+
+      # Test CLI ingestion
+      print(machine.succeed('${pkgs.quickwit}/bin/quickwit index create --index-config ${pkgs.writeText "index.yaml" index_yaml}'))
+      # Important to use `--wait`, otherwise the queries below race with index processing.
+      print(machine.succeed('${pkgs.quickwit}/bin/quickwit index ingest --index example_server_logs --input-path ${pkgs.writeText "exampleDocs.json" exampleDocs} --wait'))
+
+      # Test CLI query
+      cli_query_output = machine.succeed('${pkgs.quickwit}/bin/quickwit index search --index example_server_logs --query "exception"')
+      print(cli_query_output)
+
+      # Assert query result is as expected.
+      num_hits = len(json.loads(cli_query_output)["hits"])
+      assert num_hits == 3, f"cli_query_output contains unexpected number of results: {num_hits}"
+
+      # Test API query
+      api_query_output = machine.succeed('curl --fail http://127.0.0.1:7280/api/v1/example_server_logs/search?query=exception')
+      print(api_query_output)
+
+    quickwit.log(quickwit.succeed(
+      "systemd-analyze security quickwit.service | grep -v '✓'"
+    ))
+  '';
+})
diff --git a/nixos/tests/rabbitmq.nix b/nixos/tests/rabbitmq.nix
index 040679e68d989..4b8921662b7f4 100644
--- a/nixos/tests/rabbitmq.nix
+++ b/nixos/tests/rabbitmq.nix
@@ -9,7 +9,7 @@ in
 {
   name = "rabbitmq";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ eelco offline ];
+    maintainers = [ offline ];
   };
 
   nodes.machine = {
diff --git a/nixos/tests/renovate.nix b/nixos/tests/renovate.nix
new file mode 100644
index 0000000000000..a30b5b3d60b9c
--- /dev/null
+++ b/nixos/tests/renovate.nix
@@ -0,0 +1,69 @@
+import ./make-test-python.nix (
+  { pkgs, ... }:
+  {
+    name = "renovate";
+    meta.maintainers = with pkgs.lib.maintainers; [ marie natsukium ];
+
+    nodes.machine =
+      { config, ... }:
+      {
+        services.renovate = {
+          enable = true;
+          settings = {
+            platform = "gitea";
+            endpoint = "http://localhost:3000";
+            autodiscover = true;
+            gitAuthor = "Renovate <renovate@example.com>";
+          };
+          credentials = {
+            RENOVATE_TOKEN = "/etc/renovate-token";
+          };
+        };
+        environment.systemPackages = [
+          config.services.forgejo.package
+          pkgs.tea
+          pkgs.git
+        ];
+        services.forgejo = {
+          enable = true;
+          settings.server.HTTP_PORT = 3000;
+        };
+      };
+
+    testScript = ''
+      def gitea(command):
+        return machine.succeed(f"cd /var/lib/forgejo && sudo --user=forgejo GITEA_WORK_DIR=/var/lib/forgejo GITEA_CUSTOM=/var/lib/forgejo/custom gitea {command}")
+
+      machine.wait_for_unit("forgejo.service")
+      machine.wait_for_open_port(3000)
+
+      machine.systemctl("stop forgejo.service")
+
+      gitea("admin user create --username meow --email meow@example.com --password meow")
+
+      machine.systemctl("start forgejo.service")
+      machine.wait_for_unit("forgejo.service")
+      machine.wait_for_open_port(3000)
+
+      accessToken = gitea("admin user generate-access-token --raw --username meow --scopes all | tr -d '\n'")
+
+      machine.succeed(f"tea login add --name default --user meow --token '{accessToken}' --password meow --url http://localhost:3000")
+      machine.succeed("tea repo create --name kitty --init")
+      machine.succeed("git config --global user.name Meow")
+      machine.succeed("git config --global user.email meow@example.com")
+      machine.succeed(f"git clone http://meow:{accessToken}@localhost:3000/meow/kitty.git /tmp/kitty")
+      machine.succeed("echo '{ \"name\": \"meow\", \"version\": \"0.1.0\" }' > /tmp/kitty/package.json")
+      machine.succeed("git -C /tmp/kitty add /tmp/kitty/package.json")
+      machine.succeed("git -C /tmp/kitty commit -m 'add package.json'")
+      machine.succeed("git -C /tmp/kitty push origin")
+
+      machine.succeed(f"echo '{accessToken}' > /etc/renovate-token")
+      machine.systemctl("start renovate.service")
+
+      machine.succeed("tea pulls list --repo meow/kitty | grep 'Configure Renovate'")
+      machine.succeed("tea pulls merge --repo meow/kitty 1")
+
+      machine.systemctl("start renovate.service")
+    '';
+  }
+)
diff --git a/nixos/tests/rke2/default.nix b/nixos/tests/rke2/default.nix
new file mode 100644
index 0000000000000..e8a5f382b735f
--- /dev/null
+++ b/nixos/tests/rke2/default.nix
@@ -0,0 +1,13 @@
+{ system ? builtins.currentSystem
+, pkgs ? import ../../.. { inherit system; }
+, lib ? pkgs.lib
+}:
+let
+  allRKE2 = lib.filterAttrs (n: _: lib.strings.hasPrefix "rke2" n) pkgs;
+in
+{
+  # Run a single node rke2 cluster and verify a pod can run
+  singleNode = lib.mapAttrs (_: rke2: import ./single-node.nix { inherit system pkgs rke2; }) allRKE2;
+  # Run a multi-node rke2 cluster and verify pod networking works across nodes
+  multiNode = lib.mapAttrs (_: rke2: import ./multi-node.nix { inherit system pkgs rke2; }) allRKE2;
+}
diff --git a/nixos/tests/rke2/multi-node.nix b/nixos/tests/rke2/multi-node.nix
new file mode 100644
index 0000000000000..ddf0b60f6fba4
--- /dev/null
+++ b/nixos/tests/rke2/multi-node.nix
@@ -0,0 +1,176 @@
+import ../make-test-python.nix ({ pkgs, lib, rke2, ... }:
+  let
+    pauseImage = pkgs.dockerTools.streamLayeredImage {
+      name = "test.local/pause";
+      tag = "local";
+      contents = pkgs.buildEnv {
+        name = "rke2-pause-image-env";
+        paths = with pkgs; [ tini bashInteractive coreutils socat ];
+      };
+      config.Entrypoint = [ "/bin/tini" "--" "/bin/sleep" "inf" ];
+    };
+    # A daemonset that responds 'server' on port 8000
+    networkTestDaemonset = pkgs.writeText "test.yml" ''
+      apiVersion: apps/v1
+      kind: DaemonSet
+      metadata:
+        name: test
+        labels:
+          name: test
+      spec:
+        selector:
+          matchLabels:
+            name: test
+        template:
+          metadata:
+            labels:
+              name: test
+          spec:
+            containers:
+            - name: test
+              image: test.local/pause:local
+              imagePullPolicy: Never
+              resources:
+                limits:
+                  memory: 20Mi
+              command: ["socat", "TCP4-LISTEN:8000,fork", "EXEC:echo server"]
+    '';
+    tokenFile = pkgs.writeText "token" "p@s$w0rd";
+    agentTokenFile = pkgs.writeText "agent-token" "p@s$w0rd";
+  in
+  {
+    name = "${rke2.name}-multi-node";
+    meta.maintainers = rke2.meta.maintainers;
+
+    nodes = {
+      server1 = { pkgs, ... }: {
+        networking.firewall.enable = false;
+        networking.useDHCP = false;
+        networking.defaultGateway = "192.168.1.1";
+        networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
+          { address = "192.168.1.1"; prefixLength = 24; }
+        ];
+
+        virtualisation.memorySize = 1536;
+        virtualisation.diskSize = 4096;
+
+        services.rke2 = {
+          enable = true;
+          role = "server";
+          inherit tokenFile;
+          inherit agentTokenFile;
+          nodeName = "${rke2.name}-server1";
+          package = rke2;
+          nodeIP = "192.168.1.1";
+          disable = [
+            "rke2-coredns"
+            "rke2-metrics-server"
+            "rke2-ingress-nginx"
+          ];
+          extraFlags = [
+            "--cluster-reset"
+          ];
+        };
+      };
+
+      server2 = { pkgs, ... }: {
+        networking.firewall.enable = false;
+        networking.useDHCP = false;
+        networking.defaultGateway = "192.168.1.2";
+        networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
+          { address = "192.168.1.2"; prefixLength = 24; }
+        ];
+
+        virtualisation.memorySize = 1536;
+        virtualisation.diskSize = 4096;
+
+        services.rke2 = {
+          enable = true;
+          role = "server";
+          serverAddr = "https://192.168.1.1:6443";
+          inherit tokenFile;
+          inherit agentTokenFile;
+          nodeName = "${rke2.name}-server2";
+          package = rke2;
+          nodeIP = "192.168.1.2";
+          disable = [
+            "rke2-coredns"
+            "rke2-metrics-server"
+            "rke2-ingress-nginx"
+          ];
+        };
+      };
+
+      agent1 = { pkgs, ... }: {
+        networking.firewall.enable = false;
+        networking.useDHCP = false;
+        networking.defaultGateway = "192.168.1.3";
+        networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
+          { address = "192.168.1.3"; prefixLength = 24; }
+        ];
+
+        virtualisation.memorySize = 1536;
+        virtualisation.diskSize = 4096;
+
+        services.rke2 = {
+          enable = true;
+          role = "agent";
+          tokenFile = agentTokenFile;
+          serverAddr = "https://192.168.1.2:6443";
+          nodeName = "${rke2.name}-agent1";
+          package = rke2;
+          nodeIP = "192.168.1.3";
+        };
+      };
+    };
+
+    testScript = let
+      kubectl = "${pkgs.kubectl}/bin/kubectl --kubeconfig=/etc/rancher/rke2/rke2.yaml";
+      ctr = "${pkgs.containerd}/bin/ctr -a /run/k3s/containerd/containerd.sock";
+      jq = "${pkgs.jq}/bin/jq";
+      ping = "${pkgs.iputils}/bin/ping";
+    in ''
+      machines = [server1, server2, agent1]
+
+      for machine in machines:
+          machine.start()
+          machine.wait_for_unit("rke2")
+
+      # wait for the agent to show up
+      server1.succeed("${kubectl} get node ${rke2.name}-agent1")
+
+      for machine in machines:
+          machine.succeed("${pauseImage} | ${ctr} image import -")
+
+      server1.succeed("${kubectl} cluster-info")
+      server1.wait_until_succeeds("${kubectl} get serviceaccount default")
+
+      # Now create a pod on each node via a daemonset and verify they can talk to each other.
+      server1.succeed("${kubectl} apply -f ${networkTestDaemonset}")
+      server1.wait_until_succeeds(
+          f'[ "$(${kubectl} get ds test -o json | ${jq} .status.numberReady)" -eq {len(machines)} ]'
+      )
+
+      # Get pod IPs
+      pods = server1.succeed("${kubectl} get po -o json | ${jq} '.items[].metadata.name' -r").splitlines()
+      pod_ips = [
+          server1.succeed(f"${kubectl} get po {n} -o json | ${jq} '.status.podIP' -cr").strip() for n in pods
+      ]
+
+      # Verify each server can ping each pod ip
+      for pod_ip in pod_ips:
+          server1.succeed(f"${ping} -c 1 {pod_ip}")
+          agent1.succeed(f"${ping} -c 1 {pod_ip}")
+
+      # Verify the pods can talk to each other
+      resp = server1.wait_until_succeeds(f"${kubectl} exec {pods[0]} -- socat TCP:{pod_ips[1]}:8000 -")
+      assert resp.strip() == "server"
+      resp = server1.wait_until_succeeds(f"${kubectl} exec {pods[1]} -- socat TCP:{pod_ips[0]}:8000 -")
+      assert resp.strip() == "server"
+
+      # Cleanup
+      server1.succeed("${kubectl} delete -f ${networkTestDaemonset}")
+      for machine in machines:
+          machine.shutdown()
+    '';
+  })
diff --git a/nixos/tests/rke2/single-node.nix b/nixos/tests/rke2/single-node.nix
new file mode 100644
index 0000000000000..5a512eacca0f1
--- /dev/null
+++ b/nixos/tests/rke2/single-node.nix
@@ -0,0 +1,75 @@
+import ../make-test-python.nix ({ pkgs, lib, rke2, ... }:
+  let
+    pauseImage = pkgs.dockerTools.streamLayeredImage {
+      name = "test.local/pause";
+      tag = "local";
+      contents = pkgs.buildEnv {
+        name = "rke2-pause-image-env";
+        paths = with pkgs; [ tini (hiPrio coreutils) busybox ];
+      };
+      config.Entrypoint = [ "/bin/tini" "--" "/bin/sleep" "inf" ];
+    };
+    testPodYaml = pkgs.writeText "test.yaml" ''
+      apiVersion: v1
+      kind: Pod
+      metadata:
+        name: test
+      spec:
+        containers:
+        - name: test
+          image: test.local/pause:local
+          imagePullPolicy: Never
+          command: ["sh", "-c", "sleep inf"]
+    '';
+  in
+  {
+    name = "${rke2.name}-single-node";
+    meta.maintainers = rke2.meta.maintainers;
+
+    nodes.machine = { pkgs, ... }: {
+      networking.firewall.enable = false;
+      networking.useDHCP = false;
+      networking.defaultGateway = "192.168.1.1";
+      networking.interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
+        { address = "192.168.1.1"; prefixLength = 24; }
+      ];
+
+      virtualisation.memorySize = 1536;
+      virtualisation.diskSize = 4096;
+
+      services.rke2 = {
+        enable = true;
+        role = "server";
+        package = rke2;
+        nodeIP = "192.168.1.1";
+        disable = [
+          "rke2-coredns"
+          "rke2-metrics-server"
+          "rke2-ingress-nginx"
+        ];
+        extraFlags = [
+          "--cluster-reset"
+        ];
+      };
+    };
+
+    testScript = let
+      kubectl = "${pkgs.kubectl}/bin/kubectl --kubeconfig=/etc/rancher/rke2/rke2.yaml";
+      ctr = "${pkgs.containerd}/bin/ctr -a /run/k3s/containerd/containerd.sock";
+    in ''
+      start_all()
+
+      machine.wait_for_unit("rke2")
+      machine.succeed("${kubectl} cluster-info")
+      machine.wait_until_succeeds(
+        "${pauseImage} | ${ctr} -n k8s.io image import -"
+      )
+
+      machine.wait_until_succeeds("${kubectl} get serviceaccount default")
+      machine.succeed("${kubectl} apply -f ${testPodYaml}")
+      machine.succeed("${kubectl} wait --for 'condition=Ready' pod/test")
+      machine.succeed("${kubectl} delete -f ${testPodYaml}")
+
+      machine.shutdown()
+    '';
+  })
diff --git a/nixos/tests/rosenpass.nix b/nixos/tests/rosenpass.nix
index ec4046c8c035b..8765fd201c0e5 100644
--- a/nixos/tests/rosenpass.nix
+++ b/nixos/tests/rosenpass.nix
@@ -74,10 +74,8 @@ in
           wireguardConfig.ListenPort = server.wg.listen;
           wireguardPeers = [
             {
-              wireguardPeerConfig = {
-                AllowedIPs = [ "::/0" ];
-                PublicKey = client.wg.public;
-              };
+              AllowedIPs = [ "::/0" ];
+              PublicKey = client.wg.public;
             }
           ];
         };
@@ -97,11 +95,9 @@ in
 
         systemd.network.netdevs."10-${deviceName}".wireguardPeers = [
           {
-            wireguardPeerConfig = {
-              AllowedIPs = [ "::/0" ];
-              PublicKey = server.wg.public;
-              Endpoint = "server:${builtins.toString server.wg.listen}";
-            };
+            AllowedIPs = [ "::/0" ];
+            PublicKey = server.wg.public;
+            Endpoint = "server:${builtins.toString server.wg.listen}";
           }
         ];
 
diff --git a/nixos/tests/samba.nix b/nixos/tests/samba.nix
index 252c3dd9c76e9..53cdbbe1c40f1 100644
--- a/nixos/tests/samba.nix
+++ b/nixos/tests/samba.nix
@@ -3,7 +3,7 @@ import ./make-test-python.nix ({ pkgs, ... }:
 {
   name = "samba";
 
-  meta.maintainers = [ pkgs.lib.maintainers.eelco ];
+  meta.maintainers = [ ];
 
   nodes =
     { client =
diff --git a/nixos/tests/searx.nix b/nixos/tests/searx.nix
index 02a88f690db78..0008424f068b2 100644
--- a/nixos/tests/searx.nix
+++ b/nixos/tests/searx.nix
@@ -1,4 +1,4 @@
-import ./make-test-python.nix ({ pkgs, ...} :
+{ pkgs, ... }:
 
 {
   name = "searx";
@@ -7,108 +7,108 @@ import ./make-test-python.nix ({ pkgs, ...} :
   };
 
   # basic setup: searx running the built-in webserver
-  nodes.base = { ... }: {
-    imports = [ ../modules/profiles/minimal.nix ];
-
-    services.searx = {
-      enable = true;
-      environmentFile = pkgs.writeText "secrets" ''
-        WOLFRAM_API_KEY  = sometoken
-        SEARX_SECRET_KEY = somesecret
-      '';
+  nodes.base =
+    { ... }:
+    {
+      services.searx = {
+        enable = true;
+        environmentFile = pkgs.writeText "secrets" ''
+          WOLFRAM_API_KEY  = sometoken
+          SEARX_SECRET_KEY = somesecret
+        '';
 
-      settings.server =
-        { port = "8080";
+        settings.server = {
+          port = "8080";
           bind_address = "0.0.0.0";
           secret_key = "@SEARX_SECRET_KEY@";
         };
-      settings.engines = [
-        { name = "wolframalpha";
-          api_key = "@WOLFRAM_API_KEY@";
-          engine = "wolframalpha_api";
-        }
-        { name = "startpage";
-          shortcut = "start";
-        }
-      ];
-    };
+        settings.engines = [
+          {
+            name = "wolframalpha";
+            api_key = "@WOLFRAM_API_KEY@";
+            engine = "wolframalpha_api";
+          }
+          {
+            name = "startpage";
+            shortcut = "start";
+          }
+        ];
+      };
 
-  };
+    };
 
   # fancy setup: run in uWSGI and use nginx as proxy
-  nodes.fancy = { config, ... }: {
-    imports = [ ../modules/profiles/minimal.nix ];
-
-    services.searx = {
-      enable = true;
-      # searx refuses to run if unchanged
-      settings.server.secret_key = "somesecret";
-
-      runInUwsgi = true;
-      uwsgiConfig = {
-        # serve using the uwsgi protocol
-        socket = "/run/searx/uwsgi.sock";
-        chmod-socket = "660";
-
-        # use /searx as url "mountpoint"
-        mount = "/searx=searx.webapp:application";
-        module = "";
-        manage-script-name = true;
+  nodes.fancy =
+    { config, ... }:
+    {
+      services.searx = {
+        enable = true;
+        # searx refuses to run if unchanged
+        settings.server.secret_key = "somesecret";
+
+        runInUwsgi = true;
+        uwsgiConfig = {
+          # serve using the uwsgi protocol
+          socket = "/run/searx/uwsgi.sock";
+          chmod-socket = "660";
+
+          # use /searx as url "mountpoint"
+          mount = "/searx=searx.webapp:application";
+          module = "";
+          manage-script-name = true;
+        };
       };
-    };
 
-    # use nginx as reverse proxy
-    services.nginx.enable = true;
-    services.nginx.virtualHosts.localhost = {
-      locations."/searx".extraConfig =
-        ''
+      # use nginx as reverse proxy
+      services.nginx.enable = true;
+      services.nginx.virtualHosts.localhost = {
+        locations."/searx".extraConfig = ''
           include ${pkgs.nginx}/conf/uwsgi_params;
           uwsgi_pass unix:/run/searx/uwsgi.sock;
         '';
-      locations."/searx/static/".alias = "${config.services.searx.package}/share/static/";
-    };
-
-    # allow nginx access to the searx socket
-    users.users.nginx.extraGroups = [ "searx" ];
-
-  };
-
-  testScript =
-    ''
-      base.start()
-
-      with subtest("Settings have been merged"):
-          base.wait_for_unit("searx-init")
-          base.wait_for_file("/run/searx/settings.yml")
-          output = base.succeed(
-              "${pkgs.yq-go}/bin/yq eval"
-              " '.engines[] | select(.name==\"startpage\") | .shortcut'"
-              " /run/searx/settings.yml"
-          ).strip()
-          assert output == "start", "Settings not merged"
+        locations."/searx/static/".alias = "${config.services.searx.package}/share/static/";
+      };
 
-      with subtest("Environment variables have been substituted"):
-          base.succeed("grep -q somesecret /run/searx/settings.yml")
-          base.succeed("grep -q sometoken /run/searx/settings.yml")
-          base.copy_from_vm("/run/searx/settings.yml")
+      # allow nginx access to the searx socket
+      users.users.nginx.extraGroups = [ "searx" ];
 
-      with subtest("Basic setup is working"):
-          base.wait_for_open_port(8080)
-          base.wait_for_unit("searx")
-          base.succeed(
-              "${pkgs.curl}/bin/curl --fail http://localhost:8080"
-          )
-          base.shutdown()
+    };
 
-      with subtest("Nginx+uWSGI setup is working"):
-          fancy.start()
-          fancy.wait_for_open_port(80)
-          fancy.wait_for_unit("uwsgi")
-          fancy.succeed(
-              "${pkgs.curl}/bin/curl --fail http://localhost/searx >&2"
-          )
-          fancy.succeed(
-              "${pkgs.curl}/bin/curl --fail http://localhost/searx/static/themes/simple/js/leaflet.js >&2"
-          )
-    '';
-})
+  testScript = ''
+    base.start()
+
+    with subtest("Settings have been merged"):
+        base.wait_for_unit("searx-init")
+        base.wait_for_file("/run/searx/settings.yml")
+        output = base.succeed(
+            "${pkgs.yq-go}/bin/yq eval"
+            " '.engines[] | select(.name==\"startpage\") | .shortcut'"
+            " /run/searx/settings.yml"
+        ).strip()
+        assert output == "start", "Settings not merged"
+
+    with subtest("Environment variables have been substituted"):
+        base.succeed("grep -q somesecret /run/searx/settings.yml")
+        base.succeed("grep -q sometoken /run/searx/settings.yml")
+        base.copy_from_vm("/run/searx/settings.yml")
+
+    with subtest("Basic setup is working"):
+        base.wait_for_open_port(8080)
+        base.wait_for_unit("searx")
+        base.succeed(
+            "${pkgs.curl}/bin/curl --fail http://localhost:8080"
+        )
+        base.shutdown()
+
+    with subtest("Nginx+uWSGI setup is working"):
+        fancy.start()
+        fancy.wait_for_open_port(80)
+        fancy.wait_for_unit("uwsgi")
+        fancy.succeed(
+            "${pkgs.curl}/bin/curl --fail http://localhost/searx >&2"
+        )
+        fancy.succeed(
+            "${pkgs.curl}/bin/curl --fail http://localhost/searx/static/themes/simple/js/leaflet.js >&2"
+        )
+  '';
+}
diff --git a/nixos/tests/seatd.nix b/nixos/tests/seatd.nix
index 138a6cb1cf44c..9178492fdb0ef 100644
--- a/nixos/tests/seatd.nix
+++ b/nixos/tests/seatd.nix
@@ -39,7 +39,7 @@ in
           dwl -s 'foot touch /tmp/foot_started'
     '';
 
-    hardware.opengl.enable = true;
+    hardware.graphics.enable = true;
     virtualisation.qemu.options = [ "-vga none -device virtio-gpu-pci" ];
     services.seatd.enable = true;
   };
diff --git a/nixos/tests/simple.nix b/nixos/tests/simple.nix
index c36287b4e843b..afd49d481a65d 100644
--- a/nixos/tests/simple.nix
+++ b/nixos/tests/simple.nix
@@ -1,7 +1,7 @@
 import ./make-test-python.nix ({ pkgs, ...} : {
   name = "simple";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ eelco ];
+    maintainers = [ ];
   };
 
   nodes.machine = { ... }: {
diff --git a/nixos/tests/smokeping.nix b/nixos/tests/smokeping.nix
index 04f8139642918..fe1ecad9969b0 100644
--- a/nixos/tests/smokeping.nix
+++ b/nixos/tests/smokeping.nix
@@ -11,7 +11,6 @@ import ./make-test-python.nix ({ pkgs, ...} : {
         networking.domain = "example.com"; # FQDN: sm.example.com
         services.smokeping = {
           enable = true;
-          port = 8081;
           mailHost = "127.0.0.2";
           probeConfig = ''
             + FPing
@@ -25,12 +24,19 @@ import ./make-test-python.nix ({ pkgs, ...} : {
   testScript = ''
     start_all()
     sm.wait_for_unit("smokeping")
-    sm.wait_for_unit("thttpd")
+    sm.wait_for_unit("nginx")
     sm.wait_for_file("/var/lib/smokeping/data/Local/LocalMachine.rrd")
-    sm.succeed("curl -s -f localhost:8081/smokeping.fcgi?target=Local")
+    sm.succeed("curl -s -f localhost/smokeping.fcgi?target=Local")
     # Check that there's a helpful page without explicit path as well.
-    sm.succeed("curl -s -f localhost:8081")
+    sm.succeed("curl -s -f localhost")
     sm.succeed("ls /var/lib/smokeping/cache/Local/LocalMachine_mini.png")
     sm.succeed("ls /var/lib/smokeping/cache/index.html")
+
+    # stop and start the service like nixos-rebuild would do
+    # see https://github.com/NixOS/nixpkgs/issues/265953)
+    sm.succeed("systemctl stop smokeping")
+    sm.succeed("systemctl start smokeping")
+    # ensure all services restarted properly
+    sm.succeed("systemctl --failed | grep -q '0 loaded units listed'")
   '';
 })
diff --git a/nixos/tests/snapper.nix b/nixos/tests/snapper.nix
index 674523584fdaa..0369419930f15 100644
--- a/nixos/tests/snapper.nix
+++ b/nixos/tests/snapper.nix
@@ -19,7 +19,9 @@ import ./make-test-python.nix ({ ... }:
     services.snapper.filters = "/nix";
   };
 
-  testScript = ''
+  testScript = { nodes, ... }: let
+    inherit (nodes.machine.services.snapper) snapshotRootOnBoot;
+  in ''
     machine.succeed("btrfs subvolume create /home/.snapshots")
     machine.succeed("snapper -c home list")
     machine.succeed("snapper -c home create --description empty")
@@ -31,5 +33,6 @@ import ./make-test-python.nix ({ ... }:
     machine.succeed("snapper -c home delete 2")
     machine.succeed("systemctl --wait start snapper-timeline.service")
     machine.succeed("systemctl --wait start snapper-cleanup.service")
+    machine.${if snapshotRootOnBoot then "succeed" else "fail"}("systemctl cat snapper-boot.service")
   '';
 })
diff --git a/nixos/tests/stalwart-mail.nix b/nixos/tests/stalwart-mail.nix
index 634c0e2e39261..173b4fce4ad5d 100644
--- a/nixos/tests/stalwart-mail.nix
+++ b/nixos/tests/stalwart-mail.nix
@@ -18,8 +18,8 @@ in import ./make-test-python.nix ({ lib, ... }: {
         server.hostname = domain;
 
         certificate."snakeoil" = {
-          cert = "file://${certs.${domain}.cert}";
-          private-key = "file://${certs.${domain}.key}";
+          cert = "%{file:${certs.${domain}.cert}}%";
+          private-key = "%{file:${certs.${domain}.key}}%";
         };
 
         server.tls = {
@@ -40,24 +40,24 @@ in import ./make-test-python.nix ({ lib, ... }: {
           };
         };
 
-        session.auth.mechanisms = [ "PLAIN" ];
-        session.auth.directory = "in-memory";
-        storage.directory = "in-memory";  # shared with imap
+        session.auth.mechanisms = "[plain]";
+        session.auth.directory = "'in-memory'";
+        storage.directory = "in-memory";
 
-        session.rcpt.directory = "in-memory";
-        queue.outbound.next-hop = [ "local" ];
+        session.rcpt.directory = "'in-memory'";
+        queue.outbound.next-hop = "'local'";
 
         directory."in-memory" = {
           type = "memory";
           principals = [
             {
-              type = "individual";
+              class = "individual";
               name = "alice";
               secret = "foobar";
               email = [ "alice@${domain}" ];
             }
             {
-              type = "individual";
+              class = "individual";
               name = "bob";
               secret = "foobar";
               email = [ "bob@${domain}" ];
@@ -115,6 +115,6 @@ in import ./make-test-python.nix ({ lib, ... }: {
   '';
 
   meta = {
-    maintainers = with lib.maintainers; [ happysalada pacien ];
+    maintainers = with lib.maintainers; [ happysalada pacien onny ];
   };
 })
diff --git a/nixos/tests/step-ca.nix b/nixos/tests/step-ca.nix
index a855b590232dd..184c35f6b85cc 100644
--- a/nixos/tests/step-ca.nix
+++ b/nixos/tests/step-ca.nix
@@ -62,6 +62,24 @@ import ./make-test-python.nix ({ pkgs, ... }:
             };
           };
 
+        caclientcaddy =
+          { config, pkgs, ... }: {
+            security.pki.certificateFiles = [ "${test-certificates}/root_ca.crt" ];
+
+            networking.firewall.allowedTCPPorts = [ 80 443 ];
+
+            services.caddy = {
+              enable = true;
+              virtualHosts."caclientcaddy".extraConfig = ''
+                respond "Welcome to Caddy!"
+
+                tls caddy@example.org {
+                  ca https://caserver:8443/acme/acme/directory
+                }
+              '';
+            };
+          };
+
         catester = { config, pkgs, ... }: {
           security.pki.certificateFiles = [ "${test-certificates}/root_ca.crt" ];
         };
@@ -71,7 +89,12 @@ import ./make-test-python.nix ({ pkgs, ... }:
       ''
         catester.start()
         caserver.wait_for_unit("step-ca.service")
+        caserver.wait_until_succeeds("journalctl -o cat -u step-ca.service | grep '${pkgs.step-ca.version}'")
+
         caclient.wait_for_unit("acme-finished-caclient.target")
         catester.succeed("curl https://caclient/ | grep \"Welcome to nginx!\"")
+
+        caclientcaddy.wait_for_unit("caddy.service")
+        catester.succeed("curl https://caclientcaddy/ | grep \"Welcome to Caddy!\"")
       '';
   })
diff --git a/nixos/tests/stub-ld.nix b/nixos/tests/stub-ld.nix
index 25161301741b7..72b0aebf3e6ce 100644
--- a/nixos/tests/stub-ld.nix
+++ b/nixos/tests/stub-ld.nix
@@ -45,10 +45,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
           ${if32 "machine.succeed('test -L /${libDir32}/${ldsoBasename32}')"}
 
       with subtest("Try FHS executable"):
-          machine.copy_from_host('${test-exec.${pkgs.system}}','test-exec')
+          machine.copy_from_host('${test-exec.${pkgs.stdenv.hostPlatform.system}}','test-exec')
           machine.succeed('if test-exec/${exec-name} 2>outfile; then false; else [ $? -eq 127 ];fi')
           machine.succeed('grep -qi nixos outfile')
-          ${if32 "machine.copy_from_host('${test-exec.${pkgs32.system}}','test-exec32')"}
+          ${if32 "machine.copy_from_host('${test-exec.${pkgs32.stdenv.hostPlatform.system}}','test-exec32')"}
           ${if32 "machine.succeed('if test-exec32/${exec-name} 2>outfile32; then false; else [ $? -eq 127 ];fi')"}
           ${if32 "machine.succeed('grep -qi nixos outfile32')"}
 
diff --git a/nixos/tests/switch-test.nix b/nixos/tests/switch-test.nix
index 4a7bcd5a82264..d90e5bb088cee 100644
--- a/nixos/tests/switch-test.nix
+++ b/nixos/tests/switch-test.nix
@@ -1,6 +1,6 @@
 # Test configuration switching.
 
-import ./make-test-python.nix ({ lib, pkgs, ...} : let
+import ./make-test-python.nix ({ lib, pkgs, ng, ...} : let
 
   # Simple service that can either be socket-activated or that will
   # listen on port 1234 if not socket-activated.
@@ -48,6 +48,11 @@ in {
 
   nodes = {
     machine = { pkgs, lib, ... }: {
+      system.switch = {
+        enable = !ng;
+        enableNg = ng;
+      };
+
       environment.systemPackages = [ pkgs.socat ]; # for the socket activation stuff
       users.mutableUsers = false;
 
diff --git a/nixos/tests/systemd-confinement.nix b/nixos/tests/systemd-confinement.nix
deleted file mode 100644
index bde5b770ea50d..0000000000000
--- a/nixos/tests/systemd-confinement.nix
+++ /dev/null
@@ -1,184 +0,0 @@
-import ./make-test-python.nix {
-  name = "systemd-confinement";
-
-  nodes.machine = { pkgs, lib, ... }: let
-    testServer = pkgs.writeScript "testserver.sh" ''
-      #!${pkgs.runtimeShell}
-      export PATH=${lib.escapeShellArg "${pkgs.coreutils}/bin"}
-      ${lib.escapeShellArg pkgs.runtimeShell} 2>&1
-      echo "exit-status:$?"
-    '';
-
-    testClient = pkgs.writeScriptBin "chroot-exec" ''
-      #!${pkgs.runtimeShell} -e
-      output="$(echo "$@" | nc -NU "/run/test$(< /teststep).sock")"
-      ret="$(echo "$output" | sed -nre '$s/^exit-status:([0-9]+)$/\1/p')"
-      echo "$output" | head -n -1
-      exit "''${ret:-1}"
-    '';
-
-    mkTestStep = num: {
-      testScript,
-      config ? {},
-      serviceName ? "test${toString num}",
-    }: {
-      systemd.sockets.${serviceName} = {
-        description = "Socket for Test Service ${toString num}";
-        wantedBy = [ "sockets.target" ];
-        socketConfig.ListenStream = "/run/test${toString num}.sock";
-        socketConfig.Accept = true;
-      };
-
-      systemd.services."${serviceName}@" = {
-        description = "Confined Test Service ${toString num}";
-        confinement = (config.confinement or {}) // { enable = true; };
-        serviceConfig = (config.serviceConfig or {}) // {
-          ExecStart = testServer;
-          StandardInput = "socket";
-        };
-      } // removeAttrs config [ "confinement" "serviceConfig" ];
-
-      __testSteps = lib.mkOrder num (''
-        machine.succeed("echo ${toString num} > /teststep")
-      '' + testScript);
-    };
-
-  in {
-    imports = lib.imap1 mkTestStep [
-      { config.confinement.mode = "chroot-only";
-        testScript = ''
-          with subtest("chroot-only confinement"):
-              paths = machine.succeed('chroot-exec ls -1 / | paste -sd,').strip()
-              assert_eq(paths, "bin,nix,run")
-              uid = machine.succeed('chroot-exec id -u').strip()
-              assert_eq(uid, "0")
-              machine.succeed("chroot-exec chown 65534 /bin")
-        '';
-      }
-      { testScript = ''
-          with subtest("full confinement with APIVFS"):
-              machine.fail("chroot-exec ls -l /etc")
-              machine.fail("chroot-exec chown 65534 /bin")
-              assert_eq(machine.succeed('chroot-exec id -u').strip(), "0")
-              machine.succeed("chroot-exec chown 0 /bin")
-        '';
-      }
-      { config.serviceConfig.BindReadOnlyPaths = [ "/etc" ];
-        testScript = ''
-          with subtest("check existence of bind-mounted /etc"):
-              passwd = machine.succeed('chroot-exec cat /etc/passwd').strip()
-              assert len(passwd) > 0, "/etc/passwd must not be empty"
-        '';
-      }
-      { config.serviceConfig.User = "chroot-testuser";
-        config.serviceConfig.Group = "chroot-testgroup";
-        testScript = ''
-          with subtest("check if User/Group really runs as non-root"):
-              machine.succeed("chroot-exec ls -l /dev")
-              uid = machine.succeed('chroot-exec id -u').strip()
-              assert uid != "0", "UID of chroot-testuser shouldn't be 0"
-              machine.fail("chroot-exec touch /bin/test")
-        '';
-      }
-      (let
-        symlink = pkgs.runCommand "symlink" {
-          target = pkgs.writeText "symlink-target" "got me\n";
-        } "ln -s \"$target\" \"$out\"";
-      in {
-        config.confinement.packages = lib.singleton symlink;
-        testScript = ''
-          with subtest("check if symlinks are properly bind-mounted"):
-              machine.fail("chroot-exec test -e /etc")
-              text = machine.succeed('chroot-exec cat ${symlink}').strip()
-              assert_eq(text, "got me")
-        '';
-      })
-      { config.serviceConfig.User = "chroot-testuser";
-        config.serviceConfig.Group = "chroot-testgroup";
-        config.serviceConfig.StateDirectory = "testme";
-        testScript = ''
-          with subtest("check if StateDirectory works"):
-              machine.succeed("chroot-exec touch /tmp/canary")
-              machine.succeed('chroot-exec "echo works > /var/lib/testme/foo"')
-              machine.succeed('test "$(< /var/lib/testme/foo)" = works')
-              machine.succeed("test ! -e /tmp/canary")
-        '';
-      }
-      { testScript = ''
-          with subtest("check if /bin/sh works"):
-              machine.succeed(
-                  "chroot-exec test -e /bin/sh",
-                  'test "$(chroot-exec \'/bin/sh -c "echo bar"\')" = bar',
-              )
-        '';
-      }
-      { config.confinement.binSh = null;
-        testScript = ''
-          with subtest("check if suppressing /bin/sh works"):
-              machine.succeed("chroot-exec test ! -e /bin/sh")
-              machine.succeed('test "$(chroot-exec \'/bin/sh -c "echo foo"\')" != foo')
-        '';
-      }
-      { config.confinement.binSh = "${pkgs.hello}/bin/hello";
-        testScript = ''
-          with subtest("check if we can set /bin/sh to something different"):
-              machine.succeed("chroot-exec test -e /bin/sh")
-              machine.succeed('test "$(chroot-exec /bin/sh -g foo)" = foo')
-        '';
-      }
-      { config.environment.FOOBAR = pkgs.writeText "foobar" "eek\n";
-        testScript = ''
-          with subtest("check if only Exec* dependencies are included"):
-              machine.succeed('test "$(chroot-exec \'cat "$FOOBAR"\')" != eek')
-        '';
-      }
-      { config.environment.FOOBAR = pkgs.writeText "foobar" "eek\n";
-        config.confinement.fullUnit = true;
-        testScript = ''
-          with subtest("check if all unit dependencies are included"):
-              machine.succeed('test "$(chroot-exec \'cat "$FOOBAR"\')" = eek')
-        '';
-      }
-      { serviceName = "shipped-unitfile";
-        config.confinement.mode = "chroot-only";
-        testScript = ''
-          with subtest("check if shipped unit file still works"):
-              machine.succeed(
-                  'chroot-exec \'kill -9 $$ 2>&1 || :\' | '
-                  'grep -q "Too many levels of symbolic links"'
-              )
-        '';
-      }
-    ];
-
-    options.__testSteps = lib.mkOption {
-      type = lib.types.lines;
-      description = "All of the test steps combined as a single script.";
-    };
-
-    config.environment.systemPackages = lib.singleton testClient;
-    config.systemd.packages = lib.singleton (pkgs.writeTextFile {
-      name = "shipped-unitfile";
-      destination = "/etc/systemd/system/shipped-unitfile@.service";
-      text = ''
-        [Service]
-        SystemCallFilter=~kill
-        SystemCallErrorNumber=ELOOP
-      '';
-    });
-
-    config.users.groups.chroot-testgroup = {};
-    config.users.users.chroot-testuser = {
-      isSystemUser = true;
-      description = "Chroot Test User";
-      group = "chroot-testgroup";
-    };
-  };
-
-  testScript = { nodes, ... }: ''
-    def assert_eq(a, b):
-        assert a == b, f"{a} != {b}"
-
-    machine.wait_for_unit("multi-user.target")
-  '' + nodes.machine.config.__testSteps;
-}
diff --git a/nixos/tests/systemd-confinement/checkperms.py b/nixos/tests/systemd-confinement/checkperms.py
new file mode 100644
index 0000000000000..3c7ba279a3d20
--- /dev/null
+++ b/nixos/tests/systemd-confinement/checkperms.py
@@ -0,0 +1,187 @@
+import errno
+import os
+
+from enum import IntEnum
+from pathlib import Path
+
+
+class Accessibility(IntEnum):
+    """
+    The level of accessibility we have on a file or directory.
+
+    This is needed to assess the attack surface on the file system namespace we
+    have within a confined service. Higher levels mean more permissions for the
+    user and thus a bigger attack surface.
+    """
+    NONE = 0
+
+    # Directories can be listed or files can be read.
+    READABLE = 1
+
+    # This is for special file systems such as procfs and for stuff such as
+    # FIFOs or character special files. The reason why this has a lower value
+    # than WRITABLE is because those files are more restricted on what and how
+    # they can be written to.
+    SPECIAL = 2
+
+    # Another special case are sticky directories, which do allow write access
+    # but restrict deletion. This does *not* apply to sticky directories that
+    # are read-only.
+    STICKY = 3
+
+    # Essentially full permissions, the kind of accessibility we want to avoid
+    # in most cases.
+    WRITABLE = 4
+
+    def assert_on(self, path: Path) -> None:
+        """
+        Raise an AssertionError if the given 'path' allows for more
+        accessibility than 'self'.
+        """
+        actual = self.NONE
+
+        if path.is_symlink():
+            actual = self.READABLE
+        elif path.is_dir():
+            writable = True
+
+            dummy_file = path / 'can_i_write'
+            try:
+                dummy_file.touch()
+            except OSError as e:
+                if e.errno in [errno.EROFS, errno.EACCES]:
+                    writable = False
+                else:
+                    raise
+            else:
+                dummy_file.unlink()
+
+            if writable:
+                # The reason why we test this *after* we made sure it's
+                # writable is because we could have a sticky directory where
+                # the current user doesn't have write access.
+                if path.stat().st_mode & 0o1000 == 0o1000:
+                    actual = self.STICKY
+                else:
+                    actual = self.WRITABLE
+            else:
+                actual = self.READABLE
+        elif path.is_file():
+            try:
+                with path.open('rb') as fp:
+                    fp.read(1)
+                actual = self.READABLE
+            except PermissionError:
+                pass
+
+            writable = True
+            try:
+                with path.open('ab') as fp:
+                    fp.write('x')
+                    size = fp.tell()
+                    fp.truncate(size)
+            except PermissionError:
+                writable = False
+            except OSError as e:
+                if e.errno == errno.ETXTBSY:
+                    writable = os.access(path, os.W_OK)
+                elif e.errno == errno.EROFS:
+                    writable = False
+                else:
+                    raise
+
+            # Let's always try to fail towards being writable, so if *either*
+            # access(2) or a real write is successful it's writable. This is to
+            # make sure we don't accidentally introduce no-ops if we have bugs
+            # in the more complicated real write code above.
+            if writable or os.access(path, os.W_OK):
+                actual = self.WRITABLE
+        else:
+            # We need to be very careful when writing to or reading from
+            # special files (eg.  FIFOs), since they can possibly block. So if
+            # it's not a file, just trust that access(2) won't lie.
+            if os.access(path, os.R_OK):
+                actual = self.READABLE
+
+            if os.access(path, os.W_OK):
+                actual = self.SPECIAL
+
+        if actual > self:
+            stat = path.stat()
+            details = ', '.join([
+                f'permissions: {stat.st_mode & 0o7777:o}',
+                f'uid: {stat.st_uid}',
+                f'group: {stat.st_gid}',
+            ])
+
+            raise AssertionError(
+                f'Expected at most {self!r} but got {actual!r} for path'
+                f' {path} ({details}).'
+            )
+
+
+def is_special_fs(path: Path) -> bool:
+    """
+    Check whether the given path truly is a special file system such as procfs
+    or sysfs.
+    """
+    try:
+        if path == Path('/proc'):
+            return (path / 'version').read_text().startswith('Linux')
+        elif path == Path('/sys'):
+            return b'Linux' in (path / 'kernel' / 'notes').read_bytes()
+    except FileNotFoundError:
+        pass
+    return False
+
+
+def is_empty_dir(path: Path) -> bool:
+    try:
+        next(path.iterdir())
+        return False
+    except (StopIteration, PermissionError):
+        return True
+
+
+def _assert_permissions_in_directory(
+    directory: Path,
+    accessibility: Accessibility,
+    subdirs: dict[Path, Accessibility],
+) -> None:
+    accessibility.assert_on(directory)
+
+    for file in directory.iterdir():
+        if is_special_fs(file):
+            msg = f'Got unexpected special filesystem at {file}.'
+            assert subdirs.pop(file) == Accessibility.SPECIAL, msg
+        elif not file.is_symlink() and file.is_dir():
+            subdir_access = subdirs.pop(file, accessibility)
+            if is_empty_dir(file):
+                # Whenever we got an empty directory, we check the permission
+                # constraints on the current directory (except if specified
+                # explicitly in subdirs) because for example if we're non-root
+                # (the constraints of the current directory are thus
+                # Accessibility.READABLE), we really have to make sure that
+                # empty directories are *never* writable.
+                subdir_access.assert_on(file)
+            else:
+                _assert_permissions_in_directory(file, subdir_access, subdirs)
+        else:
+            subdirs.pop(file, accessibility).assert_on(file)
+
+
+def assert_permissions(subdirs: dict[str, Accessibility]) -> None:
+    """
+    Recursively check whether the file system conforms to the accessibility
+    specification we specified via 'subdirs'.
+    """
+    root = Path('/')
+    absolute_subdirs = {root / p: a for p, a in subdirs.items()}
+    _assert_permissions_in_directory(
+        root,
+        Accessibility.WRITABLE if os.getuid() == 0 else Accessibility.READABLE,
+        absolute_subdirs,
+    )
+    for file in absolute_subdirs.keys():
+        msg = f'Expected {file} to exist, but it was nowwhere to be found.'
+        raise AssertionError(msg)
diff --git a/nixos/tests/systemd-confinement/default.nix b/nixos/tests/systemd-confinement/default.nix
new file mode 100644
index 0000000000000..15d442d476b08
--- /dev/null
+++ b/nixos/tests/systemd-confinement/default.nix
@@ -0,0 +1,274 @@
+import ../make-test-python.nix {
+  name = "systemd-confinement";
+
+  nodes.machine = { pkgs, lib, ... }: let
+    testLib = pkgs.python3Packages.buildPythonPackage {
+      name = "confinement-testlib";
+      unpackPhase = ''
+        cat > setup.py <<EOF
+        from setuptools import setup
+        setup(name='confinement-testlib', py_modules=["checkperms"])
+        EOF
+        cp ${./checkperms.py} checkperms.py
+      '';
+    };
+
+    mkTest = name: testScript: pkgs.writers.writePython3 "${name}.py" {
+      libraries = [ pkgs.python3Packages.pytest testLib ];
+    } ''
+      # This runs our test script by using pytest's assertion rewriting, so
+      # that whenever we use "assert <something>", the actual values are
+      # printed rather than getting a generic AssertionError or the need to
+      # pass an explicit assertion error message.
+      import ast
+      from pathlib import Path
+      from _pytest.assertion.rewrite import rewrite_asserts
+
+      script = Path('${pkgs.writeText "${name}-main.py" ''
+        import errno, os, pytest, signal
+        from subprocess import run
+        from checkperms import Accessibility, assert_permissions
+
+        ${testScript}
+      ''}') # noqa
+      filename = str(script)
+      source = script.read_bytes()
+
+      tree = ast.parse(source, filename=filename)
+      rewrite_asserts(tree, source, filename)
+      exec(compile(tree, filename, 'exec', dont_inherit=True))
+    '';
+
+    mkTestStep = num: {
+      description,
+      testScript,
+      config ? {},
+      serviceName ? "test${toString num}",
+      rawUnit ? null,
+    }: {
+      systemd.packages = lib.optional (rawUnit != null) (pkgs.writeTextFile {
+        name = serviceName;
+        destination = "/etc/systemd/system/${serviceName}.service";
+        text = rawUnit;
+      });
+
+      systemd.services.${serviceName} = {
+        inherit description;
+        requiredBy = [ "multi-user.target" ];
+        confinement = (config.confinement or {}) // { enable = true; };
+        serviceConfig = (config.serviceConfig or {}) // {
+          ExecStart = mkTest serviceName testScript;
+          Type = "oneshot";
+        };
+      } // removeAttrs config [ "confinement" "serviceConfig" ];
+    };
+
+    parametrisedTests = lib.concatMap ({ user, privateTmp }: let
+      withTmp = if privateTmp then "with PrivateTmp" else "without PrivateTmp";
+
+      serviceConfig = if user == "static-user" then {
+        User = "chroot-testuser";
+        Group = "chroot-testgroup";
+      } else if user == "dynamic-user" then {
+        DynamicUser = true;
+      } else {};
+
+    in [
+      { description = "${user}, chroot-only confinement ${withTmp}";
+        config = {
+          confinement.mode = "chroot-only";
+          # Only set if privateTmp is true to ensure that the default is false.
+          serviceConfig = serviceConfig // lib.optionalAttrs privateTmp {
+            PrivateTmp = true;
+          };
+        };
+        testScript = if user == "root" then ''
+          assert os.getuid() == 0
+          assert os.getgid() == 0
+
+          assert_permissions({
+            'bin': Accessibility.READABLE,
+            'nix': Accessibility.READABLE,
+            'run': Accessibility.READABLE,
+            ${lib.optionalString privateTmp "'tmp': Accessibility.STICKY,"}
+            ${lib.optionalString privateTmp "'var': Accessibility.READABLE,"}
+            ${lib.optionalString privateTmp "'var/tmp': Accessibility.STICKY,"}
+          })
+        '' else ''
+          assert os.getuid() != 0
+          assert os.getgid() != 0
+
+          assert_permissions({
+            'bin': Accessibility.READABLE,
+            'nix': Accessibility.READABLE,
+            'run': Accessibility.READABLE,
+            ${lib.optionalString privateTmp "'tmp': Accessibility.STICKY,"}
+            ${lib.optionalString privateTmp "'var': Accessibility.READABLE,"}
+            ${lib.optionalString privateTmp "'var/tmp': Accessibility.STICKY,"}
+          })
+        '';
+      }
+      { description = "${user}, full APIVFS confinement ${withTmp}";
+        config = {
+          # Only set if privateTmp is false to ensure that the default is true.
+          serviceConfig = serviceConfig // lib.optionalAttrs (!privateTmp) {
+            PrivateTmp = false;
+          };
+        };
+        testScript = if user == "root" then ''
+          assert os.getuid() == 0
+          assert os.getgid() == 0
+
+          assert_permissions({
+            'bin': Accessibility.READABLE,
+            'nix': Accessibility.READABLE,
+            ${lib.optionalString privateTmp "'tmp': Accessibility.STICKY,"}
+            'run': Accessibility.WRITABLE,
+
+            'proc': Accessibility.SPECIAL,
+            'sys': Accessibility.SPECIAL,
+            'dev': Accessibility.WRITABLE,
+
+            ${lib.optionalString privateTmp "'var': Accessibility.READABLE,"}
+            ${lib.optionalString privateTmp "'var/tmp': Accessibility.STICKY,"}
+          })
+        '' else ''
+          assert os.getuid() != 0
+          assert os.getgid() != 0
+
+          assert_permissions({
+            'bin': Accessibility.READABLE,
+            'nix': Accessibility.READABLE,
+            ${lib.optionalString privateTmp "'tmp': Accessibility.STICKY,"}
+            'run': Accessibility.STICKY,
+
+            'proc': Accessibility.SPECIAL,
+            'sys': Accessibility.SPECIAL,
+            'dev': Accessibility.SPECIAL,
+            'dev/shm': Accessibility.STICKY,
+            'dev/mqueue': Accessibility.STICKY,
+
+            ${lib.optionalString privateTmp "'var': Accessibility.READABLE,"}
+            ${lib.optionalString privateTmp "'var/tmp': Accessibility.STICKY,"}
+          })
+        '';
+      }
+    ]) (lib.cartesianProductOfSets {
+      user = [ "root" "dynamic-user" "static-user" ];
+      privateTmp = [ true false ];
+    });
+
+  in {
+    imports = lib.imap1 mkTestStep (parametrisedTests ++ [
+      { description = "existence of bind-mounted /etc";
+        config.serviceConfig.BindReadOnlyPaths = [ "/etc" ];
+        testScript = ''
+          assert Path('/etc/passwd').read_text()
+        '';
+      }
+      (let
+        symlink = pkgs.runCommand "symlink" {
+          target = pkgs.writeText "symlink-target" "got me";
+        } "ln -s \"$target\" \"$out\"";
+      in {
+        description = "check if symlinks are properly bind-mounted";
+        config.confinement.packages = lib.singleton symlink;
+        testScript = ''
+          assert Path('${symlink}').read_text() == 'got me'
+        '';
+      })
+      { description = "check if StateDirectory works";
+        config.serviceConfig.User = "chroot-testuser";
+        config.serviceConfig.Group = "chroot-testgroup";
+        config.serviceConfig.StateDirectory = "testme";
+
+        # We restart on purpose here since we want to check whether the state
+        # directory actually persists.
+        config.serviceConfig.Restart = "on-failure";
+        config.serviceConfig.RestartMode = "direct";
+
+        testScript = ''
+          assert not Path('/tmp/canary').exists()
+          Path('/tmp/canary').touch()
+
+          if (foo := Path('/var/lib/testme/foo')).exists():
+            assert Path('/var/lib/testme/foo').read_text() == 'works'
+          else:
+            Path('/var/lib/testme/foo').write_text('works')
+            print('<4>Exiting with failure to check persistence on restart.')
+            raise SystemExit(1)
+        '';
+      }
+      { description = "check if /bin/sh works";
+        testScript = ''
+          assert Path('/bin/sh').exists()
+
+          result = run(
+            ['/bin/sh', '-c', 'echo -n bar'],
+            capture_output=True,
+            check=True,
+          )
+          assert result.stdout == b'bar'
+        '';
+      }
+      { description = "check if suppressing /bin/sh works";
+        config.confinement.binSh = null;
+        testScript = ''
+          assert not Path('/bin/sh').exists()
+          with pytest.raises(FileNotFoundError):
+            run(['/bin/sh', '-c', 'echo foo'])
+        '';
+      }
+      { description = "check if we can set /bin/sh to something different";
+        config.confinement.binSh = "${pkgs.hello}/bin/hello";
+        testScript = ''
+          assert Path('/bin/sh').exists()
+          result = run(
+            ['/bin/sh', '-g', 'foo'],
+            capture_output=True,
+            check=True,
+          )
+          assert result.stdout == b'foo\n'
+        '';
+      }
+      { description = "check if only Exec* dependencies are included";
+        config.environment.FOOBAR = pkgs.writeText "foobar" "eek";
+        testScript = ''
+          with pytest.raises(FileNotFoundError):
+            Path(os.environ['FOOBAR']).read_text()
+        '';
+      }
+      { description = "check if fullUnit includes all dependencies";
+        config.environment.FOOBAR = pkgs.writeText "foobar" "eek";
+        config.confinement.fullUnit = true;
+        testScript = ''
+          assert Path(os.environ['FOOBAR']).read_text() == 'eek'
+        '';
+      }
+      { description = "check if shipped unit file still works";
+        config.confinement.mode = "chroot-only";
+        rawUnit = ''
+          [Service]
+          SystemCallFilter=~kill
+          SystemCallErrorNumber=ELOOP
+        '';
+        testScript = ''
+          with pytest.raises(OSError) as excinfo:
+            os.kill(os.getpid(), signal.SIGKILL)
+          assert excinfo.value.errno == errno.ELOOP
+        '';
+      }
+    ]);
+
+    config.users.groups.chroot-testgroup = {};
+    config.users.users.chroot-testuser = {
+      isSystemUser = true;
+      description = "Chroot Test User";
+      group = "chroot-testgroup";
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("multi-user.target")
+  '';
+}
diff --git a/nixos/tests/systemd-initrd-luks-fido2.nix b/nixos/tests/systemd-initrd-luks-fido2.nix
index f9f75ab7f301c..207f51f4dd9b4 100644
--- a/nixos/tests/systemd-initrd-luks-fido2.nix
+++ b/nixos/tests/systemd-initrd-luks-fido2.nix
@@ -9,7 +9,6 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
       # Booting off the encrypted disk requires having a Nix store available for the init script
       mountHostNixStore = true;
       useEFIBoot = true;
-      qemu.package = lib.mkForce (pkgs.qemu_test.override { canokeySupport = true; });
       qemu.options = [ "-device canokey,file=/tmp/canokey-file" ];
     };
     boot.loader.systemd-boot.enable = true;
diff --git a/nixos/tests/systemd-initrd-modprobe.nix b/nixos/tests/systemd-initrd-modprobe.nix
index 0f93492176b44..e563552a645fd 100644
--- a/nixos/tests/systemd-initrd-modprobe.nix
+++ b/nixos/tests/systemd-initrd-modprobe.nix
@@ -4,21 +4,21 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
   nodes.machine = { pkgs, ... }: {
     testing.initrdBackdoor = true;
     boot.initrd.systemd.enable = true;
-    boot.initrd.kernelModules = [ "loop" ]; # Load module in initrd.
+    boot.initrd.kernelModules = [ "tcp_hybla" ]; # Load module in initrd.
     boot.extraModprobeConfig = ''
-      options loop max_loop=42
+      options tcp_hybla rtt0=42
     '';
   };
 
   testScript = ''
     machine.wait_for_unit("initrd.target")
-    max_loop = machine.succeed("cat /sys/module/loop/parameters/max_loop")
-    assert int(max_loop) == 42, "Parameter should be respected for initrd kernel modules"
+    rtt = machine.succeed("cat /sys/module/tcp_hybla/parameters/rtt0")
+    assert int(rtt) == 42, "Parameter should be respected for initrd kernel modules"
 
     # Make sure it sticks in stage 2
     machine.switch_root()
     machine.wait_for_unit("multi-user.target")
-    max_loop = machine.succeed("cat /sys/module/loop/parameters/max_loop")
-    assert int(max_loop) == 42, "Parameter should be respected for initrd kernel modules"
+    rtt = machine.succeed("cat /sys/module/tcp_hybla/parameters/rtt0")
+    assert int(rtt) == 42, "Parameter should be respected for initrd kernel modules"
   '';
 })
diff --git a/nixos/tests/systemd-machinectl.nix b/nixos/tests/systemd-machinectl.nix
index 9d761c6d4d8b8..555a8bb43b30e 100644
--- a/nixos/tests/systemd-machinectl.nix
+++ b/nixos/tests/systemd-machinectl.nix
@@ -76,10 +76,23 @@ in
       };
     };
 
+    systemd.nspawn.${containerName} = {
+      filesConfig = {
+        # workaround to fix kernel namespaces; needed for Nix sandbox
+        # https://github.com/systemd/systemd/issues/27994#issuecomment-1704005670
+        Bind = "/proc:/run/proc";
+      };
+    };
+
     systemd.services."systemd-nspawn@${containerName}" = {
       serviceConfig.Environment = [
         # Disable tmpfs for /tmp
         "SYSTEMD_NSPAWN_TMPFS_TMP=0"
+
+        # force unified cgroup delegation, which would be the default
+        # if systemd could check the capabilities of the installed systemd.
+        # see also: https://github.com/NixOS/nixpkgs/pull/198526
+        "SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=1"
       ];
       overrideStrategy = "asDropin";
     };
@@ -121,6 +134,17 @@ in
     machine.succeed("machinectl start ${containerName}");
     machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
 
+    # Test systemd-nspawn configured unified cgroup delegation
+    # see also:
+    # https://github.com/systemd/systemd/blob/main/docs/CGROUP_DELEGATION.md#three-different-tree-setups-
+    machine.succeed('systemd-run --pty --wait -M ${containerName} /run/current-system/sw/bin/stat --format="%T" --file-system /sys/fs/cgroup > fstype')
+    machine.succeed('test $(tr -d "\\r" < fstype) = cgroup2fs')
+
+    # Test if systemd-nspawn provides a working environment for nix to build derivations
+    # https://nixos.org/guides/nix-pills/07-working-derivation
+    machine.succeed('systemd-run --pty --wait -M ${containerName} /run/current-system/sw/bin/nix-instantiate --expr \'derivation { name = "myname"; builder = "/bin/sh"; args = [ "-c" "echo foo > $out" ]; system = "${pkgs.system}"; }\' --add-root /tmp/drv')
+    machine.succeed('systemd-run --pty --wait -M ${containerName} /run/current-system/sw/bin/nix-store --option substitute false --realize /tmp/drv')
+
     # Test nss_mymachines without nscd
     machine.succeed('LD_LIBRARY_PATH="/run/current-system/sw/lib" getent -s hosts:mymachines hosts ${containerName}');
 
diff --git a/nixos/tests/systemd-networkd-dhcpserver-static-leases.nix b/nixos/tests/systemd-networkd-dhcpserver-static-leases.nix
index f6d5411aa5cad..fda9f35cbe104 100644
--- a/nixos/tests/systemd-networkd-dhcpserver-static-leases.nix
+++ b/nixos/tests/systemd-networkd-dhcpserver-static-leases.nix
@@ -24,14 +24,14 @@ import ./make-test-python.nix ({ lib, ... }: {
           "01-eth1" = {
             name = "eth1";
             networkConfig = {
+              # IPForward prevents dynamic address configuration
+              IPForward = true;
               DHCPServer = true;
               Address = "10.0.0.1/24";
             };
             dhcpServerStaticLeases = [{
-              dhcpServerStaticLeaseConfig = {
-                MACAddress = "02:de:ad:be:ef:01";
-                Address = "10.0.0.10";
-              };
+              MACAddress = "02:de:ad:be:ef:01";
+              Address = "10.0.0.10";
             }];
           };
         };
@@ -41,20 +41,30 @@ import ./make-test-python.nix ({ lib, ... }: {
     client = {
       virtualisation.vlans = [ 1 ];
       systemd.services.systemd-networkd.environment.SYSTEMD_LOG_LEVEL = "debug";
+      systemd.network = {
+        enable = true;
+        links."10-eth1" = {
+          matchConfig.OriginalName = "eth1";
+          linkConfig.MACAddress = "02:de:ad:be:ef:01";
+        };
+        networks."40-eth1" = {
+          matchConfig.Name = "eth1";
+          networkConfig = {
+            DHCP = "ipv4";
+            IPv6AcceptRA = false;
+          };
+          # This setting is important to have the router assign the
+          # configured lease based on the client's MAC address. Also see:
+          # https://github.com/systemd/systemd/issues/21368#issuecomment-982193546
+          dhcpV4Config.ClientIdentifier = "mac";
+          linkConfig.RequiredForOnline = "routable";
+        };
+      };
       networking = {
-        useNetworkd = true;
         useDHCP = false;
         firewall.enable = false;
-        interfaces.eth1 = {
-          useDHCP = true;
-          macAddress = "02:de:ad:be:ef:01";
-        };
+        interfaces.eth1 = lib.mkForce {};
       };
-
-      # This setting is important to have the router assign the
-      # configured lease based on the client's MAC address. Also see:
-      # https://github.com/systemd/systemd/issues/21368#issuecomment-982193546
-      systemd.network.networks."40-eth1".dhcpV4Config.ClientIdentifier = "mac";
     };
   };
   testScript = ''
diff --git a/nixos/tests/systemd-networkd-dhcpserver.nix b/nixos/tests/systemd-networkd-dhcpserver.nix
index 665d8b5a05291..fda0c9d641938 100644
--- a/nixos/tests/systemd-networkd-dhcpserver.nix
+++ b/nixos/tests/systemd-networkd-dhcpserver.nix
@@ -54,7 +54,7 @@ import ./make-test-python.nix ({pkgs, ...}: {
             name = "eth1";
             networkConfig.Bridge = "br0";
             bridgeVLANs = [
-              { bridgeVLANConfig = { PVID = 2; EgressUntagged = 2; }; }
+              { PVID = 2; EgressUntagged = 2; }
             ];
           };
           "02-br0" = {
@@ -69,8 +69,8 @@ import ./make-test-python.nix ({pkgs, ...}: {
               PoolSize = 1;
             };
             bridgeVLANs = [
-              { bridgeVLANConfig = { PVID = 1; EgressUntagged = 1; }; }
-              { bridgeVLANConfig = { VLAN = 2; }; }
+              { PVID = 1; EgressUntagged = 1; }
+              { VLAN = 2; }
             ];
           };
           "02-vlan2" = {
diff --git a/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix b/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
index 1e55341657bdb..6c056d9a10183 100644
--- a/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
+++ b/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
@@ -66,6 +66,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
           settings = {
             interfaces-config.interfaces = [ "eth1" ];
             subnet6 = [ {
+              id = 1;
               interface = "eth1";
               subnet = "2001:DB8::/32";
               pd-pools = [ {
@@ -258,7 +259,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
           "01-lo" = {
             name = "lo";
             addresses = [
-              { addressConfig.Address = "FD42::1/128"; }
+              { Address = "FD42::1/128"; }
             ];
           };
         };
diff --git a/nixos/tests/systemd-networkd-vrf.nix b/nixos/tests/systemd-networkd-vrf.nix
index d4227526a30d4..4f2a45577c169 100644
--- a/nixos/tests/systemd-networkd-vrf.nix
+++ b/nixos/tests/systemd-networkd-vrf.nix
@@ -59,14 +59,14 @@ in {
           matchConfig.Name = "vrf1";
           networkConfig.IPForward = "yes";
           routes = [
-            { routeConfig = { Destination = "192.168.1.2"; Metric = 100; }; }
+            { Destination = "192.168.1.2"; Metric = 100; }
           ];
         };
         networks."10-vrf2" = {
           matchConfig.Name = "vrf2";
           networkConfig.IPForward = "yes";
           routes = [
-            { routeConfig = { Destination = "192.168.2.3"; Metric = 100; }; }
+            { Destination = "192.168.2.3"; Metric = 100; }
           ];
         };
 
diff --git a/nixos/tests/systemd-networkd.nix b/nixos/tests/systemd-networkd.nix
index 6b241b93d5118..44ad713cd6dfc 100644
--- a/nixos/tests/systemd-networkd.nix
+++ b/nixos/tests/systemd-networkd.nix
@@ -23,13 +23,13 @@ let generateNodeConf = { lib, pkgs, config, privk, pubk, peerId, nodeId, ...}: {
               ListenPort = 51820;
               FirewallMark = 42;
             };
-            wireguardPeers = [ {wireguardPeerConfig={
+            wireguardPeers = [ {
               Endpoint = "192.168.1.${peerId}:51820";
               PublicKey = pubk;
               PresharedKeyFile = pkgs.writeText "psk.key" "yTL3sCOL33Wzi6yCnf9uZQl/Z8laSE+zwpqOHC4HhFU=";
               AllowedIPs = [ "10.0.0.${peerId}/32" ];
               PersistentKeepalive = 15;
-            };}];
+            } ];
           };
         };
         networks = {
@@ -41,8 +41,8 @@ let generateNodeConf = { lib, pkgs, config, privk, pubk, peerId, nodeId, ...}: {
             matchConfig = { Name = "wg0"; };
             address = [ "10.0.0.${nodeId}/32" ];
             routes = [
-              { routeConfig = { Gateway = "10.0.0.${nodeId}"; Destination = "10.0.0.0/24"; }; }
-              { routeConfig = { Gateway = "10.0.0.${nodeId}"; Destination = "10.0.0.0/24"; Table = "custom"; }; }
+              { Gateway = "10.0.0.${nodeId}"; Destination = "10.0.0.0/24"; }
+              { Gateway = "10.0.0.${nodeId}"; Destination = "10.0.0.0/24"; Table = "custom"; }
             ];
           };
           "30-eth1" = {
@@ -52,11 +52,11 @@ let generateNodeConf = { lib, pkgs, config, privk, pubk, peerId, nodeId, ...}: {
               "fe80::${nodeId}/64"
             ];
             routingPolicyRules = [
-              { routingPolicyRuleConfig = { Table = 10; IncomingInterface = "eth1"; Family = "both"; };}
-              { routingPolicyRuleConfig = { Table = 20; OutgoingInterface = "eth1"; };}
-              { routingPolicyRuleConfig = { Table = 30; From = "192.168.1.1"; To = "192.168.1.2"; SourcePort = 666 ; DestinationPort = 667; };}
-              { routingPolicyRuleConfig = { Table = 40; IPProtocol = "tcp"; InvertRule = true; };}
-              { routingPolicyRuleConfig = { Table = 50; IncomingInterface = "eth1"; Family = "ipv4"; };}
+              { Table = 10; IncomingInterface = "eth1"; Family = "both"; }
+              { Table = 20; OutgoingInterface = "eth1"; }
+              { Table = 30; From = "192.168.1.1"; To = "192.168.1.2"; SourcePort = 666 ; DestinationPort = 667; }
+              { Table = 40; IPProtocol = "tcp"; InvertRule = true; }
+              { Table = 50; IncomingInterface = "eth1"; Family = "ipv4"; }
             ];
           };
         };
diff --git a/nixos/tests/systemd-resolved.nix b/nixos/tests/systemd-resolved.nix
new file mode 100644
index 0000000000000..3eedc17f4b34f
--- /dev/null
+++ b/nixos/tests/systemd-resolved.nix
@@ -0,0 +1,75 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "systemd-resolved";
+  meta.maintainers = [ lib.maintainers.elvishjerricco ];
+
+  nodes.server = { lib, config, ... }: let
+    exampleZone = pkgs.writeTextDir "example.com.zone" ''
+      @ SOA ns.example.com. noc.example.com. 2019031301 86400 7200 3600000 172800
+      @       A       ${(lib.head config.networking.interfaces.eth1.ipv4.addresses).address}
+      @       AAAA    ${(lib.head config.networking.interfaces.eth1.ipv6.addresses).address}
+    '';
+  in {
+    networking.firewall.enable = false;
+    networking.useDHCP = false;
+
+    networking.interfaces.eth1.ipv6.addresses = lib.mkForce [
+      { address = "fd00::1"; prefixLength = 64; }
+    ];
+
+    services.knot = {
+      enable = true;
+      settings = {
+        server.listen = [
+          "0.0.0.0@53"
+          "::@53"
+        ];
+        template.default.storage = exampleZone;
+        zone."example.com".file = "example.com.zone";
+      };
+    };
+  };
+
+  nodes.client = { nodes, ... }: let
+    inherit (lib.head nodes.server.networking.interfaces.eth1.ipv4.addresses) address;
+  in {
+    networking.nameservers = [ address ];
+    networking.interfaces.eth1.ipv6.addresses = lib.mkForce [
+      { address = "fd00::2"; prefixLength = 64; }
+    ];
+    services.resolved.enable = true;
+    services.resolved.fallbackDns = [ ];
+    networking.useNetworkd = true;
+    networking.useDHCP = false;
+    systemd.network.networks."40-eth0".enable = false;
+
+    testing.initrdBackdoor = true;
+    boot.initrd = {
+      systemd.enable = true;
+      systemd.initrdBin = [ pkgs.iputils ];
+      network.enable = true;
+      services.resolved.enable = true;
+    };
+  };
+
+  testScript = { nodes, ... }: let
+    address4 = (lib.head nodes.server.networking.interfaces.eth1.ipv4.addresses).address;
+    address6 = (lib.head nodes.server.networking.interfaces.eth1.ipv6.addresses).address;
+  in ''
+    start_all()
+    server.wait_for_unit("multi-user.target")
+
+    def test_client():
+        query = client.succeed("resolvectl query example.com")
+        assert "${address4}" in query
+        assert "${address6}" in query
+        client.succeed("ping -4 -c 1 example.com")
+        client.succeed("ping -6 -c 1 example.com")
+
+    client.wait_for_unit("initrd.target")
+    test_client()
+    client.switch_root()
+
+    client.wait_for_unit("multi-user.target")
+    test_client()
+  '';
+})
diff --git a/nixos/tests/tandoor-recipes-script-name.nix b/nixos/tests/tandoor-recipes-script-name.nix
new file mode 100644
index 0000000000000..9010ffa3ccfa1
--- /dev/null
+++ b/nixos/tests/tandoor-recipes-script-name.nix
@@ -0,0 +1,95 @@
+import ./make-test-python.nix (
+  { pkgs, lib, ... }:
+  {
+    name = "tandoor-recipes-script-name";
+
+    nodes.machine =
+      { pkgs, nodes, ... }:
+      {
+        services.tandoor-recipes = {
+          enable = true;
+          extraConfig = {
+            SCRIPT_NAME = "/any/path";
+            STATIC_URL = "${nodes.machine.services.tandoor-recipes.extraConfig.SCRIPT_NAME}/static/";
+          };
+        };
+      };
+
+    testScript =
+      { nodes, ... }:
+      let
+        inherit (nodes.machine.services.tandoor-recipes) address port;
+        inherit (nodes.machine.services.tandoor-recipes.extraConfig) SCRIPT_NAME;
+      in
+      ''
+        from html.parser import HTMLParser
+
+        origin_url = "http://${address}:${toString port}"
+        base_url = f"{origin_url}${SCRIPT_NAME}"
+        login_path = "/admin/login/"
+        login_url = f"{base_url}{login_path}"
+
+        cookie_jar_path = "/tmp/cookies.txt"
+        curl = f"curl --cookie {cookie_jar_path} --cookie-jar {cookie_jar_path} --fail --header 'Origin: {origin_url}' --show-error --silent"
+
+        print("Wait for the service to respond")
+        machine.wait_for_unit("tandoor-recipes.service")
+        machine.wait_until_succeeds(f"{curl} {login_url}")
+
+        username = "username"
+        password = "password"
+
+        print("Create admin user")
+        machine.succeed(
+            f"DJANGO_SUPERUSER_PASSWORD='{password}' /var/lib/tandoor-recipes/tandoor-recipes-manage createsuperuser --no-input --username='{username}' --email=nobody@example.com"
+        )
+
+        print("Get CSRF token for later requests")
+        csrf_token = machine.succeed(f"grep csrftoken {cookie_jar_path} | cut --fields=7").rstrip()
+
+        print("Log in as admin user")
+        machine.succeed(
+            f"{curl} --data 'csrfmiddlewaretoken={csrf_token}' --data 'username={username}' --data 'password={password}' {login_url}"
+        )
+
+        print("Get the contents of the logged in main page")
+        logged_in_page = machine.succeed(f"{curl} --location {base_url}")
+
+        class UrlParser(HTMLParser):
+            def __init__(self):
+                super().__init__()
+
+                self.urls: list[str] = []
+
+            def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
+                if tag == "form":
+                    for name, value in attrs:
+                        if name == "action" and value is not None:
+                            assert not value.endswith(login_path)
+                            break
+
+                if tag != "a":
+                    return
+
+                for name, value in attrs:
+                    if name == "href" and value is not None:
+                        if value.startswith(base_url):
+                            self.urls.append(value)
+                        elif value.startswith("/"):
+                            self.urls.append(f"{origin_url}{value}")
+                        else:
+                            print("Ignoring external URL: {value}")
+
+                        break
+
+        parser = UrlParser()
+        parser.feed(logged_in_page)
+
+        for url in parser.urls:
+            with subtest(f"Verify that {url} can be reached"):
+                machine.succeed(f"{curl} {url}")
+      '';
+
+    meta.maintainers = with lib.maintainers; [ l0b0 ];
+  }
+)
diff --git a/nixos/tests/tayga.nix b/nixos/tests/tayga.nix
index 4aade67d74d0d..e3c57b7d58feb 100644
--- a/nixos/tests/tayga.nix
+++ b/nixos/tests/tayga.nix
@@ -55,10 +55,11 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
             "100.64.0.2/24"
           ];
           routes = [
-            { routeConfig = { Destination = "192.0.2.0/24"; Gateway = "100.64.0.1"; }; }
+            { Destination = "192.0.2.0/24"; Gateway = "100.64.0.1"; }
           ];
         };
       };
+      programs.mtr.enable = true;
     };
 
     # The router is configured with static IPv4 addresses towards the server
@@ -120,6 +121,9 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
             prefixLength = 96;
           };
         };
+        mappings = {
+          "192.0.2.42" = "2001:db8::2";
+        };
       };
     };
 
@@ -171,6 +175,9 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
             prefixLength = 96;
           };
         };
+        mappings = {
+          "192.0.2.42" = "2001:db8::2";
+        };
       };
     };
 
@@ -195,11 +202,11 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
             "2001:db8::2/64"
           ];
           routes = [
-            { routeConfig = { Destination = "64:ff9b::/96"; Gateway = "2001:db8::1"; }; }
+            { Destination = "64:ff9b::/96"; Gateway = "2001:db8::1"; }
           ];
         };
       };
-      environment.systemPackages = [ pkgs.mtr ];
+      programs.mtr.enable = true;
     };
   };
 
@@ -225,10 +232,16 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
       with subtest("Wait for tayga"):
         router.wait_for_unit("tayga.service")
 
-      with subtest("Test ICMP"):
+      with subtest("Test ICMP server -> client"):
+        server.wait_until_succeeds("ping -c 3 192.0.2.42 >&2")
+
+      with subtest("Test ICMP and show a traceroute server -> client"):
+        server.wait_until_succeeds("mtr --show-ips --report-wide 192.0.2.42 >&2")
+
+      with subtest("Test ICMP client -> server"):
         client.wait_until_succeeds("ping -c 3 64:ff9b::100.64.0.2 >&2")
 
-      with subtest("Test ICMP and show a traceroute"):
+      with subtest("Test ICMP and show a traceroute client -> server"):
         client.wait_until_succeeds("mtr --show-ips --report-wide 64:ff9b::100.64.0.2 >&2")
 
       router.log(router.execute("systemd-analyze security tayga.service")[1])
diff --git a/nixos/tests/teleport.nix b/nixos/tests/teleport.nix
index 2fb347155759a..3621cce0599e1 100644
--- a/nixos/tests/teleport.nix
+++ b/nixos/tests/teleport.nix
@@ -9,7 +9,6 @@ with import ../lib/testing-python.nix { inherit system pkgs; };
 let
   packages = with pkgs; {
     "default" = teleport;
-    "13" = teleport_13;
     "14" = teleport_14;
   };
 
diff --git a/nixos/tests/prometheus.nix b/nixos/tests/thanos.nix
index 0111273893775..5bdfab7b3573f 100644
--- a/nixos/tests/prometheus.nix
+++ b/nixos/tests/thanos.nix
@@ -212,8 +212,6 @@ in import ./make-test-python.nix {
   };
 
   testScript = { nodes, ... } : ''
-    import json
-
     # Before starting the other machines we first make sure that our S3 service is online
     # and has a bucket added for thanos:
     s3.start()
@@ -289,61 +287,5 @@ in import ./make-test-python.nix {
         + "jq .thanos.labels.some_label | "
         + "grep 'required by thanos'"
     )
-
-    # Check if switching to a NixOS configuration that changes the prometheus
-    # configuration reloads (instead of restarts) prometheus before the switch
-    # finishes successfully:
-    with subtest("config change reloads prometheus"):
-        # We check if prometheus has finished reloading by looking for the message
-        # "Completed loading of configuration file" in the journal between the start
-        # and finish of switching to the new NixOS configuration.
-        #
-        # To mark the start we record the journal cursor before starting the switch:
-        cursor_before_switching = json.loads(
-            prometheus.succeed("journalctl -n1 -o json --output-fields=__CURSOR")
-        )["__CURSOR"]
-
-        # Now we switch:
-        prometheus_config_change = prometheus.succeed(
-            "readlink /run/current-system/specialisation/prometheus-config-change"
-        ).strip()
-        prometheus.succeed(prometheus_config_change + "/bin/switch-to-configuration test")
-
-        # Next we retrieve all logs since the start of switching:
-        logs_after_starting_switching = prometheus.succeed(
-            """
-              journalctl --after-cursor='{cursor_before_switching}' -o json --output-fields=MESSAGE
-            """.format(
-                cursor_before_switching=cursor_before_switching
-            )
-        )
-
-        # Finally we check if the message "Completed loading of configuration file"
-        # occurs before the "finished switching to system configuration" message:
-        finished_switching_msg = (
-            "finished switching to system configuration " + prometheus_config_change
-        )
-        reloaded_before_switching_finished = False
-        finished_switching = False
-        for log_line in logs_after_starting_switching.split("\n"):
-            msg = json.loads(log_line)["MESSAGE"]
-            if "Completed loading of configuration file" in msg:
-                reloaded_before_switching_finished = True
-            if msg == finished_switching_msg:
-                finished_switching = True
-                break
-
-        assert reloaded_before_switching_finished
-        assert finished_switching
-
-        # Check if the reloaded config includes the new s3-node_exporter job:
-        prometheus.succeed(
-          """
-            curl -sf http://127.0.0.1:${toString queryPort}/api/v1/status/config \
-              | jq -r .data.yaml \
-              | yq '.scrape_configs | any(.job_name == "s3-node_exporter")' \
-              | grep true
-          """
-        )
   '';
 }
diff --git a/nixos/tests/tigervnc.nix b/nixos/tests/tigervnc.nix
index ed575682d9338..79c4f19178d5e 100644
--- a/nixos/tests/tigervnc.nix
+++ b/nixos/tests/tigervnc.nix
@@ -7,7 +7,7 @@ with import ../lib/testing-python.nix { inherit system pkgs; };
 makeTest {
   name = "tigervnc";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ lheckemann ];
+    maintainers = [ ];
   };
 
   nodes = {
@@ -38,16 +38,18 @@ makeTest {
     server.succeed("Xvnc -geometry 720x576 :1 -PasswordFile vncpasswd >&2 &")
     server.wait_until_succeeds("nc -z localhost 5901", timeout=10)
     server.succeed("DISPLAY=:1 xwininfo -root | grep 720x576")
-    server.execute("DISPLAY=:1 display -size 360x200 -font sans -gravity south label:'HELLO VNC WORLD' >&2 &")
+    server.execute("DISPLAY=:1 display -size 360x200 -font sans -gravity south label:'HELLO VNC' >&2 &")
 
     client.wait_for_x()
     client.execute("vncviewer server:1 -PasswordFile vncpasswd >&2 &")
     client.wait_for_window(r"VNC")
     client.screenshot("screenshot")
     text = client.get_screen_text()
+
     # Displayed text
-    assert 'HELLO VNC WORLD' in text
+    assert 'HELLO VNC' in text
     # Client window title
-    assert 'TigerVNC' in text
+    # get_screen_text can't get correct string from screenshot
+    # assert 'TigerVNC' in text
   '';
 }
diff --git a/nixos/tests/timezone.nix b/nixos/tests/timezone.nix
index 7fc9a5058eee9..5d0318e33daab 100644
--- a/nixos/tests/timezone.nix
+++ b/nixos/tests/timezone.nix
@@ -1,6 +1,6 @@
 import ./make-test-python.nix ({ pkgs, ...} : {
   name = "timezone";
-  meta.maintainers = with pkgs.lib.maintainers; [ lheckemann ];
+  meta.maintainers = with pkgs.lib.maintainers; [ ];
 
   nodes = {
     node_eutz = { pkgs, ... }: {
diff --git a/nixos/tests/tinywl.nix b/nixos/tests/tinywl.nix
index 9199866b57af7..2dc354812a75e 100644
--- a/nixos/tests/tinywl.nix
+++ b/nixos/tests/tinywl.nix
@@ -16,7 +16,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
         systemPackages = with pkgs; [ tinywl foot wayland-utils ];
       };
 
-      hardware.opengl.enable = true;
+      hardware.graphics.enable = true;
 
       # Automatically start TinyWL when logging in on tty1:
       programs.bash.loginShellInit = ''
diff --git a/nixos/tests/turbovnc-headless-server.nix b/nixos/tests/turbovnc-headless-server.nix
index a155f9f907b25..e4eff4f078447 100644
--- a/nixos/tests/turbovnc-headless-server.nix
+++ b/nixos/tests/turbovnc-headless-server.nix
@@ -24,11 +24,14 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     };
 
     # So that we can ssh into the VM, see e.g.
-    # http://blog.patapon.info/nixos-local-vm/#accessing-the-vm-with-ssh
+    # https://nixos.org/manual/nixos/stable/#sec-nixos-test-port-forwarding
     services.openssh.enable = true;
-    services.openssh.settings.PermitRootLogin = "yes";
-    users.extraUsers.root.password = "";
     users.mutableUsers = false;
+    # `test-instrumentation.nix` already sets an empty root password.
+    # The following have to all be set to allow an empty SSH login password.
+    services.openssh.settings.PermitRootLogin = "yes";
+    services.openssh.settings.PermitEmptyPasswords = "yes";
+    security.pam.services.sshd.allowNullPassword = true; # the default `UsePam yes` makes this necessary
   };
 
   testScript = ''
@@ -124,7 +127,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
         machine.wait_until_succeeds("test -f /tmp/glxgears-should-fail.stderr")
         wait_until_terminated_or_succeeds(
             termination_check_shell_command="pidof glxgears",
-            success_check_shell_command="grep 'libGL error: failed to load driver: swrast' /tmp/glxgears-should-fail.stderr",
+            success_check_shell_command="grep 'MESA-LOADER: failed to open swrast' /tmp/glxgears-should-fail.stderr",
             get_detail_message_fn=lambda: "Contents of /tmp/glxgears-should-fail.stderr:\n"
             + machine.succeed("cat /tmp/glxgears-should-fail.stderr"),
         )
diff --git a/nixos/tests/udisks2.nix b/nixos/tests/udisks2.nix
index 8cc148750c7bc..b934f0b951562 100644
--- a/nixos/tests/udisks2.nix
+++ b/nixos/tests/udisks2.nix
@@ -2,6 +2,7 @@ import ./make-test-python.nix ({ pkgs, ... }:
 
 let
 
+  # FIXME: 404s
   stick = pkgs.fetchurl {
     url = "https://nixos.org/~eelco/nix/udisks-test.img.xz";
     sha256 = "0was1xgjkjad91nipzclaz5biv3m4b2nk029ga6nk7iklwi19l8b";
@@ -12,7 +13,7 @@ in
 {
   name = "udisks2";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ eelco ];
+    maintainers = [ ];
   };
 
   nodes.machine =
diff --git a/nixos/tests/vaultwarden.nix b/nixos/tests/vaultwarden.nix
index 28ff170e36107..baefa67dbf535 100644
--- a/nixos/tests/vaultwarden.nix
+++ b/nixos/tests/vaultwarden.nix
@@ -1,38 +1,94 @@
-{ system ? builtins.currentSystem
-, config ? { }
-, pkgs ? import ../.. { inherit system config; }
-}:
-
 # These tests will:
 #  * Set up a vaultwarden server
-#  * Have Firefox use the web vault to create an account, log in, and save a password to the valut
+#  * Have Firefox use the web vault to create an account, log in, and save a password to the vault
 #  * Have the bw cli log in and read that password from the vault
 #
 # Note that Firefox must be on the same machine as the server for WebCrypto APIs to be available (or HTTPS must be configured)
 #
 # The same tests should work without modification on the official bitwarden server, if we ever package that.
 
-with import ../lib/testing-python.nix { inherit system pkgs; };
-with pkgs.lib;
 let
-  backends = [ "sqlite" "mysql" "postgresql" ];
-
-  dbPassword = "please_dont_hack";
-
-  userEmail = "meow@example.com";
-  userPassword = "also_super_secret_ZJWpBKZi668QGt"; # Must be complex to avoid interstitial warning on the signup page
-
-  storedPassword = "seeeecret";
+  makeVaultwardenTest = name: {
+    backend ? name,
+    withClient ? true,
+    testScript ? null,
+  }: import ./make-test-python.nix ({ lib, pkgs, ...}: let
+    dbPassword = "please_dont_hack";
+    userEmail = "meow@example.com";
+    userPassword = "also_super_secret_ZJWpBKZi668QGt"; # Must be complex to avoid interstitial warning on the signup page
+    storedPassword = "seeeecret";
+
+    testRunner = pkgs.writers.writePython3Bin "test-runner" {
+      libraries = [ pkgs.python3Packages.selenium ];
+      flakeIgnore = [  "E501" ];
+    } ''
+
+      from selenium.webdriver.common.by import By
+      from selenium.webdriver import Firefox
+      from selenium.webdriver.firefox.options import Options
+      from selenium.webdriver.support.ui import WebDriverWait
+      from selenium.webdriver.support import expected_conditions as EC
+
+      options = Options()
+      options.add_argument('--headless')
+      driver = Firefox(options=options)
+
+      driver.implicitly_wait(20)
+      driver.get('http://localhost:8080/#/register')
+
+      wait = WebDriverWait(driver, 10)
+
+      wait.until(EC.title_contains("Vaultwarden Web"))
+
+      driver.find_element(By.CSS_SELECTOR, 'input#register-form_input_email').send_keys(
+          '${userEmail}'
+      )
+      driver.find_element(By.CSS_SELECTOR, 'input#register-form_input_name').send_keys(
+          'A Cat'
+      )
+      driver.find_element(By.CSS_SELECTOR, 'input#register-form_input_master-password').send_keys(
+          '${userPassword}'
+      )
+      driver.find_element(By.CSS_SELECTOR, 'input#register-form_input_confirm-master-password').send_keys(
+          '${userPassword}'
+      )
+      if driver.find_element(By.CSS_SELECTOR, 'input#checkForBreaches').is_selected():
+          driver.find_element(By.CSS_SELECTOR, 'input#checkForBreaches').click()
+
+      driver.find_element(By.XPATH, "//button[contains(., 'Create account')]").click()
+
+      wait.until_not(EC.title_contains("Create account"))
+
+      driver.find_element(By.XPATH, "//button[contains(., 'Continue')]").click()
+
+      driver.find_element(By.CSS_SELECTOR, 'input#login_input_master-password').send_keys(
+          '${userPassword}'
+      )
+      driver.find_element(By.XPATH, "//button[contains(., 'Log in')]").click()
+
+      wait.until(EC.title_contains("Vaults"))
+
+      driver.find_element(By.XPATH, "//button[contains(., 'New item')]").click()
+
+      driver.find_element(By.CSS_SELECTOR, 'input#name').send_keys(
+          'secrets'
+      )
+      driver.find_element(By.CSS_SELECTOR, 'input#loginPassword').send_keys(
+          '${storedPassword}'
+      )
+
+      driver.find_element(By.XPATH, "//button[contains(., 'Save')]").click()
+    '';
+  in {
+    inherit name;
 
-  makeVaultwardenTest = backend: makeTest {
-    name = "vaultwarden-${backend}";
     meta = {
-      maintainers = with pkgs.lib.maintainers; [ jjjollyjim ];
+      maintainers = with pkgs.lib.maintainers; [ dotlambda SuperSandro2000 ];
     };
 
     nodes = {
-      server = { pkgs, ... }:
-        let backendConfig = {
+      server = { pkgs, ... }: lib.mkMerge [
+        {
           mysql = {
             services.mysql = {
               enable = true;
@@ -53,119 +109,53 @@ let
           postgresql = {
             services.postgresql = {
               enable = true;
-              initialScript = pkgs.writeText "postgresql-init.sql" ''
-                CREATE USER bitwardenuser WITH PASSWORD '${dbPassword}';
-                CREATE DATABASE bitwarden WITH OWNER bitwardenuser;
-              '';
+              ensureDatabases = [ "vaultwarden" ];
+              ensureUsers = [{
+                name = "vaultwarden";
+                ensureDBOwnership = true;
+              }];
             };
 
-            services.vaultwarden.config.databaseUrl = "postgresql://bitwardenuser:${dbPassword}@localhost/bitwarden";
+            services.vaultwarden.config.databaseUrl = "postgresql:///vaultwarden?host=/run/postgresql";
 
             systemd.services.vaultwarden.after = [ "postgresql.service" ];
           };
 
-          sqlite = { };
-        };
-        in
-        mkMerge [
-          backendConfig.${backend}
-          {
-            services.vaultwarden = {
-              enable = true;
-              dbBackend = backend;
-              config = {
-                rocketAddress = "0.0.0.0";
-                rocketPort = 80;
-              };
-            };
+          sqlite = {
+            services.vaultwarden.backupDir = "/var/lib/vaultwarden/backups";
+
+            environment.systemPackages = [ pkgs.sqlite ];
+          };
+        }.${backend}
 
-            networking.firewall.allowedTCPPorts = [ 80 ];
-
-            environment.systemPackages =
-              let
-                testRunner = pkgs.writers.writePython3Bin "test-runner"
-                  {
-                    libraries = [ pkgs.python3Packages.selenium ];
-                    flakeIgnore = [
-                      "E501"
-                    ];
-                  } ''
-
-                  from selenium.webdriver.common.by import By
-                  from selenium.webdriver import Firefox
-                  from selenium.webdriver.firefox.options import Options
-                  from selenium.webdriver.support.ui import WebDriverWait
-                  from selenium.webdriver.support import expected_conditions as EC
-
-                  options = Options()
-                  options.add_argument('--headless')
-                  driver = Firefox(options=options)
-
-                  driver.implicitly_wait(20)
-                  driver.get('http://localhost/#/register')
-
-                  wait = WebDriverWait(driver, 10)
-
-                  wait.until(EC.title_contains("Vaultwarden Web"))
-
-                  driver.find_element(By.CSS_SELECTOR, 'input#register-form_input_email').send_keys(
-                      '${userEmail}'
-                  )
-                  driver.find_element(By.CSS_SELECTOR, 'input#register-form_input_name').send_keys(
-                      'A Cat'
-                  )
-                  driver.find_element(By.CSS_SELECTOR, 'input#register-form_input_master-password').send_keys(
-                      '${userPassword}'
-                  )
-                  driver.find_element(By.CSS_SELECTOR, 'input#register-form_input_confirm-master-password').send_keys(
-                      '${userPassword}'
-                  )
-                  if driver.find_element(By.CSS_SELECTOR, 'input#checkForBreaches').is_selected():
-                      driver.find_element(By.CSS_SELECTOR, 'input#checkForBreaches').click()
-
-                  driver.find_element(By.XPATH, "//button[contains(., 'Create account')]").click()
-
-                  wait.until_not(EC.title_contains("Create account"))
-
-                  driver.find_element(By.XPATH, "//button[contains(., 'Continue')]").click()
-
-                  driver.find_element(By.CSS_SELECTOR, 'input#login_input_master-password').send_keys(
-                      '${userPassword}'
-                  )
-                  driver.find_element(By.XPATH, "//button[contains(., 'Log in')]").click()
-
-                  wait.until(EC.title_contains("Vaults"))
-
-                  driver.find_element(By.XPATH, "//button[contains(., 'New item')]").click()
-
-                  driver.find_element(By.CSS_SELECTOR, 'input#name').send_keys(
-                      'secrets'
-                  )
-                  driver.find_element(By.CSS_SELECTOR, 'input#loginPassword').send_keys(
-                      '${storedPassword}'
-                  )
-
-                  driver.find_element(By.XPATH, "//button[contains(., 'Save')]").click()
-                '';
-              in
-              [ pkgs.firefox-unwrapped pkgs.geckodriver testRunner ];
-
-          }
-        ];
-
-      client = { pkgs, ... }:
         {
-          environment.systemPackages = [ pkgs.bitwarden-cli ];
-        };
+          services.vaultwarden = {
+            enable = true;
+            dbBackend = backend;
+            config = {
+              rocketAddress = "0.0.0.0";
+              rocketPort = 8080;
+            };
+          };
+
+          networking.firewall.allowedTCPPorts = [ 8080 ];
+
+          environment.systemPackages = [ pkgs.firefox-unwrapped pkgs.geckodriver testRunner ];
+        }
+      ];
+    } // lib.optionalAttrs withClient {
+      client = { pkgs, ... }: {
+        environment.systemPackages = [ pkgs.bitwarden-cli ];
+      };
     };
 
-    testScript = ''
+    testScript = if testScript != null then testScript else ''
       start_all()
       server.wait_for_unit("vaultwarden.service")
-      server.wait_for_open_port(80)
+      server.wait_for_open_port(8080)
 
       with subtest("configure the cli"):
-          client.succeed("bw --nointeraction config server http://server")
+          client.succeed("bw --nointeraction config server http://server:8080")
 
       with subtest("can't login to nonexistent account"):
           client.fail(
@@ -184,15 +174,40 @@ let
           client.succeed(f"bw --nointeraction --raw --session {key} sync -f")
 
       with subtest("get the password with the cli"):
-          password = client.succeed(
-              f"bw --nointeraction --raw --session {key} list items | ${pkgs.jq}/bin/jq -r .[].login.password"
+          password = client.wait_until_succeeds(
+              f"bw --nointeraction --raw --session {key} list items | ${pkgs.jq}/bin/jq -r .[].login.password",
+              timeout=60
           )
           assert password.strip() == "${storedPassword}"
+
+      with subtest("Check systemd unit hardening"):
+          server.log(server.succeed("systemd-analyze security vaultwarden.service | grep -v ✓"))
     '';
-  };
+  });
 in
-builtins.listToAttrs (
-  map
-    (backend: { name = backend; value = makeVaultwardenTest backend; })
-    backends
-)
+builtins.mapAttrs (k: v: makeVaultwardenTest k v) {
+  mysql = {};
+  postgresql = {};
+  sqlite = {};
+  sqlite-backup = {
+    backend = "sqlite";
+    withClient = false;
+
+    testScript = ''
+      start_all()
+      server.wait_for_unit("vaultwarden.service")
+      server.wait_for_open_port(8080)
+
+      with subtest("Set up vaultwarden"):
+          server.succeed("PYTHONUNBUFFERED=1 test-runner | systemd-cat -t test-runner")
+
+      with subtest("Run the backup script"):
+          server.start_job("backup-vaultwarden.service")
+
+      with subtest("Check that backup exists"):
+          server.succeed('[ -d "/var/lib/vaultwarden/backups" ]')
+          server.succeed('[ -f "/var/lib/vaultwarden/backups/db.sqlite3" ]')
+          server.succeed('[ -d "/var/lib/vaultwarden/backups/attachments" ]')
+    '';
+  };
+}
diff --git a/nixos/tests/vector.nix b/nixos/tests/vector.nix
deleted file mode 100644
index a55eb4e012c5b..0000000000000
--- a/nixos/tests/vector.nix
+++ /dev/null
@@ -1,37 +0,0 @@
-{ system ? builtins.currentSystem, config ? { }
-, pkgs ? import ../.. { inherit system config; } }:
-
-with import ../lib/testing-python.nix { inherit system pkgs; };
-with pkgs.lib;
-
-{
-  test1 = makeTest {
-    name = "vector-test1";
-    meta.maintainers = [ pkgs.lib.maintainers.happysalada ];
-
-    nodes.machine = { config, pkgs, ... }: {
-      services.vector = {
-        enable = true;
-        journaldAccess = true;
-        settings = {
-          sources.journald.type = "journald";
-
-          sinks = {
-            file = {
-              type = "file";
-              inputs = [ "journald" ];
-              path = "/var/lib/vector/logs.log";
-              encoding = { codec = "json"; };
-            };
-          };
-        };
-      };
-    };
-
-    # ensure vector is forwarding the messages appropriately
-    testScript = ''
-      machine.wait_for_unit("vector.service")
-      machine.wait_for_file("/var/lib/vector/logs.log")
-    '';
-  };
-}
diff --git a/nixos/tests/vector/api.nix b/nixos/tests/vector/api.nix
new file mode 100644
index 0000000000000..8aa3a0c1b771f
--- /dev/null
+++ b/nixos/tests/vector/api.nix
@@ -0,0 +1,39 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "vector-api";
+  meta.maintainers = [ pkgs.lib.maintainers.happysalada ];
+
+  nodes.machineapi = { config, pkgs, ... }: {
+    services.vector = {
+      enable = true;
+      journaldAccess = false;
+      settings = {
+        api.enabled = true;
+
+        sources = {
+          demo_logs = {
+            type = "demo_logs";
+            format = "json";
+          };
+        };
+
+        sinks = {
+          file = {
+            type = "file";
+            inputs = [ "demo_logs" ];
+            path = "/var/lib/vector/logs.log";
+            encoding = { codec = "json"; };
+          };
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    machineapi.wait_for_unit("vector")
+    machineapi.wait_for_open_port(8686)
+    machineapi.succeed("journalctl -o cat -u vector.service | grep 'API server running'")
+    machineapi.wait_until_succeeds("curl -sSf http://localhost:8686/health")
+  '';
+})
diff --git a/nixos/tests/vector/default.nix b/nixos/tests/vector/default.nix
new file mode 100644
index 0000000000000..dc3747da74216
--- /dev/null
+++ b/nixos/tests/vector/default.nix
@@ -0,0 +1,12 @@
+{ system ? builtins.currentSystem
+, config ? { }
+, pkgs ? import ../../.. { inherit system config; }
+}:
+
+{
+  file-sink = import ./file-sink.nix { inherit system pkgs; };
+  api = import ./api.nix { inherit system pkgs; };
+  dnstap = import ./dnstap.nix { inherit system pkgs; };
+  nginx-clickhouse = import ./nginx-clickhouse.nix { inherit system pkgs; };
+  syslog-quickwit = import ./syslog-quickwit.nix { inherit system pkgs; };
+}
diff --git a/nixos/tests/vector/dnstap.nix b/nixos/tests/vector/dnstap.nix
new file mode 100644
index 0000000000000..15d643311b604
--- /dev/null
+++ b/nixos/tests/vector/dnstap.nix
@@ -0,0 +1,118 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+let
+  dnstapSocket = "/var/run/vector/dnstap.sock";
+in
+{
+  name = "vector-dnstap";
+  meta.maintainers = [ pkgs.lib.maintainers.happysalada ];
+
+  nodes = {
+    unbound = { config, pkgs, ... }: {
+      networking.firewall.allowedUDPPorts = [ 53 ];
+
+      services.vector = {
+        enable = true;
+
+        settings = {
+          sources = {
+            dnstap = {
+              type = "dnstap";
+              multithreaded = true;
+              mode = "unix";
+              lowercase_hostnames = true;
+              socket_file_mode = 504;
+              socket_path = "${dnstapSocket}";
+            };
+          };
+
+          sinks = {
+            file = {
+              type = "file";
+              inputs = [ "dnstap" ];
+              path = "/var/lib/vector/logs.log";
+              encoding = { codec = "json"; };
+            };
+          };
+        };
+      };
+
+      systemd.services.vector.serviceConfig = {
+        RuntimeDirectory = "vector";
+        RuntimeDirectoryMode = "0770";
+      };
+
+      services.unbound = {
+        enable = true;
+        enableRootTrustAnchor = false;
+        package = pkgs.unbound-full;
+        settings = {
+          server = {
+            interface = [ "0.0.0.0" "::" ];
+            access-control = [ "192.168.1.0/24 allow" ];
+
+            domain-insecure = "local";
+            private-domain = "local";
+
+            local-zone = "local. static";
+            local-data = [
+              ''"test.local. 10800 IN A 192.168.123.5"''
+            ];
+          };
+
+          dnstap = {
+            dnstap-enable = "yes";
+            dnstap-socket-path = "${dnstapSocket}";
+            dnstap-send-identity = "yes";
+            dnstap-send-version = "yes";
+            dnstap-log-client-query-messages = "yes";
+            dnstap-log-client-response-messages = "yes";
+          };
+        };
+      };
+
+      systemd.services.unbound = {
+        after = [ "vector.service" ];
+        wants = [ "vector.service" ];
+        serviceConfig = {
+          # DNSTAP access
+          ReadWritePaths = [ "/var/run/vector" ];
+          SupplementaryGroups = [ "vector" ];
+        };
+      };
+    };
+
+    dnsclient = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.dig ];
+    };
+  };
+
+  testScript = ''
+    unbound.wait_for_unit("unbound")
+    unbound.wait_for_unit("vector")
+
+    unbound.wait_until_succeeds(
+      "journalctl -o cat -u vector.service | grep 'Socket permissions updated to 0o770'"
+    )
+    unbound.wait_until_succeeds(
+      "journalctl -o cat -u vector.service | grep 'component_type=dnstap' | grep 'Listening... path=\"${dnstapSocket}\"'"
+    )
+
+    unbound.wait_for_file("${dnstapSocket}")
+    unbound.succeed("test 770 -eq $(stat -c '%a' ${dnstapSocket})")
+
+    dnsclient.wait_for_unit("network-online.target")
+    dnsclient.succeed(
+      "dig @unbound test.local"
+    )
+
+    unbound.wait_for_file("/var/lib/vector/logs.log")
+
+    unbound.wait_until_succeeds(
+      "grep ClientQuery /var/lib/vector/logs.log | grep '\"domainName\":\"test.local.\"' | grep '\"rcodeName\":\"NoError\"'"
+    )
+    unbound.wait_until_succeeds(
+      "grep ClientResponse /var/lib/vector/logs.log | grep '\"domainName\":\"test.local.\"' | grep '\"rData\":\"192.168.123.5\"'"
+    )
+  '';
+})
diff --git a/nixos/tests/vector/file-sink.nix b/nixos/tests/vector/file-sink.nix
new file mode 100644
index 0000000000000..2220d20ac55c3
--- /dev/null
+++ b/nixos/tests/vector/file-sink.nix
@@ -0,0 +1,49 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "vector-test1";
+  meta.maintainers = [ pkgs.lib.maintainers.happysalada ];
+
+  nodes.machine = { config, pkgs, ... }: {
+    services.vector = {
+      enable = true;
+      journaldAccess = true;
+      settings = {
+        sources = {
+          journald.type = "journald";
+
+          vector_metrics.type = "internal_metrics";
+
+          vector_logs.type = "internal_logs";
+        };
+
+        sinks = {
+          file = {
+            type = "file";
+            inputs = [ "journald" "vector_logs" ];
+            path = "/var/lib/vector/logs.log";
+            encoding = { codec = "json"; };
+          };
+
+          prometheus_exporter = {
+            type = "prometheus_exporter";
+            inputs = [ "vector_metrics" ];
+            address = "[::]:9598";
+          };
+        };
+      };
+    };
+  };
+
+  # ensure vector is forwarding the messages appropriately
+  testScript = ''
+    machine.wait_for_unit("vector.service")
+    machine.wait_for_open_port(9598)
+    machine.wait_until_succeeds("journalctl -o cat -u vector.service | grep 'version=\"${pkgs.vector.version}\"'")
+    machine.wait_until_succeeds("journalctl -o cat -u vector.service | grep 'API is disabled'")
+    machine.wait_until_succeeds("curl -sSf http://localhost:9598/metrics | grep vector_build_info")
+    machine.wait_until_succeeds("curl -sSf http://localhost:9598/metrics | grep vector_component_received_bytes_total | grep journald")
+    machine.wait_until_succeeds("curl -sSf http://localhost:9598/metrics | grep vector_utilization | grep prometheus_exporter")
+    machine.wait_for_file("/var/lib/vector/logs.log")
+  '';
+})
diff --git a/nixos/tests/vector/nginx-clickhouse.nix b/nixos/tests/vector/nginx-clickhouse.nix
new file mode 100644
index 0000000000000..3d99bac6ac161
--- /dev/null
+++ b/nixos/tests/vector/nginx-clickhouse.nix
@@ -0,0 +1,168 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "vector-nginx-clickhouse";
+  meta.maintainers = [ pkgs.lib.maintainers.happysalada ];
+
+  nodes = {
+    clickhouse = { config, pkgs, ... }: {
+      virtualisation.memorySize = 4096;
+
+      # Clickhouse module can't listen on a non-loopback IP.
+      networking.firewall.allowedTCPPorts = [ 6000 ];
+      services.clickhouse.enable = true;
+
+      # Exercise Vector sink->source for now.
+      services.vector = {
+        enable = true;
+
+        settings = {
+          sources = {
+            vector_source = {
+              type = "vector";
+              address = "[::]:6000";
+            };
+          };
+
+          sinks = {
+            clickhouse = {
+              type = "clickhouse";
+              inputs = [ "vector_source" ];
+              endpoint = "http://localhost:8123";
+              database = "nginxdb";
+              table = "access_logs";
+              skip_unknown_fields = true;
+            };
+          };
+        };
+      };
+    };
+
+    nginx = { config, pkgs, ... }: {
+      services.nginx = {
+        enable = true;
+        virtualHosts.localhost = {};
+      };
+
+      services.vector = {
+        enable = true;
+
+        settings = {
+          sources = {
+            nginx_logs = {
+              type = "file";
+              include = [ "/var/log/nginx/access.log" ];
+              read_from = "end";
+            };
+          };
+
+          sinks = {
+            vector_sink = {
+              type = "vector";
+              inputs = [ "nginx_logs" ];
+              address = "clickhouse:6000";
+            };
+          };
+        };
+      };
+
+      systemd.services.vector.serviceConfig = {
+        SupplementaryGroups = [ "nginx" ];
+      };
+    };
+  };
+
+  testScript =
+  let
+    # work around quote/substitution complexity by Nix, Perl, bash and SQL.
+    databaseDDL = pkgs.writeText "database.sql" "CREATE DATABASE IF NOT EXISTS nginxdb";
+
+    tableDDL = pkgs.writeText "table.sql" ''
+      CREATE TABLE IF NOT EXISTS  nginxdb.access_logs (
+        message String
+      )
+      ENGINE = MergeTree()
+      ORDER BY tuple()
+    '';
+
+    # Graciously taken from https://clickhouse.com/docs/en/integrations/vector
+    tableView = pkgs.writeText "table-view.sql" ''
+      CREATE MATERIALIZED VIEW nginxdb.access_logs_view
+      (
+        RemoteAddr String,
+        Client String,
+        RemoteUser String,
+        TimeLocal DateTime,
+        RequestMethod String,
+        Request String,
+        HttpVersion String,
+        Status Int32,
+        BytesSent Int64,
+        UserAgent String
+      )
+      ENGINE = MergeTree()
+      ORDER BY RemoteAddr
+      POPULATE AS
+      WITH
+       splitByWhitespace(message) as split,
+       splitByRegexp('\S \d+ "([^"]*)"', message) as referer
+      SELECT
+        split[1] AS RemoteAddr,
+        split[2] AS Client,
+        split[3] AS RemoteUser,
+        parseDateTimeBestEffort(replaceOne(trim(LEADING '[' FROM split[4]), ':', ' ')) AS TimeLocal,
+        trim(LEADING '"' FROM split[6]) AS RequestMethod,
+        split[7] AS Request,
+        trim(TRAILING '"' FROM split[8]) AS HttpVersion,
+        split[9] AS Status,
+        split[10] AS BytesSent,
+        trim(BOTH '"' from referer[2]) AS UserAgent
+      FROM
+        (SELECT message FROM nginxdb.access_logs)
+    '';
+
+    selectQuery = pkgs.writeText "select.sql" "SELECT * from nginxdb.access_logs_view";
+  in
+  ''
+    clickhouse.wait_for_unit("clickhouse")
+    clickhouse.wait_for_open_port(8123)
+
+    clickhouse.wait_until_succeeds(
+      "journalctl -o cat -u clickhouse.service | grep 'Started ClickHouse server'"
+    )
+
+    clickhouse.wait_for_unit("vector")
+    clickhouse.wait_for_open_port(6000)
+
+    clickhouse.succeed(
+      "cat ${databaseDDL} | clickhouse-client"
+    )
+
+    clickhouse.succeed(
+      "cat ${tableDDL} | clickhouse-client"
+    )
+
+    clickhouse.succeed(
+      "cat ${tableView} | clickhouse-client"
+    )
+
+    nginx.wait_for_unit("nginx")
+    nginx.wait_for_open_port(80)
+    nginx.wait_for_unit("vector")
+    nginx.wait_until_succeeds(
+      "journalctl -o cat -u vector.service | grep 'Starting file server'"
+    )
+
+    nginx.succeed("curl http://localhost/")
+    nginx.succeed("curl http://localhost/")
+
+    nginx.wait_for_file("/var/log/nginx/access.log")
+    nginx.wait_until_succeeds(
+      "journalctl -o cat -u vector.service | grep 'Found new file to watch. file=/var/log/nginx/access.log'"
+    )
+
+    clickhouse.wait_until_succeeds(
+      "cat ${selectQuery} | clickhouse-client | grep 'curl'"
+    )
+  '';
+})
diff --git a/nixos/tests/vector/syslog-quickwit.nix b/nixos/tests/vector/syslog-quickwit.nix
new file mode 100644
index 0000000000000..89c46d42ee75c
--- /dev/null
+++ b/nixos/tests/vector/syslog-quickwit.nix
@@ -0,0 +1,155 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+# Based on https://quickwit.io/docs/log-management/send-logs/using-vector
+
+{
+  name = "vector-syslog-quickwit";
+  meta.maintainers = [ pkgs.lib.maintainers.happysalada ];
+
+  nodes = {
+    quickwit = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ 7280 ];
+
+      services.quickwit = {
+        enable = true;
+        settings = {
+          listen_address = "::";
+        };
+      };
+    };
+
+    syslog = { config, pkgs, ... }: {
+      services.vector = {
+        enable = true;
+
+        settings = {
+          sources = {
+            generate_syslog = {
+              type = "demo_logs";
+              format = "syslog";
+              interval = 0.5;
+            };
+          };
+
+          transforms = {
+            remap_syslog = {
+              inputs = ["generate_syslog"];
+              type = "remap";
+              source = ''
+                structured = parse_syslog!(.message)
+                .timestamp_nanos = to_unix_timestamp!(structured.timestamp, unit: "nanoseconds")
+                .body = structured
+                .service_name = structured.appname
+                .resource_attributes.source_type = .source_type
+                .resource_attributes.host.hostname = structured.hostname
+                .resource_attributes.service.name = structured.appname
+                .attributes.syslog.procid = structured.procid
+                .attributes.syslog.facility = structured.facility
+                .attributes.syslog.version = structured.version
+                .severity_text = if includes(["emerg", "err", "crit", "alert"], structured.severity) {
+                  "ERROR"
+                } else if structured.severity == "warning" {
+                  "WARN"
+                } else if structured.severity == "debug" {
+                  "DEBUG"
+                } else if includes(["info", "notice"], structured.severity) {
+                  "INFO"
+                } else {
+                 structured.severity
+                }
+                .scope_name = structured.msgid
+                del(.message)
+                del(.timestamp)
+                del(.service)
+                del(.source_type)
+              '';
+            };
+          };
+
+          sinks = {
+            #emit_syslog = {
+            #  inputs = ["remap_syslog"];
+            #  type = "console";
+            #  encoding.codec = "json";
+            #};
+            quickwit_logs = {
+              type = "http";
+              method = "post";
+              inputs = [ "remap_syslog" ];
+              encoding.codec = "json";
+              framing.method = "newline_delimited";
+              uri = "http://quickwit:7280/api/v1/otel-logs-v0_7/ingest";
+            };
+          };
+        };
+      };
+    };
+  };
+
+  testScript =
+  let
+    aggregationQuery = pkgs.writeText "aggregation-query.json" ''
+      {
+        "query": "*",
+        "max_hits": 0,
+        "aggs": {
+          "count_per_minute": {
+            "histogram": {
+                "field": "timestamp_nanos",
+                "interval": 60000000
+            },
+            "aggs": {
+              "severity_text_count": {
+                "terms": {
+                  "field": "severity_text"
+                }
+              }
+            }
+          }
+        }
+      }
+    '';
+  in
+  ''
+    quickwit.wait_for_unit("quickwit")
+    quickwit.wait_for_open_port(7280)
+    quickwit.wait_for_open_port(7281)
+
+    quickwit.wait_until_succeeds(
+      "journalctl -o cat -u quickwit.service | grep 'transitioned to ready state'"
+    )
+
+    syslog.wait_for_unit("vector")
+    syslog.wait_until_succeeds(
+      "journalctl -o cat -u vector.service | grep 'Vector has started'"
+    )
+
+    quickwit.wait_until_succeeds(
+      "journalctl -o cat -u quickwit.service | grep 'publish-new-splits'"
+    )
+
+    # Wait for logs to be generated
+    # Test below aggregates by the minute
+    syslog.sleep(60 * 2)
+
+    quickwit.wait_until_succeeds(
+      "curl -sSf -XGET http://127.0.0.1:7280/api/v1/otel-logs-v0_7/search?query=severity_text:ERROR |"
+      + " jq '.num_hits' | grep -v '0'"
+    )
+
+    quickwit.wait_until_succeeds(
+      "journalctl -o cat -u quickwit.service | grep 'SearchRequest'"
+    )
+
+    quickwit.wait_until_succeeds(
+      "curl -sSf -XPOST -H 'Content-Type: application/json' http://127.0.0.1:7280/api/v1/otel-logs-v0_7/search --data @${aggregationQuery} |"
+      + " jq '.num_hits' | grep -v '0'"
+    )
+
+    quickwit.wait_until_succeeds(
+      "journalctl -o cat -u quickwit.service | grep 'count_per_minute'"
+    )
+  '';
+})
diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix
index 3c2a391233dbd..5fce3ba548123 100644
--- a/nixos/tests/virtualbox.nix
+++ b/nixos/tests/virtualbox.nix
@@ -98,7 +98,6 @@ let
     cfg = (import ../lib/eval-config.nix {
       system = if use64bitGuest then "x86_64-linux" else "i686-linux";
       modules = [
-        ../modules/profiles/minimal.nix
         (testVMConfig vmName vmScript)
       ];
     }).config;
diff --git a/nixos/tests/web-apps/nextjs-ollama-llm-ui.nix b/nixos/tests/web-apps/nextjs-ollama-llm-ui.nix
new file mode 100644
index 0000000000000..3bb9d1e62aefe
--- /dev/null
+++ b/nixos/tests/web-apps/nextjs-ollama-llm-ui.nix
@@ -0,0 +1,22 @@
+{ lib, ... }:
+
+{
+  name = "nextjs-ollama-llm-ui";
+  meta.maintainers = with lib.maintainers; [ malteneuss ];
+
+  nodes.machine =
+    { pkgs, ... }:
+    {
+      services.nextjs-ollama-llm-ui = {
+        enable = true;
+        port = 8080;
+      };
+    };
+
+  testScript = ''
+    # Ensure the service is started and reachable
+    machine.wait_for_unit("nextjs-ollama-llm-ui.service")
+    machine.wait_for_open_port(8080)
+    machine.succeed("curl --fail http://127.0.0.1:8080")
+  '';
+}
diff --git a/nixos/tests/web-apps/pretalx.nix b/nixos/tests/web-apps/pretalx.nix
index a226639b076b4..cbb6580aa0515 100644
--- a/nixos/tests/web-apps/pretalx.nix
+++ b/nixos/tests/web-apps/pretalx.nix
@@ -5,13 +5,16 @@
   meta.maintainers = lib.teams.c3d2.members;
 
   nodes = {
-    pretalx = {
+    pretalx = { config, ... }: {
       networking.extraHosts = ''
         127.0.0.1 talks.local
       '';
 
       services.pretalx = {
         enable = true;
+        plugins = with config.services.pretalx.package.plugins; [
+          pages
+        ];
         nginx.domain = "talks.local";
         settings = {
           site.url = "http://talks.local";
@@ -27,5 +30,9 @@
     pretalx.wait_for_unit("pretalx-worker.service")
 
     pretalx.wait_until_succeeds("curl -q --fail http://talks.local/orga/")
+
+    pretalx.succeed("pretalx-manage --help")
+
+    pretalx.log(pretalx.succeed("systemd-analyze security pretalx-web.service"))
   '';
 }
diff --git a/nixos/tests/web-servers/stargazer.nix b/nixos/tests/web-servers/stargazer.nix
index f56d1b8c94545..52bc93af17194 100644
--- a/nixos/tests/web-servers/stargazer.nix
+++ b/nixos/tests/web-servers/stargazer.nix
@@ -1,9 +1,9 @@
 { pkgs, lib, ... }:
 let
-  test_script = pkgs.stdenv.mkDerivation rec {
+  test_script = pkgs.stdenv.mkDerivation {
     pname = "stargazer-test-script";
     inherit (pkgs.stargazer) version src;
-    buildInputs = with pkgs; [ (python3.withPackages (ps: with ps; [ cryptography ])) ];
+    buildInputs = with pkgs; [ (python3.withPackages (ps: with ps; [ cryptography urllib3 ])) ];
     dontBuild = true;
     doCheck = false;
     installPhase = ''
@@ -11,7 +11,7 @@ let
       cp scripts/gemini-diagnostics $out/bin/test
     '';
   };
-  test_env = pkgs.stdenv.mkDerivation rec {
+  test_env = pkgs.stdenv.mkDerivation {
     pname = "stargazer-test-env";
     inherit (pkgs.stargazer) version src;
     buildPhase = ''
@@ -23,7 +23,7 @@ let
       cp -r * $out/
     '';
   };
-  scgi_server = pkgs.stdenv.mkDerivation rec {
+  scgi_server = pkgs.stdenv.mkDerivation {
     pname = "stargazer-test-scgi-server";
     inherit (pkgs.stargazer) version src;
     buildInputs = with pkgs; [ python3 ];
@@ -100,7 +100,12 @@ in
           }
           {
             route = "localhost:/no-exist";
-            root = "./does_not_exist";
+            root = "${test_env}/does_not_exist";
+          }
+          {
+            route = "localhost=/rss.xml";
+            root = "${test_env}/test_data/test_site";
+            mime-override = "application/atom+xml";
           }
         ];
       };
diff --git a/nixos/tests/wstunnel.nix b/nixos/tests/wstunnel.nix
new file mode 100644
index 0000000000000..3bbc295568fb7
--- /dev/null
+++ b/nixos/tests/wstunnel.nix
@@ -0,0 +1,96 @@
+let
+  certs = import ./common/acme/server/snakeoil-certs.nix;
+  domain = certs.domain;
+in
+
+{
+  name = "wstunnel";
+
+  nodes = {
+    server = {
+      virtualisation.vlans = [ 1 ];
+
+      security.pki.certificateFiles = [ certs.ca.cert ];
+
+      networking = {
+        useNetworkd = true;
+        useDHCP = false;
+        firewall.enable = false;
+      };
+
+      systemd.network.networks."01-eth1" = {
+        name = "eth1";
+        networkConfig.Address = "10.0.0.1/24";
+      };
+
+      services.wstunnel = {
+        enable = true;
+        servers.my-server = {
+          listen = {
+            host = "10.0.0.1";
+            port = 443;
+          };
+          tlsCertificate = certs.${domain}.cert;
+          tlsKey = certs.${domain}.key;
+        };
+      };
+    };
+
+    client = {
+      virtualisation.vlans = [ 1 ];
+
+      security.pki.certificateFiles = [ certs.ca.cert ];
+
+      networking = {
+        useNetworkd = true;
+        useDHCP = false;
+        firewall.enable = false;
+        extraHosts = ''
+          10.0.0.1 ${domain}
+        '';
+      };
+
+      systemd.network.networks."01-eth1" = {
+        name = "eth1";
+        networkConfig.Address = "10.0.0.2/24";
+      };
+
+      services.wstunnel = {
+        enable = true;
+        clients.my-client = {
+          autoStart = false;
+          connectTo = "wss://${domain}:443";
+          localToRemote = [
+            "tcp://8080:localhost:2080"
+          ];
+          remoteToLocal = [
+            "tcp://2081:localhost:8081"
+          ];
+        };
+      };
+    };
+  };
+
+  testScript = /* python */ ''
+    start_all()
+    server.wait_for_unit("wstunnel-server-my-server.service")
+    client.wait_for_open_port(443, "10.0.0.1")
+
+    client.systemctl("start wstunnel-client-my-client.service")
+    client.wait_for_unit("wstunnel-client-my-client.service")
+
+    with subtest("connection from client to server"):
+      server.succeed("nc -l 2080 >/tmp/msg &")
+      client.sleep(1)
+      client.succeed('nc -w1 localhost 8080 <<<"Hello from client"')
+      server.succeed('grep "Hello from client" /tmp/msg')
+
+    with subtest("connection from server to client"):
+      client.succeed("nc -l 8081 >/tmp/msg &")
+      server.sleep(1)
+      server.succeed('nc -w1 localhost 2081 <<<"Hello from server"')
+      client.succeed('grep "Hello from server" /tmp/msg')
+
+    client.systemctl("stop wstunnel-client-my-client.service")
+  '';
+}
diff --git a/nixos/tests/ydotool.nix b/nixos/tests/ydotool.nix
new file mode 100644
index 0000000000000..45e3d27adeb49
--- /dev/null
+++ b/nixos/tests/ydotool.nix
@@ -0,0 +1,184 @@
+{
+  system ? builtins.currentSystem,
+  config ? { },
+  pkgs ? import ../.. { inherit system config; },
+  lib ? pkgs.lib,
+}:
+let
+  makeTest = import ./make-test-python.nix;
+  textInput = "This works.";
+  inputBoxText = "Enter input";
+  inputBox = pkgs.writeShellScript "zenity-input" ''
+    ${lib.getExe pkgs.gnome.zenity} --entry --text '${inputBoxText}:' > /tmp/output &
+  '';
+  asUser = ''
+    def as_user(cmd: str):
+        """
+        Return a shell command for running a shell command as a specific user.
+        """
+        return f"sudo -u alice -i {cmd}"
+  '';
+in
+{
+  headless = makeTest {
+    name = "headless";
+
+    enableOCR = true;
+
+    nodes.machine = {
+      imports = [ ./common/user-account.nix ];
+
+      users.users.alice.extraGroups = [ "ydotool" ];
+
+      programs.ydotool.enable = true;
+
+      services.getty.autologinUser = "alice";
+    };
+
+    testScript =
+      asUser
+      + ''
+        start_all()
+
+        machine.wait_for_unit("multi-user.target")
+        machine.wait_for_text("alice")
+        machine.succeed(as_user("ydotool type 'echo ${textInput} > /tmp/output'")) # text input
+        machine.succeed(as_user("ydotool key 28:1 28:0")) # text input
+        machine.screenshot("headless_input")
+        machine.wait_for_file("/tmp/output")
+        machine.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input
+      '';
+
+    meta.maintainers = with lib.maintainers; [
+      OPNA2608
+      quantenzitrone
+    ];
+  };
+
+  x11 = makeTest {
+    name = "x11";
+
+    enableOCR = true;
+
+    nodes.machine = {
+      imports = [
+        ./common/user-account.nix
+        ./common/auto.nix
+        ./common/x11.nix
+      ];
+
+      users.users.alice.extraGroups = [ "ydotool" ];
+
+      programs.ydotool.enable = true;
+
+      test-support.displayManager.auto = {
+        enable = true;
+        user = "alice";
+      };
+
+      services.xserver.windowManager.dwm.enable = true;
+      services.displayManager.defaultSession = lib.mkForce "none+dwm";
+    };
+
+    testScript =
+      asUser
+      + ''
+        start_all()
+
+        machine.wait_for_x()
+        machine.execute(as_user("${inputBox}"))
+        machine.wait_for_text("${inputBoxText}")
+        machine.succeed(as_user("ydotool type '${textInput}'")) # text input
+        machine.screenshot("x11_input")
+        machine.succeed(as_user("ydotool mousemove -a 400 110")) # mouse input
+        machine.succeed(as_user("ydotool click 0xC0")) # mouse input
+        machine.wait_for_file("/tmp/output")
+        machine.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input
+      '';
+
+    meta.maintainers = with lib.maintainers; [
+      OPNA2608
+      quantenzitrone
+    ];
+  };
+
+  wayland = makeTest {
+    name = "wayland";
+
+    enableOCR = true;
+
+    nodes.machine = {
+      imports = [ ./common/user-account.nix ];
+
+      services.cage = {
+        enable = true;
+        user = "alice";
+      };
+
+      programs.ydotool.enable = true;
+
+      services.cage.program = inputBox;
+    };
+
+    testScript = ''
+      start_all()
+
+      machine.wait_for_unit("graphical.target")
+      machine.wait_for_text("${inputBoxText}")
+      machine.succeed("ydotool type '${textInput}'") # text input
+      machine.screenshot("wayland_input")
+      machine.succeed("ydotool mousemove -a 100 100") # mouse input
+      machine.succeed("ydotool click 0xC0") # mouse input
+      machine.wait_for_file("/tmp/output")
+      machine.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input
+    '';
+
+    meta.maintainers = with lib.maintainers; [
+      OPNA2608
+      quantenzitrone
+    ];
+  };
+
+  customGroup =
+    let
+      name = "customGroup";
+      nodeName = "${name}Node";
+      insideGroupUsername = "ydotool-user";
+      outsideGroupUsername = "other-user";
+      groupName = "custom-group";
+    in
+    makeTest {
+      inherit name;
+
+      nodes."${nodeName}" = {
+        programs.ydotool = {
+          enable = true;
+          group = groupName;
+        };
+
+        users.users = {
+          "${insideGroupUsername}" = {
+            isNormalUser = true;
+            extraGroups = [ groupName ];
+          };
+          "${outsideGroupUsername}".isNormalUser = true;
+        };
+      };
+
+      testScript = ''
+        start_all()
+
+        # Wait for service to start
+        ${nodeName}.wait_for_unit("multi-user.target")
+        ${nodeName}.wait_for_unit("ydotoold.service")
+
+        # Verify that user with the configured group can use the service
+        ${nodeName}.succeed("sudo --login --user=${insideGroupUsername} ydotool type 'Hello, World!'")
+
+        # Verify that user without the configured group can't use the service
+        ${nodeName}.fail("sudo --login --user=${outsideGroupUsername} ydotool type 'Hello, World!'")
+      '';
+
+      meta.maintainers = with lib.maintainers; [ l0b0 ];
+    };
+}
diff --git a/nixos/tests/your_spotify.nix b/nixos/tests/your_spotify.nix
new file mode 100644
index 0000000000000..a1fa0e459a8e1
--- /dev/null
+++ b/nixos/tests/your_spotify.nix
@@ -0,0 +1,33 @@
+import ./make-test-python.nix ({pkgs, ...}: {
+  name = "your_spotify";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [patrickdag];
+  };
+
+  nodes.machine = {
+    services.your_spotify = {
+      enable = true;
+      spotifySecretFile = pkgs.writeText "spotifySecretFile" "deadbeef";
+      settings = {
+        CLIENT_ENDPOINT = "http://localhost";
+        API_ENDPOINT = "http://localhost:3000";
+        SPOTIFY_PUBLIC = "beefdead";
+      };
+      enableLocalDB = true;
+      nginxVirtualHost = "localhost";
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("your_spotify.service")
+
+    machine.wait_for_open_port(3000)
+    machine.wait_for_open_port(80)
+
+    out = machine.succeed("curl --fail -X GET 'http://localhost:3000/'")
+    assert "Hello !" in out
+
+    out = machine.succeed("curl --fail -X GET 'http://localhost:80/'")
+    assert "<title>Your Spotify</title>" in out
+  '';
+})