about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/configuration/wireless.xml11
-rw-r--r--nixos/doc/manual/default.nix3
-rw-r--r--nixos/doc/manual/development/sources.xml19
-rw-r--r--nixos/doc/manual/installation/installing.xml8
-rw-r--r--nixos/doc/manual/installation/upgrading.xml16
-rw-r--r--nixos/doc/manual/man-configuration.xml4
-rw-r--r--nixos/doc/manual/man-nixos-build-vms.xml4
-rw-r--r--nixos/doc/manual/man-nixos-enter.xml4
-rw-r--r--nixos/doc/manual/man-nixos-generate-config.xml4
-rw-r--r--nixos/doc/manual/man-nixos-install.xml4
-rw-r--r--nixos/doc/manual/man-nixos-option.xml4
-rw-r--r--nixos/doc/manual/man-nixos-rebuild.xml44
-rw-r--r--nixos/doc/manual/man-nixos-version.xml4
-rw-r--r--nixos/doc/manual/manual.xml27
-rw-r--r--nixos/doc/manual/preface.xml37
-rw-r--r--nixos/doc/manual/release-notes/rl-1909.xml132
-rw-r--r--nixos/doc/manual/release-notes/rl-2003.xml76
-rw-r--r--nixos/doc/xmlformat.conf1
-rw-r--r--nixos/lib/make-iso9660-image.nix6
-rwxr-xr-xnixos/maintainers/scripts/ec2/create-amis.sh2
-rwxr-xr-xnixos/maintainers/scripts/gce/create-gce.sh2
-rw-r--r--nixos/modules/config/i18n.nix6
-rw-r--r--nixos/modules/config/no-x-libs.nix1
-rw-r--r--nixos/modules/config/pulseaudio.nix8
-rw-r--r--nixos/modules/config/shells-environment.nix2
-rw-r--r--nixos/modules/config/sysctl.nix2
-rw-r--r--nixos/modules/config/system-environment.nix83
-rw-r--r--nixos/modules/config/terminfo.nix2
-rw-r--r--nixos/modules/config/unix-odbc-drivers.nix2
-rw-r--r--nixos/modules/config/update-users-groups.pl4
-rw-r--r--nixos/modules/config/users-groups.nix2
-rw-r--r--nixos/modules/config/xdg/icons.nix27
-rw-r--r--nixos/modules/hardware/brightnessctl.nix1
-rw-r--r--nixos/modules/hardware/steam-hardware.nix7
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix3
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix10
-rw-r--r--nixos/modules/installer/cd-dvd/iso-image.nix25
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix31
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image.nix23
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix8
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl20
-rw-r--r--nixos/modules/installer/tools/nixos-rebuild.sh10
-rw-r--r--nixos/modules/installer/tools/tools.nix7
-rw-r--r--nixos/modules/misc/ids.nix4
-rw-r--r--nixos/modules/misc/locate.nix5
-rw-r--r--nixos/modules/module-list.nix24
-rw-r--r--nixos/modules/profiles/hardened.nix21
-rw-r--r--nixos/modules/profiles/installation-device.nix3
-rw-r--r--nixos/modules/profiles/qemu-guest.nix4
-rw-r--r--nixos/modules/programs/blcr.nix27
-rw-r--r--nixos/modules/programs/environment.nix3
-rw-r--r--nixos/modules/programs/gnupg.nix41
-rw-r--r--nixos/modules/programs/less.nix4
-rw-r--r--nixos/modules/programs/mtr.nix14
-rw-r--r--nixos/modules/programs/seahorse.nix2
-rw-r--r--nixos/modules/programs/shadow.nix14
-rw-r--r--nixos/modules/programs/shell.nix54
-rw-r--r--nixos/modules/programs/sway.nix2
-rw-r--r--nixos/modules/programs/x2goserver.nix1
-rw-r--r--nixos/modules/rename.nix21
-rw-r--r--nixos/modules/security/acme.nix55
-rw-r--r--nixos/modules/security/pam.nix28
-rw-r--r--nixos/modules/services/admin/oxidized.nix1
-rw-r--r--nixos/modules/services/amqp/rabbitmq.nix2
-rw-r--r--nixos/modules/services/audio/jack.nix1
-rw-r--r--nixos/modules/services/audio/spotifyd.nix2
-rw-r--r--nixos/modules/services/backup/automysqlbackup.nix5
-rw-r--r--nixos/modules/services/backup/borgbackup.nix25
-rw-r--r--nixos/modules/services/backup/postgresql-wal-receiver.nix3
-rw-r--r--nixos/modules/services/backup/znapzend.nix28
-rw-r--r--nixos/modules/services/continuous-integration/buildkite-agent.nix1
-rw-r--r--nixos/modules/services/continuous-integration/hydra/default.nix24
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/default.nix2
-rw-r--r--nixos/modules/services/databases/memcached.nix1
-rw-r--r--nixos/modules/services/databases/mysql.nix10
-rw-r--r--nixos/modules/services/databases/postgresql.nix3
-rw-r--r--nixos/modules/services/databases/redis.nix19
-rw-r--r--nixos/modules/services/databases/rethinkdb.nix1
-rw-r--r--nixos/modules/services/desktops/geoclue2.nix41
-rw-r--r--nixos/modules/services/desktops/gnome3/gnome-initial-setup.nix86
-rw-r--r--nixos/modules/services/desktops/gnome3/gnome-settings-daemon.nix49
-rw-r--r--nixos/modules/services/desktops/system-config-printer.nix3
-rw-r--r--nixos/modules/services/editors/emacs.nix8
-rw-r--r--nixos/modules/services/editors/infinoted.nix1
-rw-r--r--nixos/modules/services/games/openarena.nix56
-rw-r--r--nixos/modules/services/hardware/fancontrol.nix45
-rw-r--r--nixos/modules/services/hardware/trezord.nix16
-rw-r--r--nixos/modules/services/hardware/udisks2.nix5
-rw-r--r--nixos/modules/services/hardware/usbmuxd.nix1
-rw-r--r--nixos/modules/services/hardware/vdr.nix1
-rw-r--r--nixos/modules/services/mail/dovecot.nix2
-rw-r--r--nixos/modules/services/mail/mailcatcher.nix3
-rw-r--r--nixos/modules/services/mail/mailhog.nix1
-rw-r--r--nixos/modules/services/mail/mailman.nix205
-rw-r--r--nixos/modules/services/mail/rspamd.nix2
-rw-r--r--nixos/modules/services/misc/airsonic.nix1
-rw-r--r--nixos/modules/services/misc/docker-registry.nix6
-rw-r--r--nixos/modules/services/misc/errbot.nix5
-rw-r--r--nixos/modules/services/misc/gitea.nix2
-rw-r--r--nixos/modules/services/misc/gitlab.nix202
-rw-r--r--nixos/modules/services/misc/gollum.nix1
-rw-r--r--nixos/modules/services/misc/home-assistant.nix1
-rw-r--r--nixos/modules/services/misc/jellyfin.nix5
-rw-r--r--nixos/modules/services/misc/lidarr.nix15
-rw-r--r--nixos/modules/services/misc/matrix-synapse.nix40
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix93
-rw-r--r--nixos/modules/services/misc/nix-optimise.nix4
-rw-r--r--nixos/modules/services/misc/osrm.nix1
-rw-r--r--nixos/modules/services/misc/pykms.nix39
-rw-r--r--nixos/modules/services/monitoring/collectd.nix40
-rw-r--r--nixos/modules/services/monitoring/do-agent.nix34
-rw-r--r--nixos/modules/services/monitoring/fusion-inventory.nix1
-rw-r--r--nixos/modules/services/monitoring/graphite.nix2
-rw-r--r--nixos/modules/services/monitoring/netdata.nix1
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.nix4
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix53
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix58
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nginx.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix92
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix5
-rw-r--r--nixos/modules/services/monitoring/thanos.nix34
-rw-r--r--nixos/modules/services/monitoring/zabbix-agent.nix1
-rw-r--r--nixos/modules/services/monitoring/zabbix-server.nix1
-rw-r--r--nixos/modules/services/network-filesystems/orangefs/client.nix97
-rw-r--r--nixos/modules/services/network-filesystems/orangefs/server.nix225
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix1
-rw-r--r--nixos/modules/services/networking/bitcoind.nix1
-rw-r--r--nixos/modules/services/networking/dnscache.nix2
-rw-r--r--nixos/modules/services/networking/dnscrypt-wrapper.nix1
-rw-r--r--nixos/modules/services/networking/firewall.nix15
-rw-r--r--nixos/modules/services/networking/go-shadowsocks2.nix30
-rw-r--r--nixos/modules/services/networking/hans.nix1
-rw-r--r--nixos/modules/services/networking/jormungandr.nix6
-rw-r--r--nixos/modules/services/networking/matterbridge.nix1
-rw-r--r--nixos/modules/services/networking/minidlna.nix67
-rw-r--r--nixos/modules/services/networking/morty.nix1
-rw-r--r--nixos/modules/services/networking/mxisd.nix33
-rw-r--r--nixos/modules/services/networking/networkmanager.nix178
-rw-r--r--nixos/modules/services/networking/nghttpx/default.nix1
-rw-r--r--nixos/modules/services/networking/ntp/chrony.nix (renamed from nixos/modules/services/networking/chrony.nix)11
-rw-r--r--nixos/modules/services/networking/ntp/ntpd.nix (renamed from nixos/modules/services/networking/ntpd.nix)1
-rw-r--r--nixos/modules/services/networking/ntp/openntpd.nix (renamed from nixos/modules/services/networking/openntpd.nix)1
-rw-r--r--nixos/modules/services/networking/owamp.nix1
-rw-r--r--nixos/modules/services/networking/pppd.nix134
-rw-r--r--nixos/modules/services/networking/prosody.nix2
-rw-r--r--nixos/modules/services/networking/smokeping.nix5
-rw-r--r--nixos/modules/services/networking/stunnel.nix15
-rw-r--r--nixos/modules/services/networking/thelounge.nix1
-rw-r--r--nixos/modules/services/networking/tinydns.nix2
-rw-r--r--nixos/modules/services/networking/trickster.nix112
-rw-r--r--nixos/modules/services/networking/wpa_supplicant.nix14
-rw-r--r--nixos/modules/services/networking/yggdrasil.nix193
-rw-r--r--nixos/modules/services/networking/zeronet.nix92
-rw-r--r--nixos/modules/services/printing/cupsd.nix2
-rw-r--r--nixos/modules/services/scheduling/marathon.nix2
-rw-r--r--nixos/modules/services/security/bitwarden_rs/default.nix5
-rw-r--r--nixos/modules/services/security/fprintd.nix7
-rw-r--r--nixos/modules/services/security/oauth2_proxy.nix1
-rw-r--r--nixos/modules/services/torrent/magnetico.nix1
-rw-r--r--nixos/modules/services/web-apps/codimd.nix1
-rw-r--r--nixos/modules/services/web-apps/documize.nix11
-rw-r--r--nixos/modules/services/web-apps/frab.nix1
-rw-r--r--nixos/modules/services/web-apps/gotify-server.nix49
-rw-r--r--nixos/modules/services/web-apps/limesurvey.nix5
-rw-r--r--nixos/modules/services/web-apps/matomo-doc.xml2
-rw-r--r--nixos/modules/services/web-apps/matomo.nix66
-rw-r--r--nixos/modules/services/web-apps/mediawiki.nix5
-rw-r--r--nixos/modules/services/web-apps/moodle.nix21
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix37
-rw-r--r--nixos/modules/services/web-apps/nexus.nix1
-rw-r--r--nixos/modules/services/web-apps/restya-board.nix5
-rw-r--r--nixos/modules/services/web-apps/shiori.nix50
-rw-r--r--nixos/modules/services/web-apps/tt-rss.nix2
-rw-r--r--nixos/modules/services/web-apps/virtlyst.nix1
-rw-r--r--nixos/modules/services/web-apps/wordpress.nix27
-rw-r--r--nixos/modules/services/web-apps/zabbix.nix4
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/default.nix129
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/per-server-options.nix6
-rw-r--r--nixos/modules/services/web-servers/hitch/default.nix5
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix31
-rw-r--r--nixos/modules/services/web-servers/traefik.nix1
-rw-r--r--nixos/modules/services/web-servers/unit/default.nix1
-rw-r--r--nixos/modules/services/x11/desktop-managers/enlightenment.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix26
-rw-r--r--nixos/modules/services/x11/desktop-managers/mate.nix9
-rw-r--r--nixos/modules/services/x11/desktop-managers/pantheon.nix28
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix3
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix6
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce4-14.nix34
-rw-r--r--nixos/modules/services/x11/desktop-managers/xterm.nix4
-rw-r--r--nixos/modules/services/x11/display-managers/gdm.nix29
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm-greeters/enso-os.nix36
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix36
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix7
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix14
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix39
-rw-r--r--nixos/modules/services/x11/display-managers/sddm.nix4
-rw-r--r--nixos/modules/services/x11/hardware/cmt.nix7
-rw-r--r--nixos/modules/services/x11/redshift.nix16
-rw-r--r--nixos/modules/services/x11/window-managers/cwm.nix23
-rw-r--r--nixos/modules/services/x11/window-managers/default.nix1
-rw-r--r--nixos/modules/system/activation/activation-script.nix2
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl19
-rw-r--r--nixos/modules/system/boot/kernel.nix2
-rw-r--r--nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh10
-rw-r--r--nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix6
-rw-r--r--nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix6
-rw-r--r--nixos/modules/system/boot/networkd.nix2
-rw-r--r--nixos/modules/system/boot/plymouth.nix23
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh27
-rw-r--r--nixos/modules/system/boot/stage-2-init.sh2
-rw-r--r--nixos/modules/system/boot/systemd-nspawn.nix24
-rw-r--r--nixos/modules/system/boot/systemd.nix19
-rw-r--r--nixos/modules/tasks/auto-upgrade.nix2
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix24
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix25
-rw-r--r--nixos/modules/tasks/network-interfaces.nix15
-rw-r--r--nixos/modules/virtualisation/amazon-image.nix3
-rw-r--r--nixos/modules/virtualisation/container-config.nix1
-rw-r--r--nixos/modules/virtualisation/containers.nix7
-rw-r--r--nixos/modules/virtualisation/cri-o.nix106
-rw-r--r--nixos/modules/virtualisation/ec2-amis.nix18
-rw-r--r--nixos/modules/virtualisation/virtualbox-host.nix9
-rw-r--r--nixos/release.nix11
-rw-r--r--nixos/tests/acme.nix27
-rw-r--r--nixos/tests/all-tests.nix12
-rw-r--r--nixos/tests/babeld.nix148
-rw-r--r--nixos/tests/caddy.nix82
-rw-r--r--nixos/tests/common/letsencrypt/common.nix26
-rw-r--r--nixos/tests/common/letsencrypt/default.nix370
-rw-r--r--nixos/tests/common/letsencrypt/mkcerts.nix2
-rw-r--r--nixos/tests/common/letsencrypt/snakeoil-certs.nix451
-rw-r--r--nixos/tests/fancontrol.nix25
-rw-r--r--nixos/tests/ferm.nix2
-rw-r--r--nixos/tests/gnome3-xorg.nix2
-rw-r--r--nixos/tests/gnome3.nix2
-rw-r--r--nixos/tests/gotify-server.nix45
-rw-r--r--nixos/tests/hardened.nix1
-rwxr-xr-xnixos/tests/hydra/create-trivial-project.sh2
-rw-r--r--nixos/tests/hydra/default.nix23
-rw-r--r--nixos/tests/installer.nix4
-rw-r--r--nixos/tests/kafka.nix1
-rw-r--r--nixos/tests/login.nix6
-rw-r--r--nixos/tests/matomo.nix43
-rw-r--r--nixos/tests/minidlna.nix39
-rw-r--r--nixos/tests/mumble.nix4
-rw-r--r--nixos/tests/mxisd.nix12
-rw-r--r--nixos/tests/networking.nix24
-rw-r--r--nixos/tests/nextcloud/with-postgresql-and-redis.nix37
-rw-r--r--nixos/tests/openarena.nix36
-rw-r--r--nixos/tests/orangefs.nix88
-rw-r--r--nixos/tests/os-prober.nix118
-rw-r--r--nixos/tests/pantheon.nix2
-rw-r--r--nixos/tests/plasma5.nix3
-rw-r--r--nixos/tests/pppd.nix62
-rw-r--r--nixos/tests/predictable-interface-names.nix1
-rw-r--r--nixos/tests/prometheus-exporters.nix50
-rw-r--r--nixos/tests/shiori.nix17
-rw-r--r--nixos/tests/systemd-networkd-wireguard.nix1
-rw-r--r--nixos/tests/trickster.nix29
-rw-r--r--nixos/tests/wordpress.nix11
-rw-r--r--nixos/tests/xfce.nix6
-rw-r--r--nixos/tests/xfce4-14.nix6
-rw-r--r--nixos/tests/xmonad.nix2
-rw-r--r--nixos/tests/yggdrasil.nix123
266 files changed, 4922 insertions, 1856 deletions
diff --git a/nixos/doc/manual/configuration/wireless.xml b/nixos/doc/manual/configuration/wireless.xml
index 9c0e3a8d7aa4..247d29d58314 100644
--- a/nixos/doc/manual/configuration/wireless.xml
+++ b/nixos/doc/manual/configuration/wireless.xml
@@ -19,10 +19,17 @@
   NixOS lets you specify networks for wpa_supplicant declaratively:
 <programlisting>
 <xref linkend="opt-networking.wireless.networks"/> = {
-  echelon = {
+  echelon = {                # SSID with no spaces or special characters
     psk = "abcdefgh";
   };
-  "free.wifi" = {};
+  "echelon's AP" = {         # SSID with spaces and/or special characters
+    psk = "ijklmnop";
+  };
+  echelon = {                # Hidden SSID
+    hidden = true;
+    psk = "qrstuvwx";
+  };
+  free.wifi = {};            # Public wireless network
 };
 </programlisting>
   Be aware that keys will be written to the nix store in plaintext! When no
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index f9de2db1a084..6ca75f869f45 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -62,14 +62,13 @@ let
     "--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 1"
-    "--param toc.section.depth 3"
+    "--param toc.section.depth 0"
     "--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 generate.toc 'book toc appendix toc'"
     "--stringparam chunk.toc ${toc}"
   ];
 
diff --git a/nixos/doc/manual/development/sources.xml b/nixos/doc/manual/development/sources.xml
index 3c30c782746d..b333ccabb420 100644
--- a/nixos/doc/manual/development/sources.xml
+++ b/nixos/doc/manual/development/sources.xml
@@ -13,17 +13,16 @@
 <screen>
 <prompt>$ </prompt>git clone https://github.com/NixOS/nixpkgs
 <prompt>$ </prompt>cd nixpkgs
-<prompt>$ </prompt>git remote add channels https://github.com/NixOS/nixpkgs-channels
-<prompt>$ </prompt>git remote update channels
+<prompt>$ </prompt>git remote update origin
 </screen>
   This will check out the latest Nixpkgs sources to
   <filename>./nixpkgs</filename> the NixOS sources to
   <filename>./nixpkgs/nixos</filename>. (The NixOS source tree lives in a
-  subdirectory of the Nixpkgs repository.) The remote
-  <literal>channels</literal> refers to a read-only repository that tracks the
-  Nixpkgs/NixOS channels (see <xref linkend="sec-upgrading"/> for more
+  subdirectory of the Nixpkgs repository.) The
+  <literal>nixpkgs</literal> repository has branches that correspond
+  to each Nixpkgs/NixOS channel (see <xref linkend="sec-upgrading"/> for more
   information about channels). Thus, the Git branch
-  <literal>channels/nixos-17.03</literal> will contain the latest built and
+  <literal>origin/nixos-17.03</literal> will contain the latest built and
   tested version available in the <literal>nixos-17.03</literal> channel.
  </para>
  <para>
@@ -40,15 +39,15 @@
   Or, to base your local branch on the latest version available in a NixOS
   channel:
 <screen>
-<prompt>$ </prompt>git remote update channels
-<prompt>$ </prompt>git checkout -b local channels/nixos-17.03
+<prompt>$ </prompt>git remote update origin
+<prompt>$ </prompt>git checkout -b local origin/nixos-17.03
 </screen>
   (Replace <literal>nixos-17.03</literal> with the name of the channel you want
   to use.) You can use <command>git merge</command> or <command>git
   rebase</command> to keep your local branch in sync with the channel, e.g.
 <screen>
-<prompt>$ </prompt>git remote update channels
-<prompt>$ </prompt>git merge channels/nixos-17.03
+<prompt>$ </prompt>git remote update origin
+<prompt>$ </prompt>git merge origin/nixos-17.03
 </screen>
   You can use <command>git cherry-pick</command> to copy commits from your
   local branch to the upstream branch.
diff --git a/nixos/doc/manual/installation/installing.xml b/nixos/doc/manual/installation/installing.xml
index 9cea2db610e0..8ff920eb5a88 100644
--- a/nixos/doc/manual/installation/installing.xml
+++ b/nixos/doc/manual/installation/installing.xml
@@ -68,7 +68,7 @@
     If you would like to continue the installation from a different machine you
     need to activate the SSH daemon via <command>systemctl start
     sshd</command>. You then must set a password for either <literal>root</literal> or
-    <literal>nixos</literal> with <command>passwd></command> to be able to login.
+    <literal>nixos</literal> with <command>passwd</command> to be able to login.
    </para>
   </section>
  </section>
@@ -392,7 +392,11 @@
      <filename>hardware-configuration.nix</filename> is included from
      <filename>configuration.nix</filename> and will be overwritten by future
      invocations of <command>nixos-generate-config</command>; thus, you
-     generally should not modify it.)
+     generally should not modify it.) Additionally, you may want to look at 
+     <link xlink:href="https://github.com/NixOS/nixos-hardware">Hardware
+     configuration for known-hardware</link> at this point or after
+     installation.
+      
     </para>
     <note>
      <para>
diff --git a/nixos/doc/manual/installation/upgrading.xml b/nixos/doc/manual/installation/upgrading.xml
index 35b4d266e12e..8d3f35b7c26f 100644
--- a/nixos/doc/manual/installation/upgrading.xml
+++ b/nixos/doc/manual/installation/upgrading.xml
@@ -14,7 +14,7 @@
     <para>
      <emphasis>Stable channels</emphasis>, such as
      <literal
-    xlink:href="https://nixos.org/channels/nixos-19.03">nixos-19.03</literal>.
+    xlink:href="https://nixos.org/channels/nixos-19.09">nixos-19.09</literal>.
      These only get conservative bug fixes and package upgrades. For instance,
      a channel update may cause the Linux kernel on your system to be upgraded
      from 4.19.34 to 4.19.38 (a minor bug fix), but not from
@@ -38,7 +38,7 @@
     <para>
      <emphasis>Small channels</emphasis>, such as
      <literal
-    xlink:href="https://nixos.org/channels/nixos-19.03-small">nixos-19.03-small</literal>
+    xlink:href="https://nixos.org/channels/nixos-19.09-small">nixos-19.09-small</literal>
      or
      <literal
     xlink:href="https://nixos.org/channels/nixos-unstable-small">nixos-unstable-small</literal>.
@@ -63,8 +63,8 @@
  <para>
   When you first install NixOS, you’re automatically subscribed to the NixOS
   channel that corresponds to your installation source. For instance, if you
-  installed from a 19.03 ISO, you will be subscribed to the
-  <literal>nixos-19.03</literal> channel. To see which NixOS channel you’re
+  installed from a 19.09 ISO, you will be subscribed to the
+  <literal>nixos-19.09</literal> channel. To see which NixOS channel you’re
   subscribed to, run the following as root:
 <screen>
 # nix-channel --list | grep nixos
@@ -75,13 +75,13 @@ nixos https://nixos.org/channels/nixos-unstable
 # nix-channel --add https://nixos.org/channels/<replaceable>channel-name</replaceable> nixos
 </screen>
   (Be sure to include the <literal>nixos</literal> parameter at the end.) For
-  instance, to use the NixOS 19.03 stable channel:
+  instance, to use the NixOS 19.09 stable channel:
 <screen>
-# nix-channel --add https://nixos.org/channels/nixos-19.03 nixos
+# nix-channel --add https://nixos.org/channels/nixos-19.09 nixos
 </screen>
   If you have a server, you may want to use the “small” channel instead:
 <screen>
-# nix-channel --add https://nixos.org/channels/nixos-19.03-small nixos
+# nix-channel --add https://nixos.org/channels/nixos-19.09-small nixos
 </screen>
   And if you want to live on the bleeding edge:
 <screen>
@@ -127,7 +127,7 @@ nixos https://nixos.org/channels/nixos-unstable
    current channel. (To see when the service runs, see <command>systemctl
    list-timers</command>.) You can also specify a channel explicitly, e.g.
 <programlisting>
-<xref linkend="opt-system.autoUpgrade.channel"/> = https://nixos.org/channels/nixos-19.03;
+<xref linkend="opt-system.autoUpgrade.channel"/> = https://nixos.org/channels/nixos-19.09;
 </programlisting>
   </para>
  </section>
diff --git a/nixos/doc/manual/man-configuration.xml b/nixos/doc/manual/man-configuration.xml
index 9f30b7925101..ddb1408fdcf5 100644
--- a/nixos/doc/manual/man-configuration.xml
+++ b/nixos/doc/manual/man-configuration.xml
@@ -8,8 +8,8 @@
 <!-- <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>
+  <refname><filename>configuration.nix</filename></refname>
+  <refpurpose>NixOS system configuration specification</refpurpose>
  </refnamediv>
  <refsection>
   <title>Description</title>
diff --git a/nixos/doc/manual/man-nixos-build-vms.xml b/nixos/doc/manual/man-nixos-build-vms.xml
index 7d6e04e0dd90..d114261f53be 100644
--- a/nixos/doc/manual/man-nixos-build-vms.xml
+++ b/nixos/doc/manual/man-nixos-build-vms.xml
@@ -8,8 +8,8 @@
 <!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
  </refmeta>
  <refnamediv>
-  <refname><command>nixos-build-vms</command>
-  </refname><refpurpose>build a network of virtual machines from a network of NixOS configurations</refpurpose>
+  <refname><command>nixos-build-vms</command></refname>
+  <refpurpose>build a network of virtual machines from a network of NixOS configurations</refpurpose>
  </refnamediv>
  <refsynopsisdiv>
   <cmdsynopsis>
diff --git a/nixos/doc/manual/man-nixos-enter.xml b/nixos/doc/manual/man-nixos-enter.xml
index 1481db467122..fe560d3efdd8 100644
--- a/nixos/doc/manual/man-nixos-enter.xml
+++ b/nixos/doc/manual/man-nixos-enter.xml
@@ -8,8 +8,8 @@
 <!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
  </refmeta>
  <refnamediv>
-  <refname><command>nixos-enter</command>
-  </refname><refpurpose>run a command in a NixOS chroot environment</refpurpose>
+  <refname><command>nixos-enter</command></refname>
+  <refpurpose>run a command in a NixOS chroot environment</refpurpose>
  </refnamediv>
  <refsynopsisdiv>
   <cmdsynopsis>
diff --git a/nixos/doc/manual/man-nixos-generate-config.xml b/nixos/doc/manual/man-nixos-generate-config.xml
index 61531a8f01ca..9ac3b918ff69 100644
--- a/nixos/doc/manual/man-nixos-generate-config.xml
+++ b/nixos/doc/manual/man-nixos-generate-config.xml
@@ -8,8 +8,8 @@
 <!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
  </refmeta>
  <refnamediv>
-  <refname><command>nixos-generate-config</command>
-  </refname><refpurpose>generate NixOS configuration modules</refpurpose>
+  <refname><command>nixos-generate-config</command></refname>
+  <refpurpose>generate NixOS configuration modules</refpurpose>
  </refnamediv>
  <refsynopsisdiv>
   <cmdsynopsis>
diff --git a/nixos/doc/manual/man-nixos-install.xml b/nixos/doc/manual/man-nixos-install.xml
index 4fb94ee7494c..45bbd5d81ff0 100644
--- a/nixos/doc/manual/man-nixos-install.xml
+++ b/nixos/doc/manual/man-nixos-install.xml
@@ -8,8 +8,8 @@
 <!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
  </refmeta>
  <refnamediv>
-  <refname><command>nixos-install</command>
-  </refname><refpurpose>install bootloader and NixOS</refpurpose>
+  <refname><command>nixos-install</command></refname>
+  <refpurpose>install bootloader and NixOS</refpurpose>
  </refnamediv>
  <refsynopsisdiv>
   <cmdsynopsis>
diff --git a/nixos/doc/manual/man-nixos-option.xml b/nixos/doc/manual/man-nixos-option.xml
index 3e316e10d4eb..81e3739b3be5 100644
--- a/nixos/doc/manual/man-nixos-option.xml
+++ b/nixos/doc/manual/man-nixos-option.xml
@@ -8,8 +8,8 @@
 <!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
  </refmeta>
  <refnamediv>
-  <refname><command>nixos-option</command>
-  </refname><refpurpose>inspect a NixOS configuration</refpurpose>
+  <refname><command>nixos-option</command></refname>
+  <refpurpose>inspect a NixOS configuration</refpurpose>
  </refnamediv>
  <refsynopsisdiv>
   <cmdsynopsis>
diff --git a/nixos/doc/manual/man-nixos-rebuild.xml b/nixos/doc/manual/man-nixos-rebuild.xml
index 4c20cfcdd7d2..c697b7ee0472 100644
--- a/nixos/doc/manual/man-nixos-rebuild.xml
+++ b/nixos/doc/manual/man-nixos-rebuild.xml
@@ -7,10 +7,12 @@
   <refmiscinfo class="source">NixOS</refmiscinfo>
 <!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
  </refmeta>
+
  <refnamediv>
-  <refname><command>nixos-rebuild</command>
-  </refname><refpurpose>reconfigure a NixOS machine</refpurpose>
+  <refname><command>nixos-rebuild</command></refname>
+  <refpurpose>reconfigure a NixOS machine</refpurpose>
  </refnamediv>
+
  <refsynopsisdiv>
   <cmdsynopsis>
    <command>nixos-rebuild</command><group choice='req'>
@@ -74,6 +76,7 @@
    <arg>
     <option>--builders</option> <replaceable>builder-spec</replaceable>
    </arg>
+
    <sbr />
    <arg>
     <group choice='req'>
@@ -121,8 +124,10 @@
    </arg>
   </cmdsynopsis>
  </refsynopsisdiv>
+
  <refsection>
   <title>Description</title>
+
   <para>
    This command updates the system so that it corresponds to the configuration
    specified in <filename>/etc/nixos/configuration.nix</filename>. Thus, every
@@ -133,9 +138,11 @@
    (re)starts any system services if needed. Please note that user services need
    to be started manually as they aren't detected by the activation script at the moment.
   </para>
+
   <para>
    This command has one required argument, which specifies the desired
    operation. It must be one of the following:
+
    <variablelist>
     <varlistentry>
      <term>
@@ -152,6 +159,7 @@
       </para>
      </listitem>
     </varlistentry>
+
     <varlistentry>
      <term>
       <option>boot</option>
@@ -165,6 +173,7 @@
       </para>
      </listitem>
     </varlistentry>
+
     <varlistentry>
      <term>
       <option>test</option>
@@ -179,6 +188,7 @@
       </para>
      </listitem>
     </varlistentry>
+
     <varlistentry>
      <term>
       <option>build</option>
@@ -197,6 +207,7 @@
       </para>
      </listitem>
     </varlistentry>
+
     <varlistentry>
      <term>
       <option>dry-build</option>
@@ -208,6 +219,7 @@
       </para>
      </listitem>
     </varlistentry>
+
     <varlistentry>
      <term>
       <option>dry-activate</option>
@@ -222,6 +234,7 @@
       </para>
      </listitem>
     </varlistentry>
+
     <varlistentry>
      <term>
       <option>edit</option>
@@ -232,6 +245,7 @@
       </para>
      </listitem>
     </varlistentry>
+
     <varlistentry>
      <term>
       <option>build-vm</option>
@@ -249,12 +263,14 @@
 <prompt>$ </prompt>./result/bin/run-*-vm
 </screen>
       </para>
+
       <para>
        The VM is implemented using the <literal>qemu</literal> package. For
        best performance, you should load the <literal>kvm-intel</literal> or
        <literal>kvm-amd</literal> kernel modules to get hardware
        virtualisation.
       </para>
+
       <para>
        The VM mounts the Nix store of the host through the 9P file system. The
        host Nix store is read-only, so Nix commands that modify the Nix store
@@ -262,6 +278,7 @@
        <command>nixos-rebuild</command>; to change the VM’s configuration,
        you must halt the VM and re-run the commands above.
       </para>
+
       <para>
        The VM has its own <literal>ext3</literal> root file system, which is
        automatically created when the VM is first started, and is persistent
@@ -272,6 +289,7 @@
       </para>
      </listitem>
     </varlistentry>
+
     <varlistentry>
      <term>
       <option>build-vm-with-bootloader</option>
@@ -294,11 +312,13 @@
    </variablelist>
   </para>
  </refsection>
+
  <refsection>
   <title>Options</title>
   <para>
    This command accepts the following options:
   </para>
+
   <variablelist>
    <varlistentry>
     <term>
@@ -310,6 +330,7 @@
      </para>
     </listitem>
    </varlistentry>
+
    <varlistentry>
     <term>
      <option>--install-bootloader</option>
@@ -321,6 +342,7 @@
      </para>
     </listitem>
    </varlistentry>
+
    <varlistentry>
     <term>
      <option>--no-build-nix</option>
@@ -336,6 +358,7 @@
      </para>
     </listitem>
    </varlistentry>
+
    <varlistentry>
     <term>
      <option>--fast</option>
@@ -349,6 +372,7 @@
      </para>
     </listitem>
    </varlistentry>
+
    <varlistentry>
     <term>
      <option>--rollback</option>
@@ -363,6 +387,7 @@
      </para>
     </listitem>
    </varlistentry>
+
    <varlistentry>
     <term>
      <option>--builders</option> <replaceable>builder-spec</replaceable>
@@ -382,6 +407,7 @@
      </para>
     </listitem>
    </varlistentry>
+
    <varlistentry>
     <term>
      <option>--profile-name</option>
@@ -412,6 +438,7 @@
      </para>
     </listitem>
    </varlistentry>
+
    <varlistentry>
     <term>
      <option>--build-host</option>
@@ -437,6 +464,7 @@
      </para>
     </listitem>
    </varlistentry>
+
    <varlistentry>
     <term>
      <option>--target-host</option>
@@ -449,6 +477,7 @@
       be accessible over ssh, and for the commands <option>switch</option>,
       <option>boot</option> and <option>test</option> you need root access.
      </para>
+
      <para>
       If <option>--build-host</option> is not explicitly specified,
       <option>--build-host</option> will implicitly be set to the same value as
@@ -457,6 +486,7 @@
       place remotely (and no build artifacts will be copied to the local
       machine).
      </para>
+
      <para>
       You can include a remote user name in the host name
       (<replaceable>user@host</replaceable>). You can also set ssh options by
@@ -465,6 +495,7 @@
     </listitem>
    </varlistentry>
   </variablelist>
+
   <para>
    In addition, <command>nixos-rebuild</command> accepts various Nix-related
    flags, including <option>--max-jobs</option> / <option>-j</option>,
@@ -473,8 +504,10 @@
    <option>-v</option>. See the Nix manual for details.
   </para>
  </refsection>
+
  <refsection>
   <title>Environment</title>
+
   <variablelist>
    <varlistentry>
     <term>
@@ -487,6 +520,7 @@
      </para>
     </listitem>
    </varlistentry>
+
    <varlistentry>
     <term>
      <envar>NIX_SSHOPTS</envar>
@@ -500,9 +534,12 @@
    </varlistentry>
   </variablelist>
  </refsection>
+
  <refsection>
   <title>Files</title>
+
   <variablelist>
+
    <varlistentry>
     <term>
      <filename>/run/current-system</filename>
@@ -513,6 +550,7 @@
      </para>
     </listitem>
    </varlistentry>
+
    <varlistentry>
     <term>
      <filename>/nix/var/nix/profiles/system</filename>
@@ -524,8 +562,10 @@
      </para>
     </listitem>
    </varlistentry>
+
   </variablelist>
  </refsection>
+
  <refsection>
   <title>Bugs</title>
   <para>
diff --git a/nixos/doc/manual/man-nixos-version.xml b/nixos/doc/manual/man-nixos-version.xml
index 931c4a5ad029..e9ad8bddcace 100644
--- a/nixos/doc/manual/man-nixos-version.xml
+++ b/nixos/doc/manual/man-nixos-version.xml
@@ -7,8 +7,8 @@
   <refmiscinfo class="source">NixOS</refmiscinfo>
  </refmeta>
  <refnamediv>
-  <refname><command>nixos-version</command>
-  </refname><refpurpose>show the NixOS version</refpurpose>
+  <refname><command>nixos-version</command></refname>
+  <refpurpose>show the NixOS version</refpurpose>
  </refnamediv>
  <refsynopsisdiv>
   <cmdsynopsis>
diff --git a/nixos/doc/manual/manual.xml b/nixos/doc/manual/manual.xml
index 12f52e1997c8..18a67a2dd941 100644
--- a/nixos/doc/manual/manual.xml
+++ b/nixos/doc/manual/manual.xml
@@ -8,32 +8,7 @@
   <subtitle>Version <xi:include href="./generated/version" parse="text" />
   </subtitle>
  </info>
- <preface xml:id="preface">
-  <title>Preface</title>
-  <para>
-   This manual describes how to install, use and extend NixOS, a Linux
-   distribution based on the purely functional package management system Nix.
-  </para>
-  <para>
-   If you encounter problems, please report them on the
-   <literal
-    xlink:href="https://discourse.nixos.org">Discourse</literal> or
-   on the <link
-    xlink:href="irc://irc.freenode.net/#nixos">
-   <literal>#nixos</literal> channel on Freenode</link>. Bugs should be
-   reported in
-   <link
-    xlink:href="https://github.com/NixOS/nixpkgs/issues">NixOS’
-   GitHub issue tracker</link>.
-  </para>
-  <note>
-   <para>
-    Commands prefixed with <literal>#</literal> have to be run as root, either
-    requiring to login as root user or temporarily switching to it using
-    <literal>sudo</literal> for example.
-   </para>
-  </note>
- </preface>
+ <xi:include href="preface.xml" />
  <xi:include href="installation/installation.xml" />
  <xi:include href="configuration/configuration.xml" />
  <xi:include href="administration/running.xml" />
diff --git a/nixos/doc/manual/preface.xml b/nixos/doc/manual/preface.xml
new file mode 100644
index 000000000000..6ac9ae7e7861
--- /dev/null
+++ b/nixos/doc/manual/preface.xml
@@ -0,0 +1,37 @@
+<preface xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xml:id="preface">
+ <title>Preface</title>
+ <para>
+  This manual describes how to install, use and extend NixOS, a Linux
+  distribution based on the purely functional package management system
+  <link xlink:href="https://nixos.org/nix">Nix</link>, that is composed
+  using modules and packages defined in the
+  <link xlink:href="https://nixos.org/nixpkgs">Nixpkgs</link> project.
+ </para>
+ <para>
+  Additional information regarding the Nix package manager and the Nixpkgs
+  project can be found in respectively the
+  <link xlink:href="https://nixos.org/nix/manual">Nix manual</link> and the
+  <link xlink:href="https://nixos.org/nixpkgs/manual">Nixpkgs manual</link>.
+ </para>
+ <para>
+  If you encounter problems, please report them on the
+  <literal
+   xlink:href="https://discourse.nixos.org">Discourse</literal> or
+  on the <link
+   xlink:href="irc://irc.freenode.net/#nixos">
+  <literal>#nixos</literal> channel on Freenode</link>. Bugs should be
+  reported in
+  <link
+   xlink:href="https://github.com/NixOS/nixpkgs/issues">NixOS’
+  GitHub issue tracker</link>.
+ </para>
+ <note>
+  <para>
+   Commands prefixed with <literal>#</literal> have to be run as root, either
+   requiring to login as root user or temporarily switching to it using
+   <literal>sudo</literal> for example.
+  </para>
+ </note>
+</preface>
diff --git a/nixos/doc/manual/release-notes/rl-1909.xml b/nixos/doc/manual/release-notes/rl-1909.xml
index f9cea242c153..8bd353a34304 100644
--- a/nixos/doc/manual/release-notes/rl-1909.xml
+++ b/nixos/doc/manual/release-notes/rl-1909.xml
@@ -3,7 +3,7 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="sec-release-19.09">
- <title>Release 19.09 (“Loris”, 2019/09/??)</title>
+ <title>Release 19.09 (“Loris”, 2019/10/09)</title>
 
  <section xmlns="http://docbook.org/ns/docbook"
          xmlns:xlink="http://www.w3.org/1999/xlink"
@@ -25,6 +25,26 @@
    </listitem>
    <listitem>
     <para>
+     Nix has been updated to 2.3; see its
+     <link xlink:href="https://nixos.org/nix/manual/#ssec-relnotes-2.3">release
+     notes</link>.
+    </para>
+   </listitem>
+   <listitem>
+    <para>Core version changes:</para>
+    <para>systemd: 239 -&gt; 243</para>
+    <para>gcc: 7 -&gt; 8</para>
+    <para>glibc: 2.27 (unchanged)</para>
+    <para>linux: 4.19 LTS (unchanged)</para>
+    <para>openssl: 1.0 -&gt; 1.1</para>
+   </listitem>
+   <listitem>
+    <para>Desktop version changes:</para>
+    <para>plasma5: 5.14 -&gt; 5.16</para>
+    <para>gnome3: 3.30 -&gt; 3.32</para>
+   </listitem>
+   <listitem>
+    <para>
      PHP now defaults to PHP 7.3, updated from 7.2.
     </para>
    </listitem>
@@ -170,6 +190,13 @@
      </listitem>
     </itemizedlist>
    </listitem>
+   <listitem>
+    <para>
+     <xref linkend="opt-services.blueman.enable"/> has been added.
+     If you previously had blueman installed via <option>environment.systemPackages</option> please
+     migrate to using the NixOS module, as this would result in an insufficiently configured blueman.
+    </para>
+   </listitem>
   </itemizedlist>
 
  </section>
@@ -478,6 +505,92 @@
      Prometheus 2 is now configured with <literal>services.prometheus</literal>.
     </para>
    </listitem>
+   <listitem>
+     <para>
+       Citrix Receiver (<literal>citrix_receiver</literal>) has been dropped in favor of Citrix Workspace
+       (<literal>citrix_workspace</literal>).
+     </para>
+   </listitem>
+   <listitem>
+     <para>
+       The <literal>services.gitlab</literal> module has had its literal secret options (<option>services.gitlab.smtp.password</option>,
+       <option>services.gitlab.databasePassword</option>,
+       <option>services.gitlab.initialRootPassword</option>,
+       <option>services.gitlab.secrets.secret</option>,
+       <option>services.gitlab.secrets.db</option>,
+       <option>services.gitlab.secrets.otp</option> and
+       <option>services.gitlab.secrets.jws</option>) replaced by file-based versions (<option>services.gitlab.smtp.passwordFile</option>,
+       <option>services.gitlab.databasePasswordFile</option>,
+       <option>services.gitlab.initialRootPasswordFile</option>,
+       <option>services.gitlab.secrets.secretFile</option>,
+       <option>services.gitlab.secrets.dbFile</option>,
+       <option>services.gitlab.secrets.otpFile</option> and
+       <option>services.gitlab.secrets.jwsFile</option>). This was done so that secrets aren't stored
+       in the world-readable nix store, but means that for each option you'll have to create a file with
+       the same exact string, add "File" to the end of the option name, and change the definition to a
+       string pointing to the corresponding file; e.g. <literal>services.gitlab.databasePassword = "supersecurepassword"</literal>
+       becomes <literal>services.gitlab.databasePasswordFile = "/path/to/secret_file"</literal> where the
+       file <literal>secret_file</literal> contains the string <literal>supersecurepassword</literal>.
+     </para>
+     <para>
+       The state path (<option>services.gitlab.statePath</option>) now has the following restriction:
+       no parent directory can be owned by any other user than <literal>root</literal> or the user
+       specified in <option>services.gitlab.user</option>; i.e. if <option>services.gitlab.statePath</option>
+       is set to <literal>/var/lib/gitlab/state</literal>, <literal>gitlab</literal> and all parent directories
+       must be owned by either <literal>root</literal> or the user specified in <option>services.gitlab.user</option>.
+     </para>
+   </listitem>
+   <listitem>
+     <para>
+      The <option>networking.useDHCP</option> option is unsupported in combination with
+      <option>networking.useNetworkd</option> in anticipation of defaulting to it by default.
+      It has to be set to <literal>false</literal> and enabled per
+      interface with <option>networking.interfaces.&lt;name&gt;.useDHCP = true;</option>
+    </para>
+   </listitem>
+   <listitem>
+     <para>
+       The Twitter client <literal>corebird</literal> has been dropped as <link xlink:href="https://www.patreon.com/posts/corebirds-future-18921328">it is discontinued and does not work against the new Twitter API</link>.
+       Please use the fork <literal>cawbird</literal> instead which has been adapted to the API changes and is still maintained.
+     </para>
+   </listitem>
+   <listitem>
+     <para>
+      The <literal>nodejs-11_x</literal> package has been removed as it's EOLed by upstream.
+     </para>
+   </listitem>
+   <listitem>
+     <para>
+       Because of the systemd upgrade,
+       <application>systemd-timesyncd</application> will no longer work if
+       <option>system.stateVersion</option> is not set correctly. When
+       upgrading from NixOS 19.03, please make sure that
+       <option>system.stateVersion</option> is set to
+       <literal>"19.03"</literal>, or lower if the installation dates back to an
+       earlier version of NixOS.
+     </para>
+   </listitem>
+   <listitem>
+     <para>
+       Due to the short lifetime of non-LTS kernel releases package attributes like <literal>linux_5_1</literal>,
+       <literal>linux_5_2</literal> and <literal>linux_5_3</literal> have been removed to discourage dependence
+       on specific non-LTS kernel versions in stable NixOS releases.
+
+       Going forward, versioned attributes like <literal>linux_4_9</literal> will exist for LTS versions only.
+       Please use <literal>linux_latest</literal> or <literal>linux_testing</literal> if you depend on non-LTS
+       releases. Keep in mind that <literal>linux_latest</literal> and <literal>linux_testing</literal> will
+       change versions under the hood during the lifetime of a stable release and might include breaking changes.
+     </para>
+   </listitem>
+   <listitem>
+     <para>
+       Because of the systemd upgrade,
+       some network interfaces might change their name. For details see
+       <link xlink:href="https://www.freedesktop.org/software/systemd/man/systemd.net-naming-scheme.html#History">
+       upstream docs</link> or <link xlink:href="https://github.com/NixOS/nixpkgs/issues/71086">
+       our ticket</link>.
+     </para>
+   </listitem>
   </itemizedlist>
  </section>
 
@@ -676,7 +789,7 @@
       </para>
      </listitem>
     </itemizedlist>
-     This also configures the kernel to pass coredumps to <literal>systemd-coredump</literal>,
+     This also configures the kernel to pass core dumps to <literal>systemd-coredump</literal>,
      and restricts the SysRq key combinations to the sync command only.
      These sysctl snippets can be found in <literal>/etc/sysctl.d/50-*.conf</literal>,
      and overridden via <link linkend="opt-boot.kernel.sysctl">boot.kernel.sysctl</link>
@@ -685,12 +798,15 @@
    </listitem>
    <listitem>
     <para>
-      Coredumps are now acquired by <literal>systemd-coredump</literal> by default.
-      <literal>systemd-coredump</literal> behaviour can still be modified via
-      <option>systemd.coredump.extraConfig</option>.
-      To stick to the old behaviour (having the kernel dump to a file called <literal>core</literal>
-      in the working directory), without piping it through <literal>systemd-coredump</literal>, set
-      <option>boot.kernel.sysctl."kernel.core_pattern"</option> to <literal>"core"</literal>.
+      Core dumps are now processed by <literal>systemd-coredump</literal>
+      by default. <literal>systemd-coredump</literal> behaviour can
+      still be modified via
+      <option>systemd.coredump.extraConfig</option>. To stick to the
+      old behaviour (having the kernel dump to a file called
+      <literal>core</literal> in the working directory), without piping
+      it through <literal>systemd-coredump</literal>, set
+      <option>systemd.coredump.enable</option> to
+      <literal>false</literal>.
     </para>
    </listitem>
    <listitem>
diff --git a/nixos/doc/manual/release-notes/rl-2003.xml b/nixos/doc/manual/release-notes/rl-2003.xml
index c84bc8dbb379..f001a18b1c1f 100644
--- a/nixos/doc/manual/release-notes/rl-2003.xml
+++ b/nixos/doc/manual/release-notes/rl-2003.xml
@@ -23,6 +23,32 @@
      Support is planned until the end of October 2020, handing over to 20.09.
     </para>
    </listitem>
+   <listitem>
+    <para>
+     Postgresql for NixOS service now defaults to v11.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     The graphical installer image starts the graphical session automatically.
+     Before you'd be greeted by a tty and asked to enter <command>systemctl start display-manager</command>.
+     It is now possible to disable the display-manager from running by selecting the <literal>Disable display-manager</literal>
+     quirk in the boot menu.
+    </para>
+   </listitem>
+   <listitem>
+     <para>
+       By default zfs pools will now be trimmed on a weekly basis.
+       Trimming is only done on supported devices (i.e. NVME or SSDs)
+       and should improve throughput and lifetime of these devices.
+       It is controlled by the <varname>services.zfs.trim.enable</varname> varname.
+       The zfs scrub service (<varname>services.zfs.autoScrub.enable</varname>)
+       and the zfs autosnapshot service (<varname>services.zfs.autoSnapshot.enable</varname>)
+       are now only enabled if zfs is set in <varname>config.boot.initrd.supportedFilesystems</varname> or
+       <varname>config.boot.supportedFilesystems</varname>. These lists will automatically contain
+       zfs as soon as any zfs mountpoint is configured in <varname>fileSystems</varname>.
+     </para>
+   </listitem>
   </itemizedlist>
  </section>
 
@@ -59,7 +85,45 @@
 
   <itemizedlist>
    <listitem>
-    <para />
+    <para>
+      GnuPG is now built without support for a graphical passphrase entry
+      by default. Please enable the <literal>gpg-agent</literal> user service
+      via the NixOS option <literal>programs.gnupg.agent.enable</literal>.
+      Note that upstream recommends using <literal>gpg-agent</literal> and
+      will spawn a <literal>gpg-agent</literal> on the first invocation of
+      GnuPG anyway.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     The <literal>dynamicHosts</literal> option has been removed from the
+     <link linkend="opt-networking.networkmanager.enable">networkd</link>
+     module. Allowing (multiple) regular users to override host entries
+     affecting the whole system opens up a huge attack vector.
+     There seem to be very rare cases where this might be useful.
+     Consider setting system-wide host entries using
+     <link linkend="opt-networking.hosts">networking.hosts</link>, provide
+     them via the DNS server in your network, or use
+     <link linkend="opt-environment.etc">environment.etc</link>
+     to add a file into <literal>/etc/NetworkManager/dnsmasq.d</literal>
+     reconfiguring <literal>hostsdir</literal>.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+      The <literal>99-main.network</literal> file was removed. Maching all
+      network interfaces caused many breakages, see
+      <link xlink:href="https://github.com/NixOS/nixpkgs/pull/18962">#18962</link>
+        and <link xlink:href="https://github.com/NixOS/nixpkgs/pull/71106">#71106</link>.
+    </para>
+    <para>
+      We already don't support the global <link linkend="opt-networking.useDHCP">networking.useDHCP</link>,
+      <link linkend="opt-networking.defaultGateway">networking.defaultGateway</link> and
+      <link linkend="opt-networking.defaultGateway6">networking.defaultGateway6</link> options
+      if <link linkend="opt-networking.useNetworkd">networking.useNetworkd</link> is enabled,
+      but direct users to configure the per-device
+      <link linkend="opt-networking.interfaces">networking.interfaces.&lt;name&gt;.…</link> options.
+    </para>
    </listitem>
   </itemizedlist>
  </section>
@@ -73,7 +137,15 @@
 
   <itemizedlist>
    <listitem>
-    <para />
+     <para>SD images are now compressed by default using <literal>bzip2</literal>.</para>
+   </listitem>
+   <listitem>
+    <para>
+     OpenSSH has been upgraded from 7.9 to 8.1, improving security and adding features
+     but with potential incompatibilities.  Consult the
+     <link xlink:href="https://www.openssh.com/txt/release-8.1">
+     release announcement</link> for more information.
+    </para>
    </listitem>
   </itemizedlist>
  </section>
diff --git a/nixos/doc/xmlformat.conf b/nixos/doc/xmlformat.conf
index 4a565c8465bc..c3f39c7fd81b 100644
--- a/nixos/doc/xmlformat.conf
+++ b/nixos/doc/xmlformat.conf
@@ -37,7 +37,6 @@ para abstract
   entry-break  1
   exit-break   1
   normalize    yes
-  wrap-length  79
 
 title
   format       block
diff --git a/nixos/lib/make-iso9660-image.nix b/nixos/lib/make-iso9660-image.nix
index 8cd19b6e1874..0f3f2b5b5234 100644
--- a/nixos/lib/make-iso9660-image.nix
+++ b/nixos/lib/make-iso9660-image.nix
@@ -10,9 +10,9 @@
   contents
 
 , # In addition to `contents', the closure of the store paths listed
-  # in `packages' are also placed in the Nix store of the CD.  This is
-  # a list of attribute sets {object, symlink} where `object' if a
-  # store path whose closure will be copied, and `symlink' is a
+  # in `storeContents' are also placed in the Nix store of the CD.
+  # This is a list of attribute sets {object, symlink} where `object'
+  # is a store path whose closure will be copied, and `symlink' is a
   # symlink to `object' that will be added to the CD.
   storeContents ? []
 
diff --git a/nixos/maintainers/scripts/ec2/create-amis.sh b/nixos/maintainers/scripts/ec2/create-amis.sh
index c4149e3e8ffe..f08e500e079a 100755
--- a/nixos/maintainers/scripts/ec2/create-amis.sh
+++ b/nixos/maintainers/scripts/ec2/create-amis.sh
@@ -14,7 +14,7 @@
 set -euo pipefail
 
 # configuration
-state_dir=/home/deploy/amis/ec2-images
+state_dir=$HOME/amis/ec2-images
 home_region=eu-west-1
 bucket=nixos-amis
 
diff --git a/nixos/maintainers/scripts/gce/create-gce.sh b/nixos/maintainers/scripts/gce/create-gce.sh
index 48748a59d298..77cc64e591e9 100755
--- a/nixos/maintainers/scripts/gce/create-gce.sh
+++ b/nixos/maintainers/scripts/gce/create-gce.sh
@@ -15,7 +15,7 @@ nix-build '<nixpkgs/nixos/lib/eval-config.nix>' \
    -j 10
 
 img_path=$(echo gce/*.tar.gz)
-img_name=$(basename "$img_path")
+img_name=${IMAGE_NAME:-$(basename "$img_path")}
 img_id=$(echo "$img_name" | sed 's|.raw.tar.gz$||;s|\.|-|g;s|_|-|g')
 if ! gsutil ls "gs://${BUCKET_NAME}/$img_name"; then
   gsutil cp "$img_path" "gs://${BUCKET_NAME}/$img_name"
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
index dc7305b1ba24..d0db8fedecd8 100644
--- a/nixos/modules/config/i18n.nix
+++ b/nixos/modules/config/i18n.nix
@@ -89,11 +89,7 @@ with lib;
       };
 
       consoleKeyMap = mkOption {
-        type = mkOptionType {
-          name = "string or path";
-          check = t: (isString t || types.path.check t);
-        };
-
+        type = with types; either str path;
         default = "us";
         example = "fr";
         description = ''
diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix
index 74cf74d74181..873b8073fed9 100644
--- a/nixos/modules/config/no-x-libs.nix
+++ b/nixos/modules/config/no-x-libs.nix
@@ -34,7 +34,6 @@ with lib;
       networkmanager-openvpn = super.networkmanager-openvpn.override { withGnome = false; };
       networkmanager-vpnc = super.networkmanager-vpnc.override { withGnome = false; };
       networkmanager-iodine = super.networkmanager-iodine.override { withGnome = false; };
-      pinentry = super.pinentry.override { gtk2 = null; gcr = null; qt4 = null; qt5 = null; };
       gobject-introspection = super.gobject-introspection.override { x11Support = false; };
     }));
   };
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
index 5c3e39302583..9baad9b58545 100644
--- a/nixos/modules/config/pulseaudio.nix
+++ b/nixos/modules/config/pulseaudio.nix
@@ -51,8 +51,7 @@ let
   # that we can disable the autospawn feature in programs that
   # are built with PulseAudio support (like KDE).
   clientConf = writeText "client.conf" ''
-    autospawn=${if nonSystemWide then "yes" else "no"}
-    ${optionalString nonSystemWide "daemon-binary=${binary}"}
+    autospawn=no
     ${cfg.extraClientConf}
   '';
 
@@ -99,11 +98,12 @@ in {
         description = ''
           If false, a PulseAudio server is launched automatically for
           each user that tries to use the sound system. The server runs
-          with user privileges. This is the recommended and most secure
-          way to use PulseAudio. If true, one system-wide PulseAudio
+          with user privileges. If true, one system-wide PulseAudio
           server is launched on boot, running as the user "pulse", and
           only users in the "audio" group will have access to the server.
           Please read the PulseAudio documentation for more details.
+
+          Don't enable this option unless you know what you are doing.
         '';
       };
 
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
index ba89784033e6..b79e16cd7979 100644
--- a/nixos/modules/config/shells-environment.nix
+++ b/nixos/modules/config/shells-environment.nix
@@ -165,6 +165,8 @@ in
     # terminal instead of logging out of X11).
     environment.variables = config.environment.sessionVariables;
 
+    environment.profileRelativeEnvVars = config.environment.profileRelativeSessionVariables;
+
     environment.shellAliases = mapAttrs (name: mkDefault) {
       ls = "ls --color=tty";
       ll = "ls -l";
diff --git a/nixos/modules/config/sysctl.nix b/nixos/modules/config/sysctl.nix
index fb2b58eed720..e59c7a32c287 100644
--- a/nixos/modules/config/sysctl.nix
+++ b/nixos/modules/config/sysctl.nix
@@ -54,7 +54,7 @@ in
 
     # Hide kernel pointers (e.g. in /proc/modules) for unprivileged
     # users as these make it easier to exploit kernel vulnerabilities.
-    boot.kernel.sysctl."kernel.kptr_restrict" = 1;
+    boot.kernel.sysctl."kernel.kptr_restrict" = mkDefault 1;
 
     # Disable YAMA by default to allow easy debugging.
     boot.kernel.sysctl."kernel.yama.ptrace_scope" = mkDefault 0;
diff --git a/nixos/modules/config/system-environment.nix b/nixos/modules/config/system-environment.nix
index 6011e354ece4..4888740ba3d5 100644
--- a/nixos/modules/config/system-environment.nix
+++ b/nixos/modules/config/system-environment.nix
@@ -18,25 +18,88 @@ in
       default = {};
       description = ''
         A set of environment variables used in the global environment.
-        These variables will be set by PAM.
-        The value of each variable can be either a string or a list of
-        strings.  The latter is concatenated, interspersed with colon
-        characters.
+        These variables will be set by PAM early in the login process.
+
+        The value of each session variable can be either a string or a
+        list of strings. The latter is concatenated, interspersed with
+        colon characters.
+
+        Note, due to limitations in the PAM format values may not
+        contain the <literal>"</literal> character.
+
+        Also, these variables are merged into
+        <xref linkend="opt-environment.variables"/> and it is
+        therefore not possible to use PAM style variables such as
+        <code>@{HOME}</code>.
       '';
       type = with types; attrsOf (either str (listOf str));
       apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v);
     };
 
+    environment.profileRelativeSessionVariables = mkOption {
+      type = types.attrsOf (types.listOf types.str);
+      example = { PATH = [ "/bin" ]; MANPATH = [ "/man" "/share/man" ]; };
+      description = ''
+        Attribute set of environment variable used in the global
+        environment. These variables will be set by PAM early in the
+        login process.
+
+        Variable substitution is available as described in
+        <citerefentry>
+          <refentrytitle>pam_env.conf</refentrytitle>
+          <manvolnum>5</manvolnum>
+        </citerefentry>.
+
+        Each attribute maps to a list of relative paths. Each relative
+        path is appended to the each profile of
+        <option>environment.profiles</option> to form the content of
+        the corresponding environment variable.
+
+        Also, these variables are merged into
+        <xref linkend="opt-environment.profileRelativeEnvVars"/> and it is
+        therefore not possible to use PAM style variables such as
+        <code>@{HOME}</code>.
+      '';
+    };
+
   };
 
   config = {
 
-    system.build.pamEnvironment = pkgs.writeText "pam-environment"
-       ''
-         ${concatStringsSep "\n" (
-           (mapAttrsToList (n: v: ''${n}="${concatStringsSep ":" v}"'')
-             (zipAttrsWith (const concatLists) ([ (mapAttrs (n: v: [ v ]) cfg.sessionVariables) ]))))}
-       '';
+    system.build.pamEnvironment =
+      let
+        suffixedVariables =
+          flip mapAttrs cfg.profileRelativeSessionVariables (envVar: suffixes:
+            flip concatMap cfg.profiles (profile:
+              map (suffix: "${profile}${suffix}") suffixes
+            )
+          );
+
+        # We're trying to use the same syntax for PAM variables and env variables.
+        # That means we need to map the env variables that people might use to their
+        # equivalent PAM variable.
+        replaceEnvVars = replaceStrings ["$HOME" "$USER"] ["@{HOME}" "@{PAM_USER}"];
+
+        pamVariable = n: v:
+          ''${n}   DEFAULT="${concatStringsSep ":" (map replaceEnvVars (toList v))}"'';
+
+        pamVariables =
+          concatStringsSep "\n"
+          (mapAttrsToList pamVariable
+          (zipAttrsWith (n: concatLists)
+            [
+              # Make sure security wrappers are prioritized without polluting
+              # shell environments with an extra entry. Sessions which depend on
+              # pam for its environment will otherwise have eg. broken sudo. In
+              # particular Gnome Shell sometimes fails to source a proper
+              # environment from a shell.
+              { PATH = [ config.security.wrapperDir ]; }
+
+              (mapAttrs (n: toList) cfg.sessionVariables)
+              suffixedVariables
+            ]));
+      in
+        pkgs.writeText "pam-environment" "${pamVariables}\n";
 
   };
 
diff --git a/nixos/modules/config/terminfo.nix b/nixos/modules/config/terminfo.nix
index b86ce2dbf057..1396640af672 100644
--- a/nixos/modules/config/terminfo.nix
+++ b/nixos/modules/config/terminfo.nix
@@ -12,7 +12,7 @@
       source = "${config.system.path}/share/terminfo";
     };
 
-    environment.profileRelativeEnvVars = {
+    environment.profileRelativeSessionVariables = {
       TERMINFO_DIRS = [ "/share/terminfo" ];
     };
 
diff --git a/nixos/modules/config/unix-odbc-drivers.nix b/nixos/modules/config/unix-odbc-drivers.nix
index 8dd811727389..abc12a627d6f 100644
--- a/nixos/modules/config/unix-odbc-drivers.nix
+++ b/nixos/modules/config/unix-odbc-drivers.nix
@@ -24,7 +24,7 @@ in {
         Specifies Unix ODBC drivers to be registered in
         <filename>/etc/odbcinst.ini</filename>.  You may also want to
         add <literal>pkgs.unixODBC</literal> to the system path to get
-        a command line client to connnect to ODBC databases.
+        a command line client to connect to ODBC databases.
       '';
     };
   };
diff --git a/nixos/modules/config/update-users-groups.pl b/nixos/modules/config/update-users-groups.pl
index 59cea51c611b..15e448b787aa 100644
--- a/nixos/modules/config/update-users-groups.pl
+++ b/nixos/modules/config/update-users-groups.pl
@@ -56,12 +56,12 @@ sub allocGid {
         $gidsUsed{$prevGid} = 1;
         return $prevGid;
     }
-    return allocId(\%gidsUsed, \%gidsPrevUsed, 400, 499, 0, sub { my ($gid) = @_; getgrgid($gid) });
+    return allocId(\%gidsUsed, \%gidsPrevUsed, 400, 999, 0, sub { my ($gid) = @_; getgrgid($gid) });
 }
 
 sub allocUid {
     my ($name, $isSystemUser) = @_;
-    my ($min, $max, $up) = $isSystemUser ? (400, 499, 0) : (1000, 29999, 1);
+    my ($min, $max, $up) = $isSystemUser ? (400, 999, 0) : (1000, 29999, 1);
     my $prevUid = $uidMap->{$name};
     if (defined $prevUid && $prevUid >= $min && $prevUid <= $max && !defined $uidsUsed{$prevUid}) {
         print STDERR "reviving user '$name' with UID $prevUid\n";
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index ba79bd3d6ecc..ae3bdeb00e64 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -251,7 +251,7 @@ let
         default = [];
         example = literalExample "[ pkgs.firefox pkgs.thunderbird ]";
         description = ''
-          The set of packages that should be made availabe to the user.
+          The set of packages that should be made available to the user.
           This is in contrast to <option>environment.systemPackages</option>,
           which adds packages to all users.
         '';
diff --git a/nixos/modules/config/xdg/icons.nix b/nixos/modules/config/xdg/icons.nix
index 8268a3771a0e..4677ce090b0b 100644
--- a/nixos/modules/config/xdg/icons.nix
+++ b/nixos/modules/config/xdg/icons.nix
@@ -7,21 +7,32 @@ with lib;
       type = types.bool;
       default = true;
       description = ''
-        Whether to install files to support the 
+        Whether to install files to support the
         <link xlink:href="https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html">XDG Icon Theme specification</link>.
       '';
     };
   };
 
   config = mkIf config.xdg.icons.enable {
-    environment.pathsToLink = [ 
-      "/share/icons" 
-      "/share/pixmaps" 
+    environment.pathsToLink = [
+      "/share/icons"
+      "/share/pixmaps"
+    ];
+
+    # libXcursor looks for cursors in XCURSOR_PATH
+    # it mostly follows the spec for icons
+    # See: https://www.x.org/releases/current/doc/man/man3/Xcursor.3.xhtml Themes
+
+    # These are preferred so they come first in the list
+    environment.sessionVariables.XCURSOR_PATH = [
+      "$HOME/.icons"
+      "$HOME/.local/share/icons"
+    ];
+
+    environment.profileRelativeSessionVariables.XCURSOR_PATH = [
+      "/share/icons"
+      "/share/pixmaps"
     ];
-    
-    environment.profileRelativeEnvVars = {
-      XCURSOR_PATH = [ "/share/icons" ];
-    };
   };
 
 }
diff --git a/nixos/modules/hardware/brightnessctl.nix b/nixos/modules/hardware/brightnessctl.nix
index 341e4b791c23..2d54398d10df 100644
--- a/nixos/modules/hardware/brightnessctl.nix
+++ b/nixos/modules/hardware/brightnessctl.nix
@@ -25,6 +25,7 @@ in
 
   config = mkIf cfg.enable {
     services.udev.packages = with pkgs; [ brightnessctl ];
+    environment.systemPackages = with pkgs; [ brightnessctl ];
   };
 
 }
diff --git a/nixos/modules/hardware/steam-hardware.nix b/nixos/modules/hardware/steam-hardware.nix
index 378aeffe71b5..6218c9ffbb9b 100644
--- a/nixos/modules/hardware/steam-hardware.nix
+++ b/nixos/modules/hardware/steam-hardware.nix
@@ -21,5 +21,12 @@ in
     services.udev.packages = [
       pkgs.steamPackages.steam
     ];
+
+    # The uinput module needs to be loaded in order to trigger the udev rules
+    # defined in the steam package for setting permissions on /dev/uinput.
+    #
+    # If the udev rules are not triggered, some controllers won't work with
+    # steam.
+    boot.kernelModules = [ "uinput" ];
   };
 }
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix
index 1578e1547bc1..719ba5ffb127 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix
@@ -25,9 +25,6 @@ with lib;
   services.xserver = {
     enable = true;
 
-    # Don't start the X server by default.
-    autorun = mkForce false;
-
     # Automatically login as nixos.
     displayManager.slim = {
       enable = true;
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix
index 559899b0a3b1..1dc7920ff640 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix
@@ -22,14 +22,8 @@ with lib;
 
   system.activationScripts.installerDesktop = let
 
-    manualDesktopFile = pkgs.writeScript "nixos-manual.desktop" ''
-      [Desktop Entry]
-      Version=1.0
-      Type=Application
-      Name=NixOS Manual
-      Exec=firefox ${config.system.build.manual.manual}/share/doc/nixos/index.html
-      Icon=text-html
-    '';
+    # Comes from documentation.nix when xserver and nixos.enable are true.
+    manualDesktopFile = "/run/current-system/sw/share/applications/nixos-manual.desktop";
 
     homeDir = "/home/nixos/";
     desktopDir = homeDir + "Desktop/";
diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix
index d5c92cfc1d9e..009f1e2c543a 100644
--- a/nixos/modules/installer/cd-dvd/iso-image.nix
+++ b/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -24,7 +24,7 @@ let
         # Name appended to menuentry defaults to params if no specific name given.
         option.name or (if option ? params then "(${option.params})" else "")
         }' ${if option ? class then " --class ${option.class}" else ""} {
-          linux ${defaults.image} ${defaults.params} ${
+          linux ${defaults.image} \''${isoboot} ${defaults.params} ${
             option.params or ""
           }
           initrd ${defaults.initrd}
@@ -165,8 +165,8 @@ let
     else
       "# No refind for ${targetArch}"
   ;
-  
-  grubPkgs = if config.boot.loader.grub.forcei686 then pkgs.pkgsi686Linux else pkgs; 
+
+  grubPkgs = if config.boot.loader.grub.forcei686 then pkgs.pkgsi686Linux else pkgs;
 
   grubMenuCfg = ''
     #
@@ -268,6 +268,12 @@ let
     set timeout=10
     ${grubMenuCfg}
 
+    # If the parameter iso_path is set, append the findiso parameter to the kernel
+    # line. We need this to allow the nixos iso to be booted from grub directly.
+    if [ \''${iso_path} ] ; then
+      set isoboot="findiso=\''${iso_path}"
+    fi
+
     #
     # Menu entries
     #
@@ -284,6 +290,14 @@ let
         ${buildMenuAdditionalParamsGrub2 config "video=1920x1080@60"}
       }
 
+      # If we boot into a graphical environment where X is autoran
+      # and always crashes, it makes the media unusable. Allow the user
+      # to disable this.
+      submenu "Disable display-manager" --class quirk-disable-displaymanager {
+        ${grubMenuCfg}
+        ${buildMenuAdditionalParamsGrub2 config "systemd.mask=display-manager.service"}
+      }
+
       # Some laptop and convertibles have the panel installed in an
       # inconvenient way, rotated away from the keyboard.
       # Those entries makes it easier to use the installer.
@@ -562,8 +576,6 @@ in
 
     boot.initrd.availableKernelModules = [ "squashfs" "iso9660" "uas" ];
 
-    boot.blacklistedKernelModules = [ "nouveau" ];
-
     boot.initrd.kernelModules = [ "loop" ];
 
     # Closures to be copied to the Nix store on the CD, namely the init
@@ -618,6 +630,9 @@ in
         { source = "${efiDir}/EFI";
           target = "/EFI";
         }
+        { source = pkgs.writeText "loopback.cfg" "source /EFI/boot/grub.cfg";
+          target = "/boot/grub/loopback.cfg";
+        }
       ] ++ optionals (config.boot.loader.grub.memtest86.enable && canx86BiosBoot) [
         { source = "${pkgs.memtest86plus}/memtest.bin";
           target = "/boot/memtest.bin";
diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
index 2a131d9ce980..ba4127eaa0e8 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
@@ -19,7 +19,7 @@ in
   boot.loader.generic-extlinux-compatible.enable = true;
 
   boot.consoleLogLevel = lib.mkDefault 7;
-  boot.kernelPackages = pkgs.linuxPackages_rpi;
+  boot.kernelPackages = pkgs.linuxPackages_rpi1;
 
   sdImage = {
     populateFirmwareCommands = let
diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix
new file mode 100644
index 000000000000..c545a1e7e242
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix
@@ -0,0 +1,31 @@
+# To build, use:
+# nix-build nixos -I nixos-config=nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix -A config.system.build.sdImage
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../profiles/base.nix
+    ../../profiles/installation-device.nix
+    ./sd-image.nix
+  ];
+
+  boot.loader.grub.enable = false;
+  boot.loader.raspberryPi.enable = true;
+  boot.loader.raspberryPi.version = 4;
+  boot.kernelPackages = pkgs.linuxPackages_rpi4;
+
+  boot.consoleLogLevel = lib.mkDefault 7;
+
+  sdImage = {
+    firmwareSize = 128;
+    # This is a hack to avoid replicating config.txt from boot.loader.raspberryPi
+    populateFirmwareCommands =
+      "${config.system.build.installBootLoader} ${config.system.build.toplevel} -d ./firmware";
+    # As the boot process is done entirely in the firmware partition.
+    populateRootCommands = "";
+  };
+
+  # the installation media is also the installation target,
+  # so we don't want to provide the installation configuration.nix.
+  installer.cloneConfig = false;
+}
diff --git a/nixos/modules/installer/cd-dvd/sd-image.nix b/nixos/modules/installer/cd-dvd/sd-image.nix
index 0a0150441554..d510f3b2daf2 100644
--- a/nixos/modules/installer/cd-dvd/sd-image.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image.nix
@@ -98,6 +98,16 @@ in
         populate the ./files/boot (/boot) directory.
       '';
     };
+
+    compressImage = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether the SD image should be compressed using
+        <command>bzip2</command>.
+      '';
+    };
+
   };
 
   config = {
@@ -118,10 +128,12 @@ in
 
     sdImage.storePaths = [ config.system.build.toplevel ];
 
-    system.build.sdImage = pkgs.callPackage ({ stdenv, dosfstools, e2fsprogs, mtools, libfaketime, utillinux }: stdenv.mkDerivation {
+    system.build.sdImage = pkgs.callPackage ({ stdenv, dosfstools, e2fsprogs, mtools, libfaketime, utillinux, bzip2 }: stdenv.mkDerivation {
       name = config.sdImage.imageName;
 
-      nativeBuildInputs = [ dosfstools e2fsprogs mtools libfaketime utillinux ];
+      nativeBuildInputs = [ dosfstools e2fsprogs mtools libfaketime utillinux bzip2 ];
+
+      inherit (config.sdImage) compressImage;
 
       buildCommand = ''
         mkdir -p $out/nix-support $out/sd-image
@@ -168,14 +180,19 @@ in
         # Verify the FAT partition before copying it.
         fsck.vfat -vn firmware_part.img
         dd conv=notrunc if=firmware_part.img of=$img seek=$START count=$SECTORS
+        if test -n "$compressImage"; then
+            bzip2 $img
+        fi
       '';
     }) {};
 
     boot.postBootCommands = ''
       # On the first boot do some maintenance tasks
       if [ -f /nix-path-registration ]; then
+        set -euo pipefail
+        set -x
         # Figure out device names for the boot device and root filesystem.
-        rootPart=$(readlink -f /dev/disk/by-label/NIXOS_SD)
+        rootPart=$(${pkgs.utillinux}/bin/findmnt -n -o SOURCE /)
         bootDevice=$(lsblk -npo PKNAME $rootPart)
 
         # Resize the root partition and the filesystem to fit the disk
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index 2673887d2b96..d7149b35d4c0 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,6 +1,6 @@
 {
-  x86_64-linux = "/nix/store/3ds3cgji9vjxdbgp10av6smyym1126d1-nix-2.3";
-  i686-linux = "/nix/store/ln1ndqvfpc9cdl03vqxi6kvlxm9wfv9g-nix-2.3";
-  aarch64-linux = "/nix/store/n8a1rwzrp20qcr2c4hvyn6c5q9zx8csw-nix-2.3";
-  x86_64-darwin = "/nix/store/jq6npmpld02sz4rgniz0qrsdfnm6j17a-nix-2.3";
+  x86_64-linux = "/nix/store/6chjfy4j6hjwj5f8zcbbdg02i21x1qsi-nix-2.3.1";
+  i686-linux = "/nix/store/xa8z7fwszjjm4kiwrxfc8xv9c1pzzm7a-nix-2.3.1";
+  aarch64-linux = "/nix/store/8cac1ivcnchlpzmdjby2f71l1fwpnymr-nix-2.3.1";
+  x86_64-darwin = "/nix/store/6639l9815ggdnb4aka22qcjy7p8w4hb9-nix-2.3.1";
 }
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index cfdbdaabf5c5..f2ffe61c42cb 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -563,6 +563,24 @@ $fsAndSwap
 ${\join "", (map { "  $_\n" } (uniq @attrs))}}
 EOF
 
+sub generateNetworkingDhcpConfig {
+    my $config = <<EOF;
+  # The global useDHCP flag is deprecated, therefore explicitly set to false here.
+  # Per-interface useDHCP will be mandatory in the future, so this generated config
+  # replicates the default behaviour.
+  networking.useDHCP = false;
+EOF
+
+    foreach my $path (glob "/sys/class/net/*") {
+        my $dev = basename($path);
+        if ($dev ne "lo") {
+            $config .= "  networking.interfaces.$dev.useDHCP = true;\n";
+        }
+    }
+
+    return $config;
+}
+
 
 if ($showHardwareConfig) {
     print STDOUT $hwConfig;
@@ -606,6 +624,8 @@ EOF
 EOF
         }
 
+        my $networkingDhcpConfig = generateNetworkingDhcpConfig();
+
         write_file($fn, <<EOF);
 @configuration@
 EOF
diff --git a/nixos/modules/installer/tools/nixos-rebuild.sh b/nixos/modules/installer/tools/nixos-rebuild.sh
index 6a08c9b4c6c6..891f374df536 100644
--- a/nixos/modules/installer/tools/nixos-rebuild.sh
+++ b/nixos/modules/installer/tools/nixos-rebuild.sh
@@ -22,6 +22,7 @@ repair=
 profile=/nix/var/nix/profiles/system
 buildHost=
 targetHost=
+maybeSudo=
 
 while [ "$#" -gt 0 ]; do
     i="$1"; shift 1
@@ -96,6 +97,9 @@ while [ "$#" -gt 0 ]; do
     esac
 done
 
+if [ -n "$SUDO_USER" ]; then
+    maybeSudo="sudo "
+fi
 
 if [ -z "$buildHost" -a -n "$targetHost" ]; then
     buildHost="$targetHost"
@@ -111,9 +115,9 @@ buildHostCmd() {
     if [ -z "$buildHost" ]; then
         "$@"
     elif [ -n "$remoteNix" ]; then
-        ssh $SSHOPTS "$buildHost" PATH="$remoteNix:$PATH" "$@"
+        ssh $SSHOPTS "$buildHost" env PATH="$remoteNix:$PATH" "$maybeSudo$@"
     else
-        ssh $SSHOPTS "$buildHost" "$@"
+        ssh $SSHOPTS "$buildHost" "$maybeSudo$@"
     fi
 }
 
@@ -121,7 +125,7 @@ targetHostCmd() {
     if [ -z "$targetHost" ]; then
         "$@"
     else
-        ssh $SSHOPTS "$targetHost" "$@"
+        ssh $SSHOPTS "$targetHost" "$maybeSudo$@"
     fi
 }
 
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index 05add59117d1..9e6eead3c4d7 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -96,6 +96,7 @@ in
         # networking.hostName = "nixos"; # Define your hostname.
         # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.
 
+      $networkingDhcpConfig
         # Configure network proxy if necessary
         # networking.proxy.default = "http://user:password\@proxy:port/";
         # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
@@ -119,7 +120,11 @@ in
         # Some programs need SUID wrappers, can be configured further or are
         # started in user sessions.
         # programs.mtr.enable = true;
-        # programs.gnupg.agent = { enable = true; enableSSHSupport = true; };
+        # programs.gnupg.agent = {
+        #   enable = true;
+        #   enableSSHSupport = true;
+        #   flavour = "gnome3";
+        # };
 
         # List services that you want to enable:
 
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index ac6af1ce8b77..3e8a5b07a5ed 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -328,7 +328,7 @@
       qemu-libvirtd = 301;
       # kvm = 302; # unused
       # render = 303; # unused
-      zeronet = 304;
+      # zeronet = 304; # removed 2019-01-03
       lirc = 305;
       lidarr = 306;
       slurm = 307;
@@ -629,7 +629,7 @@
       qemu-libvirtd = 301;
       kvm = 302; # default udev rules from systemd requires these
       render = 303; # default udev rules from systemd requires these
-      zeronet = 304;
+      # zeronet = 304; # removed 2019-01-03
       lirc = 305;
       lidarr = 306;
       slurm = 307;
diff --git a/nixos/modules/misc/locate.nix b/nixos/modules/misc/locate.nix
index 449149e4bb65..737ed5c0a3f6 100644
--- a/nixos/modules/misc/locate.nix
+++ b/nixos/modules/misc/locate.nix
@@ -128,7 +128,10 @@ in {
 
     # directory creation needs to be separated from main service
     # because ReadWritePaths fails when the directory doesn't already exist
-    systemd.tmpfiles.rules = [ "d ${dirOf cfg.output} 0755 root root -" ];
+    systemd.tmpfiles.rules =
+      let dir = dirOf cfg.output; in
+      mkIf (dir != "/var/cache")
+        [ "d ${dir} 0755 root root -" ];
 
     systemd.services.update-locatedb =
       { description = "Update Locate Database";
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 5b7f391ed5a5..df6e4dc1336a 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -98,7 +98,6 @@
   ./programs/autojump.nix
   ./programs/bash/bash.nix
   ./programs/bcc.nix
-  ./programs/blcr.nix
   ./programs/browserpass.nix
   ./programs/captive-browser.nix
   ./programs/ccache.nix
@@ -143,7 +142,6 @@
   ./programs/seahorse.nix
   ./programs/slock.nix
   ./programs/shadow.nix
-  ./programs/shell.nix
   ./programs/spacefm.nix
   ./programs/singularity.nix
   ./programs/ssh.nix
@@ -229,6 +227,7 @@
   ./services/backup/rsnapshot.nix
   ./services/backup/tarsnap.nix
   ./services/backup/tsm.nix
+  ./services/backup/zfs-replication.nix
   ./services/backup/znapzend.nix
   ./services/cluster/hadoop/default.nix
   ./services/cluster/kubernetes/addons/dns.nix
@@ -299,10 +298,11 @@
   ./services/desktops/gnome3/chrome-gnome-shell.nix
   ./services/desktops/gnome3/evolution-data-server.nix
   ./services/desktops/gnome3/glib-networking.nix
+  ./services/desktops/gnome3/gnome-initial-setup.nix
   ./services/desktops/gnome3/gnome-keyring.nix
   ./services/desktops/gnome3/gnome-online-accounts.nix
-  ./services/desktops/gnome3/gnome-remote-desktop.nix
   ./services/desktops/gnome3/gnome-online-miners.nix
+  ./services/desktops/gnome3/gnome-remote-desktop.nix
   ./services/desktops/gnome3/gnome-settings-daemon.nix
   ./services/desktops/gnome3/gnome-user-share.nix
   ./services/desktops/gnome3/rygel.nix
@@ -322,12 +322,14 @@
   ./services/games/factorio.nix
   ./services/games/minecraft-server.nix
   ./services/games/minetest-server.nix
+  ./services/games/openarena.nix
   ./services/games/terraria.nix
   ./services/hardware/acpid.nix
   ./services/hardware/actkbd.nix
   ./services/hardware/bluetooth.nix
   ./services/hardware/bolt.nix
   ./services/hardware/brltty.nix
+  ./services/hardware/fancontrol.nix
   ./services/hardware/freefall.nix
   ./services/hardware/fwupd.nix
   ./services/hardware/illum.nix
@@ -502,6 +504,7 @@
   ./services/monitoring/das_watchdog.nix
   ./services/monitoring/datadog-agent.nix
   ./services/monitoring/dd-agent/dd-agent.nix
+  ./services/monitoring/do-agent.nix
   ./services/monitoring/fusion-inventory.nix
   ./services/monitoring/grafana.nix
   ./services/monitoring/grafana-reporter.nix
@@ -547,6 +550,8 @@
   ./services/network-filesystems/nfsd.nix
   ./services/network-filesystems/openafs/client.nix
   ./services/network-filesystems/openafs/server.nix
+  ./services/network-filesystems/orangefs/server.nix
+  ./services/network-filesystems/orangefs/client.nix
   ./services/network-filesystems/rsyncd.nix
   ./services/network-filesystems/samba.nix
   ./services/network-filesystems/tahoe.nix
@@ -567,7 +572,6 @@
   ./services/networking/bird.nix
   ./services/networking/bitlbee.nix
   ./services/networking/charybdis.nix
-  ./services/networking/chrony.nix
   ./services/networking/cjdns.nix
   ./services/networking/cntlm.nix
   ./services/networking/connman.nix
@@ -601,6 +605,7 @@
   ./services/networking/gdomap.nix
   ./services/networking/git-daemon.nix
   ./services/networking/gnunet.nix
+  ./services/networking/go-shadowsocks2.nix
   ./services/networking/gogoclient.nix
   ./services/networking/gvpe.nix
   ./services/networking/hans.nix
@@ -650,14 +655,15 @@
   ./services/networking/nntp-proxy.nix
   ./services/networking/nsd.nix
   ./services/networking/ntopng.nix
-  ./services/networking/ntpd.nix
+  ./services/networking/ntp/chrony.nix
+  ./services/networking/ntp/ntpd.nix
+  ./services/networking/ntp/openntpd.nix
   ./services/networking/nullidentdmod.nix
   ./services/networking/nylon.nix
   ./services/networking/ocserv.nix
   ./services/networking/ofono.nix
   ./services/networking/oidentd.nix
   ./services/networking/openfire.nix
-  ./services/networking/openntpd.nix
   ./services/networking/openvpn.nix
   ./services/networking/ostinato.nix
   ./services/networking/owamp.nix
@@ -665,6 +671,7 @@
   ./services/networking/polipo.nix
   ./services/networking/powerdns.nix
   ./services/networking/pdns-recursor.nix
+  ./services/networking/pppd.nix
   ./services/networking/pptpd.nix
   ./services/networking/prayer.nix
   ./services/networking/privoxy.nix
@@ -711,6 +718,7 @@
   ./services/networking/tinc.nix
   ./services/networking/tinydns.nix
   ./services/networking/tftpd.nix
+  ./services/networking/trickster.nix
   ./services/networking/tox-bootstrapd.nix
   ./services/networking/tox-node.nix
   ./services/networking/toxvpn.nix
@@ -727,6 +735,7 @@
   ./services/networking/xinetd.nix
   ./services/networking/xl2tpd.nix
   ./services/networking/xrdp.nix
+  ./services/networking/yggdrasil.nix
   ./services/networking/zerobin.nix
   ./services/networking/zeronet.nix
   ./services/networking/zerotierone.nix
@@ -791,6 +800,7 @@
   ./services/web-apps/cryptpad.nix
   ./services/web-apps/documize.nix
   ./services/web-apps/frab.nix
+  ./services/web-apps/gotify-server.nix
   ./services/web-apps/icingaweb2/icingaweb2.nix
   ./services/web-apps/icingaweb2/module-monitoring.nix
   ./services/web-apps/limesurvey.nix
@@ -805,6 +815,7 @@
   ./services/web-apps/restya-board.nix
   ./services/web-apps/tt-rss.nix
   ./services/web-apps/selfoss.nix
+  ./services/web-apps/shiori.nix
   ./services/web-apps/virtlyst.nix
   ./services/web-apps/wordpress.nix
   ./services/web-apps/youtrack.nix
@@ -938,6 +949,7 @@
   ./virtualisation/anbox.nix
   ./virtualisation/container-config.nix
   ./virtualisation/containers.nix
+  ./virtualisation/cri-o.nix
   ./virtualisation/docker.nix
   ./virtualisation/docker-containers.nix
   ./virtualisation/ecs-agent.nix
diff --git a/nixos/modules/profiles/hardened.nix b/nixos/modules/profiles/hardened.nix
index 626d8b1d2bde..f7b2f5c7fc1e 100644
--- a/nixos/modules/profiles/hardened.nix
+++ b/nixos/modules/profiles/hardened.nix
@@ -52,6 +52,27 @@ with lib;
     "ax25"
     "netrom"
     "rose"
+
+    # Old or rare or insufficiently audited filesystems
+    "adfs"
+    "affs"
+    "bfs"
+    "befs"
+    "cramfs"
+    "efs"
+    "erofs"
+    "exofs"
+    "freevxfs"
+    "f2fs"
+    "hfs"
+    "hpfs"
+    "jfs"
+    "minix"
+    "nilfs2"
+    "qnx4"
+    "qnx6"
+    "sysv"
+    "ufs"
   ];
 
   # Restrict ptrace() usage to processes with a pre-defined relationship
diff --git a/nixos/modules/profiles/installation-device.nix b/nixos/modules/profiles/installation-device.nix
index fd30220ce1c9..4596e163404c 100644
--- a/nixos/modules/profiles/installation-device.nix
+++ b/nixos/modules/profiles/installation-device.nix
@@ -31,9 +31,6 @@ with lib;
     # Let the user play Rogue on TTY 8 during the installation.
     #services.rogue.enable = true;
 
-    # Disable some other stuff we don't need.
-    services.udisks2.enable = mkDefault false;
-
     # Use less privileged nixos user
     users.users.nixos = {
       isNormalUser = true;
diff --git a/nixos/modules/profiles/qemu-guest.nix b/nixos/modules/profiles/qemu-guest.nix
index 315d04093b13..0ea70107f717 100644
--- a/nixos/modules/profiles/qemu-guest.nix
+++ b/nixos/modules/profiles/qemu-guest.nix
@@ -1,7 +1,7 @@
 # Common configuration for virtual machines running under QEMU (using
 # virtio).
 
-{ ... }:
+{ lib, ... }:
 
 {
   boot.initrd.availableKernelModules = [ "virtio_net" "virtio_pci" "virtio_mmio" "virtio_blk" "virtio_scsi" "9p" "9pnet_virtio" ];
@@ -15,5 +15,5 @@
       hwclock -s
     '';
 
-  security.rngd.enable = false;
+  security.rngd.enable = lib.mkDefault false;
 }
diff --git a/nixos/modules/programs/blcr.nix b/nixos/modules/programs/blcr.nix
deleted file mode 100644
index 804e1d01f12b..000000000000
--- a/nixos/modules/programs/blcr.nix
+++ /dev/null
@@ -1,27 +0,0 @@
-{ config, lib, ... }:
-
-let
-  inherit (lib) mkOption mkIf;
-  cfg = config.environment.blcr;
-  blcrPkg = config.boot.kernelPackages.blcr;
-in
-
-{
-  ###### interface
-
-  options = {
-    environment.blcr.enable = mkOption {
-      default = false;
-      description =
-        "Whether to enable support for the BLCR checkpointing tool.";
-    };
-  };
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-    boot.kernelModules = [ "blcr" "blcr_imports" ];
-    boot.extraModulePackages = [ blcrPkg ];
-    environment.systemPackages = [ blcrPkg ];
-  };
-}
diff --git a/nixos/modules/programs/environment.nix b/nixos/modules/programs/environment.nix
index 66eb83482664..38bdabb4fa81 100644
--- a/nixos/modules/programs/environment.nix
+++ b/nixos/modules/programs/environment.nix
@@ -20,7 +20,6 @@ in
       { NIXPKGS_CONFIG = "/etc/nix/nixpkgs-config.nix";
         PAGER = mkDefault "less -R";
         EDITOR = mkDefault "nano";
-        XCURSOR_PATH = [ "$HOME/.icons" ];
         XDG_CONFIG_DIRS = [ "/etc/xdg" ]; # needs to be before profile-relative paths to allow changes through environment.etc
       };
 
@@ -30,7 +29,7 @@ in
       ];
 
     # TODO: move most of these elsewhere
-    environment.profileRelativeEnvVars =
+    environment.profileRelativeSessionVariables =
       { PATH = [ "/bin" ];
         INFOPATH = [ "/info" "/share/info" ];
         KDEDIRS = [ "" ];
diff --git a/nixos/modules/programs/gnupg.nix b/nixos/modules/programs/gnupg.nix
index bcbc994efe9b..2d262d906579 100644
--- a/nixos/modules/programs/gnupg.nix
+++ b/nixos/modules/programs/gnupg.nix
@@ -6,6 +6,19 @@ let
 
   cfg = config.programs.gnupg;
 
+  xserverCfg = config.services.xserver;
+
+  defaultPinentryFlavor =
+    if xserverCfg.desktopManager.lxqt.enable
+    || xserverCfg.desktopManager.plasma5.enable then
+      "qt"
+    else if xserverCfg.desktopManager.xfce.enable then
+      "gtk2"
+    else if xserverCfg.enable || config.programs.sway.enable then
+      "gnome3"
+    else
+      null;
+
 in
 
 {
@@ -54,6 +67,20 @@ in
       '';
     };
 
+    agent.pinentryFlavor = mkOption {
+      type = types.nullOr (types.enum pkgs.pinentry.flavors);
+      example = "gnome3";
+      description = ''
+        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.
+        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).
+      '';
+    };
+
     dirmngr.enable = mkOption {
       type = types.bool;
       default = false;
@@ -64,6 +91,16 @@ in
   };
 
   config = mkIf cfg.agent.enable {
+    programs.gnupg.agent.pinentryFlavor = mkDefault defaultPinentryFlavor;
+
+    # This overrides the systemd user unit shipped with the gnupg package
+    systemd.user.services.gpg-agent = mkIf (cfg.agent.pinentryFlavor != null) {
+      serviceConfig.ExecStart = [ "" ''
+        ${pkgs.gnupg}/bin/gpg-agent --supervised \
+          --pinentry-program ${pkgs.pinentry.${cfg.agent.pinentryFlavor}}/bin/pinentry
+      '' ];
+    };
+
     systemd.user.sockets.gpg-agent = {
       wantedBy = [ "sockets.target" ];
     };
@@ -83,7 +120,9 @@ in
     systemd.user.sockets.dirmngr = mkIf cfg.dirmngr.enable {
       wantedBy = [ "sockets.target" ];
     };
-    
+
+    services.dbus.packages = mkIf (cfg.agent.pinentryFlavor == "gnome3") [ pkgs.gcr ];
+
     environment.systemPackages = with pkgs; [ cfg.package ];
     systemd.packages = [ cfg.package ];
 
diff --git a/nixos/modules/programs/less.nix b/nixos/modules/programs/less.nix
index e19935b77caf..75b3e707d576 100644
--- a/nixos/modules/programs/less.nix
+++ b/nixos/modules/programs/less.nix
@@ -54,8 +54,8 @@ in
         type = types.attrsOf types.str;
         default = {};
         example = {
-          h = "noaction 5\e(";
-          l = "noaction 5\e)";
+          h = "noaction 5\\e(";
+          l = "noaction 5\\e)";
         };
         description = "Defines new command keys.";
       };
diff --git a/nixos/modules/programs/mtr.nix b/nixos/modules/programs/mtr.nix
index 1fdec4c04f68..75b710c1584f 100644
--- a/nixos/modules/programs/mtr.nix
+++ b/nixos/modules/programs/mtr.nix
@@ -4,6 +4,7 @@ with lib;
 
 let
   cfg = config.programs.mtr;
+
 in {
   options = {
     programs.mtr = {
@@ -15,13 +16,22 @@ in {
           setcap wrapper for it.
         '';
       };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.mtr;
+        description = ''
+          The package to use.
+        '';
+      };
     };
   };
 
   config = mkIf cfg.enable {
-    environment.systemPackages = with pkgs; [ mtr ];
+    environment.systemPackages = with pkgs; [ cfg.package ];
+
     security.wrappers.mtr-packet = {
-      source = "${pkgs.mtr}/bin/mtr-packet";
+      source = "${cfg.package}/bin/mtr-packet";
       capabilities = "cap_net_raw+p";
     };
   };
diff --git a/nixos/modules/programs/seahorse.nix b/nixos/modules/programs/seahorse.nix
index c08b0a85374c..b229d2a2c0db 100644
--- a/nixos/modules/programs/seahorse.nix
+++ b/nixos/modules/programs/seahorse.nix
@@ -31,6 +31,8 @@ with lib;
 
   config = mkIf config.programs.seahorse.enable {
 
+    programs.ssh.askPassword = mkDefault "${pkgs.gnome3.seahorse}/libexec/seahorse/ssh-askpass";
+
     environment.systemPackages = [
       pkgs.gnome3.seahorse
     ];
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
index 8ec4169207db..7eaf79d864e7 100644
--- a/nixos/modules/programs/shadow.nix
+++ b/nixos/modules/programs/shadow.nix
@@ -6,17 +6,27 @@ 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  499
+      SYS_UID_MAX  999
       UID_MIN      1000
       UID_MAX      29999
 
       SYS_GID_MIN  400
-      SYS_GID_MAX  499
+      SYS_GID_MAX  999
       GID_MIN      1000
       GID_MAX      29999
 
diff --git a/nixos/modules/programs/shell.nix b/nixos/modules/programs/shell.nix
deleted file mode 100644
index b7f7b91b5fbe..000000000000
--- a/nixos/modules/programs/shell.nix
+++ /dev/null
@@ -1,54 +0,0 @@
-# This module defines a standard configuration for NixOS shells.
-
-{ config, lib, ... }:
-
-with lib;
-
-{
-
-  config = {
-
-    environment.shellInit =
-      ''
-        # Set up the per-user profile.
-        mkdir -m 0755 -p "$NIX_USER_PROFILE_DIR"
-        if [ "$(stat -c '%u' "$NIX_USER_PROFILE_DIR")" != "$(id -u)" ]; then
-            echo "WARNING: the per-user profile dir $NIX_USER_PROFILE_DIR should belong to user id $(id -u)" >&2
-        fi
-
-        if [ -w "$HOME" ]; then
-          if ! [ -L "$HOME/.nix-profile" ]; then
-              if [ "$USER" != root ]; then
-                  ln -s "$NIX_USER_PROFILE_DIR/profile" "$HOME/.nix-profile"
-              else
-                  # Root installs in the system-wide profile by default.
-                  ln -s /nix/var/nix/profiles/default "$HOME/.nix-profile"
-              fi
-          fi
-
-          # Subscribe the root user to the NixOS channel by default.
-          if [ "$USER" = root -a ! -e "$HOME/.nix-channels" ]; then
-              echo "${config.system.defaultChannel} nixos" > "$HOME/.nix-channels"
-          fi
-
-          # Create the per-user garbage collector roots directory.
-          NIX_USER_GCROOTS_DIR="/nix/var/nix/gcroots/per-user/$USER"
-          mkdir -m 0755 -p "$NIX_USER_GCROOTS_DIR"
-          if [ "$(stat -c '%u' "$NIX_USER_GCROOTS_DIR")" != "$(id -u)" ]; then
-              echo "WARNING: the per-user gcroots dir $NIX_USER_GCROOTS_DIR should belong to user id $(id -u)" >&2
-          fi
-
-          # Set up a default Nix expression from which to install stuff.
-          if [ ! -e "$HOME/.nix-defexpr" -o -L "$HOME/.nix-defexpr" ]; then
-              rm -f "$HOME/.nix-defexpr"
-              mkdir -p "$HOME/.nix-defexpr"
-              if [ "$USER" != root ]; then
-                  ln -s /nix/var/nix/profiles/per-user/root/channels "$HOME/.nix-defexpr/channels_root"
-              fi
-          fi
-        fi
-      '';
-
-  };
-
-}
diff --git a/nixos/modules/programs/sway.nix b/nixos/modules/programs/sway.nix
index b4f03151cdc1..f92d09a7ef44 100644
--- a/nixos/modules/programs/sway.nix
+++ b/nixos/modules/programs/sway.nix
@@ -55,7 +55,7 @@ in {
     extraPackages = mkOption {
       type = with types; listOf package;
       default = with pkgs; [
-        swaylock swayidle
+        swaylock swayidle swaybg
         xwayland rxvt_unicode dmenu
       ];
       defaultText = literalExample ''
diff --git a/nixos/modules/programs/x2goserver.nix b/nixos/modules/programs/x2goserver.nix
index 77a1a0da7993..7d74231e956b 100644
--- a/nixos/modules/programs/x2goserver.nix
+++ b/nixos/modules/programs/x2goserver.nix
@@ -69,6 +69,7 @@ in {
     users.users.x2go = {
       home = "/var/lib/x2go/db";
       group = "x2go";
+      isSystemUser = true;
     };
 
     security.wrappers.x2gosqliteWrapper = {
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index d1303f90ad8d..886e2e83ba62 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -66,6 +66,8 @@ with lib;
 
     (mkRenamedOptionModule [ "services" "clamav" "updater" "config" ] [ "services" "clamav" "updater" "extraConfig" ])
 
+    (mkRemovedOptionModule [ "services" "pykms" "verbose" ] "Use services.pykms.logLevel instead")
+
     (mkRemovedOptionModule [ "security" "setuidOwners" ] "Use security.wrappers instead")
     (mkRemovedOptionModule [ "security" "setuidPrograms" ] "Use security.wrappers instead")
 
@@ -133,7 +135,8 @@ with lib;
     # piwik was renamed to matomo
     (mkRenamedOptionModule [ "services" "piwik" "enable" ] [ "services" "matomo" "enable" ])
     (mkRenamedOptionModule [ "services" "piwik" "webServerUser" ] [ "services" "matomo" "webServerUser" ])
-    (mkRenamedOptionModule [ "services" "piwik" "phpfpmProcessManagerConfig" ] [ "services" "matomo" "phpfpmProcessManagerConfig" ])
+    (mkRemovedOptionModule [ "services" "piwik" "phpfpmProcessManagerConfig" ] "Use services.phpfpm.pools.<name>.settings")
+    (mkRemovedOptionModule [ "services" "matomo" "phpfpmProcessManagerConfig" ] "Use services.phpfpm.pools.<name>.settings")
     (mkRenamedOptionModule [ "services" "piwik" "nginx" ] [ "services" "matomo" "nginx" ])
 
     # tarsnap
@@ -231,7 +234,6 @@ with lib;
     (mkRemovedOptionModule [ "services" "mysql" "rootPassword" ] "Use socket authentication or set the password outside of the nix store.")
     (mkRemovedOptionModule [ "services" "zabbixServer" "dbPassword" ] "Use services.zabbixServer.database.passwordFile instead.")
     (mkRemovedOptionModule [ "systemd" "generator-packages" ] "Use systemd.packages instead.")
-    (mkRemovedOptionModule [ "systemd" "coredump" "enable" ] "Enabled by default. Set boot.kernel.sysctl.\"kernel.core_pattern\" = \"core\"; to disable.")
 
     # ZSH
     (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
@@ -273,19 +275,8 @@ with lib;
     (mkRenamedOptionModule [ "networking" "extraResolvconfConf" ] [ "networking" "resolvconf" "extraConfig" ])
     (mkRenamedOptionModule [ "networking" "resolvconfOptions" ] [ "networking" "resolvconf" "extraOptions" ])
 
-    # Redshift
-    (mkChangedOptionModule [ "services" "redshift" "latitude" ] [ "location" "latitude" ]
-      (config:
-        let value = getAttrFromPath [ "services" "redshift" "latitude" ] config;
-        in if value == null then
-          throw "services.redshift.latitude is set to null, you can remove this"
-          else builtins.fromJSON value))
-    (mkChangedOptionModule [ "services" "redshift" "longitude" ] [ "location" "longitude" ]
-      (config:
-        let value = getAttrFromPath [ "services" "redshift" "longitude" ] config;
-        in if value == null then
-          throw "services.redshift.longitude is set to null, you can remove this"
-          else builtins.fromJSON value))
+    # BLCR
+    (mkRemovedOptionModule [ "environment.blcr.enable" ] "The BLCR module has been removed")
 
     # Redis
     (mkRemovedOptionModule [ "services" "redis" "user" ] "The redis module now is hardcoded to the redis user.")
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index b321c04e574c..d14613f22b05 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -20,6 +20,16 @@ let
         '';
       };
 
+      server = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          ACME Directory Resource URI. Defaults to let's encrypt
+          production endpoint,
+          https://acme-v02.api.letsencrypt.org/directory, if unset.
+        '';
+      };
+
       domain = mkOption {
         type = types.str;
         default = name;
@@ -69,9 +79,9 @@ let
       plugins = mkOption {
         type = types.listOf (types.enum [
           "cert.der" "cert.pem" "chain.pem" "external.sh"
-          "fullchain.pem" "full.pem" "key.der" "key.pem" "account_key.json"
+          "fullchain.pem" "full.pem" "key.der" "key.pem" "account_key.json" "account_reg.json"
         ]);
-        default = [ "fullchain.pem" "full.pem" "key.pem" "account_key.json" ];
+        default = [ "fullchain.pem" "full.pem" "key.pem" "account_key.json" "account_reg.json" ];
         description = ''
           Plugins to enable. With default settings simp_le will
           store public certificate bundle in <filename>fullchain.pem</filename>,
@@ -109,7 +119,15 @@ in
 {
 
   ###### interface
-
+  imports = [
+    (mkRemovedOptionModule [ "security" "acme" "production" ] ''
+      Use security.acme.server to define your staging ACME server URL instead.
+
+      To use the let's encrypt staging server, use security.acme.server =
+      "https://acme-staging-v02.api.letsencrypt.org/directory".
+    ''
+    )
+  ];
   options = {
     security.acme = {
 
@@ -129,6 +147,16 @@ in
         '';
       };
 
+      server = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          ACME Directory Resource URI. Defaults to let's encrypt
+          production endpoint,
+          <literal>https://acme-v02.api.letsencrypt.org/directory</literal>, if unset.
+        '';
+      };
+
       preliminarySelfsigned = mkOption {
         type = types.bool;
         default = true;
@@ -142,20 +170,6 @@ in
         '';
       };
 
-      production = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          If set to true, use Let's Encrypt's production environment
-          instead of the staging environment. The main benefit of the
-          staging environment is to get much higher rate limits.
-
-          See
-          <literal>https://letsencrypt.org/docs/staging-environment</literal>
-          for more detail.
-        '';
-      };
-
       certs = mkOption {
         default = { };
         type = with types; attrsOf (submodule certOpts);
@@ -198,11 +212,16 @@ in
                           ++ optionals (data.email != null) [ "--email" data.email ]
                           ++ concatMap (p: [ "-f" p ]) data.plugins
                           ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains)
-                          ++ optionals (!cfg.production) ["--server" "https://acme-staging.api.letsencrypt.org/directory"];
+                          ++ optionals (cfg.server != null || data.server != null) ["--server" (if data.server == null then cfg.server else data.server)];
                 acmeService = {
                   description = "Renew ACME Certificate for ${cert}";
                   after = [ "network.target" "network-online.target" ];
                   wants = [ "network-online.target" ];
+                  # simp_le uses requests, which uses certifi under the hood,
+                  # which doesn't respect the system trust store.
+                  # At least in the acme test, we provision a fake CA, impersonating the LE endpoint.
+                  # REQUESTS_CA_BUNDLE is a way to teach python requests to use something else
+                  environment.REQUESTS_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt";
                   serviceConfig = {
                     Type = "oneshot";
                     SuccessExitStatus = [ "0" "1" ];
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 9c7ddc2f4eea..11227354ad3b 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -351,7 +351,7 @@ let
           ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth
               "auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"}
           ${let yubi = config.security.pam.yubico; in optionalString cfg.yubicoAuth
-              "auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so id=${toString yubi.id} ${optionalString yubi.debug "debug"}"}
+              "auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"}"}
         '' +
           # Modules in this block require having the password set in PAM_AUTHTOK.
           # pam_unix is marked as 'sufficient' on NixOS which means nothing will run
@@ -415,7 +415,7 @@ let
 
           # Session management.
           ${optionalString cfg.setEnvironment ''
-            session required pam_env.so envfile=${config.system.build.pamEnvironment}
+            session required pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
           ''}
           session required pam_unix.so
           ${optionalString cfg.setLoginUid
@@ -696,6 +696,23 @@ in
           Debug output to stderr.
         '';
       };
+      mode = mkOption {
+        default = "client";
+        type = types.enum [ "client" "challenge-response" ];
+        description = ''
+          Mode of operation.
+
+          Use "client" for online validation with a YubiKey validation service such as
+          the YubiCloud.
+
+          Use "challenge-response" for offline validation using YubiKeys with HMAC-SHA-1
+          Challenge-Response configurations. See the man-page ykpamcfg(1) for further
+          details on how to configure offline Challenge-Response validation. 
+
+          More information can be found <link
+          xlink:href="https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html">here</link>.
+        '';
+      };
     };
 
     security.pam.enableEcryptfs = mkOption {
@@ -742,13 +759,6 @@ in
     environment.etc =
       mapAttrsToList (n: v: makePAMService v) config.security.pam.services;
 
-    systemd.tmpfiles.rules = optionals
-      (any (s: s.updateWtmp) (attrValues config.security.pam.services))
-      [
-        "f /var/log/wtmp"
-        "f /var/log/lastlog"
-      ];
-
     security.pam.services =
       { other.text =
           ''
diff --git a/nixos/modules/services/admin/oxidized.nix b/nixos/modules/services/admin/oxidized.nix
index 39112c3970d5..da81be3f23e8 100644
--- a/nixos/modules/services/admin/oxidized.nix
+++ b/nixos/modules/services/admin/oxidized.nix
@@ -89,6 +89,7 @@ in
       group = cfg.group;
       home = cfg.dataDir;
       createHome = true;
+      isSystemUser = true;
     };
 
     systemd.services.oxidized = {
diff --git a/nixos/modules/services/amqp/rabbitmq.nix b/nixos/modules/services/amqp/rabbitmq.nix
index 302b94de1965..697732426ccf 100644
--- a/nixos/modules/services/amqp/rabbitmq.nix
+++ b/nixos/modules/services/amqp/rabbitmq.nix
@@ -80,7 +80,7 @@ in {
       configItems = mkOption {
         default = {};
         type = types.attrsOf types.str;
-        example = ''
+        example = literalExample ''
           {
             "auth_backends.1.authn" = "rabbit_auth_backend_ldap";
             "auth_backends.1.authz" = "rabbit_auth_backend_internal";
diff --git a/nixos/modules/services/audio/jack.nix b/nixos/modules/services/audio/jack.nix
index aa3351f401af..ceff366d0bbb 100644
--- a/nixos/modules/services/audio/jack.nix
+++ b/nixos/modules/services/audio/jack.nix
@@ -223,6 +223,7 @@ in {
         group = "jackaudio";
         extraGroups = [ "audio" ];
         description = "JACK Audio system service user";
+        isSystemUser = true;
       };
       # http://jackaudio.org/faq/linux_rt_config.html
       security.pam.loginLimits = [
diff --git a/nixos/modules/services/audio/spotifyd.nix b/nixos/modules/services/audio/spotifyd.nix
index e3556b2559c2..4b74e7532795 100644
--- a/nixos/modules/services/audio/spotifyd.nix
+++ b/nixos/modules/services/audio/spotifyd.nix
@@ -28,7 +28,7 @@ in
       after = [ "network-online.target" "sound.target" ];
       description = "spotifyd, a Spotify playing daemon";
       serviceConfig = {
-        ExecStart = "${pkgs.spotifyd}/bin/spotifyd --no-daemon --cache_path /var/cache/spotifyd --config ${spotifydConf}";
+        ExecStart = "${pkgs.spotifyd}/bin/spotifyd --no-daemon --cache-path /var/cache/spotifyd --config-path ${spotifydConf}";
         Restart = "always";
         RestartSec = 12;
         DynamicUser = true;
diff --git a/nixos/modules/services/backup/automysqlbackup.nix b/nixos/modules/services/backup/automysqlbackup.nix
index 1884f3536a97..e3a8d1f79934 100644
--- a/nixos/modules/services/backup/automysqlbackup.nix
+++ b/nixos/modules/services/backup/automysqlbackup.nix
@@ -99,7 +99,10 @@ in
 
     environment.systemPackages = [ pkg ];
 
-    users.users.${user}.group = group;
+    users.users.${user} = {
+      group = group;
+      isSystemUser = true;
+    };
     users.groups.${group} = { };
 
     systemd.tmpfiles.rules = [
diff --git a/nixos/modules/services/backup/borgbackup.nix b/nixos/modules/services/backup/borgbackup.nix
index 2ad116a7872a..10d42325a6b1 100644
--- a/nixos/modules/services/backup/borgbackup.nix
+++ b/nixos/modules/services/backup/borgbackup.nix
@@ -8,7 +8,7 @@ let
     builtins.substring 0 1 x == "/"      # absolute path
     || builtins.substring 0 1 x == "."   # relative path
     || builtins.match "[.*:.*]" == null; # not machine:path
- 
+
   mkExcludeFile = cfg:
     # Write each exclude pattern to a new line
     pkgs.writeText "excludefile" (concatStringsSep "\n" cfg.exclude);
@@ -104,12 +104,12 @@ let
       install = "install -o ${cfg.user} -g ${cfg.group}";
     in
       nameValuePair "borgbackup-job-${name}" (stringAfter [ "users" ] (''
-        # Eensure that the home directory already exists
+        # Ensure that the home directory already exists
         # We can't assert createHome == true because that's not the case for root
-        cd "${config.users.users.${cfg.user}.home}"                                                                                                         
+        cd "${config.users.users.${cfg.user}.home}"
         ${install} -d .config/borg
         ${install} -d .cache/borg
-      '' + optionalString (isLocalPath cfg.repo) ''
+      '' + optionalString (isLocalPath cfg.repo && !cfg.removableDevice) ''
         ${install} -d ${escapeShellArg cfg.repo}
       ''));
 
@@ -163,6 +163,13 @@ let
       + " without at least one public key";
   };
 
+  mkRemovableDeviceAssertions = name: cfg: {
+    assertion = !(isLocalPath cfg.repo) -> !cfg.removableDevice;
+    message = ''
+      borgbackup.repos.${name}: repo isn't a local path, thus it can't be a removable device!
+    '';
+  };
+
 in {
   meta.maintainers = with maintainers; [ dotlambda ];
 
@@ -202,6 +209,12 @@ in {
             example = "user@machine:/path/to/repo";
           };
 
+          removableDevice = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Whether the repo (which must be local) is a removable device.";
+          };
+
           archiveBaseName = mkOption {
             type = types.strMatching "[^/{}]+";
             default = "${globalConfig.networking.hostName}-${name}";
@@ -511,7 +524,6 @@ in {
     type = types.attrsOf (types.submodule (
       { ... }: {
         options = {
-          
           path = mkOption {
             type = types.path;
             description = ''
@@ -598,7 +610,8 @@ in {
     (with config.services.borgbackup; {
       assertions =
         mapAttrsToList mkPassAssertion jobs
-        ++ mapAttrsToList mkKeysAssertion repos;
+        ++ mapAttrsToList mkKeysAssertion repos
+        ++ mapAttrsToList mkRemovableDeviceAssertions jobs;
 
       system.activationScripts = mapAttrs' mkActivationScript jobs;
 
diff --git a/nixos/modules/services/backup/postgresql-wal-receiver.nix b/nixos/modules/services/backup/postgresql-wal-receiver.nix
index d9a37037992e..3d9869d53431 100644
--- a/nixos/modules/services/backup/postgresql-wal-receiver.nix
+++ b/nixos/modules/services/backup/postgresql-wal-receiver.nix
@@ -169,13 +169,14 @@ in {
     systemd.services = with attrsets; mapAttrs' (name: config: nameValuePair "postgresql-wal-receiver-${name}" {
       description = "PostgreSQL WAL receiver (${name})";
       wantedBy = [ "multi-user.target" ];
+      startLimitIntervalSec = 0; # retry forever, useful in case of network disruption
 
       serviceConfig = {
         User = "postgres";
         Group = "postgres";
         KillSignal = "SIGINT";
         Restart = "always";
-        RestartSec = 30;
+        RestartSec = 60;
       };
 
       inherit (config) environment;
diff --git a/nixos/modules/services/backup/znapzend.nix b/nixos/modules/services/backup/znapzend.nix
index f317078ddda2..203631a577f0 100644
--- a/nixos/modules/services/backup/znapzend.nix
+++ b/nixos/modules/services/backup/znapzend.nix
@@ -34,6 +34,8 @@ let
     description = "string of the form number{b|k|M|G}";
   };
 
+  enabledFeatures = concatLists (mapAttrsToList (name: enabled: optional enabled name) cfg.features);
+
   # Type for a string that must contain certain other strings (the list parameter).
   # Note that these would need regex escaping.
   stringContainingStrings = list: let
@@ -354,6 +356,22 @@ in
         '';
         default = false;
       };
+
+      features.recvu = mkEnableOption ''
+        recvu feature which uses <literal>-u</literal> on the receiving end to keep the destination
+        filesystem unmounted.
+      '';
+      features.compressed = mkEnableOption ''
+        compressed feature which adds the options <literal>-Lce</literal> to
+        the <command>zfs send</command> command. When this is enabled, make
+        sure that both the sending and receiving pool have the same relevant
+        features enabled. Using <literal>-c</literal> will skip unneccessary
+        decompress-compress stages, <literal>-L</literal> is for large block
+        support and -e is for embedded data support. see
+        <citerefentry><refentrytitle>znapzend</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        and <citerefentry><refentrytitle>zfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        for more info.
+      '';
     };
   };
 
@@ -381,12 +399,22 @@ in
         '';
 
         serviceConfig = {
+          # znapzendzetup --import apparently tries to connect to the backup
+          # host 3 times with a timeout of 30 seconds, leading to a startup
+          # delay of >90s when the host is down, which is just above the default
+          # service timeout of 90 seconds. Increase the timeout so it doesn't
+          # make the service fail in that case.
+          TimeoutStartSec = 180;
+          # Needs to have write access to ZFS
+          User = "root";
           ExecStart = let
             args = concatStringsSep " " [
               "--logto=${cfg.logTo}"
               "--loglevel=${cfg.logLevel}"
               (optionalString cfg.noDestroy "--nodestroy")
               (optionalString cfg.autoCreation "--autoCreation")
+              (optionalString (enabledFeatures != [])
+                "--features=${concatStringsSep "," enabledFeatures}")
             ]; in "${pkgs.znapzend}/bin/znapzend ${args}";
           ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
           Restart = "on-failure";
diff --git a/nixos/modules/services/continuous-integration/buildkite-agent.nix b/nixos/modules/services/continuous-integration/buildkite-agent.nix
index 12cc3d2b1ccc..32f361454bc1 100644
--- a/nixos/modules/services/continuous-integration/buildkite-agent.nix
+++ b/nixos/modules/services/continuous-integration/buildkite-agent.nix
@@ -191,6 +191,7 @@ in
         createHome = true;
         description = "Buildkite agent user";
         extraGroups = [ "keys" ];
+        isSystemUser = true;
       };
 
     environment.systemPackages = [ cfg.package ];
diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix
index 500acb485620..30c5550f71c5 100644
--- a/nixos/modules/services/continuous-integration/hydra/default.nix
+++ b/nixos/modules/services/continuous-integration/hydra/default.nix
@@ -242,8 +242,8 @@ in
     environment.variables = hydraEnv;
 
     nix.extraOptions = ''
-      gc-keep-outputs = true
-      gc-keep-derivations = true
+      keep-outputs = true
+      keep-derivations = true
 
       # The default (`true') slows Nix down a lot since the build farm
       # has so many GC roots.
@@ -275,6 +275,7 @@ in
               ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -O hydra hydra
               touch ${baseDir}/.db-created
             fi
+            echo "create extension if not exists pg_trgm" | ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} -- ${config.services.postgresql.package}/bin/psql hydra
           ''}
 
           if [ ! -e ${cfg.gcRootsDir} ]; then
@@ -379,6 +380,23 @@ in
           };
       };
 
+    systemd.services.hydra-notify =
+      { wantedBy = [ "multi-user.target" ];
+        requires = [ "hydra-init.service" ];
+        after = [ "hydra-init.service" ];
+        restartTriggers = [ hydraConf ];
+        environment = env // {
+          PGPASSFILE = "${baseDir}/pgpass-queue-runner";
+        };
+        serviceConfig =
+          { ExecStart = "@${cfg.package}/bin/hydra-notify hydra-notify";
+            # FIXME: run this under a less privileged user?
+            User = "hydra-queue-runner";
+            Restart = "always";
+            RestartSec = 5;
+          };
+      };
+
     # If there is less than a certain amount of free disk space, stop
     # the queue/evaluator to prevent builds from failing or aborting.
     systemd.services.hydra-check-space =
@@ -416,6 +434,8 @@ in
         hydra-users hydra-queue-runner hydra
         hydra-users hydra-www hydra
         hydra-users root hydra
+        # The postgres user is used to create the pg_trgm extension for the hydra database
+        hydra-users postgres postgres
       '';
 
     services.postgresql.authentication = optionalString haveLocalDB
diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix
index ec6a36413fe7..0ec906713885 100644
--- a/nixos/modules/services/continuous-integration/jenkins/default.nix
+++ b/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -193,7 +193,7 @@ in {
               then ""
               else
                 let pluginCmds = lib.attrsets.mapAttrsToList
-                      (n: v: "cp ${v} ${cfg.home}/plugins/${n}.hpi")
+                      (n: v: "cp ${v} ${cfg.home}/plugins/${n}.jpi")
                       cfg.plugins;
                 in ''
                   rm -r ${cfg.home}/plugins || true
diff --git a/nixos/modules/services/databases/memcached.nix b/nixos/modules/services/databases/memcached.nix
index 84d2c8674f4e..d1dfdb41bf40 100644
--- a/nixos/modules/services/databases/memcached.nix
+++ b/nixos/modules/services/databases/memcached.nix
@@ -67,6 +67,7 @@ in
     users.users = optional (cfg.user == "memcached") {
       name = "memcached";
       description = "Memcached server user";
+      isSystemUser = true;
     };
 
     environment.systemPackages = [ memcached ];
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
index df74cfc9a26b..39192d059485 100644
--- a/nixos/modules/services/databases/mysql.nix
+++ b/nixos/modules/services/databases/mysql.nix
@@ -272,8 +272,13 @@ in
       port = ${toString cfg.port}
       datadir = ${cfg.dataDir}
       ${optionalString (cfg.bind != null) "bind-address = ${cfg.bind}" }
-      ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "log-bin=mysql-bin"}
-      ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "server-id = ${toString cfg.replication.serverId}"}
+      ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave")
+      ''
+        log-bin=mysql-bin-${toString cfg.replication.serverId}
+        log-bin-index=mysql-bin-${toString cfg.replication.serverId}.index
+        relay-log=mysql-relay-bin
+        server-id = ${toString cfg.replication.serverId}
+      ''}
       ${optionalString (cfg.ensureUsers != [])
       ''
         plugin-load-add = auth_socket.so
@@ -381,6 +386,7 @@ in
 
                         ( echo "stop slave;"
                           echo "change master to master_host='${cfg.replication.masterHost}', master_user='${cfg.replication.masterUser}', master_password='${cfg.replication.masterPassword}';"
+                          echo "set global slave_exec_mode='IDEMPOTENT';"
                           echo "start slave;"
                         ) | ${mysql}/bin/mysql -u root -N
                       ''}
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index 7bba4dacddcc..3bedfe96a180 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -226,7 +226,8 @@ in
       # Note: when changing the default, make it conditional on
       # ‘system.stateVersion’ to maintain compatibility with existing
       # systems!
-      mkDefault (if versionAtLeast config.system.stateVersion "17.09" then pkgs.postgresql_9_6
+      mkDefault (if versionAtLeast config.system.stateVersion "20.03" then pkgs.postgresql_11
+            else if versionAtLeast config.system.stateVersion "17.09" then pkgs.postgresql_9_6
             else if versionAtLeast config.system.stateVersion "16.03" then pkgs.postgresql_9_5
             else throw "postgresql_9_4 was removed, please upgrade your postgresql version.");
 
diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix
index 9c389d80a6df..5695eeaf74cb 100644
--- a/nixos/modules/services/databases/redis.nix
+++ b/nixos/modules/services/databases/redis.nix
@@ -186,26 +186,21 @@ in
 
   config = mkIf config.services.redis.enable {
 
-    boot.kernel.sysctl = mkIf cfg.vmOverCommit {
-      "vm.overcommit_memory" = "1";
-    };
+    boot.kernel.sysctl = {
+      "vm.nr_hugepages" = "0";
+    } // mkIf cfg.vmOverCommit { "vm.overcommit_memory" = "1"; };
 
     networking.firewall = mkIf cfg.openFirewall {
       allowedTCPPorts = [ cfg.port ];
     };
 
-    users.users.redis.description = "Redis database user";
+    users.users.redis = {
+      description = "Redis database user";
+      isSystemUser = true;
+    };
 
     environment.systemPackages = [ cfg.package ];
 
-    systemd.services.disable-transparent-huge-pages = {
-      description = "Disable Transparent Huge Pages (required by Redis)";
-      before = [ "redis.service" ];
-      wantedBy = [ "redis.service" ];
-      script = "echo never > /sys/kernel/mm/transparent_hugepage/enabled";
-      serviceConfig.Type = "oneshot";
-    };
-
     systemd.services.redis =
       { description = "Redis Server";
 
diff --git a/nixos/modules/services/databases/rethinkdb.nix b/nixos/modules/services/databases/rethinkdb.nix
index 4828e594b328..f18fbaf5b062 100644
--- a/nixos/modules/services/databases/rethinkdb.nix
+++ b/nixos/modules/services/databases/rethinkdb.nix
@@ -99,6 +99,7 @@ in
     users.users.rethinkdb = mkIf (cfg.user == "rethinkdb")
       { name = "rethinkdb";
         description = "RethinkDB server user";
+        isSystemUser = true;
       };
 
     users.groups = optionalAttrs (cfg.group == "rethinkdb") (singleton
diff --git a/nixos/modules/services/desktops/geoclue2.nix b/nixos/modules/services/desktops/geoclue2.nix
index 6007dddf50c0..542b2ead4104 100644
--- a/nixos/modules/services/desktops/geoclue2.nix
+++ b/nixos/modules/services/desktops/geoclue2.nix
@@ -188,34 +188,41 @@ in
 
     systemd.packages = [ package ];
 
-    users.users.geoclue = {
-      isSystemUser = true;
-      home = "/var/lib/geoclue";
-      group = "geoclue";
-      description = "Geoinformation service";
-    };
-
-    users.groups.geoclue = {};
+    # we cannot use DynamicUser as we need the the geoclue user to exist for the dbus policy to work
+    users = {
+      users.geoclue = {
+        isSystemUser = true;
+        home = "/var/lib/geoclue";
+        group = "geoclue";
+        description = "Geoinformation service";
+      };
 
-    systemd.tmpfiles.rules = [
-      "d /var/lib/geoclue 0755 geoclue geoclue"
-    ];
+      groups.geoclue = {};
+    };
 
-    # restart geoclue service when the configuration changes
-    systemd.services.geoclue.restartTriggers = [
-      config.environment.etc."geoclue/geoclue.conf".source
-    ];
+    systemd.services.geoclue = {
+      # restart geoclue service when the configuration changes
+      restartTriggers = [
+        config.environment.etc."geoclue/geoclue.conf".source
+      ];
+      serviceConfig.StateDirectory = "geoclue";
+    };
 
     # this needs to run as a user service, since it's associated with the
     # user who is making the requests
     systemd.user.services = mkIf cfg.enableDemoAgent {
       geoclue-agent = {
         description = "Geoclue agent";
-        script = "${package}/libexec/geoclue-2.0/demos/agent";
         # this should really be `partOf = [ "geoclue.service" ]`, but
         # we can't be part of a system service, and the agent should
         # be okay with the main service coming and going
         wantedBy = [ "default.target" ];
+        serviceConfig = {
+          Type = "exec";
+          ExecStart = "${package}/libexec/geoclue-2.0/demos/agent";
+          Restart = "on-failure";
+          PrivateTmp = true;
+        };
       };
     };
 
@@ -256,4 +263,6 @@ in
         };
       } // mapAttrs' appConfigToINICompatible cfg.appConfig);
   };
+
+  meta.maintainers = with lib.maintainers; [ worldofpeace ];
 }
diff --git a/nixos/modules/services/desktops/gnome3/gnome-initial-setup.nix b/nixos/modules/services/desktops/gnome3/gnome-initial-setup.nix
new file mode 100644
index 000000000000..d715d52c2d06
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/gnome-initial-setup.nix
@@ -0,0 +1,86 @@
+# GNOME Initial Setup.
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+
+  # GNOME initial setup's run is conditioned on whether
+  # the gnome-initial-setup-done file exists in XDG_CONFIG_HOME
+  # Because of this, every existing user will have initial setup
+  # running because they never ran it before.
+  #
+  # To prevent this we create the file if the users stateVersion
+  # is older than 20.03 (the release we added this module).
+
+  script = pkgs.writeScript "create-gis-stamp-files" ''
+    #!${pkgs.runtimeShell}
+    setup_done=$HOME/.config/gnome-initial-setup-done
+
+    echo "Creating g-i-s stamp file $setup_done ..."
+    cat - > $setup_done <<- EOF
+    yes
+    EOF
+  '';
+
+  createGisStampFilesAutostart = pkgs.writeTextFile rec {
+    name = "create-g-i-s-stamp-files";
+    destination = "/etc/xdg/autostart/${name}.desktop";
+    text = ''
+      [Desktop Entry]
+      Type=Application
+      Name=Create GNOME Initial Setup stamp files
+      Exec=${script}
+      StartupNotify=false
+      NoDisplay=true
+      OnlyShowIn=GNOME;
+      AutostartCondition=unless-exists gnome-initial-setup-done
+      X-GNOME-Autostart-Phase=EarlyInitialization
+    '';
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnome3.gnome-initial-setup = {
+
+      enable = mkEnableOption "GNOME Initial Setup, a Simple, easy, and safe way to prepare a new system";
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnome3.gnome-initial-setup.enable {
+
+    environment.systemPackages = [
+      pkgs.gnome3.gnome-initial-setup
+    ]
+    ++ optional (versionOlder config.system.stateVersion "20.03") createGisStampFilesAutostart
+    ;
+
+    systemd.packages = [
+      pkgs.gnome3.gnome-initial-setup
+    ];
+
+    systemd.user.targets."gnome-session".wants = [
+      "gnome-initial-setup-copy-worker.service"
+      "gnome-initial-setup-first-login.service"
+      "gnome-welcome-tour.service"
+    ];
+
+    systemd.user.targets."gnome-session@gnome-initial-setup".wants = [
+      "gnome-initial-setup.service"
+    ];
+
+  };
+
+}
diff --git a/nixos/modules/services/desktops/gnome3/gnome-settings-daemon.nix b/nixos/modules/services/desktops/gnome3/gnome-settings-daemon.nix
index 7f7adcf26acf..2f83fd653bde 100644
--- a/nixos/modules/services/desktops/gnome3/gnome-settings-daemon.nix
+++ b/nixos/modules/services/desktops/gnome3/gnome-settings-daemon.nix
@@ -12,6 +12,12 @@ in
 
 {
 
+  imports = [
+    (mkRemovedOptionModule
+      ["services" "gnome3" "gnome-settings-daemon" "package"]
+      "")
+  ];
+
   ###### interface
 
   options = {
@@ -20,13 +26,6 @@ in
 
       enable = mkEnableOption "GNOME Settings Daemon";
 
-      # There are many forks of gnome-settings-daemon
-      package = mkOption {
-        type = types.package;
-        default = pkgs.gnome3.gnome-settings-daemon;
-        description = "Which gnome-settings-daemon package to use.";
-      };
-
     };
 
   };
@@ -36,9 +35,39 @@ in
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = [ cfg.package ];
-
-    services.udev.packages = [ cfg.package ];
+    environment.systemPackages = [
+      pkgs.gnome3.gnome-settings-daemon
+    ];
+
+    services.udev.packages = [
+      pkgs.gnome3.gnome-settings-daemon
+    ];
+
+    systemd.packages = [
+      pkgs.gnome3.gnome-settings-daemon
+    ];
+
+    systemd.user.targets."gnome-session-initialized".wants = [
+      "gsd-color.target"
+      "gsd-datetime.target"
+      "gsd-keyboard.target"
+      "gsd-media-keys.target"
+      "gsd-print-notifications.target"
+      "gsd-rfkill.target"
+      "gsd-screensaver-proxy.target"
+      "gsd-sharing.target"
+      "gsd-smartcard.target"
+      "gsd-sound.target"
+      "gsd-wacom.target"
+      "gsd-wwan.target"
+      "gsd-a11y-settings.target"
+      "gsd-housekeeping.target"
+      "gsd-power.target"
+    ];
+
+    systemd.user.targets."gnome-session-x11-services".wants = [
+      "gsd-xsettings.target"
+    ];
 
   };
 
diff --git a/nixos/modules/services/desktops/system-config-printer.nix b/nixos/modules/services/desktops/system-config-printer.nix
index 8a80be266b20..09c68c587b43 100644
--- a/nixos/modules/services/desktops/system-config-printer.nix
+++ b/nixos/modules/services/desktops/system-config-printer.nix
@@ -33,6 +33,9 @@ with lib;
       pkgs.system-config-printer
     ];
 
+    # for $out/bin/install-printer-driver
+    services.packagekit.enable = true;
+
   };
 
 }
diff --git a/nixos/modules/services/editors/emacs.nix b/nixos/modules/services/editors/emacs.nix
index ba7ec967919e..d791b387665f 100644
--- a/nixos/modules/services/editors/emacs.nix
+++ b/nixos/modules/services/editors/emacs.nix
@@ -95,13 +95,7 @@ in {
 
     environment.systemPackages = [ cfg.package editorScript desktopApplicationFile ];
 
-    environment.variables = {
-      # This is required so that GTK applications launched from Emacs
-      # get properly themed:
-      GTK_DATA_PREFIX = "${config.system.path}";
-    } // (if cfg.defaultEditor then {
-        EDITOR = mkOverride 900 "${editorScript}/bin/emacseditor";
-      } else {});
+    environment.variables.EDITOR = mkIf cfg.defaultEditor (mkOverride 900 "${editorScript}/bin/emacseditor");
   };
 
   meta.doc = ./emacs.xml;
diff --git a/nixos/modules/services/editors/infinoted.nix b/nixos/modules/services/editors/infinoted.nix
index 9cc8d421270e..be3667616942 100644
--- a/nixos/modules/services/editors/infinoted.nix
+++ b/nixos/modules/services/editors/infinoted.nix
@@ -115,6 +115,7 @@ in {
       { name = "infinoted";
         description = "Infinoted user";
         group = cfg.group;
+        isSystemUser = true;
       };
     users.groups = optional (cfg.group == "infinoted")
       { name = "infinoted";
diff --git a/nixos/modules/services/games/openarena.nix b/nixos/modules/services/games/openarena.nix
new file mode 100644
index 000000000000..b7d1aea6b8d2
--- /dev/null
+++ b/nixos/modules/services/games/openarena.nix
@@ -0,0 +1,56 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.openarena;
+in
+{
+  options = {
+    services.openarena = {
+      enable = mkEnableOption "OpenArena";
+
+      openPorts = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to open firewall ports for OpenArena";
+      };
+
+      extraFlags = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''Extra flags to pass to <command>oa_ded</command>'';
+        example = [
+          "+set dedicated 2"
+          "+set sv_hostname 'My NixOS OpenArena Server'"
+          # Load a map. Mandatory for clients to be able to connect.
+          "+map oa_dm1"
+        ];
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.firewall = mkIf cfg.openPorts {
+      allowedUDPPorts = [ 27960 ];
+    };
+
+    systemd.services.openarena = {
+      description = "OpenArena";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        DynamicUser = true;
+        StateDirectory = "openarena";
+        ExecStart = "${pkgs.openarena}/bin/openarena-server +set fs_basepath ${pkgs.openarena}/openarena-0.8.8 +set fs_homepath /var/lib/openarena ${concatStringsSep " " cfg.extraFlags}";
+        Restart = "on-failure";
+
+        # Hardening
+        CapabilityBoundingSet = "";
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/hardware/fancontrol.nix b/nixos/modules/services/hardware/fancontrol.nix
new file mode 100644
index 000000000000..bb4541a784da
--- /dev/null
+++ b/nixos/modules/services/hardware/fancontrol.nix
@@ -0,0 +1,45 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.fancontrol;
+  configFile = pkgs.writeText "fancontrol.conf" cfg.config;
+
+in{
+  options.hardware.fancontrol = {
+    enable = mkEnableOption "software fan control (requires fancontrol.config)";
+
+    config = mkOption {
+      default = null;
+      type = types.lines;
+      description = "Fancontrol configuration file content. See <citerefentry><refentrytitle>pwmconfig</refentrytitle><manvolnum>8</manvolnum></citerefentry> from the lm_sensors package.";
+      example = ''
+        # Configuration file generated by pwmconfig
+        INTERVAL=10
+        DEVPATH=hwmon3=devices/virtual/thermal/thermal_zone2 hwmon4=devices/platform/f71882fg.656
+        DEVNAME=hwmon3=soc_dts1 hwmon4=f71869a
+        FCTEMPS=hwmon4/device/pwm1=hwmon3/temp1_input
+        FCFANS= hwmon4/device/pwm1=hwmon4/device/fan1_input
+        MINTEMP=hwmon4/device/pwm1=35
+        MAXTEMP=hwmon4/device/pwm1=65
+        MINSTART=hwmon4/device/pwm1=150
+        MINSTOP=hwmon4/device/pwm1=0
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.fancontrol = {
+      unitConfig.Documentation = "man:fancontrol(8)";
+      description = "software fan control";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "lm_sensors.service" ];
+
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = "${pkgs.lm_sensors}/sbin/fancontrol ${configFile}";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/hardware/trezord.nix b/nixos/modules/services/hardware/trezord.nix
index 62824ed7350a..c517e9fbb2bd 100644
--- a/nixos/modules/services/hardware/trezord.nix
+++ b/nixos/modules/services/hardware/trezord.nix
@@ -44,20 +44,7 @@ in {
   ### implementation
 
   config = mkIf cfg.enable {
-    services.udev.packages = lib.singleton (pkgs.writeTextFile {
-      name = "trezord-udev-rules";
-      destination = "/etc/udev/rules.d/51-trezor.rules";
-      text = ''
-        # TREZOR v1 (One)
-        SUBSYSTEM=="usb", ATTR{idVendor}=="534c", ATTR{idProduct}=="0001", MODE="0660", GROUP="trezord", TAG+="uaccess", SYMLINK+="trezor%n"
-        KERNEL=="hidraw*", ATTRS{idVendor}=="534c", ATTRS{idProduct}=="0001", MODE="0660", GROUP="trezord", TAG+="uaccess"
-
-        # TREZOR v2 (T)
-        SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="53c0", MODE="0660", GROUP="trezord", TAG+="uaccess", SYMLINK+="trezor%n"
-        SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="53c1", MODE="0660", GROUP="trezord", TAG+="uaccess", SYMLINK+="trezor%n"
-        KERNEL=="hidraw*", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="53c1", MODE="0660", GROUP="trezord", TAG+="uaccess"
-      '';
-    });
+    services.udev.packages = [ pkgs.trezor-udev-rules ];
 
     systemd.services.trezord = {
       description = "TREZOR Bridge";
@@ -74,6 +61,7 @@ in {
     users.users.trezord = {
       group = "trezord";
       description = "Trezor bridge daemon user";
+      isSystemUser = true;
     };
 
     users.groups.trezord = {};
diff --git a/nixos/modules/services/hardware/udisks2.nix b/nixos/modules/services/hardware/udisks2.nix
index ed8703be921c..e898f3260585 100644
--- a/nixos/modules/services/hardware/udisks2.nix
+++ b/nixos/modules/services/hardware/udisks2.nix
@@ -34,10 +34,7 @@ with lib;
 
     services.dbus.packages = [ pkgs.udisks2 ];
 
-    system.activationScripts.udisks2 =
-      ''
-        mkdir -m 0755 -p /var/lib/udisks2
-      '';
+    systemd.tmpfiles.rules = [ "d /var/lib/udisks2 0755 root root -" ];
 
     services.udev.packages = [ pkgs.udisks2 ];
 
diff --git a/nixos/modules/services/hardware/usbmuxd.nix b/nixos/modules/services/hardware/usbmuxd.nix
index 93ced0b9f04d..39bbcaf4627c 100644
--- a/nixos/modules/services/hardware/usbmuxd.nix
+++ b/nixos/modules/services/hardware/usbmuxd.nix
@@ -47,6 +47,7 @@ in
       name = cfg.user;
       description = "usbmuxd user";
       group = cfg.group;
+      isSystemUser = true;
     };
 
     users.groups = optional (cfg.group == defaultUserGroup) {
diff --git a/nixos/modules/services/hardware/vdr.nix b/nixos/modules/services/hardware/vdr.nix
index 6e246f70f515..8a6cde51b06f 100644
--- a/nixos/modules/services/hardware/vdr.nix
+++ b/nixos/modules/services/hardware/vdr.nix
@@ -66,6 +66,7 @@ in {
     users.users.vdr = {
       group = "vdr";
       home = libDir;
+      isSystemUser = true;
     };
 
     users.groups.vdr = {};
diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix
index cdbb776454b6..3fd06812c675 100644
--- a/nixos/modules/services/mail/dovecot.nix
+++ b/nixos/modules/services/mail/dovecot.nix
@@ -181,7 +181,7 @@ in
     };
 
     configFile = mkOption {
-      type = types.nullOr types.str;
+      type = types.nullOr types.path;
       default = null;
       description = "Config file used for the whole dovecot configuration.";
       apply = v: if v != null then v else pkgs.writeText "dovecot.conf" dovecotConf;
diff --git a/nixos/modules/services/mail/mailcatcher.nix b/nixos/modules/services/mail/mailcatcher.nix
index fa8d41e918d3..f5b4508b335c 100644
--- a/nixos/modules/services/mail/mailcatcher.nix
+++ b/nixos/modules/services/mail/mailcatcher.nix
@@ -3,7 +3,7 @@
 let
   cfg = config.services.mailcatcher;
 
-  inherit (lib) mkEnableOption mkIf mkOption types;
+  inherit (lib) mkEnableOption mkIf mkOption types optionalString;
 in
 {
   # interface
@@ -54,6 +54,7 @@ in
         DynamicUser = true;
         Restart = "always";
         ExecStart = "${pkgs.mailcatcher}/bin/mailcatcher --foreground --no-quit --http-ip ${cfg.http.ip} --http-port ${toString cfg.http.port} --smtp-ip ${cfg.smtp.ip} --smtp-port ${toString cfg.smtp.port}";
+        AmbientCapabilities = optionalString (cfg.http.port < 1024 || cfg.smtp.port < 1024) "cap_net_bind_service";
       };
     };
   };
diff --git a/nixos/modules/services/mail/mailhog.nix b/nixos/modules/services/mail/mailhog.nix
index b78f4c8e0e66..0f998c6d0ea6 100644
--- a/nixos/modules/services/mail/mailhog.nix
+++ b/nixos/modules/services/mail/mailhog.nix
@@ -27,6 +27,7 @@ in {
     users.users.mailhog = {
       name = cfg.user;
       description = "MailHog service user";
+      isSystemUser = true;
     };
 
     systemd.services.mailhog = {
diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix
index 11dd5cb48db0..e917209f3d1f 100644
--- a/nixos/modules/services/mail/mailman.nix
+++ b/nixos/modules/services/mail/mailman.nix
@@ -6,16 +6,35 @@ let
 
   cfg = config.services.mailman;
 
-  pythonEnv = pkgs.python3.withPackages (ps: [ps.mailman]);
+  mailmanPyEnv = pkgs.python3.withPackages (ps: with ps; [mailman mailman-hyperkitty]);
 
   mailmanExe = with pkgs; stdenv.mkDerivation {
     name = "mailman-" + python3Packages.mailman.version;
+    buildInputs = [makeWrapper];
     unpackPhase = ":";
     installPhase = ''
       mkdir -p $out/bin
-      sed >"$out/bin/mailman" <"${pythonEnv}/bin/mailman" \
-        -e "2 iexport MAILMAN_CONFIG_FILE=/etc/mailman.cfg"
-      chmod +x $out/bin/mailman
+      makeWrapper ${mailmanPyEnv}/bin/mailman $out/bin/mailman \
+        --set MAILMAN_CONFIG_FILE /etc/mailman.cfg
+   '';
+  };
+
+  mailmanWeb = pkgs.python3Packages.mailman-web.override {
+    serverEMail = cfg.siteOwner;
+    archiverKey = cfg.hyperkittyApiKey;
+    allowedHosts = cfg.webHosts;
+  };
+
+  mailmanWebPyEnv = pkgs.python3.withPackages (x: with x; [mailman-web]);
+
+  mailmanWebExe = with pkgs; stdenv.mkDerivation {
+    inherit (mailmanWeb) name;
+    buildInputs = [makeWrapper];
+    unpackPhase = ":";
+    installPhase = ''
+      mkdir -p $out/bin
+      makeWrapper ${mailmanWebPyEnv}/bin/django-admin $out/bin/mailman-web \
+        --set DJANGO_SETTINGS_MODULE settings
     '';
   };
 
@@ -28,11 +47,30 @@ let
     bin_dir: ${pkgs.python3Packages.mailman}/bin
     var_dir: /var/lib/mailman
     queue_dir: $var_dir/queue
+    template_dir: $var_dir/templates
     log_dir: $var_dir/log
     lock_dir: $var_dir/lock
     etc_dir: /etc
     ext_dir: $etc_dir/mailman.d
     pid_file: /run/mailman/master.pid
+  '' + optionalString (cfg.hyperkittyApiKey != null) ''
+    [archiver.hyperkitty]
+    class: mailman_hyperkitty.Archiver
+    enable: yes
+    configuration: ${pkgs.writeText "mailman-hyperkitty.cfg" mailmanHyperkittyCfg}
+  '';
+
+  mailmanHyperkittyCfg = ''
+    [general]
+    # This is your HyperKitty installation, preferably on the localhost. This
+    # address will be used by Mailman to forward incoming emails to HyperKitty
+    # for archiving. It does not need to be publicly available, in fact it's
+    # better if it is not.
+    base_url: ${cfg.hyperkittyBaseUrl}
+
+    # Shared API key, must be the identical to the value in HyperKitty's
+    # settings.
+    api_key: ${cfg.hyperkittyApiKey}
   '';
 
 in {
@@ -51,7 +89,7 @@ in {
 
       siteOwner = mkOption {
         type = types.str;
-        default = "postmaster";
+        default = "postmaster@example.org";
         description = ''
           Certain messages that must be delivered to a human, but which can't
           be delivered to a list owner (e.g. a bounce from a list owner), will
@@ -59,6 +97,48 @@ in {
         '';
       };
 
+      webRoot = mkOption {
+        type = types.path;
+        default = "${mailmanWeb}/${pkgs.python3.sitePackages}";
+        defaultText = "pkgs.python3Packages.mailman-web";
+        description = ''
+          The web root for the Hyperkity + Postorius apps provided by Mailman.
+          This variable can be set, of course, but it mainly exists so that site
+          admins can refer to it in their own hand-written httpd configuration files.
+        '';
+      };
+
+      webHosts = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          The list of hostnames and/or IP addresses from which the Mailman Web
+          UI will accept requests. By default, "localhost" and "127.0.0.1" are
+          enabled. All additional names under which your web server accepts
+          requests for the UI must be listed here or incoming requests will be
+          rejected.
+        '';
+      };
+
+      hyperkittyBaseUrl = mkOption {
+        type = types.str;
+        default = "http://localhost/hyperkitty/";
+        description = ''
+          Where can Mailman connect to Hyperkitty's internal API, preferably on
+          localhost?
+        '';
+      };
+
+      hyperkittyApiKey = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          The shared secret used to authenticate Mailman's internal
+          communication with Hyperkitty. Must be set to enable support for the
+          Hyperkitty archiver. Note that this secret is going to be visible to
+          all local users in the Nix store.
+        '';
+      };
 
     };
   };
@@ -71,25 +151,22 @@ in {
       { assertion = cfg.enable -> config.services.postfix.enable;
         message = "Mailman requires Postfix";
       }
-      { assertion = config.services.postfix.recipientDelimiter == "+";
-        message = "Postfix's recipientDelimiter must be set to '+'.";
-      }
     ];
 
     users.users.mailman = { description = "GNU Mailman"; isSystemUser = true; };
 
     environment = {
-      systemPackages = [ mailmanExe ];
+      systemPackages = [ mailmanExe mailmanWebExe pkgs.sassc ];
       etc."mailman.cfg".text = mailmanCfg;
     };
 
     services.postfix = {
       relayDomains = [ "hash:/var/lib/mailman/data/postfix_domains" ];
+      recipientDelimiter = "+";         # bake recipient addresses in mail envelopes via VERP
       config = {
         transport_maps = [ "hash:/var/lib/mailman/data/postfix_lmtp" ];
         local_recipient_maps = [ "hash:/var/lib/mailman/data/postfix_lmtp" ];
-        # Mailman uses recipient delimiters, so we don't need special handling.
-        owner_request_special = "no";
+        owner_request_special = "no";   # Mailman handles -owner addresses on its own
       };
     };
 
@@ -109,6 +186,112 @@ in {
       };
     };
 
+    systemd.services.mailman-web = {
+      description = "Init Postorius DB";
+      before = [ "httpd.service" ];
+      requiredBy = [ "httpd.service" ];
+      script = ''
+        ${mailmanWebExe}/bin/mailman-web migrate
+        rm -rf static
+        ${mailmanWebExe}/bin/mailman-web collectstatic
+        ${mailmanWebExe}/bin/mailman-web compress
+      '';
+      serviceConfig = {
+        User = config.services.httpd.user;
+        Type = "oneshot";
+        StateDirectory = "mailman-web";
+        StateDirectoryMode = "0700";
+        WorkingDirectory = "/var/lib/mailman-web";
+      };
+    };
+
+    systemd.services.mailman-daily = {
+      description = "Trigger daily Mailman events";
+      startAt = "daily";
+      serviceConfig = {
+        ExecStart = "${mailmanExe}/bin/mailman digests --send";
+        User = "mailman";
+      };
+    };
+
+    systemd.services.hyperkitty = {
+      enable = cfg.hyperkittyApiKey != null;
+      description = "GNU Hyperkitty QCluster Process";
+      after = [ "network.target" ];
+      wantedBy = [ "mailman.service" "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = "${mailmanWebExe}/bin/mailman-web qcluster";
+        User = config.services.httpd.user;
+        WorkingDirectory = "/var/lib/mailman-web";
+      };
+    };
+
+    systemd.services.hyperkitty-minutely = {
+      enable = cfg.hyperkittyApiKey != null;
+      description = "Trigger minutely Hyperkitty events";
+      startAt = "minutely";
+      serviceConfig = {
+        ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs minutely";
+        User = config.services.httpd.user;
+        WorkingDirectory = "/var/lib/mailman-web";
+      };
+    };
+
+    systemd.services.hyperkitty-quarter-hourly = {
+      enable = cfg.hyperkittyApiKey != null;
+      description = "Trigger quarter-hourly Hyperkitty events";
+      startAt = "*:00/15";
+      serviceConfig = {
+        ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs quarter_hourly";
+        User = config.services.httpd.user;
+        WorkingDirectory = "/var/lib/mailman-web";
+      };
+    };
+
+    systemd.services.hyperkitty-hourly = {
+      enable = cfg.hyperkittyApiKey != null;
+      description = "Trigger hourly Hyperkitty events";
+      startAt = "hourly";
+      serviceConfig = {
+        ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs hourly";
+        User = config.services.httpd.user;
+        WorkingDirectory = "/var/lib/mailman-web";
+      };
+    };
+
+    systemd.services.hyperkitty-daily = {
+      enable = cfg.hyperkittyApiKey != null;
+      description = "Trigger daily Hyperkitty events";
+      startAt = "daily";
+      serviceConfig = {
+        ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs daily";
+        User = config.services.httpd.user;
+        WorkingDirectory = "/var/lib/mailman-web";
+      };
+    };
+
+    systemd.services.hyperkitty-weekly = {
+      enable = cfg.hyperkittyApiKey != null;
+      description = "Trigger weekly Hyperkitty events";
+      startAt = "weekly";
+      serviceConfig = {
+        ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs weekly";
+        User = config.services.httpd.user;
+        WorkingDirectory = "/var/lib/mailman-web";
+      };
+    };
+
+    systemd.services.hyperkitty-yearly = {
+      enable = cfg.hyperkittyApiKey != null;
+      description = "Trigger yearly Hyperkitty events";
+      startAt = "yearly";
+      serviceConfig = {
+        ExecStart = "${mailmanWebExe}/bin/mailman-web runjobs yearly";
+        User = config.services.httpd.user;
+        WorkingDirectory = "/var/lib/mailman-web";
+      };
+    };
+
   };
 
 }
diff --git a/nixos/modules/services/mail/rspamd.nix b/nixos/modules/services/mail/rspamd.nix
index 89aa9d17ff7f..4db35d9e89ab 100644
--- a/nixos/modules/services/mail/rspamd.nix
+++ b/nixos/modules/services/mail/rspamd.nix
@@ -68,7 +68,7 @@ let
           replaced with <literal>rspamd_proxy</literal>.
         '';
         apply = let
-            from = "services.rspamd.workers.\”${name}\".type";
+            from = "services.rspamd.workers.\"${name}\".type";
             files = options.type.files;
             warning = "The option `${from}` defined in ${showFiles files} has enum value `proxy` which has been renamed to `rspamd_proxy`";
           in x: if x == "proxy" then traceWarning warning "rspamd_proxy" else x;
diff --git a/nixos/modules/services/misc/airsonic.nix b/nixos/modules/services/misc/airsonic.nix
index 919d3b2f6e64..c296e048cea4 100644
--- a/nixos/modules/services/misc/airsonic.nix
+++ b/nixos/modules/services/misc/airsonic.nix
@@ -148,6 +148,7 @@ in {
       name = cfg.user;
       home = cfg.home;
       createHome = true;
+      isSystemUser = true;
     };
   };
 }
diff --git a/nixos/modules/services/misc/docker-registry.nix b/nixos/modules/services/misc/docker-registry.nix
index c87607d2666a..89bac4f47d73 100644
--- a/nixos/modules/services/misc/docker-registry.nix
+++ b/nixos/modules/services/misc/docker-registry.nix
@@ -145,11 +145,13 @@ in {
     };
 
     users.users.docker-registry =
-      if cfg.storagePath != null
+      (if cfg.storagePath != null
       then {
         createHome = true;
         home = cfg.storagePath;
       }
-      else {};
+      else {}) // {
+        isSystemUser = true;
+      };
   };
 }
diff --git a/nixos/modules/services/misc/errbot.nix b/nixos/modules/services/misc/errbot.nix
index 256adce2f02e..b447ba5d438d 100644
--- a/nixos/modules/services/misc/errbot.nix
+++ b/nixos/modules/services/misc/errbot.nix
@@ -76,7 +76,10 @@ in {
   };
 
   config = mkIf (cfg.instances != {}) {
-    users.users.errbot.group = "errbot";
+    users.users.errbot = {
+      group = "errbot";
+      isSystemUser = true;
+    };
     users.groups.errbot = {};
 
     systemd.services = mapAttrs' (name: instanceCfg: nameValuePair "errbot-${name}" (
diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix
index 59c1c104b9b9..c8c59fb256e8 100644
--- a/nixos/modules/services/misc/gitea.nix
+++ b/nixos/modules/services/misc/gitea.nix
@@ -322,6 +322,7 @@ in
       "d '${cfg.stateDir}/conf' - ${cfg.user} gitea - -"
       "d '${cfg.stateDir}/custom' - ${cfg.user} gitea - -"
       "d '${cfg.stateDir}/custom/conf' - ${cfg.user} gitea - -"
+      "d '${cfg.stateDir}/log' - ${cfg.user} gitea - -"
       "d '${cfg.repositoryRoot}' - ${cfg.user} gitea - -"
       "Z '${cfg.stateDir}' - ${cfg.user} gitea - -"
 
@@ -408,6 +409,7 @@ in
         home = cfg.stateDir;
         useDefaultShell = true;
         group = "gitea";
+        isSystemUser = true;
       };
     };
 
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index caef4ad4ea80..07ea9c458437 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, utils, ... }:
 
-# TODO: support non-postgresql
-
 with lib;
 
 let
@@ -9,6 +7,11 @@ let
 
   ruby = cfg.packages.gitlab.ruby;
 
+  postgresqlPackage = if config.services.postgresql.enable then
+                        config.services.postgresql.package
+                      else
+                        pkgs.postgresql;
+
   gitlabSocket = "${cfg.statePath}/tmp/sockets/gitlab.socket";
   gitalySocket = "${cfg.statePath}/tmp/sockets/gitaly.socket";
   pathUrlQuote = url: replaceStrings ["/"] ["%2F"] url;
@@ -24,6 +27,9 @@ let
     } // cfg.extraDatabaseConfig;
   };
 
+  # We only want to create a database if we're actually going to connect to it.
+  databaseActuallyCreateLocally = cfg.databaseCreateLocally && cfg.databaseHost == "";
+
   gitalyToml = pkgs.writeText "gitaly.toml" ''
     socket_path = "${lib.escape ["\""] gitalySocket}"
     bin_dir = "${cfg.packages.gitaly}/bin"
@@ -140,7 +146,7 @@ let
       mkdir -p $out/bin
       makeWrapper ${cfg.packages.gitlab.rubyEnv}/bin/rake $out/bin/gitlab-rake \
           ${concatStrings (mapAttrsToList (name: value: "--set ${name} '${value}' ") gitlabEnv)} \
-          --set PATH '${lib.makeBinPath [ pkgs.nodejs pkgs.gzip pkgs.git pkgs.gnutar config.services.postgresql.package pkgs.coreutils pkgs.procps ]}:$PATH' \
+          --set PATH '${lib.makeBinPath [ pkgs.nodejs pkgs.gzip pkgs.git pkgs.gnutar postgresqlPackage pkgs.coreutils pkgs.procps ]}:$PATH' \
           --set RAKEOPT '-f ${cfg.packages.gitlab}/share/gitlab/Rakefile' \
           --run 'cd ${cfg.packages.gitlab}/share/gitlab'
      '';
@@ -155,7 +161,7 @@ let
       mkdir -p $out/bin
       makeWrapper ${cfg.packages.gitlab.rubyEnv}/bin/rails $out/bin/gitlab-rails \
           ${concatStrings (mapAttrsToList (name: value: "--set ${name} '${value}' ") gitlabEnv)} \
-          --set PATH '${lib.makeBinPath [ pkgs.nodejs pkgs.gzip pkgs.git pkgs.gnutar config.services.postgresql.package pkgs.coreutils pkgs.procps ]}:$PATH' \
+          --set PATH '${lib.makeBinPath [ pkgs.nodejs pkgs.gzip pkgs.git pkgs.gnutar postgresqlPackage pkgs.coreutils pkgs.procps ]}:$PATH' \
           --run 'cd ${cfg.packages.gitlab}/share/gitlab'
      '';
   };
@@ -225,7 +231,15 @@ in {
       statePath = mkOption {
         type = types.str;
         default = "/var/gitlab/state";
-        description = "Gitlab state directory, logs are stored here.";
+        description = ''
+          Gitlab state directory. Configuration, repositories and
+          logs, among other things, are stored here.
+
+          The directory will be created automatically if it doesn't
+          exist already. Its parent directories must be owned by
+          either <literal>root</literal> or the user set in
+          <option>services.gitlab.user</option>.
+        '';
       };
 
       backupPath = mkOption {
@@ -260,8 +274,8 @@ in {
         description = ''
           Whether a database should be automatically created on the
           local host. Set this to <literal>false</literal> if you plan
-          on provisioning a local database yourself or use an external
-          one.
+          on provisioning a local database yourself. This has no effect
+          if <option>services.gitlab.databaseHost</option> is customized.
         '';
       };
 
@@ -551,8 +565,8 @@ in {
 
     assertions = [
       {
-        assertion = cfg.databaseCreateLocally -> (cfg.user == cfg.databaseUsername);
-        message = "For local automatic database provisioning services.gitlab.user and services.gitlab.databaseUsername should be identical.";
+        assertion = databaseActuallyCreateLocally -> (cfg.user == cfg.databaseUsername);
+        message = ''For local automatic database provisioning (services.gitlab.databaseCreateLocally == true) with peer authentication (services.gitlab.databaseHost == "") to work services.gitlab.user and services.gitlab.databaseUsername must be identical.'';
       }
       {
         assertion = (cfg.databaseHost != "") -> (cfg.databasePasswordFile != null);
@@ -586,14 +600,16 @@ in {
     services.redis.enable = mkDefault true;
 
     # We use postgres as the main data store.
-    services.postgresql = optionalAttrs cfg.databaseCreateLocally {
+    services.postgresql = optionalAttrs databaseActuallyCreateLocally {
       enable = true;
       ensureUsers = singleton { name = cfg.databaseUsername; };
     };
     # The postgresql module doesn't currently support concepts like
     # objects owners and extensions; for now we tack on what's needed
     # here.
-    systemd.services.postgresql.postStart = mkAfter (optionalString cfg.databaseCreateLocally ''
+    systemd.services.postgresql.postStart = mkAfter (optionalString databaseActuallyCreateLocally ''
+      set -eu
+
       $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${cfg.databaseName}'" | grep -q 1 || $PSQL -tAc 'CREATE DATABASE "${cfg.databaseName}" OWNER "${cfg.databaseUsername}"'
       current_owner=$($PSQL -tAc "SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_catalog.pg_database WHERE datname = '${cfg.databaseName}'")
       if [[ "$current_owner" != "${cfg.databaseUsername}" ]]; then
@@ -635,7 +651,7 @@ in {
       "d ${cfg.statePath} 0750 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/builds 0750 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/config 0750 ${cfg.user} ${cfg.group} -"
-      "D ${cfg.statePath}/config/initializers 0750 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/config/initializers 0750 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/db 0750 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/log 0750 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/repositories 2770 ${cfg.user} ${cfg.group} -"
@@ -652,7 +668,6 @@ in {
       "d ${gitlabConfig.production.shared.path}/artifacts 0750 ${cfg.user} ${cfg.group} -"
       "d ${gitlabConfig.production.shared.path}/lfs-objects 0750 ${cfg.user} ${cfg.group} -"
       "d ${gitlabConfig.production.shared.path}/pages 0750 ${cfg.user} ${cfg.group} -"
-      "L+ ${cfg.statePath}/lib - - - - ${cfg.packages.gitlab}/share/gitlab/lib"
       "L+ /run/gitlab/config - - - - ${cfg.statePath}/config"
       "L+ /run/gitlab/log - - - - ${cfg.statePath}/log"
       "L+ /run/gitlab/tmp - - - - ${cfg.statePath}/tmp"
@@ -669,12 +684,16 @@ in {
       wantedBy = [ "multi-user.target" ];
       environment = gitlabEnv;
       path = with pkgs; [
-        config.services.postgresql.package
+        postgresqlPackage
         gitAndTools.git
         ruby
         openssh
         nodejs
         gnupg
+
+        # Needed for GitLab project imports
+        gnutar
+        gzip
       ];
       serviceConfig = {
         Type = "simple";
@@ -722,7 +741,6 @@ in {
         gitlab-workhorse
       ];
       serviceConfig = {
-        PermissionsStartOnly = true; # preStart must be run as root
         Type = "simple";
         User = cfg.user;
         Group = cfg.group;
@@ -746,89 +764,109 @@ in {
       wantedBy = [ "multi-user.target" ];
       environment = gitlabEnv;
       path = with pkgs; [
-        config.services.postgresql.package
+        postgresqlPackage
         gitAndTools.git
         openssh
         nodejs
         procps
         gnupg
       ];
-      preStart = ''
-        cp -f ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION
-        rm -rf ${cfg.statePath}/db/*
-        cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
-        cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db
-
-        ${cfg.packages.gitlab-shell}/bin/install
-
-        ${optionalString cfg.smtp.enable ''
-          install -m u=rw ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb
-          ${optionalString (cfg.smtp.passwordFile != null) ''
-            smtp_password=$(<'${cfg.smtp.passwordFile}')
-            ${pkgs.replace}/bin/replace-literal -e '@smtpPassword@' "$smtp_password" '${cfg.statePath}/config/initializers/smtp_settings.rb'
-          ''}
-        ''}
 
-        (
-          umask u=rwx,g=,o=
+      serviceConfig = {
+        Type = "simple";
+        User = cfg.user;
+        Group = cfg.group;
+        TimeoutSec = "infinity";
+        Restart = "on-failure";
+        WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
+        ExecStartPre = let
+          preStartFullPrivileges = ''
+            shopt -s dotglob nullglob
+            set -eu
 
-          ${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/gitlab_shell_secret
+            chown --no-dereference '${cfg.user}':'${cfg.group}' '${cfg.statePath}'/*
+            chown --no-dereference '${cfg.user}':'${cfg.group}' '${cfg.statePath}'/config/*
+          '';
+          preStart = ''
+            set -eu
 
-          ${if cfg.databasePasswordFile != null then ''
-              export db_password="$(<'${cfg.databasePasswordFile}')"
+            cp -f ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION
+            rm -rf ${cfg.statePath}/db/*
+            rm -rf ${cfg.statePath}/config/initializers/*
+            rm -f ${cfg.statePath}/lib
+            cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
+            cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db
 
-              if [[ -z "$db_password" ]]; then
-                >&2 echo "Database password was an empty string!"
-                exit 1
-              fi
+            ${cfg.packages.gitlab-shell}/bin/install
 
-              ${pkgs.jq}/bin/jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \
-                                '.production.password = $ENV.db_password' \
-                                >'${cfg.statePath}/config/database.yml'
-            ''
-            else ''
-              ${pkgs.jq}/bin/jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \
-                                >'${cfg.statePath}/config/database.yml'
-            ''
-          }
-
-          ${utils.genJqSecretsReplacementSnippet
-              gitlabConfig
-              "${cfg.statePath}/config/gitlab.yml"
-          }
-
-          if [[ -h '${cfg.statePath}/config/secrets.yml' ]]; then
-            rm '${cfg.statePath}/config/secrets.yml'
-          fi
+            ${optionalString cfg.smtp.enable ''
+              install -m u=rw ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb
+              ${optionalString (cfg.smtp.passwordFile != null) ''
+                smtp_password=$(<'${cfg.smtp.passwordFile}')
+                ${pkgs.replace}/bin/replace-literal -e '@smtpPassword@' "$smtp_password" '${cfg.statePath}/config/initializers/smtp_settings.rb'
+              ''}
+            ''}
 
-          export secret="$(<'${cfg.secrets.secretFile}')"
-          export db="$(<'${cfg.secrets.dbFile}')"
-          export otp="$(<'${cfg.secrets.otpFile}')"
-          export jws="$(<'${cfg.secrets.jwsFile}')"
-          ${pkgs.jq}/bin/jq -n '{production: {secret_key_base: $ENV.secret,
-                                              otp_key_base: $ENV.db,
-                                              db_key_base: $ENV.otp,
-                                              openid_connect_signing_key: $ENV.jws}}' \
-                            > '${cfg.statePath}/config/secrets.yml'
-        )
+            (
+              umask u=rwx,g=,o=
 
-        initial_root_password="$(<'${cfg.initialRootPasswordFile}')"
-        ${gitlab-rake}/bin/gitlab-rake gitlab:db:configure GITLAB_ROOT_PASSWORD="$initial_root_password" \
-                                                           GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}'
+              ${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/gitlab_shell_secret
 
-        # We remove potentially broken links to old gitlab-shell versions
-        rm -Rf ${cfg.statePath}/repositories/**/*.git/hooks
+              if [[ -h '${cfg.statePath}/config/database.yml' ]]; then
+                rm '${cfg.statePath}/config/database.yml'
+              fi
 
-        ${pkgs.git}/bin/git config --global core.autocrlf "input"
-      '';
+              ${if cfg.databasePasswordFile != null then ''
+                  export db_password="$(<'${cfg.databasePasswordFile}')"
+
+                  if [[ -z "$db_password" ]]; then
+                    >&2 echo "Database password was an empty string!"
+                    exit 1
+                  fi
+
+                  ${pkgs.jq}/bin/jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \
+                                    '.production.password = $ENV.db_password' \
+                                    >'${cfg.statePath}/config/database.yml'
+                ''
+                else ''
+                  ${pkgs.jq}/bin/jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \
+                                    >'${cfg.statePath}/config/database.yml'
+                ''
+              }
+
+              ${utils.genJqSecretsReplacementSnippet
+                  gitlabConfig
+                  "${cfg.statePath}/config/gitlab.yml"
+              }
+
+              if [[ -h '${cfg.statePath}/config/secrets.yml' ]]; then
+                rm '${cfg.statePath}/config/secrets.yml'
+              fi
 
-      serviceConfig = {
-        Type = "simple";
-        User = cfg.user;
-        Group = cfg.group;
-        TimeoutSec = "infinity";
-        Restart = "on-failure";
-        WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
+              export secret="$(<'${cfg.secrets.secretFile}')"
+              export db="$(<'${cfg.secrets.dbFile}')"
+              export otp="$(<'${cfg.secrets.otpFile}')"
+              export jws="$(<'${cfg.secrets.jwsFile}')"
+              ${pkgs.jq}/bin/jq -n '{production: {secret_key_base: $ENV.secret,
+                                                  otp_key_base: $ENV.otp,
+                                                  db_key_base: $ENV.db,
+                                                  openid_connect_signing_key: $ENV.jws}}' \
+                                > '${cfg.statePath}/config/secrets.yml'
+            )
+
+            initial_root_password="$(<'${cfg.initialRootPasswordFile}')"
+            ${gitlab-rake}/bin/gitlab-rake gitlab:db:configure GITLAB_ROOT_PASSWORD="$initial_root_password" \
+                                                               GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}' > /dev/null
+
+            # We remove potentially broken links to old gitlab-shell versions
+            rm -Rf ${cfg.statePath}/repositories/**/*.git/hooks
+
+            ${pkgs.git}/bin/git config --global core.autocrlf "input"
+          '';
+        in [
+          "+${pkgs.writeShellScript "gitlab-pre-start-full-privileges" preStartFullPrivileges}"
+          "${pkgs.writeShellScript "gitlab-pre-start" preStart}"
+        ];
         ExecStart = "${cfg.packages.gitlab.rubyEnv}/bin/unicorn -c ${cfg.statePath}/config/unicorn.rb -E production";
       };
 
diff --git a/nixos/modules/services/misc/gollum.nix b/nixos/modules/services/misc/gollum.nix
index 7653b415bf09..f4a9c72b1545 100644
--- a/nixos/modules/services/misc/gollum.nix
+++ b/nixos/modules/services/misc/gollum.nix
@@ -71,6 +71,7 @@ in
       group = config.users.users.gollum.name;
       description = "Gollum user";
       createHome = false;
+      isSystemUser = true;
     };
 
     users.groups.gollum = { };
diff --git a/nixos/modules/services/misc/home-assistant.nix b/nixos/modules/services/misc/home-assistant.nix
index f1b351246740..74702c97f551 100644
--- a/nixos/modules/services/misc/home-assistant.nix
+++ b/nixos/modules/services/misc/home-assistant.nix
@@ -224,6 +224,7 @@ in {
         KillSignal = "SIGINT";
         PrivateTmp = true;
         RemoveIPC = true;
+        AmbientCapabilities = "cap_net_raw,cap_net_admin+eip";
       };
       path = [
         "/run/wrappers" # needed for ping
diff --git a/nixos/modules/services/misc/jellyfin.nix b/nixos/modules/services/misc/jellyfin.nix
index 55559206568d..6ecdfb57dc35 100644
--- a/nixos/modules/services/misc/jellyfin.nix
+++ b/nixos/modules/services/misc/jellyfin.nix
@@ -41,7 +41,10 @@ in
     };
 
     users.users = mkIf (cfg.user == "jellyfin") {
-      jellyfin.group = cfg.group;
+      jellyfin = {
+        group = cfg.group;
+        isSystemUser = true;
+      };
     };
 
     users.groups = mkIf (cfg.group == "jellyfin") {
diff --git a/nixos/modules/services/misc/lidarr.nix b/nixos/modules/services/misc/lidarr.nix
index 40755c162171..8ff1adadcf23 100644
--- a/nixos/modules/services/misc/lidarr.nix
+++ b/nixos/modules/services/misc/lidarr.nix
@@ -10,6 +10,12 @@ in
     services.lidarr = {
       enable = mkEnableOption "Lidarr";
 
+      dataDir = mkOption {
+        type = types.str;
+        default = "/var/lib/lidarr/.config/Lidarr";
+        description = "The directory where Lidarr stores its data files.";
+      };
+
       package = mkOption {
         type = types.package;
         default = pkgs.lidarr;
@@ -44,6 +50,10 @@ in
   };
 
   config = mkIf cfg.enable {
+    systemd.tmpfiles.rules = [
+      "d '${cfg.dataDir}' 0700 ${cfg.user} ${cfg.group} - -"
+    ];
+
     systemd.services.lidarr = {
       description = "Lidarr";
       after = [ "network.target" ];
@@ -53,11 +63,8 @@ in
         Type = "simple";
         User = cfg.user;
         Group = cfg.group;
-        ExecStart = "${cfg.package}/bin/Lidarr";
+        ExecStart = "${cfg.package}/bin/Lidarr -nobrowser -data='${cfg.dataDir}'";
         Restart = "on-failure";
-
-        StateDirectory = "lidarr";
-        StateDirectoryMode = "0770";
       };
     };
 
diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix
index 018fac386163..0f4eb2ccfcad 100644
--- a/nixos/modules/services/misc/matrix-synapse.nix
+++ b/nixos/modules/services/misc/matrix-synapse.nix
@@ -79,7 +79,11 @@ turn_user_lifetime: "${cfg.turn_user_lifetime}"
 user_creation_max_duration: ${cfg.user_creation_max_duration}
 bcrypt_rounds: ${cfg.bcrypt_rounds}
 allow_guest_access: ${boolToString cfg.allow_guest_access}
-trusted_third_party_id_servers: ${builtins.toJSON cfg.trusted_third_party_id_servers}
+
+account_threepid_delegates:
+  ${optionalString (cfg.account_threepid_delegates.email != null) "email: ${cfg.account_threepid_delegates.email}"}
+  ${optionalString (cfg.account_threepid_delegates.msisdn != null) "msisdn: ${cfg.account_threepid_delegates.msisdn}"}
+
 room_invite_state_types: ${builtins.toJSON cfg.room_invite_state_types}
 ${optionalString (cfg.macaroon_secret_key != null) ''
   macaroon_secret_key: "${cfg.macaroon_secret_key}"
@@ -102,6 +106,7 @@ perspectives:
     '') cfg.servers)}
     }
   }
+redaction_retention_period: ${toString cfg.redaction_retention_period}
 app_service_config_files: ${builtins.toJSON cfg.app_service_config_files}
 
 ${cfg.extraConfig}
@@ -552,14 +557,18 @@ in {
           accessible to anonymous users.
         '';
       };
-      trusted_third_party_id_servers = mkOption {
-        type = types.listOf types.str;
-        default = [
-          "matrix.org"
-          "vector.im"
-        ];
+      account_threepid_delegates.email = mkOption {
+        type = types.nullOr types.str;
+        default = null;
         description = ''
-          The list of identity servers trusted to verify third party identifiers by this server.
+          Delegate email sending to https://example.org
+        '';
+      };
+      account_threepid_delegates.msisdn = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Delegate SMS sending to this local process (https://localhost:8090)
         '';
       };
       room_invite_state_types = mkOption {
@@ -600,6 +609,13 @@ in {
           A list of application service config file to use
         '';
       };
+      redaction_retention_period = mkOption {
+        type = types.int;
+        default = 7;
+        description = ''
+          How long to keep redacted events in unredacted form in the database.
+        '';
+      };
       extraConfig = mkOption {
         type = types.lines;
         default = "";
@@ -699,4 +715,12 @@ in {
       };
     };
   };
+
+  imports = [
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "trusted_third_party_id_servers" ] ''
+      The `trusted_third_party_id_servers` option as been removed in `matrix-synapse` v1.4.0
+      as the behavior is now obsolete.
+    '')
+  ];
+
 }
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index 3826f728afd0..dcec4d4fc6cd 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -10,7 +10,7 @@ let
 
   nixVersion = getVersion nix;
 
-  isNix20 = versionAtLeast nixVersion "2.0pre";
+  isNix23 = versionAtLeast nixVersion "2.3pre";
 
   makeNixBuildUser = nr:
     { name = "nixbld${toString nr}";
@@ -27,43 +27,30 @@ let
   nixbldUsers = map makeNixBuildUser (range 1 cfg.nrBuildUsers);
 
   nixConf =
-    let
-      # In Nix < 2.0, If we're using sandbox for builds, then provide
-      # /bin/sh in the sandbox as a bind-mount to bash. This means we
-      # also need to include the entire closure of bash. Nix >= 2.0
-      # provides a /bin/sh by default.
-      sh = pkgs.runtimeShell;
-      binshDeps = pkgs.writeReferencesToFile sh;
-    in
-      pkgs.runCommand "nix.conf" { preferLocalBuild = true; extraOptions = cfg.extraOptions; } (''
-        ${optionalString (!isNix20) ''
-          extraPaths=$(for i in $(cat ${binshDeps}); do if test -d $i; then echo $i; fi; done)
-        ''}
+    assert versionAtLeast nixVersion "2.2";
+    pkgs.runCommand "nix.conf" { preferLocalBuild = true; extraOptions = cfg.extraOptions; } (
+      ''
         cat > $out <<END
         # WARNING: this file is generated from the nix.* options in
         # your NixOS configuration, typically
         # /etc/nixos/configuration.nix.  Do not edit it!
         build-users-group = nixbld
-        ${if isNix20 then "max-jobs" else "build-max-jobs"} = ${toString (cfg.maxJobs)}
-        ${if isNix20 then "cores" else "build-cores"} = ${toString (cfg.buildCores)}
-        ${if isNix20 then "sandbox" else "build-use-sandbox"} = ${if (builtins.isBool cfg.useSandbox) then boolToString cfg.useSandbox else cfg.useSandbox}
-        ${if isNix20 then "extra-sandbox-paths" else "build-sandbox-paths"} = ${toString cfg.sandboxPaths} ${optionalString (!isNix20) "/bin/sh=${sh} $(echo $extraPaths)"}
-        ${if isNix20 then "substituters" else "binary-caches"} = ${toString cfg.binaryCaches}
-        ${if isNix20 then "trusted-substituters" else "trusted-binary-caches"} = ${toString cfg.trustedBinaryCaches}
-        ${if isNix20 then "trusted-public-keys" else "binary-cache-public-keys"} = ${toString cfg.binaryCachePublicKeys}
+        max-jobs = ${toString (cfg.maxJobs)}
+        cores = ${toString (cfg.buildCores)}
+        sandbox = ${if (builtins.isBool cfg.useSandbox) then boolToString cfg.useSandbox else cfg.useSandbox}
+        extra-sandbox-paths = ${toString cfg.sandboxPaths}
+        substituters = ${toString cfg.binaryCaches}
+        trusted-substituters = ${toString cfg.trustedBinaryCaches}
+        trusted-public-keys = ${toString cfg.binaryCachePublicKeys}
         auto-optimise-store = ${boolToString cfg.autoOptimiseStore}
-        ${if isNix20 then ''
-          require-sigs = ${if cfg.requireSignedBinaryCaches then "true" else "false"}
-        '' else ''
-          signed-binary-caches = ${if cfg.requireSignedBinaryCaches then "*" else ""}
-        ''}
+        require-sigs = ${if cfg.requireSignedBinaryCaches then "true" else "false"}
         trusted-users = ${toString cfg.trustedUsers}
         allowed-users = ${toString cfg.allowedUsers}
-        ${optionalString (isNix20 && !cfg.distributedBuilds) ''
+        ${optionalString (!cfg.distributedBuilds) ''
           builders =
         ''}
         system-features = ${toString cfg.systemFeatures}
-        ${optionalString (versionAtLeast nixVersion "2.3pre") ''
+        ${optionalString isNix23 ''
           sandbox-fallback = false
         ''}
         $extraOptions
@@ -74,7 +61,7 @@ let
             '' else ''
               echo "Checking that Nix can read nix.conf..."
               ln -s $out ./nix.conf
-              NIX_CONF_DIR=$PWD ${cfg.package}/bin/nix show-config >/dev/null
+              NIX_CONF_DIR=$PWD ${cfg.package}/bin/nix show-config ${optionalString isNix23 "--no-net --option experimental-features nix-command"} >/dev/null
             '')
       );
 
@@ -165,8 +152,8 @@ in
         type = types.lines;
         default = "";
         example = ''
-          gc-keep-outputs = true
-          gc-keep-derivations = true
+          keep-outputs = true
+          keep-derivations = true
         '';
         description = "Additional text appended to <filename>nix.conf</filename>.";
       };
@@ -421,8 +408,7 @@ in
 
     systemd.services.nix-daemon =
       { path = [ nix pkgs.utillinux config.programs.ssh.package ]
-          ++ optionals cfg.distributedBuilds [ pkgs.gzip ]
-          ++ optionals (!isNix20) [ pkgs.openssl.bin ];
+          ++ optionals cfg.distributedBuilds [ pkgs.gzip ];
 
         environment = cfg.envVars
           // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"; }
@@ -439,34 +425,13 @@ in
         restartTriggers = [ nixConf ];
       };
 
-    nix.envVars =
-      optionalAttrs (!isNix20) {
-        NIX_CONF_DIR = "/etc/nix";
-
-        # Enable the copy-from-other-stores substituter, which allows
-        # builds to be sped up by copying build results from remote
-        # Nix stores.  To do this, mount the remote file system on a
-        # subdirectory of /run/nix/remote-stores.
-        NIX_OTHER_STORES = "/run/nix/remote-stores/*/nix";
-      }
-
-      // optionalAttrs (cfg.distributedBuilds && !isNix20) {
-        NIX_BUILD_HOOK = "${nix}/libexec/nix/build-remote.pl";
-      };
-
     # Set up the environment variables for running Nix.
     environment.sessionVariables = cfg.envVars //
       { NIX_PATH = cfg.nixPath;
       };
 
-    environment.extraInit = optionalString (!isNix20)
+    environment.extraInit =
       ''
-        # Set up secure multi-user builds: non-root users build through the
-        # Nix daemon.
-        if [ "$USER" != root -o ! -w /nix/var/nix/db ]; then
-            export NIX_REMOTE=daemon
-        fi
-      '' + ''
         if [ -e "$HOME/.nix-defexpr/channels" ]; then
           export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
         fi
@@ -478,21 +443,15 @@ in
 
     services.xserver.displayManager.hiddenUsers = map ({ name, ... }: name) nixbldUsers;
 
-    # FIXME: use systemd-tmpfiles to create Nix directories.
     system.activationScripts.nix = stringAfter [ "etc" "users" ]
       ''
-        # Nix initialisation.
-        install -m 0755 -d \
-          /nix/var/nix/gcroots \
-          /nix/var/nix/temproots \
-          /nix/var/nix/userpool \
-          /nix/var/nix/profiles \
-          /nix/var/nix/db \
-          /nix/var/log/nix/drvs
-        install -m 1777 -d \
-          /nix/var/nix/gcroots/per-user \
-          /nix/var/nix/profiles/per-user \
-          /nix/var/nix/gcroots/tmp
+        # Create directories in /nix.
+        ${nix}/bin/nix ping-store --no-net
+
+        # 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
       '';
 
     nix.systemFeatures = mkDefault (
diff --git a/nixos/modules/services/misc/nix-optimise.nix b/nixos/modules/services/misc/nix-optimise.nix
index 416529f690e0..e02026d5f76c 100644
--- a/nixos/modules/services/misc/nix-optimise.nix
+++ b/nixos/modules/services/misc/nix-optimise.nix
@@ -40,8 +40,8 @@ in
 
     systemd.services.nix-optimise =
       { description = "Nix Store Optimiser";
-        # No point running it inside a nixos-container. It should be on the host instead.
-        unitConfig.ConditionVirtualization = "!container";
+        # 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;
       };
diff --git a/nixos/modules/services/misc/osrm.nix b/nixos/modules/services/misc/osrm.nix
index f89f37ccd9df..79c347ab7e0e 100644
--- a/nixos/modules/services/misc/osrm.nix
+++ b/nixos/modules/services/misc/osrm.nix
@@ -59,6 +59,7 @@ in
       group = config.users.users.osrm.name;
       description = "OSRM user";
       createHome = false;
+      isSystemUser = true;
     };
 
     users.groups.osrm = { };
diff --git a/nixos/modules/services/misc/pykms.nix b/nixos/modules/services/misc/pykms.nix
index ab00086e591e..e2d1254602b0 100644
--- a/nixos/modules/services/misc/pykms.nix
+++ b/nixos/modules/services/misc/pykms.nix
@@ -4,6 +4,7 @@ with lib;
 
 let
   cfg = config.services.pykms;
+  libDir = "/var/lib/pykms";
 
 in {
   meta.maintainers = with lib.maintainers; [ peterhoeg ];
@@ -28,12 +29,6 @@ in {
         description = "The port on which to listen.";
       };
 
-      verbose = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Show verbose output.";
-      };
-
       openFirewallPort = mkOption {
         type = types.bool;
         default = false;
@@ -45,30 +40,44 @@ in {
         default = "64M";
         description = "How much memory to use at most.";
       };
+
+      logLevel = mkOption {
+        type = types.enum [ "CRITICAL" "ERROR" "WARNING" "INFO" "DEBUG" "MINI" ];
+        default = "INFO";
+        description = "How much to log";
+      };
+
+      extraArgs = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = "Additional arguments";
+      };
     };
   };
 
   config = mkIf cfg.enable {
     networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewallPort [ cfg.port ];
 
-    systemd.services.pykms = let
-      home = "/var/lib/pykms";
-    in {
+    systemd.services.pykms = {
       description = "Python KMS";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       # python programs with DynamicUser = true require HOME to be set
-      environment.HOME = home;
+      environment.HOME = libDir;
       serviceConfig = with pkgs; {
         DynamicUser = true;
-        StateDirectory = baseNameOf home;
-        ExecStartPre = "${getBin pykms}/bin/create_pykms_db.sh ${home}/clients.db";
+        StateDirectory = baseNameOf libDir;
+        ExecStartPre = "${getBin pykms}/libexec/create_pykms_db.sh ${libDir}/clients.db";
         ExecStart = lib.concatStringsSep " " ([
-          "${getBin pykms}/bin/server.py"
+          "${getBin pykms}/bin/server"
+          "--logfile STDOUT"
+          "--loglevel ${cfg.logLevel}"
+        ] ++ cfg.extraArgs ++ [
           cfg.listenAddress
           (toString cfg.port)
-        ] ++ lib.optional cfg.verbose "--verbose");
-        WorkingDirectory = home;
+        ]);
+        ProtectHome = "tmpfs";
+        WorkingDirectory = libDir;
         Restart = "on-failure";
         MemoryLimit = cfg.memoryLimit;
       };
diff --git a/nixos/modules/services/monitoring/collectd.nix b/nixos/modules/services/monitoring/collectd.nix
index 6a4c678eb21f..731ac743b7c6 100644
--- a/nixos/modules/services/monitoring/collectd.nix
+++ b/nixos/modules/services/monitoring/collectd.nix
@@ -16,13 +16,29 @@ let
       NotifyLevel "OKAY"
     </Plugin>
 
+    ${concatStrings (mapAttrsToList (plugin: pluginConfig: ''
+      LoadPlugin ${plugin}
+      <Plugin "${plugin}">
+      ${pluginConfig}
+      </Plugin>
+    '') cfg.plugins)}
+
     ${concatMapStrings (f: ''
-    Include "${f}"
+      Include "${f}"
     '') cfg.include}
 
     ${cfg.extraConfig}
   '';
 
+  package =
+    if cfg.buildMinimalPackage
+    then minimalPackage
+    else cfg.package;
+
+  minimalPackage = cfg.package.override {
+    enabledPlugins = [ "syslog" ] ++ builtins.attrNames cfg.plugins;
+  };
+
 in {
   options.services.collectd = with types; {
     enable = mkEnableOption "collectd agent";
@@ -33,7 +49,15 @@ in {
       description = ''
         Which collectd package to use.
       '';
-      type = package;
+      type = types.package;
+    };
+
+    buildMinimalPackage = mkOption {
+      default = false;
+      description = ''
+        Build a minimal collectd package with only the configured `services.collectd.plugins`
+      '';
+      type = types.bool;
     };
 
     user = mkOption {
@@ -68,6 +92,15 @@ in {
       type = listOf str;
     };
 
+    plugins = mkOption {
+      default = {};
+      example = { cpu = ""; memory = ""; network = "Server 192.168.1.1 25826"; };
+      description = ''
+        Attribute set of plugin names to plugin config segments
+      '';
+      type = types.attrsOf types.str;
+    };
+
     extraConfig = mkOption {
       default = "";
       description = ''
@@ -89,7 +122,7 @@ in {
       wantedBy = [ "multi-user.target" ];
 
       serviceConfig = {
-        ExecStart = "${cfg.package}/sbin/collectd -C ${conf} -f";
+        ExecStart = "${package}/sbin/collectd -C ${conf} -f";
         User = cfg.user;
         Restart = "on-failure";
         RestartSec = 3;
@@ -98,6 +131,7 @@ in {
 
     users.users = optional (cfg.user == "collectd") {
       name = "collectd";
+      isSystemUser = true;
     };
   };
 }
diff --git a/nixos/modules/services/monitoring/do-agent.nix b/nixos/modules/services/monitoring/do-agent.nix
new file mode 100644
index 000000000000..2d3fe2f79768
--- /dev/null
+++ b/nixos/modules/services/monitoring/do-agent.nix
@@ -0,0 +1,34 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.do-agent;
+in
+{
+  options.services.do-agent = {
+    enable = mkEnableOption "do-agent, the DigitalOcean droplet metrics agent";
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.do-agent ];
+
+    systemd.services.do-agent = {
+      description = "DigitalOcean Droplet Metrics Agent";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" ];
+      wants = [ "network-online.target" ];
+      serviceConfig = {
+        ExecStart = "${pkgs.do-agent}/bin/do-agent --syslog";
+        Restart = "always";
+        OOMScoreAdjust = -900;
+        SyslogIdentifier = "DigitalOceanAgent";
+        PrivateTmp = "yes";
+        ProtectSystem = "full";
+        ProtectHome = "yes";
+        NoNewPrivileges = "yes";
+        DynamicUser = "yes";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/fusion-inventory.nix b/nixos/modules/services/monitoring/fusion-inventory.nix
index b90579bb70c7..fe19ed561954 100644
--- a/nixos/modules/services/monitoring/fusion-inventory.nix
+++ b/nixos/modules/services/monitoring/fusion-inventory.nix
@@ -49,6 +49,7 @@ in {
     users.users = singleton {
       name = "fusion-inventory";
       description = "FusionInventory user";
+      isSystemUser = true;
     };
 
     systemd.services.fusion-inventory = {
diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix
index 64cb6c3da1e5..f7874af3df29 100644
--- a/nixos/modules/services/monitoring/graphite.nix
+++ b/nixos/modules/services/monitoring/graphite.nix
@@ -239,7 +239,7 @@ in {
         description = "Any metrics received which match one of the experssions will be dropped.";
         default = null;
         type = types.nullOr types.str;
-        example = "^some\.noisy\.metric\.prefix\..*";
+        example = "^some\\.noisy\\.metric\\.prefix\\..*";
       };
 
       whitelist = mkOption {
diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix
index 463b1b882acf..7d976db96300 100644
--- a/nixos/modules/services/monitoring/netdata.nix
+++ b/nixos/modules/services/monitoring/netdata.nix
@@ -181,6 +181,7 @@ in {
 
     users.users = optional (cfg.user == defaultUser) {
       name = defaultUser;
+      isSystemUser = true;
     };
 
     users.groups = optional (cfg.group == defaultUser) {
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
index b69310c34ff5..35b513bac571 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -30,10 +30,12 @@ let
     "json"
     "mail"
     "minio"
+    "nextcloud"
     "nginx"
     "node"
     "postfix"
     "postgres"
+    "rspamd"
     "snmp"
     "surfboard"
     "tor"
@@ -193,6 +195,8 @@ in
     services.prometheus.exporters.minio.minioAddress  = mkDefault "http://localhost:9000";
     services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey;
     services.prometheus.exporters.minio.minioAccessSecret = mkDefault config.services.minio.secretKey;
+  })] ++ [(mkIf config.services.rspamd.enable {
+    services.prometheus.exporters.rspamd.url = mkDefault "http://localhost:11334/stat";
   })] ++ (mapAttrsToList (name: conf:
     mkExporterConf {
       inherit name;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix b/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
index ca4366121e12..8a90afa99842 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
@@ -3,16 +3,34 @@
 with lib;
 
 let
+  logPrefix = "services.prometheus.exporter.blackbox";
   cfg = config.services.prometheus.exporters.blackbox;
 
-  checkConfig = file: pkgs.runCommand "checked-blackbox-exporter.conf" {
-    preferLocalBuild = true;
-    buildInputs = [ pkgs.buildPackages.prometheus-blackbox-exporter ]; } ''
-    ln -s ${file} $out
-    blackbox_exporter --config.check --config.file $out
-  '';
-in
-{
+  # This ensures that we can deal with string paths, path types and
+  # store-path strings with context.
+  coerceConfigFile = file:
+    if (builtins.isPath file) || (lib.isStorePath file) then
+      file
+    else
+      (lib.warn ''
+        ${logPrefix}: configuration file "${file}" is being copied to the nix-store.
+        If you would like to avoid that, please set enableConfigCheck to false.
+      '' /. + file);
+  checkConfigLocation = file:
+    if lib.hasPrefix "/tmp/" file then
+      throw
+      "${logPrefix}: configuration file must not reside within /tmp - it won't be visible to the systemd service."
+    else
+      true;
+  checkConfig = file:
+    pkgs.runCommand "checked-blackbox-exporter.conf" {
+      preferLocalBuild = true;
+      buildInputs = [ pkgs.buildPackages.prometheus-blackbox-exporter ];
+    } ''
+      ln -s ${coerceConfigFile file} $out
+      blackbox_exporter --config.check --config.file $out
+    '';
+in {
   port = 9115;
   extraOpts = {
     configFile = mkOption {
@@ -21,14 +39,29 @@ in
         Path to configuration file.
       '';
     };
+    enableConfigCheck = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to run a correctness check for the configuration file. This depends
+        on the configuration file residing in the nix-store. Paths passed as string will
+        be copied to the store.
+      '';
+    };
   };
-  serviceOpts = {
+
+  serviceOpts = let
+    adjustedConfigFile = if cfg.enableConfigCheck then
+      checkConfig cfg.configFile
+    else
+      checkConfigLocation cfg.configFile;
+  in {
     serviceConfig = {
       AmbientCapabilities = [ "CAP_NET_RAW" ]; # for ping probes
       ExecStart = ''
         ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \
           --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
-          --config.file ${checkConfig cfg.configFile} \
+          --config.file ${adjustedConfigFile} \
           ${concatStringsSep " \\\n  " cfg.extraFlags}
       '';
       ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix b/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix
new file mode 100644
index 000000000000..5f9a52053f79
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix
@@ -0,0 +1,58 @@
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.nextcloud;
+in
+{
+  port = 9205;
+  extraOpts = {
+    url = mkOption {
+      type = types.str;
+      example = "https://domain.tld";
+      description = ''
+        URL to the Nextcloud serverinfo page.
+        Adding the path to the serverinfo API is optional, it defaults
+        to <literal>/ocs/v2.php/apps/serverinfo/api/v1/info</literal>.
+      '';
+    };
+    username = mkOption {
+      type = types.str;
+      default = "nextcloud-exporter";
+      description = ''
+        Username for connecting to Nextcloud.
+        Note that this account needs to have admin privileges in Nextcloud.
+      '';
+    };
+    passwordFile = mkOption {
+      type = types.path;
+      example = "/path/to/password-file";
+      description = ''
+        File containing the password for connecting to Nextcloud.
+        Make sure that this file is readable by the exporter user.
+      '';
+    };
+    timeout = mkOption {
+      type = types.str;
+      default = "5s";
+      description = ''
+        Timeout for getting server info document.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = false;
+      ExecStart = ''
+        ${pkgs.prometheus-nextcloud-exporter}/bin/nextcloud-exporter \
+          -a ${cfg.listenAddress}:${toString cfg.port} \
+          -u ${cfg.username} \
+          -t ${cfg.timeout} \
+          -l ${cfg.url} \
+          -p @${cfg.passwordFile} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
index 554377df37ba..ba852fea4336 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
@@ -49,6 +49,6 @@ in
     (mkRemovedOptionModule [ "insecure" ] ''
       This option was replaced by 'prometheus.exporters.nginx.sslVerify'.
     '')
-    ({ options.warnings = options.warnings; })
+    ({ options.warnings = options.warnings; options.assertions = options.assertions; })
   ];
 }
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix b/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix
new file mode 100644
index 000000000000..1f02ae207249
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix
@@ -0,0 +1,92 @@
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.rspamd;
+
+  prettyJSON = conf:
+    pkgs.runCommand "rspamd-exporter-config.yml" { } ''
+      echo '${builtins.toJSON conf}' | ${pkgs.buildPackages.jq}/bin/jq '.' > $out
+    '';
+
+  generateConfig = extraLabels: (map (path: {
+    name = "rspamd_${replaceStrings [ "." " " ] [ "_" "_" ] path}";
+    path = "$.${path}";
+    labels = extraLabels;
+  }) [
+    "actions.'add header'"
+    "actions.'no action'"
+    "actions.'rewrite subject'"
+    "actions.'soft reject'"
+    "actions.greylist"
+    "actions.reject"
+    "bytes_allocated"
+    "chunks_allocated"
+    "chunks_freed"
+    "chunks_oversized"
+    "connections"
+    "control_connections"
+    "ham_count"
+    "learned"
+    "pools_allocated"
+    "pools_freed"
+    "read_only"
+    "scanned"
+    "shared_chunks_allocated"
+    "spam_count"
+    "total_learns"
+  ]) ++ [{
+    name = "rspamd_statfiles";
+    type = "object";
+    path = "$.statfiles[*]";
+    labels = recursiveUpdate {
+      symbol = "$.symbol";
+      type = "$.type";
+    } extraLabels;
+    values = {
+      revision = "$.revision";
+      size = "$.size";
+      total = "$.total";
+      used = "$.used";
+      languages = "$.languages";
+      users = "$.users";
+    };
+  }];
+in
+{
+  port = 7980;
+  extraOpts = {
+    listenAddress = {}; # not used
+
+    url = mkOption {
+      type = types.str;
+      description = ''
+        URL to the rspamd metrics endpoint.
+        Defaults to http://localhost:11334/stat when
+        <option>services.rspamd.enable</option> is true.
+      '';
+    };
+
+    extraLabels = mkOption {
+      type = types.attrsOf types.str;
+      default = {
+        host = config.networking.hostName;
+      };
+      defaultText = "{ host = config.networking.hostName; }";
+      example = literalExample ''
+        {
+          host = config.networking.hostName;
+          custom_label = "some_value";
+        }
+      '';
+      description = "Set of labels added to each metric.";
+    };
+  };
+  serviceOpts.serviceConfig.ExecStart = ''
+    ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \
+      --port ${toString cfg.port} \
+      ${cfg.url} ${prettyJSON (generateConfig cfg.extraLabels)} \
+      ${concatStringsSep " \\\n  " cfg.extraFlags}
+  '';
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix b/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
index 8ae2c927b58c..374f83a2939d 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
@@ -6,6 +6,10 @@ let
   cfg = config.services.prometheus.exporters.wireguard;
 in {
   port = 9586;
+  imports = [
+    (mkRenamedOptionModule [ "addr" ] [ "listenAddress" ])
+    ({ options.warnings = options.warnings; options.assertions = options.assertions; })
+  ];
   extraOpts = {
     verbose = mkEnableOption "Verbose logging mode for prometheus-wireguard-exporter";
 
@@ -51,6 +55,7 @@ in {
       ExecStart = ''
         ${pkgs.prometheus-wireguard-exporter}/bin/prometheus_wireguard_exporter \
           -p ${toString cfg.port} \
+          -l ${cfg.listenAddress} \
           ${optionalString cfg.verbose "-v"} \
           ${optionalString cfg.singleSubnetPerField "-s"} \
           ${optionalString cfg.withRemoteIp "-r"} \
diff --git a/nixos/modules/services/monitoring/thanos.nix b/nixos/modules/services/monitoring/thanos.nix
index 215cd43fd864..52dab28cf72f 100644
--- a/nixos/modules/services/monitoring/thanos.nix
+++ b/nixos/modules/services/monitoring/thanos.nix
@@ -126,6 +126,8 @@ let
           '';
           description = ''
             Path to YAML file that contains tracing configuration.
+
+            See format details: <link xlink:href="https://thanos.io/tracing.md/#configuration"/>
           '';
         };
       };
@@ -141,6 +143,8 @@ let
             <option>tracing.config-file</option> will default to its path.
 
             If <option>tracing.config-file</option> is set this option has no effect.
+
+            See format details: <link xlink:href="https://thanos.io/tracing.md/#configuration"/>
           '';
         };
     };
@@ -187,6 +191,8 @@ let
           '';
           description = ''
             Path to YAML file that contains object store configuration.
+
+            See format details: <link xlink:href="https://thanos.io/storage.md/#configuration"/>
           '';
         };
       };
@@ -202,6 +208,8 @@ let
             <option>objstore.config-file</option> will default to its path.
 
             If <option>objstore.config-file</option> is set this option has no effect.
+
+            See format details: <link xlink:href="https://thanos.io/storage.md/#configuration"/>
           '';
         };
     };
@@ -276,6 +284,24 @@ let
       block-sync-concurrency = mkParamDef types.int 20 ''
         Number of goroutines to use when syncing blocks from object storage.
       '';
+
+      min-time = mkParamDef types.str "0000-01-01T00:00:00Z" ''
+        Start of time range limit to serve.
+
+        Thanos Store serves only metrics, which happened later than this
+        value. Option can be a constant time in RFC3339 format or time duration
+        relative to current time, such as -1d or 2h45m. Valid duration units are
+        ms, s, m, h, d, w, y.
+      '';
+
+      max-time = mkParamDef types.str "9999-12-31T23:59:59Z" ''
+        End of time range limit to serve.
+
+        Thanos Store serves only blocks, which happened eariler than this
+        value. Option can be a constant time in RFC3339 format or time duration
+        relative to current time, such as -1d or 2h45m. Valid duration units are
+        ms, s, m, h, d, w, y.
+      '';
     };
 
     query = params.common cfg.query // {
@@ -560,6 +586,14 @@ let
         '';
       };
 
+      downsampling.disable = mkFlagParam ''
+        Disables downsampling.
+
+        This is not recommended as querying long time ranges without
+        non-downsampled data is not efficient and useful e.g it is not possible
+        to render all samples for a human eye anyway
+      '';
+
       block-sync-concurrency = mkParamDef types.int 20 ''
         Number of goroutines to use when syncing block metadata from object storage.
       '';
diff --git a/nixos/modules/services/monitoring/zabbix-agent.nix b/nixos/modules/services/monitoring/zabbix-agent.nix
index 856b9432892b..b3383ed628b2 100644
--- a/nixos/modules/services/monitoring/zabbix-agent.nix
+++ b/nixos/modules/services/monitoring/zabbix-agent.nix
@@ -131,6 +131,7 @@ in
     users.users.${user} = {
       description = "Zabbix Agent daemon user";
       inherit group;
+      isSystemUser = true;
     };
 
     users.groups.${group} = { };
diff --git a/nixos/modules/services/monitoring/zabbix-server.nix b/nixos/modules/services/monitoring/zabbix-server.nix
index 4b4049ed360e..e9f1590760a4 100644
--- a/nixos/modules/services/monitoring/zabbix-server.nix
+++ b/nixos/modules/services/monitoring/zabbix-server.nix
@@ -30,6 +30,7 @@ let
     DBUser = ${cfg.database.user}
     ${optionalString (cfg.database.passwordFile != null) "Include ${passwordFile}"}
     ${optionalString (mysqlLocal && cfg.database.socket != null) "DBSocket = ${cfg.database.socket}"}
+    PidFile = ${runtimeDir}/zabbix_server.pid
     SocketDir = ${runtimeDir}
     FpingLocation = /run/wrappers/bin/fping
     ${optionalString (cfg.modules != {}) "LoadModulePath = ${moduleEnv}/lib"}
diff --git a/nixos/modules/services/network-filesystems/orangefs/client.nix b/nixos/modules/services/network-filesystems/orangefs/client.nix
new file mode 100644
index 000000000000..b69d9e713c3d
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/orangefs/client.nix
@@ -0,0 +1,97 @@
+{ config, lib, pkgs, ...} :
+
+with lib;
+
+let
+  cfg = config.services.orangefs.client;
+
+in {
+  ###### interface
+
+  options = {
+    services.orangefs.client = {
+      enable = mkEnableOption "OrangeFS client daemon";
+
+      extraOptions = mkOption {
+        type = with types; listOf str;
+        default = [];
+        description = "Extra command line options for pvfs2-client.";
+      };
+
+      fileSystems = mkOption {
+        description = ''
+          The orangefs file systems to be mounted.
+          This option is prefered over using <option>fileSystems</option> directly since
+          the pvfs client service needs to be running for it to be mounted.
+        '';
+
+        example = [{
+          mountPoint = "/orangefs";
+          target = "tcp://server:3334/orangefs";
+        }];
+
+        type = with types; listOf (submodule ({ ... } : {
+          options = {
+
+            mountPoint = mkOption {
+              type = types.str;
+              default = "/orangefs";
+              description = "Mount point.";
+            };
+
+            options = mkOption {
+              type = with types; listOf str;
+              default = [];
+              description = "Mount options";
+            };
+
+            target = mkOption {
+              type = types.str;
+              default = null;
+              example = "tcp://server:3334/orangefs";
+              description = "Target URL";
+            };
+          };
+        }));
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.orangefs ];
+
+    boot.supportedFilesystems = [ "pvfs2" ];
+    boot.kernelModules = [ "orangefs" ];
+
+    systemd.services.orangefs-client = {
+      requires = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+
+      serviceConfig = {
+        Type = "simple";
+
+         ExecStart = ''
+           ${pkgs.orangefs}/bin/pvfs2-client-core \
+              --logtype=syslog ${concatStringsSep " " cfg.extraOptions}
+        '';
+
+        TimeoutStopSec = "120";
+      };
+    };
+
+    systemd.mounts = map (fs: {
+      requires = [ "orangefs-client.service" ];
+      after = [ "orangefs-client.service" ];
+      bindsTo = [ "orangefs-client.service" ];
+      wantedBy = [ "remote-fs.target" ];
+      type = "pvfs2";
+      options = concatStringsSep "," fs.options;
+      what = fs.target;
+      where = fs.mountPoint;
+    }) cfg.fileSystems;
+  };
+}
+
diff --git a/nixos/modules/services/network-filesystems/orangefs/server.nix b/nixos/modules/services/network-filesystems/orangefs/server.nix
new file mode 100644
index 000000000000..74ebdc134024
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/orangefs/server.nix
@@ -0,0 +1,225 @@
+{ config, lib, pkgs, ...} :
+
+with lib;
+
+let
+  cfg = config.services.orangefs.server;
+
+  aliases = mapAttrsToList (alias: url: alias) cfg.servers;
+
+  # Maximum handle number is 2^63
+  maxHandle = 9223372036854775806;
+
+  # One range of handles for each meta/data instance
+  handleStep = maxHandle / (length aliases) / 2;
+
+  fileSystems = mapAttrsToList (name: fs: ''
+    <FileSystem>
+      Name ${name}
+      ID ${toString fs.id}
+      RootHandle ${toString fs.rootHandle}
+
+      ${fs.extraConfig}
+
+      <MetaHandleRanges>
+      ${concatStringsSep "\n" (
+          imap0 (i: alias:
+            let
+              begin = i * handleStep + 3;
+              end = begin + handleStep - 1;
+            in "Range ${alias} ${toString begin}-${toString end}") aliases
+       )}
+      </MetaHandleRanges>
+
+      <DataHandleRanges>
+      ${concatStringsSep "\n" (
+          imap0 (i: alias:
+            let
+              begin = i * handleStep + 3 + (length aliases) * handleStep;
+              end = begin + handleStep - 1;
+            in "Range ${alias} ${toString begin}-${toString end}") aliases
+       )}
+      </DataHandleRanges>
+
+      <StorageHints>
+      TroveSyncMeta ${if fs.troveSyncMeta then "yes" else "no"}
+      TroveSyncData ${if fs.troveSyncData then "yes" else "no"}
+      ${fs.extraStorageHints}
+      </StorageHints>
+
+    </FileSystem>
+  '') cfg.fileSystems;
+
+  configFile = ''
+    <Defaults>
+    LogType ${cfg.logType}
+    DataStorageSpace ${cfg.dataStorageSpace}
+    MetaDataStorageSpace ${cfg.metadataStorageSpace}
+
+    BMIModules ${concatStringsSep "," cfg.BMIModules}
+    ${cfg.extraDefaults}
+    </Defaults>
+
+    ${cfg.extraConfig}
+
+    <Aliases>
+    ${concatStringsSep "\n" (mapAttrsToList (alias: url: "Alias ${alias} ${url}") cfg.servers)}
+    </Aliases>
+
+    ${concatStringsSep "\n" fileSystems}
+  '';
+
+in {
+  ###### interface
+
+  options = {
+    services.orangefs.server = {
+      enable = mkEnableOption "OrangeFS server";
+
+      logType = mkOption {
+        type = with types; enum [ "file" "syslog" ];
+        default = "syslog";
+        description = "Destination for log messages.";
+      };
+
+      dataStorageSpace = mkOption {
+        type = types.str;
+        default = null;
+        example = "/data/storage";
+        description = "Directory for data storage.";
+      };
+
+      metadataStorageSpace = mkOption {
+        type = types.str;
+        default = null;
+        example = "/data/meta";
+        description = "Directory for meta data storage.";
+      };
+
+      BMIModules = mkOption {
+        type = with types; listOf str;
+        default = [ "bmi_tcp" ];
+        example = [ "bmi_tcp" "bmi_ib"];
+        description = "List of BMI modules to load.";
+      };
+
+      extraDefaults = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Extra config for <literal>&lt;Defaults&gt;</literal> section.";
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Extra config for the global section.";
+      };
+
+      servers = mkOption {
+        type = with types; attrsOf types.str;
+        default = {};
+        example = ''
+          {
+            node1="tcp://node1:3334";
+            node2="tcp://node2:3334";
+          }
+        '';
+        description = "URLs for storage server including port. The attribute names define the server alias.";
+      };
+
+      fileSystems = mkOption {
+        description = ''
+          These options will create the <literal>&lt;FileSystem&gt;</literal> sections of config file.
+        '';
+        default = { orangefs = {}; };
+        defaultText = literalExample "{ orangefs = {}; }";
+        example = literalExample ''
+          {
+            fs1 = {
+              id = 101;
+            };
+
+            fs2 = {
+              id = 102;
+            };
+          }
+        '';
+        type = with types; attrsOf (submodule ({ ... } : {
+          options = {
+            id = mkOption {
+              type = types.int;
+              default = 1;
+              description = "File system ID (must be unique within configuration).";
+            };
+
+            rootHandle = mkOption {
+              type = types.int;
+              default = 3;
+              description = "File system root ID.";
+            };
+
+            extraConfig = mkOption {
+              type = types.lines;
+              default = "";
+              description = "Extra config for <literal>&lt;FileSystem&gt;</literal> section.";
+            };
+
+            troveSyncMeta = mkOption {
+              type = types.bool;
+              default = true;
+              description = "Sync meta data.";
+            };
+
+            troveSyncData = mkOption {
+              type = types.bool;
+              default = false;
+              description = "Sync data.";
+            };
+
+            extraStorageHints = mkOption {
+              type = types.lines;
+              default = "";
+              description = "Extra config for <literal>&lt;StorageHints&gt;</literal> section.";
+            };
+          };
+        }));
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.orangefs ];
+
+    # orangefs daemon will run as user
+    users.users.orangefs.isSystemUser = true;
+    users.groups.orangefs = {};
+
+    # To format the file system the config file is needed.
+    environment.etc."orangefs/server.conf" = {
+      text = configFile;
+      user = "orangefs";
+      group = "orangefs";
+    };
+
+    systemd.services.orangefs-server = {
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+
+      serviceConfig = {
+        # Run as "simple" in forground mode.
+        # This is more reliable
+        ExecStart = ''
+          ${pkgs.orangefs}/bin/pvfs2-server -d \
+            /etc/orangefs/server.conf
+        '';
+        TimeoutStopSec = "120";
+        User = "orangefs";
+        Group = "orangefs";
+      };
+    };
+  };
+
+}
diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix
index 055508a32244..ce565dbaab81 100644
--- a/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -45,6 +45,7 @@ let
   daemonService = appName: args:
     { description = "Samba Service Daemon ${appName}";
 
+      after = [ "network.target" ];
       requiredBy = [ "samba.target" ];
       partOf = [ "samba.target" ];
 
diff --git a/nixos/modules/services/networking/bitcoind.nix b/nixos/modules/services/networking/bitcoind.nix
index 1439d739da9d..90f1291c0198 100644
--- a/nixos/modules/services/networking/bitcoind.nix
+++ b/nixos/modules/services/networking/bitcoind.nix
@@ -187,6 +187,7 @@ in {
       group = cfg.group;
       description = "Bitcoin daemon user";
       home = cfg.dataDir;
+      isSystemUser = true;
     };
     users.groups.${cfg.group} = {
       name = cfg.group;
diff --git a/nixos/modules/services/networking/dnscache.nix b/nixos/modules/services/networking/dnscache.nix
index 5051fc916d96..d123bca93219 100644
--- a/nixos/modules/services/networking/dnscache.nix
+++ b/nixos/modules/services/networking/dnscache.nix
@@ -84,7 +84,7 @@ in {
 
   config = mkIf config.services.dnscache.enable {
     environment.systemPackages = [ pkgs.djbdns ];
-    users.users.dnscache = {};
+    users.users.dnscache.isSystemUser = true;
 
     systemd.services.dnscache = {
       description = "djbdns dnscache server";
diff --git a/nixos/modules/services/networking/dnscrypt-wrapper.nix b/nixos/modules/services/networking/dnscrypt-wrapper.nix
index bf13d5c6f5fe..79f9e1a43083 100644
--- a/nixos/modules/services/networking/dnscrypt-wrapper.nix
+++ b/nixos/modules/services/networking/dnscrypt-wrapper.nix
@@ -142,6 +142,7 @@ in {
       description = "dnscrypt-wrapper daemon user";
       home = "${dataDir}";
       createHome = true;
+      isSystemUser = true;
     };
     users.groups.dnscrypt-wrapper = { };
 
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index 5b3aa19af3bb..5919962837a2 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -331,6 +331,17 @@ in
           '';
       };
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.iptables;
+        defaultText = "pkgs.iptables";
+        example = literalExample "pkgs.iptables-nftables-compat";
+        description =
+          ''
+            The iptables package to use for running the firewall service."
+          '';
+      };
+
       logRefusedConnections = mkOption {
         type = types.bool;
         default = true;
@@ -536,7 +547,7 @@ in
 
     networking.firewall.trustedInterfaces = [ "lo" ];
 
-    environment.systemPackages = [ pkgs.iptables ] ++ cfg.extraPackages;
+    environment.systemPackages = [ cfg.package ] ++ cfg.extraPackages;
 
     boot.kernelModules = (optional cfg.autoLoadConntrackHelpers "nf_conntrack")
       ++ map (x: "nf_conntrack_${x}") cfg.connectionTrackingModules;
@@ -555,7 +566,7 @@ in
       before = [ "network-pre.target" ];
       after = [ "systemd-modules-load.service" ];
 
-      path = [ pkgs.iptables ] ++ cfg.extraPackages;
+      path = [ cfg.package ] ++ cfg.extraPackages;
 
       # FIXME: this module may also try to load kernel modules, but
       # containers don't have CAP_SYS_MODULE.  So the host system had
diff --git a/nixos/modules/services/networking/go-shadowsocks2.nix b/nixos/modules/services/networking/go-shadowsocks2.nix
new file mode 100644
index 000000000000..afbd7ea27c65
--- /dev/null
+++ b/nixos/modules/services/networking/go-shadowsocks2.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.go-shadowsocks2.server;
+in {
+  options.services.go-shadowsocks2.server = {
+    enable = mkEnableOption "go-shadowsocks2 server";
+
+    listenAddress = mkOption {
+      type = types.str;
+      description = "Server listen address or URL";
+      example = "ss://AEAD_CHACHA20_POLY1305:your-password@:8488";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.go-shadowsocks2-server = {
+      description = "go-shadowsocks2 server";
+
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = "${pkgs.go-shadowsocks2}/bin/go-shadowsocks2 -s '${cfg.listenAddress}'";
+        DynamicUser = true;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/hans.nix b/nixos/modules/services/networking/hans.nix
index 20e57e4626ef..4f60300f5ff4 100644
--- a/nixos/modules/services/networking/hans.nix
+++ b/nixos/modules/services/networking/hans.nix
@@ -138,6 +138,7 @@ in
     users.users = singleton {
       name = hansUser;
       description = "Hans daemon user";
+      isSystemUser = true;
     };
   };
 
diff --git a/nixos/modules/services/networking/jormungandr.nix b/nixos/modules/services/networking/jormungandr.nix
index 85e804d6cf25..152cceb4bf91 100644
--- a/nixos/modules/services/networking/jormungandr.nix
+++ b/nixos/modules/services/networking/jormungandr.nix
@@ -14,8 +14,10 @@ let
     storage = dataDir;
     p2p = {
       public_address = "/ip4/127.0.0.1/tcp/8299";
-      messages = "high";
-      blocks = "high";
+      topics_of_interest = {
+        messages = "high";
+        blocks = "high";
+      };
     };
     rest = {
       listen = "127.0.0.1:8607";
diff --git a/nixos/modules/services/networking/matterbridge.nix b/nixos/modules/services/networking/matterbridge.nix
index 1fd63348c16c..682eaa6eb297 100644
--- a/nixos/modules/services/networking/matterbridge.nix
+++ b/nixos/modules/services/networking/matterbridge.nix
@@ -95,6 +95,7 @@ in
     users.users = optional (cfg.user == "matterbridge")
       { name = "matterbridge";
         group = "matterbridge";
+        isSystemUser = true;
       };
 
     users.groups = optional (cfg.group == "matterbridge")
diff --git a/nixos/modules/services/networking/minidlna.nix b/nixos/modules/services/networking/minidlna.nix
index 0947471adbc9..3ddea3c9757b 100644
--- a/nixos/modules/services/networking/minidlna.nix
+++ b/nixos/modules/services/networking/minidlna.nix
@@ -36,6 +36,37 @@ in
         '';
     };
 
+    services.minidlna.friendlyName = mkOption {
+      type = types.str;
+      default = "${config.networking.hostName} MiniDLNA";
+      defaultText = "$HOSTNAME MiniDLNA";
+      example = "rpi3";
+      description =
+        ''
+          Name that the DLNA server presents to clients.
+        '';
+    };
+
+    services.minidlna.rootContainer = mkOption {
+      type = types.str;
+      default = ".";
+      example = "B";
+      description =
+        ''
+          Use a different container as the root of the directory tree presented
+          to clients. The possible values are:
+          - "." - standard container
+          - "B" - "Browse Directory"
+          - "M" - "Music"
+          - "P" - "Pictures"
+          - "V" - "Video"
+          - Or, you can specify the ObjectID of your desired root container
+            (eg. 1$F for Music/Playlists)
+          If you specify "B" and the client device is audio-only then
+          "Music/Folders" will be used as root.
+         '';
+    };
+
     services.minidlna.loglevel = mkOption {
       type = types.str;
       default = "warn";
@@ -66,7 +97,37 @@ in
 
     services.minidlna.config = mkOption {
       type = types.lines;
-      description = "The contents of MiniDLNA's configuration file.";
+      description =
+      ''
+        The contents of MiniDLNA's configuration file.
+        When the service is activated, a basic template is generated
+        from the current options opened here.
+      '';
+    };
+
+    services.minidlna.extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      example = ''
+        # Not exhaustive example
+        # Support for streaming .jpg and .mp3 files to a TiVo supporting HMO.
+        enable_tivo=no
+        # SSDP notify interval, in seconds.
+        notify_interval=10
+        # maximum number of simultaneous connections
+        # note: many clients open several simultaneous connections while
+        # streaming
+        max_connections=50
+        # set this to yes to allow symlinks that point outside user-defined
+        # media_dirs.
+        wide_links=yes
+      '';
+      description =
+      ''
+        Extra minidlna options not yet opened for configuration here
+        (strict_dlna, model_number, model_name, etc...).  This is appended
+        to the current service already provided.
+      '';
     };
   };
 
@@ -75,13 +136,15 @@ in
     services.minidlna.config =
       ''
         port=${toString port}
-        friendly_name=${config.networking.hostName} MiniDLNA
+        friendly_name=${cfg.friendlyName}
         db_dir=/var/cache/minidlna
         log_level=${cfg.loglevel}
         inotify=yes
+        root_container=${cfg.rootContainer}
         ${concatMapStrings (dir: ''
           media_dir=${dir}
         '') cfg.mediaDirs}
+        ${cfg.extraConfig}
       '';
 
     users.users.minidlna = {
diff --git a/nixos/modules/services/networking/morty.nix b/nixos/modules/services/networking/morty.nix
index 1b3084fe9abb..e3a6444c1163 100644
--- a/nixos/modules/services/networking/morty.nix
+++ b/nixos/modules/services/networking/morty.nix
@@ -74,6 +74,7 @@ in
       { description = "Morty user";
         createHome = true;
         home = "/var/lib/morty";
+        isSystemUser = true;
       };
 
     systemd.services.morty =
diff --git a/nixos/modules/services/networking/mxisd.nix b/nixos/modules/services/networking/mxisd.nix
index 02e89f441b34..a3d61922e578 100644
--- a/nixos/modules/services/networking/mxisd.nix
+++ b/nixos/modules/services/networking/mxisd.nix
@@ -3,6 +3,15 @@
 with lib;
 
 let
+
+  isMa1sd =
+    package:
+    lib.hasPrefix "ma1sd" package.name;
+
+  isMxisd =
+    package:
+    lib.hasPrefix "mxisd" package.name;
+
   cfg = config.services.mxisd;
 
   server = optionalAttrs (cfg.server.name != null) { inherit (cfg.server) name; }
@@ -12,37 +21,41 @@ let
     matrix.domain = cfg.matrix.domain;
     key.path = "${cfg.dataDir}/signing.key";
     storage = {
-      provider.sqlite.database = "${cfg.dataDir}/mxisd.db";
+      provider.sqlite.database = if isMa1sd cfg.package
+                                 then "${cfg.dataDir}/ma1sd.db"
+                                 else "${cfg.dataDir}/mxisd.db";
     };
   } // optionalAttrs (server != {}) { inherit server; };
 
   # merges baseConfig and extraConfig into a single file
   fullConfig = recursiveUpdate baseConfig cfg.extraConfig;
 
-  configFile = pkgs.writeText "mxisd-config.yaml" (builtins.toJSON fullConfig);
+  configFile = if isMa1sd cfg.package
+               then pkgs.writeText "ma1sd-config.yaml" (builtins.toJSON fullConfig)
+               else pkgs.writeText "mxisd-config.yaml" (builtins.toJSON fullConfig);
 
 in {
   options = {
     services.mxisd = {
-      enable = mkEnableOption "mxisd matrix federated identity server";
+      enable = mkEnableOption "matrix federated identity server";
 
       package = mkOption {
         type = types.package;
         default = pkgs.mxisd;
         defaultText = "pkgs.mxisd";
-        description = "The mxisd package to use";
+        description = "The mxisd/ma1sd package to use";
       };
 
       dataDir = mkOption {
         type = types.str;
         default = "/var/lib/mxisd";
-        description = "Where data mxisd uses resides";
+        description = "Where data mxisd/ma1sd uses resides";
       };
 
       extraConfig = mkOption {
         type = types.attrs;
         default = {};
-        description = "Extra options merged into the mxisd configuration";
+        description = "Extra options merged into the mxisd/ma1sd configuration";
       };
 
       matrix = {
@@ -62,7 +75,7 @@ in {
           type = types.nullOr types.str;
           default = null;
           description = ''
-            Public hostname of mxisd, if different from the Matrix domain.
+            Public hostname of mxisd/ma1sd, if different from the Matrix domain.
           '';
         };
 
@@ -103,11 +116,13 @@ in {
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
 
-      serviceConfig = {
+      serviceConfig = let
+        executable = if isMa1sd cfg.package then "ma1sd" else "mxisd";
+      in {
         Type = "simple";
         User = "mxisd";
         Group = "mxisd";
-        ExecStart = "${cfg.package}/bin/mxisd -c ${configFile}";
+        ExecStart = "${cfg.package}/bin/${executable} -c ${configFile}";
         WorkingDirectory = cfg.dataDir;
         Restart = "on-failure";
       };
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index 2061c02fffbd..918bf891b103 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -5,11 +5,21 @@ with lib;
 let
   cfg = config.networking.networkmanager;
 
-  dynamicHostsEnabled =
-    cfg.dynamicHosts.enable && cfg.dynamicHosts.hostsDirs != {};
-
-  # /var/lib/misc is for dnsmasq.leases.
-  stateDirs = "/var/lib/NetworkManager /var/lib/dhclient /var/lib/misc";
+  basePackages = with pkgs; [
+    crda
+    modemmanager
+    networkmanager
+    networkmanager-fortisslvpn
+    networkmanager-iodine
+    networkmanager-l2tp
+    networkmanager-openconnect
+    networkmanager-openvpn
+    networkmanager-vpnc
+   ] ++ optional (!delegateWireless && !enableIwd) wpa_supplicant;
+
+  delegateWireless = config.networking.wireless.enable == true && cfg.unmanaged != [];
+
+  enableIwd = cfg.wifi.backend == "iwd";
 
   configFile = pkgs.writeText "NetworkManager.conf" ''
     [main]
@@ -25,6 +35,7 @@ let
 
     [logging]
     level=${cfg.logLevel}
+    audit=${lib.boolToString config.security.audit.enable}
 
     [connection]
     ipv6.ip6-privacy=2
@@ -35,6 +46,7 @@ let
 
     [device]
     wifi.scan-rand-mac-address=${if cfg.wifi.scanRandMacAddress then "yes" else "no"}
+    wifi.backend=${cfg.wifi.backend}
 
     ${cfg.extraConfig}
   '';
@@ -173,29 +185,18 @@ in {
         '';
       };
 
-      # Ugly hack for using the correct gnome3 packageSet
-      basePackages = mkOption {
-        type = types.attrsOf types.package;
-        default = { inherit (pkgs)
-                            networkmanager modemmanager wpa_supplicant crda
-                            networkmanager-openvpn networkmanager-vpnc
-                            networkmanager-openconnect networkmanager-fortisslvpn
-                            networkmanager-l2tp networkmanager-iodine; };
-        internal = true;
-      };
-
       packages = mkOption {
-        type = types.listOf types.path;
+        type = types.listOf types.package;
         default = [ ];
         description = ''
           Extra packages that provide NetworkManager plugins.
         '';
-        apply = list: (attrValues cfg.basePackages) ++ list;
+        apply = list: basePackages ++ list;
       };
 
       dhcp = mkOption {
         type = types.enum [ "dhclient" "dhcpcd" "internal" ];
-        default = "dhclient";
+        default = "internal";
         description = ''
           Which program (or internal library) should be used for DHCP.
         '';
@@ -232,6 +233,15 @@ in {
       wifi = {
         macAddress = macAddressOpt;
 
+        backend = mkOption {
+          type = types.enum [ "wpa_supplicant" "iwd" ];
+          default = "wpa_supplicant";
+          description = ''
+            Specify the Wi-Fi backend used for the device.
+            Currently supported are <option>wpa_supplicant</option> or <option>iwd</option> (experimental).
+          '';
+        };
+
         powersave = mkOption {
           type = types.nullOr types.bool;
           default = null;
@@ -322,73 +332,35 @@ in {
           so you don't need to to that yourself.
         '';
       };
-
-      dynamicHosts = {
-        enable = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Enabling this option requires the
-            <option>networking.networkmanager.dns</option> option to be
-            set to <literal>dnsmasq</literal>. If enabled, the directories
-            defined by the
-            <option>networking.networkmanager.dynamicHosts.hostsDirs</option>
-            option will be set up when the service starts. The dnsmasq instance
-            managed by NetworkManager will then watch those directories for
-            hosts files (see the <literal>--hostsdir</literal> option of
-            dnsmasq). This way a non-privileged user can add or override DNS
-            entries on the local system (depending on what hosts directories
-            that are configured)..
-          '';
-        };
-        hostsDirs = mkOption {
-          type = with types; attrsOf (submodule {
-            options = {
-              user = mkOption {
-                type = types.str;
-                default = "root";
-                description = ''
-                  The user that will own the hosts directory.
-                '';
-              };
-              group = mkOption {
-                type = types.str;
-                default = "root";
-                description = ''
-                  The group that will own the hosts directory.
-                '';
-              };
-            };
-          });
-          default = {};
-          description = ''
-            Defines a set of directories (relative to
-            <literal>/run/NetworkManager/hostdirs</literal>) that dnsmasq will
-            watch for hosts files.
-          '';
-        };
-      };
     };
   };
 
+  imports = [
+    (mkRemovedOptionModule ["networking" "networkmanager" "dynamicHosts"] ''
+      This option was removed because allowing (multiple) regular users to
+      override host entries affecting the whole system opens up a huge attack
+      vector. There seem to be very rare cases where this might be useful.
+      Consider setting system-wide host entries using networking.hosts, provide
+      them via the DNS server in your network, or use environment.etc
+      to add a file into /etc/NetworkManager/dnsmasq.d reconfiguring hostsdir.
+    '')
+  ];
+
 
   ###### implementation
 
   config = mkIf cfg.enable {
 
     assertions = [
-      { assertion = config.networking.wireless.enable == false;
-        message = "You can not use networking.networkmanager with networking.wireless";
-      }
-      { assertion = !dynamicHostsEnabled || (dynamicHostsEnabled && cfg.dns == "dnsmasq");
+      { assertion = config.networking.wireless.enable == true -> cfg.unmanaged != [];
         message = ''
-          To use networking.networkmanager.dynamicHosts you also need to set
-          networking.networkmanager.dns = "dnsmasq"
+          You can not use networking.networkmanager with networking.wireless.
+          Except if you mark some interfaces as <literal>unmanaged</literal> by NetworkManager.
         '';
       }
     ];
 
-    environment.etc = with cfg.basePackages; [
+    environment.etc = with pkgs; [
       { source = configFile;
         target = "NetworkManager/NetworkManager.conf";
       }
@@ -419,12 +391,6 @@ in {
         target = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}";
         mode = "0544";
       }) cfg.dispatcherScripts
-      ++ optional dynamicHostsEnabled
-           { target = "NetworkManager/dnsmasq.d/dyndns.conf";
-             text = concatMapStrings (n: ''
-               hostsdir=/run/NetworkManager/hostsdirs/${n}
-             '') (attrNames cfg.dynamicHosts.hostsDirs);
-           }
       ++ optional cfg.enableStrongSwan
            { source = "${pkgs.networkmanager_strongswan}/lib/NetworkManager/VPN/nm-strongswan-service.name";
              target = "NetworkManager/VPN/nm-strongswan-service.name";
@@ -453,35 +419,32 @@ in {
 
     systemd.packages = cfg.packages;
 
+    systemd.tmpfiles.rules = [
+      "d /etc/NetworkManager/system-connections 0700 root root -"
+      "d /etc/ipsec.d 0700 root root -"
+      "d /var/lib/NetworkManager-fortisslvpn 0700 root root -"
+
+      "d /var/lib/dhclient 0755 root root -"
+      "d /var/lib/misc 0755 root root -" # for dnsmasq.leases
+    ];
+
     systemd.services.NetworkManager = {
       wantedBy = [ "network.target" ];
       restartTriggers = [ configFile ];
 
-      preStart = ''
-        mkdir -m 700 -p /etc/NetworkManager/system-connections
-        mkdir -m 700 -p /etc/ipsec.d
-        mkdir -m 755 -p ${stateDirs}
-      '';
+      aliases = [ "dbus-org.freedesktop.NetworkManager.service" ];
+
+      serviceConfig = {
+        StateDirectory = "NetworkManager";
+        StateDirectoryMode = 755; # not sure if this really needs to be 755
+      };
     };
 
     systemd.services.NetworkManager-wait-online = {
       wantedBy = [ "network-online.target" ];
     };
 
-    systemd.services.nm-setup-hostsdirs = mkIf dynamicHostsEnabled {
-      wantedBy = [ "NetworkManager.service" ];
-      before = [ "NetworkManager.service" ];
-      partOf = [ "NetworkManager.service" ];
-      script = concatStrings (mapAttrsToList (n: d: ''
-        mkdir -p "/run/NetworkManager/hostsdirs/${n}"
-        chown "${d.user}:${d.group}" "/run/NetworkManager/hostsdirs/${n}"
-        chmod 0775 "/run/NetworkManager/hostsdirs/${n}"
-      '') cfg.dynamicHosts.hostsDirs);
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
-      };
-    };
+    systemd.services.ModemManager.aliases = [ "dbus-org.freedesktop.ModemManager1.service" ];
 
     systemd.services.NetworkManager-dispatcher = {
       wantedBy = [ "network.target" ];
@@ -489,22 +452,25 @@ in {
 
       # useful binaries for user-specified hooks
       path = [ pkgs.iproute pkgs.utillinux pkgs.coreutils ];
+      aliases = [ "dbus-org.freedesktop.nm-dispatcher.service" ];
     };
 
-    # Turn off NixOS' network management
-    networking = {
+    # Turn off NixOS' network management when networking is managed entirely by NetworkManager
+    networking = (mkIf (!delegateWireless) {
       useDHCP = false;
-      # use mkDefault to trigger the assertion about the conflict above
+      # Use mkDefault to trigger the assertion about the conflict above
       wireless.enable = mkDefault false;
-    };
+    }) // (mkIf cfg.enableStrongSwan {
+      networkmanager.packages = [ pkgs.networkmanager_strongswan ];
+    }) // (mkIf enableIwd {
+      wireless.iwd.enable = true;
+    });
 
     security.polkit.extraConfig = polkitConf;
 
-    networking.networkmanager.packages =
-      mkIf cfg.enableStrongSwan [ pkgs.networkmanager_strongswan ];
-
-    services.dbus.packages =
-      optional cfg.enableStrongSwan pkgs.strongswanNM ++ cfg.packages;
+    services.dbus.packages = cfg.packages
+      ++ optional cfg.enableStrongSwan pkgs.strongswanNM
+      ++ optional (cfg.dns == "dnsmasq") pkgs.dnsmasq;
 
     services.udev.packages = cfg.packages;
   };
diff --git a/nixos/modules/services/networking/nghttpx/default.nix b/nixos/modules/services/networking/nghttpx/default.nix
index d6e1906e3881..881a2670f5db 100644
--- a/nixos/modules/services/networking/nghttpx/default.nix
+++ b/nixos/modules/services/networking/nghttpx/default.nix
@@ -96,6 +96,7 @@ in
     users.groups.nghttpx = { };
     users.users.nghttpx = {
       group = config.users.groups.nghttpx.name;
+      isSystemUser = true;
     };
       
 
diff --git a/nixos/modules/services/networking/chrony.nix b/nixos/modules/services/networking/ntp/chrony.nix
index 77f702577000..c74476c7a155 100644
--- a/nixos/modules/services/networking/chrony.nix
+++ b/nixos/modules/services/networking/ntp/chrony.nix
@@ -9,11 +9,11 @@ let
   keyFile = "${stateDir}/chrony.keys";
 
   configFile = pkgs.writeText "chrony.conf" ''
-    ${concatMapStringsSep "\n" (server: "server " + server) cfg.servers}
+    ${concatMapStringsSep "\n" (server: "server " + server + " iburst") cfg.servers}
 
     ${optionalString
       (cfg.initstepslew.enabled && (cfg.servers != []))
-      "initstepslew ${toString cfg.initstepslew.threshold} ${concatStringsSep " " cfg.initstepslew.servers}"
+      "initstepslew ${toString cfg.initstepslew.threshold} ${concatStringsSep " " cfg.servers}"
     }
 
     driftfile ${stateDir}/chrony.drift
@@ -24,7 +24,7 @@ let
     ${cfg.extraConfig}
   '';
 
-  chronyFlags = "-m -u chrony -f ${configFile} ${toString cfg.extraFlags}";
+  chronyFlags = "-n -m -u chrony -f ${configFile} ${toString cfg.extraFlags}";
 in
 {
   options = {
@@ -48,7 +48,6 @@ in
         default = {
           enabled = true;
           threshold = 1000; # by default, same threshold as 'ntpd -g' (1000s)
-          servers = cfg.servers;
         };
         description = ''
           Allow chronyd to make a rapid measurement of the system clock error at
@@ -76,6 +75,8 @@ in
   };
 
   config = mkIf cfg.enable {
+    meta.maintainers = with lib.maintainers; [ thoughtpolice ];
+
     environment.systemPackages = [ pkgs.chrony ];
 
     users.groups = singleton
@@ -115,7 +116,7 @@ in
 
         unitConfig.ConditionCapability = "CAP_SYS_TIME";
         serviceConfig =
-          { Type = "forking";
+          { Type = "simple";
             ExecStart = "${pkgs.chrony}/bin/chronyd ${chronyFlags}";
 
             ProtectHome = "yes";
diff --git a/nixos/modules/services/networking/ntpd.nix b/nixos/modules/services/networking/ntp/ntpd.nix
index 588d1c6edb07..1197c84f0459 100644
--- a/nixos/modules/services/networking/ntpd.nix
+++ b/nixos/modules/services/networking/ntp/ntpd.nix
@@ -96,6 +96,7 @@ in
   ###### implementation
 
   config = mkIf config.services.ntp.enable {
+    meta.maintainers = with lib.maintainers; [ thoughtpolice ];
 
     # Make tools such as ntpq available in the system path.
     environment.systemPackages = [ pkgs.ntp ];
diff --git a/nixos/modules/services/networking/openntpd.nix b/nixos/modules/services/networking/ntp/openntpd.nix
index f3920aa80646..471d15b1687b 100644
--- a/nixos/modules/services/networking/openntpd.nix
+++ b/nixos/modules/services/networking/ntp/openntpd.nix
@@ -52,6 +52,7 @@ in
   ###### implementation
 
   config = mkIf cfg.enable {
+    meta.maintainers = with lib.maintainers; [ thoughtpolice ];
     services.timesyncd.enable = mkForce false;
 
     # Add ntpctl to the environment for status checking
diff --git a/nixos/modules/services/networking/owamp.nix b/nixos/modules/services/networking/owamp.nix
index 821a0258f4be..dbb2e3b4c409 100644
--- a/nixos/modules/services/networking/owamp.nix
+++ b/nixos/modules/services/networking/owamp.nix
@@ -21,6 +21,7 @@ in
       name = "owamp";
       group = "owamp";
       description = "Owamp daemon";
+      isSystemUser = true;
     };
 
     users.groups = singleton {
diff --git a/nixos/modules/services/networking/pppd.nix b/nixos/modules/services/networking/pppd.nix
new file mode 100644
index 000000000000..e96c27bd84b4
--- /dev/null
+++ b/nixos/modules/services/networking/pppd.nix
@@ -0,0 +1,134 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.pppd;
+in
+{
+  meta = {
+    maintainers = with maintainers; [ danderson ];
+  };
+
+  options = {
+    services.pppd = {
+      enable = mkEnableOption "pppd";
+
+      package = mkOption {
+        default = pkgs.ppp;
+        defaultText = "pkgs.ppp";
+        type = types.package;
+        description = "pppd package to use.";
+      };
+
+      peers = mkOption {
+        default = {};
+        description = "pppd peers.";
+        type = types.attrsOf (types.submodule (
+          { name, ... }:
+          {
+            options = {
+              name = mkOption {
+                type = types.str;
+                default = name;
+                example = "dialup";
+                description = "Name of the PPP peer.";
+              };
+
+              enable = mkOption {
+                type = types.bool;
+                default = true;
+                example = false;
+                description = "Whether to enable this PPP peer.";
+              };
+
+              autostart = mkOption {
+                type = types.bool;
+                default = true;
+                example = false;
+                description = "Whether the PPP session is automatically started at boot time.";
+              };
+
+              config = mkOption {
+                type = types.lines;
+                default = "";
+                description = "pppd configuration for this peer, see the pppd(8) man page.";
+              };
+            };
+          }));
+      };
+    };
+  };
+
+  config = let
+    enabledConfigs = filter (f: f.enable) (attrValues cfg.peers);
+
+    mkEtc = peerCfg: {
+      "ppp/peers/${peerCfg.name}".text = peerCfg.config;
+    };
+
+    mkSystemd = peerCfg: {
+      "pppd-${peerCfg.name}" = {
+        restartTriggers = [ config.environment.etc."ppp/peers/${peerCfg.name}".source ];
+        before = [ "network.target" ];
+        wants = [ "network.target" ];
+        after = [ "network-pre.target" ];
+        environment = {
+          # pppd likes to write directly into /var/run. This is rude
+          # on a modern system, so we use libredirect to transparently
+          # move those files into /run/pppd.
+          LD_PRELOAD = "${pkgs.libredirect}/lib/libredirect.so";
+          NIX_REDIRECTS = "/var/run=/run/pppd";
+        };
+        serviceConfig = {
+          ExecStart = "${getBin cfg.package}/sbin/pppd call ${peerCfg.name} nodetach nolog";
+          Restart = "always";
+          RestartSec = 5;
+
+          AmbientCapabilities = "CAP_SYS_TTY_CONFIG CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_ADMIN";
+          CapabilityBoundingSet = "CAP_SYS_TTY_CONFIG CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_ADMIN";
+          KeyringMode = "private";
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
+          PrivateMounts = true;
+          PrivateTmp = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelModules = true;
+          # pppd can be configured to tweak kernel settings.
+          ProtectKernelTunables = false;
+          ProtectSystem = "strict";
+          RemoveIPC = true;
+          RestrictAddressFamilies = "AF_PACKET AF_UNIX AF_PPPOX AF_ATMPVC AF_ATMSVC AF_INET AF_INET6 AF_IPX";
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          SecureBits = "no-setuid-fixup-locked noroot-locked";
+          SystemCallFilter = "@system-service";
+          SystemCallArchitectures = "native";
+
+          # All pppd instances on a system must share a runtime
+          # directory in order for PPP multilink to work correctly. So
+          # we give all instances the same /run/pppd directory to store
+          # things in.
+          #
+          # For the same reason, we can't set PrivateUsers=true, because
+          # all instances need to run as the same user to access the
+          # multilink database.
+          RuntimeDirectory = "pppd";
+          RuntimeDirectoryPreserve = true;
+        };
+        wantedBy = mkIf peerCfg.autostart [ "multi-user.target" ];
+      };
+    };
+
+    etcFiles = map mkEtc enabledConfigs;
+    systemdConfigs = map mkSystemd enabledConfigs;
+
+  in mkIf cfg.enable {
+    environment.etc = mkMerge etcFiles;
+    systemd.services = mkMerge systemdConfigs;
+  };
+}
diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix
index 1ae063aa6bb5..7a503e711665 100644
--- a/nixos/modules/services/networking/prosody.nix
+++ b/nixos/modules/services/networking/prosody.nix
@@ -465,7 +465,7 @@ in
 
       modules_enabled = {
 
-        ${ lib.concatStringsSep "\n\ \ " (lib.mapAttrsToList
+        ${ lib.concatStringsSep "\n  " (lib.mapAttrsToList
           (name: val: optionalString val "${toLua name};")
         cfg.modules) }
         ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.package.communityModules)}
diff --git a/nixos/modules/services/networking/smokeping.nix b/nixos/modules/services/networking/smokeping.nix
index d4d0594a9cdd..b48b0b3a9d6b 100644
--- a/nixos/modules/services/networking/smokeping.nix
+++ b/nixos/modules/services/networking/smokeping.nix
@@ -299,7 +299,8 @@ in
         mkdir -m 0755 -p ${smokepingHome}/cache ${smokepingHome}/data
         rm -f ${smokepingHome}/cropper
         ln -s ${cfg.package}/htdocs/cropper ${smokepingHome}/cropper
-        cp ${cgiHome} ${smokepingHome}/smokeping.fcgi
+        rm -f ${smokepingHome}/smokeping.fcgi
+        ln -s ${cgiHome} ${smokepingHome}/smokeping.fcgi
         ${cfg.package}/bin/smokeping --check --config=${configPath}
         ${cfg.package}/bin/smokeping --static --config=${configPath}
       '';
@@ -314,5 +315,7 @@ in
       serviceConfig.Restart = "always";
     };
   };
+
+  meta.maintainers = with lib.maintainers; [ erictapen ];
 }
 
diff --git a/nixos/modules/services/networking/stunnel.nix b/nixos/modules/services/networking/stunnel.nix
index cbc899f2b4d7..ab51bba2f6ac 100644
--- a/nixos/modules/services/networking/stunnel.nix
+++ b/nixos/modules/services/networking/stunnel.nix
@@ -57,7 +57,13 @@ let
       };
 
       CAPath = mkOption {
-        type = types.path;
+        type = types.nullOr types.path;
+        default = null;
+        description = "Path to a directory containing certificates to validate against.";
+      };
+
+      CAFile = mkOption {
+        type = types.nullOr types.path;
         default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
         description = "Path to a file containing certificates to validate against.";
       };
@@ -196,6 +202,7 @@ in
                verifyChain = ${yesNo v.verifyChain}
                verifyPeer = ${yesNo v.verifyPeer}
                ${optionalString (v.CAPath != null) "CApath = ${v.CAPath}"}
+               ${optionalString (v.CAFile != null) "CAFile = ${v.CAFile}"}
                ${optionalString (v.verifyHostname != null) "checkHost = ${v.verifyHostname}"}
                OCSPaia = yes
 
@@ -216,6 +223,12 @@ in
       };
     };
 
+    meta.maintainers = with maintainers; [
+      # Server side
+      lschuermann
+      # Client side
+      das_j
+    ];
   };
 
 }
diff --git a/nixos/modules/services/networking/thelounge.nix b/nixos/modules/services/networking/thelounge.nix
index b1d23372955e..875d8f661697 100644
--- a/nixos/modules/services/networking/thelounge.nix
+++ b/nixos/modules/services/networking/thelounge.nix
@@ -56,6 +56,7 @@ in {
     users.users.thelounge = {
       description = "thelounge service user";
       group = "thelounge";
+      isSystemUser = true;
     };
     users.groups.thelounge = {};
     systemd.services.thelounge = {
diff --git a/nixos/modules/services/networking/tinydns.nix b/nixos/modules/services/networking/tinydns.nix
index 7d5db71601ef..7b2c464ab46b 100644
--- a/nixos/modules/services/networking/tinydns.nix
+++ b/nixos/modules/services/networking/tinydns.nix
@@ -32,7 +32,7 @@ with lib;
   config = mkIf config.services.tinydns.enable {
     environment.systemPackages = [ pkgs.djbdns ];
 
-    users.users.tinydns = {};
+    users.users.tinydns.isSystemUser = true;
 
     systemd.services.tinydns = {
       description = "djbdns tinydns server";
diff --git a/nixos/modules/services/networking/trickster.nix b/nixos/modules/services/networking/trickster.nix
new file mode 100644
index 000000000000..8760dd5a9382
--- /dev/null
+++ b/nixos/modules/services/networking/trickster.nix
@@ -0,0 +1,112 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.trickster;
+in
+{
+
+  options = {
+    services.trickster = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable Trickster.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.trickster;
+        defaultText = "pkgs.trickster";
+        description = ''
+          Package that should be used for trickster.
+        '';
+      };
+
+      configFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          Path to configuration file.
+        '';
+      };
+
+      instance-id = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Instance ID for when running multiple processes (default null).
+        '';
+      };
+
+      log-level = mkOption {
+        type = types.str;
+        default = "info";
+        description = ''
+          Level of Logging to use (debug, info, warn, error) (default "info").
+        '';
+      };
+
+      metrics-port = mkOption {
+        type = types.port;
+        default = 8082;
+        description = ''
+          Port that the /metrics endpoint will listen on.
+        '';
+      };
+
+      origin = mkOption {
+        type = types.str;
+        default = "http://prometheus:9090";
+        description = ''
+          URL to the Prometheus Origin. Enter it like you would in grafana, e.g., http://prometheus:9090 (default http://prometheus:9090).
+        '';
+      };
+
+      profiler-port = mkOption {
+        type = types.nullOr types.port;
+        default = null;
+        description = ''
+          Port that the /debug/pprof endpoint will listen on.
+        '';
+      };
+
+      proxy-port = mkOption {
+        type = types.port;
+        default = 9090;
+        description = ''
+          Port that the Proxy server will listen on.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.trickster = {
+      description = "Dashboard Accelerator for Prometheus";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        DynamicUser = true;
+        ExecStart = ''
+          ${cfg.package}/bin/trickster \
+          -log-level ${cfg.log-level} \
+          -metrics-port ${toString cfg.metrics-port} \
+          -origin ${cfg.origin} \
+          -proxy-port ${toString cfg.proxy-port} \
+          ${optionalString (cfg.configFile != null) "-config ${cfg.configFile}"} \
+          ${optionalString (cfg.profiler-port != null) "-profiler-port ${cfg.profiler-port}"} \
+          ${optionalString (cfg.instance-id != null) "-instance-id ${cfg.instance-id}"}
+        '';
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        Restart = "always";
+      };
+    };
+
+  };  
+}
+
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix
index 63e59e7c8fac..294c0d70edea 100644
--- a/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixos/modules/services/networking/wpa_supplicant.nix
@@ -103,6 +103,13 @@ in {
               description = ''
                 Set this to <literal>true</literal> if the SSID of the network is hidden.
               '';
+              example = literalExample ''
+                { echelon = {
+                    hidden = true;
+                    psk = "abcdefgh";
+                  };
+                }
+              '';
             };
 
             priority = mkOption {
@@ -146,10 +153,13 @@ in {
         '';
         default = {};
         example = literalExample ''
-          { echelon = {
+          { echelon = {                   # SSID with no spaces or special characters
               psk = "abcdefgh";
             };
-            "free.wifi" = {};
+            "echelon's AP" = {            # SSID with spaces and/or special characters
+               psk = "ijklmnop";
+            };
+            "free.wifi" = {};             # Public wireless network
           }
         '';
       };
diff --git a/nixos/modules/services/networking/yggdrasil.nix b/nixos/modules/services/networking/yggdrasil.nix
new file mode 100644
index 000000000000..0da50ccc344b
--- /dev/null
+++ b/nixos/modules/services/networking/yggdrasil.nix
@@ -0,0 +1,193 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.yggdrasil;
+  configProvided = (cfg.config != {});
+  configAsFile = (if configProvided then
+                   toString (pkgs.writeTextFile {
+                     name = "yggdrasil-conf";
+                     text = builtins.toJSON cfg.config;
+                   })
+                   else null);
+  configFileProvided = (cfg.configFile != null);
+  generateConfig = (
+    if configProvided && configFileProvided then
+      "${pkgs.jq}/bin/jq -s add /run/yggdrasil/configFile.json ${configAsFile}"
+    else if configProvided then
+      "cat ${configAsFile}"
+    else if configFileProvided then
+      "cat /run/yggdrasil/configFile.json"
+    else
+      "${cfg.package}/bin/yggdrasil -genconf"
+  );
+
+in {
+  options = with types; {
+    services.yggdrasil = {
+      enable = mkEnableOption "the yggdrasil system service";
+
+      configFile = mkOption {
+        type =  nullOr str;
+        default = null;
+        example = "/run/keys/yggdrasil.conf";
+        description = ''
+          A file which contains JSON configuration for yggdrasil.
+
+          You do not have to supply a complete configuration, as
+          yggdrasil will use default values for anything which is
+          omitted.  If the encryption and signing keys are omitted,
+          yggdrasil will generate new ones each time the service is
+          started, resulting in a random IPv6 address on the yggdrasil
+          network each time.
+
+          If both this option and <option>config</option> are
+          supplied, they will be combined, with values from
+          <option>config</option> taking precedence.
+
+          You can use the command <code>nix-shell -p yggdrasil --run
+          "yggdrasil -genconf -json"</code> to generate a default
+          JSON configuration.
+        '';
+      };
+
+      config = mkOption {
+        type = attrs;
+        default = {};
+        example = {
+          Peers = [
+            "tcp://aa.bb.cc.dd:eeeee"
+            "tcp://[aaaa:bbbb:cccc:dddd::eeee]:fffff"
+          ];
+          Listen = [
+            "tcp://0.0.0.0:xxxxx"
+          ];
+        };
+        description = ''
+          Configuration for yggdrasil, as a Nix attribute set.
+
+          Warning: this is stored in the WORLD-READABLE Nix store!
+          Therefore, it is not appropriate for private keys.  If you
+          do not specify the keys, yggdrasil will generate a new set
+          each time the service is started, creating a random IPv6
+          address on the yggdrasil network each time.
+
+          If you wish to specify the keys, use
+          <option>configFile</option>.  If both
+          <option>configFile</option> and <option>config</option> are
+          supplied, they will be combined, with values from
+          <option>config</option> taking precedence.
+
+          You can use the command <code>nix-shell -p yggdrasil --run
+          "yggdrasil -genconf"</code> to generate default
+          configuration values with documentation.
+        '';
+      };
+
+      openMulticastPort = mkOption {
+        type = bool;
+        default = false;
+        description = ''
+          Whether to open the UDP port used for multicast peer
+          discovery. The NixOS firewall blocks link-local
+          communication, so in order to make local peering work you
+          will also need to set <code>LinkLocalTCPPort</code> in your
+          yggdrasil configuration (<option>config</option> or
+          <option>configFile</option>) to a port number other than 0,
+          and then add that port to
+          <option>networking.firewall.allowedTCPPorts</option>.
+        '';
+      };
+
+      denyDhcpcdInterfaces = mkOption {
+        type = listOf str;
+        default = [];
+        example = [ "tap*" ];
+        description = ''
+          Disable the DHCP client for any interface whose name matches
+          any of the shell glob patterns in this list.  Use this
+          option to prevent the DHCP client from broadcasting requests
+          on the yggdrasil network.  It is only necessary to do so
+          when yggdrasil is running in TAP mode, because TUN
+          interfaces do not support broadcasting.
+        '';
+      };
+
+      package = mkOption {
+        type = package;
+        default = pkgs.yggdrasil;
+        defaultText = "pkgs.yggdrasil";
+        description = "Yggdrasil package to use.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      { assertion = config.networking.enableIPv6;
+        message = "networking.enableIPv6 must be true for yggdrasil to work";
+      }
+    ];
+
+    environment.etc."yggdrasil.conf" = {
+      enable = true;
+      mode = "symlink";
+      source = "/run/yggdrasil/yggdrasil.conf";
+    };
+
+    systemd.services.yggdrasil = {
+      description = "Yggdrasil Network Service";
+      path = [ cfg.package ] ++ optional (configProvided && configFileProvided) pkgs.jq;
+      bindsTo = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        ${generateConfig} | yggdrasil -normaliseconf -useconf > /run/yggdrasil/yggdrasil.conf
+      '';
+
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/yggdrasil -useconffile /etc/yggdrasil.conf";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        Restart = "always";
+
+        RuntimeDirectory = "yggdrasil";
+        RuntimeDirectoryMode = "0700";
+        BindReadOnlyPaths = mkIf configFileProvided
+          [ "${cfg.configFile}:/run/yggdrasil/configFile.json" ];
+
+        # TODO: as of yggdrasil 0.3.8 and systemd 243, yggdrasil fails
+        # to set up the network adapter when DynamicUser is set.  See
+        # github.com/yggdrasil-network/yggdrasil-go/issues/557.  The
+        # following options are implied by DynamicUser according to
+        # the systemd.exec documentation, and can be removed if the
+        # upstream issue is fixed and DynamicUser is set to true:
+        PrivateTmp = true;
+        RemoveIPC = true;
+        NoNewPrivileges = true;
+        ProtectSystem = "strict";
+        RestrictSUIDSGID = true;
+        # End of list of options implied by DynamicUser.
+
+        AmbientCapabilities = "CAP_NET_ADMIN";
+        CapabilityBoundingSet = "CAP_NET_ADMIN";
+        MemoryDenyWriteExecute = true;
+        ProtectControlGroups = true;
+        ProtectHome = "tmpfs";
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @resources";
+      };
+    };
+
+    networking.dhcpcd.denyInterfaces = cfg.denyDhcpcdInterfaces;
+    networking.firewall.allowedUDPPorts = mkIf cfg.openMulticastPort [ 9001 ];
+
+    # Make yggdrasilctl available on the command line.
+    environment.systemPackages = [ cfg.package ];
+  };
+  meta.maintainers = with lib.maintainers; [ gazally ];
+}
diff --git a/nixos/modules/services/networking/zeronet.nix b/nixos/modules/services/networking/zeronet.nix
index f4988a902685..f354a9d42c79 100644
--- a/nixos/modules/services/networking/zeronet.nix
+++ b/nixos/modules/services/networking/zeronet.nix
@@ -1,44 +1,39 @@
 { config, lib, pkgs, ... }:
 
 let
+  inherit (lib) generators literalExample mkEnableOption mkIf mkOption recursiveUpdate types;
   cfg = config.services.zeronet;
-
-  zConfFile = pkgs.writeTextFile {
-    name = "zeronet.conf";
-
-    text = ''
-      [global]
-      data_dir = ${cfg.dataDir}
-      log_dir = ${cfg.logDir}
-    '' + lib.optionalString (cfg.port != null) ''
-      ui_port = ${toString cfg.port}
-    '' + lib.optionalString (cfg.fileserverPort != null) ''
-      fileserver_port = ${toString cfg.fileserverPort}
-    '' + lib.optionalString (cfg.torAlways) ''
-      tor = always
-    '' + cfg.extraConfig;
+  dataDir = "/var/lib/zeronet";
+  configFile = pkgs.writeText "zeronet.conf" (generators.toINI {} (recursiveUpdate defaultSettings cfg.settings));
+
+  defaultSettings = {
+    global = {
+      data_dir = dataDir;
+      log_dir = dataDir;
+      ui_port = cfg.port;
+      fileserver_port = cfg.fileserverPort;
+      tor = if !cfg.tor then "disable" else if cfg.torAlways then "always" else "enable";
+    };
   };
 in with lib; {
   options.services.zeronet = {
     enable = mkEnableOption "zeronet";
 
-    dataDir = mkOption {
-      type = types.path;
-      default = "/var/lib/zeronet";
-      example = "/home/okina/zeronet";
-      description = "Path to the zeronet data directory.";
-    };
+    settings = mkOption {
+      type = with types; attrsOf (oneOf [ str int bool (listOf str) ]);
+      default = {};
+      example = literalExample "global.tor = enable;";
 
-    logDir = mkOption {
-      type = types.path;
-      default = "/var/log/zeronet";
-      example = "/home/okina/zeronet/log";
-      description = "Path to the zeronet log directory.";
+      description = ''
+        <filename>zeronet.conf</filename> configuration. Refer to
+        <link xlink:href="https://zeronet.readthedocs.io/en/latest/faq/#is-it-possible-to-use-a-configuration-file"/>
+        for details on supported values;
+      '';
     };
 
     port = mkOption {
-      type = types.nullOr types.int;
-      default = null;
+      type = types.int;
+      default = 43110;
       example = 43110;
       description = "Optional zeronet web UI port.";
     };
@@ -63,22 +58,13 @@ in with lib; {
       default = false;
       description = "Use TOR for all zeronet traffic.";
     };
-
-    extraConfig = mkOption {
-      type = types.lines;
-      default = "";
-
-      description = ''
-        Extra configuration. Contents will be added verbatim to the
-        configuration file at the end.
-      '';
-    };
   };
 
   config = mkIf cfg.enable {
     services.tor = mkIf cfg.tor {
       enable = true;
       controlPort = 9051;
+
       extraConfig = ''
         CacheDirectoryGroupReadable 1
         CookieAuthentication 1
@@ -86,37 +72,25 @@ in with lib; {
       '';
     };
 
-    systemd.tmpfiles.rules = [
-      "d '${cfg.dataDir}' 750 zeronet zeronet - -"
-      "d '${cfg.logDir}' 750 zeronet zeronet - -"
-    ];
-
     systemd.services.zeronet = {
       description = "zeronet";
       after = [ "network.target" (optionalString cfg.tor "tor.service") ];
       wantedBy = [ "multi-user.target" ];
 
       serviceConfig = {
-        PrivateTmp = "yes";
         User = "zeronet";
-        Group = "zeronet";
-        ExecStart = "${pkgs.zeronet}/bin/zeronet --config_file ${zConfFile}";
-      };
-    };
-
-    users = {
-      groups.zeronet.gid = config.ids.gids.zeronet;
-
-      users.zeronet = {
-        description = "zeronet service user";
-        home = cfg.dataDir;
-        createHome = true;
-        group = "zeronet";
-        extraGroups = mkIf cfg.tor [ "tor" ];
-        uid = config.ids.uids.zeronet;
+        DynamicUser = true;
+        StateDirectory = "zeronet";
+        SupplementaryGroups = mkIf cfg.tor [ "tor" ];
+        ExecStart = "${pkgs.zeronet}/bin/zeronet --config_file ${configFile}";
       };
     };
   };
 
+  imports = [
+    (mkRemovedOptionModule [ "services" "zeronet" "dataDir" ] "Zeronet will store data by default in /var/lib/zeronet")
+    (mkRemovedOptionModule [ "services" "zeronet" "logDir" ] "Zeronet will log by default in /var/lib/zeronet")
+  ];
+
   meta.maintainers = with maintainers; [ chiiruno ];
 }
diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix
index 3fcae611dc79..1071c05d514e 100644
--- a/nixos/modules/services/printing/cupsd.nix
+++ b/nixos/modules/services/printing/cupsd.nix
@@ -31,7 +31,7 @@ let
   # part of CUPS itself, e.g. the SMB backend is part of Samba.  Since
   # we can't update ${cups.out}/lib/cups itself, we create a symlink tree
   # here and add the additional programs.  The ServerBin directive in
-  # cupsd.conf tells cupsd to use this tree.
+  # cups-files.conf tells cupsd to use this tree.
   bindir = pkgs.buildEnv {
     name = "cups-progs";
     paths =
diff --git a/nixos/modules/services/scheduling/marathon.nix b/nixos/modules/services/scheduling/marathon.nix
index 0961a67770e1..2e0d20c64b23 100644
--- a/nixos/modules/services/scheduling/marathon.nix
+++ b/nixos/modules/services/scheduling/marathon.nix
@@ -93,6 +93,6 @@ in {
       };
     };
 
-    users.users.${cfg.user} = { };
+    users.users.${cfg.user}.isSystemUser = true;
   };
 }
diff --git a/nixos/modules/services/security/bitwarden_rs/default.nix b/nixos/modules/services/security/bitwarden_rs/default.nix
index 80fd65891ff8..d1817db07555 100644
--- a/nixos/modules/services/security/bitwarden_rs/default.nix
+++ b/nixos/modules/services/security/bitwarden_rs/default.nix
@@ -74,7 +74,10 @@ in {
       webVaultEnabled = mkDefault true;
     };
 
-    users.users.bitwarden_rs = { inherit group; };
+    users.users.bitwarden_rs = {
+      inherit group;
+      isSystemUser = true;
+    };
     users.groups.bitwarden_rs = { };
 
     systemd.services.bitwarden_rs = {
diff --git a/nixos/modules/services/security/fprintd.nix b/nixos/modules/services/security/fprintd.nix
index 5662ebc61d20..8ece1ca19013 100644
--- a/nixos/modules/services/security/fprintd.nix
+++ b/nixos/modules/services/security/fprintd.nix
@@ -50,13 +50,6 @@ in
 
     systemd.packages = [ cfg.package ];
 
-
-    # The upstream unit does not use StateDirectory, and will
-    # fail if the directory it needs is not present. Should be
-    # fixed when https://gitlab.freedesktop.org/libfprint/fprintd/merge_requests/5
-    # is merged.
-    systemd.services.fprintd.serviceConfig.StateDirectory = "fprint";
-
   };
 
 }
diff --git a/nixos/modules/services/security/oauth2_proxy.nix b/nixos/modules/services/security/oauth2_proxy.nix
index bb03f7fc9e43..2abb9ec32aca 100644
--- a/nixos/modules/services/security/oauth2_proxy.nix
+++ b/nixos/modules/services/security/oauth2_proxy.nix
@@ -546,6 +546,7 @@ in
 
     users.users.oauth2_proxy = {
       description = "OAuth2 Proxy";
+      isSystemUser = true;
     };
 
     systemd.services.oauth2_proxy = {
diff --git a/nixos/modules/services/torrent/magnetico.nix b/nixos/modules/services/torrent/magnetico.nix
index 02fa2ac0750a..a7acdb78b316 100644
--- a/nixos/modules/services/torrent/magnetico.nix
+++ b/nixos/modules/services/torrent/magnetico.nix
@@ -171,6 +171,7 @@ in {
 
     users.users.magnetico = {
       description = "Magnetico daemons user";
+      isSystemUser = true;
     };
 
     systemd.services.magneticod = {
diff --git a/nixos/modules/services/web-apps/codimd.nix b/nixos/modules/services/web-apps/codimd.nix
index 7ae7cd9c52d8..5f56f8ed5a09 100644
--- a/nixos/modules/services/web-apps/codimd.nix
+++ b/nixos/modules/services/web-apps/codimd.nix
@@ -893,6 +893,7 @@ in
       extraGroups = cfg.groups;
       home = cfg.workDir;
       createHome = true;
+      isSystemUser = true;
     };
 
     systemd.services.codimd = {
diff --git a/nixos/modules/services/web-apps/documize.nix b/nixos/modules/services/web-apps/documize.nix
index 37359869cb64..1b90299aa23c 100644
--- a/nixos/modules/services/web-apps/documize.nix
+++ b/nixos/modules/services/web-apps/documize.nix
@@ -14,6 +14,15 @@ in {
   options.services.documize = {
     enable = mkEnableOption "Documize Wiki";
 
+    stateDirectoryName = mkOption {
+      type = types.str;
+      default = "documize";
+      description = ''
+        The name of the directory below <filename>/var/lib/private</filename>
+        where documize runs in and stores, for example, backups.
+      '';
+    };
+
     package = mkOption {
       type = types.package;
       default = pkgs.documize-community;
@@ -132,6 +141,8 @@ in {
         ];
         Restart = "always";
         DynamicUser = "yes";
+        StateDirectory = cfg.stateDirectoryName;
+        WorkingDirectory = "/var/lib/${cfg.stateDirectoryName}";
       };
     };
   };
diff --git a/nixos/modules/services/web-apps/frab.nix b/nixos/modules/services/web-apps/frab.nix
index 7914e5cc0ee1..a9a30b409220 100644
--- a/nixos/modules/services/web-apps/frab.nix
+++ b/nixos/modules/services/web-apps/frab.nix
@@ -177,6 +177,7 @@ in
       { name = cfg.user;
         group = cfg.group;
         home = "${cfg.statePath}";
+        isSystemUser = true;
       }
     ];
 
diff --git a/nixos/modules/services/web-apps/gotify-server.nix b/nixos/modules/services/web-apps/gotify-server.nix
new file mode 100644
index 000000000000..03e01f46a944
--- /dev/null
+++ b/nixos/modules/services/web-apps/gotify-server.nix
@@ -0,0 +1,49 @@
+{ pkgs, lib, config, ... }:
+
+with lib;
+
+let
+  cfg = config.services.gotify;
+in {
+  options = {
+    services.gotify = {
+      enable = mkEnableOption "Gotify webserver";
+
+      port = mkOption {
+        type = types.port;
+        description = ''
+          Port the server listens to.
+        '';
+      };
+
+      stateDirectoryName = mkOption {
+        type = types.str;
+        default = "gotify-server";
+        description = ''
+          The name of the directory below <filename>/var/lib</filename> where
+          gotify stores its runtime data.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.gotify-server = {
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      description = "Simple server for sending and receiving messages";
+
+      environment = {
+        GOTIFY_SERVER_PORT = toString cfg.port;
+      };
+
+      serviceConfig = {
+        WorkingDirectory = "/var/lib/${cfg.stateDirectoryName}";
+        StateDirectory = cfg.stateDirectoryName;
+        Restart = "always";
+        DynamicUser = "yes";
+        ExecStart = "${pkgs.gotify-server}/bin/server";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/limesurvey.nix b/nixos/modules/services/web-apps/limesurvey.nix
index 68b57a9b90dd..bd524524130d 100644
--- a/nixos/modules/services/web-apps/limesurvey.nix
+++ b/nixos/modules/services/web-apps/limesurvey.nix
@@ -277,7 +277,10 @@ in
 
     systemd.services.httpd.after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
 
-    users.users.${user}.group = group;
+    users.users.${user} = {
+      group = group;
+      isSystemUser = true;
+    };
 
   };
 }
diff --git a/nixos/modules/services/web-apps/matomo-doc.xml b/nixos/modules/services/web-apps/matomo-doc.xml
index 8485492c51c7..79cece551d34 100644
--- a/nixos/modules/services/web-apps/matomo-doc.xml
+++ b/nixos/modules/services/web-apps/matomo-doc.xml
@@ -105,7 +105,7 @@ GRANT ALL PRIVILEGES ON matomo.* TO 'matomo'@'localhost';
   <para>
    You can use other web servers by forwarding calls for
    <filename>index.php</filename> and <filename>piwik.php</filename> to the
-   <literal>/run/phpfpm-matomo.sock</literal> fastcgi unix socket. You can use
+   <literal><link linkend="opt-services.phpfpm.pools._name_.socket">services.phpfpm.pools.&lt;name&gt;.socket</link></literal> fastcgi unix socket. You can use
    the nginx configuration in the module code as a reference to what else
    should be configured.
   </para>
diff --git a/nixos/modules/services/web-apps/matomo.nix b/nixos/modules/services/web-apps/matomo.nix
index d9f840408cc8..352cc4c647bc 100644
--- a/nixos/modules/services/web-apps/matomo.nix
+++ b/nixos/modules/services/web-apps/matomo.nix
@@ -2,15 +2,13 @@
 with lib;
 let
   cfg = config.services.matomo;
+  fpm = config.services.phpfpm.pools.${pool};
 
   user = "matomo";
   dataDir = "/var/lib/${user}";
   deprecatedDataDir = "/var/lib/piwik";
 
   pool = user;
-  # it's not possible to use /run/phpfpm/${pool}.sock because /run/phpfpm/ is root:root 0770,
-  # and therefore is not accessible by the web server.
-  phpSocket = "/run/phpfpm-${pool}.sock";
   phpExecutionUnit = "phpfpm-${pool}";
   databaseService = "mysql.service";
 
@@ -50,7 +48,7 @@ in {
         default = null;
         example = "lighttpd";
         description = ''
-          Name of the web server user that forwards requests to the ${phpSocket} fastcgi socket for Matomo if the nginx
+          Name of the web server user that forwards requests to <option>services.phpfpm.pools.&lt;name&gt;.socket</option> the fastcgi socket for Matomo if the nginx
           option is not used. Either this option or the nginx option is mandatory.
           If you want to use another webserver than nginx, you need to set this to that server's user
           and pass fastcgi requests to `index.php`, `matomo.php` and `piwik.php` (legacy name) to this socket.
@@ -71,25 +69,6 @@ in {
         '';
       };
 
-      phpfpmProcessManagerConfig = mkOption {
-        type = types.str;
-        default = ''
-          ; default phpfpm process manager settings
-          pm = dynamic
-          pm.max_children = 75
-          pm.start_servers = 10
-          pm.min_spare_servers = 5
-          pm.max_spare_servers = 20
-          pm.max_requests = 500
-
-          ; log worker's stdout, but this has a performance hit
-          catch_workers_output = yes
-        '';
-        description = ''
-          Settings for phpfpm's process manager. You might need to change this depending on the load for Matomo.
-        '';
-      };
-
       nginx = mkOption {
         type = types.nullOr (types.submodule (
           recursiveUpdate
@@ -105,8 +84,8 @@ in {
         default = null;
         example = {
           serverAliases = [
-            "matomo.$\{config.networking.domain\}"
-            "stats.$\{config.networking.domain\}"
+            "matomo.\${config.networking.domain}"
+            "stats.\${config.networking.domain}"
           ];
           enableACME = false;
         };
@@ -115,7 +94,7 @@ in {
             Either this option or the webServerUser option is mandatory.
             Set this to {} to just enable the virtualHost if you don't need any customization.
             If enabled, then by default, the <option>serverName</option> is
-            <literal>${user}.$\{config.networking.hostName\}.$\{config.networking.domain\}</literal>,
+            <literal>''${user}.''${config.networking.hostName}.''${config.networking.domain}</literal>,
             SSL is active, and certificates are acquired via ACME.
             If this is set to null (the default), no nginx virtualHost will be configured.
         '';
@@ -233,15 +212,24 @@ in {
       else if (cfg.webServerUser != null) then cfg.webServerUser else "";
     in {
       ${pool} = {
-        listen = phpSocket;
-        extraConfig = ''
-          listen.owner = ${socketOwner}
-          listen.group = root
-          listen.mode = 0600
-          user = ${user}
-          env[PIWIK_USER_PATH] = ${dataDir}
-          ${cfg.phpfpmProcessManagerConfig}
+        inherit user;
+        phpOptions = ''
+          error_log = 'stderr'
+          log_errors = on
         '';
+        settings = mapAttrs (name: mkDefault) {
+          "listen.owner" = socketOwner;
+          "listen.group" = "root";
+          "listen.mode" = "0660";
+          "pm" = "dynamic";
+          "pm.max_children" = 75;
+          "pm.start_servers" = 10;
+          "pm.min_spare_servers" = 5;
+          "pm.max_spare_servers" = 20;
+          "pm.max_requests" = 500;
+          "catch_workers_output" = true;
+        };
+        phpEnv.PIWIK_USER_PATH = dataDir;
       };
     };
 
@@ -264,18 +252,18 @@ in {
         };
         # allow index.php for webinterface
         locations."= /index.php".extraConfig = ''
-          fastcgi_pass unix:${phpSocket};
+          fastcgi_pass unix:${fpm.socket};
         '';
         # allow matomo.php for tracking
         locations."= /matomo.php".extraConfig = ''
-          fastcgi_pass unix:${phpSocket};
+          fastcgi_pass unix:${fpm.socket};
         '';
         # allow piwik.php for tracking (deprecated name)
         locations."= /piwik.php".extraConfig = ''
-          fastcgi_pass unix:${phpSocket};
+          fastcgi_pass unix:${fpm.socket};
         '';
         # Any other attempt to access any php files is forbidden
-        locations."~* ^.+\.php$".extraConfig = ''
+        locations."~* ^.+\\.php$".extraConfig = ''
           return 403;
         '';
         # Disallow access to unneeded directories
@@ -284,7 +272,7 @@ in {
           return 403;
         '';
         # Disallow access to several helper files
-        locations."~* \.(?:bat|git|ini|sh|txt|tpl|xml|md)$".extraConfig = ''
+        locations."~* \\.(?:bat|git|ini|sh|txt|tpl|xml|md)$".extraConfig = ''
           return 403;
         '';
         # No crawling of this site for bots that obey robots.txt - no useful information here.
diff --git a/nixos/modules/services/web-apps/mediawiki.nix b/nixos/modules/services/web-apps/mediawiki.nix
index ec2568bf952d..43edc04e1a49 100644
--- a/nixos/modules/services/web-apps/mediawiki.nix
+++ b/nixos/modules/services/web-apps/mediawiki.nix
@@ -461,7 +461,10 @@ in
 
     systemd.services.httpd.after = optional (cfg.database.createLocally && cfg.database.type == "mysql") "mysql.service";
 
-    users.users.${user}.group = group;
+    users.users.${user} = {
+      group = group;
+      isSystemUser = true;
+    };
 
     environment.systemPackages = [ mediawikiScripts ];
   };
diff --git a/nixos/modules/services/web-apps/moodle.nix b/nixos/modules/services/web-apps/moodle.nix
index 7f71b86a6fe6..ac59f9e0012a 100644
--- a/nixos/modules/services/web-apps/moodle.nix
+++ b/nixos/modules/services/web-apps/moodle.nix
@@ -45,6 +45,8 @@ let
   $CFG->aspellpath = '${pkgs.aspell}/bin/aspell';
   $CFG->pathtodot = '${pkgs.graphviz}/bin/dot';
 
+  ${cfg.extraConfig}
+
   require_once('${cfg.package}/share/moodle/lib/setup.php');
 
   // There is no php closing tag in this file,
@@ -172,6 +174,19 @@ in
         for details on configuration directives.
       '';
     };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Any additional text to be appended to the config.php
+        configuration file. This is a PHP script. For configuration
+        details, see <link xlink:href="https://docs.moodle.org/37/en/Configuration_file"/>.
+      '';
+      example = ''
+        $CFG->disableupdatenotifications = true;
+      '';
+    };
   };
 
   # implementation
@@ -294,7 +309,9 @@ in
 
     systemd.services.httpd.after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
 
-    users.users.${user}.group = group;
-
+    users.users.${user} = {
+      group = group;
+      isSystemUser = true;
+    };
   };
 }
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index 5f5469e48507..b9186a1dc07f 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -120,16 +120,24 @@ in {
       '';
     };
 
-    poolConfig = mkOption {
-      type = types.lines;
-      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
+    poolSettings = mkOption {
+      type = with types; attrsOf (oneOf [ str int bool ]);
+      default = {
+        "pm" = "dynamic";
+        "pm.max_children" = "32";
+        "pm.start_servers" = "2";
+        "pm.min_spare_servers" = "2";
+        "pm.max_spare_servers" = "4";
+        "pm.max_requests" = "500";
+      };
+      description = ''
+        Options for nextcloud's PHP pool. See the documentation on <literal>php-fpm.conf</literal> for details on configuration directives.
       '';
+    };
+
+    poolConfig = mkOption {
+      type = types.nullOr types.lines;
+      default = null;
       description = ''
         Options for nextcloud's PHP pool. See the documentation on <literal>php-fpm.conf</literal> for details on configuration directives.
       '';
@@ -287,6 +295,11 @@ in {
           message = "Please specify exactly one of adminpass or adminpassFile";
         }
       ];
+
+      warnings = optional (cfg.poolConfig != null) ''
+        Using config.services.nextcloud.poolConfig is deprecated and will become unsupported in a future release.
+        Please migrate your configuration to config.services.nextcloud.poolSettings.
+      '';
     }
 
     { systemd.timers.nextcloud-cron = {
@@ -423,7 +436,7 @@ in {
           settings = mapAttrs (name: mkDefault) {
             "listen.owner" = "nginx";
             "listen.group" = "nginx";
-          };
+          } // cfg.poolSettings;
           extraConfig = cfg.poolConfig;
         };
       };
@@ -454,7 +467,7 @@ in {
               };
               "/" = {
                 priority = 200;
-                extraConfig = "rewrite ^ /index.php$request_uri;";
+                extraConfig = "rewrite ^ /index.php;";
               };
               "~ ^/store-apps" = {
                 priority = 201;
@@ -481,6 +494,7 @@ in {
                 extraConfig = ''
                   include ${config.services.nginx.package}/conf/fastcgi.conf;
                   fastcgi_split_path_info ^(.+\.php)(\\/.*)$;
+                  try_files $fastcgi_script_name =404;
                   fastcgi_param PATH_INFO $fastcgi_path_info;
                   fastcgi_param HTTPS ${if cfg.https then "on" else "off"};
                   fastcgi_param modHeadersAvailable true;
@@ -518,6 +532,7 @@ in {
               add_header X-Download-Options noopen;
               add_header X-Permitted-Cross-Domain-Policies none;
               add_header Referrer-Policy no-referrer;
+              add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
               error_page 403 /core/templates/403.php;
               error_page 404 /core/templates/404.php;
               client_max_body_size ${cfg.maxUploadSize};
diff --git a/nixos/modules/services/web-apps/nexus.nix b/nixos/modules/services/web-apps/nexus.nix
index 3af97e146d0a..d4d507362c97 100644
--- a/nixos/modules/services/web-apps/nexus.nix
+++ b/nixos/modules/services/web-apps/nexus.nix
@@ -68,6 +68,7 @@ in
           -Dkaraf.data=${cfg.home}/nexus3
           -Djava.io.tmpdir=${cfg.home}/nexus3/tmp
           -Dkaraf.startLocalConsole=false
+          -Djava.endorsed.dirs=${cfg.package}/lib/endorsed
         '';
 
         description = ''
diff --git a/nixos/modules/services/web-apps/restya-board.nix b/nixos/modules/services/web-apps/restya-board.nix
index f220669c9108..2c2f36ac598a 100644
--- a/nixos/modules/services/web-apps/restya-board.nix
+++ b/nixos/modules/services/web-apps/restya-board.nix
@@ -216,7 +216,6 @@ in
         index index.html index.php;
 
         gzip on;
-        gzip_disable "msie6";
 
         gzip_comp_level 6;
         gzip_min_length  1100;
@@ -236,7 +235,7 @@ in
 
       locations."/".root = "${runDir}/client";
 
-      locations."~ \.php$" = {
+      locations."~ \\.php$" = {
         tryFiles = "$uri =404";
         extraConfig = ''
           include ${pkgs.nginx}/conf/fastcgi_params;
@@ -247,7 +246,7 @@ in
         '';
       };
 
-      locations."~* \.(css|js|less|html|ttf|woff|jpg|jpeg|gif|png|bmp|ico)" = {
+      locations."~* \\.(css|js|less|html|ttf|woff|jpg|jpeg|gif|png|bmp|ico)" = {
         root = "${runDir}/client";
         extraConfig = ''
           if (-f $request_filename) {
diff --git a/nixos/modules/services/web-apps/shiori.nix b/nixos/modules/services/web-apps/shiori.nix
new file mode 100644
index 000000000000..1817a2039352
--- /dev/null
+++ b/nixos/modules/services/web-apps/shiori.nix
@@ -0,0 +1,50 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.shiori;
+in {
+  options = {
+    services.shiori = {
+      enable = mkEnableOption "Shiori simple bookmarks manager";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.shiori;
+        defaultText = "pkgs.shiori";
+        description = "The Shiori package to use.";
+      };
+
+      address = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          The IP address on which Shiori will listen.
+          If empty, listens on all interfaces.
+        '';
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 8080;
+        description = "The port of the Shiori web application";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.shiori = with cfg; {
+      description = "Shiori simple bookmarks manager";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = "${package}/bin/shiori serve --address '${address}' --port '${toString port}'";
+        DynamicUser = true;
+        Environment = "SHIORI_DIR=/var/lib/shiori";
+        StateDirectory = "shiori";
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ minijackson ];
+}
diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix
index abe4748591e9..b92e34498949 100644
--- a/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixos/modules/services/web-apps/tt-rss.nix
@@ -548,7 +548,7 @@ let
             index = "index.php";
           };
 
-          locations."~ \.php$" = {
+          locations."~ \\.php$" = {
             extraConfig = ''
               fastcgi_split_path_info ^(.+\.php)(/.+)$;
               fastcgi_pass unix:${config.services.phpfpm.pools.${cfg.pool}.socket};
diff --git a/nixos/modules/services/web-apps/virtlyst.nix b/nixos/modules/services/web-apps/virtlyst.nix
index e5c0bff2168a..37bdbb0e3b42 100644
--- a/nixos/modules/services/web-apps/virtlyst.nix
+++ b/nixos/modules/services/web-apps/virtlyst.nix
@@ -54,6 +54,7 @@ in
       home = stateDir;
       createHome = true;
       group = mkIf config.virtualisation.libvirtd.enable "libvirtd";
+      isSystemUser = true;
     };
 
     systemd.services.virtlyst = {
diff --git a/nixos/modules/services/web-apps/wordpress.nix b/nixos/modules/services/web-apps/wordpress.nix
index 884754370587..f1370c2854b8 100644
--- a/nixos/modules/services/web-apps/wordpress.nix
+++ b/nixos/modules/services/web-apps/wordpress.nix
@@ -61,6 +61,19 @@ let
     ?>
   '';
 
+  secretsVars = [ "AUTH_KEY" "SECURE_AUTH_KEY" "LOOGGED_IN_KEY" "NONCE_KEY" "AUTH_SALT" "SECURE_AUTH_SALT" "LOGGED_IN_SALT" "NONCE_SALT" ];
+  secretsScript = hostStateDir: ''
+    if ! test -e "${hostStateDir}/secret-keys.php"; then
+      umask 0177
+      echo "<?php" >> "${hostStateDir}/secret-keys.php"
+      ${concatMapStringsSep "\n" (var: ''
+        echo "define('${var}', '`tr -dc a-zA-Z0-9 </dev/urandom | head -c 64`');" >> "${hostStateDir}/secret-keys.php"
+      '') secretsVars}
+      echo "?>" >> "${hostStateDir}/secret-keys.php"
+      chmod 440 "${hostStateDir}/secret-keys.php"
+    fi
+  '';
+
   siteOpts = { lib, name, ... }:
     {
       options = {
@@ -340,14 +353,7 @@ in
           wantedBy = [ "multi-user.target" ];
           before = [ "phpfpm-wordpress-${hostName}.service" ];
           after = optional cfg.database.createLocally "mysql.service";
-          script = ''
-            if ! test -e "${stateDir hostName}/secret-keys.php"; then
-              echo "<?php" >> "${stateDir hostName}/secret-keys.php"
-              ${pkgs.curl}/bin/curl -s https://api.wordpress.org/secret-key/1.1/salt/ >> "${stateDir hostName}/secret-keys.php"
-              echo "?>" >> "${stateDir hostName}/secret-keys.php"
-              chmod 440 "${stateDir hostName}/secret-keys.php"
-            fi
-          '';
+          script = secretsScript (stateDir hostName);
 
           serviceConfig = {
             Type = "oneshot";
@@ -361,7 +367,10 @@ in
       })
     ];
 
-    users.users.${user}.group = group;
+    users.users.${user} = {
+      group = group;
+      isSystemUser = true;
+    };
 
   };
 }
diff --git a/nixos/modules/services/web-apps/zabbix.nix b/nixos/modules/services/web-apps/zabbix.nix
index dac243b20e97..09538726b7cd 100644
--- a/nixos/modules/services/web-apps/zabbix.nix
+++ b/nixos/modules/services/web-apps/zabbix.nix
@@ -179,7 +179,7 @@ in
       '' + optionalString (cfg.database.type == "oracle") ''
         extension=${pkgs.phpPackages.oci8}/lib/php/extensions/oci8.so
       '';
-      phpEnv.ZABBIX_CONFIG = zabbixConfig;
+      phpEnv.ZABBIX_CONFIG = "${zabbixConfig}";
       settings = {
         "listen.owner" = config.services.httpd.user;
         "listen.group" = config.services.httpd.group;
@@ -197,7 +197,7 @@ in
             <Directory "${cfg.package}/share/zabbix">
               <FilesMatch "\.php$">
                 <If "-f %{REQUEST_FILENAME}">
-                  SetHandler "proxy:unix:${fpm.listen}|fcgi://localhost/"
+                  SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
                 </If>
               </FilesMatch>
               AllowOverride all
diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix
index 098160ee3692..99304d0e48ae 100644
--- a/nixos/modules/services/web-servers/apache-httpd/default.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/default.nix
@@ -12,7 +12,7 @@ let
 
   php = mainCfg.phpPackage.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ };
 
-  phpMajorVersion = head (splitString "." php.version);
+  phpMajorVersion = lib.versions.major (lib.getVersion php);
 
   mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = httpd; };
 
@@ -31,70 +31,8 @@ let
   extraForeignModules = filter isAttrs extraModules;
   extraApacheModules = filter isString extraModules;
 
-
-  makeServerInfo = cfg: {
-    # Canonical name must not include a trailing slash.
-    canonicalNames =
-      let defaultPort = (head (defaultListen cfg)).port; in
-      map (port:
-        (if cfg.enableSSL then "https" else "http") + "://" +
-        cfg.hostName +
-        (if port != defaultPort then ":${toString port}" else "")
-        ) (map (x: x.port) (getListen cfg));
-
-    # Admin address: inherit from the main server if not specified for
-    # a virtual host.
-    adminAddr = if cfg.adminAddr != null then cfg.adminAddr else mainCfg.adminAddr;
-
-    vhostConfig = cfg;
-    serverConfig = mainCfg;
-    fullConfig = config; # machine config
-  };
-
-
   allHosts = [mainCfg] ++ mainCfg.virtualHosts;
 
-
-  callSubservices = serverInfo: defs:
-    let f = svc:
-      let
-        svcFunction =
-          if svc ? function then svc.function
-          # instead of using serviceType="mediawiki"; you can copy mediawiki.nix to any location outside nixpkgs, modify it at will, and use serviceExpression=./mediawiki.nix;
-          else if svc ? serviceExpression then import (toString svc.serviceExpression)
-          else import (toString "${toString ./.}/${if svc ? serviceType then svc.serviceType else svc.serviceName}.nix");
-        config = (evalModules
-          { modules = [ { options = res.options; config = svc.config or svc; } ];
-            check = false;
-          }).config;
-        defaults = {
-          extraConfig = "";
-          extraModules = [];
-          extraModulesPre = [];
-          extraPath = [];
-          extraServerPath = [];
-          globalEnvVars = [];
-          robotsEntries = "";
-          startupScript = "";
-          enablePHP = false;
-          enablePerl = false;
-          phpOptions = "";
-          options = {};
-          documentRoot = null;
-        };
-        res = defaults // svcFunction { inherit config lib pkgs serverInfo php; };
-      in res;
-    in map f defs;
-
-
-  # !!! callSubservices is expensive
-  subservicesFor = cfg: callSubservices (makeServerInfo cfg) cfg.extraSubservices;
-
-  mainSubservices = subservicesFor mainCfg;
-
-  allSubservices = mainSubservices ++ concatMap subservicesFor mainCfg.virtualHosts;
-
-
   enableSSL = any (vhost: vhost.enableSSL) allHosts;
 
 
@@ -188,13 +126,18 @@ let
 
   perServerConf = isMainServer: cfg: let
 
-    serverInfo = makeServerInfo cfg;
-
-    subservices = callSubservices serverInfo cfg.extraSubservices;
+    # Canonical name must not include a trailing slash.
+    canonicalNames =
+      let defaultPort = (head (defaultListen cfg)).port; in
+      map (port:
+        (if cfg.enableSSL then "https" else "http") + "://" +
+        cfg.hostName +
+        (if port != defaultPort then ":${toString port}" else "")
+        ) (map (x: x.port) (getListen cfg));
 
     maybeDocumentRoot = fold (svc: acc:
       if acc == null then svc.documentRoot else assert svc.documentRoot == null; acc
-    ) null ([ cfg ] ++ subservices);
+    ) null ([ cfg ]);
 
     documentRoot = if maybeDocumentRoot != null then maybeDocumentRoot else
       pkgs.runCommand "empty" { preferLocalBuild = true; } "mkdir -p $out";
@@ -209,15 +152,11 @@ let
       </Directory>
     '';
 
-    robotsTxt =
-      concatStringsSep "\n" (filter (x: x != "") (
-        # If this is a vhost, the include the entries for the main server as well.
-        (if isMainServer then [] else [mainCfg.robotsEntries] ++ map (svc: svc.robotsEntries) mainSubservices)
-        ++ [cfg.robotsEntries]
-        ++ (map (svc: svc.robotsEntries) subservices)));
+    # If this is a vhost, the include the entries for the main server as well.
+    robotsTxt = concatStringsSep "\n" (filter (x: x != "") ([ cfg.robotsEntries ] ++ lib.optional (!isMainServer) mainCfg.robotsEntries));
 
   in ''
-    ${concatStringsSep "\n" (map (n: "ServerName ${n}") serverInfo.canonicalNames)}
+    ${concatStringsSep "\n" (map (n: "ServerName ${n}") canonicalNames)}
 
     ${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases}
 
@@ -292,8 +231,6 @@ let
       in concatMapStrings makeDirConf cfg.servedDirs
     }
 
-    ${concatMapStrings (svc: svc.extraConfig) subservices}
-
     ${cfg.extraConfig}
   '';
 
@@ -328,13 +265,10 @@ let
 
     ${let
         load = {name, path}: "LoadModule ${name}_module ${path}\n";
-        allModules =
-          concatMap (svc: svc.extraModulesPre) allSubservices
-          ++ map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules
+        allModules = map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules
           ++ optional mainCfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; }
-          ++ optional enablePHP { name = "php${phpMajorVersion}"; path = "${php}/modules/libphp${phpMajorVersion}.so"; }
-          ++ optional enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; }
-          ++ concatMap (svc: svc.extraModules) allSubservices
+          ++ optional mainCfg.enablePHP { name = "php${phpMajorVersion}"; path = "${php}/modules/libphp${phpMajorVersion}.so"; }
+          ++ optional mainCfg.enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; }
           ++ extraForeignModules;
       in concatMapStrings load (unique allModules)
     }
@@ -385,17 +319,10 @@ let
     }
   '';
 
-
-  enablePHP = mainCfg.enablePHP || any (svc: svc.enablePHP) allSubservices;
-
-  enablePerl = mainCfg.enablePerl || any (svc: svc.enablePerl) allSubservices;
-
-
   # Generate the PHP configuration file.  Should probably be factored
   # out into a separate module.
   phpIni = pkgs.runCommand "php.ini"
-    { options = concatStringsSep "\n"
-        ([ mainCfg.phpOptions ] ++ (map (svc: svc.phpOptions) allSubservices));
+    { options = mainCfg.phpOptions;
       preferLocalBuild = true;
     }
     ''
@@ -408,6 +335,10 @@ in
 
 {
 
+  imports = [
+    (mkRemovedOptionModule [ "services" "httpd" "extraSubservices" ] "Most existing subservices have been ported to the NixOS module system. Please update your configuration accordingly.")
+  ];
+
   ###### interface
 
   options = {
@@ -637,8 +568,6 @@ in
                      message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; }
                  ];
 
-    warnings = map (cfg: "apache-httpd's extraSubservices option is deprecated. Most existing subservices have been ported to the NixOS module system. Please update your configuration accordingly.") (lib.filter (cfg: cfg.extraSubservices != []) allHosts);
-
     users.users = optionalAttrs (mainCfg.user == "wwwrun") (singleton
       { name = "wwwrun";
         group = mainCfg.group;
@@ -651,7 +580,7 @@ in
         gid = config.ids.gids.wwwrun;
       });
 
-    environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices;
+    environment.systemPackages = [httpd];
 
     services.httpd.phpOptions =
       ''
@@ -674,13 +603,11 @@ in
 
         path =
           [ httpd pkgs.coreutils pkgs.gnugrep ]
-          ++ optional enablePHP pkgs.system-sendmail # Needed for PHP's mail() function.
-          ++ concatMap (svc: svc.extraServerPath) allSubservices;
+          ++ optional mainCfg.enablePHP pkgs.system-sendmail; # Needed for PHP's mail() function.
 
         environment =
-          optionalAttrs enablePHP { PHPRC = phpIni; }
-          // optionalAttrs mainCfg.enableMellon { LD_LIBRARY_PATH  = "${pkgs.xmlsec}/lib"; }
-          // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices));
+          optionalAttrs mainCfg.enablePHP { PHPRC = phpIni; }
+          // optionalAttrs mainCfg.enableMellon { LD_LIBRARY_PATH  = "${pkgs.xmlsec}/lib"; };
 
         preStart =
           ''
@@ -698,12 +625,6 @@ in
             for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${mainCfg.user} ' | cut -f2 -d ' '); do
                 ${pkgs.utillinux}/bin/ipcrm -s $i
             done
-
-            # Run the startup hooks for the subservices.
-            for i in ${toString (map (svn: svn.startupScript) allSubservices)}; do
-                echo Running Apache startup hook $i...
-                $i
-            done
           '';
 
         serviceConfig.ExecStart = "@${httpd}/bin/httpd httpd -f ${httpdConf}";
diff --git a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
index 9d747549c274..c36207d54607 100644
--- a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
@@ -133,12 +133,6 @@ with lib;
     '';
   };
 
-  extraSubservices = mkOption {
-    type = types.listOf types.unspecified;
-    default = [];
-    description = "Extra subservices to enable in the webserver.";
-  };
-
   enableUserDir = mkOption {
     type = types.bool;
     default = false;
diff --git a/nixos/modules/services/web-servers/hitch/default.nix b/nixos/modules/services/web-servers/hitch/default.nix
index a6c4cbea1225..1812f225b74d 100644
--- a/nixos/modules/services/web-servers/hitch/default.nix
+++ b/nixos/modules/services/web-servers/hitch/default.nix
@@ -102,7 +102,10 @@ with lib;
 
     environment.systemPackages = [ pkgs.hitch ];
 
-    users.users.hitch.group = "hitch";
+    users.users.hitch = {
+      group = "hitch";
+      isSystemUser = true;
+    };
     users.groups.hitch = {};
   };
 }
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index b94b338fd4a6..eb90dae94dfe 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -61,7 +61,10 @@ let
 
     ${optionalString (cfg.httpConfig == "" && cfg.config == "") ''
     http {
-      include ${cfg.package}/conf/mime.types;
+      # The mime type definitions included with nginx are very incomplete, so
+      # we use a list of mime types from the mailcap package, which is also
+      # used by most other Linux distributions by default.
+      include ${pkgs.mailcap}/etc/nginx/mime.types;
       include ${cfg.package}/conf/fastcgi.conf;
       include ${cfg.package}/conf/uwsgi_params;
 
@@ -94,7 +97,6 @@ let
 
       ${optionalString (cfg.recommendedGzipSettings) ''
         gzip on;
-        gzip_disable "msie6";
         gzip_proxied any;
         gzip_comp_level 5;
         gzip_types
@@ -120,6 +122,14 @@ let
         include ${recommendedProxyConfig};
       ''}
 
+      ${optionalString (cfg.mapHashBucketSize != null) ''
+        map_hash_bucket_size ${toString cfg.mapHashBucketSize};
+      ''}
+
+      ${optionalString (cfg.mapHashMaxSize != null) ''
+        map_hash_max_size ${toString cfg.mapHashMaxSize};
+      ''}
+
       # $connection_upgrade is used for websocket proxying
       map $http_upgrade $connection_upgrade {
           default upgrade;
@@ -508,6 +518,23 @@ in
         '';
       };
 
+      mapHashBucketSize = mkOption {
+        type = types.nullOr (types.enum [ 32 64 128 ]);
+        default = null;
+        description = ''
+            Sets the bucket size for the map variables hash tables. Default
+            value depends on the processor’s cache line size.
+          '';
+      };
+
+      mapHashMaxSize = mkOption {
+        type = types.nullOr types.ints.positive;
+        default = null;
+        description = ''
+            Sets the maximum size of the map variables hash tables.
+          '';
+      };
+
       resolver = mkOption {
         type = types.submodule {
           options = {
diff --git a/nixos/modules/services/web-servers/traefik.nix b/nixos/modules/services/web-servers/traefik.nix
index 8de7df0d446c..5b0fc467ea46 100644
--- a/nixos/modules/services/web-servers/traefik.nix
+++ b/nixos/modules/services/web-servers/traefik.nix
@@ -117,6 +117,7 @@ in {
       group = "traefik";
       home = cfg.dataDir;
       createHome = true;
+      isSystemUser = true;
     };
 
     users.groups.traefik = {};
diff --git a/nixos/modules/services/web-servers/unit/default.nix b/nixos/modules/services/web-servers/unit/default.nix
index a4a9d370d644..32f6d475b34e 100644
--- a/nixos/modules/services/web-servers/unit/default.nix
+++ b/nixos/modules/services/web-servers/unit/default.nix
@@ -116,6 +116,7 @@ in {
     users.users = optionalAttrs (cfg.user == "unit") (singleton {
       name = "unit";
       group = cfg.group;
+      isSystemUser = true;
     });
 
     users.groups = optionalAttrs (cfg.group == "unit") (singleton {
diff --git a/nixos/modules/services/x11/desktop-managers/enlightenment.nix b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
index 9914b6687090..3745069f6eaf 100644
--- a/nixos/modules/services/x11/desktop-managers/enlightenment.nix
+++ b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
@@ -48,10 +48,6 @@ in
     services.xserver.desktopManager.session = [
     { name = "Enlightenment";
       start = ''
-        # Set GTK_DATA_PREFIX so that GTK can find the themes
-        export GTK_DATA_PREFIX=${config.system.path}
-        # find theme engines
-        export GTK_PATH=${config.system.path}/lib/gtk-3.0:${config.system.path}/lib/gtk-2.0
         export XDG_MENU_PREFIX=e-
 
         export GST_PLUGIN_PATH="${GST_PLUGIN_PATH}"
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index 30c5250221c1..5ad31e5b9d00 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -28,6 +28,12 @@ let
         (pkg: "cp -rf ${pkg}/share/gsettings-schemas/*/glib-2.0/schemas/*.xml $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas\n")
         (defaultPackages ++ cfg.extraGSettingsOverridePackages)}
 
+     cp -f ${pkgs.gnome3.gnome-shell}/share/gsettings-schemas/*/glib-2.0/schemas/*.gschema.override $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas
+
+     ${optionalString flashbackEnabled ''
+       cp -f ${pkgs.gnome3.gnome-flashback}/share/gsettings-schemas/*/glib-2.0/schemas/*.gschema.override $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas
+     ''}
+
      chmod -R a+w $out/share/gsettings-schemas/nixos-gsettings-overrides
      cat - > $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas/nixos-defaults.gschema.override <<- EOF
        [org.gnome.desktop.background]
@@ -155,10 +161,10 @@ in
 
       environment.systemPackages = cfg.sessionPath;
 
-      environment.variables.GNOME_SESSION_DEBUG = mkIf cfg.debug "1";
+      environment.sessionVariables.GNOME_SESSION_DEBUG = mkIf cfg.debug "1";
 
       # Override GSettings schemas
-      environment.variables.NIX_GSETTINGS_OVERRIDES_DIR = "${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas";
+      environment.sessionVariables.NIX_GSETTINGS_OVERRIDES_DIR = "${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas";
 
        # If gnome3 is installed, build vim for gtk3 too.
       nixpkgs.config.vim.gui = "gtk3";
@@ -209,14 +215,6 @@ in
 
       networking.networkmanager.enable = mkDefault true;
 
-      # Use the correct gnome3 packageSet
-      networking.networkmanager.basePackages = {
-        inherit (pkgs) networkmanager modemmanager wpa_supplicant crda;
-        inherit (pkgs.gnome3) networkmanager-openvpn networkmanager-vpnc
-        networkmanager-openconnect networkmanager-fortisslvpn
-        networkmanager-iodine networkmanager-l2tp;
-      };
-
       services.xserver.updateDbusEnvironment = true;
 
       # Needed for themes and backgrounds
@@ -229,6 +227,7 @@ in
       services.colord.enable = mkDefault true;
       services.gnome3.chrome-gnome-shell.enable = mkDefault true;
       services.gnome3.glib-networking.enable = true;
+      services.gnome3.gnome-initial-setup.enable = mkDefault true;
       services.gnome3.gnome-remote-desktop.enable = mkDefault true;
       services.gnome3.gnome-settings-daemon.enable = true;
       services.gnome3.gnome-user-share.enable = mkDefault true;
@@ -236,7 +235,8 @@ in
       services.gvfs.enable = true;
       services.system-config-printer.enable = (mkIf config.services.printing.enable (mkDefault true));
       services.telepathy.enable = mkDefault true;
-      systemd.packages = [ pkgs.gnome3.vino ];
+
+      systemd.packages = with pkgs.gnome3; [ vino gnome-session ];
 
       services.avahi.enable = mkDefault true;
 
@@ -329,10 +329,10 @@ in
 
       # Let nautilus find extensions
       # TODO: Create nautilus-with-extensions package
-      environment.variables.NAUTILUS_EXTENSION_DIR = "${config.system.path}/lib/nautilus/extensions-3.0";
+      environment.sessionVariables.NAUTILUS_EXTENSION_DIR = "${config.system.path}/lib/nautilus/extensions-3.0";
 
       # Override default mimeapps for nautilus
-      environment.variables.XDG_DATA_DIRS = [ "${mimeAppsList}/share" ];
+      environment.sessionVariables.XDG_DATA_DIRS = [ "${mimeAppsList}/share" ];
 
       environment.pathsToLink = [
         "/share/nautilus-python/extensions"
diff --git a/nixos/modules/services/x11/desktop-managers/mate.nix b/nixos/modules/services/x11/desktop-managers/mate.nix
index d7a871c9c704..fe63f36cf96a 100644
--- a/nixos/modules/services/x11/desktop-managers/mate.nix
+++ b/nixos/modules/services/x11/desktop-managers/mate.nix
@@ -48,12 +48,6 @@ in
       name = "mate";
       bgSupport = true;
       start = ''
-        # Set GTK_DATA_PREFIX so that GTK can find the themes
-        export GTK_DATA_PREFIX=${config.system.path}
-
-        # Find theme engines
-        export GTK_PATH=${config.system.path}/lib/gtk-3.0:${config.system.path}/lib/gtk-2.0
-
         export XDG_MENU_PREFIX=mate-
 
         # Let caja find extensions
@@ -91,6 +85,7 @@ in
         pkgs.gtk3.out
         pkgs.shared-mime-info
         pkgs.xdg-user-dirs # Update user dirs as described in https://freedesktop.org/wiki/Software/xdg-user-dirs/
+        pkgs.mate.mate-settings-daemon
       ];
 
     programs.dconf.enable = true;
@@ -104,7 +99,7 @@ in
     services.gnome3.at-spi2-core.enable = true;
     services.gnome3.gnome-keyring.enable = true;
     services.gnome3.gnome-settings-daemon.enable = true;
-    services.gnome3.gnome-settings-daemon.package = pkgs.mate.mate-settings-daemon;
+    services.udev.packages = [ pkgs.mate.mate-settings-daemon ];
     services.gvfs.enable = true;
     services.upower.enable = config.powerManagement.enable;
 
diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixos/modules/services/x11/desktop-managers/pantheon.nix
index e313a194c345..80dab135ee26 100644
--- a/nixos/modules/services/x11/desktop-managers/pantheon.nix
+++ b/nixos/modules/services/x11/desktop-managers/pantheon.nix
@@ -79,8 +79,7 @@ in
         Using Pantheon without LightDM as a displayManager will break screenlocking from the UI.
       '';
 
-    services.xserver.displayManager.lightdm.enable = mkDefault true;
-    services.xserver.displayManager.lightdm.greeters.gtk.enable = mkDefault true;
+    services.xserver.displayManager.lightdm.greeters.pantheon.enable = mkDefault true;
 
     # If not set manually Pantheon session cannot be started
     # Known issue of https://github.com/NixOS/nixpkgs/pull/43992
@@ -98,10 +97,6 @@ in
               export LD_LIBRARY_PATH=$LD_LIBRARY_PATH''${LD_LIBRARY_PATH:+:}${p}/lib
             fi
           '') cfg.sessionPath}
-
-          # Settings from elementary-default-settings
-          export GTK_CSD=1
-          export GTK_MODULES=$GTK_MODULES:pantheon-filechooser-module
       fi
     '';
 
@@ -114,8 +109,9 @@ in
     services.pantheon.files.enable = mkDefault true;
     services.tumbler.enable = mkDefault true;
     services.system-config-printer.enable = (mkIf config.services.printing.enable (mkDefault true));
-    services.dbus.packages = [
-      pkgs.pantheon.switchboard-plug-power
+    services.dbus.packages = with pkgs.pantheon; [
+      switchboard-plug-power
+      elementary-default-settings
     ];
     services.pantheon.contractor.enable = mkDefault true;
     services.gnome3.at-spi2-core.enable = true;
@@ -124,7 +120,7 @@ in
     # TODO: gnome-keyring's xdg autostarts will still be in the environment (from elementary-session-settings) if disabled forcefully
     services.gnome3.gnome-keyring.enable = true;
     services.gnome3.gnome-settings-daemon.enable = true;
-    services.gnome3.gnome-settings-daemon.package = pkgs.pantheon.elementary-settings-daemon;
+    services.udev.packages = [ pkgs.pantheon.elementary-settings-daemon ];
     services.gvfs.enable = true;
     services.gnome3.rygel.enable = mkDefault true;
     services.gsignond.enable = mkDefault true;
@@ -159,16 +155,16 @@ in
     qt5.style = "adwaita";
 
     networking.networkmanager.enable = mkDefault true;
-    networking.networkmanager.basePackages =
-      { inherit (pkgs) networkmanager modemmanager wpa_supplicant crda;
-        inherit (pkgs.gnome3) networkmanager-openvpn networkmanager-vpnc
-                              networkmanager-openconnect networkmanager-fortisslvpn
-                              networkmanager-iodine networkmanager-l2tp; };
 
     # Override GSettings schemas
-    environment.variables.NIX_GSETTINGS_OVERRIDES_DIR = "${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas";
+    environment.sessionVariables.NIX_GSETTINGS_OVERRIDES_DIR = "${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas";
+
+    environment.sessionVariables.GNOME_SESSION_DEBUG = optionalString cfg.debug "1";
 
-    environment.variables.GNOME_SESSION_DEBUG = optionalString cfg.debug "1";
+    # Settings from elementary-default-settings
+    environment.sessionVariables.GTK_CSD = "1";
+    environment.sessionVariables.GTK_MODULES = "pantheon-filechooser-module";
+    environment.etc."gtk-3.0/settings.ini".source = "${pkgs.pantheon.elementary-default-settings}/etc/gtk-3.0/settings.ini";
 
     environment.pathsToLink = [
       # FIXME: modules should link subdirs of `/share` rather than relying on this
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index b10755df4dc2..b6fb7218da6f 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -183,7 +183,8 @@ in
         ++ lib.optional config.hardware.pulseaudio.enable plasma-pa
         ++ lib.optional config.powerManagement.enable powerdevil
         ++ lib.optional config.services.colord.enable colord-kde
-        ++ lib.optionals config.services.samba.enable [ kdenetwork-filesharing pkgs.samba ];
+        ++ lib.optionals config.services.samba.enable [ kdenetwork-filesharing pkgs.samba ]
+        ++ lib.optional config.services.xserver.wacom.enable wacomtablet;
 
       environment.pathsToLink = [
         # FIXME: modules should link subdirs of `/share` rather than relying on this
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index e3249aef50c7..6965c6d26467 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -107,12 +107,6 @@ in
       start = ''
         ${cfg.extraSessionCommands}
 
-        # Set GTK_PATH so that GTK can find the theme engines.
-        export GTK_PATH="${config.system.path}/lib/gtk-2.0:${config.system.path}/lib/gtk-3.0"
-
-        # Set GTK_DATA_PREFIX so that GTK can find the Xfce themes.
-        export GTK_DATA_PREFIX=${config.system.path}
-
         ${pkgs.runtimeShell} ${pkgs.xfce.xinitrc} &
         waitPID=$!
       '';
diff --git a/nixos/modules/services/x11/desktop-managers/xfce4-14.nix b/nixos/modules/services/x11/desktop-managers/xfce4-14.nix
index 57d1268d655a..ffc99172e795 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce4-14.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce4-14.nix
@@ -53,6 +53,7 @@ in
       glib # for gsettings
       gtk3.out # gtk-update-icon-cache
 
+      gnome3.gnome-themes-extra
       gnome3.adwaita-icon-theme
       hicolor-icon-theme
       tango-icon-theme
@@ -69,16 +70,17 @@ in
 
       exo
       garcon
-      gtk-xfce-engine
       libxfce4ui
       xfconf
 
       mousepad
+      parole
       ristretto
       xfce4-appfinder
       xfce4-screenshooter
       xfce4-session
       xfce4-settings
+      xfce4-taskmanager
       xfce4-terminal
 
       # TODO: resync patch for plugins
@@ -86,12 +88,20 @@ in
       thunar
     ] # TODO: NetworkManager doesn't belong here
       ++ optional config.networking.networkmanager.enable networkmanagerapplet
-      ++ optional config.hardware.pulseaudio.enable xfce4-pulseaudio-plugin
       ++ optional config.powerManagement.enable xfce4-power-manager
-      ++ optional cfg.enableXfwm xfwm4
-      ++ optionals (!cfg.noDesktop) [
-        xfce4-panel
+      ++ optionals config.hardware.pulseaudio.enable [
+        pavucontrol
+        # volume up/down keys support:
+        # xfce4-pulseaudio-plugin includes all the functionalities of xfce4-volumed-pulse
+        # but can only be used with xfce4-panel, so for no-desktop usage we still include
+        # xfce4-volumed-pulse
+        (if cfg.noDesktop then xfce4-volumed-pulse else xfce4-pulseaudio-plugin)
+      ] ++ optionals cfg.enableXfwm [
+        xfwm4
+        xfwm4-themes
+      ] ++ optionals (!cfg.noDesktop) [
         xfce4-notifyd
+        xfce4-panel
         xfdesktop
       ];
 
@@ -102,24 +112,10 @@ in
       "/share/gtksourceview-4.0"
     ];
 
-    # Use the correct gnome3 packageSet
-    networking.networkmanager.basePackages = mkIf config.networking.networkmanager.enable {
-      inherit (pkgs) networkmanager modemmanager wpa_supplicant crda;
-      inherit (pkgs.gnome3) networkmanager-openvpn networkmanager-vpnc
-      networkmanager-openconnect networkmanager-fortisslvpn
-      networkmanager-iodine networkmanager-l2tp;
-    };
-
     services.xserver.desktopManager.session = [{
       name = "xfce4-14";
       bgSupport = true;
       start = ''
-        # Set GTK_PATH so that GTK can find the theme engines.
-        export GTK_PATH="${config.system.path}/lib/gtk-2.0:${config.system.path}/lib/gtk-3.0"
-
-        # Set GTK_DATA_PREFIX so that GTK can find the Xfce themes.
-        export GTK_DATA_PREFIX=${config.system.path}
-
         ${pkgs.runtimeShell} ${pkgs.xfce4-14.xinitrc} &
         waitPID=$!
       '';
diff --git a/nixos/modules/services/x11/desktop-managers/xterm.nix b/nixos/modules/services/x11/desktop-managers/xterm.nix
index 1408df312950..f76db278a927 100644
--- a/nixos/modules/services/x11/desktop-managers/xterm.nix
+++ b/nixos/modules/services/x11/desktop-managers/xterm.nix
@@ -5,6 +5,7 @@ with lib;
 let
 
   cfg = config.services.xserver.desktopManager.xterm;
+  xSessionEnabled = config.services.xserver.enable;
 
 in
 
@@ -13,7 +14,8 @@ in
 
     services.xserver.desktopManager.xterm.enable = mkOption {
       type = types.bool;
-      default = (versionOlder config.system.stateVersion "19.09");
+      default = (versionOlder config.system.stateVersion "19.09") && xSessionEnabled;
+      defaultText = if versionOlder config.system.stateVersion "19.09" then "config.services.xserver.enable" else "false";
       description = "Enable a xterm terminal as a desktop manager.";
     };
 
diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix
index 3f1669d08516..e5990aec4b9c 100644
--- a/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixos/modules/services/x11/display-managers/gdm.nix
@@ -42,10 +42,7 @@ in
     services.xserver.displayManager.gdm = {
 
       enable = mkEnableOption ''
-        GDM as the display manager.
-        <emphasis>GDM in NixOS is not well-tested with desktops other
-        than GNOME, so use with caution, as it could render the
-        system unusable.</emphasis>
+        GDM, the GNOME Display Manager
       '';
 
       debug = mkEnableOption ''
@@ -142,8 +139,6 @@ in
           GDM_X_SERVER_EXTRA_ARGS = toString
             (filter (arg: arg != "-terminate") cfg.xserverArgs);
           XDG_DATA_DIRS = "${cfg.session.desktops}/share/";
-          # Find the mouse
-          XCURSOR_PATH = "~/.icons:${pkgs.gnome3.adwaita-icon-theme}/share/icons";
         } // optionalAttrs (xSessionWrapper != null) {
           # Make GDM use this wrapper before running the session, which runs the
           # configured setupCommands. This relies on a patched GDM which supports
@@ -155,6 +150,12 @@ in
           mkdir -p /run/gdm/.config/pulse
           ln -sf ${pulseConfig} /run/gdm/.config/pulse/default.pa
           chown -R gdm:gdm /run/gdm/.config
+        '' + optionalString config.services.gnome3.gnome-initial-setup.enable ''
+          # Create stamp file for gnome-initial-setup to prevent run.
+          mkdir -p /run/gdm/.config
+          cat - > /run/gdm/.config/gnome-initial-setup-done <<- EOF
+          yes
+          EOF
         '';
       };
 
@@ -164,6 +165,17 @@ in
       "rc-local.service"
       "systemd-machined.service"
       "systemd-user-sessions.service"
+      "getty@tty${gdm.initialVT}.service"
+      "plymouth-quit.service"
+      "plymouth-start.service"
+    ];
+    systemd.services.display-manager.conflicts = [
+       "getty@tty${gdm.initialVT}.service"
+       # TODO: Add "plymouth-quit.service" so GDM can control when plymouth quits.
+       # Currently this breaks switching configurations while using plymouth.
+    ];
+    systemd.services.display-manager.onFailure = [
+      "plymouth-quit.service"
     ];
 
     systemd.services.display-manager.serviceConfig = {
@@ -173,6 +185,9 @@ in
       BusName = "org.gnome.DisplayManager";
       StandardOutput = "syslog";
       StandardError = "inherit";
+      ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
+      KeyringMode = "shared";
+      EnvironmentFile = "-/etc/locale.conf";
     };
 
     systemd.services.display-manager.path = [ pkgs.gnome3.gnome-session ];
@@ -262,7 +277,7 @@ in
         password required       pam_deny.so
 
         session  required       pam_succeed_if.so audit quiet_success user = gdm
-        session  required       pam_env.so envfile=${config.system.build.pamEnvironment}
+        session  required       pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
         session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
         session  optional       pam_keyinit.so force revoke
         session  optional       pam_permit.so
diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/enso-os.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/enso-os.nix
index de128809ce30..129df139c61a 100644
--- a/nixos/modules/services/x11/display-managers/lightdm-greeters/enso-os.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/enso-os.nix
@@ -10,32 +10,6 @@ let
   icons = cfg.iconTheme.package;
   cursors = cfg.cursorTheme.package;
 
-  # We need a few things in the environment for the greeter to run with
-  # fonts/icons.
-  wrappedEnsoGreeter = pkgs.runCommand "lightdm-enso-os-greeter" {
-      buildInputs = [ pkgs.makeWrapper ];
-      preferLocalBuild = true;
-    } ''
-      # This wrapper ensures that we actually get themes
-      makeWrapper ${pkgs.lightdm-enso-os-greeter}/bin/pantheon-greeter \
-        $out/greeter \
-        --prefix PATH : "${pkgs.glibc.bin}/bin" \
-        --set GDK_PIXBUF_MODULE_FILE "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" \
-        --set GTK_PATH "${theme}:${pkgs.gtk3.out}" \
-        --set GTK_EXE_PREFIX "${theme}" \
-        --set GTK_DATA_PREFIX "${theme}" \
-        --set XDG_DATA_DIRS "${theme}/share:${icons}/share:${cursors}/share" \
-        --set XDG_CONFIG_HOME "${theme}/share"
-
-      cat - > $out/lightdm-enso-os-greeter.desktop << EOF
-      [Desktop Entry]
-      Name=LightDM Greeter
-      Comment=This runs the LightDM Greeter
-      Exec=$out/greeter
-      Type=Application
-      EOF
-    '';
-
   ensoGreeterConf = pkgs.writeText "lightdm-enso-os-greeter.conf" ''
     [greeter]
     default-wallpaper=${ldmcfg.background}
@@ -144,10 +118,16 @@ in {
   config = mkIf (ldmcfg.enable && cfg.enable) {
     environment.etc."lightdm/greeter.conf".source = ensoGreeterConf;
 
+    environment.systemPackages = [
+      cursors
+      icons
+      theme
+    ];
+
     services.xserver.displayManager.lightdm = {
       greeter = mkDefault {
-        package = wrappedEnsoGreeter;
-        name = "lightdm-enso-os-greeter";
+        package = pkgs.lightdm-enso-os-greeter.xgreeters;
+        name = "pantheon-greeter";
       };
 
       greeters = {
diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
index 5b280b024233..de932e6e840a 100644
--- a/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
@@ -15,34 +15,6 @@ let
   icons = cfg.iconTheme.package;
   cursors = cfg.cursorTheme.package;
 
-  # The default greeter provided with this expression is the GTK greeter.
-  # Again, we need a few things in the environment for the greeter to run with
-  # fonts/icons.
-  wrappedGtkGreeter = pkgs.runCommand "lightdm-gtk-greeter" {
-      buildInputs = [ pkgs.makeWrapper ];
-      preferLocalBuild = true;
-    } ''
-      # This wrapper ensures that we actually get themes
-      makeWrapper ${pkgs.lightdm_gtk_greeter}/sbin/lightdm-gtk-greeter \
-        $out/greeter \
-        --prefix PATH : "${lib.getBin pkgs.stdenv.cc.libc}/bin" \
-        --set GDK_PIXBUF_MODULE_FILE "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" \
-        --set GTK_PATH "${theme}:${pkgs.gtk3.out}" \
-        --set GTK_EXE_PREFIX "${theme}" \
-        --set GTK_DATA_PREFIX "${theme}" \
-        --set XDG_DATA_DIRS "${theme}/share:${icons}/share" \
-        --set XDG_CONFIG_HOME "${theme}/share" \
-        --set XCURSOR_PATH "${cursors}/share/icons"
-
-      cat - > $out/lightdm-gtk-greeter.desktop << EOF
-      [Desktop Entry]
-      Name=LightDM Greeter
-      Comment=This runs the LightDM Greeter
-      Exec=$out/greeter
-      Type=Application
-      EOF
-    '';
-
   gtkGreeterConf = writeText "lightdm-gtk-greeter.conf"
     ''
     [greeter]
@@ -185,10 +157,16 @@ in
   config = mkIf (ldmcfg.enable && cfg.enable) {
 
     services.xserver.displayManager.lightdm.greeter = mkDefault {
-      package = wrappedGtkGreeter;
+      package = pkgs.lightdm_gtk_greeter.xgreeters;
       name = "lightdm-gtk-greeter";
     };
 
+    environment.systemPackages = [
+      cursors
+      icons
+      theme
+    ];
+
     environment.etc."lightdm/lightdm-gtk-greeter.conf".source = gtkGreeterConf;
 
   };
diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix
index ba8151a60f20..fa9445af32e7 100644
--- a/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix
@@ -8,11 +8,6 @@ let
   ldmcfg = dmcfg.lightdm;
   cfg = ldmcfg.greeters.mini;
 
-  xgreeters = pkgs.linkFarm "lightdm-mini-greeter-xgreeters" [{
-    path = "${pkgs.lightdm-mini-greeter}/share/xgreeters/lightdm-mini-greeter.desktop";
-    name = "lightdm-mini-greeter.desktop";
-  }];
-
   miniGreeterConf = pkgs.writeText "lightdm-mini-greeter.conf"
     ''
     [greeter]
@@ -90,7 +85,7 @@ in
     services.xserver.displayManager.lightdm.greeters.gtk.enable = false;
 
     services.xserver.displayManager.lightdm.greeter = mkDefault {
-      package = xgreeters;
+      package = pkgs.lightdm-mini-greeter.xgreeters;
       name = "lightdm-mini-greeter";
     };
 
diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix
index bfba174144a1..29cb6ccbc06b 100644
--- a/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix
@@ -8,11 +8,6 @@ let
   ldmcfg = dmcfg.lightdm;
   cfg = ldmcfg.greeters.pantheon;
 
-  xgreeters = pkgs.linkFarm "pantheon-greeter-xgreeters" [{
-    path = "${pkgs.pantheon.elementary-greeter}/share/xgreeters/io.elementary.greeter.desktop";
-    name = "io.elementary.greeter.desktop";
-  }];
-
 in
 {
   options = {
@@ -33,17 +28,10 @@ in
 
   config = mkIf (ldmcfg.enable && cfg.enable) {
 
-    warnings = [
-      ''
-        The Pantheon greeter is suboptimal in NixOS and can possibly put you in
-        a situation where you cannot start a session when switching desktopManagers.
-      ''
-    ];
-
     services.xserver.displayManager.lightdm.greeters.gtk.enable = false;
 
     services.xserver.displayManager.lightdm.greeter = mkDefault {
-      package = xgreeters;
+      package = pkgs.pantheon.elementary-greeter.xgreeters;
       name = "io.elementary.greeter";
     };
 
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index c26a5b615353..cf4c05acbccd 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -220,6 +220,43 @@ in
       exec ${lightdm}/sbin/lightdm
     '';
 
+    # Replaces getty
+    systemd.services.display-manager.conflicts = [
+      "getty@tty7.service"
+      # TODO: Add "plymouth-quit.service" so LightDM can control when plymouth
+      # quits. Currently this breaks switching to configurations with plymouth.
+     ];
+
+    # Pull in dependencies of services we replace.
+    systemd.services.display-manager.after = [
+      "rc-local.service"
+      "systemd-machined.service"
+      "systemd-user-sessions.service"
+      "getty@tty7.service"
+      "user.slice"
+    ];
+
+    # user.slice needs to be present
+    systemd.services.display-manager.requires = [
+      "user.slice"
+    ];
+
+    # lightdm stops plymouth so when it fails make sure plymouth stops.
+    systemd.services.display-manager.onFailure = [
+      "plymouth-quit.service"
+    ];
+
+    systemd.services.display-manager.serviceConfig = {
+      BusName = "org.freedesktop.DisplayManager";
+      IgnoreSIGPIPE = "no";
+      # This allows lightdm to pass the LUKS password through to PAM.
+      # login keyring is unlocked automatic when autologin is used.
+      KeyringMode = "shared";
+      KillMode = "mixed";
+      StandardError = "inherit";
+      StandardOutput = "syslog";
+    };
+
     environment.etc."lightdm/lightdm.conf".source = lightdmConf;
     environment.etc."lightdm/users.conf".source = usersConf;
 
@@ -249,7 +286,7 @@ in
         password required       pam_deny.so
 
         session  required       pam_succeed_if.so audit quiet_success user = lightdm
-        session  required       pam_env.so envfile=${config.system.build.pamEnvironment}
+        session  required       pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
         session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
         session  optional       pam_keyinit.so force revoke
         session  optional       pam_permit.so
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix
index c6cb281c2cc2..8847acb0c604 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/x11/display-managers/sddm.nix
@@ -219,8 +219,6 @@ in
         # Load themes from system environment
         QT_PLUGIN_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtPluginPrefix;
         QML2_IMPORT_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtQmlPrefix;
-
-        XDG_DATA_DIRS = "/run/current-system/sw/share";
       };
 
       execCmd = "exec /run/current-system/sw/bin/sddm";
@@ -242,7 +240,7 @@ in
         password required       pam_deny.so
 
         session  required       pam_succeed_if.so audit quiet_success user = sddm
-        session  required       pam_env.so envfile=${config.system.build.pamEnvironment}
+        session  required       pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
         session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
         session  optional       pam_keyinit.so force revoke
         session  optional       pam_permit.so
diff --git a/nixos/modules/services/x11/hardware/cmt.nix b/nixos/modules/services/x11/hardware/cmt.nix
index 95353e92098e..5ac824c5e419 100644
--- a/nixos/modules/services/x11/hardware/cmt.nix
+++ b/nixos/modules/services/x11/hardware/cmt.nix
@@ -47,7 +47,12 @@ in {
     assertions = [
       {
         assertion = !config.services.xserver.libinput.enable;
-        message = "cmt and libinput are incompatible, you cannot enable both (in services.xserver).";
+        message = ''
+          cmt and libinput are incompatible, meaning you cannot enable them both.
+          To use cmt you need to disable libinput with `services.xserver.libinput.enable = false`
+          If you haven't enabled it in configuration.nix, it's enabled by default on a
+          different xserver module.
+        '';
       }
     ];
   };
diff --git a/nixos/modules/services/x11/redshift.nix b/nixos/modules/services/x11/redshift.nix
index 6ddb4c83764a..21b0b33553ac 100644
--- a/nixos/modules/services/x11/redshift.nix
+++ b/nixos/modules/services/x11/redshift.nix
@@ -9,6 +9,22 @@ let
 
 in {
 
+  imports = [
+    (mkChangedOptionModule [ "services" "redshift" "latitude" ] [ "location" "latitude" ]
+      (config:
+        let value = getAttrFromPath [ "services" "redshift" "latitude" ] config;
+        in if value == null then
+          throw "services.redshift.latitude is set to null, you can remove this"
+          else builtins.fromJSON value))
+    (mkChangedOptionModule [ "services" "redshift" "longitude" ] [ "location" "longitude" ]
+      (config:
+        let value = getAttrFromPath [ "services" "redshift" "longitude" ] config;
+        in if value == null then
+          throw "services.redshift.longitude is set to null, you can remove this"
+          else builtins.fromJSON value))
+    (mkRenamedOptionModule [ "services" "redshift" "provider" ] [ "location" "provider" ])
+  ];
+
   options.services.redshift = {
     enable = mkOption {
       type = types.bool;
diff --git a/nixos/modules/services/x11/window-managers/cwm.nix b/nixos/modules/services/x11/window-managers/cwm.nix
new file mode 100644
index 000000000000..03375a226bb6
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/cwm.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.xserver.windowManager.cwm;
+in
+{
+  options = {
+    services.xserver.windowManager.cwm.enable = mkEnableOption "cwm";
+  };
+  config = mkIf cfg.enable {
+    services.xserver.windowManager.session = singleton
+      { name = "cwm";
+        start =
+          ''
+            cwm &
+            waitPID=$!
+          '';
+      };
+    environment.systemPackages = [ pkgs.cwm ];
+  };
+}
diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix
index 2a1f22fa9a4c..c17f3830d0e9 100644
--- a/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixos/modules/services/x11/window-managers/default.nix
@@ -11,6 +11,7 @@ in
     ./2bwm.nix
     ./afterstep.nix
     ./bspwm.nix
+    ./cwm.nix
     ./dwm.nix
     ./evilwm.nix
     ./exwm.nix
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
index 74c150a848d1..ddfd1af4a319 100644
--- a/nixos/modules/system/activation/activation-script.nix
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -218,7 +218,7 @@ in
 
     systemd.user = {
       services.nixos-activation = {
-        description = "Run user specific NixOS activation";
+        description = "Run user-specific NixOS activation";
         script = config.system.userActivationScripts.script;
         unitConfig.ConditionUser = "!@system";
         serviceConfig.Type = "oneshot";
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index 8ff00fa11dc7..641cf9faadc9 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -10,6 +10,9 @@ use Cwd 'abs_path';
 
 my $out = "@out@";
 
+# FIXME: maybe we should use /proc/1/exe to get the current systemd.
+my $curSystemd = abs_path("/run/current-system/sw/bin");
+
 # To be robust against interruption, record what units need to be started etc.
 my $startListFile = "/run/systemd/start-list";
 my $restartListFile = "/run/systemd/restart-list";
@@ -267,7 +270,7 @@ while (my ($unit, $state) = each %{$activePrev}) {
 sub pathToUnitName {
     my ($path) = @_;
     # Use current version of systemctl binary before daemon is reexeced.
-    open my $cmd, "-|", "/run/current-system/sw/bin/systemd-escape", "--suffix=mount", "-p", $path
+    open my $cmd, "-|", "$curSystemd/systemd-escape", "--suffix=mount", "-p", $path
         or die "Unable to escape $path!\n";
     my $escaped = join "", <$cmd>;
     chomp $escaped;
@@ -370,7 +373,7 @@ if (scalar (keys %unitsToStop) > 0) {
     print STDERR "stopping the following units: ", join(", ", @unitsToStopFiltered), "\n"
         if scalar @unitsToStopFiltered;
     # Use current version of systemctl binary before daemon is reexeced.
-    system("/run/current-system/sw/bin/systemctl", "stop", "--", sort(keys %unitsToStop)); # FIXME: ignore errors?
+    system("$curSystemd/systemctl", "stop", "--", sort(keys %unitsToStop)); # FIXME: ignore errors?
 }
 
 print STDERR "NOT restarting the following changed units: ", join(", ", sort(keys %unitsToSkip)), "\n"
@@ -382,10 +385,12 @@ my $res = 0;
 print STDERR "activating the configuration...\n";
 system("$out/activate", "$out") == 0 or $res = 2;
 
-# Restart systemd if necessary.
+# Restart systemd if necessary. Note that this is done using the
+# current version of systemd, just in case the new one has trouble
+# communicating with the running pid 1.
 if ($restartSystemd) {
     print STDERR "restarting systemd...\n";
-    system("@systemd@/bin/systemctl", "daemon-reexec") == 0 or $res = 2;
+    system("$curSystemd/systemctl", "daemon-reexec") == 0 or $res = 2;
 }
 
 # Forget about previously failed services.
@@ -401,8 +406,10 @@ while (my $f = <$listActiveUsers>) {
     my ($uid, $name) = ($+{uid}, $+{user});
     print STDERR "reloading user units for $name...\n";
 
-    system("@su@", "-s", "@shell@", "-l", $name, "-c", "XDG_RUNTIME_DIR=/run/user/$uid @systemd@/bin/systemctl --user daemon-reload");
-    system("@su@", "-s", "@shell@", "-l", $name, "-c", "XDG_RUNTIME_DIR=/run/user/$uid @systemd@/bin/systemctl --user start nixos-activation.service");
+    system("@su@", "-s", "@shell@", "-l", $name, "-c",
+           "export XDG_RUNTIME_DIR=/run/user/$uid; " .
+           "$curSystemd/systemctl --user daemon-reexec; " .
+           "@systemd@/bin/systemctl --user start nixos-activation.service");
 }
 
 close $listActiveUsers;
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index 50dbf2f83651..8a309f3bc5fe 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -108,7 +108,7 @@ in
     boot.extraModulePackages = mkOption {
       type = types.listOf types.package;
       default = [];
-      example = literalExample "[ pkgs.linuxPackages.nvidia_x11 ]";
+      example = literalExample "[ config.boot.kernelPackages.nvidia_x11 ]";
       description = "A list of additional packages supplying kernel modules.";
     };
 
diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh
index 0fb07de10c04..c8b5bf2e61af 100644
--- a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh
+++ b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh
@@ -71,7 +71,7 @@ addEntry() {
 
     local kernel=$(readlink -f $path/kernel)
     local initrd=$(readlink -f $path/initrd)
-    local dtb_path=$(readlink -f $path/kernel-modules/dtbs)
+    local dtb_path=$(readlink -f $path/dtbs)
 
     if test -n "@copyKernels@"; then
         copyToKernelsDir $kernel; kernel=$result
@@ -113,10 +113,18 @@ done
 fwdir=@firmware@/share/raspberrypi/boot/
 copyForced $fwdir/bootcode.bin  $target/bootcode.bin
 copyForced $fwdir/fixup.dat     $target/fixup.dat
+copyForced $fwdir/fixup4.dat    $target/fixup4.dat
+copyForced $fwdir/fixup4cd.dat  $target/fixup4cd.dat
+copyForced $fwdir/fixup4db.dat  $target/fixup4db.dat
+copyForced $fwdir/fixup4x.dat   $target/fixup4x.dat
 copyForced $fwdir/fixup_cd.dat  $target/fixup_cd.dat
 copyForced $fwdir/fixup_db.dat  $target/fixup_db.dat
 copyForced $fwdir/fixup_x.dat   $target/fixup_x.dat
 copyForced $fwdir/start.elf     $target/start.elf
+copyForced $fwdir/start4.elf    $target/start4.elf
+copyForced $fwdir/start4cd.elf  $target/start4cd.elf
+copyForced $fwdir/start4db.elf  $target/start4db.elf
+copyForced $fwdir/start4x.elf   $target/start4x.elf
 copyForced $fwdir/start_cd.elf  $target/start_cd.elf
 copyForced $fwdir/start_db.elf  $target/start_db.elf
 copyForced $fwdir/start_x.elf   $target/start_x.elf
diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
index 1c8354e52696..337afe9ef628 100644
--- a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
+++ b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
@@ -59,7 +59,7 @@ in
 
       version = mkOption {
         default = 2;
-        type = types.enum [ 0 1 2 3 ];
+        type = types.enum [ 0 1 2 3 4 ];
         description = ''
         '';
       };
@@ -97,8 +97,8 @@ in
 
   config = mkIf cfg.enable {
     assertions = singleton {
-      assertion = !pkgs.stdenv.hostPlatform.isAarch64 || cfg.version == 3;
-      message = "Only Raspberry Pi 3 supports aarch64.";
+      assertion = !pkgs.stdenv.hostPlatform.isAarch64 || cfg.version >= 3;
+      message = "Only Raspberry Pi >= 3 supports aarch64.";
     };
 
     system.build.installBootLoader = builder;
diff --git a/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix b/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix
index 9d4f8a93d282..1dc397e521b4 100644
--- a/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix
+++ b/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix
@@ -10,11 +10,13 @@ let
       pkgs.ubootRaspberryPi
     else if version == 2 then
       pkgs.ubootRaspberryPi2
-    else
+    else if version == 3 then
       if isAarch64 then
         pkgs.ubootRaspberryPi3_64bit
       else
-        pkgs.ubootRaspberryPi3_32bit;
+        pkgs.ubootRaspberryPi3_32bit
+    else
+      throw "U-Boot is not yet supported on the raspberry pi 4.";
 
   extlinuxConfBuilder =
     import ../generic-extlinux-compatible/extlinux-conf-builder.nix {
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index f2060e21509c..85a106527fe0 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -187,7 +187,7 @@ let
     # Note: For DHCP the values both, none, v4, v6 are deprecated
     (assertValueOneOf "DHCP" ["yes" "no" "ipv4" "ipv6" "both" "none" "v4" "v6"])
     (assertValueOneOf "DHCPServer" boolValues)
-    (assertValueOneOf "LinkLocalAddressing" ["yes" "no" "ipv4" "ipv6"])
+    (assertValueOneOf "LinkLocalAddressing" ["yes" "no" "ipv4" "ipv6" "ipv4-fallback" "fallback"])
     (assertValueOneOf "IPv4LLRoute" boolValues)
     (assertValueOneOf "LLMNR" ["yes" "resolve" "no"])
     (assertValueOneOf "MulticastDNS" ["yes" "resolve" "no"])
diff --git a/nixos/modules/system/boot/plymouth.nix b/nixos/modules/system/boot/plymouth.nix
index e4223bae7d32..adca3c3f66e7 100644
--- a/nixos/modules/system/boot/plymouth.nix
+++ b/nixos/modules/system/boot/plymouth.nix
@@ -5,17 +5,20 @@ with lib;
 let
 
   inherit (pkgs) plymouth;
+  inherit (pkgs) nixos-icons;
 
   cfg = config.boot.plymouth;
 
-  breezePlymouth = pkgs.breeze-plymouth.override {
-    nixosBranding = true;
-    nixosVersion = config.system.nixos.release;
+  nixosBreezePlymouth = pkgs.breeze-plymouth.override {
+    logoFile = cfg.logo;
+    logoName = "nixos";
+    osName = "NixOS";
+    osVersion = config.system.nixos.release;
   };
 
   themesEnv = pkgs.buildEnv {
     name = "plymouth-themes";
-    paths = [ plymouth breezePlymouth ] ++ cfg.themePackages;
+    paths = [ plymouth ] ++ cfg.themePackages;
   };
 
   configFile = pkgs.writeText "plymouthd.conf" ''
@@ -35,7 +38,7 @@ in
       enable = mkEnableOption "Plymouth boot splash screen";
 
       themePackages = mkOption {
-        default = [];
+        default = [ nixosBreezePlymouth ];
         type = types.listOf types.package;
         description = ''
           Extra theme packages for plymouth.
@@ -52,10 +55,7 @@ in
 
       logo = mkOption {
         type = types.path;
-        default = pkgs.fetchurl {
-          url = "https://nixos.org/logo/nixos-hires.png";
-          sha256 = "1ivzgd7iz0i06y36p8m5w48fd8pjqwxhdaavc0pxs7w1g7mcy5si";
-        };
+        default = "${nixos-icons}/share/icons/hicolor/128x128/apps/nix-snowflake.png";
         defaultText = ''pkgs.fetchurl {
           url = "https://nixos.org/logo/nixos-hires.png";
           sha256 = "1ivzgd7iz0i06y36p8m5w48fd8pjqwxhdaavc0pxs7w1g7mcy5si";
@@ -88,10 +88,7 @@ in
     systemd.services.plymouth-kexec.wantedBy = [ "kexec.target" ];
     systemd.services.plymouth-halt.wantedBy = [ "halt.target" ];
     systemd.services.plymouth-quit-wait.wantedBy = [ "multi-user.target" ];
-    systemd.services.plymouth-quit = {
-      wantedBy = [ "multi-user.target" ];
-      after = [ "display-manager.service" ];
-    };
+    systemd.services.plymouth-quit.wantedBy = [ "multi-user.target" ];
     systemd.services.plymouth-poweroff.wantedBy = [ "poweroff.target" ];
     systemd.services.plymouth-reboot.wantedBy = [ "reboot.target" ];
     systemd.services.plymouth-read-write.wantedBy = [ "sysinit.target" ];
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index b817a45deba3..f520bf54ad1b 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -183,6 +183,12 @@ for o in $(cat /proc/cmdline); do
         copytoram)
             copytoram=1
             ;;
+        findiso=*)
+            # if an iso name is supplied, try to find the device where
+            # the iso resides on
+            set -- $(IFS==; echo $o)
+            isoPath=$2
+            ;;
     esac
 done
 
@@ -442,6 +448,27 @@ if test -e /sys/power/resume -a -e /sys/power/disk; then
     fi
 fi
 
+# If we have a path to an iso file, find the iso and link it to /dev/root
+if [ -n "$isoPath" ]; then
+  mkdir -p /findiso
+
+  for delay in 5 10; do
+    blkid | while read -r line; do
+      device=$(echo "$line" | sed 's/:.*//')
+      type=$(echo "$line" | sed 's/.*TYPE="\([^"]*\)".*/\1/')
+
+      mount -t "$type" "$device" /findiso
+      if [ -e "/findiso$isoPath" ]; then
+        ln -sf "/findiso$isoPath" /dev/root
+        break 2
+      else
+        umount /findiso
+      fi
+    done
+
+    sleep "$delay"
+  done
+fi
 
 # Try to find and mount the root device.
 mkdir -p $targetRoot
diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
index 03daafa1ce4f..d1de7920df98 100644
--- a/nixos/modules/system/boot/stage-2-init.sh
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -142,7 +142,7 @@ fi
 # Record the boot configuration.
 ln -sfn "$systemConfig" /run/booted-system
 
-# Prevent the booted system form being garbage-collected If it weren't
+# Prevent the booted system from being garbage-collected. If it weren't
 # a gcroot, if we were running a different kernel, switched system,
 # and garbage collected all, we could not load kernel modules anymore.
 ln -sfn /run/booted-system /nix/var/nix/gcroots/booted-system
diff --git a/nixos/modules/system/boot/systemd-nspawn.nix b/nixos/modules/system/boot/systemd-nspawn.nix
index db6e06b41072..3ddd45b13482 100644
--- a/nixos/modules/system/boot/systemd-nspawn.nix
+++ b/nixos/modules/system/boot/systemd-nspawn.nix
@@ -113,11 +113,21 @@ in {
   config =
     let
       units = mapAttrs' (n: v: let nspawnFile = "${n}.nspawn"; in nameValuePair nspawnFile (instanceToUnit nspawnFile v)) cfg;
-    in mkIf (cfg != {}) {
-
-      environment.etc."systemd/nspawn".source = generateUnits "nspawn" units [] [];
-
-      systemd.targets.multi-user.wants = [ "machines.target" ];
-  };
-
+    in 
+      mkMerge [
+        (mkIf (cfg != {}) { 
+          environment.etc."systemd/nspawn".source = mkIf (cfg != {}) (generateUnits "nspawn" units [] []);
+        })
+        {
+          systemd.targets.multi-user.wants = [ "machines.target" ];
+
+          # Workaround for https://github.com/NixOS/nixpkgs/pull/67232#issuecomment-531315437 and https://github.com/systemd/systemd/issues/13622
+          # Once systemd fixes this upstream, we can re-enable -U
+          systemd.services."systemd-nspawn@".serviceConfig.ExecStart = [ 
+            ""  # deliberately empty. signals systemd to override the ExecStart
+            # Only difference between upstream is that we do not pass the -U flag
+            "${pkgs.systemd}/bin/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth --settings=override --machine=%i"
+          ];
+        }
+      ];
 }
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 2287a82418fe..33b350902a1a 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -546,6 +546,16 @@ in
       '';
     };
 
+    systemd.coredump.enable = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        Whether core dumps should be processed by
+        <command>systemd-coredump</command>. If disabled, core dumps
+        appear in the current directory of the crashing process.
+      '';
+    };
+
     systemd.coredump.extraConfig = mkOption {
       default = "";
       type = types.lines;
@@ -858,7 +868,12 @@ in
       "sysctl.d/50-coredump.conf".source = "${systemd}/example/sysctl.d/50-coredump.conf";
       "sysctl.d/50-default.conf".source = "${systemd}/example/sysctl.d/50-default.conf";
 
+      "tmpfiles.d/journal-nocow.conf".source = "${systemd}/example/tmpfiles.d/journal-nocow.conf";
+      "tmpfiles.d/static-nodes-permissions.conf".source = "${systemd}/example/tmpfiles.d/static-nodes-permissions.conf";
       "tmpfiles.d/systemd.conf".source = "${systemd}/example/tmpfiles.d/systemd.conf";
+      "tmpfiles.d/systemd-nspawn.conf".source = "${systemd}/example/tmpfiles.d/systemd-nspawn.conf";
+      "tmpfiles.d/systemd-tmp.conf".source = "${systemd}/example/tmpfiles.d/systemd-tmp.conf";
+      "tmpfiles.d/var.conf".source = "${systemd}/example/tmpfiles.d/var.conf";
       "tmpfiles.d/x11.conf".source = "${systemd}/example/tmpfiles.d/x11.conf";
 
       "tmpfiles.d/nixos.conf".text = ''
@@ -978,6 +993,10 @@ in
     # Don't bother with certain units in containers.
     systemd.services.systemd-remount-fs.unitConfig.ConditionVirtualization = "!container";
     systemd.services.systemd-random-seed.unitConfig.ConditionVirtualization = "!container";
+
+    boot.kernel.sysctl = mkIf (!cfg.coredump.enable) {
+      "kernel.core_pattern" = "core";
+    };
   };
 
   # FIXME: Remove these eventually.
diff --git a/nixos/modules/tasks/auto-upgrade.nix b/nixos/modules/tasks/auto-upgrade.nix
index 18753ae0c1ae..7fe066991918 100644
--- a/nixos/modules/tasks/auto-upgrade.nix
+++ b/nixos/modules/tasks/auto-upgrade.nix
@@ -88,7 +88,7 @@ let cfg = config.system.autoUpgrade; in
           HOME = "/root";
         } // config.networking.proxy.envVars;
 
-      path = [ pkgs.coreutils pkgs.gnutar pkgs.xz.bin pkgs.gitMinimal config.nix.package.out ];
+      path = with pkgs; [ coreutils gnutar xz.bin gzip gitMinimal config.nix.package.out ];
 
       script = let
           nixos-rebuild = "${config.system.build.nixos-rebuild}/bin/nixos-rebuild";
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 2ed8c5aa2927..fe11917c609c 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -16,9 +16,7 @@ let
   inInitrd = any (fs: fs == "zfs") config.boot.initrd.supportedFilesystems;
   inSystem = any (fs: fs == "zfs") config.boot.supportedFilesystems;
 
-  enableAutoSnapshots = cfgSnapshots.enable;
-  enableAutoScrub = cfgScrub.enable;
-  enableZfs = inInitrd || inSystem || enableAutoSnapshots || enableAutoScrub;
+  enableZfs = inInitrd || inSystem;
 
   kernel = config.boot.kernelPackages;
 
@@ -268,7 +266,12 @@ in
     };
 
     services.zfs.trim = {
-      enable = mkEnableOption "Enables periodic TRIM on all ZFS pools.";
+      enable = mkOption {
+        description = "Whether to enable periodic TRIM on all ZFS pools.";
+        default = true;
+        example = false;
+        type = types.bool;
+      };
 
       interval = mkOption {
         default = "weekly";
@@ -387,10 +390,11 @@ in
       };
 
       environment.etc."zfs/zed.d".source = "${packages.zfsUser}/etc/zfs/zed.d/";
+      environment.etc."zfs/zpool.d".source = "${packages.zfsUser}/etc/zfs/zpool.d/";
 
       system.fsPackages = [ packages.zfsUser ]; # XXX: needed? zfs doesn't have (need) a fsck
       environment.systemPackages = [ packages.zfsUser ]
-        ++ optional enableAutoSnapshots autosnapPkg; # so the user can run the command to see flags
+        ++ optional cfgSnapshots.enable autosnapPkg; # so the user can run the command to see flags
 
       services.udev.packages = [ packages.zfsUser ]; # to hook zvol naming, etc.
       systemd.packages = [ packages.zfsUser ];
@@ -482,7 +486,7 @@ in
       systemd.targets.zfs.wantedBy = [ "multi-user.target" ];
     })
 
-    (mkIf enableAutoSnapshots {
+    (mkIf (enableZfs && cfgSnapshots.enable) {
       systemd.services = let
                            descr = name: if name == "frequent" then "15 mins"
                                     else if name == "hourly" then "hour"
@@ -520,7 +524,7 @@ in
                             }) snapshotNames);
     })
 
-    (mkIf enableAutoScrub {
+    (mkIf (enableZfs && cfgScrub.enable) {
       systemd.services.zfs-scrub = {
         description = "ZFS pools scrubbing";
         after = [ "zfs-import.target" ];
@@ -547,15 +551,13 @@ in
       };
     })
 
-    (mkIf cfgTrim.enable {
+    (mkIf (enableZfs && cfgTrim.enable) {
       systemd.services.zpool-trim = {
         description = "ZFS pools trim";
         after = [ "zfs-import.target" ];
         path = [ packages.zfsUser ];
         startAt = cfgTrim.interval;
-        script = ''
-          zpool list -H -o name | xargs -n1 zpool trim
-        '';
+        serviceConfig.ExecStart = "${pkgs.runtimeShell} -c 'zpool list -H -o name | xargs --no-run-if-empty -n1 zpool trim'";
       };
     })
   ];
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index 34e270667151..9ffa1089ee69 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -12,7 +12,7 @@ let
     i.ipv4.addresses
     ++ optionals cfg.enableIPv6 i.ipv6.addresses;
 
-  dhcpStr = useDHCP: if useDHCP == true || useDHCP == null then "both" else "no";
+  dhcpStr = useDHCP: if useDHCP == true || useDHCP == null then "yes" else "no";
 
   slaves =
     concatLists (map (bond: bond.interfaces) (attrValues cfg.bonds))
@@ -31,13 +31,19 @@ in
       message = "networking.defaultGatewayWindowSize is not supported by networkd.";
     } {
       assertion = cfg.vswitches == {};
-      message = "networking.vswichtes are not supported by networkd.";
+      message = "networking.vswitches are not supported by networkd.";
     } {
       assertion = cfg.defaultGateway == null || cfg.defaultGateway.interface == null;
       message = "networking.defaultGateway.interface is not supported by networkd.";
     } {
       assertion = cfg.defaultGateway6 == null || cfg.defaultGateway6.interface == null;
       message = "networking.defaultGateway6.interface is not supported by networkd.";
+    } {
+      assertion = cfg.useDHCP == false;
+      message = ''
+        networking.useDHCP is not supported by networkd.
+        Please use per interface configuration and set the global option to false.
+      '';
     } ] ++ flip mapAttrsToList cfg.bridges (n: { rstp, ... }: {
       assertion = !rstp;
       message = "networking.bridges.${n}.rstp is not supported by networkd.";
@@ -56,9 +62,7 @@ in
         genericNetwork = override:
           let gateway = optional (cfg.defaultGateway != null) cfg.defaultGateway.address
             ++ optional (cfg.defaultGateway6 != null) cfg.defaultGateway6.address;
-          in {
-            DHCP = override (dhcpStr cfg.useDHCP);
-          } // optionalAttrs (gateway != [ ]) {
+          in optionalAttrs (gateway != [ ]) {
             routes = override [
               {
                 routeConfig = {
@@ -72,15 +76,6 @@ in
           };
       in mkMerge [ {
         enable = true;
-        networks."99-main" = (genericNetwork mkDefault) // {
-          # We keep the "broken" behaviour of applying this to all interfaces.
-          # In general we want to get rid of this workaround but there hasn't
-          # been any work on that.
-          # See the following issues for details:
-          # - https://github.com/NixOS/nixpkgs/issues/18962
-          # - https://github.com/NixOS/nixpkgs/issues/61629
-          matchConfig = mkDefault { Name = "*"; };
-        };
       }
       (mkMerge (forEach interfaces (i: {
         netdevs = mkIf i.virtual ({
@@ -97,7 +92,7 @@ in
         networks."40-${i.name}" = mkMerge [ (genericNetwork mkDefault) {
           name = mkDefault i.name;
           DHCP = mkForce (dhcpStr
-            (if i.useDHCP != null then i.useDHCP else cfg.useDHCP && interfaceIps i == [ ]));
+            (if i.useDHCP != null then i.useDHCP else false));
           address = forEach (interfaceIps i)
             (ip: "${ip.address}/${toString ip.prefixLength}");
           networkConfig.IPv6PrivacyExtensions = "kernel";
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 5bf7b0d227f0..31e2ed1cd1ea 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -305,7 +305,7 @@ let
             optional (defined ipv6Address && defined ipv6PrefixLength)
             { address = ipv6Address; prefixLength = ipv6PrefixLength; }))
 
-        ({ options.warnings = options.warnings; })
+        ({ options.warnings = options.warnings; options.assertions = options.assertions; })
       ];
 
   };
@@ -903,6 +903,11 @@ in
         Whether to use DHCP to obtain an IP address and other
         configuration for all network interfaces that are not manually
         configured.
+
+        Using this option is highly discouraged and also incompatible with
+        <option>networking.useNetworkd</option>. Please use
+        <option>networking.interfaces.&lt;name&gt;.useDHCP</option> instead
+        and set this to false.
       '';
     };
 
@@ -967,9 +972,9 @@ in
       "net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6);
       "net.ipv6.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces);
     } // listToAttrs (flip concatMap (filter (i: i.proxyARP) interfaces)
-        (i: forEach [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${i.name}.proxy_arp" true)))
+        (i: forEach [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${replaceChars ["."] ["/"] i.name}.proxy_arp" true)))
       // listToAttrs (forEach (filter (i: i.preferTempAddress) interfaces)
-        (i: nameValuePair "net.ipv6.conf.${i.name}.use_tempaddr" 2));
+        (i: nameValuePair "net.ipv6.conf.${replaceChars ["."] ["/"] i.name}.use_tempaddr" 2));
 
     # Capabilities won't work unless we have at-least a 4.3 Linux
     # kernel because we need the ambient capability
@@ -1092,7 +1097,7 @@ in
         destination = "/etc/udev/rules.d/98-${name}";
         text = ''
           # enable and prefer IPv6 privacy addresses by default
-          ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.procps}/bin/sysctl net.ipv6.conf.%k.use_tempaddr=2"
+          ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.bash}/bin/sh -c 'echo 2 > /proc/sys/net/ipv6/conf/%k/use_tempaddr'"
         '';
       })
       (pkgs.writeTextFile rec {
@@ -1100,7 +1105,7 @@ in
         destination = "/etc/udev/rules.d/99-${name}";
         text = concatMapStrings (i: ''
           # enable IPv6 privacy addresses but prefer EUI-64 addresses for ${i.name}
-          ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.procps}/bin/sysctl net.ipv6.conf.${i.name}.use_tempaddr=1"
+          ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.procps}/bin/sysctl net.ipv6.conf.${replaceChars ["."] ["/"] i.name}.use_tempaddr=1"
         '') (filter (i: !i.preferTempAddress) interfaces);
       })
     ] ++ lib.optional (cfg.wlanInterfaces != {})
diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix
index aadfc5add350..20d48add7129 100644
--- a/nixos/modules/virtualisation/amazon-image.nix
+++ b/nixos/modules/virtualisation/amazon-image.nix
@@ -135,6 +135,9 @@ in
     services.openssh.enable = true;
     services.openssh.permitRootLogin = "prohibit-password";
 
+    # Creates symlinks for block device names.
+    services.udev.packages = [ pkgs.ec2-utils ];
+
     # Force getting the hostname from EC2.
     networking.hostName = mkDefault "";
 
diff --git a/nixos/modules/virtualisation/container-config.nix b/nixos/modules/virtualisation/container-config.nix
index 604fb8a75932..f7a37d8c9f3b 100644
--- a/nixos/modules/virtualisation/container-config.nix
+++ b/nixos/modules/virtualisation/container-config.nix
@@ -7,6 +7,7 @@ with lib;
   config = mkIf config.boot.isContainer {
 
     # Disable some features that are not useful in a container.
+    nix.optimise.automatic = mkDefault false; # the store is host managed
     services.udisks2.enable = mkDefault false;
     powerManagement.enable = mkDefault false;
 
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index 9c9f8fc0c215..691ee2c136ec 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -824,5 +824,12 @@ in
     '';
 
     environment.systemPackages = [ pkgs.nixos-container ];
+
+    boot.kernelModules = [
+      "bridge"
+      "macvlan"
+      "tap"
+      "tun"
+    ];
   });
 }
diff --git a/nixos/modules/virtualisation/cri-o.nix b/nixos/modules/virtualisation/cri-o.nix
new file mode 100644
index 000000000000..14a435f6c8bb
--- /dev/null
+++ b/nixos/modules/virtualisation/cri-o.nix
@@ -0,0 +1,106 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.virtualisation.cri-o;
+in
+{
+  options.virtualisation.cri-o = {
+    enable = mkEnableOption "Container Runtime Interface for OCI (CRI-O)";
+
+    storageDriver = mkOption {
+      type = types.enum ["btrfs" "overlay" "vfs"];
+      default = "overlay";
+      description = "Storage driver to be used";
+    };
+
+    logLevel = mkOption {
+      type = types.enum ["trace" "debug" "info" "warn" "error" "fatal"];
+      default = "info";
+      description = "Log level to be used";
+    };
+
+    pauseImage = mkOption {
+      type = types.str;
+      default = "k8s.gcr.io/pause:3.1";
+      description = "Pause image for pod sandboxes to be used";
+    };
+
+    pauseCommand = mkOption {
+      type = types.str;
+      default = "/pause";
+      description = "Pause command to be executed";
+    };
+
+    registries = mkOption {
+      type = types.listOf types.str;
+      default = [ "docker.io" "quay.io" ];
+      description = "Registries to be configured for unqualified image pull";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs;
+      [ cri-o cri-tools conmon cni-plugins iptables runc utillinux ];
+    environment.etc."crictl.yaml".text = ''
+      runtime-endpoint: unix:///var/run/crio/crio.sock
+    '';
+    environment.etc."crio/crio.conf".text = ''
+      [crio]
+      storage_driver = "${cfg.storageDriver}"
+
+      [crio.image]
+      pause_image = "${cfg.pauseImage}"
+      pause_command = "${cfg.pauseCommand}"
+      registries = [
+        ${concatMapStringsSep ", " (x: "\"" + x + "\"") cfg.registries}
+      ]
+
+      [crio.runtime]
+      conmon = "${pkgs.conmon}/bin/conmon"
+      log_level = "${cfg.logLevel}"
+      manage_network_ns_lifecycle = true
+    '';
+    environment.etc."containers/policy.json".text = ''
+      {"default": [{"type": "insecureAcceptAnything"}]}
+    '';
+    environment.etc."cni/net.d/20-cri-o-bridge.conf".text = ''
+      {
+        "cniVersion": "0.3.1",
+        "name": "crio-bridge",
+        "type": "bridge",
+        "bridge": "cni0",
+        "isGateway": true,
+        "ipMasq": true,
+        "ipam": {
+          "type": "host-local",
+          "subnet": "10.88.0.0/16",
+          "routes": [
+              { "dst": "0.0.0.0/0" }
+          ]
+        }
+      }
+    '';
+
+    systemd.services.crio = {
+      description = "Container Runtime Interface for OCI (CRI-O)";
+      documentation = [ "https://github.com/cri-o/cri-o" ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      path = [ pkgs.utillinux pkgs.runc pkgs.iptables ];
+      serviceConfig = {
+        Type = "notify";
+        ExecStart = "${pkgs.cri-o}/bin/crio";
+        ExecReload = "/bin/kill -s HUP $MAINPID";
+        TasksMax = "infinity";
+        LimitNOFILE = "1048576";
+        LimitNPROC = "1048576";
+        LimitCORE = "infinity";
+        OOMScoreAdjust = "-999";
+        TimeoutStartSec = "0";
+        Restart = "on-abnormal";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/virtualisation/ec2-amis.nix b/nixos/modules/virtualisation/ec2-amis.nix
index f640bb21b133..3b4e55d39d7b 100644
--- a/nixos/modules/virtualisation/ec2-amis.nix
+++ b/nixos/modules/virtualisation/ec2-amis.nix
@@ -291,5 +291,21 @@ let self = {
   "19.03".sa-east-1.hvm-ebs = "ami-0c6a43c6e0ad1f4e2";
   "19.03".ap-south-1.hvm-ebs = "ami-0303deb1b5890f878";
 
-  latest = self."19.03";
+  # 19.09.981.205691b7cbe
+  "19.09".eu-west-1.hvm-ebs = "ami-0ebd3156e21e9642f";
+  "19.09".eu-west-2.hvm-ebs = "ami-02a2b5480a79084b7";
+  "19.09".eu-west-3.hvm-ebs = "ami-09aa175c7588734f7";
+  "19.09".eu-central-1.hvm-ebs = "ami-00a7fafd7e237a330";
+  "19.09".us-east-1.hvm-ebs = "ami-00a8eeaf232a74f84";
+  "19.09".us-east-2.hvm-ebs = "ami-093efd3a57a1e03a8";
+  "19.09".us-west-1.hvm-ebs = "ami-0913e9a2b677fac30";
+  "19.09".us-west-2.hvm-ebs = "ami-02d9a19f77b47882a";
+  "19.09".ca-central-1.hvm-ebs = "ami-0627dd3f7b3627a29";
+  "19.09".ap-southeast-1.hvm-ebs = "ami-083614e4d08f2164d";
+  "19.09".ap-southeast-2.hvm-ebs = "ami-0048c704185ded6dc";
+  "19.09".ap-northeast-1.hvm-ebs = "ami-0329e7fc2d7f60bd0";
+  "19.09".ap-northeast-2.hvm-ebs = "ami-03d4ae7d0b5fc364f";
+  "19.09".ap-south-1.hvm-ebs = "ami-0b599690b35aeef23";
+
+  latest = self."19.09";
 }; in self
diff --git a/nixos/modules/virtualisation/virtualbox-host.nix b/nixos/modules/virtualisation/virtualbox-host.nix
index 6081d4153a6c..ddb0a7bda4f3 100644
--- a/nixos/modules/virtualisation/virtualbox-host.nix
+++ b/nixos/modules/virtualisation/virtualbox-host.nix
@@ -149,5 +149,12 @@ in
     # Make sure NetworkManager won't assume this interface being up
     # means we have internet access.
     networking.networkmanager.unmanaged = ["vboxnet0"];
-  })]);
+  }) (mkIf config.networking.useNetworkd {
+    systemd.network.networks."40-vboxnet0".extraConfig = ''
+      [Link]
+      RequiredForOnline=no
+    '';
+  })
+
+]);
 }
diff --git a/nixos/release.nix b/nixos/release.nix
index c7c609658909..f40b5fa9bd7f 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -180,6 +180,11 @@ in rec {
     inherit system;
   });
 
+  sd_image_raspberrypi4 = forMatchingSystems [ "aarch64-linux" ] (system: makeSdImage {
+    module = ./modules/installer/cd-dvd/sd-image-raspberrypi4.nix;
+    inherit system;
+  });
+
   # A bootable VirtualBox virtual appliance as an OVA file (i.e. packaged OVF).
   ova = forMatchingSystems [ "x86_64-linux" ] (system:
 
@@ -292,6 +297,12 @@ in rec {
         services.xserver.desktopManager.xfce.enable = true;
       });
 
+    gnome3 = makeClosure ({ ... }:
+      { services.xserver.enable = true;
+        services.xserver.displayManager.gdm.enable = true;
+        services.xserver.desktopManager.gnome3.enable = true;
+      });
+
     # Linux/Apache/PostgreSQL/PHP stack.
     lapp = makeClosure ({ pkgs, ... }:
       { services.httpd.enable = true;
diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix
index 8cfdea4a16ef..206d97849f02 100644
--- a/nixos/tests/acme.nix
+++ b/nixos/tests/acme.nix
@@ -12,8 +12,11 @@ in import ./make-test.nix {
       networking.extraHosts = ''
         ${config.networking.primaryIPAddress} standalone.com
       '';
-      security.acme.certs."standalone.com" = {
-        webroot = "/var/lib/acme/acme-challenges";
+      security.acme = {
+        server = "https://acme-v02.api.letsencrypt.org/dir";
+        certs."standalone.com" = {
+            webroot = "/var/lib/acme/acme-challenges";
+        };
       };
       systemd.targets."acme-finished-standalone.com" = {};
       systemd.services."acme-standalone.com" = {
@@ -54,6 +57,8 @@ in import ./make-test.nix {
         '';
       };
 
+      security.acme.server = "https://acme-v02.api.letsencrypt.org/dir";
+
       nesting.clone = [
         ({pkgs, ...}: {
 
@@ -80,7 +85,7 @@ in import ./make-test.nix {
     client = commonConfig;
   };
 
-  testScript = {nodes, ...}: 
+  testScript = {nodes, ...}:
     let
       newServerSystem = nodes.webserver2.config.system.build.toplevel;
       switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test";
@@ -91,9 +96,12 @@ in import ./make-test.nix {
     # get pulled in by the oneshot units. The target units linger after activation, and hence we
     # can use them to probe that a oneshot fired. It is a bit ugly, but it is the best we can do
     ''
-      $client->waitForUnit("default.target");
+      $client->start;
+      $letsencrypt->start;
+      $acmeStandalone->start;
+
       $letsencrypt->waitForUnit("default.target");
-      $letsencrypt->waitForUnit("boulder.service");
+      $letsencrypt->waitForUnit("pebble.service");
 
       subtest "can request certificate with HTTPS-01 challenge", sub {
         $acmeStandalone->waitForUnit("default.target");
@@ -101,15 +109,20 @@ in import ./make-test.nix {
         $acmeStandalone->waitForUnit("acme-finished-standalone.com.target");
       };
 
+      $client->waitForUnit("default.target");
+
+      $client->succeed('curl https://acme-v02.api.letsencrypt.org:15000/roots/0 > /tmp/ca.crt');
+      $client->succeed('curl https://acme-v02.api.letsencrypt.org:15000/intermediate-keys/0 >> /tmp/ca.crt');
+
       subtest "Can request certificate for nginx service", sub {
         $webserver->waitForUnit("acme-finished-a.example.com.target");
-        $client->succeed('curl https://a.example.com/ | grep -qF "hello world"');
+        $client->succeed('curl --cacert /tmp/ca.crt https://a.example.com/ | grep -qF "hello world"');
       };
 
       subtest "Can add another certificate for nginx service", sub {
         $webserver->succeed("/run/current-system/fine-tune/child-1/bin/switch-to-configuration test");
         $webserver->waitForUnit("acme-finished-b.example.com.target");
-        $client->succeed('curl https://b.example.com/ | grep -qF "hello world"');
+        $client->succeed('curl --cacert /tmp/ca.crt https://b.example.com/ | grep -qF "hello world"');
       };
     '';
 }
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 60762de76d33..1ecda8d68f5f 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -25,6 +25,7 @@ in
   atd = handleTest ./atd.nix {};
   automysqlbackup = handleTest ./automysqlbackup.nix {};
   avahi = handleTest ./avahi.nix {};
+  babeld = handleTest ./babeld.nix {};
   bcachefs = handleTestOn ["x86_64-linux"] ./bcachefs.nix {}; # linux-4.18.2018.10.12 is unsupported on aarch64
   beanstalkd = handleTest ./beanstalkd.nix {};
   beegfs = handleTestOn ["x86_64-linux"] ./beegfs.nix {}; # beegfs is unsupported on aarch64
@@ -35,6 +36,7 @@ in
   boot-stage1 = handleTest ./boot-stage1.nix {};
   borgbackup = handleTest ./borgbackup.nix {};
   buildbot = handleTest ./buildbot.nix {};
+  caddy = handleTest ./caddy.nix {};
   cadvisor = handleTestOn ["x86_64-linux"] ./cadvisor.nix {};
   cassandra = handleTest ./cassandra.nix {};
   ceph = handleTestOn ["x86_64-linux"] ./ceph.nix {};
@@ -79,6 +81,7 @@ in
   env = handleTest ./env.nix {};
   etcd = handleTestOn ["x86_64-linux"] ./etcd.nix {};
   etcd-cluster = handleTestOn ["x86_64-linux"] ./etcd-cluster.nix {};
+  fancontrol = handleTest ./fancontrol.nix {};
   ferm = handleTest ./ferm.nix {};
   firefox = handleTest ./firefox.nix {};
   firewall = handleTest ./firewall.nix {};
@@ -91,6 +94,7 @@ in
   fsck = handleTest ./fsck.nix {};
   fwupd = handleTestOn ["x86_64-linux"] ./fwupd.nix {}; # libsmbios is unsupported on aarch64
   gdk-pixbuf = handleTest ./gdk-pixbuf.nix {};
+  gotify-server = handleTest ./gotify-server.nix {};
   gitea = handleTest ./gitea.nix {};
   gitlab = handleTest ./gitlab.nix {};
   gitolite = handleTest ./gitolite.nix {};
@@ -155,6 +159,7 @@ in
   #logstash = handleTest ./logstash.nix {};
   mailcatcher = handleTest ./mailcatcher.nix {};
   mathics = handleTest ./mathics.nix {};
+  matomo = handleTest ./matomo.nix {};
   matrix-synapse = handleTest ./matrix-synapse.nix {};
   mediawiki = handleTest ./mediawiki.nix {};
   memcached = handleTest ./memcached.nix {};
@@ -162,6 +167,7 @@ in
   metabase = handleTest ./metabase.nix {};
   miniflux = handleTest ./miniflux.nix {};
   minio = handleTest ./minio.nix {};
+  minidlna = handleTest ./minidlna.nix {};
   misc = handleTest ./misc.nix {};
   mongodb = handleTest ./mongodb.nix {};
   moodle = handleTest ./moodle.nix {};
@@ -198,12 +204,15 @@ in
   novacomd = handleTestOn ["x86_64-linux"] ./novacomd.nix {};
   nsd = handleTest ./nsd.nix {};
   nzbget = handleTest ./nzbget.nix {};
+  openarena = handleTest ./openarena.nix {};
   openldap = handleTest ./openldap.nix {};
   opensmtpd = handleTest ./opensmtpd.nix {};
   openssh = handleTest ./openssh.nix {};
   # openstack-image-userdata doesn't work in a sandbox as the simulated openstack instance needs network access
   #openstack-image-userdata = (handleTestOn ["x86_64-linux"] ./openstack-image.nix {}).userdata or {};
   openstack-image-metadata = (handleTestOn ["x86_64-linux"] ./openstack-image.nix {}).metadata or {};
+  orangefs = handleTest ./orangefs.nix {};
+  os-prober = handleTestOn ["x86_64-linux"] ./os-prober.nix {};
   osquery = handleTest ./osquery.nix {};
   osrm-backend = handleTest ./osrm-backend.nix {};
   ostree = handleTest ./ostree.nix {};
@@ -223,6 +232,7 @@ in
   postgresql = handleTest ./postgresql.nix {};
   postgresql-wal-receiver = handleTest ./postgresql-wal-receiver.nix {};
   powerdns = handleTest ./powerdns.nix {};
+  pppd = handleTest ./pppd.nix {};
   predictable-interface-names = handleTest ./predictable-interface-names.nix {};
   printing = handleTest ./printing.nix {};
   prometheus = handleTest ./prometheus.nix {};
@@ -270,6 +280,7 @@ in
   tor = handleTest ./tor.nix {};
   transmission = handleTest ./transmission.nix {};
   trezord = handleTest ./trezord.nix {};
+  trickster = handleTest ./trickster.nix {};
   udisks2 = handleTest ./udisks2.nix {};
   upnp = handleTest ./upnp.nix {};
   uwsgi = handleTest ./uwsgi.nix {};
@@ -286,5 +297,6 @@ in
   xrdp = handleTest ./xrdp.nix {};
   xss-lock = handleTest ./xss-lock.nix {};
   yabar = handleTest ./yabar.nix {};
+  yggdrasil = handleTest ./yggdrasil.nix {};
   zookeeper = handleTest ./zookeeper.nix {};
 }
diff --git a/nixos/tests/babeld.nix b/nixos/tests/babeld.nix
new file mode 100644
index 000000000000..5242cf395d70
--- /dev/null
+++ b/nixos/tests/babeld.nix
@@ -0,0 +1,148 @@
+
+import ./make-test.nix ({ pkgs, lib, ...} : {
+  name = "babeld";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ hexa ];
+  };
+
+  nodes =
+    { client = { pkgs, lib, ... }:
+      {
+        virtualisation.vlans = [ 10 ];
+
+        networking = {
+          useDHCP = false;
+          interfaces."eth1" = {
+            ipv4.addresses = lib.mkForce [ { address = "192.168.10.2"; prefixLength = 24; } ];
+            ipv4.routes = lib.mkForce [ { address = "0.0.0.0"; prefixLength = 0; via = "192.168.10.1"; } ];
+            ipv6.addresses = lib.mkForce [ { address = "2001:db8:10::2"; prefixLength = 64; } ];
+            ipv6.routes = lib.mkForce [ { address = "::"; prefixLength = 0; via = "2001:db8:10::1"; } ];
+          };
+        };
+      };
+
+      localRouter = { pkgs, lib, ... }:
+      {
+        virtualisation.vlans = [ 10 20 ];
+
+        boot.kernel.sysctl."net.ipv4.conf.all.forwarding" = 1;
+        boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = 1;
+
+        networking = {
+          useDHCP = false;
+          firewall.enable = false;
+
+          interfaces."eth1" = {
+            ipv4.addresses = lib.mkForce [ { address = "192.168.10.1"; prefixLength = 24; } ];
+            ipv6.addresses = lib.mkForce [ { address = "2001:db8:10::1"; prefixLength = 64; } ];
+          };
+
+          interfaces."eth2" = {
+            ipv4.addresses = lib.mkForce [ { address = "192.168.20.1"; prefixLength = 24; } ];
+            ipv6.addresses = lib.mkForce [ { address = "2001:db8:20::1"; prefixLength = 64; } ];
+          };
+        };
+
+        services.babeld = {
+          enable = true;
+          interfaces.eth2 = {
+            hello-interval = 1;
+            type = "wired";
+          };
+          extraConfig = ''
+            local-port-readwrite 33123
+
+            import-table 254 # main
+            export-table 254 # main
+
+            in ip 192.168.10.0/24 deny
+            in ip 192.168.20.0/24 deny
+            in ip 2001:db8:10::/64 deny
+            in ip 2001:db8:20::/64 deny
+
+            in ip 192.168.30.0/24 allow
+            in ip 2001:db8:30::/64 allow
+
+            in deny
+
+            redistribute local proto 2
+            redistribute local deny
+          '';
+        };
+      };
+      remoteRouter = { pkgs, lib, ... }:
+      {
+        virtualisation.vlans = [ 20 30 ];
+
+        boot.kernel.sysctl."net.ipv4.conf.all.forwarding" = 1;
+        boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = 1;
+
+        networking = {
+          useDHCP = false;
+          firewall.enable = false;
+
+          interfaces."eth1" = {
+            ipv4.addresses = lib.mkForce [ { address = "192.168.20.2"; prefixLength = 24; } ];
+            ipv6.addresses = lib.mkForce [ { address = "2001:db8:20::2"; prefixLength = 64; } ];
+          };
+
+          interfaces."eth2" = {
+            ipv4.addresses = lib.mkForce [ { address = "192.168.30.1"; prefixLength = 24; } ];
+            ipv6.addresses = lib.mkForce [ { address = "2001:db8:30::1"; prefixLength = 64; } ];
+          };
+        };
+
+        services.babeld = {
+          enable = true;
+          interfaces.eth1 = {
+            hello-interval = 1;
+            type = "wired";
+          };
+          extraConfig = ''
+            local-port-readwrite 33123
+
+            import-table 254 # main
+            export-table 254 # main
+
+            in ip 192.168.20.0/24 deny
+            in ip 192.168.30.0/24 deny
+            in ip 2001:db8:20::/64 deny
+            in ip 2001:db8:30::/64 deny
+
+            in ip 192.168.10.0/24 allow
+            in ip 2001:db8:10::/64 allow
+
+            in deny
+
+            redistribute local proto 2
+            redistribute local deny
+          '';
+        };
+
+      };
+    };
+
+  testScript =
+    ''
+      startAll;
+
+      $client->waitForUnit("network-online.target");
+      $localRouter->waitForUnit("network-online.target");
+      $remoteRouter->waitForUnit("network-online.target");
+
+      $localRouter->waitForUnit("babeld.service");
+      $remoteRouter->waitForUnit("babeld.service");
+
+      $localRouter->waitUntilSucceeds("ip route get 192.168.30.1");
+      $localRouter->waitUntilSucceeds("ip route get 2001:db8:30::1");
+
+      $remoteRouter->waitUntilSucceeds("ip route get 192.168.10.1");
+      $remoteRouter->waitUntilSucceeds("ip route get 2001:db8:10::1");
+
+      $client->succeed("ping -c1 192.168.30.1");
+      $client->succeed("ping -c1 2001:db8:30::1");
+
+      $remoteRouter->succeed("ping -c1 192.168.10.2");
+      $remoteRouter->succeed("ping -c1 2001:db8:10::2");
+    '';
+})
diff --git a/nixos/tests/caddy.nix b/nixos/tests/caddy.nix
new file mode 100644
index 000000000000..ab9d2fbf4d1d
--- /dev/null
+++ b/nixos/tests/caddy.nix
@@ -0,0 +1,82 @@
+import ./make-test.nix ({ pkgs, ... }: {
+  name = "caddy";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ xfix ];
+  };
+
+  nodes = {
+    webserver = { pkgs, lib, ... }: {
+      services.caddy.enable = true;
+      services.caddy.config = ''
+        http://localhost {
+          gzip
+
+          root ${
+            pkgs.runCommand "testdir" {} ''
+              mkdir "$out"
+              echo hello world > "$out/example.html"
+            ''
+          }
+        }
+      '';
+
+      nesting.clone = [
+        {
+          services.caddy.config = lib.mkForce ''
+            http://localhost {
+              gzip
+
+              root ${
+                pkgs.runCommand "testdir2" {} ''
+                  mkdir "$out"
+                  echo changed > "$out/example.html"
+                ''
+              }
+            }
+          '';
+        }
+
+        {
+          services.caddy.config = ''
+            http://localhost:8080 {
+            }
+          '';
+        }
+      ];
+    };
+  };
+
+  testScript = { nodes, ... }: let
+    etagSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-1";
+    justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-2";
+  in ''
+    my $url = 'http://localhost/example.html';
+    $webserver->waitForUnit("caddy");
+    $webserver->waitForOpenPort("80");
+
+    sub checkEtag {
+      my $etag = $webserver->succeed(
+        'curl -v '.$url.' 2>&1 | sed -n -e "s/^< [Ee][Tt][Aa][Gg]: *//p"'
+      );
+      $etag =~ s/\r?\n$//;
+      my $httpCode = $webserver->succeed(
+        'curl -w "%{http_code}" -X HEAD -H \'If-None-Match: '.$etag.'\' '.$url
+      );
+      die "HTTP code is not 304" unless $httpCode == 304;
+      return $etag;
+    }
+
+    subtest "check ETag if serving Nix store paths", sub {
+      my $oldEtag = checkEtag;
+      $webserver->succeed("${etagSystem}/bin/switch-to-configuration test >&2");
+      $webserver->sleep(1); # race condition
+      my $newEtag = checkEtag;
+      die "Old ETag $oldEtag is the same as $newEtag" if $oldEtag eq $newEtag;
+    };
+
+    subtest "config is reloaded on nixos-rebuild switch", sub {
+      $webserver->succeed("${justReloadSystem}/bin/switch-to-configuration test >&2");
+      $webserver->waitForOpenPort("8080");
+    };
+  '';
+})
diff --git a/nixos/tests/common/letsencrypt/common.nix b/nixos/tests/common/letsencrypt/common.nix
index 798a749f7f9b..c530de817bf2 100644
--- a/nixos/tests/common/letsencrypt/common.nix
+++ b/nixos/tests/common/letsencrypt/common.nix
@@ -1,27 +1,9 @@
-{ lib, nodes, ... }: {
+{ lib, nodes, pkgs, ... }: let
+  letsencrypt-ca = nodes.letsencrypt.config.test-support.letsencrypt.caCert;
+in {
   networking.nameservers = [
     nodes.letsencrypt.config.networking.primaryIPAddress
   ];
 
-  nixpkgs.overlays = lib.singleton (self: super: {
-    cacert = super.cacert.overrideDerivation (drv: {
-      installPhase = (drv.installPhase or "") + ''
-        cat "${nodes.letsencrypt.config.test-support.letsencrypt.caCert}" \
-          >> "$out/etc/ssl/certs/ca-bundle.crt"
-      '';
-    });
-
-    # Override certifi so that it accepts fake certificate for Let's Encrypt
-    # Need to override the attribute used by simp_le, which is python3Packages
-    python3Packages = (super.python3.override {
-      packageOverrides = lib.const (pysuper: {
-        certifi = pysuper.certifi.overridePythonAttrs (attrs: {
-          postPatch = (attrs.postPatch or "") + ''
-            cat "${self.cacert}/etc/ssl/certs/ca-bundle.crt" \
-              > certifi/cacert.pem
-          '';
-        });
-      });
-    }).pkgs;
-  });
+  security.pki.certificateFiles = [ letsencrypt-ca ];
 }
diff --git a/nixos/tests/common/letsencrypt/default.nix b/nixos/tests/common/letsencrypt/default.nix
index 58d87c64e344..110a2520971d 100644
--- a/nixos/tests/common/letsencrypt/default.nix
+++ b/nixos/tests/common/letsencrypt/default.nix
@@ -1,6 +1,3 @@
-# Fully pluggable module to have Letsencrypt's Boulder ACME service running in
-# a test environment.
-#
 # The certificate for the ACME service is exported as:
 #
 #   config.test-support.letsencrypt.caCert
@@ -54,277 +51,35 @@
 # that it has to be started _before_ the ACME service.
 { config, pkgs, lib, ... }:
 
-let
-  softhsm = pkgs.stdenv.mkDerivation rec {
-    pname = "softhsm";
-    version = "1.3.8";
-
-    src = pkgs.fetchurl {
-      url = "https://dist.opendnssec.org/source/${pname}-${version}.tar.gz";
-      sha256 = "0flmnpkgp65ym7w3qyg78d3fbmvq3aznmi66rgd420n33shf7aif";
-    };
-
-    configureFlags = [ "--with-botan=${pkgs.botan}" ];
-    buildInputs = [ pkgs.sqlite ];
-  };
-
-  pkcs11-proxy = pkgs.stdenv.mkDerivation {
-    name = "pkcs11-proxy";
-
-    src = pkgs.fetchFromGitHub {
-      owner = "SUNET";
-      repo = "pkcs11-proxy";
-      rev = "944684f78bca0c8da6cabe3fa273fed3db44a890";
-      sha256 = "1nxgd29y9wmifm11pjcdpd2y293p0dgi0x5ycis55miy97n0f5zy";
-    };
-
-    postPatch = "patchShebangs mksyscalls.sh";
-
-    nativeBuildInputs = [ pkgs.cmake ];
-    buildInputs = [ pkgs.openssl pkgs.libseccomp ];
-  };
-
-  mkGoDep = { goPackagePath, url ? "https://${goPackagePath}", rev, sha256 }: {
-    inherit goPackagePath;
-    src = pkgs.fetchgit { inherit url rev sha256; };
-  };
-
-  goose = let
-    owner = "liamstask";
-    repo = "goose";
-    rev = "8488cc47d90c8a502b1c41a462a6d9cc8ee0a895";
-    version = "20150116";
-
-  in pkgs.buildGoPackage rec {
-    name = "${repo}-${version}";
-
-    src = pkgs.fetchFromBitbucket {
-      name = "${name}-src";
-      inherit rev owner repo;
-      sha256 = "1jy0pscxjnxjdg3hj111w21g8079rq9ah2ix5ycxxhbbi3f0wdhs";
-    };
-
-    goPackagePath = "bitbucket.org/${owner}/${repo}";
-    subPackages = [ "cmd/goose" ];
-    extraSrcs = map mkGoDep [
-      { goPackagePath = "github.com/go-sql-driver/mysql";
-        rev = "2e00b5cd70399450106cec6431c2e2ce3cae5034";
-        sha256 = "085g48jq9hzmlcxg122n0c4pi41sc1nn2qpx1vrl2jfa8crsppa5";
-      }
-      { goPackagePath = "github.com/kylelemons/go-gypsy";
-        rev = "08cad365cd28a7fba23bb1e57aa43c5e18ad8bb8";
-        sha256 = "1djv7nii3hy451n5jlslk0dblqzb1hia1cbqpdwhnps1g8hqjy8q";
-      }
-      { goPackagePath = "github.com/lib/pq";
-        rev = "ba5d4f7a35561e22fbdf7a39aa0070f4d460cfc0";
-        sha256 = "1mfbqw9g00bk24bfmf53wri5c2wqmgl0qh4sh1qv2da13a7cwwg3";
-      }
-      { goPackagePath = "github.com/mattn/go-sqlite3";
-        rev = "2acfafad5870400156f6fceb12852c281cbba4d5";
-        sha256 = "1rpgil3w4hh1cibidskv1js898hwz83ps06gh0hm3mym7ki8d5h7";
-      }
-      { goPackagePath = "github.com/ziutek/mymysql";
-        rev = "0582bcf675f52c0c2045c027fd135bd726048f45";
-        sha256 = "0bkc9x8sgqbzgdimsmsnhb0qrzlzfv33fgajmmjxl4hcb21qz3rf";
-      }
-      { goPackagePath = "golang.org/x/net";
-        url = "https://go.googlesource.com/net";
-        rev = "10c134ea0df15f7e34d789338c7a2d76cc7a3ab9";
-        sha256 = "14cbr2shl08gyg85n5gj7nbjhrhhgrd52h073qd14j97qcxsakcz";
-      }
-    ];
-  };
-
-  boulder = let
-    owner = "letsencrypt";
-    repo = "boulder";
-    rev = "9c6a1f2adc4c26d925588f5ae366cfd4efb7813a";
-    version = "20180129";
-
-  in pkgs.buildGoPackage rec {
-    name = "${repo}-${version}";
-
-    src = pkgs.fetchFromGitHub {
-      name = "${name}-src";
-      inherit rev owner repo;
-      sha256 = "09kszswrifm9rc6idfaq0p1mz5w21as2qbc8gd5pphrq9cf9pn55";
-    };
-
-    postPatch = ''
-      # compat for go < 1.8
-      sed -i -e 's/time\.Until(\([^)]\+\))/\1.Sub(time.Now())/' \
-        test/ocsp/helper/helper.go
-
-      find test -type f -exec sed -i -e '/libpkcs11-proxy.so/ {
-        s,/usr/local,${pkcs11-proxy},
-      }' {} +
-
-      sed -i -r \
-        -e '/^def +install/a \    return True' \
-        -e 's,exec \./bin/,,' \
-        test/startservers.py
-
-      cat ${lib.escapeShellArg snakeOilCerts.ca.key} > test/test-ca.key
-      cat ${lib.escapeShellArg snakeOilCerts.ca.cert} > test/test-ca.pem
-    '';
-
-    # Until vendored pkcs11 is go 1.9 compatible
-    preBuild = ''
-      rm -r go/src/github.com/letsencrypt/boulder/vendor/github.com/miekg/pkcs11
-    '';
-
-    # XXX: Temporarily brought back putting the source code in the output,
-    # since e95f17e2720e67e2eabd59d7754c814d3e27a0b2 was removing that from
-    # buildGoPackage.
-    preInstall = ''
-      mkdir -p $out
-      pushd "$NIX_BUILD_TOP/go"
-      while read f; do
-        echo "$f" | grep -q '^./\(src\|pkg/[^/]*\)/${goPackagePath}' \
-          || continue
-        mkdir -p "$(dirname "$out/share/go/$f")"
-        cp "$NIX_BUILD_TOP/go/$f" "$out/share/go/$f"
-      done < <(find . -type f)
-      popd
-    '';
-
-    extraSrcs = map mkGoDep [
-      { goPackagePath = "github.com/miekg/pkcs11";
-        rev           = "6dbd569b952ec150d1425722dbbe80f2c6193f83";
-        sha256        = "1m8g6fx7df6hf6q6zsbyw1icjmm52dmsx28rgb0h930wagvngfwb";
-      }
-    ];
-
-    goPackagePath = "github.com/${owner}/${repo}";
-    buildInputs = [ pkgs.libtool ];
-  };
-
-  boulderSource = "${boulder.out}/share/go/src/${boulder.goPackagePath}";
-
-  softHsmConf = pkgs.writeText "softhsm.conf" ''
-    0:/var/lib/softhsm/slot0.db
-    1:/var/lib/softhsm/slot1.db
-  '';
 
+let
   snakeOilCerts = import ./snakeoil-certs.nix;
 
-  wfeDomain = "acme-v01.api.letsencrypt.org";
+  wfeDomain = "acme-v02.api.letsencrypt.org";
   wfeCertFile = snakeOilCerts.${wfeDomain}.cert;
   wfeKeyFile = snakeOilCerts.${wfeDomain}.key;
 
   siteDomain = "letsencrypt.org";
   siteCertFile = snakeOilCerts.${siteDomain}.cert;
   siteKeyFile = snakeOilCerts.${siteDomain}.key;
-
-  # Retrieved via:
-  # curl -s -I https://acme-v01.api.letsencrypt.org/terms \
-  #   | sed -ne 's/^[Ll]ocation: *//p'
-  tosUrl = "https://letsencrypt.org/documents/2017.11.15-LE-SA-v1.2.pdf";
-  tosPath = builtins.head (builtins.match "https?://[^/]+(.*)" tosUrl);
-
-  tosFile = pkgs.fetchurl {
-    url = tosUrl;
-    sha256 = "0yvyckqzj0b1xi61sypcha82nanizzlm8yqy828h2jbza7cxi26c";
-  };
-
+  pebble = pkgs.pebble;
   resolver = let
     message = "You need to define a resolver for the letsencrypt test module.";
     firstNS = lib.head config.networking.nameservers;
   in if config.networking.nameservers == [] then throw message else firstNS;
 
-  cfgDir = pkgs.stdenv.mkDerivation {
-    name = "boulder-config";
-    src = "${boulderSource}/test/config";
-    nativeBuildInputs = [ pkgs.jq ];
-    phases = [ "unpackPhase" "patchPhase" "installPhase" ];
-    postPatch = ''
-      sed -i -e 's/5002/80/' -e 's/5002/443/' va.json
-      sed -i -e '/listenAddress/s/:4000/:80/' wfe.json
-      sed -i -r \
-        -e ${lib.escapeShellArg "s,http://boulder:4000/terms/v1,${tosUrl},g"} \
-        -e 's,http://(boulder|127\.0\.0\.1):4000,https://${wfeDomain},g' \
-        -e '/dnsResolver/s/127\.0\.0\.1:8053/${resolver}:53/' \
-        *.json
-      if grep 4000 *.json; then exit 1; fi
-
-      # Change all ports from 1909X to 909X, because the 1909X range of ports is
-      # allocated by startservers.py in order to intercept gRPC communication.
-      sed -i -e 's/\<1\(909[0-9]\)\>/\1/' *.json
-
-      # Patch out all additional issuer certs
-      jq '. + {ca: (.ca + {Issuers:
-        [.ca.Issuers[] | select(.CertFile == "test/test-ca.pem")]
-      })}' ca.json > tmp
-      mv tmp ca.json
-    '';
-    installPhase = "cp -r . \"$out\"";
-  };
-
-  components = {
-    gsb-test-srv.args = "-apikey my-voice-is-my-passport";
-    gsb-test-srv.waitForPort = 6000;
-    gsb-test-srv.first = true;
-    boulder-sa.args = "--config ${cfgDir}/sa.json";
-    boulder-wfe.args = "--config ${cfgDir}/wfe.json";
-    boulder-ra.args = "--config ${cfgDir}/ra.json";
-    boulder-ca.args = "--config ${cfgDir}/ca.json";
-    boulder-va.args = "--config ${cfgDir}/va.json";
-    boulder-publisher.args = "--config ${cfgDir}/publisher.json";
-    boulder-publisher.waitForPort = 9091;
-    ocsp-updater.args = "--config ${cfgDir}/ocsp-updater.json";
-    ocsp-updater.after = [ "boulder-publisher" ];
-    ocsp-responder.args = "--config ${cfgDir}/ocsp-responder.json";
-    ct-test-srv = {};
-    mail-test-srv.args = let
-      key = "${boulderSource}/test/mail-test-srv/minica-key.pem";
-      crt = "${boulderSource}/test/mail-test-srv/minica.pem";
-     in
-      "--closeFirst 5 --cert ${crt} --key ${key}";
+  pebbleConf.pebble = {
+    listenAddress = "0.0.0.0:443";
+    managementListenAddress = "0.0.0.0:15000";
+    certificate = snakeOilCerts.${wfeDomain}.cert;
+    privateKey = snakeOilCerts.${wfeDomain}.key;
+    httpPort = 80;
+    tlsPort = 443;
+    ocspResponderURL = "http://0.0.0.0:4002";
   };
 
-  commonPath = [ softhsm pkgs.mariadb goose boulder ];
-
-  mkServices = a: b: with lib; listToAttrs (concatLists (mapAttrsToList a b));
-
-  componentServices = mkServices (name: attrs: let
-    mkSrvName = n: "boulder-${n}.service";
-    firsts = lib.filterAttrs (lib.const (c: c.first or false)) components;
-    firstServices = map mkSrvName (lib.attrNames firsts);
-    firstServicesNoSelf = lib.remove "boulder-${name}.service" firstServices;
-    additionalAfter = firstServicesNoSelf ++ map mkSrvName (attrs.after or []);
-    needsPort = attrs ? waitForPort;
-    inits = map (n: "boulder-init-${n}.service") [ "mysql" "softhsm" ];
-    portWaiter = {
-      name = "boulder-${name}";
-      value = {
-        description = "Wait For Port ${toString attrs.waitForPort} (${name})";
-        after = [ "boulder-real-${name}.service" "bind.service" ];
-        requires = [ "boulder-real-${name}.service" ];
-        requiredBy = [ "boulder.service" ];
-        serviceConfig.Type = "oneshot";
-        serviceConfig.RemainAfterExit = true;
-        script = let
-          netcat = "${pkgs.libressl.nc}/bin/nc";
-          portCheck = "${netcat} -z 127.0.0.1 ${toString attrs.waitForPort}";
-        in "while ! ${portCheck}; do :; done";
-      };
-    };
-  in lib.optional needsPort portWaiter ++ lib.singleton {
-    name = if needsPort then "boulder-real-${name}" else "boulder-${name}";
-    value = {
-      description = "Boulder ACME Component (${name})";
-      after = inits ++ additionalAfter;
-      requires = inits;
-      requiredBy = [ "boulder.service" ];
-      path = commonPath;
-      environment.GORACE = "halt_on_error=1";
-      environment.SOFTHSM_CONF = softHsmConf;
-      environment.PKCS11_PROXY_SOCKET = "tcp://127.0.0.1:5657";
-      serviceConfig.WorkingDirectory = boulderSource;
-      serviceConfig.ExecStart = "${boulder}/bin/${name} ${attrs.args or ""}";
-      serviceConfig.Restart = "on-failure";
-    };
-  }) components;
+  pebbleConfFile = pkgs.writeText "pebble.conf" (builtins.toJSON pebbleConf);
+  pebbleDataDir = "/root/pebble";
 
 in {
   imports = [ ../resolver.nix ];
@@ -352,94 +107,29 @@ in {
     networking.firewall.enable = false;
 
     networking.extraHosts = ''
-      127.0.0.1 ${toString [
-        "sa.boulder" "ra.boulder" "wfe.boulder" "ca.boulder" "va.boulder"
-        "publisher.boulder" "ocsp-updater.boulder" "admin-revoker.boulder"
-        "boulder" "boulder-mysql" wfeDomain
-      ]}
+      127.0.0.1 ${wfeDomain}
       ${config.networking.primaryIPAddress} ${wfeDomain} ${siteDomain}
     '';
 
-    services.mysql.enable = true;
-    services.mysql.package = pkgs.mariadb;
-
-    services.nginx.enable = true;
-    services.nginx.recommendedProxySettings = true;
-    # This fixes the test on i686
-    services.nginx.commonHttpConfig = ''
-      server_names_hash_bucket_size 64;
-    '';
-    services.nginx.virtualHosts.${wfeDomain} = {
-      onlySSL = true;
-      enableACME = false;
-      sslCertificate = wfeCertFile;
-      sslCertificateKey = wfeKeyFile;
-      locations."/".proxyPass = "http://127.0.0.1:80";
-    };
-    services.nginx.virtualHosts.${siteDomain} = {
-      onlySSL = true;
-      enableACME = false;
-      sslCertificate = siteCertFile;
-      sslCertificateKey = siteKeyFile;
-      locations."= ${tosPath}".alias = tosFile;
-    };
-
     systemd.services = {
-      pkcs11-daemon = {
-        description = "PKCS11 Daemon";
-        after = [ "boulder-init-softhsm.service" ];
-        before = map (n: "${n}.service") (lib.attrNames componentServices);
-        wantedBy = [ "multi-user.target" ];
-        environment.SOFTHSM_CONF = softHsmConf;
-        environment.PKCS11_DAEMON_SOCKET = "tcp://127.0.0.1:5657";
-        serviceConfig.ExecStart = let
-          softhsmLib = "${softhsm}/lib/softhsm/libsofthsm.so";
-        in "${pkcs11-proxy}/bin/pkcs11-daemon ${softhsmLib}";
-      };
-
-      boulder-init-mysql = {
-        description = "Boulder ACME Init (MySQL)";
-        after = [ "mysql.service" ];
-        serviceConfig.Type = "oneshot";
-        serviceConfig.RemainAfterExit = true;
-        serviceConfig.WorkingDirectory = boulderSource;
-        path = commonPath;
-        script = "${pkgs.bash}/bin/sh test/create_db.sh";
-      };
-
-      boulder-init-softhsm = {
-        description = "Boulder ACME Init (SoftHSM)";
-        environment.SOFTHSM_CONF = softHsmConf;
-        serviceConfig.Type = "oneshot";
-        serviceConfig.RemainAfterExit = true;
-        serviceConfig.WorkingDirectory = boulderSource;
-        preStart = "mkdir -p /var/lib/softhsm";
-        path = commonPath;
+      pebble = {
+        enable = true;
+        description = "Pebble ACME server";
+        requires = [ ];
+        wantedBy = [ "network.target" ];
+        preStart = ''
+          mkdir ${pebbleDataDir}
+        '';
         script = ''
-          softhsm --slot 0 --init-token \
-            --label intermediate --pin 5678 --so-pin 1234
-          softhsm --slot 0 --import test/test-ca.key \
-            --label intermediate_key --pin 5678 --id FB
-          softhsm --slot 1 --init-token \
-            --label root --pin 5678 --so-pin 1234
-          softhsm --slot 1 --import test/test-root.key \
-            --label root_key --pin 5678 --id FA
+          cd ${pebbleDataDir}
+          ${pebble}/bin/pebble -config ${pebbleConfFile}
         '';
+        serviceConfig = {
+          # Required to bind on privileged ports.
+          User = "root";
+          Group = "root";
+        };
       };
-
-      boulder = {
-        description = "Boulder ACME Server";
-        after = map (n: "${n}.service") (lib.attrNames componentServices);
-        wantedBy = [ "multi-user.target" ];
-        serviceConfig.Type = "oneshot";
-        serviceConfig.RemainAfterExit = true;
-        script = let
-          ports = lib.range 8000 8005 ++ lib.singleton 80;
-          netcat = "${pkgs.libressl.nc}/bin/nc";
-          mkPortCheck = port: "${netcat} -z 127.0.0.1 ${toString port}";
-          checks = "(${lib.concatMapStringsSep " && " mkPortCheck ports})";
-        in "while ! ${checks}; do :; done";
-      };
-    } // componentServices;
+    };
   };
 }
diff --git a/nixos/tests/common/letsencrypt/mkcerts.nix b/nixos/tests/common/letsencrypt/mkcerts.nix
index 3b4a589e4142..e7ac2bae46bd 100644
--- a/nixos/tests/common/letsencrypt/mkcerts.nix
+++ b/nixos/tests/common/letsencrypt/mkcerts.nix
@@ -1,7 +1,7 @@
 { pkgs ? import <nixpkgs> {}
 , lib ? pkgs.lib
 
-, domains ? [ "acme-v01.api.letsencrypt.org" "letsencrypt.org" ]
+, domains ? [ "acme-v02.api.letsencrypt.org" "letsencrypt.org" ]
 }:
 
 pkgs.runCommand "letsencrypt-snakeoil-ca" {
diff --git a/nixos/tests/common/letsencrypt/snakeoil-certs.nix b/nixos/tests/common/letsencrypt/snakeoil-certs.nix
index c3d29ab8f163..ca4f71ae688a 100644
--- a/nixos/tests/common/letsencrypt/snakeoil-certs.nix
+++ b/nixos/tests/common/letsencrypt/snakeoil-certs.nix
@@ -2,252 +2,253 @@
 {
   ca.key = builtins.toFile "ca.key" ''
     -----BEGIN PRIVATE KEY-----
-    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
-    Gvfq54FbDuB10XP8JdLrsy9Z6GEsmoE=
+    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
+    Eq4OVTSu/s8gmY94tgbjeOaLUPEPmg==
     -----END PRIVATE KEY-----
   '';
   ca.cert = builtins.toFile "ca.cert" ''
     -----BEGIN CERTIFICATE-----
-    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
+    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
+    kgwJ
     -----END CERTIFICATE-----
   '';
-  "acme-v01.api.letsencrypt.org".key = builtins.toFile "acme-v01.api.letsencrypt.org.key" ''
+  "acme-v02.api.letsencrypt.org".key = builtins.toFile "acme-v02.api.letsencrypt.org.key" ''
     -----BEGIN RSA PRIVATE KEY-----
-    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
+    MIIJKQIBAAKCAgEApny0WhfDwEXe6WDTCw8qBuMAPDr88pj6kbhQWfzAW2c0TggJ
+    Etjs9dktENeTpSl14nnLVMiSYIJPYY3KbOIFQH1qDaOuQ7NaOhj9CdMTm5r9bl+C
+    YAyqLIMQ9AAZDhUcQjOy3moiL7ClFHlkFYuEzZBO9DF7hJpfUFIs0Idg50mNoZh/
+    K/fb4P2skNjfCjjomTRUmZHxT6G00ImSTtSaYbN/WHut1xXwJvOoT1nlEA/PghKm
+    JJ9ZuRMSddUJmjL+sT09L8LVkK8CKeHi4r58DHM0D0u8owIFV9qsXd5UvZHaNgvQ
+    4OAWGukMX+TxRuqkUZkaj84vnNL+ttEMl4jedw0ImzNtCOYehDyTPRkfng5PLWMS
+    vWbwyP8jDd2578mSbx5BF7ypYX366+vknjIFyZ5WezcC1pscIHxLoEwuhuf+knN+
+    kFkLOHeYbqQrU6mxSnu9q0hnNvGUkTP0a/1aLOGRfQ5C/pxpE/Rebi8qfM/OJFd4
+    mSxGL93JUTXWAItiIeBnQpIne65/Ska9dWynOEfIb0okdet3kfmNHz3zc17dZ5g4
+    AdOSCgHAlQgFt/Qd8W6xXUe4C5Mfv2ctxRrfQhDwtB6rMByPwzImnciC2h3vCwD3
+    vS/vjUyWICyhZyi2LZDUQz+sCKBXCYYcYh8ThFO40j5x1OnYMq7XQvyl8QkCAwEA
+    AQKCAgBSAfdssWwRF9m3p6QNPIj9H3AMOxpB/azffqTFzsSJwYp4LWkayZPfffy+
+    4RGvN38D8e6ActP3ifjEGu3tOGBR5fUJhujeHEiDea+a2Ug9S9kuNwmnelWQ23bM
+    Wgf9cdSbn4+qEymHyEFolmsAWdsuzri1fHJVXR06GWBNz4GiLA8B3HY4GD1M1Gfe
+    aZVkGagpXyeVBdiR2xuP5VQWVI8/NQWzdiipW/sRlNABVkyI3uDeN4VzYLL3gTeE
+    p021kQz4DSxIjHZacHpmWwhBnIbKMy0fo7TlrqcnIWXqTwv63Q9Zs/RN8NOyqb0Y
+    t1NKFWafcwUsdOnrG9uv/cVwF1FNE8puydaOi8rL1zAeK89JH8NRQ02wohR9w8qy
+    b2tB6DyGMtuqBt8Il6GA16ZoEuaXeayvlsvDEmG1cS9ZwBvfgrVPAmlm2AYdIf5B
+    RHIJu4BJC6Nn2ehVLqxx1QDhog3SOnAsCmcfg5g/fCwxcVMLIhODFoiKYGeMitDG
+    Q4e5JKcOg+RR8PT/n4eY4rUDBGtsR+Nw8S2DWgXmSufyfDtKCjZB4IuLWPS29tNh
+    zF6iYfoiTWzrSs/yqPSKIFpv+PWZwkKSvjdxia6lSBYYEON4W2QICEtiEs+SvcG4
+    0eIqWM+rRmPnJyMfGqX6GCs3rHDQB2VNJPBCYPQalJ/KwZumAQKCAQEA0ezM6qPJ
+    1JM/fddgeQ50h0T9TRXVUTCISxXza+l4NuFt1NdqUOdHsGtbL1JR4GaQUG8qD1/P
+    R39YgnQEQimxpmYLCZkobkwPxTZm9oiMXpcJrlN4PB5evaWShRSv3mgigpt3Wzml
+    Td+2R9RoA/hvF/wEyIvaWznYOyugBC7GXs20dNnZDULhUapeQu7r6JvgmxBOby7S
+    0FbhGplBiSDETzZURqzH/GMJKaJtNgyyVf3Hbg4mZAQDWoBRr+8HxsNbDkxP6e91
+    QrPHy2VZFiaTmJfoxRhyMTn7/JZaLJaUHDOniOsdMj/V7vMCgpfBqh5vR8bKzuPy
+    ZINggpcFPp1IYQKCAQEAywc7AQoktMBCru/3vzBqUveXbR3RKzNyZCTH5CMm3UNH
+    zmblFgqF2nxzNil21GqAXzSwZk5FyHbkeD3yvEZm+bXzsZTDNokAwoiTgyrr2tf8
+    GLMlCHHl5euIh1xHuyg/oKajVGOoXUXK8piqiDpQKd3Zwc6u2oyQlh+gYTPKh+7i
+    ilipkYawoE6teb6JUGpvU+d27INgNhB2oDEXY3pG2PbV+wv229ykSZxh1sJUdDwT
+    a8eTg+3pCGXtOZiJoQTFwKUlD2WYTGqS4Gx6dIJco5k+ZikGNST1JGE64Jl4MZdI
+    rtyvpcYblh5Q14sJGvp4kWYS9tjEM8pA+4Z9th3JqQKCAQEAkidH0+UM1A9gmQCm
+    jiHeR39ky5Jz3f7oJT63J15479yrVxBTWNhtNQrJhXzOvGkr+JQsuF+ANMsYmFql
+    zFqy8KMC9D/JwmD6adeif+o5sHF/r/s1LsYGOAtao4TvnOzrefs7ciwERt+GTSQ4
+    9uq0jgJMYkPcVr9DKI8K7V6ThdW52dECKRVzQiRXVEp7vIsqKUuFECuNYrfaKWai
+    FhLWGkA9FKee5L0e1/naB1N3ph72Bk2btO6GVzAXr2HADEZe0umWiczJ2xLH+3go
+    Oh/JiufYi8ClYFh6dDVJutlrbOcZsV3gCegfzikqijmWABcIavSgpsJVNF2zh7gV
+    Uq62gQKCAQAdO2FHeQpn6/at8WceY/4rC/MFhvGC4tlpidIuCtGhsfo4wZ/iWImF
+    N73u4nF1jBAHpTJwyHxLrLKgjWrRqOFSutvniZ/BzmAJolh63kcvL0Hg3IpMePm8
+    7PivZJ3/WIAwxU1m7SJkq5PY8ho7mwnHvWWI/hU26l42/z68QBS9FawQd0uS5G2x
+    5yIbEU/8ABcfYYhB7XiA0EYEMo1HiWeB/ag5iTN13ILbBmUf4sL+KVgygH3A1RRk
+    XSiWzluij2lZn22ClgIjnoSfQ38uH0bvVzUgyG9YX4XcQxOTGwWvPjT82FGB8NAw
+    ARVqs14QQFfzt1qrp/I38rsAfBDFk+xhAoIBAQCEKNk/oJcy9t/jMIbLcn6z3aCc
+    Fn8GBPSXtFj0t6weN5lHof+cggw4owMFWQQyAXxo/K6NnKNydMPZ5qjtLsHNpbpQ
+    aT1Or0/1YR1bJ8Lo82B4QM++7F761GWQPvE/tyrfPkfkWl92ITIpmnlw4wycRlkq
+    9anI2fnj1nIZwixzE2peb6PcsZU2HOs9uZ5RRd9wia696I7IpNibs4O4J2WTm4va
+    +NeYif3V2g9qwgT0Va0c9/Jlg3b58R0vA8j/VCU5I0TyXpkB3Xapx+pvEdZ3viUL
+    mXZaVotmWjgBXGDtd2VQg2ZiAMXHn3RzXSgV4Z+A/XacRs75h9bNw0ZJYrz1
     -----END RSA PRIVATE KEY-----
   '';
-  "acme-v01.api.letsencrypt.org".cert = builtins.toFile "acme-v01.api.letsencrypt.org.cert" ''
+  "acme-v02.api.letsencrypt.org".cert = builtins.toFile "acme-v02.api.letsencrypt.org.cert" ''
     -----BEGIN CERTIFICATE-----
     MIIEtDCCApwCAgKaMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNVBAMMC1NuYWtlb2ls
-    IENBMCAXDTE4MDcxMjAwMjIxN1oYDzIxMTgwNjE4MDAyMjE3WjAnMSUwIwYDVQQD
-    DBxhY21lLXYwMS5hcGkubGV0c2VuY3J5cHQub3JnMIICIjANBgkqhkiG9w0BAQEF
-    AAOCAg8AMIICCgKCAgEAvG+sL4q0VkgSClBTn4NkPiUrtXx5oLyZ+CCM1jrQx/xo
-    tUt5X2S4/7vMnAK/yRLsR7R2PhXO8CZPqJ7B6OfAgaDTgvipJkZYPZQSMP3KOinM
-    3WJLssqKh7/HOxZIf0iyUXewrnX5eTAo/CLsUnhBjBD7E99nmQz/leLWSl82sSYD
-    kO3nUk3/1qJZA8iddb4uH0IEQWcNKev3WoQQzwiVrXBiftlRQOJy5JJXm5m8229M
-    CpMA1AUWmpdu6sl3/gFFdsDhUFq/a7LFrVyaUCMRIHg9szAB7ZFkixr9umQs8jKw
-    uo983JHB11h2SirwgfIzHHmyhaWhCt22ucTwEXGhq63LtrzZvLsfP8Ql5S+AuqGT
-    H0v8meuc784leAjulBZjkpuIFwDnVv9+YeUEbqJeo1hSHrILddora3nkH4E2dJWm
-    LpqpiPr++GRi+BNgYKW/BQLTJ7C6v+vUs+kdPgYJH5z7oP6f0YZkT0Wkubp/UEz7
-    UV2dfjz57d77DYx5rFWGYzJriWR/xltgL1zDpjwjwG1FDpRqwlyYbBFpjQhxI+X0
-    aT98m6fCzBDQHDb/+JgvsjTHh6OZatahFAwzFIEfrceDv1BG8sBWIaZGhLzYiWQx
-    afl8oXbWv1T6I1jpsTlCdCSkWzaJb4ZjxI9Ga1ynVu8F16+GR2a71wKWu7UbZQsC
-    AwEAATANBgkqhkiG9w0BAQsFAAOCAgEAzeGlFMz1Bo+bbpZDQ60HLdw7qDp3SPJi
-    x5LYG860yzbh9ghvyc59MIm5E6vB140LRJAs+Xo6VdVSTC4jUA2kI9k1BQsbZKds
-    XT0RqA7HkqcLS3t3JWFkkKbCshMGZTSZ//hpbaUG1qEAfUfmZw1lAxqSa0kqavbP
-    awf7k8qHbqcj7WORCdH7fjKAjntEQwIpl1GEkAdCSghOJz2/o9aWmiGZt27OM/sG
-    MLSrcmL3QBElCjOxg14P8rnsmZ+VEp6MO93otoJ4dJL7fN7vTIh5ThbS384at/4l
-    4KK/y7XctUzAtWzhnodjk/NSgrrGX2kseOGOWEM1sZc9xtinHH2tpOMqtLVOkgHD
-    Lul+TArqgqeoOdEM/9OL64kgOrO/JzxBq+egLUi4wgAul2wmtecKZK1dkwYZHeqW
-    74i55yeBp+TTomnPr0ZBns6xKFYldJVzC34OB+2YVDxe8y9XtWtuQOxFw0LQHhNb
-    zy5aBverWzZFwiIIjJoVHTQq848uKBJec0YILfMinS1Wjif4xqW/IMfi+GFS0oka
-    sKCGNE/8ur9u/Jm6cbto3f2dtV8/vkhiITQgwzM2jalyuVJ9jyPxG7EvbTvZORgw
-    pRvBRTd4/eE7I1L+UDe6x8EjR/MrqfF9FWVGOZo4vPTyNbrSWYBh6s9kYy56ds1l
-    IRxst1BXEfI=
+    IENBMCAXDTE5MTAxODA3NTQxM1oYDzIxMTkwOTI0MDc1NDEzWjAnMSUwIwYDVQQD
+    DBxhY21lLXYwMi5hcGkubGV0c2VuY3J5cHQub3JnMIICIjANBgkqhkiG9w0BAQEF
+    AAOCAg8AMIICCgKCAgEApny0WhfDwEXe6WDTCw8qBuMAPDr88pj6kbhQWfzAW2c0
+    TggJEtjs9dktENeTpSl14nnLVMiSYIJPYY3KbOIFQH1qDaOuQ7NaOhj9CdMTm5r9
+    bl+CYAyqLIMQ9AAZDhUcQjOy3moiL7ClFHlkFYuEzZBO9DF7hJpfUFIs0Idg50mN
+    oZh/K/fb4P2skNjfCjjomTRUmZHxT6G00ImSTtSaYbN/WHut1xXwJvOoT1nlEA/P
+    ghKmJJ9ZuRMSddUJmjL+sT09L8LVkK8CKeHi4r58DHM0D0u8owIFV9qsXd5UvZHa
+    NgvQ4OAWGukMX+TxRuqkUZkaj84vnNL+ttEMl4jedw0ImzNtCOYehDyTPRkfng5P
+    LWMSvWbwyP8jDd2578mSbx5BF7ypYX366+vknjIFyZ5WezcC1pscIHxLoEwuhuf+
+    knN+kFkLOHeYbqQrU6mxSnu9q0hnNvGUkTP0a/1aLOGRfQ5C/pxpE/Rebi8qfM/O
+    JFd4mSxGL93JUTXWAItiIeBnQpIne65/Ska9dWynOEfIb0okdet3kfmNHz3zc17d
+    Z5g4AdOSCgHAlQgFt/Qd8W6xXUe4C5Mfv2ctxRrfQhDwtB6rMByPwzImnciC2h3v
+    CwD3vS/vjUyWICyhZyi2LZDUQz+sCKBXCYYcYh8ThFO40j5x1OnYMq7XQvyl8QkC
+    AwEAATANBgkqhkiG9w0BAQsFAAOCAgEAkx0GLPuCvKSLTHxVLh5tP4jxSGG/zN37
+    PeZLu3QJTdRdRc8bgeOGXAVEVFbqOLTNTsuY1mvpiv2V6wxR6nns+PIHeLY/UOdc
+    mOreKPtMU2dWPp3ybec2Jwii6PhAXZJ26AKintmug1psMw7662crR3SCnn85/CvW
+    192vhr5gM1PqLBIlbsX0tAqxAwBe1YkxBb9vCq8NVghJlKme49xnwGULMTGs15MW
+    hIPx6sW93zwrGiTsDImH49ILGF+NcX1AgAq90nG0j/l5zhDgXGJglX+K1xP99X1R
+    de3I4uoufPa5q+Pjmhy7muL+o4Qt0D0Vm86RqqjTkNPsr7gAJtt66A7TJrYiIoKn
+    GTIBsgM6egeFLLYQsT0ap/59HJismO2Pjx4Jk/jHOkC8TJsXQNRq1Km76VMBnuc0
+    2CMoD9pb38GjUUH94D4hJK4Ls/gJMF3ftKUyR8Sr/LjE6qU6Yj+ZpeEQP4kW9ANq
+    Lv9KSNDQQpRTL4LwGLTGomksLTQEekge7/q4J2TQRZNYJ/mxnrBKRcv9EAMgBMXq
+    Q+7GHtKDv9tJVlMfG/MRD3CMuuSRiT3OVbvMMkFzsPkqxYAP1CqE/JGvh67TzKI+
+    MUfXKehA6TKuxrTVqCtoFIfGaqA9IWyoRTtugYq/xssB9ESeEYGeaM1A9Yueqz+h
+    KkBZO00jHSE=
     -----END CERTIFICATE-----
   '';
   "letsencrypt.org".key = builtins.toFile "letsencrypt.org.key" ''
     -----BEGIN RSA PRIVATE KEY-----
-    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
+    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
     -----END RSA PRIVATE KEY-----
   '';
   "letsencrypt.org".cert = builtins.toFile "letsencrypt.org.cert" ''
     -----BEGIN CERTIFICATE-----
     MIIEpzCCAo8CAgKaMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNVBAMMC1NuYWtlb2ls
-    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
+    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
-    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
+    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
     -----END CERTIFICATE-----
   '';
 }
diff --git a/nixos/tests/fancontrol.nix b/nixos/tests/fancontrol.nix
new file mode 100644
index 000000000000..83ddbb54c5bb
--- /dev/null
+++ b/nixos/tests/fancontrol.nix
@@ -0,0 +1,25 @@
+import ./make-test.nix ({ pkgs, ... } : {
+  name = "fancontrol";
+
+  machine =
+    { ... }:
+    { hardware.fancontrol.enable = true;
+      hardware.fancontrol.config = ''
+        INTERVAL=42
+        DEVPATH=hwmon1=devices/platform/dummy
+        DEVNAME=hwmon1=dummy
+        FCTEMPS=hwmon1/device/pwm1=hwmon1/device/temp1_input
+        FCFANS=hwmon1/device/pwm1=hwmon1/device/fan1_input
+        MINTEMP=hwmon1/device/pwm1=25
+        MAXTEMP=hwmon1/device/pwm1=65
+        MINSTART=hwmon1/device/pwm1=150
+        MINSTOP=hwmon1/device/pwm1=0
+      '';
+    };
+
+  # This configuration cannot be valid for the test VM, so it's expected to get an 'outdated' error.
+  testScript = ''
+    $machine->waitForUnit("fancontrol.service");
+    $machine->waitUntilSucceeds("journalctl -eu fancontrol | grep 'Configuration appears to be outdated'");
+  '';
+})
diff --git a/nixos/tests/ferm.nix b/nixos/tests/ferm.nix
index b8e8663e3ad2..edf9c8036aca 100644
--- a/nixos/tests/ferm.nix
+++ b/nixos/tests/ferm.nix
@@ -22,6 +22,8 @@ import ./make-test.nix ({ pkgs, ...} : {
         {
           networking = {
             dhcpcd.enable = false;
+            useNetworkd = true;
+            useDHCP = false;
             interfaces.eth1.ipv6.addresses = mkOverride 0 [ { address = "fd00::1"; prefixLength = 64; } ];
             interfaces.eth1.ipv4.addresses = mkOverride 0 [ { address = "192.168.1.1"; prefixLength = 24; } ];
           };
diff --git a/nixos/tests/gnome3-xorg.nix b/nixos/tests/gnome3-xorg.nix
index f12361da0372..eb4c376319be 100644
--- a/nixos/tests/gnome3-xorg.nix
+++ b/nixos/tests/gnome3-xorg.nix
@@ -29,7 +29,7 @@ import ./make-test.nix ({ pkgs, ...} : {
       $machine->waitForUnit("default.target","alice");
 
       # Check that logging in has given the user ownership of devices.
-      $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
+      $machine->succeed("getfacl -p /dev/snd/timer | grep -q alice");
 
       $machine->succeed("su - alice -c 'DISPLAY=:0.0 gnome-terminal &'");
       $machine->succeed("xauth merge ~alice/.Xauthority");
diff --git a/nixos/tests/gnome3.nix b/nixos/tests/gnome3.nix
index b6fe602a7327..ab363efb6a19 100644
--- a/nixos/tests/gnome3.nix
+++ b/nixos/tests/gnome3.nix
@@ -44,7 +44,7 @@ import ./make-test.nix ({ pkgs, ...} : {
       $machine->waitForUnit("default.target","alice");
 
       # Check that logging in has given the user ownership of devices.
-      $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
+      $machine->succeed("getfacl -p /dev/snd/timer | grep -q alice");
 
       # Wait for the wayland server
       $machine->waitForFile("/run/user/1000/wayland-0");
diff --git a/nixos/tests/gotify-server.nix b/nixos/tests/gotify-server.nix
new file mode 100644
index 000000000000..0ffc3138d5a1
--- /dev/null
+++ b/nixos/tests/gotify-server.nix
@@ -0,0 +1,45 @@
+import ./make-test.nix ({ pkgs, lib, ...} : {
+  name = "gotify-server";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ ma27 ];
+  };
+
+  machine = { pkgs, ... }: {
+    environment.systemPackages = [ pkgs.jq ];
+
+    services.gotify = {
+      enable = true;
+      port = 3000;
+    };
+  };
+
+  testScript = ''
+    startAll;
+
+    $machine->waitForUnit("gotify-server");
+    $machine->waitForOpenPort(3000);
+
+    my $token = $machine->succeed(
+      "curl --fail -sS -X POST localhost:3000/application -F name=nixos " .
+      '-H "Authorization: Basic $(echo -ne "admin:admin" | base64 --wrap 0)" ' .
+      '| jq .token | xargs echo -n'
+    );
+
+    my $usertoken = $machine->succeed(
+      "curl --fail -sS -X POST localhost:3000/client -F name=nixos " .
+      '-H "Authorization: Basic $(echo -ne "admin:admin" | base64 --wrap 0)" ' .
+      '| jq .token | xargs echo -n'
+    );
+
+    $machine->succeed(
+      "curl --fail -sS -X POST 'localhost:3000/message?token=$token' -H 'Accept: application/json' " .
+      '-F title=Gotify -F message=Works'
+    );
+
+    my $title = $machine->succeed(
+      "curl --fail -sS 'localhost:3000/message?since=0&token=$usertoken' | jq '.messages|.[0]|.title' | xargs echo -n"
+    );
+
+    $title eq "Gotify" or die "Wrong title ($title), expected 'Gotify'!";
+  '';
+})
diff --git a/nixos/tests/hardened.nix b/nixos/tests/hardened.nix
index 90f9793b370c..cbf76f9e5587 100644
--- a/nixos/tests/hardened.nix
+++ b/nixos/tests/hardened.nix
@@ -10,6 +10,7 @@ import ./make-test.nix ({ pkgs, ...} : {
     { users.users.alice = { isNormalUser = true; extraGroups = [ "proc" ]; };
       users.users.sybil = { isNormalUser = true; group = "wheel"; };
       imports = [ ../modules/profiles/hardened.nix ];
+      environment.memoryAllocator.provider = "graphene-hardened";
       nix.useSandbox = false;
       virtualisation.emptyDiskImages = [ 4096 ];
       boot.initrd.postDeviceCommands = ''
diff --git a/nixos/tests/hydra/create-trivial-project.sh b/nixos/tests/hydra/create-trivial-project.sh
index 39122c9b473a..5aae2d5bf90d 100755
--- a/nixos/tests/hydra/create-trivial-project.sh
+++ b/nixos/tests/hydra/create-trivial-project.sh
@@ -44,6 +44,8 @@ cat >data.json <<EOF
   "enabled": "1",
   "visible": "1",
   "keepnr": "1",
+  "enableemail": true,
+  "emailoverride": "hydra@localhost",
   "nixexprinput": "trivial",
   "nixexprpath": "trivial.nix",
   "inputs": {
diff --git a/nixos/tests/hydra/default.nix b/nixos/tests/hydra/default.nix
index f99b367ac9b7..6ca05a2c7797 100644
--- a/nixos/tests/hydra/default.nix
+++ b/nixos/tests/hydra/default.nix
@@ -8,8 +8,10 @@ let
   trivialJob = pkgs.writeTextDir "trivial.nix" ''
    { trivial = builtins.derivation {
        name = "trivial";
-       system = "x86_64-linux";
+       system = "${system}";
        builder = "/bin/sh";
+       allowSubstitutes = false;
+       preferLocalBuild = true;
        args = ["-c" "echo success > $out; exit 0"];
      };
    }
@@ -53,11 +55,16 @@ let
               notificationSender = "example@example.com";
 
               package = pkgs.hydra.override { inherit nix; };
+
+              extraConfig = ''
+                email_notification = 1
+              '';
             };
+            services.postfix.enable = true;
             nix = {
               buildMachines = [{
                 hostName = "localhost";
-                systems = [ "x86_64-linux" ];
+                systems = [ system ];
               }];
 
               binaryCaches = [];
@@ -68,12 +75,12 @@ let
           # let the system boot up
           $machine->waitForUnit("multi-user.target");
           # test whether the database is running
-          $machine->succeed("systemctl status postgresql.service");
+          $machine->waitForUnit("postgresql.service");
           # test whether the actual hydra daemons are running
-          $machine->succeed("systemctl status hydra-queue-runner.service");
-          $machine->succeed("systemctl status hydra-init.service");
-          $machine->succeed("systemctl status hydra-evaluator.service");
-          $machine->succeed("systemctl status hydra-send-stats.service");
+          $machine->waitForUnit("hydra-init.service");
+          $machine->requireActiveUnit("hydra-queue-runner.service");
+          $machine->requireActiveUnit("hydra-evaluator.service");
+          $machine->requireActiveUnit("hydra-notify.service");
 
           $machine->succeed("hydra-create-user admin --role admin --password admin");
 
@@ -84,6 +91,8 @@ let
           $machine->succeed("create-trivial-project.sh");
 
           $machine->waitUntilSucceeds('curl -L -s http://localhost:3000/build/1 -H "Accept: application/json" |  jq .buildstatus | xargs test 0 -eq');
+
+          $machine->waitUntilSucceeds('journalctl -eu hydra-notify.service -o cat | grep -q "sending mail notification to hydra@localhost"');
         '';
       })));
 
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index a136678c6eff..eb1f4f192dd1 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -54,8 +54,6 @@ let
 
         hardware.enableAllFirmware = lib.mkForce false;
 
-        services.udisks2.enable = lib.mkDefault false;
-
         ${replaceChars ["\n"] ["\n  "] extraConfig}
       }
     '';
@@ -295,8 +293,6 @@ let
               ++ optional (bootLoader == "grub" && grubVersion == 1) pkgs.grub
               ++ optionals (bootLoader == "grub" && grubVersion == 2) [ pkgs.grub2 pkgs.grub2_efi ];
 
-            services.udisks2.enable = mkDefault false;
-
             nix.binaryCaches = mkForce [ ];
             nix.extraOptions =
               ''
diff --git a/nixos/tests/kafka.nix b/nixos/tests/kafka.nix
index f526d18befee..48ca98da8fa1 100644
--- a/nixos/tests/kafka.nix
+++ b/nixos/tests/kafka.nix
@@ -73,4 +73,5 @@ in with pkgs; {
   kafka_2_0  = makeKafkaTest "kafka_2_0"  apacheKafka_2_0;
   kafka_2_1  = makeKafkaTest "kafka_2_1"  apacheKafka_2_1;
   kafka_2_2  = makeKafkaTest "kafka_2_2"  apacheKafka_2_2;
+  kafka_2_3  = makeKafkaTest "kafka_2_3"  apacheKafka_2_3;
 }
diff --git a/nixos/tests/login.nix b/nixos/tests/login.nix
index 2a7c063d3033..bd8ed23a7b8e 100644
--- a/nixos/tests/login.nix
+++ b/nixos/tests/login.nix
@@ -48,12 +48,12 @@ import ./make-test.nix ({ pkgs, latestKernel ? false, ... }:
       # Check whether systemd gives and removes device ownership as
       # needed.
       subtest "device permissions", sub {
-          $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
+          $machine->succeed("getfacl -p /dev/snd/timer | grep -q alice");
           $machine->sendKeys("alt-f1");
           $machine->waitUntilSucceeds("[ \$(fgconsole) = 1 ]");
-          $machine->fail("getfacl /dev/snd/timer | grep -q alice");
+          $machine->fail("getfacl -p /dev/snd/timer | grep -q alice");
           $machine->succeed("chvt 2");
-          $machine->waitUntilSucceeds("getfacl /dev/snd/timer | grep -q alice");
+          $machine->waitUntilSucceeds("getfacl -p /dev/snd/timer | grep -q alice");
       };
 
       # Log out.
diff --git a/nixos/tests/matomo.nix b/nixos/tests/matomo.nix
new file mode 100644
index 000000000000..4efa65a7b6de
--- /dev/null
+++ b/nixos/tests/matomo.nix
@@ -0,0 +1,43 @@
+{ system ? builtins.currentSystem, config ? { }
+, pkgs ? import ../.. { inherit system config; } }:
+
+with import ../lib/testing.nix { inherit system pkgs; };
+with pkgs.lib;
+
+let
+  matomoTest = package:
+  makeTest {
+    machine = { config, pkgs, ... }: {
+      services.matomo = {
+        package = package;
+        enable = true;
+        nginx = {
+          forceSSL = false;
+          enableACME = false;
+        };
+      };
+      services.mysql = {
+        enable = true;
+        package = pkgs.mysql;
+      };
+      services.nginx.enable = true;
+    };
+
+    testScript = ''
+      startAll;
+      $machine->waitForUnit("mysql.service");
+      $machine->waitForUnit("phpfpm-matomo.service");
+      $machine->waitForUnit("nginx.service");
+      $machine->succeed("curl -sSfL http://localhost/ | grep '<title>Matomo[^<]*Installation'");
+    '';
+  };
+in {
+  matomo = matomoTest pkgs.matomo // {
+    name = "matomo";
+    meta.maintainers = with maintainers; [ florianjacob kiwi mmilata ];
+  };
+  matomo-beta = matomoTest pkgs.matomo-beta // {
+    name = "matomo-beta";
+    meta.maintainers = with maintainers; [ florianjacob kiwi mmilata ];
+  };
+}
diff --git a/nixos/tests/minidlna.nix b/nixos/tests/minidlna.nix
new file mode 100644
index 000000000000..7bf1bed69d06
--- /dev/null
+++ b/nixos/tests/minidlna.nix
@@ -0,0 +1,39 @@
+import ./make-test.nix ({ pkgs, ... }: {
+  name = "minidlna";
+
+  nodes = {
+    server =
+      { ... }:
+      {
+        imports = [ ../modules/profiles/minimal.nix ];
+        networking.firewall.allowedTCPPorts = [ 8200 ];
+        services.minidlna = {
+          enable = true;
+          loglevel = "error";
+          mediaDirs = [
+           "PV,/tmp/stuff"
+          ];
+          friendlyName = "rpi3";
+          rootContainer = "B";
+          extraConfig =
+          ''
+            album_art_names=Cover.jpg/cover.jpg/AlbumArtSmall.jpg/albumartsmall.jpg
+            album_art_names=AlbumArt.jpg/albumart.jpg/Album.jpg/album.jpg
+            album_art_names=Folder.jpg/folder.jpg/Thumb.jpg/thumb.jpg
+            notify_interval=60
+          '';
+        };
+      };
+      client = { ... }: { };
+  };
+
+  testScript =
+  ''
+    startAll;
+    $server->succeed("mkdir -p /tmp/stuff && chown minidlna: /tmp/stuff");
+    $server->waitForUnit("minidlna");
+    $server->waitForOpenPort("8200");
+    $server->succeed("curl --fail http://localhost:8200/");
+    $client->succeed("curl --fail http://server:8200/");
+  '';
+})
diff --git a/nixos/tests/mumble.nix b/nixos/tests/mumble.nix
index dadd16fd9a0c..652d49a24b1c 100644
--- a/nixos/tests/mumble.nix
+++ b/nixos/tests/mumble.nix
@@ -63,8 +63,8 @@ in
     $client2->sendChars("y");
 
     # Find clients in logs
-    $server->waitUntilSucceeds("grep -q 'client1' /var/log/murmur/murmurd.log");
-    $server->waitUntilSucceeds("grep -q 'client2' /var/log/murmur/murmurd.log");
+    $server->waitUntilSucceeds("journalctl -eu murmur -o cat | grep -q client1");
+    $server->waitUntilSucceeds("journalctl -eu murmur -o cat | grep -q client2");
 
     $server->sleep(5); # wait to get screenshot
     $client1->screenshot("screen1");
diff --git a/nixos/tests/mxisd.nix b/nixos/tests/mxisd.nix
index 3d03a5a53e38..0039256f5861 100644
--- a/nixos/tests/mxisd.nix
+++ b/nixos/tests/mxisd.nix
@@ -10,12 +10,22 @@ import ./make-test.nix ({ pkgs, ... } : {
       services.mxisd.enable = true;
       services.mxisd.matrix.domain = "example.org";
     };
+
+    server_ma1sd = args : {
+      services.mxisd.enable = true;
+      services.mxisd.matrix.domain = "example.org";
+      services.mxisd.package = pkgs.ma1sd;
+    };
   };
 
   testScript = ''
     startAll;
     $server_mxisd->waitForUnit("mxisd.service");
     $server_mxisd->waitForOpenPort(8090);
-    $server_mxisd->succeed("curl -Ssf \"http://127.0.0.1:8090/_matrix/identity/api/v1\"")
+    $server_mxisd->succeed("curl -Ssf \"http://127.0.0.1:8090/_matrix/identity/api/v1\"");
+    $server_ma1sd->waitForUnit("mxisd.service");
+    $server_ma1sd->waitForOpenPort(8090);
+    $server_ma1sd->succeed("curl -Ssf \"http://127.0.0.1:8090/_matrix/identity/api/v1\"")
+
   '';
 })
diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix
index 7452768033ab..e0585d8f1bb4 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -72,6 +72,7 @@ let
   testCases = {
     loopback = {
       name = "Loopback";
+      machine.networking.useDHCP = false;
       machine.networking.useNetworkd = networkd;
       testScript = ''
         startAll;
@@ -139,14 +140,16 @@ let
         virtualisation.vlans = [ 1 2 ];
         networking = {
           useNetworkd = networkd;
-          useDHCP = true;
+          useDHCP = false;
           interfaces.eth1 = {
             ipv4.addresses = mkOverride 0 [ ];
             ipv6.addresses = mkOverride 0 [ ];
+            useDHCP = true;
           };
           interfaces.eth2 = {
             ipv4.addresses = mkOverride 0 [ ];
             ipv6.addresses = mkOverride 0 [ ];
+            useDHCP = true;
           };
         };
       };
@@ -320,13 +323,19 @@ let
         virtualisation.vlans = [ 1 ];
         networking = {
           useNetworkd = networkd;
+          useDHCP = false;
           firewall.logReversePathDrops = true; # to debug firewall rules
           # reverse path filtering rules for the macvlan interface seem
           # to be incorrect, causing the test to fail. Disable temporarily.
           firewall.checkReversePath = false;
-          useDHCP = true;
           macvlans.macvlan.interface = "eth1";
-          interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
+          interfaces.eth1 = {
+            ipv4.addresses = mkOverride 0 [ ];
+            useDHCP = true;
+          };
+          interfaces.macvlan = {
+            useDHCP = true;
+          };
         };
       };
       testScript = { ... }:
@@ -440,6 +449,8 @@ let
     virtual = {
       name = "Virtual";
       machine = {
+        networking.useNetworkd = networkd;
+        networking.useDHCP = false;
         networking.interfaces.tap0 = {
           ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
           ipv6.addresses = [ { address = "2001:1470:fffd:2096::"; prefixLength = 64; } ];
@@ -489,6 +500,7 @@ let
         boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
         networking = {
           useNetworkd = networkd;
+          useDHCP = false;
           interfaces.eth1.ipv6.addresses = singleton {
             address = "fd00:1234:5678:1::1";
             prefixLength = 64;
@@ -514,11 +526,12 @@ let
         virtualisation.vlans = [ 1 ];
         networking = {
           useNetworkd = networkd;
-          useDHCP = true;
+          useDHCP = false;
           interfaces.eth1 = {
             preferTempAddress = true;
             ipv4.addresses = mkOverride 0 [ ];
             ipv6.addresses = mkOverride 0 [ ];
+            useDHCP = true;
           };
         };
       };
@@ -526,11 +539,12 @@ let
         virtualisation.vlans = [ 1 ];
         networking = {
           useNetworkd = networkd;
-          useDHCP = true;
+          useDHCP = false;
           interfaces.eth1 = {
             preferTempAddress = false;
             ipv4.addresses = mkOverride 0 [ ];
             ipv6.addresses = mkOverride 0 [ ];
+            useDHCP = true;
           };
         };
       };
diff --git a/nixos/tests/nextcloud/with-postgresql-and-redis.nix b/nixos/tests/nextcloud/with-postgresql-and-redis.nix
index 81c269c23788..f655aba9d45e 100644
--- a/nixos/tests/nextcloud/with-postgresql-and-redis.nix
+++ b/nixos/tests/nextcloud/with-postgresql-and-redis.nix
@@ -36,49 +36,16 @@ in {
       };
 
       services.redis = {
-        unixSocket = "/var/run/redis/redis.sock";
         enable = true;
-        extraConfig = ''
-          unixsocketperm 770
-        '';
-      };
-
-      systemd.services.redis = {
-        preStart = ''
-          mkdir -p /var/run/redis
-          chown ${config.services.redis.user}:${config.services.nginx.group} /var/run/redis
-        '';
-        serviceConfig.PermissionsStartOnly = true;
       };
 
       systemd.services.nextcloud-setup= {
         requires = ["postgresql.service"];
         after = [
           "postgresql.service"
-          "chown-redis-socket.service"
         ];
       };
 
-      # At the time of writing, redis creates its socket with the "nobody"
-      # group.  I figure this is slightly less bad than making the socket world
-      # readable.
-      systemd.services.chown-redis-socket = {
-        enable = true;
-        script = ''
-          until ${pkgs.redis}/bin/redis-cli ping; do
-            echo "waiting for redis..."
-            sleep 1
-          done
-          chown ${config.services.redis.user}:${config.services.nginx.group} /var/run/redis/redis.sock
-        '';
-        after = [ "redis.service" ];
-        requires = [ "redis.service" ];
-        wantedBy = [ "redis.service" ];
-        serviceConfig = {
-          Type = "oneshot";
-        };
-      };
-
       services.postgresql = {
         enable = true;
         ensureDatabases = [ "nextcloud" ];
@@ -94,8 +61,8 @@ in {
   testScript = let
     configureRedis = pkgs.writeScript "configure-redis" ''
       #!${pkgs.stdenv.shell}
-      nextcloud-occ config:system:set redis 'host' --value '/var/run/redis/redis.sock' --type string
-      nextcloud-occ config:system:set redis 'port' --value 0 --type integer
+      nextcloud-occ config:system:set redis 'host' --value 'localhost' --type string
+      nextcloud-occ config:system:set redis 'port' --value 6379 --type integer
       nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\Redis' --type string
       nextcloud-occ config:system:set memcache.locking --value '\OC\Memcache\Redis' --type string
     '';
diff --git a/nixos/tests/openarena.nix b/nixos/tests/openarena.nix
new file mode 100644
index 000000000000..4cc4db229637
--- /dev/null
+++ b/nixos/tests/openarena.nix
@@ -0,0 +1,36 @@
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "openarena";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ tomfitzhenry ];
+  };
+
+  machine =
+    { pkgs, ... }:
+
+    { imports = [];
+      environment.systemPackages = with pkgs; [
+        socat
+      ];
+      services.openarena = {
+        enable = true;
+        extraFlags = [
+          "+set dedicated 2"
+          "+set sv_hostname 'My NixOS server'"
+          "+map oa_dm1"
+        ];
+      };
+    };
+
+  testScript =
+    ''
+      $machine->waitForUnit("openarena.service");
+      $machine->waitUntilSucceeds("ss --numeric --udp --listening | grep -q 27960");
+
+      # The log line containing 'resolve address' is last and only message that occurs after
+      # the server starts accepting clients.
+      $machine->waitUntilSucceeds("journalctl -u openarena.service | grep 'resolve address: dpmaster.deathmask.net'");
+
+      # Check it's possible to join the server.
+      $machine->succeed("echo -n -e '\\xff\\xff\\xff\\xffgetchallenge' | socat - UDP4-DATAGRAM:127.0.0.1:27960 | grep -q challengeResponse");
+    '';
+})
diff --git a/nixos/tests/orangefs.nix b/nixos/tests/orangefs.nix
new file mode 100644
index 000000000000..bdf4fc10c447
--- /dev/null
+++ b/nixos/tests/orangefs.nix
@@ -0,0 +1,88 @@
+import ./make-test.nix ({ ... } :
+
+let
+  server = { pkgs, ... } : {
+    networking.firewall.allowedTCPPorts = [ 3334 ];
+    boot.initrd.postDeviceCommands = ''
+      ${pkgs.e2fsprogs}/bin/mkfs.ext4 -L data /dev/vdb
+    '';
+
+    virtualisation.emptyDiskImages = [ 4096 ];
+
+    fileSystems = pkgs.lib.mkVMOverride
+      [ { mountPoint = "/data";
+          device = "/dev/disk/by-label/data";
+          fsType = "ext4";
+        }
+      ];
+
+    services.orangefs.server = {
+      enable = true;
+      dataStorageSpace = "/data/storage";
+      metadataStorageSpace = "/data/meta";
+      servers = {
+        server1 = "tcp://server1:3334";
+        server2 = "tcp://server2:3334";
+      };
+    };
+  };
+
+  client = { lib, ... } : {
+    networking.firewall.enable = true;
+
+    services.orangefs.client = {
+      enable = true;
+      fileSystems = [{
+        target = "tcp://server1:3334/orangefs";
+        mountPoint = "/orangefs";
+      }];
+    };
+  };
+
+in {
+  name = "orangefs";
+
+  nodes = {
+    server1 = server;
+    server2 = server;
+
+    client1 = client;
+    client2 = client;
+  };
+
+  testScript = ''
+    # format storage
+    foreach my $server  (($server1,$server2))
+    {
+      $server->start();
+      $server->waitForUnit("multi-user.target");
+      $server->succeed("mkdir -p /data/storage /data/meta");
+      $server->succeed("chown orangefs:orangefs /data/storage /data/meta");
+      $server->succeed("chmod 0770 /data/storage /data/meta");
+      $server->succeed("sudo -g orangefs -u orangefs pvfs2-server -f /etc/orangefs/server.conf");
+    }
+
+    # start services after storage is formated on all machines
+    foreach my $server  (($server1,$server2))
+    {
+      $server->succeed("systemctl start orangefs-server.service");
+    }
+
+    # Check if clients can reach and mount the FS
+    foreach my $client  (($client1,$client2))
+    {
+      $client->start();
+      $client->waitForUnit("orangefs-client.service");
+      # Both servers need to be reachable
+      $client->succeed("pvfs2-check-server -h server1 -f orangefs -n tcp -p 3334");
+      $client->succeed("pvfs2-check-server -h server2 -f orangefs -n tcp -p 3334");
+      $client->waitForUnit("orangefs.mount");
+
+    }
+
+    # R/W test between clients
+    $client1->succeed("echo test > /orangefs/file1");
+    $client2->succeed("grep test /orangefs/file1");
+
+  '';
+})
diff --git a/nixos/tests/os-prober.nix b/nixos/tests/os-prober.nix
new file mode 100644
index 000000000000..5407a62339fe
--- /dev/null
+++ b/nixos/tests/os-prober.nix
@@ -0,0 +1,118 @@
+import ./make-test.nix ({pkgs, lib, ...}:
+let
+  # A filesystem image with a (presumably) bootable debian
+  debianImage = pkgs.vmTools.diskImageFuns.debian9i386 {
+    # os-prober cannot detect systems installed on disks without a partition table
+    # so we create the disk ourselves
+    createRootFS = with pkgs; ''
+      ${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
+      ${utillinux}/bin/mount -t ext4 /dev/vda1 /mnt
+
+      if test -e /mnt/.debug; then
+        exec ${bash}/bin/sh
+      fi
+      touch /mnt/.debug
+
+      mkdir /mnt/proc /mnt/dev /mnt/sys
+    '';
+    extraPackages = [
+      # /etc/os-release
+      "base-files"
+      # make the disk bootable-looking
+      "grub2" "linux-image-686"
+    ];
+    # install grub
+    postInstall = ''
+      ln -sf /proc/self/mounts > /etc/mtab
+      PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \
+        grub-install /dev/vda --force
+      PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \
+        update-grub
+    '';
+  };
+
+  # options to add the disk to the test vm
+  QEMU_OPTS = "-drive index=2,file=${debianImage}/disk-image.qcow2,read-only,if=virtio";
+
+  # a part of the configuration of the test vm
+  simpleConfig = {
+    boot.loader.grub = {
+      enable = true;
+      useOSProber = true;
+      device = "/dev/vda";
+      # vda is a filesystem without partition table
+      forceInstall = true;
+    };
+    nix.binaryCaches = lib.mkForce [ ];
+    nix.extraOptions = ''
+      hashed-mirrors =
+      connect-timeout = 1
+    '';
+  };
+  # /etc/nixos/configuration.nix for the vm
+  configFile = pkgs.writeText "configuration.nix"  ''
+    {config, pkgs, ...}: ({
+    imports =
+          [ ./hardware-configuration.nix
+            <nixpkgs/nixos/modules/testing/test-instrumentation.nix>
+          ];
+    } // (builtins.fromJSON (builtins.readFile ${
+      pkgs.writeText "simpleConfig.json" (builtins.toJSON simpleConfig)
+    })))
+  '';
+in {
+  name = "os-prober";
+
+  machine = { config, pkgs, ... }: (simpleConfig // {
+      imports = [ ../modules/profiles/installation-device.nix
+                  ../modules/profiles/base.nix ];
+      virtualisation.memorySize = 1024;
+      # The test cannot access the network, so any packages
+      # nixos-rebuild needs must be included in the VM.
+      system.extraDependencies = with pkgs;
+        [ sudo
+          libxml2.bin
+          libxslt.bin
+          desktop-file-utils
+          docbook5
+          docbook_xsl_ns
+          unionfs-fuse
+          ntp
+          nixos-artwork.wallpapers.simple-dark-gray-bottom
+          perlPackages.XMLLibXML
+          perlPackages.ListCompare
+          shared-mime-info
+          texinfo
+          xorg.lndir
+          grub2
+
+          # add curl so that rather than seeing the test attempt to download
+          # curl's tarball, we see what it's trying to download
+          curl
+        ];
+  });
+
+  testScript = ''
+    # hack to add the secondary disk
+    $machine->{startCommand} = "QEMU_OPTS=\"\$QEMU_OPTS \"${lib.escapeShellArg QEMU_OPTS} ".$machine->{startCommand};
+
+    $machine->start;
+    $machine->succeed("udevadm settle");
+    $machine->waitForUnit("multi-user.target");
+
+    # check that os-prober works standalone
+    $machine->succeed("${pkgs.os-prober}/bin/os-prober | grep /dev/vdb1");
+
+    # rebuild and test that debian is available in the grub menu
+    $machine->succeed("nixos-generate-config");
+    $machine->copyFileFromHost(
+        "${configFile}",
+        "/etc/nixos/configuration.nix");
+    $machine->succeed("nixos-rebuild boot >&2");
+
+    $machine->succeed("egrep 'menuentry.*debian' /boot/grub/grub.cfg");
+  '';
+})
diff --git a/nixos/tests/pantheon.nix b/nixos/tests/pantheon.nix
index c50f77f86173..9888887ee8b5 100644
--- a/nixos/tests/pantheon.nix
+++ b/nixos/tests/pantheon.nix
@@ -42,7 +42,7 @@ import ./make-test.nix ({ pkgs, ...} :
     $machine->waitForWindow(qr/plank/);
 
     # Check that logging in has given the user ownership of devices.
-    $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
+    $machine->succeed("getfacl -p /dev/snd/timer | grep -q alice");
 
     # Open elementary terminal
     $machine->execute("su - alice -c 'DISPLAY=:0.0 io.elementary.terminal &'");
diff --git a/nixos/tests/plasma5.nix b/nixos/tests/plasma5.nix
index 788c8719c8d2..614fc9bf316e 100644
--- a/nixos/tests/plasma5.nix
+++ b/nixos/tests/plasma5.nix
@@ -30,6 +30,7 @@ import ./make-test.nix ({ pkgs, ...} :
       enable = true;
       user = "alice";
     };
+    hardware.pulseaudio.enable = true; # needed for the factl test, /dev/snd/* exists without them but udev doesn't care then
     virtualisation.memorySize = 1024;
     environment.systemPackages = [ sddm_theme ];
   };
@@ -47,7 +48,7 @@ import ./make-test.nix ({ pkgs, ...} :
     $machine->waitForWindow("^Desktop ");
 
     # Check that logging in has given the user ownership of devices.
-    $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
+    $machine->succeed("getfacl -p /dev/snd/timer | grep -q alice");
 
     $machine->execute("su - alice -c 'DISPLAY=:0.0 dolphin &'");
     $machine->waitForWindow(" Dolphin");
diff --git a/nixos/tests/pppd.nix b/nixos/tests/pppd.nix
new file mode 100644
index 000000000000..91f811859093
--- /dev/null
+++ b/nixos/tests/pppd.nix
@@ -0,0 +1,62 @@
+import ./make-test.nix (
+  let
+    chap-secrets = {
+      text = ''"flynn" * "reindeerflotilla" *'';
+      mode = "0640";
+    };
+  in {
+    nodes = {
+      server = {config, pkgs, ...}: {
+        config = {
+          # Run a PPPoE access concentrator server. It will spawn an
+          # appropriate PPP server process when a PPPoE client sets up a
+          # PPPoE session.
+          systemd.services.pppoe-server = {
+            restartTriggers = [
+              config.environment.etc."ppp/pppoe-server-options".source
+              config.environment.etc."ppp/chap-secrets".source
+            ];
+            after = ["network.target"];
+            serviceConfig = {
+              ExecStart = "${pkgs.rpPPPoE}/sbin/pppoe-server -F -O /etc/ppp/pppoe-server-options -q ${pkgs.ppp}/sbin/pppd -I eth1 -L 192.0.2.1 -R 192.0.2.2";
+            };
+            wantedBy = ["multi-user.target"];
+          };
+          environment.etc = {
+            "ppp/pppoe-server-options".text = ''
+              lcp-echo-interval 10
+              lcp-echo-failure 2
+              plugin rp-pppoe.so
+              require-chap
+              nobsdcomp
+              noccp
+              novj
+            '';
+            "ppp/chap-secrets" = chap-secrets;
+          };
+        };
+      };
+      client = {config, pkgs, ...}: {
+        services.pppd = {
+          enable = true;
+          peers.test = {
+            config = ''
+              plugin rp-pppoe.so eth1
+              name "flynn"
+              noipdefault
+              persist
+              noauth
+              debug
+            '';
+          };
+        };
+        environment.etc."ppp/chap-secrets" = chap-secrets;
+      };
+    };
+  
+    testScript = ''
+      startAll;
+      $client->waitUntilSucceeds("ping -c1 -W1 192.0.2.1");
+      $server->waitUntilSucceeds("ping -c1 -W1 192.0.2.2");
+    '';
+  })  
diff --git a/nixos/tests/predictable-interface-names.nix b/nixos/tests/predictable-interface-names.nix
index 85047f66f23c..194b4dafa772 100644
--- a/nixos/tests/predictable-interface-names.nix
+++ b/nixos/tests/predictable-interface-names.nix
@@ -16,6 +16,7 @@ in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: {
       networking.usePredictableInterfaceNames = lib.mkForce predictable;
       networking.useNetworkd = withNetworkd;
       networking.dhcpcd.enable = !withNetworkd;
+      networking.useDHCP = !withNetworkd;
     };
 
     testScript = ''
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 02d83f82f338..676183f6356f 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -229,6 +229,40 @@ let
       '';
     };
 
+    nextcloud = {
+      exporterConfig = {
+        enable = true;
+        passwordFile = "/var/nextcloud-pwfile";
+        url = "http://localhost/negative-space.xml";
+      };
+      metricProvider = {
+        systemd.services.nc-pwfile = let
+          passfile = (pkgs.writeText "pwfile" "snakeoilpw");
+        in {
+          requiredBy = [ "prometheus-nextcloud-exporter.service" ];
+          before = [ "prometheus-nextcloud-exporter.service" ];
+          serviceConfig.ExecStart = ''
+            ${pkgs.coreutils}/bin/install -o nextcloud-exporter -m 0400 ${passfile} /var/nextcloud-pwfile
+          '';
+        };
+        services.nginx = {
+          enable = true;
+          virtualHosts."localhost" = {
+            basicAuth.nextcloud-exporter = "snakeoilpw";
+            locations."/" = {
+              root = "${pkgs.prometheus-nextcloud-exporter.src}/serverinfo/testdata";
+            };
+          };
+        };
+      };
+      exporterTest = ''
+        waitForUnit("nginx.service")
+        waitForUnit("prometheus-nextcloud-exporter.service")
+        waitForOpenPort(9205)
+        succeed("curl -sSf http://localhost:9205/metrics | grep -q 'nextcloud_up 1'")
+      '';
+    };
+
     nginx = {
       exporterConfig = {
         enable = true;
@@ -297,6 +331,22 @@ let
       '';
     };
 
+    rspamd = {
+      exporterConfig = {
+        enable = true;
+      };
+      metricProvider = {
+        services.rspamd.enable = true;
+      };
+      exporterTest = ''
+        waitForUnit("rspamd.service");
+        waitForUnit("prometheus-rspamd-exporter.service");
+        waitForOpenPort(11334);
+        waitForOpenPort(7980);
+        waitUntilSucceeds("curl -sSf localhost:7980/metrics | grep -q 'rspamd_scanned{host=\"rspamd\"} 0'");
+      '';
+    };
+
     snmp = {
       exporterConfig = {
         enable = true;
diff --git a/nixos/tests/shiori.nix b/nixos/tests/shiori.nix
new file mode 100644
index 000000000000..0022a7220fe2
--- /dev/null
+++ b/nixos/tests/shiori.nix
@@ -0,0 +1,17 @@
+import ./make-test.nix ({ lib, ...}:
+
+{
+  name = "shiori";
+  meta.maintainers = with lib.maintainers; [ minijackson ];
+
+  machine =
+    { ... }:
+    { services.shiori.enable = true; };
+
+  testScript = ''
+    $machine->waitForUnit('shiori.service');
+    $machine->waitForOpenPort('8080');
+    $machine->succeed("curl --fail http://localhost:8080/");
+    $machine->succeed("curl --fail --location http://localhost:8080/ | grep -qi shiori");
+  '';
+})
diff --git a/nixos/tests/systemd-networkd-wireguard.nix b/nixos/tests/systemd-networkd-wireguard.nix
index f1ce1e791ce3..aa0ac54e7969 100644
--- a/nixos/tests/systemd-networkd-wireguard.nix
+++ b/nixos/tests/systemd-networkd-wireguard.nix
@@ -2,6 +2,7 @@ let generateNodeConf = { lib, pkgs, config, privkpath, pubk, peerId, nodeId, ...
       imports = [ common/user-account.nix ];
       systemd.services.systemd-networkd.environment.SYSTEMD_LOG_LEVEL = "debug";
       networking.useNetworkd = true;
+      networking.useDHCP = false;
       networking.firewall.enable = false;
       virtualisation.vlans = [ 1 ];
       environment.systemPackages = with pkgs; [ wireguard-tools ];
diff --git a/nixos/tests/trickster.nix b/nixos/tests/trickster.nix
new file mode 100644
index 000000000000..1461a32bb07e
--- /dev/null
+++ b/nixos/tests/trickster.nix
@@ -0,0 +1,29 @@
+import ./make-test.nix ({ pkgs, ... }: {
+  name = "trickster";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ "1000101" ];
+  };
+
+  nodes = {
+    prometheus = { ... }: {
+      services.prometheus.enable = true;
+      networking.firewall.allowedTCPPorts = [ 9090 ];
+    };
+    trickster = { ... }: {
+      services.trickster.enable = true;
+    };
+  };
+
+  testScript = ''
+    startAll;
+    $prometheus->waitForUnit("prometheus.service");
+    $prometheus->waitForOpenPort(9090);
+    $prometheus->waitUntilSucceeds("curl -L http://localhost:9090/metrics | grep 'promhttp_metric_handler_requests_total{code=\"500\"} 0'");
+    $trickster->waitForUnit("trickster.service");
+    $trickster->waitForOpenPort(8082);
+    $trickster->waitForOpenPort(9090);
+    $trickster->waitUntilSucceeds("curl -L http://localhost:8082/metrics | grep 'promhttp_metric_handler_requests_total{code=\"500\"} 0'");
+    $trickster->waitUntilSucceeds("curl -L http://prometheus:9090/metrics | grep 'promhttp_metric_handler_requests_total{code=\"500\"} 0'");
+    $trickster->waitUntilSucceeds("curl -L http://localhost:9090/metrics | grep 'promhttp_metric_handler_requests_total{code=\"500\"} 0'");
+  '';
+})
\ No newline at end of file
diff --git a/nixos/tests/wordpress.nix b/nixos/tests/wordpress.nix
index 774ef6293b51..c6acfa6c1f3d 100644
--- a/nixos/tests/wordpress.nix
+++ b/nixos/tests/wordpress.nix
@@ -20,12 +20,6 @@ import ./make-test.nix ({ pkgs, ... }:
       };
 
       networking.hosts."127.0.0.1" = [ "site1.local" "site2.local" ];
-
-      # required for wordpress-init.service to succeed
-      systemd.tmpfiles.rules = [
-        "F /var/lib/wordpress/site1.local/secret-keys.php 0440 wordpress wwwrun - -"
-        "F /var/lib/wordpress/site2.local/secret-keys.php 0440 wordpress wwwrun - -"
-      ];
     };
 
   testScript = ''
@@ -37,6 +31,11 @@ import ./make-test.nix ({ pkgs, ... }:
 
     $machine->succeed("curl -L site1.local | grep 'Welcome to the famous'");
     $machine->succeed("curl -L site2.local | grep 'Welcome to the famous'");
+
+    $machine->succeed("systemctl --no-pager show wordpress-init-site1.local.service | grep 'ExecStart=.*status=0'");
+    $machine->succeed("systemctl --no-pager show wordpress-init-site2.local.service | grep 'ExecStart=.*status=0'");
+    $machine->succeed("grep -E '^define.*NONCE_SALT.{64,};\$' /var/lib/wordpress/site1.local/secret-keys.php");
+    $machine->succeed("grep -E '^define.*NONCE_SALT.{64,};\$' /var/lib/wordpress/site2.local/secret-keys.php");
   '';
 
 })
diff --git a/nixos/tests/xfce.nix b/nixos/tests/xfce.nix
index 12d8a050d47b..7ff623062d93 100644
--- a/nixos/tests/xfce.nix
+++ b/nixos/tests/xfce.nix
@@ -17,6 +17,10 @@ import ./make-test.nix ({ pkgs, ...} : {
       services.xserver.desktopManager.xfce.enable = true;
 
       environment.systemPackages = [ pkgs.xorg.xmessage ];
+
+      hardware.pulseaudio.enable = true; # needed for the factl test, /dev/snd/* exists without them but udev doesn't care then
+
+      virtualisation.memorySize = 1024;
     };
 
   testScript =
@@ -28,7 +32,7 @@ import ./make-test.nix ({ pkgs, ...} : {
       $machine->sleep(10);
 
       # Check that logging in has given the user ownership of devices.
-      $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
+      $machine->succeed("getfacl -p /dev/snd/timer | grep -q alice");
 
       $machine->succeed("su - alice -c 'DISPLAY=:0.0 xfce4-terminal &'");
       $machine->waitForWindow(qr/Terminal/);
diff --git a/nixos/tests/xfce4-14.nix b/nixos/tests/xfce4-14.nix
index d9b10aabaa1f..d9b87b084376 100644
--- a/nixos/tests/xfce4-14.nix
+++ b/nixos/tests/xfce4-14.nix
@@ -12,6 +12,10 @@ import ./make-test.nix ({ pkgs, ...} : {
       services.xserver.displayManager.auto.user = "alice";
 
       services.xserver.desktopManager.xfce4-14.enable = true;
+
+      hardware.pulseaudio.enable = true; # needed for the factl test, /dev/snd/* exists without them but udev doesn't care then
+
+      virtualisation.memorySize = 1024;
     };
 
   testScript =
@@ -23,7 +27,7 @@ import ./make-test.nix ({ pkgs, ...} : {
       $machine->sleep(10);
 
       # Check that logging in has given the user ownership of devices.
-      $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
+      $machine->succeed("getfacl -p /dev/snd/timer | grep -q alice");
 
       $machine->succeed("su - alice -c 'DISPLAY=:0.0 xfce4-terminal &'");
       $machine->waitForWindow(qr/Terminal/);
diff --git a/nixos/tests/xmonad.nix b/nixos/tests/xmonad.nix
index 4d3bc28cd349..79c15ccffecd 100644
--- a/nixos/tests/xmonad.nix
+++ b/nixos/tests/xmonad.nix
@@ -26,7 +26,7 @@ import ./make-test.nix ({ pkgs, ...} : {
     $machine->waitForFile("/home/alice/.Xauthority");
     $machine->succeed("xauth merge ~alice/.Xauthority");
     $machine->sendKeys("alt-ctrl-x");
-    $machine->waitForWindow(qr/machine.*alice/);
+    $machine->waitForWindow(qr/alice.*machine/);
     $machine->sleep(1);
     $machine->screenshot("terminal");
     $machine->waitUntilSucceeds("xmonad --restart");
diff --git a/nixos/tests/yggdrasil.nix b/nixos/tests/yggdrasil.nix
new file mode 100644
index 000000000000..ddff35cce3a1
--- /dev/null
+++ b/nixos/tests/yggdrasil.nix
@@ -0,0 +1,123 @@
+let
+  aliceIp6 = "200:3b91:b2d8:e708:fbf3:f06:fdd5:90d0";
+  aliceKeys = {
+    EncryptionPublicKey = "13e23986fe76bc3966b42453f479bc563348b7ff76633b7efcb76e185ec7652f";
+    EncryptionPrivateKey = "9f86947b15e86f9badac095517a1982e39a2db37ca726357f95987b898d82208";
+    SigningPublicKey = "e2c43349083bc1e998e4ec4535b4c6a8f44ca9a5a8e07336561267253b2be5f4";
+    SigningPrivateKey = "fe3add8da35316c05f6d90d3ca79bd2801e6ccab6d37e5339fef4152589398abe2c43349083bc1e998e4ec4535b4c6a8f44ca9a5a8e07336561267253b2be5f4";
+  };
+  bobIp6 = "201:ebbd:bde9:f138:c302:4afa:1fb6:a19a";
+  bobConfig = {
+    InterfacePeers = {
+      eth1 = [ "tcp://192.168.1.200:12345" ];
+    };
+    MulticastInterfaces = [ "eth1" ];
+    LinkLocalTCPPort = 54321;
+    EncryptionPublicKey = "c99d6830111e12d1b004c52fe9e5a2eef0f6aefca167aca14589a370b7373279";
+    EncryptionPrivateKey = "2e698a53d3fdce5962d2ff37de0fe77742a5c8b56cd8259f5da6aa792f6e8ba3";
+    SigningPublicKey = "de111da0ec781e45bf6c63ecb45a78c24d7d4655abfaeea83b26c36eb5c0fd5b";
+    SigningPrivateKey = "2a6c21550f3fca0331df50668ffab66b6dce8237bcd5728e571e8033b363e247de111da0ec781e45bf6c63ecb45a78c24d7d4655abfaeea83b26c36eb5c0fd5b";
+  };
+
+in import ./make-test.nix ({ pkgs, ...} : {
+  name = "yggdrasil";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ gazally ];
+  };
+
+  nodes = rec {
+    # Alice is listening for peerings on a specified port,
+    # but has multicast peering disabled.  Alice has part of her
+    # yggdrasil config in Nix and part of it in a file.
+    alice =
+      { ... }:
+      {
+        networking = {
+          interfaces.eth1.ipv4.addresses = [{
+            address = "192.168.1.200";
+            prefixLength = 24;
+          }];
+          firewall.allowedTCPPorts = [ 80 12345 ];
+        };
+        services.httpd.enable = true;
+        services.httpd.adminAddr = "foo@example.org";
+
+        services.yggdrasil = {
+          enable = true;
+          config = {
+            Listen = ["tcp://0.0.0.0:12345"];
+            MulticastInterfaces = [ ];
+          };
+          configFile = toString (pkgs.writeTextFile {
+                         name = "yggdrasil-alice-conf";
+                         text = builtins.toJSON aliceKeys;
+                       });
+        };
+      };
+
+    # Bob is set up to peer with Alice, and also to do local multicast
+    # peering.  Bob's yggdrasil config is in a file.
+    bob =
+      { ... }:
+      {
+        networking.firewall.allowedTCPPorts = [ 54321 ];
+        services.yggdrasil = {
+          enable = true;
+          openMulticastPort = true;
+          configFile = toString (pkgs.writeTextFile {
+                         name = "yggdrasil-bob-conf";
+                         text = builtins.toJSON bobConfig;
+                       });
+        };
+      };
+
+    # Carol only does local peering.  Carol's yggdrasil config is all Nix.
+    carol =
+      { ... }:
+      {
+        networking.firewall.allowedTCPPorts = [ 43210 ];
+        services.yggdrasil = {
+          enable = true;
+          denyDhcpcdInterfaces = [ "ygg0" ];
+          config = {
+            IfTAPMode = true;
+            IFName = "ygg0";
+            MulticastInterfaces = [ "eth1" ];
+            LinkLocalTCPPort = 43210;
+          };
+        };
+      };
+    };
+
+  testScript =
+    ''
+      # Give Alice a head start so she is ready when Bob calls.
+      $alice->start;
+      $alice->waitForUnit("yggdrasil.service");
+
+      $bob->start;
+      $carol->start;
+      $bob->waitForUnit("yggdrasil.service");
+      $carol->waitForUnit("yggdrasil.service");
+
+      $carol->waitUntilSucceeds("[ `ip -o -6 addr show dev ygg0 scope global | grep -v tentative | wc -l` -ge 1 ]");
+      my $carolIp6 = (split /[ \/]+/, $carol->succeed("ip -o -6 addr show dev ygg0 scope global"))[3];
+
+      # If Alice can talk to Carol, then Bob's outbound peering and Carol's
+      # local peering have succeeded and everybody is connected.
+      $alice->waitUntilSucceeds("ping -c 1 $carolIp6");
+      $alice->succeed("ping -c 1 ${bobIp6}");
+
+      $bob->succeed("ping -c 1 ${aliceIp6}");
+      $bob->succeed("ping -c 1 $carolIp6");
+
+      $carol->succeed("ping -c 1 ${aliceIp6}");
+      $carol->succeed("ping -c 1 ${bobIp6}");
+
+      $carol->fail("journalctl -u dhcpcd | grep ygg0");
+
+      $alice->waitForUnit("httpd.service");
+      $carol->succeed("curl --fail -g http://[${aliceIp6}]");
+
+    '';
+})