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/x-windows.xml6
-rw-r--r--nixos/doc/manual/installation/installing.xml6
-rw-r--r--nixos/doc/manual/release-notes/release-notes.xml1
-rw-r--r--nixos/doc/manual/release-notes/rl-1909.xml10
-rw-r--r--nixos/doc/manual/release-notes/rl-2003.xml80
-rw-r--r--nixos/lib/utils.nix114
-rw-r--r--nixos/modules/config/fonts/fontconfig.nix4
-rw-r--r--nixos/modules/config/system-path.nix3
-rw-r--r--nixos/modules/hardware/printers.nix6
-rw-r--r--nixos/modules/module-list.nix7
-rw-r--r--nixos/modules/programs/mtr.nix14
-rw-r--r--nixos/modules/programs/x2goserver.nix2
-rw-r--r--nixos/modules/services/audio/roon-server.nix4
-rw-r--r--nixos/modules/services/cluster/kubernetes/flannel.nix4
-rw-r--r--nixos/modules/services/databases/postgresql.nix2
-rw-r--r--nixos/modules/services/hardware/upower.nix53
-rw-r--r--nixos/modules/services/mail/mailman.nix205
-rw-r--r--nixos/modules/services/mail/rss2email.nix5
-rw-r--r--nixos/modules/services/misc/dysnomia.nix1
-rw-r--r--nixos/modules/services/misc/gitlab.nix335
-rw-r--r--nixos/modules/services/misc/gitlab.xml56
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.nix3
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix92
-rw-r--r--nixos/modules/services/network-filesystems/ceph.nix2
-rw-r--r--nixos/modules/services/networking/networkmanager.nix27
-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/pdns-recursor.nix2
-rw-r--r--nixos/modules/services/networking/toxvpn.nix2
-rw-r--r--nixos/modules/services/printing/cupsd.nix10
-rw-r--r--nixos/modules/services/search/kibana.nix2
-rw-r--r--nixos/modules/services/torrent/deluge.nix1
-rw-r--r--nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix2
-rw-r--r--nixos/modules/services/web-apps/moodle.nix8
-rw-r--r--nixos/modules/services/web-apps/restya-board.nix3
-rw-r--r--nixos/modules/services/web-apps/selfoss.nix2
-rw-r--r--nixos/modules/services/web-apps/tt-rss.nix2
-rw-r--r--nixos/modules/services/web-apps/zabbix.nix4
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix1
-rw-r--r--nixos/modules/services/web-servers/phpfpm/default.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix6
-rw-r--r--nixos/modules/services/x11/desktop-managers/xterm.nix3
-rw-r--r--nixos/modules/services/x11/extra-layouts.nix5
-rw-r--r--nixos/modules/services/x11/hardware/cmt.nix54
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix10
-rw-r--r--nixos/tests/all-tests.nix2
-rw-r--r--nixos/tests/gitlab.nix42
-rw-r--r--nixos/tests/glib-networking.nix17
-rw-r--r--nixos/tests/libgdata.nix21
-rw-r--r--nixos/tests/login.nix1
-rw-r--r--nixos/tests/mumble.nix4
-rw-r--r--nixos/tests/plasma5.nix1
-rw-r--r--nixos/tests/prometheus-exporters.nix16
-rw-r--r--nixos/tests/xfce.nix4
-rw-r--r--nixos/tests/xfce4-14.nix4
-rw-r--r--nixos/tests/xmonad.nix2
57 files changed, 997 insertions, 291 deletions
diff --git a/nixos/doc/manual/configuration/x-windows.xml b/nixos/doc/manual/configuration/x-windows.xml
index 7cdc5196e0d2e..f6f659b02afa6 100644
--- a/nixos/doc/manual/configuration/x-windows.xml
+++ b/nixos/doc/manual/configuration/x-windows.xml
@@ -280,6 +280,12 @@ xkb_symbols "media"
 <xref linkend="opt-services.xserver.displayManager.sessionCommands"/> = "setxkbmap -keycodes media";
 </programlisting>
   <para>
+    If you are manually starting the X server, you should set the argument
+    <literal>-xkbdir /etc/X11/xkb</literal>, otherwise X won't find your layout files.
+    For example with <command>xinit</command> run
+    <screen><prompt>$ </prompt>xinit -- -xkbdir /etc/X11/xkb</screen>
+  </para>
+  <para>
    To learn how to write layouts take a look at the XKB
   <link xlink:href="https://www.x.org/releases/current/doc/xorg-docs/input/XKB-Enhancing.html#Defining_New_Layouts">
    documentation
diff --git a/nixos/doc/manual/installation/installing.xml b/nixos/doc/manual/installation/installing.xml
index 9cea2db610e0d..f1e1568c03494 100644
--- a/nixos/doc/manual/installation/installing.xml
+++ b/nixos/doc/manual/installation/installing.xml
@@ -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/release-notes/release-notes.xml b/nixos/doc/manual/release-notes/release-notes.xml
index 02b5914772143..444862c5739bb 100644
--- a/nixos/doc/manual/release-notes/release-notes.xml
+++ b/nixos/doc/manual/release-notes/release-notes.xml
@@ -8,6 +8,7 @@
   This section lists the release notes for each stable version of NixOS and
   current unstable revision.
  </para>
+ <xi:include href="rl-2003.xml" />
  <xi:include href="rl-1909.xml" />
  <xi:include href="rl-1903.xml" />
  <xi:include href="rl-1809.xml" />
diff --git a/nixos/doc/manual/release-notes/rl-1909.xml b/nixos/doc/manual/release-notes/rl-1909.xml
index ee3b035815299..f9cea242c153c 100644
--- a/nixos/doc/manual/release-notes/rl-1909.xml
+++ b/nixos/doc/manual/release-notes/rl-1909.xml
@@ -206,6 +206,11 @@
    </listitem>
    <listitem>
     <para>
+     PostgreSQL 9.4 is scheduled EOL during the 19.09 life cycle and has been removed.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
      The options <option>services.prometheus.alertmanager.user</option> and
      <option>services.prometheus.alertmanager.group</option> have been removed
      because the alertmanager service is now using systemd's <link
@@ -444,8 +449,9 @@
    </listitem>
    <listitem>
      <para>
-       <option>services.xserver.desktopManager.xterm</option> is now disabled by default.
-       It was not useful except for debugging purposes and was confusingly set as default in some circumstances.
+       <option>services.xserver.desktopManager.xterm</option> is now disabled by default if <literal>stateVersion</literal> is 19.09 or higher.
+       Previously the xterm desktopManager was enabled when xserver was enabled, but it isn't useful for all people so it didn't make sense to
+       have any desktopManager enabled default.
      </para>
    </listitem>
    <listitem>
diff --git a/nixos/doc/manual/release-notes/rl-2003.xml b/nixos/doc/manual/release-notes/rl-2003.xml
new file mode 100644
index 0000000000000..c84bc8dbb3791
--- /dev/null
+++ b/nixos/doc/manual/release-notes/rl-2003.xml
@@ -0,0 +1,80 @@
+<section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-20.03">
+ <title>Release 20.03 (“Markhor”, 2020.03/??)</title>
+
+ <section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-20.03-highlights">
+  <title>Highlights</title>
+
+  <para>
+   In addition to numerous new and upgraded packages, this release has the
+   following highlights:
+  </para>
+
+  <itemizedlist>
+   <listitem>
+    <para>
+     Support is planned until the end of October 2020, handing over to 20.09.
+    </para>
+   </listitem>
+  </itemizedlist>
+ </section>
+
+ <section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-20.03-new-services">
+  <title>New Services</title>
+
+  <para>
+   The following new services were added since the last release:
+  </para>
+
+  <itemizedlist>
+   <listitem>
+    <para />
+   </listitem>
+  </itemizedlist>
+
+ </section>
+
+ <section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-20.03-incompatibilities">
+  <title>Backward Incompatibilities</title>
+
+  <para>
+   When upgrading from a previous release, please be aware of the following
+   incompatible changes:
+  </para>
+
+  <itemizedlist>
+   <listitem>
+    <para />
+   </listitem>
+  </itemizedlist>
+ </section>
+
+ <section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-20.03-notable-changes">
+  <title>Other Notable Changes</title>
+
+  <itemizedlist>
+   <listitem>
+    <para />
+   </listitem>
+  </itemizedlist>
+ </section>
+</section>
diff --git a/nixos/lib/utils.nix b/nixos/lib/utils.nix
index 3297cf06108b7..a522834e42942 100644
--- a/nixos/lib/utils.nix
+++ b/nixos/lib/utils.nix
@@ -1,6 +1,6 @@
 pkgs: with pkgs.lib;
 
-{
+rec {
 
   # Check whenever fileSystem is needed for boot
   fsNeededForBoot = fs: fs.neededForBoot
@@ -24,4 +24,116 @@ pkgs: with pkgs.lib;
       throw "${shell} is not a shell package"
     else
       shell;
+
+  /* Recurse into a list or an attrset, searching for attrs named like
+     the value of the "attr" parameter, and return an attrset where the
+     names are the corresponding jq path where the attrs were found and
+     the values are the values of the attrs.
+
+     Example:
+       recursiveGetAttrWithJqPrefix {
+         example = [
+           {
+             irrelevant = "not interesting";
+           }
+           {
+             ignored = "ignored attr";
+             relevant = {
+               secret = {
+                 _secret = "/path/to/secret";
+               };
+             };
+           }
+         ];
+       } "_secret" -> { ".example[1].relevant.secret" = "/path/to/secret"; }
+  */
+  recursiveGetAttrWithJqPrefix = item: attr:
+    let
+      recurse = prefix: item:
+        if item ? ${attr} then
+          nameValuePair prefix item.${attr}
+        else if isAttrs item then
+          map (name: recurse (prefix + "." + name) item.${name}) (attrNames item)
+        else if isList item then
+          imap0 (index: item: recurse (prefix + "[${toString index}]") item) item
+        else
+          [];
+    in listToAttrs (flatten (recurse "" item));
+
+  /* Takes an attrset and a file path and generates a bash snippet that
+     outputs a JSON file at the file path with all instances of
+
+     { _secret = "/path/to/secret" }
+
+     in the attrset replaced with the contents of the file
+     "/path/to/secret" in the output JSON.
+
+     When a configuration option accepts an attrset that is finally
+     converted to JSON, this makes it possible to let the user define
+     arbitrary secret values.
+
+     Example:
+       If the file "/path/to/secret" contains the string
+       "topsecretpassword1234",
+
+       genJqSecretsReplacementSnippet {
+         example = [
+           {
+             irrelevant = "not interesting";
+           }
+           {
+             ignored = "ignored attr";
+             relevant = {
+               secret = {
+                 _secret = "/path/to/secret";
+               };
+             };
+           }
+         ];
+       } "/path/to/output.json"
+
+       would generate a snippet that, when run, outputs the following
+       JSON file at "/path/to/output.json":
+
+       {
+         "example": [
+           {
+             "irrelevant": "not interesting"
+           },
+           {
+             "ignored": "ignored attr",
+             "relevant": {
+               "secret": "topsecretpassword1234"
+             }
+           }
+         ]
+       }
+  */
+  genJqSecretsReplacementSnippet = genJqSecretsReplacementSnippet' "_secret";
+
+  # Like genJqSecretsReplacementSnippet, but allows the name of the
+  # attr which identifies the secret to be changed.
+  genJqSecretsReplacementSnippet' = attr: set: output:
+    let
+      secrets = recursiveGetAttrWithJqPrefix set attr;
+    in ''
+      if [[ -h '${output}' ]]; then
+        rm '${output}'
+      fi
+    ''
+    + concatStringsSep
+        "\n"
+        (imap1 (index: name: "export secret${toString index}=$(<'${secrets.${name}}')")
+               (attrNames secrets))
+    + "\n"
+    + "${pkgs.jq}/bin/jq >'${output}' '"
+    + concatStringsSep
+      " | "
+      (imap1 (index: name: ''${name} = $ENV.secret${toString index}'')
+             (attrNames secrets))
+    + ''
+      ' <<'EOF'
+      ${builtins.toJSON set}
+      EOF
+    '';
 }
diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
index bcb86f11ead76..8f227c423266b 100644
--- a/nixos/modules/config/fonts/fontconfig.nix
+++ b/nixos/modules/config/fonts/fontconfig.nix
@@ -51,8 +51,8 @@ let
                   then "fontconfig"
                   else "fontconfig_${version}";
       makeCache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.fonts; };
-      cache     = makeCache pkgs."${fcPackage}";
-      cache32   = makeCache pkgs.pkgsi686Linux."${fcPackage}";
+      cache     = makeCache pkgs.${fcPackage};
+      cache32   = makeCache pkgs.pkgsi686Linux.${fcPackage};
     in
     pkgs.writeText "fc-00-nixos-cache.conf" ''
       <?xml version='1.0'?>
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
index fae2fc7400829..aba9bc0945b19 100644
--- a/nixos/modules/config/system-path.nix
+++ b/nixos/modules/config/system-path.nix
@@ -135,6 +135,9 @@ in
       # outputs TODO: note that the tools will often not be linked by default
       postBuild =
         ''
+          # Remove wrapped binaries, they shouldn't be accessible via PATH.
+          find $out/bin -maxdepth 1 -name ".*-wrapped" -type l -delete
+
           if [ -x $out/bin/glib-compile-schemas -a -w $out/share/glib-2.0/schemas ]; then
               $out/bin/glib-compile-schemas $out/share/glib-2.0/schemas
           fi
diff --git a/nixos/modules/hardware/printers.nix b/nixos/modules/hardware/printers.nix
index 12ee5516d4ed6..56b91933477d9 100644
--- a/nixos/modules/hardware/printers.nix
+++ b/nixos/modules/hardware/printers.nix
@@ -94,8 +94,8 @@ in {
             ppdOptions = mkOption {
               type = types.attrsOf types.str;
               example = {
-                "PageSize" = "A4";
-                "Duplex" = "DuplexNoTumble";
+                PageSize = "A4";
+                Duplex = "DuplexNoTumble";
               };
               default = {};
               description = ''
@@ -110,7 +110,7 @@ in {
   };
 
   config = mkIf (cfg.ensurePrinters != [] && config.services.printing.enable) {
-    systemd.services."ensure-printers" = let
+    systemd.services.ensure-printers = let
       cupsUnit = if config.services.printing.startWhenNeeded then "cups.socket" else "cups.service";
     in {
       description = "Ensure NixOS-configured CUPS printers";
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index c21973faa89fd..fb8453f1d5373 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -567,7 +567,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
@@ -650,14 +649,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
@@ -853,6 +853,7 @@
   ./services/x11/hardware/multitouch.nix
   ./services/x11/hardware/synaptics.nix
   ./services/x11/hardware/wacom.nix
+  ./services/x11/hardware/cmt.nix
   ./services/x11/gdk-pixbuf.nix
   ./services/x11/redshift.nix
   ./services/x11/urxvtd.nix
diff --git a/nixos/modules/programs/mtr.nix b/nixos/modules/programs/mtr.nix
index 1fdec4c04f682..75b710c1584fc 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/x2goserver.nix b/nixos/modules/programs/x2goserver.nix
index d9e7b6e4a5c02..77a1a0da79938 100644
--- a/nixos/modules/programs/x2goserver.nix
+++ b/nixos/modules/programs/x2goserver.nix
@@ -6,7 +6,7 @@ let
   cfg = config.programs.x2goserver;
 
   defaults = {
-    superenicer = { "enable" = cfg.superenicer.enable; };
+    superenicer = { enable = cfg.superenicer.enable; };
   };
   confText = generators.toINI {} (recursiveUpdate defaults cfg.settings);
   x2goServerConf = pkgs.writeText "x2goserver.conf" confText;
diff --git a/nixos/modules/services/audio/roon-server.nix b/nixos/modules/services/audio/roon-server.nix
index d4b0b098b78e9..4eda3c5708da7 100644
--- a/nixos/modules/services/audio/roon-server.nix
+++ b/nixos/modules/services/audio/roon-server.nix
@@ -61,8 +61,8 @@ in {
     };
 
     
-    users.groups."${cfg.group}" = {};
-    users.users."${cfg.user}" =
+    users.groups.${cfg.group} = {};
+    users.users.${cfg.user} =
       if cfg.user == "roon-server" then {
         isSystemUser = true;
         description = "Roon Server user";
diff --git a/nixos/modules/services/cluster/kubernetes/flannel.nix b/nixos/modules/services/cluster/kubernetes/flannel.nix
index 74d10d684375f..d799e638fc94a 100644
--- a/nixos/modules/services/cluster/kubernetes/flannel.nix
+++ b/nixos/modules/services/cluster/kubernetes/flannel.nix
@@ -47,7 +47,7 @@ in
       }];
     };
 
-    systemd.services."mk-docker-opts" = {
+    systemd.services.mk-docker-opts = {
       description = "Pre-Docker Actions";
       path = with pkgs; [ gawk gnugrep ];
       script = ''
@@ -57,7 +57,7 @@ in
       serviceConfig.Type = "oneshot";
     };
 
-    systemd.paths."flannel-subnet-env" = {
+    systemd.paths.flannel-subnet-env = {
       wantedBy = [ "flannel.service" ];
       pathConfig = {
         PathModified = "/run/flannel/subnet.env";
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index 1ed4d3290ced3..7bba4dacddcca 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -228,7 +228,7 @@ in
       # systems!
       mkDefault (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 pkgs.postgresql_9_4);
+            else throw "postgresql_9_4 was removed, please upgrade your postgresql version.");
 
     services.postgresql.dataDir =
       mkDefault (if versionAtLeast config.system.stateVersion "17.09" then "/var/lib/postgresql/${config.services.postgresql.package.psqlSchema}"
diff --git a/nixos/modules/services/hardware/upower.nix b/nixos/modules/services/hardware/upower.nix
index 1da47349c0771..5e7ac7a6e6591 100644
--- a/nixos/modules/services/hardware/upower.nix
+++ b/nixos/modules/services/hardware/upower.nix
@@ -5,8 +5,11 @@
 with lib;
 
 let
+
   cfg = config.services.upower;
+
 in
+
 {
 
   ###### interface
@@ -49,55 +52,7 @@ in
 
     services.udev.packages = [ cfg.package ];
 
-    systemd.services.upower =
-      { description = "Power Management Daemon";
-        path = [ pkgs.glib.out ]; # needed for gdbus
-        serviceConfig =
-          { Type = "dbus";
-            BusName = "org.freedesktop.UPower";
-            ExecStart = "@${cfg.package}/libexec/upowerd upowerd";
-            Restart = "on-failure";
-            # Upstream lockdown:
-            # Filesystem lockdown
-            ProtectSystem = "strict";
-            # Needed by keyboard backlight support
-            ProtectKernelTunables = false;
-            ProtectControlGroups = true;
-            ReadWritePaths = "/var/lib/upower";
-            ProtectHome = true;
-            PrivateTmp = true;
-
-            # Network
-            # PrivateNetwork=true would block udev's netlink socket
-            RestrictAddressFamilies = "AF_UNIX AF_NETLINK";
-
-            # Execute Mappings
-            MemoryDenyWriteExecute = true;
-
-            # Modules
-            ProtectKernelModules = true;
-
-            # Real-time
-            RestrictRealtime = true;
-
-            # Privilege escalation
-            NoNewPrivileges = true;
-          };
-      };
-
-    system.activationScripts.upower =
-      ''
-        mkdir -m 0755 -p /var/lib/upower
-      '';
-
-    # The upower daemon seems to get stuck after doing a suspend
-    # (i.e. subsequent suspend requests will say "Sleep has already
-    # been requested and is pending").  So as a workaround, restart
-    # the daemon.
-    powerManagement.resumeCommands =
-      ''
-        ${config.systemd.package}/bin/systemctl try-restart upower
-      '';
+    systemd.packages = [ cfg.package ];
 
   };
 
diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix
index 11dd5cb48db04..e917209f3d1f5 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/rss2email.nix b/nixos/modules/services/mail/rss2email.nix
index df454abc82671..c1e5964c4536d 100644
--- a/nixos/modules/services/mail/rss2email.nix
+++ b/nixos/modules/services/mail/rss2email.nix
@@ -43,9 +43,8 @@ in {
           <literal>[DEFAULT]</literal> block along with the
           <literal>to</literal> parameter.
 
-          See
-          <literal>https://github.com/rss2email/rss2email/blob/master/r2e.1</literal>
-          for more information on which parameters are accepted.
+          See <literal>man r2e</literal> for more information on which
+          parameters are accepted.
         '';
       };
 
diff --git a/nixos/modules/services/misc/dysnomia.nix b/nixos/modules/services/misc/dysnomia.nix
index 9402d5cd801c0..33a6fb1526416 100644
--- a/nixos/modules/services/misc/dysnomia.nix
+++ b/nixos/modules/services/misc/dysnomia.nix
@@ -151,6 +151,7 @@ in
       enableSubversionRepository = config.services.svnserve.enable;
       enableTomcatWebApplication = config.services.tomcat.enable;
       enableMongoDatabase = config.services.mongodb.enable;
+      enableInfluxDatabase = config.services.influxdb.enable;
     });
 
     dysnomia.properties = {
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index 3b39dde341b53..66da6864fca9c 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -1,6 +1,4 @@
-{ config, lib, pkgs, ... }:
-
-# TODO: support non-postgresql
+{ config, lib, pkgs, utils, ... }:
 
 with lib;
 
@@ -12,14 +10,12 @@ let
   gitlabSocket = "${cfg.statePath}/tmp/sockets/gitlab.socket";
   gitalySocket = "${cfg.statePath}/tmp/sockets/gitaly.socket";
   pathUrlQuote = url: replaceStrings ["/"] ["%2F"] url;
-  pgSuperUser = config.services.postgresql.superUser;
 
   databaseConfig = {
     production = {
       adapter = "postgresql";
       database = cfg.databaseName;
       host = cfg.databaseHost;
-      password = cfg.databasePassword;
       username = cfg.databaseUsername;
       encoding = "utf8";
       pool = cfg.databasePool;
@@ -66,13 +62,6 @@ let
 
   redisConfig.production.url = "redis://localhost:6379/";
 
-  secretsConfig.production = {
-    secret_key_base = cfg.secrets.secret;
-    otp_key_base = cfg.secrets.otp;
-    db_key_base = cfg.secrets.db;
-    openid_connect_signing_key = cfg.secrets.jws;
-  };
-
   gitlabConfig = {
     # These are the default settings from config/gitlab.example.yml
     production = flip recursiveUpdate cfg.extraConfig {
@@ -180,10 +169,11 @@ let
         address: "${cfg.smtp.address}",
         port: ${toString cfg.smtp.port},
         ${optionalString (cfg.smtp.username != null) ''user_name: "${cfg.smtp.username}",''}
-        ${optionalString (cfg.smtp.password != null) ''password: "${cfg.smtp.password}",''}
+        ${optionalString (cfg.smtp.passwordFile != null) ''password: "@smtpPassword@",''}
         domain: "${cfg.smtp.domain}",
         ${optionalString (cfg.smtp.authentication != null) "authentication: :${cfg.smtp.authentication},"}
         enable_starttls_auto: ${toString cfg.smtp.enableStartTLSAuto},
+        ca_file: "/etc/ssl/certs/ca-certificates.crt",
         openssl_verify_mode: '${cfg.smtp.opensslVerifyMode}'
       }
     end
@@ -244,13 +234,33 @@ in {
 
       databaseHost = mkOption {
         type = types.str;
-        default = "127.0.0.1";
-        description = "Gitlab database hostname.";
+        default = "";
+        description = ''
+          Gitlab database hostname. An empty string means <quote>use
+          local unix socket connection</quote>.
+        '';
       };
 
-      databasePassword = mkOption {
-        type = types.str;
-        description = "Gitlab database user password.";
+      databasePasswordFile = mkOption {
+        type = with types; nullOr path;
+        default = null;
+        description = ''
+          File containing the Gitlab database user password.
+
+          This should be a string, not a nix path, since nix paths are
+          copied into the world-readable nix store.
+        '';
+      };
+
+      databaseCreateLocally = mkOption {
+        type = types.bool;
+        default = true;
+        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.
+        '';
       };
 
       databaseName = mkOption {
@@ -338,10 +348,15 @@ in {
         '';
       };
 
-      initialRootPassword = mkOption {
-        type = types.str;
+      initialRootPasswordFile = mkOption {
+        type = with types; nullOr path;
+        default = null;
         description = ''
-          Initial password of the root account if this is a new install.
+          File containing the initial password of the root account if
+          this is a new install.
+
+          This should be a string, not a nix path, since nix paths are
+          copied into the world-readable nix store.
         '';
       };
 
@@ -365,15 +380,20 @@ in {
         };
 
         username = mkOption {
-          type = types.nullOr types.str;
+          type = with types; nullOr str;
           default = null;
           description = "Username of the SMTP server for Gitlab.";
         };
 
-        password = mkOption {
-          type = types.nullOr types.str;
+        passwordFile = mkOption {
+          type = types.nullOr types.path;
           default = null;
-          description = "Password of the SMTP server for Gitlab.";
+          description = ''
+            File containing the password of the SMTP server for Gitlab.
+
+            This should be a string, not a nix path, since nix paths
+            are copied into the world-readable nix store.
+          '';
         };
 
         domain = mkOption {
@@ -383,7 +403,7 @@ in {
         };
 
         authentication = mkOption {
-          type = types.nullOr types.str;
+          type = with types; nullOr str;
           default = null;
           description = "Authentitcation type to use, see http://api.rubyonrails.org/classes/ActionMailer/Base.html";
         };
@@ -401,68 +421,125 @@ in {
         };
       };
 
-      secrets.secret = mkOption {
-        type = types.str;
+      secrets.secretFile = mkOption {
+        type = with types; nullOr path;
+        default = null;
         description = ''
-          The secret is used to encrypt variables in the DB. If
-          you change or lose this key you will be unable to access variables
-          stored in database.
+          A file containing the secret used to encrypt variables in
+          the DB. If you change or lose this key you will be unable to
+          access variables stored in database.
 
           Make sure the secret is at least 30 characters and all random,
           no regular words or you'll be exposed to dictionary attacks.
+
+          This should be a string, not a nix path, since nix paths are
+          copied into the world-readable nix store.
         '';
       };
 
-      secrets.db = mkOption {
-        type = types.str;
+      secrets.dbFile = mkOption {
+        type = with types; nullOr path;
+        default = null;
         description = ''
-          The secret is used to encrypt variables in the DB. If
-          you change or lose this key you will be unable to access variables
-          stored in database.
+          A file containing the secret used to encrypt variables in
+          the DB. If you change or lose this key you will be unable to
+          access variables stored in database.
 
           Make sure the secret is at least 30 characters and all random,
           no regular words or you'll be exposed to dictionary attacks.
+
+          This should be a string, not a nix path, since nix paths are
+          copied into the world-readable nix store.
         '';
       };
 
-      secrets.otp = mkOption {
-        type = types.str;
+      secrets.otpFile = mkOption {
+        type = with types; nullOr path;
+        default = null;
         description = ''
-          The secret is used to encrypt secrets for OTP tokens. If
-          you change or lose this key, users which have 2FA enabled for login
-          won't be able to login anymore.
+          A file containing the secret used to encrypt secrets for OTP
+          tokens. If you change or lose this key, users which have 2FA
+          enabled for login won't be able to login anymore.
 
           Make sure the secret is at least 30 characters and all random,
           no regular words or you'll be exposed to dictionary attacks.
+
+          This should be a string, not a nix path, since nix paths are
+          copied into the world-readable nix store.
         '';
       };
 
-      secrets.jws = mkOption {
-        type = types.str;
+      secrets.jwsFile = mkOption {
+        type = with types; nullOr path;
+        default = null;
         description = ''
-          The secret is used to encrypt session keys. If you change or lose
-          this key, users will be disconnected.
+          A file containing the secret used to encrypt session
+          keys. If you change or lose this key, users will be
+          disconnected.
 
           Make sure the secret is an RSA private key in PEM format. You can
           generate one with
 
           openssl genrsa 2048
+
+          This should be a string, not a nix path, since nix paths are
+          copied into the world-readable nix store.
         '';
       };
 
       extraConfig = mkOption {
         type = types.attrs;
         default = {};
-        example = {
-          gitlab = {
-            default_projects_features = {
-              builds = false;
+        example = literalExample ''
+          {
+            gitlab = {
+              default_projects_features = {
+                builds = false;
+              };
+            };
+            omniauth = {
+              enabled = true;
+              auto_sign_in_with_provider = "openid_connect";
+              allow_single_sign_on = ["openid_connect"];
+              block_auto_created_users = false;
+              providers = [
+                {
+                  name = "openid_connect";
+                  label = "OpenID Connect";
+                  args = {
+                    name = "openid_connect";
+                    scope = ["openid" "profile"];
+                    response_type = "code";
+                    issuer = "https://keycloak.example.com/auth/realms/My%20Realm";
+                    discovery = true;
+                    client_auth_method = "query";
+                    uid_field = "preferred_username";
+                    client_options = {
+                      identifier = "gitlab";
+                      secret = { _secret = "/var/keys/gitlab_oidc_secret"; };
+                      redirect_uri = "https://git.example.com/users/auth/openid_connect/callback";
+                    };
+                  };
+                }
+              ];
             };
           };
-        };
+        '';
         description = ''
-          Extra options to be merged into config/gitlab.yml as nix
-          attribute set.
+          Extra options to be added under
+          <literal>production</literal> in
+          <filename>config/gitlab.yml</filename>, as a nix attribute
+          set.
+
+          Options containing secret data should be set to an attribute
+          set containing the attribute <literal>_secret</literal> - a
+          string pointing to a file containing the value the option
+          should be set to. See the example to get a better picture of
+          this: in the resulting
+          <filename>config/gitlab.yml</filename> file, the
+          <literal>production.omniauth.providers[0].args.client_options.secret</literal>
+          key will be set to the contents of the
+          <filename>/var/keys/gitlab_oidc_secret</filename> file.
         '';
       };
     };
@@ -470,12 +547,66 @@ in {
 
   config = mkIf cfg.enable {
 
+    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 = (cfg.databaseHost != "") -> (cfg.databasePasswordFile != null);
+        message = "When services.gitlab.databaseHost is customized, services.gitlab.databasePasswordFile must be set!";
+      }
+      {
+        assertion = cfg.initialRootPasswordFile != null;
+        message = "services.gitlab.initialRootPasswordFile must be set!";
+      }
+      {
+        assertion = cfg.secrets.secretFile != null;
+        message = "services.gitlab.secrets.secretFile must be set!";
+      }
+      {
+        assertion = cfg.secrets.dbFile != null;
+        message = "services.gitlab.secrets.dbFile must be set!";
+      }
+      {
+        assertion = cfg.secrets.otpFile != null;
+        message = "services.gitlab.secrets.otpFile must be set!";
+      }
+      {
+        assertion = cfg.secrets.jwsFile != null;
+        message = "services.gitlab.secrets.jwsFile must be set!";
+      }
+    ];
+
     environment.systemPackages = [ pkgs.git gitlab-rake gitlab-rails cfg.packages.gitlab-shell ];
 
     # Redis is required for the sidekiq queue runner.
     services.redis.enable = mkDefault true;
+
     # We use postgres as the main data store.
-    services.postgresql.enable = mkDefault true;
+    services.postgresql = optionalAttrs cfg.databaseCreateLocally {
+      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 ''
+      $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
+          $PSQL -tAc 'ALTER DATABASE "${cfg.databaseName}" OWNER TO "${cfg.databaseUsername}"'
+          if [[ -e "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}" ]]; then
+              echo "Reassigning ownership of database ${cfg.databaseName} to user ${cfg.databaseUsername} failed on last boot. Failing..."
+              exit 1
+          fi
+          touch "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}"
+          $PSQL "${cfg.databaseName}" -tAc "REASSIGN OWNED BY \"$current_owner\" TO \"${cfg.databaseUsername}\""
+          rm "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}"
+      fi
+      $PSQL '${cfg.databaseName}' -tAc "CREATE EXTENSION IF NOT EXISTS pg_trgm"
+    '');
+
     # Use postfix to send out mails.
     services.postfix.enable = mkDefault true;
 
@@ -527,14 +658,9 @@ in {
 
       "L+ /run/gitlab/shell-config.yml - - - - ${pkgs.writeText "config.yml" (builtins.toJSON gitlabShellConfig)}"
 
-      "L+ ${cfg.statePath}/config/gitlab.yml - - - - ${pkgs.writeText "gitlab.yml" (builtins.toJSON gitlabConfig)}"
-      "L+ ${cfg.statePath}/config/database.yml - - - - ${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)}"
-      "L+ ${cfg.statePath}/config/secrets.yml - - - - ${pkgs.writeText "secrets.yml" (builtins.toJSON secretsConfig)}"
       "L+ ${cfg.statePath}/config/unicorn.rb - - - - ${./defaultUnicornConfig.rb}"
-
       "L+ ${cfg.statePath}/config/initializers/extra-gitlab.rb - - - - ${extraGitlabRb}"
-    ] ++ optional cfg.smtp.enable
-      "L+ ${cfg.statePath}/config/initializers/smtp_settings.rb - - - - ${smtpSettings}" ;
+    ];
 
     systemd.services.gitlab-sidekiq = {
       after = [ "network.target" "redis.service" "gitlab.service" ];
@@ -626,46 +752,75 @@ in {
         gnupg
       ];
       preStart = ''
-        ${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -f ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION
-        ${pkgs.sudo}/bin/sudo -u ${cfg.user} rm -rf ${cfg.statePath}/db/*
-        ${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
-        ${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db
-
-        ${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/gitlab_shell_secret
-
-        ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${cfg.packages.gitlab-shell}/bin/install
-
-        if ! test -e "${cfg.statePath}/db-created"; then
-          if [ "${cfg.databaseHost}" = "127.0.0.1" ]; then
-            ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql postgres -c "CREATE ROLE ${cfg.databaseUsername} WITH LOGIN NOCREATEDB NOCREATEROLE ENCRYPTED PASSWORD '${cfg.databasePassword}'"
-            ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} ${config.services.postgresql.package}/bin/createdb --owner ${cfg.databaseUsername} ${cfg.databaseName}
-
-            # enable required pg_trgm extension for gitlab
-            ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql ${cfg.databaseName} -c "CREATE EXTENSION IF NOT EXISTS pg_trgm"
+        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=
+
+          ${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/gitlab_shell_secret
+
+          ${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
 
-          ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake db:schema:load
-
-          ${pkgs.sudo}/bin/sudo -u ${cfg.user} touch "${cfg.statePath}/db-created"
-        fi
-
-        # Always do the db migrations just to be sure the database is up-to-date
-        ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake db:migrate
-
-        if ! test -e "${cfg.statePath}/db-seeded"; then
-          ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${gitlab-rake}/bin/gitlab-rake db:seed_fu \
-            GITLAB_ROOT_PASSWORD='${cfg.initialRootPassword}' GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}'
-          ${pkgs.sudo}/bin/sudo -u ${cfg.user} touch "${cfg.statePath}/db-seeded"
-        fi
+          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}'
 
         # We remove potentially broken links to old gitlab-shell versions
         rm -Rf ${cfg.statePath}/repositories/**/*.git/hooks
 
-        ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${pkgs.git}/bin/git config --global core.autocrlf "input"
+        ${pkgs.git}/bin/git config --global core.autocrlf "input"
       '';
 
       serviceConfig = {
-        PermissionsStartOnly = true; # preStart must be run as root
         Type = "simple";
         User = cfg.user;
         Group = cfg.group;
diff --git a/nixos/modules/services/misc/gitlab.xml b/nixos/modules/services/misc/gitlab.xml
index 5ff570a442f69..b6171a9a194c1 100644
--- a/nixos/modules/services/misc/gitlab.xml
+++ b/nixos/modules/services/misc/gitlab.xml
@@ -54,8 +54,8 @@
 <programlisting>
 services.gitlab = {
   <link linkend="opt-services.gitlab.enable">enable</link> = true;
-  <link linkend="opt-services.gitlab.databasePassword">databasePassword</link> = "eXaMpl3";
-  <link linkend="opt-services.gitlab.initialRootPassword">initialRootPassword</link> = "UseNixOS!";
+  <link linkend="opt-services.gitlab.databasePasswordFile">databasePasswordFile</link> = "/var/keys/gitlab/db_password";
+  <link linkend="opt-services.gitlab.initialRootPasswordFile">initialRootPasswordFile</link> = "/var/keys/gitlab/root_password";
   <link linkend="opt-services.gitlab.https">https</link> = true;
   <link linkend="opt-services.gitlab.host">host</link> = "git.example.com";
   <link linkend="opt-services.gitlab.port">port</link> = 443;
@@ -67,38 +67,10 @@ services.gitlab = {
     <link linkend="opt-services.gitlab.smtp.port">port</link> = 25;
   };
   secrets = {
-    <link linkend="opt-services.gitlab.secrets.db">db</link> = "uPgq1gtwwHiatiuE0YHqbGa5lEIXH7fMsvuTNgdzJi8P0Dg12gibTzBQbq5LT7PNzcc3BP9P1snHVnduqtGF43PgrQtU7XL93ts6gqe9CBNhjtaqUwutQUDkygP5NrV6";
-    <link linkend="opt-services.gitlab.secrets.secret">secret</link> = "devzJ0Tz0POiDBlrpWmcsjjrLaltyiAdS8TtgT9YNBOoUcDsfppiY3IXZjMVtKgXrFImIennFGOpPN8IkP8ATXpRgDD5rxVnKuTTwYQaci2NtaV1XxOQGjdIE50VGsR3";
-    <link linkend="opt-services.gitlab.secrets.otp">otp</link> = "e1GATJVuS2sUh7jxiPzZPre4qtzGGaS22FR50Xs1TerRVdgI3CBVUi5XYtQ38W4xFeS4mDqi5cQjExE838iViSzCdcG19XSL6qNsfokQP9JugwiftmhmCadtsnHErBMI";
-    <link linkend="opt-services.gitlab.secrets.jws">jws</link> = ''
-      -----BEGIN RSA PRIVATE KEY-----
-      MIIEpAIBAAKCAQEArrtx4oHKwXoqUbMNqnHgAklnnuDon3XG5LJB35yPsXKv/8GK
-      ke92wkI+s1Xkvsp8tg9BIY/7c6YK4SR07EWL+dB5qwctsWR2Q8z+/BKmTx9D99pm
-      hnsjuNIXTF7BXrx3RX6BxZpH5Vzzh9nCwWKT/JCFqtwH7afNGGL7aMf+hdaiUg/Q
-      SD05yRObioiO4iXDolsJOhrnbZvlzVHl1ZYxFJv0H6/Snc0BBA9Fl/3uj6ANpbjP
-      eXF1SnJCqT87bj46r5NdVauzaRxAsIfqHroHK4UZ98X5LjGQFGvSqTvyjPBS4I1i
-      s7VJU28ObuutHxIxSlH0ibn4HZqWmKWlTS652wIDAQABAoIBAGtPcUTTw2sJlR3x
-      4k2wfAvLexkHNbZhBdKEa5JiO5mWPuLKwUiZEY2CU7Gd6csG3oqNWcm7/IjtC7dz
-      xV8p4yp8T4yq7vQIJ93B80NqTLtBD2QTvG2RCMJEPMzJUObWxkVmyVpLQyZo7KOd
-      KE/OM+aj94OUeEYLjRkSCScz1Gvq/qFG/nAy7KPCmN9JDHuhX26WHo2Rr1OnPNT/
-      7diph0bB9F3b8gjjNTqXDrpdAqVOgR/PsjEBz6DMY+bdyMIn87q2yfmMexxRofN6
-      LulpzSaa6Yup8N8H6PzVO6KAkQuf1aQRj0sMwGk1IZEnj6I0KbuHIZkw21Nc6sf2
-      ESFySDECgYEA1PnCNn5tmLnwe62Ttmrzl20zIS3Me1gUVJ1NTfr6+ai0I9iMYU21
-      5czuAjJPm9JKQF2vY8UAaCj2ZoObtHa/anb3xsCd8NXoM3iJq5JDoXI1ldz3Y+ad
-      U/bZUg1DLRvAniTuXmw9iOTwTwPxlDIGq5k+wG2Xmi1lk7zH8ezr9BMCgYEA0gfk
-      EhgcmPH8Z5cU3YYwOdt6HSJOM0OyN4k/5gnkv+HYVoJTj02gkrJmLr+mi1ugKj46
-      7huYO9TVnrKP21tmbaSv1dp5hS3letVRIxSloEtVGXmmdvJvBRzDWos+G+KcvADi
-      fFCz6w8v9NmO40CB7y/3SxTmSiSxDQeoi9LhDBkCgYEAsPgMWm25sfOnkY2NNUIv
-      wT8bAlHlHQT2d8zx5H9NttBpR3P0ShJhuF8N0sNthSQ7ULrIN5YGHYcUH+DyLAWU
-      TuomP3/kfa+xL7vUYb269tdJEYs4AkoppxBySoz8qenqpz422D0G8M6TpIS5Y5Qi
-      GMrQ6uLl21YnlpiCaFOfSQMCgYEAmZxj1kgEQmhZrnn1LL/D7czz1vMMNrpAUhXz
-      wg9iWmSXkU3oR1sDIceQrIhHCo2M6thwyU0tXjUft93pEQocM/zLDaGoVxtmRxxV
-      J08mg8IVD3jFoyFUyWxsBIDqgAKRl38eJsXvkO+ep3mm49Z+Ma3nM+apN3j2dQ0w
-      3HLzXaECgYBFLMEAboVFwi5+MZjGvqtpg2PVTisfuJy2eYnPwHs+AXUgi/xRNFjI
-      YHEa7UBPb5TEPSzWImQpETi2P5ywcUYL1EbN/nqPWmjFnat8wVmJtV4sUpJhubF4
-      Vqm9LxIWc1uQ1q1HDCejRIxIN3aSH+wgRS3Kcj8kCTIoXd1aERb04g==
-      -----END RSA PRIVATE KEY-----
-    '';
+    <link linkend="opt-services.gitlab.secrets.dbFile">dbFile</link> = "/var/keys/gitlab/db";
+    <link linkend="opt-services.gitlab.secrets.secretFile">secretFile</link> = "/var/keys/gitlab/secret";
+    <link linkend="opt-services.gitlab.secrets.otpFile">otpFile</link> = "/var/keys/gitlab/otp";
+    <link linkend="opt-services.gitlab.secrets.jwsFile">jwsFile</link> = "/var/keys/gitlab/jws";
   };
   <link linkend="opt-services.gitlab.extraConfig">extraConfig</link> = {
     gitlab = {
@@ -113,12 +85,16 @@ services.gitlab = {
   </para>
 
   <para>
-   If you're setting up a new Gitlab instance, generate new secrets. You for
-   instance use <literal>tr -dc A-Za-z0-9 &lt; /dev/urandom | head -c
-   128</literal> to generate a new secret. Gitlab encrypts sensitive data
-   stored in the database. If you're restoring an existing Gitlab instance, you
-   must specify the secrets secret from <literal>config/secrets.yml</literal>
-   located in your Gitlab state folder.
+   If you're setting up a new Gitlab instance, generate new
+   secrets. You for instance use <literal>tr -dc A-Za-z0-9 &lt;
+   /dev/urandom | head -c 128 &gt; /var/keys/gitlab/db</literal> to
+   generate a new db secret. Make sure the files can be read by, and
+   only by, the user specified by <link
+   linkend="opt-services.gitlab.user">services.gitlab.user</link>. Gitlab
+   encrypts sensitive data stored in the database. If you're restoring
+   an existing Gitlab instance, you must specify the secrets secret
+   from <literal>config/secrets.yml</literal> located in your Gitlab
+   state folder.
   </para>
 
   <para>
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
index b69310c34ff51..84486aa98a403 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -34,6 +34,7 @@ let
     "node"
     "postfix"
     "postgres"
+    "rspamd"
     "snmp"
     "surfboard"
     "tor"
@@ -193,6 +194,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/rspamd.nix b/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix
new file mode 100644
index 0000000000000..1f02ae2072499
--- /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/network-filesystems/ceph.nix b/nixos/modules/services/network-filesystems/ceph.nix
index 3dc5b8feef65f..656a2d21b8687 100644
--- a/nixos/modules/services/network-filesystems/ceph.nix
+++ b/nixos/modules/services/network-filesystems/ceph.nix
@@ -390,7 +390,7 @@ in
 
     systemd.targets = let
       targets = [
-        { "ceph" = { description = "Ceph target allowing to start/stop all ceph service instances at once";
+        { ceph = { description = "Ceph target allowing to start/stop all ceph service instances at once";
                      wantedBy = [ "multi-user.target" ]; }; }
       ] ++ optional cfg.mon.enable (makeTarget "mon")
         ++ optional cfg.mds.enable (makeTarget "mds")
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index 2061c02fffbdb..db4d0e328e2d1 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -8,6 +8,8 @@ let
   dynamicHostsEnabled =
     cfg.dynamicHosts.enable && cfg.dynamicHosts.hostsDirs != {};
 
+  delegateWireless = config.networking.wireless.enable == true && cfg.unmanaged != [];
+
   # /var/lib/misc is for dnsmasq.leases.
   stateDirs = "/var/lib/NetworkManager /var/lib/dhclient /var/lib/misc";
 
@@ -177,10 +179,11 @@ in {
       basePackages = mkOption {
         type = types.attrsOf types.package;
         default = { inherit (pkgs)
-                            networkmanager modemmanager wpa_supplicant crda
+                            networkmanager modemmanager crda
                             networkmanager-openvpn networkmanager-vpnc
                             networkmanager-openconnect networkmanager-fortisslvpn
-                            networkmanager-l2tp networkmanager-iodine; };
+                            networkmanager-l2tp networkmanager-iodine; }
+                  // optionalAttrs (!delegateWireless) { inherit (pkgs) wpa_supplicant; };
         internal = true;
       };
 
@@ -377,8 +380,11 @@ in {
   config = mkIf cfg.enable {
 
     assertions = [
-      { assertion = config.networking.wireless.enable == false;
-        message = "You can not use networking.networkmanager with networking.wireless";
+      { assertion = config.networking.wireless.enable == true -> cfg.unmanaged != [];
+        message = ''
+          You can not use networking.networkmanager with networking.wireless.
+          Except if you mark some interfaces as <literal>unmanaged</literal> by NetworkManager.
+        '';
       }
       { assertion = !dynamicHostsEnabled || (dynamicHostsEnabled && cfg.dns == "dnsmasq");
         message = ''
@@ -491,18 +497,17 @@ in {
       path = [ pkgs.iproute pkgs.utillinux pkgs.coreutils ];
     };
 
-    # 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 ];
+    });
 
     security.polkit.extraConfig = polkitConf;
 
-    networking.networkmanager.packages =
-      mkIf cfg.enableStrongSwan [ pkgs.networkmanager_strongswan ];
-
     services.dbus.packages =
       optional cfg.enableStrongSwan pkgs.strongswanNM ++ cfg.packages;
 
diff --git a/nixos/modules/services/networking/chrony.nix b/nixos/modules/services/networking/ntp/chrony.nix
index 77f7025770005..c74476c7a1558 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 588d1c6edb073..1197c84f0459f 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 f3920aa806460..471d15b1687bd 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/pdns-recursor.nix b/nixos/modules/services/networking/pdns-recursor.nix
index ec69cc838da9d..ebfdd9f35b72b 100644
--- a/nixos/modules/services/networking/pdns-recursor.nix
+++ b/nixos/modules/services/networking/pdns-recursor.nix
@@ -168,7 +168,7 @@ in {
       disable-syslog = true;
     };
 
-    users.users."${username}" = {
+    users.users.${username} = {
       home = dataDir;
       createHome = true;
       uid = config.ids.uids.pdns-recursor;
diff --git a/nixos/modules/services/networking/toxvpn.nix b/nixos/modules/services/networking/toxvpn.nix
index 7daacba185fe7..9e97faeebc1e8 100644
--- a/nixos/modules/services/networking/toxvpn.nix
+++ b/nixos/modules/services/networking/toxvpn.nix
@@ -23,7 +23,7 @@ with lib;
         type        = types.listOf types.str;
         default     = [];
         example     = ''[ "toxid1" "toxid2" ]'';
-        description = "peers to automacally connect to on startup";
+        description = "peers to automatically connect to on startup";
       };
     };
   };
diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix
index 7f1e793b980e4..3fcae611dc793 100644
--- a/nixos/modules/services/printing/cupsd.nix
+++ b/nixos/modules/services/printing/cupsd.nix
@@ -291,6 +291,16 @@ in
 
     services.dbus.packages = [ cups.out ] ++ optional polkitEnabled cups-pk-helper;
 
+    # Allow asswordless printer admin for members of wheel group
+    security.polkit.extraConfig = mkIf polkitEnabled ''
+      polkit.addRule(function(action, subject) {
+          if (action.id == "org.opensuse.cupspkhelper.mechanism.all-edit" &&
+              subject.isInGroup("wheel")){
+              return polkit.Result.YES;
+          }
+      });
+    '';
+
     # Cups uses libusb to talk to printers, and does not use the
     # linux kernel driver. If the driver is not in a black list, it
     # gets loaded, and then cups cannot access the printers.
diff --git a/nixos/modules/services/search/kibana.nix b/nixos/modules/services/search/kibana.nix
index 75871a9df949f..43a63aa8fdc2a 100644
--- a/nixos/modules/services/search/kibana.nix
+++ b/nixos/modules/services/search/kibana.nix
@@ -9,7 +9,7 @@ let
   lt6_6 = builtins.compareVersions cfg.package.version "6.6" < 0;
 
   cfgFile = pkgs.writeText "kibana.json" (builtins.toJSON (
-    (filterAttrsRecursive (n: v: v != null) ({
+    (filterAttrsRecursive (n: v: v != null && v != []) ({
       server.host = cfg.listenAddress;
       server.port = cfg.port;
       server.ssl.certificate = cfg.cert;
diff --git a/nixos/modules/services/torrent/deluge.nix b/nixos/modules/services/torrent/deluge.nix
index e1c5e052a12f7..0c72505395ddb 100644
--- a/nixos/modules/services/torrent/deluge.nix
+++ b/nixos/modules/services/torrent/deluge.nix
@@ -178,6 +178,7 @@ in {
       "d '${cfg.dataDir}/.config' 0770 ${cfg.user} ${cfg.group}"
       "d '${cfg.dataDir}/.config/deluge' 0770 ${cfg.user} ${cfg.group}"
     ]
+    ++ optional (cfg.config ? download_location)
       "d '${cfg.config.download_location}' 0770 ${cfg.user} ${cfg.group}"
     ++ optional (cfg.config ? torrentfiles_location)
       "d '${cfg.config.torrentfiles_location}' 0770 ${cfg.user} ${cfg.group}"
diff --git a/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix b/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix
index 195ee76ff4e3b..d9ad7e9e3d397 100644
--- a/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix
+++ b/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix
@@ -165,7 +165,7 @@ in {
 
   config = mkIf cfg.enable {
     services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") {
-      "${poolName}" = {
+      ${poolName} = {
         user = "icingaweb2";
         phpOptions = ''
           extension = ${pkgs.phpPackages.imagick}/lib/php/extensions/imagick.so
diff --git a/nixos/modules/services/web-apps/moodle.nix b/nixos/modules/services/web-apps/moodle.nix
index f2516c67c6b31..7f71b86a6fe62 100644
--- a/nixos/modules/services/web-apps/moodle.nix
+++ b/nixos/modules/services/web-apps/moodle.nix
@@ -18,7 +18,7 @@ let
   global $CFG;
   $CFG = new stdClass();
 
-  $CFG->dbtype    = '${ { "mysql" = "mariadb"; "pgsql" = "pgsql"; }.${cfg.database.type} }';
+  $CFG->dbtype    = '${ { mysql = "mariadb"; pgsql = "pgsql"; }.${cfg.database.type} }';
   $CFG->dblibrary = 'native';
   $CFG->dbhost    = '${cfg.database.host}';
   $CFG->dbname    = '${cfg.database.name}';
@@ -92,8 +92,8 @@ in
         type = types.int;
         description = "Database host port.";
         default = {
-          "mysql" = 3306;
-          "pgsql" = 5432;
+          mysql = 3306;
+          pgsql = 5432;
         }.${cfg.database.type};
         defaultText = "3306";
       };
@@ -294,7 +294,7 @@ in
 
     systemd.services.httpd.after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
 
-    users.users."${user}".group = group;
+    users.users.${user}.group = group;
 
   };
 }
diff --git a/nixos/modules/services/web-apps/restya-board.nix b/nixos/modules/services/web-apps/restya-board.nix
index 11272ed591b85..1e7882488ac6e 100644
--- a/nixos/modules/services/web-apps/restya-board.nix
+++ b/nixos/modules/services/web-apps/restya-board.nix
@@ -179,7 +179,7 @@ in
   config = mkIf cfg.enable {
 
     services.phpfpm.pools = {
-      "${poolName}" = {
+      ${poolName} = {
         inherit (cfg) user group;
 
         phpOptions = ''
@@ -216,7 +216,6 @@ in
         index index.html index.php;
 
         gzip on;
-        gzip_disable "msie6";
 
         gzip_comp_level 6;
         gzip_min_length  1100;
diff --git a/nixos/modules/services/web-apps/selfoss.nix b/nixos/modules/services/web-apps/selfoss.nix
index d693c401565ff..d5a660ebf2893 100644
--- a/nixos/modules/services/web-apps/selfoss.nix
+++ b/nixos/modules/services/web-apps/selfoss.nix
@@ -115,7 +115,7 @@ in
 
   config = mkIf cfg.enable {
     services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") {
-      "${poolName}" = {
+      ${poolName} = {
         user = "nginx";
         settings = mapAttrs (name: mkDefault) {
           "listen.owner" = "nginx";
diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix
index 4daf3ff9f991c..abe4748591e96 100644
--- a/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixos/modules/services/web-apps/tt-rss.nix
@@ -520,7 +520,7 @@ let
     ];
 
     services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") {
-      "${poolName}" = {
+      ${poolName} = {
         inherit (cfg) user;
         settings = mapAttrs (name: mkDefault) {
           "listen.owner" = "nginx";
diff --git a/nixos/modules/services/web-apps/zabbix.nix b/nixos/modules/services/web-apps/zabbix.nix
index dac243b20e97a..09538726b7cd8 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/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index b94b338fd4a60..e597f34700ae9 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -94,7 +94,6 @@ let
 
       ${optionalString (cfg.recommendedGzipSettings) ''
         gzip on;
-        gzip_disable "msie6";
         gzip_proxied any;
         gzip_comp_level 5;
         gzip_types
diff --git a/nixos/modules/services/web-servers/phpfpm/default.nix b/nixos/modules/services/web-servers/phpfpm/default.nix
index e95e71e0d997e..4ab7e3f0c0a9c 100644
--- a/nixos/modules/services/web-servers/phpfpm/default.nix
+++ b/nixos/modules/services/web-servers/phpfpm/default.nix
@@ -36,7 +36,7 @@ let
 
   poolOpts = { name, ... }:
     let
-      poolOpts = cfg.pools."${name}";
+      poolOpts = cfg.pools.${name};
     in
     {
       options = {
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index a21d22261ba30..30c5250221c19 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -240,14 +240,16 @@ in
 
       services.avahi.enable = mkDefault true;
 
+      xdg.portal.extraPortals = [ pkgs.gnome3.gnome-shell ];
+
       services.geoclue2.enable = mkDefault true;
       services.geoclue2.enableDemoAgent = false; # GNOME has its own geoclue agent
 
-      services.geoclue2.appConfig."gnome-datetime-panel" = {
+      services.geoclue2.appConfig.gnome-datetime-panel = {
         isAllowed = true;
         isSystem = true;
       };
-      services.geoclue2.appConfig."gnome-color-panel" = {
+      services.geoclue2.appConfig.gnome-color-panel = {
         isAllowed = true;
         isSystem = true;
       };
diff --git a/nixos/modules/services/x11/desktop-managers/xterm.nix b/nixos/modules/services/x11/desktop-managers/xterm.nix
index 93987bd1dfc51..1408df3129503 100644
--- a/nixos/modules/services/x11/desktop-managers/xterm.nix
+++ b/nixos/modules/services/x11/desktop-managers/xterm.nix
@@ -13,8 +13,7 @@ in
 
     services.xserver.desktopManager.xterm.enable = mkOption {
       type = types.bool;
-      default = false;
-      defaultText = "config.services.xserver.enable";
+      default = (versionOlder config.system.stateVersion "19.09");
       description = "Enable a xterm terminal as a desktop manager.";
     };
 
diff --git a/nixos/modules/services/x11/extra-layouts.nix b/nixos/modules/services/x11/extra-layouts.nix
index 5523dd2bf0235..1af98a1318bb3 100644
--- a/nixos/modules/services/x11/extra-layouts.nix
+++ b/nixos/modules/services/x11/extra-layouts.nix
@@ -158,7 +158,10 @@ in
 
     });
 
-    services.xserver.xkbDir = "${pkgs.xkb_patched}/etc/X11/xkb";
+    services.xserver = {
+      xkbDir = "${pkgs.xkb_patched}/etc/X11/xkb";
+      exportConfiguration = config.services.xserver.displayManager.startx.enable;
+    };
 
   };
 
diff --git a/nixos/modules/services/x11/hardware/cmt.nix b/nixos/modules/services/x11/hardware/cmt.nix
new file mode 100644
index 0000000000000..95353e92098e4
--- /dev/null
+++ b/nixos/modules/services/x11/hardware/cmt.nix
@@ -0,0 +1,54 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+cfg = config.services.xserver.cmt;
+etcPath = "X11/xorg.conf.d";
+
+in {
+
+  options = {
+
+    services.xserver.cmt = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable chrome multitouch input (cmt). Touchpad drivers that are configured for chromebooks.";
+      };
+      models = mkOption {
+        type = types.enum [ "atlas" "banjo" "candy" "caroline" "cave" "celes" "clapper" "cyan" "daisy" "elan" "elm" "enguarde" "eve" "expresso" "falco" "gandof" "glimmer" "gnawty" "heli" "kevin" "kip" "leon" "lulu" "orco" "pbody" "peppy" "pi" "pit" "puppy" "quawks" "rambi" "samus" "snappy" "spring" "squawks" "swanky" "winky" "wolf" "auron_paine" "auron_yuna" "daisy_skate" "nyan_big" "nyan_blaze" "veyron_jaq" "veyron_jerry" "veyron_mighty" "veyron_minnie" "veyron_speedy" ];
+        example = "banjo";
+        description = ''
+          Which models to enable cmt for. Enter the Code Name for your Chromebook.
+          Code Name can be found at <link xlink:href="https://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices" />.
+        '';
+      };
+    }; #closes services
+  }; #closes options
+
+  config = mkIf cfg.enable {
+
+    services.xserver.modules = [ pkgs.xf86_input_cmt ];
+
+    environment.etc = {
+      "${etcPath}/40-touchpad-cmt.conf" = {
+        source = "${pkgs.chromium-xorg-conf}/40-touchpad-cmt.conf";
+      };
+      "${etcPath}/50-touchpad-cmt-${cfg.models}.conf" = {
+        source = "${pkgs.chromium-xorg-conf}/50-touchpad-cmt-${cfg.models}.conf";
+      };
+      "${etcPath}/60-touchpad-cmt-${cfg.models}.conf" = {
+        source = "${pkgs.chromium-xorg-conf}/60-touchpad-cmt-${cfg.models}.conf";
+      };
+    };
+
+    assertions = [
+      {
+        assertion = !config.services.xserver.libinput.enable;
+        message = "cmt and libinput are incompatible, you cannot enable both (in services.xserver).";
+      }
+    ];
+  };
+}
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index f5a593211efca..34e2706671515 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -72,7 +72,15 @@ in
           };
       in mkMerge [ {
         enable = true;
-        networks."99-main" = genericNetwork mkDefault;
+        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 ({
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 5eb8111aa6d37..60762de76d338 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -95,6 +95,7 @@ in
   gitlab = handleTest ./gitlab.nix {};
   gitolite = handleTest ./gitolite.nix {};
   gjs = handleTest ./gjs.nix {};
+  glib-networking = handleTest ./glib-networking.nix {};
   glusterfs = handleTest ./glusterfs.nix {};
   gnome3-xorg = handleTest ./gnome3-xorg.nix {};
   gnome3 = handleTest ./gnome3.nix {};
@@ -144,6 +145,7 @@ in
   latestKernel.login = handleTest ./login.nix { latestKernel = true; };
   ldap = handleTest ./ldap.nix {};
   leaps = handleTest ./leaps.nix {};
+  libgdata = handleTest ./libgdata.nix {};
   libxmlb = handleTest ./libxmlb.nix {};
   lidarr = handleTest ./lidarr.nix {};
   lightdm = handleTest ./lightdm.nix {};
diff --git a/nixos/tests/gitlab.nix b/nixos/tests/gitlab.nix
index 29978824870c5..be0b3c8746a99 100644
--- a/nixos/tests/gitlab.nix
+++ b/nixos/tests/gitlab.nix
@@ -29,44 +29,14 @@ import ./make-test.nix ({ pkgs, lib, ...} : with lib; {
 
       services.gitlab = {
         enable = true;
-        databasePassword = "dbPassword";
-        inherit initialRootPassword;
+        databasePasswordFile = pkgs.writeText "dbPassword" "xo0daiF4";
+        initialRootPasswordFile = pkgs.writeText "rootPassword" initialRootPassword;
         smtp.enable = true;
         secrets = {
-          secret = "secret";
-          otp = "otpsecret";
-          db = "dbsecret";
-
-          # nix-shell -p openssl --run "openssl genrsa 2048"
-          jws = ''
-            -----BEGIN RSA PRIVATE KEY-----
-            MIIEpAIBAAKCAQEA13/qEio76OWUtWO0WIz9lWnsTWOU8Esv4sQHDq9PCEFsLt21
-            PAXrlWhLjjWcxGfsrDwnh7YErGHYL62BMSxMdFJolaknlQK/O/V8UETDe45VoHM+
-            Znk270RfUcfYFgiihnXUZXVmL0om9TsQSk646wCcjCY9LxtxUyKNhvT7KjgYw2aX
-            z34aw7M+Js3T2p1TjZPSC82GtmtKkJEKFMi5EjprLTDE7EdcUzr9Xuw+kQ+gRm9k
-            7FE+JQqSoprwE3Q0v2OAn3UhLMgg0gNFRnsc5l6IAshDzV+H22RPqKKlJjVjjfPY
-            0TQSvYLVApigHbDPH0BoCXfjFfQazbbP3OUHrwIDAQABAoIBAQCMU+tkcMQaYIV5
-            qLdjgkwO467QpivyXcOM8wF1eosIYTHFQvIlZ+WEoSmyLQ8shlADyBgls01Pw1c3
-            lNAv6RzQEmmwKzpvOh61OKH+0whIiOMRXHoh2IUBQZCgfHYlwvGyhUAN4WjtGmhM
-            AG4XNTQNM5S9Xpkw97nP3Qwz+YskbbkrfqtCEVy9ro+4nhbjqPsuO3adbnkva4zR
-            cyurRhrHgHU6LPjn5NHnHH4qw2faY2oAsL8pmpkTbO5IqWDvOcbjNfjVPgVoq26O
-            bbaa1qs4nmc80qQgMjRPJef535xyf3eLsSlDvpf6O8sPrJzVR1zaqEqixpQCZDac
-            +kRiSBrhAoGBAOwHiq0PuyJh6VzBu7ybqX6+gF/wA4Jkwzx6mbfaBgurvU1aospp
-            kisIonAkxSbxllZMnjbkShZEdATYKeT9o5NEhnU4YnHfc5bJZbiWOZAzYGLcY7g8
-            vDQ31pBItyY4pFgPbSpNlbUvUsoPVJ45RasRADDTNCzMzdjFQQXst2V9AoGBAOm7
-            sSpzYfFPLEAhieAkuhtbsX58Boo46djiKVfzGftfp6F9aHTOfzGORU5jrZ16mSbS
-            qkkC6BEFrATX2051dzzXC89fWoJYALrsffE5I3KlKXsCAWSnCP1MMxOfH+Ls61Mr
-            7pK/LKfvJt53mUH4jIdbmmFUDwbg18oBEH+x9PmbAoGAS/+JqXu9N67rIxDGUE6W
-            3tacI0f2+U9Uhe67/DTZaXyc8YFTlXU0uWKIWy+bw5RaYeM9tlL/f/f+m2i25KK+
-            vrZ7zNag7CWU5GJovGyykDnauTpZaYM03mN0VPT08/uc/zXIYqyknbhlIeaZynCK
-            fDB3LUF0NVCknz20WCIGU0kCgYEAkxY0ZXx61Dp4pFr2wwEZxQGs7uXpz64FKyEX
-            12r6nMATY4Lh6y/Px0W6w5vis8lk+5Ny6cNUevHQ0LNuJS+yu6ywl+1vrbrnqroM
-            f3LvpcPeGLSoX8jl1VDQi7aFgG6LoKly1xJLbdsH4NPutB9PgBbbTghx9GgmI88L
-            rPA2M6UCgYBOmkYJocNgxg6B1/n4Tb9fN1Q/XuJrFDE6NxVUoke+IIyMPRH7FC3m
-            VMYzu+b7zTVJjaBb1cmJemxl/xajziWDofJYPefhdbOVU7HXtmJFY0IG3pVxU1zW
-            3bmDj5QAtCUDpuuNa6GEIT0YR4+D/V7o3DmlZ0tVIwKJmVJoQ2f5dw==
-            -----END RSA PRIVATE KEY-----
-          '';
+          secretFile = pkgs.writeText "secret" "Aig5zaic";
+          otpFile = pkgs.writeText "otpsecret" "Riew9mue";
+          dbFile = pkgs.writeText "dbsecret" "we2quaeZ";
+          jwsFile = pkgs.runCommand "oidcKeyBase" {} "${pkgs.openssl}/bin/openssl genrsa 2048 > $out";
         };
       };
     };
diff --git a/nixos/tests/glib-networking.nix b/nixos/tests/glib-networking.nix
new file mode 100644
index 0000000000000..c0bbb2b3554b0
--- /dev/null
+++ b/nixos/tests/glib-networking.nix
@@ -0,0 +1,17 @@
+# run installed tests
+import ./make-test.nix ({ pkgs, ... }:
+
+{
+  name = "glib-networking";
+  meta = {
+    maintainers = pkgs.glib-networking.meta.maintainers;
+  };
+
+  machine = { pkgs, ... }: {
+    environment.systemPackages = with pkgs; [ gnome-desktop-testing ];
+  };
+
+  testScript = ''
+    $machine->succeed("gnome-desktop-testing-runner -d '${pkgs.glib-networking.installedTests}/share'");
+  '';
+})
diff --git a/nixos/tests/libgdata.nix b/nixos/tests/libgdata.nix
new file mode 100644
index 0000000000000..10a3ca97dd224
--- /dev/null
+++ b/nixos/tests/libgdata.nix
@@ -0,0 +1,21 @@
+# run installed tests
+import ./make-test.nix ({ pkgs, ... }:
+
+{
+  name = "libgdata";
+
+  meta = {
+    maintainers = pkgs.libgdata.meta.maintainers;
+  };
+
+  machine = { pkgs, ... }: {
+    environment.systemPackages = with pkgs; [ gnome-desktop-testing ];
+    # # GLib-GIO-DEBUG: _g_io_module_get_default: Found default implementation dummy (GDummyTlsBackend) for ‘gio-tls-backend’
+    # Bail out! libgdata:ERROR:../gdata/tests/common.c:134:gdata_test_init: assertion failed (child_error == NULL): TLS support is not available (g-tls-error-quark, 0)
+    services.gnome3.glib-networking.enable = true;
+  };
+
+  testScript = ''
+    $machine->succeed("gnome-desktop-testing-runner -d '${pkgs.libgdata.installedTests}/share'");
+  '';
+})
diff --git a/nixos/tests/login.nix b/nixos/tests/login.nix
index 9844ad492e885..2a7c063d30333 100644
--- a/nixos/tests/login.nix
+++ b/nixos/tests/login.nix
@@ -9,6 +9,7 @@ import ./make-test.nix ({ pkgs, latestKernel ? false, ... }:
   machine =
     { pkgs, lib, ... }:
     { boot.kernelPackages = lib.mkIf latestKernel pkgs.linuxPackages_latest;
+      sound.enable = true; # needed for the factl test, /dev/snd/* exists without them but udev doesn't care then
     };
 
   testScript =
diff --git a/nixos/tests/mumble.nix b/nixos/tests/mumble.nix
index dadd16fd9a0c2..652d49a24b1ca 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/plasma5.nix b/nixos/tests/plasma5.nix
index 788c8719c8d2f..88d4ff334369b 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 ];
   };
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 02d83f82f338a..9826b56b74d7b 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -297,6 +297,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/xfce.nix b/nixos/tests/xfce.nix
index 12d8a050d47bc..6cb4fae2021f1 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 =
diff --git a/nixos/tests/xfce4-14.nix b/nixos/tests/xfce4-14.nix
index d9b10aabaa1f6..94378f0c8d34b 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 =
diff --git a/nixos/tests/xmonad.nix b/nixos/tests/xmonad.nix
index 4d3bc28cd349f..79c15ccffecd2 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");