about summary refs log tree commit diff
path: root/nixpkgs/nixos
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2023-08-08 16:04:42 +0000
committerAlyssa Ross <hi@alyssa.is>2023-08-13 06:35:37 +0000
commit12aaa58dac35800b5b7d77f81cf2a87c21ee55da (patch)
treebe0add9e5c22a85d20b5d78206aa74f956eb2a1b /nixpkgs/nixos
parent45892a5591202f75a1c2f1ca7c62a92c7566e3c5 (diff)
parent5a8e9243812ba528000995b294292d3b5e120947 (diff)
downloadnixlib-12aaa58dac35800b5b7d77f81cf2a87c21ee55da.tar
nixlib-12aaa58dac35800b5b7d77f81cf2a87c21ee55da.tar.gz
nixlib-12aaa58dac35800b5b7d77f81cf2a87c21ee55da.tar.bz2
nixlib-12aaa58dac35800b5b7d77f81cf2a87c21ee55da.tar.lz
nixlib-12aaa58dac35800b5b7d77f81cf2a87c21ee55da.tar.xz
nixlib-12aaa58dac35800b5b7d77f81cf2a87c21ee55da.tar.zst
nixlib-12aaa58dac35800b5b7d77f81cf2a87c21ee55da.zip
Merge branch 'nixos-unstable' of https://github.com/NixOS/nixpkgs
Conflicts:
	nixpkgs/pkgs/applications/window-managers/sway/default.nix
	nixpkgs/pkgs/build-support/go/module.nix
	nixpkgs/pkgs/build-support/rust/build-rust-package/default.nix
	nixpkgs/pkgs/development/libraries/mesa/default.nix
	nixpkgs/pkgs/servers/dict/dictd-db.nix

Link: https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config/-/issues/391
Diffstat (limited to 'nixpkgs/nixos')
-rw-r--r--nixpkgs/nixos/doc/manual/common.nix4
-rw-r--r--nixpkgs/nixos/doc/manual/configuration/gpu-accel.chapter.md2
-rw-r--r--nixpkgs/nixos/doc/manual/configuration/renaming-interfaces.section.md4
-rw-r--r--nixpkgs/nixos/doc/manual/contributing-to-this-manual.chapter.md2
-rw-r--r--nixpkgs/nixos/doc/manual/default.nix243
-rw-r--r--nixpkgs/nixos/doc/manual/development/option-types.section.md5
-rw-r--r--nixpkgs/nixos/doc/manual/development/writing-nixos-tests.section.md205
-rw-r--r--nixpkgs/nixos/doc/manual/installation/installing.chapter.md8
-rw-r--r--nixpkgs/nixos/doc/manual/man-pages.xml46
-rw-r--r--nixpkgs/nixos/doc/manual/release-notes/rl-2111.section.md2
-rw-r--r--nixpkgs/nixos/doc/manual/release-notes/rl-2305.section.md10
-rw-r--r--nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md152
-rw-r--r--nixpkgs/nixos/doc/manual/shell.nix20
-rw-r--r--nixpkgs/nixos/lib/eval-config.nix12
-rw-r--r--nixpkgs/nixos/lib/make-disk-image.nix3
-rw-r--r--nixpkgs/nixos/lib/make-options-doc/default.nix69
-rw-r--r--nixpkgs/nixos/lib/make-options-doc/mergeJSON.py60
-rw-r--r--nixpkgs/nixos/lib/make-options-doc/postprocess-option-descriptions.xsl115
-rw-r--r--nixpkgs/nixos/lib/qemu-common.nix2
-rw-r--r--nixpkgs/nixos/lib/systemd-lib.nix24
-rw-r--r--nixpkgs/nixos/lib/systemd-network-units.nix240
-rw-r--r--nixpkgs/nixos/lib/test-driver/extract-docstrings.py66
-rw-r--r--nixpkgs/nixos/lib/test-driver/nixos-test-driver-docstrings.nix13
-rw-r--r--nixpkgs/nixos/lib/test-driver/test_driver/machine.py253
-rw-r--r--nixpkgs/nixos/lib/testing/driver.nix6
-rw-r--r--nixpkgs/nixos/lib/testing/nodes.nix1
-rw-r--r--nixpkgs/nixos/lib/utils.nix3
-rw-r--r--nixpkgs/nixos/modules/config/fonts/fontconfig.nix65
-rw-r--r--nixpkgs/nixos/modules/config/fonts/fontdir.nix4
-rw-r--r--nixpkgs/nixos/modules/config/fonts/fonts.nix47
-rw-r--r--nixpkgs/nixos/modules/config/fonts/ghostscript.nix30
-rw-r--r--nixpkgs/nixos/modules/config/fonts/packages.nix43
-rw-r--r--nixpkgs/nixos/modules/config/i18n.nix1
-rw-r--r--nixpkgs/nixos/modules/config/malloc.nix2
-rw-r--r--nixpkgs/nixos/modules/config/nix-channel.nix108
-rw-r--r--nixpkgs/nixos/modules/config/nix-flakes.nix95
-rw-r--r--nixpkgs/nixos/modules/config/nix-remote-build.nix226
-rw-r--r--nixpkgs/nixos/modules/config/nix.nix379
-rw-r--r--nixpkgs/nixos/modules/config/no-x-libs.nix14
-rw-r--r--nixpkgs/nixos/modules/config/qt.nix3
-rw-r--r--nixpkgs/nixos/modules/config/swap.nix5
-rw-r--r--nixpkgs/nixos/modules/config/sysctl.nix3
-rw-r--r--nixpkgs/nixos/modules/config/update-users-groups.pl4
-rw-r--r--nixpkgs/nixos/modules/config/users-groups.nix5
-rw-r--r--nixpkgs/nixos/modules/hardware/all-firmware.nix1
-rw-r--r--nixpkgs/nixos/modules/hardware/opengl.nix8
-rw-r--r--nixpkgs/nixos/modules/hardware/usb-modeswitch.nix (renamed from nixpkgs/nixos/modules/hardware/usb-wwan.nix)13
-rw-r--r--nixpkgs/nixos/modules/hardware/video/displaylink.nix1
-rw-r--r--nixpkgs/nixos/modules/hardware/video/nvidia.nix2
-rw-r--r--nixpkgs/nixos/modules/hardware/wooting.nix4
-rw-r--r--nixpkgs/nixos/modules/i18n/input-method/fcitx5.nix34
-rw-r--r--nixpkgs/nixos/modules/image/amend-repart-definitions.py112
-rw-r--r--nixpkgs/nixos/modules/image/repart.md137
-rw-r--r--nixpkgs/nixos/modules/image/repart.nix209
-rw-r--r--nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix1
-rw-r--r--nixpkgs/nixos/modules/installer/cd-dvd/iso-image.nix12
-rw-r--r--nixpkgs/nixos/modules/installer/netboot/netboot.nix4
-rw-r--r--nixpkgs/nixos/modules/installer/tools/nixos-generate-config.pl13
-rwxr-xr-xnixpkgs/nixos/modules/installer/tools/nixos-install.sh2
-rw-r--r--nixpkgs/nixos/modules/installer/tools/tools.nix12
-rw-r--r--nixpkgs/nixos/modules/misc/documentation.nix30
-rw-r--r--nixpkgs/nixos/modules/misc/ids.nix4
-rw-r--r--nixpkgs/nixos/modules/misc/nixpkgs.nix27
-rw-r--r--nixpkgs/nixos/modules/misc/version.nix9
-rw-r--r--nixpkgs/nixos/modules/module-list.nix46
-rw-r--r--nixpkgs/nixos/modules/profiles/headless.nix4
-rw-r--r--nixpkgs/nixos/modules/profiles/installation-device.nix2
-rw-r--r--nixpkgs/nixos/modules/profiles/macos-builder.nix21
-rw-r--r--nixpkgs/nixos/modules/programs/atop.nix72
-rw-r--r--nixpkgs/nixos/modules/programs/cfs-zen-tweaks.nix8
-rw-r--r--nixpkgs/nixos/modules/programs/direnv.nix147
-rw-r--r--nixpkgs/nixos/modules/programs/firefox.nix1
-rw-r--r--nixpkgs/nixos/modules/programs/fish.nix2
-rw-r--r--nixpkgs/nixos/modules/programs/gnupg.nix89
-rw-r--r--nixpkgs/nixos/modules/programs/hyprland.nix47
-rw-r--r--nixpkgs/nixos/modules/programs/miriway.nix2
-rw-r--r--nixpkgs/nixos/modules/programs/nix-ld.nix18
-rw-r--r--nixpkgs/nixos/modules/programs/ns-usbloader.nix18
-rw-r--r--nixpkgs/nixos/modules/programs/oddjobd.nix33
-rw-r--r--nixpkgs/nixos/modules/programs/shadow.nix308
-rw-r--r--nixpkgs/nixos/modules/programs/starship.nix6
-rw-r--r--nixpkgs/nixos/modules/programs/wayland/wayfire.nix48
-rw-r--r--nixpkgs/nixos/modules/programs/wayland/wayland-session.nix2
-rw-r--r--nixpkgs/nixos/modules/programs/xonsh.nix3
-rw-r--r--nixpkgs/nixos/modules/rename.nix13
-rw-r--r--nixpkgs/nixos/modules/security/apparmor/includes.nix9
-rw-r--r--nixpkgs/nixos/modules/security/ca.nix6
-rw-r--r--nixpkgs/nixos/modules/security/lock-kernel-modules.nix11
-rw-r--r--nixpkgs/nixos/modules/security/pam.nix45
-rw-r--r--nixpkgs/nixos/modules/security/sudo.nix4
-rw-r--r--nixpkgs/nixos/modules/services/audio/jmusicbot.nix2
-rw-r--r--nixpkgs/nixos/modules/services/audio/roon-bridge.nix5
-rw-r--r--nixpkgs/nixos/modules/services/audio/roon-server.nix5
-rw-r--r--nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix186
-rw-r--r--nixpkgs/nixos/modules/services/audio/wyoming/piper.nix174
-rw-r--r--nixpkgs/nixos/modules/services/backup/borgbackup.nix2
-rw-r--r--nixpkgs/nixos/modules/services/backup/restic.nix2
-rw-r--r--nixpkgs/nixos/modules/services/backup/tarsnap.nix12
-rw-r--r--nixpkgs/nixos/modules/services/cluster/patroni/default.nix7
-rw-r--r--nixpkgs/nixos/modules/services/computing/boinc/client.nix1
-rw-r--r--nixpkgs/nixos/modules/services/continuous-integration/buildkite-agents.nix261
-rw-r--r--nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix2
-rw-r--r--nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix6
-rw-r--r--nixpkgs/nixos/modules/services/continuous-integration/jenkins/job-builder.nix27
-rw-r--r--nixpkgs/nixos/modules/services/databases/pgbouncer.nix632
-rw-r--r--nixpkgs/nixos/modules/services/databases/postgresql.nix4
-rw-r--r--nixpkgs/nixos/modules/services/editors/emacs.nix2
-rw-r--r--nixpkgs/nixos/modules/services/games/factorio.nix2
-rw-r--r--nixpkgs/nixos/modules/services/games/freeciv.nix2
-rw-r--r--nixpkgs/nixos/modules/services/games/mchprs.nix341
-rw-r--r--nixpkgs/nixos/modules/services/games/minetest-server.nix71
-rw-r--r--nixpkgs/nixos/modules/services/hardware/fwupd.nix13
-rw-r--r--nixpkgs/nixos/modules/services/hardware/joycond.nix3
-rw-r--r--nixpkgs/nixos/modules/services/hardware/keyd.nix84
-rw-r--r--nixpkgs/nixos/modules/services/hardware/pcscd.nix3
-rw-r--r--nixpkgs/nixos/modules/services/hardware/supergfxd.nix2
-rw-r--r--nixpkgs/nixos/modules/services/hardware/udev.nix4
-rw-r--r--nixpkgs/nixos/modules/services/hardware/udisks2.nix14
-rw-r--r--nixpkgs/nixos/modules/services/home-automation/ebusd.nix270
-rw-r--r--nixpkgs/nixos/modules/services/home-automation/evcc.nix2
-rw-r--r--nixpkgs/nixos/modules/services/home-automation/home-assistant.nix1
-rw-r--r--nixpkgs/nixos/modules/services/mail/davmail.nix27
-rw-r--r--nixpkgs/nixos/modules/services/mail/nullmailer.nix12
-rw-r--r--nixpkgs/nixos/modules/services/mail/public-inbox.nix18
-rw-r--r--nixpkgs/nixos/modules/services/mail/rspamd.nix2
-rw-r--r--nixpkgs/nixos/modules/services/mail/spamassassin.nix2
-rw-r--r--nixpkgs/nixos/modules/services/matrix/appservice-irc.nix13
-rw-r--r--nixpkgs/nixos/modules/services/matrix/conduit.nix2
-rw-r--r--nixpkgs/nixos/modules/services/matrix/matrix-sliding-sync.nix96
-rw-r--r--nixpkgs/nixos/modules/services/matrix/mautrix-telegram.nix3
-rw-r--r--nixpkgs/nixos/modules/services/matrix/mautrix-whatsapp.nix198
-rw-r--r--nixpkgs/nixos/modules/services/misc/ananicy.nix67
-rw-r--r--nixpkgs/nixos/modules/services/misc/ankisyncd.nix22
-rw-r--r--nixpkgs/nixos/modules/services/misc/atuin.nix69
-rw-r--r--nixpkgs/nixos/modules/services/misc/bcg.nix175
-rw-r--r--nixpkgs/nixos/modules/services/misc/calibre-server.nix94
-rw-r--r--nixpkgs/nixos/modules/services/misc/cgminer.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/disnix.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/docker-registry.nix10
-rw-r--r--nixpkgs/nixos/modules/services/misc/evdevremapkeys.nix59
-rw-r--r--nixpkgs/nixos/modules/services/misc/exhibitor.nix417
-rw-r--r--nixpkgs/nixos/modules/services/misc/gitea.nix18
-rw-r--r--nixpkgs/nixos/modules/services/misc/gitlab.nix7
-rw-r--r--nixpkgs/nixos/modules/services/misc/gogs.nix8
-rw-r--r--nixpkgs/nixos/modules/services/misc/gollum.nix24
-rw-r--r--nixpkgs/nixos/modules/services/misc/heisenbridge.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/homepage-dashboard.nix55
-rw-r--r--nixpkgs/nixos/modules/services/misc/klipper.nix22
-rw-r--r--nixpkgs/nixos/modules/services/misc/mqtt2influxdb.nix253
-rw-r--r--nixpkgs/nixos/modules/services/misc/n8n.nix10
-rw-r--r--nixpkgs/nixos/modules/services/misc/nitter.nix3
-rw-r--r--nixpkgs/nixos/modules/services/misc/nix-daemon.nix844
-rw-r--r--nixpkgs/nixos/modules/services/misc/nix-optimise.nix31
-rw-r--r--nixpkgs/nixos/modules/services/misc/ntfy-sh.nix20
-rw-r--r--nixpkgs/nixos/modules/services/misc/paperless.nix43
-rw-r--r--nixpkgs/nixos/modules/services/misc/prowlarr.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/sourcehut/default.nix6
-rw-r--r--nixpkgs/nixos/modules/services/misc/sssd.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/zoneminder.nix2
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/below.nix106
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/grafana.nix810
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/munin.nix2
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/netdata.nix31
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/opentelemetry-collector.nix73
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/osquery.nix97
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix18
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/ipmi.nix1
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/junos-czerwonk.nix72
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix65
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pve.nix12
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix33
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/ceph.nix31
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/glusterfs.nix4
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/kubo.nix6
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/samba-wsdd.nix19
-rw-r--r--nixpkgs/nixos/modules/services/networking/adguardhome.nix16
-rw-r--r--nixpkgs/nixos/modules/services/networking/avahi-daemon.nix17
-rw-r--r--nixpkgs/nixos/modules/services/networking/biboumi.nix3
-rw-r--r--nixpkgs/nixos/modules/services/networking/bird.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/cgit.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/cjdns.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/ddclient.nix243
-rw-r--r--nixpkgs/nixos/modules/services/networking/dhcpd.nix230
-rw-r--r--nixpkgs/nixos/modules/services/networking/fakeroute.nix22
-rw-r--r--nixpkgs/nixos/modules/services/networking/hostapd.nix1413
-rw-r--r--nixpkgs/nixos/modules/services/networking/i2pd.nix53
-rw-r--r--nixpkgs/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/libreswan.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/mosquitto.nix65
-rw-r--r--nixpkgs/nixos/modules/services/networking/murmur.nix52
-rw-r--r--nixpkgs/nixos/modules/services/networking/networkmanager.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/nsd.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/ntopng.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/pdns-recursor.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/powerdns.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/prosody.nix5
-rw-r--r--nixpkgs/nixos/modules/services/networking/searx.nix59
-rw-r--r--nixpkgs/nixos/modules/services/networking/sing-box.nix67
-rw-r--r--nixpkgs/nixos/modules/services/networking/ssh/lshd.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/ssh/sshd.nix12
-rw-r--r--nixpkgs/nixos/modules/services/networking/syncthing.nix601
-rw-r--r--nixpkgs/nixos/modules/services/networking/tailscale.nix41
-rw-r--r--nixpkgs/nixos/modules/services/networking/thelounge.nix18
-rw-r--r--nixpkgs/nixos/modules/services/networking/trust-dns.nix177
-rw-r--r--nixpkgs/nixos/modules/services/networking/twingate.nix30
-rw-r--r--nixpkgs/nixos/modules/services/networking/vsftpd.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/wgautomesh.nix6
-rw-r--r--nixpkgs/nixos/modules/services/networking/xrdp.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/zerobin.nix5
-rw-r--r--nixpkgs/nixos/modules/services/printing/cupsd.nix6
-rw-r--r--nixpkgs/nixos/modules/services/scheduling/fcron.nix2
-rw-r--r--nixpkgs/nixos/modules/services/search/kibana.nix4
-rw-r--r--nixpkgs/nixos/modules/services/search/typesense.nix125
-rw-r--r--nixpkgs/nixos/modules/services/security/esdm.nix102
-rw-r--r--nixpkgs/nixos/modules/services/security/fail2ban.nix225
-rw-r--r--nixpkgs/nixos/modules/services/security/kanidm.nix20
-rw-r--r--nixpkgs/nixos/modules/services/security/tor.nix5
-rw-r--r--nixpkgs/nixos/modules/services/security/usbguard.nix130
-rw-r--r--nixpkgs/nixos/modules/services/security/vault.nix1
-rw-r--r--nixpkgs/nixos/modules/services/security/vaultwarden/default.nix9
-rw-r--r--nixpkgs/nixos/modules/services/system/bpftune.nix22
-rw-r--r--nixpkgs/nixos/modules/services/system/cloud-init.nix9
-rw-r--r--nixpkgs/nixos/modules/services/system/dbus.nix2
-rw-r--r--nixpkgs/nixos/modules/services/system/nix-daemon.nix264
-rw-r--r--nixpkgs/nixos/modules/services/ttys/kmscon.nix3
-rw-r--r--nixpkgs/nixos/modules/services/video/frigate.nix17
-rw-r--r--nixpkgs/nixos/modules/services/wayland/cage.nix10
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/anuko-time-tracker.nix388
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/cloudlog.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/code-server.nix12
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/dex.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/engelsystem.nix1
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/freshrss.nix61
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/gotosocial.md64
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/gotosocial.nix173
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/guacamole-client.nix60
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/guacamole-server.nix83
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix24
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/invidious.nix8
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/lemmy.nix172
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mastodon.nix4
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mattermost.nix3
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/miniflux.nix12
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/netbox.nix42
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.md2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.nix191
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nexus.nix16
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nifi.nix4
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/openvscode-server.nix41
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/outline.nix57
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/peering-manager.nix38
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/peertube.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/pict-rs.nix72
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/pixelfed.nix21
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/plausible.nix7
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/restya-board.nix4
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/slskd.nix211
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/snipe-it.nix9
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/sogo.nix4
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/caddy/default.nix58
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/keter/default.nix79
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/lighttpd/cgit.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/nginx/default.nix43
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/rustus.nix252
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/static-web-server.nix68
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/ttyd.nix11
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/budgie.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/cde.nix4
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix8
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/deepin.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix7
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/phosh.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/xfce.nix18
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/default.nix35
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix4
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/slick.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix11
-rw-r--r--nixpkgs/nixos/modules/services/x11/window-managers/herbstluftwm.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/xserver.nix8
-rw-r--r--nixpkgs/nixos/modules/system/activation/activatable-system.nix92
-rw-r--r--nixpkgs/nixos/modules/system/activation/activation-script.nix21
-rwxr-xr-xnixpkgs/nixos/modules/system/activation/switch-to-configuration.pl36
-rw-r--r--nixpkgs/nixos/modules/system/activation/top-level.nix66
-rw-r--r--nixpkgs/nixos/modules/system/boot/initrd-network.nix21
-rw-r--r--nixpkgs/nixos/modules/system/boot/kernel_config.nix7
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix45
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix64
-rw-r--r--nixpkgs/nixos/modules/system/boot/luksroot.nix2
-rw-r--r--nixpkgs/nixos/modules/system/boot/networkd.nix359
-rw-r--r--nixpkgs/nixos/modules/system/boot/plymouth.nix58
-rw-r--r--nixpkgs/nixos/modules/system/boot/stage-1-init.sh24
-rw-r--r--nixpkgs/nixos/modules/system/boot/stage-1.nix14
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd.nix7
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/nspawn.nix2
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/repart.nix27
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/shutdown.nix6
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/sysupdate.nix136
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/user.nix2
-rw-r--r--nixpkgs/nixos/modules/system/etc/etc.nix2
-rw-r--r--nixpkgs/nixos/modules/tasks/bcache.nix8
-rw-r--r--nixpkgs/nixos/modules/tasks/filesystems/bcachefs.nix14
-rw-r--r--nixpkgs/nixos/modules/tasks/filesystems/squashfs.nix13
-rw-r--r--nixpkgs/nixos/modules/tasks/filesystems/zfs.nix77
-rw-r--r--nixpkgs/nixos/modules/tasks/lvm.nix17
-rw-r--r--nixpkgs/nixos/modules/tasks/network-interfaces.nix2
-rw-r--r--nixpkgs/nixos/modules/tasks/swraid.nix67
-rw-r--r--nixpkgs/nixos/modules/testing/test-instrumentation.nix2
-rw-r--r--nixpkgs/nixos/modules/virtualisation/docker.nix12
-rw-r--r--nixpkgs/nixos/modules/virtualisation/lxd.nix14
-rw-r--r--nixpkgs/nixos/modules/virtualisation/nixos-containers.nix8
-rw-r--r--nixpkgs/nixos/modules/virtualisation/podman/default.nix5
-rw-r--r--nixpkgs/nixos/modules/virtualisation/proxmox-image.nix41
-rw-r--r--nixpkgs/nixos/modules/virtualisation/qemu-vm.nix211
-rw-r--r--nixpkgs/nixos/modules/virtualisation/vmware-guest.nix4
-rw-r--r--nixpkgs/nixos/tests/adguardhome.nix69
-rw-r--r--nixpkgs/nixos/tests/all-tests.nix36
-rw-r--r--nixpkgs/nixos/tests/anuko-time-tracker.nix17
-rw-r--r--nixpkgs/nixos/tests/apfs.nix8
-rw-r--r--nixpkgs/nixos/tests/appliance-repart-image.nix116
-rw-r--r--nixpkgs/nixos/tests/atuin.nix2
-rw-r--r--nixpkgs/nixos/tests/bcachefs.nix2
-rw-r--r--nixpkgs/nixos/tests/binary-cache.nix10
-rw-r--r--nixpkgs/nixos/tests/bpftune.nix20
-rw-r--r--nixpkgs/nixos/tests/budgie.nix10
-rw-r--r--nixpkgs/nixos/tests/buildkite-agents.nix6
-rw-r--r--nixpkgs/nixos/tests/caddy.nix9
-rw-r--r--nixpkgs/nixos/tests/cage.nix2
-rw-r--r--nixpkgs/nixos/tests/calibre-server.nix104
-rw-r--r--nixpkgs/nixos/tests/common/auto-format-root-device.nix29
-rw-r--r--nixpkgs/nixos/tests/common/gpg-keyring.nix21
-rw-r--r--nixpkgs/nixos/tests/common/resolver.nix2
-rw-r--r--nixpkgs/nixos/tests/coturn.nix1
-rw-r--r--nixpkgs/nixos/tests/cups-pdf.nix2
-rw-r--r--nixpkgs/nixos/tests/curl-impersonate.nix157
-rw-r--r--nixpkgs/nixos/tests/deepin.nix6
-rw-r--r--nixpkgs/nixos/tests/evcc.nix2
-rw-r--r--nixpkgs/nixos/tests/fail2ban.nix18
-rw-r--r--nixpkgs/nixos/tests/fakeroute.nix22
-rw-r--r--nixpkgs/nixos/tests/fontconfig-default-fonts.nix4
-rw-r--r--nixpkgs/nixos/tests/freshrss-http-auth.nix20
-rw-r--r--nixpkgs/nixos/tests/fsck.nix10
-rw-r--r--nixpkgs/nixos/tests/gitea.nix31
-rw-r--r--nixpkgs/nixos/tests/gnome-flashback.nix14
-rw-r--r--nixpkgs/nixos/tests/guacamole-server.nix21
-rw-r--r--nixpkgs/nixos/tests/hibernate.nix143
-rw-r--r--nixpkgs/nixos/tests/homepage-dashboard.nix14
-rw-r--r--nixpkgs/nixos/tests/initrd-luks-empty-passphrase.nix3
-rw-r--r--nixpkgs/nixos/tests/initrd-network-ssh/default.nix18
-rw-r--r--nixpkgs/nixos/tests/installed-tests/default.nix1
-rw-r--r--nixpkgs/nixos/tests/installed-tests/upower.nix9
-rw-r--r--nixpkgs/nixos/tests/installer-systemd-stage-1.nix2
-rw-r--r--nixpkgs/nixos/tests/installer.nix122
-rw-r--r--nixpkgs/nixos/tests/installer/flake.nix20
-rw-r--r--nixpkgs/nixos/tests/jenkins.nix4
-rw-r--r--nixpkgs/nixos/tests/k3s/single-node.nix14
-rw-r--r--nixpkgs/nixos/tests/kafka.nix2
-rw-r--r--nixpkgs/nixos/tests/kanidm.nix53
-rw-r--r--nixpkgs/nixos/tests/kernel-generic.nix8
-rw-r--r--nixpkgs/nixos/tests/keter.nix65
-rw-r--r--nixpkgs/nixos/tests/kexec.nix11
-rw-r--r--nixpkgs/nixos/tests/keyd.nix2
-rw-r--r--nixpkgs/nixos/tests/keymap.nix37
-rw-r--r--nixpkgs/nixos/tests/lemmy.nix12
-rw-r--r--nixpkgs/nixos/tests/luks.nix2
-rw-r--r--nixpkgs/nixos/tests/lxd-ui.nix35
-rw-r--r--nixpkgs/nixos/tests/maestral.nix5
-rw-r--r--nixpkgs/nixos/tests/matomo.nix4
-rw-r--r--nixpkgs/nixos/tests/miniflux.nix6
-rw-r--r--nixpkgs/nixos/tests/miriway.nix2
-rw-r--r--nixpkgs/nixos/tests/mumble.nix4
-rw-r--r--nixpkgs/nixos/tests/n8n.nix3
-rw-r--r--nixpkgs/nixos/tests/networking.nix78
-rw-r--r--nixpkgs/nixos/tests/nextcloud/basic.nix6
-rw-r--r--nixpkgs/nixos/tests/nextcloud/default.nix2
-rw-r--r--nixpkgs/nixos/tests/nextcloud/openssl-sse.nix2
-rw-r--r--nixpkgs/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix40
-rw-r--r--nixpkgs/nixos/tests/nextcloud/with-mysql-and-memcached.nix2
-rw-r--r--nixpkgs/nixos/tests/nextcloud/with-postgresql-and-redis.nix5
-rw-r--r--nixpkgs/nixos/tests/nginx-proxyprotocol/default.nix4
-rw-r--r--nixpkgs/nixos/tests/nginx-status-page.nix72
-rw-r--r--nixpkgs/nixos/tests/nixos-test-driver/busybox.nix16
-rw-r--r--nixpkgs/nixos/tests/nixos-test-driver/lib-extend.nix31
-rw-r--r--nixpkgs/nixos/tests/non-default-filesystems.nix66
-rw-r--r--nixpkgs/nixos/tests/noto-fonts-cjk-qt-default-weight.nix2
-rw-r--r--nixpkgs/nixos/tests/noto-fonts.nix4
-rw-r--r--nixpkgs/nixos/tests/opentelemetry-collector.nix76
-rw-r--r--nixpkgs/nixos/tests/os-prober.nix4
-rw-r--r--nixpkgs/nixos/tests/osquery.nix52
-rw-r--r--nixpkgs/nixos/tests/paperless.nix14
-rw-r--r--nixpkgs/nixos/tests/pgbouncer.nix61
-rw-r--r--nixpkgs/nixos/tests/plasma-bigscreen.nix9
-rw-r--r--nixpkgs/nixos/tests/plasma5-systemd-start.nix8
-rw-r--r--nixpkgs/nixos/tests/plasma5.nix14
-rw-r--r--nixpkgs/nixos/tests/powerdns.nix8
-rw-r--r--nixpkgs/nixos/tests/prometheus-exporters.nix59
-rw-r--r--nixpkgs/nixos/tests/public-inbox.nix3
-rw-r--r--nixpkgs/nixos/tests/qemu-vm-volatile-root.nix17
-rw-r--r--nixpkgs/nixos/tests/qownnotes.nix70
-rw-r--r--nixpkgs/nixos/tests/retroarch.nix4
-rw-r--r--nixpkgs/nixos/tests/samba-wsdd.nix6
-rw-r--r--nixpkgs/nixos/tests/scaphandre.nix18
-rw-r--r--nixpkgs/nixos/tests/sddm.nix14
-rw-r--r--nixpkgs/nixos/tests/sftpgo.nix10
-rw-r--r--nixpkgs/nixos/tests/sing-box.nix48
-rw-r--r--nixpkgs/nixos/tests/snapper.nix2
-rw-r--r--nixpkgs/nixos/tests/sway.nix75
-rw-r--r--nixpkgs/nixos/tests/switch-test.nix47
-rw-r--r--nixpkgs/nixos/tests/syncthing-init.nix7
-rw-r--r--nixpkgs/nixos/tests/syncthing-no-settings.nix18
-rw-r--r--nixpkgs/nixos/tests/systemd-boot.nix3
-rw-r--r--nixpkgs/nixos/tests/systemd-initrd-luks-fido2.nix1
-rw-r--r--nixpkgs/nixos/tests/systemd-initrd-luks-keyfile.nix1
-rw-r--r--nixpkgs/nixos/tests/systemd-initrd-luks-password.nix1
-rw-r--r--nixpkgs/nixos/tests/systemd-initrd-luks-tpm2.nix1
-rw-r--r--nixpkgs/nixos/tests/systemd-initrd-networkd-ssh.nix18
-rw-r--r--nixpkgs/nixos/tests/systemd-initrd-swraid.nix12
-rw-r--r--nixpkgs/nixos/tests/systemd-initrd-vconsole.nix17
-rw-r--r--nixpkgs/nixos/tests/systemd-networkd-dhcpserver-static-leases.nix2
-rw-r--r--nixpkgs/nixos/tests/systemd-networkd-dhcpserver.nix57
-rw-r--r--nixpkgs/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix6
-rw-r--r--nixpkgs/nixos/tests/systemd-nspawn-configfile.nix128
-rw-r--r--nixpkgs/nixos/tests/systemd-nspawn.nix22
-rw-r--r--nixpkgs/nixos/tests/systemd-shutdown.nix2
-rw-r--r--nixpkgs/nixos/tests/systemd-sysupdate.nix66
-rw-r--r--nixpkgs/nixos/tests/terminal-emulators.nix7
-rw-r--r--nixpkgs/nixos/tests/tmate-ssh-server.nix1
-rw-r--r--nixpkgs/nixos/tests/twingate.nix14
-rw-r--r--nixpkgs/nixos/tests/typesense.nix23
-rw-r--r--nixpkgs/nixos/tests/virtualbox.nix2
-rw-r--r--nixpkgs/nixos/tests/vscode-remote-ssh.nix124
-rw-r--r--nixpkgs/nixos/tests/vscodium.nix2
-rw-r--r--nixpkgs/nixos/tests/web-apps/gotosocial.nix28
-rw-r--r--nixpkgs/nixos/tests/web-apps/peering-manager.nix2
-rw-r--r--nixpkgs/nixos/tests/web-servers/static-web-server.nix32
-rw-r--r--nixpkgs/nixos/tests/wpa_supplicant.nix228
440 files changed, 15871 insertions, 5682 deletions
diff --git a/nixpkgs/nixos/doc/manual/common.nix b/nixpkgs/nixos/doc/manual/common.nix
new file mode 100644
index 000000000000..48d1d909492d
--- /dev/null
+++ b/nixpkgs/nixos/doc/manual/common.nix
@@ -0,0 +1,4 @@
+{
+  outputPath = "share/doc/nixos";
+  indexPath = "index.html";
+}
diff --git a/nixpkgs/nixos/doc/manual/configuration/gpu-accel.chapter.md b/nixpkgs/nixos/doc/manual/configuration/gpu-accel.chapter.md
index aa41e25e56f3..40878b5da4b5 100644
--- a/nixpkgs/nixos/doc/manual/configuration/gpu-accel.chapter.md
+++ b/nixpkgs/nixos/doc/manual/configuration/gpu-accel.chapter.md
@@ -189,7 +189,7 @@ Older Intel GPUs use the i965 driver, which can be installed with:
 
 ```nix
 hardware.opengl.extraPackages = [
-  vaapiIntel
+  intel-vaapi-driver
 ];
 ```
 
diff --git a/nixpkgs/nixos/doc/manual/configuration/renaming-interfaces.section.md b/nixpkgs/nixos/doc/manual/configuration/renaming-interfaces.section.md
index 18390c959b24..5b515e9f82a0 100644
--- a/nixpkgs/nixos/doc/manual/configuration/renaming-interfaces.section.md
+++ b/nixpkgs/nixos/doc/manual/configuration/renaming-interfaces.section.md
@@ -37,7 +37,7 @@ even if networkd is disabled.
 Alternatively, we can use a plain old udev rule:
 
 ```nix
-services.udev.initrdRules = ''
+boot.initrd.services.udev.rules = ''
   SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", \
   ATTR{address}=="52:54:00:12:01:01", KERNEL=="eth*", NAME="wan"
 '';
@@ -45,7 +45,7 @@ services.udev.initrdRules = ''
 
 ::: {.warning}
 The rule must be installed in the initrd using
-`services.udev.initrdRules`, not the usual `services.udev.extraRules`
+`boot.initrd.services.udev.rules`, not the usual `services.udev.extraRules`
 option. This is to avoid race conditions with other programs controlling
 the interface.
 :::
diff --git a/nixpkgs/nixos/doc/manual/contributing-to-this-manual.chapter.md b/nixpkgs/nixos/doc/manual/contributing-to-this-manual.chapter.md
index c306cc084cdb..4633c7e1b058 100644
--- a/nixpkgs/nixos/doc/manual/contributing-to-this-manual.chapter.md
+++ b/nixpkgs/nixos/doc/manual/contributing-to-this-manual.chapter.md
@@ -11,6 +11,8 @@ $ nix-build nixos/release.nix -A manual.x86_64-linux
 
 If the build succeeds, the manual will be in `./result/share/doc/nixos/index.html`.
 
+There's also [a convenient development daemon](https://nixos.org/manual/nixpkgs/unstable/#sec-contributing-devmode).
+
 **Contributing to the man pages**
 
 The man pages are written in [DocBook] which is XML.
diff --git a/nixpkgs/nixos/doc/manual/default.nix b/nixpkgs/nixos/doc/manual/default.nix
index 68132f302e42..902dee701801 100644
--- a/nixpkgs/nixos/doc/manual/default.nix
+++ b/nixpkgs/nixos/doc/manual/default.nix
@@ -6,7 +6,6 @@
 , extraSources ? []
 , baseOptionsJSON ? null
 , warningsAreErrors ? true
-, allowDocBook ? true
 , prefix ? ../../..
 }:
 
@@ -17,9 +16,7 @@ let
 
   lib = pkgs.lib;
 
-  docbook_xsl_ns = pkgs.docbook-xsl-ns.override {
-    withManOptDedupPatch = true;
-  };
+  common = import ./common.nix;
 
   manpageUrls = pkgs.path + "/doc/manpage-urls.json";
 
@@ -33,7 +30,7 @@ let
   stripAnyPrefixes = lib.flip (lib.foldr lib.removePrefix) prefixesToStrip;
 
   optionsDoc = buildPackages.nixosOptionsDoc {
-    inherit options revision baseOptionsJSON warningsAreErrors allowDocBook;
+    inherit options revision baseOptionsJSON warningsAreErrors;
     transformOptions = opt: opt // {
       # Clean up declaration sites to not refer to the NixOS source tree.
       declarations = map stripAnyPrefixes opt.declarations;
@@ -68,72 +65,8 @@ let
       optionIdPrefix = "test-opt-";
     };
 
-  toc = builtins.toFile "toc.xml"
-    ''
-      <toc role="chunk-toc">
-        <d:tocentry xmlns:d="http://docbook.org/ns/docbook" linkend="book-nixos-manual"><?dbhtml filename="index.html"?>
-          <d:tocentry linkend="ch-options"><?dbhtml filename="options.html"?></d:tocentry>
-          <d:tocentry linkend="ch-release-notes"><?dbhtml filename="release-notes.html"?></d:tocentry>
-        </d:tocentry>
-      </toc>
-    '';
-
-  manualXsltprocOptions = toString [
-    "--param chapter.autolabel 0"
-    "--param part.autolabel 0"
-    "--param preface.autolabel 0"
-    "--param reference.autolabel 0"
-    "--param section.autolabel 0"
-    "--stringparam html.stylesheet 'style.css overrides.css highlightjs/mono-blue.css'"
-    "--stringparam html.script './highlightjs/highlight.pack.js ./highlightjs/loader.js'"
-    "--param xref.with.number.and.title 0"
-    "--param toc.section.depth 0"
-    "--param generate.consistent.ids 1"
-    "--stringparam admon.style ''"
-    "--stringparam callout.graphics.extension .svg"
-    "--stringparam current.docid manual"
-    "--param chunk.section.depth 0"
-    "--param chunk.first.sections 1"
-    "--param use.id.as.filename 1"
-    "--stringparam chunk.toc ${toc}"
-  ];
-
-  linterFunctions = ''
-    # outputs the context of an xmllint error output
-    # LEN lines around the failing line are printed
-    function context {
-      # length of context
-      local LEN=6
-      # lines to print before error line
-      local BEFORE=4
-
-      # xmllint output lines are:
-      # file.xml:1234: there was an error on line 1234
-      while IFS=':' read -r file line rest; do
-        echo
-        if [[ -n "$rest" ]]; then
-          echo "$file:$line:$rest"
-          local FROM=$(($line>$BEFORE ? $line - $BEFORE : 1))
-          # number lines & filter context
-          nl --body-numbering=a "$file" | sed -n "$FROM,+$LEN p"
-        else
-          if [[ -n "$line" ]]; then
-            echo "$file:$line"
-          else
-            echo "$file"
-          fi
-        fi
-      done
-    }
-
-    function lintrng {
-      xmllint --debug --noout --nonet \
-        --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \
-        "$1" \
-        2>&1 | context 1>&2
-        # ^ redirect assumes xmllint doesn’t print to stdout
-    }
-  '';
+  testDriverMachineDocstrings = pkgs.callPackage
+    ../../../nixos/lib/test-driver/nixos-test-driver-docstrings.nix {};
 
   prepareManualFromMD = ''
     cp -r --no-preserve=all $inputs/* .
@@ -147,112 +80,49 @@ let
     substituteInPlace ./nixos-options.md \
       --replace \
         '@NIXOS_OPTIONS_JSON@' \
-        ${optionsDoc.optionsJSON}/share/doc/nixos/options.json
+        ${optionsDoc.optionsJSON}/${common.outputPath}/options.json
     substituteInPlace ./development/writing-nixos-tests.section.md \
       --replace \
         '@NIXOS_TEST_OPTIONS_JSON@' \
-        ${testOptionsDoc.optionsJSON}/share/doc/nixos/options.json
+        ${testOptionsDoc.optionsJSON}/${common.outputPath}/options.json
+    sed -e '/@PYTHON_MACHINE_METHODS@/ {' -e 'r ${testDriverMachineDocstrings}/machine-methods.md' -e 'd' -e '}' \
+      -i ./development/writing-nixos-tests.section.md
   '';
 
-  manual-combined = runCommand "nixos-manual-combined"
-    { inputs = lib.sourceFilesBySuffices ./. [ ".xml" ".md" ];
-      nativeBuildInputs = [ pkgs.nixos-render-docs pkgs.libxml2.bin pkgs.libxslt.bin ];
-      meta.description = "The NixOS manual as plain docbook XML";
-    }
-    ''
-      ${prepareManualFromMD}
-
-      nixos-render-docs -j $NIX_BUILD_CORES manual docbook \
-        --manpage-urls ${manpageUrls} \
-        --revision ${lib.escapeShellArg revision} \
-        ./manual.md \
-        ./manual-combined-pre.xml
-
-      xsltproc \
-        -o manual-combined.xml ${./../../lib/make-options-doc/postprocess-option-descriptions.xsl} \
-        manual-combined-pre.xml
-
-      ${linterFunctions}
-
-      mkdir $out
-      cp manual-combined.xml $out/
-
-      lintrng $out/manual-combined.xml
-    '';
-
-  manpages-combined = runCommand "nixos-manpages-combined.xml"
-    { nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ];
-      meta.description = "The NixOS manpages as plain docbook XML";
-    }
-    ''
-      mkdir generated
-      cp -prd ${./man-pages.xml} man-pages.xml
-      ln -s ${optionsDoc.optionsDocBook} generated/options-db.xml
-
-      xmllint --xinclude --noxincludenode --output $out ./man-pages.xml
-
-      ${linterFunctions}
-
-      lintrng $out
-    '';
-
 in rec {
-  inherit (optionsDoc) optionsJSON optionsNix optionsDocBook optionsUsedDocbook;
+  inherit (optionsDoc) optionsJSON optionsNix optionsDocBook;
 
   # Generate the NixOS manual.
   manualHTML = runCommand "nixos-manual-html"
-    { nativeBuildInputs =
-        if allowDocBook then [
-          buildPackages.libxml2.bin
-          buildPackages.libxslt.bin
-        ] else [
-          buildPackages.nixos-render-docs
-        ];
-      inputs = lib.optionals (! allowDocBook) (lib.sourceFilesBySuffices ./. [ ".md" ]);
+    { nativeBuildInputs = [ buildPackages.nixos-render-docs ];
+      inputs = lib.sourceFilesBySuffices ./. [ ".md" ];
       meta.description = "The NixOS manual in HTML format";
       allowedReferences = ["out"];
     }
     ''
       # Generate the HTML manual.
-      dst=$out/share/doc/nixos
+      dst=$out/${common.outputPath}
       mkdir -p $dst
 
       cp ${../../../doc/style.css} $dst/style.css
       cp ${../../../doc/overrides.css} $dst/overrides.css
       cp -r ${pkgs.documentation-highlighter} $dst/highlightjs
 
-      ${if allowDocBook then ''
-          xsltproc \
-            ${manualXsltprocOptions} \
-            --stringparam id.warnings "1" \
-            --nonet --output $dst/ \
-            ${docbook_xsl_ns}/xml/xsl/docbook/xhtml/chunktoc.xsl \
-            ${manual-combined}/manual-combined.xml \
-            |& tee xsltproc.out
-          grep "^ID recommended on" xsltproc.out &>/dev/null && echo "error: some IDs are missing" && false
-          rm xsltproc.out
-
-          mkdir -p $dst/images/callouts
-          cp ${docbook_xsl_ns}/xml/xsl/docbook/images/callouts/*.svg $dst/images/callouts/
-        '' else ''
-          ${prepareManualFromMD}
+      ${prepareManualFromMD}
 
-          # TODO generator is set like this because the docbook/md manual compare workflow will
-          # trigger if it's different
-          nixos-render-docs -j $NIX_BUILD_CORES manual html \
-            --manpage-urls ${manpageUrls} \
-            --revision ${lib.escapeShellArg revision} \
-            --generator "DocBook XSL Stylesheets V${docbook_xsl_ns.version}" \
-            --stylesheet style.css \
-            --stylesheet overrides.css \
-            --stylesheet highlightjs/mono-blue.css \
-            --script ./highlightjs/highlight.pack.js \
-            --script ./highlightjs/loader.js \
-            --toc-depth 1 \
-            --chunk-toc-depth 1 \
-            ./manual.md \
-            $dst/index.html
-        ''}
+      nixos-render-docs -j $NIX_BUILD_CORES manual html \
+        --manpage-urls ${manpageUrls} \
+        --revision ${lib.escapeShellArg revision} \
+        --generator "nixos-render-docs ${lib.version}" \
+        --stylesheet style.css \
+        --stylesheet overrides.css \
+        --stylesheet highlightjs/mono-blue.css \
+        --script ./highlightjs/highlight.pack.js \
+        --script ./highlightjs/loader.js \
+        --toc-depth 1 \
+        --chunk-toc-depth 1 \
+        ./manual.md \
+        $dst/${common.indexPath}
 
       mkdir -p $out/nix-support
       echo "nix-build out $out" >> $out/nix-support/hydra-build-products
@@ -263,23 +133,45 @@ in rec {
   manual = manualHTML;
 
   # Index page of the NixOS manual.
-  manualHTMLIndex = "${manualHTML}/share/doc/nixos/index.html";
+  manualHTMLIndex = "${manualHTML}/${common.outputPath}/${common.indexPath}";
 
   manualEpub = runCommand "nixos-manual-epub"
     { nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin buildPackages.zip ];
+      doc = ''
+        <book xmlns="http://docbook.org/ns/docbook"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+              version="5.0"
+              xml:id="book-nixos-manual">
+          <info>
+            <title>NixOS Manual</title>
+            <subtitle>Version ${lib.version}</subtitle>
+          </info>
+          <chapter>
+            <title>Temporarily unavailable</title>
+            <para>
+              The NixOS manual is currently not available in EPUB format,
+              please use the <link xlink:href="https://nixos.org/nixos/manual">HTML manual</link>
+              instead.
+            </para>
+            <para>
+              If you've used the EPUB manual in the past and it has been useful to you, please
+              <link xlink:href="https://github.com/NixOS/nixpkgs/issues/237234">let us know</link>.
+            </para>
+          </chapter>
+        </book>
+      '';
+      passAsFile = [ "doc" ];
     }
     ''
       # Generate the epub manual.
-      dst=$out/share/doc/nixos
+      dst=$out/${common.outputPath}
 
       xsltproc \
-        ${manualXsltprocOptions} \
+        --param chapter.autolabel 0 \
         --nonet --xinclude --output $dst/epub/ \
         ${docbook_xsl_ns}/xml/xsl/docbook/epub/docbook.xsl \
-        ${manual-combined}/manual-combined.xml
+        $docPath
 
-      mkdir -p $dst/epub/OEBPS/images/callouts
-      cp -r ${docbook_xsl_ns}/xml/xsl/docbook/images/callouts/*.svg $dst/epub/OEBPS/images/callouts # */
       echo "application/epub+zip" > mimetype
       manual="$dst/nixos-manual.epub"
       zip -0Xq "$manual" mimetype
@@ -296,10 +188,6 @@ in rec {
   manpages = runCommand "nixos-manpages"
     { nativeBuildInputs = [
         buildPackages.installShellFiles
-      ] ++ lib.optionals allowDocBook [
-        buildPackages.libxml2.bin
-        buildPackages.libxslt.bin
-      ] ++ lib.optionals (! allowDocBook) [
         buildPackages.nixos-render-docs
       ];
       allowedReferences = ["out"];
@@ -308,24 +196,11 @@ in rec {
       # Generate manpages.
       mkdir -p $out/share/man/man8
       installManPage ${./manpages}/*
-      ${if allowDocBook
-        then ''
-          xsltproc --nonet \
-            --maxdepth 6000 \
-            --param man.output.in.separate.dir 1 \
-            --param man.output.base.dir "'$out/share/man/'" \
-            --param man.endnotes.are.numbered 0 \
-            --param man.break.after.slash 1 \
-            ${docbook_xsl_ns}/xml/xsl/docbook/manpages/docbook.xsl \
-            ${manpages-combined}
-        ''
-        else ''
-          mkdir -p $out/share/man/man5
-          nixos-render-docs -j $NIX_BUILD_CORES options manpage \
-            --revision ${lib.escapeShellArg revision} \
-            ${optionsJSON}/share/doc/nixos/options.json \
-            $out/share/man/man5/configuration.nix.5
-        ''}
+      mkdir -p $out/share/man/man5
+      nixos-render-docs -j $NIX_BUILD_CORES options manpage \
+        --revision ${lib.escapeShellArg revision} \
+        ${optionsJSON}/${common.outputPath}/options.json \
+        $out/share/man/man5/configuration.nix.5
     '';
 
 }
diff --git a/nixpkgs/nixos/doc/manual/development/option-types.section.md b/nixpkgs/nixos/doc/manual/development/option-types.section.md
index 9e156ebff9d3..44bb3b4782e1 100644
--- a/nixpkgs/nixos/doc/manual/development/option-types.section.md
+++ b/nixpkgs/nixos/doc/manual/development/option-types.section.md
@@ -20,6 +20,11 @@ merging is handled.
     coerced to a string. Even if derivations can be considered as
     paths, the more specific `types.package` should be preferred.
 
+`types.pathInStore`
+
+:   A path that is contained in the Nix store. This can be a top-level store
+    path like `pkgs.hello` or a descendant like `"${pkgs.hello}/bin/hello"`.
+
 `types.package`
 
 :   A top-level store path. This can be an attribute set pointing
diff --git a/nixpkgs/nixos/doc/manual/development/writing-nixos-tests.section.md b/nixpkgs/nixos/doc/manual/development/writing-nixos-tests.section.md
index 486a4b64a262..84b247fd2042 100644
--- a/nixpkgs/nixos/doc/manual/development/writing-nixos-tests.section.md
+++ b/nixpkgs/nixos/doc/manual/development/writing-nixos-tests.section.md
@@ -139,210 +139,7 @@ to Python as `machine_a`.
 
 The following methods are available on machine objects:
 
-`start`
-
-:   Start the virtual machine. This method is asynchronous --- it does
-    not wait for the machine to finish booting.
-
-`shutdown`
-
-:   Shut down the machine, waiting for the VM to exit.
-
-`crash`
-
-:   Simulate a sudden power failure, by telling the VM to exit
-    immediately.
-
-`block`
-
-:   Simulate unplugging the Ethernet cable that connects the machine to
-    the other machines.
-
-`unblock`
-
-:   Undo the effect of `block`.
-
-`screenshot`
-
-:   Take a picture of the display of the virtual machine, in PNG format.
-    The screenshot is linked from the HTML log.
-
-`get_screen_text_variants`
-
-:   Return a list of different interpretations of what is currently
-    visible on the machine's screen using optical character
-    recognition. The number and order of the interpretations is not
-    specified and is subject to change, but if no exception is raised at
-    least one will be returned.
-
-    ::: {.note}
-    This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`.
-    :::
-
-`get_screen_text`
-
-:   Return a textual representation of what is currently visible on the
-    machine's screen using optical character recognition.
-
-    ::: {.note}
-    This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`.
-    :::
-
-`send_monitor_command`
-
-:   Send a command to the QEMU monitor. This is rarely used, but allows
-    doing stuff such as attaching virtual USB disks to a running
-    machine.
-
-`send_key`
-
-:   Simulate pressing keys on the virtual keyboard, e.g.,
-    `send_key("ctrl-alt-delete")`.
-
-`send_chars`
-
-:   Simulate typing a sequence of characters on the virtual keyboard,
-    e.g., `send_chars("foobar\n")` will type the string `foobar`
-    followed by the Enter key.
-
-`send_console`
-
-:   Send keys to the kernel console. This allows interaction with the systemd
-    emergency mode, for example. Takes a string that is sent, e.g.,
-    `send_console("\n\nsystemctl default\n")`.
-
-`execute`
-
-:   Execute a shell command, returning a list `(status, stdout)`.
-
-    Commands are run with `set -euo pipefail` set:
-
-    -   If several commands are separated by `;` and one fails, the
-        command as a whole will fail.
-
-    -   For pipelines, the last non-zero exit status will be returned
-        (if there is one; otherwise zero will be returned).
-
-    -   Dereferencing unset variables fails the command.
-
-    -   It will wait for stdout to be closed.
-
-    If the command detaches, it must close stdout, as `execute` will wait
-    for this to consume all output reliably. This can be achieved by
-    redirecting stdout to stderr `>&2`, to `/dev/console`, `/dev/null` or
-    a file. Examples of detaching commands are `sleep 365d &`, where the
-    shell forks a new process that can write to stdout and `xclip -i`, where
-    the `xclip` command itself forks without closing stdout.
-
-    Takes an optional parameter `check_return` that defaults to `True`.
-    Setting this parameter to `False` will not check for the return code
-    and return -1 instead. This can be used for commands that shut down
-    the VM and would therefore break the pipe that would be used for
-    retrieving the return code.
-
-    A timeout for the command can be specified (in seconds) using the optional
-    `timeout` parameter, e.g., `execute(cmd, timeout=10)` or
-    `execute(cmd, timeout=None)`. The default is 900 seconds.
-
-`succeed`
-
-:   Execute a shell command, raising an exception if the exit status is
-    not zero, otherwise returning the standard output. Similar to `execute`,
-    except that the timeout is `None` by default. See `execute` for details on
-    command execution.
-
-`fail`
-
-:   Like `succeed`, but raising an exception if the command returns a zero
-    status.
-
-`wait_until_succeeds`
-
-:   Repeat a shell command with 1-second intervals until it succeeds.
-    Has a default timeout of 900 seconds which can be modified, e.g.
-    `wait_until_succeeds(cmd, timeout=10)`. See `execute` for details on
-    command execution.
-
-`wait_until_fails`
-
-:   Like `wait_until_succeeds`, but repeating the command until it fails.
-
-`wait_for_unit`
-
-:   Wait until the specified systemd unit has reached the "active"
-    state.
-
-`wait_for_file`
-
-:   Wait until the specified file exists.
-
-`wait_for_open_port`
-
-:   Wait until a process is listening on the given TCP port and IP address
-    (default `localhost`).
-
-`wait_for_closed_port`
-
-:   Wait until nobody is listening on the given TCP port and IP address
-    (default `localhost`).
-
-`wait_for_x`
-
-:   Wait until the X11 server is accepting connections.
-
-`wait_for_text`
-
-:   Wait until the supplied regular expressions matches the textual
-    contents of the screen by using optical character recognition (see
-    `get_screen_text` and `get_screen_text_variants`).
-
-    ::: {.note}
-    This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`.
-    :::
-
-`wait_for_console_text`
-
-:   Wait until the supplied regular expressions match a line of the
-    serial console output. This method is useful when OCR is not
-    possible or accurate enough.
-
-`wait_for_window`
-
-:   Wait until an X11 window has appeared whose name matches the given
-    regular expression, e.g., `wait_for_window("Terminal")`.
-
-`copy_from_host`
-
-:   Copies a file from host to machine, e.g.,
-    `copy_from_host("myfile", "/etc/my/important/file")`.
-
-    The first argument is the file on the host. The file needs to be
-    accessible while building the nix derivation. The second argument is
-    the location of the file on the machine.
-
-`systemctl`
-
-:   Runs `systemctl` commands with optional support for
-    `systemctl --user`
-
-    ```py
-    machine.systemctl("list-jobs --no-pager") # runs `systemctl list-jobs --no-pager`
-    machine.systemctl("list-jobs --no-pager", "any-user") # spawns a shell for `any-user` and runs `systemctl --user list-jobs --no-pager`
-    ```
-
-`shell_interact`
-
-:   Allows you to directly interact with the guest shell. This should
-    only be used during test development, not in production tests.
-    Killing the interactive session with `Ctrl-d` or `Ctrl-c` also ends
-    the guest session.
-
-`console_interact`
-
-:   Allows you to directly interact with QEMU's stdin. This should
-    only be used during test development, not in production tests.
-    Output from QEMU is only read line-wise. `Ctrl-c` kills QEMU and
-    `Ctrl-d` closes console and returns to the test runner.
+@PYTHON_MACHINE_METHODS@
 
 To test user units declared by `systemd.user.services` the optional
 `user` argument can be used:
diff --git a/nixpkgs/nixos/doc/manual/installation/installing.chapter.md b/nixpkgs/nixos/doc/manual/installation/installing.chapter.md
index 53cf9ed14c33..815bcc071cd9 100644
--- a/nixpkgs/nixos/doc/manual/installation/installing.chapter.md
+++ b/nixpkgs/nixos/doc/manual/installation/installing.chapter.md
@@ -249,14 +249,14 @@ update /etc/fstab.
     which will be used by the boot partition.
 
     ```ShellSession
-    # parted /dev/sda -- mkpart primary 512MB -8GB
+    # parted /dev/sda -- mkpart root ext4 512MB -8GB
     ```
 
 3.  Next, add a *swap* partition. The size required will vary according
     to needs, here a 8GB one is created.
 
     ```ShellSession
-    # parted /dev/sda -- mkpart primary linux-swap -8GB 100%
+    # parted /dev/sda -- mkpart swap linux-swap -8GB 100%
     ```
 
     ::: {.note}
@@ -550,8 +550,8 @@ corresponding configuration Nix expression.
 ### Example partition schemes for NixOS on `/dev/sda` (UEFI)
 ```ShellSession
 # parted /dev/sda -- mklabel gpt
-# parted /dev/sda -- mkpart primary 512MB -8GB
-# parted /dev/sda -- mkpart primary linux-swap -8GB 100%
+# parted /dev/sda -- mkpart root ext4 512MB -8GB
+# parted /dev/sda -- mkpart swap linux-swap -8GB 100%
 # parted /dev/sda -- mkpart ESP fat32 1MB 512MB
 # parted /dev/sda -- set 3 esp on
 ```
diff --git a/nixpkgs/nixos/doc/manual/man-pages.xml b/nixpkgs/nixos/doc/manual/man-pages.xml
deleted file mode 100644
index 52183f1f9ee0..000000000000
--- a/nixpkgs/nixos/doc/manual/man-pages.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<reference xmlns="http://docbook.org/ns/docbook"
-           xmlns:xlink="http://www.w3.org/1999/xlink"
-           xmlns:xi="http://www.w3.org/2001/XInclude">
- <title>NixOS Reference Pages</title>
- <info>
-  <author>
-   <personname><firstname>Eelco</firstname><surname>Dolstra</surname></personname>
-   <contrib>Author</contrib>
-  </author>
-  <author>
-   <personname><othername>The Nixpkgs/NixOS contributors</othername></personname>
-   <contrib>Author</contrib>
-  </author>
-  <copyright><year>2007-2022</year><holder>Eelco Dolstra and the Nixpkgs/NixOS contributors</holder>
-  </copyright>
- </info>
- <refentry>
-  <refmeta>
-   <refentrytitle><filename>configuration.nix</filename>
-   </refentrytitle><manvolnum>5</manvolnum>
-   <refmiscinfo class="source">NixOS</refmiscinfo>
- <!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
-  </refmeta>
-  <refnamediv>
-   <refname><filename>configuration.nix</filename></refname>
-   <refpurpose>NixOS system configuration specification</refpurpose>
-  </refnamediv>
-  <refsection>
-   <title>Description</title>
-   <para>
-    The file <filename>/etc/nixos/configuration.nix</filename> contains the
-    declarative specification of your NixOS system configuration. The command
-    <command>nixos-rebuild</command> takes this file and realises the system
-    configuration specified therein.
-   </para>
-  </refsection>
-  <refsection>
-   <title>Options</title>
-   <para>
-    You can use the following options in <filename>configuration.nix</filename>.
-   </para>
-   <xi:include href="./generated/options-db.xml"
-             xpointer="configuration-variable-list" />
-  </refsection>
- </refentry>
-</reference>
diff --git a/nixpkgs/nixos/doc/manual/release-notes/rl-2111.section.md b/nixpkgs/nixos/doc/manual/release-notes/rl-2111.section.md
index 159881a0ac4c..400eb1062d9a 100644
--- a/nixpkgs/nixos/doc/manual/release-notes/rl-2111.section.md
+++ b/nixpkgs/nixos/doc/manual/release-notes/rl-2111.section.md
@@ -441,6 +441,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - `pkgs.haskell-language-server` will now by default be linked dynamically to improve TemplateHaskell compatibility. To mitigate the increased closure size it will now by default only support our current default ghc (at the moment 9.0.2). Add other ghc versions via e.g. `pkgs.haskell-language-server.override { supportedGhcVersions = [ "90" "92" ]; }`.
 
+- `pkgs.redis` is now built using the system jemalloc. This disables the experimental active defragmentation feature of redis. Users who require this feature can switch back to redis' vendored version of jemalloc by setting `services.redis.package = pkgs.redis.override { useSystemJemalloc = false; };`.
+
 ## Other Notable Changes {#sec-release-21.11-notable-changes}
 
 
diff --git a/nixpkgs/nixos/doc/manual/release-notes/rl-2305.section.md b/nixpkgs/nixos/doc/manual/release-notes/rl-2305.section.md
index cca1d48ec564..c9da29063e1a 100644
--- a/nixpkgs/nixos/doc/manual/release-notes/rl-2305.section.md
+++ b/nixpkgs/nixos/doc/manual/release-notes/rl-2305.section.md
@@ -83,6 +83,8 @@ In addition to numerous new and updated packages, this release has the following
 
 - [gitea-actions-runner](https://gitea.com/gitea/act_runner), a CI runner for Gitea/Forgejo Actions. Available as [services.gitea-actions-runner](#opt-services.gitea-actions-runner.instances).
 
+- [evdevremapkeys](https://github.com/philipl/evdevremapkeys), a daemon to remap key events. Available as [services.evdevremapkeys](#opt-services.evdevremapkeys.enable).
+
 - [gmediarender](https://github.com/hzeller/gmrender-resurrect), a simple, headless UPnP/DLNA renderer.  Available as [services.gmediarender](options.html#opt-services.gmediarender.enable).
 
 - [go2rtc](https://github.com/AlexxIT/go2rtc), a camera streaming appliation with support for RTSP, WebRTC, HomeKit, FFMPEG, RTMP and other protocols. Available as [services.go2rtc](options.html#opt-services.go2rtc.enable).
@@ -192,7 +194,7 @@ In addition to numerous new and updated packages, this release has the following
     "hmac-sha2-512"
     "hmac-sha2-256"
     "umac-128@openssh.com"
-  };
+  ];
   ```
 
 - `podman` now uses the `netavark` network stack. Users will need to delete all of their local containers, images, volumes, etc, by running `podman system reset --force` once before upgrading their systems.
@@ -201,7 +203,7 @@ In addition to numerous new and updated packages, this release has the following
 
 - `graylog` has been updated to version 5, which can not be updated directly from the previously packaged version 3.3. If you had installed the previously packaged version 3.3, please follow the [upgrade path](https://go2docs.graylog.org/5-0/upgrading_graylog/upgrade_path.htm) from 3.3 to 4.0 to 4.3 to 5.0.
 
-- `buildFHSUserEnv` is now called `buildFHSEnv` and uses FlatPak's Bubblewrap sandboxing tool rather than Nixpkgs' own chrootenv. The old chrootenv-based implemenation is still available via `buildFHSEnvChrootenv` but is considered deprecated and will be removed when the remaining uses inside Nixpkgs have been migrated. If your FHSEnv-wrapped application misbehaves when using the new bubblewrap implementation, please create an issue in Nixpkgs.
+- `buildFHSUserEnv` is now called `buildFHSEnv` and uses FlatPak's Bubblewrap sandboxing tool rather than Nixpkgs' own chrootenv. The old chrootenv-based implemenation is still available via `buildFHSEnvChroot` but is considered deprecated and will be removed when the remaining uses inside Nixpkgs have been migrated. If your FHSEnv-wrapped application misbehaves when using the new bubblewrap implementation, please create an issue in Nixpkgs.
 
 - `nushell` has been updated to at least version 0.77.0, which includes potential breaking changes in aliases. The old aliases are now available as `old-alias` but it is recommended you migrate to the new format. See [Reworked aliases](https://www.nushell.sh/blog/2023-03-14-nushell_0_77.html#reworked-aliases-breaking-changes-kubouch).
 
@@ -316,7 +318,7 @@ In addition to numerous new and updated packages, this release has the following
 
 - The ppp plugin `rp-pppoe.so` has been renamed to `pppoe.so` in ppp 2.4.9. Starting from ppp 2.5.0, there is no longer an alias for backwards compatibility. Configurations that use this plugin must be updated accordingly from `plugin rp-pppoe.so` to `plugin pppoe.so`. See [upstream change](https://github.com/ppp-project/ppp/commit/610a7bd76eb1f99f22317541b35001b1e24877ed).
 
-- [services.xserver.videoDrivers](options.html#opt-services.xserver.videoDrivers) now defaults to the `modesetting` driver over device-specific ones. The `radeon`, `amdgpu` and `nouveau` drivers are still available, but effectively unmaintained and not recommended for use.
+- [services.xserver.videoDrivers](options.html#opt-services.xserver.videoDrivers) now defaults to the `modesetting` driver over device-specific ones. The `radeon`, `amdgpu` and `nouveau` drivers are still available, but effectively unmaintained and not recommended for use. Note that this __does not__ affect your regular graphics drivers; this only concerns the DDX component of the driver, which most people are not relying on.
 
 - [services.xserver.libinput.enable](options.html#opt-services.xserver.libinput.enable) is now set by default, enabling the more actively maintained and consistently behaved input device driver.
 
@@ -553,7 +555,7 @@ In addition to numerous new and updated packages, this release has the following
 
 - `buildDunePackage` now defaults to `strictDeps = true` which means that any library should go into `buildInputs` or `checkInputs`. Any executable that is run on the building machine should go into `nativeBuildInputs` or `nativeCheckInputs` respectively. Example of executables are `ocaml`, `findlib` and `menhir`. PPXs are libraries which are built by dune and should therefore not go into `nativeBuildInputs`.
 
-- `buildFHSUserEnv` is now called `buildFHSEnv` and uses FlatPak's Bubblewrap sandboxing tool rather than Nixpkgs' own chrootenv. The old chrootenv-based implemenation is still available via `buildFHSEnvChrootenv` but is considered deprecated and will be removed when the remaining uses inside Nixpkgs have been migrated. If your FHSEnv-wrapped application misbehaves when using the new bubblewrap implementation, please create an issue in Nixpkgs.
+- `buildFHSUserEnv` is now called `buildFHSEnv` and uses FlatPak's Bubblewrap sandboxing tool rather than Nixpkgs' own chrootenv. The old chrootenv-based implemenation is still available via `buildFHSEnvChroot` but is considered deprecated and will be removed when the remaining uses inside Nixpkgs have been migrated. If your FHSEnv-wrapped application misbehaves when using the new bubblewrap implementation, please create an issue in Nixpkgs.
 
 - Top-level `buildPlatform`, `hostPlatform`, `targetPlatform` have been deprecated, use `stdenv.X` instead.
 
diff --git a/nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md b/nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md
index bc10f5b587c7..c69e8b4317ce 100644
--- a/nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md
+++ b/nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md
@@ -4,9 +4,11 @@
 
 - FoundationDB now defaults to major version 7.
 
+- Support for WiFi6 (IEEE 802.11ax) and WPA3-SAE-PK was enabled in the `hostapd` package, along with a significant rework of the hostapd module.
+
 ## New Services {#sec-release-23.11-new-services}
 
-- Create the first release note entry in this section!
+- [MCHPRS](https://github.com/MCHPR/MCHPRS), a multithreaded Minecraft server built for redstone. Available as [services.mchprs](#opt-services.mchprs.enable).
 
 - [acme-dns](https://github.com/joohoi/acme-dns), a limited DNS server to handle ACME DNS challenges easily and securely. Available as [services.acme-dns](#opt-services.acme-dns.enable).
 
@@ -14,32 +16,180 @@
 
 - [river](https://github.com/riverwm/river), A dynamic tiling wayland compositor. Available as [programs.river](#opt-programs.river.enable).
 
+- [wayfire](https://wayfire.org), A modular and extensible wayland compositor. Available as [programs.wayfire](#opt-programs.wayfire.enable).
+
+- [mautrix-whatsapp](https://docs.mau.fi/bridges/go/whatsapp/index.html) A Matrix-WhatsApp puppeting bridge
+
+- [GoToSocial](https://gotosocial.org/), an ActivityPub social network server, written in Golang. Available as [services.gotosocial](#opt-services.gotosocial.enable).
+
+- [Typesense](https://github.com/typesense/typesense), a fast, typo-tolerant search engine for building delightful search experiences. Available as [services.typesense](#opt-services.typesense.enable).
+
+* [NS-USBLoader](https://github.com/developersu/ns-usbloader/), an all-in-one tool for managing Nintendo Switch homebrew. Available as [programs.ns-usbloader](#opt-programs.ns-usbloader.enable).
+
+- [Anuko Time Tracker](https://github.com/anuko/timetracker), a simple, easy to use, open source time tracking system. Available as [services.anuko-time-tracker](#opt-services.anuko-time-tracker.enable).
+
 - [sitespeed-io](https://sitespeed.io), a tool that can generate metrics (timings, diagnostics) for websites. Available as [services.sitespeed-io](#opt-services.sitespeed-io.enable).
 
+- [Apache Guacamole](https://guacamole.apache.org/), a cross-platform, clientless remote desktop gateway. Available as [services.guacamole-server](#opt-services.guacamole-server.enable) and [services.guacamole-client](#opt-services.guacamole-client.enable) services.
+
+- [pgBouncer](https://www.pgbouncer.org), a PostgreSQL connection pooler. Available as [services.pgbouncer](#opt-services.pgbouncer.enable).
+
+- [trust-dns](https://trust-dns.org/), a Rust based DNS server built to be safe and secure from the ground up. Available as [services.trust-dns](#opt-services.trust-dns.enable).
+
+- [osquery](https://www.osquery.io/), a SQL powered operating system instrumentation, monitoring, and analytics.
+
+- [ebusd](https://ebusd.eu), a daemon for handling communication with eBUS devices connected to a 2-wire bus system (“energy bus” used by numerous heating systems). Available as [services.ebusd](#opt-services.ebusd.enable).
+
+- [systemd-sysupdate](https://www.freedesktop.org/software/systemd/man/systemd-sysupdate.html), atomically updates the host OS, container images, portable service images or other sources. Available as [systemd.sysupdate](opt-systemd.sysupdate).
+
 ## Backward Incompatibilities {#sec-release-23.11-incompatibilities}
 
+- The `boot.loader.raspberryPi` options have been marked deprecated, with intent for removal for NixOS 24.11. They had a limited use-case, and do not work like people expect. They required either very old installs ([before mid-2019](https://github.com/NixOS/nixpkgs/pull/62462)) or customized builds out of scope of the standard and generic AArch64 support. That option set never supported the Raspberry Pi 4 family of devices.
+
+- `python3.pkgs.sequoia` was removed in favor of `python3.pkgs.pysequoia`. The latter package is based on upstream's dedicated repository for sequoia's Python bindings, where the Python bindings from [gitlab:sequoia-pgp/sequoia](https://gitlab.com/sequoia-pgp/sequoia) were removed long ago.
+
 - `writeTextFile` now requires `executable` to be boolean, values like `null` or `""` will now fail to evaluate.
 
 - The latest version of `clonehero` now stores custom content in `~/.clonehero`. See the [migration instructions](https://clonehero.net/2022/11/29/v23-to-v1-migration-instructions.html). Typically, these content files would exist along side the binary, but the previous build used a wrapper script that would store them in `~/.config/unity3d/srylain Inc_/Clone Hero`.
 
+- The `services.hostapd` module was rewritten to support `passwordFile` like options, WPA3-SAE, and management of multiple interfaces. This breaks compatibility with older configurations.
+  - `hostapd` is now started with additional systemd sandbox/hardening options for better security.
+  - `services.hostapd.interface` was replaced with a per-radio and per-bss configuration scheme using [services.hostapd.radios](#opt-services.hostapd.radios).
+  - `services.hostapd.wpa` has been replaced by [services.hostapd.radios.&lt;name&gt;.networks.&lt;name&gt;.authentication.wpaPassword](#opt-services.hostapd.radios._name_.networks._name_.authentication.wpaPassword) and [services.hostapd.radios.&lt;name&gt;.networks.&lt;name&gt;.authentication.saePasswords](#opt-services.hostapd.radios._name_.networks._name_.authentication.saePasswords) which configure WPA2-PSK and WP3-SAE respectively.
+  - The default authentication has been changed to WPA3-SAE. Options for other (legacy) schemes are still available.
+
 - `python3.pkgs.fetchPypi` (and `python3Packages.fetchPypi`) has been deprecated in favor of top-level `fetchPypi`.
 
 - `mariadb` now defaults to `mariadb_1011` instead of `mariadb_106`, meaning the default version was upgraded from 10.6.x to 10.11.x. See the [upgrade notes](https://mariadb.com/kb/en/upgrading-from-mariadb-10-6-to-mariadb-10-11/) for potential issues.
 
+- `getent` has been moved from `glibc`'s `bin` output to its own dedicated output, reducing closure size for many dependents. Dependents using the `getent` alias should not be affected; others should move from using `glibc.bin` or `getBin glibc` to `getent` (which also improves compatibility with non-glibc platforms).
+
+- The `services.ananicy.extraRules` option now has the type of `listOf attrs` instead of `string`.
+
 - `etcd` has been updated to 3.5, you will want to read the [3.3 to 3.4](https://etcd.io/docs/v3.5/upgrades/upgrade_3_4/) and [3.4 to 3.5](https://etcd.io/docs/v3.5/upgrades/upgrade_3_5/) upgrade guides
 
+- `consul` has been updated to `1.16.0`. See the [release note](https://github.com/hashicorp/consul/releases/tag/v1.16.0) for more details. Once a new Consul version has started and upgraded its data directory, it generally cannot be downgraded to the previous version.
+
 - `himalaya` has been updated to `0.8.0`, which drops the native TLS support (in favor of Rustls) and add OAuth 2.0 support. See the [release note](https://github.com/soywod/himalaya/releases/tag/v0.8.0) for more details.
 
+- The [services.caddy.acmeCA](#opt-services.caddy.acmeCA) option now defaults to `null` instead of `"https://acme-v02.api.letsencrypt.org/directory"`, to use all of Caddy's default ACME CAs and enable Caddy's automatic issuer fallback feature by default, as recommended by upstream.
+
+- The default priorities of [`services.nextcloud.phpOptions`](#opt-services.nextcloud.phpOptions) have changed. This means that e.g.
+  `services.nextcloud.phpOptions."opcache.interned_strings_buffer" = "23";` doesn't discard all of the other defaults from this option
+  anymore. The attribute values of `phpOptions` are still defaults, these can be overridden as shown here.
+
+  To override all of the options (including including `upload_max_filesize`, `post_max_size`
+  and `memory_limit` which all point to [`services.nextcloud.maxUploadSize`](#opt-services.nextcloud.maxUploadSize)
+  by default) can be done like this:
+
+  ```nix
+  {
+    services.nextcloud.phpOptions = lib.mkForce {
+      /* ... */
+    };
+  }
+  ```
+
+- `php80` is no longer supported due to upstream not supporting this version anymore.
+
+- PHP now defaults to PHP 8.2, updated from 8.1.
+
+- The ISC DHCP package and corresponding module have been removed, because they are end of life upstream. See https://www.isc.org/blogs/isc-dhcp-eol/ for details and switch to a different DHCP implementation like kea or dnsmasq.
+
 - `util-linux` is now supported on Darwin and is no longer an alias to `unixtools`. Use the `unixtools.util-linux` package for access to the Apple variants of the utilities.
 
+- `services.keyd` changed API. Now you can create multiple configuration files.
+
+- `services.ddclient` has been removed on the request of the upstream maintainer because it is unmaintained and has bugs. Please switch to a different software like `inadyn` or `knsupdate`.
+
+- The `vlock` program from the `kbd` package has been moved into its own package output and should now be referenced explicitly as `kbd.vlock` or replaced with an alternative such as the standalone `vlock` package or `physlock`.
+
 - `fileSystems.<name>.autoFormat` now uses `systemd-makefs`, which does not accept formatting options. Therefore, `fileSystems.<name>.formatOptions` has been removed.
 
 - `fileSystems.<name>.autoResize` now uses `systemd-growfs` to resize the file system online in stage 2. This means that `f2fs` and `ext2` can no longer be auto resized, while `xfs` and `btrfs` now can be.
 
+- The `services.vaultwarden.config` option default value was changed to make Vaultwarden only listen on localhost, following the [secure defaults for most NixOS services](https://github.com/NixOS/nixpkgs/issues/100192).
+
+- `services.lemmy.settings.federation` was removed in 0.17.0 and no longer has any effect. To enable federation, the hostname must be set in the configuration file and then federation must be enabled in the admin web UI. See the [release notes](https://github.com/LemmyNet/lemmy/blob/c32585b03429f0f76d1e4ff738786321a0a9df98/RELEASES.md#upgrade-instructions) for more details.
+
+- `pict-rs` was upgraded from 0.3 to 0.4 and contains an incompatible database & configuration change. To upgrade on systems with `stateVersion = "23.05";` or older follow the migration steps from https://git.asonix.dog/asonix/pict-rs#user-content-0-3-to-0-4-migration-guide and set `services.pict-rs.package = pkgs.pict-rs;`.
+
+- The following packages in `haskellPackages` have now a separate bin output: `cabal-fmt`, `calligraphy`, `eventlog2html`, `ghc-debug-brick`, `hindent`, `nixfmt`, `releaser`. This means you need to replace e.g. `"${pkgs.haskellPackages.nixfmt}/bin/nixfmt"` with `"${lib.getBin pkgs.haskellPackages.nixfmt}/bin/nixfmt"` or `"${lib.getExe pkgs.haskellPackages.nixfmt}"`. The binaries also won’t be in scope if you rely on them being installed e.g. via `ghcWithPackages`. `environment.packages` picks the `bin` output automatically, so for normal installation no intervention is required. Also, toplevel attributes like `pkgs.nixfmt` are not impacted negatively by this change.
+
+- `spamassassin` no longer supports the `Hashcash` module. The module needs to be removed from the `loadplugin` list if it was copied over from the default `initPreConf` option.
+
+- `services.outline.sequelizeArguments` has been removed, as `outline` no longer executes database migrations via the `sequelize` cli.
+
+- The binary of the package `cloud-sql-proxy` has changed from `cloud_sql_proxy` to `cloud-sql-proxy`.
+
+- The `woodpecker-*` CI packages have been updated to 1.0.0. This release is wildly incompatible with the 0.15.X versions that were previously packaged. Please read [upstream's documentation](https://woodpecker-ci.org/docs/next/migrations#100) to learn how to update your CI configurations.
+
+- The Caddy module gained a new option named `services.caddy.enableReload` which is enabled by default. It allows reloading the service instead of restarting it, if only a config file has changed. This option must be disabled if you have turned off the [Caddy admin API](https://caddyserver.com/docs/caddyfile/options#admin). If you keep this option enabled, you should consider setting [`grace_period`](https://caddyserver.com/docs/caddyfile/options#grace-period) to a non-infinite value to prevent Caddy from delaying the reload indefinitely.
+
+- mdraid support is now optional. This reduces initramfs size and prevents the potentially undesired automatic detection and activation of software RAID pools. It is disabled by default in new configurations (determined by `stateVersion`), but the appropriate settings will be generated by `nixos-generate-config` when installing to a software RAID device, so the standard installation procedure should be unaffected. If you have custom configs relying on mdraid, ensure that you use `stateVersion` correctly or set `boot.swraid.enable` manually.
+
+- The `go-ethereum` package has been updated to v1.12.0. This drops support for proof-of-work. Its GraphQL API now encodes all numeric values as hex strings and the GraphQL UI is updated to version 2.0. The default database has changed from `leveldb` to `pebble` but `leveldb` can be forced with the --db.engine=leveldb flag. The `checkpoint-admin` command was [removed along with trusted checkpoints](https://github.com/ethereum/go-ethereum/pull/27147).
+
+- The default `kops` version is now 1.27.0 and support for 1.24 and older has been dropped.
+
+- `pharo` has been updated to latest stable (PharoVM 10.0.5), which is compatible with the latest stable and oldstable images (Pharo 10 and 11). The VM in question is the 64bit Spur. The 32bit version has been dropped due to lack of maintenance. The Cog VM has been deleted because it is severily outdated. Finally, the `pharo-launcher` package has been deleted because it was not compatible with the newer VM, and due to lack of maintenance.
+
 ## Other Notable Changes {#sec-release-23.11-notable-changes}
 
 - The Cinnamon module now enables XDG desktop integration by default. If you are experiencing collisions related to xdg-desktop-portal-gtk you can safely remove `xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];` from your NixOS configuration.
 
+- `fontconfig` now defaults to using greyscale antialiasing instead of subpixel antialiasing because of a [recommendation from one of the downstreams](https://gitlab.freedesktop.org/fontconfig/fontconfig/-/issues/337). You can change this value by configuring [](#opt-fonts.fontconfig.subpixel.rgba) accordingly.
+
+- The latest available version of Nextcloud is v27 (available as `pkgs.nextcloud27`). 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 >=23.11, `pkgs.nextcloud27` will be installed by default.
+  - If [`system.stateVersion`](#opt-system.stateVersion) is >=23.05, `pkgs.nextcloud26` will be installed by default.
+  - Please note that an upgrade from v25 (or older) to v27 directly is not possible. Please upgrade to `nextcloud26` (or earlier) first. Nextcloud prohibits skipping major versions while upgrading. You can upgrade by declaring [`services.nextcloud.package = pkgs.nextcloud26;`](options.html#opt-services.nextcloud.package).
+
+- New options were added to `services.searx` for better SearXNG support, including options for the built-in rate limiter and bot protection and automatically configuring a local redis server.
+
 - A new option was added to the virtualisation module that enables specifying explicitly named network interfaces in QEMU VMs. The existing `virtualisation.vlans` is still supported for cases where the name of the network interface is irrelevant.
 
+- DocBook option documentation is no longer supported, all module documentation now uses markdown.
+
+- `buildGoModule` `go-modules` attrs have been renamed to `goModules`.
+
+- The `fonts.fonts` and `fonts.enableDefaultFonts` options have been renamed to `fonts.packages` and `fonts.enableDefaultPackages` respectively.
+
+- `services.fail2ban.jails` can now be configured with attribute sets defining settings and filters instead of lines. The stringed options `daemonConfig` and `extraSettings` have respectively been replaced by `daemonSettings` and `jails.DEFAULT.settings` which use attribute sets.
+
+- The module [services.ankisyncd](#opt-services.ankisyncd.package) has been switched to [anki-sync-server-rs](https://github.com/ankicommunity/anki-sync-server-rs) from the old python version, which was difficult to update, had not been updated in a while, and did not support recent versions of anki.
+Unfortunately all servers supporting new clients (newer version of anki-sync-server, anki's built in sync server and this new rust package) do not support the older sync protocol that was used in the old server, so such old clients will also need updating and in particular the anki package in nixpkgs is also being updated in this release.
+The module update takes care of the new config syntax and the data itself (user login and cards) are compatible, so users of the module will be able to just log in again after updating both client and server without any extra action.
+
 - `services.nginx` gained a `defaultListen` option at server-level with support for PROXY protocol listeners, also `proxyProtocol` is now exposed in `services.nginx.virtualHosts.<name>.listen` option. It is now possible to run PROXY listeners and non-PROXY listeners at a server-level, see [#213510](https://github.com/NixOS/nixpkgs/pull/213510/) for more details.
+
+- `services.prometheus.exporters` has a new exporter to monitor electrical power consumption based on PowercapRAPL sensor called [Scaphandre](https://github.com/hubblo-org/scaphandre), see [#239803](https://github.com/NixOS/nixpkgs/pull/239803) for more details.
+
+- The module `services.calibre-server` has new options to configure the `host`, `port`, `auth.enable`, `auth.mode` and `auth.userDb` path, see [#216497](https://github.com/NixOS/nixpkgs/pull/216497/) for more details.
+
+- `services.prometheus.exporters` has a new [exporter](https://github.com/hipages/php-fpm_exporter) to monitor PHP-FPM processes, see [#240394](https://github.com/NixOS/nixpkgs/pull/240394) for more details.
+
+- `programs.gnupg.agent.pinentryFlavor` is now set in `/etc/gnupg/gpg-agent.conf`, and will no longer take precedence over a `pinentry-program` set in `~/.gnupg/gpg-agent.conf`.
+
+- `wrapHelm` now exposes `passthru.pluginsDir` which can be passed to `helmfile`. For convenience, a top-level package `helmfile-wrapped` has been added, which inherits `passthru.pluginsDir` from `kubernetes-helm-wrapped`. See [#217768](https://github.com/NixOS/nixpkgs/issues/217768) for details.
+
+- `boot.initrd.network.udhcp.enable` allows control over dhcp during stage 1 regardless of what `networking.useDHCP` is set to.
+
+- Suricata was upgraded from 6.0 to 7.0 and no longer considers HTTP/2 support as experimental, see [upstream release notes](https://forum.suricata.io/t/suricata-7-0-0-released/3715) for more details.
+
+## Nixpkgs internals {#sec-release-23.11-nixpkgs-internals}
+
+- The use of `sourceRoot = "source";`, `sourceRoot = "source/subdir";`, and similar lines in package derivations using the default `unpackPhase` is deprecated as it requires `unpackPhase` to always produce a directory named "source". Use `sourceRoot = src.name`, `sourceRoot = "${src.name}/subdir";`, or `setSourceRoot = "sourceRoot=$(echo */subdir)";` or similar instead.
+
+- The `qemu-vm.nix` module by default now identifies block devices via
+  persistent names available in `/dev/disk/by-*`. Because the rootDevice is
+  identfied by its filesystem label, it needs to be formatted before the VM is
+  started. The functionality of automatically formatting the rootDevice in the
+  initrd is removed from the QEMU module. However, for tests that depend on
+  this functionality, a test utility for the scripted initrd is added
+  (`nixos/tests/common/auto-format-root-device.nix`). To use this in a NixOS
+  test, import the module, e.g. `imports = [
+  ./common/auto-format-root-device.nix ];` When you use the systemd initrd, you
+  can automatically format the root device by setting
+  `virtualisation.fileSystems."/".autoFormat = true;`.
diff --git a/nixpkgs/nixos/doc/manual/shell.nix b/nixpkgs/nixos/doc/manual/shell.nix
new file mode 100644
index 000000000000..70500a12b037
--- /dev/null
+++ b/nixpkgs/nixos/doc/manual/shell.nix
@@ -0,0 +1,20 @@
+let
+  pkgs = import ../../.. {
+    config = {};
+    overlays = [];
+  };
+
+  common = import ./common.nix;
+  inherit (common) outputPath indexPath;
+
+  web-devmode = import ../../../pkgs/tools/nix/web-devmode.nix {
+    inherit pkgs;
+    buildArgs = "../../release.nix -A manualHTML.${builtins.currentSystem}";
+    open = "/${outputPath}/${indexPath}";
+  };
+in
+  pkgs.mkShell {
+    packages = [
+      web-devmode
+    ];
+  }
diff --git a/nixpkgs/nixos/lib/eval-config.nix b/nixpkgs/nixos/lib/eval-config.nix
index 058ab7280ccc..81a5ea1750de 100644
--- a/nixpkgs/nixos/lib/eval-config.nix
+++ b/nixpkgs/nixos/lib/eval-config.nix
@@ -31,7 +31,7 @@ evalConfigArgs@
 , prefix ? []
 , lib ? import ../../lib
 , extraModules ? let e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH";
-                 in if e == "" then [] else [(import e)]
+                 in lib.optional (e != "") (import e)
 }:
 
 let pkgs_ = pkgs;
@@ -109,8 +109,10 @@ let
 
   nixosWithUserModules = noUserModules.extendModules { modules = allUserModules; };
 
+  withExtraArgs = nixosSystem: nixosSystem // {
+    inherit extraArgs;
+    inherit (nixosSystem._module.args) pkgs;
+    extendModules = args: withExtraArgs (nixosSystem.extendModules args);
+  };
 in
-withWarnings nixosWithUserModules // {
-  inherit extraArgs;
-  inherit (nixosWithUserModules._module.args) pkgs;
-}
+withWarnings (withExtraArgs nixosWithUserModules)
diff --git a/nixpkgs/nixos/lib/make-disk-image.nix b/nixpkgs/nixos/lib/make-disk-image.nix
index 33d834e36b44..e5d82f4de7c9 100644
--- a/nixpkgs/nixos/lib/make-disk-image.nix
+++ b/nixpkgs/nixos/lib/make-disk-image.nix
@@ -572,7 +572,8 @@ 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.device != "/dev/vda") ''
+        ${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}
         ''}
 
diff --git a/nixpkgs/nixos/lib/make-options-doc/default.nix b/nixpkgs/nixos/lib/make-options-doc/default.nix
index a2385582a014..99515b5b8276 100644
--- a/nixpkgs/nixos/lib/make-options-doc/default.nix
+++ b/nixpkgs/nixos/lib/make-options-doc/default.nix
@@ -39,12 +39,17 @@
 # allow docbook option docs if `true`. only markdown documentation is allowed when set to
 # `false`, and a different renderer may be used with different bugs and performance
 # characteristics but (hopefully) indistinguishable output.
-, allowDocBook ? true
+# deprecated since 23.11.
+# TODO remove in a while.
+, allowDocBook ? false
 # whether lib.mdDoc is required for descriptions to be read as markdown.
-# !!! when this is eventually flipped to true, `lib.doRename` should also default to emitting Markdown
-, markdownByDefault ? false
+# deprecated since 23.11.
+# TODO remove in a while.
+, markdownByDefault ? true
 }:
 
+assert markdownByDefault && ! allowDocBook;
+
 let
   rawOpts = lib.optionAttrSetToDocList options;
   transformedOpts = map transformOptions rawOpts;
@@ -134,41 +139,10 @@ in rec {
       TOUCH_IF_DB=$dst/.used-docbook \
       python ${./mergeJSON.py} \
         ${lib.optionalString warningsAreErrors "--warnings-are-errors"} \
-        ${if allowDocBook then "--warn-on-docbook" else "--error-on-docbook"} \
         $baseJSON $options \
         > $dst/options.json
 
-      brotli -9 < $dst/options.json > $dst/options.json.br
-
-      mkdir -p $out/nix-support
-      echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products
-      echo "file json-br $dst/options.json.br" >> $out/nix-support/hydra-build-products
-    '';
-
-  optionsUsedDocbook = pkgs.runCommand "options-used-docbook" {} ''
-    if [ -e ${optionsJSON}/share/doc/nixos/.used-docbook ]; then
-      echo 1
-    else
-      echo 0
-    fi >"$out"
-  '';
-
-  optionsDocBook = pkgs.runCommand "options-docbook.xml" {
-    nativeBuildInputs = [
-      pkgs.nixos-render-docs
-    ];
-  } ''
-    nixos-render-docs -j $NIX_BUILD_CORES options docbook \
-      --manpage-urls ${pkgs.path + "/doc/manpage-urls.json"} \
-      --revision ${lib.escapeShellArg revision} \
-      --document-type ${lib.escapeShellArg documentType} \
-      --varlist-id ${lib.escapeShellArg variablelistId} \
-      --id-prefix ${lib.escapeShellArg optionIdPrefix} \
-      ${lib.optionalString markdownByDefault "--markdown-by-default"} \
-      ${optionsJSON}/share/doc/nixos/options.json \
-      options.xml
-
-    if grep /nixpkgs/nixos/modules options.xml; then
+    if grep /nixpkgs/nixos/modules $dst/options.json; then
       echo "The manual appears to depend on the location of Nixpkgs, which is bad"
       echo "since this prevents sharing via the NixOS channel.  This is typically"
       echo "caused by an option default that refers to a relative path (see above"
@@ -176,7 +150,26 @@ in rec {
       exit 1
     fi
 
-    ${pkgs.libxslt.bin}/bin/xsltproc \
-      -o "$out" ${./postprocess-option-descriptions.xsl} options.xml
-  '';
+      brotli -9 < $dst/options.json > $dst/options.json.br
+
+      mkdir -p $out/nix-support
+      echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products
+      echo "file json-br $dst/options.json.br" >> $out/nix-support/hydra-build-products
+    '';
+
+  optionsDocBook = lib.warn "optionsDocBook is deprecated since 23.11 and will be removed in 24.05"
+    (pkgs.runCommand "options-docbook.xml" {
+      nativeBuildInputs = [
+        pkgs.nixos-render-docs
+      ];
+    } ''
+      nixos-render-docs -j $NIX_BUILD_CORES options docbook \
+        --manpage-urls ${pkgs.path + "/doc/manpage-urls.json"} \
+        --revision ${lib.escapeShellArg revision} \
+        --document-type ${lib.escapeShellArg documentType} \
+        --varlist-id ${lib.escapeShellArg variablelistId} \
+        --id-prefix ${lib.escapeShellArg optionIdPrefix} \
+        ${optionsJSON}/share/doc/nixos/options.json \
+        "$out"
+    '');
 }
diff --git a/nixpkgs/nixos/lib/make-options-doc/mergeJSON.py b/nixpkgs/nixos/lib/make-options-doc/mergeJSON.py
index b4f72b8a3fdc..4be83fcb827b 100644
--- a/nixpkgs/nixos/lib/make-options-doc/mergeJSON.py
+++ b/nixpkgs/nixos/lib/make-options-doc/mergeJSON.py
@@ -43,19 +43,11 @@ def unpivot(options: Dict[Key, Option]) -> Dict[str, JSON]:
     return result
 
 warningsAreErrors = False
-warnOnDocbook = False
-errorOnDocbook = False
 optOffset = 0
 for arg in sys.argv[1:]:
     if arg == "--warnings-are-errors":
         optOffset += 1
         warningsAreErrors = True
-    if arg == "--warn-on-docbook":
-        optOffset += 1
-        warnOnDocbook = True
-    elif arg == "--error-on-docbook":
-        optOffset += 1
-        errorOnDocbook = True
 
 options = pivot(json.load(open(sys.argv[1 + optOffset], 'r')))
 overrides = pivot(json.load(open(sys.argv[2 + optOffset], 'r')))
@@ -84,38 +76,10 @@ for (k, v) in overrides.items():
 
 severity = "error" if warningsAreErrors else "warning"
 
-def is_docbook(o, key):
-    val = o.get(key, {})
-    if not isinstance(val, dict):
-        return False
-    return val.get('_type', '') == 'literalDocBook'
-
 # check that every option has a description
 hasWarnings = False
 hasErrors = False
-hasDocBook = False
 for (k, v) in options.items():
-    if warnOnDocbook or errorOnDocbook:
-        kind = "error" if errorOnDocbook else "warning"
-        if isinstance(v.value.get('description', {}), str):
-            hasErrors |= errorOnDocbook
-            hasDocBook = True
-            print(
-                f"\x1b[1;31m{kind}: option {v.name} description uses DocBook\x1b[0m",
-                file=sys.stderr)
-        elif is_docbook(v.value, 'defaultText'):
-            hasErrors |= errorOnDocbook
-            hasDocBook = True
-            print(
-                f"\x1b[1;31m{kind}: option {v.name} default uses DocBook\x1b[0m",
-                file=sys.stderr)
-        elif is_docbook(v.value, 'example'):
-            hasErrors |= errorOnDocbook
-            hasDocBook = True
-            print(
-                f"\x1b[1;31m{kind}: option {v.name} example uses DocBook\x1b[0m",
-                file=sys.stderr)
-
     if v.value.get('description', None) is None:
         hasWarnings = True
         print(f"\x1b[1;31m{severity}: option {v.name} has no description\x1b[0m", file=sys.stderr)
@@ -126,30 +90,6 @@ for (k, v) in options.items():
             f"\x1b[1;31m{severity}: option {v.name} has no type. Please specify a valid type, see " +
             "https://nixos.org/manual/nixos/stable/index.html#sec-option-types\x1b[0m", file=sys.stderr)
 
-if hasDocBook:
-    (why, what) = (
-        ("disallowed for in-tree modules", "contribution") if errorOnDocbook
-        else ("deprecated for option documentation", "module")
-    )
-    print("Explanation: The documentation contains descriptions, examples, or defaults written in DocBook. " +
-        "NixOS is in the process of migrating from DocBook to Markdown, and " +
-        f"DocBook is {why}. To change your {what} to "+
-        "use Markdown, apply mdDoc and literalMD and use the *MD variants of option creation " +
-        "functions where they are available. For example:\n" +
-        "\n" +
-        "  example.foo = mkOption {\n" +
-        "    description = lib.mdDoc ''your description'';\n" +
-        "    defaultText = lib.literalMD ''your description of default'';\n" +
-        "  };\n" +
-        "\n" +
-        "  example.enable = mkEnableOption (lib.mdDoc ''your thing'');\n" +
-        "  example.package = mkPackageOptionMD pkgs \"your-package\" {};\n" +
-        "  imports = [ (mkAliasOptionModuleMD [ \"example\" \"args\" ] [ \"example\" \"settings\" ]) ];",
-        file = sys.stderr)
-    with open(os.getenv('TOUCH_IF_DB'), 'x'):
-        # just make sure it exists
-        pass
-
 if hasErrors:
     sys.exit(1)
 if hasWarnings and warningsAreErrors:
diff --git a/nixpkgs/nixos/lib/make-options-doc/postprocess-option-descriptions.xsl b/nixpkgs/nixos/lib/make-options-doc/postprocess-option-descriptions.xsl
deleted file mode 100644
index 1201c7612c2e..000000000000
--- a/nixpkgs/nixos/lib/make-options-doc/postprocess-option-descriptions.xsl
+++ /dev/null
@@ -1,115 +0,0 @@
-<?xml version="1.0"?>
-
-<xsl:stylesheet version="1.0"
-                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-                xmlns:str="http://exslt.org/strings"
-                xmlns:exsl="http://exslt.org/common"
-                xmlns:db="http://docbook.org/ns/docbook"
-                xmlns:nixos="tag:nixos.org"
-                extension-element-prefixes="str exsl">
-  <xsl:output method='xml' encoding="UTF-8" />
-
-  <xsl:template match="@*|node()">
-    <xsl:copy>
-      <xsl:apply-templates select="@*|node()" />
-    </xsl:copy>
-  </xsl:template>
-
-  <xsl:template name="break-up-description">
-    <xsl:param name="input" />
-    <xsl:param name="buffer" />
-
-    <!-- Every time we have two newlines following each other, we want to
-         break it into </para><para>. -->
-    <xsl:variable name="parbreak" select="'&#xa;&#xa;'" />
-
-    <!-- Similar to "(head:tail) = input" in Haskell. -->
-    <xsl:variable name="head" select="$input[1]" />
-    <xsl:variable name="tail" select="$input[position() &gt; 1]" />
-
-    <xsl:choose>
-      <xsl:when test="$head/self::text() and contains($head, $parbreak)">
-        <!-- If the haystack provided to str:split() directly starts or
-             ends with $parbreak, it doesn't generate a <token/> for that,
-             so we are doing this here. -->
-        <xsl:variable name="splitted-raw">
-          <xsl:if test="starts-with($head, $parbreak)"><token /></xsl:if>
-          <xsl:for-each select="str:split($head, $parbreak)">
-            <token><xsl:value-of select="node()" /></token>
-          </xsl:for-each>
-          <!-- Something like ends-with($head, $parbreak), but there is
-               no ends-with() in XSLT, so we need to use substring(). -->
-          <xsl:if test="
-            substring($head, string-length($head) -
-                             string-length($parbreak) + 1) = $parbreak
-          "><token /></xsl:if>
-        </xsl:variable>
-        <xsl:variable name="splitted"
-                      select="exsl:node-set($splitted-raw)/token" />
-        <!-- The buffer we had so far didn't contain any text nodes that
-             contain a $parbreak, so we can put the buffer along with the
-             first token of $splitted into a para element. -->
-        <para xmlns="http://docbook.org/ns/docbook">
-          <xsl:apply-templates select="exsl:node-set($buffer)" />
-          <xsl:apply-templates select="$splitted[1]/node()" />
-        </para>
-        <!-- We have already emitted the first splitted result, so the
-             last result is going to be set as the new $buffer later
-             because its contents may not be directly followed up by a
-             $parbreak. -->
-        <xsl:for-each select="$splitted[position() &gt; 1
-                              and position() &lt; last()]">
-          <para xmlns="http://docbook.org/ns/docbook">
-            <xsl:apply-templates select="node()" />
-          </para>
-        </xsl:for-each>
-        <xsl:call-template name="break-up-description">
-          <xsl:with-param name="input" select="$tail" />
-          <xsl:with-param name="buffer" select="$splitted[last()]/node()" />
-        </xsl:call-template>
-      </xsl:when>
-      <!-- Either non-text node or one without $parbreak, which we just
-           want to buffer and continue recursing. -->
-      <xsl:when test="$input">
-        <xsl:call-template name="break-up-description">
-          <xsl:with-param name="input" select="$tail" />
-          <!-- This essentially appends $head to $buffer. -->
-          <xsl:with-param name="buffer">
-            <xsl:if test="$buffer">
-              <xsl:for-each select="exsl:node-set($buffer)">
-                <xsl:apply-templates select="." />
-              </xsl:for-each>
-            </xsl:if>
-            <xsl:apply-templates select="$head" />
-          </xsl:with-param>
-        </xsl:call-template>
-      </xsl:when>
-      <!-- No more $input, just put the remaining $buffer in a para. -->
-      <xsl:otherwise>
-        <para xmlns="http://docbook.org/ns/docbook">
-          <xsl:apply-templates select="exsl:node-set($buffer)" />
-        </para>
-      </xsl:otherwise>
-    </xsl:choose>
-  </xsl:template>
-
-  <xsl:template match="nixos:option-description">
-    <xsl:choose>
-      <!--
-        Only process nodes that are comprised of a single <para/> element,
-        because if that's not the case the description already contains
-        </para><para> in between and we need no further processing.
-      -->
-      <xsl:when test="count(db:para) > 1">
-        <xsl:apply-templates select="node()" />
-      </xsl:when>
-      <xsl:otherwise>
-        <xsl:call-template name="break-up-description">
-          <xsl:with-param name="input"
-                          select="exsl:node-set(db:para/node())" />
-        </xsl:call-template>
-      </xsl:otherwise>
-    </xsl:choose>
-  </xsl:template>
-
-</xsl:stylesheet>
diff --git a/nixpkgs/nixos/lib/qemu-common.nix b/nixpkgs/nixos/lib/qemu-common.nix
index a8ed27dd6091..4fff2e0a6f15 100644
--- a/nixpkgs/nixos/lib/qemu-common.nix
+++ b/nixpkgs/nixos/lib/qemu-common.nix
@@ -19,7 +19,7 @@ rec {
     ];
 
   qemuSerialDevice =
-    if with pkgs.stdenv.hostPlatform; isx86 || isMips64 || isRiscV then "ttyS0"
+    if with pkgs.stdenv.hostPlatform; isx86 || isLoongArch64 || isMips64 || isRiscV then "ttyS0"
     else if (with pkgs.stdenv.hostPlatform; isAarch || isPower) then "ttyAMA0"
     else throw "Unknown QEMU serial device for system '${pkgs.stdenv.hostPlatform.system}'";
 
diff --git a/nixpkgs/nixos/lib/systemd-lib.nix b/nixpkgs/nixos/lib/systemd-lib.nix
index eb2bcb9d3b98..641b47def039 100644
--- a/nixpkgs/nixos/lib/systemd-lib.nix
+++ b/nixpkgs/nixos/lib/systemd-lib.nix
@@ -63,7 +63,12 @@ in rec {
 
   assertMacAddress = name: group: attr:
     optional (attr ? ${name} && ! isMacAddress attr.${name})
-      "Systemd ${group} field `${name}' must be a valid mac address.";
+      "Systemd ${group} field `${name}' must be a valid MAC address.";
+
+  assertNetdevMacAddress = name: group: attr:
+    optional (attr ? ${name} && (! isMacAddress attr.${name} && attr.${name} != "none"))
+      "Systemd ${group} field `${name}` must be a valid MAC address or the special value `none`.";
+
 
   isPort = i: i >= 0 && i <= 65535;
 
@@ -438,4 +443,21 @@ in rec {
           ${attrsToSection def.sliceConfig}
         '';
     };
+
+  # Create a directory that contains systemd definition files from an attrset
+  # that contains the file names as keys and the content as values. The values
+  # in that attrset are determined by the supplied format.
+  definitions = directoryName: format: definitionAttrs:
+    let
+      listOfDefinitions = lib.mapAttrsToList
+        (name: format.generate "${name}.conf")
+        definitionAttrs;
+    in
+    pkgs.runCommand directoryName { } ''
+      mkdir -p $out
+      ${(lib.concatStringsSep "\n"
+        (map (pkg: "cp ${pkg} $out/${pkg.name}") listOfDefinitions)
+      )}
+    '';
+
 }
diff --git a/nixpkgs/nixos/lib/systemd-network-units.nix b/nixpkgs/nixos/lib/systemd-network-units.nix
new file mode 100644
index 000000000000..14ff0b3742ea
--- /dev/null
+++ b/nixpkgs/nixos/lib/systemd-network-units.nix
@@ -0,0 +1,240 @@
+{ lib, systemdUtils }:
+
+with lib;
+
+let
+  attrsToSection = systemdUtils.lib.attrsToSection;
+  commonMatchText = def:
+    optionalString (def.matchConfig != { }) ''
+      [Match]
+      ${attrsToSection def.matchConfig}
+    '';
+in {
+  linkToUnit = def:
+    commonMatchText def + ''
+      [Link]
+      ${attrsToSection def.linkConfig}
+    '' + def.extraConfig;
+
+  netdevToUnit = def:
+    commonMatchText def + ''
+      [NetDev]
+      ${attrsToSection def.netdevConfig}
+    '' + optionalString (def.vlanConfig != { }) ''
+      [VLAN]
+      ${attrsToSection def.vlanConfig}
+    '' + optionalString (def.macvlanConfig != { }) ''
+      [MACVLAN]
+      ${attrsToSection def.macvlanConfig}
+    '' + optionalString (def.vxlanConfig != { }) ''
+      [VXLAN]
+      ${attrsToSection def.vxlanConfig}
+    '' + optionalString (def.tunnelConfig != { }) ''
+      [Tunnel]
+      ${attrsToSection def.tunnelConfig}
+    '' + optionalString (def.fooOverUDPConfig != { }) ''
+      [FooOverUDP]
+      ${attrsToSection def.fooOverUDPConfig}
+    '' + optionalString (def.peerConfig != { }) ''
+      [Peer]
+      ${attrsToSection def.peerConfig}
+    '' + optionalString (def.tunConfig != { }) ''
+      [Tun]
+      ${attrsToSection def.tunConfig}
+    '' + optionalString (def.tapConfig != { }) ''
+      [Tap]
+      ${attrsToSection def.tapConfig}
+    '' + optionalString (def.l2tpConfig != { }) ''
+      [L2TP]
+      ${attrsToSection def.l2tpConfig}
+    '' + flip concatMapStrings def.l2tpSessions (x: ''
+      [L2TPSession]
+      ${attrsToSection x.l2tpSessionConfig}
+    '') + optionalString (def.wireguardConfig != { }) ''
+      [WireGuard]
+      ${attrsToSection def.wireguardConfig}
+    '' + flip concatMapStrings def.wireguardPeers (x: ''
+      [WireGuardPeer]
+      ${attrsToSection x.wireguardPeerConfig}
+    '') + optionalString (def.bondConfig != { }) ''
+      [Bond]
+      ${attrsToSection def.bondConfig}
+    '' + optionalString (def.xfrmConfig != { }) ''
+      [Xfrm]
+      ${attrsToSection def.xfrmConfig}
+    '' + optionalString (def.vrfConfig != { }) ''
+      [VRF]
+      ${attrsToSection def.vrfConfig}
+    '' + optionalString (def.batmanAdvancedConfig != { }) ''
+      [BatmanAdvanced]
+      ${attrsToSection def.batmanAdvancedConfig}
+    '' + def.extraConfig;
+
+  networkToUnit = def:
+    commonMatchText def + optionalString (def.linkConfig != { }) ''
+      [Link]
+      ${attrsToSection def.linkConfig}
+    '' + ''
+      [Network]
+    '' + attrsToSection def.networkConfig
+    + optionalString (def.address != [ ]) ''
+      ${concatStringsSep "\n" (map (s: "Address=${s}") def.address)}
+    '' + optionalString (def.gateway != [ ]) ''
+      ${concatStringsSep "\n" (map (s: "Gateway=${s}") def.gateway)}
+    '' + optionalString (def.dns != [ ]) ''
+      ${concatStringsSep "\n" (map (s: "DNS=${s}") def.dns)}
+    '' + optionalString (def.ntp != [ ]) ''
+      ${concatStringsSep "\n" (map (s: "NTP=${s}") def.ntp)}
+    '' + optionalString (def.bridge != [ ]) ''
+      ${concatStringsSep "\n" (map (s: "Bridge=${s}") def.bridge)}
+    '' + optionalString (def.bond != [ ]) ''
+      ${concatStringsSep "\n" (map (s: "Bond=${s}") def.bond)}
+    '' + optionalString (def.vrf != [ ]) ''
+      ${concatStringsSep "\n" (map (s: "VRF=${s}") def.vrf)}
+    '' + optionalString (def.vlan != [ ]) ''
+      ${concatStringsSep "\n" (map (s: "VLAN=${s}") def.vlan)}
+    '' + optionalString (def.macvlan != [ ]) ''
+      ${concatStringsSep "\n" (map (s: "MACVLAN=${s}") def.macvlan)}
+    '' + optionalString (def.macvtap != [ ]) ''
+      ${concatStringsSep "\n" (map (s: "MACVTAP=${s}") def.macvtap)}
+    '' + optionalString (def.vxlan != [ ]) ''
+      ${concatStringsSep "\n" (map (s: "VXLAN=${s}") def.vxlan)}
+    '' + optionalString (def.tunnel != [ ]) ''
+      ${concatStringsSep "\n" (map (s: "Tunnel=${s}") def.tunnel)}
+    '' + optionalString (def.xfrm != [ ]) ''
+      ${concatStringsSep "\n" (map (s: "Xfrm=${s}") def.xfrm)}
+    '' + "\n" + flip concatMapStrings def.addresses (x: ''
+      [Address]
+      ${attrsToSection x.addressConfig}
+    '') + flip concatMapStrings def.routingPolicyRules (x: ''
+      [RoutingPolicyRule]
+      ${attrsToSection x.routingPolicyRuleConfig}
+    '') + flip concatMapStrings def.routes (x: ''
+      [Route]
+      ${attrsToSection x.routeConfig}
+    '') + optionalString (def.dhcpV4Config != { }) ''
+      [DHCPv4]
+      ${attrsToSection def.dhcpV4Config}
+    '' + optionalString (def.dhcpV6Config != { }) ''
+      [DHCPv6]
+      ${attrsToSection def.dhcpV6Config}
+    '' + optionalString (def.dhcpPrefixDelegationConfig != { }) ''
+      [DHCPPrefixDelegation]
+      ${attrsToSection def.dhcpPrefixDelegationConfig}
+    '' + optionalString (def.ipv6AcceptRAConfig != { }) ''
+      [IPv6AcceptRA]
+      ${attrsToSection def.ipv6AcceptRAConfig}
+    '' + optionalString (def.dhcpServerConfig != { }) ''
+      [DHCPServer]
+      ${attrsToSection def.dhcpServerConfig}
+    '' + optionalString (def.ipv6SendRAConfig != { }) ''
+      [IPv6SendRA]
+      ${attrsToSection def.ipv6SendRAConfig}
+    '' + flip concatMapStrings def.ipv6Prefixes (x: ''
+      [IPv6Prefix]
+      ${attrsToSection x.ipv6PrefixConfig}
+    '') + flip concatMapStrings def.ipv6RoutePrefixes (x: ''
+      [IPv6RoutePrefix]
+      ${attrsToSection x.ipv6RoutePrefixConfig}
+    '') + flip concatMapStrings def.dhcpServerStaticLeases (x: ''
+      [DHCPServerStaticLease]
+      ${attrsToSection x.dhcpServerStaticLeaseConfig}
+    '') + optionalString (def.bridgeConfig != { }) ''
+      [Bridge]
+      ${attrsToSection def.bridgeConfig}
+    '' + flip concatMapStrings def.bridgeFDBs (x: ''
+      [BridgeFDB]
+      ${attrsToSection x.bridgeFDBConfig}
+    '') + flip concatMapStrings def.bridgeMDBs (x: ''
+      [BridgeMDB]
+      ${attrsToSection x.bridgeMDBConfig}
+    '') + optionalString (def.lldpConfig != { }) ''
+      [LLDP]
+      ${attrsToSection def.lldpConfig}
+    '' + optionalString (def.canConfig != { }) ''
+      [CAN]
+      ${attrsToSection def.canConfig}
+    '' + optionalString (def.ipoIBConfig != { }) ''
+      [IPoIB]
+      ${attrsToSection def.ipoIBConfig}
+    '' + optionalString (def.qdiscConfig != { }) ''
+      [QDisc]
+      ${attrsToSection def.qdiscConfig}
+    '' + optionalString (def.networkEmulatorConfig != { }) ''
+      [NetworkEmulator]
+      ${attrsToSection def.networkEmulatorConfig}
+    '' + optionalString (def.tokenBucketFilterConfig != { }) ''
+      [TokenBucketFilter]
+      ${attrsToSection def.tokenBucketFilterConfig}
+    '' + optionalString (def.pieConfig != { }) ''
+      [PIE]
+      ${attrsToSection def.pieConfig}
+    '' + optionalString (def.flowQueuePIEConfig != { }) ''
+      [FlowQueuePIE]
+      ${attrsToSection def.flowQueuePIEConfig}
+    '' + optionalString (def.stochasticFairBlueConfig != { }) ''
+      [StochasticFairBlue]
+      ${attrsToSection def.stochasticFairBlueConfig}
+    '' + optionalString (def.stochasticFairnessQueueingConfig != { }) ''
+      [StochasticFairnessQueueing]
+      ${attrsToSection def.stochasticFairnessQueueingConfig}
+    '' + optionalString (def.bfifoConfig != { }) ''
+      [BFIFO]
+      ${attrsToSection def.bfifoConfig}
+    '' + optionalString (def.pfifoConfig != { }) ''
+      [PFIFO]
+      ${attrsToSection def.pfifoConfig}
+    '' + optionalString (def.pfifoHeadDropConfig != { }) ''
+      [PFIFOHeadDrop]
+      ${attrsToSection def.pfifoHeadDropConfig}
+    '' + optionalString (def.pfifoFastConfig != { }) ''
+      [PFIFOFast]
+      ${attrsToSection def.pfifoFastConfig}
+    '' + optionalString (def.cakeConfig != { }) ''
+      [CAKE]
+      ${attrsToSection def.cakeConfig}
+    '' + optionalString (def.controlledDelayConfig != { }) ''
+      [ControlledDelay]
+      ${attrsToSection def.controlledDelayConfig}
+    '' + optionalString (def.deficitRoundRobinSchedulerConfig != { }) ''
+      [DeficitRoundRobinScheduler]
+      ${attrsToSection def.deficitRoundRobinSchedulerConfig}
+    '' + optionalString (def.deficitRoundRobinSchedulerClassConfig != { }) ''
+      [DeficitRoundRobinSchedulerClass]
+      ${attrsToSection def.deficitRoundRobinSchedulerClassConfig}
+    '' + optionalString (def.enhancedTransmissionSelectionConfig != { }) ''
+      [EnhancedTransmissionSelection]
+      ${attrsToSection def.enhancedTransmissionSelectionConfig}
+    '' + optionalString (def.genericRandomEarlyDetectionConfig != { }) ''
+      [GenericRandomEarlyDetection]
+      ${attrsToSection def.genericRandomEarlyDetectionConfig}
+    '' + optionalString (def.fairQueueingControlledDelayConfig != { }) ''
+      [FairQueueingControlledDelay]
+      ${attrsToSection def.fairQueueingControlledDelayConfig}
+    '' + optionalString (def.fairQueueingConfig != { }) ''
+      [FairQueueing]
+      ${attrsToSection def.fairQueueingConfig}
+    '' + optionalString (def.trivialLinkEqualizerConfig != { }) ''
+      [TrivialLinkEqualizer]
+      ${attrsToSection def.trivialLinkEqualizerConfig}
+    '' + optionalString (def.hierarchyTokenBucketConfig != { }) ''
+      [HierarchyTokenBucket]
+      ${attrsToSection def.hierarchyTokenBucketConfig}
+    '' + optionalString (def.hierarchyTokenBucketClassConfig != { }) ''
+      [HierarchyTokenBucketClass]
+      ${attrsToSection def.hierarchyTokenBucketClassConfig}
+    '' + optionalString (def.heavyHitterFilterConfig != { }) ''
+      [HeavyHitterFilter]
+      ${attrsToSection def.heavyHitterFilterConfig}
+    '' + optionalString (def.quickFairQueueingConfig != { }) ''
+      [QuickFairQueueing]
+      ${attrsToSection def.quickFairQueueingConfig}
+    '' + optionalString (def.quickFairQueueingConfigClass != { }) ''
+      [QuickFairQueueingClass]
+      ${attrsToSection def.quickFairQueueingConfigClass}
+    '' + flip concatMapStrings def.bridgeVLANs (x: ''
+      [BridgeVLAN]
+      ${attrsToSection x.bridgeVLANConfig}
+    '') + def.extraConfig;
+
+}
diff --git a/nixpkgs/nixos/lib/test-driver/extract-docstrings.py b/nixpkgs/nixos/lib/test-driver/extract-docstrings.py
new file mode 100644
index 000000000000..5aec4c89a9d7
--- /dev/null
+++ b/nixpkgs/nixos/lib/test-driver/extract-docstrings.py
@@ -0,0 +1,66 @@
+import ast
+import sys
+
+"""
+This program takes all the Machine class methods and prints its methods in
+markdown-style. These can then be included in the NixOS test driver
+markdown style, assuming the docstrings themselves are also in markdown.
+
+These are included in the test driver documentation in the NixOS manual.
+See https://nixos.org/manual/nixos/stable/#ssec-machine-objects
+
+The python input looks like this:
+
+```py
+...
+
+class Machine(...):
+    ...
+
+    def some_function(self, param1, param2):
+        ""
+        documentation string of some_function.
+        foo bar baz.
+        ""
+        ...
+```
+
+Output will be:
+
+```markdown
+...
+
+some_function(param1, param2)
+
+:   documentation string of some_function.
+    foo bar baz.
+
+...
+```
+
+"""
+
+assert len(sys.argv) == 2
+
+with open(sys.argv[1], "r") as f:
+    module = ast.parse(f.read())
+
+class_definitions = (node for node in module.body if isinstance(node, ast.ClassDef))
+
+machine_class = next(filter(lambda x: x.name == "Machine", class_definitions))
+assert machine_class is not None
+
+function_definitions = [
+    node for node in machine_class.body if isinstance(node, ast.FunctionDef)
+]
+function_definitions.sort(key=lambda x: x.name)
+
+for f in function_definitions:
+    docstr = ast.get_docstring(f)
+    if docstr is not None:
+        args = ", ".join((a.arg for a in f.args.args[1:]))
+        args = f"({args})"
+
+        docstr = "\n".join((f"    {l}" for l in docstr.strip().splitlines()))
+
+        print(f"{f.name}{args}\n\n:{docstr[1:]}\n")
diff --git a/nixpkgs/nixos/lib/test-driver/nixos-test-driver-docstrings.nix b/nixpkgs/nixos/lib/test-driver/nixos-test-driver-docstrings.nix
new file mode 100644
index 000000000000..a3ef50e4e820
--- /dev/null
+++ b/nixpkgs/nixos/lib/test-driver/nixos-test-driver-docstrings.nix
@@ -0,0 +1,13 @@
+{ runCommand
+, python3
+}:
+
+let
+  env = { nativeBuildInputs = [ python3 ]; };
+in
+
+runCommand "nixos-test-driver-docstrings" env ''
+  mkdir $out
+  python3 ${./extract-docstrings.py} ${./test_driver/machine.py} \
+    > $out/machine-methods.md
+''
diff --git a/nixpkgs/nixos/lib/test-driver/test_driver/machine.py b/nixpkgs/nixos/lib/test-driver/test_driver/machine.py
index 1d1d5bef9bf4..809fd690d717 100644
--- a/nixpkgs/nixos/lib/test-driver/test_driver/machine.py
+++ b/nixpkgs/nixos/lib/test-driver/test_driver/machine.py
@@ -416,6 +416,10 @@ class Machine:
         return answer
 
     def send_monitor_command(self, command: str) -> str:
+        """
+        Send a command to the QEMU monitor. This allows attaching
+        virtual USB disks to a running machine, among other things.
+        """
         self.run_callbacks()
         message = f"{command}\n".encode()
         assert self.monitor is not None
@@ -425,9 +429,10 @@ class Machine:
     def wait_for_unit(
         self, unit: str, user: Optional[str] = None, timeout: int = 900
     ) -> None:
-        """Wait for a systemd unit to get into "active" state.
-        Throws exceptions on "failed" and "inactive" states as well as
-        after timing out.
+        """
+        Wait for a systemd unit to get into "active" state.
+        Throws exceptions on "failed" and "inactive" states as well as after
+        timing out.
         """
 
         def check_active(_: Any) -> bool:
@@ -476,6 +481,19 @@ class Machine:
         )
 
     def systemctl(self, q: str, user: Optional[str] = None) -> Tuple[int, str]:
+        """
+        Runs `systemctl` commands with optional support for
+        `systemctl --user`
+
+        ```py
+        # run `systemctl list-jobs --no-pager`
+        machine.systemctl("list-jobs --no-pager")
+
+        # spawn a shell for `any-user` and run
+        # `systemctl --user list-jobs --no-pager`
+        machine.systemctl("list-jobs --no-pager", "any-user")
+        ```
+        """
         if user is not None:
             q = q.replace("'", "\\'")
             return self.execute(
@@ -514,8 +532,44 @@ class Machine:
         return "".join(output_buffer)
 
     def execute(
-        self, command: str, check_return: bool = True, timeout: Optional[int] = 900
+        self,
+        command: str,
+        check_return: bool = True,
+        check_output: bool = True,
+        timeout: Optional[int] = 900,
     ) -> Tuple[int, str]:
+        """
+        Execute a shell command, returning a list `(status, stdout)`.
+
+        Commands are run with `set -euo pipefail` set:
+
+        -   If several commands are separated by `;` and one fails, the
+            command as a whole will fail.
+
+        -   For pipelines, the last non-zero exit status will be returned
+            (if there is one; otherwise zero will be returned).
+
+        -   Dereferencing unset variables fails the command.
+
+        -   It will wait for stdout to be closed.
+
+        If the command detaches, it must close stdout, as `execute` will wait
+        for this to consume all output reliably. This can be achieved by
+        redirecting stdout to stderr `>&2`, to `/dev/console`, `/dev/null` or
+        a file. Examples of detaching commands are `sleep 365d &`, where the
+        shell forks a new process that can write to stdout and `xclip -i`, where
+        the `xclip` command itself forks without closing stdout.
+
+        Takes an optional parameter `check_return` that defaults to `True`.
+        Setting this parameter to `False` will not check for the return code
+        and return -1 instead. This can be used for commands that shut down
+        the VM and would therefore break the pipe that would be used for
+        retrieving the return code.
+
+        A timeout for the command can be specified (in seconds) using the optional
+        `timeout` parameter, e.g., `execute(cmd, timeout=10)` or
+        `execute(cmd, timeout=None)`. The default is 900 seconds.
+        """
         self.run_callbacks()
         self.connect()
 
@@ -529,12 +583,15 @@ class Machine:
         # While sh is bash on NixOS, this is not the case for every distro.
         # We explicitly call bash here to allow for the driver to boot other distros as well.
         out_command = (
-            f"{timeout_str} bash -c {shlex.quote(command)} | (base64 --wrap 0; echo)\n"
+            f"{timeout_str} bash -c {shlex.quote(command)} | (base64 -w 0; echo)\n"
         )
 
         assert self.shell
         self.shell.send(out_command.encode())
 
+        if not check_output:
+            return (-2, "")
+
         # Get the output
         output = base64.b64decode(self._next_newline_closed_block_from_shell())
 
@@ -548,10 +605,11 @@ class Machine:
         return (rc, output.decode(errors="replace"))
 
     def shell_interact(self, address: Optional[str] = None) -> None:
-        """Allows you to interact with the guest shell for debugging purposes.
-
-        @address string passed to socat that will be connected to the guest shell.
-        Check the `Running Tests interactivly` chapter of NixOS manual for an example.
+        """
+        Allows you to directly interact with the guest shell. This should
+        only be used during test development, not in production tests.
+        Killing the interactive session with `Ctrl-d` or `Ctrl-c` also ends
+        the guest session.
         """
         self.connect()
 
@@ -570,12 +628,14 @@ class Machine:
             pass
 
     def console_interact(self) -> None:
-        """Allows you to interact with QEMU's stdin
-
-        The shell can be exited with Ctrl+D. Note that Ctrl+C is not allowed to be used.
-        QEMU's stdout is read line-wise.
-
-        Should only be used during test development, not in the production test."""
+        """
+        Allows you to directly interact with QEMU's stdin, by forwarding
+        terminal input to the QEMU process.
+        This is for use with the interactive test driver, not for production
+        tests, which run unattended.
+        Output from QEMU is only read line-wise. `Ctrl-c` kills QEMU and
+        `Ctrl-d` closes console and returns to the test runner.
+        """
         self.log("Terminal is ready (there is no prompt):")
 
         assert self.process
@@ -592,7 +652,12 @@ class Machine:
             self.send_console(char.decode())
 
     def succeed(self, *commands: str, timeout: Optional[int] = None) -> str:
-        """Execute each command and check that it succeeds."""
+        """
+        Execute a shell command, raising an exception if the exit status is
+        not zero, otherwise returning the standard output. Similar to `execute`,
+        except that the timeout is `None` by default. See `execute` for details on
+        command execution.
+        """
         output = ""
         for command in commands:
             with self.nested(f"must succeed: {command}"):
@@ -604,7 +669,10 @@ class Machine:
         return output
 
     def fail(self, *commands: str, timeout: Optional[int] = None) -> str:
-        """Execute each command and check that it fails."""
+        """
+        Like `succeed`, but raising an exception if the command returns a zero
+        status.
+        """
         output = ""
         for command in commands:
             with self.nested(f"must fail: {command}"):
@@ -615,7 +683,11 @@ class Machine:
         return output
 
     def wait_until_succeeds(self, command: str, timeout: int = 900) -> str:
-        """Wait until a command returns success and return its output.
+        """
+        Repeat a shell command with 1-second intervals until it succeeds.
+        Has a default timeout of 900 seconds which can be modified, e.g.
+        `wait_until_succeeds(cmd, timeout=10)`. See `execute` for details on
+        command execution.
         Throws an exception on timeout.
         """
         output = ""
@@ -630,8 +702,8 @@ class Machine:
             return output
 
     def wait_until_fails(self, command: str, timeout: int = 900) -> str:
-        """Wait until a command returns failure.
-        Throws an exception on timeout.
+        """
+        Like `wait_until_succeeds`, but repeating the command until it fails.
         """
         output = ""
 
@@ -641,7 +713,7 @@ class Machine:
             return status != 0
 
         with self.nested(f"waiting for failure: {command}"):
-            retry(check_failure)
+            retry(check_failure, timeout)
             return output
 
     def wait_for_shutdown(self) -> None:
@@ -683,12 +755,19 @@ class Machine:
             retry(tty_matches)
 
     def send_chars(self, chars: str, delay: Optional[float] = 0.01) -> None:
+        """
+        Simulate typing a sequence of characters on the virtual keyboard,
+        e.g., `send_chars("foobar\n")` will type the string `foobar`
+        followed by the Enter key.
+        """
         with self.nested(f"sending keys {repr(chars)}"):
             for char in chars:
                 self.send_key(char, delay, log=False)
 
     def wait_for_file(self, filename: str) -> None:
-        """Waits until the file exists in machine's file system."""
+        """
+        Waits until the file exists in the machine's file system.
+        """
 
         def check_file(_: Any) -> bool:
             status, _ = self.execute(f"test -e {filename}")
@@ -698,6 +777,11 @@ class Machine:
             retry(check_file)
 
     def wait_for_open_port(self, port: int, addr: str = "localhost") -> None:
+        """
+        Wait until a process is listening on the given TCP port and IP address
+        (default `localhost`).
+        """
+
         def port_is_open(_: Any) -> bool:
             status, _ = self.execute(f"nc -z {addr} {port}")
             return status == 0
@@ -706,6 +790,11 @@ class Machine:
             retry(port_is_open)
 
     def wait_for_closed_port(self, port: int, addr: str = "localhost") -> None:
+        """
+        Wait until nobody is listening on the given TCP port and IP address
+        (default `localhost`).
+        """
+
         def port_is_closed(_: Any) -> bool:
             status, _ = self.execute(f"nc -z {addr} {port}")
             return status != 0
@@ -744,8 +833,17 @@ class Machine:
             # TODO: do we want to bail after a set number of attempts?
             while not shell_ready(timeout_secs=30):
                 self.log("Guest root shell did not produce any data yet...")
+                self.log(
+                    "  To debug, enter the VM and run 'systemctl status backdoor.service'."
+                )
+
+            while True:
+                chunk = self.shell.recv(1024)
+                self.log(f"Guest shell says: {chunk!r}")
+                # NOTE: for this to work, nothing must be printed after this line!
+                if b"Spawning backdoor root shell..." in chunk:
+                    break
 
-            self.log(self.shell.recv(1024).decode())
             toc = time.time()
 
             self.log("connected to guest root shell")
@@ -753,6 +851,10 @@ class Machine:
             self.connected = True
 
     def screenshot(self, filename: str) -> None:
+        """
+        Take a picture of the display of the virtual machine, in PNG format.
+        The screenshot will be available in the derivation output.
+        """
         if "." not in filename:
             filename += ".png"
         if "/" not in filename:
@@ -782,8 +884,21 @@ class Machine:
             )
 
     def copy_from_host(self, source: str, target: str) -> None:
-        """Copy a file from the host into the guest via the `shared_dir` shared
-        among all the VMs (using a temporary directory).
+        """
+        Copies a file from host to machine, e.g.,
+        `copy_from_host("myfile", "/etc/my/important/file")`.
+
+        The first argument is the file on the host. Note that the "host" refers
+        to the environment in which the test driver runs, which is typically the
+        Nix build sandbox.
+
+        The second argument is the location of the file on the machine that will
+        be written to.
+
+        The file is copied via the `shared_dir` directory which is shared among
+        all the VMs (using a temporary directory).
+        The access rights bits will mimic the ones from the host file and
+        user:group will be root:root.
         """
         host_src = Path(source)
         vm_target = Path(target)
@@ -835,12 +950,41 @@ class Machine:
             return _perform_ocr_on_screenshot(screenshot_path, model_ids)
 
     def get_screen_text_variants(self) -> List[str]:
+        """
+        Return a list of different interpretations of what is currently
+        visible on the machine's screen using optical character
+        recognition. The number and order of the interpretations is not
+        specified and is subject to change, but if no exception is raised at
+        least one will be returned.
+
+        ::: {.note}
+        This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`.
+        :::
+        """
         return self._get_screen_text_variants([0, 1, 2])
 
     def get_screen_text(self) -> str:
+        """
+        Return a textual representation of what is currently visible on the
+        machine's screen using optical character recognition.
+
+        ::: {.note}
+        This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`.
+        :::
+        """
         return self._get_screen_text_variants([2])[0]
 
     def wait_for_text(self, regex: str) -> None:
+        """
+        Wait until the supplied regular expressions matches the textual
+        contents of the screen by using optical character recognition (see
+        `get_screen_text` and `get_screen_text_variants`).
+
+        ::: {.note}
+        This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`.
+        :::
+        """
+
         def screen_matches(last: bool) -> bool:
             variants = self.get_screen_text_variants()
             for text in variants:
@@ -857,18 +1001,15 @@ class Machine:
 
     def wait_for_console_text(self, regex: str, timeout: int | None = None) -> None:
         """
-        Wait for the provided regex to appear on console.
-        For each reads,
-
-        If timeout is None, timeout is infinite.
-
-        `timeout` is in seconds.
+        Wait until the supplied regular expressions match a line of the
+        serial console output.
+        This method is useful when OCR is not possible or inaccurate.
         """
         # Buffer the console output, this is needed
         # to match multiline regexes.
         console = io.StringIO()
 
-        def console_matches() -> bool:
+        def console_matches(_: Any) -> bool:
             nonlocal console
             try:
                 # This will return as soon as possible and
@@ -884,12 +1025,19 @@ class Machine:
             if timeout is not None:
                 retry(console_matches, timeout)
             else:
-                while not console_matches():
+                while not console_matches(False):
                     pass
 
     def send_key(
         self, key: str, delay: Optional[float] = 0.01, log: Optional[bool] = True
     ) -> None:
+        """
+        Simulate pressing keys on the virtual keyboard, e.g.,
+        `send_key("ctrl-alt-delete")`.
+
+        Please also refer to the QEMU documentation for more information on the
+        input syntax: https://en.wikibooks.org/wiki/QEMU/Monitor#sendkey_keys
+        """
         key = CHAR_TO_KEY.get(key, key)
         context = self.nested(f"sending key {repr(key)}") if log else nullcontext()
         with context:
@@ -898,12 +1046,21 @@ class Machine:
                 time.sleep(delay)
 
     def send_console(self, chars: str) -> None:
+        r"""
+        Send keys to the kernel console. This allows interaction with the systemd
+        emergency mode, for example. Takes a string that is sent, e.g.,
+        `send_console("\n\nsystemctl default\n")`.
+        """
         assert self.process
         assert self.process.stdin
         self.process.stdin.write(chars.encode())
         self.process.stdin.flush()
 
     def start(self, allow_reboot: bool = False) -> None:
+        """
+        Start the virtual machine. This method is asynchronous --- it does
+        not wait for the machine to finish booting.
+        """
         if self.booted:
             return
 
@@ -961,6 +1118,9 @@ class Machine:
         rootlog.log("if you want to keep the VM state, pass --keep-vm-state")
 
     def shutdown(self) -> None:
+        """
+        Shut down the machine, waiting for the VM to exit.
+        """
         if not self.booted:
             return
 
@@ -969,6 +1129,9 @@ class Machine:
         self.wait_for_shutdown()
 
     def crash(self) -> None:
+        """
+        Simulate a sudden power failure, by telling the VM to exit immediately.
+        """
         if not self.booted:
             return
 
@@ -986,8 +1149,8 @@ class Machine:
         self.connected = False
 
     def wait_for_x(self) -> None:
-        """Wait until it is possible to connect to the X server.  Note that
-        testing the existence of /tmp/.X11-unix/X0 is insufficient.
+        """
+        Wait until it is possible to connect to the X server.
         """
 
         def check_x(_: Any) -> bool:
@@ -1010,6 +1173,10 @@ class Machine:
         ).splitlines()
 
     def wait_for_window(self, regexp: str) -> None:
+        """
+        Wait until an X11 window has appeared whose name matches the given
+        regular expression, e.g., `wait_for_window("Terminal")`.
+        """
         pattern = re.compile(regexp)
 
         def window_is_visible(last_try: bool) -> bool:
@@ -1030,20 +1197,26 @@ class Machine:
         self.succeed(f"sleep {secs}")
 
     def forward_port(self, host_port: int = 8080, guest_port: int = 80) -> None:
-        """Forward a TCP port on the host to a TCP port on the guest.
+        """
+        Forward a TCP port on the host to a TCP port on the guest.
         Useful during interactive testing.
         """
         self.send_monitor_command(f"hostfwd_add tcp::{host_port}-:{guest_port}")
 
     def block(self) -> None:
-        """Make the machine unreachable by shutting down eth1 (the multicast
-        interface used to talk to the other VMs).  We keep eth0 up so that
-        the test driver can continue to talk to the machine.
+        """
+        Simulate unplugging the Ethernet cable that connects the machine to
+        the other machines.
+        This happens by shutting down eth1 (the multicast interface used to talk
+        to the other VMs). eth0 is kept online to still enable the test driver
+        to communicate with the machine.
         """
         self.send_monitor_command("set_link virtio-net-pci.1 off")
 
     def unblock(self) -> None:
-        """Make the machine reachable."""
+        """
+        Undo the effect of `block`.
+        """
         self.send_monitor_command("set_link virtio-net-pci.1 on")
 
     def release(self) -> None:
diff --git a/nixpkgs/nixos/lib/testing/driver.nix b/nixpkgs/nixos/lib/testing/driver.nix
index 444236efb1e7..23574698c062 100644
--- a/nixpkgs/nixos/lib/testing/driver.nix
+++ b/nixpkgs/nixos/lib/testing/driver.nix
@@ -65,7 +65,8 @@ let
           echo "${builtins.toString vlanNames}" >> testScriptWithTypes
           echo -n "$testScript" >> testScriptWithTypes
 
-          cat -n testScriptWithTypes
+          echo "Running type check (enable/disable: config.skipTypeCheck)"
+          echo "See https://nixos.org/manual/nixos/stable/#test-opt-skipTypeCheck"
 
           mypy  --no-implicit-optional \
                 --pretty \
@@ -79,6 +80,9 @@ let
 
         ${testDriver}/bin/generate-driver-symbols
         ${lib.optionalString (!config.skipLint) ''
+          echo "Linting test script (enable/disable: config.skipLint)"
+          echo "See https://nixos.org/manual/nixos/stable/#test-opt-skipLint"
+
           PYFLAKES_BUILTINS="$(
             echo -n ${lib.escapeShellArg (lib.concatStringsSep "," pythonizedNames)},
             < ${lib.escapeShellArg "driver-symbols"}
diff --git a/nixpkgs/nixos/lib/testing/nodes.nix b/nixpkgs/nixos/lib/testing/nodes.nix
index 6e439fd814db..f58759b4cdba 100644
--- a/nixpkgs/nixos/lib/testing/nodes.nix
+++ b/nixpkgs/nixos/lib/testing/nodes.nix
@@ -16,6 +16,7 @@ let
 
   baseOS =
     import ../eval-config.nix {
+      inherit lib;
       system = null; # use modularly defined system
       inherit (config.node) specialArgs;
       modules = [ config.defaults ];
diff --git a/nixpkgs/nixos/lib/utils.nix b/nixpkgs/nixos/lib/utils.nix
index def3aa13f320..7ea9d6a5c713 100644
--- a/nixpkgs/nixos/lib/utils.nix
+++ b/nixpkgs/nixos/lib/utils.nix
@@ -226,5 +226,8 @@ rec {
     lib = import ./systemd-lib.nix { inherit lib config pkgs; };
     unitOptions = import ./systemd-unit-options.nix { inherit lib systemdUtils; };
     types = import ./systemd-types.nix { inherit lib systemdUtils pkgs; };
+    network = {
+      units = import ./systemd-network-units.nix { inherit lib systemdUtils; };
+    };
   };
 }
diff --git a/nixpkgs/nixos/modules/config/fonts/fontconfig.nix b/nixpkgs/nixos/modules/config/fonts/fontconfig.nix
index 5781679241ef..5e2e054f7c4e 100644
--- a/nixpkgs/nixos/modules/config/fonts/fontconfig.nix
+++ b/nixpkgs/nixos/modules/config/fonts/fontconfig.nix
@@ -42,7 +42,7 @@ let
   # looking things up.
   makeCacheConf = { }:
     let
-      makeCache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.fonts; };
+      makeCache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.packages; };
       cache     = makeCache pkgs.fontconfig;
       cache32   = makeCache pkgs.pkgsi686Linux.fontconfig;
     in
@@ -51,7 +51,7 @@ let
       <!DOCTYPE fontconfig SYSTEM 'urn:fontconfig:fonts.dtd'>
       <fontconfig>
         <!-- Font directories -->
-        ${concatStringsSep "\n" (map (font: "<dir>${font}</dir>") config.fonts.fonts)}
+        ${concatStringsSep "\n" (map (font: "<dir>${font}</dir>") config.fonts.packages)}
         ${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
         <!-- Pre-generated font caches -->
         <cachedir>${cache}</cachedir>
@@ -77,18 +77,6 @@ let
         <edit mode="append" name="autohint">
           ${fcBool cfg.hinting.autohint}
         </edit>
-        <edit mode="append" name="hintstyle">
-          <const>${cfg.hinting.style}</const>
-        </edit>
-        <edit mode="append" name="antialias">
-          ${fcBool cfg.antialias}
-        </edit>
-        <edit mode="append" name="rgba">
-          <const>${cfg.subpixel.rgba}</const>
-        </edit>
-        <edit mode="append" name="lcdfilter">
-          <const>lcd${cfg.subpixel.lcdfilter}</const>
-        </edit>
       </match>
 
     </fontconfig>
@@ -177,6 +165,13 @@ let
     </fontconfig>
   '';
 
+  # Replace default linked config with a different variant
+  replaceDefaultConfig = defaultConfig: newConfig: ''
+    rm $dst/${defaultConfig}
+    ln -s ${pkg.out}/share/fontconfig/conf.avail/${newConfig} \
+          $dst/
+  '';
+
   # fontconfig configuration package
   confPkg = pkgs.runCommand "fontconfig-conf" {
     preferLocalBuild = true;
@@ -196,6 +191,26 @@ let
     ln -s ${pkg.out}/etc/fonts/conf.d/*.conf \
           $dst/
 
+    ${optionalString (!cfg.antialias)
+      (replaceDefaultConfig "10-yes-antialias.conf"
+        "10-no-antialias.conf")
+    }
+
+    ${optionalString (cfg.hinting.style != "slight")
+      (replaceDefaultConfig "10-hinting-slight.conf"
+        "10-hinting-${cfg.hinting.style}.conf")
+    }
+
+    ${optionalString (cfg.subpixel.rgba != "none")
+      (replaceDefaultConfig "10-sub-pixel-none.conf"
+        "10-sub-pixel-${cfg.subpixel.rgba}.conf")
+    }
+
+    ${optionalString (cfg.subpixel.lcdfilter != "default")
+      (replaceDefaultConfig "11-lcdfilter-default.conf"
+        "11-lcdfilter-${cfg.subpixel.lcdfilter}.conf")
+    }
+
     # 00-nixos-cache.conf
     ln -s ${cacheConf}  $dst/00-nixos-cache.conf
 
@@ -367,17 +382,25 @@ in
           };
 
           style = mkOption {
-            type = types.enum [ "hintnone" "hintslight" "hintmedium" "hintfull" ];
-            default = "hintslight";
+            type = types.enum ["none" "slight" "medium" "full"];
+            default = "slight";
             description = lib.mdDoc ''
               Hintstyle is the amount of font reshaping done to line up
               to the grid.
 
-              hintslight will make the font more fuzzy to line up to the grid
-              but will be better in retaining font shape, while hintfull will
-              be a crisp font that aligns well to the pixel grid but will lose
-              a greater amount of font shape.
+              slight will make the font more fuzzy to line up to the grid but
+              will be better in retaining font shape, while full will be a
+              crisp font that aligns well to the pixel grid but will lose a
+              greater amount of font shape.
             '';
+            apply =
+              val:
+              let
+                from = "fonts.fontconfig.hinting.style";
+                val' = lib.removePrefix "hint" val;
+                warning = "The option `${from}` contains a deprecated value `${val}`. Use `${val'}` instead.";
+              in
+              lib.warnIf (lib.hasPrefix "hint" val) warning val';
           };
         };
 
@@ -394,7 +417,7 @@ in
         subpixel = {
 
           rgba = mkOption {
-            default = "rgb";
+            default = "none";
             type = types.enum ["rgb" "bgr" "vrgb" "vbgr" "none"];
             description = lib.mdDoc ''
               Subpixel order. The overwhelming majority of displays are
diff --git a/nixpkgs/nixos/modules/config/fonts/fontdir.nix b/nixpkgs/nixos/modules/config/fonts/fontdir.nix
index 30e0dfe2566a..3b5eaf5b2d7f 100644
--- a/nixpkgs/nixos/modules/config/fonts/fontdir.nix
+++ b/nixpkgs/nixos/modules/config/fonts/fontdir.nix
@@ -8,8 +8,8 @@ let
 
   x11Fonts = pkgs.runCommand "X11-fonts" { preferLocalBuild = true; } ''
     mkdir -p "$out/share/X11/fonts"
-    font_regexp='.*\.\(ttf\|ttc\|otf\|pcf\|pfa\|pfb\|bdf\)\(\.gz\)?'
-    find ${toString config.fonts.fonts} -regex "$font_regexp" \
+    font_regexp='.*\.\(ttf\|ttc\|otb\|otf\|pcf\|pfa\|pfb\|bdf\)\(\.gz\)?'
+    find ${toString config.fonts.packages} -regex "$font_regexp" \
       -exec ln -sf -t "$out/share/X11/fonts" '{}' \;
     cd "$out/share/X11/fonts"
     ${optionalString cfg.decompressFonts ''
diff --git a/nixpkgs/nixos/modules/config/fonts/fonts.nix b/nixpkgs/nixos/modules/config/fonts/fonts.nix
deleted file mode 100644
index 87cf837e7c80..000000000000
--- a/nixpkgs/nixos/modules/config/fonts/fonts.nix
+++ /dev/null
@@ -1,47 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.fonts;
-
-  defaultFonts =
-    [ pkgs.dejavu_fonts
-      pkgs.freefont_ttf
-      pkgs.gyre-fonts # TrueType substitutes for standard PostScript fonts
-      pkgs.liberation_ttf
-      pkgs.unifont
-      pkgs.noto-fonts-emoji
-    ];
-in
-{
-  imports = [
-    (mkRemovedOptionModule [ "fonts" "enableCoreFonts" ] "Use fonts.fonts = [ pkgs.corefonts ]; instead.")
-  ];
-
-  options = {
-
-    fonts = {
-
-      # TODO: find another name for it.
-      fonts = mkOption {
-        type = types.listOf types.path;
-        default = [];
-        example = literalExpression "[ pkgs.dejavu_fonts ]";
-        description = lib.mdDoc "List of primary font paths.";
-      };
-
-      enableDefaultFonts = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Enable a basic set of fonts providing several font styles
-          and families and reasonable coverage of Unicode.
-        '';
-      };
-    };
-
-  };
-
-  config = { fonts.fonts = mkIf cfg.enableDefaultFonts defaultFonts; };
-}
diff --git a/nixpkgs/nixos/modules/config/fonts/ghostscript.nix b/nixpkgs/nixos/modules/config/fonts/ghostscript.nix
index c284c4a0b0ab..c41fcdaaa329 100644
--- a/nixpkgs/nixos/modules/config/fonts/ghostscript.nix
+++ b/nixpkgs/nixos/modules/config/fonts/ghostscript.nix
@@ -3,31 +3,21 @@
 with lib;
 
 {
-
   options = {
-
-    fonts = {
-
-      enableGhostscriptFonts = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Whether to add the fonts provided by Ghostscript (such as
-          various URW fonts and the “Base-14” Postscript fonts) to the
-          list of system fonts, making them available to X11
-          applications.
-        '';
-      };
-
+    fonts.enableGhostscriptFonts = mkOption {
+      type = types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Whether to add the fonts provided by Ghostscript (such as
+        various URW fonts and the “Base-14” Postscript fonts) to the
+        list of system fonts, making them available to X11
+        applications.
+      '';
     };
 
   };
 
-
   config = mkIf config.fonts.enableGhostscriptFonts {
-
-    fonts.fonts = [ "${pkgs.ghostscript}/share/ghostscript/fonts" ];
-
+    fonts.packages = [ "${pkgs.ghostscript}/share/ghostscript/fonts" ];
   };
-
 }
diff --git a/nixpkgs/nixos/modules/config/fonts/packages.nix b/nixpkgs/nixos/modules/config/fonts/packages.nix
new file mode 100644
index 000000000000..46907d5411ca
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/fonts/packages.nix
@@ -0,0 +1,43 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.fonts;
+in
+{
+  imports = [
+    (lib.mkRemovedOptionModule [ "fonts" "enableCoreFonts" ] "Use fonts.packages = [ pkgs.corefonts ]; instead.")
+    (lib.mkRenamedOptionModule [ "fonts" "enableDefaultFonts" ] [ "fonts" "enableDefaultPackages" ])
+    (lib.mkRenamedOptionModule [ "fonts" "fonts" ] [ "fonts" "packages" ])
+  ];
+
+  options = {
+    fonts = {
+      packages = lib.mkOption {
+        type = with lib.types; listOf path;
+        default = [];
+        example = lib.literalExpression "[ pkgs.dejavu_fonts ]";
+        description = lib.mdDoc "List of primary font packages.";
+      };
+
+      enableDefaultPackages = lib.mkOption {
+        type = lib.types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Enable a basic set of fonts providing several styles
+          and families and reasonable coverage of Unicode.
+        '';
+      };
+    };
+  };
+
+  config = {
+    fonts.packages = lib.mkIf cfg.enableDefaultPackages (with pkgs; [
+      dejavu_fonts
+      freefont_ttf
+      gyre-fonts # TrueType substitutes for standard PostScript fonts
+      liberation_ttf
+      unifont
+      noto-fonts-emoji
+    ]);
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/i18n.nix b/nixpkgs/nixos/modules/config/i18n.nix
index b1efc00773dc..b19d38091e75 100644
--- a/nixpkgs/nixos/modules/config/i18n.nix
+++ b/nixpkgs/nixos/modules/config/i18n.nix
@@ -66,6 +66,7 @@ with lib;
             (builtins.map (l: (replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") (
               [
                 "C.UTF-8"
+                "en_US.UTF-8"
                 config.i18n.defaultLocale
               ] ++ (attrValues (filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
             ))
diff --git a/nixpkgs/nixos/modules/config/malloc.nix b/nixpkgs/nixos/modules/config/malloc.nix
index ae0661f472f6..3d70e091983b 100644
--- a/nixpkgs/nixos/modules/config/malloc.nix
+++ b/nixpkgs/nixos/modules/config/malloc.nix
@@ -97,7 +97,7 @@ in
   };
 
   config = mkIf (cfg.provider != "libc") {
-    boot.kernel.sysctl."vm.max_map_count" = mkIf (cfg.provider == "graphene-hardened") (mkDefault 1048576);
+    boot.kernel.sysctl."vm.max_map_count" = mkIf (cfg.provider == "graphene-hardened") (mkDefault 1048576); # TODO: Default vm.max_map_count has been increased system-wide
     environment.etc."ld-nix.so.preload".text = ''
       ${providerLibPath}
     '';
diff --git a/nixpkgs/nixos/modules/config/nix-channel.nix b/nixpkgs/nixos/modules/config/nix-channel.nix
new file mode 100644
index 000000000000..3f8e088ede92
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/nix-channel.nix
@@ -0,0 +1,108 @@
+/*
+  Manages the things that are needed for a traditional nix-channel based
+  configuration to work.
+
+  See also
+  - ./nix.nix
+  - ./nix-flakes.nix
+ */
+{ config, lib, ... }:
+let
+  inherit (lib)
+    mkDefault
+    mkIf
+    mkOption
+    stringAfter
+    types
+    ;
+
+  cfg = config.nix;
+
+in
+{
+  options = {
+    nix = {
+      channel = {
+        enable = mkOption {
+          description = lib.mdDoc ''
+            Whether the `nix-channel` command and state files are made available on the machine.
+
+            The following files are initialized when enabled:
+              - `/nix/var/nix/profiles/per-user/root/channels`
+              - `/root/.nix-channels`
+              - `$HOME/.nix-defexpr/channels` (on login)
+
+            Disabling this option will not remove the state files from the system.
+          '';
+          type = types.bool;
+          default = true;
+        };
+      };
+
+      nixPath = mkOption {
+        type = types.listOf types.str;
+        default =
+          if cfg.channel.enable
+          then [
+            "nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
+            "nixos-config=/etc/nixos/configuration.nix"
+            "/nix/var/nix/profiles/per-user/root/channels"
+          ]
+          else [ ];
+        defaultText = ''
+          if nix.channel.enable
+          then [
+            "nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
+            "nixos-config=/etc/nixos/configuration.nix"
+            "/nix/var/nix/profiles/per-user/root/channels"
+          ]
+          else [];
+        '';
+        description = lib.mdDoc ''
+          The default Nix expression search path, used by the Nix
+          evaluator to look up paths enclosed in angle brackets
+          (e.g. `<nixpkgs>`).
+        '';
+      };
+    };
+
+    system = {
+      defaultChannel = mkOption {
+        internal = true;
+        type = types.str;
+        default = "https://nixos.org/channels/nixos-unstable";
+        description = lib.mdDoc "Default NixOS channel to which the root user is subscribed.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.extraInit =
+      mkIf cfg.channel.enable ''
+        if [ -e "$HOME/.nix-defexpr/channels" ]; then
+          export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
+        fi
+      '';
+
+    environment.extraSetup = mkIf (!cfg.channel.enable) ''
+      rm --force $out/bin/nix-channel
+    '';
+
+    # NIX_PATH has a non-empty default according to Nix docs, so we don't unset
+    # it when empty.
+    environment.sessionVariables = {
+      NIX_PATH = cfg.nixPath;
+    };
+
+    nix.settings.nix-path = mkIf (! cfg.channel.enable) (mkDefault "");
+
+    system.activationScripts.nix-channel = mkIf cfg.channel.enable
+      (stringAfter [ "etc" "users" ] ''
+        # Subscribe the root user to the NixOS channel by default.
+        if [ ! -e "/root/.nix-channels" ]; then
+            echo "${config.system.defaultChannel} nixos" > "/root/.nix-channels"
+        fi
+      '');
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/nix-flakes.nix b/nixpkgs/nixos/modules/config/nix-flakes.nix
new file mode 100644
index 000000000000..242d8d3b82b7
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/nix-flakes.nix
@@ -0,0 +1,95 @@
+/*
+  Manages the flake registry.
+
+  See also
+   - ./nix.nix
+   - ./nix-channel.nix
+ */
+{ config, lib, ... }:
+let
+  inherit (lib)
+    filterAttrs
+    literalExpression
+    mapAttrsToList
+    mkDefault
+    mkIf
+    mkOption
+    types
+    ;
+
+  cfg = config.nix;
+
+in
+{
+  options = {
+    nix = {
+      registry = mkOption {
+        type = types.attrsOf (types.submodule (
+          let
+            referenceAttrs = with types; attrsOf (oneOf [
+              str
+              int
+              bool
+              path
+              package
+            ]);
+          in
+          { config, name, ... }:
+          {
+            options = {
+              from = mkOption {
+                type = referenceAttrs;
+                example = { type = "indirect"; id = "nixpkgs"; };
+                description = lib.mdDoc "The flake reference to be rewritten.";
+              };
+              to = mkOption {
+                type = referenceAttrs;
+                example = { type = "github"; owner = "my-org"; repo = "my-nixpkgs"; };
+                description = lib.mdDoc "The flake reference {option}`from` is rewritten to.";
+              };
+              flake = mkOption {
+                type = types.nullOr types.attrs;
+                default = null;
+                example = literalExpression "nixpkgs";
+                description = lib.mdDoc ''
+                  The flake input {option}`from` is rewritten to.
+                '';
+              };
+              exact = mkOption {
+                type = types.bool;
+                default = true;
+                description = lib.mdDoc ''
+                  Whether the {option}`from` reference needs to match exactly. If set,
+                  a {option}`from` reference like `nixpkgs` does not
+                  match with a reference like `nixpkgs/nixos-20.03`.
+                '';
+              };
+            };
+            config = {
+              from = mkDefault { type = "indirect"; id = name; };
+              to = mkIf (config.flake != null) (mkDefault (
+                {
+                  type = "path";
+                  path = config.flake.outPath;
+                } // filterAttrs
+                  (n: _: n == "lastModified" || n == "rev" || n == "revCount" || n == "narHash")
+                  config.flake
+              ));
+            };
+          }
+        ));
+        default = { };
+        description = lib.mdDoc ''
+          A system-wide flake registry.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.etc."nix/registry.json".text = builtins.toJSON {
+      version = 2;
+      flakes = mapAttrsToList (n: v: { inherit (v) from to exact; }) cfg.registry;
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/nix-remote-build.nix b/nixpkgs/nixos/modules/config/nix-remote-build.nix
new file mode 100644
index 000000000000..98c8fc06d2ee
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/nix-remote-build.nix
@@ -0,0 +1,226 @@
+/*
+  Manages the remote build configuration, /etc/nix/machines
+
+  See also
+   - ./nix.nix
+   - nixos/modules/services/system/nix-daemon.nix
+ */
+{ config, lib, ... }:
+
+let
+  inherit (lib)
+    any
+    concatMapStrings
+    concatStringsSep
+    filter
+    getVersion
+    mkIf
+    mkMerge
+    mkOption
+    optional
+    optionalString
+    types
+    versionAtLeast
+    ;
+
+  cfg = config.nix;
+
+  nixPackage = cfg.package.out;
+
+  isNixAtLeast = versionAtLeast (getVersion nixPackage);
+
+  buildMachinesText =
+    concatMapStrings
+      (machine:
+        (concatStringsSep " " ([
+          "${optionalString (machine.protocol != null) "${machine.protocol}://"}${optionalString (machine.sshUser != null) "${machine.sshUser}@"}${machine.hostName}"
+          (if machine.system != null then machine.system else if machine.systems != [ ] then concatStringsSep "," machine.systems else "-")
+          (if machine.sshKey != null then machine.sshKey else "-")
+          (toString machine.maxJobs)
+          (toString machine.speedFactor)
+          (let res = (machine.supportedFeatures ++ machine.mandatoryFeatures);
+            in if (res == []) then "-" else (concatStringsSep "," res))
+          (let res = machine.mandatoryFeatures;
+            in if (res == []) then "-" else (concatStringsSep "," machine.mandatoryFeatures))
+        ]
+        ++ optional (isNixAtLeast "2.4pre") (if machine.publicHostKey != null then machine.publicHostKey else "-")))
+        + "\n"
+      )
+      cfg.buildMachines;
+
+in
+{
+  options = {
+    nix = {
+      buildMachines = mkOption {
+        type = types.listOf (types.submodule {
+          options = {
+            hostName = mkOption {
+              type = types.str;
+              example = "nixbuilder.example.org";
+              description = lib.mdDoc ''
+                The hostname of the build machine.
+              '';
+            };
+            protocol = mkOption {
+              type = types.enum [ null "ssh" "ssh-ng" ];
+              default = "ssh";
+              example = "ssh-ng";
+              description = lib.mdDoc ''
+                The protocol used for communicating with the build machine.
+                Use `ssh-ng` if your remote builder and your
+                local Nix version support that improved protocol.
+
+                Use `null` when trying to change the special localhost builder
+                without a protocol which is for example used by hydra.
+              '';
+            };
+            system = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "x86_64-linux";
+              description = lib.mdDoc ''
+                The system type the build machine can execute derivations on.
+                Either this attribute or {var}`systems` must be
+                present, where {var}`system` takes precedence if
+                both are set.
+              '';
+            };
+            systems = mkOption {
+              type = types.listOf types.str;
+              default = [ ];
+              example = [ "x86_64-linux" "aarch64-linux" ];
+              description = lib.mdDoc ''
+                The system types the build machine can execute derivations on.
+                Either this attribute or {var}`system` must be
+                present, where {var}`system` takes precedence if
+                both are set.
+              '';
+            };
+            sshUser = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "builder";
+              description = lib.mdDoc ''
+                The username to log in as on the remote host. This user must be
+                able to log in and run nix commands non-interactively. It must
+                also be privileged to build derivations, so must be included in
+                {option}`nix.settings.trusted-users`.
+              '';
+            };
+            sshKey = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "/root/.ssh/id_buildhost_builduser";
+              description = lib.mdDoc ''
+                The path to the SSH private key with which to authenticate on
+                the build machine. The private key must not have a passphrase.
+                If null, the building user (root on NixOS machines) must have an
+                appropriate ssh configuration to log in non-interactively.
+
+                Note that for security reasons, this path must point to a file
+                in the local filesystem, *not* to the nix store.
+              '';
+            };
+            maxJobs = mkOption {
+              type = types.int;
+              default = 1;
+              description = lib.mdDoc ''
+                The number of concurrent jobs the build machine supports. The
+                build machine will enforce its own limits, but this allows hydra
+                to schedule better since there is no work-stealing between build
+                machines.
+              '';
+            };
+            speedFactor = mkOption {
+              type = types.int;
+              default = 1;
+              description = lib.mdDoc ''
+                The relative speed of this builder. This is an arbitrary integer
+                that indicates the speed of this builder, relative to other
+                builders. Higher is faster.
+              '';
+            };
+            mandatoryFeatures = mkOption {
+              type = types.listOf types.str;
+              default = [ ];
+              example = [ "big-parallel" ];
+              description = lib.mdDoc ''
+                A list of features mandatory for this builder. The builder will
+                be ignored for derivations that don't require all features in
+                this list. All mandatory features are automatically included in
+                {var}`supportedFeatures`.
+              '';
+            };
+            supportedFeatures = mkOption {
+              type = types.listOf types.str;
+              default = [ ];
+              example = [ "kvm" "big-parallel" ];
+              description = lib.mdDoc ''
+                A list of features supported by this builder. The builder will
+                be ignored for derivations that require features not in this
+                list.
+              '';
+            };
+            publicHostKey = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              description = lib.mdDoc ''
+                The (base64-encoded) public host key of this builder. The field
+                is calculated via {command}`base64 -w0 /etc/ssh/ssh_host_type_key.pub`.
+                If null, SSH will use its regular known-hosts file when connecting.
+              '';
+            };
+          };
+        });
+        default = [ ];
+        description = lib.mdDoc ''
+          This option lists the machines to be used if distributed builds are
+          enabled (see {option}`nix.distributedBuilds`).
+          Nix will perform derivations on those machines via SSH by copying the
+          inputs to the Nix store on the remote machine, starting the build,
+          then copying the output back to the local Nix store.
+        '';
+      };
+
+      distributedBuilds = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Whether to distribute builds to the machines listed in
+          {option}`nix.buildMachines`.
+        '';
+      };
+    };
+  };
+
+  # distributedBuilds does *not* inhibit /etc/machines generation; caller may
+  # override that nix option.
+  config = mkIf cfg.enable {
+    assertions =
+      let badMachine = m: m.system == null && m.systems == [ ];
+      in
+      [
+        {
+          assertion = !(any badMachine cfg.buildMachines);
+          message = ''
+            At least one system type (via <varname>system</varname> or
+              <varname>systems</varname>) must be set for every build machine.
+              Invalid machine specifications:
+          '' + "      " +
+          (concatStringsSep "\n      "
+            (map (m: m.hostName)
+              (filter (badMachine) cfg.buildMachines)));
+        }
+      ];
+
+    # List of machines for distributed Nix builds
+    environment.etc."nix/machines" =
+      mkIf (cfg.buildMachines != [ ]) {
+        text = buildMachinesText;
+      };
+
+    # Legacy configuration conversion.
+    nix.settings = mkIf (!cfg.distributedBuilds) { builders = null; };
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/nix.nix b/nixpkgs/nixos/modules/config/nix.nix
new file mode 100644
index 000000000000..cee4f54db0cb
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/nix.nix
@@ -0,0 +1,379 @@
+/*
+  Manages /etc/nix.conf.
+
+  See also
+   - ./nix-channel.nix
+   - ./nix-flakes.nix
+   - ./nix-remote-build.nix
+   - nixos/modules/services/system/nix-daemon.nix
+ */
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib)
+    concatStringsSep
+    boolToString
+    escape
+    floatToString
+    getVersion
+    isBool
+    isDerivation
+    isFloat
+    isInt
+    isList
+    isString
+    literalExpression
+    mapAttrsToList
+    mkAfter
+    mkDefault
+    mkIf
+    mkOption
+    mkRenamedOptionModuleWith
+    optionalString
+    optionals
+    strings
+    systems
+    toPretty
+    types
+    versionAtLeast
+    ;
+
+  cfg = config.nix;
+
+  nixPackage = cfg.package.out;
+
+  isNixAtLeast = versionAtLeast (getVersion nixPackage);
+
+  legacyConfMappings = {
+    useSandbox = "sandbox";
+    buildCores = "cores";
+    maxJobs = "max-jobs";
+    sandboxPaths = "extra-sandbox-paths";
+    binaryCaches = "substituters";
+    trustedBinaryCaches = "trusted-substituters";
+    binaryCachePublicKeys = "trusted-public-keys";
+    autoOptimiseStore = "auto-optimise-store";
+    requireSignedBinaryCaches = "require-sigs";
+    trustedUsers = "trusted-users";
+    allowedUsers = "allowed-users";
+    systemFeatures = "system-features";
+  };
+
+  semanticConfType = with types;
+    let
+      confAtom = nullOr
+        (oneOf [
+          bool
+          int
+          float
+          str
+          path
+          package
+        ]) // {
+        description = "Nix config atom (null, bool, int, float, str, path or package)";
+      };
+    in
+    attrsOf (either confAtom (listOf confAtom));
+
+  nixConf =
+    assert isNixAtLeast "2.2";
+    let
+
+      mkValueString = v:
+        if v == null then ""
+        else if isInt v then toString v
+        else if isBool v then boolToString v
+        else if isFloat v then floatToString v
+        else if isList v then toString v
+        else if isDerivation v then toString v
+        else if builtins.isPath v then toString v
+        else if isString v then v
+        else if strings.isConvertibleWithToString v then toString v
+        else abort "The nix conf value: ${toPretty {} v} can not be encoded";
+
+      mkKeyValue = k: v: "${escape [ "=" ] k} = ${mkValueString v}";
+
+      mkKeyValuePairs = attrs: concatStringsSep "\n" (mapAttrsToList mkKeyValue attrs);
+
+    in
+    pkgs.writeTextFile {
+      name = "nix.conf";
+      text = ''
+        # WARNING: this file is generated from the nix.* options in
+        # your NixOS configuration, typically
+        # /etc/nixos/configuration.nix.  Do not edit it!
+        ${mkKeyValuePairs cfg.settings}
+        ${cfg.extraOptions}
+      '';
+      checkPhase = lib.optionalString cfg.checkConfig (
+        if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then ''
+          echo "Ignoring validation for cross-compilation"
+        ''
+        else ''
+          echo "Validating generated nix.conf"
+          ln -s $out ./nix.conf
+          set -e
+          set +o pipefail
+          NIX_CONF_DIR=$PWD \
+            ${cfg.package}/bin/nix show-config ${optionalString (isNixAtLeast "2.3pre") "--no-net"} \
+              ${optionalString (isNixAtLeast "2.4pre") "--option experimental-features nix-command"} \
+            |& sed -e 's/^warning:/error:/' \
+            | (! grep '${if cfg.checkAllErrors then "^error:" else "^error: unknown setting"}')
+          set -o pipefail
+        '');
+    };
+
+in
+{
+  imports = [
+    (mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "useChroot" ]; to = [ "nix" "useSandbox" ]; })
+    (mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "chrootDirs" ]; to = [ "nix" "sandboxPaths" ]; })
+  ] ++
+    mapAttrsToList
+      (oldConf: newConf:
+        mkRenamedOptionModuleWith {
+          sinceRelease = 2205;
+          from = [ "nix" oldConf ];
+          to = [ "nix" "settings" newConf ];
+      })
+      legacyConfMappings;
+
+  options = {
+    nix = {
+      checkConfig = mkOption {
+        type = types.bool;
+        default = true;
+        description = lib.mdDoc ''
+          If enabled, checks that Nix can parse the generated nix.conf.
+        '';
+      };
+
+      checkAllErrors = mkOption {
+        type = types.bool;
+        default = true;
+        description = lib.mdDoc ''
+          If enabled, checks the nix.conf parsing for any kind of error. When disabled, checks only for unknown settings.
+        '';
+      };
+
+      extraOptions = mkOption {
+        type = types.lines;
+        default = "";
+        example = ''
+          keep-outputs = true
+          keep-derivations = true
+        '';
+        description = lib.mdDoc "Additional text appended to {file}`nix.conf`.";
+      };
+
+      settings = mkOption {
+        type = types.submodule {
+          freeformType = semanticConfType;
+
+          options = {
+            max-jobs = mkOption {
+              type = types.either types.int (types.enum [ "auto" ]);
+              default = "auto";
+              example = 64;
+              description = lib.mdDoc ''
+                This option defines the maximum number of jobs that Nix will try to
+                build in parallel. The default is auto, which means it will use all
+                available logical cores. It is recommend to set it to the total
+                number of logical cores in your system (e.g., 16 for two CPUs with 4
+                cores each and hyper-threading).
+              '';
+            };
+
+            auto-optimise-store = mkOption {
+              type = types.bool;
+              default = false;
+              example = true;
+              description = lib.mdDoc ''
+                If set to true, Nix automatically detects files in the store that have
+                identical contents, and replaces them with hard links to a single copy.
+                This saves disk space. If set to false (the default), you can still run
+                nix-store --optimise to get rid of duplicate files.
+              '';
+            };
+
+            cores = mkOption {
+              type = types.int;
+              default = 0;
+              example = 64;
+              description = lib.mdDoc ''
+                This option defines the maximum number of concurrent tasks during
+                one build. It affects, e.g., -j option for make.
+                The special value 0 means that the builder should use all
+                available CPU cores in the system. Some builds may become
+                non-deterministic with this option; use with care! Packages will
+                only be affected if enableParallelBuilding is set for them.
+              '';
+            };
+
+            sandbox = mkOption {
+              type = types.either types.bool (types.enum [ "relaxed" ]);
+              default = true;
+              description = lib.mdDoc ''
+                If set, Nix will perform builds in a sandboxed environment that it
+                will set up automatically for each build. This prevents impurities
+                in builds by disallowing access to dependencies outside of the Nix
+                store by using network and mount namespaces in a chroot environment.
+
+                This is enabled by default even though it has a possible performance
+                impact due to the initial setup time of a sandbox for each build. It
+                doesn't affect derivation hashes, so changing this option will not
+                trigger a rebuild of packages.
+
+                When set to "relaxed", this option permits derivations that set
+                `__noChroot = true;` to run outside of the sandboxed environment.
+                Exercise caution when using this mode of operation! It is intended to
+                be a quick hack when building with packages that are not easily setup
+                to be built reproducibly.
+              '';
+            };
+
+            extra-sandbox-paths = mkOption {
+              type = types.listOf types.str;
+              default = [ ];
+              example = [ "/dev" "/proc" ];
+              description = lib.mdDoc ''
+                Directories from the host filesystem to be included
+                in the sandbox.
+              '';
+            };
+
+            substituters = mkOption {
+              type = types.listOf types.str;
+              description = lib.mdDoc ''
+                List of binary cache URLs used to obtain pre-built binaries
+                of Nix packages.
+
+                By default https://cache.nixos.org/ is added.
+              '';
+            };
+
+            trusted-substituters = mkOption {
+              type = types.listOf types.str;
+              default = [ ];
+              example = [ "https://hydra.nixos.org/" ];
+              description = lib.mdDoc ''
+                List of binary cache URLs that non-root users can use (in
+                addition to those specified using
+                {option}`nix.settings.substituters`) by passing
+                `--option binary-caches` to Nix commands.
+              '';
+            };
+
+            require-sigs = mkOption {
+              type = types.bool;
+              default = true;
+              description = lib.mdDoc ''
+                If enabled (the default), Nix will only download binaries from binary caches if
+                they are cryptographically signed with any of the keys listed in
+                {option}`nix.settings.trusted-public-keys`. If disabled, signatures are neither
+                required nor checked, so it's strongly recommended that you use only
+                trustworthy caches and https to prevent man-in-the-middle attacks.
+              '';
+            };
+
+            trusted-public-keys = mkOption {
+              type = types.listOf types.str;
+              example = [ "hydra.nixos.org-1:CNHJZBh9K4tP3EKF6FkkgeVYsS3ohTl+oS0Qa8bezVs=" ];
+              description = lib.mdDoc ''
+                List of public keys used to sign binary caches. If
+                {option}`nix.settings.trusted-public-keys` is enabled,
+                then Nix will use a binary from a binary cache if and only
+                if it is signed by *any* of the keys
+                listed here. By default, only the key for
+                `cache.nixos.org` is included.
+              '';
+            };
+
+            trusted-users = mkOption {
+              type = types.listOf types.str;
+              default = [ "root" ];
+              example = [ "root" "alice" "@wheel" ];
+              description = lib.mdDoc ''
+                A list of names of users that have additional rights when
+                connecting to the Nix daemon, such as the ability to specify
+                additional binary caches, or to import unsigned NARs. You
+                can also specify groups by prefixing them with
+                `@`; for instance,
+                `@wheel` means all users in the wheel
+                group.
+              '';
+            };
+
+            system-features = mkOption {
+              type = types.listOf types.str;
+              example = [ "kvm" "big-parallel" "gccarch-skylake" ];
+              description = lib.mdDoc ''
+                The set of features supported by the machine. Derivations
+                can express dependencies on system features through the
+                `requiredSystemFeatures` attribute.
+
+                By default, pseudo-features `nixos-test`, `benchmark`,
+                and `big-parallel` used in Nixpkgs are set, `kvm`
+                is also included if it is available.
+              '';
+            };
+
+            allowed-users = mkOption {
+              type = types.listOf types.str;
+              default = [ "*" ];
+              example = [ "@wheel" "@builders" "alice" "bob" ];
+              description = lib.mdDoc ''
+                A list of names of users (separated by whitespace) that are
+                allowed to connect to the Nix daemon. As with
+                {option}`nix.settings.trusted-users`, you can specify groups by
+                prefixing them with `@`. Also, you can
+                allow all users by specifying `*`. The
+                default is `*`. Note that trusted users are
+                always allowed to connect.
+              '';
+            };
+          };
+        };
+        default = { };
+        example = literalExpression ''
+          {
+            use-sandbox = true;
+            show-trace = true;
+
+            system-features = [ "big-parallel" "kvm" "recursive-nix" ];
+            sandbox-paths = { "/bin/sh" = "''${pkgs.busybox-sandbox-shell.out}/bin/busybox"; };
+          }
+        '';
+        description = lib.mdDoc ''
+          Configuration for Nix, see
+          <https://nixos.org/manual/nix/stable/command-ref/conf-file.html> or
+          {manpage}`nix.conf(5)` for available options.
+          The value declared here will be translated directly to the key-value pairs Nix expects.
+
+          You can use {command}`nix-instantiate --eval --strict '<nixpkgs/nixos>' -A config.nix.settings`
+          to view the current value. By default it is empty.
+
+          Nix configurations defined under {option}`nix.*` will be translated and applied to this
+          option. In addition, configuration specified in {option}`nix.extraOptions` will be appended
+          verbatim to the resulting config file.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.etc."nix/nix.conf".source = nixConf;
+    nix.settings = {
+      trusted-public-keys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ];
+      substituters = mkAfter [ "https://cache.nixos.org/" ];
+      system-features = mkDefault (
+        [ "nixos-test" "benchmark" "big-parallel" "kvm" ] ++
+        optionals (pkgs.stdenv.hostPlatform ? gcc.arch) (
+          # a builder can run code for `gcc.arch` and inferior architectures
+          [ "gccarch-${pkgs.stdenv.hostPlatform.gcc.arch}" ] ++
+          map (x: "gccarch-${x}") (systems.architectures.inferiors.${pkgs.stdenv.hostPlatform.gcc.arch} or [])
+        )
+      );
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/no-x-libs.nix b/nixpkgs/nixos/modules/config/no-x-libs.nix
index 0dce3b918458..f8622be59a1b 100644
--- a/nixpkgs/nixos/modules/config/no-x-libs.nix
+++ b/nixpkgs/nixos/modules/config/no-x-libs.nix
@@ -26,7 +26,12 @@ with lib;
 
     fonts.fontconfig.enable = false;
 
-    nixpkgs.overlays = singleton (const (super: {
+    nixpkgs.overlays = singleton (self: super: let
+      packageOverrides = const (python-prev: {
+        # tk feature requires wayland which fails to compile
+        matplotlib = python-prev.matplotlib.override { enableGtk3 = false; enableTk = false; enableQt = false; };
+      });
+    in {
       beam = super.beam_nox;
       cairo = super.cairo.override { x11Support = false; };
       dbus = super.dbus.override { x11Support = false; };
@@ -39,7 +44,8 @@ with lib;
       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 { enableX11 = false; };
+        gst-plugins-base = super.gst_all_1.gst-plugins-base.override { enableWayland = false; enableX11 = false; };
+        gst-plugins-good = super.gst_all_1.gst-plugins-good.override { enableX11 = false; };
       };
       imagemagick = super.imagemagick.override { libX11Support = false; libXtSupport = false; };
       imagemagickBig = super.imagemagickBig.override { libX11Support = false; libXtSupport = false; };
@@ -61,6 +67,8 @@ with lib;
       pango = super.pango.override { x11Support = false; };
       pinentry = super.pinentry.override { enabledFlavors = [ "curses" "tty" "emacs" ]; withLibsecret = false; };
       pipewire = super.pipewire.override { x11Support = false; };
+      python3 = super.python3.override { inherit packageOverrides; };
+      python3Packages = self.python3.pkgs; # required otherwise overlays from above are not forwarded
       qemu = super.qemu.override { gtkSupport = false; spiceSupport = false; sdlSupport = false; };
       qrencode = super.qrencode.overrideAttrs (_: { doCheck = false; });
       qt5 = super.qt5.overrideScope (const (super': {
@@ -71,6 +79,6 @@ with lib;
       util-linux = super.util-linux.override { translateManpages = false; };
       vim-full = super.vim-full.override { guiSupport = false; };
       zbar = super.zbar.override { enableVideo = false; withXorg = false; };
-    }));
+    });
   };
 }
diff --git a/nixpkgs/nixos/modules/config/qt.nix b/nixpkgs/nixos/modules/config/qt.nix
index cf4e9621d70d..2b09281e467f 100644
--- a/nixpkgs/nixos/modules/config/qt.nix
+++ b/nixpkgs/nixos/modules/config/qt.nix
@@ -19,7 +19,7 @@ let
       pkgs.qgnomeplatform-qt6
       pkgs.adwaita-qt6
     ]
-    else if isQtStyle then [ pkgs.libsForQt5.qtstyleplugins ]
+    else if isQtStyle then [ pkgs.libsForQt5.qtstyleplugins pkgs.qt6Packages.qt6gtk2 ]
     else if isQt5ct then [ pkgs.libsForQt5.qt5ct pkgs.qt6Packages.qt6ct ]
     else if isLxqt then [ pkgs.lxqt.lxqt-qtplugin pkgs.lxqt.lxqt-config ]
     else if isKde then [ pkgs.libsForQt5.plasma-integration pkgs.libsForQt5.systemsettings ]
@@ -86,6 +86,7 @@ in
           "adwaita-qt"
           "adwaita-qt6"
           ["libsForQt5" "qtstyleplugins"]
+          ["qt6Packages" "qt6gtk2"]
         ];
         description = lib.mdDoc ''
           Selects the style to use for Qt applications.
diff --git a/nixpkgs/nixos/modules/config/swap.nix b/nixpkgs/nixos/modules/config/swap.nix
index 0a7e45bffb26..8989a6408264 100644
--- a/nixpkgs/nixos/modules/config/swap.nix
+++ b/nixpkgs/nixos/modules/config/swap.nix
@@ -252,6 +252,11 @@ in
           let realDevice' = escapeSystemdPath sw.realDevice;
           in nameValuePair "mkswap-${sw.deviceName}"
           { description = "Initialisation of swap device ${sw.device}";
+            # The mkswap service fails for file-backed swap devices if the
+            # loop module has not been loaded before the service runs.
+            # We add an ordering constraint to run after systemd-modules-load to
+            # avoid this race condition.
+            after = [ "systemd-modules-load.service" ];
             wantedBy = [ "${realDevice'}.swap" ];
             before = [ "${realDevice'}.swap" ];
             path = [ pkgs.util-linux pkgs.e2fsprogs ]
diff --git a/nixpkgs/nixos/modules/config/sysctl.nix b/nixpkgs/nixos/modules/config/sysctl.nix
index 4346c88f7688..0bc7ab9667f9 100644
--- a/nixpkgs/nixos/modules/config/sysctl.nix
+++ b/nixpkgs/nixos/modules/config/sysctl.nix
@@ -72,5 +72,8 @@ in
     # Disable YAMA by default to allow easy debugging.
     boot.kernel.sysctl."kernel.yama.ptrace_scope" = mkDefault 0;
 
+    # Improve compatibility with applications that allocate
+    # a lot of memory, like modern games
+    boot.kernel.sysctl."vm.max_map_count" = mkDefault 1048576;
   };
 }
diff --git a/nixpkgs/nixos/modules/config/update-users-groups.pl b/nixpkgs/nixos/modules/config/update-users-groups.pl
index 7dd2b858bb2a..301fd0026400 100644
--- a/nixpkgs/nixos/modules/config/update-users-groups.pl
+++ b/nixpkgs/nixos/modules/config/update-users-groups.pl
@@ -148,7 +148,7 @@ foreach my $g (@{$spec->{groups}}) {
     if (defined $existing) {
         $g->{gid} = $existing->{gid} if !defined $g->{gid};
         if ($g->{gid} != $existing->{gid}) {
-            dry_print("warning: not applying", "warning: would not apply", "GID change of group ‘$name’ ($existing->{gid} -> $g->{gid})");
+            dry_print("warning: not applying", "warning: would not apply", "GID change of group ‘$name’ ($existing->{gid} -> $g->{gid}) in /etc/group");
             $g->{gid} = $existing->{gid};
         }
         $g->{password} = $existing->{password}; # do we want this?
@@ -210,7 +210,7 @@ foreach my $u (@{$spec->{users}}) {
     if (defined $existing) {
         $u->{uid} = $existing->{uid} if !defined $u->{uid};
         if ($u->{uid} != $existing->{uid}) {
-            dry_print("warning: not applying", "warning: would not apply", "UID change of user ‘$name’ ($existing->{uid} -> $u->{uid})");
+            dry_print("warning: not applying", "warning: would not apply", "UID change of user ‘$name’ ($existing->{uid} -> $u->{uid}) in /etc/passwd");
             $u->{uid} = $existing->{uid};
         }
     } else {
diff --git a/nixpkgs/nixos/modules/config/users-groups.nix b/nixpkgs/nixos/modules/config/users-groups.nix
index 4640a0f3d6be..4c9e286ea5fd 100644
--- a/nixpkgs/nixos/modules/config/users-groups.nix
+++ b/nixpkgs/nixos/modules/config/users-groups.nix
@@ -539,14 +539,12 @@ in {
 
     # systemd initrd
     boot.initrd.systemd.users = mkOption {
-      visible = false;
       description = ''
         Users to include in initrd.
       '';
       default = {};
       type = types.attrsOf (types.submodule ({ name, ... }: {
         options.uid = mkOption {
-          visible = false;
           type = types.int;
           description = ''
             ID of the user in initrd.
@@ -555,7 +553,6 @@ in {
           default = cfg.users.${name}.uid;
         };
         options.group = mkOption {
-          visible = false;
           type = types.singleLineStr;
           description = ''
             Group the user belongs to in initrd.
@@ -567,14 +564,12 @@ in {
     };
 
     boot.initrd.systemd.groups = mkOption {
-      visible = false;
       description = ''
         Groups to include in initrd.
       '';
       default = {};
       type = types.attrsOf (types.submodule ({ name, ... }: {
         options.gid = mkOption {
-          visible = false;
           type = types.int;
           description = ''
             ID of the group in initrd.
diff --git a/nixpkgs/nixos/modules/hardware/all-firmware.nix b/nixpkgs/nixos/modules/hardware/all-firmware.nix
index 75247286368b..9e7a01c58afe 100644
--- a/nixpkgs/nixos/modules/hardware/all-firmware.nix
+++ b/nixpkgs/nixos/modules/hardware/all-firmware.nix
@@ -55,7 +55,6 @@ in {
         intel2200BGFirmware
         rtl8192su-firmware
         rt5677-firmware
-        rtl8723bs-firmware
         rtl8761b-firmware
         rtw88-firmware
         zd1211fw
diff --git a/nixpkgs/nixos/modules/hardware/opengl.nix b/nixpkgs/nixos/modules/hardware/opengl.nix
index 9108bcbd1652..0ff018ddc47d 100644
--- a/nixpkgs/nixos/modules/hardware/opengl.nix
+++ b/nixpkgs/nixos/modules/hardware/opengl.nix
@@ -87,13 +87,13 @@ in
       extraPackages = mkOption {
         type = types.listOf types.package;
         default = [];
-        example = literalExpression "with pkgs; [ intel-media-driver intel-ocl vaapiIntel ]";
+        example = literalExpression "with pkgs; [ intel-media-driver intel-ocl intel-vaapi-driver ]";
         description = lib.mdDoc ''
           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 vaapiIntel driver.
+          intel-media-driver supports hardware Broadwell (2014) or newer. Older hardware should use the mostly unmaintained intel-vaapi-driver driver.
           :::
         '';
       };
@@ -101,13 +101,13 @@ in
       extraPackages32 = mkOption {
         type = types.listOf types.package;
         default = [];
-        example = literalExpression "with pkgs.pkgsi686Linux; [ intel-media-driver vaapiIntel ]";
+        example = literalExpression "with pkgs.pkgsi686Linux; [ intel-media-driver intel-vaapi-driver ]";
         description = lib.mdDoc ''
           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 vaapiIntel driver.
+          intel-media-driver supports hardware Broadwell (2014) or newer. Older hardware should use the mostly unmaintained intel-vaapi-driver driver.
           :::
         '';
       };
diff --git a/nixpkgs/nixos/modules/hardware/usb-wwan.nix b/nixpkgs/nixos/modules/hardware/usb-modeswitch.nix
index 69673872cf9b..773891b0032f 100644
--- a/nixpkgs/nixos/modules/hardware/usb-wwan.nix
+++ b/nixpkgs/nixos/modules/hardware/usb-modeswitch.nix
@@ -7,12 +7,15 @@ with lib;
 
   options = {
 
-    hardware.usbWwan = {
+    hardware.usb-modeswitch = {
       enable = mkOption {
         type = types.bool;
         default = false;
         description = lib.mdDoc ''
-          Enable this option to support USB WWAN adapters.
+          Enable this option to support certain USB WLAN and WWAN adapters.
+
+          These network adapters initial present themselves as Flash Drives containing their drivers.
+          This option enables automatic switching to the networking mode.
         '';
       };
     };
@@ -20,7 +23,11 @@ with lib;
 
   ###### implementation
 
-  config = mkIf config.hardware.usbWwan.enable {
+  imports = [
+    (mkRenamedOptionModule ["hardware" "usbWwan" ] ["hardware" "usb-modeswitch" ])
+  ];
+
+  config = mkIf config.hardware.usb-modeswitch.enable {
     # Attaches device specific handlers.
     services.udev.packages = with pkgs; [ usb-modeswitch-data ];
 
diff --git a/nixpkgs/nixos/modules/hardware/video/displaylink.nix b/nixpkgs/nixos/modules/hardware/video/displaylink.nix
index 912f53da836a..ce5fbeeae536 100644
--- a/nixpkgs/nixos/modules/hardware/video/displaylink.nix
+++ b/nixpkgs/nixos/modules/hardware/video/displaylink.nix
@@ -26,6 +26,7 @@ in
         Identifier  "DisplayLink"
         MatchDriver "evdi"
         Driver      "modesetting"
+        Option      "TearFree" "true"
         Option      "AccelMethod" "none"
       EndSection
     '';
diff --git a/nixpkgs/nixos/modules/hardware/video/nvidia.nix b/nixpkgs/nixos/modules/hardware/video/nvidia.nix
index 592d11d6476b..e72194653f30 100644
--- a/nixpkgs/nixos/modules/hardware/video/nvidia.nix
+++ b/nixpkgs/nixos/modules/hardware/video/nvidia.nix
@@ -265,7 +265,7 @@ in
       {
         assertion = primeEnabled -> pCfg.nvidiaBusId != "" && (pCfg.intelBusId != "" || pCfg.amdgpuBusId != "");
         message = ''
-          When NVIDIA PRIME is enabled, the GPU bus IDs must configured.
+          When NVIDIA PRIME is enabled, the GPU bus IDs must be configured.
         '';
       }
 
diff --git a/nixpkgs/nixos/modules/hardware/wooting.nix b/nixpkgs/nixos/modules/hardware/wooting.nix
index 90d046d49f4e..78bbcb61aca7 100644
--- a/nixpkgs/nixos/modules/hardware/wooting.nix
+++ b/nixpkgs/nixos/modules/hardware/wooting.nix
@@ -2,8 +2,8 @@
 
 with lib;
 {
-  options.hardware.wooting.enable =
-    mkEnableOption (lib.mdDoc "support for Wooting keyboards");
+  options.hardware.wooting.enable = mkEnableOption (lib.mdDoc ''support for Wooting keyboards.
+    Note that users must be in the "input" group for udev rules to apply'');
 
   config = mkIf config.hardware.wooting.enable {
     environment.systemPackages = [ pkgs.wootility ];
diff --git a/nixpkgs/nixos/modules/i18n/input-method/fcitx5.nix b/nixpkgs/nixos/modules/i18n/input-method/fcitx5.nix
index 7251240d26ac..39952d6c3999 100644
--- a/nixpkgs/nixos/modules/i18n/input-method/fcitx5.nix
+++ b/nixpkgs/nixos/modules/i18n/input-method/fcitx5.nix
@@ -12,12 +12,34 @@ in
     i18n.inputMethod.fcitx5 = {
       addons = mkOption {
         type = with types; listOf package;
-        default = [];
+        default = [ ];
         example = literalExpression "with pkgs; [ fcitx5-rime ]";
         description = lib.mdDoc ''
           Enabled Fcitx5 addons.
         '';
       };
+      quickPhrase = mkOption {
+        type = with types; attrsOf string;
+        default = { };
+        example = literalExpression ''
+          {
+            smile = "(・∀・)";
+            angry = "( ̄ー ̄)";
+          }
+        '';
+        description = lib.mdDoc "Quick phrases.";
+      };
+      quickPhraseFiles = mkOption {
+        type = with types; attrsOf path;
+        default = { };
+        example = literalExpression ''
+          {
+            words = ./words.mb;
+            numbers = ./numbers.mb;
+          }
+        '';
+        description = lib.mdDoc "Quick phrase files.";
+      };
     };
   };
 
@@ -30,6 +52,16 @@ in
   config = mkIf (im.enabled == "fcitx5") {
     i18n.inputMethod.package = fcitx5Package;
 
+    i18n.inputMethod.fcitx5.addons = lib.optionals (cfg.quickPhrase != { }) [
+      (pkgs.writeTextDir "share/fcitx5/data/QuickPhrase.mb"
+        (lib.concatStringsSep "\n"
+          (lib.mapAttrsToList (name: value: "${name} ${value}") cfg.quickPhrase)))
+    ] ++ lib.optionals (cfg.quickPhraseFiles != { }) [
+      (pkgs.linkFarm "quickPhraseFiles" (lib.mapAttrs'
+        (name: value: lib.nameValuePair ("share/fcitx5/data/quickphrase.d/${name}.mb") value)
+        cfg.quickPhraseFiles))
+    ];
+
     environment.variables = {
       GTK_IM_MODULE = "fcitx";
       QT_IM_MODULE = "fcitx";
diff --git a/nixpkgs/nixos/modules/image/amend-repart-definitions.py b/nixpkgs/nixos/modules/image/amend-repart-definitions.py
new file mode 100644
index 000000000000..52f10303eb5e
--- /dev/null
+++ b/nixpkgs/nixos/modules/image/amend-repart-definitions.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+
+"""Amend systemd-repart definiton files.
+
+In order to avoid Import-From-Derivation (IFD) when building images with
+systemd-repart, the definition files created by Nix need to be amended with the
+store paths from the closure.
+
+This is achieved by adding CopyFiles= instructions to the definition files.
+
+The arbitrary files configured via `contents` are also added to the definition
+files using the same mechanism.
+"""
+
+import json
+import sys
+import shutil
+from pathlib import Path
+
+
+def add_contents_to_definition(
+    definition: Path, contents: dict[str, dict[str, str]] | None
+) -> None:
+    """Add CopyFiles= instructions to a definition for all files in contents."""
+    if not contents:
+        return
+
+    copy_files_lines: list[str] = []
+    for target, options in contents.items():
+        source = options["source"]
+
+        copy_files_lines.append(f"CopyFiles={source}:{target}\n")
+
+    with open(definition, "a") as f:
+        f.writelines(copy_files_lines)
+
+
+def add_closure_to_definition(
+    definition: Path, closure: Path | None, strip_nix_store_prefix: bool | None
+) -> None:
+    """Add CopyFiles= instructions to a definition for all paths in the closure.
+
+    If strip_nix_store_prefix is True, `/nix/store` is stripped from the target path.
+    """
+    if not closure:
+        return
+
+    copy_files_lines: list[str] = []
+    with open(closure, "r") as f:
+        for line in f:
+            if not isinstance(line, str):
+                continue
+
+            source = Path(line.strip())
+            target = str(source.relative_to("/nix/store/"))
+            target = f":{target}" if strip_nix_store_prefix else ""
+
+            copy_files_lines.append(f"CopyFiles={source}{target}\n")
+
+    with open(definition, "a") as f:
+        f.writelines(copy_files_lines)
+
+
+def main() -> None:
+    """Amend the provided repart definitions by adding CopyFiles= instructions.
+
+    For each file specified in the `contents` field of a partition in the
+    partiton config file, a `CopyFiles=` instruction is added to the
+    corresponding definition file.
+
+    The same is done for every store path of the `closure` field.
+
+    Print the path to a directory that contains the amended repart
+    definitions to stdout.
+    """
+    partition_config_file = sys.argv[1]
+    if not partition_config_file:
+        print("No partition config file was supplied.")
+        sys.exit(1)
+
+    repart_definitions = sys.argv[2]
+    if not repart_definitions:
+        print("No repart definitions were supplied.")
+        sys.exit(1)
+
+    with open(partition_config_file, "rb") as f:
+        partition_config = json.load(f)
+
+    if not partition_config:
+        print("Partition config is empty.")
+        sys.exit(1)
+
+    target_dir = Path("amended-repart.d")
+    target_dir.mkdir()
+    shutil.copytree(repart_definitions, target_dir, dirs_exist_ok=True)
+
+    for name, config in partition_config.items():
+        definition = target_dir.joinpath(f"{name}.conf")
+        definition.chmod(0o644)
+
+        contents = config.get("contents")
+        add_contents_to_definition(definition, contents)
+
+        closure = config.get("closure")
+        strip_nix_store_prefix = config.get("stripStorePaths")
+        add_closure_to_definition(definition, closure, strip_nix_store_prefix)
+
+    print(target_dir.absolute())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/nixpkgs/nixos/modules/image/repart.md b/nixpkgs/nixos/modules/image/repart.md
new file mode 100644
index 000000000000..6d0675f21a03
--- /dev/null
+++ b/nixpkgs/nixos/modules/image/repart.md
@@ -0,0 +1,137 @@
+# Building Images via `systemd-repart` {#sec-image-repart}
+
+You can build disk images in NixOS with the `image.repart` option provided by
+the module [image/repart.nix][]. This module uses `systemd-repart` to build the
+images and exposes it's entire interface via the `repartConfig` option.
+
+[image/repart.nix]: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/image/repart.nix
+
+An example of how to build an image:
+
+```nix
+{ config, modulesPath, ... }: {
+
+  imports = [ "${modulesPath}/image/repart.nix" ];
+
+  image.repart = {
+    name = "image";
+    partitions = {
+      "esp" = {
+        contents = {
+          ...
+        };
+        repartConfig = {
+          Type = "esp";
+          ...
+        };
+      };
+      "root" = {
+        storePaths = [ config.system.build.toplevel ];
+        repartConfig = {
+          Type = "root";
+          Label = "nixos";
+          ...
+        };
+      };
+    };
+  };
+
+}
+```
+
+## Nix Store Partition {#sec-image-repart-store-partition}
+
+You can define a partition that only contains the Nix store and then mount it
+under `/nix/store`. Because the `/nix/store` part of the paths is already
+determined by the mount point, you have to set `stripNixStorePrefix = true;` so
+that the prefix is stripped from the paths before copying them into the image.
+
+```nix
+fileSystems."/nix/store".device = "/dev/disk/by-partlabel/nix-store"
+
+image.repart.partitions = {
+  "store" = {
+    storePaths = [ config.system.build.toplevel ];
+    stripNixStorePrefix = true;
+    repartConfig = {
+      Type = "linux-generic";
+      Label = "nix-store";
+      ...
+    };
+  };
+};
+```
+
+## Appliance Image {#sec-image-repart-appliance}
+
+The `image/repart.nix` module can also be used to build self-contained [software
+appliances][].
+
+[software appliances]: https://en.wikipedia.org/wiki/Software_appliance
+
+The generation based update mechanism of NixOS is not suited for appliances.
+Updates of appliances are usually either performed by replacing the entire
+image with a new one or by updating partitions via an A/B scheme. See the
+[Chrome OS update process][chrome-os-update] for an example of how to achieve
+this. The appliance image built in the following example does not contain a
+`configuration.nix` and thus you will not be able to call `nixos-rebuild` from
+this system.
+
+[chrome-os-update]: https://chromium.googlesource.com/aosp/platform/system/update_engine/+/HEAD/README.md
+
+```nix
+let
+  pkgs = import <nixpkgs> { };
+  efiArch = pkgs.stdenv.hostPlatform.efiArch;
+in
+(pkgs.nixos [
+  ({ config, lib, pkgs, modulesPath, ... }: {
+
+    imports = [ "${modulesPath}/image/repart.nix" ];
+
+    boot.loader.grub.enable = false;
+
+    fileSystems."/".device = "/dev/disk/by-label/nixos";
+
+    image.repart = {
+      name = "image";
+      partitions = {
+        "esp" = {
+          contents = {
+            "/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source =
+              "${pkgs.systemd}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi";
+
+            "/loader/entries/nixos.conf".source = pkgs.writeText "nixos.conf" ''
+              title NixOS
+              linux /EFI/nixos/kernel.efi
+              initrd /EFI/nixos/initrd.efi
+              options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
+            '';
+
+            "/EFI/nixos/kernel.efi".source =
+              "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}";
+
+            "/EFI/nixos/initrd.efi".source =
+              "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
+          };
+          repartConfig = {
+            Type = "esp";
+            Format = "vfat";
+            SizeMinBytes = "96M";
+          };
+        };
+        "root" = {
+          storePaths = [ config.system.build.toplevel ];
+          repartConfig = {
+            Type = "root";
+            Format = "ext4";
+            Label = "nixos";
+            Minimize = "guess";
+          };
+        };
+      };
+    };
+
+  })
+]).image
+```
diff --git a/nixpkgs/nixos/modules/image/repart.nix b/nixpkgs/nixos/modules/image/repart.nix
new file mode 100644
index 000000000000..4362982f5bac
--- /dev/null
+++ b/nixpkgs/nixos/modules/image/repart.nix
@@ -0,0 +1,209 @@
+# This module exposes options to build a disk image with a GUID Partition Table
+# (GPT). It uses systemd-repart to build the image.
+
+{ config, pkgs, lib, utils, ... }:
+
+let
+  cfg = config.image.repart;
+
+  partitionOptions = {
+    options = {
+      storePaths = lib.mkOption {
+        type = with lib.types; listOf path;
+        default = [ ];
+        description = lib.mdDoc "The store paths to include in the partition.";
+      };
+
+      stripNixStorePrefix = lib.mkOption {
+        type = lib.types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Whether to strip `/nix/store/` from the store paths. This is useful
+          when you want to build a partition that only contains store paths and
+          is mounted under `/nix/store`.
+        '';
+      };
+
+      contents = lib.mkOption {
+        type = with lib.types; attrsOf (submodule {
+          options = {
+            source = lib.mkOption {
+              type = types.path;
+              description = lib.mdDoc "Path of the source file.";
+            };
+          };
+        });
+        default = { };
+        example = lib.literalExpression '' {
+          "/EFI/BOOT/BOOTX64.EFI".source =
+            "''${pkgs.systemd}/lib/systemd/boot/efi/systemd-bootx64.efi";
+
+          "/loader/entries/nixos.conf".source = systemdBootEntry;
+        }
+        '';
+        description = lib.mdDoc "The contents to end up in the filesystem image.";
+      };
+
+      repartConfig = lib.mkOption {
+        type = with lib.types; attrsOf (oneOf [ str int bool ]);
+        example = {
+          Type = "home";
+          SizeMinBytes = "512M";
+          SizeMaxBytes = "2G";
+        };
+        description = lib.mdDoc ''
+          Specify the repart options for a partiton as a structural setting.
+          See <https://www.freedesktop.org/software/systemd/man/repart.d.html>
+          for all available options.
+        '';
+      };
+    };
+  };
+in
+{
+  options.image.repart = {
+
+    name = lib.mkOption {
+      type = lib.types.str;
+      description = lib.mdDoc "The name of the image.";
+    };
+
+    seed = lib.mkOption {
+      type = with lib.types; nullOr str;
+      # Generated with `uuidgen`. Random but fixed to improve reproducibility.
+      default = "0867da16-f251-457d-a9e8-c31f9a3c220b";
+      description = lib.mdDoc ''
+        A UUID to use as a seed. You can set this to `null` to explicitly
+        randomize the partition UUIDs.
+      '';
+    };
+
+    split = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Enables generation of split artifacts from partitions. If enabled, for
+        each partition with SplitName= set, a separate output file containing
+        just the contents of that partition is generated.
+      '';
+    };
+
+    partitions = lib.mkOption {
+      type = with lib.types; attrsOf (submodule partitionOptions);
+      default = { };
+      example = lib.literalExpression '' {
+        "10-esp" = {
+          contents = {
+            "/EFI/BOOT/BOOTX64.EFI".source =
+              "''${pkgs.systemd}/lib/systemd/boot/efi/systemd-bootx64.efi";
+          }
+          repartConfig = {
+            Type = "esp";
+            Format = "fat";
+          };
+        };
+        "20-root" = {
+          storePaths = [ config.system.build.toplevel ];
+          repartConfig = {
+            Type = "root";
+            Format = "ext4";
+            Minimize = "guess";
+          };
+        };
+      };
+      '';
+      description = lib.mdDoc ''
+        Specify partitions as a set of the names of the partitions with their
+        configuration as the key.
+      '';
+    };
+
+  };
+
+  config = {
+
+    system.build.image =
+      let
+        fileSystemToolMapping = with pkgs; {
+          "vfat" = [ dosfstools mtools ];
+          "ext4" = [ e2fsprogs.bin ];
+          "squashfs" = [ squashfsTools ];
+          "erofs" = [ erofs-utils ];
+          "btrfs" = [ btrfs-progs ];
+          "xfs" = [ xfsprogs ];
+        };
+
+        fileSystems = lib.filter
+          (f: f != null)
+          (lib.mapAttrsToList (_n: v: v.repartConfig.Format or null) cfg.partitions);
+
+        fileSystemTools = builtins.concatMap (f: fileSystemToolMapping."${f}") fileSystems;
+
+
+        makeClosure = paths: pkgs.closureInfo { rootPaths = paths; };
+
+        # Add the closure of the provided Nix store paths to cfg.partitions so
+        # that amend-repart-definitions.py can read it.
+        addClosure = _name: partitionConfig: partitionConfig // (
+          lib.optionalAttrs
+            (partitionConfig.storePaths or [ ] != [ ])
+            { closure = "${makeClosure partitionConfig.storePaths}/store-paths"; }
+        );
+
+
+        finalPartitions = lib.mapAttrs addClosure cfg.partitions;
+
+
+        amendRepartDefinitions = pkgs.runCommand "amend-repart-definitions.py"
+          {
+            nativeBuildInputs = with pkgs; [ black ruff mypy ];
+            buildInputs = [ pkgs.python3 ];
+          } ''
+          install ${./amend-repart-definitions.py} $out
+          patchShebangs --host $out
+
+          black --check --diff $out
+          ruff --line-length 88 $out
+          mypy --strict $out
+        '';
+
+        format = pkgs.formats.ini { };
+
+        definitionsDirectory = utils.systemdUtils.lib.definitions
+          "repart.d"
+          format
+          (lib.mapAttrs (_n: v: { Partition = v.repartConfig; }) finalPartitions);
+
+        partitions = pkgs.writeText "partitions.json" (builtins.toJSON finalPartitions);
+      in
+      pkgs.runCommand cfg.name
+        {
+          nativeBuildInputs = with pkgs; [
+            fakeroot
+            systemd
+          ] ++ fileSystemTools;
+        } ''
+        amendedRepartDefinitions=$(${amendRepartDefinitions} ${partitions} ${definitionsDirectory})
+
+        mkdir -p $out
+        cd $out
+
+        fakeroot systemd-repart \
+          --dry-run=no \
+          --empty=create \
+          --size=auto \
+          --seed="${cfg.seed}" \
+          --definitions="$amendedRepartDefinitions" \
+          --split="${lib.boolToString cfg.split}" \
+          --json=pretty \
+          image.raw \
+          | tee repart-output.json
+      '';
+
+    meta = {
+      maintainers = with lib.maintainers; [ nikstur ];
+      doc = ./repart.md;
+    };
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix b/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
index 573b31b439c2..ea8056ff870c 100644
--- a/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
+++ b/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
@@ -6,6 +6,7 @@
   imports = [ ./installation-cd-graphical-base.nix ];
 
   isoImage.edition = "gnome";
+  isoImage.graphicalGrub = true;
 
   services.xserver.desktopManager.gnome = {
     # Add Firefox and other tools useful for installation to the launcher
diff --git a/nixpkgs/nixos/modules/installer/cd-dvd/iso-image.nix b/nixpkgs/nixos/modules/installer/cd-dvd/iso-image.nix
index f9cbafc28657..c430048d6598 100644
--- a/nixpkgs/nixos/modules/installer/cd-dvd/iso-image.nix
+++ b/nixpkgs/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -283,7 +283,7 @@ let
     cat <<EOF > $out/EFI/boot/grub.cfg
 
     set with_fonts=false
-    set textmode=false
+    set textmode=${boolToString (!config.isoImage.graphicalGrub)}
     # If you want to use serial for "terminal_*" commands, you need to set one up:
     #   Example manual configuration:
     #    → serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
@@ -658,6 +658,16 @@ in
       '';
     };
 
+    isoImage.graphicalGrub = mkOption {
+      default = false;
+      type = types.bool;
+      example = true;
+      description = lib.mdDoc ''
+        Whether to use textmode or graphical grub.
+        false means we use textmode grub.
+      '';
+    };
+
   };
 
   # store them in lib so we can mkImageMediaOverride the
diff --git a/nixpkgs/nixos/modules/installer/netboot/netboot.nix b/nixpkgs/nixos/modules/installer/netboot/netboot.nix
index a55c0ab2d655..a50f22cbe471 100644
--- a/nixpkgs/nixos/modules/installer/netboot/netboot.nix
+++ b/nixpkgs/nixos/modules/installer/netboot/netboot.nix
@@ -39,9 +39,7 @@ with lib;
 
     # !!! Hack - attributes expected by other modules.
     environment.systemPackages = [ pkgs.grub2_efi ]
-      ++ (if pkgs.stdenv.hostPlatform.system == "aarch64-linux"
-          then []
-          else [ pkgs.grub2 pkgs.syslinux ]);
+      ++ (lib.optionals (pkgs.stdenv.hostPlatform.system != "aarch64-linux") [pkgs.grub2 pkgs.syslinux]);
 
     fileSystems."/" = mkImageMediaOverride
       { fsType = "tmpfs";
diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-generate-config.pl b/nixpkgs/nixos/modules/installer/tools/nixos-generate-config.pl
index c2a5ecbe9e2e..7d0c5898e23d 100644
--- a/nixpkgs/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixpkgs/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -85,7 +85,7 @@ sub debug {
 
 
 # nixpkgs.system
-push @attrs, "nixpkgs.hostPlatform = lib.mkDefault \"@system@\";";
+push @attrs, "nixpkgs.hostPlatform = lib.mkDefault \"@hostPlatformSystem@\";";
 
 
 my $cpuinfo = read_file "/proc/cpuinfo";
@@ -381,6 +381,7 @@ sub in {
 
 my $fileSystems;
 my %fsByDev;
+my $useSwraid = 0;
 foreach my $fs (read_file("/proc/self/mountinfo")) {
     chomp $fs;
     my @fields = split / /, $fs;
@@ -510,8 +511,8 @@ EOF
     # boot.initrd.luks.devices entry.
     if (-e $device) {
         my $deviceName = basename(abs_path($device));
-        if (-e "/sys/class/block/$deviceName"
-            && read_file("/sys/class/block/$deviceName/dm/uuid",  err_mode => 'quiet') =~ /^CRYPT-LUKS/)
+        my $dmUuid = read_file("/sys/class/block/$deviceName/dm/uuid",  err_mode => 'quiet');
+        if ($dmUuid =~ /^CRYPT-LUKS/)
         {
             my @slaves = glob("/sys/class/block/$deviceName/slaves/*");
             if (scalar @slaves == 1) {
@@ -527,8 +528,14 @@ EOF
                 }
             }
         }
+        if (-e "/sys/class/block/$deviceName/md/uuid") {
+            $useSwraid = 1;
+        }
     }
 }
+if ($useSwraid) {
+    push @attrs, "boot.swraid.enable = true;\n\n";
+}
 
 
 # Generate the hardware configuration file.
diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-install.sh b/nixpkgs/nixos/modules/installer/tools/nixos-install.sh
index 20fec525e70b..4e42875c0365 100755
--- a/nixpkgs/nixos/modules/installer/tools/nixos-install.sh
+++ b/nixpkgs/nixos/modules/installer/tools/nixos-install.sh
@@ -206,7 +206,7 @@ if [[ -z $noBootLoader ]]; then
       mount --rbind --mkdir / "$mountPoint"
       mount --make-rslave "$mountPoint"
       /run/current-system/bin/switch-to-configuration boot
-      umount -R "$mountPoint" && rmdir "$mountPoint"
+      umount -R "$mountPoint" && (rmdir "$mountPoint" 2>/dev/null || true)
 EOF
 )"
 fi
diff --git a/nixpkgs/nixos/modules/installer/tools/tools.nix b/nixpkgs/nixos/modules/installer/tools/tools.nix
index 5133ad18f4bb..4dce4f998052 100644
--- a/nixpkgs/nixos/modules/installer/tools/tools.nix
+++ b/nixpkgs/nixos/modules/installer/tools/tools.nix
@@ -35,17 +35,14 @@ let
     name = "nixos-generate-config";
     src = ./nixos-generate-config.pl;
     perl = "${pkgs.perl.withPackages (p: [ p.FileSlurp ])}/bin/perl";
-    system = pkgs.stdenv.hostPlatform.system;
+    hostPlatformSystem = pkgs.stdenv.hostPlatform.system;
     detectvirt = "${config.systemd.package}/bin/systemd-detect-virt";
     btrfs = "${pkgs.btrfs-progs}/bin/btrfs";
     inherit (config.system.nixos-generate-config) configuration desktopConfiguration;
     xserverEnabled = config.services.xserver.enable;
   };
 
-  nixos-option =
-    if lib.versionAtLeast (lib.getVersion config.nix.package) "2.4pre"
-    then null
-    else pkgs.nixos-option;
+  inherit (pkgs) nixos-option;
 
   nixos-version = makeProg {
     name = "nixos-version";
@@ -129,7 +126,7 @@ in
       # your system.  Help is available in the configuration.nix(5) man page
       # and in the NixOS manual (accessible by running `nixos-help`).
 
-      { config, pkgs, ... }:
+      { config, lib, pkgs, ... }:
 
       {
         imports =
@@ -232,9 +229,10 @@ in
         nixos-install
         nixos-rebuild
         nixos-generate-config
+        nixos-option
         nixos-version
         nixos-enter
-      ] ++ lib.optional (nixos-option != null) nixos-option;
+      ];
 
     documentation.man.man-db.skipPackages = [ nixos-version ];
 
diff --git a/nixpkgs/nixos/modules/misc/documentation.nix b/nixpkgs/nixos/modules/misc/documentation.nix
index 4abd55c1bb97..0059ade84e7d 100644
--- a/nixpkgs/nixos/modules/misc/documentation.nix
+++ b/nixpkgs/nixos/modules/misc/documentation.nix
@@ -107,7 +107,7 @@ let
             } >&2
         '';
 
-    inherit (cfg.nixos.options) warningsAreErrors allowDocBook;
+    inherit (cfg.nixos.options) warningsAreErrors;
   };
 
 
@@ -160,6 +160,9 @@ in
     (mkRenamedOptionModule [ "programs" "info" "enable" ] [ "documentation" "info" "enable" ])
     (mkRenamedOptionModule [ "programs" "man"  "enable" ] [ "documentation" "man"  "enable" ])
     (mkRenamedOptionModule [ "services" "nixosManual" "enable" ] [ "documentation" "nixos" "enable" ])
+    (mkRemovedOptionModule
+      [ "documentation" "nixos" "options" "allowDocBook" ]
+      "DocBook option documentation is no longer supported")
   ];
 
   options = {
@@ -273,23 +276,6 @@ in
         '';
       };
 
-      nixos.options.allowDocBook = mkOption {
-        type = types.bool;
-        default = true;
-        description = lib.mdDoc ''
-          Whether to allow DocBook option docs. When set to `false` all option using
-          DocBook documentation will cause a manual build error; additionally a new
-          renderer may be used.
-
-          ::: {.note}
-          The `false` setting for this option is not yet fully supported. While it
-          should work fine and produce the same output as the previous toolchain
-          using DocBook it may not work in all circumstances. Whether markdown option
-          documentation is allowed is independent of this option.
-          :::
-        '';
-      };
-
       nixos.options.warningsAreErrors = mkOption {
         type = types.bool;
         default = true;
@@ -368,14 +354,6 @@ in
     (mkIf cfg.nixos.enable {
       system.build.manual = manual;
 
-      system.activationScripts.check-manual-docbook = ''
-        if [[ $(cat ${manual.optionsUsedDocbook}) = 1 ]]; then
-          echo -e "\e[31;1mwarning\e[0m: This configuration contains option documentation in docbook." \
-                  "Support for docbook is deprecated and will be removed after NixOS 23.05." \
-                  "See nix-store --read-log ${builtins.unsafeDiscardStringContext manual.optionsJSON.drvPath}"
-        fi
-      '';
-
       environment.systemPackages = []
         ++ optional cfg.man.enable manual.manpages
         ++ optionals cfg.doc.enable [ manual.manualHTML nixos-help ];
diff --git a/nixpkgs/nixos/modules/misc/ids.nix b/nixpkgs/nixos/modules/misc/ids.nix
index 5b278b5e8062..dc59ccb357d4 100644
--- a/nixpkgs/nixos/modules/misc/ids.nix
+++ b/nixpkgs/nixos/modules/misc/ids.nix
@@ -69,7 +69,7 @@ in
       #dialout = 27; # unused
       polkituser = 28;
       #utmp = 29; # unused
-      # ddclient = 30; # converted to DynamicUser = true
+      # ddclient = 30; # software removed
       davfs2 = 31;
       disnix = 33;
       osgi = 34;
@@ -394,7 +394,7 @@ in
       dialout = 27;
       #polkituser = 28; # currently unused, polkitd doesn't need a group
       utmp = 29;
-      # ddclient = 30; # converted to DynamicUser = true
+      # ddclient = 30; # software removed
       davfs2 = 31;
       disnix = 33;
       osgi = 34;
diff --git a/nixpkgs/nixos/modules/misc/nixpkgs.nix b/nixpkgs/nixos/modules/misc/nixpkgs.nix
index 55ec08acf445..f9d8bccea284 100644
--- a/nixpkgs/nixos/modules/misc/nixpkgs.nix
+++ b/nixpkgs/nixos/modules/misc/nixpkgs.nix
@@ -55,11 +55,6 @@ let
     description = "An evaluation of Nixpkgs; the top level attribute set of packages";
   };
 
-  # Whether `pkgs` was constructed by this module - not if nixpkgs.pkgs or
-  # _module.args.pkgs is set. However, determining whether _module.args.pkgs
-  # is defined elsewhere does not seem feasible.
-  constructedByMe = !opt.pkgs.isDefined;
-
   hasBuildPlatform = opt.buildPlatform.highestPrio < (mkOptionDefault {}).priority;
   hasHostPlatform = opt.hostPlatform.isDefined;
   hasPlatform = hasHostPlatform || hasBuildPlatform;
@@ -337,10 +332,28 @@ in
 
   config = {
     _module.args = {
-      pkgs = finalPkgs.__splicedPackages;
+      pkgs =
+        # We explicitly set the default override priority, so that we do not need
+        # to evaluate finalPkgs in case an override is placed on `_module.args.pkgs`.
+        # After all, to determine a definition priority, we need to evaluate `._type`,
+        # which is somewhat costly for Nixpkgs. With an explicit priority, we only
+        # evaluate the wrapper to find out that the priority is lower, and then we
+        # don't need to evaluate `finalPkgs`.
+        lib.mkOverride lib.modules.defaultOverridePriority
+          finalPkgs.__splicedPackages;
     };
 
-    assertions = [
+    assertions = let
+      # Whether `pkgs` was constructed by this module. This is false when any of
+      # nixpkgs.pkgs or _module.args.pkgs is set.
+      constructedByMe =
+        # We set it with default priority and it can not be merged, so if the
+        # pkgs module argument has that priority, it's from us.
+        (lib.modules.mergeAttrDefinitionsWithPrio options._module.args).pkgs.highestPrio
+          == lib.modules.defaultOverridePriority
+        # Although, if nixpkgs.pkgs is set, we did forward it, but we did not construct it.
+          && !opt.pkgs.isDefined;
+    in [
       (
         let
           nixosExpectedSystem =
diff --git a/nixpkgs/nixos/modules/misc/version.nix b/nixpkgs/nixos/modules/misc/version.nix
index 780a6b2a83a6..0a66eafe933e 100644
--- a/nixpkgs/nixos/modules/misc/version.nix
+++ b/nixpkgs/nixos/modules/misc/version.nix
@@ -32,7 +32,7 @@ let
     VARIANT_ID = cfg.variant_id;
   };
 
-  initrdReleaseContents = osReleaseContents // {
+  initrdReleaseContents = (removeAttrs osReleaseContents [ "BUILD_ID" ]) // {
     PRETTY_NAME = "${osReleaseContents.PRETTY_NAME} (Initrd)";
   };
   initrdRelease = pkgs.writeText "initrd-release" (attrsToText initrdReleaseContents);
@@ -140,13 +140,6 @@ in
       '';
     };
 
-    defaultChannel = mkOption {
-      internal = true;
-      type = types.str;
-      default = "https://nixos.org/channels/nixos-unstable";
-      description = lib.mdDoc "Default NixOS channel to which the root user is subscribed.";
-    };
-
     configurationRevision = mkOption {
       type = types.nullOr types.str;
       default = null;
diff --git a/nixpkgs/nixos/modules/module-list.nix b/nixpkgs/nixos/modules/module-list.nix
index acc30776e0ff..bcd6a235eb63 100644
--- a/nixpkgs/nixos/modules/module-list.nix
+++ b/nixpkgs/nixos/modules/module-list.nix
@@ -4,8 +4,8 @@
   ./config/debug-info.nix
   ./config/fonts/fontconfig.nix
   ./config/fonts/fontdir.nix
-  ./config/fonts/fonts.nix
   ./config/fonts/ghostscript.nix
+  ./config/fonts/packages.nix
   ./config/gnu.nix
   ./config/gtk/gtk-icon-cache.nix
   ./config/i18n.nix
@@ -16,6 +16,10 @@
   ./config/malloc.nix
   ./config/mysql.nix
   ./config/networking.nix
+  ./config/nix.nix
+  ./config/nix-channel.nix
+  ./config/nix-flakes.nix
+  ./config/nix-remote-build.nix
   ./config/no-x-libs.nix
   ./config/nsswitch.nix
   ./config/power-management.nix
@@ -89,8 +93,8 @@
   ./hardware/tuxedo-keyboard.nix
   ./hardware/ubertooth.nix
   ./hardware/uinput.nix
+  ./hardware/usb-modeswitch.nix
   ./hardware/usb-storage.nix
-  ./hardware/usb-wwan.nix
   ./hardware/video/amdgpu-pro.nix
   ./hardware/video/bumblebee.nix
   ./hardware/video/capture/mwprocapture.nix
@@ -156,6 +160,7 @@
   ./programs/darling.nix
   ./programs/dconf.nix
   ./programs/digitalbitbox/default.nix
+  ./programs/direnv.nix
   ./programs/dmrconfig.nix
   ./programs/droidcam.nix
   ./programs/environment.nix
@@ -216,7 +221,9 @@
   ./programs/nncp.nix
   ./programs/noisetorch.nix
   ./programs/npm.nix
+  ./programs/ns-usbloader.nix
   ./programs/oblogout.nix
+  ./programs/oddjobd.nix
   ./programs/openvpn3.nix
   ./programs/pantheon-tweaks.nix
   ./programs/partition-manager.nix
@@ -258,6 +265,7 @@
   ./programs/wayland/river.nix
   ./programs/wayland/sway.nix
   ./programs/wayland/waybar.nix
+  ./programs/wayland/wayfire.nix
   ./programs/weylus.nix
   ./programs/wireshark.nix
   ./programs/xastir.nix
@@ -328,6 +336,8 @@
   ./services/audio/spotifyd.nix
   ./services/audio/squeezelite.nix
   ./services/audio/tts.nix
+  ./services/audio/wyoming/faster-whisper.nix
+  ./services/audio/wyoming/piper.nix
   ./services/audio/ympd.nix
   ./services/backup/automysqlbackup.nix
   ./services/backup/bacula.nix
@@ -410,6 +420,7 @@
   ./services/databases/neo4j.nix
   ./services/databases/openldap.nix
   ./services/databases/opentsdb.nix
+  ./services/databases/pgbouncer.nix
   ./services/databases/pgmanage.nix
   ./services/databases/postgresql.nix
   ./services/databases/redis.nix
@@ -474,6 +485,7 @@
   ./services/games/deliantra-server.nix
   ./services/games/factorio.nix
   ./services/games/freeciv.nix
+  ./services/games/mchprs.nix
   ./services/games/minecraft-server.nix
   ./services/games/minetest-server.nix
   ./services/games/openarena.nix
@@ -528,6 +540,7 @@
   ./services/hardware/usbrelayd.nix
   ./services/hardware/vdr.nix
   ./services/hardware/keyd.nix
+  ./services/home-automation/ebusd.nix
   ./services/home-automation/esphome.nix
   ./services/home-automation/evcc.nix
   ./services/home-automation/home-assistant.nix
@@ -588,9 +601,11 @@
   ./services/matrix/dendrite.nix
   ./services/matrix/mautrix-facebook.nix
   ./services/matrix/mautrix-telegram.nix
+  ./services/matrix/mautrix-whatsapp.nix
   ./services/matrix/mjolnir.nix
   ./services/matrix/mx-puppet-discord.nix
   ./services/matrix/pantalaimon.nix
+  ./services/matrix/matrix-sliding-sync.nix
   ./services/matrix/synapse.nix
   ./services/misc/airsonic.nix
   ./services/misc/ananicy.nix
@@ -601,6 +616,7 @@
   ./services/misc/autorandr.nix
   ./services/misc/autosuspend.nix
   ./services/misc/bazarr.nix
+  ./services/misc/bcg.nix
   ./services/misc/beanstalkd.nix
   ./services/misc/bees.nix
   ./services/misc/bepasty.nix
@@ -624,7 +640,7 @@
   ./services/misc/etcd.nix
   ./services/misc/etebase-server.nix
   ./services/misc/etesync-dav.nix
-  ./services/misc/exhibitor.nix
+  ./services/misc/evdevremapkeys.nix
   ./services/misc/felix.nix
   ./services/misc/freeswitch.nix
   ./services/misc/fstrim.nix
@@ -640,6 +656,7 @@
   ./services/misc/greenclip.nix
   ./services/misc/headphones.nix
   ./services/misc/heisenbridge.nix
+  ./services/misc/homepage-dashboard.nix
   ./services/misc/ihaskell.nix
   ./services/misc/input-remapper.nix
   ./services/misc/irkerd.nix
@@ -658,9 +675,9 @@
   ./services/misc/mediatomb.nix
   ./services/misc/metabase.nix
   ./services/misc/moonraker.nix
+  ./services/misc/mqtt2influxdb.nix
   ./services/misc/n8n.nix
   ./services/misc/nitter.nix
-  ./services/misc/nix-daemon.nix
   ./services/misc/nix-gc.nix
   ./services/misc/nix-optimise.nix
   ./services/misc/nix-ssh-serve.nix
@@ -725,6 +742,7 @@
   ./services/monitoring/alerta.nix
   ./services/monitoring/apcupsd.nix
   ./services/monitoring/arbtt.nix
+  ./services/monitoring/below.nix
   ./services/monitoring/bosun.nix
   ./services/monitoring/cadvisor.nix
   ./services/monitoring/cockpit.nix
@@ -753,6 +771,8 @@
   ./services/monitoring/munin.nix
   ./services/monitoring/nagios.nix
   ./services/monitoring/netdata.nix
+  ./services/monitoring/opentelemetry-collector.nix
+  ./services/monitoring/osquery.nix
   ./services/monitoring/parsedmarc.nix
   ./services/monitoring/prometheus/alertmanager-irc-relay.nix
   ./services/monitoring/prometheus/alertmanager.nix
@@ -846,9 +866,7 @@
   ./services/networking/create_ap.nix
   ./services/networking/croc.nix
   ./services/networking/dante.nix
-  ./services/networking/ddclient.nix
   ./services/networking/dhcpcd.nix
-  ./services/networking/dhcpd.nix
   ./services/networking/dnscache.nix
   ./services/networking/dnscrypt-proxy2.nix
   ./services/networking/dnscrypt-wrapper.nix
@@ -1011,6 +1029,7 @@
   ./services/networking/shorewall.nix
   ./services/networking/shorewall6.nix
   ./services/networking/shout.nix
+  ./services/networking/sing-box.nix
   ./services/networking/sitespeed-io.nix
   ./services/networking/skydns.nix
   ./services/networking/smartdns.nix
@@ -1051,6 +1070,7 @@
   ./services/networking/tox-node.nix
   ./services/networking/toxvpn.nix
   ./services/networking/trickster.nix
+  ./services/networking/trust-dns.nix
   ./services/networking/tvheadend.nix
   ./services/networking/twingate.nix
   ./services/networking/ucarp.nix
@@ -1094,6 +1114,7 @@
   ./services/search/meilisearch.nix
   ./services/search/opensearch.nix
   ./services/search/qdrant.nix
+  ./services/search/typesense.nix
   ./services/security/aesmd.nix
   ./services/security/authelia.nix
   ./services/security/certmgr.nix
@@ -1101,6 +1122,7 @@
   ./services/security/clamav.nix
   ./services/security/endlessh-go.nix
   ./services/security/endlessh.nix
+  ./services/security/esdm.nix
   ./services/security/fail2ban.nix
   ./services/security/fprintd.nix
   ./services/security/haka.nix
@@ -1132,6 +1154,7 @@
   ./services/security/vaultwarden/default.nix
   ./services/security/yubikey-agent.nix
   ./services/system/automatic-timezoned.nix
+  ./services/system/bpftune.nix
   ./services/system/cachix-agent/default.nix
   ./services/system/cachix-watch-store.nix
   ./services/system/cloud-init.nix
@@ -1139,6 +1162,7 @@
   ./services/system/earlyoom.nix
   ./services/system/kerberos/default.nix
   ./services/system/localtimed.nix
+  ./services/system/nix-daemon.nix
   ./services/system/nscd.nix
   ./services/system/saslauthd.nix
   ./services/system/self-deploy.nix
@@ -1166,6 +1190,7 @@
   ./services/wayland/cage.nix
   ./services/web-apps/akkoma.nix
   ./services/web-apps/alps.nix
+  ./services/web-apps/anuko-time-tracker.nix
   ./services/web-apps/atlassian/confluence.nix
   ./services/web-apps/atlassian/crowd.nix
   ./services/web-apps/atlassian/jira.nix
@@ -1189,8 +1214,11 @@
   ./services/web-apps/galene.nix
   ./services/web-apps/gerrit.nix
   ./services/web-apps/gotify-server.nix
+  ./services/web-apps/gotosocial.nix
   ./services/web-apps/grocy.nix
   ./services/web-apps/pixelfed.nix
+  ./services/web-apps/guacamole-client.nix
+  ./services/web-apps/guacamole-server.nix
   ./services/web-apps/healthchecks.nix
   ./services/web-apps/hedgedoc.nix
   ./services/web-apps/hledger-web.nix
@@ -1240,6 +1268,7 @@
   ./services/web-apps/rss-bridge.nix
   ./services/web-apps/selfoss.nix
   ./services/web-apps/shiori.nix
+  ./services/web-apps/slskd.nix
   ./services/web-apps/snipe-it.nix
   ./services/web-apps/sogo.nix
   ./services/web-apps/trilium.nix
@@ -1273,7 +1302,9 @@
   ./services/web-servers/nginx/gitweb.nix
   ./services/web-servers/phpfpm/default.nix
   ./services/web-servers/pomerium.nix
+  ./services/web-servers/rustus.nix
   ./services/web-servers/stargazer.nix
+  ./services/web-servers/static-web-server.nix
   ./services/web-servers/tomcat.nix
   ./services/web-servers/traefik.nix
   ./services/web-servers/trafficserver/default.nix
@@ -1327,6 +1358,7 @@
   ./services/x11/xbanish.nix
   ./services/x11/xfs.nix
   ./services/x11/xserver.nix
+  ./system/activation/activatable-system.nix
   ./system/activation/activation-script.nix
   ./system/activation/specialisation.nix
   ./system/activation/bootspec.nix
@@ -1369,6 +1401,7 @@
   ./system/boot/systemd/oomd.nix
   ./system/boot/systemd/repart.nix
   ./system/boot/systemd/shutdown.nix
+  ./system/boot/systemd/sysupdate.nix
   ./system/boot/systemd/tmpfiles.nix
   ./system/boot/systemd/user.nix
   ./system/boot/systemd/userdbd.nix
@@ -1396,6 +1429,7 @@
   ./tasks/filesystems/nfs.nix
   ./tasks/filesystems/ntfs.nix
   ./tasks/filesystems/reiserfs.nix
+  ./tasks/filesystems/squashfs.nix
   ./tasks/filesystems/unionfs-fuse.nix
   ./tasks/filesystems/vboxsf.nix
   ./tasks/filesystems/vfat.nix
diff --git a/nixpkgs/nixos/modules/profiles/headless.nix b/nixpkgs/nixos/modules/profiles/headless.nix
index c17cb287b72b..eb29f3d65106 100644
--- a/nixpkgs/nixos/modules/profiles/headless.nix
+++ b/nixpkgs/nixos/modules/profiles/headless.nix
@@ -6,8 +6,6 @@
 with lib;
 
 {
-  boot.vesa = false;
-
   # Don't start a tty on the serial consoles.
   systemd.services."serial-getty@ttyS0".enable = lib.mkDefault false;
   systemd.services."serial-getty@hvc0".enable = false;
@@ -15,7 +13,7 @@ with lib;
   systemd.services."autovt@".enable = false;
 
   # Since we can't manually respond to a panic, just reboot.
-  boot.kernelParams = [ "panic=1" "boot.panic_on_fail" ];
+  boot.kernelParams = [ "panic=1" "boot.panic_on_fail" "vga=0x317" "nomodeset" ];
 
   # Don't allow emergency mode, because we don't have a console.
   systemd.enableEmergencyMode = false;
diff --git a/nixpkgs/nixos/modules/profiles/installation-device.nix b/nixpkgs/nixos/modules/profiles/installation-device.nix
index 32884f4b8754..4120d5919d7d 100644
--- a/nixpkgs/nixos/modules/profiles/installation-device.nix
+++ b/nixpkgs/nixos/modules/profiles/installation-device.nix
@@ -106,6 +106,8 @@ with lib;
         systemdStage1Network
       ];
 
+    boot.swraid.enable = true;
+
     # Show all debug messages from the kernel but don't log refused packets
     # because we have the firewall enabled. This makes installs from the
     # console less cumbersome if the machine has a public IP.
diff --git a/nixpkgs/nixos/modules/profiles/macos-builder.nix b/nixpkgs/nixos/modules/profiles/macos-builder.nix
index 768c673e7f37..cc01b16960ce 100644
--- a/nixpkgs/nixos/modules/profiles/macos-builder.nix
+++ b/nixpkgs/nixos/modules/profiles/macos-builder.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, ... }:
+{ config, lib, ... }:
 
 let
   keysDirectory = "/var/keys";
@@ -21,7 +21,8 @@ in
         ../virtualisation/nixos-containers.nix
         ../services/x11/desktop-managers/xterm.nix
       ];
-      config = { };
+      # swraid's default depends on stateVersion
+      config.boot.swraid.enable = false;
       options.boot.isContainer = lib.mkOption { default = false; internal = true; };
     }
   ];
@@ -67,9 +68,9 @@ in
        '';
     };
     hostPort = mkOption {
-      default = 22;
+      default = 31022;
       type = types.int;
-      example = 31022;
+      example = 22;
       description = ''
         The localhost host port to forward TCP to the guest port.
       '';
@@ -139,13 +140,13 @@ in
 
         hostPkgs = config.virtualisation.host.pkgs;
 
-  script = hostPkgs.writeShellScriptBin "create-builder" (
+        script = hostPkgs.writeShellScriptBin "create-builder" (
           # When running as non-interactively as part of a DarwinConfiguration the working directory
           # must be set to a writeable directory.
         (if cfg.workingDirectory != "." then ''
           ${hostPkgs.coreutils}/bin/mkdir --parent "${cfg.workingDirectory}"
           cd "${cfg.workingDirectory}"
-  '' else "") + ''
+        '' else "") + ''
           KEYS="''${KEYS:-./keys}"
           ${hostPkgs.coreutils}/bin/mkdir --parent "''${KEYS}"
           PRIVATE_KEY="''${KEYS}/${user}_${keyType}"
@@ -157,7 +158,7 @@ in
           if ! ${hostPkgs.diffutils}/bin/cmp "''${PUBLIC_KEY}" ${publicKey}; then
             (set -x; sudo --reset-timestamp ${installCredentials} "''${KEYS}")
           fi
-          KEYS="$(${hostPkgs.nix}/bin/nix-store --add "$KEYS")" ${config.system.build.vm}/bin/run-nixos-vm
+          KEYS="$(${hostPkgs.nix}/bin/nix-store --add "$KEYS")" ${lib.getExe config.system.build.vm}
         '');
 
       in
@@ -177,7 +178,7 @@ in
         Please inspect the trace of the following command to figure out which module
         has a dependency on stateVersion.
 
-          nix-instantiate --attr darwin.builder --show-trace
+          nix-instantiate --attr darwin.linux-builder --show-trace
       '');
     };
 
@@ -234,6 +235,10 @@ in
       # This ensures that anything built on the guest isn't lost when the guest is
       # restarted.
       writableStoreUseTmpfs = false;
+
+      # Pass certificates from host to the guest otherwise when custom CA certificates
+      # are required we can't use the cached builder.
+      useHostCerts = true;
     };
   };
 }
diff --git a/nixpkgs/nixos/modules/programs/atop.nix b/nixpkgs/nixos/modules/programs/atop.nix
index 9d5843bd670e..a5f4d990bdbe 100644
--- a/nixpkgs/nixos/modules/programs/atop.nix
+++ b/nixpkgs/nixos/modules/programs/atop.nix
@@ -123,8 +123,8 @@ in
       boot.extraModulePackages = [ (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
       systemd =
         let
-          mkSystemd = type: cond: name: restartTriggers: {
-            ${name} = lib.mkIf cond {
+          mkSystemd = type: name: restartTriggers: {
+            ${name} = {
               inherit restartTriggers;
               wantedBy = [ (if type == "services" then "multi-user.target" else if type == "timers" then "timers.target" else null) ];
             };
@@ -134,42 +134,44 @@ in
         in
         {
           packages = [ atop (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
-          services =
-            mkService cfg.atopService.enable "atop" [ atop ]
-            // lib.mkIf cfg.atopService.enable {
-              # always convert logs to newer version first
-              # XXX might trigger TimeoutStart but restarting atop.service will
-              # convert remainings logs and start eventually
-              atop.serviceConfig.ExecStartPre = pkgs.writeShellScript "atop-update-log-format" ''
-                set -e -u
-                shopt -s nullglob
-                for logfile in "$LOGPATH"/atop_*
-                do
-                  ${atop}/bin/atopconvert "$logfile" "$logfile".new
-                  # only replace old file if version was upgraded to avoid
-                  # false positives for atop-rotate.service
-                  if ! ${pkgs.diffutils}/bin/cmp -s "$logfile" "$logfile".new
-                  then
-                    ${pkgs.coreutils}/bin/mv -v -f "$logfile".new "$logfile"
-                  else
-                    ${pkgs.coreutils}/bin/rm -f "$logfile".new
-                  fi
-                done
-              '';
-            }
-            // mkService cfg.atopacctService.enable "atopacct" [ atop ]
-            // mkService cfg.netatop.enable "netatop" [ cfg.netatop.package ]
-            // mkService cfg.atopgpu.enable "atopgpu" [ atop ];
-          timers = mkTimer cfg.atopRotateTimer.enable "atop-rotate" [ atop ];
+          services = lib.mkMerge [
+            (lib.mkIf cfg.atopService.enable (lib.recursiveUpdate
+              (mkService "atop" [ atop ])
+              {
+                # always convert logs to newer version first
+                # XXX might trigger TimeoutStart but restarting atop.service will
+                # convert remainings logs and start eventually
+                atop.preStart = ''
+                  set -e -u
+                  shopt -s nullglob
+                  for logfile in "$LOGPATH"/atop_*
+                  do
+                    ${atop}/bin/atopconvert "$logfile" "$logfile".new
+                    # only replace old file if version was upgraded to avoid
+                    # false positives for atop-rotate.service
+                    if ! ${pkgs.diffutils}/bin/cmp -s "$logfile" "$logfile".new
+                    then
+                      ${pkgs.coreutils}/bin/mv -v -f "$logfile".new "$logfile"
+                    else
+                      ${pkgs.coreutils}/bin/rm -f "$logfile".new
+                    fi
+                  done
+                '';
+              }))
+            (lib.mkIf cfg.atopacctService.enable (mkService "atopacct" [ atop ]))
+            (lib.mkIf cfg.netatop.enable (mkService "netatop" [ cfg.netatop.package ]))
+            (lib.mkIf cfg.atopgpu.enable (mkService "atopgpu" [ atop ]))
+          ];
+          timers = lib.mkIf cfg.atopRotateTimer.enable (mkTimer "atop-rotate" [ atop ]);
         };
 
       security.wrappers = lib.mkIf cfg.setuidWrapper.enable {
-        atop =
-          { setuid = true;
-            owner = "root";
-            group = "root";
-            source = "${atop}/bin/atop";
-          };
+        atop = {
+          setuid = true;
+          owner = "root";
+          group = "root";
+          source = "${atop}/bin/atop";
+        };
       };
     }
   );
diff --git a/nixpkgs/nixos/modules/programs/cfs-zen-tweaks.nix b/nixpkgs/nixos/modules/programs/cfs-zen-tweaks.nix
index 97c2570475c4..fc05bcd11ecb 100644
--- a/nixpkgs/nixos/modules/programs/cfs-zen-tweaks.nix
+++ b/nixpkgs/nixos/modules/programs/cfs-zen-tweaks.nix
@@ -23,6 +23,12 @@ in
   config = mkIf cfg.enable {
     systemd.packages = [ pkgs.cfs-zen-tweaks ];
 
-    systemd.services.set-cfs-tweak.wantedBy = [ "multi-user.target" "suspend.target" "hibernate.target" "hybrid-sleep.target" "suspend-then-hibernate.target" ];
+    systemd.services.set-cfs-tweaks.wantedBy = [
+      "multi-user.target"
+      "suspend.target"
+      "hibernate.target"
+      "hybrid-sleep.target"
+      "suspend-then-hibernate.target"
+    ];
   };
 }
diff --git a/nixpkgs/nixos/modules/programs/direnv.nix b/nixpkgs/nixos/modules/programs/direnv.nix
new file mode 100644
index 000000000000..53717fae11a0
--- /dev/null
+++ b/nixpkgs/nixos/modules/programs/direnv.nix
@@ -0,0 +1,147 @@
+{
+  lib,
+  config,
+  pkgs,
+  ...
+}: let
+  cfg = config.programs.direnv;
+in {
+  options.programs.direnv = {
+
+    enable = lib.mkEnableOption (lib.mdDoc ''
+      direnv integration. Takes care of both installation and
+      setting up the sourcing of the shell. Additionally enables nix-direnv
+      integration. Note that you need to logout and login for this change to apply.
+    '');
+
+    package = lib.mkPackageOptionMD pkgs "direnv" {};
+
+    direnvrcExtra = lib.mkOption {
+      type = lib.types.lines;
+      default = "";
+      example = ''
+        export FOO="foo"
+        echo "loaded direnv!"
+      '';
+      description = lib.mdDoc ''
+        Extra lines to append to the sourced direnvrc
+      '';
+    };
+
+    silent = lib.mkEnableOption (lib.mdDoc ''
+      the hiding of direnv logging
+    '');
+
+    persistDerivations =
+      (lib.mkEnableOption (lib.mdDoc ''
+        setting keep-derivations and keep-outputs to true
+        to prevent shells from getting garbage collected
+      ''))
+      // {
+        default = true;
+      };
+
+    loadInNixShell =
+      lib.mkEnableOption (lib.mdDoc ''
+        loading direnv in `nix-shell` `nix shell` or `nix develop`
+      '')
+      // {
+        default = true;
+      };
+
+    nix-direnv = {
+      enable =
+        (lib.mkEnableOption (lib.mdDoc ''
+          a faster, persistent implementation of use_nix and use_flake, to replace the built-in one
+        ''))
+        // {
+          default = true;
+        };
+
+      package = lib.mkPackageOptionMD pkgs "nix-direnv" {};
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    programs = {
+      zsh.interactiveShellInit = ''
+        if ${lib.boolToString cfg.loadInNixShell} || printenv PATH | grep -vqc '/nix/store'; then
+         eval "$(${lib.getExe cfg.package} hook zsh)"
+        fi
+      '';
+
+      #$NIX_GCROOT for "nix develop" https://github.com/NixOS/nix/blob/6db66ebfc55769edd0c6bc70fcbd76246d4d26e0/src/nix/develop.cc#L530
+      #$IN_NIX_SHELL for "nix-shell"
+      bash.interactiveShellInit = ''
+        if ${lib.boolToString cfg.loadInNixShell} || [ -z "$IN_NIX_SHELL$NIX_GCROOT$(printenv PATH | grep '/nix/store')" ] ; then
+         eval "$(${lib.getExe cfg.package} hook bash)"
+        fi
+      '';
+
+      fish.interactiveShellInit = ''
+        if ${lib.boolToString cfg.loadInNixShell};
+        or printenv PATH | grep -vqc '/nix/store';
+         ${lib.getExe cfg.package} hook fish | source
+        end
+      '';
+    };
+
+    nix.settings = lib.mkIf cfg.persistDerivations {
+      keep-outputs = true;
+      keep-derivations = true;
+    };
+
+    environment = {
+      systemPackages =
+        if cfg.loadInNixShell then [cfg.package]
+        else [
+          #direnv has a fish library which sources direnv for some reason
+          (cfg.package.overrideAttrs (old: {
+            installPhase =
+              (old.installPhase or "")
+              + ''
+                rm -rf $out/share/fish
+              '';
+          }))
+        ];
+
+      variables = {
+        DIRENV_CONFIG = "/etc/direnv";
+        DIRENV_LOG_FORMAT = lib.mkIf cfg.silent "";
+      };
+
+      etc = {
+        "direnv/direnvrc".text = ''
+          ${lib.optionalString cfg.nix-direnv.enable ''
+            #Load nix-direnv
+            source ${cfg.nix-direnv.package}/share/nix-direnv/direnvrc
+          ''}
+
+           #Load direnvrcExtra
+           ${cfg.direnvrcExtra}
+
+           #Load user-configuration if present (~/.direnvrc or ~/.config/direnv/direnvrc)
+           direnv_config_dir_home="''${DIRENV_CONFIG_HOME:-''${XDG_CONFIG_HOME:-$HOME/.config}/direnv}"
+           if [[ -f $direnv_config_dir_home/direnvrc ]]; then
+             source "$direnv_config_dir_home/direnvrc" >&2
+           elif [[ -f $HOME/.direnvrc ]]; then
+             source "$HOME/.direnvrc" >&2
+           fi
+
+           unset direnv_config_dir_home
+        '';
+
+        "direnv/lib/zz-user.sh".text = ''
+          direnv_config_dir_home="''${DIRENV_CONFIG_HOME:-''${XDG_CONFIG_HOME:-$HOME/.config}/direnv}"
+
+          for lib in "$direnv_config_dir_home/lib/"*.sh; do
+            source "$lib"
+          done
+
+          unset direnv_config_dir_home
+        '';
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/programs/firefox.nix b/nixpkgs/nixos/modules/programs/firefox.nix
index ead048134d8d..d67bbee9a761 100644
--- a/nixpkgs/nixos/modules/programs/firefox.nix
+++ b/nixpkgs/nixos/modules/programs/firefox.nix
@@ -233,7 +233,6 @@ in
     nixpkgs.config.firefox = {
       enableBrowserpass = nmh.browserpass;
       enableBukubrow = nmh.bukubrow;
-      enableEUWebID = nmh.euwebid;
       enableTridactylNative = nmh.tridactyl;
       enableUgetIntegrator = nmh.ugetIntegrator;
       enableFXCastBridge = nmh.fxCast;
diff --git a/nixpkgs/nixos/modules/programs/fish.nix b/nixpkgs/nixos/modules/programs/fish.nix
index 478f07d01310..8b78d3d9e2b2 100644
--- a/nixpkgs/nixos/modules/programs/fish.nix
+++ b/nixpkgs/nixos/modules/programs/fish.nix
@@ -37,7 +37,7 @@ let
   babelfishTranslate = path: name:
     pkgs.runCommandLocal "${name}.fish" {
       nativeBuildInputs = [ pkgs.babelfish ];
-    } "${pkgs.babelfish}/bin/babelfish < ${path} > $out;";
+    } "babelfish < ${path} > $out;";
 
 in
 
diff --git a/nixpkgs/nixos/modules/programs/gnupg.nix b/nixpkgs/nixos/modules/programs/gnupg.nix
index 764a67a160c1..697b6e9a0bd0 100644
--- a/nixpkgs/nixos/modules/programs/gnupg.nix
+++ b/nixpkgs/nixos/modules/programs/gnupg.nix
@@ -75,9 +75,7 @@ in
       defaultText = literalMD ''matching the configured desktop environment'';
       description = lib.mdDoc ''
         Which pinentry interface to use. If not null, the path to the
-        pinentry binary will be passed to gpg-agent via commandline and
-        thus overrides the pinentry option in gpg-agent.conf in the user's
-        home directory.
+        pinentry binary will be set in /etc/gnupg/gpg-agent.conf.
         If not set at all, it'll pick an appropriate flavor depending on the
         system configuration (qt flavor for lxqt and plasma5, gtk2 for xfce
         4.12, gnome3 on all other systems with X enabled, ncurses otherwise).
@@ -94,38 +92,111 @@ in
   };
 
   config = mkIf cfg.agent.enable {
+    environment.etc."gnupg/gpg-agent.conf".text =
+      lib.optionalString (cfg.agent.pinentryFlavor != null) ''
+      pinentry-program ${pkgs.pinentry.${cfg.agent.pinentryFlavor}}/bin/pinentry
+    '';
+
     # This overrides the systemd user unit shipped with the gnupg package
-    systemd.user.services.gpg-agent = mkIf (cfg.agent.pinentryFlavor != null) {
-      serviceConfig.ExecStart = [ "" ''
-        ${cfg.package}/bin/gpg-agent --supervised \
-          --pinentry-program ${pkgs.pinentry.${cfg.agent.pinentryFlavor}}/bin/pinentry
-      '' ];
+    systemd.user.services.gpg-agent = {
+      unitConfig = {
+        Description = "GnuPG cryptographic agent and passphrase cache";
+        Documentation = "man:gpg-agent(1)";
+        Requires = [ "gpg-agent.socket" ];
+      };
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/gpg-agent --supervised";
+        ExecReload = "${cfg.package}/bin/gpgconf --reload gpg-agent";
+      };
     };
 
     systemd.user.sockets.gpg-agent = {
+      unitConfig = {
+        Description = "GnuPG cryptographic agent and passphrase cache";
+        Documentation = "man:gpg-agent(1)";
+      };
+      socketConfig = {
+        ListenStream = "%t/gnupg/S.gpg-agent";
+        FileDescriptorName = "std";
+        SocketMode = "0600";
+        DirectoryMode = "0700";
+      };
       wantedBy = [ "sockets.target" ];
     };
 
     systemd.user.sockets.gpg-agent-ssh = mkIf cfg.agent.enableSSHSupport {
+      unitConfig = {
+        Description = "GnuPG cryptographic agent (ssh-agent emulation)";
+        Documentation = "man:gpg-agent(1) man:ssh-add(1) man:ssh-agent(1) man:ssh(1)";
+      };
+      socketConfig = {
+        ListenStream = "%t/gnupg/S.gpg-agent.ssh";
+        FileDescriptorName = "ssh";
+        Service = "gpg-agent.service";
+        SocketMode = "0600";
+        DirectoryMode = "0700";
+      };
       wantedBy = [ "sockets.target" ];
     };
 
     systemd.user.sockets.gpg-agent-extra = mkIf cfg.agent.enableExtraSocket {
+      unitConfig = {
+        Description = "GnuPG cryptographic agent and passphrase cache (restricted)";
+        Documentation = "man:gpg-agent(1)";
+      };
+      socketConfig = {
+        ListenStream = "%t/gnupg/S.gpg-agent.extra";
+        FileDescriptorName = "extra";
+        Service = "gpg-agent.service";
+        SocketMode = "0600";
+        DirectoryMode = "0700";
+      };
       wantedBy = [ "sockets.target" ];
     };
 
     systemd.user.sockets.gpg-agent-browser = mkIf cfg.agent.enableBrowserSocket {
+      unitConfig = {
+        Description = "GnuPG cryptographic agent and passphrase cache (access for web browsers)";
+        Documentation = "man:gpg-agent(1)";
+      };
+      socketConfig = {
+        ListenStream = "%t/gnupg/S.gpg-agent.browser";
+        FileDescriptorName = "browser";
+        Service = "gpg-agent.service";
+        SocketMode = "0600";
+        DirectoryMode = "0700";
+      };
       wantedBy = [ "sockets.target" ];
     };
 
+    systemd.user.services.dirmngr = mkIf cfg.dirmngr.enable {
+      unitConfig = {
+        Description = "GnuPG network certificate management daemon";
+        Documentation = "man:dirmngr(8)";
+        Requires = "dirmngr.socket";
+      };
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/dirmngr --supervised";
+        ExecReload = "${cfg.package}/bin/gpgconf --reload dirmngr";
+      };
+    };
+
     systemd.user.sockets.dirmngr = mkIf cfg.dirmngr.enable {
+      unitConfig = {
+        Description = "GnuPG network certificate management daemon";
+        Documentation = "man:dirmngr(8)";
+      };
+      socketConfig = {
+        ListenStream = "%t/gnupg/S.dirmngr";
+        SocketMode = "0600";
+        DirectoryMode = "0700";
+      };
       wantedBy = [ "sockets.target" ];
     };
 
     services.dbus.packages = mkIf (cfg.agent.pinentryFlavor == "gnome3") [ pkgs.gcr ];
 
     environment.systemPackages = with pkgs; [ cfg.package ];
-    systemd.packages = [ cfg.package ];
 
     environment.interactiveShellInit = ''
       # Bind gpg-agent to this TTY if gpg commands are used.
diff --git a/nixpkgs/nixos/modules/programs/hyprland.nix b/nixpkgs/nixos/modules/programs/hyprland.nix
index 92b8e992e648..faeaa8973fa9 100644
--- a/nixpkgs/nixos/modules/programs/hyprland.nix
+++ b/nixpkgs/nixos/modules/programs/hyprland.nix
@@ -6,10 +6,10 @@
 with lib; let
   cfg = config.programs.hyprland;
 
-  defaultHyprlandPackage = pkgs.hyprland.override {
-    enableXWayland = cfg.xwayland.enable;
-    hidpiXWayland = cfg.xwayland.hidpi;
-    nvidiaPatches = cfg.nvidiaPatches;
+  finalPortalPackage = cfg.portalPackage.override {
+    hyprland-share-picker = pkgs.hyprland-share-picker.override {
+      hyprland = cfg.finalPackage;
+    };
   };
 in
 {
@@ -25,24 +25,25 @@ in
       '';
     };
 
-    package = mkOption {
-      type = types.path;
-      default = defaultHyprlandPackage;
-      defaultText = literalExpression ''
-        pkgs.hyprland.override {
-          enableXWayland = config.programs.hyprland.xwayland.enable;
-          hidpiXWayland = config.programs.hyprland.xwayland.hidpi;
-          nvidiaPatches = config.programs.hyprland.nvidiaPatches;
-        }
-      '';
-      example = literalExpression "<Hyprland flake>.packages.<system>.default";
+    package = mkPackageOptionMD pkgs "hyprland" { };
+
+    finalPackage = mkOption {
+      type = types.package;
+      readOnly = true;
+      default = cfg.package.override {
+        enableXWayland = cfg.xwayland.enable;
+        hidpiXWayland = cfg.xwayland.hidpi;
+        nvidiaPatches = cfg.nvidiaPatches;
+      };
+      defaultText = literalExpression
+        "`wayland.windowManager.hyprland.package` with applied configuration";
       description = mdDoc ''
-        The Hyprland package to use.
-        Setting this option will make {option}`programs.hyprland.xwayland` and
-        {option}`programs.hyprland.nvidiaPatches` not work.
+        The Hyprland package after applying configuration.
       '';
     };
 
+    portalPackage = mkPackageOptionMD pkgs "xdg-desktop-portal-hyprland" { };
+
     xwayland = {
       enable = mkEnableOption (mdDoc "XWayland") // { default = true; };
       hidpi = mkEnableOption null // {
@@ -57,9 +58,9 @@ in
   };
 
   config = mkIf cfg.enable {
-    environment.systemPackages = [ cfg.package ];
+    environment.systemPackages = [ cfg.finalPackage ];
 
-    fonts.enableDefaultFonts = mkDefault true;
+    fonts.enableDefaultPackages = mkDefault true;
     hardware.opengl.enable = mkDefault true;
 
     programs = {
@@ -69,13 +70,11 @@ in
 
     security.polkit.enable = true;
 
-    services.xserver.displayManager.sessionPackages = [ cfg.package ];
+    services.xserver.displayManager.sessionPackages = [ cfg.finalPackage ];
 
     xdg.portal = {
       enable = mkDefault true;
-      extraPortals = [
-        pkgs.xdg-desktop-portal-hyprland
-      ];
+      extraPortals = [ finalPortalPackage ];
     };
   };
 }
diff --git a/nixpkgs/nixos/modules/programs/miriway.nix b/nixpkgs/nixos/modules/programs/miriway.nix
index a67e1a17a7e6..e8a10770b6a3 100644
--- a/nixpkgs/nixos/modules/programs/miriway.nix
+++ b/nixpkgs/nixos/modules/programs/miriway.nix
@@ -66,7 +66,7 @@ in {
     };
 
     hardware.opengl.enable = lib.mkDefault true;
-    fonts.enableDefaultFonts = lib.mkDefault true;
+    fonts.enableDefaultPackages = lib.mkDefault true;
     programs.dconf.enable = lib.mkDefault true;
     programs.xwayland.enable = lib.mkDefault true;
 
diff --git a/nixpkgs/nixos/modules/programs/nix-ld.nix b/nixpkgs/nixos/modules/programs/nix-ld.nix
index f0c265f0e5a3..d54b3917f89a 100644
--- a/nixpkgs/nixos/modules/programs/nix-ld.nix
+++ b/nixpkgs/nixos/modules/programs/nix-ld.nix
@@ -2,15 +2,14 @@
 let
   cfg = config.programs.nix-ld;
 
-  # TODO make glibc here configurable?
-  nix-ld-so = pkgs.runCommand "ld.so" {} ''
-    ln -s "$(cat '${pkgs.stdenv.cc}/nix-support/dynamic-linker')" $out
-  '';
-
   nix-ld-libraries = pkgs.buildEnv {
     name = "lb-library-path";
     pathsToLink = [ "/lib" ];
     paths = map lib.getLib cfg.libraries;
+    # TODO make glibc here configurable?
+    postBuild = ''
+      ln -s ${pkgs.stdenv.cc.bintools.dynamicLinker} $out/share/nix-ld/lib/ld.so
+    '';
     extraPrefix = "/share/nix-ld";
     ignoreCollisions = true;
   };
@@ -38,12 +37,7 @@ in
   meta.maintainers = [ lib.maintainers.mic92 ];
   options.programs.nix-ld = {
     enable = lib.mkEnableOption (lib.mdDoc ''nix-ld, Documentation: <https://github.com/Mic92/nix-ld>'');
-    package = lib.mkOption {
-      type = lib.types.package;
-      description = lib.mdDoc "Which package to use for the nix-ld.";
-      default = pkgs.nix-ld;
-      defaultText = lib.literalExpression "pkgs.nix-ld";
-    };
+    package = lib.mkPackageOptionMD pkgs "nix-ld" { };
     libraries = lib.mkOption {
       type = lib.types.listOf lib.types.package;
       description = lib.mdDoc "Libraries that automatically become available to all programs. The default set includes common libraries.";
@@ -60,7 +54,7 @@ in
     environment.pathsToLink = [ "/share/nix-ld" ];
 
     environment.variables = {
-      NIX_LD = toString nix-ld-so;
+      NIX_LD = "/run/current-system/sw/share/nix-ld/lib/ld.so";
       NIX_LD_LIBRARY_PATH = "/run/current-system/sw/share/nix-ld/lib";
     };
   };
diff --git a/nixpkgs/nixos/modules/programs/ns-usbloader.nix b/nixpkgs/nixos/modules/programs/ns-usbloader.nix
new file mode 100644
index 000000000000..8d0b698d6b4c
--- /dev/null
+++ b/nixpkgs/nixos/modules/programs/ns-usbloader.nix
@@ -0,0 +1,18 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.programs.ns-usbloader;
+in
+{
+  options = {
+    programs.ns-usbloader = {
+      enable = lib.mkEnableOption (lib.mdDoc "ns-usbloader application with udev rules applied");
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.ns-usbloader ];
+    services.udev.packages = [ pkgs.ns-usbloader ];
+  };
+
+  meta.maintainers = pkgs.ns-usbloader.meta.maintainers;
+}
diff --git a/nixpkgs/nixos/modules/programs/oddjobd.nix b/nixpkgs/nixos/modules/programs/oddjobd.nix
new file mode 100644
index 000000000000..b0920d007c9e
--- /dev/null
+++ b/nixpkgs/nixos/modules/programs/oddjobd.nix
@@ -0,0 +1,33 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.programs.oddjobd;
+in
+{
+  options.programs.oddjobd = {
+    enable = lib.mkEnableOption "oddjob";
+    package = lib.mkPackageOption pkgs "oddjob" {};
+  };
+
+  config = lib.mkIf cfg.enable {
+    assertions = [
+      { assertion = false;
+        message = "The oddjob service was found to be broken without NixOS test or maintainer. Please take ownership of this service.";
+      }
+    ];
+    systemd.packages = [ cfg.package ];
+
+    systemd.services.oddjobd = {
+      wantedBy = [ "multi-user.target"];
+      after = [ "network.target"];
+      description = "DBUS Odd-job Daemon";
+      enable = true;
+      documentation = [ "man:oddjobd(8)" "man:oddjobd.conf(5)" ];
+      serviceConfig = {
+        Type = "dbus";
+        BusName = "org.freedesktop.oddjob";
+        ExecStart = "${lib.getBin cfg.package}/bin/oddjobd";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/programs/shadow.nix b/nixpkgs/nixos/modules/programs/shadow.nix
index 35267acd6bb7..00895db03fc3 100644
--- a/nixpkgs/nixos/modules/programs/shadow.nix
+++ b/nixpkgs/nixos/modules/programs/shadow.nix
@@ -1,67 +1,131 @@
 # Configuration for the pwdutils suite of tools: passwd, useradd, etc.
-
 { config, lib, utils, pkgs, ... }:
-
 with lib;
-
 let
-
-  /*
-  There are three different sources for user/group id ranges, each of which gets
-  used by different programs:
-  - The login.defs file, used by the useradd, groupadd and newusers commands
-  - The update-users-groups.pl file, used by NixOS in the activation phase to
-    decide on which ids to use for declaratively defined users without a static
-    id
-  - Systemd compile time options -Dsystem-uid-max= and -Dsystem-gid-max=, used
-    by systemd for features like ConditionUser=@system and systemd-sysusers
-  */
-  loginDefs =
-    ''
-      DEFAULT_HOME yes
-
-      SYS_UID_MIN  400
-      SYS_UID_MAX  999
-      UID_MIN      1000
-      UID_MAX      29999
-
-      SYS_GID_MIN  400
-      SYS_GID_MAX  999
-      GID_MIN      1000
-      GID_MAX      29999
-
-      TTYGROUP     tty
-      TTYPERM      0620
-
-      # Ensure privacy for newly created home directories.
-      UMASK        077
-
-      # Uncomment this and install chfn SUID to allow non-root
-      # users to change their account GECOS information.
-      # This should be made configurable.
-      #CHFN_RESTRICT frwh
-
-      # The default crypt() method, keep in sync with the PAM default
-      ENCRYPT_METHOD YESCRYPT
-    '';
-
-  mkSetuidRoot = source:
-    { setuid = true;
-      owner = "root";
-      group = "root";
-      inherit source;
-    };
-
+  cfg = config.security.loginDefs;
 in
-
 {
+  options = with types; {
+    security.loginDefs = {
+      package = mkPackageOptionMD pkgs "shadow" { };
+
+      chfnRestrict = mkOption {
+        description = mdDoc ''
+          Use chfn SUID to allow non-root users to change their account GECOS information.
+        '';
+        type = nullOr str;
+        default = null;
+      };
 
-  ###### interface
-
-  options = {
+      settings = mkOption {
+        description = mdDoc ''
+          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 {
+          freeformType = (pkgs.formats.keyValue { }).type;
+          /* There are three different sources for user/group id ranges, each of which gets
+             used by different programs:
+             - The login.defs file, used by the useradd, groupadd and newusers commands
+             - The update-users-groups.pl file, used by NixOS in the activation phase to
+               decide on which ids to use for declaratively defined users without a static
+               id
+             - Systemd compile time options -Dsystem-uid-max= and -Dsystem-gid-max=, used
+               by systemd for features like ConditionUser=@system and systemd-sysusers
+              */
+          options = {
+            DEFAULT_HOME = mkOption {
+              description = mdDoc "Indicate if login is allowed if we can't cd to the home directory.";
+              default = "yes";
+              type = enum [ "yes" "no" ];
+            };
+
+            ENCRYPT_METHOD = mkOption {
+              description = mdDoc "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"];
+            };
+
+            SYS_UID_MIN = mkOption {
+              description = mdDoc "Range of user IDs used for the creation of system users by useradd or newusers.";
+              default = 400;
+              type = int;
+            };
+
+            SYS_UID_MAX = mkOption {
+              description = mdDoc "Range of user IDs used for the creation of system users by useradd or newusers.";
+              default = 999;
+              type = int;
+            };
+
+            UID_MIN = mkOption {
+              description = mdDoc "Range of user IDs used for the creation of regular users by useradd or newusers.";
+              default = 1000;
+              type = int;
+            };
+
+            UID_MAX = mkOption {
+              description = mdDoc "Range of user IDs used for the creation of regular users by useradd or newusers.";
+              default = 29999;
+              type = int;
+            };
+
+            SYS_GID_MIN = mkOption {
+              description = mdDoc "Range of group IDs used for the creation of system groups by useradd, groupadd, or newusers";
+              default = 400;
+              type = int;
+            };
+
+            SYS_GID_MAX = mkOption {
+              description = mdDoc "Range of group IDs used for the creation of system groups by useradd, groupadd, or newusers";
+              default = 999;
+              type = int;
+            };
+
+            GID_MIN = mkOption {
+              description = mdDoc "Range of group IDs used for the creation of regular groups by useradd, groupadd, or newusers.";
+              default = 1000;
+              type = int;
+            };
+
+            GID_MAX = mkOption {
+              description = mdDoc "Range of group IDs used for the creation of regular groups by useradd, groupadd, or newusers.";
+              default = 29999;
+              type = int;
+            };
+
+            TTYGROUP = mkOption {
+              description = mdDoc ''
+                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;
+            };
+
+            TTYPERM = mkOption {
+              description = mdDoc ''
+                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;
+            };
+
+            # Ensure privacy for newly created home directories.
+            UMASK = mkOption {
+              description = mdDoc "The file mode creation mask is initialized to this value.";
+              default = "077";
+              type = str;
+            };
+          };
+        };
+        default = { };
+      };
+    };
 
-    users.defaultUserShell = lib.mkOption {
-      description = lib.mdDoc ''
+    users.defaultUserShell = mkOption {
+      description = mdDoc ''
         This option defines the default shell assigned to user
         accounts. This can be either a full system path or a shell package.
 
@@ -69,63 +133,107 @@ in
         used outside the store (in particular in /etc/passwd).
       '';
       example = literalExpression "pkgs.zsh";
-      type = types.either types.path types.shellPackage;
+      type = either path shellPackage;
     };
-
   };
 
-
   ###### implementation
 
   config = {
-
-    environment.systemPackages =
-      lib.optional config.users.mutableUsers pkgs.shadow ++
-      lib.optional (types.shellPackage.check config.users.defaultUserShell)
-        config.users.defaultUserShell;
+    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 =
-      { # /etc/login.defs: global configuration for pwdutils.  You
-        # cannot login without it!
-        "login.defs".source = pkgs.writeText "login.defs" loginDefs;
+      # 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}
-          '';
+        "default/useradd".source = pkgs.writeText "useradd" ''
+          GROUP=100
+          HOME=/home
+          SHELL=${utils.toShellPath config.users.defaultUserShell}
+        '';
       };
 
-    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.pam.services = {
+      chsh = { rootOK = true; };
+      chfn = { rootOK = true; };
+      su = {
+        rootOK = true;
+        forwardXAuth = true;
+        logFailures = true;
       };
-
-    security.wrappers = {
-      su        = mkSetuidRoot "${pkgs.shadow.su}/bin/su";
-      sg        = mkSetuidRoot "${pkgs.shadow.out}/bin/sg";
-      newgrp    = mkSetuidRoot "${pkgs.shadow.out}/bin/newgrp";
-      newuidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newuidmap";
-      newgidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newgidmap";
-    } // lib.optionalAttrs config.users.mutableUsers {
-      chsh   = mkSetuidRoot "${pkgs.shadow.out}/bin/chsh";
-      passwd = mkSetuidRoot "${pkgs.shadow.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";
+      }
+      // optionalAttrs config.users.mutableUsers {
+        chsh = mkSetuidRoot "${cfg.package.out}/bin/chsh";
+        passwd = mkSetuidRoot "${cfg.package.out}/bin/passwd";
+      };
   };
 }
diff --git a/nixpkgs/nixos/modules/programs/starship.nix b/nixpkgs/nixos/modules/programs/starship.nix
index cacad8eafe3d..9dca39da5edc 100644
--- a/nixpkgs/nixos/modules/programs/starship.nix
+++ b/nixpkgs/nixos/modules/programs/starship.nix
@@ -43,21 +43,21 @@ in
 
   config = mkIf cfg.enable {
     programs.bash.${initOption} = ''
-      if [[ $TERM != "dumb" && (-z $INSIDE_EMACS || $INSIDE_EMACS == "vterm") ]]; then
+      if [[ $TERM != "dumb" ]]; then
         export STARSHIP_CONFIG=${settingsFile}
         eval "$(${pkgs.starship}/bin/starship init bash)"
       fi
     '';
 
     programs.fish.${initOption} = ''
-      if test "$TERM" != "dumb" -a \( -z "$INSIDE_EMACS" -o "$INSIDE_EMACS" = "vterm" \)
+      if test "$TERM" != "dumb"
         set -x STARSHIP_CONFIG ${settingsFile}
         eval (${pkgs.starship}/bin/starship init fish)
       end
     '';
 
     programs.zsh.${initOption} = ''
-      if [[ $TERM != "dumb" && (-z $INSIDE_EMACS || $INSIDE_EMACS == "vterm") ]]; then
+      if [[ $TERM != "dumb" ]]; then
         export STARSHIP_CONFIG=${settingsFile}
         eval "$(${pkgs.starship}/bin/starship init zsh)"
       fi
diff --git a/nixpkgs/nixos/modules/programs/wayland/wayfire.nix b/nixpkgs/nixos/modules/programs/wayland/wayfire.nix
new file mode 100644
index 000000000000..d0b280e3940f
--- /dev/null
+++ b/nixpkgs/nixos/modules/programs/wayland/wayfire.nix
@@ -0,0 +1,48 @@
+{ config, lib, pkgs, ...}:
+let
+  cfg = config.programs.wayfire;
+in
+{
+  meta.maintainers = with lib.maintainers; [ rewine ];
+
+  options.programs.wayfire = {
+    enable = lib.mkEnableOption (lib.mdDoc "Wayfire, a wayland compositor based on wlroots.");
+
+    package = lib.mkPackageOptionMD pkgs "wayfire" { };
+
+    plugins = lib.mkOption {
+      type = lib.types.listOf lib.types.package;
+      default = with pkgs.wayfirePlugins; [ wcm wf-shell ];
+      defaultText = lib.literalExpression "with pkgs.wayfirePlugins; [ wcm wf-shell ]";
+      example = lib.literalExpression ''
+        with pkgs.wayfirePlugins; [
+          wcm
+          wf-shell
+          wayfire-plugins-extra
+        ];
+      '';
+      description = lib.mdDoc ''
+        Additional plugins to use with the wayfire window manager.
+      '';
+    };
+  };
+
+  config = let
+    finalPackage = pkgs.wayfire-with-plugins.override {
+      wayfire = cfg.package;
+      plugins = cfg.plugins;
+    };
+  in
+  lib.mkIf cfg.enable {
+    environment.systemPackages = [
+      finalPackage
+    ];
+
+    services.xserver.displayManager.sessionPackages = [ finalPackage ];
+
+    xdg.portal = {
+      enable = lib.mkDefault true;
+      wlr.enable = lib.mkDefault true;
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/programs/wayland/wayland-session.nix b/nixpkgs/nixos/modules/programs/wayland/wayland-session.nix
index 3cbfef4d61de..da117ceae0ad 100644
--- a/nixpkgs/nixos/modules/programs/wayland/wayland-session.nix
+++ b/nixpkgs/nixos/modules/programs/wayland/wayland-session.nix
@@ -5,7 +5,7 @@
     };
 
     hardware.opengl.enable = mkDefault true;
-    fonts.enableDefaultFonts = mkDefault true;
+    fonts.enableDefaultPackages = mkDefault true;
 
     programs = {
       dconf.enable = mkDefault true;
diff --git a/nixpkgs/nixos/modules/programs/xonsh.nix b/nixpkgs/nixos/modules/programs/xonsh.nix
index 7202ed06c6af..167c953f5ffd 100644
--- a/nixpkgs/nixos/modules/programs/xonsh.nix
+++ b/nixpkgs/nixos/modules/programs/xonsh.nix
@@ -28,7 +28,7 @@ in
         type = types.package;
         default = pkgs.xonsh;
         defaultText = literalExpression "pkgs.xonsh";
-        example = literalExpression "pkgs.xonsh.override { configFile = \"/path/to/xonshrc\"; }";
+        example = literalExpression "pkgs.xonsh.override { extraPackages = ps: [ ps.requests ]; }";
         description = lib.mdDoc ''
           xonsh package to use.
         '';
@@ -83,4 +83,3 @@ in
   };
 
 }
-
diff --git a/nixpkgs/nixos/modules/rename.nix b/nixpkgs/nixos/modules/rename.nix
index c8e540932efa..45014ed3c68e 100644
--- a/nixpkgs/nixos/modules/rename.nix
+++ b/nixpkgs/nixos/modules/rename.nix
@@ -54,7 +54,9 @@ in
     (mkRemovedOptionModule [ "services" "chronos" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "services" "couchpotato" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "services" "dd-agent" ] "dd-agent was removed from nixpkgs in favor of the newer datadog-agent.")
+    (mkRemovedOptionModule [ "services" "ddclient" ] "ddclient has been removed on the request of the upstream maintainer because it is unmaintained and has bugs. Please switch to a different software like `inadyn` or `knsupdate`.") # Added 2023-07-04
     (mkRemovedOptionModule [ "services" "dnscrypt-proxy" ] "Use services.dnscrypt-proxy2 instead")
+    (mkRemovedOptionModule [ "services" "exhibitor" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "services" "firefox" "syncserver" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "services" "flashpolicyd" ] "The flashpolicyd module has been removed. Adobe Flash Player is deprecated.")
     (mkRemovedOptionModule [ "services" "fourStore" ] "The fourStore module has been removed")
@@ -70,7 +72,6 @@ in
     (mkRemovedOptionModule [ "services" "mesos" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "services" "moinmoin" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "services" "mwlib" ] "The corresponding package was removed from nixpkgs.")
-    (mkRemovedOptionModule [ "services" "osquery" ] "The osquery module has been removed")
     (mkRemovedOptionModule [ "services" "pantheon" "files" ] ''
       This module was removed, please add pkgs.pantheon.elementary-files to environment.systemPackages directly.
     '')
@@ -113,6 +114,16 @@ in
     (mkRemovedOptionModule [ "services" "rtsp-simple-server" ] "Package has been completely rebranded by upstream as mediamtx, and thus the service and the package were renamed in NixOS as well.")
 
     (mkRemovedOptionModule [ "i18n" "inputMethod" "fcitx" ] "The fcitx module has been removed. Please use fcitx5 instead")
+    (mkRemovedOptionModule [ "services" "dhcpd4" ] ''
+      The dhcpd4 module has been removed because ISC DHCP reached its end of life.
+      See https://www.isc.org/blogs/isc-dhcp-eol/ for details.
+      Please switch to a different implementation like kea or dnsmasq.
+    '')
+    (mkRemovedOptionModule [ "services" "dhcpd6" ] ''
+      The dhcpd6 module has been removed because ISC DHCP reached its end of life.
+      See https://www.isc.org/blogs/isc-dhcp-eol/ for details.
+      Please switch to a different implementation like kea or dnsmasq.
+    '')
 
     # Do NOT add any option renames here, see top of the file
   ];
diff --git a/nixpkgs/nixos/modules/security/apparmor/includes.nix b/nixpkgs/nixos/modules/security/apparmor/includes.nix
index adfca04426ca..88051de484c5 100644
--- a/nixpkgs/nixos/modules/security/apparmor/includes.nix
+++ b/nixpkgs/nixos/modules/security/apparmor/includes.nix
@@ -62,7 +62,7 @@ config.security.apparmor.includes = {
     include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/base"
     r ${pkgs.stdenv.cc.libc}/share/locale/**,
     r ${pkgs.stdenv.cc.libc}/share/locale.alias,
-    ${lib.optionalString (pkgs.glibcLocales != null) "r ${pkgs.glibcLocales}/lib/locale/locale-archive,"}
+    r ${config.i18n.glibcLocales}/lib/locale/locale-archive,
     ${etcRule "localtime"}
     r ${pkgs.tzdata}/share/zoneinfo/**,
     r ${pkgs.stdenv.cc.libc}/share/i18n/**,
@@ -72,7 +72,7 @@ config.security.apparmor.includes = {
 
     # bash inspects filesystems at startup
     # and /etc/mtab is linked to /proc/mounts
-    @{PROC}/mounts
+    r @{PROC}/mounts,
 
     # system-wide bash configuration
     '' + lib.concatMapStringsSep "\n" etcRule [
@@ -211,6 +211,9 @@ config.security.apparmor.includes = {
   "abstractions/nis" = ''
     include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nis"
   '';
+  "abstractions/nss-systemd" = ''
+    include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nss-systemd"
+  '';
   "abstractions/nvidia" = ''
     include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nvidia"
     ${etcRule "vdpau_wrapper.cfg"}
@@ -279,6 +282,8 @@ config.security.apparmor.includes = {
     r /var/lib/acme/*/chain.pem,
     r /var/lib/acme/*/fullchain.pem,
 
+    r /etc/pki/tls/certs/,
+
     '' + lib.concatMapStringsSep "\n" etcRule [
       "ssl/certs/ca-certificates.crt"
       "ssl/certs/ca-bundle.crt"
diff --git a/nixpkgs/nixos/modules/security/ca.nix b/nixpkgs/nixos/modules/security/ca.nix
index c704e2c1f51c..3cd56bff04d1 100644
--- a/nixpkgs/nixos/modules/security/ca.nix
+++ b/nixpkgs/nixos/modules/security/ca.nix
@@ -18,6 +18,10 @@ in
 {
 
   options = {
+    security.pki.installCACerts = mkEnableOption "Add CA certificates to system" // {
+      default = true;
+      internal = true;
+    };
 
     security.pki.certificateFiles = mkOption {
       type = types.listOf types.path;
@@ -70,7 +74,7 @@ in
 
   };
 
-  config = {
+  config = mkIf cfg.installCACerts {
 
     # NixOS canonical location + Debian/Ubuntu/Arch/Gentoo compatibility.
     environment.etc."ssl/certs/ca-certificates.crt".source = caBundle;
diff --git a/nixpkgs/nixos/modules/security/lock-kernel-modules.nix b/nixpkgs/nixos/modules/security/lock-kernel-modules.nix
index 674ba857818c..333b64801426 100644
--- a/nixpkgs/nixos/modules/security/lock-kernel-modules.nix
+++ b/nixpkgs/nixos/modules/security/lock-kernel-modules.nix
@@ -22,12 +22,11 @@ with lib;
 
   config = mkIf config.security.lockKernelModules {
     boot.kernelModules = concatMap (x:
-      if x.device != null
-        then
-          if x.fsType == "vfat"
-            then [ "vfat" "nls-cp437" "nls-iso8859-1" ]
-            else [ x.fsType ]
-        else []) config.system.build.fileSystems;
+      optionals (x.device != null) (
+        if x.fsType == "vfat"
+        then [ "vfat" "nls-cp437" "nls-iso8859-1" ]
+        else [ x.fsType ])
+      ) config.system.build.fileSystems;
 
     systemd.services.disable-kernel-module-loading = {
       description = "Disable kernel module loading";
diff --git a/nixpkgs/nixos/modules/security/pam.nix b/nixpkgs/nixos/modules/security/pam.nix
index eac67cfdec5a..ac9da4a823b7 100644
--- a/nixpkgs/nixos/modules/security/pam.nix
+++ b/nixpkgs/nixos/modules/security/pam.nix
@@ -484,6 +484,9 @@ let
           optionalString cfg.mysqlAuth ''
             account sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf
           '' +
+          optionalString (config.services.kanidm.enablePam) ''
+            account sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user
+          '' +
           optionalString (config.services.sssd.enable && cfg.sssdStrictAccess==false) ''
             account sufficient ${pkgs.sssd}/lib/security/pam_sss.so
           '' +
@@ -545,6 +548,9 @@ let
           (let yubi = config.security.pam.yubico; in optionalString cfg.yubicoAuth ''
             auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"}
           '') +
+          (let dp9ik = config.security.pam.dp9ik; in optionalString dp9ik.enable ''
+            auth ${dp9ik.control} ${pkgs.pam_dp9ik}/lib/security/pam_p9.so ${dp9ik.authserver}
+          '') +
           optionalString cfg.fprintAuth ''
             auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so
           '' +
@@ -617,6 +623,9 @@ let
           optionalString use_ldap ''
             auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass
           '' +
+          optionalString config.services.kanidm.enablePam ''
+            auth sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user use_first_pass
+          '' +
           optionalString config.services.sssd.enable ''
             auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass
           '' +
@@ -653,6 +662,9 @@ let
           optionalString cfg.mysqlAuth ''
             password sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf
           '' +
+          optionalString config.services.kanidm.enablePam ''
+            password sufficient ${pkgs.kanidm}/lib/pam_kanidm.so
+          '' +
           optionalString config.services.sssd.enable ''
             password sufficient ${pkgs.sssd}/lib/security/pam_sss.so
           '' +
@@ -714,6 +726,9 @@ let
           optionalString cfg.mysqlAuth ''
             session optional ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf
           '' +
+          optionalString config.services.kanidm.enablePam ''
+            session optional ${pkgs.kanidm}/lib/pam_kanidm.so
+          '' +
           optionalString config.services.sssd.enable ''
             session optional ${pkgs.sssd}/lib/security/pam_sss.so
           '' +
@@ -901,6 +916,32 @@ in
 
     security.pam.enableOTPW = mkEnableOption (lib.mdDoc "the OTPW (one-time password) PAM module");
 
+    security.pam.dp9ik = {
+      enable = mkEnableOption (
+        lib.mdDoc ''
+          the dp9ik pam module provided by tlsclient.
+
+          If set, users can be authenticated against the 9front
+          authentication server given in {option}`security.pam.dp9ik.authserver`.
+        ''
+      );
+      control = mkOption {
+        default = "sufficient";
+        type = types.str;
+        description = lib.mdDoc ''
+          This option sets the pam "control" used for this module.
+        '';
+      };
+      authserver = mkOption {
+        default = null;
+        type = with types; nullOr string;
+        description = lib.mdDoc ''
+          This controls the hostname for the 9front authentication server
+          that users will be authenticated against.
+        '';
+      };
+    };
+
     security.pam.krb5 = {
       enable = mkOption {
         default = config.krb5.enable;
@@ -1298,6 +1339,7 @@ in
       # Include the PAM modules in the system path mostly for the manpages.
       [ pkgs.pam ]
       ++ optional config.users.ldap.enable pam_ldap
+      ++ optional config.services.kanidm.enablePam pkgs.kanidm
       ++ optional config.services.sssd.enable pkgs.sssd
       ++ optionals config.security.pam.krb5.enable [pam_krb5 pam_ccreds]
       ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ]
@@ -1364,6 +1406,9 @@ in
       optionalString use_ldap ''
          mr ${pam_ldap}/lib/security/pam_ldap.so,
       '' +
+      optionalString config.services.kanidm.enablePam ''
+        mr ${pkgs.kanidm}/lib/pam_kanidm.so,
+      '' +
       optionalString config.services.sssd.enable ''
         mr ${pkgs.sssd}/lib/security/pam_sss.so,
       '' +
diff --git a/nixpkgs/nixos/modules/security/sudo.nix b/nixpkgs/nixos/modules/security/sudo.nix
index 296b61fd703b..9ac91bd0d368 100644
--- a/nixpkgs/nixos/modules/security/sudo.nix
+++ b/nixpkgs/nixos/modules/security/sudo.nix
@@ -216,10 +216,10 @@ in
         ${concatStringsSep "\n" (
           lists.flatten (
             map (
-              rule: if (length rule.commands != 0) then [
+              rule: optionals (length rule.commands != 0) [
                 (map (user: "${toUserString user}	${rule.host}=(${rule.runAs})	${toCommandsString rule.commands}") rule.users)
                 (map (group: "${toGroupString group}	${rule.host}=(${rule.runAs})	${toCommandsString rule.commands}") rule.groups)
-              ] else []
+              ]
             ) cfg.extraRules
           )
         )}
diff --git a/nixpkgs/nixos/modules/services/audio/jmusicbot.nix b/nixpkgs/nixos/modules/services/audio/jmusicbot.nix
index c6392c679c04..348c7b25682e 100644
--- a/nixpkgs/nixos/modules/services/audio/jmusicbot.nix
+++ b/nixpkgs/nixos/modules/services/audio/jmusicbot.nix
@@ -44,5 +44,5 @@ in
     };
   };
 
-  meta.maintainers = with maintainers; [ SuperSandro2000 ];
+  meta.maintainers = with maintainers; [ ];
 }
diff --git a/nixpkgs/nixos/modules/services/audio/roon-bridge.nix b/nixpkgs/nixos/modules/services/audio/roon-bridge.nix
index 70392b647cc6..027b0332fd1e 100644
--- a/nixpkgs/nixos/modules/services/audio/roon-bridge.nix
+++ b/nixpkgs/nixos/modules/services/audio/roon-bridge.nix
@@ -70,12 +70,11 @@ in {
 
     users.groups.${cfg.group} = {};
     users.users.${cfg.user} =
-      if cfg.user == "roon-bridge" then {
+      optionalAttrs (cfg.user == "roon-bridge") {
         isSystemUser = true;
         description = "Roon Bridge user";
         group = cfg.group;
         extraGroups = [ "audio" ];
-      }
-      else {};
+      };
   };
 }
diff --git a/nixpkgs/nixos/modules/services/audio/roon-server.nix b/nixpkgs/nixos/modules/services/audio/roon-server.nix
index fbe74f63b9da..8691c08b0d36 100644
--- a/nixpkgs/nixos/modules/services/audio/roon-server.nix
+++ b/nixpkgs/nixos/modules/services/audio/roon-server.nix
@@ -76,12 +76,11 @@ in {
 
     users.groups.${cfg.group} = {};
     users.users.${cfg.user} =
-      if cfg.user == "roon-server" then {
+      optionalAttrs (cfg.user == "roon-server") {
         isSystemUser = true;
         description = "Roon Server user";
         group = cfg.group;
         extraGroups = [ "audio" ];
-      }
-      else {};
+      };
   };
 }
diff --git a/nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix b/nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix
new file mode 100644
index 000000000000..1fb67ecfe506
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix
@@ -0,0 +1,186 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+
+let
+  cfg = config.services.wyoming.faster-whisper;
+
+  inherit (lib)
+    escapeShellArgs
+    mkOption
+    mdDoc
+    mkEnableOption
+    mkPackageOptionMD
+    types
+    ;
+
+  inherit (builtins)
+    toString
+    ;
+
+in
+
+{
+  options.services.wyoming.faster-whisper = with types; {
+    package = mkPackageOptionMD pkgs "wyoming-faster-whisper" { };
+
+    servers = mkOption {
+      default = {};
+      description = mdDoc ''
+        Attribute set of faster-whisper instances to spawn.
+      '';
+      type = types.attrsOf (types.submodule (
+        { ... }: {
+          options = {
+            enable = mkEnableOption (mdDoc "Wyoming faster-whisper server");
+
+            model = mkOption {
+              type = enum [
+                "tiny"
+                "tiny-int8"
+                "base"
+                "base-int8"
+                "small"
+                "small-int8"
+                "medium"
+                "medium-int8"
+              ];
+              default = "tiny-int8";
+              example = "medium-int8";
+              description = mdDoc ''
+                Name of the voice model to use.
+              '';
+            };
+
+            uri = mkOption {
+              type = strMatching "^(tcp|unix)://.*$";
+              example = "tcp://0.0.0.0:10300";
+              description = mdDoc ''
+                URI to bind the wyoming server to.
+              '';
+            };
+
+            device = mkOption {
+              # https://opennmt.net/CTranslate2/python/ctranslate2.models.Whisper.html#
+              type = types.enum [
+                "cpu"
+                "cuda"
+                "auto"
+              ];
+              default = "cpu";
+              description = mdDoc ''
+                Determines the platform faster-whisper is run on. CPU works everywhere, CUDA requires a compatible NVIDIA GPU.
+              '';
+            };
+
+            language = mkOption {
+              type = enum [
+                # https://github.com/home-assistant/addons/blob/master/whisper/config.yaml#L20
+                "auto" "af" "am" "ar" "as" "az" "ba" "be" "bg" "bn" "bo" "br" "bs" "ca" "cs" "cy" "da" "de" "el" "en" "es" "et" "eu" "fa" "fi" "fo" "fr" "gl" "gu" "ha" "haw" "he" "hi" "hr" "ht" "hu" "hy" "id" "is" "it" "ja" "jw" "ka" "kk" "km" "kn" "ko" "la" "lb" "ln" "lo" "lt" "lv" "mg" "mi" "mk" "ml" "mn" "mr" "ms" "mt" "my" "ne" "nl" "nn" "no" "oc" "pa" "pl" "ps" "pt" "ro" "ru" "sa" "sd" "si" "sk" "sl" "sn" "so" "sq" "sr" "su" "sv" "sw" "ta" "te" "tg" "th" "tk" "tl" "tr" "tt" "uk" "ur" "uz" "vi" "yi" "yo" "zh"
+              ];
+              example = "en";
+              description = mdDoc ''
+                The language used to to parse words and sentences.
+              '';
+            };
+
+            beamSize = mkOption {
+              type = ints.unsigned;
+              default = 1;
+              example = 5;
+              description = mdDoc ''
+                The number of beams to use in beam search.
+              '';
+              apply = toString;
+            };
+
+            extraArgs = mkOption {
+              type = listOf str;
+              default = [ ];
+              description = mdDoc ''
+                Extra arguments to pass to the server commandline.
+              '';
+              apply = escapeShellArgs;
+            };
+          };
+        }
+      ));
+    };
+  };
+
+  config = let
+    inherit (lib)
+      mapAttrs'
+      mkIf
+      nameValuePair
+    ;
+  in mkIf (cfg.servers != {}) {
+    systemd.services = mapAttrs' (server: options:
+      nameValuePair "wyoming-faster-whisper-${server}" {
+        description = "Wyoming faster-whisper server instance ${server}";
+        after = [
+          "network-online.target"
+        ];
+        wantedBy = [
+          "multi-user.target"
+        ];
+        serviceConfig = {
+          DynamicUser = true;
+          User = "wyoming-faster-whisper";
+          StateDirectory = "wyoming/faster-whisper";
+          # https://github.com/home-assistant/addons/blob/master/whisper/rootfs/etc/s6-overlay/s6-rc.d/whisper/run
+          ExecStart = ''
+            ${cfg.package}/bin/wyoming-faster-whisper \
+              --data-dir $STATE_DIRECTORY \
+              --download-dir $STATE_DIRECTORY \
+              --uri ${options.uri} \
+              --model ${options.model} \
+              --language ${options.language} \
+              --beam-size ${options.beamSize} ${options.extraArgs}
+          '';
+          CapabilityBoundingSet = "";
+          DeviceAllow = if builtins.elem options.device [ "cuda" "auto" ] then [
+            # https://docs.nvidia.com/dgx/pdf/dgx-os-5-user-guide.pdf
+            "/dev/nvidia1"
+            "/dev/nvidia2"
+            "/dev/nvidia3"
+            "/dev/nvidia4"
+            "/dev/nvidia-caps/nvidia-cap1"
+            "/dev/nvidia-caps/nvidia-cap2"
+            "/dev/nvidiactl"
+            "/dev/nvidia-modeset"
+            "/dev/nvidia-uvm"
+            "/dev/nvidia-uvm-tools"
+          ] else "";
+          DevicePolicy = "closed";
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          PrivateDevices = true;
+          PrivateUsers = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectControlGroups = true;
+          ProtectProc = "invisible";
+          ProcSubset = "pid";
+          RestrictAddressFamilies = [
+            "AF_INET"
+            "AF_INET6"
+            "AF_UNIX"
+          ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          SystemCallArchitectures = "native";
+          SystemCallFilter = [
+            "@system-service"
+            "~@privileged"
+          ];
+          UMask = "0077";
+        };
+      }) cfg.servers;
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/audio/wyoming/piper.nix b/nixpkgs/nixos/modules/services/audio/wyoming/piper.nix
new file mode 100644
index 000000000000..ed50bd9f48e9
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/audio/wyoming/piper.nix
@@ -0,0 +1,174 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+
+let
+  cfg = config.services.wyoming.piper;
+
+  inherit (lib)
+    escapeShellArgs
+    mkOption
+    mdDoc
+    mkEnableOption
+    mkPackageOptionMD
+    types
+    ;
+
+  inherit (builtins)
+    toString
+    ;
+
+in
+
+{
+  meta.buildDocsInSandbox = false;
+
+  options.services.wyoming.piper = with types; {
+    package = mkPackageOptionMD pkgs "wyoming-piper" { };
+
+    servers = mkOption {
+      default = {};
+      description = mdDoc ''
+        Attribute set of piper instances to spawn.
+      '';
+      type = types.attrsOf (types.submodule (
+        { ... }: {
+          options = {
+            enable = mkEnableOption (mdDoc "Wyoming Piper server");
+
+            piper = mkPackageOptionMD pkgs "piper-tts" { };
+
+            voice = mkOption {
+              type = str;
+              example = "en-us-ryan-medium";
+              description = mdDoc ''
+                Name of the voice model to use. See the following website for samples:
+                https://rhasspy.github.io/piper-samples/
+              '';
+            };
+
+            uri = mkOption {
+              type = strMatching "^(tcp|unix)://.*$";
+              example = "tcp://0.0.0.0:10200";
+              description = mdDoc ''
+                URI to bind the wyoming server to.
+              '';
+            };
+
+            speaker = mkOption {
+              type = ints.unsigned;
+              default = 0;
+              description = mdDoc ''
+                ID of a specific speaker in a multi-speaker model.
+              '';
+              apply = toString;
+            };
+
+            noiseScale = mkOption {
+              type = float;
+              default = 0.667;
+              description = mdDoc ''
+                Generator noise value.
+              '';
+              apply = toString;
+            };
+
+            noiseWidth = mkOption {
+              type = float;
+              default = 0.333;
+              description = mdDoc ''
+                Phoneme width noise value.
+              '';
+              apply = toString;
+            };
+
+            lengthScale = mkOption {
+              type = float;
+              default = 1.0;
+              description = mdDoc ''
+                Phoneme length value.
+              '';
+              apply = toString;
+            };
+
+            extraArgs = mkOption {
+              type = listOf str;
+              default = [ ];
+              description = mdDoc ''
+                Extra arguments to pass to the server commandline.
+              '';
+              apply = escapeShellArgs;
+            };
+          };
+        }
+      ));
+    };
+  };
+
+  config = let
+    inherit (lib)
+      mapAttrs'
+      mkIf
+      nameValuePair
+    ;
+  in mkIf (cfg.servers != {}) {
+    systemd.services = mapAttrs' (server: options:
+      nameValuePair "wyoming-piper-${server}" {
+        description = "Wyoming Piper server instance ${server}";
+        after = [
+          "network-online.target"
+        ];
+        wantedBy = [
+          "multi-user.target"
+        ];
+        serviceConfig = {
+          DynamicUser = true;
+          User = "wyoming-piper";
+          StateDirectory = "wyoming/piper";
+          # https://github.com/home-assistant/addons/blob/master/piper/rootfs/etc/s6-overlay/s6-rc.d/piper/run
+          ExecStart = ''
+            ${cfg.package}/bin/wyoming-piper \
+              --data-dir $STATE_DIRECTORY \
+              --download-dir $STATE_DIRECTORY \
+              --uri ${options.uri} \
+              --piper ${options.piper}/bin/piper \
+              --voice ${options.voice} \
+              --speaker ${options.speaker} \
+              --length-scale ${options.lengthScale} \
+              --noise-scale ${options.noiseScale} \
+              --noise-w ${options.noiseWidth} ${options.extraArgs}
+          '';
+          CapabilityBoundingSet = "";
+          DeviceAllow = "";
+          DevicePolicy = "closed";
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          PrivateDevices = true;
+          PrivateUsers = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectControlGroups = true;
+          ProtectProc = "invisible";
+          ProcSubset = "pid";
+          RestrictAddressFamilies = [
+            "AF_INET"
+            "AF_INET6"
+            "AF_UNIX"
+          ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          SystemCallArchitectures = "native";
+          SystemCallFilter = [
+            "@system-service"
+            "~@privileged"
+          ];
+          UMask = "0077";
+        };
+      }) cfg.servers;
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/backup/borgbackup.nix b/nixpkgs/nixos/modules/services/backup/borgbackup.nix
index 0da70112d48d..3b44f097ab79 100644
--- a/nixpkgs/nixos/modules/services/backup/borgbackup.nix
+++ b/nixpkgs/nixos/modules/services/backup/borgbackup.nix
@@ -33,7 +33,7 @@ let
     }
     trap on_exit EXIT
 
-    archiveName="${if cfg.archiveBaseName == null then "" else cfg.archiveBaseName + "-"}$(date ${cfg.dateFormat})"
+    archiveName="${optionalString (cfg.archiveBaseName != null) (cfg.archiveBaseName + "-")}$(date ${cfg.dateFormat})"
     archiveSuffix="${optionalString cfg.appendFailedSuffix ".failed"}"
     ${cfg.preHook}
   '' + optionalString cfg.doInit ''
diff --git a/nixpkgs/nixos/modules/services/backup/restic.nix b/nixpkgs/nixos/modules/services/backup/restic.nix
index 3a951f7cbc83..1620770e5b56 100644
--- a/nixpkgs/nixos/modules/services/backup/restic.nix
+++ b/nixpkgs/nixos/modules/services/backup/restic.nix
@@ -298,7 +298,7 @@ in
           let
             extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions;
             resticCmd = "${backup.package}/bin/restic${extraOptions}";
-            excludeFlags = if (backup.exclude != []) then ["--exclude-file=${pkgs.writeText "exclude-patterns" (concatStringsSep "\n" backup.exclude)}"] else [];
+            excludeFlags = optional (backup.exclude != []) "--exclude-file=${pkgs.writeText "exclude-patterns" (concatStringsSep "\n" backup.exclude)}";
             filesFromTmpFile = "/run/restic-backups-${name}/includes";
             backupPaths =
               if (backup.dynamicFilesFrom == null)
diff --git a/nixpkgs/nixos/modules/services/backup/tarsnap.nix b/nixpkgs/nixos/modules/services/backup/tarsnap.nix
index b34aa3ff50dd..9e1db23ca22a 100644
--- a/nixpkgs/nixos/modules/services/backup/tarsnap.nix
+++ b/nixpkgs/nixos/modules/services/backup/tarsnap.nix
@@ -32,6 +32,8 @@ in
     services.tarsnap = {
       enable = mkEnableOption (lib.mdDoc "periodic tarsnap backups");
 
+      package = mkPackageOption pkgs "tarsnap" { };
+
       keyfile = mkOption {
         type = types.str;
         default = "/root/tarsnap.key";
@@ -307,7 +309,7 @@ in
         requires    = [ "network-online.target" ];
         after       = [ "network-online.target" ];
 
-        path = with pkgs; [ iputils tarsnap util-linux ];
+        path = with pkgs; [ iputils gcfg.package util-linux ];
 
         # In order for the persistent tarsnap timer to work reliably, we have to
         # make sure that the tarsnap server is reachable after systemd starts up
@@ -318,7 +320,7 @@ in
         '';
 
         script = let
-          tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"'';
+          tarsnap = ''${lib.getExe gcfg.package} --configfile "/etc/tarsnap/${name}.conf"'';
           run = ''${tarsnap} -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \
                         ${optionalString cfg.verbose "-v"} \
                         ${optionalString cfg.explicitSymlinks "-H"} \
@@ -355,10 +357,10 @@ in
         description = "Tarsnap restore '${name}'";
         requires    = [ "network-online.target" ];
 
-        path = with pkgs; [ iputils tarsnap util-linux ];
+        path = with pkgs; [ iputils gcfg.package util-linux ];
 
         script = let
-          tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"'';
+          tarsnap = ''${lib.getExe gcfg.package} --configfile "/etc/tarsnap/${name}.conf"'';
           lastArchive = "$(${tarsnap} --list-archives | sort | tail -1)";
           run = ''${tarsnap} -x -f "${lastArchive}" ${optionalString cfg.verbose "-v"}'';
           cachedir = escapeShellArg cfg.cachedir;
@@ -402,6 +404,6 @@ in
         { text = configFile name cfg;
         }) gcfg.archives;
 
-    environment.systemPackages = [ pkgs.tarsnap ];
+    environment.systemPackages = [ gcfg.package ];
   };
 }
diff --git a/nixpkgs/nixos/modules/services/cluster/patroni/default.nix b/nixpkgs/nixos/modules/services/cluster/patroni/default.nix
index 83b372f59497..9bf3a285836c 100644
--- a/nixpkgs/nixos/modules/services/cluster/patroni/default.nix
+++ b/nixpkgs/nixos/modules/services/cluster/patroni/default.nix
@@ -6,9 +6,6 @@ let
   defaultGroup = "patroni";
   format = pkgs.formats.yaml { };
 
-  #boto doesn't support python 3.10 yet
-  patroni = pkgs.patroni.override { pythonPackages = pkgs.python39Packages; };
-
   configFileName = "patroni-${cfg.scope}-${cfg.name}.yaml";
   configFile = format.generate configFileName cfg.settings;
 in
@@ -224,7 +221,7 @@ in
 
         script = ''
           ${concatStringsSep "\n" (attrValues (mapAttrs (name: path: ''export ${name}="$(< ${escapeShellArg path})"'') cfg.environmentFiles))}
-          exec ${patroni}/bin/patroni ${configFile}
+          exec ${pkgs.patroni}/bin/patroni ${configFile}
         '';
 
         serviceConfig = mkMerge [
@@ -252,7 +249,7 @@ in
     '';
 
     environment.systemPackages = [
-      patroni
+      pkgs.patroni
       cfg.postgresqlPackage
       (mkIf cfg.raft pkgs.python310Packages.pysyncobj)
     ];
diff --git a/nixpkgs/nixos/modules/services/computing/boinc/client.nix b/nixpkgs/nixos/modules/services/computing/boinc/client.nix
index 1879fef9666f..51475171bf3f 100644
--- a/nixpkgs/nixos/modules/services/computing/boinc/client.nix
+++ b/nixpkgs/nixos/modules/services/computing/boinc/client.nix
@@ -31,6 +31,7 @@ in
         type = types.package;
         default = pkgs.boinc;
         defaultText = literalExpression "pkgs.boinc";
+        example = literalExpression "pkgs.boinc-headless";
         description = lib.mdDoc ''
           Which BOINC package to use.
         '';
diff --git a/nixpkgs/nixos/modules/services/continuous-integration/buildkite-agents.nix b/nixpkgs/nixos/modules/services/continuous-integration/buildkite-agents.nix
index 7c8f77580ff6..a35ca4168074 100644
--- a/nixpkgs/nixos/modules/services/continuous-integration/buildkite-agents.nix
+++ b/nixpkgs/nixos/modules/services/continuous-integration/buildkite-agents.nix
@@ -1,64 +1,49 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.services.buildkite-agents;
 
-  mkHookOption = { name, description, example ? null }: {
-    inherit name;
-    value = mkOption {
-      default = null;
-      description = lib.mdDoc description;
-      type = types.nullOr types.lines;
-    } // (if example == null then {} else { inherit example; });
-  };
-  mkHookOptions = hooks: listToAttrs (map mkHookOption hooks);
-
-  hooksDir = cfg: let
-    mkHookEntry = name: value: ''
-      cat > $out/${name} <<'EOF'
-      #! ${pkgs.runtimeShell}
-      set -e
-      ${value}
-      EOF
-      chmod 755 $out/${name}
+  hooksDir = hooks:
+    let
+      mkHookEntry = name: text: ''
+        ln --symbolic ${pkgs.writeShellApplication { inherit name text; }}/bin/${name} $out/${name}
+      '';
+    in
+    pkgs.runCommandLocal "buildkite-agent-hooks" { } ''
+      mkdir $out
+      ${lib.concatStringsSep "\n" (lib.mapAttrsToList mkHookEntry hooks)}
     '';
-  in pkgs.runCommand "buildkite-agent-hooks" { preferLocalBuild = true; } ''
-    mkdir $out
-    ${concatStringsSep "\n" (mapAttrsToList mkHookEntry (filterAttrs (n: v: v != null) cfg.hooks))}
-  '';
 
   buildkiteOptions = { name ? "", config, ... }: {
     options = {
-      enable = mkOption {
+      enable = lib.mkOption {
         default = true;
-        type = types.bool;
+        type = lib.types.bool;
         description = lib.mdDoc "Whether to enable this buildkite agent";
       };
 
-      package = mkOption {
+      package = lib.mkOption {
         default = pkgs.buildkite-agent;
-        defaultText = literalExpression "pkgs.buildkite-agent";
+        defaultText = lib.literalExpression "pkgs.buildkite-agent";
         description = lib.mdDoc "Which buildkite-agent derivation to use";
-        type = types.package;
+        type = lib.types.package;
       };
 
-      dataDir = mkOption {
+      dataDir = lib.mkOption {
         default = "/var/lib/buildkite-agent-${name}";
         description = lib.mdDoc "The workdir for the agent";
-        type = types.str;
+        type = lib.types.str;
       };
 
-      runtimePackages = mkOption {
+      runtimePackages = lib.mkOption {
         default = [ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ];
-        defaultText = literalExpression "[ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]";
+        defaultText = lib.literalExpression "[ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]";
         description = lib.mdDoc "Add programs to the buildkite-agent environment";
-        type = types.listOf types.package;
+        type = lib.types.listOf lib.types.package;
       };
 
-      tokenPath = mkOption {
-        type = types.path;
+      tokenPath = lib.mkOption {
+        type = lib.types.path;
         description = lib.mdDoc ''
           The token from your Buildkite "Agents" page.
 
@@ -67,25 +52,25 @@ let
         '';
       };
 
-      name = mkOption {
-        type = types.str;
+      name = lib.mkOption {
+        type = lib.types.str;
         default = "%hostname-${name}-%n";
         description = lib.mdDoc ''
           The name of the agent as seen in the buildkite dashboard.
         '';
       };
 
-      tags = mkOption {
-        type = types.attrsOf (types.either types.str (types.listOf types.str));
-        default = {};
-        example = { queue = "default"; docker = "true"; ruby2 ="true"; };
+      tags = lib.mkOption {
+        type = lib.types.attrsOf (lib.types.either lib.types.str (lib.types.listOf lib.types.str));
+        default = { };
+        example = { queue = "default"; docker = "true"; ruby2 = "true"; };
         description = lib.mdDoc ''
           Tags for the agent.
         '';
       };
 
-      extraConfig = mkOption {
-        type = types.lines;
+      extraConfig = lib.mkOption {
+        type = lib.types.lines;
         default = "";
         example = "debug=true";
         description = lib.mdDoc ''
@@ -93,8 +78,8 @@ let
         '';
       };
 
-      privateSshKeyPath = mkOption {
-        type = types.nullOr types.path;
+      privateSshKeyPath = lib.mkOption {
+        type = lib.types.nullOr lib.types.path;
         default = null;
         ## maximum care is taken so that secrets (ssh keys and the CI token)
         ## don't end up in the Nix store.
@@ -108,67 +93,25 @@ let
         '';
       };
 
-      hooks = mkHookOptions [
-        { name = "checkout";
-          description = ''
-            The `checkout` hook script will replace the default checkout routine of the
-            bootstrap.sh script. You can use this hook to do your own SCM checkout
-            behaviour
-          ''; }
-        { name = "command";
-          description = ''
-            The `command` hook script will replace the default implementation of running
-            the build command.
-          ''; }
-        { name = "environment";
-          description = ''
-            The `environment` hook will run before all other commands, and can be used
-            to set up secrets, data, etc. Anything exported in hooks will be available
-            to the build script.
-
-            Note: the contents of this file will be copied to the world-readable
-            Nix store.
-          '';
-          example = ''
-            export SECRET_VAR=`head -1 /run/keys/secret`
-          ''; }
-        { name = "post-artifact";
-          description = ''
-            The `post-artifact` hook will run just after artifacts are uploaded
-          ''; }
-        { name = "post-checkout";
-          description = ''
-            The `post-checkout` hook will run after the bootstrap script has checked out
-            your projects source code.
-          ''; }
-        { name = "post-command";
-          description = ''
-            The `post-command` hook will run after the bootstrap script has run your
-            build commands
-          ''; }
-        { name = "pre-artifact";
-          description = ''
-            The `pre-artifact` hook will run just before artifacts are uploaded
-          ''; }
-        { name = "pre-checkout";
-          description = ''
-            The `pre-checkout` hook will run just before your projects source code is
-            checked out from your SCM provider
-          ''; }
-        { name = "pre-command";
-          description = ''
-            The `pre-command` hook will run just before your build command runs
-          ''; }
-        { name = "pre-exit";
-          description = ''
-            The `pre-exit` hook will run just before your build job finishes
-          ''; }
-      ];
+      hooks = lib.mkOption {
+        type = lib.types.attrsOf lib.types.lines;
+        default = { };
+        example = lib.literalExpression ''
+          {
+            environment = '''
+              export SECRET_VAR=`head -1 /run/keys/secret`
+            ''';
+          }'';
+        description = lib.mdDoc ''
+          "Agent" hooks to install.
+          See <https://buildkite.com/docs/agent/v3/hooks> for possible options.
+        '';
+      };
 
-      hooksPath = mkOption {
-        type = types.path;
-        default = hooksDir config;
-        defaultText = literalMD "generated from {option}`services.buildkite-agents.<name>.hooks`";
+      hooksPath = lib.mkOption {
+        type = lib.types.path;
+        default = hooksDir config.hooks;
+        defaultText = lib.literalMD "generated from {option}`services.buildkite-agents.<name>.hooks`";
         description = lib.mdDoc ''
           Path to the directory storing the hooks.
           Consider using {option}`services.buildkite-agents.<name>.hooks.<name>`
@@ -176,10 +119,10 @@ let
         '';
       };
 
-      shell = mkOption {
-        type = types.str;
+      shell = lib.mkOption {
+        type = lib.types.str;
         default = "${pkgs.bash}/bin/bash -e -c";
-        defaultText = literalExpression ''"''${pkgs.bash}/bin/bash -e -c"'';
+        defaultText = lib.literalExpression ''"''${pkgs.bash}/bin/bash -e -c"'';
         description = lib.mdDoc ''
           Command that buildkite-agent 3 will execute when it spawns a shell.
         '';
@@ -190,9 +133,9 @@ let
   mapAgents = function: lib.mkMerge (lib.mapAttrsToList function enabledAgents);
 in
 {
-  options.services.buildkite-agents = mkOption {
-    type = types.attrsOf (types.submodule buildkiteOptions);
-    default = {};
+  options.services.buildkite-agents = lib.mkOption {
+    type = lib.types.attrsOf (lib.types.submodule buildkiteOptions);
+    default = { };
     description = lib.mdDoc ''
       Attribute set of buildkite agents.
       The attribute key is combined with the hostname and a unique integer to
@@ -213,23 +156,24 @@ in
     };
   });
   config.users.groups = mapAgents (name: cfg: {
-    "buildkite-agent-${name}" = {};
+    "buildkite-agent-${name}" = { };
   });
 
   config.systemd.services = mapAgents (name: cfg: {
-    "buildkite-agent-${name}" =
-      { description = "Buildkite Agent";
-        wantedBy = [ "multi-user.target" ];
-        after = [ "network.target" ];
-        path = cfg.runtimePackages ++ [ cfg.package pkgs.coreutils ];
-        environment = config.networking.proxy.envVars // {
-          HOME = cfg.dataDir;
-          NIX_REMOTE = "daemon";
-        };
+    "buildkite-agent-${name}" = {
+      description = "Buildkite Agent";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      path = cfg.runtimePackages ++ [ cfg.package pkgs.coreutils ];
+      environment = config.networking.proxy.envVars // {
+        HOME = cfg.dataDir;
+        NIX_REMOTE = "daemon";
+      };
 
-        ## NB: maximum care is taken so that secrets (ssh keys and the CI token)
-        ##     don't end up in the Nix store.
-        preStart = let
+      ## NB: maximum care is taken so that secrets (ssh keys and the CI token)
+      ##     don't end up in the Nix store.
+      preStart =
+        let
           sshDir = "${cfg.dataDir}/.ssh";
           tagStr = name: value:
             if lib.isList value
@@ -237,44 +181,39 @@ in
             else "${name}=${value}";
           tagsStr = lib.concatStringsSep "," (lib.mapAttrsToList tagStr cfg.tags);
         in
-          optionalString (cfg.privateSshKeyPath != null) ''
-            mkdir -m 0700 -p "${sshDir}"
-            install -m600 "${toString cfg.privateSshKeyPath}" "${sshDir}/id_rsa"
-          '' + ''
-            cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF
-            token="$(cat ${toString cfg.tokenPath})"
-            name="${cfg.name}"
-            shell="${cfg.shell}"
-            tags="${tagsStr}"
-            build-path="${cfg.dataDir}/builds"
-            hooks-path="${cfg.hooksPath}"
-            ${cfg.extraConfig}
-            EOF
-          '';
+        lib.optionalString (cfg.privateSshKeyPath != null) ''
+          mkdir -m 0700 -p "${sshDir}"
+          install -m600 "${toString cfg.privateSshKeyPath}" "${sshDir}/id_rsa"
+        '' + ''
+          cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF
+          token="$(cat ${toString cfg.tokenPath})"
+          name="${cfg.name}"
+          shell="${cfg.shell}"
+          tags="${tagsStr}"
+          build-path="${cfg.dataDir}/builds"
+          hooks-path="${cfg.hooksPath}"
+          ${cfg.extraConfig}
+          EOF
+        '';
 
-        serviceConfig =
-          { ExecStart = "${cfg.package}/bin/buildkite-agent start --config ${cfg.dataDir}/buildkite-agent.cfg";
-            User = "buildkite-agent-${name}";
-            RestartSec = 5;
-            Restart = "on-failure";
-            TimeoutSec = 10;
-            # set a long timeout to give buildkite-agent a chance to finish current builds
-            TimeoutStopSec = "2 min";
-            KillMode = "mixed";
-          };
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/buildkite-agent start --config ${cfg.dataDir}/buildkite-agent.cfg";
+        User = "buildkite-agent-${name}";
+        RestartSec = 5;
+        Restart = "on-failure";
+        TimeoutSec = 10;
+        # set a long timeout to give buildkite-agent a chance to finish current builds
+        TimeoutStopSec = "2 min";
+        KillMode = "mixed";
       };
+    };
   });
 
-  config.assertions = mapAgents (name: cfg: [
-      { assertion = cfg.hooksPath == (hooksDir cfg) || all (v: v == null) (attrValues cfg.hooks);
-        message = ''
-          Options `services.buildkite-agents.${name}.hooksPath' and
-          `services.buildkite-agents.${name}.hooks.<name>' are mutually exclusive.
-        '';
-      }
-  ]);
-
-  imports = [
-    (mkRemovedOptionModule [ "services" "buildkite-agent"] "services.buildkite-agent has been upgraded from version 2 to version 3 and moved to an attribute set at services.buildkite-agents. Please consult the 20.03 release notes for more information.")
-  ];
+  config.assertions = mapAgents (name: cfg: [{
+    assertion = cfg.hooksPath != hooksDir cfg.hooks -> cfg.hooks == { };
+    message = ''
+      Options `services.buildkite-agents.${name}.hooksPath' and
+      `services.buildkite-agents.${name}.hooks.<name>' are mutually exclusive.
+    '';
+  }]);
 }
diff --git a/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix b/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix
index 53f39f40daa5..10a2fe8a44dd 100644
--- a/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix
+++ b/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix
@@ -611,4 +611,6 @@ in {
     (mkRenamedOptionModule [ "services" "gitlab-runner" "sessionServer" "advertiseAddress" ] [ "services" "gitlab-runner" "settings" "session_server" "advertise_address" ] )
     (mkRenamedOptionModule [ "services" "gitlab-runner" "sessionServer" "sessionTimeout" ] [ "services" "gitlab-runner" "settings" "session_server" "session_timeout" ] )
   ];
+
+  meta.maintainers = teams.gitlab.members;
 }
diff --git a/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix
index a9a587b41e88..e4d54b0cb0f4 100644
--- a/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix
+++ b/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -210,9 +210,7 @@ in {
 
       preStart =
         let replacePlugins =
-              if cfg.plugins == null
-              then ""
-              else
+              optionalString (cfg.plugins != null) (
                 let pluginCmds = lib.attrsets.mapAttrsToList
                       (n: v: "cp ${v} ${cfg.home}/plugins/${n}.jpi")
                       cfg.plugins;
@@ -220,7 +218,7 @@ in {
                   rm -r ${cfg.home}/plugins || true
                   mkdir -p ${cfg.home}/plugins
                   ${lib.strings.concatStringsSep "\n" pluginCmds}
-                '';
+                '');
         in ''
           rm -rf ${cfg.home}/war
           ${replacePlugins}
diff --git a/nixpkgs/nixos/modules/services/continuous-integration/jenkins/job-builder.nix b/nixpkgs/nixos/modules/services/continuous-integration/jenkins/job-builder.nix
index d6a8c2a3f7cc..a8e3effd1f72 100644
--- a/nixpkgs/nixos/modules/services/continuous-integration/jenkins/job-builder.nix
+++ b/nixpkgs/nixos/modules/services/continuous-integration/jenkins/job-builder.nix
@@ -9,25 +9,20 @@ let
 in {
   options = {
     services.jenkins.jobBuilder = {
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Whether or not to enable the Jenkins Job Builder (JJB) service. It
-          allows defining jobs for Jenkins in a declarative manner.
+      enable = mkEnableOption (mdDoc ''
+        the Jenkins Job Builder (JJB) service. It
+        allows defining jobs for Jenkins in a declarative manner.
 
-          Jobs managed through the Jenkins WebUI (or by other means) are left
-          unchanged.
+        Jobs managed through the Jenkins WebUI (or by other means) are left
+        unchanged.
 
-          Note that it really is declarative configuration; if you remove a
-          previously defined job, the corresponding job directory will be
-          deleted.
+        Note that it really is declarative configuration; if you remove a
+        previously defined job, the corresponding job directory will be
+        deleted.
 
-          Please see the Jenkins Job Builder documentation for more info:
-          [
-          http://docs.openstack.org/infra/jenkins-job-builder/](http://docs.openstack.org/infra/jenkins-job-builder/)
-        '';
-      };
+        Please see the Jenkins Job Builder documentation for more info:
+        <https://jenkins-job-builder.readthedocs.io/>
+      '');
 
       accessUser = mkOption {
         default = "admin";
diff --git a/nixpkgs/nixos/modules/services/databases/pgbouncer.nix b/nixpkgs/nixos/modules/services/databases/pgbouncer.nix
new file mode 100644
index 000000000000..1aec03c114d1
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/databases/pgbouncer.nix
@@ -0,0 +1,632 @@
+{ lib, pkgs, config, ... } :
+
+with lib;
+
+let
+  cfg = config.services.pgbouncer;
+
+  confFile = pkgs.writeTextFile {
+    name = "pgbouncer.ini";
+    text =  ''
+      [databases]
+      ${concatStringsSep "\n"
+      (mapAttrsToList (dbname : settings : "${dbname} = ${settings}") cfg.databases)}
+
+      [users]
+      ${concatStringsSep "\n"
+      (mapAttrsToList (username : settings : "${username} = ${settings}") cfg.users)}
+
+      [peers]
+      ${concatStringsSep "\n"
+      (mapAttrsToList (peerid : settings : "${peerid} = ${settings}") cfg.peers)}
+
+      [pgbouncer]
+      # general
+      ${optionalString (cfg.ignoreStartupParameters != null) "ignore_startup_parameters = ${cfg.ignoreStartupParameters}"}
+      listen_port = ${toString cfg.listenPort}
+      ${optionalString (cfg.listenAddress != null) "listen_addr = ${cfg.listenAddress}"}
+      pool_mode = ${cfg.poolMode}
+      max_client_conn = ${toString cfg.maxClientConn}
+      default_pool_size = ${toString cfg.defaultPoolSize}
+      max_user_connections = ${toString cfg.maxUserConnections}
+      max_db_connections = ${toString cfg.maxDbConnections}
+
+      #auth
+      auth_type = ${cfg.authType}
+      ${optionalString (cfg.authHbaFile != null) "auth_hba_file = ${cfg.authHbaFile}"}
+      ${optionalString (cfg.authFile != null) "auth_file = ${cfg.authFile}"}
+      ${optionalString (cfg.authUser != null) "auth_user = ${cfg.authUser}"}
+      ${optionalString (cfg.authQuery != null) "auth_query = ${cfg.authQuery}"}
+      ${optionalString (cfg.authDbname != null) "auth_dbname = ${cfg.authDbname}"}
+
+      # TLS
+      ${optionalString (cfg.tls.client != null) ''
+      client_tls_sslmode = ${cfg.tls.client.sslmode}
+      client_tls_key_file = ${cfg.tls.client.keyFile}
+      client_tls_cert_file = ${cfg.tls.client.certFile}
+      client_tls_ca_file = ${cfg.tls.client.caFile}
+      ''}
+      ${optionalString (cfg.tls.server != null) ''
+      server_tls_sslmode = ${cfg.tls.server.sslmode}
+      server_tls_key_file = ${cfg.tls.server.keyFile}
+      server_tls_cert_file = ${cfg.tls.server.certFile}
+      server_tls_ca_file = ${cfg.tls.server.caFile}
+      ''}
+
+      # log
+      ${optionalString (cfg.logFile != null) "logfile = ${cfg.homeDir}/${cfg.logFile}"}
+      ${optionalString (cfg.syslog != null) ''
+      syslog = ${if cfg.syslog.enable then "1" else "0"}
+      syslog_ident = ${cfg.syslog.syslogIdent}
+      syslog_facility = ${cfg.syslog.syslogFacility}
+      ''}
+      ${optionalString (cfg.verbose != null) "verbose = ${toString cfg.verbose}"}
+
+      # console access
+      ${optionalString (cfg.adminUsers != null) "admin_users = ${cfg.adminUsers}"}
+      ${optionalString (cfg.statsUsers != null) "stats_users = ${cfg.statsUsers}"}
+
+      # linux
+      pidfile = /run/pgbouncer/pgbouncer.pid
+
+      # extra
+      ${cfg.extraConfig}
+    '';
+  };
+
+in {
+
+  options.services.pgbouncer = {
+
+    # NixOS settings
+
+    enable = mkEnableOption (lib.mdDoc "PostgreSQL connection pooler");
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.pgbouncer;
+      defaultText = literalExpression "pkgs.pgbouncer";
+      description = lib.mdDoc ''
+        The pgbouncer package to use.
+      '';
+    };
+
+    openFirewall = mkOption {
+      type = types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Whether to automatically open the specified TCP port in the firewall.
+      '';
+    };
+
+    # Generic settings
+
+    logFile = mkOption {
+      type = types.nullOr types.str;
+      default = "pgbouncer.log";
+      description = lib.mdDoc ''
+        Specifies the log file.
+        Either this or syslog has to be specified.
+      '';
+    };
+
+    listenAddress = mkOption {
+      type = types.nullOr types.commas;
+      example = "*";
+      default = null;
+      description = lib.mdDoc ''
+        Specifies a list (comma-separated) of addresses where to listen for TCP connections.
+        You may also use * meaning “listen on all addresses”.
+        When not set, only Unix socket connections are accepted.
+
+        Addresses can be specified numerically (IPv4/IPv6) or by name.
+      '';
+    };
+
+    listenPort = mkOption {
+      type = types.port;
+      default = 6432;
+      description = lib.mdDoc ''
+        Which port to listen on. Applies to both TCP and Unix sockets.
+      '';
+    };
+
+    poolMode = mkOption {
+      type = types.enum [ "session" "transaction" "statement" ];
+      default = "session";
+      description = lib.mdDoc ''
+        Specifies when a server connection can be reused by other clients.
+
+        session
+            Server is released back to pool after client disconnects. Default.
+        transaction
+            Server is released back to pool after transaction finishes.
+        statement
+            Server is released back to pool after query finishes.
+            Transactions spanning multiple statements are disallowed in this mode.
+      '';
+    };
+
+    maxClientConn = mkOption {
+      type = types.int;
+      default = 100;
+      description = lib.mdDoc ''
+        Maximum number of client connections allowed.
+
+        When this setting is increased, then the file descriptor limits in the operating system
+        might also have to be increased. Note that the number of file descriptors potentially
+        used is more than maxClientConn. If each user connects under its own user name to the server,
+        the theoretical maximum used is:
+        maxClientConn + (max pool_size * total databases * total users)
+
+        If a database user is specified in the connection string (all users connect under the same user name),
+        the theoretical maximum is:
+        maxClientConn + (max pool_size * total databases)
+
+        The theoretical maximum should never be reached, unless somebody deliberately crafts a special load for it.
+        Still, it means you should set the number of file descriptors to a safely high number.
+      '';
+    };
+
+    defaultPoolSize = mkOption {
+      type = types.int;
+      default = 20;
+      description = lib.mdDoc ''
+        How many server connections to allow per user/database pair.
+        Can be overridden in the per-database configuration.
+      '';
+    };
+
+    maxDbConnections = mkOption {
+      type = types.int;
+      default = 0;
+      description = lib.mdDoc ''
+        Do not allow more than this many server connections per database (regardless of user).
+        This considers the PgBouncer database that the client has connected to,
+        not the PostgreSQL database of the outgoing connection.
+
+        This can also be set per database in the [databases] section.
+
+        Note that when you hit the limit, closing a client connection to one pool will
+        not immediately allow a server connection to be established for another pool,
+        because the server connection for the first pool is still open.
+        Once the server connection closes (due to idle timeout),
+        a new server connection will immediately be opened for the waiting pool.
+
+        0 = unlimited
+      '';
+    };
+
+    maxUserConnections = mkOption {
+      type = types.int;
+      default = 0;
+      description = lib.mdDoc ''
+        Do not allow more than this many server connections per user (regardless of database).
+        This considers the PgBouncer user that is associated with a pool,
+        which is either the user specified for the server connection
+        or in absence of that the user the client has connected as.
+
+        This can also be set per user in the [users] section.
+
+        Note that when you hit the limit, closing a client connection to one pool
+        will not immediately allow a server connection to be established for another pool,
+        because the server connection for the first pool is still open.
+        Once the server connection closes (due to idle timeout), a new server connection
+        will immediately be opened for the waiting pool.
+
+        0 = unlimited
+      '';
+    };
+
+    ignoreStartupParameters = mkOption {
+      type = types.nullOr types.commas;
+      example = "extra_float_digits";
+      default = null;
+      description = lib.mdDoc ''
+        By default, PgBouncer allows only parameters it can keep track of in startup packets:
+        client_encoding, datestyle, timezone and standard_conforming_strings.
+
+        All others parameters will raise an error.
+        To allow others parameters, they can be specified here, so that PgBouncer knows that
+        they are handled by the admin and it can ignore them.
+
+        If you need to specify multiple values, use a comma-separated list.
+
+        IMPORTANT: When using prometheus-pgbouncer-exporter, you need:
+        extra_float_digits
+        <https://github.com/prometheus-community/pgbouncer_exporter#pgbouncer-configuration>
+      '';
+    };
+
+    # Section [databases]
+    databases = mkOption {
+      type = types.attrsOf types.str;
+      default = {};
+      example = {
+        exampledb = "host=/run/postgresql/ port=5432 auth_user=exampleuser dbname=exampledb sslmode=require";
+        bardb = "host=localhost dbname=bazdb";
+        foodb  = "host=host1.example.com port=5432";
+      };
+      description = lib.mdDoc ''
+        Detailed information about PostgreSQL database definitions:
+        <https://www.pgbouncer.org/config.html#section-databases>
+      '';
+    };
+
+    # Section [users]
+    users = mkOption {
+      type = types.attrsOf types.str;
+      default = {};
+      example = {
+        user1 = "pool_mode=session";
+      };
+      description = lib.mdDoc ''
+        Optional.
+
+        Detailed information about PostgreSQL user definitions:
+        <https://www.pgbouncer.org/config.html#section-users>
+      '';
+    };
+
+    # Section [peers]
+    peers = mkOption {
+      type = types.attrsOf types.str;
+      default = {};
+      example = {
+        "1" = "host=host1.example.com";
+        "2" = "host=/tmp/pgbouncer-2 port=5555";
+      };
+      description = lib.mdDoc ''
+        Optional.
+
+        Detailed information about PostgreSQL database definitions:
+        <https://www.pgbouncer.org/config.html#section-peers>
+      '';
+    };
+
+    # Authentication settings
+    authType = mkOption {
+      type = types.enum [ "cert" "md5" "scram-sha-256" "plain" "trust" "any" "hba" "pam" ];
+      default = "md5";
+      description = lib.mdDoc ''
+        How to authenticate users.
+
+        cert
+            Client must connect over TLS connection with a valid client certificate.
+            The user name is then taken from the CommonName field from the certificate.
+        md5
+            Use MD5-based password check. This is the default authentication method.
+            authFile may contain both MD5-encrypted and plain-text passwords.
+            If md5 is configured and a user has a SCRAM secret, then SCRAM authentication is used automatically instead.
+        scram-sha-256
+            Use password check with SCRAM-SHA-256. authFile has to contain SCRAM secrets or plain-text passwords.
+        plain
+            The clear-text password is sent over the wire. Deprecated.
+        trust
+            No authentication is done. The user name must still exist in authFile.
+        any
+            Like the trust method, but the user name given is ignored.
+            Requires that all databases are configured to log in as a specific user.
+            Additionally, the console database allows any user to log in as admin.
+        hba
+            The actual authentication type is loaded from authHbaFile.
+            This allows different authentication methods for different access paths,
+            for example: connections over Unix socket use the peer auth method, connections over TCP must use TLS.
+        pam
+            PAM is used to authenticate users, authFile is ignored.
+            This method is not compatible with databases using the authUser option.
+            The service name reported to PAM is “pgbouncer”. pam is not supported in the HBA configuration file.
+      '';
+    };
+
+    authHbaFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/secrets/pgbouncer_hba";
+      description = lib.mdDoc ''
+        HBA configuration file to use when authType is hba.
+
+        See HBA file format details:
+        <https://www.pgbouncer.org/config.html#hba-file-format>
+      '';
+    };
+
+    authFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/secrets/pgbouncer_authfile";
+      description = lib.mdDoc ''
+        The name of the file to load user names and passwords from.
+
+        See section Authentication file format details:
+        <https://www.pgbouncer.org/config.html#authentication-file-format>
+
+        Most authentication types require that either authFile or authUser be set;
+        otherwise there would be no users defined.
+      '';
+    };
+
+    authUser = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "pgbouncer";
+      description = lib.mdDoc ''
+        If authUser is set, then any user not specified in authFile will be queried
+        through the authQuery query from pg_shadow in the database, using authUser.
+        The password of authUser will be taken from authFile.
+        (If the authUser does not require a password then it does not need to be defined in authFile.)
+
+        Direct access to pg_shadow requires admin rights.
+        It's preferable to use a non-superuser that calls a SECURITY DEFINER function instead.
+      '';
+    };
+
+    authQuery = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "SELECT usename, passwd FROM pg_shadow WHERE usename=$1";
+      description = lib.mdDoc ''
+        Query to load user's password from database.
+
+        Direct access to pg_shadow requires admin rights.
+        It's preferable to use a non-superuser that calls a SECURITY DEFINER function instead.
+
+        Note that the query is run inside the target database.
+        So if a function is used, it needs to be installed into each database.
+      '';
+    };
+
+    authDbname = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "authdb";
+      description = lib.mdDoc ''
+        Database name in the [database] section to be used for authentication purposes.
+        This option can be either global or overriden in the connection string if this parameter is specified.
+      '';
+    };
+
+    # TLS settings
+    tls.client = mkOption {
+      type = types.nullOr (types.submodule {
+        options = {
+          sslmode = mkOption {
+            type = types.enum [ "disable" "allow" "prefer" "require" "verify-ca" "verify-full" ];
+            default = "disable";
+            description = lib.mdDoc ''
+              TLS mode to use for connections from clients.
+              TLS connections are disabled by default.
+
+              When enabled, tls.client.keyFile and tls.client.certFile
+              must be also configured to set up the key and certificate
+              PgBouncer uses to accept client connections.
+
+              disable
+                  Plain TCP. If client requests TLS, it's ignored. Default.
+              allow
+                  If client requests TLS, it is used. If not, plain TCP is used.
+                  If the client presents a client certificate, it is not validated.
+              prefer
+                  Same as allow.
+              require
+                  Client must use TLS. If not, the client connection is rejected.
+                  If the client presents a client certificate, it is not validated.
+              verify-ca
+                  Client must use TLS with valid client certificate.
+              verify-full
+                  Same as verify-ca
+            '';
+          };
+          certFile = mkOption {
+            type = types.path;
+            example = "/secrets/pgbouncer.key";
+            description = lib.mdDoc "Path to certificate for private key. Clients can validate it";
+          };
+          keyFile = mkOption {
+            type = types.path;
+            example = "/secrets/pgbouncer.crt";
+            description = lib.mdDoc "Path to private key for PgBouncer to accept client connections";
+          };
+          caFile = mkOption {
+            type = types.path;
+            example = "/secrets/pgbouncer.crt";
+            description = lib.mdDoc "Path to root certificate file to validate client certificates";
+          };
+        };
+      });
+      default = null;
+      description = lib.mdDoc ''
+        <https://www.pgbouncer.org/config.html#tls-settings>
+      '';
+    };
+
+    tls.server = mkOption {
+      type = types.nullOr (types.submodule {
+        options = {
+          sslmode = mkOption {
+            type = types.enum [ "disable" "allow" "prefer" "require" "verify-ca" "verify-full" ];
+            default = "disable";
+            description = lib.mdDoc ''
+              TLS mode to use for connections to PostgreSQL servers.
+              TLS connections are disabled by default.
+
+              disable
+                  Plain TCP. TLS is not even requested from the server. Default.
+              allow
+                  FIXME: if server rejects plain, try TLS?
+              prefer
+                  TLS connection is always requested first from PostgreSQL.
+                  If refused, the connection will be established over plain TCP.
+                  Server certificate is not validated.
+              require
+                  Connection must go over TLS. If server rejects it, plain TCP is not attempted.
+                  Server certificate is not validated.
+              verify-ca
+                  Connection must go over TLS and server certificate must be valid according to tls.server.caFile.
+                  Server host name is not checked against certificate.
+              verify-full
+                  Connection must go over TLS and server certificate must be valid according to tls.server.caFile.
+                  Server host name must match certificate information.
+            '';
+          };
+          certFile = mkOption {
+            type = types.path;
+            example = "/secrets/pgbouncer_server.key";
+            description = lib.mdDoc "Certificate for private key. PostgreSQL server can validate it.";
+          };
+          keyFile = mkOption {
+            type = types.path;
+            example = "/secrets/pgbouncer_server.crt";
+            description = lib.mdDoc "Private key for PgBouncer to authenticate against PostgreSQL server.";
+          };
+          caFile = mkOption {
+            type = types.path;
+            example = "/secrets/pgbouncer_server.crt";
+            description = lib.mdDoc "Root certificate file to validate PostgreSQL server certificates.";
+          };
+        };
+      });
+      default = null;
+      description = lib.mdDoc ''
+        <https://www.pgbouncer.org/config.html#tls-settings>
+      '';
+    };
+
+    # Log settings
+    syslog = mkOption {
+      type = types.nullOr (types.submodule {
+        options = {
+          enable = mkOption {
+            type = types.bool;
+            default = false;
+            description = lib.mdDoc ''
+              Toggles syslog on/off.
+            '';
+          };
+          syslogIdent = mkOption {
+            type = types.str;
+            default = "pgbouncer";
+            description = lib.mdDoc ''
+              Under what name to send logs to syslog.
+            '';
+          };
+          syslogFacility = mkOption {
+            type = types.enum [ "auth" "authpriv" "daemon" "user" "local0" "local1" "local2" "local3" "local4" "local5" "local6" "local7" ];
+            default = "daemon";
+            description = lib.mdDoc ''
+              Under what facility to send logs to syslog.
+            '';
+          };
+        };
+      });
+      default = null;
+      description = lib.mdDoc ''
+        <https://www.pgbouncer.org/config.html#log-settings>
+      '';
+    };
+
+    verbose = lib.mkOption {
+      type = lib.types.int;
+      default = 0;
+      description = lib.mdDoc ''
+        Increase verbosity. Mirrors the “-v” switch on the command line.
+      '';
+    };
+
+    # Console access control
+    adminUsers = mkOption {
+      type = types.nullOr types.commas;
+      default = null;
+      description = lib.mdDoc ''
+        Comma-separated list of database users that are allowed to connect and run all commands on the console.
+        Ignored when authType is any, in which case any user name is allowed in as admin.
+      '';
+    };
+
+    statsUsers = mkOption {
+      type = types.nullOr types.commas;
+      default = null;
+      description = lib.mdDoc ''
+        Comma-separated list of database users that are allowed to connect and run read-only queries on the console.
+        That means all SHOW commands except SHOW FDS.
+      '';
+    };
+
+    # Linux settings
+    openFilesLimit = lib.mkOption {
+      type = lib.types.int;
+      default = 65536;
+      description = lib.mdDoc ''
+        Maximum number of open files.
+      '';
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = "pgbouncer";
+      description = lib.mdDoc ''
+        The user pgbouncer is run as.
+      '';
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = "pgbouncer";
+      description = lib.mdDoc ''
+        The group pgbouncer is run as.
+      '';
+    };
+
+    homeDir = mkOption {
+      type = types.path;
+      default = "/var/lib/pgbouncer";
+      description = lib.mdDoc ''
+        Specifies the home directory.
+      '';
+    };
+
+    # Extra settings
+    extraConfig = mkOption {
+      type = types.lines;
+      description = lib.mdDoc ''
+        Any additional text to be appended to config.ini
+         <https://www.pgbouncer.org/config.html>.
+      '';
+      default = "";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.groups.${cfg.group} = { };
+    users.users.${cfg.user} = {
+      description = "PgBouncer service user";
+      group = cfg.group;
+      home = cfg.homeDir;
+      createHome = true;
+      isSystemUser = true;
+    };
+
+    systemd.services.pgbouncer = {
+      description = "PgBouncer - PostgreSQL connection pooler";
+      wants    = [ "postgresql.service" ];
+      after    = [ "postgresql.service" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "forking";
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStart = "${pkgs.pgbouncer}/bin/pgbouncer -d ${confFile}";
+        ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
+        RuntimeDirectory = "pgbouncer";
+        PIDFile = "/run/pgbouncer/pgbouncer.pid";
+        LimitNOFILE = cfg.openFilesLimit;
+      };
+    };
+
+    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
+
+  };
+
+    meta.maintainers = [ maintainers._1000101 ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/databases/postgresql.nix b/nixpkgs/nixos/modules/services/databases/postgresql.nix
index a7016bbee3a8..0acaf0fd00a6 100644
--- a/nixpkgs/nixos/modules/services/databases/postgresql.nix
+++ b/nixpkgs/nixos/modules/services/databases/postgresql.nix
@@ -404,8 +404,8 @@ in
           {
             log_connections = true;
             log_statement = "all";
-            logging_collector = true
-            log_disconnections = true
+            logging_collector = true;
+            log_disconnections = true;
             log_destination = lib.mkForce "syslog";
           }
         '';
diff --git a/nixpkgs/nixos/modules/services/editors/emacs.nix b/nixpkgs/nixos/modules/services/editors/emacs.nix
index 2be46e47d64c..fe3a10159794 100644
--- a/nixpkgs/nixos/modules/services/editors/emacs.nix
+++ b/nixpkgs/nixos/modules/services/editors/emacs.nix
@@ -96,7 +96,7 @@ in
 
     environment.systemPackages = [ cfg.package editorScript desktopApplicationFile ];
 
-    environment.variables.EDITOR = mkIf cfg.defaultEditor (mkOverride 900 "${editorScript}/bin/emacseditor");
+    environment.variables.EDITOR = mkIf cfg.defaultEditor (mkOverride 900 "emacseditor");
   };
 
   meta.doc = ./emacs.md;
diff --git a/nixpkgs/nixos/modules/services/games/factorio.nix b/nixpkgs/nixos/modules/services/games/factorio.nix
index 9b15cac149d5..b349ffa2375f 100644
--- a/nixpkgs/nixos/modules/services/games/factorio.nix
+++ b/nixpkgs/nixos/modules/services/games/factorio.nix
@@ -294,6 +294,6 @@ in
       };
     };
 
-    networking.firewall.allowedUDPPorts = if cfg.openFirewall then [ cfg.port ] else [];
+    networking.firewall.allowedUDPPorts = optional cfg.openFirewall cfg.port;
   };
 }
diff --git a/nixpkgs/nixos/modules/services/games/freeciv.nix b/nixpkgs/nixos/modules/services/games/freeciv.nix
index f33ea5c08a27..bba27ae4cb5f 100644
--- a/nixpkgs/nixos/modules/services/games/freeciv.nix
+++ b/nixpkgs/nixos/modules/services/games/freeciv.nix
@@ -16,7 +16,7 @@ let
     generate = name: value:
       let mkParam = k: v:
             if v == null then []
-            else if isBool v then if v then [("--"+k)] else []
+            else if isBool v then optional v ("--"+k)
             else [("--"+k) v];
           mkParams = k: v: map (mkParam k) (if isList v then v else [v]);
       in escapeShellArgs (concatLists (concatLists (mapAttrsToList mkParams value)));
diff --git a/nixpkgs/nixos/modules/services/games/mchprs.nix b/nixpkgs/nixos/modules/services/games/mchprs.nix
new file mode 100644
index 000000000000..a65001b0b3e2
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/games/mchprs.nix
@@ -0,0 +1,341 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.mchprs;
+  settingsFormat = pkgs.formats.toml { };
+
+  whitelistFile = pkgs.writeText "whitelist.json"
+    (builtins.toJSON
+      (mapAttrsToList (n: v: { name = n; uuid = v; }) cfg.whitelist.list));
+
+  configToml =
+    (removeAttrs cfg.settings [ "address" "port" ]) //
+    {
+      bind_address = cfg.settings.address + ":" + toString cfg.settings.port;
+      whitelist = cfg.whitelist.enable;
+    };
+
+  configTomlFile = settingsFormat.generate "Config.toml" configToml;
+in
+{
+  options = {
+    services.mchprs = {
+      enable = mkEnableOption "MCHPRS";
+
+      declarativeSettings = mkOption {
+        type = types.bool;
+        default = false;
+        description = mdDoc ''
+          Whether to use a declarative configuration for MCHPRS.
+        '';
+      };
+
+      declarativeWhitelist = mkOption {
+        type = types.bool;
+        default = false;
+        description = mdDoc ''
+          Whether to use a declarative whitelist.
+          The options {option}`services.mchprs.whitelist.list`
+          will be applied if and only if set to `true`.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/mchprs";
+        description = mdDoc ''
+          Directory to store MCHPRS database and other state/data files.
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = mdDoc ''
+          Whether to open ports in the firewall for the server.
+          Only has effect when
+          {option}`services.mchprs.declarativeSettings` is `true`.
+        '';
+      };
+
+      maxRuntime = mkOption {
+        type = types.str;
+        default = "infinity";
+        example = "7d";
+        description = mdDoc ''
+          Automatically restart the server after
+          {option}`services.mchprs.maxRuntime`.
+          The time span format is described here:
+          https://www.freedesktop.org/software/systemd/man/systemd.time.html#Parsing%20Time%20Spans.
+          If `null`, then the server is not restarted automatically.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.mchprs;
+        defaultText = literalExpression "pkgs.mchprs";
+        description = mdDoc "Version of MCHPRS to run.";
+      };
+
+      settings = mkOption {
+        type = types.submodule {
+          freeformType = settingsFormat.type;
+
+          options = {
+            port = mkOption {
+              type = types.port;
+              default = 25565;
+              description = mdDoc ''
+                Port for the server.
+                Only has effect when
+                {option}`services.mchprs.declarativeSettings` is `true`.
+              '';
+            };
+
+            address = mkOption {
+              type = types.str;
+              default = "0.0.0.0";
+              description = mdDoc ''
+                Address for the server.
+                Please use enclosing square brackets when using ipv6.
+                Only has effect when
+                {option}`services.mchprs.declarativeSettings` is `true`.
+              '';
+            };
+
+            motd = mkOption {
+              type = types.str;
+              default = "Minecraft High Performance Redstone Server";
+              description = mdDoc ''
+                Message of the day.
+                Only has effect when
+                {option}`services.mchprs.declarativeSettings` is `true`.
+              '';
+            };
+
+            chat_format = mkOption {
+              type = types.str;
+              default = "<{username}> {message}";
+              description = mdDoc ''
+                How to format chat message interpolating `username`
+                and `message` with curly braces.
+                Only has effect when
+                {option}`services.mchprs.declarativeSettings` is `true`.
+              '';
+            };
+
+            max_players = mkOption {
+              type = types.ints.positive;
+              default = 99999;
+              description = mdDoc ''
+                Maximum number of simultaneous players.
+                Only has effect when
+                {option}`services.mchprs.declarativeSettings` is `true`.
+              '';
+            };
+
+            view_distance = mkOption {
+              type = types.ints.positive;
+              default = 8;
+              description = mdDoc ''
+                Maximal distance (in chunks) between players and loaded chunks.
+                Only has effect when
+                {option}`services.mchprs.declarativeSettings` is `true`.
+              '';
+            };
+
+            bungeecord = mkOption {
+              type = types.bool;
+              default = false;
+              description = mdDoc ''
+                Enable compatibility with
+                [BungeeCord](https://github.com/SpigotMC/BungeeCord).
+                Only has effect when
+                {option}`services.mchprs.declarativeSettings` is `true`.
+              '';
+            };
+
+            schemati = mkOption {
+              type = types.bool;
+              default = false;
+              description = mdDoc ''
+                Mimic the verification and directory layout used by the
+                Open Redstone Engineers
+                [Schemati plugin](https://github.com/OpenRedstoneEngineers/Schemati).
+                Only has effect when
+                {option}`services.mchprs.declarativeSettings` is `true`.
+              '';
+            };
+
+            block_in_hitbox = mkOption {
+              type = types.bool;
+              default = true;
+              description = mdDoc ''
+                Allow placing blocks inside of players
+                (hitbox logic is simplified).
+                Only has effect when
+                {option}`services.mchprs.declarativeSettings` is `true`.
+              '';
+            };
+
+            auto_redpiler = mkOption {
+              type = types.bool;
+              default = true;
+              description = mdDoc ''
+                Use redpiler automatically.
+                Only has effect when
+                {option}`services.mchprs.declarativeSettings` is `true`.
+              '';
+            };
+          };
+        };
+        default = { };
+
+        description = mdDoc ''
+          Configuration for MCHPRS via `Config.toml`.
+          See https://github.com/MCHPR/MCHPRS/blob/master/README.md for documentation.
+        '';
+      };
+
+      whitelist = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = mdDoc ''
+            Whether or not the whitelist (in `whitelist.json`) shoud be enabled.
+            Only has effect when {option}`services.mchprs.declarativeSettings` is `true`.
+          '';
+        };
+
+        list = mkOption {
+          type =
+            let
+              minecraftUUID = types.strMatching
+                "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" // {
+                description = "Minecraft UUID";
+              };
+            in
+            types.attrsOf minecraftUUID;
+          default = { };
+          example = literalExpression ''
+            {
+              username1 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
+              username2 = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy";
+            };
+          '';
+          description = mdDoc ''
+            Whitelisted players, only has an effect when
+            {option}`services.mchprs.declarativeWhitelist` is
+            `true` and the whitelist is enabled
+            via {option}`services.mchprs.whitelist.enable`.
+            This is a mapping from Minecraft usernames to UUIDs.
+            You can use <https://mcuuid.net/> to get a
+            Minecraft UUID for a username.
+          '';
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users.mchprs = {
+      description = "MCHPRS service user";
+      home = cfg.dataDir;
+      createHome = true;
+      isSystemUser = true;
+      group = "mchprs";
+    };
+    users.groups.mchprs = { };
+
+    systemd.services.mchprs = {
+      description = "MCHPRS Service";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        ExecStart = "${lib.getExe cfg.package}";
+        Restart = "always";
+        RuntimeMaxSec = cfg.maxRuntime;
+        User = "mchprs";
+        WorkingDirectory = cfg.dataDir;
+
+        StandardOutput = "journal";
+        StandardError = "journal";
+
+        # Hardening
+        CapabilityBoundingSet = [ "" ];
+        DeviceAllow = [ "" ];
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        PrivateDevices = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        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;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        UMask = "0077";
+      };
+
+      preStart =
+        (if cfg.declarativeSettings then ''
+          if [ -e .declarativeSettings ]; then
+
+            # Settings were declarative before, no need to back up anything
+            cp -f ${configTomlFile} Config.toml
+
+          else
+
+            # Declarative settings for the first time, backup stateful files
+            cp -b --suffix=.stateful ${configTomlFile} Config.toml
+
+            echo "Autogenerated file that implies that this server configuration is managed declaratively by NixOS" \
+              > .declarativeSettings
+
+          fi
+        '' else ''
+          if [ -e .declarativeSettings ]; then
+            rm .declarativeSettings
+          fi
+        '') + (if cfg.declarativeWhitelist then ''
+          if [ -e .declarativeWhitelist ]; then
+
+            # Whitelist was declarative before, no need to back up anything
+            ln -sf ${whitelistFile} whitelist.json
+
+          else
+
+            # Declarative whitelist for the first time, backup stateful files
+            ln -sb --suffix=.stateful ${whitelistFile} whitelist.json
+
+            echo "Autogenerated file that implies that this server's whitelist is managed declaratively by NixOS" \
+              > .declarativeWhitelist
+
+          fi
+        '' else ''
+          if [ -e .declarativeWhitelist ]; then
+            rm .declarativeWhitelist
+          fi
+        '');
+    };
+
+    networking.firewall = mkIf (cfg.declarativeSettings && cfg.openFirewall) {
+      allowedUDPPorts = [ cfg.settings.port ];
+      allowedTCPPorts = [ cfg.settings.port ];
+    };
+  };
+
+  meta.maintainers = with maintainers; [ gdd ];
+}
diff --git a/nixpkgs/nixos/modules/services/games/minetest-server.nix b/nixpkgs/nixos/modules/services/games/minetest-server.nix
index 578364ec542b..8dc360153497 100644
--- a/nixpkgs/nixos/modules/services/games/minetest-server.nix
+++ b/nixpkgs/nixos/modules/services/games/minetest-server.nix
@@ -3,15 +3,52 @@
 with lib;
 
 let
+  CONTAINS_NEWLINE_RE = ".*\n.*";
+  # The following values are reserved as complete option values:
+  # { - start of a group.
+  # """ - start of a multi-line string.
+  RESERVED_VALUE_RE = "[[:space:]]*(\"\"\"|\\{)[[:space:]]*";
+  NEEDS_MULTILINE_RE = "${CONTAINS_NEWLINE_RE}|${RESERVED_VALUE_RE}";
+
+  # There is no way to encode """ on its own line in a Minetest config.
+  UNESCAPABLE_RE = ".*\n\"\"\"\n.*";
+
+  toConfMultiline = name: value:
+    assert lib.assertMsg
+      ((builtins.match UNESCAPABLE_RE value) == null)
+      ''""" can't be on its own line in a minetest config.'';
+    "${name} = \"\"\"\n${value}\n\"\"\"\n";
+
+  toConf = values:
+    lib.concatStrings
+      (lib.mapAttrsToList
+        (name: value: {
+          bool = "${name} = ${toString value}\n";
+          int = "${name} = ${toString value}\n";
+          null = "";
+          set = "${name} = {\n${toConf value}}\n";
+          string =
+            if (builtins.match NEEDS_MULTILINE_RE value) != null
+            then toConfMultiline name value
+            else "${name} = ${value}\n";
+        }.${builtins.typeOf value})
+        values);
+
   cfg   = config.services.minetest-server;
-  flag  = val: name: optionalString (val != null) "--${name} ${toString val} ";
+  flag  = val: name: lib.optionals (val != null) ["--${name}" "${toString val}"];
+
   flags = [
-    (flag cfg.gameId "gameid")
-    (flag cfg.world "world")
-    (flag cfg.configPath "config")
-    (flag cfg.logPath "logfile")
-    (flag cfg.port "port")
-  ];
+    "--server"
+  ]
+    ++ (
+      if cfg.configPath != null
+      then ["--config" cfg.configPath]
+      else ["--config" (builtins.toFile "minetest.conf" (toConf cfg.config))])
+    ++ (flag cfg.gameId "gameid")
+    ++ (flag cfg.world "world")
+    ++ (flag cfg.logPath "logfile")
+    ++ (flag cfg.port "port")
+    ++ cfg.extraArgs;
 in
 {
   options = {
@@ -55,6 +92,16 @@ in
         '';
       };
 
+      config = mkOption {
+        type = types.attrsOf types.anything;
+        default = {};
+        description = lib.mdDoc ''
+          Settings to add to the minetest config file.
+
+          This option is ignored if `configPath` is set.
+        '';
+      };
+
       logPath = mkOption {
         type        = types.nullOr types.path;
         default     = null;
@@ -75,6 +122,14 @@ in
           If set to null, the default 30000 will be used.
         '';
       };
+
+      extraArgs = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = lib.mdDoc ''
+          Additional command line flags to pass to the minetest executable.
+        '';
+      };
     };
   };
 
@@ -100,7 +155,7 @@ in
       script = ''
         cd /var/lib/minetest
 
-        exec ${pkgs.minetest}/bin/minetest --server ${concatStrings flags}
+        exec ${pkgs.minetest}/bin/minetest ${lib.escapeShellArgs flags}
       '';
     };
   };
diff --git a/nixpkgs/nixos/modules/services/hardware/fwupd.nix b/nixpkgs/nixos/modules/services/hardware/fwupd.nix
index b8c2ac94845b..4e5913fd2751 100644
--- a/nixpkgs/nixos/modules/services/hardware/fwupd.nix
+++ b/nixpkgs/nixos/modules/services/hardware/fwupd.nix
@@ -13,16 +13,13 @@ let
   };
 
   customEtc = {
-    "fwupd/daemon.conf" = {
-      source = format.generate "daemon.conf" {
+    "fwupd/fwupd.conf" = {
+      source = format.generate "fwupd.conf" {
         fwupd = cfg.daemonSettings;
-      };
-    };
-
-    "fwupd/uefi_capsule.conf" = {
-      source = format.generate "uefi_capsule.conf" {
         uefi_capsule = cfg.uefiCapsuleSettings;
       };
+      # fwupd tries to chmod the file if it doesn't have the right permissions
+      mode = "0640";
     };
   };
 
@@ -53,7 +50,7 @@ let
     # to install it because it would create a cyclic dependency between
     # the outputs. We also need to enable the remote,
     # which should not be done by default.
-    if cfg.enableTestRemote then (enableRemote cfg.package.installedTests "fwupd-tests") else {}
+    lib.optionalAttrs cfg.enableTestRemote (enableRemote cfg.package.installedTests "fwupd-tests")
   );
 
 in {
diff --git a/nixpkgs/nixos/modules/services/hardware/joycond.nix b/nixpkgs/nixos/modules/services/hardware/joycond.nix
index 1af18b3b63d3..df3239cb2a7d 100644
--- a/nixpkgs/nixos/modules/services/hardware/joycond.nix
+++ b/nixpkgs/nixos/modules/services/hardware/joycond.nix
@@ -2,7 +2,6 @@
 
 let
   cfg = config.services.joycond;
-  kernelPackages = config.boot.kernelPackages;
 in
 
 with lib;
@@ -24,8 +23,6 @@ with lib;
   config = mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
 
-    boot.extraModulePackages = optional (versionOlder kernelPackages.kernel.version "5.16") kernelPackages.hid-nintendo;
-
     services.udev.packages = [ cfg.package ];
 
     systemd.packages = [ cfg.package ];
diff --git a/nixpkgs/nixos/modules/services/hardware/keyd.nix b/nixpkgs/nixos/modules/services/hardware/keyd.nix
index d17b0e4303ef..969383fd4dc7 100644
--- a/nixpkgs/nixos/modules/services/hardware/keyd.nix
+++ b/nixpkgs/nixos/modules/services/hardware/keyd.nix
@@ -3,12 +3,9 @@ with lib;
 let
   cfg = config.services.keyd;
   settingsFormat = pkgs.formats.ini { };
-in
-{
-  options = {
-    services.keyd = {
-      enable = mkEnableOption (lib.mdDoc "keyd, a key remapping daemon");
 
+  keyboardOptions = { ... }: {
+    options = {
       ids = mkOption {
         type = types.listOf types.string;
         default = [ "*" ];
@@ -35,24 +32,71 @@ in
           };
         };
         description = lib.mdDoc ''
-          Configuration, except `ids` section, that is written to {file}`/etc/keyd/default.conf`.
+          Configuration, except `ids` section, that is written to {file}`/etc/keyd/<keyboard>.conf`.
+          Appropriate names can be used to write non-alpha keys, for example "equal" instead of "=" sign (see <https://github.com/NixOS/nixpkgs/issues/236622>).
           See <https://github.com/rvaiya/keyd> how to configure.
         '';
       };
     };
   };
+in
+{
+  imports = [
+    (mkRemovedOptionModule [ "services" "keyd" "ids" ]
+      ''Use keyboards.<filename>.ids instead. If you don't need a multi-file configuration, just add keyboards.default before the ids. See https://github.com/NixOS/nixpkgs/pull/243271.'')
+    (mkRemovedOptionModule [ "services" "keyd" "settings" ]
+      ''Use keyboards.<filename>.settings instead. If you don't need a multi-file configuration, just add keyboards.default before the settings. See https://github.com/NixOS/nixpkgs/pull/243271.'')
+  ];
+
+  options.services.keyd = {
+    enable = mkEnableOption (lib.mdDoc "keyd, a key remapping daemon");
+
+    keyboards = mkOption {
+      type = types.attrsOf (types.submodule keyboardOptions);
+      default = { };
+      example = literalExpression ''
+        {
+          default = {
+            ids = [ "*" ];
+            settings = {
+              main = {
+                capslock = "overload(control, esc)";
+              };
+            };
+          };
+          externalKeyboard = {
+            ids = [ "1ea7:0907" ];
+            settings = {
+              main = {
+                esc = capslock;
+              };
+            };
+          };
+        }
+      '';
+      description = mdDoc ''
+        Configuration for one or more device IDs. Corresponding files in the /etc/keyd/ directory are created according to the name of the keys (like `default` or `externalKeyboard`).
+      '';
+    };
+  };
 
   config = mkIf cfg.enable {
-    environment.etc."keyd/default.conf".source = pkgs.runCommand "default.conf"
-      {
-        ids = ''
-          [ids]
-          ${concatStringsSep "\n" cfg.ids}
-        '';
-        passAsFile = [ "ids" ];
-      } ''
-      cat $idsPath <(echo) ${settingsFormat.generate "keyd-main.conf" cfg.settings} >$out
-    '';
+    # Creates separate files in the `/etc/keyd/` directory for each key in the dictionary
+    environment.etc = mapAttrs'
+      (name: options:
+        nameValuePair "keyd/${name}.conf" {
+          source = pkgs.runCommand "${name}.conf"
+            {
+              ids = ''
+                [ids]
+                ${concatStringsSep "\n" options.ids}
+              '';
+              passAsFile = [ "ids" ];
+            } ''
+            cat $idsPath <(echo) ${settingsFormat.generate "keyd-${name}.conf" options.settings} >$out
+          '';
+        })
+      cfg.keyboards;
 
     hardware.uinput.enable = lib.mkDefault true;
 
@@ -62,9 +106,11 @@ in
 
       wantedBy = [ "multi-user.target" ];
 
-      restartTriggers = [
-        config.environment.etc."keyd/default.conf".source
-      ];
+      restartTriggers = mapAttrsToList
+        (name: options:
+          config.environment.etc."keyd/${name}.conf".source
+        )
+        cfg.keyboards;
 
       # this is configurable in 2.4.2, later versions seem to remove this option.
       # post-2.4.2 may need to set makeFlags in the derivation:
diff --git a/nixpkgs/nixos/modules/services/hardware/pcscd.nix b/nixpkgs/nixos/modules/services/hardware/pcscd.nix
index a09c64645c48..a9e4998efe37 100644
--- a/nixpkgs/nixos/modules/services/hardware/pcscd.nix
+++ b/nixpkgs/nixos/modules/services/hardware/pcscd.nix
@@ -24,7 +24,6 @@ in
 
     plugins = mkOption {
       type = types.listOf types.package;
-      default = [ pkgs.ccid ];
       defaultText = literalExpression "[ pkgs.ccid ]";
       example = literalExpression "[ pkgs.pcsc-cyberjack ]";
       description = lib.mdDoc "Plugin packages to be used for PCSC-Lite.";
@@ -56,6 +55,8 @@ in
     environment.systemPackages = [ package ];
     systemd.packages = [ (getBin package) ];
 
+    services.pcscd.plugins = [ pkgs.ccid ];
+
     systemd.sockets.pcscd.wantedBy = [ "sockets.target" ];
 
     systemd.services.pcscd = {
diff --git a/nixpkgs/nixos/modules/services/hardware/supergfxd.nix b/nixpkgs/nixos/modules/services/hardware/supergfxd.nix
index 5ea05ac27716..bd82775e8246 100644
--- a/nixpkgs/nixos/modules/services/hardware/supergfxd.nix
+++ b/nixpkgs/nixos/modules/services/hardware/supergfxd.nix
@@ -32,7 +32,7 @@ in
 
     systemd.packages = [ pkgs.supergfxctl ];
     systemd.services.supergfxd.wantedBy = [ "multi-user.target" ];
-    systemd.services.supergfxd.path = [ pkgs.kmod ];
+    systemd.services.supergfxd.path = [ pkgs.kmod pkgs.pciutils ];
 
     services.dbus.packages = [ pkgs.supergfxctl ];
     services.udev.packages = [ pkgs.supergfxctl ];
diff --git a/nixpkgs/nixos/modules/services/hardware/udev.nix b/nixpkgs/nixos/modules/services/hardware/udev.nix
index 94406b60b29c..56120094871c 100644
--- a/nixpkgs/nixos/modules/services/hardware/udev.nix
+++ b/nixpkgs/nixos/modules/services/hardware/udev.nix
@@ -72,7 +72,7 @@ let
           --replace \"/sbin/blkid \"${pkgs.util-linux}/sbin/blkid \
           --replace \"/bin/mount \"${pkgs.util-linux}/bin/mount \
           --replace /usr/bin/readlink ${pkgs.coreutils}/bin/readlink \
-          --replace /usr/bin/basename ${pkgs.coreutils}/bin/basename
+          --replace /usr/bin/basename ${pkgs.coreutils}/bin/basename 2>/dev/null
       ${optionalString (initrdBin != null) ''
         substituteInPlace $i --replace '/run/current-system/systemd' "${removeSuffix "/bin" initrdBin}"
       ''}
@@ -296,7 +296,6 @@ in
       packages = mkOption {
         type = types.listOf types.path;
         default = [];
-        visible = false;
         description = lib.mdDoc ''
           *This will only be used when systemd is used in stage 1.*
 
@@ -311,7 +310,6 @@ in
       binPackages = mkOption {
         type = types.listOf types.path;
         default = [];
-        visible = false;
         description = lib.mdDoc ''
           *This will only be used when systemd is used in stage 1.*
 
diff --git a/nixpkgs/nixos/modules/services/hardware/udisks2.nix b/nixpkgs/nixos/modules/services/hardware/udisks2.nix
index c53dbf477742..5c058f1f0a6f 100644
--- a/nixpkgs/nixos/modules/services/hardware/udisks2.nix
+++ b/nixpkgs/nixos/modules/services/hardware/udisks2.nix
@@ -71,12 +71,16 @@ in
 
     environment.systemPackages = [ pkgs.udisks2 ];
 
-    environment.etc = (mapAttrs' (name: value: nameValuePair "udisks2/${name}" { source = value; } ) configFiles) // {
-      # We need to make sure /etc/libblockdev/conf.d is populated to avoid
+    environment.etc = (mapAttrs' (name: value: nameValuePair "udisks2/${name}" { source = value; } ) configFiles) // (
+    let
+      libblockdev = pkgs.udisks2.libblockdev;
+      majorVer = versions.major libblockdev.version;
+    in {
+      # We need to make sure /etc/libblockdev/@major_ver@/conf.d is populated to avoid
       # warnings
-      "libblockdev/conf.d/00-default.cfg".source = "${pkgs.libblockdev}/etc/libblockdev/conf.d/00-default.cfg";
-      "libblockdev/conf.d/10-lvm-dbus.cfg".source = "${pkgs.libblockdev}/etc/libblockdev/conf.d/10-lvm-dbus.cfg";
-    };
+      "libblockdev/${majorVer}/conf.d/00-default.cfg".source = "${libblockdev}/etc/libblockdev/${majorVer}/conf.d/00-default.cfg";
+      "libblockdev/${majorVer}/conf.d/10-lvm-dbus.cfg".source = "${libblockdev}/etc/libblockdev/${majorVer}/conf.d/10-lvm-dbus.cfg";
+    });
 
     security.polkit.enable = true;
 
diff --git a/nixpkgs/nixos/modules/services/home-automation/ebusd.nix b/nixpkgs/nixos/modules/services/home-automation/ebusd.nix
new file mode 100644
index 000000000000..519d116e0e55
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/home-automation/ebusd.nix
@@ -0,0 +1,270 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.ebusd;
+
+  package = pkgs.ebusd;
+
+  arguments = [
+    "${package}/bin/ebusd"
+    "--foreground"
+    "--updatecheck=off"
+    "--device=${cfg.device}"
+    "--port=${toString cfg.port}"
+    "--configpath=${cfg.configpath}"
+    "--scanconfig=${cfg.scanconfig}"
+    "--log=main:${cfg.logs.main}"
+    "--log=network:${cfg.logs.network}"
+    "--log=bus:${cfg.logs.bus}"
+    "--log=update:${cfg.logs.update}"
+    "--log=other:${cfg.logs.other}"
+    "--log=all:${cfg.logs.all}"
+  ] ++ lib.optionals cfg.readonly [
+    "--readonly"
+  ] ++ lib.optionals cfg.mqtt.enable [
+    "--mqtthost=${cfg.mqtt.host}"
+    "--mqttport=${toString cfg.mqtt.port}"
+    "--mqttuser=${cfg.mqtt.user}"
+    "--mqttpass=${cfg.mqtt.password}"
+  ] ++ lib.optionals cfg.mqtt.home-assistant [
+    "--mqttint=${package}/etc/ebusd/mqtt-hassio.cfg"
+    "--mqttjson"
+  ] ++ lib.optionals cfg.mqtt.retain [
+    "--mqttretain"
+  ] ++ cfg.extraArguments;
+
+  usesDev = hasPrefix "/" cfg.device;
+
+  command = concatStringsSep " " arguments;
+
+in
+{
+  meta.maintainers = with maintainers; [ nathan-gs ];
+
+  options.services.ebusd = {
+    enable = mkEnableOption (lib.mdDoc "ebusd service");
+
+    device = mkOption {
+      type = types.str;
+      default = "";
+      example = "IP:PORT";
+      description = lib.mdDoc ''
+        Use DEV as eBUS device [/dev/ttyUSB0].
+        This can be either:
+          enh:DEVICE or enh:IP:PORT for enhanced device (only adapter v3 and newer),
+          ens:DEVICE for enhanced high speed serial device (only adapter v3 and newer with firmware since 20220731),
+          DEVICE for serial device (normal speed, for all other serial adapters like adapter v2 as well as adapter v3 in non-enhanced mode), or
+          [udp:]IP:PORT for network device.
+        https://github.com/john30/ebusd/wiki/2.-Run#device-options
+      '';
+    };
+
+    port = mkOption {
+      default = 8888;
+      type = types.port;
+      description = lib.mdDoc ''
+        The port on which to listen on
+      '';
+    };
+
+    readonly = mkOption {
+      type = types.bool;
+      default = false;
+      description = lib.mdDoc ''
+         Only read from device, never write to it
+      '';
+    };
+
+    configpath = mkOption {
+      type = types.str;
+      default = "https://cfg.ebusd.eu/";
+      description = lib.mdDoc ''
+        Read CSV config files from PATH (local folder or HTTPS URL) [https://cfg.ebusd.eu/]
+      '';
+    };
+
+    scanconfig = mkOption {
+      type = types.str;
+      default = "full";
+      description = lib.mdDoc ''
+        Pick CSV config files matching initial scan ("none" or empty for no initial scan message, "full" for full scan, or a single hex address to scan, default is to send a broadcast ident message).
+        If combined with --checkconfig, you can add scan message data as arguments for checking a particular scan configuration, e.g. "FF08070400/0AB5454850303003277201". For further details on this option,
+        see [Automatic configuration](https://github.com/john30/ebusd/wiki/4.7.-Automatic-configuration).
+      '';
+    };
+
+    logs = {
+      main = mkOption {
+        type = types.enum [ "error" "notice" "info" "debug"];
+        default = "info";
+        description = lib.mdDoc ''
+          Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice].
+        '';
+      };
+
+      network = mkOption {
+        type = types.enum [ "error" "notice" "info" "debug"];
+        default = "info";
+        description = lib.mdDoc ''
+          Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice].
+        '';
+      };
+
+      bus = mkOption {
+        type = types.enum [ "error" "notice" "info" "debug"];
+        default = "info";
+        description = lib.mdDoc ''
+          Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice].
+        '';
+      };
+
+      update = mkOption {
+        type = types.enum [ "error" "notice" "info" "debug"];
+        default = "info";
+        description = lib.mdDoc ''
+          Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice].
+        '';
+      };
+
+      other = mkOption {
+        type = types.enum [ "error" "notice" "info" "debug"];
+        default = "info";
+        description = lib.mdDoc ''
+          Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice].
+        '';
+      };
+
+      all = mkOption {
+        type = types.enum [ "error" "notice" "info" "debug"];
+        default = "info";
+        description = lib.mdDoc ''
+          Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice].
+        '';
+      };
+    };
+
+    mqtt = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Adds support for MQTT
+        '';
+      };
+
+      host = mkOption {
+        type = types.str;
+        default = "localhost";
+        description = lib.mdDoc ''
+          Connect to MQTT broker on HOST.
+        '';
+      };
+
+      port = mkOption {
+        default = 1883;
+        type = types.port;
+        description = lib.mdDoc ''
+          The port on which to connect to MQTT
+        '';
+      };
+
+      home-assistant = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Adds the Home Assistant topics to MQTT, read more at [MQTT Integration](https://github.com/john30/ebusd/wiki/MQTT-integration)
+        '';
+      };
+
+      retain = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Set the retain flag on all topics instead of only selected global ones
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        description = lib.mdDoc ''
+          The MQTT user to use
+        '';
+      };
+
+      password = mkOption {
+        type = types.str;
+        description = lib.mdDoc ''
+          The MQTT password.
+        '';
+      };
+
+    };
+
+    extraArguments = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = lib.mdDoc ''
+        Extra arguments to the ebus daemon
+      '';
+    };
+
+  };
+
+  config = mkIf (cfg.enable) {
+
+    systemd.services.ebusd = {
+      description = "EBUSd Service";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      serviceConfig = {
+        ExecStart = command;
+        DynamicUser = true;
+        Restart = "on-failure";
+
+        # Hardening
+        CapabilityBoundingSet = "";
+        DeviceAllow = lib.optionals usesDev [
+          cfg.device
+        ] ;
+        DevicePolicy = "closed";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = false;
+        NoNewPrivileges = true;
+        PrivateDevices = usesDev;
+        PrivateUsers = true;
+        PrivateTmp = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProcSubset = "pid";
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SupplementaryGroups = [
+          "dialout"
+        ];
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service @pkey"
+          "~@privileged @resources"
+        ];
+        UMask = "0077";
+      };
+    };
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/home-automation/evcc.nix b/nixpkgs/nixos/modules/services/home-automation/evcc.nix
index efa2cf244313..d0ce3fb4a1ce 100644
--- a/nixpkgs/nixos/modules/services/home-automation/evcc.nix
+++ b/nixpkgs/nixos/modules/services/home-automation/evcc.nix
@@ -50,7 +50,7 @@ in
       ];
       environment.HOME = "/var/lib/evcc";
       path = with pkgs; [
-        glibc # requires getent
+        getent
       ];
       serviceConfig = {
         ExecStart = "${package}/bin/evcc --config ${configFile} ${escapeShellArgs cfg.extraArgs}";
diff --git a/nixpkgs/nixos/modules/services/home-automation/home-assistant.nix b/nixpkgs/nixos/modules/services/home-automation/home-assistant.nix
index abe0b93e412c..0b8b1d719418 100644
--- a/nixpkgs/nixos/modules/services/home-automation/home-assistant.nix
+++ b/nixpkgs/nixos/modules/services/home-automation/home-assistant.nix
@@ -451,6 +451,7 @@ in {
           "eufylife_ble"
           "esphome"
           "fjaraskupan"
+          "gardena_bluetooth"
           "govee_ble"
           "homekit_controller"
           "inkbird"
diff --git a/nixpkgs/nixos/modules/services/mail/davmail.nix b/nixpkgs/nixos/modules/services/mail/davmail.nix
index 483f591a7268..9cdb435af4a1 100644
--- a/nixpkgs/nixos/modules/services/mail/davmail.nix
+++ b/nixpkgs/nixos/modules/services/mail/davmail.nix
@@ -91,6 +91,33 @@ in
           Restart = "on-failure";
           DynamicUser = "yes";
           LogsDirectory = "davmail";
+
+          CapabilityBoundingSet = [ "" ];
+          DeviceAllow = [ "" ];
+          LockPersonality = true;
+          NoNewPrivileges = true;
+          PrivateDevices = true;
+          PrivateTmp = true;
+          PrivateUsers = true;
+          ProtectClock = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectSystem = "strict";
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectProc = "invisible";
+          RemoveIPC = true;
+          RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          SystemCallArchitectures = "native";
+          SystemCallFilter = "@system-service";
+          SystemCallErrorNumber = "EPERM";
+          UMask = "0077";
+
         };
       };
 
diff --git a/nixpkgs/nixos/modules/services/mail/nullmailer.nix b/nixpkgs/nixos/modules/services/mail/nullmailer.nix
index 7c72229efb24..f6befe246b12 100644
--- a/nixpkgs/nixos/modules/services/mail/nullmailer.nix
+++ b/nixpkgs/nixos/modules/services/mail/nullmailer.nix
@@ -203,7 +203,7 @@ with lib;
     users = {
       users.${cfg.user} = {
         description = "Nullmailer relay-only mta user";
-        group = cfg.group;
+        inherit (cfg) group;
         isSystemUser = true;
       };
 
@@ -211,10 +211,10 @@ with lib;
     };
 
     systemd.tmpfiles.rules = [
-      "d /var/spool/nullmailer - ${cfg.user} - - -"
-      "d /var/spool/nullmailer/failed 750 ${cfg.user} - - -"
-      "d /var/spool/nullmailer/queue 750 ${cfg.user} - - -"
-      "d /var/spool/nullmailer/tmp 750 ${cfg.user} - - -"
+      "d /var/spool/nullmailer - ${cfg.user} ${cfg.group} - -"
+      "d /var/spool/nullmailer/failed 770 ${cfg.user} ${cfg.group} - -"
+      "d /var/spool/nullmailer/queue 770 ${cfg.user} ${cfg.group} - -"
+      "d /var/spool/nullmailer/tmp 770 ${cfg.user} ${cfg.group} - -"
     ];
 
     systemd.services.nullmailer = {
@@ -238,7 +238,7 @@ with lib;
       program = "sendmail";
       source = "${pkgs.nullmailer}/bin/sendmail";
       owner = cfg.user;
-      group = cfg.group;
+      inherit (cfg) group;
       setuid = true;
       setgid = true;
     };
diff --git a/nixpkgs/nixos/modules/services/mail/public-inbox.nix b/nixpkgs/nixos/modules/services/mail/public-inbox.nix
index 3380f0da181d..e8f943207b67 100644
--- a/nixpkgs/nixos/modules/services/mail/public-inbox.nix
+++ b/nixpkgs/nixos/modules/services/mail/public-inbox.nix
@@ -89,7 +89,7 @@ let
       PrivateNetwork = mkDefault (!needNetwork);
       ProcSubset = "pid";
       ProtectClock = true;
-      ProtectHome = mkDefault true;
+      ProtectHome = "tmpfs";
       ProtectHostname = true;
       ProtectKernelLogs = true;
       ProtectProc = "invisible";
@@ -177,8 +177,7 @@ in
           description = lib.mdDoc "The email addresses of the public-inbox.";
         };
         options.url = mkOption {
-          type = with types; nullOr str;
-          default = null;
+          type = types.nonEmptyStr;
           example = "https://example.org/lists/example-discuss";
           description = lib.mdDoc "URL where this inbox can be accessed over HTTP.";
         };
@@ -474,6 +473,8 @@ in
           after = [ "public-inbox-init.service" "public-inbox-watch.service" ];
           requires = [ "public-inbox-init.service" ];
           serviceConfig = {
+            BindPathsReadOnly =
+              map (c: c.dir) (lib.attrValues cfg.settings.coderepo);
             ExecStart = escapeShellArgs (
               [ "${cfg.package}/bin/public-inbox-httpd" ] ++
               cfg.http.args ++
@@ -576,16 +577,7 @@ in
                 ${pkgs.git}/bin/git config core.sharedRepository 0640
               fi
             '') cfg.inboxes
-            ) + ''
-            shopt -s nullglob
-            for inbox in ${stateDir}/inboxes/*/; do
-              # This should be idempotent, but only do it for new
-              # inboxes anyway because it's only needed once, and could
-              # be slow for large pre-existing inboxes.
-              ls -1 "$inbox" | grep -q '^xap' ||
-              ${cfg.package}/bin/public-inbox-index "$inbox"
-            done
-          '';
+            );
           serviceConfig = {
             Type = "oneshot";
             RemainAfterExit = true;
diff --git a/nixpkgs/nixos/modules/services/mail/rspamd.nix b/nixpkgs/nixos/modules/services/mail/rspamd.nix
index f9be9024dd4f..ca88d8122179 100644
--- a/nixpkgs/nixos/modules/services/mail/rspamd.nix
+++ b/nixpkgs/nixos/modules/services/mail/rspamd.nix
@@ -215,7 +215,7 @@ let
       text = v.extraConfig;
     })
     (filterAttrs (n: v: v.extraConfig != "") cfg.workers))
-    // (if cfg.extraConfig == "" then {} else {
+    // (lib.optionalAttrs (cfg.extraConfig != "") {
       "extra-config.inc".text = cfg.extraConfig;
     });
 in
diff --git a/nixpkgs/nixos/modules/services/mail/spamassassin.nix b/nixpkgs/nixos/modules/services/mail/spamassassin.nix
index 49d1d9315985..072172e31451 100644
--- a/nixpkgs/nixos/modules/services/mail/spamassassin.nix
+++ b/nixpkgs/nixos/modules/services/mail/spamassassin.nix
@@ -77,9 +77,9 @@ in
           loadplugin Mail::SpamAssassin::Plugin::Check
           #loadplugin Mail::SpamAssassin::Plugin::DCC
           loadplugin Mail::SpamAssassin::Plugin::DKIM
+          loadplugin Mail::SpamAssassin::Plugin::DMARC
           loadplugin Mail::SpamAssassin::Plugin::DNSEval
           loadplugin Mail::SpamAssassin::Plugin::FreeMail
-          loadplugin Mail::SpamAssassin::Plugin::Hashcash
           loadplugin Mail::SpamAssassin::Plugin::HeaderEval
           loadplugin Mail::SpamAssassin::Plugin::HTMLEval
           loadplugin Mail::SpamAssassin::Plugin::HTTPSMismatch
diff --git a/nixpkgs/nixos/modules/services/matrix/appservice-irc.nix b/nixpkgs/nixos/modules/services/matrix/appservice-irc.nix
index 388553d4182e..d153ffc2ace8 100644
--- a/nixpkgs/nixos/modules/services/matrix/appservice-irc.nix
+++ b/nixpkgs/nixos/modules/services/matrix/appservice-irc.nix
@@ -12,16 +12,14 @@ let
 
   configFile = pkgs.runCommand "matrix-appservice-irc.yml" {
     # Because this program will be run at build time, we need `nativeBuildInputs`
-    nativeBuildInputs = [ (pkgs.python3.withPackages (ps: [ ps.pyyaml ps.jsonschema ])) ];
+    nativeBuildInputs = [ (pkgs.python3.withPackages (ps: [ ps.jsonschema ])) pkgs.remarshal ];
     preferLocalBuild = true;
 
     config = builtins.toJSON cfg.settings;
     passAsFile = [ "config" ];
   } ''
     # The schema is given as yaml, we need to convert it to json
-    python -c 'import json; import yaml; import sys; json.dump(yaml.safe_load(sys.stdin), sys.stdout)' \
-      < ${pkg}/lib/node_modules/matrix-appservice-irc/config.schema.yml \
-      > config.schema.json
+    remarshal --if yaml --of json -i ${pkg}/config.schema.yml -o config.schema.json
     python -m jsonschema config.schema.json -i $configPath
     cp "$configPath" "$out"
   '';
@@ -187,7 +185,7 @@ in {
           sed -i "s/^as_token:.*$/$as_token/g" ${registrationFile}
         fi
         # Allow synapse access to the registration
-        if ${getBin pkgs.glibc}/bin/getent group matrix-synapse > /dev/null; then
+        if ${pkgs.getent}/bin/getent group matrix-synapse > /dev/null; then
           chgrp matrix-synapse ${registrationFile}
           chmod g+r ${registrationFile}
         fi
@@ -215,7 +213,10 @@ in {
         LockPersonality = true;
         RestrictRealtime = true;
         PrivateMounts = true;
-        SystemCallFilter = "~@aio @clock @cpu-emulation @debug @keyring @memlock @module @mount @obsolete @raw-io @setuid @swap";
+        SystemCallFilter = [
+          "@system-service @pkey"
+          "~@privileged @resources"
+        ];
         SystemCallArchitectures = "native";
         # AF_UNIX is required to connect to a postgres socket.
         RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
diff --git a/nixpkgs/nixos/modules/services/matrix/conduit.nix b/nixpkgs/nixos/modules/services/matrix/conduit.nix
index c8d89ed33f51..16c4f571da94 100644
--- a/nixpkgs/nixos/modules/services/matrix/conduit.nix
+++ b/nixpkgs/nixos/modules/services/matrix/conduit.nix
@@ -138,10 +138,12 @@ in
             "~@privileged"
           ];
           StateDirectory = "matrix-conduit";
+          StateDirectoryMode = "0700";
           ExecStart = "${cfg.package}/bin/conduit";
           Restart = "on-failure";
           RestartSec = 10;
           StartLimitBurst = 5;
+          UMask = "077";
         };
       };
     };
diff --git a/nixpkgs/nixos/modules/services/matrix/matrix-sliding-sync.nix b/nixpkgs/nixos/modules/services/matrix/matrix-sliding-sync.nix
new file mode 100644
index 000000000000..9bf4de3317cc
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/matrix/matrix-sliding-sync.nix
@@ -0,0 +1,96 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.matrix-synapse.sliding-sync;
+in
+{
+  options.services.matrix-synapse.sliding-sync = {
+    enable = lib.mkEnableOption (lib.mdDoc "sliding sync");
+
+    package = lib.mkPackageOption pkgs "matrix-sliding-sync" { };
+
+    settings = lib.mkOption {
+      type = lib.types.submodule {
+        freeformType = with lib.types; attrsOf str;
+        options = {
+          SYNCV3_SERVER = lib.mkOption {
+            type = lib.types.str;
+            description = lib.mdDoc ''
+              The destination homeserver to talk to not including `/_matrix/` e.g `https://matrix.example.org`.
+            '';
+          };
+
+          SYNCV3_DB = lib.mkOption {
+            type = lib.types.str;
+            default = "postgresql:///matrix-sliding-sync?host=/run/postgresql";
+            description = lib.mdDoc ''
+              The postgres connection string.
+              Refer to <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>.
+            '';
+          };
+
+          SYNCV3_BINDADDR = lib.mkOption {
+            type = lib.types.str;
+            default = "127.0.0.1:8009";
+            example = "[::]:8008";
+            description = lib.mdDoc "The interface and port to listen on.";
+          };
+
+          SYNCV3_LOG_LEVEL = lib.mkOption {
+            type = lib.types.enum [ "trace" "debug" "info" "warn" "error" "fatal" ];
+            default = "info";
+            description = lib.mdDoc "The level of verbosity for messages logged.";
+          };
+        };
+      };
+      default = { };
+      description = ''
+        Freeform environment variables passed to the sliding sync proxy.
+        Refer to <https://github.com/matrix-org/sliding-sync#setup> for all supported values.
+      '';
+    };
+
+    createDatabase = lib.mkOption {
+      type = lib.types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Whether to enable and configure `services.postgres` to ensure that the database user `matrix-sliding-sync`
+        and the database `matrix-sliding-sync` exist.
+      '';
+    };
+
+    environmentFile = lib.mkOption {
+      type = lib.types.str;
+      description = lib.mdDoc ''
+        Environment file as defined in {manpage}`systemd.exec(5)`.
+
+        This must contain the {env}`SYNCV3_SECRET` variable which should
+        be generated with {command}`openssl rand -hex 32`.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    services.postgresql = lib.optionalAttrs cfg.createDatabase {
+      enable = true;
+      ensureDatabases = [ "matrix-sliding-sync" ];
+      ensureUsers = [ rec {
+        name = "matrix-sliding-sync";
+        ensurePermissions."DATABASE \"${name}\"" = "ALL PRIVILEGES";
+      } ];
+    };
+
+    systemd.services.matrix-sliding-sync = {
+      after = lib.optional cfg.createDatabase "postgresql.service";
+      wantedBy = [ "multi-user.target" ];
+      environment = cfg.settings;
+      serviceConfig = {
+        DynamicUser = true;
+        EnvironmentFile = cfg.environmentFile;
+        ExecStart = lib.getExe cfg.package;
+        StateDirectory = "matrix-sliding-sync";
+        WorkingDirectory = "%S/matrix-sliding-sync";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/matrix/mautrix-telegram.nix b/nixpkgs/nixos/modules/services/matrix/mautrix-telegram.nix
index b64cc71d9873..17032ed808e9 100644
--- a/nixpkgs/nixos/modules/services/matrix/mautrix-telegram.nix
+++ b/nixpkgs/nixos/modules/services/matrix/mautrix-telegram.nix
@@ -80,6 +80,9 @@ in {
               "example.com" = "full";
               "@admin:example.com" = "admin";
             };
+            telegram = {
+              connection.use_ipv6 = true;
+            };
           }
         '';
         description = lib.mdDoc ''
diff --git a/nixpkgs/nixos/modules/services/matrix/mautrix-whatsapp.nix b/nixpkgs/nixos/modules/services/matrix/mautrix-whatsapp.nix
new file mode 100644
index 000000000000..80c85980196f
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/matrix/mautrix-whatsapp.nix
@@ -0,0 +1,198 @@
+{
+  lib,
+  config,
+  pkgs,
+  ...
+}: let
+  cfg = config.services.mautrix-whatsapp;
+  dataDir = "/var/lib/mautrix-whatsapp";
+  registrationFile = "${dataDir}/whatsapp-registration.yaml";
+  settingsFile = "${dataDir}/config.json";
+  settingsFileUnsubstituted = settingsFormat.generate "mautrix-whatsapp-config-unsubstituted.json" cfg.settings;
+  settingsFormat = pkgs.formats.json {};
+  appservicePort = 29318;
+in {
+  imports = [];
+  options.services.mautrix-whatsapp = {
+    enable = lib.mkEnableOption "mautrix-whatsapp, a puppeting/relaybot bridge between Matrix and WhatsApp.";
+
+    settings = lib.mkOption {
+      type = settingsFormat.type;
+      default = {
+        appservice = {
+          address = "http://localhost:${toString appservicePort}";
+          hostname = "[::]";
+          port = appservicePort;
+          database = {
+            type = "sqlite3";
+            uri = "${dataDir}/mautrix-whatsapp.db";
+          };
+          id = "whatsapp";
+          bot = {
+            username = "whatsappbot";
+            displayname = "WhatsApp Bridge Bot";
+          };
+          as_token = "";
+          hs_token = "";
+        };
+        bridge = {
+          username_template = "whatsapp_{{.}}";
+          displayname_template = "{{if .BusinessName}}{{.BusinessName}}{{else if .PushName}}{{.PushName}}{{else}}{{.JID}}{{end}} (WA)";
+          double_puppet_server_map = {};
+          login_shared_secret_map = {};
+          command_prefix = "!wa";
+          permissions."*" = "relay";
+          relay.enabled = true;
+        };
+        logging = {
+          min_level = "info";
+          writers = [
+            {
+              type = "stdout";
+              format = "pretty-colored";
+            }
+            {
+              type = "file";
+              format = "json";
+            }
+          ];
+        };
+      };
+      description = lib.mdDoc ''
+        {file}`config.yaml` configuration as a Nix attribute set.
+        Configuration options should match those described in
+        [example-config.yaml](https://github.com/mautrix/whatsapp/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_whatsapp?host=/run/postgresql";
+          };
+          id = "whatsapp";
+          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 = lib.mdDoc ''
+        File containing environment variables to be passed to the mautrix-whatsapp service,
+        in which secret tokens can be specified securely by optionally defining a value for
+        `MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET`.
+      '';
+    };
+
+    serviceDependencies = lib.mkOption {
+      type = with lib.types; listOf str;
+      default = lib.optional config.services.matrix-synapse.enable "matrix-synapse.service";
+      defaultText = lib.literalExpression ''
+        optional config.services.matrix-synapse.enable "matrix-synapse.service"
+      '';
+      description = lib.mdDoc ''
+        List of Systemd services to require and wait for when starting the application service.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    services.mautrix-whatsapp.settings = {
+      homeserver.domain = lib.mkDefault config.services.matrix-synapse.settings.server_name;
+    };
+
+    systemd.services.mautrix-whatsapp = {
+      description = "Mautrix-WhatsApp Service - A WhatsApp bridge for Matrix";
+
+      wantedBy = ["multi-user.target"];
+      wants = ["network-online.target"] ++ cfg.serviceDependencies;
+      after = ["network-online.target"] ++ cfg.serviceDependencies;
+
+      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-whatsapp}/bin/mautrix-whatsapp \
+            --generate-registration \
+            --config='${settingsFile}' \
+            --registration='${registrationFile}'
+        fi
+        chmod 640 ${registrationFile}
+
+        umask 0177
+        ${pkgs.yq}/bin/yq -s '.[0].appservice.as_token = .[1].as_token
+          | .[0].appservice.hs_token = .[1].hs_token
+          | .[0]' '${settingsFile}' '${registrationFile}' \
+          > '${settingsFile}.tmp'
+        mv '${settingsFile}.tmp' '${settingsFile}'
+        umask $old_umask
+      '';
+
+      serviceConfig = {
+        DynamicUser = true;
+        EnvironmentFile = cfg.environmentFile;
+        StateDirectory = baseNameOf dataDir;
+        WorkingDirectory = "${dataDir}";
+        ExecStart = ''
+          ${pkgs.mautrix-whatsapp}/bin/mautrix-whatsapp \
+          --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; [frederictobiasc];
+}
diff --git a/nixpkgs/nixos/modules/services/misc/ananicy.nix b/nixpkgs/nixos/modules/services/misc/ananicy.nix
index d2287fba6afc..bc1b28efc0ba 100644
--- a/nixpkgs/nixos/modules/services/misc/ananicy.nix
+++ b/nixpkgs/nixos/modules/services/misc/ananicy.nix
@@ -5,7 +5,9 @@ with lib;
 let
   cfg = config.services.ananicy;
   configFile = pkgs.writeText "ananicy.conf" (generators.toKeyValue { } cfg.settings);
-  extraRules = pkgs.writeText "extraRules" cfg.extraRules;
+  extraRules = pkgs.writeText "extraRules" (concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.extraRules);
+  extraTypes = pkgs.writeText "extraTypes" (concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.extraTypes);
+  extraCgroups = pkgs.writeText "extraCgroups" (concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.extraCgroups);
   servicename = if ((lib.getName cfg.package) == (lib.getName pkgs.ananicy-cpp)) then "ananicy-cpp" else "ananicy";
 in
 {
@@ -23,6 +25,16 @@ in
         '';
       };
 
+      rulesProvider = mkOption {
+        type = types.package;
+        default = pkgs.ananicy;
+        defaultText = literalExpression "pkgs.ananicy";
+        example = literalExpression "pkgs.ananicy-cpp";
+        description = lib.mdDoc ''
+          Which package to copy default rules,types,cgroups from.
+        '';
+      };
+
       settings = mkOption {
         type = with types; attrsOf (oneOf [ int bool str ]);
         default = { };
@@ -35,20 +47,40 @@ in
       };
 
       extraRules = mkOption {
-        type = types.str;
-        default = "";
+        type = with types; listOf attrs;
+        default = [ ];
         description = lib.mdDoc ''
-          Extra rules in json format on separate lines. See:
+          Rules to write in 'nixRules.rules'. See:
           <https://github.com/Nefelim4ag/Ananicy#configuration>
           <https://gitlab.com/ananicy-cpp/ananicy-cpp/#global-configuration>
         '';
-        example = literalExpression ''
-          '''
-            { "name": "eog", "type": "Image-View" }
-            { "name": "fdupes", "type": "BG_CPUIO" }
-          '''
+        example = [
+          { name = "eog"; type = "Image-Viewer"; }
+          { name = "fdupes"; type = "BG_CPUIO"; }
+        ];
+      };
+      extraTypes = mkOption {
+        type = with types; listOf attrs;
+        default = [ ];
+        description = lib.mdDoc ''
+          Types to write in 'nixTypes.types'. See:
+          <https://gitlab.com/ananicy-cpp/ananicy-cpp/#types>
         '';
-
+        example = [
+          { type = "my_type"; nice = 19; other_parameter = "value"; }
+          { type = "compiler"; nice = 19; sched = "batch"; ioclass = "idle"; }
+        ];
+      };
+      extraCgroups = mkOption {
+        type = with types; listOf attrs;
+        default = [ ];
+        description = lib.mdDoc ''
+          Cgroups to write in 'nixCgroups.cgroups'. See:
+          <https://gitlab.com/ananicy-cpp/ananicy-cpp/#cgroups>
+        '';
+        example = [
+          { cgroup = "cpu80"; CPUQuota = 80; }
+        ];
       };
     };
   };
@@ -59,10 +91,18 @@ in
       etc."ananicy.d".source = pkgs.runCommandLocal "ananicyfiles" { } ''
         mkdir -p $out
         # ananicy-cpp does not include rules or settings on purpose
-        cp -r ${pkgs.ananicy}/etc/ananicy.d/* $out
-        rm $out/ananicy.conf
+        if [[ -d "${cfg.rulesProvider}/etc/ananicy.d/00-default" ]]; then
+          cp -r ${cfg.rulesProvider}/etc/ananicy.d/* $out
+        else
+          cp -r ${cfg.rulesProvider}/* $out
+        fi
+
+        # configured through .setings
+        rm -f $out/ananicy.conf
         cp ${configFile} $out/ananicy.conf
-        ${optionalString (cfg.extraRules != "") "cp ${extraRules} $out/nixRules.rules"}
+        ${optionalString (cfg.extraRules != [ ]) "cp ${extraRules} $out/nixRules.rules"}
+        ${optionalString (cfg.extraTypes != [ ]) "cp ${extraTypes} $out/nixTypes.types"}
+        ${optionalString (cfg.extraCgroups != [ ]) "cp ${extraCgroups} $out/nixCgroups.cgroups"}
       '';
     };
 
@@ -85,6 +125,7 @@ in
         # https://gitlab.com/ananicy-cpp/ananicy-cpp/-/blob/master/src/config.cpp#L12
         loglevel = mkOD "warn"; # default is info but its spammy
         cgroup_realtime_workaround = mkOD config.systemd.enableUnifiedCgroupHierarchy;
+        log_applied_rule = mkOD false;
       } else {
         # https://github.com/Nefelim4ag/Ananicy/blob/master/ananicy.d/ananicy.conf
         check_disks_schedulers = mkOD true;
diff --git a/nixpkgs/nixos/modules/services/misc/ankisyncd.nix b/nixpkgs/nixos/modules/services/misc/ankisyncd.nix
index 5198b8242023..7be8dc7dab8f 100644
--- a/nixpkgs/nixos/modules/services/misc/ankisyncd.nix
+++ b/nixpkgs/nixos/modules/services/misc/ankisyncd.nix
@@ -9,22 +9,16 @@ let
 
   stateDir = "/var/lib/${name}";
 
-  authDbPath = "${stateDir}/auth.db";
+  toml = pkgs.formats.toml {};
 
-  sessionDbPath = "${stateDir}/session.db";
-
-  configFile = pkgs.writeText "ankisyncd.conf" (lib.generators.toINI {} {
-    sync_app = {
+  configFile = toml.generate "ankisyncd.conf" {
+    listen = {
       host = cfg.host;
       port = cfg.port;
-      data_root = stateDir;
-      auth_db_path = authDbPath;
-      session_db_path = sessionDbPath;
-
-      base_url = "/sync/";
-      base_media_url = "/msync/";
     };
-  });
+    paths.root_dir = stateDir;
+    # encryption.ssl_enable / cert_file / key_file
+  };
 in
   {
     options.services.ankisyncd = {
@@ -59,8 +53,6 @@ in
     config = mkIf cfg.enable {
       networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
 
-      environment.etc."ankisyncd/ankisyncd.conf".source = configFile;
-
       systemd.services.ankisyncd = {
         description = "ankisyncd - Anki sync server";
         after = [ "network.target" ];
@@ -71,7 +63,7 @@ in
           Type = "simple";
           DynamicUser = true;
           StateDirectory = name;
-          ExecStart = "${cfg.package}/bin/ankisyncd";
+          ExecStart = "${cfg.package}/bin/ankisyncd --config ${configFile}";
           Restart = "always";
         };
       };
diff --git a/nixpkgs/nixos/modules/services/misc/atuin.nix b/nixpkgs/nixos/modules/services/misc/atuin.nix
index 202bd4dfca11..57ff02df7d68 100644
--- a/nixpkgs/nixos/modules/services/misc/atuin.nix
+++ b/nixpkgs/nixos/modules/services/misc/atuin.nix
@@ -1,14 +1,12 @@
 { config, pkgs, lib, ... }:
-
-with lib;
-
 let
+  inherit (lib) mkOption types mdDoc mkIf;
   cfg = config.services.atuin;
 in
 {
   options = {
     services.atuin = {
-      enable = mkEnableOption (mdDoc "Enable server for shell history sync with atuin");
+      enable = lib.mkEnableOption (mdDoc "Enable server for shell history sync with atuin");
 
       openRegistration = mkOption {
         type = types.bool;
@@ -50,16 +48,28 @@ in
         createLocally = mkOption {
           type = types.bool;
           default = true;
-          description = lib.mdDoc "Create the database and database user locally.";
+          description = mdDoc "Create the database and database user locally.";
+        };
+
+        uri = mkOption {
+          type = types.str;
+          default = "postgresql:///atuin?host=/run/postgresql";
+          example = "postgresql://atuin@localhost:5432/atuin";
+          description = mdDoc "URI to the database";
         };
       };
     };
   };
 
   config = mkIf cfg.enable {
-
-    # enable postgres to host atuin db
-    services.postgresql = {
+    assertions = [
+      {
+        assertion = cfg.database.createLocally -> config.services.postgresql.enable;
+        message = "Postgresql must be enabled to create a local database";
+      }
+    ];
+
+    services.postgresql = mkIf cfg.database.createLocally {
       enable = true;
       ensureUsers = [{
         name = "atuin";
@@ -73,7 +83,7 @@ in
     systemd.services.atuin = {
       description = "atuin server";
       requires = lib.optionals cfg.database.createLocally [ "postgresql.service" ];
-      after = [ "network.target" ] ++ lib.optionals cfg.database.createLocally [ "postgresql.service" ] ;
+      after = [ "network.target" ] ++ lib.optionals cfg.database.createLocally [ "postgresql.service" ];
       wantedBy = [ "multi-user.target" ];
 
       serviceConfig = {
@@ -81,20 +91,55 @@ in
         RuntimeDirectory = "atuin";
         RuntimeDirectoryMode = "0700";
         DynamicUser = true;
+
+        # Hardening
+        CapabilityBoundingSet = "";
+        LockPersonality = true;
+        NoNewPrivileges = true;
+        MemoryDenyWriteExecute = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "full";
+        RemoveIPC = true;
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+          # Required for connecting to database sockets,
+          "AF_UNIX"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "~@privileged"
+        ];
+        UMask = "0077";
       };
 
       environment = {
         ATUIN_HOST = cfg.host;
         ATUIN_PORT = toString cfg.port;
         ATUIN_MAX_HISTORY_LENGTH = toString cfg.maxHistoryLength;
-        ATUIN_OPEN_REGISTRATION = boolToString cfg.openRegistration;
-        ATUIN_DB_URI =  mkIf cfg.database.createLocally "postgresql:///atuin";
+        ATUIN_OPEN_REGISTRATION = lib.boolToString cfg.openRegistration;
+        ATUIN_DB_URI = cfg.database.uri;
         ATUIN_PATH = cfg.path;
         ATUIN_CONFIG_DIR = "/run/atuin"; # required to start, but not used as configuration is via environment variables
       };
     };
 
     networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
-
   };
 }
diff --git a/nixpkgs/nixos/modules/services/misc/bcg.nix b/nixpkgs/nixos/modules/services/misc/bcg.nix
new file mode 100644
index 000000000000..214c89dbfe72
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/bcg.nix
@@ -0,0 +1,175 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+with lib;
+
+let
+  cfg = config.services.bcg;
+  configFile = (pkgs.formats.yaml {}).generate "bcg.conf.yaml" (
+    filterAttrsRecursive (n: v: v != null) {
+      inherit (cfg) device name mqtt;
+      retain_node_messages = cfg.retainNodeMessages;
+      qos_node_messages = cfg.qosNodeMessages;
+      base_topic_prefix = cfg.baseTopicPrefix;
+      automatic_remove_kit_from_names = cfg.automaticRemoveKitFromNames;
+      automatic_rename_kit_nodes = cfg.automaticRenameKitNodes;
+      automatic_rename_generic_nodes = cfg.automaticRenameGenericNodes;
+      automatic_rename_nodes = cfg.automaticRenameNodes;
+    }
+  );
+in
+{
+  options = {
+    services.bcg = {
+      enable = mkEnableOption (mdDoc "BigClown gateway");
+      package = mkOption {
+        default = pkgs.python3Packages.bcg;
+        defaultText = literalExpression "pkgs.python3Packages.bcg";
+        description = mdDoc "Which bcg derivation to use.";
+        type = types.package;
+      };
+      environmentFiles = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        example = [ "/run/keys/bcg.env" ];
+        description = mdDoc ''
+          File to load as environment file. Environment variables from this file
+          will be interpolated into the config file using envsubst with this
+          syntax: `$ENVIRONMENT` or `''${VARIABLE}`.
+          This is useful to avoid putting secrets into the nix store.
+        '';
+      };
+      verbose = mkOption {
+        type = types.enum ["CRITICAL" "ERROR" "WARNING" "INFO" "DEBUG"];
+        default = "WARNING";
+        description = mdDoc "Verbosity level.";
+      };
+      device = mkOption {
+        type = types.str;
+        description = mdDoc "Device name to configure gateway to use.";
+      };
+      name = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = mdDoc ''
+          Name for the device.
+
+          Supported variables:
+          * `{ip}` IP address
+          * `{id}` The ID of the connected usb-dongle or core-module
+
+          `null` can be used for automatic detection from gateway firmware.
+        '';
+      };
+      mqtt = {
+        host = mkOption {
+          type = types.str;
+          default = "127.0.0.1";
+          description = mdDoc "Host where MQTT server is running.";
+        };
+        port = mkOption {
+          type = types.port;
+          default = 1883;
+          description = mdDoc "Port of MQTT server.";
+        };
+        username = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = mdDoc "MQTT server access username.";
+        };
+        password = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = mdDoc "MQTT server access password.";
+        };
+        cafile = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = mdDoc "Certificate Authority file for MQTT server access.";
+        };
+        certfile = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = mdDoc "Certificate file for MQTT server access.";
+        };
+        keyfile = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = mdDoc "Key file for MQTT server access.";
+        };
+      };
+      retainNodeMessages = mkOption {
+        type = types.bool;
+        default = false;
+        description = mdDoc "Specify that node messages should be retaied in MQTT broker.";
+      };
+      qosNodeMessages = mkOption {
+        type = types.int;
+        default = 1;
+        description = mdDoc "Set the guarantee of MQTT message delivery.";
+      };
+      baseTopicPrefix = mkOption {
+        type = types.str;
+        default = "";
+        description = mdDoc "Topic prefix added to all MQTT messages.";
+      };
+      automaticRemoveKitFromNames = mkOption {
+        type = types.bool;
+        default = true;
+        description = mdDoc "Automatically remove kits.";
+      };
+      automaticRenameKitNodes = mkOption {
+        type = types.bool;
+        default = true;
+        description = mdDoc "Automatically rename kit's nodes.";
+      };
+      automaticRenameGenericNodes = mkOption {
+        type = types.bool;
+        default = true;
+        description = mdDoc "Automatically rename generic nodes.";
+      };
+      automaticRenameNodes = mkOption {
+        type = types.bool;
+        default = true;
+        description = mdDoc "Automatically rename all nodes.";
+      };
+      rename = mkOption {
+        type = with types; attrsOf str;
+        default = {};
+        description = mdDoc "Rename nodes to different name.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs; [
+      python3Packages.bcg
+      python3Packages.bch
+    ];
+
+    systemd.services.bcg = let
+      envConfig = cfg.environmentFiles != [];
+      finalConfig = if envConfig
+                    then "$RUNTIME_DIRECTORY/bcg.config.yaml"
+                    else configFile;
+    in {
+      description = "BigClown Gateway";
+      wantedBy = [ "multi-user.target" ];
+      wants = mkIf config.services.mosquitto.enable [ "mosquitto.service" ];
+      after = [ "network-online.target" ];
+      preStart = ''
+        umask 077
+        ${pkgs.envsubst}/bin/envsubst -i "${configFile}" -o "${finalConfig}"
+        '';
+      serviceConfig = {
+        EnvironmentFile = cfg.environmentFiles;
+        ExecStart="${cfg.package}/bin/bcg -c ${finalConfig} -v ${cfg.verbose}";
+        RuntimeDirectory = "bcg";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/calibre-server.nix b/nixpkgs/nixos/modules/services/misc/calibre-server.nix
index 77c60381a312..e1ddae1de1f8 100644
--- a/nixpkgs/nixos/modules/services/misc/calibre-server.nix
+++ b/nixpkgs/nixos/modules/services/misc/calibre-server.nix
@@ -6,6 +6,17 @@ let
 
   cfg = config.services.calibre-server;
 
+  documentationLink = "https://manual.calibre-ebook.com";
+  generatedDocumentationLink = documentationLink + "/generated/en/calibre-server.html";
+
+  execFlags = (concatStringsSep " "
+    (mapAttrsToList (k: v: "${k} ${toString v}") (filterAttrs (name: value: value != null) {
+      "--listen-on" = cfg.host;
+      "--port" = cfg.port;
+      "--auth-mode" = cfg.auth.mode;
+      "--userdb" = cfg.auth.userDb;
+    }) ++ [(optionalString (cfg.auth.enable == true) "--enable-auth")])
+  );
 in
 
 {
@@ -18,52 +29,100 @@ in
     )
   ];
 
-  ###### interface
-
   options = {
     services.calibre-server = {
 
       enable = mkEnableOption (lib.mdDoc "calibre-server");
+      package = lib.mkPackageOptionMD pkgs "calibre" { };
 
       libraries = mkOption {
+        type = types.listOf types.path;
+        default = [ "/var/lib/calibre-server" ];
         description = lib.mdDoc ''
+          Make sure each library path is initialized before service startup.
           The directories of the libraries to serve. They must be readable for the user under which the server runs.
+          See the [calibredb documentation](${documentationLink}/generated/en/calibredb.html#add) for details.
         '';
-        type = types.listOf types.path;
       };
 
       user = mkOption {
-        description = lib.mdDoc "The user under which calibre-server runs.";
         type = types.str;
         default = "calibre-server";
+        description = lib.mdDoc "The user under which calibre-server runs.";
       };
 
       group = mkOption {
-        description = lib.mdDoc "The group under which calibre-server runs.";
         type = types.str;
         default = "calibre-server";
+        description = lib.mdDoc "The group under which calibre-server runs.";
       };
 
-    };
-  };
+      host = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        example = "::1";
+        description = lib.mdDoc ''
+          The interface on which to listen for connections.
+          See the [calibre-server documentation](${generatedDocumentationLink}#cmdoption-calibre-server-listen-on) for details.
+        '';
+      };
+
+      port = mkOption {
+        default = 8080;
+        type = types.port;
+        description = lib.mdDoc ''
+          The port on which to listen for connections.
+          See the [calibre-server documentation](${generatedDocumentationLink}#cmdoption-calibre-server-port) for details.
+        '';
+      };
 
+      auth = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = lib.mdDoc ''
+            Password based authentication to access the server.
+            See the [calibre-server documentation](${generatedDocumentationLink}#cmdoption-calibre-server-enable-auth) for details.
+          '';
+        };
 
-  ###### implementation
+        mode = mkOption {
+          type = types.enum [ "auto" "basic" "digest" ];
+          default = "auto";
+          description = lib.mdDoc ''
+            Choose the type of authentication used.
+            Set the HTTP authentication mode used by the server.
+            See the [calibre-server documentation](${generatedDocumentationLink}#cmdoption-calibre-server-auth-mode) for details.
+          '';
+        };
+
+        userDb = mkOption {
+          default = null;
+          type = types.nullOr types.path;
+          description = lib.mdDoc ''
+            Choose users database file to use for authentication.
+            Make sure users database file is initialized before service startup.
+            See the [calibre-server documentation](${documentationLink}/server.html#managing-user-accounts-from-the-command-line-only) for details.
+          '';
+        };
+      };
+    };
+  };
 
   config = mkIf cfg.enable {
 
     systemd.services.calibre-server = {
-        description = "Calibre Server";
-        after = [ "network.target" ];
-        wantedBy = [ "multi-user.target" ];
-        serviceConfig = {
-          User = cfg.user;
-          Restart = "always";
-          ExecStart = "${pkgs.calibre}/bin/calibre-server ${lib.concatStringsSep " " cfg.libraries}";
-        };
-
+      description = "Calibre Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User = cfg.user;
+        Restart = "always";
+        ExecStart = "${cfg.package}/bin/calibre-server ${lib.concatStringsSep " " cfg.libraries} ${execFlags}";
       };
 
+    };
+
     environment.systemPackages = [ pkgs.calibre ];
 
     users.users = optionalAttrs (cfg.user == "calibre-server") {
@@ -83,4 +142,5 @@ in
 
   };
 
+  meta.maintainers = with lib.maintainers; [ gaelreyrol ];
 }
diff --git a/nixpkgs/nixos/modules/services/misc/cgminer.nix b/nixpkgs/nixos/modules/services/misc/cgminer.nix
index fced106cb325..a6fbfee73bad 100644
--- a/nixpkgs/nixos/modules/services/misc/cgminer.nix
+++ b/nixpkgs/nixos/modules/services/misc/cgminer.nix
@@ -11,7 +11,7 @@ let
     mapAttrsToList (n: v: ''"${n}": "${(concatStringsSep "," (map convType v))}"'')
       (foldAttrs (n: a: [n] ++ a) [] cfg.hardware);
   mergedConfig = with builtins;
-    mapAttrsToList (n: v: ''"${n}":  ${if isBool v then "" else ''"''}${convType v}${if isBool v then "" else ''"''}'')
+    mapAttrsToList (n: v: ''"${n}":  ${if isBool v then convType v else ''"${convType v}"''}'')
       cfg.config;
 
   cgminerConfig = pkgs.writeText "cgminer.conf" ''
diff --git a/nixpkgs/nixos/modules/services/misc/disnix.nix b/nixpkgs/nixos/modules/services/misc/disnix.nix
index 1cdfeef57cef..13c57ce6b85b 100644
--- a/nixpkgs/nixos/modules/services/misc/disnix.nix
+++ b/nixpkgs/nixos/modules/services/misc/disnix.nix
@@ -87,8 +87,8 @@ in
         environment = {
           HOME = "/root";
         }
-        // (if config.environment.variables ? DYSNOMIA_CONTAINERS_PATH then { inherit (config.environment.variables) DYSNOMIA_CONTAINERS_PATH; } else {})
-        // (if config.environment.variables ? DYSNOMIA_MODULES_PATH then { inherit (config.environment.variables) DYSNOMIA_MODULES_PATH; } else {});
+        // (optionalAttrs (config.environment.variables ? DYSNOMIA_CONTAINERS_PATH) { inherit (config.environment.variables) DYSNOMIA_CONTAINERS_PATH; })
+        // (optionalAttrs (config.environment.variables ? DYSNOMIA_MODULES_PATH) { inherit (config.environment.variables) DYSNOMIA_MODULES_PATH; });
 
         serviceConfig.ExecStart = "${cfg.package}/bin/disnix-service";
       };
diff --git a/nixpkgs/nixos/modules/services/misc/docker-registry.nix b/nixpkgs/nixos/modules/services/misc/docker-registry.nix
index 8ea81f9a7ee2..b0e910634637 100644
--- a/nixpkgs/nixos/modules/services/misc/docker-registry.nix
+++ b/nixpkgs/nixos/modules/services/misc/docker-registry.nix
@@ -15,9 +15,7 @@ let
     storage = {
       cache.blobdescriptor = blobCache;
       delete.enabled = cfg.enableDelete;
-    } // (if cfg.storagePath != null
-          then { filesystem.rootdirectory = cfg.storagePath; }
-          else {});
+    } // (optionalAttrs (cfg.storagePath != null) { filesystem.rootdirectory = cfg.storagePath; });
     http = {
       addr = "${cfg.listenAddress}:${builtins.toString cfg.port}";
       headers.X-Content-Type-Options = ["nosniff"];
@@ -152,12 +150,10 @@ in {
     };
 
     users.users.docker-registry =
-      (if cfg.storagePath != null
-      then {
+      (optionalAttrs (cfg.storagePath != null) {
         createHome = true;
         home = cfg.storagePath;
-      }
-      else {}) // {
+      }) // {
         group = "docker-registry";
         isSystemUser = true;
       };
diff --git a/nixpkgs/nixos/modules/services/misc/evdevremapkeys.nix b/nixpkgs/nixos/modules/services/misc/evdevremapkeys.nix
new file mode 100644
index 000000000000..11ea6a5f03f2
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/evdevremapkeys.nix
@@ -0,0 +1,59 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  format = pkgs.formats.yaml { };
+  cfg = config.services.evdevremapkeys;
+
+in
+{
+  options.services.evdevremapkeys = {
+    enable = mkEnableOption (lib.mdDoc ''evdevremapkeys'');
+
+    settings = mkOption {
+      type = format.type;
+      default = { };
+      description = lib.mdDoc ''
+        config.yaml for evdevremapkeys
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    boot.kernelModules = [ "uinput" ];
+    services.udev.extraRules = ''
+      KERNEL=="uinput", MODE="0660", GROUP="input"
+    '';
+    users.groups.evdevremapkeys = { };
+    users.users.evdevremapkeys = {
+      description = "evdevremapkeys service user";
+      group = "evdevremapkeys";
+      extraGroups = [ "input" ];
+      isSystemUser = true;
+    };
+    systemd.services.evdevremapkeys = {
+      description = "evdevremapkeys";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig =
+        let
+          config = format.generate "config.yaml" cfg.settings;
+        in
+        {
+          ExecStart = "${pkgs.evdevremapkeys}/bin/evdevremapkeys --config-file ${config}";
+          User = "evdevremapkeys";
+          Group = "evdevremapkeys";
+          StateDirectory = "evdevremapkeys";
+          Restart = "always";
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
+          PrivateNetwork = true;
+          PrivateTmp = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectKernelTunables = true;
+          ProtectSystem = true;
+        };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/exhibitor.nix b/nixpkgs/nixos/modules/services/misc/exhibitor.nix
deleted file mode 100644
index 91a87b55af59..000000000000
--- a/nixpkgs/nixos/modules/services/misc/exhibitor.nix
+++ /dev/null
@@ -1,417 +0,0 @@
-{ config, lib, options, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.exhibitor;
-  opt = options.services.exhibitor;
-  exhibitorConfig = ''
-    zookeeper-install-directory=${cfg.baseDir}/zookeeper
-    zookeeper-data-directory=${cfg.zkDataDir}
-    zookeeper-log-directory=${cfg.zkLogDir}
-    zoo-cfg-extra=${cfg.zkExtraCfg}
-    client-port=${toString cfg.zkClientPort}
-    connect-port=${toString cfg.zkConnectPort}
-    election-port=${toString cfg.zkElectionPort}
-    cleanup-period-ms=${toString cfg.zkCleanupPeriod}
-    servers-spec=${concatStringsSep "," cfg.zkServersSpec}
-    auto-manage-instances=${toString cfg.autoManageInstances}
-    ${cfg.extraConf}
-  '';
-  # NB: toString rather than lib.boolToString on cfg.autoManageInstances is intended.
-  # Exhibitor tests if it's an integer not equal to 0, so the empty string (toString false)
-  # will operate in the same fashion as a 0.
-  configDir = pkgs.writeTextDir "exhibitor.properties" exhibitorConfig;
-  cliOptionsCommon = {
-    configtype = cfg.configType;
-    defaultconfig = "${configDir}/exhibitor.properties";
-    port = toString cfg.port;
-    hostname = cfg.hostname;
-    headingtext = if (cfg.headingText != null) then (lib.escapeShellArg cfg.headingText) else null;
-    nodemodification = lib.boolToString cfg.nodeModification;
-    configcheckms = toString cfg.configCheckMs;
-    jquerystyle = cfg.jqueryStyle;
-    loglines = toString cfg.logLines;
-    servo = lib.boolToString cfg.servo;
-    timeout = toString cfg.timeout;
-  };
-  s3CommonOptions = { s3region = cfg.s3Region; s3credentials = cfg.s3Credentials; };
-  cliOptionsPerConfig = {
-    s3 = {
-      s3config = "${cfg.s3Config.bucketName}:${cfg.s3Config.objectKey}";
-      s3configprefix = cfg.s3Config.configPrefix;
-    };
-    zookeeper = {
-      zkconfigconnect = concatStringsSep "," cfg.zkConfigConnect;
-      zkconfigexhibitorpath = cfg.zkConfigExhibitorPath;
-      zkconfigpollms = toString cfg.zkConfigPollMs;
-      zkconfigretry = "${toString cfg.zkConfigRetry.sleepMs}:${toString cfg.zkConfigRetry.retryQuantity}";
-      zkconfigzpath = cfg.zkConfigZPath;
-      zkconfigexhibitorport = toString cfg.zkConfigExhibitorPort; # NB: This might be null
-    };
-    file = {
-      fsconfigdir = cfg.fsConfigDir;
-      fsconfiglockprefix = cfg.fsConfigLockPrefix;
-      fsConfigName = fsConfigName;
-    };
-    none = {
-      noneconfigdir = configDir;
-    };
-  };
-  cliOptions = concatStringsSep " " (mapAttrsToList (k: v: "--${k} ${v}") (filterAttrs (k: v: v != null && v != "") (cliOptionsCommon //
-               cliOptionsPerConfig.${cfg.configType} //
-               s3CommonOptions //
-               optionalAttrs cfg.s3Backup { s3backup = "true"; } //
-               optionalAttrs cfg.fileSystemBackup { filesystembackup = "true"; }
-               )));
-in
-{
-  options = {
-    services.exhibitor = {
-      enable = mkEnableOption (lib.mdDoc "exhibitor server");
-
-      # See https://github.com/soabase/exhibitor/wiki/Running-Exhibitor for what these mean
-      # General options for any type of config
-      port = mkOption {
-        type = types.port;
-        default = 8080;
-        description = lib.mdDoc ''
-          The port for exhibitor to listen on and communicate with other exhibitors.
-        '';
-      };
-      baseDir = mkOption {
-        type = types.str;
-        default = "/var/exhibitor";
-        description = lib.mdDoc ''
-          Baseline directory for exhibitor runtime config.
-        '';
-      };
-      configType = mkOption {
-        type = types.enum [ "file" "s3" "zookeeper" "none" ];
-        description = lib.mdDoc ''
-          Which configuration type you want to use. Additional config will be
-          required depending on which type you are using.
-        '';
-      };
-      hostname = mkOption {
-        type = types.nullOr types.str;
-        description = lib.mdDoc ''
-          Hostname to use and advertise
-        '';
-        default = null;
-      };
-      nodeModification = mkOption {
-        type = types.bool;
-        description = lib.mdDoc ''
-          Whether the Explorer UI will allow nodes to be modified (use with caution).
-        '';
-        default = true;
-      };
-      configCheckMs = mkOption {
-        type = types.int;
-        description = lib.mdDoc ''
-          Period (ms) to check for shared config updates.
-        '';
-        default = 30000;
-      };
-      headingText = mkOption {
-        type = types.nullOr types.str;
-        description = lib.mdDoc ''
-          Extra text to display in UI header
-        '';
-        default = null;
-      };
-      jqueryStyle = mkOption {
-        type = types.enum [ "red" "black" "custom" ];
-        description = lib.mdDoc ''
-          Styling used for the JQuery-based UI.
-        '';
-        default = "red";
-      };
-      logLines = mkOption {
-        type = types.int;
-        description = lib.mdDoc ''
-        Max lines of logging to keep in memory for display.
-        '';
-        default = 1000;
-      };
-      servo = mkOption {
-        type = types.bool;
-        description = lib.mdDoc ''
-          ZooKeeper will be queried once a minute for its state via the 'mntr' four
-          letter word (this requires ZooKeeper 3.4.x+). Servo will be used to publish
-          this data via JMX.
-        '';
-        default = false;
-      };
-      timeout = mkOption {
-        type = types.int;
-        description = lib.mdDoc ''
-          Connection timeout (ms) for ZK connections.
-        '';
-        default = 30000;
-      };
-      autoManageInstances = mkOption {
-        type = types.bool;
-        description = lib.mdDoc ''
-          Automatically manage ZooKeeper instances in the ensemble
-        '';
-        default = false;
-      };
-      zkDataDir = mkOption {
-        type = types.str;
-        default = "${cfg.baseDir}/zkData";
-        defaultText = literalExpression ''"''${config.${opt.baseDir}}/zkData"'';
-        description = lib.mdDoc ''
-          The Zookeeper data directory
-        '';
-      };
-      zkLogDir = mkOption {
-        type = types.path;
-        default = "${cfg.baseDir}/zkLogs";
-        defaultText = literalExpression ''"''${config.${opt.baseDir}}/zkLogs"'';
-        description = lib.mdDoc ''
-          The Zookeeper logs directory
-        '';
-      };
-      extraConf = mkOption {
-        type = types.str;
-        default = "";
-        description = lib.mdDoc ''
-          Extra Exhibitor configuration to put in the ZooKeeper config file.
-        '';
-      };
-      zkExtraCfg = mkOption {
-        type = types.str;
-        default = "initLimit=5&syncLimit=2&tickTime=2000";
-        description = lib.mdDoc ''
-          Extra options to pass into Zookeeper
-        '';
-      };
-      zkClientPort = mkOption {
-        type = types.int;
-        default = 2181;
-        description = lib.mdDoc ''
-          Zookeeper client port
-        '';
-      };
-      zkConnectPort = mkOption {
-        type = types.int;
-        default = 2888;
-        description = lib.mdDoc ''
-          The port to use for followers to talk to each other.
-        '';
-      };
-      zkElectionPort = mkOption {
-        type = types.int;
-        default = 3888;
-        description = lib.mdDoc ''
-          The port for Zookeepers to use for leader election.
-        '';
-      };
-      zkCleanupPeriod = mkOption {
-        type = types.int;
-        default = 0;
-        description = lib.mdDoc ''
-          How often (in milliseconds) to run the Zookeeper log cleanup task.
-        '';
-      };
-      zkServersSpec = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = lib.mdDoc ''
-          Zookeeper server spec for all servers in the ensemble.
-        '';
-        example = [ "S:1:zk1.example.com" "S:2:zk2.example.com" "S:3:zk3.example.com" "O:4:zk-observer.example.com" ];
-      };
-
-      # Backup options
-      s3Backup = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Whether to enable backups to S3
-        '';
-      };
-      fileSystemBackup = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Enables file system backup of ZooKeeper log files
-        '';
-      };
-
-      # Options for using zookeeper configType
-      zkConfigConnect = mkOption {
-        type = types.listOf types.str;
-        description = lib.mdDoc ''
-          The initial connection string for ZooKeeper shared config storage
-        '';
-        example = ["host1:2181" "host2:2181"];
-      };
-      zkConfigExhibitorPath = mkOption {
-        type = types.str;
-        description = lib.mdDoc ''
-          If the ZooKeeper shared config is also running Exhibitor, the URI path for the REST call
-        '';
-        default = "/";
-      };
-      zkConfigExhibitorPort = mkOption {
-        type = types.nullOr types.int;
-        description = lib.mdDoc ''
-          If the ZooKeeper shared config is also running Exhibitor, the port that
-          Exhibitor is listening on. IMPORTANT: if this value is not set it implies
-          that Exhibitor is not being used on the ZooKeeper shared config.
-        '';
-      };
-      zkConfigPollMs = mkOption {
-        type = types.int;
-        description = lib.mdDoc ''
-          The period in ms to check for changes in the config ensemble
-        '';
-        default = 10000;
-      };
-      zkConfigRetry = {
-        sleepMs = mkOption {
-          type = types.int;
-          default = 1000;
-          description = lib.mdDoc ''
-            Retry sleep time connecting to the ZooKeeper config
-          '';
-        };
-        retryQuantity = mkOption {
-          type = types.int;
-          default = 3;
-          description = lib.mdDoc ''
-            Retries connecting to the ZooKeeper config
-          '';
-        };
-      };
-      zkConfigZPath = mkOption {
-        type = types.str;
-        description = lib.mdDoc ''
-          The base ZPath that Exhibitor should use
-        '';
-        example = "/exhibitor/config";
-      };
-
-      # Config options for s3 configType
-      s3Config = {
-        bucketName = mkOption {
-          type = types.str;
-          description = lib.mdDoc ''
-            Bucket name to store config
-          '';
-        };
-        objectKey = mkOption {
-          type = types.str;
-          description = lib.mdDoc ''
-            S3 key name to store the config
-          '';
-        };
-        configPrefix = mkOption {
-          type = types.str;
-          description = lib.mdDoc ''
-            When using AWS S3 shared config files, the prefix to use for values such as locks
-          '';
-          default = "exhibitor-";
-        };
-      };
-
-      # The next two are used for either s3backup or s3 configType
-      s3Credentials = mkOption {
-        type = types.nullOr types.path;
-        description = lib.mdDoc ''
-          Optional credentials to use for s3backup or s3config. Argument is the path
-          to an AWS credential properties file with two properties:
-          com.netflix.exhibitor.s3.access-key-id and com.netflix.exhibitor.s3.access-secret-key
-        '';
-        default = null;
-      };
-      s3Region = mkOption {
-        type = types.nullOr types.str;
-        description = lib.mdDoc ''
-          Optional region for S3 calls
-        '';
-        default = null;
-      };
-
-      # Config options for file config type
-      fsConfigDir = mkOption {
-        type = types.path;
-        description = lib.mdDoc ''
-          Directory to store Exhibitor properties (cannot be used with s3config).
-          Exhibitor uses file system locks so you can specify a shared location
-          so as to enable complete ensemble management.
-        '';
-      };
-      fsConfigLockPrefix = mkOption {
-        type = types.str;
-        description = lib.mdDoc ''
-          A prefix for a locking mechanism used in conjunction with fsconfigdir
-        '';
-        default = "exhibitor-lock-";
-      };
-      fsConfigName = mkOption {
-        type = types.str;
-        description = lib.mdDoc ''
-          The name of the file to store config in
-        '';
-        default = "exhibitor.properties";
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    systemd.services.exhibitor = {
-      description = "Exhibitor Daemon";
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
-      environment = {
-        ZOO_LOG_DIR = cfg.baseDir;
-      };
-      serviceConfig = {
-        /***
-          Exhibitor is a bit un-nixy. It wants to present to you a user interface in order to
-          mutate the configuration of both itself and ZooKeeper, and to coordinate changes
-          among the members of the Zookeeper ensemble. I'm going for a different approach here,
-          which is to manage all the configuration via nix and have it write out the configuration
-          files that exhibitor will use, and to reduce the amount of inter-exhibitor orchestration.
-        ***/
-        ExecStart = ''
-          ${pkgs.exhibitor}/bin/startExhibitor.sh ${cliOptions}
-        '';
-        User = "zookeeper";
-        PermissionsStartOnly = true;
-      };
-      # This is a bit wonky, but the reason for this is that Exhibitor tries to write to
-      # ${cfg.baseDir}/zookeeper/bin/../conf/zoo.cfg
-      # I want everything but the conf directory to be in the immutable nix store, and I want defaults
-      # from the nix store
-      # If I symlink the bin directory in, then bin/../ will resolve to the parent of the symlink in the
-      # immutable nix store. Bind mounting a writable conf over the existing conf might work, but it gets very
-      # messy with trying to copy the existing out into a mutable store.
-      # Another option is to try to patch upstream exhibitor, but the current package just pulls down the
-      # prebuild JARs off of Maven, rather than building them ourselves, as Maven support in Nix isn't
-      # very mature. So, it seems like a reasonable compromise is to just copy out of the immutable store
-      # just before starting the service, so we're running binaries from the immutable store, but we work around
-      # Exhibitor's desire to mutate its current installation.
-      preStart = ''
-        mkdir -m 0700 -p ${cfg.baseDir}/zookeeper
-        # Not doing a chown -R to keep the base ZK files owned by root
-        chown zookeeper ${cfg.baseDir} ${cfg.baseDir}/zookeeper
-        cp -Rf ${pkgs.zookeeper}/* ${cfg.baseDir}/zookeeper
-        chown -R zookeeper ${cfg.baseDir}/zookeeper/conf
-        chmod -R u+w ${cfg.baseDir}/zookeeper/conf
-        replace_what=$(echo ${pkgs.zookeeper} | sed 's/[\/&]/\\&/g')
-        replace_with=$(echo ${cfg.baseDir}/zookeeper | sed 's/[\/&]/\\&/g')
-        sed -i 's/'"$replace_what"'/'"$replace_with"'/g' ${cfg.baseDir}/zookeeper/bin/zk*.sh
-      '';
-    };
-    users.users.zookeeper = {
-      uid = config.ids.uids.zookeeper;
-      description = "Zookeeper daemon user";
-      home = cfg.baseDir;
-    };
-  };
-}
diff --git a/nixpkgs/nixos/modules/services/misc/gitea.nix b/nixpkgs/nixos/modules/services/misc/gitea.nix
index 5c35cfa177a2..ec88de6da3ba 100644
--- a/nixpkgs/nixos/modules/services/misc/gitea.nix
+++ b/nixpkgs/nixos/modules/services/misc/gitea.nix
@@ -15,6 +15,7 @@ let
     APP_NAME = ${cfg.appName}
     RUN_USER = ${cfg.user}
     RUN_MODE = prod
+    WORK_PATH = ${cfg.stateDir}
 
     ${generators.toINI {} cfg.settings}
 
@@ -439,6 +440,8 @@ in
       lfs = mkIf cfg.lfs.enable {
         PATH = cfg.lfs.contentDir;
       };
+
+      packages.CHUNKED_UPLOAD_PATH = "${cfg.stateDir}/tmp/package-upload";
     };
 
     services.postgresql = optionalAttrs (usePostgresql && cfg.database.createDatabase) {
@@ -467,10 +470,8 @@ in
     systemd.tmpfiles.rules = [
       "d '${cfg.dump.backupDir}' 0750 ${cfg.user} ${cfg.group} - -"
       "z '${cfg.dump.backupDir}' 0750 ${cfg.user} ${cfg.group} - -"
-      "Z '${cfg.dump.backupDir}' - ${cfg.user} ${cfg.group} - -"
       "d '${cfg.repositoryRoot}' 0750 ${cfg.user} ${cfg.group} - -"
       "z '${cfg.repositoryRoot}' 0750 ${cfg.user} ${cfg.group} - -"
-      "Z '${cfg.repositoryRoot}' - ${cfg.user} ${cfg.group} - -"
       "d '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -"
       "d '${cfg.stateDir}/conf' 0750 ${cfg.user} ${cfg.group} - -"
       "d '${cfg.customDir}' 0750 ${cfg.user} ${cfg.group} - -"
@@ -484,7 +485,6 @@ in
       "z '${cfg.customDir}/conf' 0750 ${cfg.user} ${cfg.group} - -"
       "z '${cfg.stateDir}/data' 0750 ${cfg.user} ${cfg.group} - -"
       "z '${cfg.stateDir}/log' 0750 ${cfg.user} ${cfg.group} - -"
-      "Z '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -"
 
       # If we have a folder or symlink with gitea locales, remove it
       # And symlink the current gitea locales in place
@@ -493,13 +493,12 @@ in
     ] ++ lib.optionals cfg.lfs.enable [
       "d '${cfg.lfs.contentDir}' 0750 ${cfg.user} ${cfg.group} - -"
       "z '${cfg.lfs.contentDir}' 0750 ${cfg.user} ${cfg.group} - -"
-      "Z '${cfg.lfs.contentDir}' - ${cfg.user} ${cfg.group} - -"
     ];
 
     systemd.services.gitea = {
       description = "gitea";
       after = [ "network.target" ] ++ optional usePostgresql "postgresql.service" ++ optional useMysql "mysql.service";
-      requires = optional usePostgresql "postgresql.service" ++ optional useMysql "mysql.service";
+      requires = optional (cfg.database.createDatabase && usePostgresql) "postgresql.service" ++ optional (cfg.database.createDatabase && useMysql) "mysql.service";
       wantedBy = [ "multi-user.target" ];
       path = [ cfg.package pkgs.git pkgs.gnupg ];
 
@@ -588,6 +587,9 @@ in
         # Runtime directory and mode
         RuntimeDirectory = "gitea";
         RuntimeDirectoryMode = "0755";
+        # Proc filesystem
+        ProcSubset = "pid";
+        ProtectProc = "invisible";
         # Access write directories
         ReadWritePaths = [ cfg.customDir cfg.dump.backupDir cfg.repositoryRoot cfg.stateDir cfg.lfs.contentDir ];
         UMask = "0027";
@@ -607,15 +609,17 @@ in
         ProtectKernelModules = true;
         ProtectKernelLogs = true;
         ProtectControlGroups = true;
-        RestrictAddressFamilies = [ "AF_UNIX AF_INET AF_INET6" ];
+        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
         LockPersonality = true;
         MemoryDenyWriteExecute = true;
         RestrictRealtime = true;
         RestrictSUIDSGID = true;
+        RemoveIPC = true;
         PrivateMounts = true;
         # System Call Filtering
         SystemCallArchitectures = "native";
-        SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @setuid @swap";
+        SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid" "setrlimit" ];
       };
 
       environment = {
diff --git a/nixpkgs/nixos/modules/services/misc/gitlab.nix b/nixpkgs/nixos/modules/services/misc/gitlab.nix
index 9c18a2eed1c6..c5e38b498829 100644
--- a/nixpkgs/nixos/modules/services/misc/gitlab.nix
+++ b/nixpkgs/nixos/modules/services/misc/gitlab.nix
@@ -152,6 +152,7 @@ let
         api_url = "http://${config.services.dockerRegistry.listenAddress}:${toString config.services.dockerRegistry.port}/";
         issuer = cfg.registry.issuer;
       };
+      elasticsearch.indexer_path = "${pkgs.gitlab-elasticsearch-indexer}/bin/gitlab-elasticsearch-indexer";
       extra = {};
       uploads.storage_path = cfg.statePath;
       pages = optionalAttrs cfg.pages.enable {
@@ -1281,6 +1282,7 @@ in {
       "d ${gitlabConfig.production.shared.path}/pages 0750 ${cfg.user} ${cfg.group} -"
       "d ${gitlabConfig.production.shared.path}/registry 0750 ${cfg.user} ${cfg.group} -"
       "d ${gitlabConfig.production.shared.path}/terraform_state 0750 ${cfg.user} ${cfg.group} -"
+      "d ${gitlabConfig.production.shared.path}/ci_secure_files 0750 ${cfg.user} ${cfg.group} -"
       "L+ /run/gitlab/config - - - - ${cfg.statePath}/config"
       "L+ /run/gitlab/log - - - - ${cfg.statePath}/log"
       "L+ /run/gitlab/tmp - - - - ${cfg.statePath}/tmp"
@@ -1633,7 +1635,7 @@ in {
         "gitlab-config.service"
         "gitlab-db-config.service"
       ] ++ optional (cfg.databaseHost == "") "postgresql.service";
-      wantedBy = [ "gitlab.target" ];
+      requiredBy = [ "gitlab.target" ];
       partOf = [ "gitlab.target" ];
       environment = gitlabEnv;
       path = with pkgs; [
@@ -1643,6 +1645,7 @@ in {
         nodejs
         procps
         gnupg
+        gzip
       ];
       serviceConfig = {
         Type = "notify";
@@ -1682,5 +1685,5 @@ in {
   };
 
   meta.doc = ./gitlab.md;
-
+  meta.maintainers = teams.gitlab.members;
 }
diff --git a/nixpkgs/nixos/modules/services/misc/gogs.nix b/nixpkgs/nixos/modules/services/misc/gogs.nix
index fa172ed277dc..9bf7e4aab814 100644
--- a/nixpkgs/nixos/modules/services/misc/gogs.nix
+++ b/nixpkgs/nixos/modules/services/misc/gogs.nix
@@ -6,16 +6,16 @@ let
   cfg = config.services.gogs;
   opt = options.services.gogs;
   configFile = pkgs.writeText "app.ini" ''
-    APP_NAME = ${cfg.appName}
+    BRAND_NAME = ${cfg.appName}
     RUN_USER = ${cfg.user}
     RUN_MODE = prod
 
     [database]
-    DB_TYPE = ${cfg.database.type}
+    TYPE = ${cfg.database.type}
     HOST = ${cfg.database.host}:${toString cfg.database.port}
     NAME = ${cfg.database.name}
     USER = ${cfg.database.user}
-    PASSWD = #dbpass#
+    PASSWORD = #dbpass#
     PATH = ${cfg.database.path}
 
     [repository]
@@ -25,7 +25,7 @@ let
     DOMAIN = ${cfg.domain}
     HTTP_ADDR = ${cfg.httpAddress}
     HTTP_PORT = ${toString cfg.httpPort}
-    ROOT_URL = ${cfg.rootUrl}
+    EXTERNAL_URL = ${cfg.rootUrl}
 
     [session]
     COOKIE_NAME = session
diff --git a/nixpkgs/nixos/modules/services/misc/gollum.nix b/nixpkgs/nixos/modules/services/misc/gollum.nix
index 4eec9610b5e9..d607e92e5ec9 100644
--- a/nixpkgs/nixos/modules/services/misc/gollum.nix
+++ b/nixpkgs/nixos/modules/services/misc/gollum.nix
@@ -91,18 +91,30 @@ in
         The package used in the service
       '';
     };
+
+    user = mkOption {
+      type = types.str;
+      default = "gollum";
+      description = lib.mdDoc "Specifies the owner of the wiki directory";
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = "gollum";
+      description = lib.mdDoc "Specifies the owner group of the wiki directory";
+    };
   };
 
   config = mkIf cfg.enable {
 
-    users.users.gollum = {
-      group = config.users.users.gollum.name;
+    users.users.gollum = mkIf (cfg.user == "gollum") {
+      group = cfg.group;
       description = "Gollum user";
       createHome = false;
       isSystemUser = true;
     };
 
-    users.groups.gollum = { };
+    users.groups."${cfg.group}" = { };
 
     systemd.tmpfiles.rules = [
       "d '${cfg.stateDir}' - ${config.users.users.gollum.name} ${config.users.groups.gollum.name} - -"
@@ -120,8 +132,8 @@ in
       '';
 
       serviceConfig = {
-        User = config.users.users.gollum.name;
-        Group = config.users.groups.gollum.name;
+        User = cfg.user;
+        Group = cfg.group;
         WorkingDirectory = cfg.stateDir;
         ExecStart = ''
           ${cfg.package}/bin/gollum \
@@ -142,5 +154,5 @@ in
     };
   };
 
-  meta.maintainers = with lib.maintainers; [ erictapen bbenno ];
+  meta.maintainers = with lib.maintainers; [ erictapen bbenno joscha ];
 }
diff --git a/nixpkgs/nixos/modules/services/misc/heisenbridge.nix b/nixpkgs/nixos/modules/services/misc/heisenbridge.nix
index d07e0e420462..822a09d7cd4d 100644
--- a/nixpkgs/nixos/modules/services/misc/heisenbridge.nix
+++ b/nixpkgs/nixos/modules/services/misc/heisenbridge.nix
@@ -137,7 +137,7 @@ in
         mv -f ${registrationFile}.new ${registrationFile}
 
         # Grant Synapse access to the registration
-        if ${getBin pkgs.glibc}/bin/getent group matrix-synapse > /dev/null; then
+        if ${pkgs.getent}/bin/getent group matrix-synapse > /dev/null; then
           chgrp -v matrix-synapse ${registrationFile}
           chmod -v g+r ${registrationFile}
         fi
diff --git a/nixpkgs/nixos/modules/services/misc/homepage-dashboard.nix b/nixpkgs/nixos/modules/services/misc/homepage-dashboard.nix
new file mode 100644
index 000000000000..e68571253433
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/homepage-dashboard.nix
@@ -0,0 +1,55 @@
+{ config
+, pkgs
+, lib
+, ...
+}:
+
+let
+  cfg = config.services.homepage-dashboard;
+in
+{
+  options = {
+    services.homepage-dashboard = {
+      enable = lib.mkEnableOption (lib.mdDoc "Homepage Dashboard");
+
+      package = lib.mkPackageOptionMD pkgs "homepage-dashboard" { };
+
+      openFirewall = lib.mkOption {
+        type = lib.types.bool;
+        default = false;
+        description = lib.mdDoc "Open ports in the firewall for Homepage.";
+      };
+
+      listenPort = lib.mkOption {
+        type = lib.types.int;
+        default = 8082;
+        description = lib.mdDoc "Port for Homepage to bind to.";
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.homepage-dashboard = {
+      description = "Homepage Dashboard";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      environment = {
+        HOMEPAGE_CONFIG_DIR = "/var/lib/homepage-dashboard";
+        PORT = "${toString cfg.listenPort}";
+      };
+
+      serviceConfig = {
+        Type = "simple";
+        DynamicUser = true;
+        StateDirectory = "homepage-dashboard";
+        ExecStart = "${lib.getExe cfg.package}";
+        Restart = "on-failure";
+      };
+    };
+
+    networking.firewall = lib.mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.listenPort ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/klipper.nix b/nixpkgs/nixos/modules/services/misc/klipper.nix
index ad881d4462a3..67a217c994e4 100644
--- a/nixpkgs/nixos/modules/services/misc/klipper.nix
+++ b/nixpkgs/nixos/modules/services/misc/klipper.nix
@@ -111,8 +111,11 @@ in
           (submodule {
             options = {
               enable = mkEnableOption (lib.mdDoc ''
-                building of firmware and addition of klipper-flash tools for manual flashing.
-                This will add `klipper-flash-$mcu` scripts to your environment which can be called to flash the firmware.
+                building of firmware for manual flashing.
+              '');
+              enableKlipperFlash = mkEnableOption (lib.mdDoc ''
+                flashings scripts for firmware. This will add `klipper-flash-$mcu` scripts to your environment which can be called to flash the firmware.
+                Please check the configs at [klipper](https://github.com/Klipper3d/klipper/tree/master/config) whether your board supports flashing via `make flash`.
               '');
               serial = mkOption {
                 type = types.nullOr path;
@@ -213,11 +216,14 @@ in
       with pkgs;
       let
         default = a: b: if a != null then a else b;
-        firmwares = filterAttrs (n: v: v!= null) (mapAttrs
-          (mcu: { enable, configFile, serial }: if enable then pkgs.klipper-firmware.override {
-            mcu = lib.strings.sanitizeDerivationName mcu;
-            firmwareConfig = configFile;
-          } else null)
+        firmwares = filterAttrs (n: v: v != null) (mapAttrs
+          (mcu: { enable, enableKlipperFlash, configFile, serial }:
+            if enable then
+              pkgs.klipper-firmware.override
+                {
+                  mcu = lib.strings.sanitizeDerivationName mcu;
+                  firmwareConfig = configFile;
+                } else null)
           cfg.firmwares);
         firmwareFlasher = mapAttrsToList
           (mcu: firmware: pkgs.klipper-flash.override {
@@ -226,7 +232,7 @@ in
             flashDevice = default cfg.firmwares."${mcu}".serial cfg.settings."${mcu}".serial;
             firmwareConfig = cfg.firmwares."${mcu}".configFile;
           })
-          firmwares;
+          (filterAttrs (mcu: firmware: cfg.firmwares."${mcu}".enableKlipperFlash) firmwares);
       in
       [ klipper-genconf ] ++ firmwareFlasher ++ attrValues firmwares;
   };
diff --git a/nixpkgs/nixos/modules/services/misc/mqtt2influxdb.nix b/nixpkgs/nixos/modules/services/misc/mqtt2influxdb.nix
new file mode 100644
index 000000000000..621f51a4e7fd
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/mqtt2influxdb.nix
@@ -0,0 +1,253 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+with lib;
+
+let
+  cfg = config.services.mqtt2influxdb;
+  filterNull = filterAttrsRecursive (n: v: v != null);
+  configFile = (pkgs.formats.yaml {}).generate "mqtt2influxdb.config.yaml" (
+    filterNull {
+      inherit (cfg) mqtt influxdb;
+      points = map filterNull cfg.points;
+    }
+  );
+
+  pointType = types.submodule {
+    options = {
+      measurement = mkOption {
+        type = types.str;
+        description = mdDoc "Name of the measurement";
+      };
+      topic = mkOption {
+        type = types.str;
+        description = mdDoc "MQTT topic to subscribe to.";
+      };
+      fields = mkOption {
+        type = types.submodule {
+          options = {
+            value = mkOption {
+              type = types.str;
+              default = "$.payload";
+              description = mdDoc "Value to be picked up";
+            };
+            type = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = mdDoc "Type to be picked up";
+            };
+          };
+        };
+        description = mdDoc "Field selector.";
+      };
+      tags = mkOption {
+        type = with types; attrsOf str;
+        default = {};
+        description = mdDoc "Tags applied";
+      };
+    };
+  };
+
+  defaultPoints = [
+    {
+      measurement = "temperature";
+      topic = "node/+/thermometer/+/temperature";
+      fields.value = "$.payload";
+      tags = {
+        id = "$.topic[1]";
+        channel = "$.topic[3]";
+      };
+    }
+    {
+      measurement = "relative-humidity";
+      topic = "node/+/hygrometer/+/relative-humidity";
+      fields.value = "$.payload";
+      tags = {
+        id = "$.topic[1]";
+        channel = "$.topic[3]";
+      };
+    }
+    {
+      measurement = "illuminance";
+      topic = "node/+/lux-meter/0:0/illuminance";
+      fields.value = "$.payload";
+      tags = {
+        id = "$.topic[1]";
+      };
+    }
+    {
+      measurement = "pressure";
+      topic = "node/+/barometer/0:0/pressure";
+      fields.value = "$.payload";
+      tags = {
+        id = "$.topic[1]";
+      };
+    }
+    {
+      measurement = "co2";
+      topic = "node/+/co2-meter/-/concentration";
+      fields.value = "$.payload";
+      tags = {
+        id = "$.topic[1]";
+      };
+    }
+    {
+      measurement = "voltage";
+      topic = "node/+/battery/+/voltage";
+      fields.value = "$.payload";
+      tags = {
+        id = "$.topic[1]";
+      };
+    }
+    {
+      measurement = "button";
+      topic = "node/+/push-button/+/event-count";
+      fields.value = "$.payload";
+      tags = {
+        id = "$.topic[1]";
+        channel = "$.topic[3]";
+      };
+    }
+    {
+      measurement = "tvoc";
+      topic = "node/+/voc-lp-sensor/0:0/tvoc";
+      fields.value = "$.payload";
+      tags = {
+        id = "$.topic[1]";
+      };
+    }
+  ];
+in {
+  options = {
+    services.mqtt2influxdb = {
+      enable = mkEnableOption (mdDoc "BigClown MQTT to InfluxDB bridge.");
+      environmentFiles = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        example = [ "/run/keys/mqtt2influxdb.env" ];
+        description = mdDoc ''
+          File to load as environment file. Environment variables from this file
+          will be interpolated into the config file using envsubst with this
+          syntax: `$ENVIRONMENT` or `''${VARIABLE}`.
+          This is useful to avoid putting secrets into the nix store.
+        '';
+      };
+      mqtt = {
+        host = mkOption {
+          type = types.str;
+          default = "127.0.0.1";
+          description = mdDoc "Host where MQTT server is running.";
+        };
+        port = mkOption {
+          type = types.port;
+          default = 1883;
+          description = mdDoc "MQTT server port.";
+        };
+        username = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = mdDoc "Username used to connect to the MQTT server.";
+        };
+        password = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = mdDoc ''
+            MQTT password.
+
+            It is highly suggested to use here replacement through
+            environmentFiles as otherwise the password is put world readable to
+            the store.
+          '';
+        };
+        cafile = mkOption {
+          type = with types; nullOr path;
+          default = null;
+          description = mdDoc "Certification Authority file for MQTT";
+        };
+        certfile = mkOption {
+          type = with types; nullOr path;
+          default = null;
+          description = mdDoc "Certificate file for MQTT";
+        };
+        keyfile = mkOption {
+          type = with types; nullOr path;
+          default = null;
+          description = mdDoc "Key file for MQTT";
+        };
+      };
+      influxdb = {
+        host = mkOption {
+          type = types.str;
+          default = "127.0.0.1";
+          description = mdDoc "Host where InfluxDB server is running.";
+        };
+        port = mkOption {
+          type = types.port;
+          default = 8086;
+          description = mdDoc "InfluxDB server port";
+        };
+        database = mkOption {
+          type = types.str;
+          description = mdDoc "Name of the InfluxDB database.";
+        };
+        username = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = mdDoc "Username for InfluxDB login.";
+        };
+        password = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = mdDoc ''
+            Password for InfluxDB login.
+
+            It is highly suggested to use here replacement through
+            environmentFiles as otherwise the password is put world readable to
+            the store.
+            '';
+        };
+        ssl = mkOption {
+          type = types.bool;
+          default = false;
+          description = mdDoc "Use SSL to connect to the InfluxDB server.";
+        };
+        verify_ssl = mkOption {
+          type = types.bool;
+          default = true;
+          description = mdDoc "Verify SSL certificate when connecting to the InfluxDB server.";
+        };
+      };
+      points = mkOption {
+        type = types.listOf pointType;
+        default = defaultPoints;
+        description = mdDoc "Points to bridge from MQTT to InfluxDB.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.bigclown-mqtt2influxdb = let
+      envConfig = cfg.environmentFiles != [];
+      finalConfig = if envConfig
+        then "$RUNTIME_DIRECTORY/mqtt2influxdb.config.yaml"
+        else configFile;
+    in {
+      description = "BigClown MQTT to InfluxDB bridge";
+      wantedBy = ["multi-user.target"];
+      wants = mkIf config.services.mosquitto.enable ["mosquitto.service"];
+      preStart = ''
+        umask 077
+        ${pkgs.envsubst}/bin/envsubst -i "${configFile}" -o "${finalConfig}"
+      '';
+      serviceConfig = {
+        EnvironmentFile = cfg.environmentFiles;
+        ExecStart = "${cfg.package}/bin/mqtt2influxdb -dc ${finalConfig}";
+        RuntimeDirectory = "mqtt2influxdb";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/n8n.nix b/nixpkgs/nixos/modules/services/misc/n8n.nix
index cdfe9dc8482c..2af37fba910a 100644
--- a/nixpkgs/nixos/modules/services/misc/n8n.nix
+++ b/nixpkgs/nixos/modules/services/misc/n8n.nix
@@ -26,6 +26,15 @@ in
       '';
     };
 
+    webhookUrl = mkOption {
+      type = types.str;
+      default = "";
+      description = lib.mdDoc ''
+        WEBHOOK_URL for n8n, in case we're running behind a reverse proxy.
+        This cannot be set through configuration and must reside in an environment variable.
+      '';
+    };
+
   };
 
   config = mkIf cfg.enable {
@@ -44,6 +53,7 @@ in
         N8N_USER_FOLDER = "/var/lib/n8n";
         HOME = "/var/lib/n8n";
         N8N_CONFIG_FILES = "${configFile}";
+        WEBHOOK_URL = "${cfg.webhookUrl}";
 
         # Don't phone home
         N8N_DIAGNOSTICS_ENABLED = "false";
diff --git a/nixpkgs/nixos/modules/services/misc/nitter.nix b/nixpkgs/nixos/modules/services/misc/nitter.nix
index 9336dbe38f34..77f5459d117c 100644
--- a/nixpkgs/nixos/modules/services/misc/nitter.nix
+++ b/nixpkgs/nixos/modules/services/misc/nitter.nix
@@ -334,7 +334,8 @@ in
     systemd.services.nitter = {
         description = "Nitter (An alternative Twitter front-end)";
         wantedBy = [ "multi-user.target" ];
-        after = [ "network.target" ];
+        wants = [ "network-online.target" ];
+        after = [ "network-online.target" ];
         serviceConfig = {
           DynamicUser = true;
           StateDirectory = "nitter";
diff --git a/nixpkgs/nixos/modules/services/misc/nix-daemon.nix b/nixpkgs/nixos/modules/services/misc/nix-daemon.nix
deleted file mode 100644
index 26dbae344164..000000000000
--- a/nixpkgs/nixos/modules/services/misc/nix-daemon.nix
+++ /dev/null
@@ -1,844 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  cfg = config.nix;
-
-  nixPackage = cfg.package.out;
-
-  isNixAtLeast = versionAtLeast (getVersion nixPackage);
-
-  makeNixBuildUser = nr: {
-    name = "nixbld${toString nr}";
-    value = {
-      description = "Nix build user ${toString nr}";
-
-      /*
-        For consistency with the setgid(2), setuid(2), and setgroups(2)
-        calls in `libstore/build.cc', don't add any supplementary group
-        here except "nixbld".
-      */
-      uid = builtins.add config.ids.uids.nixbld nr;
-      isSystemUser = true;
-      group = "nixbld";
-      extraGroups = [ "nixbld" ];
-    };
-  };
-
-  nixbldUsers = listToAttrs (map makeNixBuildUser (range 1 cfg.nrBuildUsers));
-
-  nixConf =
-    assert isNixAtLeast "2.2";
-    let
-
-      mkValueString = v:
-        if v == null then ""
-        else if isInt v then toString v
-        else if isBool v then boolToString v
-        else if isFloat v then floatToString v
-        else if isList v then toString v
-        else if isDerivation v then toString v
-        else if builtins.isPath v then toString v
-        else if isString v then v
-        else if strings.isConvertibleWithToString v then toString v
-        else abort "The nix conf value: ${toPretty {} v} can not be encoded";
-
-      mkKeyValue = k: v: "${escape [ "=" ] k} = ${mkValueString v}";
-
-      mkKeyValuePairs = attrs: concatStringsSep "\n" (mapAttrsToList mkKeyValue attrs);
-
-    in
-    pkgs.writeTextFile {
-      name = "nix.conf";
-      text = ''
-        # WARNING: this file is generated from the nix.* options in
-        # your NixOS configuration, typically
-        # /etc/nixos/configuration.nix.  Do not edit it!
-        ${mkKeyValuePairs cfg.settings}
-        ${cfg.extraOptions}
-      '';
-      checkPhase = lib.optionalString cfg.checkConfig (
-        if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then ''
-          echo "Ignoring validation for cross-compilation"
-        ''
-        else ''
-          echo "Validating generated nix.conf"
-          ln -s $out ./nix.conf
-          set -e
-          set +o pipefail
-          NIX_CONF_DIR=$PWD \
-            ${cfg.package}/bin/nix show-config ${optionalString (isNixAtLeast "2.3pre") "--no-net"} \
-              ${optionalString (isNixAtLeast "2.4pre") "--option experimental-features nix-command"} \
-            |& sed -e 's/^warning:/error:/' \
-            | (! grep '${if cfg.checkAllErrors then "^error:" else "^error: unknown setting"}')
-          set -o pipefail
-        '');
-    };
-
-  legacyConfMappings = {
-    useSandbox = "sandbox";
-    buildCores = "cores";
-    maxJobs = "max-jobs";
-    sandboxPaths = "extra-sandbox-paths";
-    binaryCaches = "substituters";
-    trustedBinaryCaches = "trusted-substituters";
-    binaryCachePublicKeys = "trusted-public-keys";
-    autoOptimiseStore = "auto-optimise-store";
-    requireSignedBinaryCaches = "require-sigs";
-    trustedUsers = "trusted-users";
-    allowedUsers = "allowed-users";
-    systemFeatures = "system-features";
-  };
-
-  semanticConfType = with types;
-    let
-      confAtom = nullOr
-        (oneOf [
-          bool
-          int
-          float
-          str
-          path
-          package
-        ]) // {
-        description = "Nix config atom (null, bool, int, float, str, path or package)";
-      };
-    in
-    attrsOf (either confAtom (listOf confAtom));
-
-in
-
-{
-  imports = [
-    (mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "useChroot" ]; to = [ "nix" "useSandbox" ]; })
-    (mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "chrootDirs" ]; to = [ "nix" "sandboxPaths" ]; })
-    (mkRenamedOptionModuleWith { sinceRelease = 2205; from = [ "nix" "daemonIONiceLevel" ]; to = [ "nix" "daemonIOSchedPriority" ]; })
-    (mkRenamedOptionModuleWith { sinceRelease = 2211; from = [ "nix" "readOnlyStore" ]; to = [ "boot" "readOnlyNixStore" ]; })
-    (mkRemovedOptionModule [ "nix" "daemonNiceLevel" ] "Consider nix.daemonCPUSchedPolicy instead.")
-  ] ++ mapAttrsToList (oldConf: newConf: mkRenamedOptionModuleWith { sinceRelease = 2205; from = [ "nix" oldConf ]; to = [ "nix" "settings" newConf ]; }) legacyConfMappings;
-
-  ###### interface
-
-  options = {
-
-    nix = {
-
-      enable = mkOption {
-        type = types.bool;
-        default = true;
-        description = lib.mdDoc ''
-          Whether to enable Nix.
-          Disabling Nix makes the system hard to modify and the Nix programs and configuration will not be made available by NixOS itself.
-        '';
-      };
-
-      package = mkOption {
-        type = types.package;
-        default = pkgs.nix;
-        defaultText = literalExpression "pkgs.nix";
-        description = lib.mdDoc ''
-          This option specifies the Nix package instance to use throughout the system.
-        '';
-      };
-
-      distributedBuilds = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Whether to distribute builds to the machines listed in
-          {option}`nix.buildMachines`.
-        '';
-      };
-
-      daemonCPUSchedPolicy = mkOption {
-        type = types.enum [ "other" "batch" "idle" ];
-        default = "other";
-        example = "batch";
-        description = lib.mdDoc ''
-          Nix daemon process CPU scheduling policy. This policy propagates to
-          build processes. `other` is the default scheduling
-          policy for regular tasks. The `batch` policy is
-          similar to `other`, but optimised for
-          non-interactive tasks. `idle` is for extremely
-          low-priority tasks that should only be run when no other task
-          requires CPU time.
-
-          Please note that while using the `idle` policy may
-          greatly improve responsiveness of a system performing expensive
-          builds, it may also slow down and potentially starve crucial
-          configuration updates during load.
-
-          `idle` may therefore be a sensible policy for
-          systems that experience only intermittent phases of high CPU load,
-          such as desktop or portable computers used interactively. Other
-          systems should use the `other` or
-          `batch` policy instead.
-
-          For more fine-grained resource control, please refer to
-          {manpage}`systemd.resource-control(5)` and adjust
-          {option}`systemd.services.nix-daemon` directly.
-      '';
-      };
-
-      daemonIOSchedClass = mkOption {
-        type = types.enum [ "best-effort" "idle" ];
-        default = "best-effort";
-        example = "idle";
-        description = lib.mdDoc ''
-          Nix daemon process I/O scheduling class. This class propagates to
-          build processes. `best-effort` is the default
-          class for regular tasks. The `idle` class is for
-          extremely low-priority tasks that should only perform I/O when no
-          other task does.
-
-          Please note that while using the `idle` scheduling
-          class can improve responsiveness of a system performing expensive
-          builds, it might also slow down or starve crucial configuration
-          updates during load.
-
-          `idle` may therefore be a sensible class for
-          systems that experience only intermittent phases of high I/O load,
-          such as desktop or portable computers used interactively. Other
-          systems should use the `best-effort` class.
-      '';
-      };
-
-      daemonIOSchedPriority = mkOption {
-        type = types.int;
-        default = 4;
-        example = 1;
-        description = lib.mdDoc ''
-          Nix daemon process I/O scheduling priority. This priority propagates
-          to build processes. The supported priorities depend on the
-          scheduling policy: With idle, priorities are not used in scheduling
-          decisions. best-effort supports values in the range 0 (high) to 7
-          (low).
-        '';
-      };
-
-      buildMachines = mkOption {
-        type = types.listOf (types.submodule {
-          options = {
-            hostName = mkOption {
-              type = types.str;
-              example = "nixbuilder.example.org";
-              description = lib.mdDoc ''
-                The hostname of the build machine.
-              '';
-            };
-            protocol = mkOption {
-              type = types.enum [ null "ssh" "ssh-ng" ];
-              default = "ssh";
-              example = "ssh-ng";
-              description = lib.mdDoc ''
-                The protocol used for communicating with the build machine.
-                Use `ssh-ng` if your remote builder and your
-                local Nix version support that improved protocol.
-
-                Use `null` when trying to change the special localhost builder
-                without a protocol which is for example used by hydra.
-              '';
-            };
-            system = mkOption {
-              type = types.nullOr types.str;
-              default = null;
-              example = "x86_64-linux";
-              description = lib.mdDoc ''
-                The system type the build machine can execute derivations on.
-                Either this attribute or {var}`systems` must be
-                present, where {var}`system` takes precedence if
-                both are set.
-              '';
-            };
-            systems = mkOption {
-              type = types.listOf types.str;
-              default = [ ];
-              example = [ "x86_64-linux" "aarch64-linux" ];
-              description = lib.mdDoc ''
-                The system types the build machine can execute derivations on.
-                Either this attribute or {var}`system` must be
-                present, where {var}`system` takes precedence if
-                both are set.
-              '';
-            };
-            sshUser = mkOption {
-              type = types.nullOr types.str;
-              default = null;
-              example = "builder";
-              description = lib.mdDoc ''
-                The username to log in as on the remote host. This user must be
-                able to log in and run nix commands non-interactively. It must
-                also be privileged to build derivations, so must be included in
-                {option}`nix.settings.trusted-users`.
-              '';
-            };
-            sshKey = mkOption {
-              type = types.nullOr types.str;
-              default = null;
-              example = "/root/.ssh/id_buildhost_builduser";
-              description = lib.mdDoc ''
-                The path to the SSH private key with which to authenticate on
-                the build machine. The private key must not have a passphrase.
-                If null, the building user (root on NixOS machines) must have an
-                appropriate ssh configuration to log in non-interactively.
-
-                Note that for security reasons, this path must point to a file
-                in the local filesystem, *not* to the nix store.
-              '';
-            };
-            maxJobs = mkOption {
-              type = types.int;
-              default = 1;
-              description = lib.mdDoc ''
-                The number of concurrent jobs the build machine supports. The
-                build machine will enforce its own limits, but this allows hydra
-                to schedule better since there is no work-stealing between build
-                machines.
-              '';
-            };
-            speedFactor = mkOption {
-              type = types.int;
-              default = 1;
-              description = lib.mdDoc ''
-                The relative speed of this builder. This is an arbitrary integer
-                that indicates the speed of this builder, relative to other
-                builders. Higher is faster.
-              '';
-            };
-            mandatoryFeatures = mkOption {
-              type = types.listOf types.str;
-              default = [ ];
-              example = [ "big-parallel" ];
-              description = lib.mdDoc ''
-                A list of features mandatory for this builder. The builder will
-                be ignored for derivations that don't require all features in
-                this list. All mandatory features are automatically included in
-                {var}`supportedFeatures`.
-              '';
-            };
-            supportedFeatures = mkOption {
-              type = types.listOf types.str;
-              default = [ ];
-              example = [ "kvm" "big-parallel" ];
-              description = lib.mdDoc ''
-                A list of features supported by this builder. The builder will
-                be ignored for derivations that require features not in this
-                list.
-              '';
-            };
-            publicHostKey = mkOption {
-              type = types.nullOr types.str;
-              default = null;
-              description = lib.mdDoc ''
-                The (base64-encoded) public host key of this builder. The field
-                is calculated via {command}`base64 -w0 /etc/ssh/ssh_host_type_key.pub`.
-                If null, SSH will use its regular known-hosts file when connecting.
-              '';
-            };
-          };
-        });
-        default = [ ];
-        description = lib.mdDoc ''
-          This option lists the machines to be used if distributed builds are
-          enabled (see {option}`nix.distributedBuilds`).
-          Nix will perform derivations on those machines via SSH by copying the
-          inputs to the Nix store on the remote machine, starting the build,
-          then copying the output back to the local Nix store.
-        '';
-      };
-
-      # Environment variables for running Nix.
-      envVars = mkOption {
-        type = types.attrs;
-        internal = true;
-        default = { };
-        description = lib.mdDoc "Environment variables used by Nix.";
-      };
-
-      nrBuildUsers = mkOption {
-        type = types.int;
-        description = lib.mdDoc ''
-          Number of `nixbld` user accounts created to
-          perform secure concurrent builds.  If you receive an error
-          message saying that “all build users are currently in use”,
-          you should increase this value.
-        '';
-      };
-
-      nixPath = mkOption {
-        type = types.listOf types.str;
-        default = [
-          "nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
-          "nixos-config=/etc/nixos/configuration.nix"
-          "/nix/var/nix/profiles/per-user/root/channels"
-        ];
-        description = lib.mdDoc ''
-          The default Nix expression search path, used by the Nix
-          evaluator to look up paths enclosed in angle brackets
-          (e.g. `<nixpkgs>`).
-        '';
-      };
-
-      checkConfig = mkOption {
-        type = types.bool;
-        default = true;
-        description = lib.mdDoc ''
-          If enabled, checks that Nix can parse the generated nix.conf.
-        '';
-      };
-
-      checkAllErrors = mkOption {
-        type = types.bool;
-        default = true;
-        description = lib.mdDoc ''
-          If enabled, checks the nix.conf parsing for any kind of error. When disabled, checks only for unknown settings.
-        '';
-      };
-
-      registry = mkOption {
-        type = types.attrsOf (types.submodule (
-          let
-            referenceAttrs = with types; attrsOf (oneOf [
-              str
-              int
-              bool
-              path
-              package
-            ]);
-          in
-          { config, name, ... }:
-          {
-            options = {
-              from = mkOption {
-                type = referenceAttrs;
-                example = { type = "indirect"; id = "nixpkgs"; };
-                description = lib.mdDoc "The flake reference to be rewritten.";
-              };
-              to = mkOption {
-                type = referenceAttrs;
-                example = { type = "github"; owner = "my-org"; repo = "my-nixpkgs"; };
-                description = lib.mdDoc "The flake reference {option}`from` is rewritten to.";
-              };
-              flake = mkOption {
-                type = types.nullOr types.attrs;
-                default = null;
-                example = literalExpression "nixpkgs";
-                description = lib.mdDoc ''
-                  The flake input {option}`from` is rewritten to.
-                '';
-              };
-              exact = mkOption {
-                type = types.bool;
-                default = true;
-                description = lib.mdDoc ''
-                  Whether the {option}`from` reference needs to match exactly. If set,
-                  a {option}`from` reference like `nixpkgs` does not
-                  match with a reference like `nixpkgs/nixos-20.03`.
-                '';
-              };
-            };
-            config = {
-              from = mkDefault { type = "indirect"; id = name; };
-              to = mkIf (config.flake != null) (mkDefault (
-                {
-                  type = "path";
-                  path = config.flake.outPath;
-                } // filterAttrs
-                  (n: _: n == "lastModified" || n == "rev" || n == "revCount" || n == "narHash")
-                  config.flake
-              ));
-            };
-          }
-        ));
-        default = { };
-        description = lib.mdDoc ''
-          A system-wide flake registry.
-        '';
-      };
-
-      extraOptions = mkOption {
-        type = types.lines;
-        default = "";
-        example = ''
-          keep-outputs = true
-          keep-derivations = true
-        '';
-        description = lib.mdDoc "Additional text appended to {file}`nix.conf`.";
-      };
-
-      settings = mkOption {
-        type = types.submodule {
-          freeformType = semanticConfType;
-
-          options = {
-            max-jobs = mkOption {
-              type = types.either types.int (types.enum [ "auto" ]);
-              default = "auto";
-              example = 64;
-              description = lib.mdDoc ''
-                This option defines the maximum number of jobs that Nix will try to
-                build in parallel. The default is auto, which means it will use all
-                available logical cores. It is recommend to set it to the total
-                number of logical cores in your system (e.g., 16 for two CPUs with 4
-                cores each and hyper-threading).
-              '';
-            };
-
-            auto-optimise-store = mkOption {
-              type = types.bool;
-              default = false;
-              example = true;
-              description = lib.mdDoc ''
-                If set to true, Nix automatically detects files in the store that have
-                identical contents, and replaces them with hard links to a single copy.
-                This saves disk space. If set to false (the default), you can still run
-                nix-store --optimise to get rid of duplicate files.
-              '';
-            };
-
-            cores = mkOption {
-              type = types.int;
-              default = 0;
-              example = 64;
-              description = lib.mdDoc ''
-                This option defines the maximum number of concurrent tasks during
-                one build. It affects, e.g., -j option for make.
-                The special value 0 means that the builder should use all
-                available CPU cores in the system. Some builds may become
-                non-deterministic with this option; use with care! Packages will
-                only be affected if enableParallelBuilding is set for them.
-              '';
-            };
-
-            sandbox = mkOption {
-              type = types.either types.bool (types.enum [ "relaxed" ]);
-              default = true;
-              description = lib.mdDoc ''
-                If set, Nix will perform builds in a sandboxed environment that it
-                will set up automatically for each build. This prevents impurities
-                in builds by disallowing access to dependencies outside of the Nix
-                store by using network and mount namespaces in a chroot environment.
-
-                This is enabled by default even though it has a possible performance
-                impact due to the initial setup time of a sandbox for each build. It
-                doesn't affect derivation hashes, so changing this option will not
-                trigger a rebuild of packages.
-
-                When set to "relaxed", this option permits derivations that set
-                `__noChroot = true;` to run outside of the sandboxed environment.
-                Exercise caution when using this mode of operation! It is intended to
-                be a quick hack when building with packages that are not easily setup
-                to be built reproducibly.
-              '';
-            };
-
-            extra-sandbox-paths = mkOption {
-              type = types.listOf types.str;
-              default = [ ];
-              example = [ "/dev" "/proc" ];
-              description = lib.mdDoc ''
-                Directories from the host filesystem to be included
-                in the sandbox.
-              '';
-            };
-
-            substituters = mkOption {
-              type = types.listOf types.str;
-              description = lib.mdDoc ''
-                List of binary cache URLs used to obtain pre-built binaries
-                of Nix packages.
-
-                By default https://cache.nixos.org/ is added.
-              '';
-            };
-
-            trusted-substituters = mkOption {
-              type = types.listOf types.str;
-              default = [ ];
-              example = [ "https://hydra.nixos.org/" ];
-              description = lib.mdDoc ''
-                List of binary cache URLs that non-root users can use (in
-                addition to those specified using
-                {option}`nix.settings.substituters`) by passing
-                `--option binary-caches` to Nix commands.
-              '';
-            };
-
-            require-sigs = mkOption {
-              type = types.bool;
-              default = true;
-              description = lib.mdDoc ''
-                If enabled (the default), Nix will only download binaries from binary caches if
-                they are cryptographically signed with any of the keys listed in
-                {option}`nix.settings.trusted-public-keys`. If disabled, signatures are neither
-                required nor checked, so it's strongly recommended that you use only
-                trustworthy caches and https to prevent man-in-the-middle attacks.
-              '';
-            };
-
-            trusted-public-keys = mkOption {
-              type = types.listOf types.str;
-              example = [ "hydra.nixos.org-1:CNHJZBh9K4tP3EKF6FkkgeVYsS3ohTl+oS0Qa8bezVs=" ];
-              description = lib.mdDoc ''
-                List of public keys used to sign binary caches. If
-                {option}`nix.settings.trusted-public-keys` is enabled,
-                then Nix will use a binary from a binary cache if and only
-                if it is signed by *any* of the keys
-                listed here. By default, only the key for
-                `cache.nixos.org` is included.
-              '';
-            };
-
-            trusted-users = mkOption {
-              type = types.listOf types.str;
-              default = [ "root" ];
-              example = [ "root" "alice" "@wheel" ];
-              description = lib.mdDoc ''
-                A list of names of users that have additional rights when
-                connecting to the Nix daemon, such as the ability to specify
-                additional binary caches, or to import unsigned NARs. You
-                can also specify groups by prefixing them with
-                `@`; for instance,
-                `@wheel` means all users in the wheel
-                group.
-              '';
-            };
-
-            system-features = mkOption {
-              type = types.listOf types.str;
-              example = [ "kvm" "big-parallel" "gccarch-skylake" ];
-              description = lib.mdDoc ''
-                The set of features supported by the machine. Derivations
-                can express dependencies on system features through the
-                `requiredSystemFeatures` attribute.
-
-                By default, pseudo-features `nixos-test`, `benchmark`,
-                and `big-parallel` used in Nixpkgs are set, `kvm`
-                is also included if it is available.
-              '';
-            };
-
-            allowed-users = mkOption {
-              type = types.listOf types.str;
-              default = [ "*" ];
-              example = [ "@wheel" "@builders" "alice" "bob" ];
-              description = lib.mdDoc ''
-                A list of names of users (separated by whitespace) that are
-                allowed to connect to the Nix daemon. As with
-                {option}`nix.settings.trusted-users`, you can specify groups by
-                prefixing them with `@`. Also, you can
-                allow all users by specifying `*`. The
-                default is `*`. Note that trusted users are
-                always allowed to connect.
-              '';
-            };
-          };
-        };
-        default = { };
-        example = literalExpression ''
-          {
-            use-sandbox = true;
-            show-trace = true;
-
-            system-features = [ "big-parallel" "kvm" "recursive-nix" ];
-            sandbox-paths = { "/bin/sh" = "''${pkgs.busybox-sandbox-shell.out}/bin/busybox"; };
-          }
-        '';
-        description = lib.mdDoc ''
-          Configuration for Nix, see
-          <https://nixos.org/manual/nix/stable/#sec-conf-file> or
-          {manpage}`nix.conf(5)` for available options.
-          The value declared here will be translated directly to the key-value pairs Nix expects.
-
-          You can use {command}`nix-instantiate --eval --strict '<nixpkgs/nixos>' -A config.nix.settings`
-          to view the current value. By default it is empty.
-
-          Nix configurations defined under {option}`nix.*` will be translated and applied to this
-          option. In addition, configuration specified in {option}`nix.extraOptions` which will be appended
-          verbatim to the resulting config file.
-        '';
-      };
-    };
-  };
-
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-    environment.systemPackages =
-      [
-        nixPackage
-        pkgs.nix-info
-      ]
-      ++ optional (config.programs.bash.enableCompletion) pkgs.nix-bash-completions;
-
-    environment.etc."nix/nix.conf".source = nixConf;
-
-    environment.etc."nix/registry.json".text = builtins.toJSON {
-      version = 2;
-      flakes = mapAttrsToList (n: v: { inherit (v) from to exact; }) cfg.registry;
-    };
-
-    # List of machines for distributed Nix builds in the format
-    # expected by build-remote.pl.
-    environment.etc."nix/machines" = mkIf (cfg.buildMachines != [ ]) {
-      text =
-        concatMapStrings
-          (machine:
-            (concatStringsSep " " ([
-              "${optionalString (machine.protocol != null) "${machine.protocol}://"}${optionalString (machine.sshUser != null) "${machine.sshUser}@"}${machine.hostName}"
-              (if machine.system != null then machine.system else if machine.systems != [ ] then concatStringsSep "," machine.systems else "-")
-              (if machine.sshKey != null then machine.sshKey else "-")
-              (toString machine.maxJobs)
-              (toString machine.speedFactor)
-              (let res = (machine.supportedFeatures ++ machine.mandatoryFeatures);
-               in if (res == []) then "-" else (concatStringsSep "," res))
-              (let res = machine.mandatoryFeatures;
-               in if (res == []) then "-" else (concatStringsSep "," machine.mandatoryFeatures))
-            ]
-            ++ optional (isNixAtLeast "2.4pre") (if machine.publicHostKey != null then machine.publicHostKey else "-")))
-            + "\n"
-          )
-          cfg.buildMachines;
-    };
-
-    assertions =
-      let badMachine = m: m.system == null && m.systems == [ ];
-      in
-      [
-        {
-          assertion = !(any badMachine cfg.buildMachines);
-          message = ''
-            At least one system type (via <varname>system</varname> or
-              <varname>systems</varname>) must be set for every build machine.
-              Invalid machine specifications:
-          '' + "      " +
-          (concatStringsSep "\n      "
-            (map (m: m.hostName)
-              (filter (badMachine) cfg.buildMachines)));
-        }
-      ];
-
-    systemd.packages = [ nixPackage ];
-
-    # Will only work once https://github.com/NixOS/nix/pull/6285 is merged
-    # systemd.tmpfiles.packages = [ nixPackage ];
-
-    # Can be dropped for Nix > https://github.com/NixOS/nix/pull/6285
-    systemd.tmpfiles.rules = [
-      "d /nix/var/nix/daemon-socket 0755 root root - -"
-    ];
-
-    systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ];
-
-    systemd.services.nix-daemon =
-      {
-        path = [ nixPackage pkgs.util-linux config.programs.ssh.package ]
-          ++ optionals cfg.distributedBuilds [ pkgs.gzip ];
-
-        environment = cfg.envVars
-          // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"; }
-          // config.networking.proxy.envVars;
-
-        unitConfig.RequiresMountsFor = "/nix/store";
-
-        serviceConfig =
-          {
-            CPUSchedulingPolicy = cfg.daemonCPUSchedPolicy;
-            IOSchedulingClass = cfg.daemonIOSchedClass;
-            IOSchedulingPriority = cfg.daemonIOSchedPriority;
-            LimitNOFILE = 1048576;
-          };
-
-        restartTriggers = [ nixConf ];
-
-        # `stopIfChanged = false` changes to switch behavior
-        # from   stop -> update units -> start
-        #   to   update units -> restart
-        #
-        # The `stopIfChanged` setting therefore controls a trade-off between a
-        # more predictable lifecycle, which runs the correct "version" of
-        # the `ExecStop` line, and on the other hand the availability of
-        # sockets during the switch, as the effectiveness of the stop operation
-        # depends on the socket being stopped as well.
-        #
-        # As `nix-daemon.service` does not make use of `ExecStop`, we prefer
-        # to keep the socket up and available. This is important for machines
-        # that run Nix-based services, such as automated build, test, and deploy
-        # services, that expect the daemon socket to be available at all times.
-        #
-        # Notably, the Nix client does not retry on failure to connect to the
-        # daemon socket, and the in-process RemoteStore instance will disable
-        # itself. This makes retries infeasible even for services that are
-        # aware of the issue. Failure to connect can affect not only new client
-        # processes, but also new RemoteStore instances in existing processes,
-        # as well as existing RemoteStore instances that have not saturated
-        # their connection pool.
-        #
-        # Also note that `stopIfChanged = true` does not kill existing
-        # connection handling daemons, as one might wish to happen before a
-        # breaking Nix upgrade (which is rare). The daemon forks that handle
-        # the individual connections split off into their own sessions, causing
-        # them not to be stopped by systemd.
-        # If a Nix upgrade does require all existing daemon processes to stop,
-        # nix-daemon must do so on its own accord, and only when the new version
-        # starts and detects that Nix's persistent state needs an upgrade.
-        stopIfChanged = false;
-
-      };
-
-    # Set up the environment variables for running Nix.
-    environment.sessionVariables = cfg.envVars // { NIX_PATH = cfg.nixPath; };
-
-    environment.extraInit =
-      ''
-        if [ -e "$HOME/.nix-defexpr/channels" ]; then
-          export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
-        fi
-      '';
-
-    nix.nrBuildUsers = mkDefault (
-      if cfg.settings.auto-allocate-uids or false then 0
-      else max 32 (if cfg.settings.max-jobs == "auto" then 0 else cfg.settings.max-jobs)
-    );
-
-    users.users = nixbldUsers;
-
-    services.xserver.displayManager.hiddenUsers = attrNames nixbldUsers;
-
-    system.activationScripts.nix = stringAfter [ "etc" "users" ]
-      ''
-        install -m 0755 -d /nix/var/nix/{gcroots,profiles}/per-user
-
-        # Subscribe the root user to the NixOS channel by default.
-        if [ ! -e "/root/.nix-channels" ]; then
-            echo "${config.system.defaultChannel} nixos" > "/root/.nix-channels"
-        fi
-      '';
-
-    # Legacy configuration conversion.
-    nix.settings = mkMerge [
-      {
-        trusted-public-keys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ];
-        substituters = mkAfter [ "https://cache.nixos.org/" ];
-
-        system-features = mkDefault (
-          [ "nixos-test" "benchmark" "big-parallel" "kvm" ] ++
-          optionals (pkgs.stdenv.hostPlatform ? gcc.arch) (
-            # a builder can run code for `gcc.arch` and inferior architectures
-            [ "gccarch-${pkgs.stdenv.hostPlatform.gcc.arch}" ] ++
-            map (x: "gccarch-${x}") (systems.architectures.inferiors.${pkgs.stdenv.hostPlatform.gcc.arch} or [])
-          )
-        );
-      }
-
-      (mkIf (!cfg.distributedBuilds) { builders = null; })
-
-      (mkIf (isNixAtLeast "2.3pre") { sandbox-fallback = false; })
-    ];
-
-  };
-
-}
diff --git a/nixpkgs/nixos/modules/services/misc/nix-optimise.nix b/nixpkgs/nixos/modules/services/misc/nix-optimise.nix
index db8148c060e7..0398229a13da 100644
--- a/nixpkgs/nixos/modules/services/misc/nix-optimise.nix
+++ b/nixpkgs/nixos/modules/services/misc/nix-optimise.nix
@@ -1,28 +1,21 @@
 { config, lib, ... }:
 
-with lib;
-
 let
   cfg = config.nix.optimise;
 in
 
 {
-
-  ###### interface
-
   options = {
-
     nix.optimise = {
-
-      automatic = mkOption {
+      automatic = lib.mkOption {
         default = false;
-        type = types.bool;
+        type = lib.types.bool;
         description = lib.mdDoc "Automatically run the nix store optimiser at a specific time.";
       };
 
-      dates = mkOption {
+      dates = lib.mkOption {
         default = ["03:45"];
-        type = types.listOf types.str;
+        type = with lib.types; listOf str;
         description = lib.mdDoc ''
           Specification (in the format described by
           {manpage}`systemd.time(7)`) of the time at
@@ -32,9 +25,6 @@ in
     };
   };
 
-
-  ###### implementation
-
   config = {
     assertions = [
       {
@@ -43,14 +33,19 @@ in
       }
     ];
 
-    systemd.services.nix-optimise = lib.mkIf config.nix.enable
-      { description = "Nix Store Optimiser";
+    systemd = lib.mkIf config.nix.enable {
+      services.nix-optimise = {
+        description = "Nix Store Optimiser";
         # No point this if the nix daemon (and thus the nix store) is outside
         unitConfig.ConditionPathIsReadWrite = "/nix/var/nix/daemon-socket";
         serviceConfig.ExecStart = "${config.nix.package}/bin/nix-store --optimise";
-        startAt = optionals cfg.automatic cfg.dates;
+        startAt = lib.optionals cfg.automatic cfg.dates;
       };
 
+      timers.nix-optimise.timerConfig = {
+        Persistent = true;
+        RandomizedDelaySec = 1800;
+      };
+    };
   };
-
 }
diff --git a/nixpkgs/nixos/modules/services/misc/ntfy-sh.nix b/nixpkgs/nixos/modules/services/misc/ntfy-sh.nix
index 3258f91f7044..8fc1df93afb1 100644
--- a/nixpkgs/nixos/modules/services/misc/ntfy-sh.nix
+++ b/nixpkgs/nixos/modules/services/misc/ntfy-sh.nix
@@ -32,7 +32,25 @@ in
     };
 
     settings = mkOption {
-      type = types.submodule { freeformType = settingsFormat.type; };
+      type = types.submodule {
+        freeformType = settingsFormat.type;
+        options = {
+          base-url = mkOption {
+            type = types.str;
+            example = "https://ntfy.example";
+            description = lib.mdDoc ''
+              Public facing base URL of the service
+
+              This setting is required for any of the following features:
+              - attachments (to return a download URL)
+              - e-mail sending (for the topic URL in the email footer)
+              - iOS push notifications for self-hosted servers
+                (to calculate the Firebase poll_request topic)
+              - Matrix Push Gateway (to validate that the pushkey is correct)
+            '';
+          };
+        };
+      };
 
       default = { };
 
diff --git a/nixpkgs/nixos/modules/services/misc/paperless.nix b/nixpkgs/nixos/modules/services/misc/paperless.nix
index 4199e7713304..0683a1f922ab 100644
--- a/nixpkgs/nixos/modules/services/misc/paperless.nix
+++ b/nixpkgs/nixos/modules/services/misc/paperless.nix
@@ -7,6 +7,7 @@ let
 
   defaultUser = "paperless";
   nltkDir = "/var/cache/paperless/nltk";
+  defaultFont = "${pkgs.liberation_ttf}/share/fonts/truetype/LiberationSerif-Regular.ttf";
 
   # Don't start a redis instance if the user sets a custom redis connection
   enableRedis = !hasAttr "PAPERLESS_REDIS" cfg.extraConfig;
@@ -17,6 +18,7 @@ let
     PAPERLESS_MEDIA_ROOT = cfg.mediaDir;
     PAPERLESS_CONSUMPTION_DIR = cfg.consumptionDir;
     PAPERLESS_NLTK_DIR = nltkDir;
+    PAPERLESS_THUMBNAIL_FONT_NAME = defaultFont;
     GUNICORN_CMD_ARGS = "--bind=${cfg.address}:${toString cfg.port}";
   } // optionalAttrs (config.time.timeZone != null) {
     PAPERLESS_TIME_ZONE = config.time.timeZone;
@@ -26,14 +28,11 @@ let
     lib.mapAttrs (_: toString) cfg.extraConfig
   );
 
-  manage =
-    let
-      setupEnv = lib.concatStringsSep "\n" (mapAttrsToList (name: val: "export ${name}=\"${val}\"") env);
-    in
-    pkgs.writeShellScript "manage" ''
-      ${setupEnv}
-      exec ${pkg}/bin/paperless-ngx "$@"
-    '';
+  manage = pkgs.writeShellScript "manage" ''
+    set -o allexport # Export the following env vars
+    ${lib.toShellVars env}
+    exec ${pkg}/bin/paperless-ngx "$@"
+  '';
 
   # Secure the services
   defaultServiceConfig = {
@@ -86,12 +85,11 @@ let
     SupplementaryGroups = optional enableRedis redisServer.user;
     SystemCallArchitectures = "native";
     SystemCallFilter = [ "@system-service" "~@privileged @setuid @keyring" ];
-    # Does not work well with the temporary root
-    #UMask = "0066";
+    UMask = "0066";
   };
 in
 {
-  meta.maintainers = with maintainers; [ erikarvstedt Flakebi ];
+  meta.maintainers = with maintainers; [ erikarvstedt Flakebi leona ];
 
   imports = [
     (mkRenamedOptionModule [ "services" "paperless-ng" ] [ "services" "paperless" ])
@@ -173,19 +171,32 @@ in
       description = lib.mdDoc "Web interface port.";
     };
 
+    # FIXME this should become an RFC42-style settings attr
     extraConfig = mkOption {
       type = types.attrs;
       default = { };
       description = lib.mdDoc ''
         Extra paperless config options.
 
-        See [the documentation](https://paperless-ngx.readthedocs.io/en/latest/configuration.html)
+        See [the documentation](https://docs.paperless-ngx.com/configuration/)
         for available options.
+
+        Note that some options such as `PAPERLESS_CONSUMER_IGNORE_PATTERN` expect JSON values. Use `builtins.toJSON` to ensure proper quoting.
+      '';
+      example = literalExpression ''
+        {
+          PAPERLESS_OCR_LANGUAGE = "deu+eng";
+
+          PAPERLESS_DBHOST = "/run/postgresql";
+
+          PAPERLESS_CONSUMER_IGNORE_PATTERN = builtins.toJSON [ ".DS_STORE/*" "desktop.ini" ];
+
+          PAPERLESS_OCR_USER_ARGS = builtins.toJSON {
+            optimize = 1;
+            pdfa_image_compression = "lossless";
+          };
+        };
       '';
-      example = {
-        PAPERLESS_OCR_LANGUAGE = "deu+eng";
-        PAPERLESS_DBHOST = "/run/postgresql";
-      };
     };
 
     user = mkOption {
diff --git a/nixpkgs/nixos/modules/services/misc/prowlarr.nix b/nixpkgs/nixos/modules/services/misc/prowlarr.nix
index 77b8ec989479..836280d3e5fe 100644
--- a/nixpkgs/nixos/modules/services/misc/prowlarr.nix
+++ b/nixpkgs/nixos/modules/services/misc/prowlarr.nix
@@ -11,6 +11,8 @@ in
     services.prowlarr = {
       enable = mkEnableOption (lib.mdDoc "Prowlarr");
 
+      package = mkPackageOptionMD pkgs "prowlarr" { };
+
       openFirewall = mkOption {
         type = types.bool;
         default = false;
@@ -29,7 +31,7 @@ in
         Type = "simple";
         DynamicUser = true;
         StateDirectory = "prowlarr";
-        ExecStart = "${pkgs.prowlarr}/bin/Prowlarr -nobrowser -data=/var/lib/prowlarr";
+        ExecStart = "${lib.getExe cfg.package} -nobrowser -data=/var/lib/prowlarr";
         Restart = "on-failure";
       };
     };
diff --git a/nixpkgs/nixos/modules/services/misc/sourcehut/default.nix b/nixpkgs/nixos/modules/services/misc/sourcehut/default.nix
index d4391bc49e31..580a009a0ad3 100644
--- a/nixpkgs/nixos/modules/services/misc/sourcehut/default.nix
+++ b/nixpkgs/nixos/modules/services/misc/sourcehut/default.nix
@@ -8,13 +8,13 @@ let
   settingsFormat = pkgs.formats.ini {
     listToValue = concatMapStringsSep "," (generators.mkValueStringDefault {});
     mkKeyValue = k: v:
-      if v == null then ""
-      else generators.mkKeyValueDefault {
+      optionalString (v != null)
+      (generators.mkKeyValueDefault {
         mkValueString = v:
           if v == true then "yes"
           else if v == false then "no"
           else generators.mkValueStringDefault {} v;
-      } "=" k v;
+      } "=" k v);
   };
   configIniOfService = srv: settingsFormat.generate "sourcehut-${srv}-config.ini"
     # Each service needs access to only a subset of sections (and secrets).
diff --git a/nixpkgs/nixos/modules/services/misc/sssd.nix b/nixpkgs/nixos/modules/services/misc/sssd.nix
index 7c7a3b464a83..f83c82bbb7d7 100644
--- a/nixpkgs/nixos/modules/services/misc/sssd.nix
+++ b/nixpkgs/nixos/modules/services/misc/sssd.nix
@@ -135,7 +135,7 @@ in {
           ExecStart = "${pkgs.sssd}/libexec/sssd/sssd_kcm --uid 0 --gid 0";
         };
         restartTriggers = [
-          config.environment.etc."sssd/sssd.conf".source
+          settingsFileUnsubstituted
         ];
       };
       systemd.sockets.sssd-kcm = {
diff --git a/nixpkgs/nixos/modules/services/misc/zoneminder.nix b/nixpkgs/nixos/modules/services/misc/zoneminder.nix
index 11722979851c..616a60a123ea 100644
--- a/nixpkgs/nixos/modules/services/misc/zoneminder.nix
+++ b/nixpkgs/nixos/modules/services/misc/zoneminder.nix
@@ -351,7 +351,7 @@ in {
           CacheDirectory = dirs cacheDirs;
           RuntimeDirectory = dirName;
           ReadWriteDirectories = lib.mkIf useCustomDir [ cfg.storageDir ];
-          StateDirectory = dirs (if useCustomDir then [] else libDirs);
+          StateDirectory = dirs (lib.optional (!useCustomDir) libDirs);
           LogsDirectory = dirName;
           PrivateTmp = true;
           ProtectSystem = "strict";
diff --git a/nixpkgs/nixos/modules/services/monitoring/below.nix b/nixpkgs/nixos/modules/services/monitoring/below.nix
new file mode 100644
index 000000000000..92ee3882cac8
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/below.nix
@@ -0,0 +1,106 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.below;
+  cfgContents = concatStringsSep "\n" (
+    mapAttrsToList (n: v: ''${n} = "${v}"'') (filterAttrs (_k: v: v != null) {
+      log_dir = cfg.dirs.log;
+      store_dir = cfg.dirs.store;
+      cgroup_filter_out = cfg.cgroupFilterOut;
+    })
+  );
+
+  mkDisableOption = n: mkOption {
+    type = types.bool;
+    default = true;
+    description = mdDoc "Whether to enable ${n}.";
+  };
+  optionalType = ty: x: mkOption (x // {
+    description = mdDoc x.description;
+    type = (types.nullOr ty);
+    default = null;
+  });
+  optionalPath = optionalType types.path;
+  optionalStr = optionalType types.str;
+  optionalInt = optionalType types.int;
+in {
+  options = {
+    services.below = {
+      enable = mkEnableOption (mdDoc "'below' resource monitor");
+
+      cgroupFilterOut = optionalStr {
+        description = "A regexp matching the full paths of cgroups whose data shouldn't be collected";
+        example = "user.slice.*";
+      };
+      collect = {
+        diskStats = mkDisableOption "dist_stat collection";
+        ioStats   = mkEnableOption (mdDoc "io.stat collection for cgroups");
+        exitStats = mkDisableOption "eBPF-based exitstats";
+      };
+      compression.enable = mkEnableOption (mdDoc "data compression");
+      retention = {
+        size = optionalInt {
+          description = ''
+            Size limit for below's data, in bytes. Data is deleted oldest-first, in 24h 'shards'.
+
+            ::: {.note}
+            The size limit may be exceeded by at most the size of the active shard, as:
+            - the active shard cannot be deleted;
+            - the size limit is only enforced when a new shard is created.
+            :::
+          '';
+        };
+        time = optionalInt {
+          description = ''
+            Retention time, in seconds.
+
+            ::: {.note}
+            As data is stored in 24 hour shards which are discarded as a whole,
+            only data expired by 24h (or more) is guaranteed to be discarded.
+            :::
+
+            ::: {.note}
+            If `retention.size` is set, data may be discarded earlier than the specified time.
+            :::
+          '';
+        };
+      };
+      dirs = {
+        log = optionalPath { description = "Where to store below's logs"; };
+        store = optionalPath {
+          description = "Where to store below's data";
+          example = "/var/lib/below";
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.below ];
+    # /etc/below.conf is also refered to by the `below` CLI tool,
+    #  so this can't be a store-only file whose path is passed to the service
+    environment.etc."below/below.conf".text = cfgContents;
+
+    systemd = {
+      packages = [ pkgs.below ];
+      services.below = {
+        # Workaround for https://github.com/NixOS/nixpkgs/issues/81138
+        wantedBy = [ "multi-user.target" ];
+        restartTriggers = [ cfgContents ];
+
+        serviceConfig.ExecStart = [
+          ""
+          ("${lib.getExe pkgs.below} record " + (concatStringsSep " " (
+            optional (!cfg.collect.diskStats) "--disable-disk-stat" ++
+            optional   cfg.collect.ioStats    "--collect-io-stat"   ++
+            optional (!cfg.collect.exitStats) "--disable-exitstats" ++
+            optional   cfg.compression.enable "--compress"          ++
+
+            optional (cfg.retention.size != null) "--store-size-limit ${toString cfg.retention.size}" ++
+            optional (cfg.retention.time != null) "--retain-for-s ${toString cfg.retention.time}"
+          )))
+        ];
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/grafana.nix b/nixpkgs/nixos/modules/services/monitoring/grafana.nix
index adffadf3fc47..571b9a3aeebd 100644
--- a/nixpkgs/nixos/modules/services/monitoring/grafana.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/grafana.nix
@@ -5,25 +5,44 @@ with lib;
 let
   cfg = config.services.grafana;
   opt = options.services.grafana;
-  provisioningSettingsFormat = pkgs.formats.yaml {};
+  provisioningSettingsFormat = pkgs.formats.yaml { };
   declarativePlugins = pkgs.linkFarm "grafana-plugins" (builtins.map (pkg: { name = pkg.pname; path = pkg; }) cfg.declarativePlugins);
   useMysql = cfg.settings.database.type == "mysql";
   usePostgresql = cfg.settings.database.type == "postgres";
 
-  settingsFormatIni = pkgs.formats.ini {};
+  # Prefer using the values from the default config file[0] directly. This way,
+  # people reading the NixOS manual can see them without cross-referencing the
+  # official documentation.
+  #
+  # However, if there is no default entry or if the setting is optional, use
+  # `null` as the default value. It will be turned into the empty string.
+  #
+  # If a setting is a list, always allow setting it as a plain string as well.
+  #
+  # [0]: https://github.com/grafana/grafana/blob/main/conf/defaults.ini
+  settingsFormatIni = pkgs.formats.ini {
+    listToValue = concatMapStringsSep " " (generators.mkValueStringDefault { });
+    mkKeyValue = generators.mkKeyValueDefault
+      {
+        mkValueString = v:
+          if v == null then ""
+          else generators.mkValueStringDefault { } v;
+      }
+      "=";
+  };
   configFile = settingsFormatIni.generate "config.ini" cfg.settings;
 
   mkProvisionCfg = name: attr: provisionCfg:
     if provisionCfg.path != null
-      then provisionCfg.path
+    then provisionCfg.path
     else
       provisioningSettingsFormat.generate "${name}.yaml"
         (if provisionCfg.settings != null
-          then provisionCfg.settings
-          else {
-            apiVersion = 1;
-            ${attr} = [];
-          });
+        then provisionCfg.settings
+        else {
+          apiVersion = 1;
+          ${attr} = [ ];
+        });
 
   datasourceFileOrDir = mkProvisionCfg "datasource" "datasources" cfg.provision.datasources;
   dashboardFileOrDir = mkProvisionCfg "dashboard" "providers" cfg.provision.dashboards;
@@ -35,9 +54,10 @@ let
 
   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
-                                        else cfg.provision.alerting."${x}".path;
+  generateAlertingProvisioningYaml = x:
+    if (cfg.provision.alerting."${x}".path == null)
+    then provisioningSettingsFormat.generate "${x}.yaml" cfg.provision.alerting."${x}".settings
+    else cfg.provision.alerting."${x}".path;
   rulesFileOrDir = generateAlertingProvisioningYaml "rules";
   contactPointsFileOrDir = generateAlertingProvisioningYaml "contactPoints";
   policiesFileOrDir = generateAlertingProvisioningYaml "policies";
@@ -102,7 +122,7 @@ let
         description = lib.mdDoc "Datasource type. Required.";
       };
       access = mkOption {
-        type = types.enum ["proxy" "direct"];
+        type = types.enum [ "proxy" "direct" ];
         default = "proxy";
         description = lib.mdDoc "Access mode. proxy or direct (Server or Browser in the UI). Required.";
       };
@@ -170,7 +190,7 @@ let
         description = lib.mdDoc "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"];
+        type = types.enum [ "dingding" "discord" "email" "googlechat" "hipchat" "kafka" "line" "teams" "opsgenie" "pagerduty" "prometheus-alertmanager" "pushover" "sensu" "sensugo" "slack" "telegram" "threema" "victorops" "webhook" ];
         description = lib.mdDoc "Notifier type.";
       };
       uid = mkOption {
@@ -225,7 +245,8 @@ let
       };
     };
   };
-in {
+in
+{
   imports = [
     (mkRenamedOptionModule [ "services" "grafana" "protocol" ] [ "services" "grafana" "settings" "server" "protocol" ])
     (mkRenamedOptionModule [ "services" "grafana" "addr" ] [ "services" "grafana" "settings" "server" "http_addr" ])
@@ -354,7 +375,7 @@ in {
             protocol = mkOption {
               description = lib.mdDoc "Which protocol to listen.";
               default = "http";
-              type = types.enum ["http" "https" "h2" "socket"];
+              type = types.enum [ "http" "https" "h2" "socket" ];
             };
 
             http_addr = mkOption {
@@ -376,17 +397,60 @@ in {
             };
 
             domain = mkOption {
-              description = lib.mdDoc "The public facing domain name used to access grafana from a browser.";
+              description = lib.mdDoc ''
+                The public facing domain name used to access grafana from a browser.
+
+                This setting is only used in the default value of the `root_url` setting.
+                If you set the latter manually, this option does not have to be specified.
+              '';
               default = "localhost";
               type = types.str;
             };
 
+            enforce_domain = mkOption {
+              description = lib.mdDoc ''
+                Redirect to correct domain if the host header does not match the domain.
+                Prevents DNS rebinding attacks.
+              '';
+              default = false;
+              type = types.bool;
+            };
+
             root_url = mkOption {
-              description = lib.mdDoc "Full public facing url.";
+              description = lib.mdDoc ''
+                This is the full URL used to access Grafana from a web browser.
+                This is important if you use Google or GitHub OAuth authentication (for the callback URL to be correct).
+
+                This setting is also important if you have a reverse proxy in front of Grafana that exposes it through a subpath.
+                In that case add the subpath to the end of this URL setting.
+              '';
               default = "%(protocol)s://%(domain)s:%(http_port)s/";
               type = types.str;
             };
 
+            serve_from_sub_path = mkOption {
+              description = lib.mdDoc ''
+                Serve Grafana from subpath specified in the `root_url` setting.
+                By default it is set to `false` for compatibility reasons.
+
+                By enabling this setting and using a subpath in `root_url` above,
+                e.g. `root_url = "http://localhost:3000/grafana"`,
+                Grafana is accessible on `http://localhost:3000/grafana`.
+                If accessed without subpath, Grafana will redirect to an URL with the subpath.
+              '';
+              default = false;
+              type = types.bool;
+            };
+
+            router_logging = mkOption {
+              description = lib.mdDoc ''
+                Set to `true` for Grafana to log all HTTP requests (not just errors).
+                These are logged as Info level events to the Grafana log.
+              '';
+              default = false;
+              type = types.bool;
+            };
+
             static_root_path = mkOption {
               description = lib.mdDoc "Root path for static assets.";
               default = "${cfg.package}/share/grafana/public";
@@ -396,60 +460,119 @@ in {
 
             enable_gzip = mkOption {
               description = lib.mdDoc ''
-                Set this option to true to enable HTTP compression, this can improve transfer speed and bandwidth utilization.
-                It is recommended that most users set it to true. By default it is set to false for compatibility reasons.
+                Set this option to `true` to enable HTTP compression, this can improve transfer speed and bandwidth utilization.
+                It is recommended that most users set it to `true`. By default it is set to `false` for compatibility reasons.
               '';
               default = false;
               type = types.bool;
             };
 
             cert_file = mkOption {
-              description = lib.mdDoc "Cert file for ssl.";
-              default = "";
-              type = types.str;
+              description = lib.mdDoc ''
+                Path to the certificate file (if `protocol` is set to `https` or `h2`).
+              '';
+              default = null;
+              type = types.nullOr types.str;
             };
 
             cert_key = mkOption {
-              description = lib.mdDoc "Cert key for ssl.";
-              default = "";
+              description = lib.mdDoc ''
+                Path to the certificate key file (if `protocol` is set to `https` or `h2`).
+              '';
+              default = null;
+              type = types.nullOr types.str;
+            };
+
+            socket_gid = mkOption {
+              description = lib.mdDoc ''
+                GID where the socket should be set when `protocol=socket`.
+                Make sure that the target group is in the group of Grafana process and that Grafana process is the file owner before you change this setting.
+                It is recommended to set the gid as http server user gid.
+                Not set when the value is -1.
+              '';
+              default = -1;
+              type = types.int;
+            };
+
+            socket_mode = mkOption {
+              description = lib.mdDoc ''
+                Mode where the socket should be set when `protocol=socket`.
+                Make sure that Grafana process is the file owner before you change this setting.
+              '';
+              # I assume this value is interpreted as octal literal by grafana.
+              # If this was an int, people following tutorials or porting their
+              # old config could stumble across nix not having octal literals.
+              default = "0660";
               type = types.str;
             };
 
             socket = mkOption {
-              description = lib.mdDoc "Path where the socket should be created when protocol=socket. Make sure that Grafana has appropriate permissions before you change this setting.";
+              description = lib.mdDoc ''
+                Path where the socket should be created when `protocol=socket`.
+                Make sure that Grafana has appropriate permissions before you change this setting.
+              '';
               default = "/run/grafana/grafana.sock";
               type = types.str;
             };
+
+            cdn_url = mkOption {
+              description = lib.mdDoc ''
+                Specify a full HTTP URL address to the root of your Grafana CDN assets.
+                Grafana will add edition and version paths.
+
+                For example, given a cdn url like `https://cdn.myserver.com`
+                grafana will try to load a javascript file from `http://cdn.myserver.com/grafana-oss/7.4.0/public/build/app.<hash>.js`.
+              '';
+              default = null;
+              type = types.nullOr types.str;
+            };
+
+            read_timeout = mkOption {
+              description = lib.mdDoc ''
+                Sets the maximum time using a duration format (5s/5m/5ms)
+                before timing out read of an incoming request and closing idle connections.
+                0 means there is no timeout for reading the request.
+              '';
+              default = "0";
+              type = types.str;
+            };
           };
 
           database = {
             type = mkOption {
               description = lib.mdDoc "Database type.";
               default = "sqlite3";
-              type = types.enum ["mysql" "sqlite3" "postgres"];
+              type = types.enum [ "mysql" "sqlite3" "postgres" ];
             };
 
             host = mkOption {
-              description = lib.mdDoc "Database host.";
+              description = lib.mdDoc ''
+                Only applicable to MySQL or Postgres.
+                Includes IP or hostname and port or in case of Unix sockets the path to it.
+                For example, for MySQL running on the same host as Grafana: `host = "127.0.0.1:3306"`
+                or with Unix sockets: `host = "/var/run/mysqld/mysqld.sock"`
+              '';
               default = "127.0.0.1:3306";
               type = types.str;
             };
 
             name = mkOption {
-              description = lib.mdDoc "Database name.";
+              description = lib.mdDoc "The name of the Grafana database.";
               default = "grafana";
               type = types.str;
             };
 
             user = mkOption {
-              description = lib.mdDoc "Database user.";
+              description = lib.mdDoc "The database user (not applicable for `sqlite3`).";
               default = "root";
               type = types.str;
             };
 
             password = mkOption {
               description = lib.mdDoc ''
-                Database password. Please note that the contents of this option
+                The database user's password (not applicable for `sqlite3`).
+
+                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:
@@ -459,15 +582,144 @@ in {
               type = types.str;
             };
 
+            max_idle_conn = mkOption {
+              description = lib.mdDoc "The maximum number of connections in the idle connection pool.";
+              default = 2;
+              type = types.int;
+            };
+
+            max_open_conn = mkOption {
+              description = lib.mdDoc "The maximum number of open connections to the database.";
+              default = 0;
+              type = types.int;
+            };
+
+            conn_max_lifetime = mkOption {
+              description = lib.mdDoc ''
+                Sets the maximum amount of time a connection may be reused.
+                The default is 14400 (which means 14400 seconds or 4 hours).
+                For MySQL, this setting should be shorter than the `wait_timeout` variable.
+              '';
+              default = 14400;
+              type = types.int;
+            };
+
+            locking_attempt_timeout_sec = mkOption {
+              description = lib.mdDoc ''
+                For `mysql`, if the `migrationLocking` feature toggle is set,
+                specify the time (in seconds) to wait before failing to lock the database for the migrations.
+              '';
+              default = 0;
+              type = types.int;
+            };
+
+            log_queries = mkOption {
+              description = lib.mdDoc "Set to `true` to log the sql calls and execution times";
+              default = false;
+              type = types.bool;
+            };
+
+            ssl_mode = mkOption {
+              description = lib.mdDoc ''
+                For Postgres, use either `disable`, `require` or `verify-full`.
+                For MySQL, use either `true`, `false`, or `skip-verify`.
+              '';
+              default = "disable";
+              type = types.enum [ "disable" "require" "verify-full" "true" "false" "skip-verify" ];
+            };
+
+            isolation_level = mkOption {
+              description = lib.mdDoc ''
+                Only the MySQL driver supports isolation levels in Grafana.
+                In case the value is empty, the driver's default isolation level is applied.
+              '';
+              default = null;
+              type = types.nullOr (types.enum [ "READ-UNCOMMITTED" "READ-COMMITTED" "REPEATABLE-READ" "SERIALIZABLE" ]);
+            };
+
+            ca_cert_path = mkOption {
+              description = lib.mdDoc "The path to the CA certificate to use.";
+              default = null;
+              type = types.nullOr types.str;
+            };
+
+            client_key_path = mkOption {
+              description = lib.mdDoc "The path to the client key. Only if server requires client authentication.";
+              default = null;
+              type = types.nullOr types.str;
+            };
+
+            client_cert_path = mkOption {
+              description = lib.mdDoc "The path to the client cert. Only if server requires client authentication.";
+              default = null;
+              type = types.nullOr types.str;
+            };
+
+            server_cert_name = mkOption {
+              description = lib.mdDoc ''
+                The common name field of the certificate used by the `mysql` or `postgres` server.
+                Not necessary if `ssl_mode` is set to `skip-verify`.
+              '';
+              default = null;
+              type = types.nullOr types.str;
+            };
+
             path = mkOption {
-              description = lib.mdDoc "Only applicable to sqlite3 database. The file path where the database will be stored.";
+              description = lib.mdDoc "Only applicable to `sqlite3` database. The file path where the database will be stored.";
               default = "${cfg.dataDir}/data/grafana.db";
               defaultText = literalExpression ''"''${config.${opt.dataDir}}/data/grafana.db"'';
               type = types.path;
             };
+
+            cache_mode = mkOption {
+              description = lib.mdDoc ''
+                For `sqlite3` only.
+                [Shared cache](https://www.sqlite.org/sharedcache.html) setting used for connecting to the database.
+              '';
+              default = "private";
+              type = types.enum [ "private" "shared" ];
+            };
+
+            wal = mkOption {
+              description = lib.mdDoc ''
+                For `sqlite3` only.
+                Setting to enable/disable [Write-Ahead Logging](https://sqlite.org/wal.html).
+              '';
+              default = false;
+              type = types.bool;
+            };
+
+            query_retries = mkOption {
+              description = lib.mdDoc ''
+                This setting applies to `sqlite3` only and controls the number of times the system retries a query when the database is locked.
+              '';
+              default = 0;
+              type = types.int;
+            };
+
+            transaction_retries = mkOption {
+              description = lib.mdDoc ''
+                This setting applies to `sqlite3` only and controls the number of times the system retries a transaction when the database is locked.
+              '';
+              default = 5;
+              type = types.int;
+            };
+
+            # TODO Add "instrument_queries" option when upgrading to grafana 10.0
+            # instrument_queries = mkOption {
+            #   description = lib.mdDoc "Set to `true` to add metrics and tracing for database queries.";
+            #   default = false;
+            #   type = types.bool;
+            # };
           };
 
           security = {
+            disable_initial_admin_creation = mkOption {
+              description = lib.mdDoc "Disable creation of admin user on first start of Grafana.";
+              default = false;
+              type = types.bool;
+            };
+
             admin_user = mkOption {
               description = lib.mdDoc "Default admin username.";
               default = "admin";
@@ -486,6 +738,12 @@ in {
               type = types.str;
             };
 
+            admin_email = mkOption {
+              description = lib.mdDoc "The email of the default Grafana Admin, created on startup.";
+              default = "admin@localhost";
+              type = types.str;
+            };
+
             secret_key = mkOption {
               description = lib.mdDoc ''
                 Secret key used for signing. Please note that the contents of this option
@@ -497,6 +755,160 @@ in {
               default = "SW2YcwTIb9zpOOhoPsMm";
               type = types.str;
             };
+
+            disable_gravatar = mkOption {
+              description = lib.mdDoc "Set to `true` to disable the use of Gravatar for user profile images.";
+              default = false;
+              type = types.bool;
+            };
+
+            data_source_proxy_whitelist = mkOption {
+              description = lib.mdDoc ''
+                Define a whitelist of allowed IP addresses or domains, with ports,
+                to be used in data source URLs with the Grafana data source proxy.
+                Format: `ip_or_domain:port` separated by spaces.
+                PostgreSQL, MySQL, and MSSQL data sources do not use the proxy and are therefore unaffected by this setting.
+              '';
+              default = [ ];
+              type = types.oneOf [ types.str (types.listOf types.str) ];
+            };
+
+            disable_brute_force_login_protection = mkOption {
+              description = lib.mdDoc "Set to `true` to disable [brute force login protection](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html#account-lockout).";
+              default = false;
+              type = types.bool;
+            };
+
+            cookie_secure = mkOption {
+              description = lib.mdDoc "Set to `true` if you host Grafana behind HTTPS.";
+              default = false;
+              type = types.bool;
+            };
+
+            cookie_samesite = mkOption {
+              description = lib.mdDoc ''
+                Sets the `SameSite` cookie attribute and prevents the browser from sending this cookie along with cross-site requests.
+                The main goal is to mitigate the risk of cross-origin information leakage.
+                This setting also provides some protection against cross-site request forgery attacks (CSRF),
+                [read more about SameSite here](https://owasp.org/www-community/SameSite).
+                Using value `disabled` does not add any `SameSite` attribute to cookies.
+              '';
+              default = "lax";
+              type = types.enum [ "lax" "strict" "none" "disabled" ];
+            };
+
+            allow_embedding = mkOption {
+              description = lib.mdDoc ''
+                When `false`, the HTTP header `X-Frame-Options: deny` will be set in Grafana HTTP responses
+                which will instruct browsers to not allow rendering Grafana in a `<frame>`, `<iframe>`, `<embed>` or `<object>`.
+                The main goal is to mitigate the risk of [Clickjacking](https://owasp.org/www-community/attacks/Clickjacking).
+              '';
+              default = false;
+              type = types.bool;
+            };
+
+            strict_transport_security = mkOption {
+              description = lib.mdDoc ''
+                Set to `true` if you want to enable HTTP `Strict-Transport-Security` (HSTS) response header.
+                Only use this when HTTPS is enabled in your configuration,
+                or when there is another upstream system that ensures your application does HTTPS (like a frontend load balancer).
+                HSTS tells browsers that the site should only be accessed using HTTPS.
+              '';
+              default = false;
+              type = types.bool;
+            };
+
+            strict_transport_security_max_age_seconds = mkOption {
+              description = lib.mdDoc ''
+                Sets how long a browser should cache HSTS in seconds.
+                Only applied if `strict_transport_security` is enabled.
+              '';
+              default = 86400;
+              type = types.int;
+            };
+
+            strict_transport_security_preload = mkOption {
+              description = lib.mdDoc ''
+                Set to `true` to enable HSTS `preloading` option.
+                Only applied if `strict_transport_security` is enabled.
+              '';
+              default = false;
+              type = types.bool;
+            };
+
+            strict_transport_security_subdomains = mkOption {
+              description = lib.mdDoc ''
+                Set to `true` to enable HSTS `includeSubDomains` option.
+                Only applied if `strict_transport_security` is enabled.
+              '';
+              default = false;
+              type = types.bool;
+            };
+
+            x_content_type_options = mkOption {
+              description = lib.mdDoc ''
+                Set to `false` to disable the `X-Content-Type-Options` response header.
+                The `X-Content-Type-Options` response HTTP header is a marker used by the server
+                to indicate that the MIME types advertised in the `Content-Type` headers should not be changed and be followed.
+              '';
+              default = true;
+              type = types.bool;
+            };
+
+            x_xss_protection = mkOption {
+              description = lib.mdDoc ''
+                Set to `false` to disable the `X-XSS-Protection` header,
+                which tells browsers to stop pages from loading when they detect reflected cross-site scripting (XSS) attacks.
+              '';
+              default = true;
+              type = types.bool;
+            };
+
+            content_security_policy = mkOption {
+              description = lib.mdDoc ''
+                Set to `true` to add the `Content-Security-Policy` header to your requests.
+                CSP allows to control resources that the user agent can load and helps prevent XSS attacks.
+              '';
+              default = false;
+              type = types.bool;
+            };
+
+            content_security_policy_report_only = mkOption {
+              description = lib.mdDoc ''
+                Set to `true` to add the `Content-Security-Policy-Report-Only` header to your requests.
+                CSP in Report Only mode enables you to experiment with policies by monitoring their effects without enforcing them.
+                You can enable both policies simultaneously.
+              '';
+              default = false;
+              type = types.bool;
+            };
+
+            # The options content_security_policy_template and
+            # content_security_policy_template are missing because I'm not sure
+            # how exactly the quoting of the default value works. See also
+            # https://github.com/grafana/grafana/blob/cb7e18938b8eb6860a64b91aaba13a7eb31bc95b/conf/defaults.ini#L364
+            # https://github.com/grafana/grafana/blob/cb7e18938b8eb6860a64b91aaba13a7eb31bc95b/conf/defaults.ini#L373
+
+            # These two options are lists joined with spaces:
+            # https://github.com/grafana/grafana/blob/916d9793aa81c2990640b55a15dee0db6b525e41/pkg/middleware/csrf/csrf.go#L37-L38
+
+            csrf_trusted_origins = mkOption {
+              description = lib.mdDoc ''
+                List of additional allowed URLs to pass by the CSRF check.
+                Suggested when authentication comes from an IdP.
+              '';
+              default = [ ];
+              type = types.oneOf [ types.str (types.listOf types.str) ];
+            };
+
+            csrf_additional_headers = mkOption {
+              description = lib.mdDoc ''
+                List of allowed headers to be set by the user.
+                Suggested to use for if authentication lives behind reverse proxies.
+              '';
+              default = [ ];
+              type = types.oneOf [ types.str (types.listOf types.str) ];
+            };
           };
 
           smtp = {
@@ -505,16 +917,19 @@ in {
               default = false;
               type = types.bool;
             };
+
             host = mkOption {
               description = lib.mdDoc "Host to connect to.";
               default = "localhost:25";
               type = types.str;
             };
+
             user = mkOption {
               description = lib.mdDoc "User used for authentication.";
-              default = "";
-              type = types.str;
+              default = null;
+              type = types.nullOr types.str;
             };
+
             password = mkOption {
               description = lib.mdDoc ''
                 Password used for authentication. Please note that the contents of this option
@@ -526,43 +941,212 @@ in {
               default = "";
               type = types.str;
             };
+
+            cert_file = mkOption {
+              description = lib.mdDoc "File path to a cert file.";
+              default = null;
+              type = types.nullOr types.str;
+            };
+
+            key_file = mkOption {
+              description = lib.mdDoc "File path to a key file.";
+              default = null;
+              type = types.nullOr types.str;
+            };
+
+            skip_verify = mkOption {
+              description = lib.mdDoc "Verify SSL for SMTP server.";
+              default = false;
+              type = types.bool;
+            };
+
             from_address = mkOption {
-              description = lib.mdDoc "Email address used for sending.";
+              description = lib.mdDoc "Address used when sending out emails.";
               default = "admin@grafana.localhost";
               type = types.str;
             };
+
+            from_name = mkOption {
+              description = lib.mdDoc "Name to be used as client identity for EHLO in SMTP dialog.";
+              default = "Grafana";
+              type = types.str;
+            };
+
+            ehlo_identity = mkOption {
+              description = lib.mdDoc "Name to be used as client identity for EHLO in SMTP dialog.";
+              default = null;
+              type = types.nullOr types.str;
+            };
+
+            startTLS_policy = mkOption {
+              description = lib.mdDoc "StartTLS policy when connecting to server.";
+              default = null;
+              type = types.nullOr (types.enum [ "OpportunisticStartTLS" "MandatoryStartTLS" "NoStartTLS" ]);
+            };
           };
 
           users = {
             allow_sign_up = mkOption {
-              description = lib.mdDoc "Disable user signup / registration.";
+              description = lib.mdDoc ''
+                Set to false to prohibit users from being able to sign up / create user accounts.
+                The admin user can still create users.
+              '';
               default = false;
               type = types.bool;
             };
 
             allow_org_create = mkOption {
-              description = lib.mdDoc "Whether user is allowed to create organizations.";
+              description = lib.mdDoc "Set to `false` to prohibit users from creating new organizations.";
               default = false;
               type = types.bool;
             };
 
             auto_assign_org = mkOption {
-              description = lib.mdDoc "Whether to automatically assign new users to default org.";
+              description = lib.mdDoc ''
+                Set to `true` to automatically add new users to the main organization (id 1).
+                When set to `false,` new users automatically cause a new organization to be created for that new user.
+                The organization will be created even if the `allow_org_create` setting is set to `false`.
+              '';
               default = true;
               type = types.bool;
             };
 
+            auto_assign_org_id = mkOption {
+              description = lib.mdDoc ''
+                Set this value to automatically add new users to the provided org.
+                This requires `auto_assign_org` to be set to `true`.
+                Please make sure that this organization already exists.
+              '';
+              default = 1;
+              type = types.int;
+            };
+
             auto_assign_org_role = mkOption {
-              description = lib.mdDoc "Default role new users will be auto assigned.";
+              description = lib.mdDoc ''
+                The role new users will be assigned for the main organization (if the `auto_assign_org` setting is set to `true`).
+              '';
               default = "Viewer";
-              type = types.enum ["Viewer" "Editor" "Admin"];
+              type = types.enum [ "Viewer" "Editor" "Admin" ];
+            };
+
+            verify_email_enabled = mkOption {
+              description = lib.mdDoc "Require email validation before sign up completes.";
+              default = false;
+              type = types.bool;
+            };
+
+            login_hint = mkOption {
+              description = lib.mdDoc "Text used as placeholder text on login page for login/username input.";
+              default = "email or username";
+              type = types.str;
+            };
+
+            password_hint = mkOption {
+              description = lib.mdDoc "Text used as placeholder text on login page for password input.";
+              default = "password";
+              type = types.str;
+            };
+
+            default_theme = mkOption {
+              description = lib.mdDoc "Sets the default UI theme. `system` matches the user's system theme.";
+              default = "dark";
+              type = types.enum [ "dark" "light" "system" ];
+            };
+
+            default_language = mkOption {
+              description = lib.mdDoc "This setting configures the default UI language, which must be a supported IETF language tag, such as `en-US`.";
+              default = "en-US";
+              type = types.str;
+            };
+
+            home_page = mkOption {
+              description = lib.mdDoc ''
+                Path to a custom home page.
+                Users are only redirected to this if the default home dashboard is used.
+                It should match a frontend route and contain a leading slash.
+              '';
+              default = "";
+              type = types.str;
+            };
+
+            viewers_can_edit = mkOption {
+              description = lib.mdDoc ''
+                Viewers can access and use Explore and perform temporary edits on panels in dashboards they have access to.
+                They cannot save their changes.
+              '';
+              default = false;
+              type = types.bool;
+            };
+
+            editors_can_admin = mkOption {
+              description = lib.mdDoc "Editors can administrate dashboards, folders and teams they create.";
+              default = false;
+              type = types.bool;
+            };
+
+            user_invite_max_lifetime_duration = mkOption {
+              description = lib.mdDoc ''
+                The duration in time a user invitation remains valid before expiring.
+                This setting should be expressed as a duration.
+                Examples: `6h` (hours), `2d` (days), `1w` (week).
+                The minimum supported duration is `15m` (15 minutes).
+              '';
+              default = "24h";
+              type = types.str;
+            };
+
+            # Lists are joined via space, so this option can't be a list.
+            # Users have to manually join their values.
+            hidden_users = mkOption {
+              description = lib.mdDoc ''
+                This is a comma-separated list of usernames.
+                Users specified here are hidden in the Grafana UI.
+                They are still visible to Grafana administrators and to themselves.
+              '';
+              default = "";
+              type = types.str;
             };
           };
 
-          analytics.reporting_enabled = mkOption {
-            description = lib.mdDoc "Whether to allow anonymous usage reporting to stats.grafana.net.";
-            default = true;
-            type = types.bool;
+          analytics = {
+            reporting_enabled = mkOption {
+              description = lib.mdDoc ''
+                When enabled Grafana will send anonymous usage statistics to `stats.grafana.org`.
+                No IP addresses are being tracked, only simple counters to track running instances, versions, dashboard and error counts.
+                Counters are sent every 24 hours.
+              '';
+              default = true;
+              type = types.bool;
+            };
+
+            check_for_updates = mkOption {
+              description = lib.mdDoc ''
+                When set to `false`, disables checking for new versions of Grafana from Grafana's GitHub repository.
+                When enabled, the check for a new version runs every 10 minutes.
+                It will notify, via the UI, when a new version is available.
+                The check itself will not prompt any auto-updates of the Grafana software, nor will it send any sensitive information.
+              '';
+              default = false;
+              type = types.bool;
+            };
+
+            check_for_plugin_updates = mkOption {
+              description = lib.mdDoc ''
+                When set to `false`, disables checking for new versions of installed plugins from https://grafana.com.
+                When enabled, the check for a new plugin runs every 10 minutes.
+                It will notify, via the UI, when a new plugin update exists.
+                The check itself will not prompt any auto-updates of the plugin, nor will it send any sensitive information.
+              '';
+              default = cfg.declarativePlugins == null;
+              defaultText = literalExpression "cfg.declarativePlugins == null";
+              type = types.bool;
+            };
+
+            feedback_links_enabled = mkOption {
+              description = lib.mdDoc "Set to `false` to remove all feedback links from the UI.";
+              default = true;
+              type = types.bool;
+            };
           };
         };
       };
@@ -575,7 +1159,7 @@ in {
         description = lib.mdDoc ''
           Declaratively provision Grafana's datasources.
         '';
-        default = {};
+        default = { };
         type = submodule' {
           options.settings = mkOption {
             description = lib.mdDoc ''
@@ -595,13 +1179,13 @@ in {
 
                 datasources = mkOption {
                   description = lib.mdDoc "List of datasources to insert/update.";
-                  default = [];
+                  default = [ ];
                   type = types.listOf grafanaTypes.datasourceConfig;
                 };
 
                 deleteDatasources = mkOption {
                   description = lib.mdDoc "List of datasources that should be deleted from the database.";
-                  default = [];
+                  default = [ ];
                   type = types.listOf (types.submodule {
                     options.name = mkOption {
                       description = lib.mdDoc "Name of the datasource to delete.";
@@ -650,7 +1234,7 @@ in {
         description = lib.mdDoc ''
           Declaratively provision Grafana's dashboards.
         '';
-        default = {};
+        default = { };
         type = submodule' {
           options.settings = mkOption {
             description = lib.mdDoc ''
@@ -669,7 +1253,7 @@ in {
 
               options.providers = mkOption {
                 description = lib.mdDoc "List of dashboards to insert/update.";
-                default = [];
+                default = [ ];
                 type = types.listOf grafanaTypes.dashboardConfig;
               };
             });
@@ -700,7 +1284,7 @@ in {
 
       notifiers = mkOption {
         description = lib.mdDoc "Grafana notifier configuration.";
-        default = [];
+        default = [ ];
         type = types.listOf grafanaTypes.notifierConfig;
         apply = x: map _filter x;
       };
@@ -736,7 +1320,7 @@ in {
 
                 groups = mkOption {
                   description = lib.mdDoc "List of rule groups to import or update.";
-                  default = [];
+                  default = [ ];
                   type = types.listOf (types.submodule {
                     freeformType = provisioningSettingsFormat.type;
 
@@ -759,7 +1343,7 @@ in {
 
                 deleteRules = mkOption {
                   description = lib.mdDoc "List of alert rule UIDs that should be deleted.";
-                  default = [];
+                  default = [ ];
                   type = types.listOf (types.submodule {
                     options.orgId = mkOption {
                       description = lib.mdDoc "Organization ID, default = 1";
@@ -860,7 +1444,7 @@ in {
 
                 contactPoints = mkOption {
                   description = lib.mdDoc "List of contact points to import or update.";
-                  default = [];
+                  default = [ ];
                   type = types.listOf (types.submodule {
                     freeformType = provisioningSettingsFormat.type;
 
@@ -873,7 +1457,7 @@ in {
 
                 deleteContactPoints = mkOption {
                   description = lib.mdDoc "List of receivers that should be deleted.";
-                  default = [];
+                  default = [ ];
                   type = types.listOf (types.submodule {
                     options.orgId = mkOption {
                       description = lib.mdDoc "Organization ID, default = 1.";
@@ -941,7 +1525,7 @@ in {
 
                 policies = mkOption {
                   description = lib.mdDoc "List of contact points to import or update.";
-                  default = [];
+                  default = [ ];
                   type = types.listOf (types.submodule {
                     freeformType = provisioningSettingsFormat.type;
                   });
@@ -949,7 +1533,7 @@ in {
 
                 resetPolicies = mkOption {
                   description = lib.mdDoc "List of orgIds that should be reset to the default policy.";
-                  default = [];
+                  default = [ ];
                   type = types.listOf types.int;
                 };
               };
@@ -1011,7 +1595,7 @@ in {
 
                 templates = mkOption {
                   description = lib.mdDoc "List of templates to import or update.";
-                  default = [];
+                  default = [ ];
                   type = types.listOf (types.submodule {
                     freeformType = provisioningSettingsFormat.type;
 
@@ -1029,7 +1613,7 @@ in {
 
                 deleteTemplates = mkOption {
                   description = lib.mdDoc "List of alert rule UIDs that should be deleted.";
-                  default = [];
+                  default = [ ];
                   type = types.listOf (types.submodule {
                     options.orgId = mkOption {
                       description = lib.mdDoc "Organization ID, default = 1.";
@@ -1093,7 +1677,7 @@ in {
 
                 muteTimes = mkOption {
                   description = lib.mdDoc "List of mute time intervals to import or update.";
-                  default = [];
+                  default = [ ];
                   type = types.listOf (types.submodule {
                     freeformType = provisioningSettingsFormat.type;
 
@@ -1106,7 +1690,7 @@ in {
 
                 deleteMuteTimes = mkOption {
                   description = lib.mdDoc "List of mute time intervals that should be deleted.";
-                  default = [];
+                  default = [ ];
                   type = types.listOf (types.submodule {
                     options.orgId = mkOption {
                       description = lib.mdDoc "Organization ID, default = 1.";
@@ -1168,45 +1752,58 @@ in {
   };
 
   config = mkIf cfg.enable {
-    warnings = let
-      doesntUseFileProvider = opt: defaultValue:
-        let
-          regex = "${optionalString (defaultValue != null) "^${defaultValue}$|"}^\\$__(file|env)\\{.*}$|^\\$[^_\\$][^ ]+$";
-        in builtins.match regex opt == null;
-    in
-      # Ensure that no custom credentials are leaked into the Nix store. Unless the default value
-      # is specified, this can be achieved by using the file/env provider:
-      # https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#variable-expansion
-      (optional (
-        doesntUseFileProvider cfg.settings.database.password "" ||
-        doesntUseFileProvider cfg.settings.security.admin_password "admin"
-      ) ''
-        Grafana passwords will be stored as plaintext in the Nix store!
-        Use file provider or an env-var instead.
-      '')
-      # Warn about deprecated notifiers.
-      ++ (optional (cfg.provision.notifiers != []) ''
-        Notifiers are deprecated upstream and will be removed in Grafana 10.
-        Use `services.grafana.provision.alerting.contactPoints` instead.
-      '')
-      # Ensure that `secureJsonData` of datasources provisioned via `datasources.settings`
-      # only uses file/env providers.
-      ++ (optional (
-        let
-          datasourcesToCheck = optionals
-            (cfg.provision.datasources.settings != null)
-            cfg.provision.datasources.settings.datasources;
-          declarationUnsafe = { secureJsonData, ... }:
-            secureJsonData != null
-            && any (flip doesntUseFileProvider null) (attrValues secureJsonData);
-        in any declarationUnsafe datasourcesToCheck
-      ) ''
-        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!
-      '')
-      ++ (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.");
+    warnings =
+      let
+        doesntUseFileProvider = opt: defaultValue:
+          let regex = "${optionalString (defaultValue != null) "^${defaultValue}$|"}^\\$__(file|env)\\{.*}$|^\\$[^_\\$][^ ]+$";
+          in builtins.match regex opt == null;
+
+        # Ensure that no custom credentials are leaked into the Nix store. Unless the default value
+        # is specified, this can be achieved by using the file/env provider:
+        # https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#variable-expansion
+        passwordWithoutFileProvider = optional
+          (
+            doesntUseFileProvider cfg.settings.database.password "" ||
+            doesntUseFileProvider cfg.settings.security.admin_password "admin"
+          )
+          ''
+            Grafana passwords will be stored as plaintext in the Nix store!
+            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
+          (
+            let
+              datasourcesToCheck = optionals
+                (cfg.provision.datasources.settings != null)
+                cfg.provision.datasources.settings.datasources;
+              declarationUnsafe = { secureJsonData, ... }:
+                secureJsonData != null
+                && any (flip doesntUseFileProvider null) (attrValues secureJsonData);
+            in
+            any declarationUnsafe datasourcesToCheck
+          )
+          ''
+            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 ];
 
@@ -1216,11 +1813,12 @@ in {
         message = "Cannot set both datasources settings and datasources path";
       }
       {
-        assertion = let
-          prometheusIsNotDirect = opt: all
-          ({ type, access, ... }: type == "prometheus" -> access != "direct")
-          opt;
-        in
+        assertion =
+          let
+            prometheusIsNotDirect = opt: all
+              ({ type, access, ... }: type == "prometheus" -> access != "direct")
+              opt;
+          in
           cfg.provision.datasources.settings == null || prometheusIsNotDirect cfg.provision.datasources.settings.datasources;
         message = "For datasources of type `prometheus`, the `direct` access mode is not supported anymore (since Grafana 9.2.0)";
       }
@@ -1252,8 +1850,8 @@ in {
 
     systemd.services.grafana = {
       description = "Grafana Service Daemon";
-      wantedBy = ["multi-user.target"];
-      after = ["networking.target"] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "networking.target" ] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service";
       script = ''
         set -o errexit -o pipefail -o nounset -o errtrace
         shopt -s inherit_errexit
@@ -1309,6 +1907,6 @@ in {
       createHome = true;
       group = "grafana";
     };
-    users.groups.grafana = {};
+    users.groups.grafana = { };
   };
 }
diff --git a/nixpkgs/nixos/modules/services/monitoring/munin.nix b/nixpkgs/nixos/modules/services/monitoring/munin.nix
index 9461bd3f35b8..f37f2689927e 100644
--- a/nixpkgs/nixos/modules/services/monitoring/munin.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/munin.nix
@@ -375,7 +375,7 @@ in
 
     # Munin is hardcoded to use DejaVu Mono and the graphs come out wrong if
     # it's not available.
-    fonts.fonts = [ pkgs.dejavu_fonts ];
+    fonts.packages = [ pkgs.dejavu_fonts ];
 
     systemd.timers.munin-cron = {
       description = "batch Munin master programs";
diff --git a/nixpkgs/nixos/modules/services/monitoring/netdata.nix b/nixpkgs/nixos/modules/services/monitoring/netdata.nix
index bd0dea83e1a8..89a842023c88 100644
--- a/nixpkgs/nixos/modules/services/monitoring/netdata.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/netdata.nix
@@ -159,6 +159,15 @@ in {
         '';
       };
 
+      claimTokenFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = lib.mdDoc ''
+          If set, automatically registers the agent using the given claim token
+          file.
+        '';
+      };
+
       enableAnalyticsReporting = mkOption {
         type = types.bool;
         default = false;
@@ -216,7 +225,7 @@ in {
         ExecStart = "${cfg.package}/bin/netdata -P /run/netdata/netdata.pid -D -c /etc/netdata/netdata.conf";
         ExecReload = "${pkgs.util-linux}/bin/kill -s HUP -s USR1 -s USR2 $MAINPID";
         ExecStartPost = pkgs.writeShellScript "wait-for-netdata-up" ''
-          while [ "$(${pkgs.netdata}/bin/netdatacli ping)" != pong ]; do sleep 0.5; done
+          while [ "$(${cfg.package}/bin/netdatacli ping)" != pong ]; do sleep 0.5; done
         '';
 
         TimeoutStopSec = cfg.deadlineBeforeStopSec;
@@ -260,7 +269,25 @@ in {
         PrivateTmp = true;
         ProtectControlGroups = true;
         PrivateMounts = true;
-      };
+      } // (lib.optionalAttrs (cfg.claimTokenFile != null) {
+        LoadCredential = [
+          "netdata_claim_token:${cfg.claimTokenFile}"
+        ];
+
+        ExecStartPre = pkgs.writeShellScript "netdata-claim" ''
+          set -euo pipefail
+
+          if [[ -f /var/lib/netdata/cloud.d/claimed_id ]]; then
+            # Already registered
+            exit
+          fi
+
+          exec ${cfg.package}/bin/netdata-claim.sh \
+            -token="$(< "$CREDENTIALS_DIRECTORY/netdata_claim_token")" \
+            -url=https://app.netdata.cloud \
+            -daemon-not-running
+        '';
+      });
     };
 
     systemd.enableCgroupAccounting = true;
diff --git a/nixpkgs/nixos/modules/services/monitoring/opentelemetry-collector.nix b/nixpkgs/nixos/modules/services/monitoring/opentelemetry-collector.nix
new file mode 100644
index 000000000000..1d211b689777
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/opentelemetry-collector.nix
@@ -0,0 +1,73 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) mkEnableOption mkIf mkOption types getExe;
+
+  cfg = config.services.opentelemetry-collector;
+  opentelemetry-collector = cfg.package;
+
+  settingsFormat = pkgs.formats.yaml {};
+in {
+  options.services.opentelemetry-collector = {
+    enable = mkEnableOption (lib.mdDoc "Opentelemetry Collector");
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.opentelemetry-collector;
+      defaultText = lib.literalExpression "pkgs.opentelemetry-collector";
+      description = lib.mdDoc "The opentelemetry-collector package to use.";
+    };
+
+    settings = mkOption {
+      type = settingsFormat.type;
+      default = {};
+      description = lib.mdDoc ''
+        Specify the configuration for Opentelemetry Collector in Nix.
+
+        See https://opentelemetry.io/docs/collector/configuration/ for available options.
+      '';
+    };
+
+    configFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = lib.mdDoc ''
+        Specify a path to a configuration file that Opentelemetry Collector should use.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [{
+      assertion = (
+        (cfg.settings == {}) != (cfg.configFile == null)
+      );
+      message  = ''
+        Please specify a configuration for Opentelemetry Collector with either
+        'services.opentelemetry-collector.settings' or
+        'services.opentelemetry-collector.configFile'.
+      '';
+    }];
+
+    systemd.services.opentelemetry-collector = {
+      description = "Opentelemetry Collector Service Daemon";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = let
+        conf = if cfg.configFile == null
+               then settingsFormat.generate "config.yaml" cfg.settings
+               else cfg.configFile;
+      in
+      {
+        ExecStart = "${getExe opentelemetry-collector} --config=file:${conf}";
+        DynamicUser = true;
+        Restart = "always";
+        ProtectSystem = "full";
+        DevicePolicy = "closed";
+        NoNewPrivileges = true;
+        WorkingDirectory = "/var/lib/opentelemetry-collector";
+        StateDirectory = "opentelemetry-collector";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/osquery.nix b/nixpkgs/nixos/modules/services/monitoring/osquery.nix
new file mode 100644
index 000000000000..4f6c2557a641
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/osquery.nix
@@ -0,0 +1,97 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.osquery;
+  dirname = path: with lib.strings; with lib.lists; concatStringsSep "/"
+    (init (splitString "/" (normalizePath path)));
+
+  # conf is the osquery configuration file used when the --config_plugin=filesystem.
+  # filesystem is the osquery default value for the config_plugin flag.
+  conf = pkgs.writeText "osquery.conf" (builtins.toJSON cfg.settings);
+
+  # flagfile is the file containing osquery command line flags to be
+  # provided to the application using the special --flagfile option.
+  flagfile = pkgs.writeText "osquery.flags"
+    (concatStringsSep "\n"
+      (mapAttrsToList (name: value: "--${name}=${value}")
+        # Use the conf derivation if not otherwise specified.
+        ({ config_path = conf; } // cfg.flags)));
+
+  osqueryi = pkgs.runCommand "osqueryi" { nativeBuildInputs = [ pkgs.makeWrapper ]; } ''
+    mkdir -p $out/bin
+    makeWrapper ${pkgs.osquery}/bin/osqueryi $out/bin/osqueryi \
+      --add-flags "--flagfile ${flagfile} --disable-database"
+  '';
+in
+{
+  options.services.osquery = {
+    enable = mkEnableOption (mdDoc "osqueryd daemon");
+
+    settings = mkOption {
+      default = { };
+      description = mdDoc ''
+        Configuration to be written to the osqueryd JSON configuration file.
+        To understand the configuration format, refer to https://osquery.readthedocs.io/en/stable/deployment/configuration/#configuration-components.
+      '';
+      example = {
+        options.utc = false;
+      };
+      type = types.attrs;
+    };
+
+    flags = mkOption {
+      default = { };
+      description = mdDoc ''
+        Attribute set of flag names and values to be written to the osqueryd flagfile.
+        For more information, refer to https://osquery.readthedocs.io/en/stable/installation/cli-flags.
+      '';
+      example = {
+        config_refresh = "10";
+      };
+      type = with types;
+        submodule {
+          freeformType = attrsOf str;
+          options = {
+            database_path = mkOption {
+              default = "/var/lib/osquery/osquery.db";
+              readOnly = true;
+              description = mdDoc "Path used for the database file.";
+              type = path;
+            };
+            logger_path = mkOption {
+              default = "/var/log/osquery";
+              readOnly = true;
+              description = mdDoc "Base directory used for logging.";
+              type = path;
+            };
+            pidfile = mkOption {
+              default = "/run/osquery/osqueryd.pid";
+              readOnly = true;
+              description = mdDoc "Path used for pid file.";
+              type = path;
+            };
+          };
+        };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ osqueryi ];
+    systemd.services.osqueryd = {
+      after = [ "network.target" "syslog.service" ];
+      description = "The osquery daemon";
+      serviceConfig = {
+        ExecStart = "${pkgs.osquery}/bin/osqueryd --flagfile ${flagfile}";
+        PIDFile = cfg.flags.pidfile;
+        LogsDirectory = cfg.flags.logger_path;
+        StateDirectory = dirname cfg.flags.database_path;
+        Restart = "always";
+      };
+      wantedBy = [ "multi-user.target" ];
+    };
+    systemd.tmpfiles.rules = [
+      "d ${dirname (cfg.flags.pidfile)} 0755 root root -"
+    ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
index 235296168934..397125b51230 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -40,6 +40,7 @@ let
     "ipmi"
     "json"
     "jitsi"
+    "junos-czerwonk"
     "kea"
     "keylight"
     "knot"
@@ -55,6 +56,7 @@ let
     "nut"
     "openldap"
     "openvpn"
+    "php-fpm"
     "pihole"
     "postfix"
     "postgres"
@@ -64,6 +66,7 @@ let
     "redis"
     "rspamd"
     "rtl_433"
+    "scaphandre"
     "script"
     "shelly"
     "snmp"
@@ -300,6 +303,21 @@ in
         Please specify either 'services.prometheus.exporters.sql.configuration' or
           'services.prometheus.exporters.sql.configFile'
       '';
+    } {
+      assertion = cfg.scaphandre.enable -> (pkgs.stdenv.targetPlatform.isx86_64 == true);
+      message = ''
+        Scaphandre only support x86_64 architectures.
+      '';
+    } {
+      assertion = cfg.scaphandre.enable -> ((lib.kernel.whenHelpers pkgs.linux.version).whenOlder "5.11" true).condition == false;
+      message = ''
+        Scaphandre requires a kernel version newer than '5.11', '${pkgs.linux.version}' given.
+      '';
+    } {
+      assertion = cfg.scaphandre.enable -> (builtins.elem "intel_rapl_common" config.boot.kernelModules);
+      message = ''
+        Scaphandre needs 'intel_rapl_common' kernel module to be enabled. Please add it in 'boot.kernelModules'.
+      '';
     } ] ++ (flip map (attrNames exporterOpts) (exporter: {
       assertion = cfg.${exporter}.firewallFilter != null -> cfg.${exporter}.openFirewall;
       message = ''
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/ipmi.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/ipmi.nix
index 55c4f4aa4826..9adbe31d84d6 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/ipmi.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/ipmi.nix
@@ -37,5 +37,6 @@ in {
     ] ++ extraFlags);
 
     ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+    RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
   };
 }
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/junos-czerwonk.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/junos-czerwonk.nix
new file mode 100644
index 000000000000..15e0c9ecb177
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/junos-czerwonk.nix
@@ -0,0 +1,72 @@
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.junos-czerwonk;
+
+  configFile = if cfg.configuration != null then configurationFile else (escapeShellArg cfg.configurationFile);
+
+  configurationFile = pkgs.writeText "prometheus-junos-czerwonk-exporter.conf" (builtins.toJSON (cfg.configuration));
+in
+{
+  port = 9326;
+  extraOpts = {
+    environmentFile = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = lib.mdDoc ''
+        File containing env-vars to be substituted into the exporter's config.
+      '';
+    };
+    configurationFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = lib.mdDoc ''
+        Specify the JunOS exporter configuration file to use.
+      '';
+    };
+    configuration = mkOption {
+      type = types.nullOr types.attrs;
+      default = null;
+      description = lib.mdDoc ''
+        JunOS exporter configuration as nix attribute set. Mutually exclusive with the `configurationFile` option.
+      '';
+      example = {
+        devices = [
+          {
+            host = "router1";
+            key_file = "/path/to/key";
+          }
+        ];
+      };
+    };
+    telemetryPath = mkOption {
+      type = types.str;
+      default = "/metrics";
+      description = lib.mdDoc ''
+        Path under which to expose metrics.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = false;
+      EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
+      RuntimeDirectory = "prometheus-junos-czerwonk-exporter";
+      ExecStartPre = [
+        "${pkgs.writeShellScript "subst-secrets-junos-czerwonk-exporter" ''
+          umask 0077
+          ${pkgs.envsubst}/bin/envsubst -i ${configFile} -o ''${RUNTIME_DIRECTORY}/junos-exporter.json
+        ''}"
+      ];
+      ExecStart = ''
+        ${pkgs.prometheus-junos-czerwonk-exporter}/bin/junos_exporter \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          -web.telemetry-path ${cfg.telemetryPath} \
+          -config.file ''${RUNTIME_DIRECTORY}/junos-exporter.json \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix
new file mode 100644
index 000000000000..8f6942002f79
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix
@@ -0,0 +1,65 @@
+{ config
+, lib
+, pkgs
+, options
+}:
+
+let
+  logPrefix = "services.prometheus.exporter.php-fpm";
+  cfg = config.services.prometheus.exporters.php-fpm;
+in {
+  port = 9253;
+  extraOpts = {
+    package = lib.mkPackageOptionMD pkgs "prometheus-php-fpm-exporter" {};
+
+    telemetryPath = lib.mkOption {
+      type = lib.types.str;
+      default = "/metrics";
+      description = lib.mdDoc ''
+        Path under which to expose metrics.
+      '';
+    };
+
+    environmentFile = lib.mkOption {
+      type = lib.types.nullOr lib.types.path;
+      default = null;
+      example = "/root/prometheus-php-fpm-exporter.env";
+      description = lib.mdDoc ''
+        Environment file as defined in {manpage}`systemd.exec(5)`.
+
+        Secrets may be passed to the service without adding them to the
+        world-readable Nix store, by specifying placeholder variables as
+        the option value in Nix and setting these variables accordingly in the
+        environment file.
+
+        Environment variables from this file will be interpolated into the
+        config file using envsubst with this syntax:
+        `$ENVIRONMENT ''${VARIABLE}`
+
+        For variables to use see [options and defaults](https://github.com/hipages/php-fpm_exporter#options-and-defaults).
+
+        The main use is to set the PHP_FPM_SCRAPE_URI that indicate how to connect to PHP-FPM process.
+
+        ```
+          # Content of the environment file
+          PHP_FPM_SCRAPE_URI="unix:///tmp/php.sock;/status"
+        ```
+
+        Note that this file needs to be available on the host on which
+        this exporter is running.
+      '';
+    };
+  };
+
+  serviceOpts = {
+    serviceConfig = {
+      EnvironmentFile = lib.mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
+      ExecStart = ''
+        ${lib.getExe cfg.package} server \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          --web.telemetry-path ${cfg.telemetryPath} \
+          ${lib.concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pve.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pve.nix
index e02acad3ecd1..f95412efd7dd 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pve.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pve.nix
@@ -104,12 +104,12 @@ in
       LoadCredential = "configFile:${computedConfigFile}";
       ExecStart = ''
         ${cfg.package}/bin/pve_exporter \
-          --${if cfg.collectors.status == true then "" else "no-"}collector.status \
-          --${if cfg.collectors.version == true then "" else "no-"}collector.version \
-          --${if cfg.collectors.node == true then "" else "no-"}collector.node \
-          --${if cfg.collectors.cluster == true then "" else "no-"}collector.cluster \
-          --${if cfg.collectors.resources == true then "" else "no-"}collector.resources \
-          --${if cfg.collectors.config == true then "" else "no-"}collector.config \
+          --${optionalString (!cfg.collectors.status) "no-"}collector.status \
+          --${optionalString (!cfg.collectors.version) "no-"}collector.version \
+          --${optionalString (!cfg.collectors.node) "no-"}collector.node \
+          --${optionalString (!cfg.collectors.cluster) "no-"}collector.cluster \
+          --${optionalString (!cfg.collectors.resources) "no-"}collector.resources \
+          --${optionalString (!cfg.collectors.config) "no-"}collector.config \
           %d/configFile \
           ${toString cfg.port} ${cfg.listenAddress}
       '';
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix
new file mode 100644
index 000000000000..3b6ebf65b090
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix
@@ -0,0 +1,33 @@
+{ config
+, lib
+, pkgs
+, options
+}:
+
+let
+  logPrefix = "services.prometheus.exporter.scaphandre";
+  cfg = config.services.prometheus.exporters.scaphandre;
+in {
+  port = 8080;
+  extraOpts = {
+    telemetryPath = lib.mkOption {
+      type = lib.types.str;
+      default = "/metrics";
+      description = lib.mdDoc ''
+        Path under which to expose metrics.
+      '';
+    };
+  };
+
+  serviceOpts = {
+    serviceConfig = {
+      ExecStart = ''
+        ${pkgs.scaphandre}/bin/scaphandre prometheus \
+          --address ${cfg.listenAddress} \
+          --port ${toString cfg.port} \
+          --suffix ${cfg.telemetryPath} \
+          ${lib.concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/ceph.nix b/nixpkgs/nixos/modules/services/network-filesystems/ceph.nix
index 22d58f29cb81..aad03728b203 100644
--- a/nixpkgs/nixos/modules/services/network-filesystems/ceph.nix
+++ b/nixpkgs/nixos/modules/services/network-filesystems/ceph.nix
@@ -3,18 +3,18 @@
 with lib;
 
 let
-  cfg  = config.services.ceph;
+  cfg = config.services.ceph;
 
   # function that translates "camelCaseOptions" to "camel case options", credits to tilpner in #nixos@freenode
   expandCamelCase = replaceStrings upperChars (map (s: " ${s}") lowerChars);
   expandCamelCaseAttrs = mapAttrs' (name: value: nameValuePair (expandCamelCase name) value);
 
-  makeServices = (daemonType: daemonIds:
+  makeServices = daemonType: daemonIds:
     mkMerge (map (daemonId:
-      { "ceph-${daemonType}-${daemonId}" = makeService daemonType daemonId cfg.global.clusterName pkgs.ceph; })
-      daemonIds));
+      { "ceph-${daemonType}-${daemonId}" = makeService daemonType daemonId cfg.global.clusterName cfg.${daemonType}.package; })
+      daemonIds);
 
-  makeService = (daemonType: daemonId: clusterName: ceph:
+  makeService = daemonType: daemonId: clusterName: ceph:
     let
       stateDirectory = "ceph/${if daemonType == "rgw" then "radosgw" else daemonType}/${clusterName}-${daemonId}"; in {
     enable = true;
@@ -54,9 +54,9 @@ let
     } // optionalAttrs ( daemonType == "mon") {
       RestartSec = "10";
     };
-  });
+  };
 
-  makeTarget = (daemonType:
+  makeTarget = daemonType:
     {
       "ceph-${daemonType}" = {
         description = "Ceph target allowing to start/stop all ceph-${daemonType} services at once";
@@ -65,8 +65,7 @@ let
         before = [ "ceph.target" ];
         unitConfig.StopWhenUnneeded = true;
       };
-    }
-  );
+    };
 in
 {
   options.services.ceph = {
@@ -211,6 +210,7 @@ in
           to the id part in ceph i.e. [ "name1" ] would result in mgr.name1
         '';
       };
+      package = mkPackageOptionMD pkgs "ceph" { };
       extraConfig = mkOption {
         type = with types; attrsOf str;
         default = {};
@@ -231,6 +231,7 @@ in
           to the id part in ceph i.e. [ "name1" ] would result in mon.name1
         '';
       };
+      package = mkPackageOptionMD pkgs "ceph" { };
       extraConfig = mkOption {
         type = with types; attrsOf str;
         default = {};
@@ -251,7 +252,7 @@ in
           to the id part in ceph i.e. [ "name1" ] would result in osd.name1
         '';
       };
-
+      package = mkPackageOptionMD pkgs "ceph" { };
       extraConfig = mkOption {
         type = with types; attrsOf str;
         default = {
@@ -279,6 +280,7 @@ in
           to the id part in ceph i.e. [ "name1" ] would result in mds.name1
         '';
       };
+      package = mkPackageOptionMD pkgs "ceph" { };
       extraConfig = mkOption {
         type = with types; attrsOf str;
         default = {};
@@ -290,6 +292,7 @@ in
 
     rgw = {
       enable = mkEnableOption (lib.mdDoc "Ceph RadosGW daemon");
+      package = mkPackageOptionMD pkgs "ceph" { };
       daemons = mkOption {
         type = with types; listOf str;
         default = [];
@@ -328,16 +331,16 @@ in
       { assertion = cfg.global.fsid != "";
         message = "fsid has to be set to a valid uuid for the cluster to function";
       }
-      { assertion = cfg.mon.enable == true -> cfg.mon.daemons != [];
+      { assertion = cfg.mon.enable -> cfg.mon.daemons != [];
         message = "have to set id of atleast one MON if you're going to enable Monitor";
       }
-      { assertion = cfg.mds.enable == true -> cfg.mds.daemons != [];
+      { assertion = cfg.mds.enable -> cfg.mds.daemons != [];
         message = "have to set id of atleast one MDS if you're going to enable Metadata Service";
       }
-      { assertion = cfg.osd.enable == true -> cfg.osd.daemons != [];
+      { assertion = cfg.osd.enable -> cfg.osd.daemons != [];
         message = "have to set id of atleast one OSD if you're going to enable OSD";
       }
-      { assertion = cfg.mgr.enable == true -> cfg.mgr.daemons != [];
+      { assertion = cfg.mgr.enable -> cfg.mgr.daemons != [];
         message = "have to set id of atleast one MGR if you're going to enable MGR";
       }
     ];
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/glusterfs.nix b/nixpkgs/nixos/modules/services/network-filesystems/glusterfs.nix
index 5c3e197b687d..ee03bada492d 100644
--- a/nixpkgs/nixos/modules/services/network-filesystems/glusterfs.nix
+++ b/nixpkgs/nixos/modules/services/network-filesystems/glusterfs.nix
@@ -15,11 +15,11 @@ let
     rm -f /var/lib/glusterd/secure-access
   '';
 
-  restartTriggers = if (cfg.tlsSettings != null) then [
+  restartTriggers = optionals (cfg.tlsSettings != null) [
     config.environment.etc."ssl/glusterfs.pem".source
     config.environment.etc."ssl/glusterfs.key".source
     config.environment.etc."ssl/glusterfs.ca".source
-  ] else [];
+  ];
 
   cfg = config.services.glusterfs;
 
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/kubo.nix b/nixpkgs/nixos/modules/services/network-filesystems/kubo.nix
index a5c370b5be89..5a355f3441d8 100644
--- a/nixpkgs/nixos/modules/services/network-filesystems/kubo.nix
+++ b/nixpkgs/nixos/modules/services/network-filesystems/kubo.nix
@@ -278,6 +278,12 @@ in
           You can't set services.kubo.settings.Pinning.RemoteServices because the ``config replace`` subcommand used at startup does not work with it.
         '';
       }
+      {
+        assertion = !((lib.versionAtLeast cfg.package.version "0.21") && (builtins.hasAttr "Experimental" cfg.settings) && (builtins.hasAttr "AcceleratedDHTClient" cfg.settings.Experimental));
+        message = ''
+    The `services.kubo.settings.Experimental.AcceleratedDHTClient` option was renamed to `services.kubo.settings.Routing.AcceleratedDHTClient` in Kubo 0.21.
+  '';
+      }
     ];
 
     environment.systemPackages = [ cfg.package ];
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/samba-wsdd.nix b/nixpkgs/nixos/modules/services/network-filesystems/samba-wsdd.nix
index 24407f05de6a..ad600796217b 100644
--- a/nixpkgs/nixos/modules/services/network-filesystems/samba-wsdd.nix
+++ b/nixpkgs/nixos/modules/services/network-filesystems/samba-wsdd.nix
@@ -11,13 +11,6 @@ in {
       enable = mkEnableOption (lib.mdDoc ''
         Web Services Dynamic Discovery host daemon. This enables (Samba) hosts, like your local NAS device,
         to be found by Web Service Discovery Clients like Windows.
-
-        ::: {.note}
-        If you use the firewall consider adding the following:
-
-            networking.firewall.allowedTCPPorts = [ 5357 ];
-            networking.firewall.allowedUDPPorts = [ 3702 ];
-        :::
       '');
       interface = mkOption {
         type = types.nullOr types.str;
@@ -31,6 +24,13 @@ in {
         example = 2;
         description = lib.mdDoc "Hop limit for multicast packets (default = 1).";
       };
+      openFirewall = mkOption {
+        description = lib.mdDoc ''
+          Whether to open the required firewall ports in the firewall.
+        '';
+        default = false;
+        type = lib.types.bool;
+      };
       workgroup = mkOption {
         type = types.nullOr types.str;
         default = null;
@@ -120,5 +120,10 @@ in {
         SystemCallFilter = "~@cpu-emulation @debug @mount @obsolete @privileged @resources";
       };
     };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ 5357 ];
+      allowedUDPPorts = [ 3702 ];
+    };
   };
 }
diff --git a/nixpkgs/nixos/modules/services/networking/adguardhome.nix b/nixpkgs/nixos/modules/services/networking/adguardhome.nix
index bda99cb7942b..1701e5b439c1 100644
--- a/nixpkgs/nixos/modules/services/networking/adguardhome.nix
+++ b/nixpkgs/nixos/modules/services/networking/adguardhome.nix
@@ -41,6 +41,20 @@ in
       '';
     };
 
+    allowDHCP = mkOption {
+      default = cfg.settings.dhcp.enabled or false;
+      defaultText = literalExpression ''config.services.adguardhome.settings.dhcp.enabled or false'';
+      type = bool;
+      description = lib.mdDoc ''
+        Allows AdGuard Home to open raw sockets (`CAP_NET_RAW`), which is
+        required for the integrated DHCP server.
+
+        The default enables this conditionally if the declarative configuration
+        enables the integrated DHCP server. Manually setting this option is only
+        required for non-declarative setups.
+      '';
+    };
+
     mutableSettings = mkOption {
       default = true;
       type = bool;
@@ -147,7 +161,7 @@ in
       serviceConfig = {
         DynamicUser = true;
         ExecStart = "${pkgs.adguardhome}/bin/adguardhome ${args}";
-        AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
+        AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ] ++ optionals cfg.allowDHCP [ "CAP_NET_RAW" ];
         Restart = "always";
         RestartSec = 10;
         RuntimeDirectory = "AdGuardHome";
diff --git a/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix b/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix
index 3a7519c7230b..bdbf9aad9acc 100644
--- a/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix
+++ b/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix
@@ -56,6 +56,15 @@ in
       '';
     };
 
+    package = mkOption {
+      type = types.package;
+      default = pkgs.avahi;
+      defaultText = literalExpression "pkgs.avahi";
+      description = lib.mdDoc ''
+        The avahi package to use for running the daemon.
+      '';
+    };
+
     hostName = mkOption {
       type = types.str;
       default = config.networking.hostName;
@@ -260,7 +269,7 @@ in
       (mkAfter [ "mdns" ]) # after dns
     ]);
 
-    environment.systemPackages = [ pkgs.avahi ];
+    environment.systemPackages = [ cfg.package ];
 
     environment.etc = (mapAttrs'
       (n: v: nameValuePair
@@ -286,19 +295,19 @@ in
       # return a sensible value.
       environment.LD_LIBRARY_PATH = config.system.nssModules.path;
 
-      path = [ pkgs.coreutils pkgs.avahi ];
+      path = [ pkgs.coreutils cfg.package ];
 
       serviceConfig = {
         NotifyAccess = "main";
         BusName = "org.freedesktop.Avahi";
         Type = "dbus";
-        ExecStart = "${pkgs.avahi}/sbin/avahi-daemon --syslog -f ${avahiDaemonConf}";
+        ExecStart = "${cfg.package}/sbin/avahi-daemon --syslog -f ${avahiDaemonConf}";
         ConfigurationDirectory = "avahi/services";
       };
     };
 
     services.dbus.enable = true;
-    services.dbus.packages = [ pkgs.avahi ];
+    services.dbus.packages = [ cfg.package ];
 
     networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ 5353 ];
   };
diff --git a/nixpkgs/nixos/modules/services/networking/biboumi.nix b/nixpkgs/nixos/modules/services/networking/biboumi.nix
index 1428856764e6..d44a46b35a29 100644
--- a/nixpkgs/nixos/modules/services/networking/biboumi.nix
+++ b/nixpkgs/nixos/modules/services/networking/biboumi.nix
@@ -8,8 +8,7 @@ let
   settingsFile = pkgs.writeText "biboumi.cfg" (
     generators.toKeyValue {
       mkKeyValue = k: v:
-        if v == null then ""
-        else generators.mkKeyValueDefault {} "=" k v;
+        lib.optionalString (v != null) (generators.mkKeyValueDefault {} "=" k v);
     } cfg.settings);
   need_CAP_NET_BIND_SERVICE = cfg.settings.identd_port != 0 && cfg.settings.identd_port < 1024;
 in
diff --git a/nixpkgs/nixos/modules/services/networking/bird.nix b/nixpkgs/nixos/modules/services/networking/bird.nix
index 77e0b3f8af9b..9deeb7694d2a 100644
--- a/nixpkgs/nixos/modules/services/networking/bird.nix
+++ b/nixpkgs/nixos/modules/services/networking/bird.nix
@@ -61,7 +61,7 @@ in
       checkPhase = optionalString cfg.checkConfig ''
         ln -s $out bird2.conf
         ${cfg.preCheckConfig}
-        ${pkgs.bird}/bin/bird -d -p -c bird2.conf
+        ${pkgs.buildPackages.bird}/bin/bird -d -p -c bird2.conf
       '';
     };
 
diff --git a/nixpkgs/nixos/modules/services/networking/cgit.nix b/nixpkgs/nixos/modules/services/networking/cgit.nix
index 672b0b030eee..7d1f12fa9146 100644
--- a/nixpkgs/nixos/modules/services/networking/cgit.nix
+++ b/nixpkgs/nixos/modules/services/networking/cgit.nix
@@ -14,7 +14,9 @@ let
       # taken from https://github.com/python/cpython/blob/05cb728d68a278d11466f9a6c8258d914135c96c/Lib/re.py#L251-L266
       special = [
         "(" ")" "[" "]" "{" "}" "?" "*" "+" "-" "|" "^" "$" "\\" "." "&" "~"
-        "#" " " "\t" "\n" "\r" "\v" "\f"
+        "#" " " "\t" "\n" "\r"
+        "" # \v / 0x0B
+        "" # \f / 0x0C
       ];
     in
       replaceStrings special (map (c: "\\${c}") special);
diff --git a/nixpkgs/nixos/modules/services/networking/cjdns.nix b/nixpkgs/nixos/modules/services/networking/cjdns.nix
index 5a19475161fd..80085da92702 100644
--- a/nixpkgs/nixos/modules/services/networking/cjdns.nix
+++ b/nixpkgs/nixos/modules/services/networking/cjdns.nix
@@ -239,7 +239,7 @@ in
       after = [ "network-online.target" ];
       bindsTo = [ "network-online.target" ];
 
-      preStart = if cfg.confFile != null then "" else ''
+      preStart = optionalString (cfg.confFile == null) ''
         [ -e /etc/cjdns.keys ] && source /etc/cjdns.keys
 
         if [ -z "$CJDNS_PRIVATE_KEY" ]; then
diff --git a/nixpkgs/nixos/modules/services/networking/ddclient.nix b/nixpkgs/nixos/modules/services/networking/ddclient.nix
deleted file mode 100644
index 7caee8a8eb3d..000000000000
--- a/nixpkgs/nixos/modules/services/networking/ddclient.nix
+++ /dev/null
@@ -1,243 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-let
-  cfg = config.services.ddclient;
-  boolToStr = bool: if bool then "yes" else "no";
-  dataDir = "/var/lib/ddclient";
-  StateDirectory = builtins.baseNameOf dataDir;
-  RuntimeDirectory = StateDirectory;
-
-  configFile' = pkgs.writeText "ddclient.conf" ''
-    # 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}
-    login=${cfg.username}
-    password=${if cfg.protocol == "nsupdate" then "/run/${RuntimeDirectory}/ddclient.key" else "@password_placeholder@"}
-    protocol=${cfg.protocol}
-    ${lib.optionalString (cfg.script != "") "script=${cfg.script}"}
-    ${lib.optionalString (cfg.server != "") "server=${cfg.server}"}
-    ${lib.optionalString (cfg.zone != "")   "zone=${cfg.zone}"}
-    ssl=${boolToStr cfg.ssl}
-    wildcard=YES
-    ipv6=${boolToStr cfg.ipv6}
-    quiet=${boolToStr cfg.quiet}
-    verbose=${boolToStr cfg.verbose}
-    ${cfg.extraConfig}
-    ${lib.concatStringsSep "," cfg.domains}
-  '';
-  configFile = if (cfg.configFile != null) then cfg.configFile else configFile';
-
-  preStart = ''
-    install --mode=600 --owner=$USER ${configFile} /run/${RuntimeDirectory}/ddclient.conf
-    ${lib.optionalString (cfg.configFile == null) (if (cfg.protocol == "nsupdate") then ''
-      install --mode=600 --owner=$USER ${cfg.passwordFile} /run/${RuntimeDirectory}/ddclient.key
-    '' else if (cfg.passwordFile != null) then ''
-      "${pkgs.replace-secret}/bin/replace-secret" "@password_placeholder@" "${cfg.passwordFile}" "/run/${RuntimeDirectory}/ddclient.conf"
-    '' else ''
-      sed -i '/^password=@password_placeholder@$/d' /run/${RuntimeDirectory}/ddclient.conf
-    '')}
-  '';
-
-in
-
-with lib;
-
-{
-
-  imports = [
-    (mkChangedOptionModule [ "services" "ddclient" "domain" ] [ "services" "ddclient" "domains" ]
-      (config:
-        let value = getAttrFromPath [ "services" "ddclient" "domain" ] config;
-        in if value != "" then [ value ] else []))
-    (mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "")
-    (mkRemovedOptionModule [ "services" "ddclient" "password" ] "Use services.ddclient.passwordFile instead.")
-  ];
-
-  ###### interface
-
-  options = {
-
-    services.ddclient = with lib.types; {
-
-      enable = mkOption {
-        default = false;
-        type = bool;
-        description = lib.mdDoc ''
-          Whether to synchronise your machine's IP address with a dynamic DNS provider (e.g. dyndns.org).
-        '';
-      };
-
-      package = mkOption {
-        type = package;
-        default = pkgs.ddclient;
-        defaultText = lib.literalExpression "pkgs.ddclient";
-        description = lib.mdDoc ''
-          The ddclient executable package run by the service.
-        '';
-      };
-
-      domains = mkOption {
-        default = [ "" ];
-        type = listOf str;
-        description = lib.mdDoc ''
-          Domain name(s) to synchronize.
-        '';
-      };
-
-      username = mkOption {
-        # For `nsupdate` username contains the path to the nsupdate executable
-        default = lib.optionalString (config.services.ddclient.protocol == "nsupdate") "${pkgs.bind.dnsutils}/bin/nsupdate";
-        defaultText = "";
-        type = str;
-        description = lib.mdDoc ''
-          User name.
-        '';
-      };
-
-      passwordFile = mkOption {
-        default = null;
-        type = nullOr str;
-        description = lib.mdDoc ''
-          A file containing the password or a TSIG key in named format when using the nsupdate protocol.
-        '';
-      };
-
-      interval = mkOption {
-        default = "10min";
-        type = str;
-        description = lib.mdDoc ''
-          The interval at which to run the check and update.
-          See {command}`man 7 systemd.time` for the format.
-        '';
-      };
-
-      configFile = mkOption {
-        default = null;
-        type = nullOr path;
-        description = lib.mdDoc ''
-          Path to configuration file.
-          When set this overrides the generated configuration from module options.
-        '';
-        example = "/root/nixos/secrets/ddclient.conf";
-      };
-
-      protocol = mkOption {
-        default = "dyndns2";
-        type = str;
-        description = lib.mdDoc ''
-          Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols).
-        '';
-      };
-
-      server = mkOption {
-        default = "";
-        type = str;
-        description = lib.mdDoc ''
-          Server address.
-        '';
-      };
-
-      ssl = mkOption {
-        default = true;
-        type = bool;
-        description = lib.mdDoc ''
-          Whether to use SSL/TLS to connect to dynamic DNS provider.
-        '';
-      };
-
-      ipv6 = mkOption {
-        default = false;
-        type = bool;
-        description = lib.mdDoc ''
-          Whether to use IPv6.
-        '';
-      };
-
-
-      quiet = mkOption {
-        default = false;
-        type = bool;
-        description = lib.mdDoc ''
-          Print no messages for unnecessary updates.
-        '';
-      };
-
-      script = mkOption {
-        default = "";
-        type = str;
-        description = lib.mdDoc ''
-          script as required by some providers.
-        '';
-      };
-
-      use = mkOption {
-        default = "web, web=checkip.dyndns.com/, web-skip='Current IP Address: '";
-        type = str;
-        description = lib.mdDoc ''
-          Method to determine the IP address to send to the dynamic DNS provider.
-        '';
-      };
-
-      verbose = mkOption {
-        default = false;
-        type = bool;
-        description = lib.mdDoc ''
-          Print verbose information.
-        '';
-      };
-
-      zone = mkOption {
-        default = "";
-        type = str;
-        description = lib.mdDoc ''
-          zone as required by some providers.
-        '';
-      };
-
-      extraConfig = mkOption {
-        default = "";
-        type = lines;
-        description = lib.mdDoc ''
-          Extra configuration. Contents will be added verbatim to the configuration file.
-
-          ::: {.note}
-          `daemon` should not be added here because it does not work great with the systemd-timer approach the service uses.
-          :::
-        '';
-      };
-    };
-  };
-
-
-  ###### implementation
-
-  config = mkIf config.services.ddclient.enable {
-    systemd.services.ddclient = {
-      description = "Dynamic DNS Client";
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
-      restartTriggers = optional (cfg.configFile != null) cfg.configFile;
-      path = lib.optional (lib.hasPrefix "if," cfg.use) pkgs.iproute2;
-
-      serviceConfig = {
-        DynamicUser = true;
-        RuntimeDirectoryMode = "0700";
-        inherit RuntimeDirectory;
-        inherit StateDirectory;
-        Type = "oneshot";
-        ExecStartPre = "!${pkgs.writeShellScript "ddclient-prestart" preStart}";
-        ExecStart = "${lib.getBin cfg.package}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf";
-      };
-    };
-
-    systemd.timers.ddclient = {
-      description = "Run ddclient";
-      wantedBy = [ "timers.target" ];
-      timerConfig = {
-        OnBootSec = cfg.interval;
-        OnUnitInactiveSec = cfg.interval;
-      };
-    };
-  };
-}
diff --git a/nixpkgs/nixos/modules/services/networking/dhcpd.nix b/nixpkgs/nixos/modules/services/networking/dhcpd.nix
deleted file mode 100644
index a981a255c3ee..000000000000
--- a/nixpkgs/nixos/modules/services/networking/dhcpd.nix
+++ /dev/null
@@ -1,230 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  cfg4 = config.services.dhcpd4;
-  cfg6 = config.services.dhcpd6;
-
-  writeConfig = postfix: cfg: pkgs.writeText "dhcpd.conf"
-    ''
-      default-lease-time 600;
-      max-lease-time 7200;
-      ${optionalString (!cfg.authoritative) "not "}authoritative;
-      ddns-update-style interim;
-      log-facility local1; # see dhcpd.nix
-
-      ${cfg.extraConfig}
-
-      ${lib.concatMapStrings
-          (machine: ''
-            host ${machine.hostName} {
-              hardware ethernet ${machine.ethernetAddress};
-              fixed-address${
-                optionalString (postfix == "6") postfix
-              } ${machine.ipAddress};
-            }
-          '')
-          cfg.machines
-      }
-    '';
-
-  dhcpdService = postfix: cfg:
-    let
-      configFile =
-        if cfg.configFile != null
-          then cfg.configFile
-          else writeConfig postfix cfg;
-      leaseFile = "/var/lib/dhcpd${postfix}/dhcpd.leases";
-      args = [
-        "@${pkgs.dhcp}/sbin/dhcpd" "dhcpd${postfix}" "-${postfix}"
-        "-pf" "/run/dhcpd${postfix}/dhcpd.pid"
-        "-cf" configFile
-        "-lf" leaseFile
-      ] ++ cfg.extraFlags
-        ++ cfg.interfaces;
-    in
-      optionalAttrs cfg.enable {
-        "dhcpd${postfix}" = {
-          description = "DHCPv${postfix} server";
-          wantedBy = [ "multi-user.target" ];
-          after = [ "network.target" ];
-
-          preStart = "touch ${leaseFile}";
-          serviceConfig = {
-            ExecStart = concatMapStringsSep " " escapeShellArg args;
-            Type = "forking";
-            Restart = "always";
-            DynamicUser = true;
-            User = "dhcpd";
-            Group = "dhcpd";
-            AmbientCapabilities = [
-              "CAP_NET_RAW"          # to send ICMP messages
-              "CAP_NET_BIND_SERVICE" # to bind on DHCP port (67)
-            ];
-            StateDirectory   = "dhcpd${postfix}";
-            RuntimeDirectory = "dhcpd${postfix}";
-            PIDFile = "/run/dhcpd${postfix}/dhcpd.pid";
-          };
-        };
-      };
-
-  machineOpts = { ... }: {
-
-    options = {
-
-      hostName = mkOption {
-        type = types.str;
-        example = "foo";
-        description = lib.mdDoc ''
-          Hostname which is assigned statically to the machine.
-        '';
-      };
-
-      ethernetAddress = mkOption {
-        type = types.str;
-        example = "00:16:76:9a:32:1d";
-        description = lib.mdDoc ''
-          MAC address of the machine.
-        '';
-      };
-
-      ipAddress = mkOption {
-        type = types.str;
-        example = "192.168.1.10";
-        description = lib.mdDoc ''
-          IP address of the machine.
-        '';
-      };
-
-    };
-  };
-
-  dhcpConfig = postfix: {
-
-    enable = mkOption {
-      type = types.bool;
-      default = false;
-      description = lib.mdDoc ''
-        Whether to enable the DHCPv${postfix} server.
-      '';
-    };
-
-    extraConfig = mkOption {
-      type = types.lines;
-      default = "";
-      example = ''
-        option subnet-mask 255.255.255.0;
-        option broadcast-address 192.168.1.255;
-        option routers 192.168.1.5;
-        option domain-name-servers 130.161.158.4, 130.161.33.17, 130.161.180.1;
-        option domain-name "example.org";
-        subnet 192.168.1.0 netmask 255.255.255.0 {
-          range 192.168.1.100 192.168.1.200;
-        }
-      '';
-      description = lib.mdDoc ''
-        Extra text to be appended to the DHCP server configuration
-        file. Currently, you almost certainly need to specify something
-        there, such as the options specifying the subnet mask, DNS servers,
-        etc.
-      '';
-    };
-
-    extraFlags = mkOption {
-      type = types.listOf types.str;
-      default = [];
-      description = lib.mdDoc ''
-        Additional command line flags to be passed to the dhcpd daemon.
-      '';
-    };
-
-    configFile = mkOption {
-      type = types.nullOr types.path;
-      default = null;
-      description = lib.mdDoc ''
-        The path of the DHCP server configuration file.  If no file
-        is specified, a file is generated using the other options.
-      '';
-    };
-
-    interfaces = mkOption {
-      type = types.listOf types.str;
-      default = ["eth0"];
-      description = lib.mdDoc ''
-        The interfaces on which the DHCP server should listen.
-      '';
-    };
-
-    machines = mkOption {
-      type = with types; listOf (submodule machineOpts);
-      default = [];
-      example = [
-        { hostName = "foo";
-          ethernetAddress = "00:16:76:9a:32:1d";
-          ipAddress = "192.168.1.10";
-        }
-        { hostName = "bar";
-          ethernetAddress = "00:19:d1:1d:c4:9a";
-          ipAddress = "192.168.1.11";
-        }
-      ];
-      description = lib.mdDoc ''
-        A list mapping Ethernet addresses to IPv${postfix} addresses for the
-        DHCP server.
-      '';
-    };
-
-    authoritative = mkOption {
-      type = types.bool;
-      default = true;
-      description = lib.mdDoc ''
-        Whether the DHCP server shall send DHCPNAK messages to misconfigured
-        clients. If this is not done, clients may be unable to get a correct
-        IP address after changing subnets until their old lease has expired.
-      '';
-    };
-
-  };
-
-in
-
-{
-
-  imports = [
-    (mkRenamedOptionModule [ "services" "dhcpd" ] [ "services" "dhcpd4" ])
-  ] ++ flip map [ "4" "6" ] (postfix:
-    mkRemovedOptionModule [ "services" "dhcpd${postfix}" "stateDir" ] ''
-      The DHCP server state directory is now managed with the systemd's DynamicUser mechanism.
-      This means the directory is named after the service (dhcpd${postfix}), created under
-      /var/lib/private/ and symlinked to /var/lib/.
-    ''
-  );
-
-  ###### interface
-
-  options = {
-
-    services.dhcpd4 = dhcpConfig "4";
-    services.dhcpd6 = dhcpConfig "6";
-
-  };
-
-
-  ###### implementation
-
-  config = mkIf (cfg4.enable || cfg6.enable) {
-
-    systemd.services = dhcpdService "4" cfg4 // dhcpdService "6" cfg6;
-
-    warnings = [
-      ''
-        The dhcpd4 and dhcpd6 modules will be removed from NixOS 23.11, because ISC DHCP reached its end of life.
-        See https://www.isc.org/blogs/isc-dhcp-eol/ for details.
-        Please switch to a different implementation like kea, systemd-networkd or dnsmasq.
-      ''
-    ];
-  };
-
-}
diff --git a/nixpkgs/nixos/modules/services/networking/fakeroute.nix b/nixpkgs/nixos/modules/services/networking/fakeroute.nix
index ed6b1a3c4d26..faf5879a6ed3 100644
--- a/nixpkgs/nixos/modules/services/networking/fakeroute.nix
+++ b/nixpkgs/nixos/modules/services/networking/fakeroute.nix
@@ -1,10 +1,8 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.services.fakeroute;
-  routeConf = pkgs.writeText "route.conf" (concatStringsSep "\n" cfg.route);
+  routeConf = pkgs.writeText "route.conf" (lib.concatStringsSep "\n" cfg.route);
 
 in
 
@@ -16,16 +14,10 @@ in
 
     services.fakeroute = {
 
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Whether to enable the fakeroute service.
-        '';
-      };
+      enable = lib.mkEnableOption (lib.mdDoc "the fakeroute service");
 
-      route = mkOption {
-        type = types.listOf types.str;
+      route = lib.mkOption {
+        type = with lib.types; listOf str;
         default = [];
         example = [
           "216.102.187.130"
@@ -46,14 +38,16 @@ in
 
   ###### implementation
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     systemd.services.fakeroute = {
       description = "Fakeroute Daemon";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
         Type = "forking";
-        User = "root";
+        User = "fakeroute";
+        DynamicUser = true;
+        AmbientCapabilities = [ "CAP_NET_RAW" ];
         ExecStart = "${pkgs.fakeroute}/bin/fakeroute -f ${routeConf}";
       };
     };
diff --git a/nixpkgs/nixos/modules/services/networking/hostapd.nix b/nixpkgs/nixos/modules/services/networking/hostapd.nix
index ecc158f8151d..924abfc2953f 100644
--- a/nixpkgs/nixos/modules/services/networking/hostapd.nix
+++ b/nixpkgs/nixos/modules/services/networking/hostapd.nix
@@ -1,222 +1,1283 @@
 { config, lib, pkgs, utils, ... }:
+# All hope abandon ye who enter here. hostapd's configuration
+# format is ... special, and you won't be able to infer any
+# of their assumptions from just reading the "documentation"
+# (i.e. the example config). Assume footguns at all points -
+# to make informed decisions you will probably need to look
+# at hostapd's code. You have been warned, proceed with care.
+let
+  inherit
+    (lib)
+    attrNames
+    attrValues
+    concatLists
+    concatMap
+    concatMapStrings
+    concatStringsSep
+    count
+    escapeShellArg
+    filter
+    flip
+    generators
+    getAttr
+    hasPrefix
+    imap0
+    isInt
+    isString
+    length
+    literalExpression
+    maintainers
+    mapAttrsToList
+    mdDoc
+    mkDefault
+    mkEnableOption
+    mkIf
+    mkOption
+    mkPackageOption
+    mkRemovedOptionModule
+    optional
+    optionalAttrs
+    optionalString
+    optionals
+    singleton
+    stringLength
+    toLower
+    types
+    unique
+    ;
 
-# TODO:
-#
-# asserts
-#   ensure that the nl80211 module is loaded/compiled in the kernel
-#   wpa_supplicant and hostapd on the same wireless interface doesn't make any sense
+  cfg = config.services.hostapd;
 
-with lib;
+  extraSettingsFormat = {
+    type = let
+      singleAtom = types.oneOf [ types.bool types.int types.str ];
+      atom = types.either singleAtom (types.listOf singleAtom) // {
+        description = "atom (bool, int or string) or a list of them for duplicate keys";
+      };
+    in types.attrsOf atom;
 
-let
+    generate = name: value: pkgs.writeText name (generators.toKeyValue {
+      listsAsDuplicateKeys = true;
+      mkKeyValue = generators.mkKeyValueDefault {
+        mkValueString = v:
+          if      isInt    v then toString v
+          else if isString v then v
+          else if true  == v then "1"
+          else if false == v then "0"
+          else throw "unsupported type ${builtins.typeOf v}: ${(generators.toPretty {}) v}";
+      } "=";
+    } value);
+  };
 
-  cfg = config.services.hostapd;
+  # Generates the header for a single BSS (i.e. WiFi network)
+  writeBssHeader = radio: bss: bssIdx: pkgs.writeText "hostapd-radio-${radio}-bss-${bss}.conf" ''
+    ''\n''\n# BSS ${toString bssIdx}: ${bss}
+    ################################
 
-  escapedInterface = utils.escapeSystemdPath cfg.interface;
+    ${if bssIdx == 0 then "interface" else "bss"}=${bss}
+  '';
 
-  configFile = pkgs.writeText "hostapd.conf" ''
-    interface=${cfg.interface}
-    driver=${cfg.driver}
-    ssid=${cfg.ssid}
-    hw_mode=${cfg.hwMode}
-    channel=${toString cfg.channel}
-    ieee80211n=1
-    ieee80211ac=1
-    ${optionalString (cfg.countryCode != null) "country_code=${cfg.countryCode}"}
-    ${optionalString (cfg.countryCode != null) "ieee80211d=1"}
+  makeRadioRuntimeFiles = radio: radioCfg:
+    pkgs.writeShellScript "make-hostapd-${radio}-files" (''
+      set -euo pipefail
 
-    # logging (debug level)
-    logger_syslog=-1
-    logger_syslog_level=${toString cfg.logLevel}
-    logger_stdout=-1
-    logger_stdout_level=${toString cfg.logLevel}
+      hostapd_config_file=/run/hostapd/${escapeShellArg radio}.hostapd.conf
+      rm -f "$hostapd_config_file"
+      cat > "$hostapd_config_file" <<EOF
+      # Radio base configuration: ${radio}
+      ################################
 
-    ctrl_interface=/run/hostapd
-    ctrl_interface_group=${cfg.group}
+      EOF
 
-    ${optionalString cfg.wpa ''
-      wpa=2
-      wpa_pairwise=CCMP
-      wpa_passphrase=${cfg.wpaPassphrase}
-    ''}
-    ${optionalString cfg.noScan "noscan=1"}
+      cat ${escapeShellArg (extraSettingsFormat.generate "hostapd-radio-${radio}-extra.conf" radioCfg.settings)} >> "$hostapd_config_file"
+      ${concatMapStrings (script: "${script} \"$hostapd_config_file\"\n") (attrValues radioCfg.dynamicConfigScripts)}
+    ''
+    + concatMapStrings (x: "${x}\n") (imap0 (i: f: f i)
+      (mapAttrsToList (bss: bssCfg: bssIdx: ''
+        ''\n# BSS configuration: ${bss}
 
-    ${cfg.extraConfig}
-  '' ;
+        mac_allow_file=/run/hostapd/${escapeShellArg bss}.mac.allow
+        rm -f "$mac_allow_file"
+        touch "$mac_allow_file"
 
-in
+        mac_deny_file=/run/hostapd/${escapeShellArg bss}.mac.deny
+        rm -f "$mac_deny_file"
+        touch "$mac_deny_file"
 
-{
-  ###### interface
+        cat ${writeBssHeader radio bss bssIdx} >> "$hostapd_config_file"
+        cat ${escapeShellArg (extraSettingsFormat.generate "hostapd-radio-${radio}-bss-${bss}-extra.conf" bssCfg.settings)} >> "$hostapd_config_file"
+        ${concatMapStrings (script: "${script} \"$hostapd_config_file\" \"$mac_allow_file\" \"$mac_deny_file\"\n") (attrValues bssCfg.dynamicConfigScripts)}
+      '') radioCfg.networks)));
 
-  options = {
+  runtimeConfigFiles = mapAttrsToList (radio: _: "/run/hostapd/${radio}.hostapd.conf") cfg.radios;
+in {
+  meta.maintainers = with maintainers; [ oddlama ];
 
+  options = {
     services.hostapd = {
+      enable = mkEnableOption (mdDoc ''
+        Whether to enable hostapd. hostapd is a user space daemon for access point and
+        authentication servers. It implements IEEE 802.11 access point management,
+        IEEE 802.1X/WPA/WPA2/EAP Authenticators, RADIUS client, EAP server, and RADIUS
+        authentication server.
+      '');
 
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Enable putting a wireless interface into infrastructure mode,
-          allowing other wireless devices to associate with the wireless
-          interface and do wireless networking. A simple access point will
-          {option}`enable hostapd.wpa`,
-          {option}`hostapd.wpaPassphrase`, and
-          {option}`hostapd.ssid`, as well as DHCP on the wireless
-          interface to provide IP addresses to the associated stations, and
-          NAT (from the wireless interface to an upstream interface).
-        '';
-      };
+      package = mkPackageOption pkgs "hostapd" {};
 
-      interface = mkOption {
-        example = "wlp2s0";
-        type = types.str;
-        description = lib.mdDoc ''
-          The interfaces {command}`hostapd` will use.
-        '';
-      };
+      radios = mkOption {
+        default = {};
+        example = literalExpression ''
+          {
+            # Simple 2.4GHz AP
+            wlp2s0 = {
+              # countryCode = "US";
+              networks.wlp2s0 = {
+                ssid = "AP 1";
+                authentication.saePasswords = [{ password = "a flakey password"; }]; # Use saePasswordsFile if possible.
+              };
+            };
 
-      noScan = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Do not scan for overlapping BSSs in HT40+/- mode.
-          Caution: turning this on will violate regulatory requirements!
-        '';
-      };
+            # WiFi 5 (5GHz) with two advertised networks
+            wlp3s0 = {
+              band = "5g";
+              channel = 0; # Enable automatic channel selection (ACS). Use only if your hardware supports it.
+              # countryCode = "US";
+              networks.wlp3s0 = {
+                ssid = "My AP";
+                authentication.saePasswords = [{ password = "a flakey password"; }]; # Use saePasswordsFile if possible.
+              };
+              networks.wlp3s0-1 = {
+                ssid = "Open AP with WiFi5";
+                authentication.mode = "none";
+              };
+            };
 
-      driver = mkOption {
-        default = "nl80211";
-        example = "hostapd";
-        type = types.str;
-        description = lib.mdDoc ''
-          Which driver {command}`hostapd` will use.
-          Most applications will probably use the default.
+            # Legacy WPA2 example
+            wlp4s0 = {
+              # countryCode = "US";
+              networks.wlp4s0 = {
+                ssid = "AP 2";
+                authentication = {
+                  mode = "wpa2-sha256";
+                  wpaPassword = "a flakey password"; # Use wpaPasswordFile if possible.
+                };
+              };
+            };
+          }
         '';
-      };
+        description = mdDoc ''
+          This option allows you to define APs for one or multiple physical radios.
+          At least one radio must be specified.
 
-      ssid = mkOption {
-        default = config.system.nixos.distroId;
-        defaultText = literalExpression "config.system.nixos.distroId";
-        example = "mySpecialSSID";
-        type = types.str;
-        description = lib.mdDoc "SSID to be used in IEEE 802.11 management frames.";
-      };
+          For each radio, hostapd requires a separate logical interface (like wlp3s0, wlp3s1, ...).
+          A default interface is usually be created automatically by your system, but to use
+          multiple radios of a single device, it may be required to create additional logical interfaces
+          for example by using {option}`networking.wlanInterfaces`.
 
-      hwMode = mkOption {
-        default = "g";
-        type = types.enum [ "a" "b" "g" ];
-        description = lib.mdDoc ''
-          Operation mode.
-          (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g).
+          Each physical radio can only support a single hardware-mode that is configured via
+          ({option}`services.hostapd.radios.<radio>.band`). To create a dual-band
+          or tri-band AP, you will have to use a device that has multiple physical radios
+          and supports configuring multiple APs (Refer to valid interface combinations in
+          {command}`iw list`).
         '';
-      };
+        type = types.attrsOf (types.submodule (radioSubmod: {
+          options = {
+            driver = mkOption {
+              default = "nl80211";
+              example = "none";
+              type = types.str;
+              description = mdDoc ''
+                The driver {command}`hostapd` will use.
+                {var}`nl80211` is used with all Linux mac80211 drivers.
+                {var}`none` is used if building a standalone RADIUS server that does
+                not control any wireless/wired driver.
+                Most applications will probably use the default.
+              '';
+            };
 
-      channel = mkOption {
-        default = 7;
-        example = 11;
-        type = types.int;
-        description = lib.mdDoc ''
-          Channel number (IEEE 802.11)
-          Please note that some drivers do not use this value from
-          {command}`hostapd` and the channel will need to be configured
-          separately with {command}`iwconfig`.
-        '';
-      };
+            noScan = mkOption {
+              type = types.bool;
+              default = false;
+              description = mdDoc ''
+                Disables scan for overlapping BSSs in HT40+/- mode.
+                Caution: turning this on will likely violate regulatory requirements!
+              '';
+            };
 
-      group = mkOption {
-        default = "wheel";
-        example = "network";
-        type = types.str;
-        description = lib.mdDoc ''
-          Members of this group can control {command}`hostapd`.
-        '';
-      };
+            countryCode = mkOption {
+              default = null;
+              example = "US";
+              type = types.nullOr types.str;
+              description = mdDoc ''
+                Country code (ISO/IEC 3166-1). Used to set regulatory domain.
+                Set as needed to indicate country in which device is operating.
+                This can limit available channels and transmit power.
+                These two octets are used as the first two octets of the Country String
+                (dot11CountryString).
 
-      wpa = mkOption {
-        type = types.bool;
-        default = true;
-        description = lib.mdDoc ''
-          Enable WPA (IEEE 802.11i/D3.0) to authenticate with the access point.
-        '';
-      };
+                Setting this will force you to also enable IEEE 802.11d and IEEE 802.11h.
 
-      wpaPassphrase = mkOption {
-        default = "my_sekret";
-        example = "any_64_char_string";
-        type = types.str;
-        description = lib.mdDoc ''
-          WPA-PSK (pre-shared-key) passphrase. Clients will need this
-          passphrase to associate with this access point.
-          Warning: This passphrase will get put into a world-readable file in
-          the Nix store!
-        '';
-      };
+                IEEE 802.11d: This advertises the countryCode and the set of allowed channels
+                and transmit power levels based on the regulatory limits.
 
-      logLevel = mkOption {
-        default = 2;
-        type = types.int;
-        description = lib.mdDoc ''
-          Levels (minimum value for logged events):
-          0 = verbose debugging
-          1 = debugging
-          2 = informational messages
-          3 = notification
-          4 = warning
-        '';
-      };
+                IEEE802.11h: This enables radar detection and DFS (Dynamic Frequency Selection)
+                support if available. DFS support is required on outdoor 5 GHz channels in most
+                countries of the world.
+              '';
+            };
 
-      countryCode = mkOption {
-        default = null;
-        example = "US";
-        type = with types; nullOr str;
-        description = lib.mdDoc ''
-          Country code (ISO/IEC 3166-1). Used to set regulatory domain.
-          Set as needed to indicate country in which device is operating.
-          This can limit available channels and transmit power.
-          These two octets are used as the first two octets of the Country String
-          (dot11CountryString).
-          If set this enables IEEE 802.11d. This advertises the countryCode and
-          the set of allowed channels and transmit power levels based on the
-          regulatory limits.
-        '';
-      };
+            band = mkOption {
+              default = "2g";
+              type = types.enum ["2g" "5g" "6g" "60g"];
+              description = mdDoc ''
+                Specifies the frequency band to use, possible values are 2g for 2.4 GHz,
+                5g for 5 GHz, 6g for 6 GHz and 60g for 60 GHz.
+              '';
+            };
+
+            channel = mkOption {
+              default = 7;
+              example = 11;
+              type = types.int;
+              description = mdDoc ''
+                The channel to operate on. Use 0 to enable ACS (Automatic Channel Selection).
+                Beware that not every device supports ACS in which case {command}`hostapd`
+                will fail to start.
+              '';
+            };
+
+            settings = mkOption {
+              default = {};
+              example = { acs_exclude_dfs = true; };
+              type = types.submodule {
+                freeformType = extraSettingsFormat.type;
+              };
+              description = mdDoc ''
+                Extra configuration options to put at the end of global initialization, before defining BSSs.
+                To find out which options are global and which are per-bss you have to read hostapd's source code,
+                which is non-trivial and not documented otherwise.
+
+                Lists will be converted to multiple definitions of the same key, and booleans to 0/1.
+                Otherwise, the inputs are not modified or checked for correctness.
+              '';
+            };
+
+            dynamicConfigScripts = mkOption {
+              default = {};
+              type = types.attrsOf types.path;
+              example = literalExpression ''
+                {
+                  exampleDynamicConfig = pkgs.writeShellScript "dynamic-config" '''
+                    HOSTAPD_CONFIG=$1
+
+                    cat >> "$HOSTAPD_CONFIG" << EOF
+                    # Add some dynamically generated statements here,
+                    # for example based on the physical adapter in use
+                    EOF
+                  ''';
+                }
+              '';
+              description = mdDoc ''
+                All of these scripts will be executed in lexicographical order before hostapd
+                is started, right after the global segment was generated and may dynamically
+                append global options the generated configuration file.
+
+                The first argument will point to the configuration file that you may append to.
+              '';
+            };
+
+            #### IEEE 802.11n (WiFi 4) related configuration
+
+            wifi4 = {
+              enable = mkOption {
+                default = true;
+                type = types.bool;
+                description = mdDoc ''
+                  Enables support for IEEE 802.11n (WiFi 4, HT).
+                  This is enabled by default, since the vase majority of devices
+                  are expected to support this.
+                '';
+              };
+
+              capabilities = mkOption {
+                type = types.listOf types.str;
+                default = ["HT40" "HT40-" "SHORT-GI-20" "SHORT-GI-40"];
+                example = ["LDPC" "HT40+" "HT40-" "GF" "SHORT-GI-20" "SHORT-GI-40" "TX-STBC" "RX-STBC1"];
+                description = mdDoc ''
+                  HT (High Throughput) capabilities given as a list of flags.
+                  Please refer to the hostapd documentation for allowed values and
+                  only set values supported by your physical adapter.
+
+                  The default contains common values supported by most adapters.
+                '';
+              };
+
+              require = mkOption {
+                default = false;
+                type = types.bool;
+                description = mdDoc "Require stations (clients) to support WiFi 4 (HT) and disassociate them if they don't.";
+              };
+            };
+
+            #### IEEE 802.11ac (WiFi 5) related configuration
+
+            wifi5 = {
+              enable = mkOption {
+                default = true;
+                type = types.bool;
+                description = mdDoc "Enables support for IEEE 802.11ac (WiFi 5, VHT)";
+              };
+
+              capabilities = mkOption {
+                type = types.listOf types.str;
+                default = [];
+                example = ["SHORT-GI-80" "TX-STBC-2BY1" "RX-STBC-1" "RX-ANTENNA-PATTERN" "TX-ANTENNA-PATTERN"];
+                description = mdDoc ''
+                  VHT (Very High Throughput) capabilities given as a list of flags.
+                  Please refer to the hostapd documentation for allowed values and
+                  only set values supported by your physical adapter.
+                '';
+              };
+
+              require = mkOption {
+                default = false;
+                type = types.bool;
+                description = mdDoc "Require stations (clients) to support WiFi 5 (VHT) and disassociate them if they don't.";
+              };
+
+              operatingChannelWidth = mkOption {
+                default = "20or40";
+                type = types.enum ["20or40" "80" "160" "80+80"];
+                apply = x:
+                  getAttr x {
+                    "20or40" = 0;
+                    "80" = 1;
+                    "160" = 2;
+                    "80+80" = 3;
+                  };
+                description = mdDoc ''
+                  Determines the operating channel width for VHT.
+
+                  - {var}`"20or40"`: 20 or 40 MHz operating channel width
+                  - {var}`"80"`: 80 MHz channel width
+                  - {var}`"160"`: 160 MHz channel width
+                  - {var}`"80+80"`: 80+80 MHz channel width
+                '';
+              };
+            };
+
+            #### IEEE 802.11ax (WiFi 6) related configuration
+
+            wifi6 = {
+              enable = mkOption {
+                default = false;
+                type = types.bool;
+                description = mdDoc "Enables support for IEEE 802.11ax (WiFi 6, HE)";
+              };
+
+              require = mkOption {
+                default = false;
+                type = types.bool;
+                description = mdDoc "Require stations (clients) to support WiFi 6 (HE) and disassociate them if they don't.";
+              };
+
+              singleUserBeamformer = mkOption {
+                default = false;
+                type = types.bool;
+                description = mdDoc "HE single user beamformer support";
+              };
+
+              singleUserBeamformee = mkOption {
+                default = false;
+                type = types.bool;
+                description = mdDoc "HE single user beamformee support";
+              };
+
+              multiUserBeamformer = mkOption {
+                default = false;
+                type = types.bool;
+                description = mdDoc "HE multi user beamformee support";
+              };
+
+              operatingChannelWidth = mkOption {
+                default = "20or40";
+                type = types.enum ["20or40" "80" "160" "80+80"];
+                apply = x:
+                  getAttr x {
+                    "20or40" = 0;
+                    "80" = 1;
+                    "160" = 2;
+                    "80+80" = 3;
+                  };
+                description = mdDoc ''
+                  Determines the operating channel width for HE.
+
+                  - {var}`"20or40"`: 20 or 40 MHz operating channel width
+                  - {var}`"80"`: 80 MHz channel width
+                  - {var}`"160"`: 160 MHz channel width
+                  - {var}`"80+80"`: 80+80 MHz channel width
+                '';
+              };
+            };
+
+            #### IEEE 802.11be (WiFi 7) related configuration
+
+            wifi7 = {
+              enable = mkOption {
+                default = false;
+                type = types.bool;
+                description = mdDoc ''
+                  Enables support for IEEE 802.11be (WiFi 7, EHT). This is currently experimental
+                  and requires you to manually enable CONFIG_IEEE80211BE when building hostapd.
+                '';
+              };
+
+              singleUserBeamformer = mkOption {
+                default = false;
+                type = types.bool;
+                description = mdDoc "EHT single user beamformer support";
+              };
+
+              singleUserBeamformee = mkOption {
+                default = false;
+                type = types.bool;
+                description = mdDoc "EHT single user beamformee support";
+              };
+
+              multiUserBeamformer = mkOption {
+                default = false;
+                type = types.bool;
+                description = mdDoc "EHT multi user beamformee support";
+              };
+
+              operatingChannelWidth = mkOption {
+                default = "20or40";
+                type = types.enum ["20or40" "80" "160" "80+80"];
+                apply = x:
+                  getAttr x {
+                    "20or40" = 0;
+                    "80" = 1;
+                    "160" = 2;
+                    "80+80" = 3;
+                  };
+                description = mdDoc ''
+                  Determines the operating channel width for EHT.
+
+                  - {var}`"20or40"`: 20 or 40 MHz operating channel width
+                  - {var}`"80"`: 80 MHz channel width
+                  - {var}`"160"`: 160 MHz channel width
+                  - {var}`"80+80"`: 80+80 MHz channel width
+                '';
+              };
+            };
+
+            #### BSS definitions
+
+            networks = mkOption {
+              default = {};
+              example = literalExpression ''
+                {
+                  wlp2s0 = {
+                    ssid = "Primary advertised network";
+                    authentication.saePasswords = [{ password = "a flakey password"; }]; # Use saePasswordsFile if possible.
+                  };
+                  wlp2s0-1 = {
+                    ssid = "Secondary advertised network (Open)";
+                    authentication.mode = "none";
+                  };
+                }
+              '';
+              description = mdDoc ''
+                This defines a BSS, colloquially known as a WiFi network.
+                You have to specify at least one.
+              '';
+              type = types.attrsOf (types.submodule (bssSubmod: {
+                options = {
+                  logLevel = mkOption {
+                    default = 2;
+                    type = types.int;
+                    description = mdDoc ''
+                      Levels (minimum value for logged events):
+                      0 = verbose debugging
+                      1 = debugging
+                      2 = informational messages
+                      3 = notification
+                      4 = warning
+                    '';
+                  };
+
+                  group = mkOption {
+                    default = "wheel";
+                    example = "network";
+                    type = types.str;
+                    description = mdDoc ''
+                      Members of this group can access the control socket for this interface.
+                    '';
+                  };
+
+                  utf8Ssid = mkOption {
+                    default = true;
+                    type = types.bool;
+                    description = mdDoc "Whether the SSID is to be interpreted using UTF-8 encoding.";
+                  };
 
-      extraConfig = mkOption {
-        default = "";
-        example = ''
-          auth_algo=0
-          ieee80211n=1
-          ht_capab=[HT40-][SHORT-GI-40][DSSS_CCK-40]
-          '';
-        type = types.lines;
-        description = lib.mdDoc "Extra configuration options to put in hostapd.conf.";
+                  ssid = mkOption {
+                    example = "❄️ cool ❄️";
+                    type = types.str;
+                    description = mdDoc "SSID to be used in IEEE 802.11 management frames.";
+                  };
+
+                  bssid = mkOption {
+                    type = types.nullOr types.str;
+                    default = null;
+                    example = "11:22:33:44:55:66";
+                    description = mdDoc ''
+                      Specifies the BSSID for this BSS. Usually determined automatically,
+                      but for now you have to manually specify them when using multiple BSS.
+                      Try assigning related addresses from the locally administered MAC address ranges,
+                      by reusing the hardware address but replacing the second nibble with 2, 6, A or E.
+                      (e.g. if real address is `XX:XX:XX:XX:XX`, try `X2:XX:XX:XX:XX:XX`, `X6:XX:XX:XX:XX:XX`, ...
+                      for the second, third, ... BSS)
+                    '';
+                  };
+
+                  macAcl = mkOption {
+                    default = "deny";
+                    type = types.enum ["deny" "allow" "radius"];
+                    apply = x:
+                      getAttr x {
+                        "deny" = 0;
+                        "allow" = 1;
+                        "radius" = 2;
+                      };
+                    description = mdDoc ''
+                      Station MAC address -based authentication. The following modes are available:
+
+                      - {var}`"deny"`: Allow unless listed in {option}`macDeny` (default)
+                      - {var}`"allow"`: Deny unless listed in {option}`macAllow`
+                      - {var}`"radius"`: Use external radius server, but check both {option}`macAllow` and {option}`macDeny` first
+
+                      Please note that this kind of access control requires a driver that uses
+                      hostapd to take care of management frame processing and as such, this can be
+                      used with driver=hostap or driver=nl80211, but not with driver=atheros.
+                    '';
+                  };
+
+                  macAllow = mkOption {
+                    type = types.listOf types.str;
+                    default = [];
+                    example = ["11:22:33:44:55:66"];
+                    description = mdDoc ''
+                      Specifies the MAC addresses to allow if {option}`macAcl` is set to {var}`"allow"` or {var}`"radius"`.
+                      These values will be world-readable in the Nix store. Values will automatically be merged with
+                      {option}`macAllowFile` if necessary.
+                    '';
+                  };
+
+                  macAllowFile = mkOption {
+                    type = types.nullOr types.path;
+                    default = null;
+                    description = mdDoc ''
+                      Specifies a file containing the MAC addresses to allow if {option}`macAcl` is set to {var}`"allow"` or {var}`"radius"`.
+                      The file should contain exactly one MAC address per line. Comments and empty lines are ignored,
+                      only lines starting with a valid MAC address will be considered (e.g. `11:22:33:44:55:66`) and
+                      any content after the MAC address is ignored.
+                    '';
+                  };
+
+                  macDeny = mkOption {
+                    type = types.listOf types.str;
+                    default = [];
+                    example = ["11:22:33:44:55:66"];
+                    description = mdDoc ''
+                      Specifies the MAC addresses to deny if {option}`macAcl` is set to {var}`"deny"` or {var}`"radius"`.
+                      These values will be world-readable in the Nix store. Values will automatically be merged with
+                      {option}`macDenyFile` if necessary.
+                    '';
+                  };
+
+                  macDenyFile = mkOption {
+                    type = types.nullOr types.path;
+                    default = null;
+                    description = mdDoc ''
+                      Specifies a file containing the MAC addresses to deny if {option}`macAcl` is set to {var}`"deny"` or {var}`"radius"`.
+                      The file should contain exactly one MAC address per line. Comments and empty lines are ignored,
+                      only lines starting with a valid MAC address will be considered (e.g. `11:22:33:44:55:66`) and
+                      any content after the MAC address is ignored.
+                    '';
+                  };
+
+                  ignoreBroadcastSsid = mkOption {
+                    default = "disabled";
+                    type = types.enum ["disabled" "empty" "clear"];
+                    apply = x:
+                      getAttr x {
+                        "disabled" = 0;
+                        "empty" = 1;
+                        "clear" = 2;
+                      };
+                    description = mdDoc ''
+                      Send empty SSID in beacons and ignore probe request frames that do not
+                      specify full SSID, i.e., require stations to know SSID. Note that this does
+                      not increase security, since your clients will then broadcast the SSID instead,
+                      which can increase congestion.
+
+                      - {var}`"disabled"`: Advertise ssid normally.
+                      - {var}`"empty"`: send empty (length=0) SSID in beacon and ignore probe request for broadcast SSID
+                      - {var}`"clear"`: clear SSID (ASCII 0), but keep the original length (this may be required with some
+                        legacy clients that do not support empty SSID) and ignore probe requests for broadcast SSID. Only
+                        use this if empty does not work with your clients.
+                    '';
+                  };
+
+                  apIsolate = mkOption {
+                    default = false;
+                    type = types.bool;
+                    description = mdDoc ''
+                      Isolate traffic between stations (clients) and prevent them from
+                      communicating with each other.
+                    '';
+                  };
+
+                  settings = mkOption {
+                    default = {};
+                    example = { multi_ap = true; };
+                    type = types.submodule {
+                      freeformType = extraSettingsFormat.type;
+                    };
+                    description = mdDoc ''
+                      Extra configuration options to put at the end of this BSS's defintion in the
+                      hostapd.conf for the associated interface. To find out which options are global
+                      and which are per-bss you have to read hostapd's source code, which is non-trivial
+                      and not documented otherwise.
+
+                      Lists will be converted to multiple definitions of the same key, and booleans to 0/1.
+                      Otherwise, the inputs are not modified or checked for correctness.
+                    '';
+                  };
+
+                  dynamicConfigScripts = mkOption {
+                    default = {};
+                    type = types.attrsOf types.path;
+                    example = literalExpression ''
+                      {
+                        exampleDynamicConfig = pkgs.writeShellScript "dynamic-config" '''
+                          HOSTAPD_CONFIG=$1
+                          # These always exist, but may or may not be used depending on the actual configuration
+                          MAC_ALLOW_FILE=$2
+                          MAC_DENY_FILE=$3
+
+                          cat >> "$HOSTAPD_CONFIG" << EOF
+                          # Add some dynamically generated statements here
+                          EOF
+                        ''';
+                      }
+                    '';
+                    description = mdDoc ''
+                      All of these scripts will be executed in lexicographical order before hostapd
+                      is started, right after the bss segment was generated and may dynamically
+                      append bss options to the generated configuration file.
+
+                      The first argument will point to the configuration file that you may append to.
+                      The second and third argument will point to this BSS's MAC allow and MAC deny file respectively.
+                    '';
+                  };
+
+                  #### IEEE 802.11i (WPA) configuration
+
+                  authentication = {
+                    mode = mkOption {
+                      default = "wpa3-sae";
+                      type = types.enum ["none" "wpa2-sha256" "wpa3-sae-transition" "wpa3-sae"];
+                      description = mdDoc ''
+                        Selects the authentication mode for this AP.
+
+                        - {var}`"none"`: Don't configure any authentication. This will disable wpa alltogether
+                          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
+                          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.
+                          You will have to specify both {option}`wpaPassword` and {option}`saePasswords` (or one of their alternatives).
+                        - {var}`"wpa3-sae"`: Use WPA3-Personal (SAE). This is currently the recommended way to
+                          setup a secured WiFi AP (as of March 2023) and therefore the default. Passwords are set
+                          using either {option}`saePasswords` or preferably {option}`saePasswordsFile`.
+                      '';
+                    };
+
+                    pairwiseCiphers = mkOption {
+                      default = ["CCMP"];
+                      example = ["CCMP-256" "GCMP-256"];
+                      type = types.listOf types.str;
+                      description = mdDoc ''
+                        Set of accepted cipher suites (encryption algorithms) for pairwise keys (unicast packets).
+                        By default this allows just CCMP, which is the only commonly supported secure option.
+                        Use {option}`enableRecommendedPairwiseCiphers` to also enable newer recommended ciphers.
+
+                        Please refer to the hostapd documentation for allowed values. Generally, only
+                        CCMP or GCMP modes should be considered safe options. Most devices support CCMP while
+                        GCMP is often only available with devices supporting WiFi 5 (IEEE 802.11ac) or higher.
+                      '';
+                    };
+
+                    enableRecommendedPairwiseCiphers = mkOption {
+                      default = false;
+                      example = true;
+                      type = types.bool;
+                      description = mdDoc ''
+                        Additionally enable the recommended set of pairwise ciphers.
+                        This enables newer secure ciphers, additionally to those defined in {option}`pairwiseCiphers`.
+                        You will have to test whether your hardware supports these by trial-and-error, because
+                        even if `iw list` indicates hardware support, your driver might not expose it.
+
+                        Beware {command}`hostapd` will most likely not return a useful error message in case
+                        this is enabled despite the driver or hardware not supporting the newer ciphers.
+                        Look out for messages like `Failed to set beacon parameters`.
+                      '';
+                    };
+
+                    wpaPassword = mkOption {
+                      default = null;
+                      example = "a flakey password";
+                      type = types.nullOr types.str;
+                      description = mdDoc ''
+                        Sets the password for WPA-PSK that will be converted to the pre-shared key.
+                        The password length must be in the range [8, 63] characters. While some devices
+                        may allow arbitrary characters (such as UTF-8) to be used, but the standard specifies
+                        that each character in the passphrase must be an ASCII character in the range [0x20, 0x7e]
+                        (IEEE Std. 802.11i-2004, Annex H.4.1). Use emojis at your own risk.
+
+                        Not used when {option}`mode` is {var}`"wpa3-sae"`.
+
+                        Warning: This password will get put into a world-readable file in the Nix store!
+                        Using {option}`wpaPasswordFile` or {option}`wpaPskFile` instead is recommended.
+                      '';
+                    };
+
+                    wpaPasswordFile = mkOption {
+                      default = null;
+                      type = types.nullOr types.path;
+                      description = mdDoc ''
+                        Sets the password for WPA-PSK. Follows the same rules as {option}`wpaPassword`,
+                        but reads the password from the given file to prevent the password from being
+                        put into the Nix store.
+
+                        Not used when {option}`mode` is {var}`"wpa3-sae"`.
+                      '';
+                    };
+
+                    wpaPskFile = mkOption {
+                      default = null;
+                      type = types.nullOr types.path;
+                      description = mdDoc ''
+                        Sets the password(s) for WPA-PSK. Similar to {option}`wpaPasswordFile`,
+                        but additionally allows specifying multiple passwords, and some other options.
+
+                        Each line, except for empty lines and lines starting with #, must contain a
+                        MAC address and either a 64-hex-digit PSK or a password separated with a space.
+                        The password must follow the same rules as outlined in {option}`wpaPassword`.
+                        The special MAC address `00:00:00:00:00:00` can be used to configure PSKs
+                        that any client can use.
+
+                        An optional key identifier can be added by prefixing the line with `keyid=<keyid_string>`
+                        An optional VLAN ID can be specified by prefixing the line with `vlanid=<VLAN ID>`.
+                        An optional WPS tag can be added by prefixing the line with `wps=<0/1>` (default: 0).
+                        Any matching entry with that tag will be used when generating a PSK for a WPS Enrollee
+                        instead of generating a new random per-Enrollee PSK.
+
+                        Not used when {option}`mode` is {var}`"wpa3-sae"`.
+                      '';
+                    };
+
+                    saePasswords = mkOption {
+                      default = [];
+                      example = literalExpression ''
+                        [
+                          # Any client may use these passwords
+                          { password = "Wi-Figure it out"; }
+                          { password = "second password for everyone"; mac = "ff:ff:ff:ff:ff:ff"; }
+
+                          # Only the client with MAC-address 11:22:33:44:55:66 can use this password
+                          { password = "sekret pazzword"; mac = "11:22:33:44:55:66"; }
+                        ]
+                      '';
+                      description = mdDoc ''
+                        Sets allowed passwords for WPA3-SAE.
+
+                        The last matching (based on peer MAC address and identifier) entry is used to
+                        select which password to use. An empty string has the special meaning of
+                        removing all previously added entries.
+
+                        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"`.
+                      '';
+                      type = types.listOf (types.submodule {
+                        options = {
+                          password = mkOption {
+                            example = "a flakey password";
+                            type = types.str;
+                            description = mdDoc ''
+                              The password for this entry. SAE technically imposes no restrictions on
+                              password length or character set. But due to limitations of {command}`hostapd`'s
+                              config file format, a true newline character cannot be parsed.
+
+                              Warning: This password will get put into a world-readable file in
+                              the Nix store! Using {option}`wpaPasswordFile` or {option}`wpaPskFile` is recommended.
+                            '';
+                          };
+
+                          mac = mkOption {
+                            default = null;
+                            example = "11:22:33:44:55:66";
+                            type = types.nullOr types.str;
+                            description = mdDoc ''
+                              If this attribute is not included, or if is set to the wildcard address (`ff:ff:ff:ff:ff:ff`),
+                              the entry is available for any station (client) to use. If a specific peer MAC address is included,
+                              only a station with that MAC address is allowed to use the entry.
+                            '';
+                          };
+
+                          vlanid = mkOption {
+                            default = null;
+                            example = 1;
+                            type = types.nullOr types.int;
+                            description = mdDoc "If this attribute is given, all clients using this entry will get tagged with the given VLAN ID.";
+                          };
+
+                          pk = mkOption {
+                            default = null;
+                            example = "";
+                            type = types.nullOr types.str;
+                            description = mdDoc ''
+                              If this attribute is given, SAE-PK will be enabled for this connection.
+                              This prevents evil-twin attacks, but a public key is required additionally to connect.
+                              (Essentially adds pubkey authentication such that the client can verify identity of the AP)
+                            '';
+                          };
+
+                          id = mkOption {
+                            default = null;
+                            example = "";
+                            type = types.nullOr types.str;
+                            description = mdDoc ''
+                              If this attribute is given with non-zero length, it will set the password identifier
+                              for this entry. It can then only be used with that identifier.
+                            '';
+                          };
+                        };
+                      });
+                    };
+
+                    saePasswordsFile = mkOption {
+                      default = null;
+                      type = types.nullOr types.path;
+                      description = mdDoc ''
+                        Sets the password for WPA3-SAE. Follows the same rules as {option}`saePasswords`,
+                        but reads the entries from the given file to prevent them from being
+                        put into the Nix store.
+
+                        One entry per line, empty lines and lines beginning with # will be ignored.
+                        Each line must match the following format, although the order of optional
+                        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"`.
+                      '';
+                    };
+
+                    saeAddToMacAllow = mkOption {
+                      type = types.bool;
+                      default = false;
+                      description = mdDoc ''
+                        If set, all sae password entries that have a non-wildcard MAC associated to
+                        them will additionally be used to populate the MAC allow list. This is
+                        additional to any entries set via {option}`macAllow` or {option}`macAllowFile`.
+                      '';
+                    };
+                  };
+
+                  managementFrameProtection = mkOption {
+                    default = "required";
+                    type = types.enum ["disabled" "optional" "required"];
+                    apply = x:
+                      getAttr x {
+                        "disabled" = 0;
+                        "optional" = 1;
+                        "required" = 2;
+                      };
+                    description = mdDoc ''
+                      Management frame protection (MFP) authenticates management frames
+                      to prevent deauthentication (or related) attacks.
+
+                      - {var}`"disabled"`: No management frame protection
+                      - {var}`"optional"`: Use MFP if a connection allows it
+                      - {var}`"required"`: Force MFP for all clients
+                    '';
+                  };
+                };
+
+                config = let
+                  bssCfg = bssSubmod.config;
+                  pairwiseCiphers =
+                    concatStringsSep " " (unique (bssCfg.authentication.pairwiseCiphers
+                      ++ optionals bssCfg.authentication.enableRecommendedPairwiseCiphers ["CCMP" "CCMP-256" "GCMP" "GCMP-256"]));
+                in {
+                  settings = {
+                    ssid = bssCfg.ssid;
+                    utf8_ssid = bssCfg.ssid;
+
+                    logger_syslog = mkDefault (-1);
+                    logger_syslog_level = bssCfg.logLevel;
+                    logger_stdout = mkDefault (-1);
+                    logger_stdout_level = bssCfg.logLevel;
+                    ctrl_interface = mkDefault "/run/hostapd";
+                    ctrl_interface_group = bssCfg.group;
+
+                    macaddr_acl = bssCfg.macAcl;
+
+                    ignore_broadcast_ssid = bssCfg.ignoreBroadcastSsid;
+
+                    # IEEE 802.11i (authentication) related configuration
+                    # Encrypt management frames to protect against deauthentication and similar attacks
+                    ieee80211w = bssCfg.managementFrameProtection;
+
+                    # Only allow WPA by default and disable insecure WEP
+                    auth_algs = mkDefault 1;
+                    # Always enable QoS, which is required for 802.11n and above
+                    wmm_enabled = mkDefault true;
+                    ap_isolate = bssCfg.apIsolate;
+
+                    sae_password = flip map bssCfg.authentication.saePasswords (
+                      entry:
+                        entry.password
+                        + optionalString (entry.mac != null) "|mac=${entry.mac}"
+                        + optionalString (entry.vlanid != null) "|vlanid=${toString entry.vlanid}"
+                        + optionalString (entry.pk != null) "|pk=${entry.pk}"
+                        + optionalString (entry.id != null) "|id=${entry.id}"
+                    );
+                  } // optionalAttrs (bssCfg.bssid != null) {
+                    bssid = bssCfg.bssid;
+                  } // optionalAttrs (bssCfg.macAllow != [] || bssCfg.macAllowFile != null || bssCfg.authentication.saeAddToMacAllow) {
+                    accept_mac_file = "/run/hostapd/${bssCfg._module.args.name}.mac.allow";
+                  } // optionalAttrs (bssCfg.macDeny != [] || bssCfg.macDenyFile != null) {
+                    deny_mac_file = "/run/hostapd/${bssCfg._module.args.name}.mac.deny";
+                  } // optionalAttrs (bssCfg.authentication.mode == "none") {
+                    wpa = mkDefault 0;
+                  } // optionalAttrs (bssCfg.authentication.mode == "wpa3-sae") {
+                    wpa = 2;
+                    wpa_key_mgmt = "SAE";
+                    # Derive PWE using both hunting-and-pecking loop and hash-to-element
+                    sae_pwe = 2;
+                    # Prevent downgrade attacks by indicating to clients that they should
+                    # disable any transition modes from now on.
+                    transition_disable = "0x01";
+                  } // optionalAttrs (bssCfg.authentication.mode == "wpa3-sae-transition") {
+                    wpa = 2;
+                    wpa_key_mgmt = "WPA-PSK-SHA256 SAE";
+                  } // optionalAttrs (bssCfg.authentication.mode == "wpa2-sha256") {
+                    wpa = 2;
+                    wpa_key_mgmt = "WPA-PSK-SHA256";
+                  } // optionalAttrs (bssCfg.authentication.mode != "none") {
+                    wpa_pairwise = pairwiseCiphers;
+                    rsn_pairwise = pairwiseCiphers;
+                  } // optionalAttrs (bssCfg.authentication.wpaPassword != null) {
+                    wpa_passphrase = bssCfg.authentication.wpaPassword;
+                  } // optionalAttrs (bssCfg.authentication.wpaPskFile != null) {
+                    wpa_psk_file = bssCfg.authentication.wpaPskFile;
+                  };
+
+                  dynamicConfigScripts = let
+                    # All MAC addresses from SAE entries that aren't the wildcard address
+                    saeMacs = filter (mac: mac != null && (toLower mac) != "ff:ff:ff:ff:ff:ff") (map (x: x.mac) bssCfg.authentication.saePasswords);
+                  in {
+                    "20-addMacAllow" = mkIf (bssCfg.macAllow != []) (pkgs.writeShellScript "add-mac-allow" ''
+                      MAC_ALLOW_FILE=$2
+                      cat >> "$MAC_ALLOW_FILE" <<EOF
+                      ${concatStringsSep "\n" bssCfg.macAllow}
+                      EOF
+                    '');
+                    "20-addMacAllowFile" = mkIf (bssCfg.macAllowFile != null) (pkgs.writeShellScript "add-mac-allow-file" ''
+                      MAC_ALLOW_FILE=$2
+                      grep -Eo '^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})' ${escapeShellArg bssCfg.macAllowFile} >> "$MAC_ALLOW_FILE"
+                    '');
+                    "20-addMacAllowFromSae" = mkIf (bssCfg.authentication.saeAddToMacAllow && saeMacs != []) (pkgs.writeShellScript "add-mac-allow-from-sae" ''
+                      MAC_ALLOW_FILE=$2
+                      cat >> "$MAC_ALLOW_FILE" <<EOF
+                      ${concatStringsSep "\n" saeMacs}
+                      EOF
+                    '');
+                    # Populate mac allow list from saePasswordsFile
+                    # (filter for lines with mac=;  exclude commented lines; filter for real mac-addresses; strip mac=)
+                    "20-addMacAllowFromSaeFile" = mkIf (bssCfg.authentication.saeAddToMacAllow && bssCfg.authentication.saePasswordsFile != null) (pkgs.writeShellScript "add-mac-allow-from-sae-file" ''
+                      MAC_ALLOW_FILE=$2
+                      grep mac= ${escapeShellArg bssCfg.authentication.saePasswordsFile} \
+                        | grep -v '\s*#' \
+                        | grep -Eo 'mac=([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})' \
+                        | sed 's|^mac=||' >> "$MAC_ALLOW_FILE"
+                    '');
+                    "20-addMacDeny" = mkIf (bssCfg.macDeny != []) (pkgs.writeShellScript "add-mac-deny" ''
+                      MAC_DENY_FILE=$3
+                      cat >> "$MAC_DENY_FILE" <<EOF
+                      ${concatStringsSep "\n" bssCfg.macDeny}
+                      EOF
+                    '');
+                    "20-addMacDenyFile" = mkIf (bssCfg.macDenyFile != null) (pkgs.writeShellScript "add-mac-deny-file" ''
+                      MAC_DENY_FILE=$3
+                      grep -Eo '^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})' ${escapeShellArg bssCfg.macDenyFile} >> "$MAC_DENY_FILE"
+                    '');
+                    # Add wpa_passphrase from file
+                    "20-wpaPasswordFile" = mkIf (bssCfg.authentication.wpaPasswordFile != null) (pkgs.writeShellScript "wpa-password-file" ''
+                      HOSTAPD_CONFIG_FILE=$1
+                      cat >> "$HOSTAPD_CONFIG_FILE" <<EOF
+                      wpa_passphrase=$(cat ${escapeShellArg bssCfg.authentication.wpaPasswordFile})
+                      EOF
+                    '');
+                    # Add sae passwords from file
+                    "20-saePasswordsFile" = mkIf (bssCfg.authentication.saePasswordsFile != null) (pkgs.writeShellScript "sae-passwords-file" ''
+                      HOSTAPD_CONFIG_FILE=$1
+                      grep -v '\s*#' ${escapeShellArg bssCfg.authentication.saePasswordsFile} \
+                        | sed 's/^/sae_password=/' >> "$HOSTAPD_CONFIG_FILE"
+                    '');
+                  };
+                };
+              }));
+            };
+          };
+
+          config.settings = let
+            radioCfg = radioSubmod.config;
+          in {
+            driver = radioCfg.driver;
+            hw_mode = {
+              "2g" = "g";
+              "5g" = "a";
+              "6g" = "a";
+              "60g" = "ad";
+            }.${radioCfg.band};
+            channel = radioCfg.channel;
+            noscan = radioCfg.noScan;
+          } // optionalAttrs (radioCfg.countryCode != null) {
+            country_code = radioCfg.countryCode;
+            # IEEE 802.11d: Limit to frequencies allowed in country
+            ieee80211d = true;
+            # IEEE 802.11h: Enable radar detection and DFS (Dynamic Frequency Selection)
+            ieee80211h = true;
+          } // optionalAttrs radioCfg.wifi4.enable {
+            # IEEE 802.11n (WiFi 4) related configuration
+            ieee80211n = true;
+            require_ht = radioCfg.wifi4.require;
+            ht_capab = concatMapStrings (x: "[${x}]") radioCfg.wifi4.capabilities;
+          } // optionalAttrs radioCfg.wifi5.enable {
+            # IEEE 802.11ac (WiFi 5) related configuration
+            ieee80211ac = true;
+            require_vht = radioCfg.wifi5.require;
+            vht_oper_chwidth = radioCfg.wifi5.operatingChannelWidth;
+            vht_capab = concatMapStrings (x: "[${x}]") radioCfg.wifi5.capabilities;
+          } // optionalAttrs radioCfg.wifi6.enable {
+            # IEEE 802.11ax (WiFi 6) related configuration
+            ieee80211ax = true;
+            require_he = mkIf radioCfg.wifi6.require true;
+            he_oper_chwidth = radioCfg.wifi6.operatingChannelWidth;
+            he_su_beamformer = radioCfg.wifi6.singleUserBeamformer;
+            he_su_beamformee = radioCfg.wifi6.singleUserBeamformee;
+            he_mu_beamformer = radioCfg.wifi6.multiUserBeamformer;
+          } // optionalAttrs radioCfg.wifi7.enable {
+            # IEEE 802.11be (WiFi 7) related configuration
+            ieee80211be = true;
+            eht_oper_chwidth = radioCfg.wifi7.operatingChannelWidth;
+            eht_su_beamformer = radioCfg.wifi7.singleUserBeamformer;
+            eht_su_beamformee = radioCfg.wifi7.singleUserBeamformee;
+            eht_mu_beamformer = radioCfg.wifi7.multiUserBeamformer;
+          };
+        }));
       };
     };
   };
 
+  imports = let
+    renamedOptionMessage = message: ''
+      ${message}
+      Refer to the documentation of `services.hostapd.radios` for an example and more information.
+    '';
+  in [
+    (mkRemovedOptionModule ["services" "hostapd" "interface"]
+      (renamedOptionMessage "All other options for this interface are now set via `services.hostapd.radios.«interface».*`."))
+
+    (mkRemovedOptionModule ["services" "hostapd" "driver"]
+      (renamedOptionMessage "It has been replaced by `services.hostapd.radios.«interface».driver`."))
+    (mkRemovedOptionModule ["services" "hostapd" "noScan"]
+      (renamedOptionMessage "It has been replaced by `services.hostapd.radios.«interface».noScan`."))
+    (mkRemovedOptionModule ["services" "hostapd" "countryCode"]
+      (renamedOptionMessage "It has been replaced by `services.hostapd.radios.«interface».countryCode`."))
+    (mkRemovedOptionModule ["services" "hostapd" "hwMode"]
+      (renamedOptionMessage "It has been replaced by `services.hostapd.radios.«interface».band`."))
+    (mkRemovedOptionModule ["services" "hostapd" "channel"]
+      (renamedOptionMessage "It has been replaced by `services.hostapd.radios.«interface».channel`."))
+    (mkRemovedOptionModule ["services" "hostapd" "extraConfig"]
+      (renamedOptionMessage ''
+        It has been replaced by `services.hostapd.radios.«interface».settings` and
+        `services.hostapd.radios.«interface».networks.«network».settings` respectively
+        for per-radio and per-network extra configuration. The module now supports a lot more
+        options inherently, so please re-check whether using settings is still necessary.''))
+
+    (mkRemovedOptionModule ["services" "hostapd" "logLevel"]
+      (renamedOptionMessage "It has been replaced by `services.hostapd.radios.«interface».networks.«network».logLevel`."))
+    (mkRemovedOptionModule ["services" "hostapd" "group"]
+      (renamedOptionMessage "It has been replaced by `services.hostapd.radios.«interface».networks.«network».group`."))
+    (mkRemovedOptionModule ["services" "hostapd" "ssid"]
+      (renamedOptionMessage "It has been replaced by `services.hostapd.radios.«interface».networks.«network».ssid`."))
 
-  ###### implementation
+    (mkRemovedOptionModule ["services" "hostapd" "wpa"]
+      (renamedOptionMessage "It has been replaced by `services.hostapd.radios.«interface».networks.«network».authentication.mode`."))
+    (mkRemovedOptionModule ["services" "hostapd" "wpaPassphrase"]
+      (renamedOptionMessage ''
+        It has been replaced by `services.hostapd.radios.«interface».networks.«network».authentication.wpaPassword`.
+        While upgrading your config, please consider using the newer SAE authentication scheme
+        and one of the new `passwordFile`-like options to avoid putting the password into the world readable nix-store.''))
+  ];
 
   config = mkIf cfg.enable {
+    assertions =
+      [
+        {
+          assertion = cfg.radios != {};
+          message = "At least one radio must be configured with hostapd!";
+        }
+      ]
+      # Radio warnings
+      ++ (concatLists (mapAttrsToList (
+          radio: radioCfg:
+            [
+              {
+                assertion = radioCfg.networks != {};
+                message = "hostapd radio ${radio}: At least one network must be configured!";
+              }
+              # XXX: There could be many more useful assertions about (band == xy) -> ensure other required settings.
+              # see https://github.com/openwrt/openwrt/blob/539cb5389d9514c99ec1f87bd4465f77c7ed9b93/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh#L158
+              {
+                assertion = length (filter (bss: bss == radio) (attrNames radioCfg.networks)) == 1;
+                message = ''hostapd radio ${radio}: Exactly one network must be named like the radio, for reasons internal to hostapd.'';
+              }
+              {
+                assertion = (radioCfg.wifi4.enable && builtins.elem "HT40-" radioCfg.wifi4.capabilities) -> radioCfg.channel != 0;
+                message = ''hostapd radio ${radio}: using ACS (channel = 0) together with HT40- (wifi4.capabilities) is unsupported by hostapd'';
+              }
+            ]
+            # BSS warnings
+            ++ (concatLists (mapAttrsToList (bss: bssCfg: let
+                auth = bssCfg.authentication;
+                countWpaPasswordDefinitions = count (x: x != null) [
+                  auth.wpaPassword
+                  auth.wpaPasswordFile
+                  auth.wpaPskFile
+                ];
+              in [
+                {
+                  assertion = hasPrefix radio bss;
+                  message = "hostapd radio ${radio} bss ${bss}: The bss (network) name ${bss} is invalid. It must be prefixed by the radio name for reasons internal to hostapd. A valid name would be e.g. ${radio}, ${radio}-1, ...";
+                }
+                {
+                  assertion = (length (attrNames radioCfg.networks) > 1) -> (bssCfg.bssid != null);
+                  message = ''hostapd radio ${radio} bss ${bss}: bssid must be specified manually (for now) since this radio uses multiple BSS.'';
+                }
+                {
+                  assertion = auth.mode == "wpa3-sae" -> bssCfg.managementFrameProtection == 2;
+                  message = ''hostapd radio ${radio} bss ${bss}: uses WPA3-SAE which requires managementFrameProtection="required"'';
+                }
+                {
+                  assertion = auth.mode == "wpa3-sae-transition" -> bssCfg.managementFrameProtection != 0;
+                  message = ''hostapd radio ${radio} bss ${bss}: uses WPA3-SAE in transition mode with WPA2-SHA256, which requires managementFrameProtection="optional" or ="required"'';
+                }
+                {
+                  assertion = countWpaPasswordDefinitions <= 1;
+                  message = ''hostapd radio ${radio} bss ${bss}: must use at most one WPA password option (wpaPassword, wpaPasswordFile, wpaPskFile)'';
+                }
+                {
+                  assertion = auth.wpaPassword != null -> (stringLength auth.wpaPassword >= 8 && stringLength auth.wpaPassword <= 63);
+                  message = ''hostapd radio ${radio} bss ${bss}: uses a wpaPassword of invalid length (must be in [8,63]).'';
+                }
+                {
+                  assertion = auth.saePasswords == [] || auth.saePasswordsFile == null;
+                  message = ''hostapd radio ${radio} bss ${bss}: must use only one SAE password option (saePasswords or saePasswordsFile)'';
+                }
+                {
+                  assertion = auth.mode == "wpa3-sae" -> (auth.saePasswords != [] || auth.saePasswordsFile != null);
+                  message = ''hostapd radio ${radio} bss ${bss}: uses WPA3-SAE which requires defining a sae password option'';
+                }
+                {
+                  assertion = auth.mode == "wpa3-sae-transition" -> (auth.saePasswords != [] || auth.saePasswordsFile != null) && countWpaPasswordDefinitions == 1;
+                  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'';
+                }
+              ])
+              radioCfg.networks))
+        )
+        cfg.radios));
 
-    environment.systemPackages =  [ pkgs.hostapd ];
+    environment.systemPackages = [cfg.package];
 
-    services.udev.packages = optionals (cfg.countryCode != null) [ pkgs.crda ];
+    services.udev.packages = with pkgs; [crda];
 
-    systemd.services.hostapd =
-      { description = "hostapd wireless AP";
+    systemd.services.hostapd = {
+      description = "IEEE 802.11 Host Access-Point Daemon";
 
-        path = [ pkgs.hostapd ];
-        after = [ "sys-subsystem-net-devices-${escapedInterface}.device" ];
-        bindsTo = [ "sys-subsystem-net-devices-${escapedInterface}.device" ];
-        requiredBy = [ "network-link-${cfg.interface}.service" ];
-        wantedBy = [ "multi-user.target" ];
+      path = [cfg.package];
+      after = map (radio: "sys-subsystem-net-devices-${utils.escapeSystemdPath radio}.device") (attrNames cfg.radios);
+      bindsTo = map (radio: "sys-subsystem-net-devices-${utils.escapeSystemdPath radio}.device") (attrNames cfg.radios);
+      wantedBy = ["multi-user.target"];
 
-        serviceConfig =
-          { ExecStart = "${pkgs.hostapd}/bin/hostapd ${configFile}";
-            Restart = "always";
-          };
+      # Create merged configuration and acl files for each radio (and their bss's) prior to starting
+      preStart = concatStringsSep "\n" (mapAttrsToList makeRadioRuntimeFiles cfg.radios);
+
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/hostapd ${concatStringsSep " " runtimeConfigFiles}";
+        Restart = "always";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        RuntimeDirectory = "hostapd";
+
+        # Hardening
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        DevicePolicy = "closed";
+        DeviceAllow = "/dev/rfkill rw";
+        NoNewPrivileges = true;
+        PrivateUsers = false; # hostapd requires true root access.
+        PrivateTmp = 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"
+          "AF_UNIX"
+          "AF_PACKET"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "~@privileged"
+          "@chown"
+        ];
+        UMask = "0077";
       };
+    };
   };
 }
diff --git a/nixpkgs/nixos/modules/services/networking/i2pd.nix b/nixpkgs/nixos/modules/services/networking/i2pd.nix
index 3f6cb97296b5..c940324ad096 100644
--- a/nixpkgs/nixos/modules/services/networking/i2pd.nix
+++ b/nixpkgs/nixos/modules/services/networking/i2pd.nix
@@ -169,15 +169,15 @@ let
         (boolOpt "enabled" proto.enable)
         (strOpt "address" proto.address)
         (intOpt "port" proto.port)
-        ] ++ (if proto ? keys then optionalNullString "keys" proto.keys else [])
-        ++ (if proto ? auth then optionalNullBool "auth" proto.auth else [])
-        ++ (if proto ? user then optionalNullString "user" proto.user else [])
-        ++ (if proto ? pass then optionalNullString "pass" proto.pass else [])
-        ++ (if proto ? strictHeaders then optionalNullBool "strictheaders" proto.strictHeaders else [])
-        ++ (if proto ? hostname then optionalNullString "hostname" proto.hostname else [])
-        ++ (if proto ? outproxy then optionalNullString "outproxy" proto.outproxy else [])
-        ++ (if proto ? outproxyPort then optionalNullInt "outproxyport" proto.outproxyPort else [])
-        ++ (if proto ? outproxyEnable then optionalNullBool "outproxy.enabled" proto.outproxyEnable else []);
+        ] ++ (optionals (proto ? keys) (optionalNullString "keys" proto.keys))
+        ++ (optionals (proto ? auth) (optionalNullBool "auth" proto.auth))
+        ++ (optionals (proto ? user) (optionalNullString "user" proto.user))
+        ++ (optionals (proto ? pass) (optionalNullString "pass" proto.pass))
+        ++ (optionals (proto ? strictHeaders) (optionalNullBool "strictheaders" proto.strictHeaders))
+        ++ (optionals (proto ? hostname) (optionalNullString "hostname" proto.hostname))
+        ++ (optionals (proto ? outproxy) (optionalNullString "outproxy" proto.outproxy))
+        ++ (optionals (proto ? outproxyPort) (optionalNullInt "outproxyport" proto.outproxyPort))
+        ++ (optionals (proto ? outproxyEnable) (optionalNullBool "outproxy.enabled" proto.outproxyEnable));
         in (concatStringsSep "\n" protoOpts)
       ));
   in
@@ -192,21 +192,14 @@ let
         "type = client"
         (intOpt "port" tun.port)
         (strOpt "destination" tun.destination)
-        ] ++ (if tun ? destinationPort then optionalNullInt "destinationport" tun.destinationPort else [])
-        ++ (if tun ? keys then
-            optionalNullString "keys" tun.keys else [])
-        ++ (if tun ? address then
-            optionalNullString "address" tun.address else [])
-        ++ (if tun ? inbound.length then
-            optionalNullInt "inbound.length" tun.inbound.length else [])
-        ++ (if tun ? inbound.quantity then
-            optionalNullInt "inbound.quantity" tun.inbound.quantity else [])
-        ++ (if tun ? outbound.length then
-            optionalNullInt "outbound.length" tun.outbound.length else [])
-        ++ (if tun ? outbound.quantity then
-            optionalNullInt "outbound.quantity" tun.outbound.quantity else [])
-        ++ (if tun ? crypto.tagsToSend then
-            optionalNullInt "crypto.tagstosend" tun.crypto.tagsToSend else []);
+        ] ++ (optionals (tun ? destinationPort) (optionalNullInt "destinationport" tun.destinationPort))
+        ++ (optionals (tun ? keys) (optionalNullString "keys" tun.keys))
+        ++ (optionals (tun ? address) (optionalNullString "address" tun.address))
+        ++ (optionals (tun ? inbound.length) (optionalNullInt "inbound.length" tun.inbound.length))
+        ++ (optionals (tun ? inbound.quantity) (optionalNullInt "inbound.quantity" tun.inbound.quantity))
+        ++ (optionals (tun ? outbound.length) (optionalNullInt "outbound.length" tun.outbound.length))
+        ++ (optionals (tun ? outbound.quantity) (optionalNullInt "outbound.quantity" tun.outbound.quantity))
+        ++ (optionals (tun ? crypto.tagsToSend) (optionalNullInt "crypto.tagstosend" tun.crypto.tagsToSend));
         in concatStringsSep "\n" outTunOpts))
     (flip map
       (collect (tun: tun ? port && tun ? address) cfg.inTunnels)
@@ -215,14 +208,10 @@ let
         "type = server"
         (intOpt "port" tun.port)
         (strOpt "host" tun.address)
-      ] ++ (if tun ? destination then
-            optionalNullString "destination" tun.destination else [])
-        ++ (if tun ? keys then
-            optionalNullString "keys" tun.keys else [])
-        ++ (if tun ? inPort then
-            optionalNullInt "inport" tun.inPort else [])
-        ++ (if tun ? accessList then
-            optionalEmptyList "accesslist" tun.accessList else []);
+      ] ++ (optionals (tun ? destination) (optionalNullString "destination" tun.destination))
+        ++ (optionals (tun ? keys) (optionalNullString "keys" tun.keys))
+        ++ (optionals (tun ? inPort) (optionalNullInt "inport" tun.inPort))
+        ++ (optionals (tun ? accessList) (optionalEmptyList "accesslist" tun.accessList));
         in concatStringsSep "\n" inTunOpts))];
     in pkgs.writeText "i2pd-tunnels.conf" opts;
 
diff --git a/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix b/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix
index 20e5558d7829..35401d439a91 100644
--- a/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix
+++ b/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix
@@ -22,9 +22,9 @@ with lib;
     };
 
     virtualRouterId = mkOption {
-      type = types.int;
+      type = types.ints.between 1 255;
       description = lib.mdDoc ''
-        Arbitrary unique number 0..255. Used to differentiate multiple instances
+        Arbitrary unique number 1..255. Used to differentiate multiple instances
         of vrrpd running on the same NIC (and hence same socket).
       '';
     };
diff --git a/nixpkgs/nixos/modules/services/networking/libreswan.nix b/nixpkgs/nixos/modules/services/networking/libreswan.nix
index 785729d8f742..db4d2f7f0ba0 100644
--- a/nixpkgs/nixos/modules/services/networking/libreswan.nix
+++ b/nixpkgs/nixos/modules/services/networking/libreswan.nix
@@ -14,8 +14,8 @@ let
     nonchars = filter (x : !(elem x.value chars))
                (imap0 (i: v: {ind = i; value = v;}) (stringToCharacters str));
   in
-    if length nonchars == 0 then ""
-    else substring (head nonchars).ind (add 1 (sub (last nonchars).ind (head nonchars).ind)) str;
+    lib.optionalString (nonchars != [ ])
+      (substring (head nonchars).ind (add 1 (sub (last nonchars).ind (head nonchars).ind)) str);
   indent = str: concatStrings (concatMap (s: ["  " (trim [" " "\t"] s) "\n"]) (splitString "\n" str));
   configText = indent (toString cfg.configSetup);
   connectionText = concatStrings (mapAttrsToList (n: v:
diff --git a/nixpkgs/nixos/modules/services/networking/mosquitto.nix b/nixpkgs/nixos/modules/services/networking/mosquitto.nix
index a4fd2fd7c89f..c53d86c0babc 100644
--- a/nixpkgs/nixos/modules/services/networking/mosquitto.nix
+++ b/nixpkgs/nixos/modules/services/networking/mosquitto.nix
@@ -42,12 +42,15 @@ let
       };
 
       passwordFile = mkOption {
-        type = uniq (nullOr types.path);
+        type = uniq (nullOr path);
         example = "/path/to/file";
         default = null;
         description = lib.mdDoc ''
           Specifies the path to a file containing the
           clear text password for the MQTT user.
+          The file is securely passed to mosquitto by
+          leveraging systemd credentials. No special
+          permissions need to be set on this file.
         '';
       };
 
@@ -64,7 +67,7 @@ let
       };
 
       hashedPasswordFile = mkOption {
-        type = uniq (nullOr types.path);
+        type = uniq (nullOr path);
         example = "/path/to/file";
         default = null;
         description = mdDoc ''
@@ -73,6 +76,9 @@ let
           To generate hashed password install the `mosquitto`
           package and use `mosquitto_passwd`, then remove the
           `username:` prefix from the generated file.
+          The file is securely passed to mosquitto by
+          leveraging systemd credentials. No special
+          permissions need to be set on this file.
         '';
       };
 
@@ -102,15 +108,43 @@ let
         message = "Cannot set more than one password option for user ${n} in ${prefix}";
       }) users;
 
-  makePasswordFile = users: path:
+  listenerScope = index: "listener-${toString index}";
+  userScope = prefix: index: "${prefix}-user-${toString index}";
+  credentialID = prefix: credential: "${prefix}-${credential}";
+
+  toScopedUsers = listenerScope: users: pipe users [
+    attrNames
+    (imap0 (index: user: nameValuePair user
+      (users.${user} // { scope = userScope listenerScope index; })
+    ))
+    listToAttrs
+  ];
+
+  userCredentials = user: credentials: pipe credentials [
+    (filter (credential: user.${credential} != null))
+    (map (credential: "${credentialID user.scope credential}:${user.${credential}}"))
+  ];
+  usersCredentials = listenerScope: users: credentials: pipe users [
+    (toScopedUsers listenerScope)
+    (mapAttrsToList (_: user: userCredentials user credentials))
+    concatLists
+  ];
+  systemdCredentials = listeners: listenerCredentials: pipe listeners [
+    (imap0 (index: listener: listenerCredentials (listenerScope index) listener))
+    concatLists
+  ];
+
+  makePasswordFile = listenerScope: users: path:
     let
-      makeLines = store: file:
+      makeLines = store: file: let
+        scopedUsers = toScopedUsers listenerScope users;
+      in
         mapAttrsToList
-          (n: u: "addLine ${escapeShellArg n} ${escapeShellArg u.${store}}")
-          (filterAttrs (_: u: u.${store} != null) users)
+          (name: user: ''addLine ${escapeShellArg name} "''$(systemd-creds cat ${credentialID user.scope store})"'')
+          (filterAttrs (_: user: user.${store} != null) scopedUsers)
         ++ mapAttrsToList
-          (n: u: "addFile ${escapeShellArg n} ${escapeShellArg "${u.${file}}"}")
-          (filterAttrs (_: u: u.${file} != null) users);
+          (name: user: ''addFile ${escapeShellArg name} "''${CREDENTIALS_DIRECTORY}/${credentialID user.scope file}"'')
+          (filterAttrs (_: user: user.${file} != null) scopedUsers);
       plainLines = makeLines "password" "passwordFile";
       hashedLines = makeLines "hashedPassword" "hashedPasswordFile";
     in
@@ -581,6 +615,19 @@ in
         ExecStart = "${cfg.package}/bin/mosquitto -c ${configFile}";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
 
+        # Credentials
+        SetCredential = let
+          listenerCredentials = listenerScope: listener:
+            usersCredentials listenerScope listener.users [ "password" "hashedPassword" ];
+        in
+          systemdCredentials cfg.listeners listenerCredentials;
+
+        LoadCredential = let
+          listenerCredentials = listenerScope: listener:
+            usersCredentials listenerScope listener.users [ "passwordFile" "hashedPasswordFile" ];
+        in
+          systemdCredentials cfg.listeners listenerCredentials;
+
         # Hardening
         CapabilityBoundingSet = "";
         DevicePolicy = "closed";
@@ -653,7 +700,7 @@ in
         concatStringsSep
           "\n"
           (imap0
-            (idx: listener: makePasswordFile listener.users "${cfg.dataDir}/passwd-${toString idx}")
+            (idx: listener: makePasswordFile (listenerScope idx) listener.users "${cfg.dataDir}/passwd-${toString idx}")
             cfg.listeners);
     };
 
diff --git a/nixpkgs/nixos/modules/services/networking/murmur.nix b/nixpkgs/nixos/modules/services/networking/murmur.nix
index ebade7aa8e40..20c2eff11e62 100644
--- a/nixpkgs/nixos/modules/services/networking/murmur.nix
+++ b/nixpkgs/nixos/modules/services/networking/murmur.nix
@@ -19,8 +19,8 @@ let
     welcometext="${cfg.welcometext}"
     port=${toString cfg.port}
 
-    ${if cfg.hostName == "" then "" else "host="+cfg.hostName}
-    ${if cfg.password == "" then "" else "serverpassword="+cfg.password}
+    ${optionalString (cfg.hostName != "") "host=${cfg.hostName}"}
+    ${optionalString (cfg.password != "") "serverpassword=${cfg.password}"}
 
     bandwidth=${toString cfg.bandwidth}
     users=${toString cfg.users}
@@ -32,17 +32,17 @@ let
     bonjour=${boolToString cfg.bonjour}
     sendversion=${boolToString cfg.sendVersion}
 
-    ${if cfg.registerName     == "" then "" else "registerName="+cfg.registerName}
-    ${if cfg.registerPassword == "" then "" else "registerPassword="+cfg.registerPassword}
-    ${if cfg.registerUrl      == "" then "" else "registerUrl="+cfg.registerUrl}
-    ${if cfg.registerHostname == "" then "" else "registerHostname="+cfg.registerHostname}
+    ${optionalString (cfg.registerName != "") "registerName=${cfg.registerName}"}
+    ${optionalString (cfg.registerPassword == "") "registerPassword=${cfg.registerPassword}"}
+    ${optionalString (cfg.registerUrl != "") "registerUrl=${cfg.registerUrl}"}
+    ${optionalString (cfg.registerHostname != "") "registerHostname=${cfg.registerHostname}"}
 
     certrequired=${boolToString cfg.clientCertRequired}
-    ${if cfg.sslCert == "" then "" else "sslCert="+cfg.sslCert}
-    ${if cfg.sslKey  == "" then "" else "sslKey="+cfg.sslKey}
-    ${if cfg.sslCa   == "" then "" else "sslCA="+cfg.sslCa}
+    ${optionalString (cfg.sslCert != "") "sslCert=${cfg.sslCert}"}
+    ${optionalString (cfg.sslKey != "") "sslKey=${cfg.sslKey}"}
+    ${optionalString (cfg.sslCa != "") "sslCA=${cfg.sslCa}"}
 
-    ${lib.optionalString (cfg.dbus != null) "dbus=${cfg.dbus}"}
+    ${optionalString (cfg.dbus != null) "dbus=${cfg.dbus}"}
 
     ${cfg.extraConfig}
   '';
@@ -355,5 +355,37 @@ in
       '';
       destination = "/share/dbus-1/system.d/murmur.conf";
     })];
+
+    security.apparmor.policies."bin.mumble-server".profile = ''
+      include <tunables/global>
+
+      ${cfg.package}/bin/{mumble-server,.mumble-server-wrapped} {
+        include <abstractions/base>
+        include <abstractions/nameservice>
+        include <abstractions/ssl_certs>
+        include "${pkgs.apparmorRulesFromClosure { name = "mumble-server"; } cfg.package}"
+        pix ${cfg.package}/bin/.mumble-server-wrapped,
+
+        r ${config.environment.etc."os-release".source},
+        r ${config.environment.etc."lsb-release".source},
+        owner rwk /var/lib/murmur/murmur.sqlite,
+        owner rw /var/lib/murmur/murmur.sqlite-journal,
+        owner r /var/lib/murmur/,
+        r /run/murmur/murmurd.pid,
+        r /run/murmur/murmurd.ini,
+        r ${configFile},
+      '' + optionalString (cfg.logFile != null) ''
+        rw ${cfg.logFile},
+      '' + optionalString (cfg.sslCert != "") ''
+        r ${cfg.sslCert},
+      '' + optionalString (cfg.sslKey != "") ''
+        r ${cfg.sslKey},
+      '' + optionalString (cfg.sslCa != "") ''
+        r ${cfg.sslCa},
+      '' + optionalString (cfg.dbus != null) ''
+        dbus bus=${cfg.dbus}
+      '' + ''
+      }
+    '';
   };
 }
diff --git a/nixpkgs/nixos/modules/services/networking/networkmanager.nix b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
index 3b28cec83cb7..e28f96f7a6d6 100644
--- a/nixpkgs/nixos/modules/services/networking/networkmanager.nix
+++ b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
@@ -461,6 +461,8 @@ in {
       "d /var/lib/NetworkManager-fortisslvpn 0700 root root -"
 
       "d /var/lib/misc 0755 root root -" # for dnsmasq.leases
+      # ppp isn't able to mkdir that directory at runtime
+      "d /run/pppd/lock 0700 root root -"
     ];
 
     systemd.services.NetworkManager = {
diff --git a/nixpkgs/nixos/modules/services/networking/nsd.nix b/nixpkgs/nixos/modules/services/networking/nsd.nix
index 09f3bdc7ae07..6db728e7aa5a 100644
--- a/nixpkgs/nixos/modules/services/networking/nsd.nix
+++ b/nixpkgs/nixos/modules/services/networking/nsd.nix
@@ -137,8 +137,8 @@ let
   '';
 
   yesOrNo = b: if b then "yes" else "no";
-  maybeString = prefix: x: if x == null then "" else ''${prefix} "${x}"'';
-  maybeToString = prefix: x: if x == null then "" else ''${prefix} ${toString x}'';
+  maybeString = prefix: x: optionalString (x != null) ''${prefix} "${x}"'';
+  maybeToString = prefix: x: optionalString (x != null) ''${prefix} ${toString x}'';
   forEach = pre: l: concatMapStrings (x: pre + x + "\n") l;
 
 
diff --git a/nixpkgs/nixos/modules/services/networking/ntopng.nix b/nixpkgs/nixos/modules/services/networking/ntopng.nix
index bf7ec19f02a6..a47ee0773d17 100644
--- a/nixpkgs/nixos/modules/services/networking/ntopng.nix
+++ b/nixpkgs/nixos/modules/services/networking/ntopng.nix
@@ -20,7 +20,7 @@ let
     ''
     else
     pkgs.writeText "ntopng.conf" ''
-      ${concatStringsSep " " (map (e: "--interface=" + e) cfg.interfaces)}
+      ${concatStringsSep "\n" (map (e: "--interface=${e}") cfg.interfaces)}
       --http-port=${toString cfg.httpPort}
       --redis=${cfg.redis.address}
       --data-dir=/var/lib/ntopng
diff --git a/nixpkgs/nixos/modules/services/networking/pdns-recursor.nix b/nixpkgs/nixos/modules/services/networking/pdns-recursor.nix
index 2f07cefc736e..f929532ba09f 100644
--- a/nixpkgs/nixos/modules/services/networking/pdns-recursor.nix
+++ b/nixpkgs/nixos/modules/services/networking/pdns-recursor.nix
@@ -159,6 +159,8 @@ in {
 
   config = mkIf cfg.enable {
 
+    environment.etc."pdns-recursor".source = configDir;
+
     services.pdns-recursor.settings = mkDefaultAttrs {
       local-address = cfg.dns.address;
       local-port    = cfg.dns.port;
diff --git a/nixpkgs/nixos/modules/services/networking/powerdns.nix b/nixpkgs/nixos/modules/services/networking/powerdns.nix
index 850a128cf1a4..03bf93301d85 100644
--- a/nixpkgs/nixos/modules/services/networking/powerdns.nix
+++ b/nixpkgs/nixos/modules/services/networking/powerdns.nix
@@ -38,6 +38,8 @@ in {
 
   config = mkIf cfg.enable {
 
+    environment.etc.pdns.source = finalConfigDir;
+
     systemd.packages = [ pkgs.pdns ];
 
     systemd.services.pdns = {
diff --git a/nixpkgs/nixos/modules/services/networking/prosody.nix b/nixpkgs/nixos/modules/services/networking/prosody.nix
index 9f68853f9fa8..0066c77438f4 100644
--- a/nixpkgs/nixos/modules/services/networking/prosody.nix
+++ b/nixpkgs/nixos/modules/services/networking/prosody.nix
@@ -757,9 +757,8 @@ in
 
     environment.etc."prosody/prosody.cfg.lua".text =
       let
-        httpDiscoItems = if (cfg.uploadHttp != null)
-            then [{ url = cfg.uploadHttp.domain; description = "HTTP upload endpoint";}]
-            else [];
+        httpDiscoItems = optionals (cfg.uploadHttp != null)
+            [{ url = cfg.uploadHttp.domain; description = "HTTP upload endpoint";}];
         mucDiscoItems = builtins.foldl'
             (acc: muc: [{ url = muc.domain; description = "${muc.domain} MUC endpoint";}] ++ acc)
             []
diff --git a/nixpkgs/nixos/modules/services/networking/searx.nix b/nixpkgs/nixos/modules/services/networking/searx.nix
index 6c57ddbde2d4..40648c724812 100644
--- a/nixpkgs/nixos/modules/services/networking/searx.nix
+++ b/nixpkgs/nixos/modules/services/networking/searx.nix
@@ -10,6 +10,8 @@ let
   settingsFile = pkgs.writeText "settings.yml"
     (builtins.toJSON cfg.settings);
 
+  limiterSettingsFile = (pkgs.formats.toml { }).generate "limiter.toml" cfg.limiterSettings;
+
   generateConfig = ''
     cd ${runDir}
 
@@ -65,6 +67,15 @@ in
         '';
       };
 
+      redisCreateLocally = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Configure a local Redis server for SearXNG. This is required if you
+          want to enable the rate limiter and bot protection of SearXNG.
+        '';
+      };
+
       settings = mkOption {
         type = types.attrsOf settingType;
         default = { };
@@ -111,6 +122,31 @@ in
         '';
       };
 
+      limiterSettings = mkOption {
+        type = types.attrsOf settingType;
+        default = { };
+        example = literalExpression ''
+          {
+            real_ip = {
+              x_for = 1;
+              ipv4_prefix = 32;
+              ipv6_prefix = 56;
+            }
+            botdetection.ip_lists.block_ip = [
+              # "93.184.216.34" # example.org
+            ];
+          }
+        '';
+        description = lib.mdDoc ''
+          Limiter settings for SearXNG.
+
+          ::: {.note}
+          For available settings, see the SearXNG
+          [schema file](https://github.com/searxng/searxng/blob/master/searx/botdetection/limiter.toml).
+          :::
+        '';
+      };
+
       package = mkOption {
         type = types.package;
         default = pkgs.searx;
@@ -158,6 +194,17 @@ in
   ###### implementation
 
   config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = (cfg.limiterSettings != { }) -> cfg.package.pname == "searxng";
+        message = "services.searx.limiterSettings requires services.searx.package to be searxng.";
+      }
+      {
+        assertion = cfg.redisCreateLocally -> cfg.package.pname == "searxng";
+        message = "services.searx.redisCreateLocally requires services.searx.package to be searxng.";
+      }
+    ];
+
     environment.systemPackages = [ cfg.package ];
 
     users.users.searx =
@@ -206,6 +253,7 @@ in
     services.searx.settings = {
       # merge NixOS settings with defaults settings.yml
       use_default_settings = mkDefault true;
+      redis.url = lib.mkIf cfg.redisCreateLocally "unix://${config.services.redis.servers.searx.unixSocket}";
     };
 
     services.uwsgi = mkIf (cfg.runInUwsgi) {
@@ -231,7 +279,16 @@ in
       } // cfg.uwsgiConfig;
     };
 
+    services.redis.servers.searx = lib.mkIf cfg.redisCreateLocally {
+      enable = true;
+      user = "searx";
+      port = 0;
+    };
+
+    environment.etc."searxng/limiter.toml" = lib.mkIf (cfg.limiterSettings != { }) {
+      source = limiterSettingsFile;
+    };
   };
 
-  meta.maintainers = with maintainers; [ rnhmjoj ];
+  meta.maintainers = with maintainers; [ rnhmjoj _999eagle ];
 }
diff --git a/nixpkgs/nixos/modules/services/networking/sing-box.nix b/nixpkgs/nixos/modules/services/networking/sing-box.nix
new file mode 100644
index 000000000000..a884bcd271ec
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/sing-box.nix
@@ -0,0 +1,67 @@
+{ config, lib, pkgs, utils, ... }:
+let
+  cfg = config.services.sing-box;
+  settingsFormat = pkgs.formats.json { };
+in
+{
+
+  meta = {
+    maintainers = with lib.maintainers; [ nickcao ];
+  };
+
+  options = {
+    services.sing-box = {
+      enable = lib.mkEnableOption (lib.mdDoc "sing-box universal proxy platform");
+
+      package = lib.mkPackageOptionMD pkgs "sing-box" { };
+
+      settings = lib.mkOption {
+        type = lib.types.submodule {
+          freeformType = settingsFormat.type;
+          options = {
+            route = {
+              geoip.path = lib.mkOption {
+                type = lib.types.path;
+                default = "${pkgs.sing-geoip}/share/sing-box/geoip.db";
+                defaultText = lib.literalExpression "\${pkgs.sing-geoip}/share/sing-box/geoip.db";
+                description = lib.mdDoc ''
+                  The path to the sing-geoip database.
+                '';
+              };
+              geosite.path = lib.mkOption {
+                type = lib.types.path;
+                default = "${pkgs.sing-geosite}/share/sing-box/geosite.db";
+                defaultText = lib.literalExpression "\${pkgs.sing-geosite}/share/sing-box/geosite.db";
+                description = lib.mdDoc ''
+                  The path to the sing-geosite database.
+                '';
+              };
+            };
+          };
+        };
+        default = { };
+        description = lib.mdDoc ''
+          The sing-box configuration, see https://sing-box.sagernet.org/configuration/ for documentation.
+
+          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 {
+    systemd.packages = [ cfg.package ];
+
+    systemd.services.sing-box = {
+      preStart = ''
+        umask 0077
+        mkdir -p /etc/sing-box
+        ${utils.genJqSecretsReplacementSnippet cfg.settings "/etc/sing-box/config.json"}
+      '';
+      wantedBy = [ "multi-user.target" ];
+    };
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ssh/lshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/lshd.nix
index af64969c2fcd..129e42055514 100644
--- a/nixpkgs/nixos/modules/services/networking/ssh/lshd.nix
+++ b/nixpkgs/nixos/modules/services/networking/ssh/lshd.nix
@@ -165,9 +165,7 @@ in
         ${lsh}/sbin/lshd --daemonic \
           --password-helper="${lsh}/sbin/lsh-pam-checkpw" \
           -p ${toString portNumber} \
-          ${if interfaces == [] then ""
-            else (concatStrings (map (i: "--interface=\"${i}\"")
-                                     interfaces))} \
+          ${optionalString (interfaces != []) (concatStrings (map (i: "--interface=\"${i}\"") interfaces))} \
           -h "${hostKey}" \
           ${optionalString (!syslog) "--no-syslog" } \
           ${if passwordAuthentication then "--password" else "--no-password" } \
diff --git a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
index 1367381b7a96..162267b5a85d 100644
--- a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
@@ -279,10 +279,12 @@ in
       settings = mkOption {
         description = lib.mdDoc "Configuration for `sshd_config(5)`.";
         default = { };
-        example = literalExpression ''{
-          UseDns = true;
-          PasswordAuthentication = false;
-        }'';
+        example = literalExpression ''
+          {
+            UseDns = true;
+            PasswordAuthentication = false;
+          }
+        '';
         type = types.submodule ({name, ...}: {
           freeformType = settingsFormat.type;
           options = {
@@ -530,7 +532,7 @@ in
 
       };
 
-    networking.firewall.allowedTCPPorts = if cfg.openFirewall then cfg.ports else [];
+    networking.firewall.allowedTCPPorts = optionals cfg.openFirewall cfg.ports;
 
     security.pam.services.sshd =
       { startSession = true;
diff --git a/nixpkgs/nixos/modules/services/networking/syncthing.nix b/nixpkgs/nixos/modules/services/networking/syncthing.nix
index 3d41fe4013ea..346b50700c79 100644
--- a/nixpkgs/nixos/modules/services/networking/syncthing.nix
+++ b/nixpkgs/nixos/modules/services/networking/syncthing.nix
@@ -7,31 +7,33 @@ let
   opt = options.services.syncthing;
   defaultUser = "syncthing";
   defaultGroup = defaultUser;
+  settingsFormat = pkgs.formats.json { };
+  cleanedConfig = converge (filterAttrsRecursive (_: v: v != null && v != {})) cfg.settings;
 
-  devices = mapAttrsToList (name: device: {
+  devices = mapAttrsToList (_: device: device // {
     deviceID = device.id;
-    inherit (device) name addresses introducer autoAcceptFolders;
-  }) cfg.devices;
-
-  folders = mapAttrsToList ( _: folder: {
-    inherit (folder) path id label type;
-    devices = map (device: { deviceId = cfg.devices.${device}.id; }) folder.devices;
-    rescanIntervalS = folder.rescanInterval;
-    fsWatcherEnabled = folder.watch;
-    fsWatcherDelayS = folder.watchDelay;
-    ignorePerms = folder.ignorePerms;
-    ignoreDelete = folder.ignoreDelete;
-    versioning = folder.versioning;
-  }) (filterAttrs (
-    _: folder:
+  }) cfg.settings.devices;
+
+  folders = mapAttrsToList (_: folder: folder //
+    throwIf (folder?rescanInterval || folder?watch || folder?watchDelay) ''
+      The options services.syncthing.settings.folders.<name>.{rescanInterval,watch,watchDelay}
+      were removed. Please use, respectively, {rescanIntervalS,fsWatcherEnabled,fsWatcherDelayS} instead.
+    '' {
+    devices = map (device:
+      if builtins.isString device then
+        { deviceId = cfg.settings.devices.${device}.id; }
+      else
+        device
+    ) folder.devices;
+  }) (filterAttrs (_: folder:
     folder.enable
-  ) cfg.folders);
+  ) cfg.settings.folders);
 
-  updateConfig = pkgs.writers.writeDash "merge-syncthing-config" ''
+  jq = "${pkgs.jq}/bin/jq";
+  updateConfig = pkgs.writers.writeBash "merge-syncthing-config" (''
     set -efu
 
     # be careful not to leak secrets in the filesystem or in process listings
-
     umask 0077
 
     # get the api key by parsing the config.xml
@@ -49,25 +51,85 @@ let
             --retry 1000 --retry-delay 1 --retry-all-errors \
             "$@"
     }
-
-    # query the old config
-    old_cfg=$(curl ${cfg.guiAddress}/rest/config)
-
-    # generate the new config by merging with the NixOS config options
-    new_cfg=$(printf '%s\n' "$old_cfg" | ${pkgs.jq}/bin/jq -c '. * {
-        "devices": (${builtins.toJSON devices}${optionalString (cfg.devices == {} || ! cfg.overrideDevices) " + .devices"}),
-        "folders": (${builtins.toJSON folders}${optionalString (cfg.folders == {} || ! cfg.overrideFolders) " + .folders"})
-    } * ${builtins.toJSON cfg.extraOptions}')
-
-    # send the new config
-    curl -X PUT -d "$new_cfg" ${cfg.guiAddress}/rest/config
-
+  '' +
+
+  /* Syncthing's rest API for the folders and devices is almost identical.
+  Hence we iterate them using lib.pipe and generate shell commands for both at
+  the sime time. */
+  (lib.pipe {
+    # The attributes below are the only ones that are different for devices /
+    # folders.
+    devs = {
+      new_conf_IDs = map (v: v.id) devices;
+      GET_IdAttrName = "deviceID";
+      override = cfg.overrideDevices;
+      conf = devices;
+      baseAddress = "${cfg.guiAddress}/rest/config/devices";
+    };
+    dirs = {
+      new_conf_IDs = map (v: v.id) folders;
+      GET_IdAttrName = "id";
+      override = cfg.overrideFolders;
+      conf = folders;
+      baseAddress = "${cfg.guiAddress}/rest/config/folders";
+    };
+  } [
+    # Now for each of these attributes, write the curl commands that are
+    # identical to both folders and devices.
+    (mapAttrs (conf_type: s:
+      # We iterate the `conf` list now, and run a curl -X POST command for each, that
+      # should update that device/folder only.
+      lib.pipe s.conf [
+        # Quoting https://docs.syncthing.net/rest/config.html:
+        #
+        # > PUT takes an array and POST a single object. In both cases if a
+        # given folder/device already exists, it’s replaced, otherwise a new
+        # one is added.
+        #
+        # What's not documented, is that using PUT will remove objects that
+        # don't exist in the array given. That's why we use here `POST`, and
+        # only if s.override == true then we DELETE the relevant folders
+        # afterwards.
+        (map (new_cfg: ''
+          curl -d ${lib.escapeShellArg (builtins.toJSON new_cfg)} -X POST ${s.baseAddress}
+        ''))
+        (lib.concatStringsSep "\n")
+      ]
+      /* If we need to override devices/folders, we iterate all currently configured
+      IDs, via another `curl -X GET`, and we delete all IDs that are not part of
+      the Nix configured list of IDs
+      */
+      + lib.optionalString s.override ''
+        old_conf_${conf_type}_ids="$(curl -X GET ${s.baseAddress} | ${jq} --raw-output '.[].${s.GET_IdAttrName}')"
+        for id in ''${old_conf_${conf_type}_ids}; do
+          if echo ${lib.concatStringsSep " " s.new_conf_IDs} | grep -q $id; then
+            continue
+          else
+            curl -X DELETE ${s.baseAddress}/$id
+          fi
+        done
+      ''
+    ))
+    builtins.attrValues
+    (lib.concatStringsSep "\n")
+  ]) +
+  /* Now we update the other settings defined in cleanedConfig which are not
+  "folders" or "devices". */
+  (lib.pipe cleanedConfig [
+    builtins.attrNames
+    (lib.subtractLists ["folders" "devices"])
+    (map (subOption: ''
+      curl -X PUT -d ${lib.escapeShellArg (builtins.toJSON cleanedConfig.${subOption})} \
+        ${cfg.guiAddress}/rest/config/${subOption}
+    ''))
+    (lib.concatStringsSep "\n")
+  ]) + ''
     # restart Syncthing if required
     if curl ${cfg.guiAddress}/rest/config/restart-required |
-       ${pkgs.jq}/bin/jq -e .requiresRestart > /dev/null; then
+       ${jq} -e .requiresRestart > /dev/null; then
         curl -X POST ${cfg.guiAddress}/rest/system/restart
     fi
-  '';
+  '');
 in {
   ###### interface
   options = {
@@ -99,287 +161,282 @@ in {
         default = true;
         description = mdDoc ''
           Whether to delete the devices which are not configured via the
-          [devices](#opt-services.syncthing.devices) option.
+          [devices](#opt-services.syncthing.settings.devices) option.
           If set to `false`, devices added via the web
           interface will persist and will have to be deleted manually.
         '';
       };
 
-      devices = mkOption {
-        default = {};
-        description = mdDoc ''
-          Peers/devices which Syncthing should communicate with.
-
-          Note that you can still add devices manually, but those changes
-          will be reverted on restart if [overrideDevices](#opt-services.syncthing.overrideDevices)
-          is enabled.
-        '';
-        example = {
-          bigbox = {
-            id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU";
-            addresses = [ "tcp://192.168.0.10:51820" ];
-          };
-        };
-        type = types.attrsOf (types.submodule ({ name, ... }: {
-          options = {
-
-            name = mkOption {
-              type = types.str;
-              default = name;
-              description = lib.mdDoc ''
-                The name of the device.
-              '';
-            };
-
-            addresses = mkOption {
-              type = types.listOf types.str;
-              default = [];
-              description = lib.mdDoc ''
-                The addresses used to connect to the device.
-                If this is left empty, dynamic configuration is attempted.
-              '';
-            };
-
-            id = mkOption {
-              type = types.str;
-              description = mdDoc ''
-                The device ID. See <https://docs.syncthing.net/dev/device-ids.html>.
-              '';
-            };
-
-            introducer = mkOption {
-              type = types.bool;
-              default = false;
-              description = mdDoc ''
-                Whether the device should act as an introducer and be allowed
-                to add folders on this computer.
-                See <https://docs.syncthing.net/users/introducer.html>.
-              '';
-            };
-
-            autoAcceptFolders = mkOption {
-              type = types.bool;
-              default = false;
-              description = mdDoc ''
-                Automatically create or share folders that this device advertises at the default path.
-                See <https://docs.syncthing.net/users/config.html?highlight=autoaccept#config-file-format>.
-              '';
-            };
-
-          };
-        }));
-      };
-
       overrideFolders = mkOption {
         type = types.bool;
         default = true;
         description = mdDoc ''
           Whether to delete the folders which are not configured via the
-          [folders](#opt-services.syncthing.folders) option.
+          [folders](#opt-services.syncthing.settings.folders) option.
           If set to `false`, folders added via the web
           interface will persist and will have to be deleted manually.
         '';
       };
 
-      folders = mkOption {
-        default = {};
-        description = mdDoc ''
-          Folders which should be shared by Syncthing.
-
-          Note that you can still add folders manually, but those changes
-          will be reverted on restart if [overrideFolders](#opt-services.syncthing.overrideFolders)
-          is enabled.
-        '';
-        example = literalExpression ''
-          {
-            "/home/user/sync" = {
-              id = "syncme";
-              devices = [ "bigbox" ];
-            };
-          }
-        '';
-        type = types.attrsOf (types.submodule ({ name, ... }: {
+      settings = mkOption {
+        type = types.submodule {
+          freeformType = settingsFormat.type;
           options = {
-
-            enable = mkOption {
-              type = types.bool;
-              default = true;
-              description = lib.mdDoc ''
-                Whether to share this folder.
-                This option is useful when you want to define all folders
-                in one place, but not every machine should share all folders.
+            # global options
+            options = mkOption {
+              default = {};
+              description = mdDoc ''
+                The options element contains all other global configuration options
               '';
-            };
+              type = types.submodule ({ name, ... }: {
+                freeformType = settingsFormat.type;
+                options = {
+                  localAnnounceEnabled = mkOption {
+                    type = types.nullOr types.bool;
+                    default = null;
+                    description = lib.mdDoc ''
+                      Whether to send announcements to the local LAN, also use such announcements to find other devices.
+                    '';
+                  };
 
-            path = mkOption {
-              # TODO for release 23.05: allow relative paths again and set
-              # working directory to cfg.dataDir
-              type = types.str // {
-                check = x: types.str.check x && (substring 0 1 x == "/" || substring 0 2 x == "~/");
-                description = types.str.description + " starting with / or ~/";
-              };
-              default = name;
-              description = lib.mdDoc ''
-                The path to the folder which should be shared.
-                Only absolute paths (starting with `/`) and paths relative to
-                the [user](#opt-services.syncthing.user)'s home directory
-                (starting with `~/`) are allowed.
-              '';
-            };
+                  localAnnouncePort = mkOption {
+                    type = types.nullOr types.int;
+                    default = null;
+                    description = lib.mdDoc ''
+                      The port on which to listen and send IPv4 broadcast announcements to.
+                    '';
+                  };
 
-            id = mkOption {
-              type = types.str;
-              default = name;
-              description = lib.mdDoc ''
-                The ID of the folder. Must be the same on all devices.
-              '';
-            };
+                  relaysEnabled = mkOption {
+                    type = types.nullOr types.bool;
+                    default = null;
+                    description = lib.mdDoc ''
+                      When true, relays will be connected to and potentially used for device to device connections.
+                    '';
+                  };
 
-            label = mkOption {
-              type = types.str;
-              default = name;
-              description = lib.mdDoc ''
-                The label of the folder.
-              '';
+                  urAccepted = mkOption {
+                    type = types.nullOr types.int;
+                    default = null;
+                    description = lib.mdDoc ''
+                      Whether the user has accepted to submit anonymous usage data.
+                      The default, 0, mean the user has not made a choice, and Syncthing will ask at some point in the future.
+                      "-1" means no, a number above zero means that that version of usage reporting has been accepted.
+                    '';
+                  };
+
+                  limitBandwidthInLan = mkOption {
+                    type = types.nullOr types.bool;
+                    default = null;
+                    description = lib.mdDoc ''
+                      Whether to apply bandwidth limits to devices in the same broadcast domain as the local device.
+                    '';
+                  };
+
+                  maxFolderConcurrency = mkOption {
+                    type = types.nullOr types.int;
+                    default = null;
+                    description = lib.mdDoc ''
+                      This option controls how many folders may concurrently be in I/O-intensive operations such as syncing or scanning.
+                      The mechanism is described in detail in a [separate chapter](https://docs.syncthing.net/advanced/option-max-concurrency.html).
+                    '';
+                  };
+                };
+              });
             };
 
+            # device settings
             devices = mkOption {
-              type = types.listOf types.str;
-              default = [];
+              default = {};
               description = mdDoc ''
-                The devices this folder should be shared with. Each device must
-                be defined in the [devices](#opt-services.syncthing.devices) option.
-              '';
-            };
+                Peers/devices which Syncthing should communicate with.
 
-            versioning = mkOption {
-              default = null;
-              description = mdDoc ''
-                How to keep changed/deleted files with Syncthing.
-                There are 4 different types of versioning with different parameters.
-                See <https://docs.syncthing.net/users/versioning.html>.
+                Note that you can still add devices manually, but those changes
+                will be reverted on restart if [overrideDevices](#opt-services.syncthing.overrideDevices)
+                is enabled.
               '';
-              example = literalExpression ''
-                [
-                  {
-                    versioning = {
-                      type = "simple";
-                      params.keep = "10";
-                    };
-                  }
-                  {
-                    versioning = {
-                      type = "trashcan";
-                      params.cleanoutDays = "1000";
-                    };
-                  }
-                  {
-                    versioning = {
-                      type = "staggered";
-                      fsPath = "/syncthing/backup";
-                      params = {
-                        cleanInterval = "3600";
-                        maxAge = "31536000";
-                      };
-                    };
-                  }
-                  {
-                    versioning = {
-                      type = "external";
-                      params.versionsPath = pkgs.writers.writeBash "backup" '''
-                        folderpath="$1"
-                        filepath="$2"
-                        rm -rf "$folderpath/$filepath"
-                      ''';
-                    };
-                  }
-                ]
-              '';
-              type = with types; nullOr (submodule {
+              example = {
+                bigbox = {
+                  id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU";
+                  addresses = [ "tcp://192.168.0.10:51820" ];
+                };
+              };
+              type = types.attrsOf (types.submodule ({ name, ... }: {
+                freeformType = settingsFormat.type;
                 options = {
-                  type = mkOption {
-                    type = enum [ "external" "simple" "staggered" "trashcan" ];
-                    description = mdDoc ''
-                      The type of versioning.
-                      See <https://docs.syncthing.net/users/versioning.html>.
+
+                  name = mkOption {
+                    type = types.str;
+                    default = name;
+                    description = lib.mdDoc ''
+                      The name of the device.
                     '';
                   };
-                  fsPath = mkOption {
-                    default = "";
-                    type = either str path;
+
+                  id = mkOption {
+                    type = types.str;
                     description = mdDoc ''
-                      Path to the versioning folder.
-                      See <https://docs.syncthing.net/users/versioning.html>.
+                      The device ID. See <https://docs.syncthing.net/dev/device-ids.html>.
                     '';
                   };
-                  params = mkOption {
-                    type = attrsOf (either str path);
+
+                  autoAcceptFolders = mkOption {
+                    type = types.bool;
+                    default = false;
                     description = mdDoc ''
-                      The parameters for versioning. Structure depends on
-                      [versioning.type](#opt-services.syncthing.folders._name_.versioning.type).
-                      See <https://docs.syncthing.net/users/versioning.html>.
+                      Automatically create or share folders that this device advertises at the default path.
+                      See <https://docs.syncthing.net/users/config.html?highlight=autoaccept#config-file-format>.
                     '';
                   };
+
                 };
-              });
+              }));
             };
 
-            rescanInterval = mkOption {
-              type = types.int;
-              default = 3600;
-              description = lib.mdDoc ''
-                How often the folder should be rescanned for changes.
-              '';
-            };
+            # folder settings
+            folders = mkOption {
+              default = {};
+              description = mdDoc ''
+                Folders which should be shared by Syncthing.
 
-            type = mkOption {
-              type = types.enum [ "sendreceive" "sendonly" "receiveonly" "receiveencrypted" ];
-              default = "sendreceive";
-              description = lib.mdDoc ''
-                Whether to only send changes for this folder, only receive them
-                or both. `receiveencrypted` can be used for untrusted devices. See
-                <https://docs.syncthing.net/users/untrusted.html> for reference.
+                Note that you can still add folders manually, but those changes
+                will be reverted on restart if [overrideFolders](#opt-services.syncthing.overrideFolders)
+                is enabled.
               '';
-            };
-
-            watch = mkOption {
-              type = types.bool;
-              default = true;
-              description = lib.mdDoc ''
-                Whether the folder should be watched for changes by inotify.
+              example = literalExpression ''
+                {
+                  "/home/user/sync" = {
+                    id = "syncme";
+                    devices = [ "bigbox" ];
+                  };
+                }
               '';
-            };
+              type = types.attrsOf (types.submodule ({ name, ... }: {
+                freeformType = settingsFormat.type;
+                options = {
 
-            watchDelay = mkOption {
-              type = types.int;
-              default = 10;
-              description = lib.mdDoc ''
-                The delay after an inotify event is triggered.
-              '';
-            };
+                  enable = mkOption {
+                    type = types.bool;
+                    default = true;
+                    description = lib.mdDoc ''
+                      Whether to share this folder.
+                      This option is useful when you want to define all folders
+                      in one place, but not every machine should share all folders.
+                    '';
+                  };
 
-            ignorePerms = mkOption {
-              type = types.bool;
-              default = true;
-              description = lib.mdDoc ''
-                Whether to ignore permission changes.
-              '';
-            };
+                  path = mkOption {
+                    # TODO for release 23.05: allow relative paths again and set
+                    # working directory to cfg.dataDir
+                    type = types.str // {
+                      check = x: types.str.check x && (substring 0 1 x == "/" || substring 0 2 x == "~/");
+                      description = types.str.description + " starting with / or ~/";
+                    };
+                    default = name;
+                    description = lib.mdDoc ''
+                      The path to the folder which should be shared.
+                      Only absolute paths (starting with `/`) and paths relative to
+                      the [user](#opt-services.syncthing.user)'s home directory
+                      (starting with `~/`) are allowed.
+                    '';
+                  };
 
-            ignoreDelete = mkOption {
-              type = types.bool;
-              default = false;
-              description = mdDoc ''
-                Whether to skip deleting files that are deleted by peers.
-                See <https://docs.syncthing.net/advanced/folder-ignoredelete.html>.
-              '';
+                  id = mkOption {
+                    type = types.str;
+                    default = name;
+                    description = lib.mdDoc ''
+                      The ID of the folder. Must be the same on all devices.
+                    '';
+                  };
+
+                  label = mkOption {
+                    type = types.str;
+                    default = name;
+                    description = lib.mdDoc ''
+                      The label of the folder.
+                    '';
+                  };
+
+                  devices = mkOption {
+                    type = types.listOf types.str;
+                    default = [];
+                    description = mdDoc ''
+                      The devices this folder should be shared with. Each device must
+                      be defined in the [devices](#opt-services.syncthing.settings.devices) option.
+                    '';
+                  };
+
+                  versioning = mkOption {
+                    default = null;
+                    description = mdDoc ''
+                      How to keep changed/deleted files with Syncthing.
+                      There are 4 different types of versioning with different parameters.
+                      See <https://docs.syncthing.net/users/versioning.html>.
+                    '';
+                    example = literalExpression ''
+                      [
+                        {
+                          versioning = {
+                            type = "simple";
+                            params.keep = "10";
+                          };
+                        }
+                        {
+                          versioning = {
+                            type = "trashcan";
+                            params.cleanoutDays = "1000";
+                          };
+                        }
+                        {
+                          versioning = {
+                            type = "staggered";
+                            fsPath = "/syncthing/backup";
+                            params = {
+                              cleanInterval = "3600";
+                              maxAge = "31536000";
+                            };
+                          };
+                        }
+                        {
+                          versioning = {
+                            type = "external";
+                            params.versionsPath = pkgs.writers.writeBash "backup" '''
+                              folderpath="$1"
+                              filepath="$2"
+                              rm -rf "$folderpath/$filepath"
+                            ''';
+                          };
+                        }
+                      ]
+                    '';
+                    type = with types; nullOr (submodule {
+                      freeformType = settingsFormat.type;
+                      options = {
+                        type = mkOption {
+                          type = enum [ "external" "simple" "staggered" "trashcan" ];
+                          description = mdDoc ''
+                            The type of versioning.
+                            See <https://docs.syncthing.net/users/versioning.html>.
+                          '';
+                        };
+                      };
+                    });
+                  };
+
+                  copyOwnershipFromParent = mkOption {
+                    type = types.bool;
+                    default = false;
+                    description = mdDoc ''
+                      On Unix systems, tries to copy file/folder ownership from the parent directory (the directory it’s located in).
+                      Requires running Syncthing as a privileged user, or granting it additional capabilities (e.g. CAP_CHOWN on Linux).
+                    '';
+                  };
+                };
+              }));
             };
-          };
-        }));
-      };
 
-      extraOptions = mkOption {
-        type = types.addCheck (pkgs.formats.json {}).type isAttrs;
+          };
+        };
         default = {};
         description = mdDoc ''
           Extra configuration options for Syncthing.
@@ -530,6 +587,10 @@ in {
       This option was removed because Syncthing now has the inotify functionality included under the name "fswatcher".
       It can be enabled on a per-folder basis through the web interface.
     '')
+    (mkRenamedOptionModule [ "services" "syncthing" "extraOptions" ] [ "services" "syncthing" "settings" ])
+    (mkRenamedOptionModule [ "services" "syncthing" "folders" ] [ "services" "syncthing" "settings" "folders" ])
+    (mkRenamedOptionModule [ "services" "syncthing" "devices" ] [ "services" "syncthing" "settings" "devices" ])
+    (mkRenamedOptionModule [ "services" "syncthing" "options" ] [ "services" "syncthing" "settings" "options" ])
   ] ++ map (o:
     mkRenamedOptionModule [ "services" "syncthing" "declarative" o ] [ "services" "syncthing" o ]
   ) [ "cert" "key" "devices" "folders" "overrideDevices" "overrideFolders" "extraOptions"];
@@ -615,9 +676,7 @@ in {
           ];
         };
       };
-      syncthing-init = mkIf (
-        cfg.devices != {} || cfg.folders != {} || cfg.extraOptions != {}
-      ) {
+      syncthing-init = mkIf (cleanedConfig != {}) {
         description = "Syncthing configuration updater";
         requisite = [ "syncthing.service" ];
         after = [ "syncthing.service" ];
diff --git a/nixpkgs/nixos/modules/services/networking/tailscale.nix b/nixpkgs/nixos/modules/services/networking/tailscale.nix
index c81cf293ab6d..f308b7e33114 100644
--- a/nixpkgs/nixos/modules/services/networking/tailscale.nix
+++ b/nixpkgs/nixos/modules/services/networking/tailscale.nix
@@ -29,12 +29,7 @@ in {
       description = lib.mdDoc "Username or user ID of the user allowed to to fetch Tailscale TLS certificates for the node.";
     };
 
-    package = mkOption {
-      type = types.package;
-      default = pkgs.tailscale;
-      defaultText = literalExpression "pkgs.tailscale";
-      description = lib.mdDoc "The package to use for tailscale";
-    };
+    package = lib.mkPackageOptionMD pkgs "tailscale" {};
 
     useRoutingFeatures = mkOption {
       type = types.enum [ "none" "client" "server" "both" ];
@@ -49,6 +44,22 @@ in {
         When set to `server` or `both`, IP forwarding will be enabled.
       '';
     };
+
+    authKeyFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/run/secrets/tailscale_key";
+      description = lib.mdDoc ''
+        A file containing the auth key.
+      '';
+    };
+
+    extraUpFlags = mkOption {
+      description = lib.mdDoc "Extra flags to pass to {command}`tailscale up`.";
+      type = types.listOf types.str;
+      default = [];
+      example = ["--ssh"];
+    };
   };
 
   config = mkIf cfg.enable {
@@ -59,7 +70,8 @@ in {
       path = [
         config.networking.resolvconf.package # for configuring DNS in some configs
         pkgs.procps     # for collecting running services (opt-in feature)
-        pkgs.glibc      # for `getent` to look up user shells
+        pkgs.getent     # for `getent` to look up user shells
+        pkgs.kmod       # required to pass tailscale's v6nat check
       ];
       serviceConfig.Environment = [
         "PORT=${toString cfg.port}"
@@ -81,6 +93,21 @@ in {
       stopIfChanged = false;
     };
 
+    systemd.services.tailscaled-autoconnect = mkIf (cfg.authKeyFile != null) {
+      after = ["tailscale.service"];
+      wants = ["tailscale.service"];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "oneshot";
+      };
+      script = ''
+        status=$(${config.systemd.package}/bin/systemctl show -P StatusText tailscaled.service)
+        if [[ $status != Connected* ]]; then
+          ${cfg.package}/bin/tailscale up --auth-key 'file:${cfg.authKeyFile}' ${escapeShellArgs cfg.extraUpFlags}
+        fi
+      '';
+    };
+
     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/nixpkgs/nixos/modules/services/networking/thelounge.nix b/nixpkgs/nixos/modules/services/networking/thelounge.nix
index 2c4c32bc7cf3..321e46fb5d4d 100644
--- a/nixpkgs/nixos/modules/services/networking/thelounge.nix
+++ b/nixpkgs/nixos/modules/services/networking/thelounge.nix
@@ -48,14 +48,16 @@ in
     extraConfig = mkOption {
       default = { };
       type = types.attrs;
-      example = literalExpression ''{
-        reverseProxy = true;
-        defaults = {
-          name = "Your Network";
-          host = "localhost";
-          port = 6697;
-        };
-      }'';
+      example = literalExpression ''
+        {
+          reverseProxy = true;
+          defaults = {
+            name = "Your Network";
+            host = "localhost";
+            port = 6697;
+          };
+        }
+      '';
       description = lib.mdDoc ''
         The Lounge's {file}`config.js` contents as attribute set (will be
         converted to JSON to generate the configuration file).
diff --git a/nixpkgs/nixos/modules/services/networking/trust-dns.nix b/nixpkgs/nixos/modules/services/networking/trust-dns.nix
new file mode 100644
index 000000000000..a3b4d12479b4
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/trust-dns.nix
@@ -0,0 +1,177 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.trust-dns;
+  toml = pkgs.formats.toml { };
+
+  configFile = toml.generate "trust-dns.toml" (
+    lib.filterAttrsRecursive (_: v: v != null) cfg.settings
+  );
+
+  zoneType = lib.types.submodule ({ config, ... }: {
+    options = with lib; {
+      zone = mkOption {
+        type = types.str;
+        description = mdDoc ''
+          Zone name, like "example.com", "localhost", or "0.0.127.in-addr.arpa".
+        '';
+      };
+      zone_type = mkOption {
+        type = types.enum [ "Primary" "Secondary" "Hint" "Forward" ];
+        default = "Primary";
+        description = mdDoc ''
+          One of:
+          - "Primary" (the master, authority for the zone).
+          - "Secondary" (the slave, replicated from the primary).
+          - "Hint" (a cached zone with recursive resolver abilities).
+          - "Forward" (a cached zone where all requests are forwarded to another resolver).
+
+          For more details about these zone types, consult the documentation for BIND,
+          though note that trust-dns supports only a subset of BIND's zone types:
+          <https://bind9.readthedocs.io/en/v9_18_4/reference.html#type>
+        '';
+      };
+      file = mkOption {
+        type = types.either types.path types.str;
+        default = "${config.zone}.zone";
+        defaultText = literalExpression ''"''${config.zone}.zone"'';
+        description = mdDoc ''
+          Path to the .zone file.
+          If not fully-qualified, this path will be interpreted relative to the `directory` option.
+          If omitted, defaults to the value of the `zone` option suffixed with ".zone".
+        '';
+      };
+    };
+  });
+in
+{
+  meta.maintainers = with lib.maintainers; [ colinsane ];
+  options = {
+    services.trust-dns = with lib; {
+      enable = mkEnableOption (lib.mdDoc "trust-dns");
+      package = mkOption {
+        type = types.package;
+        default = pkgs.trust-dns;
+        defaultText = "pkgs.trust-dns";
+        description = mdDoc ''
+          Trust-dns package to use.
+          Only `bin/named` need be provided: the other trust-dns utilities (client and resolver) are not needed.
+        '';
+      };
+      quiet = mkOption {
+        type = types.bool;
+        default = false;
+        description = mdDoc ''
+          Log ERROR level messages only.
+          This option is mutually exclusive with the `debug` option.
+          If neither `quiet` nor `debug` are enabled, logging defaults to the INFO level.
+        '';
+      };
+      debug = mkOption {
+        type = types.bool;
+        default = false;
+        description = mdDoc ''
+          Log DEBUG, INFO, WARN and ERROR messages.
+          This option is mutually exclusive with the `debug` option.
+          If neither `quiet` nor `debug` are enabled, logging defaults to the INFO level.
+        '';
+      };
+      settings = mkOption {
+        description = lib.mdDoc ''
+          Settings for trust-dns. The options enumerated here are not exhaustive.
+          Refer to upstream documentation for all available options:
+          - [Example settings](https://github.com/bluejekyll/trust-dns/blob/main/tests/test-data/test_configs/example.toml)
+        '';
+        type = types.submodule {
+          freeformType = toml.type;
+          options = {
+            listen_addrs_ipv4 = mkOption {
+              type = types.listOf types.str;
+              default = [ "0.0.0.0" ];
+              description = mdDoc ''
+              List of ipv4 addresses on which to listen for DNS queries.
+              '';
+            };
+            listen_addrs_ipv6 = mkOption {
+              type = types.listOf types.str;
+              default = lib.optional config.networking.enableIPv6 "::0";
+              defaultText = literalExpression ''lib.optional config.networking.enableIPv6 "::0"'';
+              description = mdDoc ''
+                List of ipv6 addresses on which to listen for DNS queries.
+              '';
+            };
+            listen_port = mkOption {
+              type = types.port;
+              default = 53;
+              description = mdDoc ''
+                Port to listen on (applies to all listen addresses).
+              '';
+            };
+            directory = mkOption {
+              type = types.str;
+              default = "/var/lib/trust-dns";
+              description = mdDoc ''
+                The directory in which trust-dns should look for .zone files,
+                whenever zones aren't specified by absolute path.
+              '';
+            };
+            zones = mkOption {
+              description = mdDoc "List of zones to serve.";
+              default = {};
+              type = types.listOf (types.coercedTo types.str (zone: { inherit zone; }) zoneType);
+            };
+          };
+        };
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.trust-dns = {
+      description = "trust-dns Domain Name Server";
+      unitConfig.Documentation = "https://trust-dns.org/";
+      serviceConfig = {
+        ExecStart =
+        let
+          flags =  (lib.optional cfg.debug "--debug") ++ (lib.optional cfg.quiet "--quiet");
+          flagsStr = builtins.concatStringsSep " " flags;
+        in ''
+          ${cfg.package}/bin/named --config ${configFile} ${flagsStr}
+        '';
+        Type = "simple";
+        Restart = "on-failure";
+        RestartSec = "10s";
+        DynamicUser = true;
+
+        StateDirectory = "trust-dns";
+        ReadWritePaths = [ cfg.settings.directory ];
+
+        AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
+        CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        PrivateTmp = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "full";
+        RemoveIPC = true;
+        RestrictAddressFamilies = [ "AF_INET AF_INET6" ];
+        RestrictNamespaces = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
+      };
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/twingate.nix b/nixpkgs/nixos/modules/services/networking/twingate.nix
index 17140bffd218..170d392bf213 100644
--- a/nixpkgs/nixos/modules/services/networking/twingate.nix
+++ b/nixpkgs/nixos/modules/services/networking/twingate.nix
@@ -1,28 +1,24 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.services.twingate;
-
-in {
-
+in
+{
   options.services.twingate = {
-    enable = mkEnableOption (lib.mdDoc "Twingate Client daemon");
+    enable = lib.mkEnableOption (lib.mdDoc "Twingate Client daemon");
+    package = lib.mkPackageOptionMD pkgs "twingate" { };
   };
 
-  config = mkIf cfg.enable {
-
-    networking.firewall.checkReversePath = lib.mkDefault false;
-    networking.networkmanager.enable = true;
-
-    environment.systemPackages = [ pkgs.twingate ]; # for the CLI
-    systemd.packages = [ pkgs.twingate ];
+  config = lib.mkIf cfg.enable {
+    systemd.packages = [ cfg.package ];
+    systemd.services.twingate = {
+      preStart = "cp -r --update=none ${cfg.package}/etc/twingate/. /etc/twingate/";
+      wantedBy = [ "multi-user.target" ];
+    };
 
-    systemd.services.twingate.preStart = ''
-      cp -r -n ${pkgs.twingate}/etc/twingate/. /etc/twingate/
-    '';
+    networking.firewall.checkReversePath = lib.mkDefault "loose";
+    services.resolved.enable = !(config.networking.networkmanager.enable);
 
-    systemd.services.twingate.wantedBy = [ "multi-user.target" ];
+    environment.systemPackages = [ cfg.package ]; # For the CLI.
   };
 }
diff --git a/nixpkgs/nixos/modules/services/networking/vsftpd.nix b/nixpkgs/nixos/modules/services/networking/vsftpd.nix
index b1f0f7403243..318ceb4e5094 100644
--- a/nixpkgs/nixos/modules/services/networking/vsftpd.nix
+++ b/nixpkgs/nixos/modules/services/networking/vsftpd.nix
@@ -305,7 +305,7 @@ in
 
     # If you really have to access root via FTP use mkOverride or userlistDeny
     # = false and whitelist root
-    services.vsftpd.userlist = if cfg.userlistDeny then ["root"] else [];
+    services.vsftpd.userlist = optional cfg.userlistDeny "root";
 
     systemd = {
       tmpfiles.rules = optional cfg.anonymousUser
diff --git a/nixpkgs/nixos/modules/services/networking/wgautomesh.nix b/nixpkgs/nixos/modules/services/networking/wgautomesh.nix
index 7549d82eae0b..094281403f73 100644
--- a/nixpkgs/nixos/modules/services/networking/wgautomesh.nix
+++ b/nixpkgs/nixos/modules/services/networking/wgautomesh.nix
@@ -35,8 +35,10 @@ in
     gossipSecretFile = mkOption {
       type = types.path;
       description = mdDoc ''
-        File containing the shared secret key to use for gossip encryption.
-        Required if `enableGossipEncryption` is set.
+        File containing the gossip secret, a shared secret key to use for gossip
+        encryption.  Required if `enableGossipEncryption` is set.  This file
+        may contain any arbitrary-length utf8 string.  To generate a new gossip
+        secret, use a command such as `openssl rand -base64 32`.
       '';
     };
     enablePersistence = mkOption {
diff --git a/nixpkgs/nixos/modules/services/networking/xrdp.nix b/nixpkgs/nixos/modules/services/networking/xrdp.nix
index ed7f1dadd370..218b440aab3c 100644
--- a/nixpkgs/nixos/modules/services/networking/xrdp.nix
+++ b/nixpkgs/nixos/modules/services/networking/xrdp.nix
@@ -121,7 +121,7 @@ in
       icons.enable = true;
     };
 
-    fonts.enableDefaultFonts = mkDefault true;
+    fonts.enableDefaultPackages = mkDefault true;
 
     systemd = {
       services.xrdp = {
diff --git a/nixpkgs/nixos/modules/services/networking/zerobin.nix b/nixpkgs/nixos/modules/services/networking/zerobin.nix
index 9e07666f3e14..735d4fa25fb1 100644
--- a/nixpkgs/nixos/modules/services/networking/zerobin.nix
+++ b/nixpkgs/nixos/modules/services/networking/zerobin.nix
@@ -75,13 +75,12 @@ in
 
     config = mkIf (cfg.enable) {
       users.users.${cfg.user} =
-      if cfg.user == "zerobin" then {
+      optionalAttrs (cfg.user == "zerobin") {
         isSystemUser = true;
         group = cfg.group;
         home = cfg.dataDir;
         createHome = true;
-      }
-      else {};
+      };
       users.groups.${cfg.group} = {};
 
       systemd.services.zerobin = {
diff --git a/nixpkgs/nixos/modules/services/printing/cupsd.nix b/nixpkgs/nixos/modules/services/printing/cupsd.nix
index f6a23fb900f0..279b26bb8957 100644
--- a/nixpkgs/nixos/modules/services/printing/cupsd.nix
+++ b/nixpkgs/nixos/modules/services/printing/cupsd.nix
@@ -4,7 +4,7 @@ with lib;
 
 let
 
-  inherit (pkgs) cups cups-pk-helper cups-filters;
+  inherit (pkgs) cups cups-pk-helper cups-filters xdg-utils;
 
   cfg = config.services.printing;
 
@@ -313,7 +313,9 @@ in
         description = "CUPS printing services";
       };
 
-    environment.systemPackages = [ cups.out ] ++ optional polkitEnabled cups-pk-helper;
+    # We need xdg-open (part of xdg-utils) for the desktop-file to proper open the users default-browser when opening "Manage Printing"
+    # https://github.com/NixOS/nixpkgs/pull/237994#issuecomment-1597510969
+    environment.systemPackages = [ cups.out xdg-utils ] ++ optional polkitEnabled cups-pk-helper;
     environment.etc.cups.source = "/var/lib/cups";
 
     services.dbus.packages = [ cups.out ] ++ optional polkitEnabled cups-pk-helper;
diff --git a/nixpkgs/nixos/modules/services/scheduling/fcron.nix b/nixpkgs/nixos/modules/services/scheduling/fcron.nix
index f1d2f462a755..47bd358f979d 100644
--- a/nixpkgs/nixos/modules/services/scheduling/fcron.nix
+++ b/nixpkgs/nixos/modules/services/scheduling/fcron.nix
@@ -6,7 +6,7 @@ let
 
   cfg = config.services.fcron;
 
-  queuelen = if cfg.queuelen == null then "" else "-q ${toString cfg.queuelen}";
+  queuelen = optionalString (cfg.queuelen != null) "-q ${toString cfg.queuelen}";
 
   # Duplicate code, also found in cron.nix. Needs deduplication.
   systemCronJobs =
diff --git a/nixpkgs/nixos/modules/services/search/kibana.nix b/nixpkgs/nixos/modules/services/search/kibana.nix
index 5eb2381d5d39..a5e132d5c38d 100644
--- a/nixpkgs/nixos/modules/services/search/kibana.nix
+++ b/nixpkgs/nixos/modules/services/search/kibana.nix
@@ -130,9 +130,9 @@ in {
 
           This defaults to the singleton list [ca] when the {option}`ca` option is defined.
         '';
-        default = if cfg.elasticsearch.ca == null then [] else [ca];
+        default = lib.optional (cfg.elasticsearch.ca != null) ca;
         defaultText = literalExpression ''
-          if config.${opt.elasticsearch.ca} == null then [ ] else [ ca ]
+          lib.optional (config.${opt.elasticsearch.ca} != null) ca
         '';
         type = types.listOf types.path;
       };
diff --git a/nixpkgs/nixos/modules/services/search/typesense.nix b/nixpkgs/nixos/modules/services/search/typesense.nix
new file mode 100644
index 000000000000..856c3cad22df
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/search/typesense.nix
@@ -0,0 +1,125 @@
+{ config, lib, pkgs, ... }: let
+  inherit
+    (lib)
+    concatMapStringsSep
+    generators
+    mdDoc
+    mkEnableOption
+    mkIf
+    mkOption
+    mkPackageOption
+    optionalString
+    types
+    ;
+
+  cfg = config.services.typesense;
+  settingsFormatIni = pkgs.formats.ini {
+    listToValue = concatMapStringsSep " " (generators.mkValueStringDefault { });
+    mkKeyValue = generators.mkKeyValueDefault
+      {
+        mkValueString = v:
+          if v == null then ""
+          else generators.mkValueStringDefault { } v;
+      }
+      "=";
+  };
+  configFile = settingsFormatIni.generate "typesense.ini" cfg.settings;
+in {
+  options.services.typesense = {
+    enable = mkEnableOption "typesense";
+    package = mkPackageOption pkgs "typesense" {};
+
+    apiKeyFile = mkOption {
+      type = types.path;
+      description = ''
+        Sets the admin api key for typesense. Always use this option
+        instead of {option}`settings.server.api-key` to prevent the key
+        from being written to the world-readable nix store.
+      '';
+    };
+
+    settings = mkOption {
+      description = mdDoc "Typesense configuration. Refer to [the documentation](https://typesense.org/docs/0.24.1/api/server-configuration.html) for supported values.";
+      default = {};
+      type = types.submodule {
+        freeformType = settingsFormatIni.type;
+        options.server = {
+          data-dir = mkOption {
+            type = types.str;
+            default = "/var/lib/typesense";
+            description = mdDoc "Path to the directory where data will be stored on disk.";
+          };
+
+          api-address = mkOption {
+            type = types.str;
+            description = mdDoc "Address to which Typesense API service binds.";
+          };
+
+          api-port = mkOption {
+            type = types.port;
+            default = 8108;
+            description = mdDoc "Port on which the Typesense API service listens.";
+          };
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.typesense = {
+      description = "Typesense search engine";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      script = ''
+        export TYPESENSE_API_KEY=$(cat ${cfg.apiKeyFile})
+        exec ${cfg.package}/bin/typesense-server --config ${configFile}
+      '';
+
+      serviceConfig = {
+        Restart = "on-failure";
+        DynamicUser = true;
+        User = "typesense";
+        Group = "typesense";
+
+        StateDirectory = "typesense";
+        StateDirectoryMode = "0700";
+
+        # Hardening
+        CapabilityBoundingSet = "";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        PrivateUsers = true;
+        PrivateTmp = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProcSubset = "pid";
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+          "AF_UNIX"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "~@privileged"
+        ];
+        UMask = "0077";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/security/esdm.nix b/nixpkgs/nixos/modules/services/security/esdm.nix
new file mode 100644
index 000000000000..2b246fff7e96
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/security/esdm.nix
@@ -0,0 +1,102 @@
+{ lib, config, pkgs, ... }:
+
+let
+  cfg = config.services.esdm;
+in
+{
+  options.services.esdm = {
+    enable = lib.mkEnableOption (lib.mdDoc "ESDM service configuration");
+    package = lib.mkPackageOptionMD pkgs "esdm" { };
+    serverEnable = lib.mkOption {
+      type = lib.types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Enable option for ESDM server service. If serverEnable == false, then the esdm-server
+        will not start. Also the subsequent services esdm-cuse-random, esdm-cuse-urandom
+        and esdm-proc will not start as these have the entry Want=esdm-server.service.
+      '';
+    };
+    cuseRandomEnable = lib.mkOption {
+      type = lib.types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Enable option for ESDM cuse-random service. Determines if the esdm-cuse-random.service
+        is started.
+      '';
+    };
+    cuseUrandomEnable = lib.mkOption {
+      type = lib.types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Enable option for ESDM cuse-urandom service. Determines if the esdm-cuse-urandom.service
+        is started.
+      '';
+    };
+    procEnable = lib.mkOption {
+      type = lib.types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Enable option for ESDM proc service. Determines if the esdm-proc.service
+        is started.
+      '';
+    };
+    verbose = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Enable verbose ExecStart for ESDM. If verbose == true, then the corresponding "ExecStart"
+        values of the 4 aforementioned services are overwritten with the option
+        for the highest verbosity.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable (
+    lib.mkMerge [
+      ({
+        systemd.packages = [ cfg.package ];
+      })
+      # It is necessary to set those options for these services to be started by systemd in NixOS
+      (lib.mkIf cfg.serverEnable {
+        systemd.services."esdm-server".wantedBy = [ "basic.target" ];
+        systemd.services."esdm-server".serviceConfig = lib.mkIf cfg.verbose {
+          ExecStart = [
+            " " # unset previous value defined in 'esdm-server.service'
+            "${cfg.package}/bin/esdm-server -f -vvvvvv"
+          ];
+        };
+      })
+
+      (lib.mkIf cfg.cuseRandomEnable {
+        systemd.services."esdm-cuse-random".wantedBy = [ "basic.target" ];
+        systemd.services."esdm-cuse-random".serviceConfig = lib.mkIf cfg.verbose {
+          ExecStart = [
+            " " # unset previous value defined in 'esdm-cuse-random.service'
+            "${cfg.package}/bin/esdm-cuse-random -f -v 6"
+          ];
+        };
+      })
+
+      (lib.mkIf cfg.cuseUrandomEnable {
+        systemd.services."esdm-cuse-urandom".wantedBy = [ "basic.target" ];
+        systemd.services."esdm-cuse-urandom".serviceConfig = lib.mkIf cfg.verbose {
+          ExecStart = [
+            " " # unset previous value defined in 'esdm-cuse-urandom.service'
+            "${config.services.esdm.package}/bin/esdm-cuse-urandom -f -v 6"
+          ];
+        };
+      })
+
+      (lib.mkIf cfg.procEnable {
+        systemd.services."esdm-proc".wantedBy = [ "basic.target" ];
+        systemd.services."esdm-proc".serviceConfig = lib.mkIf cfg.verbose {
+          ExecStart = [
+            " " # unset previous value defined in 'esdm-proc.service'
+            "${cfg.package}/bin/esdm-proc --relabel -f -o allow_other /proc/sys/kernel/random -v 6"
+          ];
+        };
+      })
+    ]);
+
+  meta.maintainers = with lib.maintainers; [ orichter thillux ];
+}
diff --git a/nixpkgs/nixos/modules/services/security/fail2ban.nix b/nixpkgs/nixos/modules/services/security/fail2ban.nix
index eebf2a2f7b55..9393fa751288 100644
--- a/nixpkgs/nixos/modules/services/security/fail2ban.nix
+++ b/nixpkgs/nixos/modules/services/security/fail2ban.nix
@@ -3,23 +3,44 @@
 with lib;
 
 let
-
   cfg = config.services.fail2ban;
 
-  fail2banConf = pkgs.writeText "fail2ban.local" cfg.daemonConfig;
+  settingsFormat = pkgs.formats.keyValue { };
 
-  jailConf = pkgs.writeText "jail.local" ''
-    [INCLUDES]
+  configFormat = pkgs.formats.ini {
+    mkKeyValue = generators.mkKeyValueDefault { } " = ";
+  };
 
-    before = paths-nixos.conf
+  mkJailConfig = name: attrs:
+    optionalAttrs (name != "DEFAULT") { inherit (attrs) enabled; } //
+    optionalAttrs (attrs.filter != null) { filter = if (builtins.isString filter) then filter else name; } //
+    attrs.settings;
 
-    ${concatStringsSep "\n" (attrValues (flip mapAttrs cfg.jails (name: def:
-      optionalString (def != "")
-        ''
-          [${name}]
-          ${def}
-        '')))}
-  '';
+  mkFilter = name: attrs: nameValuePair "fail2ban/filter.d/${name}.conf" {
+    source = configFormat.generate "filter.d/${name}.conf" attrs.filter;
+  };
+
+  fail2banConf = configFormat.generate "fail2ban.local" cfg.daemonSettings;
+
+  strJails = filterAttrs (_: builtins.isString) cfg.jails;
+  attrsJails = filterAttrs (_: builtins.isAttrs) cfg.jails;
+
+  jailConf =
+    let
+      configFile = configFormat.generate "jail.local" (
+        { INCLUDES.before = "paths-nixos.conf"; } // (mapAttrs mkJailConfig attrsJails)
+      );
+      extraConfig = concatStringsSep "\n" (attrValues (mapAttrs
+        (name: def:
+          optionalString (def != "")
+            ''
+              [${name}]
+              ${def}
+            '')
+        strJails));
+
+    in
+    pkgs.concatText "jail.local" [ configFile (pkgs.writeText "extra-jail.local" extraConfig) ];
 
   pathsConf = pkgs.writeText "paths-nixos.conf" ''
     # NixOS
@@ -32,15 +53,18 @@ let
 
     [DEFAULT]
   '';
-
 in
 
 {
 
+  imports = [
+    (mkRemovedOptionModule [ "services" "fail2ban" "daemonConfig" ] "The daemon is now configured through the attribute set `services.fail2ban.daemonSettings`.")
+    (mkRemovedOptionModule [ "services" "fail2ban" "extraSettings" ] "The extra default configuration can now be set using `services.fail2ban.jails.DEFAULT.settings`.")
+  ];
+
   ###### interface
 
   options = {
-
     services.fail2ban = {
       enable = mkOption {
         default = false;
@@ -69,7 +93,7 @@ in
       };
 
       extraPackages = mkOption {
-        default = [];
+        default = [ ];
         type = types.listOf types.package;
         example = lib.literalExpression "[ pkgs.ipset ]";
         description = lib.mdDoc ''
@@ -180,7 +204,7 @@ in
         example = true;
         description = lib.mdDoc ''
           "bantime.overalljails" (if true) specifies the search of IP in the database will be executed
-          cross over all jails, if false (default), only current jail of the ban IP will be searched
+          cross over all jails, if false (default), only current jail of the ban IP will be searched.
         '';
       };
 
@@ -194,60 +218,75 @@ in
         '';
       };
 
-      daemonConfig = mkOption {
-        default = ''
-          [Definition]
-          logtarget = SYSLOG
-          socket    = /run/fail2ban/fail2ban.sock
-          pidfile   = /run/fail2ban/fail2ban.pid
-          dbfile    = /var/lib/fail2ban/fail2ban.sqlite3
-        '';
-        type = types.lines;
-        description = lib.mdDoc ''
-          The contents of Fail2ban's main configuration file.  It's
-          generally not necessary to change it.
-       '';
-      };
+      daemonSettings = mkOption {
+        inherit (configFormat) type;
 
-      extraSettings = mkOption {
-        type = with types; attrsOf (oneOf [ bool ints.positive str ]);
-        default = {};
-        description = lib.mdDoc ''
-          Extra default configuration for all jails (i.e. `[DEFAULT]`). See
-          <https://github.com/fail2ban/fail2ban/blob/master/config/jail.conf> for an overview.
-        '';
-        example = literalExpression ''
+        defaultText = literalExpression ''
           {
-            findtime = "15m";
+            Definition = {
+              logtarget = "SYSLOG";
+              socket = "/run/fail2ban/fail2ban.sock";
+              pidfile = "/run/fail2ban/fail2ban.pid";
+              dbfile = "/var/lib/fail2ban/fail2ban.sqlite3";
+            };
           }
         '';
+        description = lib.mdDoc ''
+          The contents of Fail2ban's main configuration file.
+          It's generally not necessary to change it.
+        '';
       };
 
       jails = mkOption {
         default = { };
         example = literalExpression ''
-          { apache-nohome-iptables = '''
-              # Block an IP address if it accesses a non-existent
-              # home directory more than 5 times in 10 minutes,
-              # since that indicates that it's scanning.
-              filter   = apache-nohome
-              action   = iptables-multiport[name=HTTP, port="http,https"]
-              logpath  = /var/log/httpd/error_log*
-              backend = auto
-              findtime = 600
-              bantime  = 600
-              maxretry = 5
-            ''';
-           dovecot = '''
-             # block IPs which failed to log-in
-             # aggressive mode add blocking for aborted connections
-             enabled = true
-             filter = dovecot[mode=aggressive]
-             maxretry = 3
-           ''';
-          }
+          {
+            apache-nohome-iptables = {
+              settings = {
+                # Block an IP address if it accesses a non-existent
+                # home directory more than 5 times in 10 minutes,
+                # since that indicates that it's scanning.
+                filter = "apache-nohome";
+                action = '''iptables-multiport[name=HTTP, port="http,https"]''';
+                logpath = "/var/log/httpd/error_log*";
+                backend = "auto";
+                findtime = 600;
+                bantime = 600;
+                maxretry = 5;
+              };
+            };
+            dovecot = {
+              settings = {
+                # block IPs which failed to log-in
+                # aggressive mode add blocking for aborted connections
+                filter = "dovecot[mode=aggressive]";
+                maxretry = 3;
+              };
+            };
+          };
         '';
-        type = types.attrsOf types.lines;
+        type = with types; attrsOf (either lines (submodule ({ name, ... }: {
+          options = {
+            enabled = mkEnableOption "this jail." // {
+              default = true;
+              readOnly = name == "DEFAULT";
+            };
+
+            filter = mkOption {
+              type = nullOr (either str configFormat.type);
+
+              default = null;
+              description = lib.mdDoc "Content of the filter used for this jail.";
+            };
+
+            settings = mkOption {
+              inherit (settingsFormat) type;
+
+              default = { };
+              description = lib.mdDoc "Additional settings for this jail.";
+            };
+          };
+        })));
         description = lib.mdDoc ''
           The configuration of each Fail2ban “jail”.  A jail
           consists of an action (such as blocking a port using
@@ -278,7 +317,7 @@ in
   config = mkIf cfg.enable {
     assertions = [
       {
-        assertion = (cfg.bantime-increment.formula == null || cfg.bantime-increment.multipliers == null);
+        assertion = cfg.bantime-increment.formula == null || cfg.bantime-increment.multipliers == null;
         message = ''
           Options `services.fail2ban.bantime-increment.formula` and `services.fail2ban.bantime-increment.multipliers` cannot be both specified.
         '';
@@ -300,7 +339,7 @@ in
       "fail2ban/paths-nixos.conf".source = pathsConf;
       "fail2ban/action.d".source = "${cfg.package}/etc/fail2ban/action.d/*.conf";
       "fail2ban/filter.d".source = "${cfg.package}/etc/fail2ban/filter.d/*.conf";
-    };
+    } // (mapAttrs' mkFilter (filterAttrs (_: v: v.filter != null && !builtins.isString v.filter) attrsJails));
 
     systemd.packages = [ cfg.package ];
     systemd.services.fail2ban = {
@@ -335,39 +374,41 @@ in
       };
     };
 
+    # Defaults for the daemon settings
+    services.fail2ban.daemonSettings.Definition = {
+      logtarget = mkDefault "SYSLOG";
+      socket = mkDefault "/run/fail2ban/fail2ban.sock";
+      pidfile = mkDefault "/run/fail2ban/fail2ban.pid";
+      dbfile = mkDefault "/var/lib/fail2ban/fail2ban.sqlite3";
+    };
+
     # Add some reasonable default jails.  The special "DEFAULT" jail
     # sets default values for all other jails.
-    services.fail2ban.jails.DEFAULT = ''
-      # Bantime increment options
-      bantime.increment = ${boolToString cfg.bantime-increment.enable}
-      ${optionalString (cfg.bantime-increment.rndtime != null) "bantime.rndtime = ${cfg.bantime-increment.rndtime}"}
-      ${optionalString (cfg.bantime-increment.maxtime != null) "bantime.maxtime = ${cfg.bantime-increment.maxtime}"}
-      ${optionalString (cfg.bantime-increment.factor != null) "bantime.factor = ${cfg.bantime-increment.factor}"}
-      ${optionalString (cfg.bantime-increment.formula != null) "bantime.formula = ${cfg.bantime-increment.formula}"}
-      ${optionalString (cfg.bantime-increment.multipliers != null) "bantime.multipliers = ${cfg.bantime-increment.multipliers}"}
-      ${optionalString (cfg.bantime-increment.overalljails != null) "bantime.overalljails = ${boolToString cfg.bantime-increment.overalljails}"}
-      # Miscellaneous options
-      ignoreip    = 127.0.0.1/8 ${optionalString config.networking.enableIPv6 "::1"} ${concatStringsSep " " cfg.ignoreIP}
-      ${optionalString (cfg.bantime != null) ''
-        bantime     = ${cfg.bantime}
-      ''}
-      maxretry    = ${toString cfg.maxretry}
-      backend     = systemd
-      # Actions
-      banaction   = ${cfg.banaction}
-      banaction_allports = ${cfg.banaction-allports}
-      ${optionalString (cfg.extraSettings != {}) ''
-        # Extra settings
-        ${generators.toKeyValue {} cfg.extraSettings}
-      ''}
-    '';
-    # Block SSH if there are too many failing connection attempts.
+    services.fail2ban.jails = mkMerge [
+      {
+        DEFAULT.settings = (optionalAttrs cfg.bantime-increment.enable
+          ({ "bantime.increment" = cfg.bantime-increment.enable; } // (mapAttrs'
+            (name: nameValuePair "bantime.${name}")
+            (filterAttrs (n: v: v != null && n != "enable") cfg.bantime-increment))
+          )
+        ) // {
+          # Miscellaneous options
+          inherit (cfg) banaction maxretry;
+          ignoreip = ''127.0.0.1/8 ${optionalString config.networking.enableIPv6 "::1"} ${concatStringsSep " " cfg.ignoreIP}'';
+          backend = "systemd";
+          # Actions
+          banaction_allports = cfg.banaction-allports;
+        };
+      }
+
+      # Block SSH if there are too many failing connection attempts.
+      (mkIf config.services.openssh.enable {
+        sshd.settings.port = mkDefault (concatMapStringsSep "," builtins.toString config.services.openssh.ports);
+      })
+    ];
+
     # Benefits from verbose sshd logging to observe failed login attempts,
     # so we set that here unless the user overrode it.
-    services.openssh.settings.LogLevel = lib.mkDefault "VERBOSE";
-    services.fail2ban.jails.sshd = mkDefault ''
-      enabled = true
-      port    = ${concatMapStringsSep "," (p: toString p) config.services.openssh.ports}
-    '';
+    services.openssh.settings.LogLevel = mkDefault "VERBOSE";
   };
 }
diff --git a/nixpkgs/nixos/modules/services/security/kanidm.nix b/nixpkgs/nixos/modules/services/security/kanidm.nix
index 7ec7a1598735..6fb9f71a489e 100644
--- a/nixpkgs/nixos/modules/services/security/kanidm.nix
+++ b/nixpkgs/nixos/modules/services/security/kanidm.nix
@@ -17,7 +17,7 @@ let
       # If the new path is a prefix to some existing path, we need to filter it out
       filteredPaths = lib.filter (p: !lib.hasPrefix (builtins.toString newPath) (builtins.toString p)) merged;
       # If a prefix of the new path is already in the list, do not add it
-      filteredNew = if hasPrefixInList filteredPaths newPath then [] else [ newPath ];
+      filteredNew = lib.optional (!hasPrefixInList filteredPaths newPath) newPath;
     in filteredPaths ++ filteredNew) [];
 
   defaultServiceConfig = {
@@ -122,8 +122,8 @@ in
           };
           log_level = lib.mkOption {
             description = lib.mdDoc "Log level of the server.";
-            default = "default";
-            type = lib.types.enum [ "default" "verbose" "perfbasic" "perffull" ];
+            default = "info";
+            type = lib.types.enum [ "info" "debug" "trace" ];
           };
           role = lib.mkOption {
             description = lib.mdDoc "The role of this server. This affects the replication relationship and thereby available features.";
@@ -236,17 +236,23 @@ in
         {
           StateDirectory = "kanidm";
           StateDirectoryMode = "0700";
+          RuntimeDirectory = "kanidmd";
           ExecStart = "${pkgs.kanidm}/bin/kanidmd server -c ${serverConfigFile}";
           User = "kanidm";
           Group = "kanidm";
 
+          BindPaths = [
+            # To create the socket
+            "/run/kanidmd:/run/kanidmd"
+          ];
+
           AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
           CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
           # This would otherwise override the CAP_NET_BIND_SERVICE capability.
           PrivateUsers = lib.mkForce false;
           # Port needs to be exposed to the host network
           PrivateNetwork = lib.mkForce false;
-          RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+          RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
           TemporaryFileSystem = "/:ro";
         }
       ];
@@ -273,6 +279,8 @@ in
             "-/etc/static/kanidm"
             "-/etc/ssl"
             "-/etc/static/ssl"
+            "-/etc/passwd"
+            "-/etc/group"
           ];
           BindPaths = [
             # To create the socket
@@ -320,12 +328,16 @@ in
         ProtectHome = false;
         RestrictAddressFamilies = [ "AF_UNIX" ];
         TemporaryFileSystem = "/:ro";
+        Restart = "on-failure";
       };
       environment.RUST_LOG = "info";
     };
 
     # These paths are hardcoded
     environment.etc = lib.mkMerge [
+      (lib.mkIf cfg.enableServer {
+        "kanidm/server.toml".source = serverConfigFile;
+      })
       (lib.mkIf options.services.kanidm.clientSettings.isDefined {
         "kanidm/config".source = clientConfigFile;
       })
diff --git a/nixpkgs/nixos/modules/services/security/tor.nix b/nixpkgs/nixos/modules/services/security/tor.nix
index 2aa2964f8818..9e786eb2bf06 100644
--- a/nixpkgs/nixos/modules/services/security/tor.nix
+++ b/nixpkgs/nixos/modules/services/security/tor.nix
@@ -769,7 +769,7 @@ in
           };
           options.SOCKSPort = mkOption {
             description = lib.mdDoc (descriptionGeneric "SOCKSPort");
-            default = if cfg.settings.HiddenServiceNonAnonymousMode == true then [{port = 0;}] else [];
+            default = lib.optionals cfg.settings.HiddenServiceNonAnonymousMode [{port = 0;}];
             defaultText = literalExpression ''
               if config.${opt.settings}.HiddenServiceNonAnonymousMode == true
               then [ { port = 0; } ]
@@ -897,8 +897,7 @@ in
       allowedTCPPorts =
         concatMap (o:
           if isInt o && o > 0 then [o]
-          else if o ? "port" && isInt o.port && o.port > 0 then [o.port]
-          else []
+          else optionals (o ? "port" && isInt o.port && o.port > 0) [o.port]
         ) (flatten [
           cfg.settings.ORPort
           cfg.settings.DirPort
diff --git a/nixpkgs/nixos/modules/services/security/usbguard.nix b/nixpkgs/nixos/modules/services/security/usbguard.nix
index 1d846b194077..9b158bb9d18c 100644
--- a/nixpkgs/nixos/modules/services/security/usbguard.nix
+++ b/nixpkgs/nixos/modules/services/security/usbguard.nix
@@ -15,7 +15,7 @@ let
   daemonConf = ''
     # generated by nixos/modules/services/security/usbguard.nix
     RuleFile=${ruleFile}
-    ImplicitPolicyTarget=${cfg.implictPolicyTarget}
+    ImplicitPolicyTarget=${cfg.implicitPolicyTarget}
     PresentDevicePolicy=${cfg.presentDevicePolicy}
     PresentControllerPolicy=${cfg.presentControllerPolicy}
     InsertedDevicePolicy=${cfg.insertedDevicePolicy}
@@ -73,7 +73,7 @@ in
         '';
       };
 
-      implictPolicyTarget = mkOption {
+      implicitPolicyTarget = mkOption {
         type = policy;
         default = "block";
         description = lib.mdDoc ''
@@ -150,6 +150,8 @@ in
           Generate device specific rules including the "via-port" attribute.
         '';
       };
+
+      dbus.enable = mkEnableOption (lib.mdDoc "USBGuard dbus daemon");
     };
   };
 
@@ -160,53 +162,95 @@ in
 
     environment.systemPackages = [ cfg.package ];
 
-    systemd.services.usbguard = {
-      description = "USBGuard daemon";
-
-      wantedBy = [ "basic.target" ];
-      wants = [ "systemd-udevd.service" ];
-
-      # make sure an empty rule file exists
-      preStart = ''[ -f "${ruleFile}" ] || touch ${ruleFile}'';
-
-      serviceConfig = {
-        Type = "simple";
-        ExecStart = "${cfg.package}/bin/usbguard-daemon -P -k -c ${daemonConfFile}";
-        Restart = "on-failure";
-
-        StateDirectory = [
-          "usbguard"
-          "usbguard/IPCAccessControl.d"
-        ];
-
-        AmbientCapabilities = "";
-        CapabilityBoundingSet = "CAP_CHOWN CAP_FOWNER";
-        DeviceAllow = "/dev/null rw";
-        DevicePolicy = "strict";
-        IPAddressDeny = "any";
-        LockPersonality = true;
-        MemoryDenyWriteExecute = true;
-        NoNewPrivileges = true;
-        PrivateDevices = true;
-        PrivateTmp = true;
-        ProtectControlGroups = true;
-        ProtectHome = true;
-        ProtectKernelModules = true;
-        ProtectSystem = true;
-        ReadOnlyPaths = "-/";
-        ReadWritePaths = "-/dev/shm -/tmp";
-        RestrictAddressFamilies = [ "AF_UNIX" "AF_NETLINK" ];
-        RestrictNamespaces = true;
-        RestrictRealtime = true;
-        SystemCallArchitectures = "native";
-        SystemCallFilter = "@system-service";
-        UMask = "0077";
+    systemd.services = {
+      usbguard = {
+        description = "USBGuard daemon";
+
+        wantedBy = [ "basic.target" ];
+        wants = [ "systemd-udevd.service" ];
+
+        # make sure an empty rule file exists
+        preStart = ''[ -f "${ruleFile}" ] || touch ${ruleFile}'';
+
+        serviceConfig = {
+          Type = "simple";
+          ExecStart = "${cfg.package}/bin/usbguard-daemon -P -k -c ${daemonConfFile}";
+          Restart = "on-failure";
+
+          StateDirectory = [
+            "usbguard"
+            "usbguard/IPCAccessControl.d"
+          ];
+
+          AmbientCapabilities = "";
+          CapabilityBoundingSet = "CAP_CHOWN CAP_FOWNER";
+          DeviceAllow = "/dev/null rw";
+          DevicePolicy = "strict";
+          IPAddressDeny = "any";
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
+          PrivateDevices = true;
+          PrivateTmp = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectKernelModules = true;
+          ProtectSystem = true;
+          ReadOnlyPaths = "-/";
+          ReadWritePaths = "-/dev/shm -/tmp";
+          RestrictAddressFamilies = [ "AF_UNIX" "AF_NETLINK" ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          SystemCallArchitectures = "native";
+          SystemCallFilter = "@system-service";
+          UMask = "0077";
+        };
+      };
+
+      usbguard-dbus = mkIf cfg.dbus.enable {
+        description = "USBGuard D-Bus Service";
+
+        wantedBy = [ "multi-user.target" ];
+        requires = [ "usbguard.service" ];
+
+        serviceConfig = {
+          Type = "dbus";
+          BusName = "org.usbguard1";
+          ExecStart = "${cfg.package}/bin/usbguard-dbus --system";
+          Restart = "on-failure";
+        };
+
+        aliases = [ "dbus-org.usbguard.service" ];
       };
     };
+
+    security.polkit.extraConfig =
+      let
+        groupCheck = (lib.concatStrings (map
+          (g: "subject.isInGroup(\"${g}\") || ")
+          cfg.IPCAllowedGroups))
+        + "false";
+      in
+      optionalString cfg.dbus.enable ''
+        polkit.addRule(function(action, subject) {
+            if ((action.id == "org.usbguard.Policy1.listRules" ||
+                 action.id == "org.usbguard.Policy1.appendRule" ||
+                 action.id == "org.usbguard.Policy1.removeRule" ||
+                 action.id == "org.usbguard.Devices1.applyDevicePolicy" ||
+                 action.id == "org.usbguard.Devices1.listDevices" ||
+                 action.id == "org.usbguard1.getParameter" ||
+                 action.id == "org.usbguard1.setParameter") &&
+                subject.active == true && subject.local == true &&
+                (${groupCheck})) {
+                    return polkit.Result.YES;
+            }
+        });
+      '';
   };
   imports = [
     (mkRemovedOptionModule [ "services" "usbguard" "ruleFile" ] "The usbguard module now uses ${defaultRuleFile} as ruleFile. Alternatively, use services.usbguard.rules to configure rules.")
     (mkRemovedOptionModule [ "services" "usbguard" "IPCAccessControlFiles" ] "The usbguard module now hardcodes IPCAccessControlFiles to /var/lib/usbguard/IPCAccessControl.d.")
     (mkRemovedOptionModule [ "services" "usbguard" "auditFilePath" ] "Removed usbguard module audit log files. Audit logs can be found in the systemd journal.")
+    (mkRenamedOptionModule [ "services" "usbguard" "implictPolicyTarget" ] [ "services" "usbguard" "implicitPolicyTarget" ])
   ];
 }
diff --git a/nixpkgs/nixos/modules/services/security/vault.nix b/nixpkgs/nixos/modules/services/security/vault.nix
index 7b9e31a8d990..18d981cdb0d2 100644
--- a/nixpkgs/nixos/modules/services/security/vault.nix
+++ b/nixpkgs/nixos/modules/services/security/vault.nix
@@ -221,6 +221,7 @@ in
         ProtectHome = "read-only";
         AmbientCapabilities = "cap_ipc_lock";
         NoNewPrivileges = true;
+        LimitCORE = 0;
         KillSignal = "SIGINT";
         TimeoutStopSec = "30s";
         Restart = "on-failure";
diff --git a/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix b/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix
index aaa3f5507f77..d22e6b5b40cd 100644
--- a/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix
+++ b/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix
@@ -59,7 +59,12 @@ in {
 
     config = mkOption {
       type = attrsOf (nullOr (oneOf [ bool int str ]));
-      default = {};
+      default = {
+        config = {
+          ROCKET_ADDRESS = "::1"; # default to localhost
+          ROCKET_PORT = 8222;
+        };
+      };
       example = literalExpression ''
         {
           DOMAIN = "https://bitwarden.example.com";
@@ -116,7 +121,7 @@ in {
         The available configuration options can be found in
         [the environment template file](https://github.com/dani-garcia/vaultwarden/blob/${vaultwarden.version}/.env.template).
 
-        See ()[#opt-services.vaultwarden.environmentFile) for how
+        See [](#opt-services.vaultwarden.environmentFile) for how
         to set up access to the Admin UI to invite initial users.
       '';
     };
diff --git a/nixpkgs/nixos/modules/services/system/bpftune.nix b/nixpkgs/nixos/modules/services/system/bpftune.nix
new file mode 100644
index 000000000000..d656a19c0ad1
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/system/bpftune.nix
@@ -0,0 +1,22 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.services.bpftune;
+in
+{
+  meta = {
+    maintainers = with lib.maintainers; [ nickcao ];
+  };
+
+  options = {
+    services.bpftune = {
+      enable = lib.mkEnableOption (lib.mdDoc "bpftune BPF driven auto-tuning");
+
+      package = lib.mkPackageOptionMD pkgs "bpftune" { };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.packages = [ cfg.package ];
+    systemd.services.bpftune.wantedBy = [ "multi-user.target" ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/system/cloud-init.nix b/nixpkgs/nixos/modules/services/system/cloud-init.nix
index 4cda06ed4248..d782bb1a3666 100644
--- a/nixpkgs/nixos/modules/services/system/cloud-init.nix
+++ b/nixpkgs/nixos/modules/services/system/cloud-init.nix
@@ -15,6 +15,7 @@ let
   ]
   ++ optional cfg.btrfs.enable btrfs-progs
   ++ optional cfg.ext4.enable e2fsprogs
+  ++ optional cfg.xfs.enable xfsprogs
   ;
   settingsFormat = pkgs.formats.yaml { };
   cfgfile = settingsFormat.generate "cloud.cfg" cfg.settings;
@@ -57,6 +58,14 @@ in
         '';
       };
 
+      xfs.enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = mdDoc ''
+          Allow the cloud-init service to operate `xfs` filesystem.
+        '';
+      };
+
       network.enable = mkOption {
         type = types.bool;
         default = false;
diff --git a/nixpkgs/nixos/modules/services/system/dbus.nix b/nixpkgs/nixos/modules/services/system/dbus.nix
index 9d8a62ec78c5..8d5b25e61762 100644
--- a/nixpkgs/nixos/modules/services/system/dbus.nix
+++ b/nixpkgs/nixos/modules/services/system/dbus.nix
@@ -22,7 +22,7 @@ in
   options = {
 
     boot.initrd.systemd.dbus = {
-      enable = mkEnableOption (lib.mdDoc "dbus in stage 1") // { visible = false; };
+      enable = mkEnableOption (lib.mdDoc "dbus in stage 1");
     };
 
     services.dbus = {
diff --git a/nixpkgs/nixos/modules/services/system/nix-daemon.nix b/nixpkgs/nixos/modules/services/system/nix-daemon.nix
new file mode 100644
index 000000000000..c9df20196dbd
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/system/nix-daemon.nix
@@ -0,0 +1,264 @@
+/*
+  Declares what makes the nix-daemon work on systemd.
+
+  See also
+   - nixos/modules/config/nix.nix: the nix.conf
+   - nixos/modules/config/nix-remote-build.nix: the nix.conf
+*/
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.nix;
+
+  nixPackage = cfg.package.out;
+
+  isNixAtLeast = versionAtLeast (getVersion nixPackage);
+
+  makeNixBuildUser = nr: {
+    name = "nixbld${toString nr}";
+    value = {
+      description = "Nix build user ${toString nr}";
+
+      /*
+        For consistency with the setgid(2), setuid(2), and setgroups(2)
+        calls in `libstore/build.cc', don't add any supplementary group
+        here except "nixbld".
+      */
+      uid = builtins.add config.ids.uids.nixbld nr;
+      isSystemUser = true;
+      group = "nixbld";
+      extraGroups = [ "nixbld" ];
+    };
+  };
+
+  nixbldUsers = listToAttrs (map makeNixBuildUser (range 1 cfg.nrBuildUsers));
+
+in
+
+{
+  imports = [
+    (mkRenamedOptionModuleWith { sinceRelease = 2205; from = [ "nix" "daemonIONiceLevel" ]; to = [ "nix" "daemonIOSchedPriority" ]; })
+    (mkRenamedOptionModuleWith { sinceRelease = 2211; from = [ "nix" "readOnlyStore" ]; to = [ "boot" "readOnlyNixStore" ]; })
+    (mkRemovedOptionModule [ "nix" "daemonNiceLevel" ] "Consider nix.daemonCPUSchedPolicy instead.")
+  ];
+
+  ###### interface
+
+  options = {
+
+    nix = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = lib.mdDoc ''
+          Whether to enable Nix.
+          Disabling Nix makes the system hard to modify and the Nix programs and configuration will not be made available by NixOS itself.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.nix;
+        defaultText = literalExpression "pkgs.nix";
+        description = lib.mdDoc ''
+          This option specifies the Nix package instance to use throughout the system.
+        '';
+      };
+
+      daemonCPUSchedPolicy = mkOption {
+        type = types.enum [ "other" "batch" "idle" ];
+        default = "other";
+        example = "batch";
+        description = lib.mdDoc ''
+          Nix daemon process CPU scheduling policy. This policy propagates to
+          build processes. `other` is the default scheduling
+          policy for regular tasks. The `batch` policy is
+          similar to `other`, but optimised for
+          non-interactive tasks. `idle` is for extremely
+          low-priority tasks that should only be run when no other task
+          requires CPU time.
+
+          Please note that while using the `idle` policy may
+          greatly improve responsiveness of a system performing expensive
+          builds, it may also slow down and potentially starve crucial
+          configuration updates during load.
+
+          `idle` may therefore be a sensible policy for
+          systems that experience only intermittent phases of high CPU load,
+          such as desktop or portable computers used interactively. Other
+          systems should use the `other` or
+          `batch` policy instead.
+
+          For more fine-grained resource control, please refer to
+          {manpage}`systemd.resource-control(5)` and adjust
+          {option}`systemd.services.nix-daemon` directly.
+      '';
+      };
+
+      daemonIOSchedClass = mkOption {
+        type = types.enum [ "best-effort" "idle" ];
+        default = "best-effort";
+        example = "idle";
+        description = lib.mdDoc ''
+          Nix daemon process I/O scheduling class. This class propagates to
+          build processes. `best-effort` is the default
+          class for regular tasks. The `idle` class is for
+          extremely low-priority tasks that should only perform I/O when no
+          other task does.
+
+          Please note that while using the `idle` scheduling
+          class can improve responsiveness of a system performing expensive
+          builds, it might also slow down or starve crucial configuration
+          updates during load.
+
+          `idle` may therefore be a sensible class for
+          systems that experience only intermittent phases of high I/O load,
+          such as desktop or portable computers used interactively. Other
+          systems should use the `best-effort` class.
+      '';
+      };
+
+      daemonIOSchedPriority = mkOption {
+        type = types.int;
+        default = 4;
+        example = 1;
+        description = lib.mdDoc ''
+          Nix daemon process I/O scheduling priority. This priority propagates
+          to build processes. The supported priorities depend on the
+          scheduling policy: With idle, priorities are not used in scheduling
+          decisions. best-effort supports values in the range 0 (high) to 7
+          (low).
+        '';
+      };
+
+      # Environment variables for running Nix.
+      envVars = mkOption {
+        type = types.attrs;
+        internal = true;
+        default = { };
+        description = lib.mdDoc "Environment variables used by Nix.";
+      };
+
+      nrBuildUsers = mkOption {
+        type = types.int;
+        description = lib.mdDoc ''
+          Number of `nixbld` user accounts created to
+          perform secure concurrent builds.  If you receive an error
+          message saying that “all build users are currently in use”,
+          you should increase this value.
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages =
+      [
+        nixPackage
+        pkgs.nix-info
+      ]
+      ++ optional (config.programs.bash.enableCompletion) pkgs.nix-bash-completions;
+
+    systemd.packages = [ nixPackage ];
+
+    systemd.tmpfiles = mkMerge [
+      (mkIf (isNixAtLeast "2.8") {
+        packages = [ nixPackage ];
+      })
+      (mkIf (!isNixAtLeast "2.8") {
+        rules = [
+          "d /nix/var/nix/daemon-socket 0755 root root - -"
+        ];
+      })
+    ];
+
+    systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ];
+
+    systemd.services.nix-daemon =
+      {
+        path = [ nixPackage pkgs.util-linux config.programs.ssh.package ]
+          ++ optionals cfg.distributedBuilds [ pkgs.gzip ];
+
+        environment = cfg.envVars
+          // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"; }
+          // config.networking.proxy.envVars;
+
+        unitConfig.RequiresMountsFor = "/nix/store";
+
+        serviceConfig =
+          {
+            CPUSchedulingPolicy = cfg.daemonCPUSchedPolicy;
+            IOSchedulingClass = cfg.daemonIOSchedClass;
+            IOSchedulingPriority = cfg.daemonIOSchedPriority;
+            LimitNOFILE = 1048576;
+          };
+
+        restartTriggers = [ config.environment.etc."nix/nix.conf".source ];
+
+        # `stopIfChanged = false` changes to switch behavior
+        # from   stop -> update units -> start
+        #   to   update units -> restart
+        #
+        # The `stopIfChanged` setting therefore controls a trade-off between a
+        # more predictable lifecycle, which runs the correct "version" of
+        # the `ExecStop` line, and on the other hand the availability of
+        # sockets during the switch, as the effectiveness of the stop operation
+        # depends on the socket being stopped as well.
+        #
+        # As `nix-daemon.service` does not make use of `ExecStop`, we prefer
+        # to keep the socket up and available. This is important for machines
+        # that run Nix-based services, such as automated build, test, and deploy
+        # services, that expect the daemon socket to be available at all times.
+        #
+        # Notably, the Nix client does not retry on failure to connect to the
+        # daemon socket, and the in-process RemoteStore instance will disable
+        # itself. This makes retries infeasible even for services that are
+        # aware of the issue. Failure to connect can affect not only new client
+        # processes, but also new RemoteStore instances in existing processes,
+        # as well as existing RemoteStore instances that have not saturated
+        # their connection pool.
+        #
+        # Also note that `stopIfChanged = true` does not kill existing
+        # connection handling daemons, as one might wish to happen before a
+        # breaking Nix upgrade (which is rare). The daemon forks that handle
+        # the individual connections split off into their own sessions, causing
+        # them not to be stopped by systemd.
+        # If a Nix upgrade does require all existing daemon processes to stop,
+        # nix-daemon must do so on its own accord, and only when the new version
+        # starts and detects that Nix's persistent state needs an upgrade.
+        stopIfChanged = false;
+
+      };
+
+    # Set up the environment variables for running Nix.
+    environment.sessionVariables = cfg.envVars;
+
+    nix.nrBuildUsers = mkDefault (
+      if cfg.settings.auto-allocate-uids or false then 0
+      else max 32 (if cfg.settings.max-jobs == "auto" then 0 else cfg.settings.max-jobs)
+    );
+
+    users.users = nixbldUsers;
+
+    services.xserver.displayManager.hiddenUsers = attrNames nixbldUsers;
+
+    system.activationScripts.nix = stringAfter [ "etc" "users" ]
+      ''
+        install -m 0755 -d /nix/var/nix/{gcroots,profiles}/per-user
+      '';
+
+    # Legacy configuration conversion.
+    nix.settings = mkMerge [
+      (mkIf (isNixAtLeast "2.3pre") { sandbox-fallback = false; })
+    ];
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/ttys/kmscon.nix b/nixpkgs/nixos/modules/services/ttys/kmscon.nix
index f5a8d8b104d2..0a12ef48d084 100644
--- a/nixpkgs/nixos/modules/services/ttys/kmscon.nix
+++ b/nixpkgs/nixos/modules/services/ttys/kmscon.nix
@@ -99,6 +99,7 @@ in {
     systemd.units."kmsconvt@.service".aliases = [ "autovt@.service" ];
 
     systemd.services.systemd-vconsole-setup.enable = false;
+    systemd.services.reload-systemd-vconsole-setup.enable = false;
 
     services.kmscon.extraConfig =
       let
@@ -110,7 +111,7 @@ in {
 
     fonts = mkIf (cfg.fonts != null) {
       fontconfig.enable = true;
-      fonts = map (f: f.package) cfg.fonts;
+      packages = map (f: f.package) cfg.fonts;
     };
   };
 }
diff --git a/nixpkgs/nixos/modules/services/video/frigate.nix b/nixpkgs/nixos/modules/services/video/frigate.nix
index 217637cbebcf..8db2bfae80ac 100644
--- a/nixpkgs/nixos/modules/services/video/frigate.nix
+++ b/nixpkgs/nixos/modules/services/video/frigate.nix
@@ -322,6 +322,16 @@ in
       '';
     };
 
+    systemd.services.nginx.serviceConfig.SupplementaryGroups = [
+      "frigate"
+    ];
+
+    users.users.frigate = {
+      isSystemUser = true;
+      group = "frigate";
+    };
+    users.groups.frigate = {};
+
     systemd.services.frigate = {
       after = [
         "go2rtc.service"
@@ -349,15 +359,18 @@ in
       serviceConfig = {
         ExecStart = "${cfg.package.python.interpreter} -m frigate";
 
-        DynamicUser = true;
         User = "frigate";
+        Group = "frigate";
+
+        UMask = "0027";
 
         StateDirectory = "frigate";
-        UMask = "0077";
+        StateDirectoryMode = "0750";
 
         # Caches
         PrivateTmp = true;
         CacheDirectory = "frigate";
+        CacheDirectoryMode = "0750";
 
         BindPaths = [
           "/migrations:${cfg.package}/share/frigate/migrations:ro"
diff --git a/nixpkgs/nixos/modules/services/wayland/cage.nix b/nixpkgs/nixos/modules/services/wayland/cage.nix
index 330dce1d0c0f..cf4c0798cd48 100644
--- a/nixpkgs/nixos/modules/services/wayland/cage.nix
+++ b/nixpkgs/nixos/modules/services/wayland/cage.nix
@@ -23,6 +23,15 @@ in {
     example = ["-d"];
   };
 
+  options.services.cage.environment = mkOption {
+    type = types.attrsOf types.str;
+    default = {};
+    example = {
+      WLR_LIBINPUT_NO_DEVICES = "1";
+    };
+    description = lib.mdDoc "Additional environment variables to pass to Cage.";
+  };
+
   options.services.cage.program = mkOption {
     type = types.path;
     default = "${pkgs.xterm}/bin/xterm";
@@ -79,6 +88,7 @@ in {
         # Set up a full (custom) user session for the user, required by Cage.
         PAMName = "cage";
       };
+      environment = cfg.environment;
     };
 
     security.polkit.enable = true;
diff --git a/nixpkgs/nixos/modules/services/web-apps/anuko-time-tracker.nix b/nixpkgs/nixos/modules/services/web-apps/anuko-time-tracker.nix
new file mode 100644
index 000000000000..f43cbc40ec7a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/anuko-time-tracker.nix
@@ -0,0 +1,388 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.services.anuko-time-tracker;
+  configFile = let
+    smtpPassword = if cfg.settings.email.smtpPasswordFile == null
+                   then "''"
+                   else "trim(file_get_contents('${cfg.settings.email.smtpPasswordFile}'))";
+
+  in pkgs.writeText "config.php" ''
+    <?php
+    // Set include path for PEAR and its modules, which we include in the distribution.
+    // Updated for the correct location in the nix store.
+    set_include_path('${cfg.package}/WEB-INF/lib/pear' . PATH_SEPARATOR . get_include_path());
+    define('DSN', 'mysqli://${cfg.database.user}@${cfg.database.host}/${cfg.database.name}?charset=utf8mb4');
+    define('MULTIORG_MODE', ${lib.boolToString cfg.settings.multiorgMode});
+    define('EMAIL_REQUIRED', ${lib.boolToString cfg.settings.emailRequired});
+    define('WEEKEND_START_DAY', ${toString cfg.settings.weekendStartDay});
+    define('FORUM_LINK', '${cfg.settings.forumLink}');
+    define('HELP_LINK', '${cfg.settings.helpLink}');
+    define('SENDER', '${cfg.settings.email.sender}');
+    define('MAIL_MODE', '${cfg.settings.email.mode}');
+    define('MAIL_SMTP_HOST', '${toString cfg.settings.email.smtpHost}');
+    define('MAIL_SMTP_PORT', '${toString cfg.settings.email.smtpPort}');
+    define('MAIL_SMTP_USER', '${cfg.settings.email.smtpUser}');
+    define('MAIL_SMTP_PASSWORD', ${smtpPassword});
+    define('MAIL_SMTP_AUTH', ${lib.boolToString cfg.settings.email.smtpAuth});
+    define('MAIL_SMTP_DEBUG', ${lib.boolToString cfg.settings.email.smtpDebug});
+    define('DEFAULT_CSS', 'default.css');
+    define('RTL_CSS', 'rtl.css'); // For right to left languages.
+    define('LANG_DEFAULT', '${cfg.settings.defaultLanguage}');
+    define('CURRENCY_DEFAULT', '${cfg.settings.defaultCurrency}');
+    define('EXPORT_DECIMAL_DURATION', ${lib.boolToString cfg.settings.exportDecimalDuration});
+    define('REPORT_FOOTER', ${lib.boolToString cfg.settings.reportFooter});
+    define('AUTH_MODULE', 'db');
+  '';
+  package = pkgs.stdenv.mkDerivation rec {
+    pname = "anuko-time-tracker";
+    inherit (src) version;
+    src = cfg.package;
+    installPhase = ''
+      mkdir -p $out
+      cp -r * $out/
+
+      # Link config file
+      ln -s ${configFile} $out/WEB-INF/config.php
+
+      # Link writable templates_c directory
+      rm -rf $out/WEB-INF/templates_c
+      ln -s ${cfg.dataDir}/templates_c $out/WEB-INF/templates_c
+
+      # Remove unsafe dbinstall.php
+      rm -f $out/dbinstall.php
+    '';
+  };
+in
+{
+  options.services.anuko-time-tracker = {
+    enable = lib.mkEnableOption (lib.mdDoc "Anuko Time Tracker");
+
+    package = lib.mkPackageOptionMD pkgs "anuko-time-tracker" {};
+
+    database = {
+      createLocally = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = lib.mdDoc "Create the database and database user locally.";
+      };
+
+      host = lib.mkOption {
+        type = lib.types.str;
+        description = lib.mdDoc "Database host.";
+        default = "localhost";
+      };
+
+      name = lib.mkOption {
+        type = lib.types.str;
+        description = lib.mdDoc "Database name.";
+        default = "anuko_time_tracker";
+      };
+
+      user = lib.mkOption {
+        type = lib.types.str;
+        description = lib.mdDoc "Database username.";
+        default = "anuko_time_tracker";
+      };
+
+      passwordFile = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
+        description = lib.mdDoc "Database user password file.";
+        default = null;
+      };
+    };
+
+    poolConfig = lib.mkOption {
+      type = lib.types.attrsOf (lib.types.oneOf [ lib.types.str lib.types.int lib.types.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 = lib.mdDoc ''
+        Options for Anuko Time Tracker's PHP-FPM pool.
+      '';
+    };
+
+    hostname = lib.mkOption {
+      type = lib.types.str;
+      default =
+        if config.networking.domain != null
+        then config.networking.fqdn
+        else config.networking.hostName;
+      defaultText = lib.literalExpression "config.networking.fqdn";
+      example = "anuko.example.com";
+      description = lib.mdDoc ''
+        The hostname to serve Anuko Time Tracker on.
+      '';
+    };
+
+    nginx = lib.mkOption {
+      type = lib.types.submodule (
+        lib.recursiveUpdate
+          (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) {}
+      );
+      default = {};
+      example = lib.literalExpression ''
+        {
+          serverAliases = [
+            "anuko.''${config.networking.domain}"
+          ];
+
+          # To enable encryption and let let's encrypt take care of certificate
+          forceSSL = true;
+          enableACME = true;
+        }
+      '';
+      description = lib.mdDoc ''
+        With this option, you can customize the Nginx virtualHost settings.
+      '';
+    };
+
+    dataDir = lib.mkOption {
+      type = lib.types.str;
+      default = "/var/lib/anuko-time-tracker";
+      description = lib.mdDoc "Default data folder for Anuko Time Tracker.";
+      example = "/mnt/anuko-time-tracker";
+    };
+
+    user = lib.mkOption {
+      type = lib.types.str;
+      default = "anuko_time_tracker";
+      description = lib.mdDoc "User under which Anuko Time Tracker runs.";
+    };
+
+    settings = {
+      multiorgMode = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = lib.mdDoc ''
+          Defines whether users see the Register option in the menu of Time Tracker that allows them
+          to self-register and create new organizations (top groups).
+        '';
+      };
+
+      emailRequired = lib.mkOption {
+        type = lib.types.bool;
+        default = false;
+        description = lib.mdDoc "Defines whether an email is required for new registrations.";
+      };
+
+      weekendStartDay = lib.mkOption {
+        type = lib.types.int;
+        default = 6;
+        description = lib.mdDoc ''
+          This option defines which days are highlighted with weekend color.
+          6 means Saturday. For Saudi Arabia, etc. set it to 4 for Thursday and Friday to be
+          weekend days.
+        '';
+      };
+
+      forumLink = lib.mkOption {
+        type = lib.types.str;
+        description = lib.mdDoc "Forum link from the main menu.";
+        default = "https://www.anuko.com/forum/viewforum.php?f=4";
+      };
+
+      helpLink = lib.mkOption {
+        type = lib.types.str;
+        description = lib.mdDoc "Help link from the main menu.";
+        default = "https://www.anuko.com/time-tracker/user-guide/index.htm";
+      };
+
+      email = {
+        sender = lib.mkOption {
+          type = lib.types.str;
+          description = lib.mdDoc "Default sender for mail.";
+          default = "Anuko Time Tracker <bounces@example.com>";
+        };
+
+        mode = lib.mkOption {
+          type = lib.types.str;
+          description = lib.mdDoc "Mail sending mode. Can be 'mail' or 'smtp'.";
+          default = "smtp";
+        };
+
+        smtpHost = lib.mkOption {
+          type = lib.types.str;
+          description = lib.mdDoc "MTA hostname.";
+          default = "localhost";
+        };
+
+        smtpPort = lib.mkOption {
+          type = lib.types.int;
+          description = lib.mdDoc "MTA port.";
+          default = 25;
+        };
+
+        smtpUser = lib.mkOption {
+          type = lib.types.str;
+          description = lib.mdDoc "MTA authentication username.";
+          default = "";
+        };
+
+        smtpAuth = lib.mkOption {
+          type = lib.types.bool;
+          default = false;
+          description = lib.mdDoc "MTA requires authentication.";
+        };
+
+        smtpPasswordFile = lib.mkOption {
+          type = lib.types.nullOr lib.types.path;
+          default = null;
+          example = "/var/lib/anuko-time-tracker/secrets/smtp-password";
+          description = lib.mdDoc ''
+            Path to file containing the MTA authentication password.
+          '';
+        };
+
+        smtpDebug = lib.mkOption {
+          type = lib.types.bool;
+          default = false;
+          description = lib.mdDoc "Debug mail sending.";
+        };
+      };
+
+      defaultLanguage = lib.mkOption {
+        type = lib.types.str;
+        description = lib.mdDoc ''
+          Defines Anuko Time Tracker default language. It is used on Time Tracker login page.
+          After login, a language set for user group is used.
+          Empty string means the language is defined by user browser.
+        '';
+        default = "";
+        example = "nl";
+      };
+
+      defaultCurrency = lib.mkOption {
+        type = lib.types.str;
+        description = lib.mdDoc ''
+          Defines a default currency symbol for new groups.
+          Use €, £, a more specific dollar like US$, CAD, etc.
+        '';
+        default = "$";
+        example = "€";
+      };
+
+      exportDecimalDuration = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = lib.mdDoc ''
+          Defines whether time duration values are decimal in CSV and XML data
+          exports (1.25 vs 1:15).
+        '';
+      };
+
+      reportFooter = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = lib.mdDoc "Defines whether to use a footer on reports.";
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    assertions = [
+      {
+        assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
+        message = ''
+          <option>services.anuko-time-tracker.database.passwordFile</option> cannot be specified if
+          <option>services.anuko-time-tracker.database.createLocally</option> is set to true.
+        '';
+      }
+      {
+        assertion = cfg.settings.email.smtpAuth -> (cfg.settings.email.smtpPasswordFile != null);
+        message = ''
+          <option>services.anuko-time-tracker.settings.email.smtpPasswordFile</option> needs to be set if
+          <option>services.anuko-time-tracker.settings.email.smtpAuth</option> is enabled.
+        '';
+      }
+    ];
+
+    services.phpfpm = {
+      pools.anuko-time-tracker = {
+        inherit (cfg) user;
+        group = config.services.nginx.group;
+        settings = {
+          "listen.owner" = config.services.nginx.user;
+          "listen.group" = config.services.nginx.group;
+        } // cfg.poolConfig;
+      };
+    };
+
+    services.nginx = {
+      enable = lib.mkDefault true;
+      recommendedTlsSettings = true;
+      recommendedOptimisation = true;
+      recommendedGzipSettings = true;
+      virtualHosts."${cfg.hostname}" = lib.mkMerge [
+        cfg.nginx
+        {
+          root = lib.mkForce "${package}";
+          locations = {
+            "/".index = "index.php";
+            "~ [^/]\\.php(/|$)" = {
+              extraConfig = ''
+                fastcgi_split_path_info ^(.+?\.php)(/.*)$;
+                fastcgi_pass unix:${config.services.phpfpm.pools.anuko-time-tracker.socket};
+              '';
+            };
+          };
+        }
+      ];
+    };
+
+    services.mysql = lib.mkIf cfg.database.createLocally {
+      enable = lib.mkDefault true;
+      package = lib.mkDefault pkgs.mariadb;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [{
+        name = cfg.database.user;
+        ensurePermissions = {
+          "${cfg.database.name}.*" = "ALL PRIVILEGES";
+        };
+      }];
+    };
+
+    systemd = {
+      services = {
+        anuko-time-tracker-setup-database = lib.mkIf cfg.database.createLocally {
+          description = "Set up Anuko Time Tracker database";
+          serviceConfig = {
+            Type = "oneshot";
+            RemainAfterExit = true;
+          };
+          wantedBy = [ "phpfpm-anuko-time-tracker.service" ];
+          after = [ "mysql.service" ];
+          script =
+            let
+              mysql = "${config.services.mysql.package}/bin/mysql";
+            in
+            ''
+              if [ ! -f ${cfg.dataDir}/.dbexists ]; then
+                # Load database schema provided with package
+                ${mysql} ${cfg.database.name} < ${cfg.package}/mysql.sql
+
+                touch ${cfg.dataDir}/.dbexists
+              fi
+            '';
+        };
+      };
+      tmpfiles.rules = [
+        "d ${cfg.dataDir} 0750 ${cfg.user} ${config.services.nginx.group} -"
+        "d ${cfg.dataDir}/templates_c 0750 ${cfg.user} ${config.services.nginx.group} -"
+      ];
+    };
+
+    users.users."${cfg.user}" = {
+      isSystemUser = true;
+      group = config.services.nginx.group;
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ michaelshmitty ];
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/cloudlog.nix b/nixpkgs/nixos/modules/services/web-apps/cloudlog.nix
index da2cf93d7f1c..9261de8d4354 100644
--- a/nixpkgs/nixos/modules/services/web-apps/cloudlog.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/cloudlog.nix
@@ -308,6 +308,8 @@ in
       pools.cloudlog = {
         inherit (cfg) user;
         group = config.services.nginx.group;
+        # cloudlog is currently broken on php 8.2
+        phpPackage = pkgs.php81;
         settings =  {
           "listen.owner" = config.services.nginx.user;
           "listen.group" = config.services.nginx.group;
diff --git a/nixpkgs/nixos/modules/services/web-apps/code-server.nix b/nixpkgs/nixos/modules/services/web-apps/code-server.nix
index fa7d4a348c23..11601f6c3044 100644
--- a/nixpkgs/nixos/modules/services/web-apps/code-server.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/code-server.nix
@@ -9,7 +9,17 @@ in {
     services.code-server = {
       enable = lib.mkEnableOption (lib.mdDoc "code-server");
 
-      package = lib.mkPackageOptionMD pkgs "code-server" { };
+      package = lib.mkPackageOptionMD pkgs "code-server" {
+        example = ''
+          pkgs.vscode-with-extensions.override {
+            vscode = pkgs.code-server;
+            vscodeExtensions = with pkgs.vscode-extensions; [
+              bbenoist.nix
+              dracula-theme.theme-dracula
+            ];
+          }
+        '';
+      };
 
       extraPackages = lib.mkOption {
         default = [ ];
diff --git a/nixpkgs/nixos/modules/services/web-apps/dex.nix b/nixpkgs/nixos/modules/services/web-apps/dex.nix
index f69f1749aeb8..bd041db007a1 100644
--- a/nixpkgs/nixos/modules/services/web-apps/dex.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/dex.nix
@@ -6,7 +6,7 @@ let
   cfg = config.services.dex;
   fixClient = client: if client ? secretFile then ((builtins.removeAttrs client [ "secretFile" ]) // { secret = client.secretFile; }) else client;
   filteredSettings = mapAttrs (n: v: if n == "staticClients" then (builtins.map fixClient v) else v) cfg.settings;
-  secretFiles = flatten (builtins.map (c: if c ? secretFile then [ c.secretFile ] else []) (cfg.settings.staticClients or []));
+  secretFiles = flatten (builtins.map (c: optional (c ? secretFile) c.secretFile) (cfg.settings.staticClients or []));
 
   settingsFormat = pkgs.formats.yaml {};
   configFile = settingsFormat.generate "config.yaml" filteredSettings;
diff --git a/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix b/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix
index 9e685c127da7..9e9bfb1bfd83 100644
--- a/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix
@@ -337,7 +337,7 @@ let
 
         phpPackage = mkOption {
           type = types.package;
-          relatedPackages = [ "php80" "php81" ];
+          relatedPackages = [ "php81" "php82" ];
           default = pkgs.php81;
           defaultText = "pkgs.php81";
           description = lib.mdDoc ''
diff --git a/nixpkgs/nixos/modules/services/web-apps/engelsystem.nix b/nixpkgs/nixos/modules/services/web-apps/engelsystem.nix
index f1d71f174471..138e2f3f1b90 100644
--- a/nixpkgs/nixos/modules/services/web-apps/engelsystem.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/engelsystem.nix
@@ -104,6 +104,7 @@ in {
       '';
 
     services.phpfpm.pools.engelsystem = {
+      phpPackage = pkgs.php81;
       user = "engelsystem";
       settings = {
         "listen.owner" = config.services.nginx.user;
diff --git a/nixpkgs/nixos/modules/services/web-apps/freshrss.nix b/nixpkgs/nixos/modules/services/web-apps/freshrss.nix
index 89e29f7ccb51..ffc05d0e41f8 100644
--- a/nixpkgs/nixos/modules/services/web-apps/freshrss.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/freshrss.nix
@@ -7,7 +7,7 @@ let
   poolName = "freshrss";
 in
 {
-  meta.maintainers = with maintainers; [ etu stunkymonkey ];
+  meta.maintainers = with maintainers; [ etu stunkymonkey mattchrist ];
 
   options.services.freshrss = {
     enable = mkEnableOption (mdDoc "FreshRSS feed reader");
@@ -27,7 +27,8 @@ in
     };
 
     passwordFile = mkOption {
-      type = types.path;
+      type = types.nullOr types.path;
+      default = null;
       description = mdDoc "Password for the defaultUser for FreshRSS.";
       example = "/run/secrets/freshrss";
     };
@@ -120,7 +121,13 @@ in
     user = mkOption {
       type = types.str;
       default = "freshrss";
-      description = lib.mdDoc "User under which Freshrss runs.";
+      description = lib.mdDoc "User under which FreshRSS runs.";
+    };
+
+    authType = mkOption {
+      type = types.enum [ "form" "http_auth" "none" ];
+      default = "form";
+      description = mdDoc "Authentication type for FreshRSS.";
     };
   };
 
@@ -160,6 +167,14 @@ in
       };
     in
     mkIf cfg.enable {
+      assertions = mkIf (cfg.authType == "form") [
+        {
+          assertion = cfg.passwordFile != null;
+          message = ''
+            `passwordFile` must be supplied when using "form" authentication!
+          '';
+        }
+      ];
       # Set up a Nginx virtual host.
       services.nginx = mkIf (cfg.virtualHost != null) {
         enable = true;
@@ -227,7 +242,7 @@ in
           settingsFlags = concatStringsSep " \\\n    "
             (mapAttrsToList (k: v: "${k} ${toString v}") {
               "--default_user" = ''"${cfg.defaultUser}"'';
-              "--auth_type" = ''"form"'';
+              "--auth_type" = ''"${cfg.authType}"'';
               "--base_url" = ''"${cfg.baseUrl}"'';
               "--language" = ''"${cfg.language}"'';
               "--db-type" = ''"${cfg.database.type}"'';
@@ -255,20 +270,30 @@ in
             FRESHRSS_DATA_PATH = cfg.dataDir;
           };
 
-          script = ''
-            # do installation or reconfigure
-            if test -f ${cfg.dataDir}/config.php; then
-              # reconfigure with settings
-              ./cli/reconfigure.php ${settingsFlags}
-              ./cli/update-user.php --user ${cfg.defaultUser} --password "$(cat ${cfg.passwordFile})"
-            else
-              # check correct folders in data folder
-              ./cli/prepare.php
-              # install with settings
-              ./cli/do-install.php ${settingsFlags}
-              ./cli/create-user.php --user ${cfg.defaultUser} --password "$(cat ${cfg.passwordFile})"
-            fi
-          '';
+          script =
+            let
+              userScriptArgs = ''--user ${cfg.defaultUser} --password "$(cat ${cfg.passwordFile})"'';
+              updateUserScript = optionalString (cfg.authType == "form") ''
+                ./cli/update-user.php ${userScriptArgs}
+              '';
+              createUserScript = optionalString (cfg.authType == "form") ''
+                ./cli/create-user.php ${userScriptArgs}
+              '';
+            in
+            ''
+              # do installation or reconfigure
+              if test -f ${cfg.dataDir}/config.php; then
+                # reconfigure with settings
+                ./cli/reconfigure.php ${settingsFlags}
+                ${updateUserScript}
+              else
+                # check correct folders in data folder
+                ./cli/prepare.php
+                # install with settings
+                ./cli/do-install.php ${settingsFlags}
+                ${createUserScript}
+              fi
+            '';
         };
 
       systemd.services.freshrss-updater = {
diff --git a/nixpkgs/nixos/modules/services/web-apps/gotosocial.md b/nixpkgs/nixos/modules/services/web-apps/gotosocial.md
new file mode 100644
index 000000000000..a290d7d1893a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/gotosocial.md
@@ -0,0 +1,64 @@
+# GoToSocial {#module-services-gotosocial}
+
+[GoToSocial](https://gotosocial.org/) is an ActivityPub social network server, written in Golang.
+
+## Service configuration {#modules-services-gotosocial-service-configuration}
+
+The following configuration sets up the PostgreSQL as database backend and binds
+GoToSocial to `127.0.0.1:8080`, expecting to be run behind a HTTP proxy on `gotosocial.example.com`.
+
+```nix
+services.gotosocial = {
+  enable = true;
+  setupPostgresqlDB = true;
+  settings = {
+    application-name = "My GoToSocial";
+    host = "gotosocial.example.com";
+    protocol = "https";
+    bind-address = "127.0.0.1";
+    port = 8080;
+  };
+};
+```
+
+Please refer to the [GoToSocial Documentation](https://docs.gotosocial.org/en/latest/configuration/general/)
+for additional configuration options.
+
+## Proxy configuration {#modules-services-gotosocial-proxy-configuration}
+
+Although it is possible to expose GoToSocial directly, it is common practice to operate it behind an
+HTTP reverse proxy such as nginx.
+
+```nix
+networking.firewall.allowedTCPPorts = [ 80 443 ];
+services.nginx = {
+  enable = true;
+  clientMaxBodySize = "40M";
+  virtualHosts = with config.services.gotosocial.settings; {
+    "${host}" = {
+      enableACME = true;
+      forceSSL = true;
+      locations = {
+        "/" = {
+          recommendedProxySettings = true;
+          proxyWebsockets = true;
+          proxyPass = "http://${bind-address}:${toString port}";
+        };
+      };
+    };
+  };
+};
+```
+
+Please refer to [](#module-security-acme) for details on how to provision an SSL/TLS certificate.
+
+## User management {#modules-services-gotosocial-user-management}
+
+After the GoToSocial service is running, the `gotosocial-admin` utility can be used to manage users. In particular an
+administrative user can be created with
+
+```ShellSession
+$ sudo gotosocial-admin account create --username <nickname> --email <email> --password <password>
+$ sudo gotosocial-admin account confirm --username <nickname>
+$ sudo gotosocial-admin account promote --username <nickname>
+```
diff --git a/nixpkgs/nixos/modules/services/web-apps/gotosocial.nix b/nixpkgs/nixos/modules/services/web-apps/gotosocial.nix
new file mode 100644
index 000000000000..f7ae018d5b7c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/gotosocial.nix
@@ -0,0 +1,173 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.services.gotosocial;
+  settingsFormat = pkgs.formats.yaml { };
+  configFile = settingsFormat.generate "config.yml" cfg.settings;
+  defaultSettings = {
+    application-name = "gotosocial";
+
+    protocol = "https";
+
+    bind-address = "127.0.0.1";
+    port = 8080;
+
+    storage-local-base-path = "/var/lib/gotosocial/storage";
+
+    db-type = "sqlite";
+    db-address = "/var/lib/gotosocial/database.sqlite";
+  };
+  gotosocial-admin = pkgs.writeShellScriptBin "gotosocial-admin" ''
+    exec systemd-run \
+      -u gotosocial-admin.service \
+      -p Group=gotosocial \
+      -p User=gotosocial \
+      -q -t -G --wait --service-type=exec \
+      ${cfg.package}/bin/gotosocial --config-path ${configFile} admin "$@"
+  '';
+in
+{
+  meta.doc = ./gotosocial.md;
+  meta.maintainers = with lib.maintainers; [ misuzu ];
+
+  options.services.gotosocial = {
+    enable = lib.mkEnableOption (lib.mdDoc "ActivityPub social network server");
+
+    package = lib.mkPackageOptionMD pkgs "gotosocial" { };
+
+    openFirewall = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Open the configured port in the firewall.
+        Using a reverse proxy instead is highly recommended.
+      '';
+    };
+
+    setupPostgresqlDB = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Whether to setup a local postgres database and populate the
+        `db-type` fields in `services.gotosocial.settings`.
+      '';
+    };
+
+    settings = lib.mkOption {
+      type = settingsFormat.type;
+      default = defaultSettings;
+      example = {
+        application-name = "My GoToSocial";
+        host = "gotosocial.example.com";
+      };
+      description = lib.mdDoc ''
+        Contents of the GoToSocial YAML config.
+
+        Please refer to the
+        [documentation](https://docs.gotosocial.org/en/latest/configuration/)
+        and
+        [example config](https://github.com/superseriousbusiness/gotosocial/blob/main/example/config.yaml).
+
+        Please note that the `host` option cannot be changed later so it is important to configure this correctly before you start GoToSocial.
+      '';
+    };
+
+    environmentFile = lib.mkOption {
+      type = lib.types.nullOr lib.types.path;
+      description = lib.mdDoc ''
+        File path containing environment variables for configuring the GoToSocial service
+        in the format of an EnvironmentFile as described by systemd.exec(5).
+
+        This option could be used to pass sensitive configuration to the GoToSocial daemon.
+
+        Please refer to the Environment Variables section in the
+        [documentation](https://docs.gotosocial.org/en/latest/configuration/).
+      '';
+      default = null;
+      example = "/root/nixos/secrets/gotosocial.env";
+    };
+
+  };
+
+  config = lib.mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = cfg.settings.host or null != null;
+        message = ''
+          You have to define a hostname for GoToSocial (`services.gotosocial.settings.host`), it cannot be changed later without starting over!
+        '';
+      }
+    ];
+
+    services.gotosocial.settings = (lib.mapAttrs (name: lib.mkDefault) (
+      defaultSettings // {
+        web-asset-base-dir = "${cfg.package}/share/gotosocial/web/assets/";
+        web-template-base-dir = "${cfg.package}/share/gotosocial/web/template/";
+      }
+    )) // (lib.optionalAttrs cfg.setupPostgresqlDB {
+      db-type = "postgres";
+      db-address = "/run/postgresql";
+      db-database = "gotosocial";
+      db-user = "gotosocial";
+    });
+
+    environment.systemPackages = [ gotosocial-admin ];
+
+    users.groups.gotosocial = { };
+    users.users.gotosocial = {
+      group = "gotosocial";
+      isSystemUser = true;
+    };
+
+    networking.firewall = lib.mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.settings.port ];
+    };
+
+    services.postgresql = lib.mkIf cfg.setupPostgresqlDB {
+      enable = true;
+      ensureDatabases = [ "gotosocial" ];
+      ensureUsers = [
+        {
+          name = "gotosocial";
+          ensurePermissions = {
+            "DATABASE gotosocial" = "ALL PRIVILEGES";
+          };
+        }
+      ];
+    };
+
+    systemd.services.gotosocial = {
+      description = "ActivityPub social network server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ]
+        ++ lib.optional cfg.setupPostgresqlDB "postgresql.service";
+      requires = lib.optional cfg.setupPostgresqlDB "postgresql.service";
+      restartTriggers = [ configFile ];
+
+      serviceConfig = {
+        EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
+        ExecStart = "${cfg.package}/bin/gotosocial --config-path ${configFile} server start";
+        Restart = "on-failure";
+        Group = "gotosocial";
+        User = "gotosocial";
+        StateDirectory = "gotosocial";
+        WorkingDirectory = "/var/lib/gotosocial";
+
+        # Security options:
+        # Based on https://github.com/superseriousbusiness/gotosocial/blob/v0.8.1/example/gotosocial.service
+        AmbientCapabilities = lib.optional (cfg.settings.port < 1024) "CAP_NET_BIND_SERVICE";
+        NoNewPrivileges = true;
+        PrivateTmp = true;
+        PrivateDevices = true;
+        RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        DevicePolicy = "closed";
+        ProtectSystem = "full";
+        ProtectControlGroups = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        LockPersonality = true;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/guacamole-client.nix b/nixpkgs/nixos/modules/services/web-apps/guacamole-client.nix
new file mode 100644
index 000000000000..c12f6582468c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/guacamole-client.nix
@@ -0,0 +1,60 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+let
+  cfg = config.services.guacamole-client;
+  settingsFormat = pkgs.formats.javaProperties { };
+in
+{
+  options = {
+    services.guacamole-client = {
+      enable = lib.mkEnableOption (lib.mdDoc "Apache Guacamole Client (Tomcat)");
+      package = lib.mkPackageOptionMD pkgs "guacamole-client" { };
+
+      settings = lib.mkOption {
+        type = lib.types.submodule {
+          freeformType = settingsFormat.type;
+        };
+        default = {
+          guacd-hostname = "localhost";
+          guacd-port = 4822;
+        };
+        description = lib.mdDoc ''
+          Configuration written to `guacamole.properties`.
+
+          ::: {.note}
+          The Guacamole web application uses one main configuration file called
+          `guacamole.properties`. This file is the common location for all
+          configuration properties read by Guacamole or any extension of
+          Guacamole, including authentication providers.
+          :::
+        '';
+      };
+
+      enableWebserver = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = lib.mdDoc ''
+          Enable the Guacamole web application in a Tomcat webserver.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.etc."guacamole/guacamole.properties" = lib.mkIf
+      (cfg.settings != {})
+      { source = (settingsFormat.generate "guacamole.properties" cfg.settings); };
+
+    services = lib.mkIf cfg.enableWebserver {
+      tomcat = {
+        enable = true;
+        webapps = [
+          cfg.package
+        ];
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/guacamole-server.nix b/nixpkgs/nixos/modules/services/web-apps/guacamole-server.nix
new file mode 100644
index 000000000000..0cffdce83d83
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/guacamole-server.nix
@@ -0,0 +1,83 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+let
+  cfg = config.services.guacamole-server;
+in
+{
+  options = {
+    services.guacamole-server = {
+      enable = lib.mkEnableOption (lib.mdDoc "Apache Guacamole Server (guacd)");
+      package = lib.mkPackageOptionMD pkgs "guacamole-server" { };
+
+      extraEnvironment = lib.mkOption {
+        type = lib.types.attrsOf lib.types.str;
+        default = { };
+        example = lib.literalExpression ''
+          {
+            ENVIRONMENT = "production";
+          }
+        '';
+        description = lib.mdDoc "Environment variables to pass to guacd.";
+      };
+
+      host = lib.mkOption {
+        default = "127.0.0.1";
+        description = lib.mdDoc ''
+          The host name or IP address the server should listen to.
+        '';
+        type = lib.types.str;
+      };
+
+      port = lib.mkOption {
+        default = 4822;
+        description = lib.mdDoc ''
+          The port the guacd server should listen to.
+        '';
+        type = lib.types.port;
+      };
+
+      logbackXml = lib.mkOption {
+        type = lib.types.nullOr lib.types.path;
+        default = null;
+        example = "/path/to/logback.xml";
+        description = lib.mdDoc ''
+          Configuration file that correspond to `logback.xml`.
+        '';
+      };
+
+      userMappingXml = lib.mkOption {
+        type = lib.types.nullOr lib.types.path;
+        default = null;
+        example = "/path/to/user-mapping.xml";
+        description = lib.mdDoc ''
+          Configuration file that correspond to `user-mapping.xml`.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    # Setup configuration files.
+    environment.etc."guacamole/logback.xml" = lib.mkIf (cfg.logbackXml != null) { source = cfg.logbackXml; };
+    environment.etc."guacamole/user-mapping.xml" = lib.mkIf (cfg.userMappingXml != null) { source = cfg.userMappingXml; };
+
+    systemd.services.guacamole-server = {
+      description = "Apache Guacamole server (guacd)";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      environment = {
+        HOME = "/run/guacamole-server";
+      } // cfg.extraEnvironment;
+      serviceConfig = {
+        ExecStart = "${lib.getExe cfg.package} -f -b ${cfg.host} -l ${toString cfg.port}";
+        RuntimeDirectory = "guacamole-server";
+        DynamicUser = true;
+        PrivateTmp = "yes";
+        Restart = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix b/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix
index e2014a9b7e35..bfa5fd5aff25 100644
--- a/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix
@@ -1,8 +1,8 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
+  inherit (lib) literalExpression mdDoc mkEnableOption mkIf mkOption mkPackageOptionMD mkRenamedOptionModule types versionAtLeast;
+
   cfg = config.services.hedgedoc;
 
   # 21.03 will not be an official release - it was instead 21.05.  This
@@ -32,6 +32,7 @@ in
   ];
 
   options.services.hedgedoc = {
+    package = mkPackageOptionMD pkgs "hedgedoc" { };
     enable = mkEnableOption (lib.mdDoc "the HedgeDoc Markdown Editor");
 
     groups = mkOption {
@@ -107,6 +108,13 @@ in
           {option}`protocolUseSSL`.
         '';
       };
+      enableStatsApi = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Enables or disables the /status and /metrics endpoint.
+        '';
+      };
       hsts = {
         enable = mkOption {
           type = types.bool;
@@ -1018,16 +1026,6 @@ in
         `HedgeDoc` is running.
       '';
     };
-
-    package = mkOption {
-      type = types.package;
-      default = pkgs.hedgedoc;
-      defaultText = literalExpression "pkgs.hedgedoc";
-      description = lib.mdDoc ''
-        Package that provides HedgeDoc.
-      '';
-    };
-
   };
 
   config = mkIf cfg.enable {
@@ -1060,7 +1058,7 @@ in
       serviceConfig = {
         WorkingDirectory = cfg.workDir;
         StateDirectory = [ cfg.workDir cfg.settings.uploadsPath ];
-        ExecStart = "${cfg.package}/bin/hedgedoc";
+        ExecStart = "${lib.getExe cfg.package}";
         EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
         Environment = [
           "CMD_CONFIG_FILE=${cfg.workDir}/config.json"
diff --git a/nixpkgs/nixos/modules/services/web-apps/invidious.nix b/nixpkgs/nixos/modules/services/web-apps/invidious.nix
index 61c52ee03dc6..8823da010014 100644
--- a/nixpkgs/nixos/modules/services/web-apps/invidious.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/invidious.nix
@@ -41,6 +41,12 @@ let
         RestrictNamespaces = true;
         SystemCallArchitectures = "native";
         SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
+
+        # Because of various issues Invidious must be restarted often, at least once a day, ideally
+        # every hour.
+        # This option enables the automatic restarting of the Invidious instance.
+        Restart = lib.mkDefault "always";
+        RuntimeMaxSec = lib.mkDefault "1h";
       };
     };
 
@@ -56,7 +62,7 @@ let
         port = cfg.database.port;
         # Blank for unix sockets, see
         # https://github.com/will/crystal-pg/blob/1548bb255210/src/pq/conninfo.cr#L100-L108
-        host = if cfg.database.host == null then "" else cfg.database.host;
+        host = lib.optionalString (cfg.database.host != null) cfg.database.host;
         # Not needed because peer authentication is enabled
         password = lib.mkIf (cfg.database.host == null) "";
       };
diff --git a/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix b/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix
index 8be1fd3055d0..f419b75cf70f 100644
--- a/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix
@@ -16,7 +16,7 @@ let
     DB_HOSTNAME=${cfg.database.host}
     DB_USERNAME=${cfg.database.user}
     # NOTE: file_get_contents adds newline at the end of returned string
-    DB_PASSWORD=${if cfg.database.passwordFile == null then "" else "trim(file_get_contents('${cfg.database.passwordFile}'), \"\\r\\n\")"}
+    DB_PASSWORD=${optionalString (cfg.database.passwordFile != null) "trim(file_get_contents('${cfg.database.passwordFile}'), \"\\r\\n\")"}
     DB_DATABASE=${cfg.database.name}
     DB_PORT=${toString cfg.database.port}
     SESS_EXPIRATION=864000
diff --git a/nixpkgs/nixos/modules/services/web-apps/lemmy.nix b/nixpkgs/nixos/modules/services/web-apps/lemmy.nix
index f48afd98a6c9..6dfba907fb5d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/lemmy.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/lemmy.nix
@@ -1,4 +1,4 @@
-{ lib, pkgs, config, ... }:
+{ lib, pkgs, config, utils, ... }:
 with lib;
 let
   cfg = config.services.lemmy;
@@ -16,7 +16,13 @@ in
 
     enable = mkEnableOption (lib.mdDoc "lemmy a federated alternative to reddit in rust");
 
+    server = {
+      package = mkPackageOptionMD pkgs "lemmy-server" {};
+    };
+
     ui = {
+      package = mkPackageOptionMD pkgs "lemmy-ui" {};
+
       port = mkOption {
         type = types.port;
         default = 1234;
@@ -25,8 +31,41 @@ in
     };
 
     caddy.enable = mkEnableOption (lib.mdDoc "exposing lemmy with the caddy reverse proxy");
+    nginx.enable = mkEnableOption (lib.mdDoc "exposing lemmy with the nginx reverse proxy");
+
+    database = {
+      createLocally = mkEnableOption (lib.mdDoc "creation of database on the instance");
+
+      uri = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = lib.mdDoc "The connection URI to use. Takes priority over the configuration file if set.";
+      };
+
+      uriFile = mkOption {
+        type = with types; nullOr path;
+        default = null;
+        description = lib.mdDoc "File which contains the database uri.";
+      };
+    };
+
+    pictrsApiKeyFile = mkOption {
+      type = with types; nullOr path;
+      default = null;
+      description = lib.mdDoc "File which contains the value of `pictrs.api_key`.";
+    };
+
+    smtpPasswordFile = mkOption {
+      type = with types; nullOr path;
+      default = null;
+      description = lib.mdDoc "File which contains the value of `email.smtp_password`.";
+    };
 
-    database.createLocally = mkEnableOption (lib.mdDoc "creation of database on the instance");
+    adminPasswordFile = mkOption {
+      type = with types; nullOr path;
+      default = null;
+      description = lib.mdDoc "File which contains the value of `setup.admin_password`.";
+    };
 
     settings = mkOption {
       default = { };
@@ -47,10 +86,6 @@ in
           description = lib.mdDoc "Port where lemmy should listen for incoming requests.";
         };
 
-        options.federation = {
-          enabled = mkEnableOption (lib.mdDoc "activitypub federation");
-        };
-
         options.captcha = {
           enabled = mkOption {
             type = types.bool;
@@ -65,16 +100,26 @@ in
         };
       };
     };
-
   };
 
   config =
+    let
+      secretOptions = {
+        pictrsApiKeyFile = { setting = [ "pictrs" "api_key" ]; path = cfg.pictrsApiKeyFile; };
+        smtpPasswordFile = { setting = [ "email" "smtp_password" ]; path = cfg.smtpPasswordFile; };
+        adminPasswordFile = { setting = [ "setup" "admin_password" ]; path = cfg.adminPasswordFile; };
+        uriFile = { setting = [ "database" "uri" ]; path = cfg.database.uriFile; };
+      };
+      secrets = lib.filterAttrs (option: data: data.path != null) secretOptions;
+    in
     lib.mkIf cfg.enable {
-      services.lemmy.settings = (mapAttrs (name: mkDefault)
+      services.lemmy.settings = lib.attrsets.recursiveUpdate (mapAttrs (name: mkDefault)
         {
           bind = "127.0.0.1";
           tls_enabled = true;
-          pictrs_url = with config.services.pict-rs; "http://${address}:${toString port}";
+          pictrs = {
+            url = with config.services.pict-rs; "http://${address}:${toString port}";
+          };
           actor_name_max_length = 20;
 
           rate_limit.message = 180;
@@ -86,14 +131,15 @@ in
           rate_limit.image = 6;
           rate_limit.image_per_second = 3600;
         } // {
-        database = mapAttrs (name: mkDefault) {
-          user = "lemmy";
-          host = "/run/postgresql";
-          port = 5432;
-          database = "lemmy";
-          pool_size = 5;
-        };
-      });
+          database = mapAttrs (name: mkDefault) {
+            user = "lemmy";
+            host = "/run/postgresql";
+            port = 5432;
+            database = "lemmy";
+            pool_size = 5;
+          };
+        }) (lib.foldlAttrs (acc: option: data: acc // lib.setAttrByPath data.setting { _secret = option; }) {} secrets);
+        # the option name is the id of the credential loaded by LoadCredential
 
       services.postgresql = mkIf cfg.database.createLocally {
         enable = true;
@@ -111,7 +157,7 @@ in
         virtualHosts."${cfg.settings.hostname}" = {
           extraConfig = ''
             handle_path /static/* {
-              root * ${pkgs.lemmy-ui}/dist
+              root * ${cfg.ui.package}/dist
               file_server
             }
             @for_backend {
@@ -140,19 +186,64 @@ in
         };
       };
 
-      assertions = [{
-        assertion = cfg.database.createLocally -> cfg.settings.database.host == "localhost" || cfg.settings.database.host == "/run/postgresql";
-        message = "if you want to create the database locally, you need to use a local database";
-      }];
+      services.nginx = mkIf cfg.nginx.enable {
+        enable = mkDefault true;
+        virtualHosts."${cfg.settings.hostname}".locations = let
+          ui = "http://127.0.0.1:${toString cfg.ui.port}";
+          backend = "http://127.0.0.1:${toString cfg.settings.port}";
+        in {
+          "~ ^/(api|pictrs|feeds|nodeinfo|.well-known)" = {
+            # backend requests
+            proxyPass = backend;
+            proxyWebsockets = true;
+            recommendedProxySettings = true;
+          };
+          "/" = {
+            # mixed frontend and backend requests, based on the request headers
+            proxyPass = "$proxpass";
+            recommendedProxySettings = true;
+            extraConfig = ''
+              set $proxpass "${ui}";
+              if ($http_accept = "application/activity+json") {
+                set $proxpass "${backend}";
+              }
+              if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
+                set $proxpass "${backend}";
+              }
+              if ($request_method = POST) {
+                set $proxpass "${backend}";
+              }
+
+              # Cuts off the trailing slash on URLs to make them valid
+              rewrite ^(.+)/+$ $1 permanent;
+            '';
+          };
+        };
+      };
 
-      systemd.services.lemmy = {
+      assertions = [
+        {
+          assertion = cfg.database.createLocally -> cfg.settings.database.host == "localhost" || cfg.settings.database.host == "/run/postgresql";
+          message = "if you want to create the database locally, you need to use a local database";
+        }
+        {
+          assertion = (!(hasAttrByPath ["federation"] cfg.settings)) && (!(hasAttrByPath ["federation" "enabled"] cfg.settings));
+          message = "`services.lemmy.settings.federation` was removed in 0.17.0 and no longer has any effect";
+        }
+        {
+          assertion = cfg.database.uriFile != null -> cfg.database.uri == null && !cfg.database.createLocally;
+          message = "specifying a database uri while also specifying a database uri file is not allowed";
+        }
+      ];
+
+      systemd.services.lemmy = let
+        substitutedConfig = "/run/lemmy/config.hjson";
+      in {
         description = "Lemmy server";
 
         environment = {
-          LEMMY_CONFIG_LOCATION = "/run/lemmy/config.hjson";
-
-          # Verify how this is used, and don't put the password in the nix store
-          LEMMY_DATABASE_URL = with cfg.settings.database;"postgres:///${database}?host=${host}";
+          LEMMY_CONFIG_LOCATION = if secrets == {} then settingsFormat.generate "config.hjson" cfg.settings else substitutedConfig;
+          LEMMY_DATABASE_URL = if cfg.database.uri != null then cfg.database.uri else (mkIf (cfg.database.createLocally) "postgres:///lemmy?host=/run/postgresql&user=lemmy");
         };
 
         documentation = [
@@ -166,11 +257,23 @@ in
 
         requires = lib.optionals cfg.database.createLocally [ "postgresql.service" ];
 
+        # substitute secrets and prevent others from reading the result
+        # if somehow $CREDENTIALS_DIRECTORY is not set we fail
+        preStart = mkIf (secrets != {}) ''
+          set -u
+          umask u=rw,g=,o=
+          cd "$CREDENTIALS_DIRECTORY"
+          ${utils.genJqSecretsReplacementSnippet cfg.settings substitutedConfig}
+        '';
+
         serviceConfig = {
           DynamicUser = true;
           RuntimeDirectory = "lemmy";
-          ExecStartPre = "${pkgs.coreutils}/bin/install -m 600 ${settingsFormat.generate "config.hjson" cfg.settings} /run/lemmy/config.hjson";
-          ExecStart = "${pkgs.lemmy-server}/bin/lemmy_server";
+          ExecStart = "${cfg.server.package}/bin/lemmy_server";
+          LoadCredential = lib.foldlAttrs (acc: option: data: acc ++ [ "${option}:${toString data.path}" ]) [] secrets;
+          PrivateTmp = true;
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
         };
       };
 
@@ -179,9 +282,10 @@ in
 
         environment = {
           LEMMY_UI_HOST = "127.0.0.1:${toString cfg.ui.port}";
-          LEMMY_INTERNAL_HOST = "127.0.0.1:${toString cfg.settings.port}";
-          LEMMY_EXTERNAL_HOST = cfg.settings.hostname;
-          LEMMY_HTTPS = "false";
+          LEMMY_UI_LEMMY_INTERNAL_HOST = "127.0.0.1:${toString cfg.settings.port}";
+          LEMMY_UI_LEMMY_EXTERNAL_HOST = cfg.settings.hostname;
+          LEMMY_UI_HTTPS = "false";
+          NODE_ENV = "production";
         };
 
         documentation = [
@@ -197,8 +301,8 @@ in
 
         serviceConfig = {
           DynamicUser = true;
-          WorkingDirectory = "${pkgs.lemmy-ui}";
-          ExecStart = "${pkgs.nodejs}/bin/node ${pkgs.lemmy-ui}/dist/js/server.js";
+          WorkingDirectory = "${cfg.ui.package}";
+          ExecStart = "${pkgs.nodejs}/bin/node ${cfg.ui.package}/dist/js/server.js";
         };
       };
     };
diff --git a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix
index 2ad6cd6aae19..2aab97438b7d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix
@@ -91,9 +91,7 @@ let
 
   envFile = pkgs.writeText "mastodon.env" (lib.concatMapStrings (s: s + "\n") (
     (lib.concatLists (lib.mapAttrsToList (name: value:
-      if value != null then [
-        "${name}=\"${toString value}\""
-      ] else []
+      lib.optional (value != null) ''${name}="${toString value}"''
     ) env))));
 
   mastodonTootctl = let
diff --git a/nixpkgs/nixos/modules/services/web-apps/mattermost.nix b/nixpkgs/nixos/modules/services/web-apps/mattermost.nix
index db5122e79f00..66e5f1695a15 100644
--- a/nixpkgs/nixos/modules/services/web-apps/mattermost.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/mattermost.nix
@@ -86,8 +86,7 @@ let
   mattermostConf = recursiveUpdate
     mattermostConfWithoutPlugins
     (
-      if mattermostPlugins == null then {}
-      else {
+      lib.optionalAttrs (mattermostPlugins != null) {
         PluginSettings = {
           Enable = true;
         };
diff --git a/nixpkgs/nixos/modules/services/web-apps/miniflux.nix b/nixpkgs/nixos/modules/services/web-apps/miniflux.nix
index 7cc8ce10ffe0..3374c746ad3d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/miniflux.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/miniflux.nix
@@ -130,5 +130,17 @@ in
       environment = cfg.config;
     };
     environment.systemPackages = [ cfg.package ];
+
+    security.apparmor.policies."bin.miniflux".profile = ''
+      include <tunables/global>
+      ${cfg.package}/bin/miniflux {
+        include <abstractions/base>
+        include <abstractions/nameservice>
+        include <abstractions/ssl_certs>
+        include "${pkgs.apparmorRulesFromClosure { name = "miniflux"; } cfg.package}"
+        r ${cfg.package}/bin/miniflux,
+        r @{sys}/kernel/mm/transparent_hugepage/hpage_pmd_size,
+      }
+    '';
   };
 }
diff --git a/nixpkgs/nixos/modules/services/web-apps/netbox.nix b/nixpkgs/nixos/modules/services/web-apps/netbox.nix
index 0ecb20e8c2c0..e2ef350ba4e5 100644
--- a/nixpkgs/nixos/modules/services/web-apps/netbox.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/netbox.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, buildEnv, ... }:
+{ config, lib, pkgs, ... }:
 
 with lib;
 
@@ -261,6 +261,7 @@ in {
         StateDirectory = "netbox";
         StateDirectoryMode = "0750";
         Restart = "on-failure";
+        RestartSec = 30;
       };
     in {
       netbox-migration = {
@@ -276,13 +277,18 @@ in {
           ExecStart = ''
             ${pkg}/bin/netbox migrate
           '';
+          PrivateTmp = true;
         };
       };
 
       netbox = {
         description = "NetBox WSGI Service";
+        documentation = [ "https://docs.netbox.dev/" ];
+
         wantedBy = [ "netbox.target" ];
-        after = [ "netbox-migration.service" ];
+
+        after = [ "network-online.target" "netbox-migration.service" ];
+        wants = [ "network-online.target" ];
 
         preStart = ''
           ${pkg}/bin/netbox trace_paths --no-input
@@ -290,9 +296,7 @@ in {
           ${pkg}/bin/netbox remove_stale_contenttypes --no-input
         '';
 
-        environment = {
-          PYTHONPATH = pkg.pythonPath;
-        };
+        environment.PYTHONPATH = pkg.pythonPath;
 
         serviceConfig = defaultServiceConfig // {
           ExecStart = ''
@@ -300,32 +304,37 @@ in {
               --bind ${cfg.listenAddress}:${toString cfg.port} \
               --pythonpath ${pkg}/opt/netbox/netbox
           '';
+          PrivateTmp = true;
         };
       };
 
       netbox-rq = {
         description = "NetBox Request Queue Worker";
+        documentation = [ "https://docs.netbox.dev/" ];
+
         wantedBy = [ "netbox.target" ];
         after = [ "netbox.service" ];
 
-        environment = {
-          PYTHONPATH = pkg.pythonPath;
-        };
+        environment.PYTHONPATH = pkg.pythonPath;
 
         serviceConfig = defaultServiceConfig // {
           ExecStart = ''
             ${pkg}/bin/netbox rqworker high default low
           '';
+          PrivateTmp = true;
         };
       };
 
       netbox-housekeeping = {
         description = "NetBox housekeeping job";
-        after = [ "netbox.service" ];
+        documentation = [ "https://docs.netbox.dev/" ];
 
-        environment = {
-          PYTHONPATH = pkg.pythonPath;
-        };
+        wantedBy = [ "multi-user.target" ];
+
+        after = [ "network-online.target" ];
+        wants = [ "network-online.target" ];
+
+        environment.PYTHONPATH = pkg.pythonPath;
 
         serviceConfig = defaultServiceConfig // {
           Type = "oneshot";
@@ -338,10 +347,17 @@ in {
 
     systemd.timers.netbox-housekeeping = {
       description = "Run NetBox housekeeping job";
-      wantedBy = [ "timers.target" ];
+      documentation = [ "https://docs.netbox.dev/" ];
+
+      wantedBy = [ "multi-user.target" ];
+
+      after = [ "network-online.target" ];
+      wants = [ "network-online.target" ];
 
       timerConfig = {
         OnCalendar = "daily";
+        AccuracySec = "1h";
+        Persistent = true;
       };
     };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.md b/nixpkgs/nixos/modules/services/web-apps/nextcloud.md
index 5be81a18dfec..cbd7b5b3d066 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.md
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.md
@@ -5,7 +5,7 @@ self-hostable cloud platform. The server setup can be automated using
 [services.nextcloud](#opt-services.nextcloud.enable). A
 desktop client is packaged at `pkgs.nextcloud-client`.
 
-The current default by NixOS is `nextcloud26` which is also the latest
+The current default by NixOS is `nextcloud27` which is also the latest
 major version available.
 
 ## Basic usage {#module-services-nextcloud-basic-usage}
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
index a8142cf42d75..503db124635c 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
@@ -8,6 +8,21 @@ let
 
   jsonFormat = pkgs.formats.json {};
 
+  defaultPHPSettings = {
+    short_open_tag = "Off";
+    expose_php = "Off";
+    error_reporting = "E_ALL & ~E_DEPRECATED & ~E_STRICT";
+    display_errors = "stderr";
+    "opcache.enable_cli" = "1";
+    "opcache.interned_strings_buffer" = "8";
+    "opcache.max_accelerated_files" = "10000";
+    "opcache.memory_consumption" = "128";
+    "opcache.revalidate_freq" = "1";
+    "opcache.fast_shutdown" = "1";
+    "openssl.cafile" = "/etc/ssl/certs/ca-certificates.crt";
+    catch_workers_output = "yes";
+  };
+
   inherit (cfg) datadir;
 
   phpPackage = cfg.phpPackage.buildEnv {
@@ -26,22 +41,13 @@ let
         ++ optional cfg.caching.memcached memcached
       )
       ++ cfg.phpExtraExtensions all; # Enabled by user
-    extraConfig = toKeyValue phpOptions;
+    extraConfig = toKeyValue cfg.phpOptions;
   };
 
   toKeyValue = generators.toKeyValue {
     mkKeyValue = generators.mkKeyValueDefault {} " = ";
   };
 
-  phpOptions = {
-    upload_max_filesize = cfg.maxUploadSize;
-    post_max_size = cfg.maxUploadSize;
-    memory_limit = cfg.maxUploadSize;
-  } // cfg.phpOptions
-    // optionalAttrs cfg.caching.apcu {
-      "apc.enable_cli" = "1";
-    };
-
   occ = pkgs.writeScriptBin "nextcloud-occ" ''
     #! ${pkgs.runtimeShell}
     cd ${cfg.package}
@@ -136,8 +142,8 @@ in {
       default = config.services.nextcloud.home;
       defaultText = literalExpression "config.services.nextcloud.home";
       description = lib.mdDoc ''
-        Data storage path of nextcloud.  Will be [](#opt-services.nextcloud.home) by default.
-        This folder will be populated with a config.php and data folder which contains the state of the instance (excl the database).";
+        Nextcloud's data storage path.  Will be [](#opt-services.nextcloud.home) by default.
+        This folder will be populated with a config.php file and a data folder which contains the state of the instance (excluding the database).";
       '';
       example = "/mnt/nextcloud-file";
     };
@@ -170,8 +176,8 @@ in {
       type = types.bool;
       default = true;
       description = lib.mdDoc ''
-        Automatically enable the apps in [](#opt-services.nextcloud.extraApps) every time nextcloud starts.
-        If set to false, apps need to be enabled in the Nextcloud user interface or with nextcloud-occ app:enable.
+        Automatically enable the apps in [](#opt-services.nextcloud.extraApps) every time Nextcloud starts.
+        If set to false, apps need to be enabled in the Nextcloud web user interface or with `nextcloud-occ app:enable`.
       '';
     };
     appstoreEnable = mkOption {
@@ -179,16 +185,28 @@ in {
       default = null;
       example = true;
       description = lib.mdDoc ''
-        Allow the installation of apps and app updates from the store.
+        Allow the installation and updating of apps from the Nextcloud appstore.
         Enabled by default unless there are packages in [](#opt-services.nextcloud.extraApps).
-        Set to true to force enable the store even if [](#opt-services.nextcloud.extraApps) is used.
-        Set to false to disable the installation of apps from the global appstore. App management is always enabled regardless of this setting.
+        Set this to true to force enable the store even if [](#opt-services.nextcloud.extraApps) is used.
+        Set this to false to disable the installation of apps from the global appstore. App management is always enabled regardless of this setting.
       '';
     };
     logLevel = mkOption {
       type = types.ints.between 0 4;
       default = 2;
-      description = lib.mdDoc "Log level value between 0 (DEBUG) and 4 (FATAL).";
+      description = lib.mdDoc ''
+        Log level value between 0 (DEBUG) and 4 (FATAL).
+
+        - 0 (debug): Log all activity.
+
+        - 1 (info): Log activity such as user logins and file activities, plus warnings, errors, and fatal errors.
+
+        - 2 (warn): Log successful operations, as well as warnings of potential problems, errors and fatal errors.
+
+        - 3 (error): Log failed operations and fatal errors.
+
+        - 4 (fatal): Log only fatal errors that cause the server to stop.
+      '';
     };
     logType = mkOption {
       type = types.enum [ "errorlog" "file" "syslog" "systemd" ];
@@ -202,16 +220,16 @@ in {
     https = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc "Use https for generated links.";
+      description = lib.mdDoc "Use HTTPS for generated links.";
     };
     package = mkOption {
       type = types.package;
       description = lib.mdDoc "Which package to use for the Nextcloud instance.";
-      relatedPackages = [ "nextcloud25" "nextcloud26" ];
+      relatedPackages = [ "nextcloud25" "nextcloud26" "nextcloud27" ];
     };
     phpPackage = mkOption {
       type = types.package;
-      relatedPackages = [ "php80" "php81" ];
+      relatedPackages = [ "php81" "php82" ];
       defaultText = "pkgs.php";
       description = lib.mdDoc ''
         PHP package to use for Nextcloud.
@@ -222,7 +240,7 @@ in {
       default = "512M";
       type = types.str;
       description = lib.mdDoc ''
-        Defines the upload limit for files. This changes the relevant options
+        The upload limit for files. This changes the relevant options
         in php.ini and nginx if enabled.
       '';
     };
@@ -251,10 +269,10 @@ in {
       default = all: [];
       defaultText = literalExpression "all: []";
       description = lib.mdDoc ''
-        Additional PHP extensions to use for nextcloud.
-        By default, only extensions necessary for a vanilla nextcloud installation are enabled,
+        Additional PHP extensions to use for Nextcloud.
+        By default, only extensions necessary for a vanilla Nextcloud installation are enabled,
         but you may choose from the list of available extensions and add further ones.
-        This is sometimes necessary to be able to install a certain nextcloud app that has additional requirements.
+        This is sometimes necessary to be able to install a certain Nextcloud app that has additional requirements.
       '';
       example = literalExpression ''
         all: [ all.pdlib all.bz2 ]
@@ -263,22 +281,33 @@ in {
 
     phpOptions = mkOption {
       type = types.attrsOf types.str;
-      default = {
-        short_open_tag = "Off";
-        expose_php = "Off";
-        error_reporting = "E_ALL & ~E_DEPRECATED & ~E_STRICT";
-        display_errors = "stderr";
-        "opcache.enable_cli" = "1";
-        "opcache.interned_strings_buffer" = "8";
-        "opcache.max_accelerated_files" = "10000";
-        "opcache.memory_consumption" = "128";
-        "opcache.revalidate_freq" = "1";
-        "opcache.fast_shutdown" = "1";
-        "openssl.cafile" = "/etc/ssl/certs/ca-certificates.crt";
-        catch_workers_output = "yes";
-      };
+      defaultText = literalExpression (generators.toPretty { } defaultPHPSettings);
       description = lib.mdDoc ''
         Options for PHP's php.ini file for nextcloud.
+
+        Please note that this option is _additive_ on purpose while the
+        attribute values inside the default are option defaults: that means that
+
+        ```nix
+        {
+          services.nextcloud.phpOptions."opcache.interned_strings_buffer" = "23";
+        }
+        ```
+
+        will override the `php.ini` option `opcache.interned_strings_buffer` without
+        discarding the rest of the defaults.
+
+        Overriding all of `phpOptions` (including `upload_max_filesize`, `post_max_size`
+        and `memory_limit` which all point to [](#opt-services.nextcloud.maxUploadSize)
+        by default) can be done like this:
+
+        ```nix
+        {
+          services.nextcloud.phpOptions = lib.mkForce {
+            /* ... */
+          };
+        }
+        ```
       '';
     };
 
@@ -301,7 +330,7 @@ in {
       type = types.nullOr types.lines;
       default = null;
       description = lib.mdDoc ''
-        Options for nextcloud's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives.
+        Options for Nextcloud's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives.
       '';
     };
 
@@ -319,7 +348,7 @@ in {
         type = types.bool;
         default = false;
         description = lib.mdDoc ''
-          Create the database and database user locally.
+          Whether to create the database and database user locally.
         '';
       };
 
@@ -357,9 +386,10 @@ in {
           else "localhost";
         defaultText = "localhost";
         description = lib.mdDoc ''
-          Database host or socket path. Defaults to the correct unix socket
-          instead if `services.nextcloud.database.createLocally` is true and
-          `services.nextcloud.config.dbtype` is either `pgsql` or `mysql`.
+          Database host or socket path.
+          If [](#opt-services.nextcloud.database.createLocally) is true and
+          [](#opt-services.nextcloud.config.dbtype) is either `pgsql` or `mysql`,
+          defaults to the correct Unix socket instead.
         '';
       };
       dbport = mkOption {
@@ -370,18 +400,19 @@ in {
       dbtableprefix = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = lib.mdDoc "Table prefix in Nextcloud database.";
+        description = lib.mdDoc "Table prefix in Nextcloud's database.";
       };
       adminuser = mkOption {
         type = types.str;
         default = "root";
-        description = lib.mdDoc "Admin username.";
+        description = lib.mdDoc "Username for the admin account.";
       };
       adminpassFile = mkOption {
         type = types.str;
         description = lib.mdDoc ''
           The full path to a file that contains the admin's password. Must be
-          readable by user `nextcloud`.
+          readable by user `nextcloud`. The password is set only in the initial
+          setup of Nextcloud by the systemd service `nextcloud-setup.service`.
         '';
       };
 
@@ -389,7 +420,7 @@ in {
         type = types.listOf types.str;
         default = [];
         description = lib.mdDoc ''
-          Trusted domains, from which the nextcloud installation will be
+          Trusted domains from which the Nextcloud installation will be
           accessible.  You don't need to add
           `services.nextcloud.hostname` here.
         '';
@@ -399,8 +430,8 @@ in {
         type = types.listOf types.str;
         default = [];
         description = lib.mdDoc ''
-          Trusted proxies, to provide if the nextcloud installation is being
-          proxied to secure against e.g. spoofing.
+          Trusted proxies to provide if the Nextcloud installation is being
+          proxied to secure against, e.g. spoofing.
         '';
       };
 
@@ -410,10 +441,10 @@ in {
         example = "https";
 
         description = lib.mdDoc ''
-          Force Nextcloud to always use HTTPS i.e. for link generation. Nextcloud
-          uses the currently used protocol by default, but when behind a reverse-proxy,
-          it may use `http` for everything although Nextcloud
-          may be served via HTTPS.
+          Force Nextcloud to always use HTTP or HTTPS i.e. for link generation.
+          Nextcloud uses the currently used protocol by default, but when
+          behind a reverse-proxy, it may use `http` for everything although
+          Nextcloud may be served via HTTPS.
         '';
       };
 
@@ -422,16 +453,12 @@ in {
         type = types.nullOr types.str;
         example = "DE";
         description = lib.mdDoc ''
-          ::: {.warning}
-          This option exists since Nextcloud 21! If older versions are used,
-          this will throw an eval-error!
-          :::
+          An [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html)
+          country code which replaces automatic phone-number detection
+          without a country code.
 
-          [ISO 3611-1](https://www.iso.org/iso-3166-country-codes.html)
-          country codes for automatic phone-number detection without a country code.
-
-          With e.g. `DE` set, the `+49` can be omitted for
-          phone-numbers.
+          As an example, with `DE` set as the default phone region,
+          the `+49` prefix can be omitted for phone numbers.
         '';
       };
 
@@ -556,10 +583,10 @@ in {
       default = config.services.nextcloud.notify_push.enable;
       defaultText = literalExpression "config.services.nextcloud.notify_push.enable";
       description = lib.mdDoc ''
-        Whether to configure nextcloud to use the recommended redis settings for small instances.
+        Whether to configure Nextcloud to use the recommended Redis settings for small instances.
 
         ::: {.note}
-        The `notify_push` app requires redis to be configured. If this option is turned off, this must be configured manually.
+        The `notify_push` app requires Redis to be configured. If this option is turned off, this must be configured manually.
         :::
       '';
     };
@@ -596,7 +623,7 @@ in {
         type = types.bool;
         default = false;
         description = lib.mdDoc ''
-          Run regular auto update of all apps installed from the nextcloud app store.
+          Run a regular auto-update of all apps installed from the Nextcloud app store.
         '';
       };
       startAt = mkOption {
@@ -643,7 +670,7 @@ in {
       type = jsonFormat.type;
       default = {};
       description = lib.mdDoc ''
-        Extra options which should be appended to nextcloud's config.php file.
+        Extra options which should be appended to Nextcloud's config.php file.
       '';
       example = literalExpression '' {
         redis = {
@@ -660,7 +687,7 @@ in {
       type = types.nullOr types.str;
       default = null;
       description = lib.mdDoc ''
-        Secret options which will be appended to nextcloud's config.php file (written as JSON, in the same
+        Secret options which will be appended to Nextcloud's config.php file (written as JSON, in the same
         form as the [](#opt-services.nextcloud.extraOptions) option), for example
         `{"redis":{"password":"secret"}}`.
       '';
@@ -688,13 +715,13 @@ in {
 
   config = mkIf cfg.enable (mkMerge [
     { warnings = let
-        latest = 26;
+        latest = 27;
         upgradeWarning = major: nixos:
           ''
             A legacy Nextcloud install (from before NixOS ${nixos}) may be installed.
 
             After nextcloud${toString major} is installed successfully, you can safely upgrade
-            to ${toString (major + 1)}. The latest version available is nextcloud${toString latest}.
+            to ${toString (major + 1)}. The latest version available is Nextcloud${toString latest}.
 
             Please note that Nextcloud doesn't support upgrades across multiple major versions
             (i.e. an upgrade from 16 is possible to 17, but not 16 to 18).
@@ -707,10 +734,9 @@ 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 (versionOlder cfg.package.version "23") (upgradeWarning 22 "22.05"))
-        ++ (optional (versionOlder cfg.package.version "24") (upgradeWarning 23 "22.05"))
         ++ (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"))
         ++ (optional cfg.enableBrokenCiphersForSSE ''
           You're using PHP's openssl extension built against OpenSSL 1.1 for Nextcloud.
           This is only necessary if you're using Nextcloud's server-side encryption.
@@ -743,12 +769,25 @@ in {
             ''
           else if versionOlder stateVersion "22.11" then nextcloud24
           else if versionOlder stateVersion "23.05" then nextcloud25
-          else nextcloud26
+          else if versionOlder stateVersion "23.11" then nextcloud26
+          else nextcloud27
         );
 
       services.nextcloud.phpPackage =
         if versionOlder cfg.package.version "26" then pkgs.php81
         else pkgs.php82;
+
+      services.nextcloud.phpOptions = mkMerge [
+        (mapAttrs (const mkOptionDefault) defaultPHPSettings)
+        {
+          upload_max_filesize = cfg.maxUploadSize;
+          post_max_size = cfg.maxUploadSize;
+          memory_limit = cfg.maxUploadSize;
+        }
+        (mkIf cfg.caching.apcu {
+          "apc.enable_cli" = "1";
+        })
+      ];
     }
 
     { assertions = [
@@ -1064,10 +1103,8 @@ in {
       services.nextcloud = lib.mkIf cfg.configureRedis {
         caching.redis = true;
         extraOptions = {
-          memcache = {
-            distributed = ''\OC\Memcache\Redis'';
-            locking = ''\OC\Memcache\Redis'';
-          };
+          "memcache.distributed" = ''\OC\Memcache\Redis'';
+          "memcache.locking" = ''\OC\Memcache\Redis'';
           redis = {
             host = config.services.redis.servers.nextcloud.unixSocket;
             port = 0;
diff --git a/nixpkgs/nixos/modules/services/web-apps/nexus.nix b/nixpkgs/nixos/modules/services/web-apps/nexus.nix
index 1f4a758b87ea..c67562d38992 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nexus.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/nexus.nix
@@ -7,18 +7,14 @@ let
   cfg = config.services.nexus;
 
 in
-
 {
   options = {
     services.nexus = {
       enable = mkEnableOption (lib.mdDoc "Sonatype Nexus3 OSS service");
 
-      package = mkOption {
-        type = types.package;
-        default = pkgs.nexus;
-        defaultText = literalExpression "pkgs.nexus";
-        description = lib.mdDoc "Package which runs Nexus3";
-      };
+      package = lib.mkPackageOption pkgs "nexus" { };
+
+      jdkPackage = lib.mkPackageOption pkgs "openjdk8" { };
 
       user = mkOption {
         type = types.str;
@@ -105,12 +101,11 @@ in
   config = mkIf cfg.enable {
     users.users.${cfg.user} = {
       isSystemUser = true;
-      group = cfg.group;
-      home = cfg.home;
+      inherit (cfg) group home;
       createHome = true;
     };
 
-    users.groups.${cfg.group} = {};
+    users.groups.${cfg.group} = { };
 
     systemd.services.nexus = {
       description = "Sonatype Nexus3";
@@ -123,6 +118,7 @@ in
         NEXUS_USER = cfg.user;
         NEXUS_HOME = cfg.home;
 
+        INSTALL4J_JAVA_HOME = cfg.jdkPackage;
         VM_OPTS_FILE = pkgs.writeText "nexus.vmoptions" cfg.jvmOpts;
       };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/nifi.nix b/nixpkgs/nixos/modules/services/web-apps/nifi.nix
index f643e24d81d9..5ce561077836 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nifi.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/nifi.nix
@@ -13,9 +13,7 @@ let
 
   envFile = pkgs.writeText "nifi.env" (lib.concatMapStrings (s: s + "\n") (
     (lib.concatLists (lib.mapAttrsToList (name: value:
-      if value != null then [
-        "${name}=\"${toString value}\""
-      ] else []
+      lib.optional (value != null) ''${name}="${toString value}"''
     ) env))));
 
   nifiEnv = pkgs.writeShellScriptBin "nifi-env" ''
diff --git a/nixpkgs/nixos/modules/services/web-apps/openvscode-server.nix b/nixpkgs/nixos/modules/services/web-apps/openvscode-server.nix
index d0db614d8d72..3daf238c57e1 100644
--- a/nixpkgs/nixos/modules/services/web-apps/openvscode-server.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/openvscode-server.nix
@@ -4,7 +4,8 @@ let
   cfg = config.services.openvscode-server;
   defaultUser = "openvscode-server";
   defaultGroup = defaultUser;
-in {
+in
+{
   options = {
     services.openvscode-server = {
       enable = lib.mkEnableOption (lib.mdDoc "openvscode-server");
@@ -126,12 +127,12 @@ in {
       };
 
       telemetryLevel = lib.mkOption {
-        default = "off";
+        default = null;
         example = "crash";
         description = lib.mdDoc ''
           Sets the initial telemetry level. Valid levels are: 'off', 'crash', 'error' and 'all'.
         '';
-        type = lib.types.str;
+        type = lib.types.nullOr (lib.types.enum [ "off" "crash" "error" "all" ]);
       };
 
       connectionToken = lib.mkOption {
@@ -167,23 +168,23 @@ in {
             --accept-server-license-terms \
             --host=${cfg.host} \
             --port=${toString cfg.port} \
-          '' + lib.optionalString (cfg.telemetryLevel == true) ''
-            --telemetry-level=${cfg.telemetryLevel} \
-          '' + lib.optionalString (cfg.withoutConnectionToken == true) ''
-            --without-connection-token \
-          '' + lib.optionalString (cfg.socketPath != null) ''
-            --socket-path=${cfg.socketPath} \
-          '' + lib.optionalString (cfg.userDataDir != null) ''
-            --user-data-dir=${cfg.userDataDir} \
-          '' + lib.optionalString (cfg.serverDataDir != null) ''
-            --server-data-dir=${cfg.serverDataDir} \
-          '' + lib.optionalString (cfg.extensionsDir != null) ''
-            --extensions-dir=${cfg.extensionsDir} \
-          '' + lib.optionalString (cfg.connectionToken != null) ''
-            --connection-token=${cfg.connectionToken} \
-          '' + lib.optionalString (cfg.connectionTokenFile != null) ''
-            --connection-token-file=${cfg.connectionTokenFile} \
-          '' + lib.escapeShellArgs cfg.extraArguments;
+        '' + lib.optionalString (cfg.telemetryLevel != null) ''
+          --telemetry-level=${cfg.telemetryLevel} \
+        '' + lib.optionalString (cfg.withoutConnectionToken) ''
+          --without-connection-token \
+        '' + lib.optionalString (cfg.socketPath != null) ''
+          --socket-path=${cfg.socketPath} \
+        '' + lib.optionalString (cfg.userDataDir != null) ''
+          --user-data-dir=${cfg.userDataDir} \
+        '' + lib.optionalString (cfg.serverDataDir != null) ''
+          --server-data-dir=${cfg.serverDataDir} \
+        '' + lib.optionalString (cfg.extensionsDir != null) ''
+          --extensions-dir=${cfg.extensionsDir} \
+        '' + lib.optionalString (cfg.connectionToken != null) ''
+          --connection-token=${cfg.connectionToken} \
+        '' + lib.optionalString (cfg.connectionTokenFile != null) ''
+          --connection-token-file=${cfg.connectionTokenFile} \
+        '' + lib.escapeShellArgs cfg.extraArguments;
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
         RuntimeDirectory = cfg.user;
         User = cfg.user;
diff --git a/nixpkgs/nixos/modules/services/web-apps/outline.nix b/nixpkgs/nixos/modules/services/web-apps/outline.nix
index 6f63198a68a5..1d8298963e6d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/outline.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/outline.nix
@@ -3,8 +3,12 @@
 let
   defaultUser = "outline";
   cfg = config.services.outline;
+  inherit (lib) mkRemovedOptionModule;
 in
 {
+  imports = [
+    (mkRemovedOptionModule [ "services" "outline" "sequelizeArguments" ] "Database migration are run agains configurated database by outline directly")
+  ];
   # See here for a reference of all the options:
   #   https://github.com/outline/outline/blob/v0.67.0/.env.sample
   #   https://github.com/outline/outline/blob/v0.67.0/app.json
@@ -25,7 +29,7 @@ in
           # to still land in the same team. Note that this effectively makes
           # Outline a single-team instance.
           patchPhase = ${"''"}
-            sed -i 's/const domain = parts\.length && parts\[1\];/const domain = "example.com";/g' server/routes/auth/providers/oidc.ts
+            sed -i 's/const domain = parts\.length && parts\[1\];/const domain = "example.com";/g' plugins/oidc/server/auth/oidc.ts
           ${"''"};
         })
       '';
@@ -51,15 +55,6 @@ in
       '';
     };
 
-    sequelizeArguments = lib.mkOption {
-      type = lib.types.str;
-      default = "";
-      example = "--env=production-ssl-disabled";
-      description = lib.mdDoc ''
-        Optional arguments to pass to `sequelize` calls.
-      '';
-    };
-
     #
     # Required options
     #
@@ -583,16 +578,6 @@ in
     systemd.services.outline = let
       localRedisUrl = "redis+unix:///run/redis-outline/redis.sock";
       localPostgresqlUrl = "postgres://localhost/outline?host=/run/postgresql";
-
-      # Create an outline-sequalize wrapper (a wrapper around the wrapper) that
-      # has the config file's path baked in. This is necessary because there is
-      # at least two occurrences of outline calling this from its own code.
-      sequelize = pkgs.writeShellScriptBin "outline-sequelize" ''
-        exec ${cfg.package}/bin/outline-sequelize \
-          --config $RUNTIME_DIRECTORY/database.json \
-          ${cfg.sequelizeArguments} \
-          "$@"
-      '';
     in {
       description = "Outline wiki and knowledge base";
       wantedBy = [ "multi-user.target" ];
@@ -603,7 +588,6 @@ in
         ++ lib.optional (cfg.redisUrl == "local") "redis-outline.service";
       path = [
         pkgs.openssl # Required by the preStart script
-        sequelize
       ];
 
 
@@ -687,37 +671,6 @@ in
           openssl rand -hex 32 > ${lib.escapeShellArg cfg.utilsSecretFile}
         fi
 
-        # The config file is required for the sequelize CLI.
-        ${if (cfg.databaseUrl == "local") then ''
-          cat <<EOF > $RUNTIME_DIRECTORY/database.json
-          {
-            "production-ssl-disabled": {
-              "host": "/run/postgresql",
-              "username": null,
-              "password": null,
-              "dialect": "postgres"
-            }
-          }
-          EOF
-        '' else ''
-          cat <<EOF > $RUNTIME_DIRECTORY/database.json
-          {
-            "production": {
-              "use_env_variable": "DATABASE_URL",
-              "dialect": "postgres",
-              "dialectOptions": {
-                "ssl": {
-                  "rejectUnauthorized": false
-                }
-              }
-            },
-            "production-ssl-disabled": {
-              "use_env_variable": "DATABASE_URL",
-              "dialect": "postgres"
-            }
-          }
-          EOF
-        ''}
       '';
 
       script = ''
diff --git a/nixpkgs/nixos/modules/services/web-apps/peering-manager.nix b/nixpkgs/nixos/modules/services/web-apps/peering-manager.nix
index 666b82621268..641a3644614f 100644
--- a/nixpkgs/nixos/modules/services/web-apps/peering-manager.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/peering-manager.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, buildEnv, ... }:
 
-with lib;
-
 let
   cfg = config.services.peering-manager;
   configFile = pkgs.writeTextFile {
@@ -41,24 +39,24 @@ let
   pkg = (pkgs.peering-manager.overrideAttrs (old: {
     postInstall = ''
       ln -s ${configFile} $out/opt/peering-manager/peering_manager/configuration.py
-    '' + optionalString cfg.enableLdap ''
+    '' + lib.optionalString cfg.enableLdap ''
       ln -s ${cfg.ldapConfigPath} $out/opt/peering-manager/peering_manager/ldap_config.py
     '';
   })).override {
     inherit (cfg) plugins;
   };
-  peeringManagerManageScript = with pkgs; (writeScriptBin "peering-manager-manage" ''
-    #!${stdenv.shell}
+  peeringManagerManageScript = pkgs.writeScriptBin "peering-manager-manage" ''
+    #!${pkgs.stdenv.shell}
     export PYTHONPATH=${pkg.pythonPath}
     sudo -u peering-manager ${pkg}/bin/peering-manager "$@"
-  '');
+  '';
 
 in {
-  options.services.peering-manager = {
+  options.services.peering-manager = with lib; {
     enable = mkOption {
-      type = lib.types.bool;
+      type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = mdDoc ''
         Enable Peering Manager.
 
         This module requires a reverse proxy that serves `/static` separately.
@@ -69,7 +67,7 @@ in {
     listenAddress = mkOption {
       type = types.str;
       default = "[::1]";
-      description = lib.mdDoc ''
+      description = mdDoc ''
         Address the server will listen on.
       '';
     };
@@ -77,7 +75,7 @@ in {
     port = mkOption {
       type = types.port;
       default = 8001;
-      description = lib.mdDoc ''
+      description = mdDoc ''
         Port the server will listen on.
       '';
     };
@@ -88,14 +86,14 @@ in {
       defaultText = literalExpression ''
         python3Packages: with python3Packages; [];
       '';
-      description = lib.mdDoc ''
+      description = mdDoc ''
         List of plugin packages to install.
       '';
     };
 
     secretKeyFile = mkOption {
       type = types.path;
-      description = lib.mdDoc ''
+      description = mdDoc ''
         Path to a file containing the secret key.
       '';
     };
@@ -103,7 +101,7 @@ in {
     peeringdbApiKeyFile = mkOption {
       type = with types; nullOr path;
       default = null;
-      description = lib.mdDoc ''
+      description = mdDoc ''
         Path to a file containing the PeeringDB API key.
       '';
     };
@@ -111,7 +109,7 @@ in {
     extraConfig = mkOption {
       type = types.lines;
       default = "";
-      description = lib.mdDoc ''
+      description = mdDoc ''
         Additional lines of configuration appended to the `configuration.py`.
         See the [documentation](https://peering-manager.readthedocs.io/en/stable/configuration/optional-settings/) for more possible options.
       '';
@@ -120,7 +118,7 @@ in {
     enableLdap = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = mdDoc ''
         Enable LDAP-Authentication for Peering Manager.
 
         This requires a configuration file being pass through `ldapConfigPath`.
@@ -129,15 +127,15 @@ in {
 
     ldapConfigPath = mkOption {
       type = types.path;
-      description = lib.mdDoc ''
+      description = mdDoc ''
         Path to the Configuration-File for LDAP-Authentication, will be loaded as `ldap_config.py`.
         See the [documentation](https://peering-manager.readthedocs.io/en/stable/setup/6-ldap/#configuration) for possible options.
       '';
     };
   };
 
-  config = mkIf cfg.enable {
-    services.peering-manager.plugins = mkIf cfg.enableLdap (ps: [ ps.django-auth-ldap ]);
+  config = lib.mkIf cfg.enable {
+    services.peering-manager.plugins = lib.mkIf cfg.enableLdap (ps: [ ps.django-auth-ldap ]);
 
     system.build.peeringManagerPkg = pkg;
 
@@ -262,4 +260,6 @@ in {
     users.groups.peering-manager = {};
     users.groups."${config.services.redis.servers.peering-manager.user}".members = [ "peering-manager" ];
   };
+
+  meta.maintainers = with lib.maintainers; [ yuka ];
 }
diff --git a/nixpkgs/nixos/modules/services/web-apps/peertube.nix b/nixpkgs/nixos/modules/services/web-apps/peertube.nix
index 4ef2d7dce532..17e170c33dee 100644
--- a/nixpkgs/nixos/modules/services/web-apps/peertube.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/peertube.nix
@@ -52,9 +52,7 @@ let
 
   envFile = pkgs.writeText "peertube.env" (lib.concatMapStrings (s: s + "\n") (
     (lib.concatLists (lib.mapAttrsToList (name: value:
-      if value != null then [
-        "${name}=\"${toString value}\""
-      ] else []
+      lib.optional (value != null) ''${name}="${toString value}"''
     ) env))));
 
   peertubeEnv = pkgs.writeShellScriptBin "peertube-env" ''
@@ -350,7 +348,7 @@ in {
         };
         redis = {
           hostname = "${toString cfg.redis.host}";
-          port = (if cfg.redis.port == null then "" else cfg.redis.port);
+          port = (lib.optionalString (cfg.redis.port != null) cfg.redis.port);
         };
         storage = {
           tmp = lib.mkDefault "/var/lib/peertube/storage/tmp/";
diff --git a/nixpkgs/nixos/modules/services/web-apps/pict-rs.nix b/nixpkgs/nixos/modules/services/web-apps/pict-rs.nix
index 3270715a051b..e1b8c8333553 100644
--- a/nixpkgs/nixos/modules/services/web-apps/pict-rs.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/pict-rs.nix
@@ -1,21 +1,53 @@
 { lib, pkgs, config, ... }:
-with lib;
+
 let
   cfg = config.services.pict-rs;
+  inherit (lib) maintainers mkOption types;
+
+  is03 = lib.versionOlder cfg.package.version "0.4.0";
+
 in
 {
   meta.maintainers = with maintainers; [ happysalada ];
   meta.doc = ./pict-rs.md;
 
   options.services.pict-rs = {
-    enable = mkEnableOption (lib.mdDoc "pict-rs server");
+    enable = lib.mkEnableOption (lib.mdDoc "pict-rs server");
+
+    package = mkOption {
+      type = types.package;
+      example = lib.literalExpression "pkgs.pict-rs";
+      description = lib.mdDoc ''
+        pict-rs package to use.
+      '';
+    };
+
     dataDir = mkOption {
       type = types.path;
       default = "/var/lib/pict-rs";
       description = lib.mdDoc ''
+        The directory where to store the uploaded images & database.
+      '';
+    };
+
+    repoPath = mkOption {
+      type = types.nullOr (types.path);
+      default = null;
+      description = lib.mdDoc ''
+        The directory where to store the database.
+        This option takes precedence over dataDir.
+      '';
+    };
+
+    storePath = mkOption {
+      type = types.nullOr (types.path);
+      default = null;
+      description = lib.mdDoc ''
         The directory where to store the uploaded images.
+        This option takes precedence over dataDir.
       '';
     };
+
     address = mkOption {
       type = types.str;
       default = "127.0.0.1";
@@ -23,6 +55,7 @@ in
         The IPv4 address to deploy the service to.
       '';
     };
+
     port = mkOption {
       type = types.port;
       default = 8080;
@@ -31,18 +64,43 @@ in
       '';
     };
   };
+
   config = lib.mkIf cfg.enable {
+    services.pict-rs.package = lib.mkDefault (
+      # An incompatible db change happened in the transition from 0.3 to 0.4.
+      if lib.versionAtLeast config.system.stateVersion "23.11"
+      then pkgs.pict-rs
+      else pkgs.pict-rs_0_3
+    );
+
+    # Account for config differences between 0.3 and 0.4
+    assertions = [
+      {
+        assertion = !is03 || (cfg.repoPath == null && cfg.storePath == null);
+        message = ''
+          Using `services.pict-rs.repoPath` or `services.pict-rs.storePath` with pict-rs 0.3 or older has no effect.
+        '';
+      }
+    ];
+
     systemd.services.pict-rs = {
-      environment = {
-        PICTRS__PATH = cfg.dataDir;
-        PICTRS__ADDR = "${cfg.address}:${toString cfg.port}";
-      };
+      # Pict-rs split it's database and image storage paths in 0.4.0.
+      environment =
+        if is03 then {
+          PICTRS__PATH = cfg.dataDir;
+          PICTRS__ADDR = "${cfg.address}:${toString cfg.port}";
+        } else {
+          PICTRS__REPO__PATH = if cfg.repoPath != null then cfg.repoPath else "${cfg.dataDir}/sled-repo";
+          PICTRS__STORE__PATH = if cfg.storePath != null then cfg.storePath else "${cfg.dataDir}/files";
+          PICTRS__SERVER__ADDR = "${cfg.address}:${toString cfg.port}";
+        };
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
         DynamicUser = true;
         StateDirectory = "pict-rs";
-        ExecStart = "${pkgs.pict-rs}/bin/pict-rs";
+        ExecStart = if is03 then "${lib.getBin cfg.package}/bin/pict-rs" else "${lib.getBin cfg.package}/bin/pict-rs run";
       };
     };
   };
+
 }
diff --git a/nixpkgs/nixos/modules/services/web-apps/pixelfed.nix b/nixpkgs/nixos/modules/services/web-apps/pixelfed.nix
index 430a368650ec..159fb52476aa 100644
--- a/nixpkgs/nixos/modules/services/web-apps/pixelfed.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/pixelfed.nix
@@ -24,7 +24,7 @@ let
     if [[ "$USER" != ${user} ]]; then
       sudo='exec /run/wrappers/bin/sudo -u ${user}'
     fi
-    $sudo ${cfg.phpPackage}/bin/php artisan "$@"
+    $sudo ${phpPackage}/bin/php artisan "$@"
   '';
   dbSocket = {
     "pgsql" = "/run/postgresql";
@@ -356,7 +356,8 @@ in {
         ExecStart = "${pixelfed-manage}/bin/pixelfed-manage schedule:run";
         User = user;
         Group = group;
-        StateDirectory = cfg.dataDir;
+        StateDirectory =
+          lib.mkIf (cfg.dataDir == "/var/lib/pixelfed") "pixelfed";
       };
     };
 
@@ -379,6 +380,12 @@ in {
       };
 
       script = ''
+        # Before running any PHP program, cleanup the code cache.
+        # It's necessary if you upgrade the application otherwise you might
+        # try to import non-existent modules.
+        rm -f ${cfg.runtimeDir}/app.php
+        rm -rf ${cfg.runtimeDir}/cache/*
+
         # Concatenate non-secret .env and secret .env
         rm -f ${cfg.dataDir}/.env
         cp --no-preserve=all ${configFile} ${cfg.dataDir}/.env
@@ -391,6 +398,9 @@ in {
         rsync -av --no-perms ${pixelfed}/storage-static/ ${cfg.dataDir}/storage
         chmod -R +w ${cfg.dataDir}/storage
 
+        chmod g+x ${cfg.dataDir}/storage ${cfg.dataDir}/storage/app
+        chmod -R g+rX ${cfg.dataDir}/storage/app/public
+
         # Link the app.php in the runtime folder.
         # We cannot link the cache folder only because bootstrap folder needs to be writeable.
         ln -sf ${pixelfed}/bootstrap-static/app.php ${cfg.runtimeDir}/app.php
@@ -402,11 +412,6 @@ in {
         # Install Horizon
         # FIXME: require write access to public/ — should be done as part of install — pixelfed-manage horizon:publish
 
-        # Before running any PHP program, cleanup the bootstrap.
-        # It's necessary if you upgrade the application otherwise you might
-        # try to import non-existent modules.
-        rm -rf ${cfg.runtimeDir}/bootstrap/*
-
         # Perform the first migration.
         [[ ! -f ${cfg.dataDir}/.initial-migration ]] && pixelfed-manage migrate --force && touch ${cfg.dataDir}/.initial-migration
 
@@ -441,7 +446,7 @@ in {
     ];
 
     # Enable NGINX to access our phpfpm-socket.
-    users.users."${config.services.nginx.group}".extraGroups = [ cfg.group ];
+    users.users."${config.services.nginx.user}".extraGroups = [ cfg.group ];
     services.nginx = mkIf (cfg.nginx != null) {
       enable = true;
       virtualHosts."${cfg.domain}" = mkMerge [
diff --git a/nixpkgs/nixos/modules/services/web-apps/plausible.nix b/nixpkgs/nixos/modules/services/web-apps/plausible.nix
index 893dfa10acbc..911daa53e658 100644
--- a/nixpkgs/nixos/modules/services/web-apps/plausible.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/plausible.nix
@@ -238,9 +238,12 @@ in {
           path = [ cfg.package ]
             ++ optional cfg.database.postgres.setup config.services.postgresql.package;
           script = ''
-            export CONFIG_DIR=$CREDENTIALS_DIRECTORY
-
             export RELEASE_COOKIE="$(< $CREDENTIALS_DIRECTORY/RELEASE_COOKIE )"
+            export ADMIN_USER_PWD="$(< $CREDENTIALS_DIRECTORY/ADMIN_USER_PWD )"
+            export SECRET_KEY_BASE="$(< $CREDENTIALS_DIRECTORY/SECRET_KEY_BASE )"
+
+            ${lib.optionalString (cfg.mail.smtp.passwordFile != null)
+              ''export SMTP_USER_PWD="$(< $CREDENTIALS_DIRECTORY/SMTP_USER_PWD )"''}
 
             # setup
             ${cfg.package}/createdb.sh
diff --git a/nixpkgs/nixos/modules/services/web-apps/restya-board.nix b/nixpkgs/nixos/modules/services/web-apps/restya-board.nix
index 4b32f06826e2..959bcbc5c9f1 100644
--- a/nixpkgs/nixos/modules/services/web-apps/restya-board.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/restya-board.nix
@@ -263,8 +263,8 @@ in
       serviceConfig.RemainAfterExit = true;
 
       wantedBy = [ "multi-user.target" ];
-      requires = if cfg.database.host == null then [] else [ "postgresql.service" ];
-      after = [ "network.target" ] ++ (if cfg.database.host == null then [] else [ "postgresql.service" ]);
+      requires = lib.optional (cfg.database.host != null) "postgresql.service";
+      after = [ "network.target" ] ++ (lib.optional (cfg.database.host != null) "postgresql.service");
 
       script = ''
         rm -rf "${runDir}"
diff --git a/nixpkgs/nixos/modules/services/web-apps/slskd.nix b/nixpkgs/nixos/modules/services/web-apps/slskd.nix
new file mode 100644
index 000000000000..33353a59440c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/slskd.nix
@@ -0,0 +1,211 @@
+{ lib, pkgs, config, ... }:
+
+let
+  settingsFormat = pkgs.formats.yaml {};
+in {
+  options.services.slskd = with lib; with types; {
+    enable = mkEnableOption "enable slskd";
+
+    rotateLogs = mkEnableOption "enable an unit and timer that will rotate logs in /var/slskd/logs";
+
+    package = mkPackageOptionMD pkgs "slskd" { };
+
+    nginx = mkOption {
+      description = lib.mdDoc "options for nginx";
+      example = {
+        enable = true;
+        domain = "example.com";
+        contextPath = "/slskd";
+      };
+      type = submodule ({name, config, ...}: {
+        options = {
+          enable = mkEnableOption "enable nginx as a reverse proxy";
+
+          domainName = mkOption {
+            type = str;
+            description = "Domain you want to use";
+          };
+          contextPath = mkOption {
+            type = types.path;
+            default = "/";
+            description = lib.mdDoc ''
+              The context path, i.e., the last part of the slskd
+              URL. Typically '/' or '/slskd'. Default '/'
+            '';
+          };
+        };
+      });
+    };
+
+    environmentFile = mkOption {
+      type = path;
+      description = ''
+        Path to a file containing secrets.
+        It must at least contain the variable `SLSKD_SLSK_PASSWORD`
+      '';
+    };
+
+    openFirewall = mkOption {
+      type = bool;
+      description = ''
+        Whether to open the firewall for services.slskd.settings.listen_port";
+      '';
+      default = false;
+    };
+
+    settings = mkOption {
+      description = lib.mdDoc ''
+        Configuration for slskd, see
+        [available options](https://github.com/slskd/slskd/blob/master/docs/config.md)
+        `APP_DIR` is set to /var/lib/slskd, where default download & incomplete directories,
+        log and databases will be created.
+      '';
+      default = {};
+      type = submodule {
+        freeformType = settingsFormat.type;
+        options = {
+
+          soulseek = {
+            username = mkOption {
+              type = str;
+              description = "Username on the Soulseek Network";
+            };
+            listen_port = mkOption {
+              type = port;
+              description = "Port to use for communication on the Soulseek Network";
+              default = 50000;
+            };
+          };
+
+          web = {
+            port = mkOption {
+              type = port;
+              default = 5001;
+              description = "The HTTP listen port";
+            };
+            url_base = mkOption {
+              type = path;
+              default = config.services.slskd.nginx.contextPath;
+              defaultText = "config.services.slskd.nginx.contextPath";
+              description = lib.mdDoc ''
+                The context path, i.e., the last part of the slskd URL
+              '';
+            };
+          };
+
+          shares = {
+            directories = mkOption {
+              type = listOf str;
+              description = lib.mdDoc ''
+                Paths to your shared directories. See
+                [documentation](https://github.com/slskd/slskd/blob/master/docs/config.md#directories)
+                for advanced usage
+              '';
+            };
+          };
+
+          directories = {
+            incomplete = mkOption {
+              type = nullOr path;
+              description = "Directory where downloading files are stored";
+              defaultText = "<APP_DIR>/incomplete";
+              default = null;
+            };
+            downloads = mkOption {
+              type = nullOr path;
+              description = "Directory where downloaded files are stored";
+              defaultText = "<APP_DIR>/downloads";
+              default = null;
+            };
+          };
+        };
+      };
+    };
+  };
+
+  config = let
+    cfg = config.services.slskd;
+
+    confWithoutNullValues = (lib.filterAttrs (key: value: value != null) cfg.settings);
+
+    configurationYaml = settingsFormat.generate "slskd.yml" confWithoutNullValues;
+
+  in lib.mkIf cfg.enable {
+
+    users = {
+      users.slskd = {
+        isSystemUser = true;
+        group = "slskd";
+      };
+      groups.slskd = {};
+    };
+
+    # Reverse proxy configuration
+    services.nginx.enable = true;
+    services.nginx.virtualHosts."${cfg.nginx.domainName}" = {
+      forceSSL = true;
+      enableACME = true;
+      locations = {
+        "${cfg.nginx.contextPath}" = {
+          proxyPass = "http://localhost:${toString cfg.settings.web.port}";
+          proxyWebsockets = true;
+        };
+      };
+    };
+
+    # Hide state & logs
+    systemd.tmpfiles.rules = [
+      "d /var/lib/slskd/data 0750 slskd slskd - -"
+      "d /var/lib/slskd/logs 0750 slskd slskd - -"
+    ];
+
+    systemd.services.slskd = {
+      description = "A modern client-server application for the Soulseek file sharing network";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "simple";
+        User = "slskd";
+        EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
+        StateDirectory = "slskd";
+        ExecStart = "${cfg.package}/bin/slskd --app-dir /var/lib/slskd --config ${configurationYaml}";
+        Restart = "on-failure";
+        ReadOnlyPaths = map (d: builtins.elemAt (builtins.split "[^/]*(/.+)" d) 1) cfg.settings.shares.directories;
+        LockPersonality = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        RestrictNamespaces = true;
+        RestrictSUIDSGID = true;
+      };
+    };
+
+    networking.firewall.allowedTCPPorts = lib.optional cfg.openFirewall cfg.settings.soulseek.listen_port;
+
+    systemd.services.slskd-rotatelogs = lib.mkIf cfg.rotateLogs {
+      description = "Rotate slskd logs";
+      serviceConfig = {
+        Type = "oneshot";
+        User = "slskd";
+        ExecStart = [
+          "${pkgs.findutils}/bin/find /var/lib/slskd/logs/ -type f -mtime +10 -delete"
+          "${pkgs.findutils}/bin/find /var/lib/slskd/logs/ -type f -mtime +1  -exec ${pkgs.gzip}/bin/gzip -q {} ';'"
+        ];
+      };
+      startAt = "daily";
+    };
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix b/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
index 93b0aafab64b..e861a4185194 100644
--- a/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
@@ -15,6 +15,8 @@ let
 
   tlsEnabled = cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME;
 
+  inherit (snipe-it.passthru) phpPackage;
+
   # shell script for local administration
   artisan = pkgs.writeScriptBin "snipe-it" ''
     #! ${pkgs.runtimeShell}
@@ -23,7 +25,7 @@ let
     if [[ "$USER" != ${user} ]]; then
       sudo='exec /run/wrappers/bin/sudo -u ${user}'
     fi
-    $sudo ${pkgs.php}/bin/php artisan $*
+    $sudo ${phpPackage}/bin/php artisan $*
   '';
 in {
   options.services.snipe-it = {
@@ -340,8 +342,7 @@ in {
     };
 
     services.phpfpm.pools.snipe-it = {
-      inherit user group;
-      phpPackage = pkgs.php81;
+      inherit user group phpPackage;
       phpOptions = ''
         post_max_size = ${cfg.maxUploadSize}
         upload_max_filesize = ${cfg.maxUploadSize}
@@ -450,7 +451,7 @@ in {
           rm "${cfg.dataDir}"/bootstrap/cache/*.php || true
 
           # migrate db
-          ${pkgs.php}/bin/php artisan migrate --force
+          ${phpPackage}/bin/php artisan migrate --force
 
           # A placeholder file for invalid barcodes
           invalid_barcode_location="${cfg.dataDir}/public/uploads/barcodes/invalid_barcode.gif"
diff --git a/nixpkgs/nixos/modules/services/web-apps/sogo.nix b/nixpkgs/nixos/modules/services/web-apps/sogo.nix
index 5e5d9472829d..9427eff35d14 100644
--- a/nixpkgs/nixos/modules/services/web-apps/sogo.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/sogo.nix
@@ -232,8 +232,8 @@ in {
         proxy_connect_timeout 90;
         proxy_send_timeout 90;
         proxy_read_timeout 90;
-        proxy_buffer_size 4k;
-        proxy_buffers 4 32k;
+        proxy_buffer_size 64k;
+        proxy_buffers 8 64k;
         proxy_busy_buffers_size 64k;
         proxy_temp_file_write_size 64k;
         client_max_body_size 50m;
diff --git a/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix b/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix
index f5a9cfac5d77..5cc9ef6dd6d9 100644
--- a/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix
@@ -14,7 +14,7 @@ let
     in
       ''
         ${hostOpts.hostName} ${concatStringsSep " " hostOpts.serverAliases} {
-          bind ${concatStringsSep " " hostOpts.listenAddresses}
+          ${optionalString (hostOpts.listenAddresses != [ ]) "bind ${concatStringsSep " " hostOpts.listenAddresses}"}
           ${optionalString (hostOpts.useACMEHost != null) "tls ${sslCertDir}/cert.pem ${sslCertDir}/key.pem"}
           log {
             ${hostOpts.logFormat}
@@ -41,6 +41,10 @@ let
     in
       "${if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform then Caddyfile-formatted else Caddyfile}/Caddyfile";
 
+  etcConfigFile = "caddy/caddy_config";
+
+  configPath = "/etc/${etcConfigFile}";
+
   acmeHosts = unique (catAttrs "useACMEHost" acmeVHosts);
 
   mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix;
@@ -155,11 +159,16 @@ in
       description = lib.mdDoc ''
         Override the configuration file used by Caddy. By default,
         NixOS generates one automatically.
+
+        The configuration file is exposed at {file}`${configPath}`.
       '';
     };
 
     adapter = mkOption {
-      default = null;
+      default = if (builtins.baseNameOf cfg.configFile) == "Caddyfile" then "caddyfile" else null;
+      defaultText = literalExpression ''
+        if (builtins.baseNameOf cfg.configFile) == "Caddyfile" then "caddyfile" else null
+      '';
       example = literalExpression "nginx";
       type = with types; nullOr str;
       description = lib.mdDoc ''
@@ -245,15 +254,23 @@ in
     };
 
     acmeCA = mkOption {
-      default = "https://acme-v02.api.letsencrypt.org/directory";
-      example = "https://acme-staging-v02.api.letsencrypt.org/directory";
+      default = null;
+      example = "https://acme-v02.api.letsencrypt.org/directory";
       type = with types; nullOr str;
       description = lib.mdDoc ''
+        ::: {.note}
+        Sets the [`acme_ca` option](https://caddyserver.com/docs/caddyfile/options#acme-ca)
+        in the global options block of the resulting Caddyfile.
+        :::
+
         The URL to the ACME CA's directory. It is strongly recommended to set
-        this to Let's Encrypt's staging endpoint for testing or development.
+        this to `https://acme-staging-v02.api.letsencrypt.org/directory` for
+        Let's Encrypt's [staging endpoint](https://letsencrypt.org/docs/staging-environment/)
+        while testing or in development.
 
-        Set it to `null` if you want to write a more
-        fine-grained configuration manually.
+        Value `null` should be prefered for production setups,
+        as it omits the `acme_ca` option to enable
+        [automatic issuer fallback](https://caddyserver.com/docs/automatic-https#issuer-fallback).
       '';
     };
 
@@ -267,6 +284,21 @@ in
       '';
     };
 
+    enableReload = mkOption {
+      default = true;
+      type = types.bool;
+      description = lib.mdDoc ''
+        Reload Caddy instead of restarting it when configuration file changes.
+
+        Note that enabling this option requires the [admin API](https://caddyserver.com/docs/caddyfile/options#admin)
+        to not be turned off.
+
+        If you enable this option, consider setting [`grace_period`](https://caddyserver.com/docs/caddyfile/options#grace-period)
+        to a non-infinite value in {option}`services.caddy.globalConfig`
+        to prevent Caddy waiting for active connections to finish,
+        which could delay the reload essentially indefinitely.
+      '';
+    };
   };
 
   # implementation
@@ -303,13 +335,16 @@ in
       wantedBy = [ "multi-user.target" ];
       startLimitIntervalSec = 14400;
       startLimitBurst = 10;
+      reloadTriggers = optional cfg.enableReload cfg.configFile;
 
-      serviceConfig = {
+      serviceConfig = let
+        runOptions = ''--config ${configPath} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"}'';
+      in {
         # 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 --config ${cfg.configFile} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"} ${optionalString cfg.resume "--resume"}'' ];
-        ExecReload = [ "" ''${cfg.package}/bin/caddy reload --config ${cfg.configFile} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"} --force'' ];
-        ExecStartPre = ''${cfg.package}/bin/caddy validate --config ${cfg.configFile} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"}'';
+        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'' ];
         User = cfg.user;
         Group = cfg.group;
         ReadWriteDirectories = cfg.dataDir;
@@ -345,5 +380,6 @@ in
       in
         listToAttrs certCfg;
 
+    environment.etc.${etcConfigFile}.source = cfg.configFile;
   };
 }
diff --git a/nixpkgs/nixos/modules/services/web-servers/keter/default.nix b/nixpkgs/nixos/modules/services/web-servers/keter/default.nix
index 9adbe65de69f..3916c486475d 100644
--- a/nixpkgs/nixos/modules/services/web-servers/keter/default.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/keter/default.nix
@@ -1,53 +1,82 @@
 { config, pkgs, lib, ... }:
 let
   cfg = config.services.keter;
+  yaml = pkgs.formats.yaml { };
 in
 {
   meta = {
     maintainers = with lib.maintainers; [ jappie ];
   };
 
+  imports = [
+    (lib.mkRenamedOptionModule [ "services" "keter" "keterRoot" ] [ "services" "keter" "root" ])
+    (lib.mkRenamedOptionModule [ "services" "keter" "keterPackage" ] [ "services" "keter" "package" ])
+  ];
+
   options.services.keter = {
     enable = lib.mkEnableOption (lib.mdDoc ''keter, a web app deployment manager.
 Note that this module only support loading of webapps:
 Keep an old app running and swap the ports when the new one is booted.
 '');
 
-    keterRoot = lib.mkOption {
+    root = lib.mkOption {
       type = lib.types.str;
       default = "/var/lib/keter";
       description = lib.mdDoc "Mutable state folder for keter";
     };
 
-    keterPackage = lib.mkOption {
+    package = lib.mkOption {
       type = lib.types.package;
       default = pkgs.haskellPackages.keter;
       defaultText = lib.literalExpression "pkgs.haskellPackages.keter";
       description = lib.mdDoc "The keter package to be used";
     };
 
+
     globalKeterConfig = lib.mkOption {
-      type = lib.types.attrs;
-      default = {
-        ip-from-header = true;
-        listeners = [{
-          host = "*4";
-          port = 6981;
-        }];
+      type = lib.types.submodule {
+        freeformType = yaml.type;
+        options = {
+          ip-from-header = lib.mkOption {
+            default = true;
+            type = lib.types.bool;
+            description = lib.mdDoc "You want that ip-from-header in the nginx setup case. It allows nginx setting the original ip address rather then it being localhost (due to reverse proxying)";
+          };
+          listeners = lib.mkOption {
+            default = [{ host = "*"; port = 6981; }];
+            type = lib.types.listOf (lib.types.submodule {
+              options = {
+                host = lib.mkOption {
+                  type = lib.types.str;
+                  description = lib.mdDoc "host";
+                };
+                port = lib.mkOption {
+                  type = lib.types.port;
+                  description = lib.mdDoc "port";
+                };
+              };
+            });
+            description = lib.mdDoc ''
+              You want that ip-from-header in
+              the nginx setup case.
+              It allows nginx setting the original ip address rather
+              then it being localhost (due to reverse proxying).
+              However if you configure keter to accept connections
+              directly you may want to set this to false.'';
+          };
+          rotate-logs = lib.mkOption {
+            default = false;
+            type = lib.types.bool;
+            description = lib.mdDoc ''
+              emits keter logs and it's applications to stderr.
+              which allows journald to capture them.
+              Set to true to let keter put the logs in files
+              (useful on non systemd systems, this is the old approach
+              where keter handled log management)'';
+          };
+        };
       };
-      # You want that ip-from-header in the nginx setup case
-      # so it's not set to 127.0.0.1.
-      # using a port above 1024 allows you to avoid needing CAP_NET_BIND_SERVICE
-      defaultText = lib.literalExpression ''
-        {
-          ip-from-header = true;
-          listeners = [{
-            host = "*4";
-            port = 6981;
-          }];
-        }
-      '';
-      description = lib.mdDoc "Global config for keter";
+      description = lib.mdDoc "Global config for keter, see <https://github.com/snoyberg/keter/blob/master/etc/keter-config.yaml> for reference";
     };
 
     bundle = {
@@ -90,12 +119,12 @@ Keep an old app running and swap the ports when the new one is booted.
 
   config = lib.mkIf cfg.enable (
     let
-      incoming = "${cfg.keterRoot}/incoming";
+      incoming = "${cfg.root}/incoming";
 
 
       globalKeterConfigFile = pkgs.writeTextFile {
         name = "keter-config.yml";
-        text = (lib.generators.toYAML { } (cfg.globalKeterConfig // { root = cfg.keterRoot; }));
+        text = (lib.generators.toYAML { } (cfg.globalKeterConfig // { root = cfg.root; }));
       };
 
       # If things are expected to change often, put it in the bundle!
@@ -122,7 +151,7 @@ Keep an old app running and swap the ports when the new one is booted.
         script = ''
           set -xe
           mkdir -p ${incoming}
-          { tail -F ${cfg.keterRoot}/log/keter/current.log -n 0 & ${cfg.keterPackage}/bin/keter ${globalKeterConfigFile}; }
+          ${lib.getExe cfg.package} ${globalKeterConfigFile};
         '';
         wantedBy = [ "multi-user.target" "nginx.service" ];
 
diff --git a/nixpkgs/nixos/modules/services/web-servers/lighttpd/cgit.nix b/nixpkgs/nixos/modules/services/web-servers/lighttpd/cgit.nix
index 5042fbf1f8f2..e9f42c41183b 100644
--- a/nixpkgs/nixos/modules/services/web-servers/lighttpd/cgit.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/lighttpd/cgit.nix
@@ -4,7 +4,7 @@ with lib;
 
 let
   cfg = config.services.lighttpd.cgit;
-  pathPrefix = if stringLength cfg.subdir == 0 then "" else "/" + cfg.subdir;
+  pathPrefix = optionalString (stringLength cfg.subdir != 0) ("/" + cfg.subdir);
   configFile = pkgs.writeText "cgitrc"
     ''
       # default paths to static assets
diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
index e87159ba99c7..fccc31b5116c 100644
--- a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
@@ -261,23 +261,6 @@ let
 
       ${proxyCachePathConfig}
 
-      ${optionalString cfg.statusPage ''
-        server {
-          listen ${toString cfg.defaultHTTPListenPort};
-          ${optionalString enableIPv6 "listen [::]:${toString cfg.defaultHTTPListenPort};" }
-
-          server_name localhost;
-
-          location /nginx_status {
-            stub_status on;
-            access_log off;
-            allow 127.0.0.1;
-            ${optionalString enableIPv6 "allow ::1;"}
-            deny all;
-          }
-        }
-      ''}
-
       ${vhosts}
 
       ${cfg.appendHttpConfig}
@@ -352,7 +335,7 @@ let
           + ";"))
           + "
             listen ${addr}:${toString port} "
-          + optionalString (ssl && vhost.http2) "http2 "
+          + optionalString (ssl && vhost.http2 && oldHTTP2) "http2 "
           + optionalString ssl "ssl "
           + optionalString vhost.default "default_server "
           + optionalString vhost.reuseport "reuseport "
@@ -362,7 +345,9 @@ let
 
         redirectListen = filter (x: !x.ssl) defaultListen;
 
-        acmeLocation = optionalString (vhost.enableACME || vhost.useACMEHost != null) ''
+        # 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
+        acmeLocation = optionalString (vhost.enableACME || (vhost.useACMEHost != null && config.security.acme.certs.${vhost.useACMEHost}.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
           # otherwise easily override this intended match accidentally).
@@ -395,6 +380,9 @@ let
         server {
           ${concatMapStringsSep "\n" listenString hostListen}
           server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases};
+          ${optionalString (hasSSL && vhost.http2 && !oldHTTP2) ''
+            http2 on;
+          ''}
           ${optionalString (hasSSL && vhost.quic) ''
             http3 ${if vhost.http3 then "on" else "off"};
             http3_hq ${if vhost.http3_hq then "on" else "off"};
@@ -478,6 +466,8 @@ let
   );
 
   mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix;
+
+  oldHTTP2 = versionOlder cfg.package.version "1.25.1";
 in
 
 {
@@ -1175,6 +1165,21 @@ in
     services.nginx.additionalModules = optional cfg.recommendedBrotliSettings pkgs.nginxModules.brotli
       ++ lib.optional cfg.recommendedZstdSettings pkgs.nginxModules.zstd;
 
+    services.nginx.virtualHosts.localhost = mkIf cfg.statusPage {
+      listenAddresses = lib.mkDefault ([
+        "0.0.0.0"
+      ] ++ lib.optional enableIPv6 "[::]");
+      locations."/nginx_status" = {
+        extraConfig = ''
+          stub_status on;
+          access_log off;
+          allow 127.0.0.1;
+          ${optionalString enableIPv6 "allow ::1;"}
+          deny all;
+        '';
+      };
+    };
+
     systemd.services.nginx = {
       description = "Nginx Web Server";
       wantedBy = [ "multi-user.target" ];
diff --git a/nixpkgs/nixos/modules/services/web-servers/rustus.nix b/nixpkgs/nixos/modules/services/web-servers/rustus.nix
new file mode 100644
index 000000000000..95c9a6455579
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/rustus.nix
@@ -0,0 +1,252 @@
+{ lib, pkgs, config, ... }:
+with lib;
+let
+  cfg = config.services.rustus;
+in
+{
+  meta.maintainers = with maintainers; [ happysalada ];
+
+  options.services.rustus = {
+
+    enable = mkEnableOption (lib.mdDoc "TUS protocol implementation in Rust.");
+
+    host = mkOption {
+      type = types.str;
+      description = lib.mdDoc ''
+        The host that rustus will connect to.
+      '';
+      default = "127.0.0.1";
+      example = "127.0.0.1";
+    };
+
+    port = mkOption {
+      type = types.port;
+      description = lib.mdDoc ''
+        The port that rustus will connect to.
+      '';
+      default = 1081;
+      example = 1081;
+    };
+
+    log_level = mkOption {
+      type = types.enum [ "DEBUG" "INFO" "ERROR" ];
+      description = lib.mdDoc ''
+        Desired log level
+      '';
+      default = "INFO";
+      example = "ERROR";
+    };
+
+    max_body_size = mkOption {
+      type = types.str;
+      description = lib.mdDoc ''
+        Maximum body size in bytes
+      '';
+      default = "10000000"; # 10 mb
+      example = "100000000";
+    };
+
+    url = mkOption {
+      type = types.str;
+      description = lib.mdDoc ''
+        url path for uploads
+      '';
+      default = "/files";
+    };
+
+    disable_health_access_logs = mkOption {
+      type = types.bool;
+      description = lib.mdDoc ''
+        disable access log for /health endpoint
+      '';
+      default = false;
+    };
+
+    cors = mkOption {
+      type = types.listOf types.str;
+      description = lib.mdDoc ''
+        list of origins allowed to upload
+      '';
+      default = ["*"];
+      example = ["*.staging.domain" "*.prod.domain"];
+    };
+
+    tus_extensions = mkOption {
+      type = types.listOf (types.enum [
+        "getting"
+        "creation"
+        "termination"
+        "creation-with-upload"
+        "creation-defer-length"
+        "concatenation"
+        "checksum"
+      ]);
+      description = lib.mdDoc ''
+        Since TUS protocol offers extensibility you can turn off some protocol extensions.
+      '';
+      default = [
+        "getting"
+        "creation"
+        "termination"
+        "creation-with-upload"
+        "creation-defer-length"
+        "concatenation"
+        "checksum"
+      ];
+    };
+
+    remove_parts = mkOption {
+      type = types.bool;
+      description = lib.mdDoc ''
+        remove parts files after successful concatenation
+      '';
+      default = true;
+      example = false;
+    };
+
+    storage = lib.mkOption {
+      description = lib.mdDoc ''
+        Storages are used to actually store your files. You can configure where you want to store files.
+      '';
+      default = {};
+      example = lib.literalExpression ''
+        {
+          type = "hybrid-s3"
+          s3_access_key_file = konfig.age.secrets.R2_ACCESS_KEY.path;
+          s3_secret_key_file = konfig.age.secrets.R2_SECRET_KEY.path;
+          s3_bucket = "my_bucket";
+          s3_url = "https://s3.example.com";
+        }
+      '';
+      type = lib.types.submodule {
+        options = {
+          type = lib.mkOption {
+            type = lib.types.enum ["file-storage" "hybrid-s3"];
+            description = lib.mdDoc "Type of storage to use";
+          };
+          s3_access_key_file = lib.mkOption {
+            type = lib.types.str;
+            description = lib.mdDoc "File path that contains the S3 access key.";
+          };
+          s3_secret_key_file = lib.mkOption {
+            type = lib.types.path;
+            description = lib.mdDoc "File path that contains the S3 secret key.";
+          };
+          s3_region = lib.mkOption {
+            type = lib.types.str;
+            default = "us-east-1";
+            description = lib.mdDoc "S3 region name.";
+          };
+          s3_bucket = lib.mkOption {
+            type = lib.types.str;
+            description = lib.mdDoc "S3 bucket.";
+          };
+          s3_url = lib.mkOption {
+            type = lib.types.str;
+            description = lib.mdDoc "S3 url.";
+          };
+
+          force_sync = lib.mkOption {
+            type = lib.types.bool;
+            description = lib.mdDoc "calls fsync system call after every write to disk in local storage";
+            default = true;
+          };
+          data_dir = lib.mkOption {
+            type = lib.types.str;
+            description = lib.mdDoc "path to the local directory where all files are stored";
+            default = "/var/lib/rustus";
+          };
+          dir_structure = lib.mkOption {
+            type = lib.types.str;
+            description = lib.mdDoc "pattern of a directory structure locally and on s3";
+            default = "{year}/{month}/{day}";
+          };
+        };
+      };
+    };
+
+    info_storage = lib.mkOption {
+      description = lib.mdDoc ''
+        Info storages are used to store information about file uploads. These storages must be persistent, because every time chunk is uploaded rustus updates information about upload. And when someone wants to download file, information about it requested from storage to get actual path of an upload.
+      '';
+      default = {};
+      type = lib.types.submodule {
+        options = {
+          type = lib.mkOption {
+            type = lib.types.enum ["file-info-storage"];
+            description = lib.mdDoc "Type of info storage to use";
+            default = "file-info-storage";
+          };
+          dir = lib.mkOption {
+            type = lib.types.str;
+            description = lib.mdDoc "directory to store info about uploads";
+            default = "/var/lib/rustus";
+          };
+        };
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    systemd.services.rustus =
+      let
+        isHybridS3 = cfg.storage.type == "hybrid-s3";
+      in
+    {
+      description = "Rustus server";
+      documentation = [ "https://s3rius.github.io/rustus/" ];
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      environment = {
+        RUSTUS_SERVER_HOST = cfg.host;
+        RUSTUS_SERVER_PORT = toString cfg.port;
+        RUSTUS_LOG_LEVEL = cfg.log_level;
+        RUSTUS_MAX_BODY_SIZE = cfg.max_body_size;
+        RUSTUS_URL = cfg.url;
+        RUSTUS_DISABLE_HEALTH_ACCESS_LOG = lib.mkIf cfg.disable_health_access_logs "true";
+        RUSTUS_CORS = lib.concatStringsSep "," cfg.cors;
+        RUSTUS_TUS_EXTENSIONS = lib.concatStringsSep "," cfg.tus_extensions;
+        RUSTUS_REMOVE_PARTS= if cfg.remove_parts then "true" else "false";
+        RUSTUS_STORAGE = cfg.storage.type;
+        RUSTUS_DATA_DIR = cfg.storage.data_dir;
+        RUSTUS_DIR_STRUCTURE = cfg.storage.dir_structure;
+        RUSTUS_FORCE_FSYNC = if cfg.storage.force_sync then "true" else "false";
+        RUSTUS_S3_URL = mkIf isHybridS3 cfg.storage.s3_url;
+        RUSTUS_S3_BUCKET = mkIf isHybridS3 cfg.storage.s3_bucket;
+        RUSTUS_S3_REGION = mkIf isHybridS3 cfg.storage.s3_region;
+        RUSTUS_S3_ACCESS_KEY_PATH = mkIf isHybridS3 "%d/S3_ACCESS_KEY_PATH";
+        RUSTUS_S3_SECRET_KEY_PATH = mkIf isHybridS3 "%d/S3_SECRET_KEY_PATH";
+        RUSTUS_INFO_STORAGE = cfg.info_storage.type;
+        RUSTUS_INFO_DIR = cfg.info_storage.dir;
+      };
+
+      serviceConfig = {
+        ExecStart = "${pkgs.rustus}/bin/rustus";
+        StateDirectory = "rustus";
+        DynamicUser = true;
+        LoadCredential = lib.optionals isHybridS3 [
+          "S3_ACCESS_KEY_PATH:${cfg.storage.s3_access_key_file}"
+          "S3_SECRET_KEY_PATH:${cfg.storage.s3_secret_key_file}"
+        ];
+        # hardening
+        RestrictRealtime=true;
+        RestrictNamespaces=true;
+        LockPersonality=true;
+        ProtectKernelModules=true;
+        ProtectKernelTunables=true;
+        ProtectKernelLogs=true;
+        ProtectControlGroups=true;
+        ProtectHostUserNamespaces=true;
+        ProtectClock=true;
+        RestrictSUIDSGID=true;
+        SystemCallArchitectures="native";
+        CapabilityBoundingSet="";
+        ProtectProc = "invisible";
+        # TODO consider SystemCallFilter LimitAS ProcSubset
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/static-web-server.nix b/nixpkgs/nixos/modules/services/web-servers/static-web-server.nix
new file mode 100644
index 000000000000..07187f00fecc
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/static-web-server.nix
@@ -0,0 +1,68 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.static-web-server;
+  toml = pkgs.formats.toml {};
+  configFilePath = toml.generate "config.toml" cfg.configuration;
+in {
+  options = {
+    services.static-web-server = {
+      enable = lib.mkEnableOption (lib.mdDoc ''Static Web Server'');
+      listen = lib.mkOption {
+        default = "[::]:8787";
+        type = lib.types.str;
+        description = lib.mdDoc ''
+          The "ListenStream" used in static-web-server.socket.
+          This is equivalent to SWS's "host" and "port" options.
+          See here for specific syntax: <https://www.freedesktop.org/software/systemd/man/systemd.socket.html#ListenStream=>
+        '';
+      };
+      root = lib.mkOption {
+        type = lib.types.path;
+        description = lib.mdDoc ''
+          The location of files for SWS to serve. Equivalent to SWS's "root" config value.
+          NOTE: This folder must exist before starting SWS.
+        '';
+      };
+      configuration = lib.mkOption {
+        default = { };
+        type = toml.type;
+        example = {
+          general = { log-level = "error"; directory-listing = true; };
+        };
+        description = lib.mdDoc ''
+          Configuration for Static Web Server. See
+          <https://static-web-server.net/configuration/config-file/>.
+          NOTE: Don't set "host", "port", or "root" here. They will be ignored.
+          Use the top-level "listen" and "root" options instead.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.static-web-server ];
+    systemd.packages = [ pkgs.static-web-server ];
+    # Have to set wantedBy since systemd.packages ignores the "Install" section
+    systemd.sockets.static-web-server = {
+      wantedBy = [ "sockets.target" ];
+      # Start with empty string to reset upstream option
+      listenStreams = [ "" cfg.listen ];
+    };
+    systemd.services.static-web-server = {
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        # Remove upstream sample environment file; use config.toml exclusively
+        EnvironmentFile = [ "" ];
+        ExecStart = [ "" "${pkgs.static-web-server}/bin/static-web-server --fd 0 --config-file ${configFilePath} --root ${cfg.root}" ];
+        # Supplementary groups doesn't work unless we create the group ourselves
+        SupplementaryGroups = [ "" ];
+        # If the user is serving files from their home dir, override ProtectHome to allow that
+        ProtectHome = if lib.hasPrefix "/home" cfg.root then "tmpfs" else "true";
+        BindReadOnlyPaths = cfg.root;
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ mac-chaffee ];
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/ttyd.nix b/nixpkgs/nixos/modules/services/web-servers/ttyd.nix
index e0a8b5179e06..3b1d87ccb483 100644
--- a/nixpkgs/nixos/modules/services/web-servers/ttyd.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/ttyd.nix
@@ -78,11 +78,12 @@ in
       clientOptions = mkOption {
         type = types.attrsOf types.str;
         default = {};
-        example = literalExpression ''{
-          fontSize = "16";
-          fontFamily = "Fira Code";
-
-        }'';
+        example = literalExpression ''
+          {
+            fontSize = "16";
+            fontFamily = "Fira Code";
+          }
+        '';
         description = lib.mdDoc ''
           Attribute set of client options for xtermjs.
           <https://xtermjs.org/docs/api/terminal/interfaces/iterminaloptions/>
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/budgie.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/budgie.nix
index b7341d4d8b49..a734bc288c9f 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/budgie.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/budgie.nix
@@ -156,7 +156,7 @@ in {
       ++ cfg.sessionPath;
 
     # Fonts.
-    fonts.fonts = mkDefault [
+    fonts.packages = mkDefault [
       pkgs.noto-fonts
       pkgs.hack-font
     ];
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/cde.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/cde.nix
index e0b4fb0e7bfb..ad4b5d27f9d9 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/cde.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/cde.nix
@@ -36,7 +36,7 @@ in {
         name = "cmsd";
         protocol = "udp";
         user = "root";
-        server = "${pkgs.cdesktopenv}/opt/dt/bin/rpc.cmsd";
+        server = "${pkgs.cdesktopenv}/bin/rpc.cmsd";
         extraConfig = ''
           type  = RPC UNLISTED
           rpc_number  = 100068
@@ -64,7 +64,7 @@ in {
     services.xserver.desktopManager.session = [
     { name = "CDE";
       start = ''
-        exec ${pkgs.cdesktopenv}/opt/dt/bin/Xsession
+        exec ${pkgs.cdesktopenv}/bin/Xsession
       '';
     }];
   };
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix
index 7ced5b63c839..b3cbe4c324df 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix
@@ -71,7 +71,7 @@ in
           package = mkDefault pkgs.cinnamon.mint-themes;
         };
         iconTheme = mkIf (notExcluded pkgs.cinnamon.mint-y-icons) {
-          name = mkDefault "Mint-Y-Aqua";
+          name = mkDefault "Mint-Y-Sand";
           package = mkDefault pkgs.cinnamon.mint-y-icons;
         };
         cursorTheme = mkIf (notExcluded pkgs.cinnamon.mint-cursor-themes) {
@@ -113,6 +113,8 @@ in
       services.gnome.glib-networking.enable = true;
       services.gnome.gnome-keyring.enable = true;
       services.gvfs.enable = true;
+      services.switcherooControl.enable = mkDefault true; # xapp-gpu-offload-helper
+      services.touchegg.enable = mkDefault true;
       services.udisks2.enable = true;
       services.upower.enable = mkDefault config.powerManagement.enable;
       services.xserver.libinput.enable = mkDefault true;
@@ -178,6 +180,8 @@ in
         nixos-artwork.wallpapers.simple-dark-gray
         mint-artwork
         mint-cursor-themes
+        mint-l-icons
+        mint-l-theme
         mint-themes
         mint-x-icons
         mint-y-icons
@@ -214,7 +218,7 @@ in
       qt.style = "adwaita";
 
       # Default Fonts
-      fonts.fonts = with pkgs; [
+      fonts.packages = with pkgs; [
         source-code-pro # Default monospace font in 3.32
         ubuntu_font_family # required for default theme
       ];
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/deepin.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/deepin.nix
index 70be6ed7c05e..b2369e2426f8 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/deepin.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/deepin.nix
@@ -67,7 +67,7 @@ in
       networking.networkmanager.enable = mkDefault true;
       programs.dconf.enable = mkDefault true;
 
-      fonts.fonts = with pkgs; [ noto-fonts ];
+      fonts.packages = with pkgs; [ noto-fonts ];
       xdg.mime.enable = true;
       xdg.menus.enable = true;
       xdg.icons.enable = true;
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix
index 2de5d845d68b..1512b5fdf8a0 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix
@@ -92,7 +92,7 @@ in
 
     environment.etc."X11/xkb".source = xcfg.xkbDir;
 
-    fonts.fonts = [ pkgs.dejavu_fonts pkgs.ubuntu_font_family ];
+    fonts.packages = [ pkgs.dejavu_fonts pkgs.ubuntu_font_family ];
 
     services.udisks2.enable = true;
     services.upower.enable = config.powerManagement.enable;
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
index 79b2e7c6ead7..5d950f7d7fc5 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
@@ -432,7 +432,7 @@ in
         isSystem = true;
       };
 
-      fonts.fonts = with pkgs; [
+      fonts.packages = with pkgs; [
         cantarell-fonts
         dejavu_fonts
         source-code-pro # Default monospace font in 3.32
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix
index c61c53642f4b..d8a47e93afc7 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix
@@ -113,6 +113,7 @@ in
 
       services.xserver.displayManager.sessionCommands = ''
         if test "$XDG_CURRENT_DESKTOP" = "Pantheon"; then
+            true
             ${concatMapStrings (p: ''
               if [ -d "${p}/share/gsettings-schemas/${p.name}" ]; then
                 export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${p}/share/gsettings-schemas/${p.name}
@@ -198,6 +199,7 @@ in
         gnome.adwaita-icon-theme
         gtk3.out # for gtk-launch program
         onboard
+        orca # elementary/greeter#668
         qgnomeplatform
         sound-theme-freedesktop
         xdg-user-dirs # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
@@ -264,7 +266,7 @@ in
       qt.style = "adwaita";
 
       # Default Fonts
-      fonts.fonts = with pkgs; [
+      fonts.packages = with pkgs; [
         inter
         open-dyslexic
         open-sans
@@ -305,7 +307,7 @@ in
       ])) config.environment.pantheon.excludePackages;
 
       # needed by screenshot
-      fonts.fonts = [
+      fonts.packages = [
         pkgs.pantheon.elementary-redacted-script
       ];
     })
@@ -314,7 +316,6 @@ in
       environment.systemPackages = with pkgs.pantheon; [
         contractor
         file-roller-contract
-        gnome-bluetooth-contract
       ];
 
       environment.pathsToLink = [
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/phosh.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/phosh.nix
index 3cfa6e044b73..e4cd9fd99e40 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/phosh.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/phosh.nix
@@ -100,7 +100,7 @@ let
     };
   };
 
-  optionalKV = k: v: if v == null then "" else "${k} = ${builtins.toString v}";
+  optionalKV = k: v: optionalString (v != null) "${k} = ${builtins.toString v}";
 
   renderPhocOutput = name: output: let
     modelines = if builtins.isList output.modeline
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
index 38f932ffb420..15a510fd8f96 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -332,7 +332,7 @@ in
       # Enable GTK applications to load SVG icons
       services.xserver.gdk-pixbuf.modulePackages = [ pkgs.librsvg ];
 
-      fonts.fonts = with pkgs; [ cfg.notoPackage hack-font ];
+      fonts.packages = with pkgs; [ cfg.notoPackage hack-font ];
       fonts.fontconfig.defaultFonts = {
         monospace = [ "Hack" "Noto Sans Mono" ];
         sansSerif = [ "Noto Sans" ];
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/xfce.nix
index eee1f63ebdcc..191b3690c02f 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -1,9 +1,10 @@
-{ config, lib, pkgs, ... }:
+{ config, lib, pkgs, utils, ... }:
 
 with lib;
 
 let
   cfg = config.services.xserver.desktopManager.xfce;
+  excludePackages = config.environment.xfce.excludePackages;
 
 in
 {
@@ -69,10 +70,17 @@ in
         description = lib.mdDoc "Enable the XFCE screensaver.";
       };
     };
+
+    environment.xfce.excludePackages = mkOption {
+      default = [];
+      example = literalExpression "[ pkgs.xfce.xfce4-volumed-pulse ]";
+      type = types.listOf types.package;
+      description = lib.mdDoc "Which packages XFCE should exclude from the default environment";
+    };
   };
 
   config = mkIf cfg.enable {
-    environment.systemPackages = with pkgs.xfce // pkgs; [
+    environment.systemPackages = utils.removePackagesByName (with pkgs.xfce // pkgs; [
       glib # for gsettings
       gtk3.out # gtk-update-icon-cache
 
@@ -121,7 +129,7 @@ in
       ] ++ optionals (!cfg.noDesktop) [
         xfce4-panel
         xfdesktop
-      ] ++ optional cfg.enableScreensaver xfce4-screensaver;
+      ] ++ optional cfg.enableScreensaver xfce4-screensaver) excludePackages;
 
     programs.xfconf.enable = true;
     programs.thunar.enable = true;
@@ -165,9 +173,9 @@ in
     programs.zsh.vteIntegration = mkDefault true;
 
     # Systemd services
-    systemd.packages = with pkgs.xfce; [
+    systemd.packages = utils.removePackagesByName (with pkgs.xfce; [
       xfce4-notifyd
-    ];
+    ]) excludePackages;
 
     security.pam.services.xfce4-screensaver.unixAuth = cfg.enableScreensaver;
   };
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/default.nix b/nixpkgs/nixos/modules/services/x11/display-managers/default.nix
index 995ecd231c43..1f08ded7c96f 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/default.nix
@@ -27,6 +27,30 @@ let
     Xft.hintstyle: ${fontconfig.hinting.style}
   '';
 
+  # FIXME: this is an ugly hack.
+  # Some sessions (read: most WMs) don't activate systemd's `graphical-session.target`.
+  # Other sessions (read: most non-WMs) expect `graphical-session.target` to be reached
+  # when the entire session is actually ready. We used to just unconditionally force
+  # `graphical-session.target` to be activated in the session wrapper so things like
+  # xdg-autostart-generator work on sessions that are wrong, but this broke sessions
+  # that do things right. So, preserve this behavior (with some extra steps) by matching
+  # on XDG_CURRENT_DESKTOP and deliberately ignoring sessions we know can do the right thing.
+  fakeSession = action: ''
+      session_is_systemd_aware=$(
+        IFS=:
+        for i in $XDG_CURRENT_DESKTOP; do
+          case $i in
+            KDE|GNOME|X-NIXOS-SYSTEMD-AWARE) echo "1"; exit; ;;
+            *) ;;
+          esac
+        done
+      )
+
+      if [ -z "$session_is_systemd_aware" ]; then
+        /run/current-system/systemd/bin/systemctl --user ${action} nixos-fake-graphical-session.target
+      fi
+  '';
+
   # file provided by services.xserver.displayManager.sessionData.wrapper
   xsessionWrapper = pkgs.writeScript "xsession-wrapper"
     ''
@@ -90,8 +114,7 @@ let
 
       ${cfg.displayManager.sessionCommands}
 
-      # Start systemd user services for graphical sessions
-      /run/current-system/systemd/bin/systemctl --user start graphical-session.target
+      ${fakeSession "start"}
 
       # Allow the user to setup a custom session type.
       if test -x ~/.xsession; then
@@ -417,10 +440,10 @@ in
       "XDG_SESSION_ID"
     ];
 
-    systemd.user.targets.graphical-session = {
+    systemd.user.targets.nixos-fake-graphical-session = {
       unitConfig = {
-        RefuseManualStart = false;
-        StopWhenUnneeded = false;
+        Description = "Fake graphical-session target for non-systemd-aware sessions";
+        BindsTo = "graphical-session.target";
       };
     };
 
@@ -451,7 +474,7 @@ in
 
           test -n "$waitPID" && wait "$waitPID"
 
-          /run/current-system/systemd/bin/systemctl --user stop graphical-session.target
+          ${fakeSession "stop"}
 
           exit 0
         '';
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
index f8f82bda3fa4..676d08b93e2c 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
@@ -207,7 +207,9 @@ in
     # conflicts display-manager.service, then when nixos-rebuild
     # switch starts multi-user.target, display-manager.service is
     # stopped so plymouth-quit.service can be started.)
-    systemd.services.plymouth-quit.wantedBy = lib.mkForce [];
+    systemd.services.plymouth-quit = mkIf config.boot.plymouth.enable {
+      wantedBy = lib.mkForce [];
+    };
 
     systemd.services.display-manager.serviceConfig = {
       # Restart = "always"; - already defined in xserver.nix
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/slick.nix b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/slick.nix
index 4456374cc569..ee9b4016c8ef 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/slick.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/slick.nix
@@ -142,7 +142,7 @@ in
       theme
     ];
 
-    fonts.fonts = [ font ];
+    fonts.packages = [ font ];
 
     environment.etc."lightdm/slick-greeter.conf".source = slickGreeterConf;
   };
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
index 0ddeac0f1098..c04edd0d4b7a 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
@@ -268,6 +268,17 @@ in
     environment.systemPackages = [ sddm ];
     services.dbus.packages = [ sddm ];
 
+    # We're not using the upstream unit, so copy these: https://github.com/sddm/sddm/blob/develop/services/sddm.service.in
+    systemd.services.display-manager.after = [
+      "systemd-user-sessions.service"
+      "getty@tty7.service"
+      "plymouth-quit.service"
+      "systemd-logind.service"
+    ];
+    systemd.services.display-manager.conflicts = [
+      "getty@tty7.service"
+    ];
+
     # To enable user switching, allow sddm to allocate TTYs/displays dynamically.
     services.xserver.tty = null;
     services.xserver.display = null;
diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/herbstluftwm.nix b/nixpkgs/nixos/modules/services/x11/window-managers/herbstluftwm.nix
index 816cbb36cafd..93705ada116d 100644
--- a/nixpkgs/nixos/modules/services/x11/window-managers/herbstluftwm.nix
+++ b/nixpkgs/nixos/modules/services/x11/window-managers/herbstluftwm.nix
@@ -40,7 +40,7 @@ in
             (cfg.configFile != null)
             ''-c "${cfg.configFile}"''
             ;
-        in "${cfg.package}/bin/herbstluftwm ${configFileClause}";
+        in "${cfg.package}/bin/herbstluftwm ${configFileClause} &";
     };
     environment.systemPackages = [ cfg.package ];
   };
diff --git a/nixpkgs/nixos/modules/services/x11/xserver.nix b/nixpkgs/nixos/modules/services/x11/xserver.nix
index 6d2321be8ef7..c2e6da4b453b 100644
--- a/nixpkgs/nixos/modules/services/x11/xserver.nix
+++ b/nixpkgs/nixos/modules/services/x11/xserver.nix
@@ -22,7 +22,7 @@ let
   };
 
   fontsForXServer =
-    config.fonts.fonts ++
+    config.fonts.packages ++
     # We don't want these fonts in fonts.conf, because then modern,
     # fontconfig-based applications will get horrible bitmapped
     # Helvetica fonts.  It's better to get a substitution (like Nimbus
@@ -725,7 +725,7 @@ in
     systemd.defaultUnit = mkIf cfg.autorun "graphical.target";
 
     systemd.services.display-manager =
-      { description = "X11 Server";
+      { description = "Display Manager";
 
         after = [ "acpid.service" "systemd-logind.service" "systemd-user-sessions.service" ];
 
@@ -883,8 +883,8 @@ in
         ${cfg.extraConfig}
       '';
 
-    fonts.enableDefaultFonts = mkDefault true;
-    fonts.fonts = [
+    fonts.enableDefaultPackages = mkDefault true;
+    fonts.packages = [
       (if cfg.upscaleDefaultCursor then fontcursormisc_hidpi else pkgs.xorg.fontcursormisc)
       pkgs.xorg.fontmiscmisc
     ];
diff --git a/nixpkgs/nixos/modules/system/activation/activatable-system.nix b/nixpkgs/nixos/modules/system/activation/activatable-system.nix
new file mode 100644
index 000000000000..7f6154794bd8
--- /dev/null
+++ b/nixpkgs/nixos/modules/system/activation/activatable-system.nix
@@ -0,0 +1,92 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib)
+    mkOption
+    optionalString
+    types
+    ;
+
+  perlWrapped = pkgs.perl.withPackages (p: with p; [ ConfigIniFiles FileSlurp ]);
+
+  systemBuilderArgs = {
+    activationScript = config.system.activationScripts.script;
+    dryActivationScript = config.system.dryActivationScript;
+  };
+
+  systemBuilderCommands = ''
+    echo "$activationScript" > $out/activate
+    echo "$dryActivationScript" > $out/dry-activate
+    substituteInPlace $out/activate --subst-var-by out ''${!toplevelVar}
+    substituteInPlace $out/dry-activate --subst-var-by out ''${!toplevelVar}
+    chmod u+x $out/activate $out/dry-activate
+    unset activationScript dryActivationScript
+
+    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
+    ${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
+    ''}
+  '';
+
+in
+{
+  options = {
+    system.activatable = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to add the activation script to the system profile.
+
+        The default, to have the script available all the time, is what we normally
+        do, but for image based systems, this may not be needed or not be desirable.
+      '';
+    };
+    system.build.separateActivationScript = mkOption {
+      type = types.package;
+      description = ''
+        A separate activation script package that's not part of the system profile.
+
+        This is useful for configurations where `system.activatable` is `false`.
+        Otherwise, you can just use `system.build.toplevel`.
+      '';
+    };
+  };
+  config = {
+    system.systemBuilderCommands = lib.mkIf config.system.activatable systemBuilderCommands;
+    system.systemBuilderArgs = lib.mkIf config.system.activatable
+      (systemBuilderArgs // {
+        toplevelVar = "out";
+      });
+
+    system.build.separateActivationScript =
+      pkgs.runCommand
+        "separate-activation-script"
+        (systemBuilderArgs // {
+          toplevelVar = "toplevel";
+          toplevel = config.system.build.toplevel;
+        })
+        ''
+          mkdir $out
+          ${systemBuilderCommands}
+        '';
+  };
+}
diff --git a/nixpkgs/nixos/modules/system/activation/activation-script.nix b/nixpkgs/nixos/modules/system/activation/activation-script.nix
index f23d4809e356..c8407dd6779a 100644
--- a/nixpkgs/nixos/modules/system/activation/activation-script.nix
+++ b/nixpkgs/nixos/modules/system/activation/activation-script.nix
@@ -204,6 +204,27 @@ in
         `/usr/bin/env`.
       '';
     };
+
+    system.build.installBootLoader = mkOption {
+      internal = true;
+      # "; true" => make the `$out` argument from switch-to-configuration.pl
+      #             go to `true` instead of `echo`, hiding the useless path
+      #             from the log.
+      default = "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
+      description = lib.mdDoc ''
+        A program that writes a bootloader installation script to the path passed in the first command line argument.
+
+        See `nixos/modules/system/activation/switch-to-configuration.pl`.
+      '';
+      type = types.unique {
+        message = ''
+          Only one bootloader can be enabled at a time. This requirement has not
+          been checked until NixOS 22.05. Earlier versions defaulted to the last
+          definition. Change your configuration to enable only one bootloader.
+        '';
+      } (types.either types.str types.package);
+    };
+
   };
 
 
diff --git a/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl b/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl
index de6e43dd30d8..cfad64039868 100755
--- a/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl
@@ -31,8 +31,10 @@ use Cwd qw(abs_path);
 ## no critic(ValuesAndExpressions::ProhibitNoisyQuotes, ValuesAndExpressions::ProhibitMagicNumbers, ValuesAndExpressions::ProhibitEmptyQuotes, ValuesAndExpressions::ProhibitInterpolationOfLiterals)
 ## no critic(RegularExpressions::ProhibitEscapedMetacharacters)
 
-# System closure path to switch to
+# Location of activation scripts
 my $out = "@out@";
+# System closure path to switch to
+my $toplevel = "@toplevel@";
 # Path to the directory containing systemd tools of the old system
 my $cur_systemd = abs_path("/run/current-system/sw/bin");
 # Path to the systemd store path of the new system
@@ -96,7 +98,7 @@ if ($action eq "switch" || $action eq "boot") {
     chomp(my $install_boot_loader = <<'EOFBOOTLOADER');
 @installBootLoader@
 EOFBOOTLOADER
-    system("$install_boot_loader $out") == 0 or exit 1;
+    system("$install_boot_loader $toplevel") == 0 or exit 1;
 }
 
 # Just in case the new configuration hangs the system, do a sync now.
@@ -110,7 +112,7 @@ if ($action eq "boot") {
 
 # Check if we can activate the new configuration.
 my $cur_init_interface_version = read_file("/run/current-system/init-interface-version", err_mode => "quiet") // "";
-my $new_init_interface_version = read_file("$out/init-interface-version");
+my $new_init_interface_version = read_file("$toplevel/init-interface-version");
 
 if ($new_init_interface_version ne $cur_init_interface_version) {
     print STDERR <<'EOF';
@@ -477,7 +479,7 @@ sub handle_modified_unit { ## no critic(Subroutines::ProhibitManyArgs, Subroutin
                             $units_to_stop->{$socket} = 1;
                             # Only restart sockets that actually
                             # exist in new configuration:
-                            if (-e "$out/etc/systemd/system/$socket") {
+                            if (-e "$toplevel/etc/systemd/system/$socket") {
                                 $units_to_start->{$socket} = 1;
                                 if ($units_to_start eq $units_to_restart) {
                                     record_unit($restart_list_file, $socket);
@@ -539,13 +541,13 @@ while (my ($unit, $state) = each(%{$active_cur})) {
     my $base_unit = $unit;
 
     my $cur_unit_file = "/etc/systemd/system/$base_unit";
-    my $new_unit_file = "$out/etc/systemd/system/$base_unit";
+    my $new_unit_file = "$toplevel/etc/systemd/system/$base_unit";
 
     # Detect template instances.
     if (!-e $cur_unit_file && !-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
       $base_unit = "$1\@.$2";
       $cur_unit_file = "/etc/systemd/system/$base_unit";
-      $new_unit_file = "$out/etc/systemd/system/$base_unit";
+      $new_unit_file = "$toplevel/etc/systemd/system/$base_unit";
     }
 
     my $base_name = $base_unit;
@@ -626,7 +628,7 @@ sub path_to_unit_name {
 # we generated units for all mounts; then we could unify this with the
 # unit checking code above.
 my ($cur_fss, $cur_swaps) = parse_fstab("/etc/fstab");
-my ($new_fss, $new_swaps) = parse_fstab("$out/etc/fstab");
+my ($new_fss, $new_swaps) = parse_fstab("$toplevel/etc/fstab");
 foreach my $mount_point (keys(%{$cur_fss})) {
     my $cur = $cur_fss->{$mount_point};
     my $new = $new_fss->{$mount_point};
@@ -655,7 +657,7 @@ foreach my $device (keys(%{$cur_swaps})) {
         # "systemctl stop" here because systemd has lots of alias
         # units that prevent a stop from actually calling
         # "swapoff".
-        if ($action ne "dry-activate") {
+        if ($action eq "dry-activate") {
             print STDERR "would stop swap device: $device\n";
         } else {
             print STDERR "stopping swap device: $device\n";
@@ -670,7 +672,7 @@ foreach my $device (keys(%{$cur_swaps})) {
 my $cur_pid1_path = abs_path("/proc/1/exe") // "/unknown";
 my $cur_systemd_system_config = abs_path("/etc/systemd/system.conf") // "/unknown";
 my $new_pid1_path = abs_path("$new_systemd/lib/systemd/systemd") or die;
-my $new_systemd_system_config = abs_path("$out/etc/systemd/system.conf") // "/unknown";
+my $new_systemd_system_config = abs_path("$toplevel/etc/systemd/system.conf") // "/unknown";
 
 my $restart_systemd = $cur_pid1_path ne $new_pid1_path;
 if ($cur_systemd_system_config ne $new_systemd_system_config) {
@@ -709,12 +711,12 @@ if ($action eq "dry-activate") {
     foreach (split(/\n/msx, read_file($dry_restart_by_activation_file, err_mode => "quiet") // "")) {
         my $unit = $_;
         my $base_unit = $unit;
-        my $new_unit_file = "$out/etc/systemd/system/$base_unit";
+        my $new_unit_file = "$toplevel/etc/systemd/system/$base_unit";
 
         # Detect template instances.
         if (!-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
           $base_unit = "$1\@.$2";
-          $new_unit_file = "$out/etc/systemd/system/$base_unit";
+          $new_unit_file = "$toplevel/etc/systemd/system/$base_unit";
         }
 
         my $base_name = $base_unit;
@@ -757,7 +759,7 @@ if ($action eq "dry-activate") {
 }
 
 
-syslog(LOG_NOTICE, "switching to system configuration $out");
+syslog(LOG_NOTICE, "switching to system configuration $toplevel");
 
 if (scalar(keys(%units_to_stop)) > 0) {
     if (scalar(@units_to_stop_filtered)) {
@@ -781,12 +783,12 @@ system("$out/activate", "$out") == 0 or $res = 2;
 foreach (split(/\n/msx, read_file($restart_by_activation_file, err_mode => "quiet") // "")) {
     my $unit = $_;
     my $base_unit = $unit;
-    my $new_unit_file = "$out/etc/systemd/system/$base_unit";
+    my $new_unit_file = "$toplevel/etc/systemd/system/$base_unit";
 
     # Detect template instances.
     if (!-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
       $base_unit = "$1\@.$2";
-      $new_unit_file = "$out/etc/systemd/system/$base_unit";
+      $new_unit_file = "$toplevel/etc/systemd/system/$base_unit";
     }
 
     my $base_name = $base_unit;
@@ -857,7 +859,7 @@ if (scalar(keys(%units_to_reload)) > 0) {
     for my $unit (keys(%units_to_reload)) {
         if (!unit_is_active($unit)) {
             # Figure out if we need to start the unit
-            my %unit_info = parse_unit("$out/etc/systemd/system/$unit");
+            my %unit_info = parse_unit("$toplevel/etc/systemd/system/$unit");
             if (!(parse_systemd_bool(\%unit_info, "Unit", "RefuseManualStart", 0) || parse_systemd_bool(\%unit_info, "Unit", "X-OnlyManualStart", 0))) {
                 $units_to_start{$unit} = 1;
                 record_unit($start_list_file, $unit);
@@ -940,9 +942,9 @@ if (scalar(@failed) > 0) {
 }
 
 if ($res == 0) {
-    syslog(LOG_NOTICE, "finished switching to system configuration $out");
+    syslog(LOG_NOTICE, "finished switching to system configuration $toplevel");
 } else {
-    syslog(LOG_ERR, "switching to system configuration $out failed (status $res)");
+    syslog(LOG_ERR, "switching to system configuration $toplevel failed (status $res)");
 }
 
 exit($res);
diff --git a/nixpkgs/nixos/modules/system/activation/top-level.nix b/nixpkgs/nixos/modules/system/activation/top-level.nix
index c4427149d9c9..07c2e05ce374 100644
--- a/nixpkgs/nixos/modules/system/activation/top-level.nix
+++ b/nixpkgs/nixos/modules/system/activation/top-level.nix
@@ -36,13 +36,6 @@ let
         ln -s ${config.hardware.firmware}/lib/firmware $out/firmware
       ''}
 
-      echo "$activationScript" > $out/activate
-      echo "$dryActivationScript" > $out/dry-activate
-      substituteInPlace $out/activate --subst-var out
-      substituteInPlace $out/dry-activate --subst-var out
-      chmod u+x $out/activate $out/dry-activate
-      unset activationScript dryActivationScript
-
       ${if config.boot.initrd.systemd.enable then ''
         cp ${config.system.build.bootStage2} $out/prepare-root
         substituteInPlace $out/prepare-root --subst-var-by systemConfig $out
@@ -63,19 +56,6 @@ let
       echo -n "$nixosLabel" > $out/nixos-version
       echo -n "${config.boot.kernelPackages.stdenv.hostPlatform.system}" > $out/system
 
-      mkdir $out/bin
-      export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive"
-      export distroId=${config.system.nixos.distroId};
-      substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
-      chmod +x $out/bin/switch-to-configuration
-      ${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
-        if ! output=$($perl/bin/perl -c $out/bin/switch-to-configuration 2>&1); then
-          echo "switch-to-configuration syntax is not valid:"
-          echo "$output"
-          exit 1
-        fi
-      ''}
-
       ${config.system.systemBuilderCommands}
 
       cp "$extraDependenciesPath" "$out/extra-dependencies"
@@ -93,7 +73,7 @@ let
   # symlinks to the various parts of the built configuration (the
   # kernel, systemd units, init scripts, etc.) as well as a script
   # `switch-to-configuration' that activates the configuration and
-  # makes it bootable.
+  # makes it bootable. See `activatable-system.nix`.
   baseSystem = pkgs.stdenvNoCC.mkDerivation ({
     name = "nixos-system-${config.system.name}-${config.system.nixos.label}";
     preferLocalBuild = true;
@@ -101,22 +81,12 @@ let
     passAsFile = [ "extraDependencies" ];
     buildCommand = systemBuilder;
 
-    inherit (pkgs) coreutils;
     systemd = config.systemd.package;
-    shell = "${pkgs.bash}/bin/sh";
-    su = "${pkgs.shadow.su}/bin/su";
-    utillinux = pkgs.util-linux;
 
     kernelParams = config.boot.kernelParams;
-    installBootLoader = config.system.build.installBootLoader;
-    activationScript = config.system.activationScripts.script;
-    dryActivationScript = config.system.dryActivationScript;
     nixosLabel = config.system.nixos.label;
 
     inherit (config.system) extraDependencies;
-
-    # Needed by switch-to-configuration.
-    perl = pkgs.perl.withPackages (p: with p; [ ConfigIniFiles FileSlurp ]);
   } // config.system.systemBuilderArgs);
 
   # Handle assertions and warnings
@@ -178,26 +148,6 @@ in
     };
 
     system.build = {
-      installBootLoader = mkOption {
-        internal = true;
-        # "; true" => make the `$out` argument from switch-to-configuration.pl
-        #             go to `true` instead of `echo`, hiding the useless path
-        #             from the log.
-        default = "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
-        description = lib.mdDoc ''
-          A program that writes a bootloader installation script to the path passed in the first command line argument.
-
-          See `nixos/modules/system/activation/switch-to-configuration.pl`.
-        '';
-        type = types.unique {
-          message = ''
-            Only one bootloader can be enabled at a time. This requirement has not
-            been checked until NixOS 22.05. Earlier versions defaulted to the last
-            definition. Change your configuration to enable only one bootloader.
-          '';
-        } (types.either types.str types.package);
-      };
-
       toplevel = mkOption {
         type = types.package;
         readOnly = true;
@@ -260,10 +210,10 @@ in
     };
 
     system.extraDependencies = mkOption {
-      type = types.listOf types.package;
+      type = types.listOf types.pathInStore;
       default = [];
       description = lib.mdDoc ''
-        A list of packages that should be included in the system
+        A list of paths that should be included in the system
         closure but generally not visible to users.
 
         This option has also been used for build-time checks, but the
@@ -380,6 +330,16 @@ in
         '';
 
     system.systemBuilderArgs = {
+
+      # Legacy environment variables. These were used by the activation script,
+      # but some other script might still depend on them, although unlikely.
+      installBootLoader = config.system.build.installBootLoader;
+      localeArchive = "${config.i18n.glibcLocales}/lib/locale/locale-archive";
+      distroId = config.system.nixos.distroId;
+      perl = pkgs.perl.withPackages (p: with p; [ ConfigIniFiles FileSlurp ]);
+      # End if legacy environment variables
+
+
       # Not actually used in the builder. `passedChecks` is just here to create
       # the build dependencies. Checks are similar to build dependencies in the
       # sense that if they fail, the system build fails. However, checks do not
diff --git a/nixpkgs/nixos/modules/system/boot/initrd-network.nix b/nixpkgs/nixos/modules/system/boot/initrd-network.nix
index e8bbf1d04032..1d95742face3 100644
--- a/nixpkgs/nixos/modules/system/boot/initrd-network.nix
+++ b/nixpkgs/nixos/modules/system/boot/initrd-network.nix
@@ -7,8 +7,8 @@ let
   cfg = config.boot.initrd.network;
 
   dhcpInterfaces = lib.attrNames (lib.filterAttrs (iface: v: v.useDHCP == true) (config.networking.interfaces or {}));
-  doDhcp = config.networking.useDHCP || dhcpInterfaces != [];
-  dhcpIfShellExpr = if config.networking.useDHCP
+  doDhcp = cfg.udhcpc.enable || dhcpInterfaces != [];
+  dhcpIfShellExpr = if config.networking.useDHCP || cfg.udhcpc.enable
                       then "$(ls /sys/class/net/ | grep -v ^lo$)"
                       else lib.concatMapStringsSep " " lib.escapeShellArg dhcpInterfaces;
 
@@ -79,13 +79,24 @@ in
       '';
     };
 
+    boot.initrd.network.udhcpc.enable = mkOption {
+      default = config.networking.useDHCP;
+      defaultText = "networking.useDHCP";
+      type = types.bool;
+      description = lib.mdDoc ''
+        Enables the udhcpc service during stage 1 of the boot process. This
+        defaults to {option}`networking.useDHCP`. Therefore, this useful if
+        useDHCP is off but the initramfs should do dhcp.
+      '';
+    };
+
     boot.initrd.network.udhcpc.extraArgs = mkOption {
       default = [];
       type = types.listOf types.str;
       description = lib.mdDoc ''
-        Additional command-line arguments passed verbatim to udhcpc if
-        {option}`boot.initrd.network.enable` and {option}`networking.useDHCP`
-        are enabled.
+        Additional command-line arguments passed verbatim to
+        udhcpc if {option}`boot.initrd.network.enable` and
+        {option}`boot.initrd.network.udhcpc.enable` are enabled.
       '';
     };
 
diff --git a/nixpkgs/nixos/modules/system/boot/kernel_config.nix b/nixpkgs/nixos/modules/system/boot/kernel_config.nix
index 31e9ec626ca6..e618070f0dc3 100644
--- a/nixpkgs/nixos/modules/system/boot/kernel_config.nix
+++ b/nixpkgs/nixos/modules/system/boot/kernel_config.nix
@@ -70,11 +70,10 @@ let
       let
         val = if item.freeform != null then item.freeform else item.tristate;
       in
-        if val == null
-          then ""
-          else if (item.optional)
+        optionalString (val != null)
+            (if (item.optional)
             then "${key}? ${mkValue val}\n"
-            else "${key} ${mkValue val}\n";
+            else "${key} ${mkValue val}\n");
 
     mkConf = cfg: concatStrings (mapAttrsToList mkConfigLine cfg);
   in mkConf exprs;
diff --git a/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix b/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix
index 9f80b40d116c..8f2f578e3070 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix
@@ -40,7 +40,10 @@ let
       backgroundColor = f cfg.backgroundColor;
       entryOptions = f cfg.entryOptions;
       subEntryOptions = f cfg.subEntryOptions;
-      grub = f grub;
+      # PC platforms (like x86_64-linux) have a non-EFI target (`grubTarget`), but other platforms
+      # (like aarch64-linux) have an undefined `grubTarget`. Avoid providing the path to a non-EFI
+      # GRUB on those platforms.
+      grub = f (if (grub.grubTarget or "") != "" then grub else "");
       grubTarget = f (grub.grubTarget or "");
       shell = "${pkgs.runtimeShell}";
       fullName = lib.getName realGrub;
@@ -65,8 +68,8 @@ let
         [ coreutils gnused gnugrep findutils diffutils btrfs-progs util-linux mdadm ]
         ++ optional cfg.efiSupport efibootmgr
         ++ optionals cfg.useOSProber [ busybox os-prober ]);
-      font = if cfg.font == null then ""
-        else (if lib.last (lib.splitString "." cfg.font) == "pf2"
+      font = lib.optionalString (cfg.font != null) (
+             if lib.last (lib.splitString "." cfg.font) == "pf2"
              then cfg.font
              else "${convertedFont}");
     });
@@ -352,10 +355,6 @@ in
         default = "";
         type = types.lines;
         example = ''
-          # GRUB 1 example (not GRUB 2 compatible)
-          title Windows
-            chainloader (hd0,1)+1
-
           # GRUB 2 example
           menuentry "Windows 7" {
             chainloader (hd0,4)+1
@@ -410,14 +409,6 @@ in
           Set to `null` to run GRUB in text mode.
 
           ::: {.note}
-          For grub 1:
-          It must be a 640x480,
-          14-colour image in XPM format, optionally compressed with
-          {command}`gzip` or {command}`bzip2`.
-          :::
-
-          ::: {.note}
-          For grub 2:
           File must be one of .png, .tga, .jpg, or .jpeg. JPEG images must
           not be progressive.
           The image will be scaled if necessary to fit the screen.
@@ -431,10 +422,6 @@ in
         default = null;
         description = lib.mdDoc ''
           Background color to be used for GRUB to fill the areas the image isn't filling.
-
-          ::: {.note}
-          This options has no effect for GRUB 1.
-          :::
         '';
       };
 
@@ -443,10 +430,6 @@ in
         type = types.nullOr types.str;
         description = lib.mdDoc ''
           Options applied to the primary NixOS menu entry.
-
-          ::: {.note}
-          This options has no effect for GRUB 1.
-          :::
         '';
       };
 
@@ -455,10 +438,6 @@ in
         type = types.nullOr types.str;
         description = lib.mdDoc ''
           Options applied to the secondary NixOS submenu entry.
-
-          ::: {.note}
-          This options has no effect for GRUB 1.
-          :::
         '';
       };
 
@@ -468,10 +447,6 @@ in
         default = null;
         description = lib.mdDoc ''
           Grub theme to be used.
-
-          ::: {.note}
-          This options has no effect for GRUB 1.
-          :::
         '';
       };
 
@@ -480,10 +455,6 @@ in
         default = "stretch";
         description = lib.mdDoc ''
           Whether to stretch the image or show the image in the top-left corner unstretched.
-
-          ::: {.note}
-          This options has no effect for GRUB 1.
-          :::
         '';
       };
 
@@ -592,8 +563,6 @@ in
         type = types.bool;
         description = lib.mdDoc ''
           Whether GRUB should be built against libzfs.
-          ZFS support is only available for GRUB v2.
-          This option is ignored for GRUB v1.
         '';
       };
 
@@ -602,8 +571,6 @@ in
         type = types.bool;
         description = lib.mdDoc ''
           Whether GRUB should be built with EFI support.
-          EFI support is only available for GRUB v2.
-          This option is ignored for GRUB v1.
         '';
       };
 
diff --git a/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
index 1dde55074336..9c9bee93de8a 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
+++ b/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
@@ -52,6 +52,10 @@ in
           Whether to create files with the system generations in
           `/boot`.
           `/boot/old` will hold files from old generations.
+
+          ::: {.note}
+          These options are deprecated, unsupported, and may not work like expected.
+          :::
         '';
       };
 
@@ -67,6 +71,10 @@ in
           type = types.bool;
           description = lib.mdDoc ''
             Enable using uboot as bootmanager for the raspberry pi.
+
+            ::: {.note}
+            These options are deprecated, unsupported, and may not work like expected.
+            :::
           '';
         };
 
@@ -76,6 +84,10 @@ in
           type = types.int;
           description = lib.mdDoc ''
             Maximum number of configurations in the boot menu.
+
+            ::: {.note}
+            These options are deprecated, unsupported, and may not work like expected.
+            :::
           '';
         };
 
@@ -87,19 +99,53 @@ in
         description = lib.mdDoc ''
           Extra options that will be appended to `/boot/config.txt` file.
           For possible values, see: https://www.raspberrypi.com/documentation/computers/config_txt.html
+
+          ::: {.note}
+          These options are deprecated, unsupported, and may not work like expected.
+          :::
         '';
       };
     };
   };
 
-  config = mkIf cfg.enable {
-    assertions = singleton {
-      assertion = !pkgs.stdenv.hostPlatform.isAarch64 || cfg.version >= 3;
-      message = "Only Raspberry Pi >= 3 supports aarch64.";
-    };
+  config = mkMerge[
+    (mkIf cfg.uboot.enable {
+      warnings = [
+        ''
+          The option set for `boot.loader.raspberrypi.uboot` has been recommended against
+          for years, and is now formally deprecated.
+
+          It is possible it already did not work like you expected.
+
+          It never worked on the Raspberry Pi 4 family.
+
+          These options will be removed by NixOS 24.11.
+        ''
+      ];
+    })
+    (mkIf cfg.enable {
+      warnings = [
+        ''
+          The option set for `boot.loader.raspberrypi` has been recommended against
+          for years, and is now formally deprecated.
+
+          It is possible it already did not work like you expected.
+
+          It never worked on the Raspberry Pi 4 family.
+
+          These options will be removed by NixOS 24.11.
+        ''
+      ];
+    })
+    (mkIf cfg.enable {
+      assertions = singleton {
+        assertion = !pkgs.stdenv.hostPlatform.isAarch64 || cfg.version >= 3;
+        message = "Only Raspberry Pi >= 3 supports aarch64.";
+      };
 
-    system.build.installBootLoader = builder;
-    system.boot.loader.id = "raspberrypi";
-    system.boot.loader.kernelFile = pkgs.stdenv.hostPlatform.linux-kernel.target;
-  };
+      system.build.installBootLoader = builder;
+      system.boot.loader.id = "raspberrypi";
+      system.boot.loader.kernelFile = pkgs.stdenv.hostPlatform.linux-kernel.target;
+    })
+  ];
 }
diff --git a/nixpkgs/nixos/modules/system/boot/luksroot.nix b/nixpkgs/nixos/modules/system/boot/luksroot.nix
index 71036044a2dc..dc3fe163116e 100644
--- a/nixpkgs/nixos/modules/system/boot/luksroot.nix
+++ b/nixpkgs/nixos/modules/system/boot/luksroot.nix
@@ -980,7 +980,7 @@ in
       ++ luks.cryptoModules
       # workaround until https://marc.info/?l=linux-crypto-vger&m=148783562211457&w=4 is merged
       # remove once 'modprobe --show-depends xts' shows ecb as a dependency
-      ++ (if builtins.elem "xts" luks.cryptoModules then ["ecb"] else []);
+      ++ (optional (builtins.elem "xts" luks.cryptoModules) "ecb");
 
     # copy the cryptsetup binary and it's dependencies
     boot.initrd.extraUtilsCommands = let
diff --git a/nixpkgs/nixos/modules/system/boot/networkd.nix b/nixpkgs/nixos/modules/system/boot/networkd.nix
index d88f88f9fdaf..6d0afcc57fcc 100644
--- a/nixpkgs/nixos/modules/system/boot/networkd.nix
+++ b/nixpkgs/nixos/modules/system/boot/networkd.nix
@@ -2,6 +2,7 @@
 
 with utils.systemdUtils.unitOptions;
 with utils.systemdUtils.lib;
+with utils.systemdUtils.network.units;
 with lib;
 
 let
@@ -170,7 +171,7 @@ let
           "batadv"
         ])
         (assertByteFormat "MTUBytes")
-        (assertMacAddress "MACAddress")
+        (assertNetdevMacAddress "MACAddress")
       ];
 
       sectionVLAN = checkUnitConfig "VLAN" [
@@ -222,6 +223,7 @@ let
           "PortRange"
           "FlowLabel"
           "IPDoNotFragment"
+          "Independent"
         ])
         (assertInt "VNI")
         (assertRange "VNI" 1 16777215)
@@ -241,6 +243,7 @@ let
         (assertInt "FlowLabel")
         (assertRange "FlowLabel" 0 1048575)
         (assertValueOneOf "IPDoNotFragment" (boolValues + ["inherit"]))
+        (assertValueOneOf "Independent" boolValues)
       ];
 
       sectionTunnel = checkUnitConfig "Tunnel" [
@@ -581,6 +584,7 @@ let
           "VLAN"
           "IPVLAN"
           "MACVLAN"
+          "MACVTAP"
           "VXLAN"
           "Tunnel"
           "MACsec"
@@ -1897,7 +1901,7 @@ let
 
   bridgeVLANOptions = {
     options = {
-      bridgeMDBConfig = mkOption {
+      bridgeVLANConfig = mkOption {
         default = {};
         example = { VLAN = 20; };
         type = types.addCheck (types.attrsOf unitOption) check.network.sectionBridgeVLAN;
@@ -2388,17 +2392,6 @@ let
       '';
     };
 
-    bridgeVLANConfig = mkOption {
-      default = {};
-      example = { VLAN = "10-20"; };
-      type = types.addCheck (types.attrsOf unitOption) check.network.sectionBridgeVLAN;
-      description = lib.mdDoc ''
-        Each attribute in this set specifies an option in the
-        `[BridgeVLAN]` section of the unit.  See
-        {manpage}`systemd.network(5)` for details.
-      '';
-    };
-
     bridgeVLANs = mkOption {
       default = [];
       example = [ { bridgeVLANConfig = { VLAN = "10-20"; }; } ];
@@ -2514,6 +2507,15 @@ let
       '';
     };
 
+    macvtap = mkOption {
+      default = [ ];
+      type = types.listOf types.str;
+      description = lib.mdDoc ''
+        A list of macvtap interfaces to be added to the network section of the
+        unit.  See {manpage}`systemd.network(5)` for details.
+      '';
+    };
+
     vxlan = mkOption {
       default = [ ];
       type = types.listOf types.str;
@@ -2615,95 +2617,6 @@ let
     };
   };
 
-  commonMatchText = def: optionalString (def.matchConfig != { }) ''
-    [Match]
-    ${attrsToSection def.matchConfig}
-  '';
-
-  linkToUnit = name: def:
-    { inherit (def) enable;
-      text = commonMatchText def
-        + ''
-          [Link]
-          ${attrsToSection def.linkConfig}
-        ''
-        + def.extraConfig;
-    };
-
-  netdevToUnit = name: def:
-    { inherit (def) enable;
-      text = commonMatchText def
-        + ''
-          [NetDev]
-          ${attrsToSection def.netdevConfig}
-        ''
-        + optionalString (def.vlanConfig != { }) ''
-          [VLAN]
-          ${attrsToSection def.vlanConfig}
-        ''
-        + optionalString (def.macvlanConfig != { }) ''
-          [MACVLAN]
-          ${attrsToSection def.macvlanConfig}
-        ''
-        + optionalString (def.vxlanConfig != { }) ''
-          [VXLAN]
-          ${attrsToSection def.vxlanConfig}
-        ''
-        + optionalString (def.tunnelConfig != { }) ''
-          [Tunnel]
-          ${attrsToSection def.tunnelConfig}
-        ''
-        + optionalString (def.fooOverUDPConfig != { }) ''
-          [FooOverUDP]
-          ${attrsToSection def.fooOverUDPConfig}
-        ''
-        + optionalString (def.peerConfig != { }) ''
-          [Peer]
-          ${attrsToSection def.peerConfig}
-        ''
-        + optionalString (def.tunConfig != { }) ''
-          [Tun]
-          ${attrsToSection def.tunConfig}
-        ''
-        + optionalString (def.tapConfig != { }) ''
-          [Tap]
-          ${attrsToSection def.tapConfig}
-        ''
-        + optionalString (def.l2tpConfig != { }) ''
-          [L2TP]
-          ${attrsToSection def.l2tpConfig}
-        ''
-        + flip concatMapStrings def.l2tpSessions (x: ''
-          [L2TPSession]
-          ${attrsToSection x.l2tpSessionConfig}
-        '')
-        + optionalString (def.wireguardConfig != { }) ''
-          [WireGuard]
-          ${attrsToSection def.wireguardConfig}
-        ''
-        + flip concatMapStrings def.wireguardPeers (x: ''
-          [WireGuardPeer]
-          ${attrsToSection x.wireguardPeerConfig}
-        '')
-        + optionalString (def.bondConfig != { }) ''
-          [Bond]
-          ${attrsToSection def.bondConfig}
-        ''
-        + optionalString (def.xfrmConfig != { }) ''
-          [Xfrm]
-          ${attrsToSection def.xfrmConfig}
-        ''
-        + optionalString (def.vrfConfig != { }) ''
-          [VRF]
-          ${attrsToSection def.vrfConfig}
-        ''
-        + optionalString (def.batmanAdvancedConfig != { }) ''
-          [BatmanAdvanced]
-          ${attrsToSection def.batmanAdvancedConfig}
-        ''
-        + def.extraConfig;
-    };
-
   renderConfig = def:
     { text = ''
         [Network]
@@ -2718,235 +2631,6 @@ let
         ${attrsToSection def.dhcpV6Config}
       ''; };
 
-  networkToUnit = name: def:
-    { inherit (def) enable;
-      text = commonMatchText def
-        + optionalString (def.linkConfig != { }) ''
-          [Link]
-          ${attrsToSection def.linkConfig}
-        ''
-        + ''
-          [Network]
-        ''
-        + attrsToSection def.networkConfig
-        + optionalString (def.address != [ ]) ''
-          ${concatStringsSep "\n" (map (s: "Address=${s}") def.address)}
-        ''
-        + optionalString (def.gateway != [ ]) ''
-          ${concatStringsSep "\n" (map (s: "Gateway=${s}") def.gateway)}
-        ''
-        + optionalString (def.dns != [ ]) ''
-          ${concatStringsSep "\n" (map (s: "DNS=${s}") def.dns)}
-        ''
-        + optionalString (def.ntp != [ ]) ''
-          ${concatStringsSep "\n" (map (s: "NTP=${s}") def.ntp)}
-        ''
-        + optionalString (def.bridge != [ ]) ''
-          ${concatStringsSep "\n" (map (s: "Bridge=${s}") def.bridge)}
-        ''
-        + optionalString (def.bond != [ ]) ''
-          ${concatStringsSep "\n" (map (s: "Bond=${s}") def.bond)}
-        ''
-        + optionalString (def.vrf != [ ]) ''
-          ${concatStringsSep "\n" (map (s: "VRF=${s}") def.vrf)}
-        ''
-        + optionalString (def.vlan != [ ]) ''
-          ${concatStringsSep "\n" (map (s: "VLAN=${s}") def.vlan)}
-        ''
-        + optionalString (def.macvlan != [ ]) ''
-          ${concatStringsSep "\n" (map (s: "MACVLAN=${s}") def.macvlan)}
-        ''
-        + optionalString (def.vxlan != [ ]) ''
-          ${concatStringsSep "\n" (map (s: "VXLAN=${s}") def.vxlan)}
-        ''
-        + optionalString (def.tunnel != [ ]) ''
-          ${concatStringsSep "\n" (map (s: "Tunnel=${s}") def.tunnel)}
-        ''
-        + optionalString (def.xfrm != [ ]) ''
-          ${concatStringsSep "\n" (map (s: "Xfrm=${s}") def.xfrm)}
-        ''
-        + ''
-
-        ''
-        + flip concatMapStrings def.addresses (x: ''
-          [Address]
-          ${attrsToSection x.addressConfig}
-        '')
-        + flip concatMapStrings def.routingPolicyRules (x: ''
-          [RoutingPolicyRule]
-          ${attrsToSection x.routingPolicyRuleConfig}
-        '')
-        + flip concatMapStrings def.routes (x: ''
-          [Route]
-          ${attrsToSection x.routeConfig}
-        '')
-        + optionalString (def.dhcpV4Config != { }) ''
-          [DHCPv4]
-          ${attrsToSection def.dhcpV4Config}
-        ''
-        + optionalString (def.dhcpV6Config != { }) ''
-          [DHCPv6]
-          ${attrsToSection def.dhcpV6Config}
-        ''
-        + optionalString (def.dhcpPrefixDelegationConfig != { }) ''
-          [DHCPPrefixDelegation]
-          ${attrsToSection def.dhcpPrefixDelegationConfig}
-        ''
-        + optionalString (def.ipv6AcceptRAConfig != { }) ''
-          [IPv6AcceptRA]
-          ${attrsToSection def.ipv6AcceptRAConfig}
-        ''
-        + optionalString (def.dhcpServerConfig != { }) ''
-          [DHCPServer]
-          ${attrsToSection def.dhcpServerConfig}
-        ''
-        + optionalString (def.ipv6SendRAConfig != { }) ''
-          [IPv6SendRA]
-          ${attrsToSection def.ipv6SendRAConfig}
-        ''
-        + flip concatMapStrings def.ipv6Prefixes (x: ''
-          [IPv6Prefix]
-          ${attrsToSection x.ipv6PrefixConfig}
-        '')
-        + flip concatMapStrings def.ipv6RoutePrefixes (x: ''
-          [IPv6RoutePrefix]
-          ${attrsToSection x.ipv6RoutePrefixConfig}
-        '')
-        + flip concatMapStrings def.dhcpServerStaticLeases (x: ''
-          [DHCPServerStaticLease]
-          ${attrsToSection x.dhcpServerStaticLeaseConfig}
-        '')
-        + optionalString (def.bridgeConfig != { }) ''
-          [Bridge]
-          ${attrsToSection def.bridgeConfig}
-        ''
-        + flip concatMapStrings def.bridgeFDBs (x: ''
-          [BridgeFDB]
-          ${attrsToSection x.bridgeFDBConfig}
-        '')
-        + flip concatMapStrings def.bridgeMDBs (x: ''
-          [BridgeMDB]
-          ${attrsToSection x.bridgeMDBConfig}
-        '')
-        + optionalString (def.lldpConfig != { }) ''
-          [LLDP]
-          ${attrsToSection def.lldpConfig}
-        ''
-        + optionalString (def.canConfig != { }) ''
-          [CAN]
-          ${attrsToSection def.canConfig}
-        ''
-        + optionalString (def.ipoIBConfig != { }) ''
-          [IPoIB]
-          ${attrsToSection def.ipoIBConfig}
-        ''
-        + optionalString (def.qdiscConfig != { }) ''
-          [QDisc]
-          ${attrsToSection def.qdiscConfig}
-        ''
-        + optionalString (def.networkEmulatorConfig != { }) ''
-          [NetworkEmulator]
-          ${attrsToSection def.networkEmulatorConfig}
-        ''
-        + optionalString (def.tokenBucketFilterConfig != { }) ''
-          [TokenBucketFilter]
-          ${attrsToSection def.tokenBucketFilterConfig}
-        ''
-        + optionalString (def.pieConfig != { }) ''
-          [PIE]
-          ${attrsToSection def.pieConfig}
-        ''
-        + optionalString (def.flowQueuePIEConfig != { }) ''
-          [FlowQueuePIE]
-          ${attrsToSection def.flowQueuePIEConfig}
-        ''
-        + optionalString (def.stochasticFairBlueConfig != { }) ''
-          [StochasticFairBlue]
-          ${attrsToSection def.stochasticFairBlueConfig}
-        ''
-        + optionalString (def.stochasticFairnessQueueingConfig != { }) ''
-          [StochasticFairnessQueueing]
-          ${attrsToSection def.stochasticFairnessQueueingConfig}
-        ''
-        + optionalString (def.bfifoConfig != { }) ''
-          [BFIFO]
-          ${attrsToSection def.bfifoConfig}
-        ''
-        + optionalString (def.pfifoConfig != { }) ''
-          [PFIFO]
-          ${attrsToSection def.pfifoConfig}
-        ''
-        + optionalString (def.pfifoHeadDropConfig != { }) ''
-          [PFIFOHeadDrop]
-          ${attrsToSection def.pfifoHeadDropConfig}
-        ''
-        + optionalString (def.pfifoFastConfig != { }) ''
-          [PFIFOFast]
-          ${attrsToSection def.pfifoFastConfig}
-        ''
-        + optionalString (def.cakeConfig != { }) ''
-          [CAKE]
-          ${attrsToSection def.cakeConfig}
-        ''
-        + optionalString (def.controlledDelayConfig != { }) ''
-          [ControlledDelay]
-          ${attrsToSection def.controlledDelayConfig}
-        ''
-        + optionalString (def.deficitRoundRobinSchedulerConfig != { }) ''
-          [DeficitRoundRobinScheduler]
-          ${attrsToSection def.deficitRoundRobinSchedulerConfig}
-        ''
-        + optionalString (def.deficitRoundRobinSchedulerClassConfig != { }) ''
-          [DeficitRoundRobinSchedulerClass]
-          ${attrsToSection def.deficitRoundRobinSchedulerClassConfig}
-        ''
-        + optionalString (def.enhancedTransmissionSelectionConfig != { }) ''
-          [EnhancedTransmissionSelection]
-          ${attrsToSection def.enhancedTransmissionSelectionConfig}
-        ''
-        + optionalString (def.genericRandomEarlyDetectionConfig != { }) ''
-          [GenericRandomEarlyDetection]
-          ${attrsToSection def.genericRandomEarlyDetectionConfig}
-        ''
-        + optionalString (def.fairQueueingControlledDelayConfig != { }) ''
-          [FairQueueingControlledDelay]
-          ${attrsToSection def.fairQueueingControlledDelayConfig}
-        ''
-        + optionalString (def.fairQueueingConfig != { }) ''
-          [FairQueueing]
-          ${attrsToSection def.fairQueueingConfig}
-        ''
-        + optionalString (def.trivialLinkEqualizerConfig != { }) ''
-          [TrivialLinkEqualizer]
-          ${attrsToSection def.trivialLinkEqualizerConfig}
-        ''
-        + optionalString (def.hierarchyTokenBucketConfig != { }) ''
-          [HierarchyTokenBucket]
-          ${attrsToSection def.hierarchyTokenBucketConfig}
-        ''
-        + optionalString (def.hierarchyTokenBucketClassConfig != { }) ''
-          [HierarchyTokenBucketClass]
-          ${attrsToSection def.hierarchyTokenBucketClassConfig}
-        ''
-        + optionalString (def.heavyHitterFilterConfig != { }) ''
-          [HeavyHitterFilter]
-          ${attrsToSection def.heavyHitterFilterConfig}
-        ''
-        + optionalString (def.quickFairQueueingConfig != { }) ''
-          [QuickFairQueueing]
-          ${attrsToSection def.quickFairQueueingConfig}
-        ''
-        + optionalString (def.quickFairQueueingConfigClass != { }) ''
-          [QuickFairQueueingClass]
-          ${attrsToSection def.quickFairQueueingConfigClass}
-        ''
-        + flip concatMapStrings def.bridgeVLANs (x: ''
-          [BridgeVLAN]
-          ${attrsToSection x.bridgeVLANConfig}
-        '')
-        + def.extraConfig;
-    };
-
   mkUnitFiles = prefix: cfg: listToAttrs (map (name: {
     name = "${prefix}systemd/network/${name}";
     value.source = "${cfg.units.${name}.unit}/${name}";
@@ -3059,11 +2743,14 @@ let
 
   };
 
-  commonConfig = config: let cfg = config.systemd.network; in mkMerge [
+  commonConfig = config: let
+    cfg = config.systemd.network;
+    mkUnit = f: def: { inherit (def) enable; text = f def; };
+  in mkMerge [
 
     # .link units are honored by udev, no matter if systemd-networkd is enabled or not.
     {
-      systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links;
+      systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (mkUnit linkToUnit v)) cfg.links;
 
       systemd.network.wait-online.extraArgs =
         [ "--timeout=${toString cfg.wait-online.timeout}" ]
@@ -3073,8 +2760,8 @@ let
 
     (mkIf config.systemd.network.enable {
 
-      systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.netdevs
-        // mapAttrs' (n: v: nameValuePair "${n}.network" (networkToUnit n v)) cfg.networks;
+      systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.netdev" (mkUnit netdevToUnit v)) cfg.netdevs
+        // mapAttrs' (n: v: nameValuePair "${n}.network" (mkUnit networkToUnit v)) cfg.networks;
 
       # systemd-networkd is socket-activated by kernel netlink route change
       # messages. It is important to have systemd buffer those on behalf of
@@ -3165,7 +2852,7 @@ let
 
     (mkIf cfg.enable {
 
-      systemd.package = pkgs.systemdStage1Network;
+      systemd.package = mkDefault pkgs.systemdStage1Network;
 
       # For networkctl
       systemd.dbus.enable = mkDefault true;
diff --git a/nixpkgs/nixos/modules/system/boot/plymouth.nix b/nixpkgs/nixos/modules/system/boot/plymouth.nix
index a1ab70938575..b041b8951fa3 100644
--- a/nixpkgs/nixos/modules/system/boot/plymouth.nix
+++ b/nixpkgs/nixos/modules/system/boot/plymouth.nix
@@ -98,10 +98,13 @@ in
         type = types.path;
         # Dimensions are 48x48 to match GDM logo
         default = "${nixos-icons}/share/icons/hicolor/48x48/apps/nix-snowflake-white.png";
-        defaultText = literalExpression ''pkgs.fetchurl {
-          url = "https://nixos.org/logo/nixos-hires.png";
-          sha256 = "1ivzgd7iz0i06y36p8m5w48fd8pjqwxhdaavc0pxs7w1g7mcy5si";
-        }'';
+        defaultText = literalExpression ''"''${nixos-icons}/share/icons/hicolor/48x48/apps/nix-snowflake-white.png"'';
+        example = literalExpression ''
+          pkgs.fetchurl {
+            url = "https://nixos.org/logo/nixos-hires.png";
+            sha256 = "1ivzgd7iz0i06y36p8m5w48fd8pjqwxhdaavc0pxs7w1g7mcy5si";
+          }
+        '';
         description = lib.mdDoc ''
           Logo which is displayed on the splash screen.
         '';
@@ -134,6 +137,13 @@ in
     # XXX: Needed because we supply a different set of plugins in initrd.
     environment.etc."plymouth/plugins".source = "${plymouth}/lib/plymouth";
 
+    systemd.tmpfiles.rules = [
+      "d /run/plymouth 0755 root root 0 -"
+      "L+ /run/plymouth/plymouthd.defaults - - - - /etc/plymouth/plymouthd.defaults"
+      "L+ /run/plymouth/themes - - - - /etc/plymouth/themes"
+      "L+ /run/plymouth/plugins - - - - /etc/plymouth/plugins"
+    ];
+
     systemd.packages = [ plymouth ];
 
     systemd.services.plymouth-kexec.wantedBy = [ "kexec.target" ];
@@ -160,8 +170,8 @@ in
       contents = {
         # Files
         "/etc/plymouth/plymouthd.conf".source = configFile;
-        "/etc/plymouth/plymouthd.defaults".source = "${plymouth}/share/plymouth/plymouthd.defaults";
         "/etc/plymouth/logo.png".source = cfg.logo;
+        "/etc/plymouth/plymouthd.defaults".source = "${plymouth}/share/plymouth/plymouthd.defaults";
         # Directories
         "/etc/plymouth/plugins".source = pkgs.runCommand "plymouth-initrd-plugins" {} ''
           # Check if the actual requested theme is here
@@ -174,8 +184,8 @@ in
 
           mkdir -p $out/renderers
           # module might come from a theme
-          cp ${themesEnv}/lib/plymouth/{text,details,label,$moduleName}.so $out
-          cp ${plymouth}/lib/plymouth/renderers/{drm,frame-buffer}.so $out/renderers
+          cp ${themesEnv}/lib/plymouth/*.so $out
+          cp ${plymouth}/lib/plymouth/renderers/*.so $out/renderers
         '';
         "/etc/plymouth/themes".source = pkgs.runCommand "plymouth-initrd-themes" {} ''
           # Check if the actual requested theme is here
@@ -184,19 +194,24 @@ in
               exit 1
           fi
 
-          mkdir $out
-          cp -r ${themesEnv}/share/plymouth/themes/${cfg.theme} $out
+          mkdir -p $out/${cfg.theme}
+          cp -r ${themesEnv}/share/plymouth/themes/${cfg.theme}/* $out/${cfg.theme}
           # Copy more themes if the theme depends on others
-          for theme in $(grep -hRo '/etc/plymouth/themes/.*$' $out | xargs -n1 basename); do
+          for theme in $(grep -hRo '/share/plymouth/themes/.*$' $out | xargs -n1 basename); do
               if [[ -d "${themesEnv}/share/plymouth/themes/$theme" ]]; then
                   if [[ ! -d "$out/$theme" ]]; then
                     echo "Adding dependent theme: $theme"
-                    cp -r "${themesEnv}/share/plymouth/themes/$theme" $out
+                    mkdir -p "$out/$theme"
+                    cp -r "${themesEnv}/share/plymouth/themes/$theme"/* "$out/$theme"
                   fi
               else
                 echo "Missing theme dependency: $theme"
               fi
           done
+          # Fixup references
+          for theme in $out/*/*.plymouth; do
+            sed -i "s,${builtins.storeDir}/.*/share/plymouth/themes,$out," "$theme"
+          done
         '';
 
         # Fonts
@@ -225,6 +240,11 @@ in
         plymouth-switch-root-initramfs.wantedBy = [ "halt.target" "kexec.target" "plymouth-switch-root-initramfs.service" "poweroff.target" "reboot.target" ];
         plymouth-switch-root.wantedBy = [ "initrd-switch-root.target" ];
       };
+      # Link in runtime files before starting
+      services.plymouth-start.preStart = ''
+        mkdir -p /run/plymouth
+        ln -sf /etc/plymouth/{plymouthd.defaults,themes,plugins} /run/plymouth/
+      '';
     };
 
     # Insert required udev rules. We take stage 2 systemd because the udev
@@ -249,8 +269,8 @@ in
 
       mkdir -p $out/lib/plymouth/renderers
       # module might come from a theme
-      cp ${themesEnv}/lib/plymouth/{text,details,label,$moduleName}.so $out/lib/plymouth
-      cp ${plymouth}/lib/plymouth/renderers/{drm,frame-buffer}.so $out/lib/plymouth/renderers
+      cp ${themesEnv}/lib/plymouth/*.so $out/lib/plymouth
+      cp ${plymouth}/lib/plymouth/renderers/*.so $out/lib/plymouth/renderers
 
       mkdir -p $out/share/plymouth/themes
       cp ${plymouth}/share/plymouth/plymouthd.defaults $out/share/plymouth
@@ -267,7 +287,7 @@ in
       chmod -R +w themes
       find themes -type f | while read file
       do
-        sed -i "s,/nix/.*/share/plymouth/themes,$out/share/plymouth/themes,g" $file
+        sed -i "s,${builtins.storeDir}/.*/share/plymouth/themes,$out/share/plymouth/themes,g" $file
       done
 
       # Install themes
@@ -275,7 +295,7 @@ in
 
       # Install logo
       mkdir -p $out/etc/plymouth
-      cp -r -L ${themesEnv}/etc/plymouth $out
+      cp -r -L ${themesEnv}/etc/plymouth $out/etc
 
       # Setup font
       mkdir -p $out/share/fonts
@@ -304,11 +324,11 @@ in
     boot.initrd.preLVMCommands = mkIf (!config.boot.initrd.systemd.enable) (mkAfter ''
       mkdir -p /etc/plymouth
       mkdir -p /run/plymouth
+      ln -s $extraUtils/etc/plymouth/logo.png /etc/plymouth/logo.png
       ln -s ${configFile} /etc/plymouth/plymouthd.conf
-      ln -s $extraUtils/share/plymouth/plymouthd.defaults /etc/plymouth/plymouthd.defaults
-      ln -s $extraUtils/share/plymouth/logo.png /etc/plymouth/logo.png
-      ln -s $extraUtils/share/plymouth/themes /etc/plymouth/themes
-      ln -s $extraUtils/lib/plymouth /etc/plymouth/plugins
+      ln -s $extraUtils/share/plymouth/plymouthd.defaults /run/plymouth/plymouthd.defaults
+      ln -s $extraUtils/share/plymouth/themes /run/plymouth/themes
+      ln -s $extraUtils/lib/plymouth /run/plymouth/plugins
       ln -s $extraUtils/etc/fonts /etc/fonts
 
       plymouthd --mode=boot --pid-file=/run/plymouth/pid --attach-to-session
diff --git a/nixpkgs/nixos/modules/system/boot/stage-1-init.sh b/nixpkgs/nixos/modules/system/boot/stage-1-init.sh
index bad045ec101f..bc2fc7f7b108 100644
--- a/nixpkgs/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixpkgs/nixos/modules/system/boot/stage-1-init.sh
@@ -114,6 +114,28 @@ waitDevice() {
     done
 }
 
+# Create the mount point if required.
+makeMountPoint() {
+    local device="$1"
+    local mountPoint="$2"
+    local options="$3"
+
+    local IFS=,
+
+    # If we're bind mounting a file, the mount point should also be a file.
+    if ! [ -d "$device" ]; then
+        for opt in $options; do
+            if [ "$opt" = bind ] || [ "$opt" = rbind ]; then
+                mkdir -p "$(dirname "/mnt-root$mountPoint")"
+                touch "/mnt-root$mountPoint"
+                return
+            fi
+        done
+    fi
+
+    mkdir -m 0755 -p "/mnt-root$mountPoint"
+}
+
 # Mount special file systems.
 specialMount() {
   local device="$1"
@@ -384,7 +406,7 @@ mountFS() {
 
     info "mounting $device on $mountPoint..."
 
-    mkdir -p "/mnt-root$mountPoint"
+    makeMountPoint "$device" "$mountPoint" "$optionsPrefixed"
 
     # For ZFS and CIFS mounts, retry a few times before giving up.
     # We do this for ZFS as a workaround for issue NixOS/nixpkgs#25383.
diff --git a/nixpkgs/nixos/modules/system/boot/stage-1.nix b/nixpkgs/nixos/modules/system/boot/stage-1.nix
index dcb15cf7d42b..7aaa3f85bfe0 100644
--- a/nixpkgs/nixos/modules/system/boot/stage-1.nix
+++ b/nixpkgs/nixos/modules/system/boot/stage-1.nix
@@ -91,7 +91,7 @@ let
   # we just copy what we need from Glibc and use patchelf to make it
   # work.
   extraUtils = pkgs.runCommand "extra-utils"
-    { nativeBuildInputs = [pkgs.buildPackages.nukeReferences];
+    { nativeBuildInputs = with pkgs.buildPackages; [ nukeReferences bintools ];
       allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd
     }
     ''
@@ -122,7 +122,7 @@ let
         # code, using default options and effectively ignore security relevant
         # ZFS properties such as `setuid=off` and `exec=off` (unless manually
         # duplicated in `fileSystems.*.options`, defeating "zfsutil"'s purpose).
-        copy_bin_and_libs ${pkgs.util-linux}/bin/mount
+        copy_bin_and_libs ${lib.getOutput "mount" pkgs.util-linux}/bin/mount
         copy_bin_and_libs ${pkgs.zfs}/bin/mount.zfs
       ''}
 
@@ -133,10 +133,6 @@ let
       copy_bin_and_libs ${getBin pkgs.lvm2}/bin/dmsetup
       copy_bin_and_libs ${getBin pkgs.lvm2}/bin/lvm
 
-      # Add RAID mdadm tool.
-      copy_bin_and_libs ${pkgs.mdadm}/sbin/mdadm
-      copy_bin_and_libs ${pkgs.mdadm}/sbin/mdmon
-
       # Copy udev.
       copy_bin_and_libs ${udev}/bin/udevadm
       copy_bin_and_libs ${udev}/lib/systemd/systemd-sysctl
@@ -225,7 +221,6 @@ let
       $out/bin/udevadm --version
       $out/bin/dmsetup --version 2>&1 | tee -a log | grep -q "version:"
       LVM_SYSTEM_DIR=$out $out/bin/lvm version 2>&1 | tee -a log | grep -q "LVM"
-      $out/bin/mdadm --version
       ${optionalString config.services.multipath.enable ''
         ($out/bin/multipath || true) 2>&1 | grep -q 'need to be root'
         ($out/bin/multipathd || true) 2>&1 | grep -q 'need to be root'
@@ -354,9 +349,6 @@ let
       [ { object = bootStage1;
           symlink = "/init";
         }
-        { object = pkgs.writeText "mdadm.conf" config.boot.initrd.services.swraid.mdadmConf;
-          symlink = "/etc/mdadm.conf";
-        }
         { object = pkgs.runCommand "initrd-kmod-blacklist-ubuntu" {
               src = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
               preferLocalBuild = true;
@@ -727,6 +719,6 @@ in
   };
 
   imports = [
-    (mkRenamedOptionModule [ "boot" "initrd" "mdadmConf" ] [ "boot" "initrd" "services" "swraid" "mdadmConf" ])
+    (mkRenamedOptionModule [ "boot" "initrd" "mdadmConf" ] [ "boot" "swraid" "mdadmConf" ])
   ];
 }
diff --git a/nixpkgs/nixos/modules/system/boot/systemd.nix b/nixpkgs/nixos/modules/system/boot/systemd.nix
index d2af86fef57c..ac55461107fb 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd.nix
@@ -587,7 +587,7 @@ in
     # Some overrides to upstream units.
     systemd.services."systemd-backlight@".restartIfChanged = false;
     systemd.services."systemd-fsck@".restartIfChanged = false;
-    systemd.services."systemd-fsck@".path = [ config.system.path ];
+    systemd.services."systemd-fsck@".path = [ pkgs.util-linux ] ++ config.system.fsPackages;
     systemd.services."systemd-makefs@" = {
       restartIfChanged = false;
       path = [ pkgs.util-linux ] ++ config.system.fsPackages;
@@ -597,6 +597,11 @@ in
       # drop-in in /etc, it does apply.
       overrideStrategy = "asDropin";
     };
+    systemd.services."systemd-mkswap@" = {
+      restartIfChanged = false;
+      path = [ pkgs.util-linux ];
+      overrideStrategy = "asDropin";
+    };
     systemd.services.systemd-random-seed.restartIfChanged = false;
     systemd.services.systemd-remount-fs.restartIfChanged = false;
     systemd.services.systemd-update-utmp.restartIfChanged = false;
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/nspawn.nix b/nixpkgs/nixos/modules/system/boot/systemd/nspawn.nix
index cbc89554c9fd..b513aa051f28 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd/nspawn.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd/nspawn.nix
@@ -11,7 +11,7 @@ let
     (assertOnlyFields [
       "Boot" "ProcessTwo" "Parameters" "Environment" "User" "WorkingDirectory"
       "PivotRoot" "Capability" "DropCapability" "NoNewPrivileges" "KillSignal"
-      "Personality" "MachineId" "PrivateUsers" "NotifyReady" "SystemCallFilter"
+      "Personality" "MachineID" "PrivateUsers" "NotifyReady" "SystemCallFilter"
       "LimitCPU" "LimitFSIZE" "LimitDATA" "LimitSTACK" "LimitCORE" "LimitRSS"
       "LimitNOFILE" "LimitAS" "LimitNPROC" "LimitMEMLOCK" "LimitLOCKS"
       "LimitSIGPENDING" "LimitMSGQUEUE" "LimitNICE" "LimitRTPRIO" "LimitRTTIME"
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/repart.nix b/nixpkgs/nixos/modules/system/boot/systemd/repart.nix
index e81b3e4ff2a1..2431c68ea17b 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd/repart.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd/repart.nix
@@ -1,28 +1,15 @@
-{ config, pkgs, lib, utils, ... }:
+{ config, lib, pkgs, utils, ... }:
 
 let
   cfg = config.systemd.repart;
   initrdCfg = config.boot.initrd.systemd.repart;
 
-  writeDefinition = name: partitionConfig: pkgs.writeText
-    "${name}.conf"
-    (lib.generators.toINI { } { Partition = partitionConfig; });
-
-  listOfDefinitions = lib.mapAttrsToList
-    writeDefinition
-    (lib.filterAttrs (k: _: !(lib.hasPrefix "_" k)) cfg.partitions);
-
-  # Create a directory in the store that contains a copy of all definition
-  # files. This is then passed to systemd-repart in the initrd so it can access
-  # the definition files after the sysroot has been mounted but before
-  # activation. This needs a hard copy of the files and not just symlinks
-  # because otherwise the files do not show up in the sysroot.
-  definitionsDirectory = pkgs.runCommand "systemd-repart-definitions" { } ''
-    mkdir -p $out
-    ${(lib.concatStringsSep "\n"
-      (map (pkg: "cp ${pkg} $out/${pkg.name}") listOfDefinitions)
-    )}
-  '';
+  format = pkgs.formats.ini { };
+
+  definitionsDirectory = utils.systemdUtils.lib.definitions
+    "repart.d"
+    format
+    (lib.mapAttrs (_n: v: { Partition = v; }) cfg.partitions);
 in
 {
   options = {
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/shutdown.nix b/nixpkgs/nixos/modules/system/boot/systemd/shutdown.nix
index b4b750fa9aaf..d7300e940af2 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd/shutdown.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd/shutdown.nix
@@ -30,7 +30,11 @@ in {
   };
 
   config = lib.mkIf cfg.enable {
-    systemd.shutdownRamfs.contents."/shutdown".source = "${config.systemd.package}/lib/systemd/systemd-shutdown";
+    systemd.shutdownRamfs.contents = {
+      "/shutdown".source = "${config.systemd.package}/lib/systemd/systemd-shutdown";
+      "/etc/initrd-release".source = config.environment.etc.os-release.source;
+      "/etc/os-release".source = config.environment.etc.os-release.source;
+    };
     systemd.shutdownRamfs.storePaths = [pkgs.runtimeShell "${pkgs.coreutils}/bin"];
 
     systemd.mounts = [{
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/sysupdate.nix b/nixpkgs/nixos/modules/system/boot/systemd/sysupdate.nix
new file mode 100644
index 000000000000..b1914a9c4e76
--- /dev/null
+++ b/nixpkgs/nixos/modules/system/boot/systemd/sysupdate.nix
@@ -0,0 +1,136 @@
+{ config, lib, pkgs, utils, ... }:
+
+let
+  cfg = config.systemd.sysupdate;
+
+  format = pkgs.formats.ini { };
+
+  definitionsDirectory = utils.systemdUtils.lib.definitions
+    "sysupdate.d"
+    format
+    cfg.transfers;
+in
+{
+  options.systemd.sysupdate = {
+
+    enable = lib.mkEnableOption (lib.mdDoc "systemd-sysupdate") // {
+      description = lib.mdDoc ''
+        Atomically update the host OS, container images, portable service
+        images or other sources.
+
+        If enabled, updates are triggered in regular intervals via a
+        `systemd.timer` unit.
+
+        Please see
+        <https://www.freedesktop.org/software/systemd/man/systemd-sysupdate.html>
+        for more details.
+      '';
+    };
+
+    timerConfig = utils.systemdUtils.unitOptions.timerOptions.options.timerConfig // {
+      default = { };
+      description = lib.mdDoc ''
+        The timer configuration for performing the update.
+
+        By default, the upstream configuration is used:
+        <https://github.com/systemd/systemd/blob/main/units/systemd-sysupdate.timer>
+      '';
+    };
+
+    reboot = {
+      enable = lib.mkEnableOption (lib.mdDoc "automatically rebooting after an update") // {
+        description = lib.mdDoc ''
+          Whether to automatically reboot after an update.
+
+          If set to `true`, the system will automatically reboot via a
+          `systemd.timer` unit but only after a new version was installed.
+
+          This uses a unit completely separate from the one performing the
+          update because it is typically advisable to download updates
+          regularly while the system is up, but delay reboots until the
+          appropriate time (i.e. typically at night).
+
+          Set this to `false` if you do not want to reboot after an update. This
+          is useful when you update a container image or another source where
+          rebooting is not necessary in order to finalize the update.
+        '';
+      };
+
+      timerConfig = utils.systemdUtils.unitOptions.timerOptions.options.timerConfig // {
+        default = { };
+        description = lib.mdDoc ''
+          The timer configuration for rebooting after an update.
+
+          By default, the upstream configuration is used:
+          <https://github.com/systemd/systemd/blob/main/units/systemd-sysupdate-reboot.timer>
+        '';
+      };
+    };
+
+    transfers = lib.mkOption {
+      type = with lib.types; attrsOf format.type;
+      default = { };
+      example = {
+        "10-uki.conf" = {
+          Transfer = {
+            ProtectVersion = "%A";
+          };
+
+          Source = {
+            Type = "url-file";
+            Path = "https://download.example.com/";
+            MatchPattern = "nixos_@v.efi.xz";
+          };
+
+          Target = {
+            Type = "regular-file";
+            Path = "/EFI/Linux";
+            PathRelativeTo = "boot";
+            MatchPattern = ''
+              nixos_@v+@l-@d.efi"; \
+              nixos_@v+@l.efi \
+              nixos_@v.efi
+            '';
+            Mode = "0444";
+            TriesLeft = 3;
+            TriesDone = 0;
+            InstancesMax = 2;
+          };
+        };
+      };
+      description = lib.mdDoc ''
+        Specify transfers as a set of the names of the transfer files as the
+        key and the configuration as its value. The configuration can use all
+        upstream options. See
+        <https://www.freedesktop.org/software/systemd/man/sysupdate.d.html>
+        for all available options.
+      '';
+    };
+
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    systemd.additionalUpstreamSystemUnits = [
+      "systemd-sysupdate.service"
+      "systemd-sysupdate.timer"
+      "systemd-sysupdate-reboot.service"
+      "systemd-sysupdate-reboot.timer"
+    ];
+
+    systemd.timers = {
+      "systemd-sysupdate" = {
+        wantedBy = [ "timers.target" ];
+        timerConfig = cfg.timerConfig;
+      };
+      "systemd-sysupdate-reboot" = lib.mkIf cfg.reboot.enable {
+        wantedBy = [ "timers.target" ];
+        timerConfig = cfg.reboot.timerConfig;
+      };
+    };
+
+    environment.etc."sysupdate.d".source = definitionsDirectory;
+  };
+
+  meta.maintainers = with lib.maintainers; [ nikstur ];
+}
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/user.nix b/nixpkgs/nixos/modules/system/boot/systemd/user.nix
index 92e1b087392d..1b6398d2f929 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd/user.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd/user.nix
@@ -42,7 +42,7 @@ let
 
   writeTmpfiles = { rules, user ? null }:
     let
-      suffix = if user == null then "" else "-${user}";
+      suffix = optionalString (user != null) "-${user}";
     in
     pkgs.writeTextFile {
       name = "nixos-user-tmpfiles.d${suffix}";
diff --git a/nixpkgs/nixos/modules/system/etc/etc.nix b/nixpkgs/nixos/modules/system/etc/etc.nix
index cfb9c39458ea..ea61e7384e60 100644
--- a/nixpkgs/nixos/modules/system/etc/etc.nix
+++ b/nixpkgs/nixos/modules/system/etc/etc.nix
@@ -173,7 +173,7 @@ in
           config = {
             target = mkDefault name;
             source = mkIf (config.text != null) (
-              let name' = "etc-" + baseNameOf name;
+              let name' = "etc-" + lib.replaceStrings ["/"] ["-"] name;
               in mkDerivedConfig options.text (pkgs.writeText name')
             );
           };
diff --git a/nixpkgs/nixos/modules/tasks/bcache.nix b/nixpkgs/nixos/modules/tasks/bcache.nix
index 408ddc02373f..35b922dc8a1d 100644
--- a/nixpkgs/nixos/modules/tasks/bcache.nix
+++ b/nixpkgs/nixos/modules/tasks/bcache.nix
@@ -1,8 +1,12 @@
 { config, lib, pkgs, ... }:
 
 {
-  options.boot.initrd.services.bcache.enable = (lib.mkEnableOption (lib.mdDoc "bcache support in the initrd")) // {
-    visible = false; # only works with systemd stage 1
+  options.boot.initrd.services.bcache.enable = lib.mkEnableOption (lib.mdDoc "bcache support in the initrd") // {
+    description = lib.mdDoc ''
+      *This will only be used when systemd is used in stage 1.*
+
+      Whether to enable bcache support in the initrd.
+    '';
   };
 
   config = {
diff --git a/nixpkgs/nixos/modules/tasks/filesystems/bcachefs.nix b/nixpkgs/nixos/modules/tasks/filesystems/bcachefs.nix
index 851c09781339..19ef188ce783 100644
--- a/nixpkgs/nixos/modules/tasks/filesystems/bcachefs.nix
+++ b/nixpkgs/nixos/modules/tasks/filesystems/bcachefs.nix
@@ -6,6 +6,15 @@ let
 
   bootFs = filterAttrs (n: fs: (fs.fsType == "bcachefs") && (utils.fsNeededForBoot fs)) config.fileSystems;
 
+  mountCommand = pkgs.runCommand "mount.bcachefs" {} ''
+    mkdir -p $out/bin
+    cat > $out/bin/mount.bcachefs <<EOF
+    #!/bin/sh
+    exec "/bin/bcachefs" mount "\$@"
+    EOF
+    chmod +x $out/bin/mount.bcachefs
+  '';
+
   commonFunctions = ''
     prompt() {
         local name="$1"
@@ -58,13 +67,12 @@ in
 
       boot.initrd.systemd.extraBin = {
         "bcachefs" = "${pkgs.bcachefs-tools}/bin/bcachefs";
-        "mount.bcachefs" = pkgs.runCommand "mount.bcachefs" {} ''
-          cp -pdv ${pkgs.bcachefs-tools}/bin/.mount.bcachefs.sh-wrapped $out
-        '';
+        "mount.bcachefs" = "${mountCommand}/bin/mount.bcachefs";
       };
 
       boot.initrd.extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable) ''
         copy_bin_and_libs ${pkgs.bcachefs-tools}/bin/bcachefs
+        copy_bin_and_libs ${mountCommand}/bin/mount.bcachefs
       '';
       boot.initrd.extraUtilsCommandsTest = ''
         $out/bin/bcachefs version
diff --git a/nixpkgs/nixos/modules/tasks/filesystems/squashfs.nix b/nixpkgs/nixos/modules/tasks/filesystems/squashfs.nix
new file mode 100644
index 000000000000..10d45a21d3ca
--- /dev/null
+++ b/nixpkgs/nixos/modules/tasks/filesystems/squashfs.nix
@@ -0,0 +1,13 @@
+{ config, lib, ... }:
+
+let
+
+  inInitrd = lib.any (fs: fs == "squashfs") config.boot.initrd.supportedFilesystems;
+
+in
+
+{
+
+  boot.initrd.availableKernelModules = lib.mkIf inInitrd [ "squashfs" ];
+
+}
diff --git a/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix b/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix
index 16dc0c44c18d..21d604bee6e3 100644
--- a/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix
@@ -110,17 +110,18 @@ let
   createImportService = { pool, systemd, force, prefix ? "" }:
     nameValuePair "zfs-import-${pool}" {
       description = "Import ZFS pool \"${pool}\"";
-      # we need systemd-udev-settle to ensure devices are available
+      # We wait for systemd-udev-settle to ensure devices are available,
+      # but don't *require* it, because mounts shouldn't be killed if it's stopped.
       # In the future, hopefully someone will complete this:
       # https://github.com/zfsonlinux/zfs/pull/4943
-      requires = [ "systemd-udev-settle.service" ];
+      wants = [ "systemd-udev-settle.service" ];
       after = [
         "systemd-udev-settle.service"
         "systemd-modules-load.service"
         "systemd-ask-password-console.service"
       ];
-      wantedBy = (getPoolMounts prefix pool) ++ [ "local-fs.target" ];
-      before = (getPoolMounts prefix pool) ++ [ "local-fs.target" ];
+      requiredBy = getPoolMounts prefix pool ++ [ "zfs-import.target" ];
+      before = getPoolMounts prefix pool ++ [ "zfs-import.target" ];
       unitConfig = {
         DefaultDependencies = "no";
       };
@@ -323,6 +324,30 @@ in
           Defaults to 0, which waits forever.
         '';
       };
+
+      removeLinuxDRM = lib.mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Linux 6.2 dropped some kernel symbols required on aarch64 required by zfs.
+          Enabling this option will bring them back to allow this kernel version.
+          Note that in some jurisdictions this may be illegal as it might be considered
+          removing copyright protection from the code.
+          See https://www.ifross.org/?q=en/artikel/ongoing-dispute-over-value-exportsymbolgpl-function for further information.
+
+          If configure your kernel package with `zfs.latestCompatibleLinuxPackages`, you will need to also pass removeLinuxDRM to that package like this:
+
+          ```
+          { pkgs, ... }: {
+            boot.kernelPackages = (pkgs.zfs.override {
+              removeLinuxDRM = pkgs.hostPlatform.isAarch64;
+            }).latestCompatibleLinuxPackages;
+
+            boot.zfs.removeLinuxDRM = true;
+          }
+          ```
+        '';
+      };
     };
 
     services.zfs.autoSnapshot = {
@@ -523,6 +548,15 @@ in
           assertion = cfgZfs.allowHibernation -> !cfgZfs.forceImportRoot && !cfgZfs.forceImportAll;
           message = "boot.zfs.allowHibernation while force importing is enabled will cause data corruption";
         }
+        {
+          assertion = !(elem "" allPools);
+          message = ''
+            Automatic pool detection found an empty pool name, which can't be used.
+            Hint: for `fileSystems` entries with `fsType = zfs`, the `device` attribute
+            should be a zfs dataset name, like `device = "pool/data/set"`.
+            This error can be triggered by using an absolute path, such as `"/dev/disk/..."`.
+          '';
+        }
       ];
 
       boot = {
@@ -532,11 +566,13 @@ in
         # https://github.com/NixOS/nixpkgs/issues/106093
         kernelParams = lib.optionals (!config.boot.zfs.allowHibernation) [ "nohibernate" ];
 
-        extraModulePackages = [
-          (if config.boot.zfs.enableUnstable then
+        extraModulePackages = let
+          kernelPkg = if config.boot.zfs.enableUnstable then
             config.boot.kernelPackages.zfsUnstable
            else
-            config.boot.kernelPackages.zfs)
+            config.boot.kernelPackages.zfs;
+        in [
+          (kernelPkg.override { inherit (cfgZfs) removeLinuxDRM; })
         ];
       };
 
@@ -593,6 +629,8 @@ in
             force = cfgZfs.forceImportRoot;
             prefix = "/sysroot";
           }) rootPools);
+          targets.zfs-import.wantedBy = [ "zfs.target" ];
+          targets.zfs.wantedBy = [ "initrd.target" ];
           extraBin = {
             # zpool and zfs are already in thanks to fsPackages
             awk = "${pkgs.gawk}/bin/awk";
@@ -654,6 +692,21 @@ in
       services.udev.packages = [ cfgZfs.package ]; # to hook zvol naming, etc.
       systemd.packages = [ cfgZfs.package ];
 
+      # Export kernel_neon_* symbols again.
+      # This change is necessary until ZFS figures out a solution
+      # with upstream or in their build system to fill the gap for
+      # this symbol.
+      # In the meantime, we restore what was once a working piece of code
+      # in the kernel.
+      boot.kernelPatches = lib.optional (cfgZfs.removeLinuxDRM && pkgs.stdenv.hostPlatform.system == "aarch64-linux") {
+        name = "export-neon-symbols-as-gpl";
+        patch = pkgs.fetchpatch {
+          url = "https://github.com/torvalds/linux/commit/aaeca98456431a8d9382ecf48ac4843e252c07b3.patch";
+          hash = "sha256-L2g4G1tlWPIi/QRckMuHDcdWBcKpObSWSRTvbHRIwIk=";
+          revert = true;
+        };
+      };
+
       systemd.services = let
         createImportService' = pool: createImportService {
           inherit pool;
@@ -689,15 +742,7 @@ in
                       map createSyncService allPools ++
                       map createZfsService [ "zfs-mount" "zfs-share" "zfs-zed" ]);
 
-      systemd.targets.zfs-import =
-        let
-          services = map (pool: "zfs-import-${pool}.service") dataPools;
-        in
-          {
-            requires = services;
-            after = services;
-            wantedBy = [ "zfs.target" ];
-          };
+      systemd.targets.zfs-import.wantedBy = [ "zfs.target" ];
 
       systemd.targets.zfs.wantedBy = [ "multi-user.target" ];
     })
diff --git a/nixpkgs/nixos/modules/tasks/lvm.nix b/nixpkgs/nixos/modules/tasks/lvm.nix
index a14f26c02e48..325a5aa45b1e 100644
--- a/nixpkgs/nixos/modules/tasks/lvm.nix
+++ b/nixpkgs/nixos/modules/tasks/lvm.nix
@@ -25,8 +25,12 @@ in {
     boot.vdo.enable = mkEnableOption (lib.mdDoc "support for booting from VDOLVs");
   };
 
-  options.boot.initrd.services.lvm.enable = (mkEnableOption (lib.mdDoc "enable booting from LVM2 in the initrd")) // {
-    visible = false;
+  options.boot.initrd.services.lvm.enable = mkEnableOption (lib.mdDoc "booting from LVM2 in the initrd") // {
+    description = lib.mdDoc ''
+      *This will only be used when systemd is used in stage 1.*
+
+      Whether to enable booting from LVM2 in the initrd.
+    '';
   };
 
   config = mkMerge [
@@ -40,12 +44,13 @@ in {
       systemd.packages = [ cfg.package ];
 
       services.udev.packages = [ cfg.package.out ];
-
+    })
+    (mkIf config.boot.initrd.services.lvm.enable {
       # We need lvm2 for the device-mapper rules
-      boot.initrd.services.udev.packages = lib.mkIf config.boot.initrd.services.lvm.enable [ cfg.package ];
+      boot.initrd.services.udev.packages = [ cfg.package ];
       # The device-mapper rules want to call tools from lvm2
-      boot.initrd.systemd.initrdBin = lib.mkIf config.boot.initrd.services.lvm.enable [ cfg.package ];
-      boot.initrd.services.udev.binPackages = lib.mkIf config.boot.initrd.services.lvm.enable [ cfg.package ];
+      boot.initrd.systemd.initrdBin = [ cfg.package ];
+      boot.initrd.services.udev.binPackages = [ cfg.package ];
     })
     (mkIf cfg.dmeventd.enable {
       systemd.sockets."dm-event".wantedBy = [ "sockets.target" ];
diff --git a/nixpkgs/nixos/modules/tasks/network-interfaces.nix b/nixpkgs/nixos/modules/tasks/network-interfaces.nix
index 954d7ffba71d..eb1c7512d920 100644
--- a/nixpkgs/nixos/modules/tasks/network-interfaces.nix
+++ b/nixpkgs/nixos/modules/tasks/network-interfaces.nix
@@ -1496,7 +1496,7 @@ in
           in
           ''
             # override to ${msg} for ${i.name}
-            ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.procps}/bin/sysctl net.ipv6.conf.${replaceStrings ["."] ["/"] i.name}.use_tempaddr=${val}"
+            ACTION=="add", SUBSYSTEM=="net", NAME=="${i.name}", RUN+="${pkgs.procps}/bin/sysctl net.ipv6.conf.${replaceStrings ["."] ["/"] i.name}.use_tempaddr=${val}"
           '') (filter (i: i.tempAddress != cfg.tempAddresses) interfaces);
       })
     ] ++ lib.optional (cfg.wlanInterfaces != {})
diff --git a/nixpkgs/nixos/modules/tasks/swraid.nix b/nixpkgs/nixos/modules/tasks/swraid.nix
index 7832bbf92016..f624294565b0 100644
--- a/nixpkgs/nixos/modules/tasks/swraid.nix
+++ b/nixpkgs/nixos/modules/tasks/swraid.nix
@@ -1,43 +1,76 @@
 { config, pkgs, lib, ... }: let
 
-  cfg = config.boot.initrd.services.swraid;
+  cfg = config.boot.swraid;
 
 in {
+  imports = [
+    (lib.mkRenamedOptionModule [ "boot" "initrd" "services" "swraid" "enable" ] [ "boot" "swraid" "enable" ])
+    (lib.mkRenamedOptionModule [ "boot" "initrd" "services" "swraid" "mdadmConf" ] [ "boot" "swraid" "mdadmConf" ])
+  ];
 
-  options.boot.initrd.services.swraid = {
-    enable = (lib.mkEnableOption (lib.mdDoc "swraid support using mdadm")) // {
-      visible = false; # only has effect when the new stage 1 is in place
+
+  options.boot.swraid = {
+    enable = lib.mkEnableOption (lib.mdDoc "swraid support using mdadm") // {
+      description = lib.mdDoc ''
+        Whether to enable support for Linux MD RAID arrays.
+
+        When this is enabled, mdadm will be added to the system path,
+        and MD RAID arrays will be detected and activated
+        automatically, both in stage-1 (initramfs) and in stage-2 (the
+        final NixOS system).
+
+        This should be enabled if you want to be able to access and/or
+        boot from MD RAID arrays. {command}`nixos-generate-config`
+        should detect it correctly in the standard installation
+        procedure.
+      '';
+      default = lib.versionOlder config.system.stateVersion "23.11";
+      defaultText = lib.mdDoc "`true` if stateVersion is older than 23.11";
     };
 
     mdadmConf = lib.mkOption {
-      description = lib.mdDoc "Contents of {file}`/etc/mdadm.conf` in initrd.";
+      description = lib.mdDoc "Contents of {file}`/etc/mdadm.conf`.";
       type = lib.types.lines;
       default = "";
     };
   };
 
-  config = {
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ pkgs.mdadm ];
 
     services.udev.packages = [ pkgs.mdadm ];
 
     systemd.packages = [ pkgs.mdadm ];
 
-    boot.initrd.availableKernelModules = lib.mkIf (config.boot.initrd.systemd.enable -> cfg.enable) [ "md_mod" "raid0" "raid1" "raid10" "raid456" ];
+    boot.initrd = {
+      availableKernelModules = [ "md_mod" "raid0" "raid1" "raid10" "raid456" ];
+
+      extraUdevRulesCommands = lib.mkIf (!config.boot.initrd.systemd.enable) ''
+        cp -v ${pkgs.mdadm}/lib/udev/rules.d/*.rules $out/
+      '';
 
-    boot.initrd.extraUdevRulesCommands = lib.mkIf (!config.boot.initrd.systemd.enable) ''
-      cp -v ${pkgs.mdadm}/lib/udev/rules.d/*.rules $out/
-    '';
+      extraUtilsCommands = ''
+        # Add RAID mdadm tool.
+        copy_bin_and_libs ${pkgs.mdadm}/sbin/mdadm
+        copy_bin_and_libs ${pkgs.mdadm}/sbin/mdmon
+      '';
 
-    boot.initrd.systemd = lib.mkIf cfg.enable {
-      contents."/etc/mdadm.conf" = lib.mkIf (cfg.mdadmConf != "") {
-        text = cfg.mdadmConf;
+      extraUtilsCommandsTest = ''
+        $out/bin/mdadm --version
+      '';
+
+      extraFiles."/etc/mdadm.conf".source = pkgs.writeText "mdadm.conf" config.boot.swraid.mdadmConf;
+
+      systemd = {
+        contents."/etc/mdadm.conf" = lib.mkIf (cfg.mdadmConf != "") {
+          text = cfg.mdadmConf;
+        };
+
+        packages = [ pkgs.mdadm ];
+        initrdBin = [ pkgs.mdadm ];
       };
 
-      packages = [ pkgs.mdadm ];
-      initrdBin = [ pkgs.mdadm ];
+      services.udev.packages = [ pkgs.mdadm ];
     };
-
-    boot.initrd.services.udev.packages = lib.mkIf cfg.enable [ pkgs.mdadm ];
   };
 }
diff --git a/nixpkgs/nixos/modules/testing/test-instrumentation.nix b/nixpkgs/nixos/modules/testing/test-instrumentation.nix
index 67fdc0ea4335..6dc4091bad17 100644
--- a/nixpkgs/nixos/modules/testing/test-instrumentation.nix
+++ b/nixpkgs/nixos/modules/testing/test-instrumentation.nix
@@ -115,10 +115,12 @@ in
       ShowStatus=no
       # Allow very slow start
       DefaultTimeoutStartSec=300
+      DefaultDeviceTimeoutSec=300
     '';
     systemd.user.extraConfig = ''
       # Allow very slow start
       DefaultTimeoutStartSec=300
+      DefaultDeviceTimeoutSec=300
     '';
 
     boot.initrd.systemd.extraConfig = config.systemd.extraConfig;
diff --git a/nixpkgs/nixos/modules/virtualisation/docker.nix b/nixpkgs/nixos/modules/virtualisation/docker.nix
index 046b8e2f7901..20f47a76c87b 100644
--- a/nixpkgs/nixos/modules/virtualisation/docker.nix
+++ b/nixpkgs/nixos/modules/virtualisation/docker.nix
@@ -158,6 +158,15 @@ in
         Docker package to be used in the module.
       '';
     };
+
+    extraPackages = mkOption {
+      type = types.listOf types.package;
+      default = [ ];
+      example = literalExpression "with pkgs; [ criu ]";
+      description = lib.mdDoc ''
+        Extra packages to add to PATH for the docker daemon process.
+      '';
+    };
   };
 
   ###### implementation
@@ -194,7 +203,8 @@ in
         };
 
         path = [ pkgs.kmod ] ++ optional (cfg.storageDriver == "zfs") pkgs.zfs
-          ++ optional cfg.enableNvidia pkgs.nvidia-docker;
+          ++ optional cfg.enableNvidia pkgs.nvidia-docker
+          ++ cfg.extraPackages;
       };
 
       systemd.sockets.docker = {
diff --git a/nixpkgs/nixos/modules/virtualisation/lxd.nix b/nixpkgs/nixos/modules/virtualisation/lxd.nix
index c06716e5eb60..e22ba9a0ae2c 100644
--- a/nixpkgs/nixos/modules/virtualisation/lxd.nix
+++ b/nixpkgs/nixos/modules/virtualisation/lxd.nix
@@ -85,6 +85,14 @@ in {
           considered failed and systemd will attempt to restart it.
         '';
       };
+
+      ui = {
+        enable = lib.mkEnableOption (lib.mdDoc ''
+          Enables the (experimental) LXD UI.
+        '');
+
+        package = mkPackageOption pkgs.lxd-unwrapped "ui" { };
+      };
     };
   };
 
@@ -143,6 +151,10 @@ in {
       path = [ pkgs.util-linux ]
         ++ optional cfg.zfsSupport config.boot.zfs.package;
 
+      environment = mkIf (cfg.ui.enable) {
+        "LXD_UI" = cfg.ui.package;
+      };
+
       serviceConfig = {
         ExecStart = "@${cfg.package}/bin/lxd lxd --group lxd";
         ExecStartPost = "${cfg.package}/bin/lxd waitready --timeout=${cfg.startTimeout}";
@@ -177,7 +189,7 @@ in {
       "fs.inotify.max_queued_events" = 1048576;
       "fs.inotify.max_user_instances" = 1048576;
       "fs.inotify.max_user_watches" = 1048576;
-      "vm.max_map_count" = 262144;
+      "vm.max_map_count" = 262144; # TODO: Default vm.max_map_count has been increased system-wide
       "kernel.dmesg_restrict" = 1;
       "net.ipv4.neigh.default.gc_thresh3" = 8192;
       "net.ipv6.neigh.default.gc_thresh3" = 8192;
diff --git a/nixpkgs/nixos/modules/virtualisation/nixos-containers.nix b/nixpkgs/nixos/modules/virtualisation/nixos-containers.nix
index c3949564d4bd..5df9942dbc04 100644
--- a/nixpkgs/nixos/modules/virtualisation/nixos-containers.nix
+++ b/nixpkgs/nixos/modules/virtualisation/nixos-containers.nix
@@ -800,14 +800,14 @@ in
       # declarative containers
       ++ (mapAttrsToList (name: cfg: nameValuePair "container@${name}" (let
           containerConfig = cfg // (
-          if cfg.enableTun then
+          optionalAttrs cfg.enableTun
             {
               allowedDevices = cfg.allowedDevices
                 ++ [ { node = "/dev/net/tun"; modifier = "rw"; } ];
               additionalCapabilities = cfg.additionalCapabilities
                 ++ [ "CAP_NET_ADMIN" ];
             }
-          else {});
+          );
         in
           recursiveUpdate unit {
             preStart = preStartScript containerConfig;
@@ -817,7 +817,7 @@ in
             unitConfig.RequiresMountsFor = lib.optional (!containerConfig.ephemeral) "${stateDirectory}/%i";
             environment.root = if containerConfig.ephemeral then "/run/nixos-containers/%i" else "${stateDirectory}/%i";
           } // (
-          if containerConfig.autoStart then
+          optionalAttrs containerConfig.autoStart
             {
               wantedBy = [ "machines.target" ];
               wants = [ "network.target" ];
@@ -828,7 +828,7 @@ in
               ];
               restartIfChanged = true;
             }
-          else {})
+          )
       )) config.containers)
     ));
 
diff --git a/nixpkgs/nixos/modules/virtualisation/podman/default.nix b/nixpkgs/nixos/modules/virtualisation/podman/default.nix
index c3fae4bac41b..ec0b713e58b3 100644
--- a/nixpkgs/nixos/modules/virtualisation/podman/default.nix
+++ b/nixpkgs/nixos/modules/virtualisation/podman/default.nix
@@ -206,6 +206,11 @@ in
 
       systemd.user.sockets.podman.wantedBy = [ "sockets.target" ];
 
+      systemd.timers.podman-prune.timerConfig = lib.mkIf cfg.autoPrune.enable {
+        Persistent = true;
+        RandomizedDelaySec = 1800;
+      };
+
       systemd.tmpfiles.packages = [
         # The /run/podman rule interferes with our podman group, so we remove
         # it and let the systemd socket logic take care of it.
diff --git a/nixpkgs/nixos/modules/virtualisation/proxmox-image.nix b/nixpkgs/nixos/modules/virtualisation/proxmox-image.nix
index b5d4ecd02683..62778f2626f8 100644
--- a/nixpkgs/nixos/modules/virtualisation/proxmox-image.nix
+++ b/nixpkgs/nixos/modules/virtualisation/proxmox-image.nix
@@ -69,6 +69,34 @@ with lib;
           VM name
         '';
       };
+      additionalSpace = mkOption {
+        type = types.str;
+        default = "512M";
+        example = "2048M";
+        description = lib.mdDoc ''
+          additional disk space to be added to the image if diskSize "auto"
+          is used.
+        '';
+      };
+      bootSize = mkOption {
+        type = types.str;
+        default = "256M";
+        example = "512M";
+        description = lib.mdDoc ''
+          Size of the boot partition. Is only used if partitionTableType is
+          either "efi" or "hybrid".
+        '';
+      };
+      diskSize = mkOption {
+        type = types.str;
+        default = "auto";
+        example = "20480";
+        description = lib.mdDoc ''
+          The size of the disk, in megabytes.
+          if "auto" size is calculated based on the contents copied to it and
+          additionalSpace is taken into account.
+        '';
+      };
       net0 = mkOption {
         type = types.commas;
         default = "virtio=00:00:00:00:00:00,bridge=vmbr0,firewall=1";
@@ -98,10 +126,12 @@ with lib;
     qemuExtraConf = mkOption {
       type = with types; attrsOf (oneOf [ str int ]);
       default = {};
-      example = literalExpression ''{
-        cpu = "host";
-        onboot = 1;
-      }'';
+      example = literalExpression ''
+        {
+          cpu = "host";
+          onboot = 1;
+        }
+      '';
       description = lib.mdDoc ''
         Additional options appended to qemu-server.conf
       '';
@@ -167,7 +197,7 @@ with lib;
     ];
     system.build.VMA = import ../../lib/make-disk-image.nix {
       name = "proxmox-${cfg.filenameSuffix}";
-      inherit partitionTableType;
+      inherit (cfg) partitionTableType;
       postVM = let
         # Build qemu with PVE's patch that adds support for the VMA format
         vma = (pkgs.qemu_kvm.override {
@@ -234,6 +264,7 @@ with lib;
         mkdir -p $out/nix-support
         echo "file vma $out/vzdump-qemu-${cfg.filenameSuffix}.vma.zst" >> $out/nix-support/hydra-build-products
       '';
+      inherit (cfg.qemuConf) additionalSpace diskSize bootSize;
       format = "raw";
       inherit config lib pkgs;
     };
diff --git a/nixpkgs/nixos/modules/virtualisation/qemu-vm.nix b/nixpkgs/nixos/modules/virtualisation/qemu-vm.nix
index 5cbc648b7a9c..d0a5ddd87ccf 100644
--- a/nixpkgs/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixpkgs/nixos/modules/virtualisation/qemu-vm.nix
@@ -18,6 +18,8 @@ let
 
   qemu = cfg.qemu.package;
 
+  hostPkgs = cfg.host.pkgs;
+
   consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles;
 
   driveOpts = { ... }: {
@@ -81,53 +83,50 @@ let
 
   drivesCmdLine = drives: concatStringsSep "\\\n    " (imap1 driveCmdline drives);
 
-
-  # Creates a device name from a 1-based a numerical index, e.g.
-  # * `driveDeviceName 1` -> `/dev/vda`
-  # * `driveDeviceName 2` -> `/dev/vdb`
-  driveDeviceName = idx:
-    let letter = elemAt lowerChars (idx - 1);
-    in if cfg.qemu.diskInterface == "scsi" then
-      "/dev/sd${letter}"
-    else
-      "/dev/vd${letter}";
-
-  lookupDriveDeviceName = driveName: driveList:
-    (findSingle (drive: drive.name == driveName)
-      (throw "Drive ${driveName} not found")
-      (throw "Multiple drives named ${driveName}") driveList).device;
-
-  addDeviceNames =
-    imap1 (idx: drive: drive // { device = driveDeviceName idx; });
-
   # Shell script to start the VM.
   startVM =
     ''
-      #! ${cfg.host.pkgs.runtimeShell}
+      #! ${hostPkgs.runtimeShell}
 
-      export PATH=${makeBinPath [ cfg.host.pkgs.coreutils ]}''${PATH:+:}$PATH
+      export PATH=${makeBinPath [ hostPkgs.coreutils ]}''${PATH:+:}$PATH
 
       set -e
 
+      # Create an empty ext4 filesystem image. A filesystem image does not
+      # contain a partition table but just a filesystem.
+      createEmptyFilesystemImage() {
+        local name=$1
+        local size=$2
+        local temp=$(mktemp)
+        ${qemu}/bin/qemu-img create -f raw "$temp" "$size"
+        ${hostPkgs.e2fsprogs}/bin/mkfs.ext4 -L ${rootFilesystemLabel} "$temp"
+        ${qemu}/bin/qemu-img convert -f raw -O qcow2 "$temp" "$name"
+        rm "$temp"
+      }
+
       NIX_DISK_IMAGE=$(readlink -f "''${NIX_DISK_IMAGE:-${toString config.virtualisation.diskImage}}") || test -z "$NIX_DISK_IMAGE"
 
       if test -n "$NIX_DISK_IMAGE" && ! test -e "$NIX_DISK_IMAGE"; then
           echo "Disk image do not exist, creating the virtualisation disk image..."
-          # If we are using a bootloader and default filesystems layout.
-          # We have to reuse the system image layout as a backing image format (CoW)
-          # So we can write on the top of it.
-
-          # If we are not using the default FS layout, potentially, we are interested into
-          # performing operations in postDeviceCommands or at early boot on the raw device.
-          # We can still boot through QEMU direct kernel boot feature.
-
-          # CoW prevent size to be attributed to an image.
-          # FIXME: raise this issue to upstream.
-          ${qemu}/bin/qemu-img create \
-          ${concatStringsSep " \\\n" ([ "-f qcow2" ]
-          ++ optional (cfg.useBootLoader && cfg.useDefaultFilesystems) "-F qcow2 -b ${systemImage}/nixos.qcow2"
-          ++ optional (!(cfg.useBootLoader && cfg.useDefaultFilesystems)) "-o size=${toString config.virtualisation.diskSize}M"
-          ++ [ ''"$NIX_DISK_IMAGE"'' ])}
+
+          ${if (cfg.useBootLoader && cfg.useDefaultFilesystems) then ''
+            # Create a writable qcow2 image using the systemImage as a backing
+            # image.
+
+            # CoW prevent size to be attributed to an image.
+            # FIXME: raise this issue to upstream.
+            ${qemu}/bin/qemu-img create \
+              -f qcow2 \
+              -b ${systemImage}/nixos.qcow2 \
+              -F qcow2 \
+              "$NIX_DISK_IMAGE"
+          '' else if cfg.useDefaultFilesystems then ''
+            createEmptyFilesystemImage "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
+          '' else ''
+            # Create an empty disk image without a filesystem.
+            ${qemu}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
+          ''
+          }
           echo "Virtualisation disk image created."
       fi
 
@@ -145,16 +144,17 @@ let
           else ''
             (
               cd ${builtins.storeDir}
-              ${pkgs.erofs-utils}/bin/mkfs.erofs \
+              ${hostPkgs.erofs-utils}/bin/mkfs.erofs \
                 --force-uid=0 \
                 --force-gid=0 \
+                -L ${nixStoreFilesystemLabel} \
                 -U eb176051-bd15-49b7-9e6b-462e0b467019 \
                 -T 0 \
                 --exclude-regex="$(
-                  <${pkgs.closureInfo { rootPaths = [ config.system.build.toplevel regInfo ]; }}/store-paths \
+                  <${hostPkgs.closureInfo { rootPaths = [ config.system.build.toplevel regInfo ]; }}/store-paths \
                     sed -e 's^.*/^^g' \
                   | cut -c -10 \
-                  | ${pkgs.python3}/bin/python ${./includes-to-excludes.py} )" \
+                  | ${hostPkgs.python3}/bin/python ${./includes-to-excludes.py} )" \
                 "$TMPDIR"/store.img \
                 . \
                 </dev/null >/dev/null
@@ -166,6 +166,16 @@ let
       # Create a directory for exchanging data with the VM.
       mkdir -p "$TMPDIR/xchg"
 
+      ${lib.optionalString cfg.useHostCerts
+      ''
+        mkdir -p "$TMPDIR/certs"
+        if [ -e "$NIX_SSL_CERT_FILE" ]; then
+          cp -L "$NIX_SSL_CERT_FILE" "$TMPDIR"/certs/ca-certificates.crt
+        else
+          echo \$NIX_SSL_CERT_FILE should point to a valid file if virtualisation.useHostCerts is enabled.
+        fi
+      ''}
+
       ${lib.optionalString cfg.useEFIBoot
       ''
         # Expose EFI variables, it's useful even when we are not using a bootloader (!).
@@ -216,7 +226,20 @@ let
     '';
 
 
-  regInfo = pkgs.closureInfo { rootPaths = config.virtualisation.additionalPaths; };
+  regInfo = hostPkgs.closureInfo { rootPaths = config.virtualisation.additionalPaths; };
+
+  # Use well-defined and persistent filesystem labels to identify block devices.
+  rootFilesystemLabel = "nixos";
+  espFilesystemLabel = "ESP"; # Hard-coded by make-disk-image.nix
+  nixStoreFilesystemLabel = "nix-store";
+
+  # The root drive is a raw disk which does not necessarily contain a
+  # filesystem or partition table. It thus cannot be identified via the typical
+  # persistent naming schemes (e.g. /dev/disk/by-{label, uuid, partlabel,
+  # partuuid}. Instead, supply a well-defined and persistent serial attribute
+  # via QEMU. Inside the running system, the disk can then be identified via
+  # the /dev/disk/by-id scheme.
+  rootDriveSerialAttr = "root";
 
   # System image is akin to a complete NixOS install with
   # a boot partition and root partition.
@@ -225,6 +248,7 @@ let
     additionalPaths = [ regInfo ];
     format = "qcow2";
     onlyNixStore = false;
+    label = rootFilesystemLabel;
     partitionTableType = selectPartitionTableLayout { inherit (cfg) useDefaultFilesystems useEFIBoot; };
     # Bootloader should be installed on the system image only if we are booting through bootloaders.
     # Though, if a user is not using our default filesystems, it is possible to not have any ESP
@@ -247,6 +271,7 @@ let
     additionalPaths = [ regInfo ];
     format = "qcow2";
     onlyNixStore = true;
+    label = nixStoreFilesystemLabel;
     partitionTableType = "none";
     installBootLoader = false;
     touchEFIVars = false;
@@ -255,28 +280,6 @@ let
     copyChannel = false;
   };
 
-  bootConfiguration =
-    if cfg.useDefaultFilesystems
-    then
-      if cfg.useBootLoader
-      then
-        if cfg.useEFIBoot then "efi_bootloading_with_default_fs"
-        else "legacy_bootloading_with_default_fs"
-      else
-        if cfg.directBoot.enable then "direct_boot_with_default_fs"
-        else "custom"
-    else
-      "custom";
-  suggestedRootDevice = {
-    "efi_bootloading_with_default_fs" = "${cfg.bootLoaderDevice}2";
-    "legacy_bootloading_with_default_fs" = "${cfg.bootLoaderDevice}1";
-    "direct_boot_with_default_fs" = cfg.bootLoaderDevice;
-    # This will enforce a NixOS module type checking error
-    # to ask explicitly the user to set a rootDevice.
-    # As it will look like `rootDevice = lib.mkDefault null;` after
-    # all "computations".
-    "custom" = null;
-  }.${bootConfiguration};
 in
 
 {
@@ -343,44 +346,39 @@ in
     virtualisation.bootLoaderDevice =
       mkOption {
         type = types.path;
-        default = lookupDriveDeviceName "root" cfg.qemu.drives;
-        defaultText = literalExpression ''lookupDriveDeviceName "root" cfg.qemu.drives'';
-        example = "/dev/vda";
+        default = "/dev/disk/by-id/virtio-${rootDriveSerialAttr}";
+        defaultText = literalExpression ''/dev/disk/by-id/virtio-${rootDriveSerialAttr}'';
+        example = "/dev/disk/by-id/virtio-boot-loader-device";
         description =
           lib.mdDoc ''
-            The disk to be used for the boot filesystem.
-            By default, it is the same disk as the root filesystem.
+            The path (inside th VM) to the device to boot from when legacy booting.
           '';
         };
 
     virtualisation.bootPartition =
       mkOption {
         type = types.nullOr types.path;
-        default = if cfg.useEFIBoot then "${cfg.bootLoaderDevice}1" else null;
-        defaultText = literalExpression ''if cfg.useEFIBoot then "''${cfg.bootLoaderDevice}1" else null'';
-        example = "/dev/vda1";
+        default = if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null;
+        defaultText = literalExpression ''if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null'';
+        example = "/dev/disk/by-label/esp";
         description =
           lib.mdDoc ''
-            The boot partition to be used to mount /boot filesystem.
-            In legacy boots, this should be null.
-            By default, in EFI boot, it is the first partition of the boot device.
+            The path (inside the VM) to the device containing the EFI System Partition (ESP).
+
+            If you are *not* booting from a UEFI firmware, this value is, by
+            default, `null`. The ESP is mounted under `/boot`.
           '';
       };
 
     virtualisation.rootDevice =
       mkOption {
         type = types.nullOr types.path;
-        example = "/dev/vda2";
+        default = "/dev/disk/by-label/${rootFilesystemLabel}";
+        defaultText = literalExpression ''/dev/disk/by-label/${rootFilesystemLabel}'';
+        example = "/dev/disk/by-label/nixos";
         description =
           lib.mdDoc ''
-            The disk or partition to be used for the root filesystem.
-            By default (read the source code for more details):
-
-            - under EFI with a bootloader: 2nd partition of the boot disk
-            - in legacy boot with a bootloader: 1st partition of the boot disk
-            - in direct boot (i.e. without a bootloader): whole disk
-
-            In case you are not using a default boot device or a default filesystem, you have to set explicitly your root device.
+            The path (inside the VM) to the device containing the root filesystem.
           '';
       };
 
@@ -658,7 +656,7 @@ in
       package =
         mkOption {
           type = types.package;
-          default = cfg.host.pkgs.qemu_kvm;
+          default = hostPkgs.qemu_kvm;
           defaultText = literalExpression "config.virtualisation.host.pkgs.qemu_kvm";
           example = literalExpression "pkgs.qemu_test";
           description = lib.mdDoc "QEMU package to use.";
@@ -711,7 +709,6 @@ in
         mkOption {
           type = types.listOf (types.submodule driveOpts);
           description = lib.mdDoc "Drives passed to qemu.";
-          apply = addDeviceNames;
         };
 
       diskInterface =
@@ -890,7 +887,6 @@ in
           '';
       };
 
-
     virtualisation.bios =
       mkOption {
         type = types.nullOr types.package;
@@ -903,6 +899,17 @@ in
           '';
       };
 
+    virtualisation.useHostCerts =
+      mkOption {
+        type = types.bool;
+        default = false;
+        description =
+          lib.mdDoc ''
+            If enabled, when `NIX_SSL_CERT_FILE` is set on the host,
+            pass the CA certificates from the host to the VM.
+          '';
+      };
+
   };
 
   config = {
@@ -975,29 +982,11 @@ in
     # FIXME: make a sense of this mess wrt to multiple ESP present in the system, probably use boot.efiSysMountpoint?
     boot.loader.grub.device = mkVMOverride (if cfg.useEFIBoot then "nodev" else cfg.bootLoaderDevice);
     boot.loader.grub.gfxmodeBios = with cfg.resolution; "${toString x}x${toString y}";
-    virtualisation.rootDevice = mkDefault suggestedRootDevice;
 
     boot.initrd.kernelModules = optionals (cfg.useNixStoreImage && !cfg.writableStore) [ "erofs" ];
 
     boot.loader.supportsInitrdSecrets = mkIf (!cfg.useBootLoader) (mkVMOverride false);
 
-    boot.initrd.extraUtilsCommands = lib.mkIf (cfg.useDefaultFilesystems && !config.boot.initrd.systemd.enable)
-      ''
-        # We need mke2fs in the initrd.
-        copy_bin_and_libs ${pkgs.e2fsprogs}/bin/mke2fs
-      '';
-
-    boot.initrd.postDeviceCommands = lib.mkIf (cfg.useDefaultFilesystems && !config.boot.initrd.systemd.enable)
-      ''
-        # If the disk image appears to be empty, run mke2fs to
-        # initialise.
-        FSTYPE=$(blkid -o value -s TYPE ${cfg.rootDevice} || true)
-        PARTTYPE=$(blkid -o value -s PTTYPE ${cfg.rootDevice} || true)
-        if test -z "$FSTYPE" -a -z "$PARTTYPE"; then
-            mke2fs -t ext4 ${cfg.rootDevice}
-        fi
-      '';
-
     boot.initrd.postMountCommands = lib.mkIf (!config.boot.initrd.systemd.enable)
       ''
         # Mark this as a NixOS machine.
@@ -1055,8 +1044,14 @@ in
         source = ''"''${SHARED_DIR:-$TMPDIR/xchg}"'';
         target = "/tmp/shared";
       };
+      certs = mkIf cfg.useHostCerts {
+        source = ''"$TMPDIR"/certs'';
+        target = "/etc/ssl/certs";
+      };
     };
 
+    security.pki.installCACerts = mkIf cfg.useHostCerts false;
+
     virtualisation.qemu.networkingOptions =
       let
         forwardingOptions = flip concatMapStrings cfg.forwardPorts
@@ -1112,6 +1107,7 @@ in
         driveExtraOpts.cache = "writeback";
         driveExtraOpts.werror = "report";
         deviceExtraOpts.bootindex = "1";
+        deviceExtraOpts.serial = rootDriveSerialAttr;
       }])
       (mkIf cfg.useNixStoreImage [{
         name = "nix-store";
@@ -1154,7 +1150,6 @@ in
         } else {
           device = cfg.rootDevice;
           fsType = "ext4";
-          autoFormat = true;
         });
         "/tmp" = lib.mkIf config.boot.tmp.useTmpfs {
           device = "tmpfs";
@@ -1164,7 +1159,7 @@ in
           options = [ "mode=1777" "strictatime" "nosuid" "nodev" "size=${toString config.boot.tmp.tmpfsSize}" ];
         };
         "/nix/${if cfg.writableStore then ".ro-store" else "store"}" = lib.mkIf cfg.useNixStoreImage {
-          device = "${lookupDriveDeviceName "nix-store" cfg.qemu.drives}";
+          device = "/dev/disk/by-label/${nixStoreFilesystemLabel}";
           neededForBoot = true;
           options = [ "ro" ];
         };
@@ -1174,7 +1169,7 @@ in
           neededForBoot = true;
         };
         "/boot" = lib.mkIf (cfg.useBootLoader && cfg.bootPartition != null) {
-          device = cfg.bootPartition; # 1 for e.g. `vda1`, as created in `systemImage`
+          device = cfg.bootPartition;
           fsType = "vfat";
           noCheck = true; # fsck fails on a r/o filesystem
         };
@@ -1213,14 +1208,14 @@ in
 
     services.qemuGuest.enable = cfg.qemu.guestAgent.enable;
 
-    system.build.vm = cfg.host.pkgs.runCommand "nixos-vm" {
+    system.build.vm = hostPkgs.runCommand "nixos-vm" {
       preferLocalBuild = true;
       meta.mainProgram = "run-${config.system.name}-vm";
     }
       ''
         mkdir -p $out/bin
         ln -s ${config.system.build.toplevel} $out/system
-        ln -s ${cfg.host.pkgs.writeScript "run-nixos-vm" startVM} $out/bin/run-${config.system.name}-vm
+        ln -s ${hostPkgs.writeScript "run-nixos-vm" startVM} $out/bin/run-${config.system.name}-vm
       '';
 
     # When building a regular system configuration, override whatever
diff --git a/nixpkgs/nixos/modules/virtualisation/vmware-guest.nix b/nixpkgs/nixos/modules/virtualisation/vmware-guest.nix
index b8f0a4cf668e..6880a257c2be 100644
--- a/nixpkgs/nixos/modules/virtualisation/vmware-guest.nix
+++ b/nixpkgs/nixos/modules/virtualisation/vmware-guest.nix
@@ -24,12 +24,12 @@ in
 
   config = mkIf cfg.enable {
     assertions = [ {
-      assertion = pkgs.stdenv.hostPlatform.isx86;
+      assertion = pkgs.stdenv.hostPlatform.isx86 || pkgs.stdenv.hostPlatform.isAarch64;
       message = "VMWare guest is not currently supported on ${pkgs.stdenv.hostPlatform.system}";
     } ];
 
     boot.initrd.availableKernelModules = [ "mptspi" ];
-    boot.initrd.kernelModules = [ "vmw_pvscsi" ];
+    boot.initrd.kernelModules = lib.optionals pkgs.stdenv.hostPlatform.isx86 [ "vmw_pvscsi" ];
 
     environment.systemPackages = [ open-vm-tools ];
 
diff --git a/nixpkgs/nixos/tests/adguardhome.nix b/nixpkgs/nixos/tests/adguardhome.nix
index ec1cc1e497b5..9f8ddc33f57e 100644
--- a/nixpkgs/nixos/tests/adguardhome.nix
+++ b/nixpkgs/nixos/tests/adguardhome.nix
@@ -40,6 +40,67 @@
         };
       };
     };
+
+    dhcpConf = { lib, ... }: {
+      virtualisation.vlans = [ 1 ];
+
+      networking = {
+        # Configure static IP for DHCP server
+        useDHCP = false;
+        interfaces."eth1" = lib.mkForce {
+          useDHCP = false;
+          ipv4 = {
+            addresses = [{
+              address = "10.0.10.1";
+              prefixLength = 24;
+            }];
+
+            routes = [{
+              address = "10.0.10.0";
+              prefixLength = 24;
+            }];
+          };
+        };
+
+        # Required for DHCP
+        firewall.allowedUDPPorts = [ 67 68 ];
+      };
+
+      services.adguardhome = {
+        enable = true;
+        allowDHCP = true;
+        mutableSettings = false;
+        settings = {
+          schema_version = 0;
+          dns = {
+            bind_host = "0.0.0.0";
+            bootstrap_dns = "127.0.0.1";
+          };
+          dhcp = {
+            # This implicitly enables CAP_NET_RAW
+            enabled = true;
+            interface_name = "eth1";
+            local_domain_name = "lan";
+            dhcpv4 = {
+              gateway_ip = "10.0.10.1";
+              range_start = "10.0.10.100";
+              range_end = "10.0.10.101";
+              subnet_mask = "255.255.255.0";
+            };
+          };
+        };
+      };
+    };
+
+    client = { lib, ... }: {
+      virtualisation.vlans = [ 1 ];
+      networking = {
+        interfaces.eth1 = {
+          useDHCP = true;
+          ipv4.addresses = lib.mkForce [ ];
+        };
+      };
+    };
   };
 
   testScript = ''
@@ -63,5 +124,13 @@
         mixedConf.systemctl("restart adguardhome.service")
         mixedConf.wait_for_unit("adguardhome.service")
         mixedConf.wait_for_open_port(3000)
+
+    with subtest("Testing successful DHCP start"):
+        dhcpConf.wait_for_unit("adguardhome.service")
+        client.wait_for_unit("network-online.target")
+        # Test IP assignment via DHCP
+        dhcpConf.wait_until_succeeds("ping -c 5 10.0.10.100")
+        # Test hostname resolution over DHCP-provided DNS
+        dhcpConf.wait_until_succeeds("ping -c 5 client.lan")
   '';
 }
diff --git a/nixpkgs/nixos/tests/all-tests.nix b/nixpkgs/nixos/tests/all-tests.nix
index e597a26f31bb..3b4a39f5ff96 100644
--- a/nixpkgs/nixos/tests/all-tests.nix
+++ b/nixpkgs/nixos/tests/all-tests.nix
@@ -87,7 +87,9 @@ in {
   # Testing the test driver
   nixos-test-driver = {
     extra-python-packages = handleTest ./nixos-test-driver/extra-python-packages.nix {};
+    lib-extend = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./nixos-test-driver/lib-extend.nix {};
     node-name = runTest ./nixos-test-driver/node-name.nix;
+    busybox = runTest ./nixos-test-driver/busybox.nix;
   };
 
   # NixOS vm tests and non-vm unit tests
@@ -107,8 +109,10 @@ in {
   allTerminfo = handleTest ./all-terminfo.nix {};
   alps = handleTest ./alps.nix {};
   amazon-init-shell = handleTest ./amazon-init-shell.nix {};
+  anuko-time-tracker = handleTest ./anuko-time-tracker.nix {};
   apcupsd = handleTest ./apcupsd.nix {};
-  apfs = handleTest ./apfs.nix {};
+  apfs = runTest ./apfs.nix;
+  appliance-repart-image = runTest ./appliance-repart-image.nix;
   apparmor = handleTest ./apparmor.nix {};
   atd = handleTest ./atd.nix {};
   atop = handleTest ./atop.nix {};
@@ -136,6 +140,7 @@ in {
   borgbackup = handleTest ./borgbackup.nix {};
   botamusique = handleTest ./botamusique.nix {};
   bpf = handleTestOn ["x86_64-linux" "aarch64-linux"] ./bpf.nix {};
+  bpftune = handleTest ./bpftune.nix {};
   breitbandmessung = handleTest ./breitbandmessung.nix {};
   brscan5 = handleTest ./brscan5.nix {};
   btrbk = handleTest ./btrbk.nix {};
@@ -150,6 +155,7 @@ in {
   cage = handleTest ./cage.nix {};
   cagebreak = handleTest ./cagebreak.nix {};
   calibre-web = handleTest ./calibre-web.nix {};
+  calibre-server = handleTest ./calibre-server.nix {};
   cassandra_3_0 = handleTest ./cassandra.nix { testPackage = pkgs.cassandra_3_0; };
   cassandra_3_11 = handleTest ./cassandra.nix { testPackage = pkgs.cassandra_3_11; };
   cassandra_4 = handleTest ./cassandra.nix { testPackage = pkgs.cassandra_4; };
@@ -199,6 +205,7 @@ in {
   couchdb = handleTest ./couchdb.nix {};
   cri-o = handleTestOn ["aarch64-linux" "x86_64-linux"] ./cri-o.nix {};
   cups-pdf = handleTest ./cups-pdf.nix {};
+  curl-impersonate = handleTest ./curl-impersonate.nix {};
   custom-ca = handleTest ./custom-ca.nix {};
   croc = handleTest ./croc.nix {};
   darling = handleTest ./darling.nix {};
@@ -253,6 +260,8 @@ in {
   etebase-server = handleTest ./etebase-server.nix {};
   etesync-dav = handleTest ./etesync-dav.nix {};
   evcc = handleTest ./evcc.nix {};
+  fail2ban = handleTest ./fail2ban.nix { };
+  fakeroute = handleTest ./fakeroute.nix {};
   fancontrol = handleTest ./fancontrol.nix {};
   fcitx5 = handleTest ./fcitx5 {};
   fenics = handleTest ./fenics.nix {};
@@ -262,6 +271,7 @@ in {
   firefox-devedition = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-devedition; };
   firefox-esr    = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-esr; }; # used in `tested` job
   firefox-esr-102 = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-esr-102; };
+  firefox-esr-115 = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-esr-115; };
   firejail = handleTest ./firejail.nix {};
   firewall = handleTest ./firewall.nix { nftables = false; };
   firewall-nftables = handleTest ./firewall.nix { nftables = true; };
@@ -306,12 +316,15 @@ in {
   gonic = handleTest ./gonic.nix {};
   google-oslogin = handleTest ./google-oslogin {};
   gotify-server = handleTest ./gotify-server.nix {};
+  gotosocial = runTest ./web-apps/gotosocial.nix;
   grafana = handleTest ./grafana {};
   grafana-agent = handleTest ./grafana-agent.nix {};
   graphite = handleTest ./graphite.nix {};
   graylog = handleTest ./graylog.nix {};
   grocy = handleTest ./grocy.nix {};
   grub = handleTest ./grub.nix {};
+  guacamole-client = handleTest ./guacamole-client.nix {};
+  guacamole-server = handleTest ./guacamole-server.nix {};
   gvisor = handleTest ./gvisor.nix {};
   hadoop = import ./hadoop { inherit handleTestOn; package=pkgs.hadoop; };
   hadoop_3_2 = import ./hadoop { inherit handleTestOn; package=pkgs.hadoop_3_2; };
@@ -328,6 +341,7 @@ in {
   hbase3 = handleTest ./hbase.nix { package=pkgs.hbase3; };
   hedgedoc = handleTest ./hedgedoc.nix {};
   herbstluftwm = handleTest ./herbstluftwm.nix {};
+  homepage-dashboard = handleTest ./homepage-dashboard.nix {};
   installed-tests = pkgs.recurseIntoAttrs (handleTest ./installed-tests {});
   invidious = handleTest ./invidious.nix {};
   oci-containers = handleTestOn ["aarch64-linux" "x86_64-linux"] ./oci-containers.nix {};
@@ -427,6 +441,7 @@ in {
   lxd = handleTest ./lxd.nix {};
   lxd-nftables = handleTest ./lxd-nftables.nix {};
   lxd-image-server = handleTest ./lxd-image-server.nix {};
+  lxd-ui = handleTest ./lxd-ui.nix {};
   #logstash = handleTest ./logstash.nix {};
   lorri = handleTest ./lorri/default.nix {};
   maddy = discoverTests (import ./maddy { inherit handleTest; });
@@ -520,11 +535,12 @@ in {
   nginx-http3 = handleTest ./nginx-http3.nix {};
   nginx-modsecurity = handleTest ./nginx-modsecurity.nix {};
   nginx-njs = handleTest ./nginx-njs.nix {};
+  nginx-proxyprotocol = handleTest ./nginx-proxyprotocol {};
   nginx-pubhtml = handleTest ./nginx-pubhtml.nix {};
   nginx-sandbox = handleTestOn ["x86_64-linux"] ./nginx-sandbox.nix {};
   nginx-sso = handleTest ./nginx-sso.nix {};
+  nginx-status-page = handleTest ./nginx-status-page.nix {};
   nginx-variants = handleTest ./nginx-variants.nix {};
-  nginx-proxyprotocol = handleTest ./nginx-proxyprotocol {};
   nifi = handleTestOn ["x86_64-linux"] ./web-apps/nifi.nix {};
   nitter = handleTest ./nitter.nix {};
   nix-ld = handleTest ./nix-ld.nix {};
@@ -558,12 +574,14 @@ in {
   openstack-image-metadata = (handleTestOn ["x86_64-linux"] ./openstack-image.nix {}).metadata or {};
   openstack-image-userdata = (handleTestOn ["x86_64-linux"] ./openstack-image.nix {}).userdata or {};
   opentabletdriver = handleTest ./opentabletdriver.nix {};
+  opentelemetry-collector = handleTest ./opentelemetry-collector.nix {};
   owncast = handleTest ./owncast.nix {};
   outline = handleTest ./outline.nix {};
   image-contents = handleTest ./image-contents.nix {};
   openvscode-server = handleTest ./openvscode-server.nix {};
   orangefs = handleTest ./orangefs.nix {};
   os-prober = handleTestOn ["x86_64-linux"] ./os-prober.nix {};
+  osquery = handleTestOn ["x86_64-linux"] ./osquery.nix {};
   osrm-backend = handleTest ./osrm-backend.nix {};
   overlayfs = handleTest ./overlayfs.nix {};
   pacemaker = handleTest ./pacemaker.nix {};
@@ -585,14 +603,15 @@ in {
   peertube = handleTestOn ["x86_64-linux"] ./web-apps/peertube.nix {};
   peroxide = handleTest ./peroxide.nix {};
   pgadmin4 = handleTest ./pgadmin4.nix {};
+  pgbouncer = handleTest ./pgbouncer.nix {};
   pgjwt = handleTest ./pgjwt.nix {};
   pgmanage = handleTest ./pgmanage.nix {};
   phosh = handleTest ./phosh.nix {};
   photoprism = handleTest ./photoprism.nix {};
   php = handleTest ./php {};
-  php80 = handleTest ./php { php = pkgs.php80; };
   php81 = handleTest ./php { php = pkgs.php81; };
   php82 = handleTest ./php { php = pkgs.php82; };
+  php83 = handleTest ./php { php = pkgs.php83; };
   phylactery = handleTest ./web-apps/phylactery.nix {};
   pict-rs = handleTest ./pict-rs.nix {};
   pinnwand = handleTest ./pinnwand.nix {};
@@ -640,8 +659,10 @@ in {
   pulseaudio = discoverTests (import ./pulseaudio.nix);
   qboot = handleTestOn ["x86_64-linux" "i686-linux"] ./qboot.nix {};
   qemu-vm-restrictnetwork = handleTest ./qemu-vm-restrictnetwork.nix {};
+  qemu-vm-volatile-root = runTest ./qemu-vm-volatile-root.nix;
   quorum = handleTest ./quorum.nix {};
   quake3 = handleTest ./quake3.nix {};
+  qownnotes = handleTest ./qownnotes.nix {};
   rabbitmq = handleTest ./rabbitmq.nix {};
   radarr = handleTest ./radarr.nix {};
   radicale = handleTest ./radicale.nix {};
@@ -665,6 +686,7 @@ in {
   samba = handleTest ./samba.nix {};
   samba-wsdd = handleTest ./samba-wsdd.nix {};
   sanoid = handleTest ./sanoid.nix {};
+  scaphandre = handleTest ./scaphandre.nix {};
   schleuder = handleTest ./schleuder.nix {};
   sddm = handleTest ./sddm.nix {};
   seafile = handleTest ./seafile.nix {};
@@ -679,6 +701,7 @@ in {
   shiori = handleTest ./shiori.nix {};
   signal-desktop = handleTest ./signal-desktop.nix {};
   simple = handleTest ./simple.nix {};
+  sing-box = handleTest ./sing-box.nix {};
   slurm = handleTest ./slurm.nix {};
   smokeping = handleTest ./smokeping.nix {};
   snapcast = handleTest ./snapcast.nix {};
@@ -697,6 +720,7 @@ in {
   sssd-ldap = handleTestOn ["x86_64-linux"] ./sssd-ldap.nix {};
   stargazer = runTest ./web-servers/stargazer.nix;
   starship = handleTest ./starship.nix {};
+  static-web-server = handleTest ./web-servers/static-web-server.nix {};
   step-ca = handleTestOn ["x86_64-linux"] ./step-ca.nix {};
   stratis = handleTest ./stratis {};
   strongswan-swanctl = handleTest ./strongswan-swanctl.nix {};
@@ -709,6 +733,7 @@ in {
   switchTest = handleTest ./switch-test.nix {};
   sympa = handleTest ./sympa.nix {};
   syncthing = handleTest ./syncthing.nix {};
+  syncthing-no-settings = handleTest ./syncthing-no-settings.nix {};
   syncthing-init = handleTest ./syncthing-init.nix {};
   syncthing-relay = handleTest ./syncthing-relay.nix {};
   systemd = handleTest ./systemd.nix {};
@@ -744,10 +769,12 @@ in {
   systemd-networkd-vrf = handleTest ./systemd-networkd-vrf.nix {};
   systemd-no-tainted = handleTest ./systemd-no-tainted.nix {};
   systemd-nspawn = handleTest ./systemd-nspawn.nix {};
+  systemd-nspawn-configfile = handleTest ./systemd-nspawn-configfile.nix {};
   systemd-oomd = handleTest ./systemd-oomd.nix {};
   systemd-portabled = handleTest ./systemd-portabled.nix {};
   systemd-repart = handleTest ./systemd-repart.nix {};
   systemd-shutdown = handleTest ./systemd-shutdown.nix {};
+  systemd-sysupdate = runTest ./systemd-sysupdate.nix;
   systemd-timesyncd = handleTest ./systemd-timesyncd.nix {};
   systemd-user-tmpfiles-rules = handleTest ./systemd-user-tmpfiles-rules.nix {};
   systemd-misc = handleTest ./systemd-misc.nix {};
@@ -785,6 +812,8 @@ in {
   tuptime = handleTest ./tuptime.nix {};
   turbovnc-headless-server = handleTest ./turbovnc-headless-server.nix {};
   tuxguitar = handleTest ./tuxguitar.nix {};
+  twingate = runTest ./twingate.nix;
+  typesense = handleTest ./typesense.nix {};
   ucarp = handleTest ./ucarp.nix {};
   udisks2 = handleTest ./udisks2.nix {};
   ulogd = handleTest ./ulogd.nix {};
@@ -812,6 +841,7 @@ in {
   victoriametrics = handleTest ./victoriametrics.nix {};
   vikunja = handleTest ./vikunja.nix {};
   virtualbox = handleTestOn ["x86_64-linux"] ./virtualbox.nix {};
+  vscode-remote-ssh = handleTestOn ["x86_64-linux"] ./vscode-remote-ssh.nix {};
   vscodium = discoverTests (import ./vscodium.nix);
   vsftpd = handleTest ./vsftpd.nix {};
   warzone2100 = handleTest ./warzone2100.nix {};
diff --git a/nixpkgs/nixos/tests/anuko-time-tracker.nix b/nixpkgs/nixos/tests/anuko-time-tracker.nix
new file mode 100644
index 000000000000..18c3bf5cf695
--- /dev/null
+++ b/nixpkgs/nixos/tests/anuko-time-tracker.nix
@@ -0,0 +1,17 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "anuko-time-tracker";
+  meta = {
+    maintainers = with pkgs.lib.maintainers; [ michaelshmitty ];
+  };
+  nodes = {
+    machine = {
+      services.anuko-time-tracker.enable = true;
+    };
+  };
+  testScript = ''
+    start_all()
+    machine.wait_for_unit("phpfpm-anuko-time-tracker")
+    machine.wait_for_open_port(80);
+    machine.wait_until_succeeds("curl -s --fail -L http://localhost/time.php | grep 'Anuko Time Tracker'")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/apfs.nix b/nixpkgs/nixos/tests/apfs.nix
index 9fe689951c78..15ed5aa7f573 100644
--- a/nixpkgs/nixos/tests/apfs.nix
+++ b/nixpkgs/nixos/tests/apfs.nix
@@ -1,8 +1,8 @@
-import ./make-test-python.nix ({ pkgs, ... }: {
+{ lib, ... }: {
   name = "apfs";
-  meta.maintainers = with pkgs.lib.maintainers; [ Luflosi ];
+  meta.maintainers = with lib.maintainers; [ Luflosi ];
 
-  nodes.machine = { pkgs, ... }: {
+  nodes.machine = {
     virtualisation.emptyDiskImages = [ 1024 ];
 
     boot.supportedFilesystems = [ "apfs" ];
@@ -62,4 +62,4 @@ import ./make-test-python.nix ({ pkgs, ... }: {
           "apfsck /dev/vdb",
       )
   '';
-})
+}
diff --git a/nixpkgs/nixos/tests/appliance-repart-image.nix b/nixpkgs/nixos/tests/appliance-repart-image.nix
new file mode 100644
index 000000000000..3f256db84621
--- /dev/null
+++ b/nixpkgs/nixos/tests/appliance-repart-image.nix
@@ -0,0 +1,116 @@
+# Tests building and running a GUID Partition Table (GPT) appliance image.
+# "Appliance" here means that the image does not contain the normal NixOS
+# infrastructure of a system profile and cannot be re-built via
+# `nixos-rebuild`.
+
+{ lib, ... }:
+
+let
+  rootPartitionLabel = "root";
+
+  bootLoaderConfigPath = "/loader/entries/nixos.conf";
+  kernelPath = "/EFI/nixos/kernel.efi";
+  initrdPath = "/EFI/nixos/initrd.efi";
+in
+{
+  name = "appliance-gpt-image";
+
+  meta.maintainers = with lib.maintainers; [ nikstur ];
+
+  nodes.machine = { config, lib, pkgs, ... }: {
+
+    imports = [ ../modules/image/repart.nix ];
+
+    virtualisation.directBoot.enable = false;
+    virtualisation.mountHostNixStore = false;
+    virtualisation.useEFIBoot = true;
+
+    # Disable boot loaders because we install one "manually".
+    # TODO(raitobezarius): revisit this when #244907 lands
+    boot.loader.grub.enable = false;
+
+    virtualisation.fileSystems = lib.mkForce {
+      "/" = {
+        device = "/dev/disk/by-partlabel/${rootPartitionLabel}";
+        fsType = "ext4";
+      };
+    };
+
+    image.repart = {
+      name = "appliance-gpt-image";
+      partitions = {
+        "esp" = {
+          contents =
+            let
+              efiArch = config.nixpkgs.hostPlatform.efiArch;
+            in
+            {
+              "/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source =
+                "${pkgs.systemd}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi";
+
+              # TODO: create an abstraction for Boot Loader Specification (BLS) entries.
+              "${bootLoaderConfigPath}".source = pkgs.writeText "nixos.conf" ''
+                title NixOS
+                linux ${kernelPath}
+                initrd ${initrdPath}
+                options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
+              '';
+
+              "${kernelPath}".source =
+                "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}";
+
+              "${initrdPath}".source =
+                "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
+            };
+          repartConfig = {
+            Type = "esp";
+            Format = "vfat";
+            # Minimize = "guess" seems to not work very vell for vfat
+            # partitons. It's better to set a sensible default instead. The
+            # aarch64 kernel seems to generally be a little bigger than the
+            # x86_64 kernel. To stay on the safe side, leave some more slack
+            # for every platform other than x86_64.
+            SizeMinBytes = if config.nixpkgs.hostPlatform.isx86_64 then "64M" else "96M";
+          };
+        };
+        "root" = {
+          storePaths = [ config.system.build.toplevel ];
+          repartConfig = {
+            Type = "root";
+            Format = config.fileSystems."/".fsType;
+            Label = rootPartitionLabel;
+            Minimize = "guess";
+          };
+        };
+      };
+    };
+  };
+
+  testScript = { nodes, ... }: ''
+    import os
+    import subprocess
+    import tempfile
+
+    tmp_disk_image = tempfile.NamedTemporaryFile()
+
+    subprocess.run([
+      "${nodes.machine.virtualisation.qemu.package}/bin/qemu-img",
+      "create",
+      "-f",
+      "qcow2",
+      "-b",
+      "${nodes.machine.system.build.image}/image.raw",
+      "-F",
+      "raw",
+      tmp_disk_image.name,
+    ])
+
+    # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image.
+    os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name
+
+    bootctl_status = machine.succeed("bootctl status")
+    assert "${bootLoaderConfigPath}" in bootctl_status
+    assert "${kernelPath}" in bootctl_status
+    assert "${initrdPath}" in bootctl_status
+  '';
+}
diff --git a/nixpkgs/nixos/tests/atuin.nix b/nixpkgs/nixos/tests/atuin.nix
index 9f23a3cf6713..3164c83c683d 100644
--- a/nixpkgs/nixos/tests/atuin.nix
+++ b/nixpkgs/nixos/tests/atuin.nix
@@ -14,6 +14,8 @@ in
     server =
       { ... }:
       {
+        services.postgresql.enable = true;
+
         services.atuin = {
           enable = true;
           port = testPort;
diff --git a/nixpkgs/nixos/tests/bcachefs.nix b/nixpkgs/nixos/tests/bcachefs.nix
index 68ac49d0a6a6..0385e098997f 100644
--- a/nixpkgs/nixos/tests/bcachefs.nix
+++ b/nixpkgs/nixos/tests/bcachefs.nix
@@ -23,7 +23,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
         "keyctl link @u @s",
         "echo password | bcachefs format --encrypted --metadata_replicas 2 --label vtest /dev/vdb1 /dev/vdb2",
         "echo password | bcachefs unlock /dev/vdb1",
-        "mount -t bcachefs /dev/vdb1:/dev/vdb2 /tmp/mnt",
+        "echo password | mount -t bcachefs /dev/vdb1:/dev/vdb2 /tmp/mnt",
         "udevadm settle",
         "bcachefs fs usage /tmp/mnt",
         "umount /tmp/mnt",
diff --git a/nixpkgs/nixos/tests/binary-cache.nix b/nixpkgs/nixos/tests/binary-cache.nix
index 0809e59e5a11..bc1c6fb9a267 100644
--- a/nixpkgs/nixos/tests/binary-cache.nix
+++ b/nixpkgs/nixos/tests/binary-cache.nix
@@ -1,16 +1,14 @@
-import ./make-test-python.nix ({ lib, ... }:
-
-with lib;
+import ./make-test-python.nix ({ lib, pkgs, ... }:
 
 {
   name = "binary-cache";
-  meta.maintainers = with maintainers; [ thomasjm ];
+  meta.maintainers = with lib.maintainers; [ thomasjm ];
 
   nodes.machine =
     { pkgs, ... }: {
       imports = [ ../modules/installer/cd-dvd/channel.nix ];
-      environment.systemPackages = with pkgs; [python3];
-      system.extraDependencies = with pkgs; [hello.inputDerivation];
+      environment.systemPackages = [ pkgs.python3 ];
+      system.extraDependencies = [ pkgs.hello.inputDerivation ];
       nix.extraOptions = ''
         experimental-features = nix-command
       '';
diff --git a/nixpkgs/nixos/tests/bpftune.nix b/nixpkgs/nixos/tests/bpftune.nix
new file mode 100644
index 000000000000..c17bbcd11092
--- /dev/null
+++ b/nixpkgs/nixos/tests/bpftune.nix
@@ -0,0 +1,20 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
+
+  name = "bpftune";
+
+  meta = {
+    maintainers = with lib.maintainers; [ nickcao ];
+  };
+
+  nodes = {
+    machine = { pkgs, ... }: {
+      services.bpftune.enable = true;
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("bpftune.service")
+    machine.wait_for_console_text("bpftune works")
+  '';
+
+})
diff --git a/nixpkgs/nixos/tests/budgie.nix b/nixpkgs/nixos/tests/budgie.nix
index b1b0e618c884..34ee1e303de9 100644
--- a/nixpkgs/nixos/tests/budgie.nix
+++ b/nixpkgs/nixos/tests/budgie.nix
@@ -32,7 +32,15 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     in
     ''
       with subtest("Wait for login"):
-          machine.wait_for_x()
+          # wait_for_x() checks graphical-session.target, which is expected to be
+          # inactive on Budgie before #228946 (i.e. systemd managed gnome-session) is
+          # done on upstream.
+          # https://github.com/BuddiesOfBudgie/budgie-desktop/blob/v10.7.2/src/session/budgie-desktop.in#L16
+          #
+          # Previously this was unconditionally touched by xsessionWrapper but was
+          # changed in #233981 (we have Budgie:GNOME in XDG_CURRENT_DESKTOP).
+          # machine.wait_for_x()
+          machine.wait_until_succeeds('journalctl -t gnome-session-binary --grep "Entering running state"')
           machine.wait_for_file("${user.home}/.Xauthority")
           machine.succeed("xauth merge ${user.home}/.Xauthority")
 
diff --git a/nixpkgs/nixos/tests/buildkite-agents.nix b/nixpkgs/nixos/tests/buildkite-agents.nix
index 2c5593323e87..a5abfdb5e2e5 100644
--- a/nixpkgs/nixos/tests/buildkite-agents.nix
+++ b/nixpkgs/nixos/tests/buildkite-agents.nix
@@ -1,10 +1,8 @@
-import ./make-test-python.nix ({ pkgs, ... }:
+import ./make-test-python.nix ({ lib, pkgs, ... }:
 
 {
   name = "buildkite-agent";
-  meta = with pkgs.lib.maintainers; {
-    maintainers = [ flokli ];
-  };
+  meta.maintainers = with lib.maintainers; [ flokli ];
 
   nodes.machine = { pkgs, ... }: {
     services.buildkite-agents = {
diff --git a/nixpkgs/nixos/tests/caddy.nix b/nixpkgs/nixos/tests/caddy.nix
index c4abd33d0395..ed88f08739e8 100644
--- a/nixpkgs/nixos/tests/caddy.nix
+++ b/nixpkgs/nixos/tests/caddy.nix
@@ -20,6 +20,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
           }
         }
       '';
+      services.caddy.enableReload = true;
 
       specialisation.etag.configuration = {
         services.caddy.extraConfig = lib.mkForce ''
@@ -54,9 +55,9 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
   testScript = { nodes, ... }:
     let
-      etagSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/etag";
-      justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/config-reload";
-      multipleConfigs = "${nodes.webserver.config.system.build.toplevel}/specialisation/multiple-configs";
+      etagSystem = "${nodes.webserver.system.build.toplevel}/specialisation/etag";
+      justReloadSystem = "${nodes.webserver.system.build.toplevel}/specialisation/config-reload";
+      multipleConfigs = "${nodes.webserver.system.build.toplevel}/specialisation/multiple-configs";
     in
     ''
       url = "http://localhost/example.html"
@@ -96,6 +97,8 @@ import ./make-test-python.nix ({ pkgs, ... }: {
               "${justReloadSystem}/bin/switch-to-configuration test >&2"
           )
           webserver.wait_for_open_port(8080)
+          webserver.fail("journalctl -u caddy | grep -q -i stopped")
+          webserver.succeed("journalctl -u caddy | grep -q -i reloaded")
 
       with subtest("multiple configs are correctly merged"):
           webserver.succeed(
diff --git a/nixpkgs/nixos/tests/cage.nix b/nixpkgs/nixos/tests/cage.nix
index db1b7854673d..3b49185124f3 100644
--- a/nixpkgs/nixos/tests/cage.nix
+++ b/nixpkgs/nixos/tests/cage.nix
@@ -11,7 +11,7 @@ import ./make-test-python.nix ({ pkgs, ...} :
   {
     imports = [ ./common/user-account.nix ];
 
-    fonts.fonts = with pkgs; [ dejavu_fonts ];
+    fonts.packages = with pkgs; [ dejavu_fonts ];
 
     services.cage = {
       enable = true;
diff --git a/nixpkgs/nixos/tests/calibre-server.nix b/nixpkgs/nixos/tests/calibre-server.nix
new file mode 100644
index 000000000000..4b1753aaa704
--- /dev/null
+++ b/nixpkgs/nixos/tests/calibre-server.nix
@@ -0,0 +1,104 @@
+{ system ? builtins.currentSystem
+, config ? { }
+, pkgs ? import ../.. { inherit system config; }
+}:
+
+let
+  inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
+  inherit (pkgs.lib) concatStringsSep maintainers mapAttrs mkMerge
+    removeSuffix splitString;
+
+  tests = {
+    default = {
+      calibreConfig = {};
+      calibreScript = ''
+        wait_for_unit("calibre-server.service")
+      '';
+    };
+    customLibrary = {
+      calibreConfig = {
+        libraries = [ "/var/lib/calibre-data" ];
+      };
+      calibreScript = ''
+        succeed("ls -la /var/lib/calibre-data")
+        wait_for_unit("calibre-server.service")
+      '';
+    };
+    multipleLibraries = {
+      calibreConfig = {
+        libraries = [ "/var/lib/calibre-data" "/var/lib/calibre-server" ];
+      };
+      calibreScript = ''
+        succeed("ls -la /var/lib/calibre-data")
+        succeed("ls -la /var/lib/calibre-server")
+        wait_for_unit("calibre-server.service")
+      '';
+    };
+    hostAndPort = {
+      calibreConfig = {
+        host = "127.0.0.1";
+        port = 8888;
+      };
+      calibreScript = ''
+        wait_for_unit("calibre-server.service")
+        wait_for_open_port(8888)
+        succeed("curl --fail http://127.0.0.1:8888")
+      '';
+    };
+    basicAuth = {
+      calibreConfig = {
+        host = "127.0.0.1";
+        port = 8888;
+        auth = {
+          enable = true;
+          mode = "basic";
+        };
+      };
+      calibreScript = ''
+        wait_for_unit("calibre-server.service")
+        wait_for_open_port(8888)
+        fail("curl --fail http://127.0.0.1:8888")
+      '';
+    };
+  };
+in
+mapAttrs
+  (test: testConfig: (makeTest (
+    let
+      nodeName = testConfig.nodeName or test;
+      calibreConfig = {
+        enable = true;
+        libraries = [ "/var/lib/calibre-server" ];
+      } // testConfig.calibreConfig or {};
+      librariesInitScript = path: ''
+        ${nodeName}.execute("touch /tmp/test.epub")
+        ${nodeName}.execute("zip -r /tmp/test.zip /tmp/test.epub")
+        ${nodeName}.execute("mkdir -p ${path}")
+        ${nodeName}.execute("calibredb add -d --with-library ${path} /tmp/test.zip")
+      '';
+    in
+    {
+      name = "calibre-server-${test}";
+
+      nodes.${nodeName} = mkMerge [{
+        environment.systemPackages = [ pkgs.zip ];
+        services.calibre-server = calibreConfig;
+      } testConfig.calibreProvider or { }];
+
+      testScript = ''
+        ${nodeName}.start()
+        ${concatStringsSep "\n" (map librariesInitScript calibreConfig.libraries)}
+        ${concatStringsSep "\n" (map (line:
+          if (builtins.substring 0 1 line == " " || builtins.substring 0 1 line == ")")
+          then line
+          else "${nodeName}.${line}"
+        ) (splitString "\n" (removeSuffix "\n" testConfig.calibreScript)))}
+        ${nodeName}.shutdown()
+      '';
+
+      meta = with maintainers; {
+        maintainers = [ gaelreyrol ];
+      };
+    }
+  )))
+  tests
diff --git a/nixpkgs/nixos/tests/common/auto-format-root-device.nix b/nixpkgs/nixos/tests/common/auto-format-root-device.nix
new file mode 100644
index 000000000000..56eecef2f411
--- /dev/null
+++ b/nixpkgs/nixos/tests/common/auto-format-root-device.nix
@@ -0,0 +1,29 @@
+# This is a test utility that automatically formats
+# `config.virtualisation.rootDevice` in the initrd.
+# Note that when you are using
+# `boot.initrd.systemd.enable = true`, you can use
+# `virtualisation.fileSystems."/".autoFormat = true;`
+# instead.
+
+{ config, pkgs, ... }:
+
+let
+  rootDevice = config.virtualisation.rootDevice;
+in
+{
+
+  boot.initrd.extraUtilsCommands = ''
+    # We need mke2fs in the initrd.
+    copy_bin_and_libs ${pkgs.e2fsprogs}/bin/mke2fs
+  '';
+
+  boot.initrd.postDeviceCommands = ''
+    # If the disk image appears to be empty, run mke2fs to
+    # initialise.
+    FSTYPE=$(blkid -o value -s TYPE ${rootDevice} || true)
+    PARTTYPE=$(blkid -o value -s PTTYPE ${rootDevice} || true)
+    if test -z "$FSTYPE" -a -z "$PARTTYPE"; then
+        mke2fs -t ext4 ${rootDevice}
+    fi
+  '';
+}
diff --git a/nixpkgs/nixos/tests/common/gpg-keyring.nix b/nixpkgs/nixos/tests/common/gpg-keyring.nix
new file mode 100644
index 000000000000..fb8d07b1183e
--- /dev/null
+++ b/nixpkgs/nixos/tests/common/gpg-keyring.nix
@@ -0,0 +1,21 @@
+{ pkgs, ... }:
+
+pkgs.runCommand "gpg-keyring" { nativeBuildInputs = [ pkgs.gnupg ]; } ''
+  mkdir -p $out
+  export GNUPGHOME=$out
+  cat > foo <<EOF
+    %echo Generating a basic OpenPGP key
+    %no-protection
+    Key-Type: EdDSA
+    Key-Curve: ed25519
+    Name-Real: Bob Foobar
+    Name-Email: bob@foo.bar
+    Expire-Date: 0
+    # Do a commit here, so that we can later print "done"
+    %commit
+    %echo done
+  EOF
+  gpg --batch --generate-key foo
+  rm $out/S.gpg-agent $out/S.gpg-agent.*
+  gpg --export bob@foo.bar -a > $out/pubkey.gpg
+''
diff --git a/nixpkgs/nixos/tests/common/resolver.nix b/nixpkgs/nixos/tests/common/resolver.nix
index 3ddf730668c4..609058a7374a 100644
--- a/nixpkgs/nixos/tests/common/resolver.nix
+++ b/nixpkgs/nixos/tests/common/resolver.nix
@@ -63,7 +63,7 @@
             matched = builtins.match "[ \t]+(${reHost})(.*)" str;
             continue = lib.singleton (lib.head matched)
                     ++ matchAliases (lib.last matched);
-          in if matched == null then [] else continue;
+          in lib.optional (matched != null) continue;
 
           matchLine = str: let
             result = builtins.match "[ \t]*(${reIp})[ \t]+(${reHost})(.*)" str;
diff --git a/nixpkgs/nixos/tests/coturn.nix b/nixpkgs/nixos/tests/coturn.nix
index 301b34b0da70..b44bf8d06e39 100644
--- a/nixpkgs/nixos/tests/coturn.nix
+++ b/nixpkgs/nixos/tests/coturn.nix
@@ -24,6 +24,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
       with subtest("works with static-auth-secret-file"):
           secretsfile.wait_for_unit("coturn.service")
+          secretsfile.wait_for_open_port(3478)
           secretsfile.succeed("grep 'some-very-secret-string' /run/coturn/turnserver.cfg")
           # Forbidden IP, fails:
           secretsfile.fail("${pkgs.coturn}/bin/turnutils_uclient -W some-very-secret-string 127.0.0.1 -DgX -e 127.0.0.1 -n 1 -c -y")
diff --git a/nixpkgs/nixos/tests/cups-pdf.nix b/nixpkgs/nixos/tests/cups-pdf.nix
index 957b0296a755..5602193b0408 100644
--- a/nixpkgs/nixos/tests/cups-pdf.nix
+++ b/nixpkgs/nixos/tests/cups-pdf.nix
@@ -4,7 +4,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
   nodes.machine = { pkgs, ... }: {
     imports = [ ./common/user-account.nix ];
     environment.systemPackages = [ pkgs.poppler_utils ];
-    fonts.fonts = [ pkgs.dejavu_fonts ];  # yields more OCR-able pdf
+    fonts.packages = [ pkgs.dejavu_fonts ];  # yields more OCR-able pdf
     services.printing.cups-pdf.enable = true;
     services.printing.cups-pdf.instances = {
       opt = {};
diff --git a/nixpkgs/nixos/tests/curl-impersonate.nix b/nixpkgs/nixos/tests/curl-impersonate.nix
new file mode 100644
index 000000000000..7954e9e5584c
--- /dev/null
+++ b/nixpkgs/nixos/tests/curl-impersonate.nix
@@ -0,0 +1,157 @@
+/*
+  Test suite for curl-impersonate
+
+  Abstract:
+    Uses the test suite from the curl-impersonate source repo which:
+
+    1. Performs requests with libcurl and captures the TLS client-hello
+       packets with tcpdump to compare against known-good signatures
+    2. Spins up an nghttpd2 server to test client HTTP/2 headers against
+       known-good headers
+
+    See https://github.com/lwthiker/curl-impersonate/tree/main/tests/signatures
+    for details.
+
+  Notes:
+    - We need to have our own web server running because the tests expect to be able
+      to hit domains like wikipedia.org and the sandbox has no internet
+    - We need to be able to do (verifying) TLS handshakes without internet access.
+      We do that by creating a trusted CA and issuing a cert that includes
+      all of the test domains as subject-alternative names and then spoofs the
+      hostnames in /etc/hosts.
+*/
+
+import ./make-test-python.nix ({ pkgs, lib, ... }: let
+  # Update with domains in TestImpersonate.TEST_URLS if needed from:
+  # https://github.com/lwthiker/curl-impersonate/blob/main/tests/test_impersonate.py
+  domains = [
+    "www.wikimedia.org"
+    "www.wikipedia.org"
+    "www.mozilla.org"
+    "www.apache.org"
+    "www.kernel.org"
+    "git-scm.com"
+  ];
+
+  tls-certs = let
+    # Configure CA with X.509 v3 extensions that would be trusted by curl
+    ca-cert-conf = pkgs.writeText "curl-impersonate-ca.cnf" ''
+      basicConstraints = critical, CA:TRUE
+      subjectKeyIdentifier = hash
+      authorityKeyIdentifier = keyid:always, issuer:always
+      keyUsage = critical, cRLSign, digitalSignature, keyCertSign
+    '';
+
+    # Configure leaf certificate with X.509 v3 extensions that would be trusted
+    # by curl and set subject-alternative names for test domains
+    tls-cert-conf = pkgs.writeText "curl-impersonate-tls.cnf" ''
+      basicConstraints = critical, CA:FALSE
+      subjectKeyIdentifier = hash
+      authorityKeyIdentifier = keyid:always, issuer:always
+      keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, keyAgreement
+      extendedKeyUsage = critical, serverAuth
+      subjectAltName = @alt_names
+
+      [alt_names]
+      ${lib.concatStringsSep "\n" (lib.imap0 (idx: domain: "DNS.${toString idx} = ${domain}") domains)}
+    '';
+  in pkgs.runCommand "curl-impersonate-test-certs" {
+    nativeBuildInputs = [ pkgs.openssl ];
+  } ''
+    # create CA certificate and key
+    openssl req -newkey rsa:4096 -keyout ca-key.pem -out ca-csr.pem -nodes -subj '/CN=curl-impersonate-ca.nixos.test'
+    openssl x509 -req -sha512 -in ca-csr.pem -key ca-key.pem -out ca.pem -extfile ${ca-cert-conf} -days 36500
+    openssl x509 -in ca.pem -text
+
+    # create server certificate and key
+    openssl req -newkey rsa:4096 -keyout key.pem -out csr.pem -nodes -subj '/CN=curl-impersonate.nixos.test'
+    openssl x509 -req -sha512 -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile ${tls-cert-conf} -days 36500
+    openssl x509 -in cert.pem -text
+
+    # output CA cert and server cert and key
+    mkdir -p $out
+    cp key.pem cert.pem ca.pem $out
+  '';
+
+  # Test script
+  curl-impersonate-test = let
+    # Build miniature libcurl client used by test driver
+    minicurl = pkgs.runCommandCC "minicurl" {
+      buildInputs = [ pkgs.curl ];
+    } ''
+      mkdir -p $out/bin
+      $CC -Wall -Werror -o $out/bin/minicurl ${pkgs.curl-impersonate.src}/tests/minicurl.c `curl-config --libs`
+    '';
+  in pkgs.writeShellScript "curl-impersonate-test" ''
+    set -euxo pipefail
+
+    # Test driver requirements
+    export PATH="${with pkgs; lib.makeBinPath [
+      bash
+      coreutils
+      python3Packages.pytest
+      nghttp2
+      tcpdump
+    ]}"
+    export PYTHONPATH="${with pkgs.python3Packages; makePythonPath [
+      pyyaml
+      pytest-asyncio
+      dpkt
+    ]}"
+
+    # Prepare test root prefix
+    mkdir -p usr/{bin,lib}
+    cp -rs ${pkgs.curl-impersonate}/* ${minicurl}/* usr/
+
+    cp -r ${pkgs.curl-impersonate.src}/tests ./
+
+    # Run tests
+    cd tests
+    pytest . --install-dir ../usr --capture-interface eth1
+  '';
+in {
+  name = "curl-impersonate";
+
+  meta = with lib.maintainers; {
+    maintainers = [ lilyinstarlight ];
+  };
+
+  nodes = {
+    web = { nodes, pkgs, lib, config, ... }: {
+      networking.firewall.allowedTCPPorts = [ 80 443 ];
+
+      services = {
+        nginx = {
+          enable = true;
+          virtualHosts."curl-impersonate.nixos.test" = {
+            default = true;
+            addSSL = true;
+            sslCertificate = "${tls-certs}/cert.pem";
+            sslCertificateKey = "${tls-certs}/key.pem";
+          };
+        };
+      };
+    };
+
+    curl = { nodes, pkgs, lib, config, ... }: {
+      networking.extraHosts = lib.concatStringsSep "\n" (map (domain: "${nodes.web.networking.primaryIPAddress}  ${domain}") domains);
+
+      security.pki.certificateFiles = [ "${tls-certs}/ca.pem" ];
+    };
+  };
+
+  testScript = { nodes, ... }: ''
+    start_all()
+
+    with subtest("Wait for network"):
+        web.wait_for_unit("network-online.target")
+        curl.wait_for_unit("network-online.target")
+
+    with subtest("Wait for web server"):
+        web.wait_for_unit("nginx.service")
+        web.wait_for_open_port(443)
+
+    with subtest("Run curl-impersonate tests"):
+        curl.succeed("${curl-impersonate-test}")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/deepin.nix b/nixpkgs/nixos/tests/deepin.nix
index 0fe93486b6cb..7b2e2430f31c 100644
--- a/nixpkgs/nixos/tests/deepin.nix
+++ b/nixpkgs/nixos/tests/deepin.nix
@@ -1,15 +1,15 @@
 import ./make-test-python.nix ({ pkgs, lib, ... }: {
   name = "deepin";
 
-  meta = with lib; {
-    maintainers = teams.deepin.members;
-  };
+  meta.maintainers = lib.teams.deepin.members;
 
   nodes.machine = { ... }: {
     imports = [
       ./common/user-account.nix
     ];
 
+    virtualisation.memorySize = 2048;
+
     services.xserver.enable = true;
 
     services.xserver.displayManager = {
diff --git a/nixpkgs/nixos/tests/evcc.nix b/nixpkgs/nixos/tests/evcc.nix
index b445735ede98..7ebdc6a6f5ab 100644
--- a/nixpkgs/nixos/tests/evcc.nix
+++ b/nixpkgs/nixos/tests/evcc.nix
@@ -46,7 +46,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
             type = "custom";
             status = {
               source = "script";
-              cmd = "/bin/sh -c 'echo charger status F'";
+              cmd = "/bin/sh -c 'echo charger status A'";
             };
             enabled = {
               source = "script";
diff --git a/nixpkgs/nixos/tests/fail2ban.nix b/nixpkgs/nixos/tests/fail2ban.nix
new file mode 100644
index 000000000000..c3708575b702
--- /dev/null
+++ b/nixpkgs/nixos/tests/fail2ban.nix
@@ -0,0 +1,18 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "fail2ban";
+
+  nodes.machine = _: {
+    services.fail2ban = {
+      enable = true;
+      bantime-increment.enable = true;
+    };
+
+    services.openssh.enable = true;
+  };
+
+  testScript = ''
+    machine.wait_for_unit("multi-user.target")
+
+    machine.wait_for_unit("fail2ban")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/fakeroute.nix b/nixpkgs/nixos/tests/fakeroute.nix
new file mode 100644
index 000000000000..37b174524ab8
--- /dev/null
+++ b/nixpkgs/nixos/tests/fakeroute.nix
@@ -0,0 +1,22 @@
+import ./make-test-python.nix ({ lib, pkgs, ...} : {
+  name = "fakeroute";
+  meta.maintainers = with lib.maintainers; [ rnhmjoj ];
+
+  nodes.machine = { ... }: {
+    imports = [ ../modules/profiles/minimal.nix ];
+    services.fakeroute.enable = true;
+    services.fakeroute.route =
+      [ "216.102.187.130" "4.0.1.122"
+        "198.116.142.34" "63.199.8.242"
+      ];
+    environment.systemPackages = [ pkgs.traceroute ];
+  };
+
+  testScript =
+    ''
+      start_all()
+      machine.wait_for_unit("fakeroute.service")
+      machine.succeed("traceroute 127.0.0.1 | grep -q 216.102.187.130")
+    '';
+})
+
diff --git a/nixpkgs/nixos/tests/fontconfig-default-fonts.nix b/nixpkgs/nixos/tests/fontconfig-default-fonts.nix
index 664afc9bf44c..d8c6ea0f721b 100644
--- a/nixpkgs/nixos/tests/fontconfig-default-fonts.nix
+++ b/nixpkgs/nixos/tests/fontconfig-default-fonts.nix
@@ -7,8 +7,8 @@ import ./make-test-python.nix ({ lib, ... }:
   ];
 
   nodes.machine = { config, pkgs, ... }: {
-    fonts.enableDefaultFonts = true; # Background fonts
-    fonts.fonts = with pkgs; [
+    fonts.enableDefaultPackages = true; # Background fonts
+    fonts.packages = with pkgs; [
       noto-fonts-emoji
       cantarell-fonts
       twitter-color-emoji
diff --git a/nixpkgs/nixos/tests/freshrss-http-auth.nix b/nixpkgs/nixos/tests/freshrss-http-auth.nix
new file mode 100644
index 000000000000..d0ec3da31689
--- /dev/null
+++ b/nixpkgs/nixos/tests/freshrss-http-auth.nix
@@ -0,0 +1,20 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
+  name = "freshrss";
+  meta.maintainers = with lib.maintainers; [ mattchrist ];
+
+  nodes.machine = { pkgs, ... }: {
+    services.freshrss = {
+      enable = true;
+      baseUrl = "http://localhost";
+      dataDir = "/srv/freshrss";
+      authType = "http_auth";
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("multi-user.target")
+    machine.wait_for_open_port(80)
+    response = machine.succeed("curl -vvv -s -H 'Host: freshrss' -H 'Remote-User: testuser' http://127.0.0.1:80/i/")
+    assert 'Account: testuser' in response, "http_auth method didn't work."
+  '';
+})
diff --git a/nixpkgs/nixos/tests/fsck.nix b/nixpkgs/nixos/tests/fsck.nix
index ec6bfa69ae84..31ed8bdf78c0 100644
--- a/nixpkgs/nixos/tests/fsck.nix
+++ b/nixpkgs/nixos/tests/fsck.nix
@@ -21,13 +21,17 @@ import ./make-test-python.nix {
     boot.initrd.systemd.enable = systemdStage1;
   };
 
-  testScript = ''
+  testScript =  { nodes, ...}:
+  let
+    rootDevice = nodes.machine.virtualisation.rootDevice;
+  in
+  ''
     machine.wait_for_unit("default.target")
 
     with subtest("root fs is fsckd"):
         machine.succeed("journalctl -b | grep '${if systemdStage1
-          then "fsck.*vda.*clean"
-          else "fsck.ext4.*/dev/vda"}'")
+          then "fsck.*${builtins.baseNameOf rootDevice}.*clean"
+          else "fsck.ext4.*${rootDevice}"}'")
 
     with subtest("mnt fs is fsckd"):
         machine.succeed("journalctl -b | grep 'fsck.*vdb.*clean'")
diff --git a/nixpkgs/nixos/tests/gitea.nix b/nixpkgs/nixos/tests/gitea.nix
index 2285bb5a4252..b747659de829 100644
--- a/nixpkgs/nixos/tests/gitea.nix
+++ b/nixpkgs/nixos/tests/gitea.nix
@@ -37,9 +37,25 @@ let
           package = giteaPackage;
           settings.service.DISABLE_REGISTRATION = true;
           settings."repository.signing".SIGNING_KEY = signingPrivateKeyId;
+          settings.actions.ENABLED = true;
         };
         environment.systemPackages = [ giteaPackage pkgs.gnupg pkgs.jq ];
         services.openssh.enable = true;
+
+        specialisation.runner = {
+          inheritParentConfig = true;
+
+          configuration.services.gitea-actions-runner.instances."test" = {
+            enable = true;
+            name = "ci";
+            url = "http://localhost:3000";
+            labels = [
+              # don't require docker/podman
+              "native:host"
+            ];
+            tokenFile = "/var/lib/gitea/runner_token";
+          };
+        };
       };
       client1 = { config, pkgs, ... }: {
         environment.systemPackages = [ pkgs.git ];
@@ -49,8 +65,9 @@ let
       };
     };
 
-    testScript = let
+    testScript = { nodes, ... }: let
       inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
+      serverSystem = nodes.server.system.build.toplevel;
     in ''
       GIT_SSH_COMMAND = "ssh -i $HOME/.ssh/privk -o StrictHostKeyChecking=no"
       REPO = "gitea@server:test/repo"
@@ -121,14 +138,18 @@ let
       client2.succeed(f"GIT_SSH_COMMAND='{GIT_SSH_COMMAND}' git clone {REPO}")
       client2.succeed('test "$(cat repo/testfile | xargs echo -n)" = "hello world"')
 
-      server.succeed(
+      server.wait_until_succeeds(
           'test "$(curl http://localhost:3000/api/v1/repos/test/repo/commits '
           + '-H "Accept: application/json" | jq length)" = "1"'
       )
 
-      client1.shutdown()
-      client2.shutdown()
-      server.shutdown()
+      with subtest("Testing runner registration"):
+          server.succeed(
+              "su -l gitea -c 'GITEA_WORK_DIR=/var/lib/gitea gitea actions generate-runner-token' | sed 's/^/TOKEN=/' | tee /var/lib/gitea/runner_token"
+          )
+          server.succeed("${serverSystem}/specialisation/runner/bin/switch-to-configuration test")
+          server.wait_for_unit("gitea-runner-test.service")
+          server.succeed("journalctl -o cat -u gitea-runner-test.service | grep -q 'Runner registered successfully'")
     '';
   });
 in
diff --git a/nixpkgs/nixos/tests/gnome-flashback.nix b/nixpkgs/nixos/tests/gnome-flashback.nix
index 70569db797ec..876d36477c1a 100644
--- a/nixpkgs/nixos/tests/gnome-flashback.nix
+++ b/nixpkgs/nixos/tests/gnome-flashback.nix
@@ -4,7 +4,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
 
   nodes.machine = { nodes, ... }:
     let
-      user = nodes.machine.config.users.users.alice;
+      user = nodes.machine.users.users.alice;
     in
 
     { imports = [ ./common/user-account.nix ];
@@ -27,12 +27,20 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
     };
 
   testScript = { nodes, ... }: let
-    user = nodes.machine.config.users.users.alice;
+    user = nodes.machine.users.users.alice;
     uid = toString user.uid;
     xauthority = "/run/user/${uid}/gdm/Xauthority";
   in ''
       with subtest("Login to GNOME Flashback with GDM"):
-          machine.wait_for_x()
+          # wait_for_x() checks graphical-session.target, which is expected to be
+          # inactive on gnome-flashback before #228946 (i.e. systemd managed
+          # gnome-session) is done.
+          # https://github.com/NixOS/nixpkgs/pull/208060
+          #
+          # Previously this was unconditionally touched by xsessionWrapper but was
+          # changed in #233981 (we have GNOME-Flashback:GNOME in XDG_CURRENT_DESKTOP).
+          # machine.wait_for_x()
+          machine.wait_until_succeeds('journalctl -t gnome-session-binary --grep "Entering running state"')
           # Wait for alice to be logged in"
           machine.wait_for_unit("default.target", "${user.name}")
           machine.wait_for_file("${xauthority}")
diff --git a/nixpkgs/nixos/tests/guacamole-server.nix b/nixpkgs/nixos/tests/guacamole-server.nix
new file mode 100644
index 000000000000..48194fddfb22
--- /dev/null
+++ b/nixpkgs/nixos/tests/guacamole-server.nix
@@ -0,0 +1,21 @@
+import ./make-test-python.nix ({pkgs, lib, ...}:
+{
+  name = "guacamole-server";
+
+  nodes = {
+    machine = {pkgs, ...}: {
+      services.guacamole-server = {
+        enable = true;
+        host = "0.0.0.0";
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+    machine.wait_for_unit("guacamole-server.service")
+    machine.wait_for_open_port(4822)
+  '';
+
+  meta.maintainers = [ lib.maintainers.drupol ];
+})
diff --git a/nixpkgs/nixos/tests/hibernate.nix b/nixpkgs/nixos/tests/hibernate.nix
index 24b4ed382c12..296aa9ba68b9 100644
--- a/nixpkgs/nixos/tests/hibernate.nix
+++ b/nixpkgs/nixos/tests/hibernate.nix
@@ -8,127 +8,48 @@
 
 with import ../lib/testing-python.nix { inherit system pkgs; };
 
-let
-  # System configuration of the installed system, which is used for the actual
-  # hibernate testing.
-  installedConfig = with pkgs.lib; {
-    imports = [
-      ../modules/testing/test-instrumentation.nix
-      ../modules/profiles/qemu-guest.nix
-      ../modules/profiles/minimal.nix
-    ];
-
-    hardware.enableAllFirmware = mkForce false;
-    documentation.nixos.enable = false;
-    boot.loader.grub.device = "/dev/vda";
-
-    systemd.services.backdoor.conflicts = [ "sleep.target" ];
-
-    powerManagement.resumeCommands = "systemctl --no-block restart backdoor.service";
-
-    fileSystems."/" = {
-      device = "/dev/vda2";
-      fsType = "ext3";
-    };
-    swapDevices = mkOverride 0 [ { device = "/dev/vda1"; } ];
-    boot.resumeDevice = mkIf systemdStage1 "/dev/vda1";
-    boot.initrd.systemd = mkIf systemdStage1 {
-      enable = true;
-      emergencyAccess = true;
-    };
-  };
-  installedSystem = (import ../lib/eval-config.nix {
-    inherit system;
-    modules = [ installedConfig ];
-  }).config.system.build.toplevel;
-in makeTest {
+makeTest {
   name = "hibernate";
 
   nodes = {
-    # System configuration used for installing the installedConfig from above.
     machine = { config, lib, pkgs, ... }: {
       imports = [
-        ../modules/profiles/installation-device.nix
-        ../modules/profiles/base.nix
+        ./common/auto-format-root-device.nix
       ];
 
-      nix.settings = {
-        substituters = lib.mkForce [];
-        hashed-mirrors = null;
-        connect-timeout = 1;
-      };
+      systemd.services.backdoor.conflicts = [ "sleep.target" ];
+      powerManagement.resumeCommands = "systemctl --no-block restart backdoor.service";
 
-      virtualisation.diskSize = 8 * 1024;
-      virtualisation.emptyDiskImages = [
-        # Small root disk for installer
-        512
-      ];
-      virtualisation.rootDevice = "/dev/vdb";
+      virtualisation.emptyDiskImages = [ (2 * config.virtualisation.memorySize) ];
+      virtualisation.useNixStoreImage = true;
+
+      swapDevices = lib.mkOverride 0 [ { device = "/dev/vdc"; options = [ "x-systemd.makefs" ]; } ];
+      boot.resumeDevice = "/dev/vdc";
+      boot.initrd.systemd.enable = systemdStage1;
     };
   };
 
-  # 9P doesn't support reconnection to virtio transport after a hibernation.
-  # Therefore, machine just hangs on any Nix store access.
-  # To avoid this, we install NixOS onto a temporary disk with everything we need
-  # included into the store.
-
-  testScript =
-    ''
-      def create_named_machine(name):
-          machine = create_machine(
-              {
-                  "qemuFlags": "-cpu max ${
-                    if system == "x86_64-linux" then "-m 1024"
-                    else "-m 768 -enable-kvm -machine virt,gic-version=host"}",
-                  "hdaInterface": "virtio",
-                  "hda": "vm-state-machine/machine.qcow2",
-                  "name": name,
-              }
-          )
-          driver.machines.append(machine)
-          return machine
-
-
-      # Install NixOS
-      machine.start()
-      machine.succeed(
-          # Partition /dev/vda
-          "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
-          + " mkpart primary linux-swap 1M 1024M"
-          + " mkpart primary ext2 1024M -1s",
-          "udevadm settle",
-          "mkfs.ext3 -L nixos /dev/vda2",
-          "mount LABEL=nixos /mnt",
-          "mkswap /dev/vda1 -L swap",
-          # Install onto /mnt
-          "nix-store --load-db < ${pkgs.closureInfo {rootPaths = [installedSystem];}}/registration",
-          "nixos-install --root /mnt --system ${installedSystem} --no-root-passwd --no-channel-copy >&2",
-      )
-      machine.shutdown()
-
-      # Start up
-      hibernate = create_named_machine("hibernate")
-
-      # Drop in file that checks if we un-hibernated properly (and not booted fresh)
-      hibernate.succeed(
-          "mkdir /run/test",
-          "mount -t ramfs -o size=1m ramfs /run/test",
-          "echo not persisted to disk > /run/test/suspended",
-      )
-
-      # Hibernate machine
-      hibernate.execute("systemctl hibernate >&2 &", check_return=False)
-      hibernate.wait_for_shutdown()
-
-      # Restore machine from hibernation, validate our ramfs file is there.
-      resume = create_named_machine("resume")
-      resume.start()
-      resume.succeed("grep 'not persisted to disk' /run/test/suspended")
-
-      # Ensure we don't restore from hibernation when booting again
-      resume.crash()
-      resume.wait_for_unit("default.target")
-      resume.fail("grep 'not persisted to disk' /run/test/suspended")
-    '';
+  testScript = ''
+    # Drop in file that checks if we un-hibernated properly (and not booted fresh)
+    machine.wait_for_unit("default.target")
+    machine.succeed(
+        "mkdir /run/test",
+        "mount -t ramfs -o size=1m ramfs /run/test",
+        "echo not persisted to disk > /run/test/suspended",
+    )
+
+    # Hibernate machine
+    machine.execute("systemctl hibernate >&2 &", check_return=False)
+    machine.wait_for_shutdown()
+
+    # Restore machine from hibernation, validate our ramfs file is there.
+    machine.start()
+    machine.succeed("grep 'not persisted to disk' /run/test/suspended")
+
+    # Ensure we don't restore from hibernation when booting again
+    machine.crash()
+    machine.wait_for_unit("default.target")
+    machine.fail("grep 'not persisted to disk' /run/test/suspended")
+  '';
 
 }
diff --git a/nixpkgs/nixos/tests/homepage-dashboard.nix b/nixpkgs/nixos/tests/homepage-dashboard.nix
new file mode 100644
index 000000000000..56e077f5ff6d
--- /dev/null
+++ b/nixpkgs/nixos/tests/homepage-dashboard.nix
@@ -0,0 +1,14 @@
+import ./make-test-python.nix ({ lib, ... }: {
+  name = "homepage-dashboard";
+  meta.maintainers = with lib.maintainers; [ jnsgruk ];
+
+  nodes.machine = { pkgs, ... }: {
+    services.homepage-dashboard.enable = true;
+  };
+
+  testScript = ''
+    machine.wait_for_unit("homepage-dashboard.service")
+    machine.wait_for_open_port(8082)
+    machine.succeed("curl --fail http://localhost:8082/")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/initrd-luks-empty-passphrase.nix b/nixpkgs/nixos/tests/initrd-luks-empty-passphrase.nix
index 521456e7e0b2..a846c120415d 100644
--- a/nixpkgs/nixos/tests/initrd-luks-empty-passphrase.nix
+++ b/nixpkgs/nixos/tests/initrd-luks-empty-passphrase.nix
@@ -14,6 +14,8 @@ in {
   name = "initrd-luks-empty-passphrase";
 
   nodes.machine = { pkgs, ... }: {
+    imports = lib.optionals (!systemdStage1) [ ./common/auto-format-root-device.nix ];
+
     virtualisation = {
       emptyDiskImages = [ 512 ];
       useBootLoader = true;
@@ -23,6 +25,7 @@ in {
       # the new root device is /dev/vdb
       # an empty 512MiB drive, containing no Nix store.
       mountHostNixStore = true;
+      fileSystems."/".autoFormat = lib.mkIf systemdStage1 true;
     };
 
     boot.loader.systemd-boot.enable = true;
diff --git a/nixpkgs/nixos/tests/initrd-network-ssh/default.nix b/nixpkgs/nixos/tests/initrd-network-ssh/default.nix
index 017de6882081..17b6c21ff1e9 100644
--- a/nixpkgs/nixos/tests/initrd-network-ssh/default.nix
+++ b/nixpkgs/nixos/tests/initrd-network-ssh/default.nix
@@ -1,12 +1,10 @@
-import ../make-test-python.nix ({ lib, ... }:
+import ../make-test-python.nix ({ lib, pkgs, ... }:
 
 {
   name = "initrd-network-ssh";
-  meta = with lib.maintainers; {
-    maintainers = [ willibutz emily ];
-  };
+  meta.maintainers = with lib.maintainers; [ willibutz emily ];
 
-  nodes = with lib; {
+  nodes = {
     server =
       { config, ... }:
       {
@@ -17,7 +15,7 @@ import ../make-test-python.nix ({ lib, ... }:
           enable = true;
           ssh = {
             enable = true;
-            authorizedKeys = [ (readFile ./id_ed25519.pub) ];
+            authorizedKeys = [ (lib.readFile ./id_ed25519.pub) ];
             port = 22;
             hostKeys = [ ./ssh_host_ed25519_key ];
           };
@@ -37,12 +35,12 @@ import ../make-test-python.nix ({ lib, ... }:
       {
         environment.etc = {
           knownHosts = {
-            text = concatStrings [
+            text = lib.concatStrings [
               "server,"
-              "${toString (head (splitString " " (
-                toString (elemAt (splitString "\n" config.networking.extraHosts) 2)
+              "${toString (lib.head (lib.splitString " " (
+                toString (lib.elemAt (lib.splitString "\n" config.networking.extraHosts) 2)
               )))} "
-              "${readFile ./ssh_host_ed25519_key.pub}"
+              "${lib.readFile ./ssh_host_ed25519_key.pub}"
             ];
           };
           sshKey = {
diff --git a/nixpkgs/nixos/tests/installed-tests/default.nix b/nixpkgs/nixos/tests/installed-tests/default.nix
index 78a6325a245e..e87edb2007e9 100644
--- a/nixpkgs/nixos/tests/installed-tests/default.nix
+++ b/nixpkgs/nixos/tests/installed-tests/default.nix
@@ -107,5 +107,6 @@ in
   malcontent = callInstalledTest ./malcontent.nix {};
   ostree = callInstalledTest ./ostree.nix {};
   pipewire = callInstalledTest ./pipewire.nix {};
+  upower = callInstalledTest ./upower.nix {};
   xdg-desktop-portal = callInstalledTest ./xdg-desktop-portal.nix {};
 }
diff --git a/nixpkgs/nixos/tests/installed-tests/upower.nix b/nixpkgs/nixos/tests/installed-tests/upower.nix
new file mode 100644
index 000000000000..a8e777a55527
--- /dev/null
+++ b/nixpkgs/nixos/tests/installed-tests/upower.nix
@@ -0,0 +1,9 @@
+{ pkgs, makeInstalledTest, ... }:
+
+makeInstalledTest {
+  tested = pkgs.upower;
+
+  testConfig = {
+    services.upower.enable = true;
+  };
+}
diff --git a/nixpkgs/nixos/tests/installer-systemd-stage-1.nix b/nixpkgs/nixos/tests/installer-systemd-stage-1.nix
index 05fb2b2ae89c..85155a6c682b 100644
--- a/nixpkgs/nixos/tests/installer-systemd-stage-1.nix
+++ b/nixpkgs/nixos/tests/installer-systemd-stage-1.nix
@@ -28,7 +28,7 @@
     simpleUefiGrubSpecialisation
     simpleUefiSystemdBoot
     stratisRoot
-    # swraid
+    swraid
     zfsroot
     ;
 
diff --git a/nixpkgs/nixos/tests/installer.nix b/nixpkgs/nixos/tests/installer.nix
index 1ac164f4b816..56ba85b76e6f 100644
--- a/nixpkgs/nixos/tests/installer.nix
+++ b/nixpkgs/nixos/tests/installer.nix
@@ -11,16 +11,20 @@ let
 
   # The configuration to install.
   makeConfig = { bootLoader, grubDevice, grubIdentifier, grubUseEfi
-               , extraConfig, forceGrubReinstallCount ? 0
+               , extraConfig, forceGrubReinstallCount ? 0, flake ? false
                }:
     pkgs.writeText "configuration.nix" ''
       { config, lib, pkgs, modulesPath, ... }:
 
       { imports =
           [ ./hardware-configuration.nix
-            <nixpkgs/nixos/modules/testing/test-instrumentation.nix>
+            ${if flake
+              then "" # Still included, but via installer/flake.nix
+              else "<nixpkgs/nixos/modules/testing/test-instrumentation.nix>"}
           ];
 
+        networking.hostName = "thatworked";
+
         documentation.enable = false;
 
         # To ensure that we can rebuild the grub configuration on the nixos-rebuild
@@ -67,7 +71,7 @@ let
   # partitions and filesystems.
   testScriptFun = { bootLoader, createPartitions, grubDevice, grubUseEfi
                   , grubIdentifier, preBootCommands, postBootCommands, extraConfig
-                  , testSpecialisationConfig
+                  , testSpecialisationConfig, testFlakeSwitch
                   }:
     let iface = "virtio";
         isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
@@ -86,9 +90,14 @@ let
 
       qemu_flags = {"qemuFlags": assemble_qemu_flags()}
 
+      import os
+
+      image_dir = machine.state_dir
+      disk_image = os.path.join(image_dir, "machine.qcow2")
+
       hd_flags = {
           "hdaInterface": "${iface}",
-          "hda": "vm-state-machine/machine.qcow2",
+          "hda": disk_image,
       }
       ${optionalString isEfi ''
         hd_flags.update(
@@ -232,6 +241,11 @@ let
       machine = create_machine_named("boot-after-rebuild-switch")
       ${preBootCommands}
       machine.wait_for_unit("network.target")
+
+      # Sanity check, is it the configuration.nix we generated?
+      hostname = machine.succeed("hostname").strip()
+      assert hostname == "thatworked"
+
       ${postBootCommands}
       machine.shutdown()
 
@@ -272,6 +286,84 @@ let
 
       ${postBootCommands}
       machine.shutdown()
+    ''
+    + optionalString testFlakeSwitch ''
+      ${preBootCommands}
+      machine.start()
+
+      with subtest("Configure system with flake"):
+        # TODO: evaluate as user?
+        machine.succeed("""
+          mkdir /root/my-config
+          mv /etc/nixos/hardware-configuration.nix /root/my-config/
+          mv /etc/nixos/secret /root/my-config/
+          rm /etc/nixos/configuration.nix
+        """)
+        machine.copy_from_host_via_shell(
+          "${makeConfig {
+               inherit bootLoader grubDevice grubIdentifier grubUseEfi extraConfig;
+               forceGrubReinstallCount = 1;
+               flake = true;
+            }}",
+          "/root/my-config/configuration.nix",
+        )
+        machine.copy_from_host_via_shell(
+          "${./installer/flake.nix}",
+          "/root/my-config/flake.nix",
+        )
+        machine.succeed("""
+          # for some reason the image does not have `pkgs.path`, so
+          # we use readlink to find a Nixpkgs source.
+          pkgs=$(readlink -f /nix/var/nix/profiles/per-user/root/channels)/nixos
+          if ! [[ -e $pkgs/pkgs/top-level/default.nix ]]; then
+            echo 1>&2 "$pkgs does not seem to be a nixpkgs source. Please fix the test so that pkgs points to a nixpkgs source.";
+            exit 1;
+          fi
+          sed -e s^@nixpkgs@^$pkgs^ -i /root/my-config/flake.nix
+        """)
+
+      with subtest("Switch to flake based config"):
+        machine.succeed("nixos-rebuild switch --flake /root/my-config#xyz")
+
+      ${postBootCommands}
+      machine.shutdown()
+
+      ${preBootCommands}
+      machine.start()
+
+      machine.wait_for_unit("multi-user.target")
+
+      with subtest("nix-channel command is not available anymore"):
+        machine.succeed("! which nix-channel")
+
+      # Note that the channel profile is still present on disk, but configured
+      # not to be used.
+      with subtest("builtins.nixPath is now empty"):
+        machine.succeed("""
+          [[ "[ ]" == "$(nix-instantiate builtins.nixPath --eval --expr)" ]]
+        """)
+
+      with subtest("<nixpkgs> does not resolve"):
+        machine.succeed("""
+          ! nix-instantiate '<nixpkgs>' --eval --expr
+        """)
+
+      with subtest("Evaluate flake config in fresh env without nix-channel"):
+        machine.succeed("nixos-rebuild switch --flake /root/my-config#xyz")
+
+      with subtest("Evaluate flake config in fresh env without channel profiles"):
+        machine.succeed("""
+          (
+            exec 1>&2
+            rm -v /root/.nix-channels
+            rm -vrf ~/.nix-defexpr
+            rm -vrf /nix/var/nix/profiles/per-user/root/channels*
+          )
+        """)
+        machine.succeed("nixos-rebuild switch --flake /root/my-config#xyz")
+
+      ${postBootCommands}
+      machine.shutdown()
     '';
 
 
@@ -282,11 +374,12 @@ let
     , grubDevice ? "/dev/vda", grubIdentifier ? "uuid", grubUseEfi ? false
     , enableOCR ? false, meta ? {}
     , testSpecialisationConfig ? false
+    , testFlakeSwitch ? false
     }:
     makeTest {
       inherit enableOCR;
       name = "installer-" + name;
-      meta = with pkgs.lib.maintainers; {
+      meta = {
         # put global maintainers here, individuals go into makeInstallerTest fkt call
         maintainers = (meta.maintainers or []);
       };
@@ -298,8 +391,13 @@ let
             ../modules/profiles/installation-device.nix
             ../modules/profiles/base.nix
             extraInstallerConfig
+            ./common/auto-format-root-device.nix
           ];
 
+          # In systemdStage1, also automatically format the device backing the
+          # root filesystem.
+          virtualisation.fileSystems."/".autoFormat = systemdStage1;
+
           # builds stuff in the VM, needs more juice
           virtualisation.diskSize = 8 * 1024;
           virtualisation.cores = 8;
@@ -329,15 +427,13 @@ let
           # The test cannot access the network, so any packages we
           # need must be included in the VM.
           system.extraDependencies = with pkgs; [
+            bintools
             brotli
             brotli.dev
             brotli.lib
             desktop-file-utils
             docbook5
             docbook_xsl_ns
-            (docbook-xsl-ns.override {
-              withManOptDedupPatch = true;
-            })
             kbd.dev
             kmod.dev
             libarchive.dev
@@ -385,7 +481,7 @@ let
       testScript = testScriptFun {
         inherit bootLoader createPartitions preBootCommands postBootCommands
                 grubDevice grubIdentifier grubUseEfi extraConfig
-                testSpecialisationConfig;
+                testSpecialisationConfig testFlakeSwitch;
       };
     };
 
@@ -437,6 +533,10 @@ let
     '';
   };
 
+  simple-test-config-flake = simple-test-config // {
+    testFlakeSwitch = true;
+  };
+
   simple-uefi-grub-config = {
     createPartitions = ''
       machine.succeed(
@@ -491,6 +591,8 @@ in {
   # one big filesystem partition.
   simple = makeInstallerTest "simple" simple-test-config;
 
+  switchToFlake = makeInstallerTest "switch-to-flake" simple-test-config-flake;
+
   # Test cloned configurations with the simple grub configuration
   simpleSpecialised = makeInstallerTest "simpleSpecialised" (simple-test-config // specialisation-test-extraconfig);
 
@@ -832,7 +934,7 @@ in {
         "keyctl link @u @s",
         "echo password | mkfs.bcachefs -L root --encrypted /dev/vda3",
         "echo password | bcachefs unlock /dev/vda3",
-        "mount -t bcachefs /dev/vda3 /mnt",
+        "echo password | mount -t bcachefs /dev/vda3 /mnt",
         "mkfs.ext3 -L boot /dev/vda1",
         "mkdir -p /mnt/boot",
         "mount /dev/vda1 /mnt/boot",
diff --git a/nixpkgs/nixos/tests/installer/flake.nix b/nixpkgs/nixos/tests/installer/flake.nix
new file mode 100644
index 000000000000..4bbef44e34fc
--- /dev/null
+++ b/nixpkgs/nixos/tests/installer/flake.nix
@@ -0,0 +1,20 @@
+# This file gets copied into the installation
+
+{
+  # To keep things simple, we'll use an absolute path dependency here.
+  inputs.nixpkgs.url = "@nixpkgs@";
+
+  outputs = { nixpkgs, ... }: {
+
+    nixosConfigurations.xyz = nixpkgs.lib.nixosSystem {
+      modules = [
+        ./configuration.nix
+        ( nixpkgs + "/nixos/modules/testing/test-instrumentation.nix" )
+        {
+          # We don't need nix-channel anymore
+          nix.channel.enable = false;
+        }
+      ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/tests/jenkins.nix b/nixpkgs/nixos/tests/jenkins.nix
index a1ede6dc917b..a8f621000654 100644
--- a/nixpkgs/nixos/tests/jenkins.nix
+++ b/nixpkgs/nixos/tests/jenkins.nix
@@ -73,8 +73,8 @@ import ./make-test-python.nix ({ pkgs, ...} : {
 
   testScript = { nodes, ... }:
     let
-      configWithoutJobs = "${nodes.master.config.system.build.toplevel}/specialisation/noJenkinsJobs";
-      jenkinsPort = nodes.master.config.services.jenkins.port;
+      configWithoutJobs = "${nodes.master.system.build.toplevel}/specialisation/noJenkinsJobs";
+      jenkinsPort = nodes.master.services.jenkins.port;
       jenkinsUrl = "http://localhost:${toString jenkinsPort}";
     in ''
     start_all()
diff --git a/nixpkgs/nixos/tests/k3s/single-node.nix b/nixpkgs/nixos/tests/k3s/single-node.nix
index d61595d889e2..e059603b9c9d 100644
--- a/nixpkgs/nixos/tests/k3s/single-node.nix
+++ b/nixpkgs/nixos/tests/k3s/single-node.nix
@@ -62,20 +62,20 @@ import ../make-test-python.nix ({ pkgs, lib, k3s, ... }:
       start_all()
 
       machine.wait_for_unit("k3s")
-      machine.succeed("k3s kubectl cluster-info")
-      machine.fail("sudo -u noprivs k3s kubectl cluster-info")
+      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")'' + ''
 
       machine.succeed(
-          "${pauseImage} | k3s ctr image import -"
+          "${pauseImage} | ctr image import -"
       )
 
       # Also wait for our service account to show up; it takes a sec
-      machine.wait_until_succeeds("k3s kubectl get serviceaccount default")
-      machine.succeed("k3s kubectl apply -f ${testPodYaml}")
-      machine.succeed("k3s kubectl wait --for 'condition=Ready' pod/test")
-      machine.succeed("k3s kubectl delete -f ${testPodYaml}")
+      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'")
diff --git a/nixpkgs/nixos/tests/kafka.nix b/nixpkgs/nixos/tests/kafka.nix
index 79af02710c32..864253fd8c73 100644
--- a/nixpkgs/nixos/tests/kafka.nix
+++ b/nixpkgs/nixos/tests/kafka.nix
@@ -72,5 +72,7 @@ in with pkgs; {
   kafka_3_1  = makeKafkaTest "kafka_3_1"  apacheKafka_3_1;
   kafka_3_2  = makeKafkaTest "kafka_3_2"  apacheKafka_3_2;
   kafka_3_3  = makeKafkaTest "kafka_3_3"  apacheKafka_3_3;
+  kafka_3_4  = makeKafkaTest "kafka_3_4"  apacheKafka_3_4;
+  kafka_3_5  = makeKafkaTest "kafka_3_5"  apacheKafka_3_5;
   kafka  = makeKafkaTest "kafka"  apacheKafka;
 }
diff --git a/nixpkgs/nixos/tests/kanidm.nix b/nixpkgs/nixos/tests/kanidm.nix
index 7e174590cb5f..3f5bca397740 100644
--- a/nixpkgs/nixos/tests/kanidm.nix
+++ b/nixpkgs/nixos/tests/kanidm.nix
@@ -2,6 +2,10 @@ import ./make-test-python.nix ({ pkgs, ... }:
   let
     certs = import ./common/acme/server/snakeoil-certs.nix;
     serverDomain = certs.domain;
+
+    testCredentials = {
+      password = "Password1_cZPEwpCWvrReripJmAZdmVIZd8HHoHcl";
+    };
   in
   {
     name = "kanidm";
@@ -63,9 +67,10 @@ import ./make-test-python.nix ({ pkgs, ... }:
       ''
         start_all()
         server.wait_for_unit("kanidm.service")
+        client.wait_for_unit("network-online.target")
 
         with subtest("Test HTTP interface"):
-            server.wait_until_succeeds("curl -sf https://${serverDomain} | grep Kanidm")
+            server.wait_until_succeeds("curl -Lsf https://${serverDomain} | grep Kanidm")
 
         with subtest("Test LDAP interface"):
             server.succeed("ldapsearch -H ldaps://${serverDomain}:636 -b '${ldapBaseDN}' -x '(name=test)'")
@@ -73,17 +78,51 @@ import ./make-test-python.nix ({ pkgs, ... }:
         with subtest("Test CLI login"):
             client.succeed("kanidm login -D anonymous")
             client.succeed("kanidm self whoami | grep anonymous@${serverDomain}")
+            client.succeed("kanidm logout")
 
         with subtest("Recover idm_admin account"):
-            # Must stop the server for account recovery or else kanidmd fails with
-            # "unable to lock kanidm exclusive lock at /var/lib/kanidm/kanidm.db.klock".
-            server.succeed("systemctl stop kanidm")
-            server.succeed("su - kanidm -c 'kanidmd recover-account -c ${serverConfigFile} idm_admin 2>&1 | rg -o \'[A-Za-z0-9]{48}\' '")
-            server.succeed("systemctl start kanidm")
+            idm_admin_password = server.succeed("su - kanidm -c 'kanidmd recover-account -c ${serverConfigFile} idm_admin 2>&1 | rg -o \'[A-Za-z0-9]{48}\' '").strip().removeprefix("'").removesuffix("'")
 
         with subtest("Test unixd connection"):
             client.wait_for_unit("kanidm-unixd.service")
-            # TODO: client.wait_for_file("/run/kanidm-unixd/sock")
+            client.wait_for_file("/run/kanidm-unixd/sock")
             client.wait_until_succeeds("kanidm-unix status | grep working!")
+
+        with subtest("Test user creation"):
+            client.wait_for_unit("getty@tty1.service")
+            client.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
+            client.wait_until_tty_matches("1", "login: ")
+            client.send_chars("root\n")
+            client.send_chars("kanidm login -D idm_admin\n")
+            client.wait_until_tty_matches("1", "Enter password: ")
+            client.send_chars(f"{idm_admin_password}\n")
+            client.wait_until_tty_matches("1", "Login Success for idm_admin")
+            client.succeed("kanidm person create testuser TestUser")
+            client.succeed("kanidm person posix set --shell \"$SHELL\" testuser")
+            client.send_chars("kanidm person posix set-password testuser\n")
+            client.wait_until_tty_matches("1", "Enter new")
+            client.send_chars("${testCredentials.password}\n")
+            client.wait_until_tty_matches("1", "Retype")
+            client.send_chars("${testCredentials.password}\n")
+            output = client.succeed("getent passwd testuser")
+            assert "TestUser" in output
+            client.succeed("kanidm group create shell")
+            client.succeed("kanidm group posix set shell")
+            client.succeed("kanidm group add-members shell testuser")
+
+        with subtest("Test user login"):
+            client.send_key("alt-f2")
+            client.wait_until_succeeds("[ $(fgconsole) = 2 ]")
+            client.wait_for_unit("getty@tty2.service")
+            client.wait_until_succeeds("pgrep -f 'agetty.*tty2'")
+            client.wait_until_tty_matches("2", "login: ")
+            client.send_chars("testuser\n")
+            client.wait_until_tty_matches("2", "login: testuser")
+            client.wait_until_succeeds("pgrep login")
+            client.wait_until_tty_matches("2", "Password: ")
+            client.send_chars("${testCredentials.password}\n")
+            client.wait_until_succeeds("systemctl is-active user@$(id -u testuser).service")
+            client.send_chars("touch done\n")
+            client.wait_for_file("/home/testuser@${serverDomain}/done")
       '';
   })
diff --git a/nixpkgs/nixos/tests/kernel-generic.nix b/nixpkgs/nixos/tests/kernel-generic.nix
index 3e74554de339..e4a8e06df1ef 100644
--- a/nixpkgs/nixos/tests/kernel-generic.nix
+++ b/nixpkgs/nixos/tests/kernel-generic.nix
@@ -9,7 +9,7 @@ let
   testsForLinuxPackages = linuxPackages: (import ./make-test-python.nix ({ pkgs, ... }: {
     name = "kernel-${linuxPackages.kernel.version}";
     meta = with pkgs.lib.maintainers; {
-      maintainers = [ nequissimus atemu ];
+      maintainers = [ nequissimus atemu ma27 ];
     };
 
     nodes.machine = { ... }:
@@ -31,6 +31,12 @@ let
       linux_5_10_hardened
       linux_5_15_hardened
       linux_6_1_hardened
+      linux_6_4_hardened
+      linux_rt_5_4
+      linux_rt_5_10
+      linux_rt_5_15
+      linux_rt_6_1
+      linux_libre
 
       linux_testing;
   };
diff --git a/nixpkgs/nixos/tests/keter.nix b/nixpkgs/nixos/tests/keter.nix
index 0bfb96e1c324..1cc2ffbde0a0 100644
--- a/nixpkgs/nixos/tests/keter.nix
+++ b/nixpkgs/nixos/tests/keter.nix
@@ -1,42 +1,43 @@
 import ./make-test-python.nix ({ pkgs, ... }:
-let
-  port = 81;
-in
-{
-  name = "keter";
-  meta = with pkgs.lib.maintainers; {
-    maintainers = [ jappie ];
-  };
+  let
+    port = 81;
+  in
+  {
+    name = "keter";
+    meta = with pkgs.lib.maintainers; {
+      maintainers = [ jappie ];
+    };
 
 
-  nodes.machine = { config, pkgs, ... }: {
-    services.keter = {
-      enable = true;
+    nodes.machine = { config, pkgs, ... }: {
+      services.keter = {
+        enable = true;
 
-      globalKeterConfig = {
-        listeners = [{
-          host = "*4";
-          inherit port;
-        }];
-      };
-      bundle = {
-        appName = "test-bundle";
-        domain = "localhost";
-        executable = pkgs.writeShellScript "run" ''
-          ${pkgs.python3}/bin/python -m http.server $PORT
-        '';
+        globalKeterConfig = {
+          cli-port = 123; # just adding this to test the freeform
+          listeners = [{
+            host = "*4";
+            inherit port;
+          }];
+        };
+        bundle = {
+          appName = "test-bundle";
+          domain = "localhost";
+          executable = pkgs.writeShellScript "run" ''
+            ${pkgs.python3}/bin/python -m http.server $PORT
+          '';
+        };
       };
     };
-  };
 
-  testScript =
-    ''
-      machine.wait_for_unit("keter.service")
+    testScript =
+      ''
+        machine.wait_for_unit("keter.service")
 
-      machine.wait_for_open_port(${toString port})
-      machine.wait_for_console_text("Activating app test-bundle with hosts: localhost")
+        machine.wait_for_open_port(${toString port})
+        machine.wait_for_console_text("Activating app test-bundle with hosts: localhost")
 
 
-      machine.succeed("curl --fail http://localhost:${toString port}/")
-    '';
-})
+        machine.succeed("curl --fail http://localhost:${toString port}/")
+      '';
+  })
diff --git a/nixpkgs/nixos/tests/kexec.nix b/nixpkgs/nixos/tests/kexec.nix
index 3f5a6f521af0..4d1be497b8ba 100644
--- a/nixpkgs/nixos/tests/kexec.nix
+++ b/nixpkgs/nixos/tests/kexec.nix
@@ -8,10 +8,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     node1 = { ... }: {
       virtualisation.vlans = [ ];
       virtualisation.memorySize = 4 * 1024;
-      virtualisation.useBootLoader = true;
-      virtualisation.useEFIBoot = true;
-      boot.loader.systemd-boot.enable = true;
-      boot.loader.efi.canTouchEfiVariables = true;
     };
 
     node2 = { modulesPath, ... }: {
@@ -19,6 +15,8 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
       environment.systemPackages = [ pkgs.hello ];
       imports = [
         "${modulesPath}/installer/netboot/netboot-minimal.nix"
+        "${modulesPath}/testing/test-instrumentation.nix"
+        "${modulesPath}/profiles/qemu-guest.nix"
       ];
     };
   };
@@ -39,7 +37,10 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     # Kexec node1 to the toplevel of node2 via the kexec-boot script
     node1.succeed('touch /run/foo')
     node1.fail('hello')
-    node1.execute('${nodes.node2.config.system.build.kexecTree}/kexec-boot', check_return=False)
+    node1.execute('${nodes.node2.system.build.kexecTree}/kexec-boot', check_output=False)
+    node1.connected = False
+    node1.connect()
+    node1.wait_for_unit("multi-user.target")
     node1.succeed('! test -e /run/foo')
     node1.succeed('hello')
     node1.succeed('[ "$(hostname)" = "node2" ]')
diff --git a/nixpkgs/nixos/tests/keyd.nix b/nixpkgs/nixos/tests/keyd.nix
index d492cc194895..1ee08b4101f7 100644
--- a/nixpkgs/nixos/tests/keyd.nix
+++ b/nixpkgs/nixos/tests/keyd.nix
@@ -32,7 +32,7 @@ let
     nodes.machine = {
       services.keyd = {
         enable = true;
-        inherit settings;
+        keyboards.default = { inherit settings; };
       };
     };
 
diff --git a/nixpkgs/nixos/tests/keymap.nix b/nixpkgs/nixos/tests/keymap.nix
index 0bde21093b0a..cc45824667ed 100644
--- a/nixpkgs/nixos/tests/keymap.nix
+++ b/nixpkgs/nixos/tests/keymap.nix
@@ -29,10 +29,10 @@ let
   mkKeyboardTest = layout: { extraConfig ? {}, tests }: with pkgs.lib; makeTest {
     name = "keymap-${layout}";
 
-    machine.console.keyMap = mkOverride 900 layout;
-    machine.services.xserver.desktopManager.xterm.enable = false;
-    machine.services.xserver.layout = mkOverride 900 layout;
-    machine.imports = [ ./common/x11.nix extraConfig ];
+    nodes.machine.console.keyMap = mkOverride 900 layout;
+    nodes.machine.services.xserver.desktopManager.xterm.enable = false;
+    nodes.machine.services.xserver.layout = mkOverride 900 layout;
+    nodes.machine.imports = [ ./common/x11.nix extraConfig ];
 
     testScript = ''
       import json
@@ -201,4 +201,33 @@ in pkgs.lib.mapAttrs mkKeyboardTest {
     extraConfig.console.keyMap = "de";
     extraConfig.services.xserver.layout = "de";
   };
+
+  custom = {
+    tests = {
+      us.qwerty = [ "a" "b" "g" "d" "z" "shift-2" "shift-3" ];
+      us.expect = [ "a" "b" "g" "d" "z" "@" "#" ];
+      greek.qwerty = map (x: "alt_r-${x}")
+                     [ "a" "b" "g" "d" "z" ];
+      greek.expect = [ "α" "β" "γ" "δ" "ζ" ];
+    };
+
+    extraConfig.console.useXkbConfig = true;
+    extraConfig.services.xserver.layout = "us-greek";
+    extraConfig.services.xserver.extraLayouts.us-greek =
+      { description = "US layout with alt-gr greek";
+        languages   = [ "eng" ];
+        symbolsFile = pkgs.writeText "us-greek" ''
+          xkb_symbols "us-greek"
+          {
+            include "us(basic)"
+            include "level3(ralt_switch)"
+            key <LatA> { [ a, A, Greek_alpha ] };
+            key <LatB> { [ b, B, Greek_beta  ] };
+            key <LatG> { [ g, G, Greek_gamma ] };
+            key <LatD> { [ d, D, Greek_delta ] };
+            key <LatZ> { [ z, Z, Greek_zeta  ] };
+          };
+        '';
+      };
+  };
 }
diff --git a/nixpkgs/nixos/tests/lemmy.nix b/nixpkgs/nixos/tests/lemmy.nix
index fb64daa80e64..de2c4938fe23 100644
--- a/nixpkgs/nixos/tests/lemmy.nix
+++ b/nixpkgs/nixos/tests/lemmy.nix
@@ -22,14 +22,16 @@ in
           # Without setup, the /feeds/* and /nodeinfo/* API endpoints won't return 200
           setup = {
             admin_username = "mightyiam";
-            admin_password = "ThisIsWhatIUseEverywhereTryIt";
             site_name = "Lemmy FTW";
             admin_email = "mightyiam@example.com";
           };
         };
+        adminPasswordFile = /etc/lemmy-admin-password.txt;
         caddy.enable = true;
       };
 
+      environment.etc."lemmy-admin-password.txt".text = "ThisIsWhatIUseEverywhereTryIt";
+
       networking.firewall.allowedTCPPorts = [ 80 ];
 
       # pict-rs seems to need more than 1025114112 bytes
@@ -40,8 +42,14 @@ in
   testScript = ''
     server = ${lemmyNodeName}
 
-    with subtest("the backend starts and responds"):
+    with subtest("the merged config is secure"):
         server.wait_for_unit("lemmy.service")
+        config_permissions = server.succeed("stat --format %A /run/lemmy/config.hjson").rstrip()
+        assert config_permissions == "-rw-------", f"merged config permissions {config_permissions} are insecure"
+        directory_permissions = server.succeed("stat --format %A /run/lemmy").rstrip()
+        assert directory_permissions[5] == directory_permissions[8] == "-", "merged config can be replaced"
+
+    with subtest("the backend starts and responds"):
         server.wait_for_open_port(${toString backendPort})
         server.succeed("curl --fail localhost:${toString backendPort}/api/v3/site")
 
diff --git a/nixpkgs/nixos/tests/luks.nix b/nixpkgs/nixos/tests/luks.nix
index d5ac550a3c57..da1d0c63b95d 100644
--- a/nixpkgs/nixos/tests/luks.nix
+++ b/nixpkgs/nixos/tests/luks.nix
@@ -2,6 +2,8 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
   name = "luks";
 
   nodes.machine = { pkgs, ... }: {
+    imports = [ ./common/auto-format-root-device.nix ];
+
     # Use systemd-boot
     virtualisation = {
       emptyDiskImages = [ 512 512 ];
diff --git a/nixpkgs/nixos/tests/lxd-ui.nix b/nixpkgs/nixos/tests/lxd-ui.nix
new file mode 100644
index 000000000000..19eaa226c0bf
--- /dev/null
+++ b/nixpkgs/nixos/tests/lxd-ui.nix
@@ -0,0 +1,35 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "lxd-ui";
+
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ jnsgruk ];
+  };
+
+  nodes.machine = { lib, ... }: {
+    virtualisation = {
+      lxd.enable = true;
+      lxd.ui.enable = true;
+    };
+
+    environment.systemPackages = [ pkgs.curl ];
+  };
+
+  testScript = ''
+    machine.wait_for_unit("sockets.target")
+    machine.wait_for_unit("lxd.service")
+    machine.wait_for_file("/var/lib/lxd/unix.socket")
+
+    # Wait for lxd to settle
+    machine.succeed("lxd waitready")
+
+    # Configure LXC listen address
+    machine.succeed("lxc config set core.https_address :8443")
+    machine.succeed("systemctl restart lxd")
+
+    # Check that the LXD_UI environment variable is populated in the systemd unit
+    machine.succeed("cat /etc/systemd/system/lxd.service | grep 'LXD_UI'")
+
+    # Ensure the endpoint returns an HTML page with 'LXD UI' in the title
+    machine.succeed("curl -kLs https://localhost:8443/ui | grep '<title>LXD UI</title>'")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/maestral.nix b/nixpkgs/nixos/tests/maestral.nix
index ba2e0b2f3baa..67a265926187 100644
--- a/nixpkgs/nixos/tests/maestral.nix
+++ b/nixpkgs/nixos/tests/maestral.nix
@@ -52,7 +52,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
   testScript = { nodes, ... }:
     let
-      user = nodes.cli.config.users.users.alice;
+      user = nodes.cli.users.users.alice;
     in
     ''
       start_all()
@@ -65,7 +65,8 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
       with subtest("GUI"):
         gui.wait_for_x()
-        gui.succeed("xauth merge ${user.home}/.Xauthority")
+        gui.wait_for_file("/tmp/xauth_*")
+        gui.succeed("xauth merge /tmp/xauth_*")
         gui.wait_for_window("^Desktop ")
         gui.wait_for_unit("maestral.service", "${user.name}")
     '';
diff --git a/nixpkgs/nixos/tests/matomo.nix b/nixpkgs/nixos/tests/matomo.nix
index 0e09ad295f95..7dbef63136aa 100644
--- a/nixpkgs/nixos/tests/matomo.nix
+++ b/nixpkgs/nixos/tests/matomo.nix
@@ -41,10 +41,10 @@ let
 in {
   matomo = matomoTest pkgs.matomo // {
     name = "matomo";
-    meta.maintainers = with maintainers; [ florianjacob kiwi mmilata ];
+    meta.maintainers = with maintainers; [ florianjacob kiwi mmilata twey boozedog ];
   };
   matomo-beta = matomoTest pkgs.matomo-beta // {
     name = "matomo-beta";
-    meta.maintainers = with maintainers; [ florianjacob kiwi mmilata ];
+    meta.maintainers = with maintainers; [ florianjacob kiwi mmilata twey boozedog ];
   };
 }
diff --git a/nixpkgs/nixos/tests/miniflux.nix b/nixpkgs/nixos/tests/miniflux.nix
index be3e7abb6abd..a3af53db0e7a 100644
--- a/nixpkgs/nixos/tests/miniflux.nix
+++ b/nixpkgs/nixos/tests/miniflux.nix
@@ -25,6 +25,7 @@ in
     default =
       { ... }:
       {
+        security.apparmor.enable = true;
         services.miniflux = {
           enable = true;
           inherit adminCredentialsFile;
@@ -34,6 +35,7 @@ in
     withoutSudo =
       { ... }:
       {
+        security.apparmor.enable = true;
         services.miniflux = {
           enable = true;
           inherit adminCredentialsFile;
@@ -44,6 +46,7 @@ in
     customized =
       { ... }:
       {
+        security.apparmor.enable = true;
         services.miniflux = {
           enable = true;
           config = {
@@ -63,6 +66,7 @@ in
     default.succeed(
         "curl 'http://localhost:${toString defaultPort}/v1/me' -u '${defaultUsername}:${defaultPassword}' -H Content-Type:application/json | grep '\"is_admin\":true'"
     )
+    default.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""')
 
     withoutSudo.wait_for_unit("miniflux.service")
     withoutSudo.wait_for_open_port(${toString defaultPort})
@@ -70,6 +74,7 @@ in
     withoutSudo.succeed(
         "curl 'http://localhost:${toString defaultPort}/v1/me' -u '${defaultUsername}:${defaultPassword}' -H Content-Type:application/json | grep '\"is_admin\":true'"
     )
+    withoutSudo.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""')
 
     customized.wait_for_unit("miniflux.service")
     customized.wait_for_open_port(${toString port})
@@ -77,5 +82,6 @@ in
     customized.succeed(
         "curl 'http://localhost:${toString port}/v1/me' -u '${username}:${password}' -H Content-Type:application/json | grep '\"is_admin\":true'"
     )
+    customized.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""')
   '';
 })
diff --git a/nixpkgs/nixos/tests/miriway.nix b/nixpkgs/nixos/tests/miriway.nix
index 9000e9278197..f12c4d5ecc41 100644
--- a/nixpkgs/nixos/tests/miriway.nix
+++ b/nixpkgs/nixos/tests/miriway.nix
@@ -83,7 +83,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
       };
     };
 
-    fonts.fonts = [ pkgs.inconsolata ];
+    fonts.packages = [ pkgs.inconsolata ];
   };
 
   enableOCR = true;
diff --git a/nixpkgs/nixos/tests/mumble.nix b/nixpkgs/nixos/tests/mumble.nix
index 2b5cc20163bc..8eee454721a1 100644
--- a/nixpkgs/nixos/tests/mumble.nix
+++ b/nixpkgs/nixos/tests/mumble.nix
@@ -20,6 +20,7 @@ in
 
   nodes = {
     server = { config, ... }: {
+      security.apparmor.enable = true;
       services.murmur.enable = true;
       services.murmur.registerName = "NixOS tests";
       services.murmur.password = "$MURMURD_PASSWORD";
@@ -81,5 +82,8 @@ in
     server.sleep(5)  # wait to get screenshot
     client1.screenshot("screen1")
     client2.screenshot("screen2")
+
+    # check if apparmor denied anything
+    server.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""')
   '';
 })
diff --git a/nixpkgs/nixos/tests/n8n.nix b/nixpkgs/nixos/tests/n8n.nix
index 771296bf37a5..0a12192d5c71 100644
--- a/nixpkgs/nixos/tests/n8n.nix
+++ b/nixpkgs/nixos/tests/n8n.nix
@@ -1,6 +1,7 @@
 import ./make-test-python.nix ({ lib, ... }:
 let
   port = 5678;
+  webhookUrl = "http://example.com";
 in
 {
   name = "n8n";
@@ -11,6 +12,7 @@ in
     {
       services.n8n = {
         enable = true;
+        webhookUrl = webhookUrl;
       };
     };
 
@@ -18,5 +20,6 @@ in
     machine.wait_for_unit("n8n.service")
     machine.wait_for_console_text("Editor is now accessible via")
     machine.succeed("curl --fail -vvv http://localhost:${toString port}/")
+    machine.succeed("grep -qF ${webhookUrl} /etc/systemd/system/n8n.service")
   '';
 })
diff --git a/nixpkgs/nixos/tests/networking.nix b/nixpkgs/nixos/tests/networking.nix
index 05fed0f4b473..46fc715d0891 100644
--- a/nixpkgs/nixos/tests/networking.nix
+++ b/nixpkgs/nixos/tests/networking.nix
@@ -29,23 +29,49 @@ let
             ipv6.addresses = [ { address = "fd00:1234:5678:${toString n}::1"; prefixLength = 64; } ];
           })));
       };
-      services.dhcpd4 = {
-        enable = true;
-        interfaces = map (n: "eth${toString n}") vlanIfs;
-        extraConfig = flip concatMapStrings vlanIfs (n: ''
-          subnet 192.168.${toString n}.0 netmask 255.255.255.0 {
-            option routers 192.168.${toString n}.1;
-            range 192.168.${toString n}.3 192.168.${toString n}.254;
-          }
-        '')
-        ;
-        machines = flip map vlanIfs (vlan:
-          {
-            hostName = "client${toString vlan}";
-            ethernetAddress = qemu-common.qemuNicMac vlan 1;
-            ipAddress = "192.168.${toString vlan}.2";
-          }
-        );
+      services.kea = {
+        dhcp4 = {
+          enable = true;
+          settings = {
+            interfaces-config = {
+              interfaces = map (n: "eth${toString n}") vlanIfs;
+              dhcp-socket-type = "raw";
+              service-sockets-require-all = true;
+              service-sockets-max-retries = 5;
+              service-sockets-retry-wait-time = 2500;
+            };
+            subnet4 = map (n: {
+              id = n;
+              subnet = "192.168.${toString n}.0/24";
+              pools = [{ pool = "192.168.${toString n}.3 - 192.168.${toString n}.254"; }];
+              option-data = [{ name = "routers"; data = "192.168.${toString n}.1"; }];
+
+              reservations = [{
+                hw-address = qemu-common.qemuNicMac n 1;
+                hostname = "client${toString n}";
+                ip-address = "192.168.${toString n}.2";
+              }];
+            }) vlanIfs;
+          };
+        };
+        dhcp6 = {
+          enable = true;
+          settings = {
+            interfaces-config = {
+              interfaces = map (n: "eth${toString n}") vlanIfs;
+              service-sockets-require-all = true;
+              service-sockets-max-retries = 5;
+              service-sockets-retry-wait-time = 2500;
+            };
+
+            subnet6 = map (n: {
+              id = n;
+              subnet = "fd00:1234:5678:${toString n}::/64";
+              interface = "eth${toString n}";
+              pools = [{ pool = "fd00:1234:5678:${toString n}::2-fd00:1234:5678:${toString n}::2"; }];
+            }) vlanIfs;
+          };
+        };
       };
       services.radvd = {
         enable = true;
@@ -61,17 +87,6 @@ let
           };
         '');
       };
-      services.dhcpd6 = {
-        enable = true;
-        interfaces = map (n: "eth${toString n}") vlanIfs;
-        extraConfig = ''
-          authoritative;
-        '' + flip concatMapStrings vlanIfs (n: ''
-          subnet6 fd00:1234:5678:${toString n}::/64 {
-            range6 fd00:1234:5678:${toString n}::2 fd00:1234:5678:${toString n}::2;
-          }
-        '');
-      };
     };
 
   testCases = {
@@ -117,8 +132,9 @@ let
           client.wait_for_unit("network.target")
           router.wait_for_unit("network-online.target")
 
-          with subtest("Make sure dhcpcd is not started"):
-              client.fail("systemctl status dhcpcd.service")
+          with subtest("Make sure DHCP server is not started"):
+              client.fail("systemctl status kea-dhcp4-server.service")
+              client.fail("systemctl status kea-dhcp6-server.service")
 
           with subtest("Test vlan 1"):
               client.wait_until_succeeds("ping -c 1 192.168.1.1")
@@ -1035,7 +1051,7 @@ let
       testScript = ''
         machine.succeed("udevadm settle")
         print(machine.succeed("ip link show dev enCustom"))
-        machine.wait_until_succeeds("ip link show dev enCustom | grep -q '52:54:00:12:0b:01")
+        machine.wait_until_succeeds("ip link show dev enCustom | grep -q 52:54:00:12:0b:01")
       '';
     };
   };
diff --git a/nixpkgs/nixos/tests/nextcloud/basic.nix b/nixpkgs/nixos/tests/nextcloud/basic.nix
index e17f701c54b7..db5cee9f6584 100644
--- a/nixpkgs/nixos/tests/nextcloud/basic.nix
+++ b/nixpkgs/nixos/tests/nextcloud/basic.nix
@@ -14,12 +14,12 @@ in {
     client = { ... }: {
       services.davfs2.enable = true;
       system.activationScripts.davfs2-secrets = ''
-        echo "http://nextcloud/remote.php/webdav/ ${adminuser} ${adminpass}" > /tmp/davfs2-secrets
+        echo "http://nextcloud/remote.php/dav/files/${adminuser} ${adminuser} ${adminpass}" > /tmp/davfs2-secrets
         chmod 600 /tmp/davfs2-secrets
       '';
       virtualisation.fileSystems = {
         "/mnt/dav" = {
-          device = "http://nextcloud/remote.php/webdav/";
+          device = "http://nextcloud/remote.php/dav/files/${adminuser}";
           fsType = "davfs";
           options = let
             davfs2Conf = (pkgs.writeText "davfs2.conf" "secrets /tmp/davfs2-secrets");
@@ -70,7 +70,7 @@ in {
     withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
       #!${pkgs.runtimeShell}
       export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
-      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/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})"
diff --git a/nixpkgs/nixos/tests/nextcloud/default.nix b/nixpkgs/nixos/tests/nextcloud/default.nix
index 78fe026b4a84..b9f35b398cfe 100644
--- a/nixpkgs/nixos/tests/nextcloud/default.nix
+++ b/nixpkgs/nixos/tests/nextcloud/default.nix
@@ -26,4 +26,4 @@ foldl
     };
   })
 { }
-  [ 25 26 ]
+  [ 25 26 27 ]
diff --git a/nixpkgs/nixos/tests/nextcloud/openssl-sse.nix b/nixpkgs/nixos/tests/nextcloud/openssl-sse.nix
index 659a4311cddd..92beb869eb03 100644
--- a/nixpkgs/nixos/tests/nextcloud/openssl-sse.nix
+++ b/nixpkgs/nixos/tests/nextcloud/openssl-sse.nix
@@ -33,7 +33,7 @@ in {
     withRcloneEnv = host: pkgs.writeScript "with-rclone-env" ''
       #!${pkgs.runtimeShell}
       export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
-      export RCLONE_CONFIG_NEXTCLOUD_URL="http://${host}/remote.php/webdav/"
+      export RCLONE_CONFIG_NEXTCLOUD_URL="http://${host}/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})"
diff --git a/nixpkgs/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix b/nixpkgs/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix
index ce0019e9da4a..e638f2e5b861 100644
--- a/nixpkgs/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix
+++ b/nixpkgs/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix
@@ -1,5 +1,6 @@
-import ../make-test-python.nix ({ pkgs, ...}: let
-  username = "custom_admin_username";
+args@{ nextcloudVersion ? 27, ... }:
+(import ../make-test-python.nix ({ pkgs, ...}: let
+  adminuser = "custom_admin_username";
   # This will be used both for redis and postgresql
   pass = "hunter2";
   # Don't do this at home, use a file outside of the nix store instead
@@ -9,7 +10,7 @@ import ../make-test-python.nix ({ pkgs, ...}: let
 in {
   name = "nextcloud-with-declarative-redis";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ eqyiel ];
+    maintainers = [ eqyiel ma27 ];
   };
 
   nodes = {
@@ -22,6 +23,7 @@ in {
       services.nextcloud = {
         enable = true;
         hostName = "nextcloud";
+        package = pkgs.${"nextcloud" + (toString nextcloudVersion)};
         caching = {
           apcu = false;
           redis = true;
@@ -32,28 +34,26 @@ in {
         config = {
           dbtype = "pgsql";
           dbname = "nextcloud";
-          dbuser = username;
+          dbuser = adminuser;
           dbpassFile = passFile;
-          adminuser = username;
+          adminuser = adminuser;
           adminpassFile = passFile;
         };
         secretFile = "/etc/nextcloud-secrets.json";
 
         extraOptions.redis = {
-          host = "/run/redis/redis.sock";
-          port = 0;
           dbindex = 0;
           timeout = 1.5;
           # password handled via secretfile below
         };
-        extraOptions.memcache = {
-          local = "\OC\Memcache\Redis";
-          locking = "\OC\Memcache\Redis";
-        };
+        configureRedis = true;
       };
 
-      services.redis.servers."nextcloud".enable = true;
-      services.redis.servers."nextcloud".port = 6379;
+      services.redis.servers."nextcloud" = {
+        enable = true;
+        port = 6379;
+        requirePass = "secret";
+      };
 
       systemd.services.nextcloud-setup= {
         requires = ["postgresql.service"];
@@ -66,15 +66,15 @@ in {
       systemd.services.postgresql.postStart = pkgs.lib.mkAfter ''
         password=$(cat ${passFile})
         ${config.services.postgresql.package}/bin/psql <<EOF
-          CREATE ROLE ${username} WITH LOGIN PASSWORD '$password' CREATEDB;
+          CREATE ROLE ${adminuser} WITH LOGIN PASSWORD '$password' CREATEDB;
           CREATE DATABASE nextcloud;
-          GRANT ALL PRIVILEGES ON DATABASE nextcloud TO ${username};
+          GRANT ALL PRIVILEGES ON DATABASE nextcloud TO ${adminuser};
         EOF
       '';
 
       # This file is meant to contain secret options which should
       # not go into the nix store. Here it is just used to set the
-      # databyse type to postgres.
+      # redis password.
       environment.etc."nextcloud-secrets.json".text = ''
         {
           "redis": {
@@ -89,9 +89,9 @@ in {
     withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
       #!${pkgs.runtimeShell}
       export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
-      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/"
+      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/dav/files/${adminuser}"
       export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
-      export RCLONE_CONFIG_NEXTCLOUD_USER="${username}"
+      export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
       export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${pass})"
       "''${@}"
     '';
@@ -117,6 +117,6 @@ in {
     )
 
     # redis cache should not be empty
-    nextcloud.fail("redis-cli KEYS * | grep -q 'empty array'")
+    nextcloud.fail('test "[]" = "$(redis-cli --json KEYS "*")"')
   '';
-})
+})) args
diff --git a/nixpkgs/nixos/tests/nextcloud/with-mysql-and-memcached.nix b/nixpkgs/nixos/tests/nextcloud/with-mysql-and-memcached.nix
index e57aabfaf86b..035a7fdcb0c8 100644
--- a/nixpkgs/nixos/tests/nextcloud/with-mysql-and-memcached.nix
+++ b/nixpkgs/nixos/tests/nextcloud/with-mysql-and-memcached.nix
@@ -49,7 +49,7 @@ in {
     withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
       #!${pkgs.runtimeShell}
       export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
-      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/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})"
diff --git a/nixpkgs/nixos/tests/nextcloud/with-postgresql-and-redis.nix b/nixpkgs/nixos/tests/nextcloud/with-postgresql-and-redis.nix
index 1cbb13104287..586bf50fd939 100644
--- a/nixpkgs/nixos/tests/nextcloud/with-postgresql-and-redis.nix
+++ b/nixpkgs/nixos/tests/nextcloud/with-postgresql-and-redis.nix
@@ -60,7 +60,7 @@ in {
     withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
       #!${pkgs.runtimeShell}
       export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
-      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/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})"
@@ -89,5 +89,8 @@ in {
         "${withRcloneEnv} ${diffSharedFile}"
     )
     nextcloud.wait_until_succeeds("journalctl -u nextcloud-notify_push | grep -q \"Sending ping to ${adminuser}\"")
+
+    # redis cache should not be empty
+    nextcloud.fail('test "[]" = "$(redis-cli --json KEYS "*")"')
   '';
 })) args
diff --git a/nixpkgs/nixos/tests/nginx-proxyprotocol/default.nix b/nixpkgs/nixos/tests/nginx-proxyprotocol/default.nix
index 9ef5d01cd65b..2ff7debfcbe2 100644
--- a/nixpkgs/nixos/tests/nginx-proxyprotocol/default.nix
+++ b/nixpkgs/nixos/tests/nginx-proxyprotocol/default.nix
@@ -4,6 +4,10 @@ in
 import ../make-test-python.nix ({ pkgs, ... }: {
   name = "nginx-proxyprotocol";
 
+  meta = {
+    maintainers = with pkgs.lib.maintainers; [ raitobezarius ];
+  };
+
   nodes = {
     webserver = { pkgs, lib, ... }: {
       environment.systemPackages = [ pkgs.netcat ];
diff --git a/nixpkgs/nixos/tests/nginx-status-page.nix b/nixpkgs/nixos/tests/nginx-status-page.nix
new file mode 100644
index 000000000000..ff2c0940379c
--- /dev/null
+++ b/nixpkgs/nixos/tests/nginx-status-page.nix
@@ -0,0 +1,72 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "nginx-status-page";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ h7x4 ];
+  };
+
+  nodes = {
+    webserver = { ... }: {
+      virtualisation.vlans = [ 1 ];
+
+      networking = {
+        useNetworkd = true;
+        useDHCP = false;
+        firewall.enable = false;
+      };
+
+      systemd.network.networks."01-eth1" = {
+        name = "eth1";
+        networkConfig.Address = "10.0.0.1/24";
+      };
+
+      services.nginx = {
+        enable = true;
+        statusPage = true;
+        virtualHosts."localhost".locations."/index.html".return = "200 'hello world\n'";
+      };
+
+      environment.systemPackages = with pkgs; [ curl ];
+    };
+
+    client = { ... }: {
+      virtualisation.vlans = [ 1 ];
+
+      networking = {
+        useNetworkd = true;
+        useDHCP = false;
+        firewall.enable = false;
+      };
+
+      systemd.network.networks."01-eth1" = {
+        name = "eth1";
+        networkConfig.Address = "10.0.0.2/24";
+      };
+
+      environment.systemPackages = with pkgs; [ curl ];
+    };
+  };
+
+  testScript = { nodes, ... }: ''
+    start_all()
+
+    webserver.wait_for_unit("nginx")
+    webserver.wait_for_open_port(80)
+
+    def expect_http_code(node, code, url):
+        http_code = node.succeed(f"curl -w '%{{http_code}}' '{url}'")
+        assert http_code.split("\n")[-1].strip() == code, \
+          f"expected {code} but got following response:\n{http_code}"
+
+    with subtest("localhost can access status page"):
+        expect_http_code(webserver, "200", "http://localhost/nginx_status")
+
+    with subtest("localhost can access other page"):
+        expect_http_code(webserver, "200", "http://localhost/index.html")
+
+    with subtest("client can not access status page"):
+        expect_http_code(client, "403", "http://10.0.0.1/nginx_status")
+
+    with subtest("client can access other page"):
+        expect_http_code(client, "200", "http://10.0.0.1/index.html")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/nixos-test-driver/busybox.nix b/nixpkgs/nixos/tests/nixos-test-driver/busybox.nix
new file mode 100644
index 000000000000..426f4494436e
--- /dev/null
+++ b/nixpkgs/nixos/tests/nixos-test-driver/busybox.nix
@@ -0,0 +1,16 @@
+{
+  name = "Test that basic tests work when busybox is installed";
+
+  nodes = {
+    machine = ({ pkgs, ... }: {
+      environment.systemPackages = [
+        pkgs.busybox
+      ];
+    });
+  };
+
+  testScript = ''
+    start_all()
+    machine.wait_for_unit("multi-user.target")
+  '';
+}
diff --git a/nixpkgs/nixos/tests/nixos-test-driver/lib-extend.nix b/nixpkgs/nixos/tests/nixos-test-driver/lib-extend.nix
new file mode 100644
index 000000000000..4fb7cf494aed
--- /dev/null
+++ b/nixpkgs/nixos/tests/nixos-test-driver/lib-extend.nix
@@ -0,0 +1,31 @@
+{ pkgs, ... }:
+
+let
+  patchedPkgs = pkgs.extend (new: old: {
+    lib = old.lib.extend (self: super: {
+      sorry_dave = "sorry dave";
+    });
+  });
+
+  testBody = {
+    name = "demo lib overlay";
+
+    nodes = {
+      machine = { lib, ... }: {
+        environment.etc."got-lib-overlay".text = lib.sorry_dave;
+      };
+    };
+
+    # We don't need to run an actual test. Instead we build the `machine` configuration
+    # and call it a day, because that already proves that `lib` is wired up correctly.
+    # See the attrset returned at the bottom of this file.
+    testScript = "";
+  };
+
+  inherit (patchedPkgs.testers) nixosTest runNixOSTest;
+  evaluationNixosTest = nixosTest testBody;
+  evaluationRunNixOSTest = runNixOSTest testBody;
+in {
+  nixosTest = evaluationNixosTest.driver.nodes.machine.system.build.toplevel;
+  runNixOSTest = evaluationRunNixOSTest.driver.nodes.machine.system.build.toplevel;
+}
diff --git a/nixpkgs/nixos/tests/non-default-filesystems.nix b/nixpkgs/nixos/tests/non-default-filesystems.nix
index 03cc5bf709a4..08a17107dd2f 100644
--- a/nixpkgs/nixos/tests/non-default-filesystems.nix
+++ b/nixpkgs/nixos/tests/non-default-filesystems.nix
@@ -6,6 +6,31 @@
 with import ../lib/testing-python.nix { inherit system pkgs; };
 with pkgs.lib;
 {
+  bind = makeTest {
+    name = "non-default-filesystem-bind";
+
+    nodes.machine = { ... }: {
+      virtualisation.writableStore = false;
+
+      virtualisation.fileSystems."/test-bind-dir/bind" = {
+        device = "/";
+        neededForBoot = true;
+        options = [ "bind" ];
+      };
+
+      virtualisation.fileSystems."/test-bind-file/bind" = {
+        depends = [ "/nix/store" ];
+        device = builtins.toFile "empty" "";
+        neededForBoot = true;
+        options = [ "bind" ];
+      };
+    };
+
+    testScript = ''
+      machine.wait_for_unit("multi-user.target")
+    '';
+  };
+
   btrfs = makeTest
     {
       name = "non-default-filesystems-btrfs";
@@ -69,6 +94,8 @@ with pkgs.lib;
     makeTest {
       name = "non-default-filesystems-erofs";
 
+      meta.maintainers = with maintainers; [ nikstur ];
+
       nodes.machine = _: {
         virtualisation.qemu.drives = [{
           name = "non-default-filesystem";
@@ -103,4 +130,43 @@ with pkgs.lib;
         assert "erofs" in file_contents
       '';
     };
+
+  squashfs =
+    let
+      fsImage = "/tmp/non-default-filesystem.img";
+    in
+    makeTest {
+      name = "non-default-filesystems-squashfs";
+
+      meta.maintainers = with maintainers; [ nikstur ];
+
+      nodes.machine = {
+        virtualisation.qemu.drives = [{
+          name = "non-default-filesystem";
+          file = fsImage;
+          deviceExtraOpts.serial = "non-default";
+        }];
+
+        virtualisation.fileSystems."/non-default" = {
+          device = "/dev/disk/by-id/virtio-non-default";
+          fsType = "squashfs";
+          neededForBoot = true;
+        };
+      };
+
+      testScript = ''
+        import subprocess
+
+        with open("filesystem", "w") as f:
+          f.write("squashfs")
+
+        subprocess.run([
+          "${pkgs.squashfsTools}/bin/mksquashfs",
+          "filesystem",
+          "${fsImage}",
+        ])
+
+        assert "squashfs" in machine.succeed("cat /non-default/filesystem")
+      '';
+    };
 }
diff --git a/nixpkgs/nixos/tests/noto-fonts-cjk-qt-default-weight.nix b/nixpkgs/nixos/tests/noto-fonts-cjk-qt-default-weight.nix
index 678013cf3ab9..c2e0cb3adaeb 100644
--- a/nixpkgs/nixos/tests/noto-fonts-cjk-qt-default-weight.nix
+++ b/nixpkgs/nixos/tests/noto-fonts-cjk-qt-default-weight.nix
@@ -5,7 +5,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
   nodes.machine = {
     imports = [ ./common/x11.nix ];
     fonts = {
-      enableDefaultFonts = false;
+      enableDefaultPackages = false;
       fonts = [ pkgs.noto-fonts-cjk-sans ];
     };
   };
diff --git a/nixpkgs/nixos/tests/noto-fonts.nix b/nixpkgs/nixos/tests/noto-fonts.nix
index 0515f16d101c..edbb0db4cb7a 100644
--- a/nixpkgs/nixos/tests/noto-fonts.nix
+++ b/nixpkgs/nixos/tests/noto-fonts.nix
@@ -4,9 +4,9 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
 
   nodes.machine = {
     imports = [ ./common/x11.nix ];
-    environment.systemPackages = [ pkgs.gnome.gedit ];
+    environment.systemPackages = [ pkgs.gedit ];
     fonts = {
-      enableDefaultFonts = false;
+      enableDefaultPackages = false;
       fonts = with pkgs;[
         noto-fonts
         noto-fonts-cjk-sans
diff --git a/nixpkgs/nixos/tests/opentelemetry-collector.nix b/nixpkgs/nixos/tests/opentelemetry-collector.nix
new file mode 100644
index 000000000000..9a56a22ca47e
--- /dev/null
+++ b/nixpkgs/nixos/tests/opentelemetry-collector.nix
@@ -0,0 +1,76 @@
+import ./make-test-python.nix ({ pkgs, ...} : let
+  port = 4318;
+in {
+  name = "opentelemetry-collector";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ tylerjl ];
+  };
+
+  nodes.machine = { ... }: {
+    networking.firewall.allowedTCPPorts = [ port ];
+    services.opentelemetry-collector = {
+      enable = true;
+      settings = {
+        exporters.logging.verbosity = "detailed";
+        receivers.otlp.protocols.http = {};
+        service = {
+          pipelines.logs = {
+            receivers = [ "otlp" ];
+            exporters = [ "logging" ];
+          };
+        };
+      };
+    };
+    virtualisation.forwardPorts = [{
+      host.port = port;
+      guest.port = port;
+    }];
+  };
+
+  extraPythonPackages = p: [
+    p.requests
+    p.types-requests
+  ];
+
+  # Send a log event through the OTLP pipeline and check for its
+  # presence in the collector logs.
+  testScript = /* python */ ''
+    import requests
+    import time
+
+    from uuid import uuid4
+
+    flag = str(uuid4())
+
+    machine.wait_for_unit("opentelemetry-collector.service")
+    machine.wait_for_open_port(${toString port})
+
+    event = {
+        "resourceLogs": [
+            {
+                "resource": {"attributes": []},
+                "scopeLogs": [
+                    {
+                        "logRecords": [
+                            {
+                                "timeUnixNano": str(time.time_ns()),
+                                "severityNumber": 9,
+                                "severityText": "Info",
+                                "name": "logTest",
+                                "body": {
+                                    "stringValue": flag
+                                },
+                                "attributes": []
+                            },
+                        ]
+                    }
+                ]
+            }
+        ]
+    }
+
+    response = requests.post("http://localhost:${toString port}/v1/logs", json=event)
+    assert response.status_code == 200
+    assert flag in machine.execute("journalctl -u opentelemetry-collector")[-1]
+  '';
+})
diff --git a/nixpkgs/nixos/tests/os-prober.nix b/nixpkgs/nixos/tests/os-prober.nix
index 8f3e2494047c..22e720824c80 100644
--- a/nixpkgs/nixos/tests/os-prober.nix
+++ b/nixpkgs/nixos/tests/os-prober.nix
@@ -8,7 +8,7 @@ let
       ${parted}/bin/parted --script /dev/vda mklabel msdos
       ${parted}/sbin/parted --script /dev/vda -- mkpart primary ext2 1M -1s
       mkdir /mnt
-      ${e2fsprogs}/bin/mkfs.ext4 /dev/vda1
+      ${e2fsprogs}/bin/mkfs.ext4 -O '^metadata_csum_seed' /dev/vda1
       ${util-linux}/bin/mount -t ext4 /dev/vda1 /mnt
 
       if test -e /mnt/.debug; then
@@ -83,6 +83,8 @@ in {
           docbook5
           docbook_xsl_ns
           grub2
+          kbd
+          kbd.dev
           kmod.dev
           libarchive
           libarchive.dev
diff --git a/nixpkgs/nixos/tests/osquery.nix b/nixpkgs/nixos/tests/osquery.nix
new file mode 100644
index 000000000000..9aa9820e50c5
--- /dev/null
+++ b/nixpkgs/nixos/tests/osquery.nix
@@ -0,0 +1,52 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }:
+
+let
+  config_refresh = "10";
+  nullvalue = "NULL";
+  utc = false;
+in
+{
+  name = "osquery";
+  meta.maintainers = with lib.maintainers; [ znewman01 lewo ];
+
+  nodes.machine = { config, pkgs, ... }: {
+    services.osquery = {
+      enable = true;
+
+      settings.options = { inherit nullvalue utc; };
+      flags = {
+        inherit config_refresh;
+        nullvalue = "IGNORED";
+      };
+    };
+  };
+
+  testScript = { nodes, ... }:
+    let
+      cfg = nodes.machine.services.osquery;
+    in
+    ''
+      machine.start()
+      machine.wait_for_unit("osqueryd.service")
+
+      # Stop the osqueryd service so that we can use osqueryi to check information stored in the database.
+      machine.wait_until_succeeds("systemctl stop osqueryd.service")
+
+      # osqueryd was able to query information about the host.
+      machine.succeed("echo 'SELECT address FROM etc_hosts LIMIT 1;' | osqueryi | tee /dev/console | grep -q '127.0.0.1'")
+
+      # osquery binaries respect configuration from the Nix config option.
+      machine.succeed("echo 'SELECT value FROM osquery_flags WHERE name = \"utc\";' | osqueryi | tee /dev/console | grep -q ${boolToString utc}")
+
+      # osquery binaries respect configuration from the Nix flags option.
+      machine.succeed("echo 'SELECT value FROM osquery_flags WHERE name = \"config_refresh\";' | osqueryi | tee /dev/console | grep -q ${config_refresh}")
+
+      # Demonstrate that osquery binaries prefer configuration plugin options over CLI flags.
+      # https://osquery.readthedocs.io/en/latest/deployment/configuration/#options.
+      machine.succeed("echo 'SELECT value FROM osquery_flags WHERE name = \"nullvalue\";' | osqueryi | tee /dev/console | grep -q ${nullvalue}")
+
+      # Module creates directories for default database_path and pidfile flag values.
+      machine.succeed("test -d $(dirname ${cfg.flags.database_path})")
+      machine.succeed("test -d $(dirname ${cfg.flags.pidfile})")
+    '';
+})
diff --git a/nixpkgs/nixos/tests/paperless.nix b/nixpkgs/nixos/tests/paperless.nix
index 7f36de4c29b7..ce6a4d8128df 100644
--- a/nixpkgs/nixos/tests/paperless.nix
+++ b/nixpkgs/nixos/tests/paperless.nix
@@ -30,20 +30,27 @@ import ./make-test-python.nix ({ lib, ... }: {
     with subtest("Task-queue gets ready"):
         machine.wait_for_unit("paperless-task-queue.service")
 
-    with subtest("Add a document via the web interface"):
+    with subtest("Add a png document via the web interface"):
         machine.succeed(
             "convert -size 400x40 xc:white -font 'DejaVu-Sans' -pointsize 20 -fill black "
             "-annotate +5+20 'hello web 16-10-2005' /tmp/webdoc.png"
         )
         machine.wait_until_succeeds("curl -u admin:admin -F document=@/tmp/webdoc.png -fs localhost:28981/api/documents/post_document/")
 
+    with subtest("Add a txt document via the web interface"):
+        machine.succeed(
+            "echo 'hello web 16-10-2005' > /tmp/webdoc.txt"
+        )
+        machine.wait_until_succeeds("curl -u admin:admin -F document=@/tmp/webdoc.txt -fs localhost:28981/api/documents/post_document/")
+
     with subtest("Documents are consumed"):
         machine.wait_until_succeeds(
-            "(($(curl -u admin:admin -fs localhost:28981/api/documents/ | jq .count) == 2))"
+            "(($(curl -u admin:admin -fs localhost:28981/api/documents/ | jq .count) == 3))"
         )
         docs = json.loads(machine.succeed("curl -u admin:admin -fs localhost:28981/api/documents/"))['results']
         assert "2005-10-16" in docs[0]['created']
         assert "2005-10-16" in docs[1]['created']
+        assert "2005-10-16" in docs[2]['created']
 
     # Detects gunicorn issues, see PR #190888
     with subtest("Document metadata can be accessed"):
@@ -52,5 +59,8 @@ import ./make-test-python.nix ({ lib, ... }: {
 
         metadata = json.loads(machine.succeed("curl -u admin:admin -fs localhost:28981/api/documents/2/metadata/"))
         assert "original_checksum" in metadata
+
+        metadata = json.loads(machine.succeed("curl -u admin:admin -fs localhost:28981/api/documents/3/metadata/"))
+        assert "original_checksum" in metadata
   '';
 })
diff --git a/nixpkgs/nixos/tests/pgbouncer.nix b/nixpkgs/nixos/tests/pgbouncer.nix
new file mode 100644
index 000000000000..1e72327d4200
--- /dev/null
+++ b/nixpkgs/nixos/tests/pgbouncer.nix
@@ -0,0 +1,61 @@
+import ./make-test-python.nix ({ pkgs, ... } :
+let
+  testAuthFile = pkgs.writeTextFile {
+    name = "authFile";
+    text = ''
+      "testuser" "testpass"
+    '';
+  };
+in
+{
+  name = "pgbouncer";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ _1000101 ];
+  };
+  nodes = {
+    one = { config, pkgs, ... }: {
+
+      systemd.services.postgresql = {
+        postStart = ''
+            ${pkgs.postgresql}/bin/psql -U postgres -c "ALTER ROLE testuser WITH LOGIN PASSWORD 'testpass'";
+        '';
+      };
+
+      services = {
+        postgresql = {
+          enable = true;
+          ensureDatabases = [ "testdb" ];
+          ensureUsers = [
+          {
+            name = "testuser";
+            ensurePermissions = {
+              "DATABASE testdb" = "ALL PRIVILEGES";
+            };
+          }];
+          authentication = ''
+            local testdb testuser scram-sha-256
+          '';
+        };
+
+        pgbouncer = {
+          enable = true;
+          listenAddress = "localhost";
+          databases = { testdb = "host=/run/postgresql/ port=5432 auth_user=testuser dbname=testdb"; };
+          authType = "scram-sha-256";
+          authFile = testAuthFile;
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+    one.wait_for_unit("default.target")
+    one.require_unit_state("pgbouncer.service", "active")
+
+    # Test if we can make a query through PgBouncer
+    one.wait_until_succeeds(
+        "psql 'postgres://testuser:testpass@localhost:6432/testdb' -c 'SELECT 1;'"
+    )
+  '';
+})
diff --git a/nixpkgs/nixos/tests/plasma-bigscreen.nix b/nixpkgs/nixos/tests/plasma-bigscreen.nix
index 1c61cafcbff3..2fe90fa9b539 100644
--- a/nixpkgs/nixos/tests/plasma-bigscreen.nix
+++ b/nixpkgs/nixos/tests/plasma-bigscreen.nix
@@ -22,14 +22,11 @@ import ./make-test-python.nix ({ pkgs, ...} :
     users.users.alice.extraGroups = ["uinput"];
   };
 
-  testScript = { nodes, ... }: let
-    user = nodes.machine.users.users.alice;
-    xdo = "${pkgs.xdotool}/bin/xdotool";
-  in ''
+  testScript = { nodes, ... }: ''
     with subtest("Wait for login"):
         start_all()
-        machine.wait_for_file("${user.home}/.Xauthority")
-        machine.succeed("xauth merge ${user.home}/.Xauthority")
+        machine.wait_for_file("/tmp/xauth_*")
+        machine.succeed("xauth merge /tmp/xauth_*")
 
     with subtest("Check plasmashell started"):
         machine.wait_until_succeeds("pgrep plasmashell")
diff --git a/nixpkgs/nixos/tests/plasma5-systemd-start.nix b/nixpkgs/nixos/tests/plasma5-systemd-start.nix
index f584c1ec137a..31a313af308b 100644
--- a/nixpkgs/nixos/tests/plasma5-systemd-start.nix
+++ b/nixpkgs/nixos/tests/plasma5-systemd-start.nix
@@ -23,13 +23,11 @@ import ./make-test-python.nix ({ pkgs, ...} :
     };
   };
 
-  testScript = { nodes, ... }: let
-    user = nodes.machine.config.users.users.alice;
-  in ''
+  testScript = { nodes, ... }: ''
     with subtest("Wait for login"):
         start_all()
-        machine.wait_for_file("${user.home}/.Xauthority")
-        machine.succeed("xauth merge ${user.home}/.Xauthority")
+        machine.wait_for_file("/tmp/xauth_*")
+        machine.succeed("xauth merge /tmp/xauth_*")
 
     with subtest("Check plasmashell started"):
         machine.wait_until_succeeds("pgrep plasmashell")
diff --git a/nixpkgs/nixos/tests/plasma5.nix b/nixpkgs/nixos/tests/plasma5.nix
index b3836cf641d4..fb8a5b73832e 100644
--- a/nixpkgs/nixos/tests/plasma5.nix
+++ b/nixpkgs/nixos/tests/plasma5.nix
@@ -13,10 +13,8 @@ import ./make-test-python.nix ({ pkgs, ...} :
     services.xserver.enable = true;
     services.xserver.displayManager.sddm.enable = true;
     services.xserver.displayManager.defaultSession = "plasma";
-    services.xserver.desktopManager.plasma5 = {
-      enable = true;
-      excludePackages = [ pkgs.plasma5Packages.elisa ];
-    };
+    services.xserver.desktopManager.plasma5.enable = true;
+    environment.plasma5.excludePackages = [ pkgs.plasma5Packages.elisa ];
     services.xserver.displayManager.autoLogin = {
       enable = true;
       user = "alice";
@@ -25,13 +23,13 @@ import ./make-test-python.nix ({ pkgs, ...} :
   };
 
   testScript = { nodes, ... }: let
-    user = nodes.machine.config.users.users.alice;
+    user = nodes.machine.users.users.alice;
     xdo = "${pkgs.xdotool}/bin/xdotool";
   in ''
     with subtest("Wait for login"):
         start_all()
-        machine.wait_for_file("${user.home}/.Xauthority")
-        machine.succeed("xauth merge ${user.home}/.Xauthority")
+        machine.wait_for_file("/tmp/xauth_*")
+        machine.succeed("xauth merge /tmp/xauth_*")
 
     with subtest("Check plasmashell started"):
         machine.wait_until_succeeds("pgrep plasmashell")
@@ -46,6 +44,8 @@ import ./make-test-python.nix ({ pkgs, ...} :
     with subtest("Ensure Elisa is not installed"):
         machine.fail("which elisa")
 
+    machine.succeed("su - ${user.name} -c 'xauth merge /tmp/xauth_*'")
+
     with subtest("Run Dolphin"):
         machine.execute("su - ${user.name} -c 'DISPLAY=:0.0 dolphin >&2 &'")
         machine.wait_for_window(" Dolphin")
diff --git a/nixpkgs/nixos/tests/powerdns.nix b/nixpkgs/nixos/tests/powerdns.nix
index d3708d25f0fb..599d5ea67efe 100644
--- a/nixpkgs/nixos/tests/powerdns.nix
+++ b/nixpkgs/nixos/tests/powerdns.nix
@@ -28,8 +28,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
   };
 
   testScript = ''
-    import re
-
     with subtest("PowerDNS database exists"):
         server.wait_for_unit("mysql")
         server.succeed("echo 'SHOW DATABASES;' | sudo -u pdns mysql -u pdns >&2")
@@ -46,11 +44,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
 
     with subtest("Adding an example zone works"):
         # Extract configuration file needed by pdnsutil
-        unit = server.succeed("systemctl cat pdns")
-        match = re.search("(--config-dir=[^ ]+)", unit)
-        assert(match is not None)
-        conf = match.group(1)
-        pdnsutil = "sudo -u pdns pdnsutil " + conf
+        pdnsutil = "sudo -u pdns pdnsutil "
         server.succeed(f"{pdnsutil} create-zone example.com ns1.example.com")
         server.succeed(f"{pdnsutil} add-record  example.com ns1 A 192.168.1.2")
 
diff --git a/nixpkgs/nixos/tests/prometheus-exporters.nix b/nixpkgs/nixos/tests/prometheus-exporters.nix
index a69f2347b54b..23740dd98e3d 100644
--- a/nixpkgs/nixos/tests/prometheus-exporters.nix
+++ b/nixpkgs/nixos/tests/prometheus-exporters.nix
@@ -6,7 +6,7 @@
 let
   inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
   inherit (pkgs.lib) concatStringsSep maintainers mapAttrs mkMerge
-    removeSuffix replaceStrings singleton splitString;
+    removeSuffix replaceStrings singleton splitString makeBinPath;
 
   /*
     * The attrset `exporterTests` contains one attribute
@@ -914,6 +914,47 @@ let
       '';
     };
 
+    php-fpm = {
+      nodeName = "php_fpm";
+      exporterConfig = {
+        enable = true;
+        environmentFile = pkgs.writeTextFile {
+          name = "/tmp/prometheus-php-fpm-exporter.env";
+          text = ''
+            PHP_FPM_SCRAPE_URI="tcp://127.0.0.1:9000/status"
+          '';
+        };
+      };
+      metricProvider = {
+        users.users."php-fpm-exporter" = {
+          isSystemUser = true;
+          group  = "php-fpm-exporter";
+        };
+        users.groups."php-fpm-exporter" = {};
+        services.phpfpm.pools."php-fpm-exporter" = {
+          user = "php-fpm-exporter";
+          group = "php-fpm-exporter";
+          settings = {
+            "pm" = "dynamic";
+            "pm.max_children" = 32;
+            "pm.max_requests" = 500;
+            "pm.start_servers" = 2;
+            "pm.min_spare_servers" = 2;
+            "pm.max_spare_servers" = 5;
+            "pm.status_path" = "/status";
+            "listen" = "127.0.0.1:9000";
+            "listen.allowed_clients" = "127.0.0.1";
+          };
+          phpEnv."PATH" = makeBinPath [ pkgs.php ];
+        };
+      };
+      exporterTest = ''
+        wait_for_unit("phpfpm-php-fpm-exporter.service")
+        wait_for_unit("prometheus-php-fpm-exporter.service")
+        succeed("curl -sSf http://localhost:9253/metrics | grep 'phpfpm_up{.*} 1'")
+      '';
+    };
+
     postfix = {
       exporterConfig = {
         enable = true;
@@ -1085,6 +1126,22 @@ let
       '';
     };
 
+    scaphandre = {
+      exporterConfig = {
+        enable = true;
+      };
+      metricProvider = {
+        boot.kernelModules = [ "intel_rapl_common" ];
+      };
+      exporterTest = ''
+        wait_for_unit("prometheus-scaphandre-exporter.service")
+        wait_for_open_port(8080)
+        wait_until_succeeds(
+            "curl -sSf 'localhost:8080/metrics'"
+        )
+      '';
+    };
+
     shelly = {
       exporterConfig = {
         enable = true;
diff --git a/nixpkgs/nixos/tests/public-inbox.nix b/nixpkgs/nixos/tests/public-inbox.nix
index 784ef9e3dc28..4d06d3e1738e 100644
--- a/nixpkgs/nixos/tests/public-inbox.nix
+++ b/nixpkgs/nixos/tests/public-inbox.nix
@@ -223,5 +223,8 @@ in
     # require to use --all
     machine.succeed("curl -L https://machine.${domain}/inbox/repo1/repo1@root-1/raw | sudo -u public-inbox public-inbox-learn rm --all")
     machine.fail("curl -L https://machine.${domain}/inbox/repo1/repo1@root-1/T/#u | grep 'This is a testing mail.'")
+
+    # Compact the database
+    machine.succeed("sudo -u public-inbox public-inbox-compact --all")
   '';
 })
diff --git a/nixpkgs/nixos/tests/qemu-vm-volatile-root.nix b/nixpkgs/nixos/tests/qemu-vm-volatile-root.nix
new file mode 100644
index 000000000000..bc8fd853409d
--- /dev/null
+++ b/nixpkgs/nixos/tests/qemu-vm-volatile-root.nix
@@ -0,0 +1,17 @@
+# Test that the root filesystem is a volatile tmpfs.
+
+{ lib, ... }:
+
+{
+  name = "qemu-vm-volatile-root";
+
+  meta.maintainers = with lib.maintainers; [ nikstur ];
+
+  nodes.machine = _: {
+    virtualisation.diskImage = null;
+  };
+
+  testScript = ''
+    machine.succeed("findmnt --kernel --types tmpfs /")
+  '';
+}
diff --git a/nixpkgs/nixos/tests/qownnotes.nix b/nixpkgs/nixos/tests/qownnotes.nix
new file mode 100644
index 000000000000..93801cb98702
--- /dev/null
+++ b/nixpkgs/nixos/tests/qownnotes.nix
@@ -0,0 +1,70 @@
+import ./make-test-python.nix ({ lib, pkgs, ...} :
+
+{
+  name = "qownnotes";
+  meta.maintainers = [ lib.maintainers.pbek ];
+
+  nodes.machine = { ... }:
+
+  {
+    imports = [
+      ./common/user-account.nix
+      ./common/x11.nix
+    ];
+
+    test-support.displayManager.auto.user = "alice";
+    environment.systemPackages = [
+      pkgs.qownnotes
+      pkgs.xdotool
+    ];
+  };
+
+  enableOCR = true;
+
+  testScript = { nodes, ... }: let
+    aliceDo = cmd: ''machine.succeed("su - alice -c '${cmd}' >&2 &");'';
+    in ''
+    with subtest("Ensure X starts"):
+        start_all()
+        machine.wait_for_x()
+
+    with subtest("Check QOwnNotes version on CLI"):
+        ${aliceDo "qownnotes --version"}
+
+        machine.wait_for_console_text("QOwnNotes ${pkgs.qownnotes.version}")
+
+    with subtest("Ensure QOwnNotes starts"):
+        # start QOwnNotes window
+        ${aliceDo "qownnotes"}
+
+        machine.wait_for_text("Welcome to QOwnNotes")
+        machine.screenshot("QOwnNotes-Welcome")
+
+    with subtest("Finish first-run wizard"):
+        # The wizard should show up now
+        machine.wait_for_text("Note folder")
+        machine.send_key("ret")
+        machine.wait_for_console_text("Note path '/home/alice/Notes' was now created.")
+        machine.wait_for_text("Panel layout")
+        machine.send_key("ret")
+        machine.wait_for_text("Nextcloud")
+        machine.send_key("ret")
+        machine.wait_for_text("App metric")
+        machine.send_key("ret")
+
+        # The main window should now show up
+        machine.wait_for_text("QOwnNotes - ${pkgs.qownnotes.version}")
+        machine.wait_for_open_port(22222)
+        machine.wait_for_console_text("QOwnNotes server listening on port 22222")
+
+        machine.screenshot("QOwnNotes-DemoNote")
+
+    with subtest("Create a new note"):
+        machine.send_key("ctrl-n")
+        machine.sleep(1)
+        machine.send_chars("This is a NixOS test!\n")
+        machine.wait_for_text("This is a NixOS test!")
+
+        machine.screenshot("QOwnNotes-NewNote")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/retroarch.nix b/nixpkgs/nixos/tests/retroarch.nix
index f4bf232ea725..0e5f60aa8be2 100644
--- a/nixpkgs/nixos/tests/retroarch.nix
+++ b/nixpkgs/nixos/tests/retroarch.nix
@@ -30,8 +30,8 @@ import ./make-test-python.nix ({ pkgs, ... }:
       in ''
         with subtest("Wait for login"):
             start_all()
-            machine.wait_for_file("${user.home}/.Xauthority")
-            machine.succeed("xauth merge ${user.home}/.Xauthority")
+            machine.wait_for_file("/tmp/xauth_*")
+            machine.succeed("xauth merge /tmp/xauth_*")
 
         with subtest("Check RetroArch started"):
             machine.wait_until_succeeds("pgrep retroarch")
diff --git a/nixpkgs/nixos/tests/samba-wsdd.nix b/nixpkgs/nixos/tests/samba-wsdd.nix
index 0e3185b0c684..666a626d1b4a 100644
--- a/nixpkgs/nixos/tests/samba-wsdd.nix
+++ b/nixpkgs/nixos/tests/samba-wsdd.nix
@@ -8,25 +8,23 @@ import ./make-test-python.nix ({ pkgs, ... }:
     client_wsdd = { pkgs, ... }: {
       services.samba-wsdd = {
         enable = true;
+        openFirewall = true;
         interface = "eth1";
         workgroup = "WORKGROUP";
         hostname = "CLIENT-WSDD";
         discovery = true;
         extraOptions = [ "--no-host" ];
       };
-      networking.firewall.allowedTCPPorts = [ 5357 ];
-      networking.firewall.allowedUDPPorts = [ 3702 ];
     };
 
     server_wsdd = { ... }: {
       services.samba-wsdd = {
         enable = true;
+        openFirewall = true;
         interface = "eth1";
         workgroup = "WORKGROUP";
         hostname = "SERVER-WSDD";
       };
-      networking.firewall.allowedTCPPorts = [ 5357 ];
-      networking.firewall.allowedUDPPorts = [ 3702 ];
     };
   };
 
diff --git a/nixpkgs/nixos/tests/scaphandre.nix b/nixpkgs/nixos/tests/scaphandre.nix
new file mode 100644
index 000000000000..f0a411748503
--- /dev/null
+++ b/nixpkgs/nixos/tests/scaphandre.nix
@@ -0,0 +1,18 @@
+import ./make-test-python.nix {
+  name = "scaphandre";
+
+  nodes = {
+    scaphandre = { pkgs, ... } : {
+      boot.kernelModules = [ "intel_rapl_common" ];
+
+      environment.systemPackages = [ pkgs.scaphandre ];
+    };
+  };
+
+  testScript = { nodes, ... } : ''
+    scaphandre.start()
+    scaphandre.wait_until_succeeds(
+        "scaphandre stdout -t 4",
+    )
+  '';
+}
diff --git a/nixpkgs/nixos/tests/sddm.nix b/nixpkgs/nixos/tests/sddm.nix
index c76a9683e66d..b6c05deac05e 100644
--- a/nixpkgs/nixos/tests/sddm.nix
+++ b/nixpkgs/nixos/tests/sddm.nix
@@ -23,14 +23,14 @@ let
       enableOCR = true;
 
       testScript = { nodes, ... }: let
-        user = nodes.machine.config.users.users.alice;
+        user = nodes.machine.users.users.alice;
       in ''
         start_all()
         machine.wait_for_text("(?i)select your user")
         machine.screenshot("sddm")
         machine.send_chars("${user.password}\n")
-        machine.wait_for_file("${user.home}/.Xauthority")
-        machine.succeed("xauth merge ${user.home}/.Xauthority")
+        machine.wait_for_file("/tmp/xauth_*")
+        machine.succeed("xauth merge /tmp/xauth_*")
         machine.wait_for_window("^IceWM ")
       '';
     };
@@ -55,12 +55,10 @@ let
         services.xserver.windowManager.icewm.enable = true;
       };
 
-      testScript = { nodes, ... }: let
-        user = nodes.machine.config.users.users.alice;
-      in ''
+      testScript = { nodes, ... }: ''
         start_all()
-        machine.wait_for_file("${user.home}/.Xauthority")
-        machine.succeed("xauth merge ${user.home}/.Xauthority")
+        machine.wait_for_file("/tmp/xauth_*")
+        machine.succeed("xauth merge /tmp/xauth_*")
         machine.wait_for_window("^IceWM ")
       '';
     };
diff --git a/nixpkgs/nixos/tests/sftpgo.nix b/nixpkgs/nixos/tests/sftpgo.nix
index ca55b9c962a0..8cd5675c1d4d 100644
--- a/nixpkgs/nixos/tests/sftpgo.nix
+++ b/nixpkgs/nixos/tests/sftpgo.nix
@@ -12,8 +12,6 @@
 # would be a nice to have for the future.
 { pkgs, lib, ...  }:
 
-with lib;
-
 let
   inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
 
@@ -54,7 +52,7 @@ let
       # inside the dataprovider they will be automatically created.
       # You have to create the folder on the filesystem yourself
       virtual_folders =
-        optional (isMemberOf config sharedFolderName user) {
+        lib.optional (lib.isMemberOf config sharedFolderName user) {
           name = sharedFolderName;
           mapped_path = "${config.services.sftpgo.dataDir}/${sharedFolderName}";
           virtual_path = "/${sharedFolderName}";
@@ -62,10 +60,10 @@ let
 
       # Defines the ACL on the virtual filesystem
       permissions =
-        recursiveUpdate {
+        lib.recursiveUpdate {
           "/" = [ "list" ];     # read-only top level directory
           "/private" = [ "*" ]; # private subdirectory, not shared with others
-        } (optionalAttrs (isMemberOf config "shared" user) {
+        } (lib.optionalAttrs (lib.isMemberOf config "shared" user) {
           "/shared" = [ "*" ];
         });
 
@@ -91,7 +89,7 @@ let
   # of users and folders to import to SFTPGo.
   loadDataJson = config: pkgs.writeText "users-and-folders.json" (builtins.toJSON {
     users =
-      mapAttrsToList (name: user: generateUserAttrSet config user) (normalUsers config);
+      lib.mapAttrsToList (name: user: lib.generateUserAttrSet config user) (normalUsers config);
 
     folders = [
       {
diff --git a/nixpkgs/nixos/tests/sing-box.nix b/nixpkgs/nixos/tests/sing-box.nix
new file mode 100644
index 000000000000..582d594be3fd
--- /dev/null
+++ b/nixpkgs/nixos/tests/sing-box.nix
@@ -0,0 +1,48 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
+
+  name = "sing-box";
+
+  meta = {
+    maintainers = with lib.maintainers; [ nickcao ];
+  };
+
+  nodes.machine = { pkgs, ... }: {
+    environment.systemPackages = [ pkgs.curl ];
+    services.nginx = {
+      enable = true;
+      statusPage = true;
+    };
+    services.sing-box = {
+      enable = true;
+      settings = {
+        inbounds = [{
+          type = "mixed";
+          tag = "inbound";
+          listen = "127.0.0.1";
+          listen_port = 1080;
+          users = [{
+            username = "user";
+            password = { _secret = pkgs.writeText "password" "supersecret"; };
+          }];
+        }];
+        outbounds = [{
+          type = "direct";
+          tag = "outbound";
+        }];
+      };
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("nginx.service")
+    machine.wait_for_unit("sing-box.service")
+
+    machine.wait_for_open_port(80)
+    machine.wait_for_open_port(1080)
+
+    machine.succeed("curl --fail --max-time 10 --proxy http://user:supersecret@localhost:1080 http://localhost")
+    machine.fail("curl --fail --max-time 10 --proxy http://user:supervillain@localhost:1080 http://localhost")
+    machine.succeed("curl --fail --max-time 10 --proxy socks5://user:supersecret@localhost:1080 http://localhost")
+  '';
+
+})
diff --git a/nixpkgs/nixos/tests/snapper.nix b/nixpkgs/nixos/tests/snapper.nix
index 651adc8934d3..674523584fda 100644
--- a/nixpkgs/nixos/tests/snapper.nix
+++ b/nixpkgs/nixos/tests/snapper.nix
@@ -15,7 +15,7 @@ import ./make-test-python.nix ({ ... }:
         fsType = "btrfs";
       };
     };
-    services.snapper.configs.home.subvolume = "/home";
+    services.snapper.configs.home.SUBVOLUME = "/home";
     services.snapper.filters = "/nix";
   };
 
diff --git a/nixpkgs/nixos/tests/sway.nix b/nixpkgs/nixos/tests/sway.nix
index 52e2c7c99ec4..695d4a770810 100644
--- a/nixpkgs/nixos/tests/sway.nix
+++ b/nixpkgs/nixos/tests/sway.nix
@@ -45,9 +45,13 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
           regular2 = foreground;
         };
       };
+
+      etc."gpg-agent.conf".text = ''
+        pinentry-timeout 86400
+      '';
     };
 
-    fonts.fonts = [ pkgs.inconsolata ];
+    fonts.packages = [ pkgs.inconsolata ];
 
     # Automatically configure and start Sway when logging in on tty1:
     programs.bash.loginShellInit = ''
@@ -71,16 +75,53 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     virtualisation.qemu.options = [ "-vga none -device virtio-gpu-pci" ];
   };
 
-  enableOCR = true;
-
   testScript = { nodes, ... }: ''
     import shlex
+    import json
+
+    q = shlex.quote
+    NODE_GROUPS = ["nodes", "floating_nodes"]
+
+
+    def swaymsg(command: str = "", succeed=True, type="command"):
+        assert command != "" or type != "command", "Must specify command or type"
+        shell = q(f"swaymsg -t {q(type)} -- {q(command)}")
+        with machine.nested(
+            f"sending swaymsg {shell!r}" + " (allowed to fail)" * (not succeed)
+        ):
+            ret = (machine.succeed if succeed else machine.execute)(
+                f"su - alice -c {shell}"
+            )
+
+        # execute also returns a status code, but disregard.
+        if not succeed:
+            _, ret = ret
+
+        if not succeed and not ret:
+            return None
+
+        parsed = json.loads(ret)
+        return parsed
 
-    def swaymsg(command: str, succeed=True):
-        with machine.nested(f"sending swaymsg {command!r}" + " (allowed to fail)" * (not succeed)):
-          (machine.succeed if succeed else machine.execute)(
-            f"su - alice -c {shlex.quote('swaymsg -- ' + command)}"
-          )
+
+    def walk(tree):
+        yield tree
+        for group in NODE_GROUPS:
+            for node in tree.get(group, []):
+                yield from walk(node)
+
+
+    def wait_for_window(pattern):
+        def func(last_chance):
+            nodes = (node["name"] for node in walk(swaymsg(type="get_tree")))
+
+            if last_chance:
+                nodes = list(nodes)
+                machine.log(f"Last call! Current list of windows: {nodes}")
+
+            return any(pattern in name for name in nodes)
+
+        retry(func)
 
     start_all()
     machine.wait_for_unit("multi-user.target")
@@ -94,7 +135,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
 
     # Test XWayland (foot does not support X):
     swaymsg("exec WINIT_UNIX_BACKEND=x11 WAYLAND_DISPLAY=invalid alacritty")
-    machine.wait_for_text("alice@machine")
+    wait_for_window("alice@machine")
     machine.send_chars("test-x11\n")
     machine.wait_for_file("/tmp/test-x11-exit-ok")
     print(machine.succeed("cat /tmp/test-x11.out"))
@@ -106,7 +147,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     machine.send_key("alt-3")
     machine.sleep(3)
     machine.send_key("alt-ret")
-    machine.wait_for_text("alice@machine")
+    wait_for_window("alice@machine")
     machine.send_chars("test-wayland\n")
     machine.wait_for_file("/tmp/test-wayland-exit-ok")
     print(machine.succeed("cat /tmp/test-wayland.out"))
@@ -117,16 +158,24 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
 
     # Test gpg-agent starting pinentry-gnome3 via D-Bus (tests if
     # $WAYLAND_DISPLAY is correctly imported into the D-Bus user env):
-    swaymsg("exec gpg --no-tty --yes --quick-generate-key test")
+    swaymsg("exec mkdir -p ~/.gnupg")
+    swaymsg("exec cp /etc/gpg-agent.conf ~/.gnupg")
+
+    swaymsg("exec DISPLAY=INVALID gpg --no-tty --yes --quick-generate-key test", succeed=False)
     machine.wait_until_succeeds("pgrep --exact gpg")
-    machine.wait_for_text("Passphrase")
+    wait_for_window("gpg")
+    machine.succeed("pgrep --exact gpg")
     machine.screenshot("gpg_pinentry")
     machine.send_key("alt-shift-q")
     machine.wait_until_fails("pgrep --exact gpg")
 
     # Test swaynag:
+    def get_height():
+        return [node['rect']['height'] for node in walk(swaymsg(type="get_tree")) if node['focused']][0]
+
+    before = get_height()
     machine.send_key("alt-shift-e")
-    machine.wait_for_text("You pressed the exit shortcut.")
+    retry(lambda _: get_height() < before)
     machine.screenshot("sway_exit")
 
     swaymsg("exec swaylock")
diff --git a/nixpkgs/nixos/tests/switch-test.nix b/nixpkgs/nixos/tests/switch-test.nix
index f891a2cb2f4c..f44dede7fef4 100644
--- a/nixpkgs/nixos/tests/switch-test.nix
+++ b/nixpkgs/nixos/tests/switch-test.nix
@@ -70,6 +70,19 @@ in {
           };
         };
 
+        simpleServiceSeparateActivationScript.configuration = {
+          system.activatable = false;
+          systemd.services.test = {
+            wantedBy = [ "multi-user.target" ];
+            serviceConfig = {
+              Type = "oneshot";
+              RemainAfterExit = true;
+              ExecStart = "${pkgs.coreutils}/bin/true";
+              ExecReload = "${pkgs.coreutils}/bin/true";
+            };
+          };
+        };
+
         simpleServiceDifferentDescription.configuration = {
           imports = [ simpleService.configuration ];
           systemd.services.test.description = "Test unit";
@@ -482,9 +495,9 @@ in {
   };
 
   testScript = { nodes, ... }: let
-    originalSystem = nodes.machine.config.system.build.toplevel;
-    otherSystem = nodes.other.config.system.build.toplevel;
-    machine = nodes.machine.config.system.build.toplevel;
+    originalSystem = nodes.machine.system.build.toplevel;
+    otherSystem = nodes.other.system.build.toplevel;
+    machine = nodes.machine.system.build.toplevel;
 
     # Ensures failures pass through using pipefail, otherwise failing to
     # switch-to-configuration is hidden by the success of `tee`.
@@ -497,11 +510,15 @@ in {
   in /* python */ ''
     def switch_to_specialisation(system, name, action="test", fail=False):
         if name == "":
-            stc = f"{system}/bin/switch-to-configuration"
+            switcher = f"{system}/bin/switch-to-configuration"
         else:
-            stc = f"{system}/specialisation/{name}/bin/switch-to-configuration"
-        out = machine.fail(f"{stc} {action} 2>&1") if fail \
-            else machine.succeed(f"{stc} {action} 2>&1")
+            switcher = f"{system}/specialisation/{name}/bin/switch-to-configuration"
+        return run_switch(switcher, action, fail)
+
+    # like above but stc = switcher
+    def run_switch(switcher, action="test", fail=False):
+        out = machine.fail(f"{switcher} {action} 2>&1") if fail \
+            else machine.succeed(f"{switcher} {action} 2>&1")
         assert_lacks(out, "switch-to-configuration line")  # Perl warnings
         return out
 
@@ -639,6 +656,22 @@ in {
         assert_lacks(out, "the following new units were started:")
         assert_contains(out, "would start the following units: test.service\n")
 
+        out = switch_to_specialisation("${machine}", "", action="test")
+
+        # Ensure the service can be started when the activation script isn't in toplevel
+        # This is a lot like "Start a simple service", except activation-only deps could be gc-ed
+        out = run_switch("${nodes.machine.specialisation.simpleServiceSeparateActivationScript.configuration.system.build.separateActivationScript}/bin/switch-to-configuration");
+        assert_lacks(out, "installing dummy bootloader")  # test does not install a bootloader
+        assert_lacks(out, "stopping the following units:")
+        assert_lacks(out, "NOT restarting the following changed units:")
+        assert_contains(out, "reloading the following units: dbus.service\n")  # huh
+        assert_lacks(out, "\nrestarting the following units:")
+        assert_lacks(out, "\nstarting the following units:")
+        assert_contains(out, "the following new units were started: test.service\n")
+        machine.succeed("! test -e /run/current-system/activate")
+        machine.succeed("! test -e /run/current-system/dry-activate")
+        machine.succeed("! test -e /run/current-system/bin/switch-to-configuration")
+
         # Ensure \ works in unit names
         out = switch_to_specialisation("${machine}", "unitWithBackslash")
         assert_contains(out, "stopping the following units: test.service\n")
diff --git a/nixpkgs/nixos/tests/syncthing-init.nix b/nixpkgs/nixos/tests/syncthing-init.nix
index fcd90739e6a5..195c157ffb6e 100644
--- a/nixpkgs/nixos/tests/syncthing-init.nix
+++ b/nixpkgs/nixos/tests/syncthing-init.nix
@@ -1,6 +1,7 @@
 import ./make-test-python.nix ({ lib, pkgs, ... }: let
 
   testId = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU";
+  testName = "testDevice foo'bar";
 
 in {
   name = "syncthing-init";
@@ -9,14 +10,14 @@ in {
   nodes.machine = {
     services.syncthing = {
       enable = true;
-      devices.testDevice = {
+      settings.devices.testDevice = {
         id = testId;
       };
-      folders.testFolder = {
+      settings.folders.testFolder = {
         path = "/tmp/test";
         devices = [ "testDevice" ];
       };
-      extraOptions.gui.user = "guiUser";
+      settings.gui.user = "guiUser";
     };
   };
 
diff --git a/nixpkgs/nixos/tests/syncthing-no-settings.nix b/nixpkgs/nixos/tests/syncthing-no-settings.nix
new file mode 100644
index 000000000000..fee122b5e35c
--- /dev/null
+++ b/nixpkgs/nixos/tests/syncthing-no-settings.nix
@@ -0,0 +1,18 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
+  name = "syncthing";
+  meta.maintainers = with pkgs.lib.maintainers; [ chkno ];
+
+  nodes = {
+    a = {
+      environment.systemPackages = with pkgs; [ curl libxml2 syncthing ];
+      services.syncthing = {
+        enable = true;
+      };
+    };
+  };
+  # Test that indeed a syncthing-init.service systemd service is not created.
+  #
+  testScript = /* python */ ''
+    a.succeed("systemctl list-unit-files | awk '$1 == \"syncthing-init.service\" {exit 1;}'")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/systemd-boot.nix b/nixpkgs/nixos/tests/systemd-boot.nix
index 814cdc5f1443..84a4da5aa6ec 100644
--- a/nixpkgs/nixos/tests/systemd-boot.nix
+++ b/nixpkgs/nixos/tests/systemd-boot.nix
@@ -258,7 +258,8 @@ in
     '';
   };
 
-  # See: [Firmware file size bug] in systemd/default.nix
+  # Some UEFI firmwares fail on large reads. Now that systemd-boot loads initrd
+  # itself, systems with such firmware won't boot without this fix
   uefiLargeFileWorkaround = makeTest {
     name = "uefi-large-file-workaround";
 
diff --git a/nixpkgs/nixos/tests/systemd-initrd-luks-fido2.nix b/nixpkgs/nixos/tests/systemd-initrd-luks-fido2.nix
index 32c79b731d80..f9f75ab7f301 100644
--- a/nixpkgs/nixos/tests/systemd-initrd-luks-fido2.nix
+++ b/nixpkgs/nixos/tests/systemd-initrd-luks-fido2.nix
@@ -26,6 +26,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
         };
       };
       virtualisation.rootDevice = "/dev/mapper/cryptroot";
+      virtualisation.fileSystems."/".autoFormat = true;
     };
   };
 
diff --git a/nixpkgs/nixos/tests/systemd-initrd-luks-keyfile.nix b/nixpkgs/nixos/tests/systemd-initrd-luks-keyfile.nix
index 5ca0f48c333a..617c003484b9 100644
--- a/nixpkgs/nixos/tests/systemd-initrd-luks-keyfile.nix
+++ b/nixpkgs/nixos/tests/systemd-initrd-luks-keyfile.nix
@@ -34,6 +34,7 @@ in {
         };
       };
       virtualisation.rootDevice = "/dev/mapper/cryptroot";
+      virtualisation.fileSystems."/".autoFormat = true;
       boot.initrd.secrets."/etc/cryptroot.key" = keyfile;
     };
   };
diff --git a/nixpkgs/nixos/tests/systemd-initrd-luks-password.nix b/nixpkgs/nixos/tests/systemd-initrd-luks-password.nix
index a90a59feed6f..66b5022d87fd 100644
--- a/nixpkgs/nixos/tests/systemd-initrd-luks-password.nix
+++ b/nixpkgs/nixos/tests/systemd-initrd-luks-password.nix
@@ -25,6 +25,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
         cryptroot2.device = "/dev/vdc";
       };
       virtualisation.rootDevice = "/dev/mapper/cryptroot";
+      virtualisation.fileSystems."/".autoFormat = true;
       # test mounting device unlocked in initrd after switching root
       virtualisation.fileSystems."/cryptroot2".device = "/dev/mapper/cryptroot2";
     };
diff --git a/nixpkgs/nixos/tests/systemd-initrd-luks-tpm2.nix b/nixpkgs/nixos/tests/systemd-initrd-luks-tpm2.nix
index 73aa190ad620..d9dd9118a3a2 100644
--- a/nixpkgs/nixos/tests/systemd-initrd-luks-tpm2.nix
+++ b/nixpkgs/nixos/tests/systemd-initrd-luks-tpm2.nix
@@ -28,6 +28,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
         };
       };
       virtualisation.rootDevice = "/dev/mapper/cryptroot";
+      virtualisation.fileSystems."/".autoFormat = true;
     };
   };
 
diff --git a/nixpkgs/nixos/tests/systemd-initrd-networkd-ssh.nix b/nixpkgs/nixos/tests/systemd-initrd-networkd-ssh.nix
index 46dbdf537393..6aaa6c828f7b 100644
--- a/nixpkgs/nixos/tests/systemd-initrd-networkd-ssh.nix
+++ b/nixpkgs/nixos/tests/systemd-initrd-networkd-ssh.nix
@@ -2,21 +2,23 @@ import ./make-test-python.nix ({ lib, ... }: {
   name = "systemd-initrd-network-ssh";
   meta.maintainers = [ lib.maintainers.elvishjerricco ];
 
-  nodes = with lib; {
+  nodes = {
     server = { config, pkgs, ... }: {
-      environment.systemPackages = [pkgs.cryptsetup];
+      environment.systemPackages = [ pkgs.cryptsetup ];
       boot.loader.systemd-boot.enable = true;
       boot.loader.timeout = 0;
       virtualisation = {
         emptyDiskImages = [ 4096 ];
         useBootLoader = true;
-        # Booting off the encrypted disk requires an available init script from the Nix store
+        # Booting off the encrypted disk requires an available init script from
+        # the Nix store
         mountHostNixStore = true;
         useEFIBoot = true;
       };
 
       specialisation.encrypted-root.configuration = {
         virtualisation.rootDevice = "/dev/mapper/root";
+        virtualisation.fileSystems."/".autoFormat = true;
         boot.initrd.luks.devices = lib.mkVMOverride {
           root.device = "/dev/vdb";
         };
@@ -25,7 +27,7 @@ import ./make-test-python.nix ({ lib, ... }: {
           enable = true;
           ssh = {
             enable = true;
-            authorizedKeys = [ (readFile ./initrd-network-ssh/id_ed25519.pub) ];
+            authorizedKeys = [ (lib.readFile ./initrd-network-ssh/id_ed25519.pub) ];
             port = 22;
             # Terrible hack so it works with useBootLoader
             hostKeys = [ { outPath = "${./initrd-network-ssh/ssh_host_ed25519_key}"; } ];
@@ -37,13 +39,13 @@ import ./make-test-python.nix ({ lib, ... }: {
     client = { config, ... }: {
       environment.etc = {
         knownHosts = {
-          text = concatStrings [
+          text = lib.concatStrings [
             "server,"
             "${
-              toString (head (splitString " " (toString
-                (elemAt (splitString "\n" config.networking.extraHosts) 2))))
+              toString (lib.head (lib.splitString " " (toString
+                (lib.elemAt (lib.splitString "\n" config.networking.extraHosts) 2))))
             } "
-            "${readFile ./initrd-network-ssh/ssh_host_ed25519_key.pub}"
+            "${lib.readFile ./initrd-network-ssh/ssh_host_ed25519_key.pub}"
           ];
         };
         sshKey = {
diff --git a/nixpkgs/nixos/tests/systemd-initrd-swraid.nix b/nixpkgs/nixos/tests/systemd-initrd-swraid.nix
index 0d5a1c6354d0..d87170c92574 100644
--- a/nixpkgs/nixos/tests/systemd-initrd-swraid.nix
+++ b/nixpkgs/nixos/tests/systemd-initrd-swraid.nix
@@ -14,17 +14,17 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
     boot.loader.efi.canTouchEfiVariables = true;
 
     environment.systemPackages = with pkgs; [ mdadm e2fsprogs ]; # for mdadm and mkfs.ext4
+    boot.swraid = {
+      enable = true;
+      mdadmConf = ''
+        ARRAY /dev/md0 devices=/dev/vdb,/dev/vdc
+      '';
+    };
     boot.initrd = {
       systemd = {
         enable = true;
         emergencyAccess = true;
       };
-      services.swraid = {
-        enable = true;
-        mdadmConf = ''
-          ARRAY /dev/md0 devices=/dev/vdb,/dev/vdc
-        '';
-      };
       kernelModules = [ "raid0" ];
     };
 
diff --git a/nixpkgs/nixos/tests/systemd-initrd-vconsole.nix b/nixpkgs/nixos/tests/systemd-initrd-vconsole.nix
index b74df410c422..d4c2a57680c1 100644
--- a/nixpkgs/nixos/tests/systemd-initrd-vconsole.nix
+++ b/nixpkgs/nixos/tests/systemd-initrd-vconsole.nix
@@ -2,7 +2,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
   name = "systemd-initrd-vconsole";
 
   nodes.machine = { pkgs, ... }: {
-    boot.kernelParams = [ "rd.systemd.unit=rescue.target" ];
+    boot.kernelParams = lib.mkAfter [ "rd.systemd.unit=rescue.target" "loglevel=3" "udev.log_level=3" "systemd.log_level=warning" ];
 
     boot.initrd.systemd = {
       enable = true;
@@ -20,14 +20,23 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
     machine.start()
     machine.wait_for_console_text("Press Enter for maintenance")
     machine.send_console("\n")
-    machine.wait_for_console_text("Logging in with home")
+
+    # Wait for shell to become ready
+    for _ in range(300):
+      machine.send_console("printf '%s to receive commands:\\n' Ready\n")
+      try:
+        machine.wait_for_console_text("Ready to receive commands:", timeout=1)
+        break
+      except Exception:
+        continue
+    else:
+      raise RuntimeError("Rescue shell never became ready")
 
     # Check keymap
-    machine.send_console("(printf '%s to receive text: \\n' Ready && read text && echo \"$text\") </dev/tty1\n")
+    machine.send_console("(printf '%s to receive text:\\n' Ready && read text && echo \"$text\") </dev/tty1\n")
     machine.wait_for_console_text("Ready to receive text:")
     for key in "asdfjkl;\n":
       machine.send_key(key)
     machine.wait_for_console_text("arstneio")
-    machine.send_console("systemctl poweroff\n")
   '';
 })
diff --git a/nixpkgs/nixos/tests/systemd-networkd-dhcpserver-static-leases.nix b/nixpkgs/nixos/tests/systemd-networkd-dhcpserver-static-leases.nix
index a8254a158016..f6d5411aa5ca 100644
--- a/nixpkgs/nixos/tests/systemd-networkd-dhcpserver-static-leases.nix
+++ b/nixpkgs/nixos/tests/systemd-networkd-dhcpserver-static-leases.nix
@@ -3,7 +3,7 @@
 import ./make-test-python.nix ({ lib, ... }: {
   name = "systemd-networkd-dhcpserver-static-leases";
   meta = with lib.maintainers; {
-    maintainers = [ veehaitch tomfitzhenry ];
+    maintainers = [ veehaitch ];
   };
   nodes = {
     router = {
diff --git a/nixpkgs/nixos/tests/systemd-networkd-dhcpserver.nix b/nixpkgs/nixos/tests/systemd-networkd-dhcpserver.nix
index b52c1499718b..cf0ccb744211 100644
--- a/nixpkgs/nixos/tests/systemd-networkd-dhcpserver.nix
+++ b/nixpkgs/nixos/tests/systemd-networkd-dhcpserver.nix
@@ -1,10 +1,16 @@
 # This test predominantly tests systemd-networkd DHCP server, by
 # setting up a DHCP server and client, and ensuring they are mutually
 # reachable via the DHCP allocated address.
+# Two DHCP servers are set up on bridge VLANs, testing to make sure that
+# bridge VLAN settings are correctly applied.
+#
+# br0 ----untagged---v
+#                    +---PVID 1+VLAN 2---[bridge]---PVID 2---eth1
+# vlan2 ---VLAN 2----^
 import ./make-test-python.nix ({pkgs, ...}: {
   name = "systemd-networkd-dhcpserver";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ tomfitzhenry ];
+    maintainers = [ ];
   };
   nodes = {
     router = { config, pkgs, ... }: {
@@ -16,6 +22,28 @@ import ./make-test-python.nix ({pkgs, ...}: {
         firewall.enable = false;
       };
       systemd.network = {
+        netdevs = {
+          br0 = {
+            enable = true;
+            netdevConfig = {
+              Name = "br0";
+              Kind = "bridge";
+            };
+            extraConfig = ''
+              [Bridge]
+              VLANFiltering=yes
+              DefaultPVID=none
+            '';
+          };
+          vlan2 = {
+            enable = true;
+            netdevConfig = {
+              Name = "vlan2";
+              Kind = "vlan";
+            };
+            vlanConfig.Id = 2;
+          };
+        };
         networks = {
           # systemd-networkd will load the first network unit file
           # that matches, ordered lexiographically by filename.
@@ -24,9 +52,32 @@ import ./make-test-python.nix ({pkgs, ...}: {
           # however, hence why this network is named such.
           "01-eth1" = {
             name = "eth1";
+            networkConfig.Bridge = "br0";
+            bridgeVLANs = [
+              { bridgeVLANConfig = { PVID = 2; EgressUntagged = 2; }; }
+            ];
+          };
+          "02-br0" = {
+            name = "br0";
             networkConfig = {
               DHCPServer = true;
               Address = "10.0.0.1/24";
+              VLAN = ["vlan2"];
+            };
+            dhcpServerConfig = {
+              PoolOffset = 100;
+              PoolSize = 1;
+            };
+            bridgeVLANs = [
+              { bridgeVLANConfig = { PVID = 1; EgressUntagged = 1; }; }
+              { bridgeVLANConfig = { VLAN = 2; }; }
+            ];
+          };
+          "02-vlan2" = {
+            name = "vlan2";
+            networkConfig = {
+              DHCPServer = true;
+              Address = "10.0.2.1/24";
             };
             dhcpServerConfig = {
               PoolOffset = 100;
@@ -52,7 +103,7 @@ import ./make-test-python.nix ({pkgs, ...}: {
     start_all()
     router.wait_for_unit("systemd-networkd-wait-online.service")
     client.wait_for_unit("systemd-networkd-wait-online.service")
-    client.wait_until_succeeds("ping -c 5 10.0.0.1")
-    router.wait_until_succeeds("ping -c 5 10.0.0.100")
+    client.wait_until_succeeds("ping -c 5 10.0.2.1")
+    router.wait_until_succeeds("ping -c 5 10.0.2.100")
   '';
 })
diff --git a/nixpkgs/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix b/nixpkgs/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
index e6bed6b9218f..54f371e6c070 100644
--- a/nixpkgs/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
+++ b/nixpkgs/nixos/tests/systemd-networkd-ipv6-prefix-delegation.nix
@@ -67,14 +67,14 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
             interfaces-config.interfaces = [ "eth1" ];
             subnet6 = [ {
               interface = "eth1";
-              subnet = "2001:DB8:F::/36";
+              subnet = "2001:DB8::/32";
               pd-pools = [ {
-                prefix = "2001:DB8:F::";
+                prefix = "2001:DB8:1000::";
                 prefix-len = 36;
                 delegated-len = 48;
               } ];
               pools = [ {
-                pool = "2001:DB8:0000:0000:FFFF::-2001:DB8:0000:0000:FFFF::FFFF";
+                pool = "2001:DB8:0000:0000::-2001:DB8:0FFF:FFFF::FFFF";
               } ];
             } ];
 
diff --git a/nixpkgs/nixos/tests/systemd-nspawn-configfile.nix b/nixpkgs/nixos/tests/systemd-nspawn-configfile.nix
new file mode 100644
index 000000000000..12ab21b7f9b5
--- /dev/null
+++ b/nixpkgs/nixos/tests/systemd-nspawn-configfile.nix
@@ -0,0 +1,128 @@
+import ./make-test-python.nix ({ lib, ... }:
+let
+  execOptions = [
+    "Boot"
+    "ProcessTwo"
+    "Parameters"
+    "Environment"
+    "User"
+    "WorkingDirectory"
+    "PivotRoot"
+    "Capability"
+    "DropCapability"
+    "NoNewPrivileges"
+    "KillSignal"
+    "Personality"
+    "MachineID"
+    "PrivateUsers"
+    "NotifyReady"
+    "SystemCallFilter"
+    "LimitCPU"
+    "LimitFSIZE"
+    "LimitDATA"
+    "LimitSTACK"
+    "LimitCORE"
+    "LimitRSS"
+    "LimitNOFILE"
+    "LimitAS"
+    "LimitNPROC"
+    "LimitMEMLOCK"
+    "LimitLOCKS"
+    "LimitSIGPENDING"
+    "LimitMSGQUEUE"
+    "LimitNICE"
+    "LimitRTPRIO"
+    "LimitRTTIME"
+    "OOMScoreAdjust"
+    "CPUAffinity"
+    "Hostname"
+    "ResolvConf"
+    "Timezone"
+    "LinkJournal"
+    "Ephemeral"
+    "AmbientCapability"
+  ];
+
+  filesOptions = [
+    "ReadOnly"
+    "Volatile"
+    "Bind"
+    "BindReadOnly"
+    "TemporaryFileSystem"
+    "Overlay"
+    "OverlayReadOnly"
+    "PrivateUsersChown"
+    "BindUser"
+    "Inaccessible"
+    "PrivateUsersOwnership"
+  ];
+
+  networkOptions = [
+    "Private"
+    "VirtualEthernet"
+    "VirtualEthernetExtra"
+    "Interface"
+    "MACVLAN"
+    "IPVLAN"
+    "Bridge"
+    "Zone"
+    "Port"
+  ];
+
+  optionsToConfig = opts: builtins.listToAttrs (map (n: lib.nameValuePair n "testdata") opts);
+
+  grepForOptions = opts: ''node.succeed(
+    "for o in ${builtins.concatStringsSep " " opts} ; do grep --quiet $o ${configFile} || exit 1 ; done"
+  )'';
+
+  unitName = "options-test";
+  configFile = "/etc/systemd/nspawn/${unitName}.nspawn";
+
+in
+{
+  name = "systemd-nspawn-configfile";
+
+  nodes = {
+    node = { pkgs, ... }: {
+      systemd.nspawn."${unitName}" = {
+        enable = true;
+
+        execConfig = optionsToConfig execOptions // {
+          Boot = true;
+          ProcessTwo = true;
+          NotifyReady = true;
+        };
+
+        filesConfig = optionsToConfig filesOptions // {
+          ReadOnly = true;
+          Volatile = "state";
+          PrivateUsersChown = true;
+          PrivateUsersOwnership = "auto";
+        };
+
+        networkConfig = optionsToConfig networkOptions // {
+          Private = true;
+          VirtualEthernet = true;
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+
+    node.wait_for_file("${configFile}")
+
+    with subtest("Test for presence of all specified options in config file"):
+      ${grepForOptions execOptions}
+      ${grepForOptions filesOptions}
+      ${grepForOptions networkOptions}
+
+    with subtest("Test for absence of misspelled option 'MachineId' (instead of 'MachineID')"):
+      node.fail("grep --quiet MachineId ${configFile}")
+  '';
+
+  meta.maintainers = [
+    lib.maintainers.zi3m5f
+  ];
+})
diff --git a/nixpkgs/nixos/tests/systemd-nspawn.nix b/nixpkgs/nixos/tests/systemd-nspawn.nix
index bc77ee2a4d15..1a4251ef069e 100644
--- a/nixpkgs/nixos/tests/systemd-nspawn.nix
+++ b/nixpkgs/nixos/tests/systemd-nspawn.nix
@@ -1,26 +1,6 @@
 import ./make-test-python.nix ({pkgs, lib, ...}:
 let
-  gpgKeyring = (pkgs.runCommand "gpg-keyring" { buildInputs = [ pkgs.gnupg ]; } ''
-    mkdir -p $out
-    export GNUPGHOME=$out
-    cat > foo <<EOF
-      %echo Generating a basic OpenPGP key
-      %no-protection
-      Key-Type: DSA
-      Key-Length: 1024
-      Subkey-Type: ELG-E
-      Subkey-Length: 1024
-      Name-Real: Bob Foobar
-      Name-Email: bob@foo.bar
-      Expire-Date: 0
-      # Do a commit here, so that we can later print "done"
-      %commit
-      %echo done
-    EOF
-    gpg --batch --generate-key foo
-    rm $out/S.gpg-agent $out/S.gpg-agent.*
-    gpg --export bob@foo.bar -a > $out/pubkey.gpg
-  '');
+  gpgKeyring = import ./common/gpg-keyring.nix { inherit pkgs; };
 
   nspawnImages = (pkgs.runCommand "localhost" { buildInputs = [ pkgs.coreutils pkgs.gnupg ]; } ''
     mkdir -p $out
diff --git a/nixpkgs/nixos/tests/systemd-shutdown.nix b/nixpkgs/nixos/tests/systemd-shutdown.nix
index dad8167f198f..ca6754046f57 100644
--- a/nixpkgs/nixos/tests/systemd-shutdown.nix
+++ b/nixpkgs/nixos/tests/systemd-shutdown.nix
@@ -22,6 +22,6 @@ in {
     machine.wait_for_console_text("Unmounting '/oldroot'")
     machine.wait_for_console_text("${msg}")
     # Don't try to sync filesystems
-    machine.booted = False
+    machine.wait_for_shutdown()
   '';
 })
diff --git a/nixpkgs/nixos/tests/systemd-sysupdate.nix b/nixpkgs/nixos/tests/systemd-sysupdate.nix
new file mode 100644
index 000000000000..37811605dbb2
--- /dev/null
+++ b/nixpkgs/nixos/tests/systemd-sysupdate.nix
@@ -0,0 +1,66 @@
+# Tests downloading a signed update aritfact from a server to a target machine.
+# This test does not rely on the `systemd.timer` units provided by the
+# `systemd-sysupdate` module but triggers the `systemd-sysupdate` service
+# manually to make the test more robust.
+
+{ lib, pkgs, ... }:
+
+let
+  gpgKeyring = import ./common/gpg-keyring.nix { inherit pkgs; };
+in
+{
+  name = "systemd-sysupdate";
+
+  meta.maintainers = with lib.maintainers; [ nikstur ];
+
+  nodes = {
+    server = { pkgs, ... }: {
+      networking.firewall.enable = false;
+      services.nginx = {
+        enable = true;
+        virtualHosts."server" = {
+          root = pkgs.runCommand "sysupdate-artifacts" { buildInputs = [ pkgs.gnupg ]; } ''
+            mkdir -p $out
+            cd $out
+
+            echo "nixos" > nixos_1.efi
+            sha256sum nixos_1.efi > SHA256SUMS
+
+            export GNUPGHOME="$(mktemp -d)"
+            cp -R ${gpgKeyring}/* $GNUPGHOME
+
+            gpg --batch --sign --detach-sign --output SHA256SUMS.gpg SHA256SUMS
+          '';
+        };
+      };
+    };
+
+    target = {
+      systemd.sysupdate = {
+        enable = true;
+        transfers = {
+          "uki" = {
+            Source = {
+              Type = "url-file";
+              Path = "http://server/";
+              MatchPattern = "nixos_@v.efi";
+            };
+            Target = {
+              Path = "/boot/EFI/Linux";
+              MatchPattern = "nixos_@v.efi";
+            };
+          };
+        };
+      };
+
+      environment.etc."systemd/import-pubring.gpg".source = "${gpgKeyring}/pubkey.gpg";
+    };
+  };
+
+  testScript = ''
+    server.wait_for_unit("nginx.service")
+
+    target.succeed("systemctl start systemd-sysupdate")
+    assert "nixos" in target.wait_until_succeeds("cat /boot/EFI/Linux/nixos_1.efi", timeout=5)
+  '';
+}
diff --git a/nixpkgs/nixos/tests/terminal-emulators.nix b/nixpkgs/nixos/tests/terminal-emulators.nix
index 4269d05056d8..6d76cc8e5741 100644
--- a/nixpkgs/nixos/tests/terminal-emulators.nix
+++ b/nixpkgs/nixos/tests/terminal-emulators.nix
@@ -35,6 +35,8 @@ let tests = {
 
       darktile.pkg = p: p.darktile;
 
+      deepin-terminal.pkg = p: p.deepin.deepin-terminal;
+
       eterm.pkg = p: p.eterm;
       eterm.executable = "Eterm";
       eterm.pinkValue = "#D40055";
@@ -72,6 +74,9 @@ let tests = {
       qterminal.pkg = p: p.lxqt.qterminal;
       qterminal.kill = true;
 
+      rio.pkg = p: p.rio;
+      rio.cmd = "rio -e $command";
+
       roxterm.pkg = p: p.roxterm;
       roxterm.cmd = "roxterm -e $command";
 
@@ -118,7 +123,7 @@ in mapAttrs (name: { pkg, executable ? name, cmd ? "SHELL=$command ${executable}
     maintainers = [ jjjollyjim ];
   };
 
-  machine = { pkgsInner, ... }:
+  nodes.machine = { pkgsInner, ... }:
 
   {
     imports = [ ./common/x11.nix ./common/user-account.nix ];
diff --git a/nixpkgs/nixos/tests/tmate-ssh-server.nix b/nixpkgs/nixos/tests/tmate-ssh-server.nix
index e7f94db9bfcf..122434c505c1 100644
--- a/nixpkgs/nixos/tests/tmate-ssh-server.nix
+++ b/nixpkgs/nixos/tests/tmate-ssh-server.nix
@@ -24,6 +24,7 @@ in
         services.tmate-ssh-server = {
           enable = true;
           port = 2223;
+          openFirewall = true;
         };
       };
       client = { ... }: {
diff --git a/nixpkgs/nixos/tests/twingate.nix b/nixpkgs/nixos/tests/twingate.nix
new file mode 100644
index 000000000000..f8bede09d9f2
--- /dev/null
+++ b/nixpkgs/nixos/tests/twingate.nix
@@ -0,0 +1,14 @@
+{
+  name = "twingate";
+
+  nodes.machine.services.twingate.enable = true;
+
+  testScript = { nodes, ... }: ''
+    machine.wait_for_unit("twingate.service")
+    machine.succeed("twingate --version | grep '${nodes.machine.services.twingate.package.version}' >&2")
+    machine.succeed("twingate config log-level 'debug'")
+    machine.systemctl("restart twingate.service")
+    machine.succeed("grep 'debug' /etc/twingate/log_level.conf >&2")
+    machine.succeed("twingate config log-level | grep 'debug' >&2")
+  '';
+}
diff --git a/nixpkgs/nixos/tests/typesense.nix b/nixpkgs/nixos/tests/typesense.nix
new file mode 100644
index 000000000000..4f07a2e194be
--- /dev/null
+++ b/nixpkgs/nixos/tests/typesense.nix
@@ -0,0 +1,23 @@
+import ./make-test-python.nix ({ pkgs, ... }: let
+  testPort = 8108;
+in {
+  name = "typesense";
+  meta.maintainers = with pkgs.lib.maintainers; [ oddlama ];
+
+  nodes.machine = { ... }: {
+    services.typesense = {
+      enable = true;
+      apiKeyFile = pkgs.writeText "typesense-api-key" "dummy";
+      settings.server = {
+        api-port = testPort;
+        api-address = "0.0.0.0";
+      };
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("typesense.service")
+    machine.wait_for_open_port(${toString testPort})
+    assert machine.succeed("curl --fail http://localhost:${toString testPort}/health") == '{"ok":true}'
+  '';
+})
diff --git a/nixpkgs/nixos/tests/virtualbox.nix b/nixpkgs/nixos/tests/virtualbox.nix
index 1ebd99f5d75d..062b125eb611 100644
--- a/nixpkgs/nixos/tests/virtualbox.nix
+++ b/nixpkgs/nixos/tests/virtualbox.nix
@@ -519,4 +519,4 @@ in mapAttrs (mkVBoxTest false vboxVMs) {
     destroy_vm_test1()
     destroy_vm_test2()
   '';
-} // (if enableUnfree then unfreeTests else {})
+} // (lib.optionalAttrs enableUnfree unfreeTests)
diff --git a/nixpkgs/nixos/tests/vscode-remote-ssh.nix b/nixpkgs/nixos/tests/vscode-remote-ssh.nix
new file mode 100644
index 000000000000..de7cc6badc9a
--- /dev/null
+++ b/nixpkgs/nixos/tests/vscode-remote-ssh.nix
@@ -0,0 +1,124 @@
+import ./make-test-python.nix ({ lib, ... }@args: let
+  pkgs = args.pkgs.extend (self: super: {
+    stdenv = super.stdenv.override {
+      config = super.config // {
+        allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
+          "vscode" "vscode-with-extensions" "vscode-extension-ms-vscode-remote-remote-ssh"
+        ];
+      };
+    };
+  });
+
+  inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
+
+  inherit (pkgs.vscode.passthru) rev vscodeServer;
+in {
+  name = "vscode-remote-ssh";
+  meta.maintainers = with lib.maintainers; [ Enzime ];
+
+  nodes = let
+    serverAddress = "192.168.0.2";
+    clientAddress = "192.168.0.1";
+  in {
+    server = { ... }: {
+      networking.interfaces.eth1.ipv4.addresses = [ { address = serverAddress; prefixLength = 24; } ];
+      services.openssh.enable = true;
+      users.users.root.openssh.authorizedKeys.keys = [ snakeOilPublicKey ];
+      virtualisation.additionalPaths = with pkgs; [ patchelf bintools stdenv.cc.cc.lib ];
+    };
+    client = { ... }: {
+      imports = [ ./common/x11.nix ./common/user-account.nix ];
+      networking.interfaces.eth1.ipv4.addresses = [ { address = clientAddress; prefixLength = 24; } ];
+      networking.hosts.${serverAddress} = [ "server" ];
+      test-support.displayManager.auto.user = "alice";
+      environment.systemPackages = [
+        (pkgs.vscode-with-extensions.override {
+          vscodeExtensions = [
+            pkgs.vscode-extensions.ms-vscode-remote.remote-ssh
+          ];
+        })
+      ];
+    };
+  };
+
+  enableOCR = true;
+
+  testScript = let
+    jq = "${pkgs.jq}/bin/jq";
+
+    sshConfig = builtins.toFile "ssh.conf" ''
+      UserKnownHostsFile=/dev/null
+      StrictHostKeyChecking=no
+    '';
+
+    vscodeConfig = builtins.toFile "settings.json" ''
+      {
+        "window.zoomLevel": 1,
+        "security.workspace.trust.startupPrompt": "always"
+      }
+    '';
+  in ''
+    def connect_with_remote_ssh(screenshot, should_succeed):
+      print(f"connect_with_remote_ssh({screenshot=}, {should_succeed=})")
+
+      if server.execute("test -d ~/.vscode-server")[0] == 0:
+        server.succeed("rm -r ~/.vscode-server")
+
+      server.succeed("mkdir -p ~/.vscode-server/bin")
+      server.succeed("cp -r ${vscodeServer} ~/.vscode-server/bin/${rev}")
+
+      client.succeed("sudo -u alice code --remote=ssh-remote+root@server /root")
+      client.wait_for_window("Visual Studio Code")
+
+      client.wait_for_text("Do you trust the authors" if should_succeed else "Disconnected from SSH")
+      client.screenshot(screenshot)
+
+      if should_succeed:
+        # Press the Don't Trust button
+        client.send_key("tab")
+        client.send_key("tab")
+        client.send_key("tab")
+        client.send_key("\n")
+      else:
+        # Close the error dialog
+        client.send_key("esc")
+
+      # Don't send Ctrl-q too quickly otherwise it might not get sent to VS Code
+      client.sleep(1)
+      client.send_key("ctrl-q")
+      client.wait_until_fails("pidof code")
+
+
+    start_all()
+    server.wait_for_open_port(22)
+
+    VSCODE_COMMIT = server.execute("${jq} -r .commit ${pkgs.vscode}/lib/vscode/resources/app/product.json")[1].rstrip()
+    SERVER_COMMIT = server.execute("${jq} -r .commit ${vscodeServer}/product.json")[1].rstrip()
+
+    print(f"{VSCODE_COMMIT=} {SERVER_COMMIT=}")
+    assert VSCODE_COMMIT == SERVER_COMMIT, "VSCODE_COMMIT and SERVER_COMMIT do not match"
+
+    client.wait_until_succeeds("ping -c1 server")
+    client.succeed("sudo -u alice mkdir ~alice/.ssh")
+    client.succeed("sudo -u alice install -Dm 600 ${snakeOilPrivateKey} ~alice/.ssh/id_ecdsa")
+    client.succeed("sudo -u alice install ${sshConfig} ~alice/.ssh/config")
+    client.succeed("sudo -u alice install -Dm 644 ${vscodeConfig} ~alice/.config/Code/User/settings.json")
+
+    client.wait_for_x()
+    client.wait_for_file("~alice/.Xauthority")
+    client.succeed("xauth merge ~alice/.Xauthority")
+    # Move the mouse out of the way
+    client.succeed("${pkgs.xdotool}/bin/xdotool mousemove 0 0")
+
+    with subtest("fails to connect when nixpkgs isn't available"):
+      server.fail("nix-build '<nixpkgs>' -A hello")
+      connect_with_remote_ssh(screenshot="no_node_installed", should_succeed=False)
+      server.succeed("test -e ~/.vscode-server/bin/${rev}/node")
+      server.fail("~/.vscode-server/bin/${rev}/node -v")
+
+    with subtest("connects when server can patch Node"):
+      server.succeed("mkdir -p /nix/var/nix/profiles/per-user/root/channels")
+      server.succeed("ln -s ${pkgs.path} /nix/var/nix/profiles/per-user/root/channels/nixos")
+      connect_with_remote_ssh(screenshot="build_node_with_nix", should_succeed=True)
+  '';
+})
diff --git a/nixpkgs/nixos/tests/vscodium.nix b/nixpkgs/nixos/tests/vscodium.nix
index 3eda8b6cfb20..d817ce927ff8 100644
--- a/nixpkgs/nixos/tests/vscodium.nix
+++ b/nixpkgs/nixos/tests/vscodium.nix
@@ -8,7 +8,7 @@ let
       environment.variables.NIXOS_OZONE_WL = "1";
       environment.variables.DISPLAY = "do not use";
 
-      fonts.fonts = with pkgs; [ dejavu_fonts ];
+      fonts.packages = with pkgs; [ dejavu_fonts ];
     };
     xorg = { pkgs, ... }: {
       imports = [ ./common/user-account.nix ./common/x11.nix ];
diff --git a/nixpkgs/nixos/tests/web-apps/gotosocial.nix b/nixpkgs/nixos/tests/web-apps/gotosocial.nix
new file mode 100644
index 000000000000..6d279ab63a79
--- /dev/null
+++ b/nixpkgs/nixos/tests/web-apps/gotosocial.nix
@@ -0,0 +1,28 @@
+{ lib, ... }:
+{
+  name = "gotosocial";
+  meta.maintainers = with lib.maintainers; [ misuzu ];
+
+  nodes.machine = { pkgs, ... }: {
+    environment.systemPackages = [ pkgs.jq ];
+    services.gotosocial = {
+      enable = true;
+      setupPostgresqlDB = true;
+      settings = {
+        host = "localhost:8081";
+        port = 8081;
+      };
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("gotosocial.service")
+    machine.wait_for_unit("postgresql.service")
+    machine.wait_for_open_port(8081)
+
+    # check user registration via cli
+    machine.succeed("curl -sS -f http://localhost:8081/nodeinfo/2.0 | jq '.usage.users.total' | grep -q '^0$'")
+    machine.succeed("gotosocial-admin account create --username nickname --email email@example.com --password kurtz575VPeBgjVm")
+    machine.succeed("curl -sS -f http://localhost:8081/nodeinfo/2.0 | jq '.usage.users.total' | grep -q '^1$'")
+  '';
+}
diff --git a/nixpkgs/nixos/tests/web-apps/peering-manager.nix b/nixpkgs/nixos/tests/web-apps/peering-manager.nix
index 56b7eebfadff..3f0acd560d13 100644
--- a/nixpkgs/nixos/tests/web-apps/peering-manager.nix
+++ b/nixpkgs/nixos/tests/web-apps/peering-manager.nix
@@ -34,7 +34,7 @@ import ../make-test-python.nix ({ lib, pkgs, ... }: {
             "sudo -u postgres psql -c 'ALTER USER \"peering-manager\" WITH SUPERUSER;'"
         )
         machine.succeed(
-            "cd ${nodes.machine.config.system.build.peeringManagerPkg}/opt/peering-manager ; peering-manager-manage test --no-input"
+            "cd ${nodes.machine.system.build.peeringManagerPkg}/opt/peering-manager ; peering-manager-manage test --no-input"
         )
   '';
 })
diff --git a/nixpkgs/nixos/tests/web-servers/static-web-server.nix b/nixpkgs/nixos/tests/web-servers/static-web-server.nix
new file mode 100644
index 000000000000..da1a9bdec5d2
--- /dev/null
+++ b/nixpkgs/nixos/tests/web-servers/static-web-server.nix
@@ -0,0 +1,32 @@
+import ../make-test-python.nix ({ pkgs, lib, ... } : {
+  name = "static-web-server";
+  meta = {
+    maintainers = with lib.maintainers; [ mac-chaffee ];
+  };
+
+  nodes.machine = { pkgs, ... }: {
+    services.static-web-server = {
+      enable = true;
+      listen = "[::]:8080";
+      root = toString (pkgs.writeTextDir "nixos-test.html" ''
+        <h1>Hello NixOS!</h1>
+      '');
+      configuration = {
+        general = { directory-listing = true; };
+      };
+    };
+  };
+
+  testScript = ''
+    machine.start()
+    machine.wait_for_unit("static-web-server.socket")
+    machine.wait_for_open_port(8080)
+    # We don't use wait_until_succeeds() because we're testing socket
+    # activation which better work on the first request
+    response = machine.succeed("curl -fsS localhost:8080")
+    assert "nixos-test.html" in response, "The directory listing page did not include a link to our nixos-test.html file"
+    response = machine.succeed("curl -fsS localhost:8080/nixos-test.html")
+    assert "Hello NixOS!" in response
+    machine.wait_for_unit("static-web-server.service")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/wpa_supplicant.nix b/nixpkgs/nixos/tests/wpa_supplicant.nix
index a05a79e8367d..8c701ca7d5f7 100644
--- a/nixpkgs/nixos/tests/wpa_supplicant.nix
+++ b/nixpkgs/nixos/tests/wpa_supplicant.nix
@@ -2,63 +2,160 @@ import ./make-test-python.nix ({ pkgs, lib, ...}:
 {
   name = "wpa_supplicant";
   meta = with lib.maintainers; {
-    maintainers = [ rnhmjoj ];
+    maintainers = [ oddlama rnhmjoj ];
   };
 
-  nodes.machine = { ... }: {
-    imports = [ ../modules/profiles/minimal.nix ];
+  nodes = let
+    machineWithHostapd = extraConfigModule: { ... }: {
+      imports = [
+        ../modules/profiles/minimal.nix
+        extraConfigModule
+      ];
+
+      # add a virtual wlan interface
+      boot.kernelModules = [ "mac80211_hwsim" ];
+
+      # wireless access point
+      services.hostapd = {
+        enable = true;
+        radios.wlan0 = {
+          band = "2g";
+          countryCode = "US";
+          networks = {
+            wlan0 = {
+              ssid = "nixos-test-sae";
+              authentication = {
+                mode = "wpa3-sae";
+                saePasswords = [ { password = "reproducibility"; } ];
+              };
+              bssid = "02:00:00:00:00:00";
+            };
+            wlan0-1 = {
+              ssid = "nixos-test-mixed";
+              authentication = {
+                mode = "wpa3-sae-transition";
+                saeAddToMacAllow = true;
+                saePasswordsFile = pkgs.writeText "password" "reproducibility";
+                wpaPasswordFile = pkgs.writeText "password" "reproducibility";
+              };
+              bssid = "02:00:00:00:00:01";
+            };
+            wlan0-2 = {
+              ssid = "nixos-test-wpa2";
+              authentication = {
+                mode = "wpa2-sha256";
+                wpaPassword = "reproducibility";
+              };
+              bssid = "02:00:00:00:00:02";
+            };
+          };
+        };
+      };
 
-    # add a virtual wlan interface
-    boot.kernelModules = [ "mac80211_hwsim" ];
+      # wireless client
+      networking.wireless = {
+        # the override is needed because the wifi is
+        # disabled with mkVMOverride in qemu-vm.nix.
+        enable = lib.mkOverride 0 true;
+        userControlled.enable = true;
+        interfaces = [ "wlan1" ];
+        fallbackToWPA2 = lib.mkDefault true;
+
+        # networks will be added on-demand below for the specific
+        # network that should be tested
+
+        # secrets
+        environmentFile = pkgs.writeText "wpa-secrets" ''
+          PSK_NIXOS_TEST="reproducibility"
+        '';
+      };
+    };
+  in {
+    basic = { ... }: {
+      imports = [ ../modules/profiles/minimal.nix ];
+
+      # add a virtual wlan interface
+      boot.kernelModules = [ "mac80211_hwsim" ];
+
+      # wireless client
+      networking.wireless = {
+        # the override is needed because the wifi is
+        # disabled with mkVMOverride in qemu-vm.nix.
+        enable = lib.mkOverride 0 true;
+        userControlled.enable = true;
+        interfaces = [ "wlan1" ];
+        fallbackToWPA2 = true;
+
+        networks = {
+          # test WPA2 fallback
+          mixed-wpa = {
+            psk = "password";
+            authProtocols = [ "WPA-PSK" "SAE" ];
+          };
+          sae-only = {
+            psk = "password";
+            authProtocols = [ "SAE" ];
+          };
+
+          # secrets substitution test cases
+          test1.psk = "@PSK_VALID@";              # should be replaced
+          test2.psk = "@PSK_SPECIAL@";            # should be replaced
+          test3.psk = "@PSK_MISSING@";            # should not be replaced
+          test4.psk = "P@ssowrdWithSome@tSymbol"; # should not be replaced
+        };
 
-    # wireless access point
-    services.hostapd = {
-      enable = true;
-      wpa = true;
-      interface = "wlan0";
-      ssid = "nixos-test";
-      wpaPassphrase = "reproducibility";
+        # secrets
+        environmentFile = pkgs.writeText "wpa-secrets" ''
+          PSK_VALID="S0m3BadP4ssw0rd";
+          # taken from https://github.com/minimaxir/big-list-of-naughty-strings
+          PSK_SPECIAL=",./;'[]\-= <>?:\"{}|_+ !@#$%^\&*()`~";
+        '';
+      };
     };
 
-    # wireless client
-    networking.wireless = {
-      # the override is needed because the wifi is
-      # disabled with mkVMOverride in qemu-vm.nix.
-      enable = lib.mkOverride 0 true;
-      userControlled.enable = true;
-      interfaces = [ "wlan1" ];
-      fallbackToWPA2 = true;
-
-      networks = {
-        # test WPA2 fallback
-        mixed-wpa = {
-          psk = "password";
-          authProtocols = [ "WPA-PSK" "SAE" ];
-        };
-        sae-only = {
-          psk = "password";
+    # Test connecting to the SAE-only hotspot using SAE
+    machineSae = machineWithHostapd {
+      networking.wireless = {
+        fallbackToWPA2 = false;
+        networks.nixos-test-sae = {
+          psk = "@PSK_NIXOS_TEST@";
           authProtocols = [ "SAE" ];
         };
+      };
+    };
 
-        # test network
-        nixos-test.psk = "@PSK_NIXOS_TEST@";
-
-        # secrets substitution test cases
-        test1.psk = "@PSK_VALID@";              # should be replaced
-        test2.psk = "@PSK_SPECIAL@";            # should be replaced
-        test3.psk = "@PSK_MISSING@";            # should not be replaced
-        test4.psk = "P@ssowrdWithSome@tSymbol"; # should not be replaced
+    # Test connecting to the SAE and WPA2 mixed hotspot using SAE
+    machineMixedUsingSae = machineWithHostapd {
+      networking.wireless = {
+        fallbackToWPA2 = false;
+        networks.nixos-test-mixed = {
+          psk = "@PSK_NIXOS_TEST@";
+          authProtocols = [ "SAE" ];
+        };
       };
+    };
 
-      # secrets
-      environmentFile = pkgs.writeText "wpa-secrets" ''
-        PSK_NIXOS_TEST="reproducibility"
-        PSK_VALID="S0m3BadP4ssw0rd";
-        # taken from https://github.com/minimaxir/big-list-of-naughty-strings
-        PSK_SPECIAL=",./;'[]\-= <>?:\"{}|_+ !@#$%^\&*()`~";
-      '';
+    # Test connecting to the SAE and WPA2 mixed hotspot using WPA2
+    machineMixedUsingWpa2 = machineWithHostapd {
+      networking.wireless = {
+        fallbackToWPA2 = true;
+        networks.nixos-test-mixed = {
+          psk = "@PSK_NIXOS_TEST@";
+          authProtocols = [ "WPA-PSK-SHA256" ];
+        };
+      };
     };
 
+    # Test connecting to the WPA2 legacy hotspot using WPA2
+    machineWpa2 = machineWithHostapd {
+      networking.wireless = {
+        fallbackToWPA2 = true;
+        networks.nixos-test-wpa2 = {
+          psk = "@PSK_NIXOS_TEST@";
+          authProtocols = [ "WPA-PSK-SHA256" ];
+        };
+      };
+    };
   };
 
   testScript =
@@ -66,30 +163,47 @@ import ./make-test-python.nix ({ pkgs, lib, ...}:
       config_file = "/run/wpa_supplicant/wpa_supplicant.conf"
 
       with subtest("Configuration file is inaccessible to other users"):
-          machine.wait_for_file(config_file)
-          machine.fail(f"sudo -u nobody ls {config_file}")
+          basic.wait_for_file(config_file)
+          basic.fail(f"sudo -u nobody ls {config_file}")
 
       with subtest("Secrets variables have been substituted"):
-          machine.fail(f"grep -q @PSK_VALID@ {config_file}")
-          machine.fail(f"grep -q @PSK_SPECIAL@ {config_file}")
-          machine.succeed(f"grep -q @PSK_MISSING@ {config_file}")
-          machine.succeed(f"grep -q P@ssowrdWithSome@tSymbol {config_file}")
+          basic.fail(f"grep -q @PSK_VALID@ {config_file}")
+          basic.fail(f"grep -q @PSK_SPECIAL@ {config_file}")
+          basic.succeed(f"grep -q @PSK_MISSING@ {config_file}")
+          basic.succeed(f"grep -q P@ssowrdWithSome@tSymbol {config_file}")
 
       with subtest("WPA2 fallbacks have been generated"):
-          assert int(machine.succeed(f"grep -c sae-only {config_file}")) == 1
-          assert int(machine.succeed(f"grep -c mixed-wpa {config_file}")) == 2
+          assert int(basic.succeed(f"grep -c sae-only {config_file}")) == 1
+          assert int(basic.succeed(f"grep -c mixed-wpa {config_file}")) == 2
 
       # save file for manual inspection
-      machine.copy_from_vm(config_file)
+      basic.copy_from_vm(config_file)
 
       with subtest("Daemon is running and accepting connections"):
-          machine.wait_for_unit("wpa_supplicant-wlan1.service")
-          status = machine.succeed("wpa_cli -i wlan1 status")
+          basic.wait_for_unit("wpa_supplicant-wlan1.service")
+          status = basic.succeed("wpa_cli -i wlan1 status")
           assert "Failed to connect" not in status, \
                  "Failed to connect to the daemon"
 
-      with subtest("Daemon can connect to the access point"):
-          machine.wait_until_succeeds(
+      machineSae.wait_for_unit("hostapd.service")
+      machineSae.copy_from_vm("/run/hostapd/wlan0.hostapd.conf")
+      with subtest("Daemon can connect to the SAE access point using SAE"):
+          machineSae.wait_until_succeeds(
+            "wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
+          )
+
+      with subtest("Daemon can connect to the SAE and WPA2 mixed access point using SAE"):
+          machineMixedUsingSae.wait_until_succeeds(
+            "wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
+          )
+
+      with subtest("Daemon can connect to the SAE and WPA2 mixed access point using WPA2"):
+          machineMixedUsingWpa2.wait_until_succeeds(
+            "wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
+          )
+
+      with subtest("Daemon can connect to the WPA2 access point using WPA2"):
+          machineWpa2.wait_until_succeeds(
             "wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
           )
     '';