about summary refs log tree commit diff
path: root/nixpkgs/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos')
-rw-r--r--nixpkgs/nixos/doc/manual/configuration/matrix.xml2
-rw-r--r--nixpkgs/nixos/doc/manual/configuration/x-windows.xml134
-rw-r--r--nixpkgs/nixos/doc/manual/default.nix106
-rwxr-xr-xnixpkgs/nixos/doc/manual/development/releases.xml10
-rw-r--r--nixpkgs/nixos/doc/manual/man-pages.xml2
-rw-r--r--nixpkgs/nixos/doc/manual/release-notes/rl-1909.xml99
-rw-r--r--nixpkgs/nixos/lib/build-vms.nix2
-rw-r--r--nixpkgs/nixos/lib/make-options-doc/default.nix125
-rw-r--r--nixpkgs/nixos/lib/make-options-doc/options-to-docbook.xsl (renamed from nixpkgs/nixos/doc/manual/options-to-docbook.xsl)0
-rw-r--r--nixpkgs/nixos/lib/make-options-doc/postprocess-option-descriptions.xsl (renamed from nixpkgs/nixos/doc/manual/postprocess-option-descriptions.xsl)0
-rw-r--r--nixpkgs/nixos/lib/test-driver/Machine.pm12
-rw-r--r--nixpkgs/nixos/modules/config/locale.nix (renamed from nixpkgs/nixos/modules/config/timezone.nix)35
-rw-r--r--nixpkgs/nixos/modules/config/networking.nix104
-rw-r--r--nixpkgs/nixos/modules/config/no-x-libs.nix2
-rw-r--r--nixpkgs/nixos/modules/config/resolvconf.nix149
-rw-r--r--nixpkgs/nixos/modules/config/xdg/portal.nix58
-rw-r--r--nixpkgs/nixos/modules/hardware/opengl.nix20
-rw-r--r--nixpkgs/nixos/modules/hardware/video/amdgpu-pro.nix3
-rw-r--r--nixpkgs/nixos/modules/hardware/video/ati.nix3
-rw-r--r--nixpkgs/nixos/modules/hardware/video/nvidia.nix1
-rw-r--r--nixpkgs/nixos/modules/i18n/input-method/ibus.nix2
-rw-r--r--nixpkgs/nixos/modules/installer/netboot/netboot.nix2
-rw-r--r--nixpkgs/nixos/modules/installer/tools/nixos-generate-config.pl85
-rw-r--r--nixpkgs/nixos/modules/installer/tools/tools.nix105
-rw-r--r--nixpkgs/nixos/modules/misc/ids.nix2
-rw-r--r--nixpkgs/nixos/modules/misc/nixpkgs.nix2
-rw-r--r--nixpkgs/nixos/modules/module-list.nix28
-rw-r--r--nixpkgs/nixos/modules/profiles/hardened.nix7
-rw-r--r--nixpkgs/nixos/modules/programs/evince.nix (renamed from nixpkgs/nixos/modules/services/desktops/gnome3/evince.nix)13
-rw-r--r--nixpkgs/nixos/modules/programs/file-roller.nix (renamed from nixpkgs/nixos/modules/services/desktops/gnome3/file-roller.nix)11
-rw-r--r--nixpkgs/nixos/modules/programs/gnome-disks.nix (renamed from nixpkgs/nixos/modules/services/desktops/gnome3/gnome-disks.nix)15
-rw-r--r--nixpkgs/nixos/modules/programs/gnome-documents.nix (renamed from nixpkgs/nixos/modules/services/desktops/gnome3/gnome-documents.nix)15
-rw-r--r--nixpkgs/nixos/modules/programs/gpaste.nix (renamed from nixpkgs/nixos/modules/services/desktops/gnome3/gpaste.nix)15
-rw-r--r--nixpkgs/nixos/modules/programs/nylas-mail.nix36
-rw-r--r--nixpkgs/nixos/modules/programs/shell.nix4
-rw-r--r--nixpkgs/nixos/modules/programs/ssh.nix11
-rw-r--r--nixpkgs/nixos/modules/programs/tsm-client.nix287
-rw-r--r--nixpkgs/nixos/modules/programs/xonsh.nix5
-rw-r--r--nixpkgs/nixos/modules/rename.nix31
-rw-r--r--nixpkgs/nixos/modules/security/misc.nix24
-rw-r--r--nixpkgs/nixos/modules/services/audio/spotifyd.nix42
-rw-r--r--nixpkgs/nixos/modules/services/backup/postgresql-wal-receiver.nix203
-rw-r--r--nixpkgs/nixos/modules/services/backup/tsm.nix106
-rw-r--r--nixpkgs/nixos/modules/services/backup/zfs-replication.nix90
-rw-r--r--nixpkgs/nixos/modules/services/databases/couchdb.nix8
-rw-r--r--nixpkgs/nixos/modules/services/databases/openldap.nix4
-rw-r--r--nixpkgs/nixos/modules/services/databases/postgresql.nix45
-rw-r--r--nixpkgs/nixos/modules/services/databases/postgresql.xml72
-rw-r--r--nixpkgs/nixos/modules/services/desktops/flatpak.nix28
-rw-r--r--nixpkgs/nixos/modules/services/desktops/flatpak.xml2
-rw-r--r--nixpkgs/nixos/modules/services/desktops/tumbler.nix2
-rw-r--r--nixpkgs/nixos/modules/services/development/bloop.nix23
-rw-r--r--nixpkgs/nixos/modules/services/editors/emacs.xml3
-rw-r--r--nixpkgs/nixos/modules/services/hardware/throttled.nix11
-rw-r--r--nixpkgs/nixos/modules/services/logging/graylog.nix3
-rw-r--r--nixpkgs/nixos/modules/services/mail/postfix.nix19
-rw-r--r--nixpkgs/nixos/modules/services/misc/dwm-status.nix73
-rw-r--r--nixpkgs/nixos/modules/services/misc/gitea.nix18
-rw-r--r--nixpkgs/nixos/modules/services/misc/gitlab.nix90
-rw-r--r--nixpkgs/nixos/modules/services/misc/gitolite.nix29
-rw-r--r--nixpkgs/nixos/modules/services/misc/greenclip.nix31
-rw-r--r--nixpkgs/nixos/modules/services/misc/nix-daemon.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/redmine.nix75
-rw-r--r--nixpkgs/nixos/modules/services/misc/taskserver/default.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/tiddlywiki.nix52
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/datadog-agent.nix8
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/grafana.nix6
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix26
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix90
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.xml44
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bind.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix12
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dnsmasq.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix7
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/json.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mail.nix157
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/minio.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix27
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/node.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postgres.nix47
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/surfboard.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/tor.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix4
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix37
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/thanos.nix801
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/zabbix-agent.nix163
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/zabbix-proxy.nix298
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/zabbix-server.nix341
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/kbfs.nix1
-rw-r--r--nixpkgs/nixos/modules/services/networking/bind.nix8
-rw-r--r--nixpkgs/nixos/modules/services/networking/dhcpcd.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/dnsmasq.nix11
-rw-r--r--nixpkgs/nixos/modules/services/networking/keybase.nix1
-rw-r--r--nixpkgs/nixos/modules/services/networking/kresd.nix7
-rw-r--r--nixpkgs/nixos/modules/services/networking/networkmanager.nix59
-rw-r--r--nixpkgs/nixos/modules/services/networking/rdnssd.nix5
-rw-r--r--nixpkgs/nixos/modules/services/networking/syncthing.nix22
-rw-r--r--nixpkgs/nixos/modules/services/networking/thelounge.nix75
-rw-r--r--nixpkgs/nixos/modules/services/networking/unbound.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/unifi.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix1
-rw-r--r--nixpkgs/nixos/modules/services/security/sshguard.nix2
-rw-r--r--nixpkgs/nixos/modules/services/security/tor.nix2
-rw-r--r--nixpkgs/nixos/modules/services/system/nscd.conf18
-rw-r--r--nixpkgs/nixos/modules/services/system/nscd.nix31
-rw-r--r--nixpkgs/nixos/modules/services/torrent/deluge.nix91
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mediawiki.nix473
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.nix33
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.xml64
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/tt-rss.nix62
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/zabbix.nix225
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix11
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/foswiki.nix78
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix349
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix8
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix103
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/trac.nix121
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/zabbix.nix84
-rw-r--r--nixpkgs/nixos/modules/services/x11/clight.nix115
-rw-r--r--nixpkgs/nixos/modules/services/x11/compton.nix136
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/gnome3.nix15
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix6
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix24
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/surf-display.nix127
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix46
-rw-r--r--nixpkgs/nixos/modules/services/x11/extra-layouts.nix165
-rw-r--r--nixpkgs/nixos/modules/services/x11/gdk-pixbuf.nix8
-rw-r--r--nixpkgs/nixos/modules/services/x11/redshift.nix58
-rw-r--r--nixpkgs/nixos/modules/services/x11/xserver.nix12
-rw-r--r--nixpkgs/nixos/modules/system/boot/binfmt.nix9
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl23
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix15
-rw-r--r--nixpkgs/nixos/modules/system/boot/resolved.nix43
-rw-r--r--nixpkgs/nixos/modules/system/boot/stage-2.nix9
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd-unit-options.nix2
-rw-r--r--nixpkgs/nixos/modules/tasks/auto-upgrade.nix32
-rw-r--r--nixpkgs/nixos/modules/tasks/filesystems/xfs.nix1
-rw-r--r--nixpkgs/nixos/modules/tasks/filesystems/zfs.nix39
-rw-r--r--nixpkgs/nixos/modules/tasks/network-interfaces-scripted.nix2
-rw-r--r--nixpkgs/nixos/modules/tasks/network-interfaces.nix23
-rw-r--r--nixpkgs/nixos/modules/virtualisation/parallels-guest.nix3
-rw-r--r--nixpkgs/nixos/release-combined.nix7
-rw-r--r--nixpkgs/nixos/tests/all-tests.nix15
-rw-r--r--nixpkgs/nixos/tests/boot.nix107
-rw-r--r--nixpkgs/nixos/tests/cassandra.nix66
-rw-r--r--nixpkgs/nixos/tests/deluge.nix6
-rw-r--r--nixpkgs/nixos/tests/flatpak-builder.nix1
-rw-r--r--nixpkgs/nixos/tests/fluentd.nix46
-rw-r--r--nixpkgs/nixos/tests/gdk-pixbuf.nix4
-rw-r--r--nixpkgs/nixos/tests/gnome3-gdm.nix63
-rw-r--r--nixpkgs/nixos/tests/gnome3-xorg.nix41
-rw-r--r--nixpkgs/nixos/tests/gnome3.nix51
-rw-r--r--nixpkgs/nixos/tests/grafana.nix90
-rw-r--r--nixpkgs/nixos/tests/graylog.nix111
-rw-r--r--nixpkgs/nixos/tests/initdb.nix26
-rw-r--r--nixpkgs/nixos/tests/installer.nix130
-rw-r--r--nixpkgs/nixos/tests/ipv6.nix11
-rw-r--r--nixpkgs/nixos/tests/lightdm.nix2
-rw-r--r--nixpkgs/nixos/tests/mediawiki.nix19
-rw-r--r--nixpkgs/nixos/tests/mysql.nix2
-rw-r--r--nixpkgs/nixos/tests/networking.nix22
-rw-r--r--nixpkgs/nixos/tests/nextcloud/with-postgresql-and-redis.nix15
-rw-r--r--nixpkgs/nixos/tests/nixos-generate-config.nix24
-rw-r--r--nixpkgs/nixos/tests/ostree.nix2
-rw-r--r--nixpkgs/nixos/tests/postgresql-wal-receiver.nix86
-rw-r--r--nixpkgs/nixos/tests/prometheus-2.nix208
-rw-r--r--nixpkgs/nixos/tests/prometheus-exporters.nix78
-rw-r--r--nixpkgs/nixos/tests/redmine.nix24
-rw-r--r--nixpkgs/nixos/tests/syncthing-init.nix8
-rw-r--r--nixpkgs/nixos/tests/tiddlywiki.nix67
-rw-r--r--nixpkgs/nixos/tests/tomcat.nix30
-rw-r--r--nixpkgs/nixos/tests/trac.nix74
178 files changed, 6737 insertions, 2269 deletions
diff --git a/nixpkgs/nixos/doc/manual/configuration/matrix.xml b/nixpkgs/nixos/doc/manual/configuration/matrix.xml
index 4c559a71e813..ef8d5cbda889 100644
--- a/nixpkgs/nixos/doc/manual/configuration/matrix.xml
+++ b/nixpkgs/nixos/doc/manual/configuration/matrix.xml
@@ -95,7 +95,7 @@ in {
 
         # forward all Matrix API calls to the synapse Matrix homeserver
         locations."/_matrix" = {
-          proxyPass = "http://[::1]:8008";
+          proxyPass = "http://[::1]:8008"; # without a trailing /
         };
       };
     };
diff --git a/nixpkgs/nixos/doc/manual/configuration/x-windows.xml b/nixpkgs/nixos/doc/manual/configuration/x-windows.xml
index 798d1fbdfd85..7cdc5196e0d2 100644
--- a/nixpkgs/nixos/doc/manual/configuration/x-windows.xml
+++ b/nixpkgs/nixos/doc/manual/configuration/x-windows.xml
@@ -125,10 +125,12 @@
    You will need to reboot after enabling this driver to prevent a clash with
    other kernel modules.
   </para>
+  <note>
   <para>
-   Note: for recent AMD GPUs you most likely want to keep either the defaults
+   For recent AMD GPUs you most likely want to keep either the defaults
    or <literal>"amdgpu"</literal> (both free).
   </para>
+  </note>
  </simplesect>
  <simplesect xml:id="sec-x11-touchpads">
   <title>Touchpads</title>
@@ -157,4 +159,134 @@
    versions.
   </para>
  </simplesect>
+ <simplesect xml:id="custom-xkb-layouts">
+  <title>Custom XKB layouts</title>
+  <para>
+   It is possible to install custom
+   <link xlink:href="https://en.wikipedia.org/wiki/X_keyboard_extension">
+    XKB
+   </link>
+   keyboard layouts using the option
+   <option>
+    <link linkend="opt-services.xserver.extraLayouts">
+     services.xserver.extraLayouts
+    </link>
+   </option>.
+   As a first example, we are going to create a layout based on the basic US
+   layout, with an additional layer to type some greek symbols by pressing the
+   right-alt key.
+  </para>
+  <para>
+   To do this we are going to create a <literal>us-greek</literal> file
+   with a <literal>xkb_symbols</literal> section.
+  </para>
+<programlisting>
+xkb_symbols &quot;us-greek&quot;
+{
+  include &quot;us(basic)&quot;            // includes the base US keys
+  include &quot;level3(ralt_switch)&quot;  // configures right alt as a third level switch
+
+  key &lt;LatA&gt; { [ a, A, Greek_alpha ] };
+  key &lt;LatB&gt; { [ b, B, Greek_beta  ] };
+  key &lt;LatG&gt; { [ g, G, Greek_gamma ] };
+  key &lt;LatD&gt; { [ d, D, Greek_delta ] };
+  key &lt;LatZ&gt; { [ z, Z, Greek_zeta  ] };
+};
+</programlisting>
+  <para>
+   To install the layout, the filepath, a description and the list of
+   languages must be given:
+  </para>
+<programlisting>
+<xref linkend="opt-services.xserver.extraLayouts"/>.us-greek = {
+  description = "US layout with alt-gr greek";
+  languages   = [ "eng" ];
+  symbolsFile = /path/to/us-greek;
+}
+</programlisting>
+  <note>
+  <para>
+   The name should match the one given to the
+   <literal>xkb_symbols</literal> block.
+  </para>
+  </note>
+  <para>
+   The layout should now be installed and ready to use: try it by
+   running <literal>setxkbmap us-greek</literal> and type
+   <literal>&lt;alt&gt;+a</literal>. To change the default the usual
+   <option>
+    <link linkend="opt-services.xserver.layout">
+     services.xserver.layout
+    </link>
+   </option>
+   option can still be used.
+  </para>
+  <para>
+   A layout can have several other components besides
+   <literal>xkb_symbols</literal>, for example we will define new
+   keycodes for some multimedia key and bind these to some symbol.
+  </para>
+  <para>
+   Use the <emphasis>xev</emphasis> utility from
+   <literal>pkgs.xorg.xev</literal> to find the codes of the keys of
+   interest, then create a <literal>media-key</literal> file to hold
+   the keycodes definitions
+  </para>
+<programlisting>
+xkb_keycodes &quot;media&quot;
+{
+ &lt;volUp&gt;   = 123;
+ &lt;volDown&gt; = 456;
+}
+</programlisting>
+  <para>
+    Now use the newly define keycodes in <literal>media-sym</literal>:
+  </para>
+<programlisting>
+xkb_symbols &quot;media&quot;
+{
+ key.type = &quot;ONE_LEVEL&quot;;
+ key &lt;volUp&gt;   { [ XF86AudioLowerVolume ] };
+ key &lt;volDown&gt; { [ XF86AudioRaiseVolume ] };
+}
+</programlisting>
+  <para>
+    As before, to install the layout do
+  </para>
+<programlisting>
+<xref linkend="opt-services.xserver.extraLayouts"/>.media = {
+  description  = "Multimedia keys remapping";
+  languages    = [ "eng" ];
+  symbolsFile  = /path/to/media-key;
+  keycodesFile = /path/to/media-sym;
+};
+</programlisting>
+  <note>
+  <para>
+   The function <literal>pkgs.writeText &lt;filename&gt; &lt;content&gt;
+   </literal> can be useful if you prefer to keep the layout definitions
+   inside the NixOS configuration.
+  </para>
+  </note>
+  <para>
+    Unfortunately, the Xorg server does not (currently) support setting a
+    keymap directly but relies instead on XKB rules to select the matching
+    components (keycodes, types, ...) of a layout. This means that components
+    other than symbols won't be loaded by default. As a workaround, you
+    can set the keymap using <literal>setxkbmap</literal> at the start of the
+    session with:
+  </para>
+<programlisting>
+<xref linkend="opt-services.xserver.displayManager.sessionCommands"/> = "setxkbmap -keycodes media";
+</programlisting>
+  <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
+  </link>. More example layouts can also be found
+  <link xlink:href="https://wiki.archlinux.org/index.php/X_KeyBoard_extension#Basic_examples">
+   here
+  </link>.
+  </para>
+</simplesect>
 </chapter>
diff --git a/nixpkgs/nixos/doc/manual/default.nix b/nixpkgs/nixos/doc/manual/default.nix
index 7fc0ad702f84..f9de2db1a084 100644
--- a/nixpkgs/nixos/doc/manual/default.nix
+++ b/nixpkgs/nixos/doc/manual/default.nix
@@ -5,55 +5,6 @@ with pkgs;
 let
   lib = pkgs.lib;
 
-  # Remove invisible and internal options.
-  optionsListVisible = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options);
-
-  # Replace functions by the string <function>
-  substFunction = x:
-    if builtins.isAttrs x then lib.mapAttrs (name: substFunction) x
-    else if builtins.isList x then map substFunction x
-    else if lib.isFunction x then "<function>"
-    else x;
-
-  # Generate DocBook documentation for a list of packages. This is
-  # what `relatedPackages` option of `mkOption` from
-  # ../../../lib/options.nix influences.
-  #
-  # Each element of `relatedPackages` can be either
-  # - a string:  that will be interpreted as an attribute name from `pkgs`,
-  # - a list:    that will be interpreted as an attribute path from `pkgs`,
-  # - an attrset: that can specify `name`, `path`, `package`, `comment`
-  #   (either of `name`, `path` is required, the rest are optional).
-  genRelatedPackages = packages:
-    let
-      unpack = p: if lib.isString p then { name = p; }
-                  else if lib.isList p then { path = p; }
-                  else p;
-      describe = args:
-        let
-          title = args.title or null;
-          name = args.name or (lib.concatStringsSep "." args.path);
-          path = args.path or [ args.name ];
-          package = args.package or (lib.attrByPath path (throw "Invalid package attribute path `${toString path}'") pkgs);
-        in "<listitem>"
-        + "<para><literal>${lib.optionalString (title != null) "${title} aka "}pkgs.${name} (${package.meta.name})</literal>"
-        + lib.optionalString (!package.meta.available) " <emphasis>[UNAVAILABLE]</emphasis>"
-        + ": ${package.meta.description or "???"}.</para>"
-        + lib.optionalString (args ? comment) "\n<para>${args.comment}</para>"
-        # Lots of `longDescription's break DocBook, so we just wrap them into <programlisting>
-        + lib.optionalString (package.meta ? longDescription) "\n<programlisting>${package.meta.longDescription}</programlisting>"
-        + "</listitem>";
-    in "<itemizedlist>${lib.concatStringsSep "\n" (map (p: describe (unpack p)) packages)}</itemizedlist>";
-
-  optionsListDesc = lib.flip map optionsListVisible (opt: opt // {
-    # Clean up declaration sites to not refer to the NixOS source tree.
-    declarations = map stripAnyPrefixes opt.declarations;
-  }
-  // lib.optionalAttrs (opt ? example) { example = substFunction opt.example; }
-  // lib.optionalAttrs (opt ? default) { default = substFunction opt.default; }
-  // lib.optionalAttrs (opt ? type) { type = substFunction opt.type; }
-  // lib.optionalAttrs (opt ? relatedPackages && opt.relatedPackages != []) { relatedPackages = genRelatedPackages opt.relatedPackages; });
-
   # We need to strip references to /nix/store/* from options,
   # including any `extraSources` if some modules came from elsewhere,
   # or else the build will fail.
@@ -63,37 +14,13 @@ let
   prefixesToStrip = map (p: "${toString p}/") ([ ../../.. ] ++ extraSources);
   stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix) prefixesToStrip;
 
-  # Custom "less" that pushes up all the things ending in ".enable*"
-  # and ".package*"
-  optionLess = a: b:
-    let
-      ise = lib.hasPrefix "enable";
-      isp = lib.hasPrefix "package";
-      cmp = lib.splitByAndCompare ise lib.compare
-                                 (lib.splitByAndCompare isp lib.compare lib.compare);
-    in lib.compareLists cmp a.loc b.loc < 0;
-
-  # Customly sort option list for the man page.
-  optionsList = lib.sort optionLess optionsListDesc;
-
-  # Convert the list of options into an XML file.
-  optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsList);
-
-  optionsDocBook = runCommand "options-db.xml" {} ''
-    optionsXML=${optionsXML}
-    if grep /nixpkgs/nixos/modules $optionsXML; then
-      echo "The manual appears to depend on the location of Nixpkgs, which is bad"
-      echo "since this prevents sharing via the NixOS channel.  This is typically"
-      echo "caused by an option default that refers to a relative path (see above"
-      echo "for hints about the offending path)."
-      exit 1
-    fi
-    ${buildPackages.libxslt.bin}/bin/xsltproc \
-      --stringparam revision '${revision}' \
-      -o intermediate.xml ${./options-to-docbook.xsl} $optionsXML
-    ${buildPackages.libxslt.bin}/bin/xsltproc \
-      -o "$out" ${./postprocess-option-descriptions.xsl} intermediate.xml
-  '';
+  optionsDoc = buildPackages.nixosOptionsDoc {
+    inherit options revision;
+    transformOptions = opt: opt // {
+      # Clean up declaration sites to not refer to the NixOS source tree.
+      declarations = map stripAnyPrefixes opt.declarations;
+    };
+  };
 
   sources = lib.sourceFilesBySuffices ./. [".xml"];
 
@@ -108,7 +35,7 @@ let
   generatedSources = runCommand "generated-docbook" {} ''
     mkdir $out
     ln -s ${modulesDoc} $out/modules.xml
-    ln -s ${optionsDocBook} $out/options-db.xml
+    ln -s ${optionsDoc.optionsDocBook} $out/options-db.xml
     printf "%s" "${version}" > $out/version
   '';
 
@@ -234,22 +161,7 @@ let
 in rec {
   inherit generatedSources;
 
-  # The NixOS options in JSON format.
-  optionsJSON = runCommand "options-json"
-    { meta.description = "List of NixOS options in JSON format";
-    }
-    ''
-      # Export list of options in different format.
-      dst=$out/share/doc/nixos
-      mkdir -p $dst
-
-      cp ${builtins.toFile "options.json" (builtins.unsafeDiscardStringContext (builtins.toJSON
-        (builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList))))
-      } $dst/options.json
-
-      mkdir -p $out/nix-support
-      echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products
-    ''; # */
+  inherit (optionsDoc) optionsJSON optionsXML optionsDocBook;
 
   # Generate the NixOS manual.
   manualHTML = runCommand "nixos-manual-html"
diff --git a/nixpkgs/nixos/doc/manual/development/releases.xml b/nixpkgs/nixos/doc/manual/development/releases.xml
index f45fecd16c35..3cb16d33cd48 100755
--- a/nixpkgs/nixos/doc/manual/development/releases.xml
+++ b/nixpkgs/nixos/doc/manual/development/releases.xml
@@ -100,6 +100,16 @@
     </listitem>
     <listitem>
      <para>
+      Remove attributes that we know we will not be able to support,
+      especially if there is a stable alternative. E.g. Check that our
+      Linux kernels'
+      <link xlink:href="https://www.kernel.org/category/releases.html">
+      projected end-of-life</link> are after our release projected
+      end-of-life
+     </para>
+    </listitem>
+    <listitem>
+     <para>
       Edit changelog at
       <literal>nixos/doc/manual/release-notes/rl-1709.xml</literal> (double
       check desktop versions are noted)
diff --git a/nixpkgs/nixos/doc/manual/man-pages.xml b/nixpkgs/nixos/doc/manual/man-pages.xml
index 0390dda6468f..f5a1dd2d69f4 100644
--- a/nixpkgs/nixos/doc/manual/man-pages.xml
+++ b/nixpkgs/nixos/doc/manual/man-pages.xml
@@ -6,7 +6,7 @@
   <author><personname><firstname>Eelco</firstname><surname>Dolstra</surname></personname>
    <contrib>Author</contrib>
   </author>
-  <copyright><year>2007-2018</year><holder>Eelco Dolstra</holder>
+  <copyright><year>2007-2019</year><holder>Eelco Dolstra</holder>
   </copyright>
  </info>
  <xi:include href="man-configuration.xml" />
diff --git a/nixpkgs/nixos/doc/manual/release-notes/rl-1909.xml b/nixpkgs/nixos/doc/manual/release-notes/rl-1909.xml
index 53f5b8bb7321..b12858cfc963 100644
--- a/nixpkgs/nixos/doc/manual/release-notes/rl-1909.xml
+++ b/nixpkgs/nixos/doc/manual/release-notes/rl-1909.xml
@@ -33,6 +33,15 @@
      PHP 7.1 is no longer supported due to upstream not supporting this version for the entire lifecycle of the 19.09 release.
     </para>
    </listitem>
+   <listitem>
+     <para>
+       The binfmt module is now easier to use. Additional systems can
+       be added through <option>boot.binfmt.emulatedSystems</option>.
+       For instance, <literal>boot.binfmt.emulatedSystems = [
+       "wasm32-wasi" "x86_64-windows" "aarch64-linux" ];</literal> will
+       set up binfmt interpreters for each of those listed systems.
+     </para>
+   </listitem>
   </itemizedlist>
  </section>
 
@@ -47,6 +56,13 @@
    The following new services were added since the last release:
   </para>
 
+  <itemizedlist>
+   <listitem>
+    <para>
+     <literal>./programs/dwm-status.nix</literal>
+    </para>
+   </listitem>
+  </itemizedlist>
  </section>
 
  <section xmlns="http://docbook.org/ns/docbook"
@@ -123,6 +139,19 @@
     </para>
    </listitem>
    <listitem>
+     <para>
+     IPv6 Privacy Extensions are now enabled by default for undeclared
+     interfaces. The previous behaviour was quite misleading — even though
+     the default value for
+     <option>networking.interfaces.*.preferTempAddress</option> was
+     <literal>true</literal>, undeclared interfaces would not prefer temporary
+     addresses. Now, interfaces not mentioned in the config will prefer
+     temporary addresses. EUI64 addresses can still be set as preferred by
+     explicitly setting the option to <literal>false</literal> for the
+     interface in question.
+    </para>
+   </listitem>
+   <listitem>
     <para>
      Since Bittorrent Sync was superseded by Resilio Sync in 2016, the
      <literal>bittorrentSync</literal>, <literal>bittorrentSync14</literal>,
@@ -136,10 +165,22 @@
    </listitem>
    <listitem>
     <para>
-     Several of the apache subservices have been replaced with full NixOS
-     modules including LimeSurvey and WordPress.
-     These modules can be enabled using the <option>services.limesurvey.enable</option>
-     and <option>services.wordpress.enable</option> options.
+     The httpd service no longer attempts to start the postgresql service. If you have come to depend
+     on this behaviour then you can preserve the behavior with the following configuration:
+     <literal>systemd.services.httpd.after = [ "postgresql.service" ];</literal>
+    </para>
+    <para>
+     The option <option>services.httpd.extraSubservices</option> has been
+     marked as deprecated. You may still use this feature, but it will be
+     removed in a future release of NixOS. You are encouraged to convert any
+     httpd subservices you may have written to a full NixOS module.
+    </para>
+    <para>
+     Most of the httpd subservices packaged with NixOS have been replaced with
+     full NixOS modules including LimeSurvey, WordPress, and Zabbix. These
+     modules can be enabled using the <option>services.limesurvey.enable</option>,
+     <option>services.mediawiki.enable</option>, <option>services.wordpress.enable</option>,
+     and <option>services.zabbixWeb.enable</option> options.
     </para>
    </listitem>
    <listitem>
@@ -182,6 +223,31 @@
      <xref linkend="opt-programs.zsh.loginShellInit" /> and <xref linkend="opt-programs.zsh.promptInit" /> may break if it relies on those options being set.
     </para>
    </listitem>
+   <listitem>
+    <para>
+      The <literal>prometheus-nginx-exporter</literal> package now uses the offical exporter provided by NGINX Inc.
+      Its metrics are differently structured and are incompatible to the old ones. For information about the metrics,
+      have a look at the <link xlink:href="https://github.com/nginxinc/nginx-prometheus-exporter">official repo</link>.
+    </para>
+   </listitem>
+   <listitem>
+     <para>
+       Nodejs 8 is scheduled EOL under the lifetime of 19.09 and has been dropped.
+     </para>
+   </listitem>
+   <listitem>
+     <para>
+       By default, prometheus exporters are now run with <literal>DynamicUser</literal> enabled.
+       Exporters that need a real user, now run under a seperate user and group which follow the pattern <literal>&lt;exporter-name&gt;-exporter</literal>, instead of the previous default <literal>nobody</literal> and <literal>nogroup</literal>.
+       Only some exporters are affected by the latter, namely the exporters <literal>dovecot</literal>, <literal>node</literal>, <literal>postfix</literal> and <literal>varnish</literal>.
+     </para>
+   </listitem>
+   <listitem>
+     <para>
+       The <literal>ibus-qt</literal> package is not installed by default anymore when <xref linkend="opt-i18n.inputMethod.enabled" /> is set to <literal>ibus</literal>.
+       If IBus support in Qt 4.x applications is required, add the <literal>ibus-qt</literal> package to your <xref linkend="opt-environment.systemPackages" /> manually.
+     </para>
+   </listitem>
   </itemizedlist>
  </section>
 
@@ -318,6 +384,31 @@
      The <literal>mercurial</literal> <literal>httpd.extraSubservice</literal> has been removed from nixpkgs due to lack of maintainer.
     </para>
    </listitem>
+   <listitem>
+    <para>
+     The <literal>trac</literal> <literal>httpd.extraSubservice</literal> has been removed from nixpkgs because it was unmaintained.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     The <literal>foswiki</literal> package and associated <literal>httpd.extraSubservice</literal> have been removed
+     from nixpkgs due to lack of maintainer.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     The <literal>tomcat-connector</literal> <literal>httpd.extraSubservice</literal> has been removed from nixpkgs.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     It's now possible to change configuration in
+     <link linkend="opt-services.nextcloud.enable">services.nextcloud</link> after the initial deploy
+     since all config parameters are persisted in an additional config file generated by the module.
+     Previously core configuration like database parameters were set using their imperative
+     installer after creating <literal>/var/lib/nextcloud</literal>.
+    </para>
+   </listitem>
   </itemizedlist>
  </section>
 </section>
diff --git a/nixpkgs/nixos/lib/build-vms.nix b/nixpkgs/nixos/lib/build-vms.nix
index 6c92aa1ffa2f..b622a4d59e89 100644
--- a/nixpkgs/nixos/lib/build-vms.nix
+++ b/nixpkgs/nixos/lib/build-vms.nix
@@ -67,7 +67,7 @@ rec {
             in
             { key = "ip-address";
               config =
-                { networking.hostName = m.fst;
+                { networking.hostName = mkDefault m.fst;
 
                   networking.interfaces = listToAttrs interfaces;
 
diff --git a/nixpkgs/nixos/lib/make-options-doc/default.nix b/nixpkgs/nixos/lib/make-options-doc/default.nix
new file mode 100644
index 000000000000..c22c7500335d
--- /dev/null
+++ b/nixpkgs/nixos/lib/make-options-doc/default.nix
@@ -0,0 +1,125 @@
+/* Generate JSON, XML and DocBook documentation for given NixOS options.
+
+   Minimal example:
+
+    { pkgs,  }:
+
+    let
+      eval = import (pkgs.path + "/nixos/lib/eval-config.nix") {
+        baseModules = [
+          ../module.nix
+        ];
+        modules = [];
+      };
+    in pkgs.nixosOptionsDoc {
+      options = eval.options;
+    }
+
+*/
+{ pkgs
+, lib
+, options
+, transformOptions ? lib.id  # function for additional tranformations of the options
+, revision ? "" # Specify revision for the options
+}:
+
+let
+  # Replace functions by the string <function>
+  substFunction = x:
+    if builtins.isAttrs x then lib.mapAttrs (name: substFunction) x
+    else if builtins.isList x then map substFunction x
+    else if lib.isFunction x then "<function>"
+    else x;
+
+  optionsListDesc = lib.flip map optionsListVisible
+   (opt: transformOptions opt
+    // lib.optionalAttrs (opt ? example) { example = substFunction opt.example; }
+    // lib.optionalAttrs (opt ? default) { default = substFunction opt.default; }
+    // lib.optionalAttrs (opt ? type) { type = substFunction opt.type; }
+    // lib.optionalAttrs (opt ? relatedPackages && opt.relatedPackages != []) { relatedPackages = genRelatedPackages opt.relatedPackages; }
+   );
+
+  # Generate DocBook documentation for a list of packages. This is
+  # what `relatedPackages` option of `mkOption` from
+  # ../../../lib/options.nix influences.
+  #
+  # Each element of `relatedPackages` can be either
+  # - a string:  that will be interpreted as an attribute name from `pkgs`,
+  # - a list:    that will be interpreted as an attribute path from `pkgs`,
+  # - an attrset: that can specify `name`, `path`, `package`, `comment`
+  #   (either of `name`, `path` is required, the rest are optional).
+  genRelatedPackages = packages:
+    let
+      unpack = p: if lib.isString p then { name = p; }
+                  else if lib.isList p then { path = p; }
+                  else p;
+      describe = args:
+        let
+          title = args.title or null;
+          name = args.name or (lib.concatStringsSep "." args.path);
+          path = args.path or [ args.name ];
+          package = args.package or (lib.attrByPath path (throw "Invalid package attribute path `${toString path}'") pkgs);
+        in "<listitem>"
+        + "<para><literal>${lib.optionalString (title != null) "${title} aka "}pkgs.${name} (${package.meta.name})</literal>"
+        + lib.optionalString (!package.meta.available) " <emphasis>[UNAVAILABLE]</emphasis>"
+        + ": ${package.meta.description or "???"}.</para>"
+        + lib.optionalString (args ? comment) "\n<para>${args.comment}</para>"
+        # Lots of `longDescription's break DocBook, so we just wrap them into <programlisting>
+        + lib.optionalString (package.meta ? longDescription) "\n<programlisting>${package.meta.longDescription}</programlisting>"
+        + "</listitem>";
+    in "<itemizedlist>${lib.concatStringsSep "\n" (map (p: describe (unpack p)) packages)}</itemizedlist>";
+
+  # Custom "less" that pushes up all the things ending in ".enable*"
+  # and ".package*"
+  optionLess = a: b:
+    let
+      ise = lib.hasPrefix "enable";
+      isp = lib.hasPrefix "package";
+      cmp = lib.splitByAndCompare ise lib.compare
+                                 (lib.splitByAndCompare isp lib.compare lib.compare);
+    in lib.compareLists cmp a.loc b.loc < 0;
+
+  # Remove invisible and internal options.
+  optionsListVisible = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options);
+
+  # Customly sort option list for the man page.
+  optionsList = lib.sort optionLess optionsListDesc;
+
+  # Convert the list of options into an XML file.
+  optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsList);
+
+in rec {
+  # The NixOS options in JSON format.
+  optionsJSON = pkgs.runCommand "options.json"
+    { meta.description = "List of NixOS options in JSON format";
+    }
+    ''
+      # Export list of options in different format.
+      dst=$out/share/doc/nixos
+      mkdir -p $dst
+
+      cp ${builtins.toFile "options.json" (builtins.unsafeDiscardStringContext (builtins.toJSON
+        (builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList))))
+      } $dst/options.json
+
+      mkdir -p $out/nix-support
+      echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products
+    ''; # */
+
+  optionsDocBook = pkgs.runCommand "options-docbook.xml" {} ''
+    optionsXML=${optionsXML}
+    if grep /nixpkgs/nixos/modules $optionsXML; then
+      echo "The manual appears to depend on the location of Nixpkgs, which is bad"
+      echo "since this prevents sharing via the NixOS channel.  This is typically"
+      echo "caused by an option default that refers to a relative path (see above"
+      echo "for hints about the offending path)."
+      exit 1
+    fi
+
+    ${pkgs.libxslt.bin}/bin/xsltproc \
+      --stringparam revision '${revision}' \
+      -o intermediate.xml ${./options-to-docbook.xsl} $optionsXML
+    ${pkgs.libxslt.bin}/bin/xsltproc \
+      -o "$out" ${./postprocess-option-descriptions.xsl} intermediate.xml
+  '';
+}
diff --git a/nixpkgs/nixos/doc/manual/options-to-docbook.xsl b/nixpkgs/nixos/lib/make-options-doc/options-to-docbook.xsl
index 72ac89d4ff62..72ac89d4ff62 100644
--- a/nixpkgs/nixos/doc/manual/options-to-docbook.xsl
+++ b/nixpkgs/nixos/lib/make-options-doc/options-to-docbook.xsl
diff --git a/nixpkgs/nixos/doc/manual/postprocess-option-descriptions.xsl b/nixpkgs/nixos/lib/make-options-doc/postprocess-option-descriptions.xsl
index 1201c7612c2e..1201c7612c2e 100644
--- a/nixpkgs/nixos/doc/manual/postprocess-option-descriptions.xsl
+++ b/nixpkgs/nixos/lib/make-options-doc/postprocess-option-descriptions.xsl
diff --git a/nixpkgs/nixos/lib/test-driver/Machine.pm b/nixpkgs/nixos/lib/test-driver/Machine.pm
index 006da889671e..4d3d63cd2dbf 100644
--- a/nixpkgs/nixos/lib/test-driver/Machine.pm
+++ b/nixpkgs/nixos/lib/test-driver/Machine.pm
@@ -31,9 +31,17 @@ sub new {
 
     if (!$startCommand) {
         # !!! merge with qemu-vm.nix.
+        my $netBackend = "-netdev user,id=net0";
+        my $netFrontend = "-device virtio-net-pci,netdev=net0";
+
+        $netBackend .= "," . $args->{netBackendArgs}
+            if defined $args->{netBackendArgs};
+
+        $netFrontend .= "," . $args->{netFrontendArgs}
+            if defined $args->{netFrontendArgs};
+
         $startCommand =
-            "qemu-kvm -m 384 " .
-            "-net nic,model=virtio \$QEMU_OPTS ";
+            "qemu-kvm -m 384 $netBackend $netFrontend \$QEMU_OPTS ";
 
         if (defined $args->{hda}) {
             if ($args->{hdaInterface} eq "scsi") {
diff --git a/nixpkgs/nixos/modules/config/timezone.nix b/nixpkgs/nixos/modules/config/locale.nix
index b15948f6e2e5..6f0565881877 100644
--- a/nixpkgs/nixos/modules/config/timezone.nix
+++ b/nixpkgs/nixos/modules/config/locale.nix
@@ -9,6 +9,8 @@ let
   timezone = types.nullOr (types.addCheck types.str nospace)
     // { description = "null or string without spaces"; };
 
+  lcfg = config.location;
+
 in
 
 {
@@ -37,12 +39,45 @@ in
       };
 
     };
+
+    location = {
+
+      latitude = mkOption {
+        type = types.float;
+        description = ''
+          Your current latitude, between
+          <literal>-90.0</literal> and <literal>90.0</literal>. Must be provided
+          along with longitude.
+        '';
+      };
+
+      longitude = mkOption {
+        type = types.float;
+        description = ''
+          Your current longitude, between
+          between <literal>-180.0</literal> and <literal>180.0</literal>. Must be
+          provided along with latitude.
+        '';
+      };
+
+      provider = mkOption {
+        type = types.enum [ "manual" "geoclue2" ];
+        default = "manual";
+        description = ''
+          The location provider to use for determining your location. If set to
+          <literal>manual</literal> you must also provide latitude/longitude.
+        '';
+      };
+
+    };
   };
 
   config = {
 
     environment.sessionVariables.TZDIR = "/etc/zoneinfo";
 
+    services.geoclue2.enable = mkIf (lcfg.provider == "geoclue2") true;
+
     # This way services are restarted when tzdata changes.
     systemd.globalEnvironment.TZDIR = tzdir;
 
diff --git a/nixpkgs/nixos/modules/config/networking.nix b/nixpkgs/nixos/modules/config/networking.nix
index eab4e73e19a1..4b9086022ed5 100644
--- a/nixpkgs/nixos/modules/config/networking.nix
+++ b/nixpkgs/nixos/modules/config/networking.nix
@@ -7,16 +7,6 @@ with lib;
 let
 
   cfg = config.networking;
-  dnsmasqResolve = config.services.dnsmasq.enable &&
-                   config.services.dnsmasq.resolveLocalQueries;
-  hasLocalResolver = config.services.bind.enable ||
-                     config.services.unbound.enable ||
-                     dnsmasqResolve;
-
-  resolvconfOptions = cfg.resolvconfOptions
-    ++ optional cfg.dnsSingleRequest "single-request"
-    ++ optional cfg.dnsExtensionMechanism "edns0";
-
 
   localhostMapped4 = cfg.hosts ? "127.0.0.1" && elem "localhost" cfg.hosts."127.0.0.1";
   localhostMapped6 = cfg.hosts ? "::1"       && elem "localhost" cfg.hosts."::1";
@@ -64,48 +54,6 @@ in
       '';
     };
 
-    networking.dnsSingleRequest = lib.mkOption {
-      type = types.bool;
-      default = false;
-      description = ''
-        Recent versions of glibc will issue both ipv4 (A) and ipv6 (AAAA)
-        address queries at the same time, from the same port. Sometimes upstream
-        routers will systemically drop the ipv4 queries. The symptom of this problem is
-        that 'getent hosts example.com' only returns ipv6 (or perhaps only ipv4) addresses. The
-        workaround for this is to specify the option 'single-request' in
-        /etc/resolv.conf. This option enables that.
-      '';
-    };
-
-    networking.dnsExtensionMechanism = lib.mkOption {
-      type = types.bool;
-      default = true;
-      description = ''
-        Enable the <code>edns0</code> option in <filename>resolv.conf</filename>. With
-        that option set, <code>glibc</code> supports use of the extension mechanisms for
-        DNS (EDNS) specified in RFC 2671. The most popular user of that feature is DNSSEC,
-        which does not work without it.
-      '';
-    };
-
-    networking.extraResolvconfConf = lib.mkOption {
-      type = types.lines;
-      default = "";
-      example = "libc=NO";
-      description = ''
-        Extra configuration to append to <filename>resolvconf.conf</filename>.
-      '';
-    };
-
-    networking.resolvconfOptions = lib.mkOption {
-      type = types.listOf types.str;
-      default = [];
-      example = [ "ndots:1" "rotate" ];
-      description = ''
-        Set the options in <filename>/etc/resolv.conf</filename>.
-      '';
-    };
-
     networking.timeServers = mkOption {
       default = [
         "0.nixos.pool.ntp.org"
@@ -240,35 +188,6 @@ in
         # /etc/host.conf: resolver configuration file
         "host.conf".text = cfg.hostConf;
 
-        # /etc/resolvconf.conf: Configuration for openresolv.
-        "resolvconf.conf".text =
-            ''
-              # This is the default, but we must set it here to prevent
-              # a collision with an apparently unrelated environment
-              # variable with the same name exported by dhcpcd.
-              interface_order='lo lo[0-9]*'
-            '' + optionalString config.services.nscd.enable ''
-              # Invalidate the nscd cache whenever resolv.conf is
-              # regenerated.
-              libc_restart='${pkgs.systemd}/bin/systemctl try-restart --no-block nscd.service 2> /dev/null'
-            '' + optionalString (length resolvconfOptions > 0) ''
-              # Options as described in resolv.conf(5)
-              resolv_conf_options='${concatStringsSep " " resolvconfOptions}'
-            '' + optionalString hasLocalResolver ''
-              # This hosts runs a full-blown DNS resolver.
-              name_servers='127.0.0.1'
-            '' + optionalString dnsmasqResolve ''
-              dnsmasq_conf=/etc/dnsmasq-conf.conf
-              dnsmasq_resolv=/etc/dnsmasq-resolv.conf
-            '' + cfg.extraResolvconfConf + ''
-            '';
-
-      } // optionalAttrs config.services.resolved.enable {
-        # symlink the dynamic stub resolver of resolv.conf as recommended by upstream:
-        # https://www.freedesktop.org/software/systemd/man/systemd-resolved.html#/etc/resolv.conf
-        "resolv.conf".source = "/run/systemd/resolve/stub-resolv.conf";
-      } // optionalAttrs (config.services.resolved.enable && dnsmasqResolve) {
-        "dnsmasq-resolv.conf".source = "/run/systemd/resolve/resolv.conf";
       } // optionalAttrs (pkgs.stdenv.hostPlatform.libc == "glibc") {
         # /etc/rpc: RPC program numbers.
         "rpc".source = pkgs.glibc.out + "/etc/rpc";
@@ -295,29 +214,6 @@ in
     # Install the proxy environment variables
     environment.sessionVariables = cfg.proxy.envVars;
 
-    # This is needed when /etc/resolv.conf is being overriden by networkd
-    # and other configurations. If the file is destroyed by an environment
-    # activation then it must be rebuilt so that applications which interface
-    # with /etc/resolv.conf directly don't break.
-    system.activationScripts.resolvconf = stringAfter [ "etc" "specialfs" "var" ]
-      ''
-        # Systemd resolved controls its own resolv.conf
-        rm -f /run/resolvconf/interfaces/systemd
-        ${optionalString config.services.resolved.enable ''
-          rm -rf /run/resolvconf/interfaces
-          mkdir -p /run/resolvconf/interfaces
-          ln -s /run/systemd/resolve/resolv.conf /run/resolvconf/interfaces/systemd
-        ''}
-
-        # Make sure resolv.conf is up to date if not managed manually, by systemd or
-        # by NetworkManager
-        ${optionalString (!config.environment.etc?"resolv.conf" &&
-                          (cfg.networkmanager.enable ->
-                            cfg.networkmanager.rc-manager == "resolvconf")) ''
-          ${pkgs.openresolv}/bin/resolvconf -u
-        ''}
-      '';
-
   };
 
 }
diff --git a/nixpkgs/nixos/modules/config/no-x-libs.nix b/nixpkgs/nixos/modules/config/no-x-libs.nix
index aad02a9ca4e3..74cf74d74181 100644
--- a/nixpkgs/nixos/modules/config/no-x-libs.nix
+++ b/nixpkgs/nixos/modules/config/no-x-libs.nix
@@ -34,7 +34,7 @@ with lib;
       networkmanager-openvpn = super.networkmanager-openvpn.override { withGnome = false; };
       networkmanager-vpnc = super.networkmanager-vpnc.override { withGnome = false; };
       networkmanager-iodine = super.networkmanager-iodine.override { withGnome = false; };
-      pinentry = super.pinentry.override { gtk2 = null; gcr = null; qt = null; };
+      pinentry = super.pinentry.override { gtk2 = null; gcr = null; qt4 = null; qt5 = null; };
       gobject-introspection = super.gobject-introspection.override { x11Support = false; };
     }));
   };
diff --git a/nixpkgs/nixos/modules/config/resolvconf.nix b/nixpkgs/nixos/modules/config/resolvconf.nix
new file mode 100644
index 000000000000..406c6a7ac329
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/resolvconf.nix
@@ -0,0 +1,149 @@
+# /etc files related to networking, such as /etc/services.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.networking.resolvconf;
+
+  resolvconfOptions = cfg.extraOptions
+    ++ optional cfg.dnsSingleRequest "single-request"
+    ++ optional cfg.dnsExtensionMechanism "edns0";
+
+  configText =
+    ''
+      # This is the default, but we must set it here to prevent
+      # a collision with an apparently unrelated environment
+      # variable with the same name exported by dhcpcd.
+      interface_order='lo lo[0-9]*'
+    '' + optionalString config.services.nscd.enable ''
+      # Invalidate the nscd cache whenever resolv.conf is
+      # regenerated.
+      libc_restart='${pkgs.systemd}/bin/systemctl try-restart --no-block nscd.service 2> /dev/null'
+    '' + optionalString (length resolvconfOptions > 0) ''
+      # Options as described in resolv.conf(5)
+      resolv_conf_options='${concatStringsSep " " resolvconfOptions}'
+    '' + optionalString cfg.useLocalResolver ''
+      # This hosts runs a full-blown DNS resolver.
+      name_servers='127.0.0.1'
+    '' + cfg.extraConfig;
+
+in
+
+{
+
+  options = {
+
+    networking.resolvconf = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        internal = true;
+        description = ''
+          DNS configuration is managed by resolvconf.
+        '';
+      };
+
+      useHostResolvConf = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          In containers, whether to use the
+          <filename>resolv.conf</filename> supplied by the host.
+        '';
+      };
+
+      dnsSingleRequest = lib.mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Recent versions of glibc will issue both ipv4 (A) and ipv6 (AAAA)
+          address queries at the same time, from the same port. Sometimes upstream
+          routers will systemically drop the ipv4 queries. The symptom of this problem is
+          that 'getent hosts example.com' only returns ipv6 (or perhaps only ipv4) addresses. The
+          workaround for this is to specify the option 'single-request' in
+          /etc/resolv.conf. This option enables that.
+        '';
+      };
+
+      dnsExtensionMechanism = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Enable the <code>edns0</code> option in <filename>resolv.conf</filename>. With
+          that option set, <code>glibc</code> supports use of the extension mechanisms for
+          DNS (EDNS) specified in RFC 2671. The most popular user of that feature is DNSSEC,
+          which does not work without it.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = "libc=NO";
+        description = ''
+          Extra configuration to append to <filename>resolvconf.conf</filename>.
+        '';
+      };
+
+      extraOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "ndots:1" "rotate" ];
+        description = ''
+          Set the options in <filename>/etc/resolv.conf</filename>.
+        '';
+      };
+
+      useLocalResolver = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Use local DNS server for resolving.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkMerge [
+    {
+      networking.resolvconf.enable = !(config.environment.etc ? "resolv.conf");
+
+      environment.etc."resolvconf.conf".text =
+        if !cfg.enable then
+          # Force-stop any attempts to use resolvconf
+          ''
+            echo "resolvconf is disabled on this system but was used anyway:" >&2
+            echo "$0 $*" >&2
+            exit 1
+          ''
+        else configText;
+    }
+
+    (mkIf cfg.enable {
+      environment.systemPackages = [ pkgs.openresolv ];
+
+      systemd.services.resolvconf = {
+        description = "resolvconf update";
+
+        before = [ "network-pre.target" ];
+        wants = [ "network-pre.target" ];
+        wantedBy = [ "multi-user.target" ];
+        restartTriggers = [ config.environment.etc."resolvconf.conf".source ];
+
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${pkgs.openresolv}/bin/resolvconf -u";
+          RemainAfterExit = true;
+        };
+      };
+
+    })
+  ];
+
+}
diff --git a/nixpkgs/nixos/modules/config/xdg/portal.nix b/nixpkgs/nixos/modules/config/xdg/portal.nix
new file mode 100644
index 000000000000..bdbbfda2bb42
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/xdg/portal.nix
@@ -0,0 +1,58 @@
+{ config, pkgs ,lib ,... }:
+
+with lib;
+
+{
+  options.xdg.portal = {
+    enable =
+      mkEnableOption "<link xlink:href='https://github.com/flatpak/xdg-desktop-portal'>xdg desktop integration</link>"//{
+        default = false;
+      };
+
+    extraPortals = mkOption {
+      type = types.listOf types.package;
+      default = [];
+      description = ''
+        List of additional portals to add to path. Portals allow interaction
+        with system, like choosing files or taking screenshots. At minimum,
+        a desktop portal implementation should be listed. GNOME and KDE already
+        adds <package>xdg-desktop-portal-gtk</package>; and
+        <package>xdg-desktop-portal-kde</package> respectively. On other desktop
+        environments you probably want to add them yourself.
+      '';
+    };
+
+    gtkUsePortal = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Sets environment variable <literal>GTK_USE_PORTAL</literal> to <literal>1</literal>.
+        This is needed for packages ran outside Flatpak to respect and use XDG Desktop Portals.
+        For example, you'd need to set this for non-flatpak Firefox to use native filechoosers.
+        Defaults to <literal>false</literal> to respect its opt-in nature.
+      '';
+    };
+  };
+
+  config =
+    let
+      cfg = config.xdg.portal;
+      packages = [ pkgs.xdg-desktop-portal ] ++ cfg.extraPortals;
+
+    in mkIf cfg.enable {
+
+      assertions = [
+        { assertion = (cfg.gtkUsePortal -> cfg.extraPortals != []);
+          message = "Setting xdg.portal.gtkUsePortal to true requires a portal implementation in xdg.portal.extraPortals such as xdg-desktop-portal-gtk or xdg-desktop-portal-kde.";
+        }
+      ];
+
+      services.dbus.packages  = packages;
+      systemd.packages = packages;
+
+      environment.variables = {
+        GTK_USE_PORTAL = mkIf cfg.gtkUsePortal "1";
+        XDG_DESKTOP_PORTAL_PATH = map (p: "${p}/share/xdg-desktop-portal/portals") cfg.extraPortals;
+      };
+    };
+}
diff --git a/nixpkgs/nixos/modules/hardware/opengl.nix b/nixpkgs/nixos/modules/hardware/opengl.nix
index 2defab51bc3a..57cac56bd8ab 100644
--- a/nixpkgs/nixos/modules/hardware/opengl.nix
+++ b/nixpkgs/nixos/modules/hardware/opengl.nix
@@ -118,6 +118,19 @@ in
           set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc.
         '';
       };
+
+      setLdLibraryPath = mkOption {
+        type = types.bool;
+        internal = true;
+        default = false;
+        description = ''
+          Whether the <literal>LD_LIBRARY_PATH</literal> environment variable
+          should be set to the locations of driver libraries. Drivers which
+          rely on overriding libraries should set this to true. Drivers which
+          support <literal>libglvnd</literal> and other dispatch libraries
+          instead of overriding libraries should not set this.
+        '';
+      };
     };
 
   };
@@ -145,11 +158,8 @@ in
       )
     ];
 
-    environment.sessionVariables.LD_LIBRARY_PATH =
-      [ "/run/opengl-driver/lib" ] ++ optional cfg.driSupport32Bit "/run/opengl-driver-32/lib";
-
-    environment.variables.XDG_DATA_DIRS =
-      [ "/run/opengl-driver/share" ] ++ optional cfg.driSupport32Bit "/run/opengl-driver-32/share";
+    environment.sessionVariables.LD_LIBRARY_PATH = mkIf cfg.setLdLibraryPath
+      ([ "/run/opengl-driver/lib" ] ++ optional cfg.driSupport32Bit "/run/opengl-driver-32/lib");
 
     hardware.opengl.package = mkDefault (makePackage pkgs);
     hardware.opengl.package32 = mkDefault (makePackage pkgs.pkgsi686Linux);
diff --git a/nixpkgs/nixos/modules/hardware/video/amdgpu-pro.nix b/nixpkgs/nixos/modules/hardware/video/amdgpu-pro.nix
index ab9e0c92020e..8e91e9d2baa9 100644
--- a/nixpkgs/nixos/modules/hardware/video/amdgpu-pro.nix
+++ b/nixpkgs/nixos/modules/hardware/video/amdgpu-pro.nix
@@ -30,10 +30,11 @@ in
     nixpkgs.config.xorg.abiCompat = "1.19";
 
     services.xserver.drivers = singleton
-      { name = "amdgpu"; modules = [ package ]; libPath = [ package ]; };
+      { name = "amdgpu"; modules = [ package ]; };
 
     hardware.opengl.package = package;
     hardware.opengl.package32 = package32;
+    hardware.opengl.setLdLibraryPath = true;
 
     boot.extraModulePackages = [ package ];
 
diff --git a/nixpkgs/nixos/modules/hardware/video/ati.nix b/nixpkgs/nixos/modules/hardware/video/ati.nix
index 6102919f0155..f867bba80630 100644
--- a/nixpkgs/nixos/modules/hardware/video/ati.nix
+++ b/nixpkgs/nixos/modules/hardware/video/ati.nix
@@ -21,10 +21,11 @@ in
     nixpkgs.config.xorg.abiCompat = "1.17";
 
     services.xserver.drivers = singleton
-      { name = "fglrx"; modules = [ ati_x11 ]; libPath = [ "${ati_x11}/lib" ]; };
+      { name = "fglrx"; modules = [ ati_x11 ]; };
 
     hardware.opengl.package = ati_x11;
     hardware.opengl.package32 = pkgs.pkgsi686Linux.linuxPackages.ati_drivers_x11.override { libsOnly = true; kernel = null; };
+    hardware.opengl.setLdLibraryPath = true;
 
     environment.systemPackages = [ ati_x11 ];
 
diff --git a/nixpkgs/nixos/modules/hardware/video/nvidia.nix b/nixpkgs/nixos/modules/hardware/video/nvidia.nix
index a5740929a310..da3c8ee5a9fa 100644
--- a/nixpkgs/nixos/modules/hardware/video/nvidia.nix
+++ b/nixpkgs/nixos/modules/hardware/video/nvidia.nix
@@ -138,7 +138,6 @@ in
     services.xserver.drivers = singleton {
       name = "nvidia";
       modules = [ nvidia_x11.bin ];
-      libPath = [ nvidia_x11 ];
       deviceSection = optionalString optimusCfg.enable
         ''
           BusID "${optimusCfg.nvidiaBusId}"
diff --git a/nixpkgs/nixos/modules/i18n/input-method/ibus.nix b/nixpkgs/nixos/modules/i18n/input-method/ibus.nix
index f8e021f551e8..8109ef76c402 100644
--- a/nixpkgs/nixos/modules/i18n/input-method/ibus.nix
+++ b/nixpkgs/nixos/modules/i18n/input-method/ibus.nix
@@ -55,7 +55,7 @@ in
 
     # Without dconf enabled it is impossible to use IBus
     environment.systemPackages = with pkgs; [
-      ibus-qt gnome3.dconf ibusAutostart
+      gnome3.dconf ibusAutostart
     ];
 
     environment.variables = {
diff --git a/nixpkgs/nixos/modules/installer/netboot/netboot.nix b/nixpkgs/nixos/modules/installer/netboot/netboot.nix
index cdfba5ab9e31..f9b8d95c684d 100644
--- a/nixpkgs/nixos/modules/installer/netboot/netboot.nix
+++ b/nixpkgs/nixos/modules/installer/netboot/netboot.nix
@@ -84,7 +84,7 @@ with lib;
 
     system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" ''
       #!ipxe
-      kernel ${pkgs.stdenv.hostPlatform.platform.kernelTarget} init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
+      kernel ${pkgs.stdenv.hostPlatform.platform.kernelTarget} init=${config.system.build.toplevel}/init initrd=initrd ${toString config.boot.kernelParams}
       initrd initrd
       boot
     '';
diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-generate-config.pl b/nixpkgs/nixos/modules/installer/tools/nixos-generate-config.pl
index c09def1fceae..cfdbdaabf5c5 100644
--- a/nixpkgs/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixpkgs/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -607,90 +607,7 @@ EOF
         }
 
         write_file($fn, <<EOF);
-# Edit this configuration file to define what should be installed on
-# your system.  Help is available in the configuration.nix(5) man page
-# and in the NixOS manual (accessible by running ‘nixos-help’).
-
-{ config, pkgs, ... }:
-
-{
-  imports =
-    [ # Include the results of the hardware scan.
-      ./hardware-configuration.nix
-    ];
-
-$bootLoaderConfig
-  # networking.hostName = "nixos"; # Define your hostname.
-  # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.
-
-  # Configure network proxy if necessary
-  # networking.proxy.default = "http://user:password\@proxy:port/";
-  # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
-
-  # Select internationalisation properties.
-  # i18n = {
-  #   consoleFont = "Lat2-Terminus16";
-  #   consoleKeyMap = "us";
-  #   defaultLocale = "en_US.UTF-8";
-  # };
-
-  # Set your time zone.
-  # time.timeZone = "Europe/Amsterdam";
-
-  # List packages installed in system profile. To search, run:
-  # \$ nix search wget
-  # environment.systemPackages = with pkgs; [
-  #   wget vim
-  # ];
-
-  # Some programs need SUID wrappers, can be configured further or are
-  # started in user sessions.
-  # programs.mtr.enable = true;
-  # programs.gnupg.agent = { enable = true; enableSSHSupport = true; };
-
-  # List services that you want to enable:
-
-  # Enable the OpenSSH daemon.
-  # services.openssh.enable = true;
-
-  # Open ports in the firewall.
-  # networking.firewall.allowedTCPPorts = [ ... ];
-  # networking.firewall.allowedUDPPorts = [ ... ];
-  # Or disable the firewall altogether.
-  # networking.firewall.enable = false;
-
-  # Enable CUPS to print documents.
-  # services.printing.enable = true;
-
-  # Enable sound.
-  # sound.enable = true;
-  # hardware.pulseaudio.enable = true;
-
-  # Enable the X11 windowing system.
-  # services.xserver.enable = true;
-  # services.xserver.layout = "us";
-  # services.xserver.xkbOptions = "eurosign:e";
-
-  # Enable touchpad support.
-  # services.xserver.libinput.enable = true;
-
-  # Enable the KDE Desktop Environment.
-  # services.xserver.displayManager.sddm.enable = true;
-  # services.xserver.desktopManager.plasma5.enable = true;
-
-  # Define a user account. Don't forget to set a password with ‘passwd’.
-  # users.users.jane = {
-  #   isNormalUser = true;
-  #   extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
-  # };
-
-  # This value determines the NixOS release with which your system is to be
-  # compatible, in order to avoid breaking some software such as database
-  # servers. You should change this only after NixOS release notes say you
-  # should.
-  system.stateVersion = "${\(qw(@release@))}"; # Did you read the comment?
-
-}
+@configuration@
 EOF
     } else {
         print STDERR "warning: not overwriting existing $fn\n";
diff --git a/nixpkgs/nixos/modules/installer/tools/tools.nix b/nixpkgs/nixos/modules/installer/tools/tools.nix
index 59eb4a63af40..05add59117d1 100644
--- a/nixpkgs/nixos/modules/installer/tools/tools.nix
+++ b/nixpkgs/nixos/modules/installer/tools/tools.nix
@@ -38,7 +38,7 @@ let
     src = ./nixos-generate-config.pl;
     path = lib.optionals (lib.elem "btrfs" config.boot.supportedFilesystems) [ pkgs.btrfs-progs ];
     perl = "${pkgs.perl}/bin/perl -I${pkgs.perlPackages.FileSlurp}/${pkgs.perl.libPrefix}";
-    inherit (config.system.nixos) release;
+    inherit (config.system.nixos-generate-config) configuration;
   };
 
   nixos-option = makeProg {
@@ -61,8 +61,111 @@ in
 
 {
 
+  options.system.nixos-generate-config.configuration = mkOption {
+    internal = true;
+    type = types.str;
+    description = ''
+      The NixOS module that <literal>nixos-generate-config</literal>
+      saves to <literal>/etc/nixos/configuration.nix</literal>.
+
+      This is an internal option. No backward compatibility is guaranteed.
+      Use at your own risk!
+
+      Note that this string gets spliced into a Perl script. The perl
+      variable <literal>$bootLoaderConfig</literal> can be used to
+      splice in the boot loader configuration.
+    '';
+  };
+
   config = {
 
+    system.nixos-generate-config.configuration = mkDefault ''
+      # Edit this configuration file to define what should be installed on
+      # your system.  Help is available in the configuration.nix(5) man page
+      # and in the NixOS manual (accessible by running ‘nixos-help’).
+
+      { config, pkgs, ... }:
+
+      {
+        imports =
+          [ # Include the results of the hardware scan.
+            ./hardware-configuration.nix
+          ];
+
+      $bootLoaderConfig
+        # networking.hostName = "nixos"; # Define your hostname.
+        # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.
+
+        # Configure network proxy if necessary
+        # networking.proxy.default = "http://user:password\@proxy:port/";
+        # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
+
+        # Select internationalisation properties.
+        # i18n = {
+        #   consoleFont = "Lat2-Terminus16";
+        #   consoleKeyMap = "us";
+        #   defaultLocale = "en_US.UTF-8";
+        # };
+
+        # Set your time zone.
+        # time.timeZone = "Europe/Amsterdam";
+
+        # List packages installed in system profile. To search, run:
+        # \$ nix search wget
+        # environment.systemPackages = with pkgs; [
+        #   wget vim
+        # ];
+
+        # Some programs need SUID wrappers, can be configured further or are
+        # started in user sessions.
+        # programs.mtr.enable = true;
+        # programs.gnupg.agent = { enable = true; enableSSHSupport = true; };
+
+        # List services that you want to enable:
+
+        # Enable the OpenSSH daemon.
+        # services.openssh.enable = true;
+
+        # Open ports in the firewall.
+        # networking.firewall.allowedTCPPorts = [ ... ];
+        # networking.firewall.allowedUDPPorts = [ ... ];
+        # Or disable the firewall altogether.
+        # networking.firewall.enable = false;
+
+        # Enable CUPS to print documents.
+        # services.printing.enable = true;
+
+        # Enable sound.
+        # sound.enable = true;
+        # hardware.pulseaudio.enable = true;
+
+        # Enable the X11 windowing system.
+        # services.xserver.enable = true;
+        # services.xserver.layout = "us";
+        # services.xserver.xkbOptions = "eurosign:e";
+
+        # Enable touchpad support.
+        # services.xserver.libinput.enable = true;
+
+        # Enable the KDE Desktop Environment.
+        # services.xserver.displayManager.sddm.enable = true;
+        # services.xserver.desktopManager.plasma5.enable = true;
+
+        # Define a user account. Don't forget to set a password with ‘passwd’.
+        # users.users.jane = {
+        #   isNormalUser = true;
+        #   extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
+        # };
+
+        # This value determines the NixOS release with which your system is to be
+        # compatible, in order to avoid breaking some software such as database
+        # servers. You should change this only after NixOS release notes say you
+        # should.
+        system.stateVersion = "${config.system.nixos.release}"; # Did you read the comment?
+
+      }
+    '';
+
     environment.systemPackages =
       [ nixos-build-vms
         nixos-install
diff --git a/nixpkgs/nixos/modules/misc/ids.nix b/nixpkgs/nixos/modules/misc/ids.nix
index 08f7b827948d..d7599a1e2b7a 100644
--- a/nixpkgs/nixos/modules/misc/ids.nix
+++ b/nixpkgs/nixos/modules/misc/ids.nix
@@ -516,7 +516,7 @@
       tss = 176;
       #memcached = 177; # unused, removed 2018-01-03
       #ntp = 179; # unused
-      #zabbix = 180; # unused
+      zabbix = 180;
       #redis = 181; # unused, removed 2018-01-03
       #unifi = 183; # unused
       #uptimed = 184; # unused
diff --git a/nixpkgs/nixos/modules/misc/nixpkgs.nix b/nixpkgs/nixos/modules/misc/nixpkgs.nix
index e0c192246c0c..afb74581e239 100644
--- a/nixpkgs/nixos/modules/misc/nixpkgs.nix
+++ b/nixpkgs/nixos/modules/misc/nixpkgs.nix
@@ -19,7 +19,7 @@ let
       lhs = optCall lhs_ { inherit pkgs; };
       rhs = optCall rhs_ { inherit pkgs; };
     in
-    lhs // rhs //
+    recursiveUpdate lhs rhs //
     optionalAttrs (lhs ? packageOverrides) {
       packageOverrides = pkgs:
         optCall lhs.packageOverrides pkgs //
diff --git a/nixpkgs/nixos/modules/module-list.nix b/nixpkgs/nixos/modules/module-list.nix
index 1c4cf4da4097..b077deb332c2 100644
--- a/nixpkgs/nixos/modules/module-list.nix
+++ b/nixpkgs/nixos/modules/module-list.nix
@@ -11,6 +11,7 @@
   ./config/xdg/icons.nix
   ./config/xdg/menus.nix
   ./config/xdg/mime.nix
+  ./config/xdg/portal.nix
   ./config/appstream.nix
   ./config/xdg/sounds.nix
   ./config/gtk/gtk-icon-cache.nix
@@ -19,19 +20,20 @@
   ./config/iproute2.nix
   ./config/krb5/default.nix
   ./config/ldap.nix
+  ./config/locale.nix
   ./config/malloc.nix
   ./config/networking.nix
   ./config/no-x-libs.nix
   ./config/nsswitch.nix
   ./config/power-management.nix
   ./config/pulseaudio.nix
+  ./config/resolvconf.nix
   ./config/shells-environment.nix
   ./config/swap.nix
   ./config/sysctl.nix
   ./config/system-environment.nix
   ./config/system-path.nix
   ./config/terminfo.nix
-  ./config/timezone.nix
   ./config/unix-odbc-drivers.nix
   ./config/users-groups.nix
   ./config/vpnc.nix
@@ -104,9 +106,14 @@
   ./programs/digitalbitbox/default.nix
   ./programs/dmrconfig.nix
   ./programs/environment.nix
+  ./programs/evince.nix
+  ./programs/file-roller.nix
   ./programs/firejail.nix
   ./programs/fish.nix
   ./programs/freetds.nix
+  ./programs/gnome-disks.nix
+  ./programs/gnome-documents.nix
+  ./programs/gpaste.nix
   ./programs/gnupg.nix
   ./programs/gphoto2.nix
   ./programs/iftop.nix
@@ -138,6 +145,7 @@
   ./programs/sway.nix
   ./programs/thefuck.nix
   ./programs/tmux.nix
+  ./programs/tsm-client.nix
   ./programs/udevil.nix
   ./programs/venus.nix
   ./programs/vim.nix
@@ -195,6 +203,7 @@
   ./services/audio/slimserver.nix
   ./services/audio/snapserver.nix
   ./services/audio/squeezelite.nix
+  ./services/audio/spotifyd.nix
   ./services/audio/ympd.nix
   ./services/backup/automysqlbackup.nix
   ./services/backup/bacula.nix
@@ -205,10 +214,12 @@
   ./services/backup/duplicity.nix
   ./services/backup/mysql-backup.nix
   ./services/backup/postgresql-backup.nix
+  ./services/backup/postgresql-wal-receiver.nix
   ./services/backup/restic.nix
   ./services/backup/restic-rest-server.nix
   ./services/backup/rsnapshot.nix
   ./services/backup/tarsnap.nix
+  ./services/backup/tsm.nix
   ./services/backup/znapzend.nix
   ./services/cluster/hadoop/default.nix
   ./services/cluster/kubernetes/addons/dns.nix
@@ -275,12 +286,8 @@
   ./services/desktops/pipewire.nix
   ./services/desktops/gnome3/at-spi2-core.nix
   ./services/desktops/gnome3/chrome-gnome-shell.nix
-  ./services/desktops/gnome3/evince.nix
   ./services/desktops/gnome3/evolution-data-server.nix
-  ./services/desktops/gnome3/file-roller.nix
   ./services/desktops/gnome3/glib-networking.nix
-  ./services/desktops/gnome3/gnome-disks.nix
-  ./services/desktops/gnome3/gnome-documents.nix
   ./services/desktops/gnome3/gnome-keyring.nix
   ./services/desktops/gnome3/gnome-online-accounts.nix
   ./services/desktops/gnome3/gnome-remote-desktop.nix
@@ -288,7 +295,6 @@
   ./services/desktops/gnome3/gnome-settings-daemon.nix
   ./services/desktops/gnome3/gnome-terminal-server.nix
   ./services/desktops/gnome3/gnome-user-share.nix
-  ./services/desktops/gnome3/gpaste.nix
   ./services/desktops/gnome3/gvfs.nix
   ./services/desktops/gnome3/rygel.nix
   ./services/desktops/gnome3/seahorse.nix
@@ -398,6 +404,7 @@
   ./services/misc/couchpotato.nix
   ./services/misc/devmon.nix
   ./services/misc/dictd.nix
+  ./services/misc/dwm-status.nix
   ./services/misc/dysnomia.nix
   ./services/misc/disnix.nix
   ./services/misc/docker-registry.nix
@@ -419,6 +426,7 @@
   ./services/misc/gollum.nix
   ./services/misc/gpsd.nix
   ./services/misc/headphones.nix
+  ./services/misc/greenclip.nix
   ./services/misc/home-assistant.nix
   ./services/misc/ihaskell.nix
   ./services/misc/irkerd.nix
@@ -470,6 +478,7 @@
   ./services/misc/synergy.nix
   ./services/misc/sysprof.nix
   ./services/misc/taskserver
+  ./services/misc/tiddlywiki.nix
   ./services/misc/tzupdate.nix
   ./services/misc/uhub.nix
   ./services/misc/weechat.nix
@@ -513,10 +522,12 @@
   ./services/monitoring/systemhealth.nix
   ./services/monitoring/teamviewer.nix
   ./services/monitoring/telegraf.nix
+  ./services/monitoring/thanos.nix
   ./services/monitoring/ups.nix
   ./services/monitoring/uptime.nix
   ./services/monitoring/vnstat.nix
   ./services/monitoring/zabbix-agent.nix
+  ./services/monitoring/zabbix-proxy.nix
   ./services/monitoring/zabbix-server.nix
   ./services/network-filesystems/beegfs.nix
   ./services/network-filesystems/cachefilesd.nix
@@ -686,6 +697,7 @@
   ./services/networking/tcpcrypt.nix
   ./services/networking/teamspeak3.nix
   ./services/networking/tedicross.nix
+  ./services/networking/thelounge.nix
   ./services/networking/tinc.nix
   ./services/networking/tinydns.nix
   ./services/networking/tftpd.nix
@@ -772,6 +784,7 @@
   ./services/web-apps/icingaweb2/module-monitoring.nix
   ./services/web-apps/limesurvey.nix
   ./services/web-apps/mattermost.nix
+  ./services/web-apps/mediawiki.nix
   ./services/web-apps/miniflux.nix
   ./services/web-apps/nextcloud.nix
   ./services/web-apps/nexus.nix
@@ -783,6 +796,7 @@
   ./services/web-apps/virtlyst.nix
   ./services/web-apps/wordpress.nix
   ./services/web-apps/youtrack.nix
+  ./services/web-apps/zabbix.nix
   ./services/web-servers/apache-httpd/default.nix
   ./services/web-servers/caddy.nix
   ./services/web-servers/fcgiwrap.nix
@@ -806,6 +820,8 @@
   ./services/web-servers/uwsgi.nix
   ./services/web-servers/varnish/default.nix
   ./services/web-servers/zope2.nix
+  ./services/x11/extra-layouts.nix
+  ./services/x11/clight.nix
   ./services/x11/colord.nix
   ./services/x11/compton.nix
   ./services/x11/unclutter.nix
diff --git a/nixpkgs/nixos/modules/profiles/hardened.nix b/nixpkgs/nixos/modules/profiles/hardened.nix
index 97279a78a57b..9e9ddd4f3788 100644
--- a/nixpkgs/nixos/modules/profiles/hardened.nix
+++ b/nixpkgs/nixos/modules/profiles/hardened.nix
@@ -26,7 +26,9 @@ with lib;
 
   security.allowSimultaneousMultithreading = mkDefault false;
 
-  security.virtualization.flushL1DataCache = mkDefault "always";
+  security.forcePageTableIsolation = mkDefault true;
+
+  security.virtualisation.flushL1DataCache = mkDefault "always";
 
   security.apparmor.enable = mkDefault true;
 
@@ -42,9 +44,6 @@ with lib;
 
     # Disable legacy virtual syscalls
     "vsyscall=none"
-
-    # Enable PTI even if CPU claims to be safe from meltdown
-    "pti=on"
   ];
 
   boot.blacklistedKernelModules = [
diff --git a/nixpkgs/nixos/modules/services/desktops/gnome3/evince.nix b/nixpkgs/nixos/modules/programs/evince.nix
index 5f040a16f067..473fddb09d02 100644
--- a/nixpkgs/nixos/modules/services/desktops/gnome3/evince.nix
+++ b/nixpkgs/nixos/modules/programs/evince.nix
@@ -6,14 +6,21 @@ with lib;
 
 {
 
+  # Added 2019-08-09
+  imports = [
+    (mkRenamedOptionModule
+      [ "services" "gnome3" "evince" "enable" ]
+      [ "programs" "evince" "enable" ])
+  ];
+
   ###### interface
 
   options = {
 
-    services.gnome3.evince = {
+    programs.evince = {
 
       enable = mkEnableOption
-        "systemd and dbus services for Evince, the GNOME document viewer";
+        "Evince, the GNOME document viewer";
 
     };
 
@@ -22,7 +29,7 @@ with lib;
 
   ###### implementation
 
-  config = mkIf config.services.gnome3.evince.enable {
+  config = mkIf config.programs.evince.enable {
 
     environment.systemPackages = [ pkgs.evince ];
 
diff --git a/nixpkgs/nixos/modules/services/desktops/gnome3/file-roller.nix b/nixpkgs/nixos/modules/programs/file-roller.nix
index 7fb558a98953..64f6a94e7641 100644
--- a/nixpkgs/nixos/modules/services/desktops/gnome3/file-roller.nix
+++ b/nixpkgs/nixos/modules/programs/file-roller.nix
@@ -6,11 +6,18 @@ with lib;
 
 {
 
+  # Added 2019-08-09
+  imports = [
+    (mkRenamedOptionModule
+      [ "services" "gnome3" "file-roller" "enable" ]
+      [ "programs" "file-roller" "enable" ])
+  ];
+
   ###### interface
 
   options = {
 
-    services.gnome3.file-roller = {
+    programs.file-roller = {
 
       enable = mkEnableOption "File Roller, an archive manager for GNOME";
 
@@ -21,7 +28,7 @@ with lib;
 
   ###### implementation
 
-  config = mkIf config.services.gnome3.file-roller.enable {
+  config = mkIf config.programs.file-roller.enable {
 
     environment.systemPackages = [ pkgs.gnome3.file-roller ];
 
diff --git a/nixpkgs/nixos/modules/services/desktops/gnome3/gnome-disks.nix b/nixpkgs/nixos/modules/programs/gnome-disks.nix
index 139534cdb892..1cf839a6ddb0 100644
--- a/nixpkgs/nixos/modules/services/desktops/gnome3/gnome-disks.nix
+++ b/nixpkgs/nixos/modules/programs/gnome-disks.nix
@@ -1,4 +1,4 @@
-# GNOME Disks daemon.
+# GNOME Disks.
 
 { config, pkgs, lib, ... }:
 
@@ -6,17 +6,24 @@ with lib;
 
 {
 
+  # Added 2019-08-09
+  imports = [
+    (mkRenamedOptionModule
+      [ "services" "gnome3" "gnome-disks" "enable" ]
+      [ "programs" "gnome-disks" "enable" ])
+  ];
+
   ###### interface
 
   options = {
 
-    services.gnome3.gnome-disks = {
+    programs.gnome-disks = {
 
       enable = mkOption {
         type = types.bool;
         default = false;
         description = ''
-          Whether to enable GNOME Disks daemon, a service designed to
+          Whether to enable GNOME Disks daemon, a program designed to
           be a UDisks2 graphical front-end.
         '';
       };
@@ -28,7 +35,7 @@ with lib;
 
   ###### implementation
 
-  config = mkIf config.services.gnome3.gnome-disks.enable {
+  config = mkIf config.programs.gnome-disks.enable {
 
     environment.systemPackages = [ pkgs.gnome3.gnome-disk-utility ];
 
diff --git a/nixpkgs/nixos/modules/services/desktops/gnome3/gnome-documents.nix b/nixpkgs/nixos/modules/programs/gnome-documents.nix
index f6efb6684240..bfa3d409ee30 100644
--- a/nixpkgs/nixos/modules/services/desktops/gnome3/gnome-documents.nix
+++ b/nixpkgs/nixos/modules/programs/gnome-documents.nix
@@ -1,4 +1,4 @@
-# GNOME Documents daemon.
+# GNOME Documents.
 
 { config, pkgs, lib, ... }:
 
@@ -6,17 +6,24 @@ with lib;
 
 {
 
+  # Added 2019-08-09
+  imports = [
+    (mkRenamedOptionModule
+      [ "services" "gnome3" "gnome-documents" "enable" ]
+      [ "programs" "gnome-documents" "enable" ])
+  ];
+
   ###### interface
 
   options = {
 
-    services.gnome3.gnome-documents = {
+    programs.gnome-documents = {
 
       enable = mkOption {
         type = types.bool;
         default = false;
         description = ''
-          Whether to enable GNOME Documents services, a document
+          Whether to enable GNOME Documents, a document
           manager application for GNOME.
         '';
       };
@@ -28,7 +35,7 @@ with lib;
 
   ###### implementation
 
-  config = mkIf config.services.gnome3.gnome-documents.enable {
+  config = mkIf config.programs.gnome-documents.enable {
 
     environment.systemPackages = [ pkgs.gnome3.gnome-documents ];
 
diff --git a/nixpkgs/nixos/modules/services/desktops/gnome3/gpaste.nix b/nixpkgs/nixos/modules/programs/gpaste.nix
index 5a8258775e0a..4f6deb77e5eb 100644
--- a/nixpkgs/nixos/modules/services/desktops/gnome3/gpaste.nix
+++ b/nixpkgs/nixos/modules/programs/gpaste.nix
@@ -1,12 +1,20 @@
-# GPaste daemon.
+# GPaste.
 { config, lib, pkgs, ... }:
 
 with lib;
 
 {
+
+  # Added 2019-08-09
+  imports = [
+    (mkRenamedOptionModule
+      [ "services" "gnome3" "gpaste" "enable" ]
+      [ "programs" "gpaste" "enable" ])
+  ];
+
   ###### interface
   options = {
-    services.gnome3.gpaste = {
+     programs.gpaste = {
       enable = mkOption {
         type = types.bool;
         default = false;
@@ -18,10 +26,9 @@ with lib;
   };
 
   ###### implementation
-  config = mkIf config.services.gnome3.gpaste.enable {
+  config = mkIf config.programs.gpaste.enable {
     environment.systemPackages = [ pkgs.gnome3.gpaste ];
     services.dbus.packages = [ pkgs.gnome3.gpaste ];
-    services.xserver.desktopManager.gnome3.sessionPath = [ pkgs.gnome3.gpaste ];
     systemd.packages = [ pkgs.gnome3.gpaste ];
   };
 }
diff --git a/nixpkgs/nixos/modules/programs/nylas-mail.nix b/nixpkgs/nixos/modules/programs/nylas-mail.nix
deleted file mode 100644
index 08a6cd0a6049..000000000000
--- a/nixpkgs/nixos/modules/programs/nylas-mail.nix
+++ /dev/null
@@ -1,36 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.nylas-mail;
-in {
-  ###### interface
-  options = {
-    services.nylas-mail = {
-
-      enable = mkEnableOption ''
-        nylas-mail - Open-source mail client built on the modern web with Electron, React, and Flux
-      '';
-
-      gnome3-keyring = mkOption {
-        type = types.bool;
-        default = true;
-        description = "Enable gnome3 keyring for nylas-mail.";
-      };
-    };
-  };
-
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-
-    environment.systemPackages = [ pkgs.nylas-mail-bin ];
-
-    services.gnome3.gnome-keyring = mkIf cfg.gnome3-keyring {
-      enable = true;
-    };
-
-  };
-}
diff --git a/nixpkgs/nixos/modules/programs/shell.nix b/nixpkgs/nixos/modules/programs/shell.nix
index 9842e2bef643..b7f7b91b5fbe 100644
--- a/nixpkgs/nixos/modules/programs/shell.nix
+++ b/nixpkgs/nixos/modules/programs/shell.nix
@@ -12,7 +12,7 @@ with lib;
       ''
         # Set up the per-user profile.
         mkdir -m 0755 -p "$NIX_USER_PROFILE_DIR"
-        if [ "$(stat --printf '%u' "$NIX_USER_PROFILE_DIR")" != "$(id -u)" ]; then
+        if [ "$(stat -c '%u' "$NIX_USER_PROFILE_DIR")" != "$(id -u)" ]; then
             echo "WARNING: the per-user profile dir $NIX_USER_PROFILE_DIR should belong to user id $(id -u)" >&2
         fi
 
@@ -34,7 +34,7 @@ with lib;
           # Create the per-user garbage collector roots directory.
           NIX_USER_GCROOTS_DIR="/nix/var/nix/gcroots/per-user/$USER"
           mkdir -m 0755 -p "$NIX_USER_GCROOTS_DIR"
-          if [ "$(stat --printf '%u' "$NIX_USER_GCROOTS_DIR")" != "$(id -u)" ]; then
+          if [ "$(stat -c '%u' "$NIX_USER_GCROOTS_DIR")" != "$(id -u)" ]; then
               echo "WARNING: the per-user gcroots dir $NIX_USER_GCROOTS_DIR should belong to user id $(id -u)" >&2
           fi
 
diff --git a/nixpkgs/nixos/modules/programs/ssh.nix b/nixpkgs/nixos/modules/programs/ssh.nix
index 46965dd35b71..733b8f7636fd 100644
--- a/nixpkgs/nixos/modules/programs/ssh.nix
+++ b/nixpkgs/nixos/modules/programs/ssh.nix
@@ -21,7 +21,7 @@ let
 
   knownHostsText = (flip (concatMapStringsSep "\n") knownHosts
     (h: assert h.hostNames != [];
-      concatStringsSep "," h.hostNames + " "
+      optionalString h.certAuthority "@cert-authority " + concatStringsSep "," h.hostNames + " "
       + (if h.publicKey != null then h.publicKey else readFile h.publicKeyFile)
     )) + "\n";
 
@@ -128,6 +128,14 @@ in
         default = {};
         type = types.loaOf (types.submodule ({ name, ... }: {
           options = {
+            certAuthority = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                This public key is an SSH certificate authority, rather than an
+                individual host's key.
+              '';
+            };
             hostNames = mkOption {
               type = types.listOf types.str;
               default = [];
@@ -227,6 +235,7 @@ in
     systemd.user.services.ssh-agent = mkIf cfg.startAgent
       { description = "SSH Agent";
         wantedBy = [ "default.target" ];
+        unitConfig.ConditionUser = "!@system";
         serviceConfig =
           { ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent";
             ExecStart =
diff --git a/nixpkgs/nixos/modules/programs/tsm-client.nix b/nixpkgs/nixos/modules/programs/tsm-client.nix
new file mode 100644
index 000000000000..eb6f12475286
--- /dev/null
+++ b/nixpkgs/nixos/modules/programs/tsm-client.nix
@@ -0,0 +1,287 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  inherit (builtins) length map;
+  inherit (lib.attrsets) attrNames filterAttrs hasAttr mapAttrs mapAttrsToList optionalAttrs;
+  inherit (lib.modules) mkDefault mkIf;
+  inherit (lib.options) literalExample mkEnableOption mkOption;
+  inherit (lib.strings) concatStringsSep optionalString toLower;
+  inherit (lib.types) addCheck attrsOf lines loaOf nullOr package path port str strMatching submodule;
+
+  # Checks if given list of strings contains unique
+  # elements when compared without considering case.
+  # Type: checkIUnique :: [string] -> bool
+  # Example: checkIUnique ["foo" "Foo"] => false
+  checkIUnique = lst:
+    let
+      lenUniq = l: length (lib.lists.unique l);
+    in
+      lenUniq lst == lenUniq (map toLower lst);
+
+  # TSM rejects servername strings longer than 64 chars.
+  servernameType = strMatching ".{1,64}";
+
+  serverOptions = { name, config, ... }: {
+    options.name = mkOption {
+      type = servernameType;
+      example = "mainTsmServer";
+      description = ''
+        Local name of the IBM TSM server,
+        must be uncapitalized and no longer than 64 chars.
+        The value will be used for the
+        <literal>server</literal>
+        directive in <filename>dsm.sys</filename>.
+      '';
+    };
+    options.server = mkOption {
+      type = strMatching ".+";
+      example = "tsmserver.company.com";
+      description = ''
+        Host/domain name or IP address of the IBM TSM server.
+        The value will be used for the
+        <literal>tcpserveraddress</literal>
+        directive in <filename>dsm.sys</filename>.
+      '';
+    };
+    options.port = mkOption {
+      type = addCheck port (p: p<=32767);
+      default = 1500;  # official default
+      description = ''
+        TCP port of the IBM TSM server.
+        The value will be used for the
+        <literal>tcpport</literal>
+        directive in <filename>dsm.sys</filename>.
+        TSM does not support ports above 32767.
+      '';
+    };
+    options.node = mkOption {
+      type = strMatching ".+";
+      example = "MY-TSM-NODE";
+      description = ''
+        Target node name on the IBM TSM server.
+        The value will be used for the
+        <literal>nodename</literal>
+        directive in <filename>dsm.sys</filename>.
+      '';
+    };
+    options.genPasswd = mkEnableOption ''
+      automatic client password generation.
+      This option influences the
+      <literal>passwordaccess</literal>
+      directive in <filename>dsm.sys</filename>.
+      The password will be stored in the directory
+      given by the option <option>passwdDir</option>.
+      <emphasis>Caution</emphasis>:
+      If this option is enabled and the server forces
+      to renew the password (e.g. on first connection),
+      a random password will be generated and stored
+    '';
+    options.passwdDir = mkOption {
+      type = path;
+      example = "/home/alice/tsm-password";
+      description = ''
+        Directory that holds the TSM
+        node's password information.
+        The value will be used for the
+        <literal>passworddir</literal>
+        directive in <filename>dsm.sys</filename>.
+      '';
+    };
+    options.includeExclude = mkOption {
+      type = lines;
+      default = "";
+      example = ''
+        exclude.dir     /nix/store
+        include.encrypt /home/.../*
+      '';
+      description = ''
+        <literal>include.*</literal> and
+        <literal>exclude.*</literal> directives to be
+        used when sending files to the IBM TSM server.
+        The lines will be written into a file that the
+        <literal>inclexcl</literal>
+        directive in <filename>dsm.sys</filename> points to.
+      '';
+    };
+    options.extraConfig = mkOption {
+      # TSM option keys are case insensitive;
+      # we have to ensure there are no keys that
+      # differ only by upper and lower case.
+      type = addCheck
+        (attrsOf (nullOr str))
+        (attrs: checkIUnique (attrNames attrs));
+      default = {};
+      example.compression = "yes";
+      example.passwordaccess = null;
+      description = ''
+        Additional key-value pairs for the server stanza.
+        Values must be strings, or <literal>null</literal>
+        for the key not to be used in the stanza
+        (e.g. to overrule values generated by other options).
+      '';
+    };
+    options.text = mkOption {
+      type = lines;
+      example = literalExample
+        ''lib.modules.mkAfter "compression no"'';
+      description = ''
+        Additional text lines for the server stanza.
+        This option can be used if certion configuration keys
+        must be used multiple times or ordered in a certain way
+        as the <option>extraConfig</option> option can't
+        control the order of lines in the resulting stanza.
+        Note that the <literal>server</literal>
+        line at the beginning of the stanza is
+        not part of this option's value.
+      '';
+    };
+    options.stanza = mkOption {
+      type = str;
+      internal = true;
+      visible = false;
+      description = "Server stanza text generated from the options.";
+    };
+    config.name = mkDefault name;
+    # Client system-options file directives are explained here:
+    # https://www.ibm.com/support/knowledgecenter/SSEQVQ_8.1.8/client/c_opt_usingopts.html
+    config.extraConfig =
+      mapAttrs (lib.trivial.const mkDefault) (
+        {
+          commmethod = "v6tcpip";  # uses v4 or v6, based on dns lookup result
+          tcpserveraddress = config.server;
+          tcpport = builtins.toString config.port;
+          nodename = config.node;
+          passwordaccess = if config.genPasswd then "generate" else "prompt";
+          passworddir = ''"${config.passwdDir}"'';
+        } // optionalAttrs (config.includeExclude!="") {
+          inclexcl = ''"${pkgs.writeText "inclexcl.dsm.sys" config.includeExclude}"'';
+        }
+      );
+    config.text =
+      let
+        attrset = filterAttrs (k: v: v!=null) config.extraConfig;
+        mkLine = k: v: k + optionalString (v!="") "  ${v}";
+        lines = mapAttrsToList mkLine attrset;
+      in
+        concatStringsSep "\n" lines;
+    config.stanza = ''
+      server  ${config.name}
+      ${config.text}
+    '';
+  };
+
+  options.programs.tsmClient = {
+    enable = mkEnableOption ''
+      IBM Spectrum Protect (Tivoli Storage Manager, TSM)
+      client command line applications with a
+      client system-options file "dsm.sys"
+    '';
+    servers = mkOption {
+      type = loaOf (submodule [ serverOptions ]);
+      default = {};
+      example.mainTsmServer = {
+        server = "tsmserver.company.com";
+        node = "MY-TSM-NODE";
+        extraConfig.compression = "yes";
+      };
+      description = ''
+        Server definitions ("stanzas")
+        for the client system-options file.
+      '';
+    };
+    defaultServername = mkOption {
+      type = nullOr servernameType;
+      default = null;
+      example = "mainTsmServer";
+      description = ''
+        If multiple server stanzas are declared with
+        <option>programs.tsmClient.servers</option>,
+        this option may be used to name a default
+        server stanza that IBM TSM uses in the absence of
+        a user-defined <filename>dsm.opt</filename> file.
+        This option translates to a
+        <literal>defaultserver</literal> configuration line.
+      '';
+    };
+    dsmSysText = mkOption {
+      type = lines;
+      readOnly = true;
+      description = ''
+        This configuration key contains the effective text
+        of the client system-options file "dsm.sys".
+        It should not be changed, but may be
+        used to feed the configuration into other
+        TSM-depending packages used on the system.
+      '';
+    };
+    package = mkOption {
+      type = package;
+      default = pkgs.tsm-client;
+      defaultText = "pkgs.tsm-client";
+      example = literalExample "pkgs.tsm-client-withGui";
+      description = ''
+        The TSM client derivation to be
+        added to the system environment.
+        It will called with <literal>.override</literal>
+        to add paths to the client system-options file.
+      '';
+    };
+    wrappedPackage = mkOption {
+      type = package;
+      readOnly = true;
+      description = ''
+        The TSM client derivation, wrapped with the path
+        to the client system-options file "dsm.sys".
+        This option is to provide the effective derivation
+        for other modules that want to call TSM executables.
+      '';
+    };
+  };
+
+  cfg = config.programs.tsmClient;
+
+  assertions = [
+    {
+      assertion = checkIUnique (mapAttrsToList (k: v: v.name) cfg.servers);
+      message = ''
+        TSM servernames contain duplicate name
+        (note that case doesn't matter!)
+      '';
+    }
+    {
+      assertion = (cfg.defaultServername!=null)->(hasAttr cfg.defaultServername cfg.servers);
+      message = "TSM defaultServername not found in list of servers";
+    }
+  ];
+
+  dsmSysText = ''
+    ****  IBM Spectrum Protect (Tivoli Storage Manager)
+    ****  client system-options file "dsm.sys".
+    ****  Do not edit!
+    ****  This file is generated by NixOS configuration.
+
+    ${optionalString (cfg.defaultServername!=null) "defaultserver  ${cfg.defaultServername}"}
+
+    ${concatStringsSep "\n" (mapAttrsToList (k: v: v.stanza) cfg.servers)}
+  '';
+
+in
+
+{
+
+  inherit options;
+
+  config = mkIf cfg.enable {
+    inherit assertions;
+    programs.tsmClient.dsmSysText = dsmSysText;
+    programs.tsmClient.wrappedPackage = cfg.package.override rec {
+      dsmSysCli = pkgs.writeText "dsm.sys" cfg.dsmSysText;
+      dsmSysApi = dsmSysCli;
+    };
+    environment.systemPackages = [ cfg.wrappedPackage ];
+  };
+
+  meta.maintainers = [ lib.maintainers.yarny ];
+
+}
diff --git a/nixpkgs/nixos/modules/programs/xonsh.nix b/nixpkgs/nixos/modules/programs/xonsh.nix
index ceab9b5db931..5cd2a49f8073 100644
--- a/nixpkgs/nixos/modules/programs/xonsh.nix
+++ b/nixpkgs/nixos/modules/programs/xonsh.nix
@@ -26,6 +26,7 @@ in
 
       package = mkOption {
         type = types.package;
+        default = pkgs.xonsh;
         example = literalExample "pkgs.xonsh.override { configFile = \"/path/to/xonshrc\"; }";
         description = ''
           xonsh package to use.
@@ -46,11 +47,11 @@ in
 
     environment.etc."xonshrc".text = cfg.config;
 
-    environment.systemPackages = [ pkgs.xonsh ];
+    environment.systemPackages = [ cfg.package ];
 
     environment.shells =
       [ "/run/current-system/sw/bin/xonsh"
-        "${pkgs.xonsh}/bin/xonsh"
+        "${cfg.package}/bin/xonsh"
       ];
 
   };
diff --git a/nixpkgs/nixos/modules/rename.nix b/nixpkgs/nixos/modules/rename.nix
index 1b77a895d717..7734209973b2 100644
--- a/nixpkgs/nixos/modules/rename.nix
+++ b/nixpkgs/nixos/modules/rename.nix
@@ -19,6 +19,7 @@ with lib;
         let value = getAttrFromPath [ "services" "ddclient" "domain" ] config;
         in if value != "" then [ value ] else []))
     (mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "")
+    (mkRenamedOptionModule [ "services" "flatpak" "extraPortals" ] [ "xdg" "portal" "extraPortals" ])
     (mkRenamedOptionModule [ "services" "i2pd" "extIp" ] [ "services" "i2pd" "address" ])
     (mkRenamedOptionModule [ "services" "kubernetes" "apiserver" "admissionControl" ] [ "services" "kubernetes" "apiserver" "enableAdmissionPlugins" ])
     (mkRenamedOptionModule [ "services" "kubernetes" "apiserver" "address" ] ["services" "kubernetes" "apiserver" "bindAddress"])
@@ -50,6 +51,10 @@ with lib;
     (mkRemovedOptionModule [ "services" "misc" "nzbget" "openFirewall" ] "The port used by nzbget is managed through the web interface so you should adjust your firewall rules accordingly.")
     (mkRemovedOptionModule [ "services" "prometheus" "alertmanager" "user" ] "The alertmanager service is now using systemd's DynamicUser mechanism which obviates a user setting.")
     (mkRemovedOptionModule [ "services" "prometheus" "alertmanager" "group" ] "The alertmanager service is now using systemd's DynamicUser mechanism which obviates a group setting.")
+    (mkRemovedOptionModule [ "services" "prometheus2" "alertmanagerURL" ] ''
+      Due to incompatibility, the alertmanagerURL option has been removed,
+      please use 'services.prometheus2.alertmanagers' instead.
+    '')
     (mkRenamedOptionModule [ "services" "tor" "relay" "portSpec" ] [ "services" "tor" "relay" "port" ])
     (mkRenamedOptionModule [ "services" "vmwareGuest" ] [ "virtualisation" "vmware" "guest" ])
     (mkRenamedOptionModule [ "jobs" ] [ "systemd" "services" ])
@@ -62,6 +67,8 @@ with lib;
     (mkRemovedOptionModule [ "security" "setuidOwners" ] "Use security.wrappers instead")
     (mkRemovedOptionModule [ "security" "setuidPrograms" ] "Use security.wrappers instead")
 
+    (mkRenamedOptionModule [ "security" "virtualization" "flushL1DataCache" ] [ "security" "virtualisation" "flushL1DataCache" ])
+
     # PAM
     (mkRenamedOptionModule [ "security" "pam" "enableU2F" ] [ "security" "pam" "u2f" "enable" ])
 
@@ -171,6 +178,9 @@ with lib;
        The starting time can be configured via <literal>services.postgresqlBackup.startAt</literal>.
     '')
 
+    # zabbixServer
+    (mkRenamedOptionModule [ "services" "zabbixServer" "dbServer" ] [ "services" "zabbixServer" "database" "host" ])
+
     # Profile splitting
     (mkRenamedOptionModule [ "virtualisation" "growPartition" ] [ "boot" "growPartition" ])
 
@@ -214,6 +224,7 @@ with lib;
     (mkRemovedOptionModule [ "services" "winstone" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "services" "mysql" "pidDir" ] "Don't wait for pidfiles, describe dependencies through systemd")
     (mkRemovedOptionModule [ "services" "mysql" "rootPassword" ] "Use socket authentication or set the password outside of the nix store.")
+    (mkRemovedOptionModule [ "services" "zabbixServer" "dbPassword" ] "Use services.zabbixServer.database.passwordFile instead.")
 
     # ZSH
     (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
@@ -244,6 +255,26 @@ with lib;
     # KSM
     (mkRenamedOptionModule [ "hardware" "enableKSM" ] [ "hardware" "ksm" "enable" ])
 
+    # resolvconf
+    (mkRenamedOptionModule [ "networking" "dnsSingleRequest" ] [ "networking" "resolvconf" "dnsSingleRequest" ])
+    (mkRenamedOptionModule [ "networking" "dnsExtensionMechanism" ] [ "networking" "resolvconf" "dnsExtensionMechanism" ])
+    (mkRenamedOptionModule [ "networking" "extraResolvconfConf" ] [ "networking" "resolvconf" "extraConfig" ])
+    (mkRenamedOptionModule [ "networking" "resolvconfOptions" ] [ "networking" "resolvconf" "extraOptions" ])
+
+    # Redshift
+    (mkChangedOptionModule [ "services" "redshift" "latitude" ] [ "location" "latitude" ]
+      (config:
+        let value = getAttrFromPath [ "services" "redshift" "latitude" ] config;
+        in if value == null then
+          throw "services.redshift.latitude is set to null, you can remove this"
+          else builtins.fromJSON value))
+    (mkChangedOptionModule [ "services" "redshift" "longitude" ] [ "location" "longitude" ]
+      (config:
+        let value = getAttrFromPath [ "services" "redshift" "longitude" ] config;
+        in if value == null then
+          throw "services.redshift.longitude is set to null, you can remove this"
+          else builtins.fromJSON value))
+
   ] ++ (flip map [ "blackboxExporter" "collectdExporter" "fritzboxExporter"
                    "jsonExporter" "minioExporter" "nginxExporter" "nodeExporter"
                    "snmpExporter" "unifiExporter" "varnishExporter" ]
diff --git a/nixpkgs/nixos/modules/security/misc.nix b/nixpkgs/nixos/modules/security/misc.nix
index bf474ac0a546..16e3bfb14199 100644
--- a/nixpkgs/nixos/modules/security/misc.nix
+++ b/nixpkgs/nixos/modules/security/misc.nix
@@ -48,13 +48,25 @@ with lib;
         e.g., shared caches).  This attack vector is unproven.
 
         Disabling SMT is a supplement to the L1 data cache flushing mitigation
-        (see <xref linkend="opt-security.virtualization.flushL1DataCache"/>)
+        (see <xref linkend="opt-security.virtualisation.flushL1DataCache"/>)
         versus malicious VM guests (SMT could "bring back" previously flushed
         data).
       '';
     };
 
-    security.virtualization.flushL1DataCache = mkOption {
+    security.forcePageTableIsolation = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to force-enable the Page Table Isolation (PTI) Linux kernel
+        feature even on CPU models that claim to be safe from Meltdown.
+
+        This hardening feature is most beneficial to systems that run untrusted
+        workloads that rely on address space isolation for security.
+      '';
+    };
+
+    security.virtualisation.flushL1DataCache = mkOption {
       type = types.nullOr (types.enum [ "never" "cond" "always" ]);
       default = null;
       description = ''
@@ -114,8 +126,12 @@ with lib;
       boot.kernelParams = [ "nosmt" ];
     })
 
-    (mkIf (config.security.virtualization.flushL1DataCache != null) {
-      boot.kernelParams = [ "kvm-intel.vmentry_l1d_flush=${config.security.virtualization.flushL1DataCache}" ];
+    (mkIf config.security.forcePageTableIsolation {
+      boot.kernelParams = [ "pti=on" ];
+    })
+
+    (mkIf (config.security.virtualisation.flushL1DataCache != null) {
+      boot.kernelParams = [ "kvm-intel.vmentry_l1d_flush=${config.security.virtualisation.flushL1DataCache}" ];
     })
   ];
 }
diff --git a/nixpkgs/nixos/modules/services/audio/spotifyd.nix b/nixpkgs/nixos/modules/services/audio/spotifyd.nix
new file mode 100644
index 000000000000..e3556b2559c2
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/audio/spotifyd.nix
@@ -0,0 +1,42 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.spotifyd;
+  spotifydConf = pkgs.writeText "spotifyd.conf" cfg.config;
+in
+{
+  options = {
+    services.spotifyd = {
+      enable = mkEnableOption "spotifyd, a Spotify playing daemon";
+
+      config = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''
+          Configuration for Spotifyd. For syntax and directives, see
+          https://github.com/Spotifyd/spotifyd#Configuration.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.spotifyd = {
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" "sound.target" ];
+      description = "spotifyd, a Spotify playing daemon";
+      serviceConfig = {
+        ExecStart = "${pkgs.spotifyd}/bin/spotifyd --no-daemon --cache_path /var/cache/spotifyd --config ${spotifydConf}";
+        Restart = "always";
+        RestartSec = 12;
+        DynamicUser = true;
+        CacheDirectory = "spotifyd";
+        SupplementaryGroups = ["audio"];
+      };
+    };
+  };
+
+  meta.maintainers = [ maintainers.anderslundstedt ];
+}
diff --git a/nixpkgs/nixos/modules/services/backup/postgresql-wal-receiver.nix b/nixpkgs/nixos/modules/services/backup/postgresql-wal-receiver.nix
new file mode 100644
index 000000000000..d9a37037992e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/backup/postgresql-wal-receiver.nix
@@ -0,0 +1,203 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  receiverSubmodule = {
+    options = {
+      postgresqlPackage = mkOption {
+        type = types.package;
+        example = literalExample "pkgs.postgresql_11";
+        description = ''
+          PostgreSQL package to use.
+        '';
+      };
+
+      directory = mkOption {
+        type = types.path;
+        example = literalExample "/mnt/pg_wal/main/";
+        description = ''
+          Directory to write the output to.
+        '';
+      };
+
+      statusInterval = mkOption {
+        type = types.int;
+        default = 10;
+        description = ''
+          Specifies the number of seconds between status packets sent back to the server.
+          This allows for easier monitoring of the progress from server.
+          A value of zero disables the periodic status updates completely,
+          although an update will still be sent when requested by the server, to avoid timeout disconnect.
+        '';
+      };
+
+      slot = mkOption {
+        type = types.str;
+        default = "";
+        example = "some_slot_name";
+        description = ''
+          Require <command>pg_receivewal</command> to use an existing replication slot (see
+          <link xlink:href="https://www.postgresql.org/docs/current/warm-standby.html#STREAMING-REPLICATION-SLOTS">Section 26.2.6 of the PostgreSQL manual</link>).
+          When this option is used, <command>pg_receivewal</command> will report a flush position to the server,
+          indicating when each segment has been synchronized to disk so that the server can remove that segment if it is not otherwise needed.
+
+          When the replication client of <command>pg_receivewal</command> is configured on the server as a synchronous standby,
+          then using a replication slot will report the flush position to the server, but only when a WAL file is closed.
+          Therefore, that configuration will cause transactions on the primary to wait for a long time and effectively not work satisfactorily.
+          The option <option>synchronous</option> must be specified in addition to make this work correctly.
+        '';
+      };
+
+      synchronous = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Flush the WAL data to disk immediately after it has been received.
+          Also send a status packet back to the server immediately after flushing, regardless of <option>statusInterval</option>.
+
+          This option should be specified if the replication client of <command>pg_receivewal</command> is configured on the server as a synchronous standby,
+          to ensure that timely feedback is sent to the server.
+        '';
+      };
+
+      compress = mkOption {
+        type = types.ints.between 0 9;
+        default = 0;
+        description = ''
+          Enables gzip compression of write-ahead logs, and specifies the compression level
+          (<literal>0</literal> through <literal>9</literal>, <literal>0</literal> being no compression and <literal>9</literal> being best compression).
+          The suffix <literal>.gz</literal> will automatically be added to all filenames.
+
+          This option requires PostgreSQL >= 10.
+        '';
+      };
+
+      connection = mkOption {
+        type = types.str;
+        example = "postgresql://user@somehost";
+        description = ''
+          Specifies parameters used to connect to the server, as a connection string.
+          See <link xlink:href="https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING">Section 34.1.1 of the PostgreSQL manual</link> for more information.
+
+          Because <command>pg_receivewal</command> doesn't connect to any particular database in the cluster,
+          database name in the connection string will be ignored.
+        '';
+      };
+
+      extraArgs = mkOption {
+        type = with types; listOf str;
+        default = [ ];
+        example = literalExample ''
+          [
+            "--no-sync"
+          ]
+        '';
+        description = ''
+          A list of extra arguments to pass to the <command>pg_receivewal</command> command.
+        '';
+      };
+
+      environment = mkOption {
+        type = with types; attrsOf str;
+        default = { };
+        example = literalExample ''
+          {
+            PGPASSFILE = "/private/passfile";
+            PGSSLMODE = "require";
+          }
+        '';
+        description = ''
+          Environment variables passed to the service.
+          Usable parameters are listed in <link xlink:href="https://www.postgresql.org/docs/current/libpq-envars.html">Section 34.14 of the PostgreSQL manual</link>.
+        '';
+      };
+    };
+  };
+
+in {
+  options = {
+    services.postgresqlWalReceiver = {
+      receivers = mkOption {
+        type = with types; attrsOf (submodule receiverSubmodule);
+        default = { };
+        example = literalExample ''
+          {
+            main = {
+              postgresqlPackage = pkgs.postgresql_11;
+              directory = /mnt/pg_wal/main/;
+              slot = "main_wal_receiver";
+              connection = "postgresql://user@somehost";
+            };
+          }
+        '';
+        description = ''
+          PostgreSQL WAL receivers.
+          Stream write-ahead logs from a PostgreSQL server using <command>pg_receivewal</command> (formerly <command>pg_receivexlog</command>).
+          See <link xlink:href="https://www.postgresql.org/docs/current/app-pgreceivewal.html">the man page</link> for more information.
+        '';
+      };
+    };
+  };
+
+  config = let
+    receivers = config.services.postgresqlWalReceiver.receivers;
+  in mkIf (receivers != { }) {
+    users = {
+      users.postgres = {
+        uid = config.ids.uids.postgres;
+        group = "postgres";
+        description = "PostgreSQL server user";
+      };
+
+      groups.postgres = {
+        gid = config.ids.gids.postgres;
+      };
+    };
+
+    assertions = concatLists (attrsets.mapAttrsToList (name: config: [
+      {
+        assertion = config.compress > 0 -> versionAtLeast config.postgresqlPackage.version "10";
+        message = "Invalid configuration for WAL receiver \"${name}\": compress requires PostgreSQL version >= 10.";
+      }
+    ]) receivers);
+
+    systemd.tmpfiles.rules = mapAttrsToList (name: config: ''
+      d ${escapeShellArg config.directory} 0750 postgres postgres - -
+    '') receivers;
+
+    systemd.services = with attrsets; mapAttrs' (name: config: nameValuePair "postgresql-wal-receiver-${name}" {
+      description = "PostgreSQL WAL receiver (${name})";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User = "postgres";
+        Group = "postgres";
+        KillSignal = "SIGINT";
+        Restart = "always";
+        RestartSec = 30;
+      };
+
+      inherit (config) environment;
+
+      script = let
+        receiverCommand = postgresqlPackage:
+         if (versionAtLeast postgresqlPackage.version "10")
+           then "${postgresqlPackage}/bin/pg_receivewal"
+           else "${postgresqlPackage}/bin/pg_receivexlog";
+      in ''
+        ${receiverCommand config.postgresqlPackage} \
+          --no-password \
+          --directory=${escapeShellArg config.directory} \
+          --status-interval=${toString config.statusInterval} \
+          --dbname=${escapeShellArg config.connection} \
+          ${optionalString (config.compress > 0) "--compress=${toString config.compress}"} \
+          ${optionalString (config.slot != "") "--slot=${escapeShellArg config.slot}"} \
+          ${optionalString config.synchronous "--synchronous"} \
+          ${concatStringsSep " " config.extraArgs}
+      '';
+    }) receivers;
+  };
+
+  meta.maintainers = with maintainers; [ pacien ];
+}
diff --git a/nixpkgs/nixos/modules/services/backup/tsm.nix b/nixpkgs/nixos/modules/services/backup/tsm.nix
new file mode 100644
index 000000000000..3b2bb37491b5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/backup/tsm.nix
@@ -0,0 +1,106 @@
+{ config, lib, ... }:
+
+let
+
+  inherit (lib.attrsets) hasAttr;
+  inherit (lib.modules) mkDefault mkIf;
+  inherit (lib.options) mkEnableOption mkOption;
+  inherit (lib.types) nullOr strMatching;
+
+  options.services.tsmBackup = {
+    enable = mkEnableOption ''
+      automatic backups with the
+      IBM Spectrum Protect (Tivoli Storage Manager, TSM) client.
+      This also enables
+      <option>programs.tsmClient.enable</option>
+    '';
+    command = mkOption {
+      type = strMatching ".+";
+      default = "backup";
+      example = "incr";
+      description = ''
+        The actual command passed to the
+        <literal>dsmc</literal> executable to start the backup.
+      '';
+    };
+    servername = mkOption {
+      type = strMatching ".+";
+      example = "mainTsmServer";
+      description = ''
+        Create a systemd system service
+        <literal>tsm-backup.service</literal> that starts
+        a backup based on the given servername's stanza.
+        Note that this server's
+        <option>passwdDir</option> will default to
+        <filename>/var/lib/tsm-backup/password</filename>
+        (but may be overridden);
+        also, the service will use
+        <filename>/var/lib/tsm-backup</filename> as
+        <literal>HOME</literal> when calling
+        <literal>dsmc</literal>.
+      '';
+    };
+    autoTime = mkOption {
+      type = nullOr (strMatching ".+");
+      default = null;
+      example = "12:00";
+      description = ''
+        The backup service will be invoked
+        automatically at the given date/time,
+        which must be in the format described in
+        <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+        The default <literal>null</literal>
+        disables automatic backups.
+      '';
+    };
+  };
+
+  cfg = config.services.tsmBackup;
+  cfgPrg = config.programs.tsmClient;
+
+  assertions = [
+    {
+      assertion = hasAttr cfg.servername cfgPrg.servers;
+      message = "TSM service servername not found in list of servers";
+    }
+    {
+      assertion = cfgPrg.servers.${cfg.servername}.genPasswd;
+      message = "TSM service requires automatic password generation";
+    }
+  ];
+
+in
+
+{
+
+  inherit options;
+
+  config = mkIf cfg.enable {
+    inherit assertions;
+    programs.tsmClient.enable = true;
+    programs.tsmClient.servers."${cfg.servername}".passwdDir =
+      mkDefault "/var/lib/tsm-backup/password";
+    systemd.services.tsm-backup = {
+      description = "IBM Spectrum Protect (Tivoli Storage Manager) Backup";
+      # DSM_LOG needs a trailing slash to have it treated as a directory.
+      # `/var/log` would be littered with TSM log files otherwise.
+      environment.DSM_LOG = "/var/log/tsm-backup/";
+      # TSM needs a HOME dir to store certificates.
+      environment.HOME = "/var/lib/tsm-backup";
+      # for exit status description see
+      # https://www.ibm.com/support/knowledgecenter/en/SSEQVQ_8.1.8/client/c_sched_rtncode.html
+      serviceConfig.SuccessExitStatus = "4 8";
+      # The `-se` option must come after the command.
+      # The `-optfile` option suppresses a `dsm.opt`-not-found warning.
+      serviceConfig.ExecStart =
+        "${cfgPrg.wrappedPackage}/bin/dsmc ${cfg.command} -se='${cfg.servername}' -optfile=/dev/null";
+      serviceConfig.LogsDirectory = "tsm-backup";
+      serviceConfig.StateDirectory = "tsm-backup";
+      serviceConfig.StateDirectoryMode = "0750";
+      startAt = mkIf (cfg.autoTime!=null) cfg.autoTime;
+    };
+  };
+
+  meta.maintainers = [ lib.maintainers.yarny ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/backup/zfs-replication.nix b/nixpkgs/nixos/modules/services/backup/zfs-replication.nix
new file mode 100644
index 000000000000..785cedb98694
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/backup/zfs-replication.nix
@@ -0,0 +1,90 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+let
+  cfg = config.services.zfs.autoReplication;
+  recursive = optionalString cfg.recursive " --recursive";
+  followDelete = optionalString cfg.followDelete " --follow-delete";
+in {
+  options = {
+    services.zfs.autoReplication = {
+      enable = mkEnableOption "ZFS snapshot replication.";
+
+      followDelete = mkOption {
+        description = "Remove remote snapshots that don't have a local correspondant.";
+        default = true;
+        type = types.bool;
+      };
+
+      host = mkOption {
+        description = "Remote host where snapshots should be sent.";
+        example = "example.com";
+        type = types.str;
+      };
+
+      identityFilePath = mkOption {
+        description = "Path to SSH key used to login to host.";
+        example = "/home/username/.ssh/id_rsa";
+        type = types.path;
+      };
+
+      localFilesystem = mkOption {
+        description = "Local ZFS fileystem from which snapshots should be sent.  Defaults to the attribute name.";
+        example = "pool/file/path";
+        type = types.str;
+      };
+
+      remoteFilesystem = mkOption {
+        description = "Remote ZFS filesystem where snapshots should be sent.";
+        example = "pool/file/path";
+        type = types.str;
+      };
+
+      recursive = mkOption {
+        description = "Recursively discover snapshots to send.";
+        default = true;
+        type = types.bool;
+      };
+
+      username = mkOption {
+        description = "Username used by SSH to login to remote host.";
+        example = "username";
+        type = types.str;
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [
+      pkgs.lz4
+    ];
+
+    systemd.services."zfs-replication" = {
+      after = [
+        "zfs-snapshot-daily.service"
+        "zfs-snapshot-frequent.service"
+        "zfs-snapshot-hourly.service"
+        "zfs-snapshot-monthly.service"
+        "zfs-snapshot-weekly.service"
+      ];
+      description = "ZFS Snapshot Replication";
+      documentation = [
+        "https://github.com/alunduil/zfs-replicate"
+      ];
+      restartIfChanged = false;
+      serviceConfig.ExecStart = "${pkgs.zfs-replicate}/bin/zfs-replicate${recursive} -l ${escapeShellArg cfg.username} -i ${escapeShellArg cfg.identityFilePath}${followDelete} ${escapeShellArg cfg.host} ${escapeShellArg cfg.remoteFilesystem} ${escapeShellArg cfg.localFilesystem}";
+      wantedBy = [
+        "zfs-snapshot-daily.service"
+        "zfs-snapshot-frequent.service"
+        "zfs-snapshot-hourly.service"
+        "zfs-snapshot-monthly.service"
+        "zfs-snapshot-weekly.service"
+      ];
+    };
+  };
+
+  meta = {
+    maintainers = with lib.maintainers; [ alunduil ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/databases/couchdb.nix b/nixpkgs/nixos/modules/services/databases/couchdb.nix
index 5ddf8ba4bfbd..77e404116c8a 100644
--- a/nixpkgs/nixos/modules/services/databases/couchdb.nix
+++ b/nixpkgs/nixos/modules/services/databases/couchdb.nix
@@ -160,7 +160,7 @@ in {
 
     systemd.tmpfiles.rules = [
       "d '${dirOf cfg.uriFile}' - ${cfg.user} ${cfg.group} - -"
-      "d '${dirOf cfg.logFile}' - ${cfg.user} ${cfg.group} - -"
+      "f '${cfg.logFile}' - ${cfg.user} ${cfg.group} - -"
       "d '${cfg.databaseDir}' -  ${cfg.user} ${cfg.group} - -"
       "d '${cfg.viewIndexDir}' -  ${cfg.user} ${cfg.group} - -"
     ];
@@ -169,11 +169,9 @@ in {
       description = "CouchDB Server";
       wantedBy = [ "multi-user.target" ];
 
-      preStart =
-        ''
+      preStart = ''
         touch ${cfg.configFile}
-        touch -a ${cfg.logFile}
-        '';
+      '';
 
       environment = mkIf useVersion2 {
         # we are actually specifying 4 configuration files:
diff --git a/nixpkgs/nixos/modules/services/databases/openldap.nix b/nixpkgs/nixos/modules/services/databases/openldap.nix
index c2f458c03794..d8e2c715afb9 100644
--- a/nixpkgs/nixos/modules/services/databases/openldap.nix
+++ b/nixpkgs/nixos/modules/services/databases/openldap.nix
@@ -237,8 +237,8 @@ in
   config = mkIf cfg.enable {
     assertions = [
       {
-        assertion = cfg.rootpwFile != null || cfg.rootpw != null;
-        message = "Either services.openldap.rootpw or services.openldap.rootpwFile must be set";
+        assertion = cfg.configDir != null || cfg.rootpwFile != null || cfg.rootpw != null;
+        message = "services.openldap: Unless configDir is set, either rootpw or rootpwFile must be set";
       }
     ];
 
diff --git a/nixpkgs/nixos/modules/services/databases/postgresql.nix b/nixpkgs/nixos/modules/services/databases/postgresql.nix
index 5661edbee2db..10250bb5193a 100644
--- a/nixpkgs/nixos/modules/services/databases/postgresql.nix
+++ b/nixpkgs/nixos/modules/services/databases/postgresql.nix
@@ -6,23 +6,10 @@ let
 
   cfg = config.services.postgresql;
 
-  # see description of extraPlugins
-  postgresqlAndPlugins = pg:
-    if cfg.extraPlugins == [] then pg
-    else pkgs.buildEnv {
-      name = "postgresql-and-plugins-${(builtins.parseDrvName pg.name).version}";
-      paths = [ pg pg.lib ] ++ cfg.extraPlugins;
-      buildInputs = [ pkgs.makeWrapper ];
-      postBuild =
-        ''
-          mkdir -p $out/bin
-          rm $out/bin/{pg_config,postgres,pg_ctl}
-          cp --target-directory=$out/bin ${pg}/bin/{postgres,pg_config,pg_ctl}
-          wrapProgram $out/bin/postgres --set NIX_PGLIBDIR $out/lib
-        '';
-    };
-
-  postgresql = postgresqlAndPlugins cfg.package;
+  postgresql =
+    if cfg.extraPlugins == []
+      then cfg.package
+      else cfg.package.withPackages (_: cfg.extraPlugins);
 
   # The main PostgreSQL configuration file.
   configFile = pkgs.writeText "postgresql.conf"
@@ -55,7 +42,7 @@ in
 
       package = mkOption {
         type = types.package;
-        example = literalExample "pkgs.postgresql_9_6";
+        example = literalExample "pkgs.postgresql_11";
         description = ''
           PostgreSQL package to use.
         '';
@@ -71,7 +58,7 @@ in
 
       dataDir = mkOption {
         type = types.path;
-        example = "/var/lib/postgresql/9.6";
+        example = "/var/lib/postgresql/11";
         description = ''
           Data directory for PostgreSQL.
         '';
@@ -192,17 +179,11 @@ in
       extraPlugins = mkOption {
         type = types.listOf types.path;
         default = [];
-        example = literalExample "[ (pkgs.postgis.override { postgresql = pkgs.postgresql_9_4; }) ]";
+        example = literalExample "with pkgs.postgresql_11.pkgs; [ postgis pg_repack ]";
         description = ''
-          When this list contains elements a new store path is created.
-          PostgreSQL and the elements are symlinked into it. Then pg_config,
-          postgres and pg_ctl are copied to make them use the new
-          $out/lib directory as pkglibdir. This makes it possible to use postgis
-          without patching the .sql files which reference $libdir/postgis-1.5.
+          List of PostgreSQL plugins. PostgreSQL version for each plugin should
+          match version for <literal>services.postgresql.package</literal> value.
         '';
-        # Note: the duplication of executables is about 4MB size.
-        # So a nicer solution was patching postgresql to allow setting the
-        # libdir explicitely.
       };
 
       extraConfig = mkOption {
@@ -270,6 +251,10 @@ in
 
     environment.systemPackages = [ postgresql ];
 
+    environment.pathsToLink = [
+     "/share/postgresql"
+    ];
+
     systemd.services.postgresql =
       { description = "PostgreSQL Server";
 
@@ -345,13 +330,13 @@ in
             fi
           '' + optionalString (cfg.ensureDatabases != []) ''
             ${concatMapStrings (database: ''
-              $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${database}'" | grep -q 1 || $PSQL -tAc "CREATE DATABASE ${database}"
+              $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${database}'" | grep -q 1 || $PSQL -tAc 'CREATE DATABASE "${database}"'
             '') cfg.ensureDatabases}
           '' + ''
             ${concatMapStrings (user: ''
               $PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc "CREATE USER ${user.name}"
               ${concatStringsSep "\n" (mapAttrsToList (database: permission: ''
-                $PSQL -tAc "GRANT ${permission} ON ${database} TO ${user.name}"
+                $PSQL -tAc 'GRANT ${permission} ON ${database} TO ${user.name}'
               '') user.ensurePermissions)}
             '') cfg.ensureUsers}
           '';
diff --git a/nixpkgs/nixos/modules/services/databases/postgresql.xml b/nixpkgs/nixos/modules/services/databases/postgresql.xml
index 00bb02dcc5bf..72d4a8249a32 100644
--- a/nixpkgs/nixos/modules/services/databases/postgresql.xml
+++ b/nixpkgs/nixos/modules/services/databases/postgresql.xml
@@ -27,10 +27,10 @@
    <filename>configuration.nix</filename>:
 <programlisting>
 <xref linkend="opt-services.postgresql.enable"/> = true;
-<xref linkend="opt-services.postgresql.package"/> = pkgs.postgresql_9_4;
+<xref linkend="opt-services.postgresql.package"/> = pkgs.postgresql_11;
 </programlisting>
    Note that you are required to specify the desired version of PostgreSQL
-   (e.g. <literal>pkgs.postgresql_9_4</literal>). Since upgrading your
+   (e.g. <literal>pkgs.postgresql_11</literal>). Since upgrading your
    PostgreSQL version requires a database dump and reload (see below), NixOS
    cannot provide a default value for
    <xref linkend="opt-services.postgresql.package"/> such as the most recent
@@ -52,7 +52,7 @@ Type "help" for help.
 
   <para>
    By default, PostgreSQL stores its databases in
-   <filename>/var/db/postgresql</filename>. You can override this using
+   <filename>/var/lib/postgresql/$psqlSchema</filename>. You can override this using
    <xref linkend="opt-services.postgresql.dataDir"/>, e.g.
 <programlisting>
 <xref linkend="opt-services.postgresql.dataDir"/> = "/data/postgresql";
@@ -74,4 +74,70 @@ Type "help" for help.
    <link linkend="opt-services.postgresql.enable">here</link>.
   </para>
  </section>
+ <section xml:id="module-services-postgres-plugins">
+  <title>Plugins</title>
+
+  <para>
+   Plugins collection for each PostgreSQL version can be accessed with
+   <literal>.pkgs</literal>. For example, for
+   <literal>pkgs.postgresql_11</literal> package, its plugin collection is
+   accessed by <literal>pkgs.postgresql_11.pkgs</literal>:
+<screen>
+<prompt>$ </prompt>nix repl '&lt;nixpkgs&gt;'
+
+Loading '&lt;nixpkgs&gt;'...
+Added 10574 variables.
+
+<prompt>nix-repl&gt; </prompt>postgresql_11.pkgs.&lt;TAB&gt;&lt;TAB&gt;
+postgresql_11.pkgs.cstore_fdw        postgresql_11.pkgs.pg_repack
+postgresql_11.pkgs.pg_auto_failover  postgresql_11.pkgs.pg_safeupdate
+postgresql_11.pkgs.pg_bigm           postgresql_11.pkgs.pg_similarity
+postgresql_11.pkgs.pg_cron           postgresql_11.pkgs.pg_topn
+postgresql_11.pkgs.pg_hll            postgresql_11.pkgs.pgjwt
+postgresql_11.pkgs.pg_partman        postgresql_11.pkgs.pgroonga
+...
+</screen>
+  </para>
+  <para>
+    To add plugins via NixOS configuration, set <literal>services.postgresql.extraPlugins</literal>:
+<programlisting>
+<xref linkend="opt-services.postgresql.package"/> = pkgs.postgresql_11;
+<xref linkend="opt-services.postgresql.extraPlugins"/> = with pkgs.postgresql_11.pkgs; [
+  pg_repack
+  postgis
+];
+</programlisting>
+  </para>
+  <para>
+   You can build custom PostgreSQL-with-plugins (to be used outside of NixOS) using
+   function <literal>.withPackages</literal>. For example, creating a custom
+   PostgreSQL package in an overlay can look like:
+<programlisting>
+self: super: {
+  postgresql_custom = self.postgresql_11.withPackages (ps: [
+    ps.pg_repack
+    ps.postgis
+  ]);
+}
+</programlisting>
+  </para>
+  <para>
+    Here's a recipe on how to override a particular plugin through an overlay:
+<programlisting>
+self: super: {
+  postgresql_11 = super.postgresql_11.override { this = self.postgresql_11; } // {
+    pkgs = super.postgresql_11.pkgs // {
+      pg_repack = super.postgresql_11.pkgs.pg_repack.overrideAttrs (_: {
+        name = "pg_repack-v20181024";
+        src = self.fetchzip {
+          url = "https://github.com/reorg/pg_repack/archive/923fa2f3c709a506e111cc963034bf2fd127aa00.tar.gz";
+          sha256 = "17k6hq9xaax87yz79j773qyigm4fwk8z4zh5cyp6z0sxnwfqxxw5";
+        };
+      });
+    };
+  };
+}
+</programlisting>
+  </para>
+ </section>
 </chapter>
diff --git a/nixpkgs/nixos/modules/services/desktops/flatpak.nix b/nixpkgs/nixos/modules/services/desktops/flatpak.nix
index cfca1893bd82..1492d855aa03 100644
--- a/nixpkgs/nixos/modules/services/desktops/flatpak.nix
+++ b/nixpkgs/nixos/modules/services/desktops/flatpak.nix
@@ -15,38 +15,28 @@ in {
   options = {
     services.flatpak = {
       enable = mkEnableOption "flatpak";
-
-      extraPortals = mkOption {
-        type = types.listOf types.package;
-        default = [];
-        description = ''
-          List of additional portals to add to path. Portals allow interaction
-          with system, like choosing files or taking screenshots. At minimum,
-          a desktop portal implementation should be listed. GNOME already
-          adds <package>xdg-desktop-portal-gtk</package>; for KDE, there
-          is <package>xdg-desktop-portal-kde</package>. Other desktop
-          environments will probably want to do the same.
-        '';
-      };
     };
   };
 
 
   ###### implementation
   config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = (config.xdg.portal.enable == true);
+        message = "To use Flatpak you must enable XDG Desktop Portals with xdg.portal.enable.";
+      }
+    ];
+
     environment.systemPackages = [ pkgs.flatpak ];
 
-    services.dbus.packages = [ pkgs.flatpak pkgs.xdg-desktop-portal ] ++ cfg.extraPortals;
+    services.dbus.packages = [ pkgs.flatpak ];
 
-    systemd.packages = [ pkgs.flatpak pkgs.xdg-desktop-portal ] ++ cfg.extraPortals;
+    systemd.packages = [ pkgs.flatpak ];
 
     environment.profiles = [
       "$HOME/.local/share/flatpak/exports"
       "/var/lib/flatpak/exports"
     ];
-
-    environment.variables = {
-      XDG_DESKTOP_PORTAL_PATH = map (p: "${p}/share/xdg-desktop-portal/portals") cfg.extraPortals;
-    };
   };
 }
diff --git a/nixpkgs/nixos/modules/services/desktops/flatpak.xml b/nixpkgs/nixos/modules/services/desktops/flatpak.xml
index fb27bd1f62b2..8f080b250228 100644
--- a/nixpkgs/nixos/modules/services/desktops/flatpak.xml
+++ b/nixpkgs/nixos/modules/services/desktops/flatpak.xml
@@ -29,7 +29,7 @@
   in other cases, you will need to add something like the following to your
   <filename>configuration.nix</filename>:
 <programlisting>
-  <xref linkend="opt-services.flatpak.extraPortals"/> = [ pkgs.xdg-desktop-portal-gtk ];
+  <xref linkend="opt-xdg.portal.extraPortals"/> = [ pkgs.xdg-desktop-portal-gtk ];
 </programlisting>
  </para>
  <para>
diff --git a/nixpkgs/nixos/modules/services/desktops/tumbler.nix b/nixpkgs/nixos/modules/services/desktops/tumbler.nix
index ccbb6d1434d9..d18088d4634b 100644
--- a/nixpkgs/nixos/modules/services/desktops/tumbler.nix
+++ b/nixpkgs/nixos/modules/services/desktops/tumbler.nix
@@ -23,7 +23,7 @@ in
 
       package = mkOption {
         type = types.package;
-        default = pkgs.xfce4-13.tumbler;
+        default = pkgs.xfce4-14.tumbler;
         description = "Which tumbler package to use";
         example = pkgs.xfce4-12.tumbler;
       };
diff --git a/nixpkgs/nixos/modules/services/development/bloop.nix b/nixpkgs/nixos/modules/services/development/bloop.nix
index 56904b7c40e6..226718a9e80a 100644
--- a/nixpkgs/nixos/modules/services/development/bloop.nix
+++ b/nixpkgs/nixos/modules/services/development/bloop.nix
@@ -9,6 +9,20 @@ let
 in {
 
   options.services.bloop = {
+    extraOptions = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      example = [
+        "-J-Xmx2G"
+        "-J-XX:MaxInlineLevel=20"
+        "-J-XX:+UseParallelGC"
+      ];
+      description = ''
+        Specifies additional command line argument to pass to bloop
+        java process.
+      '';
+    };
+
     install = mkOption {
       type = types.bool;
       default = false;
@@ -25,10 +39,13 @@ in {
     systemd.user.services.bloop = {
       description = "Bloop Scala build server";
 
+      environment = {
+        PATH = mkForce "${makeBinPath [ config.programs.java.package ]}";
+      };
       serviceConfig = {
-        Type      = "simple";
-        ExecStart = ''${pkgs.bloop}/bin/blp-server'';
-        Restart   = "always";
+        Type        = "simple";
+        ExecStart   = ''${pkgs.bloop}/bin/bloop server'';
+        Restart     = "always";
       };
     };
 
diff --git a/nixpkgs/nixos/modules/services/editors/emacs.xml b/nixpkgs/nixos/modules/services/editors/emacs.xml
index 88d7c4e1daf0..a3041ae22e78 100644
--- a/nixpkgs/nixos/modules/services/editors/emacs.xml
+++ b/nixpkgs/nixos/modules/services/editors/emacs.xml
@@ -95,7 +95,8 @@
     also available in Nixpkgs:
     <link xlink:href="https://www.gnu.org/software/zile/">Zile</link>,
     <link xlink:href="http://homepage.boetes.org/software/mg/">mg</link>,
-    <link xlink:href="http://yi-editor.github.io/">Yi</link>.
+    <link xlink:href="http://yi-editor.github.io/">Yi</link>,
+    <link xlink:href="https://joe-editor.sourceforge.io/">jmacs</link>.
    </para>
   </section>
 
diff --git a/nixpkgs/nixos/modules/services/hardware/throttled.nix b/nixpkgs/nixos/modules/services/hardware/throttled.nix
index cd5b01450e44..13fc5e4792e6 100644
--- a/nixpkgs/nixos/modules/services/hardware/throttled.nix
+++ b/nixpkgs/nixos/modules/services/hardware/throttled.nix
@@ -8,6 +8,12 @@ in {
   options = {
     services.throttled = {
       enable = mkEnableOption "fix for Intel CPU throttling";
+
+      extraConfig = mkOption {
+        type = types.str;
+        default = "";
+        description = "Alternative configuration";
+      };
     };
   };
 
@@ -16,6 +22,9 @@ in {
     # The upstream package has this in Install, but that's not enough, see the NixOS manual
     systemd.services."lenovo_fix".wantedBy = [ "multi-user.target" ];
 
-    environment.etc."lenovo_fix.conf".source = "${pkgs.throttled}/etc/lenovo_fix.conf";
+    environment.etc."lenovo_fix.conf".source =
+      if cfg.extraConfig != ""
+      then pkgs.writeText "lenovo_fix.conf" cfg.extraConfig
+      else "${pkgs.throttled}/etc/lenovo_fix.conf";
   };
 }
diff --git a/nixpkgs/nixos/modules/services/logging/graylog.nix b/nixpkgs/nixos/modules/services/logging/graylog.nix
index c8c4a9ff06db..a889a44d4b2b 100644
--- a/nixpkgs/nixos/modules/services/logging/graylog.nix
+++ b/nixpkgs/nixos/modules/services/logging/graylog.nix
@@ -150,6 +150,9 @@ in
         rm -rf /var/lib/graylog/plugins || true
         mkdir -p /var/lib/graylog/plugins -m 755
 
+        mkdir -p "$(dirname ${cfg.nodeIdFile})"
+        chown -R ${cfg.user} "$(dirname ${cfg.nodeIdFile})"
+
         for declarativeplugin in `ls ${glPlugins}/bin/`; do
           ln -sf ${glPlugins}/bin/$declarativeplugin /var/lib/graylog/plugins/$declarativeplugin
         done
diff --git a/nixpkgs/nixos/modules/services/mail/postfix.nix b/nixpkgs/nixos/modules/services/mail/postfix.nix
index d43733484ffa..dab1b29aa4be 100644
--- a/nixpkgs/nixos/modules/services/mail/postfix.nix
+++ b/nixpkgs/nixos/modules/services/mail/postfix.nix
@@ -13,6 +13,7 @@ let
                       || cfg.extraAliases != "";
   haveTransport = cfg.transport != "";
   haveVirtual = cfg.virtual != "";
+  haveLocalRecipients = cfg.localRecipients != null;
 
   clientAccess =
     optional (cfg.dnsBlacklistOverrides != "")
@@ -244,6 +245,7 @@ let
 
   aliasesFile = pkgs.writeText "postfix-aliases" aliases;
   virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual;
+  localRecipientMapFile = pkgs.writeText "postfix-local-recipient-map" (concatMapStrings (x: x + " ACCEPT\n") cfg.localRecipients);
   checkClientAccessFile = pkgs.writeText "postfix-check-client-access" cfg.dnsBlacklistOverrides;
   mainCfFile = pkgs.writeText "postfix-main.cf" mainCf;
   masterCfFile = pkgs.writeText "postfix-master.cf" masterCfContent;
@@ -506,6 +508,19 @@ in
         '';
       };
 
+      localRecipients = mkOption {
+        type = with types; nullOr (listOf string);
+        default = null;
+        description = ''
+          List of accepted local users. Specify a bare username, an
+          <literal>"@domain.tld"</literal> wild-card, or a complete
+          <literal>"user@domain.tld"</literal> address. If set, these names end
+          up in the local recipient map -- see the local(8) man-page -- and
+          effectively replace the system user database lookup that's otherwise
+          used by default.
+        '';
+      };
+
       transport = mkOption {
         default = "";
         description = "
@@ -742,6 +757,7 @@ in
       // optionalAttrs haveAliases { alias_maps = [ "${cfg.aliasMapType}:/etc/postfix/aliases" ]; }
       // optionalAttrs haveTransport { transport_maps = [ "hash:/etc/postfix/transport" ]; }
       // optionalAttrs haveVirtual { virtual_alias_maps = [ "${cfg.virtualMapType}:/etc/postfix/virtual" ]; }
+      // optionalAttrs haveLocalRecipients { local_recipient_maps = [ "hash:/etc/postfix/local_recipients" ] ++ optional haveAliases "$alias_maps"; }
       // optionalAttrs (cfg.dnsBlacklists != []) { smtpd_client_restrictions = clientRestrictions; }
       // optionalAttrs cfg.useSrs {
         sender_canonical_maps = [ "tcp:127.0.0.1:10001" ];
@@ -869,6 +885,9 @@ in
     (mkIf haveVirtual {
       services.postfix.mapFiles."virtual" = virtualFile;
     })
+    (mkIf haveLocalRecipients {
+      services.postfix.mapFiles."local_recipients" = localRecipientMapFile;
+    })
     (mkIf cfg.enableHeaderChecks {
       services.postfix.mapFiles."header_checks" = headerChecksFile;
     })
diff --git a/nixpkgs/nixos/modules/services/misc/dwm-status.nix b/nixpkgs/nixos/modules/services/misc/dwm-status.nix
new file mode 100644
index 000000000000..b98a42e6a6d2
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/dwm-status.nix
@@ -0,0 +1,73 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.dwm-status;
+
+  order = concatMapStringsSep "," (feature: ''"${feature}"'') cfg.order;
+
+  configFile = pkgs.writeText "dwm-status.toml" ''
+    order = [${order}]
+
+    ${cfg.extraConfig}
+  '';
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.dwm-status = {
+
+      enable = mkEnableOption "dwm-status user service";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.dwm-status;
+        defaultText = "pkgs.dwm-status";
+        example = "pkgs.dwm-status.override { enableAlsaUtils = false; }";
+        description = ''
+          Which dwm-status package to use.
+        '';
+      };
+
+      order = mkOption {
+        type = types.listOf (types.enum [ "audio" "backlight" "battery" "cpu_load" "network" "time" ]);
+        description = ''
+          List of enabled features in order.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra config in TOML format.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.upower.enable = elem "battery" cfg.order;
+
+    systemd.user.services.dwm-status = {
+      description = "Highly performant and configurable DWM status service";
+      wantedBy = [ "graphical-session.target" ];
+      partOf = [ "graphical-session.target" ];
+
+      serviceConfig.ExecStart = "${cfg.package}/bin/dwm-status ${configFile}";
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/misc/gitea.nix b/nixpkgs/nixos/modules/services/misc/gitea.nix
index 5a964e672ede..59c1c104b9b9 100644
--- a/nixpkgs/nixos/modules/services/misc/gitea.nix
+++ b/nixpkgs/nixos/modules/services/misc/gitea.nix
@@ -55,6 +55,11 @@ let
     [service]
     DISABLE_REGISTRATION = ${boolToString cfg.disableRegistration}
 
+    ${optionalString (cfg.mailerPasswordFile != null) ''
+      [mailer]
+      PASSWD = #mailerpass#
+    ''}
+
     ${cfg.extraConfig}
   '';
 in
@@ -255,6 +260,13 @@ in
         description = "Upper level of template and static files path.";
       };
 
+      mailerPasswordFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/lib/secrets/gitea/mailpw";
+        description = "Path to a file containing the SMTP password.";
+      };
+
       disableRegistration = mkEnableOption "the registration lock" // {
         description = ''
           By default any user can create an account on this <literal>gitea</literal> instance.
@@ -344,9 +356,15 @@ in
           KEY="$(head -n1 ${secretKey})"
           DBPASS="$(head -n1 ${cfg.database.passwordFile})"
           JWTSECRET="$(head -n1 ${jwtSecret})"
+          ${if (cfg.mailerPasswordFile == null) then ''
+            MAILERPASSWORD="#mailerpass#"
+          '' else ''
+            MAILERPASSWORD="$(head -n1 ${cfg.mailerPasswordFile} || :)"
+          ''}
           sed -e "s,#secretkey#,$KEY,g" \
               -e "s,#dbpass#,$DBPASS,g" \
               -e "s,#jwtsecet#,$JWTSECET,g" \
+              -e "s,#mailerpass#,$MAILERPASSWORD,g" \
               -i ${runConfig}
           chmod 640 ${runConfig} ${secretKey} ${jwtSecret}
         ''}
diff --git a/nixpkgs/nixos/modules/services/misc/gitlab.nix b/nixpkgs/nixos/modules/services/misc/gitlab.nix
index 576ca7bfc055..087630f21776 100644
--- a/nixpkgs/nixos/modules/services/misc/gitlab.nix
+++ b/nixpkgs/nixos/modules/services/misc/gitlab.nix
@@ -52,7 +52,7 @@ let
     gitlab_url = "http+unix://${pathUrlQuote gitlabSocket}";
     http_settings.self_signed_cert = false;
     repos_path = "${cfg.statePath}/repositories";
-    secret_file = "${cfg.statePath}/config/gitlab_shell_secret";
+    secret_file = "${cfg.statePath}/gitlab_shell_secret";
     log_file = "${cfg.statePath}/log/gitlab-shell.log";
     custom_hooks_dir = "${cfg.statePath}/custom_hooks";
     redis = {
@@ -109,7 +109,7 @@ let
       gitlab_shell = {
         path = "${cfg.packages.gitlab-shell}";
         hooks_path = "${cfg.statePath}/shell/hooks";
-        secret_file = "${cfg.statePath}/config/gitlab_shell_secret";
+        secret_file = "${cfg.statePath}/gitlab_shell_secret";
         upload_pack = true;
         receive_pack = true;
       };
@@ -132,14 +132,9 @@ let
     HOME = "${cfg.statePath}/home";
     UNICORN_PATH = "${cfg.statePath}/";
     GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/";
-    GITLAB_STATE_PATH = cfg.statePath;
-    GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads";
     SCHEMA = "${cfg.statePath}/db/schema.rb";
+    GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads";
     GITLAB_LOG_PATH = "${cfg.statePath}/log";
-    GITLAB_SHELL_PATH = "${cfg.packages.gitlab-shell}";
-    GITLAB_SHELL_CONFIG_PATH = "${cfg.statePath}/shell/config.yml";
-    GITLAB_SHELL_SECRET_PATH = "${cfg.statePath}/config/gitlab_shell_secret";
-    GITLAB_SHELL_HOOKS_PATH = "${cfg.statePath}/shell/hooks";
     GITLAB_REDIS_CONFIG_FILE = pkgs.writeText "redis.yml" (builtins.toJSON redisConfig);
     prometheus_multiproc_dir = "/run/gitlab";
     RAILS_ENV = "production";
@@ -502,23 +497,44 @@ in {
     systemd.tmpfiles.rules = [
       "d /run/gitlab 0755 ${cfg.user} ${cfg.group} -"
       "d ${gitlabEnv.HOME} 0750 ${cfg.user} ${cfg.group} -"
+      "z ${gitlabEnv.HOME}/.ssh/authorized_keys 0600 ${cfg.user} ${cfg.group} -"
       "d ${cfg.backupPath} 0750 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath} 0750 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/builds 0750 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/config 0750 ${cfg.user} ${cfg.group} -"
+      "D ${cfg.statePath}/config/initializers 0750 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/db 0750 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/log 0750 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/repositories 2770 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/shell 0750 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/tmp 0750 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/tmp/pids 0750 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/tmp/sockets 0750 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/uploads 0700 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/custom_hooks 0700 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/custom_hooks/pre-receive.d 0700 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/custom_hooks/post-receive.d 0700 ${cfg.user} ${cfg.group} -"
       "d ${cfg.statePath}/custom_hooks/update.d 0700 ${cfg.user} ${cfg.group} -"
+      "d ${gitlabConfig.production.shared.path} 0750 ${cfg.user} ${cfg.group} -"
       "d ${gitlabConfig.production.shared.path}/artifacts 0750 ${cfg.user} ${cfg.group} -"
       "d ${gitlabConfig.production.shared.path}/lfs-objects 0750 ${cfg.user} ${cfg.group} -"
       "d ${gitlabConfig.production.shared.path}/pages 0750 ${cfg.user} ${cfg.group} -"
-    ];
+      "L+ ${cfg.statePath}/lib - - - - ${cfg.packages.gitlab}/share/gitlab/lib"
+      "L+ /run/gitlab/config - - - - ${cfg.statePath}/config"
+      "L+ /run/gitlab/log - - - - ${cfg.statePath}/log"
+      "L+ /run/gitlab/tmp - - - - ${cfg.statePath}/tmp"
+      "L+ /run/gitlab/uploads - - - - ${cfg.statePath}/uploads"
+
+      "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" ];
@@ -570,6 +586,7 @@ in {
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       path = with pkgs; [
+        exiftool
         gitAndTools.git
         gnutar
         gzip
@@ -609,40 +626,14 @@ in {
         gnupg
       ];
       preStart = ''
-        cp -rf ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db
-        rm -rf ${cfg.statePath}/config
-        mkdir ${cfg.statePath}/config
-        if [ -e ${cfg.statePath}/lib ]; then
-          rm ${cfg.statePath}/lib
-        fi
+        ${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
 
-        ln -sf ${cfg.packages.gitlab}/share/gitlab/lib ${cfg.statePath}/lib
-        [ -L /run/gitlab/config ] || ln -sf ${cfg.statePath}/config /run/gitlab/config
-        [ -L /run/gitlab/log ] || ln -sf ${cfg.statePath}/log /run/gitlab/log
-        [ -L /run/gitlab/tmp ] || ln -sf ${cfg.statePath}/tmp /run/gitlab/tmp
-        [ -L /run/gitlab/uploads ] || ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads
-        cp ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION
-        cp -rf ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
-        ln -sf ${extraGitlabRb} ${cfg.statePath}/config/initializers/extra-gitlab.rb
-        ${optionalString cfg.smtp.enable ''
-          ln -sf ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb
-        ''}
-        ${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/config/gitlab_shell_secret
-
-        # JSON is a subset of YAML
-        ln -sf ${pkgs.writeText "gitlab.yml" (builtins.toJSON gitlabConfig)} ${cfg.statePath}/config/gitlab.yml
-        ln -sf ${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} ${cfg.statePath}/config/database.yml
-        ln -sf ${pkgs.writeText "secrets.yml" (builtins.toJSON secretsConfig)} ${cfg.statePath}/config/secrets.yml
-        ln -sf ${./defaultUnicornConfig.rb} ${cfg.statePath}/config/unicorn.rb
-
-        # Install the shell required to push repositories
-        ln -sf ${pkgs.writeText "config.yml" (builtins.toJSON gitlabShellConfig)} /run/gitlab/shell-config.yml
-        [ -L ${cfg.statePath}/shell/hooks ] ||  ln -sf ${cfg.packages.gitlab-shell}/hooks ${cfg.statePath}/shell/hooks
-        ${cfg.packages.gitlab-shell}/bin/install
-
-        chown -R ${cfg.user}:${cfg.group} ${cfg.statePath}/
-        chmod -R ug+rwX,o-rwx+X ${cfg.statePath}/
-        chown -R ${cfg.user}:${cfg.group} /run/gitlab
+        ${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
@@ -655,7 +646,7 @@ in {
 
           ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake db:schema:load
 
-          touch "${cfg.statePath}/db-created"
+          ${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
@@ -664,22 +655,13 @@ in {
         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}'
-          touch "${cfg.statePath}/db-seeded"
+          ${pkgs.sudo}/bin/sudo -u ${cfg.user} touch "${cfg.statePath}/db-seeded"
         fi
 
-        # The gitlab:shell:create_hooks task seems broken for fixing links
-        # so we instead delete all the hooks and create them anew
+        # We remove potentially broken links to old gitlab-shell versions
         rm -f ${cfg.statePath}/repositories/**/*.git/hooks
-        ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake gitlab:shell:create_hooks
 
         ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${pkgs.git}/bin/git config --global core.autocrlf "input"
-
-        # Change permissions in the last step because some of the
-        # intermediary scripts like to create directories as root.
-        chmod -R u+rwX,go-rwx+X ${gitlabEnv.HOME}
-        chmod -R ug+rwX,o-rwx ${cfg.statePath}/repositories
-        chmod -R ug-s ${cfg.statePath}/repositories
-        find ${cfg.statePath}/repositories -type d -print0 | xargs -0 chmod g+s
       '';
 
       serviceConfig = {
diff --git a/nixpkgs/nixos/modules/services/misc/gitolite.nix b/nixpkgs/nixos/modules/services/misc/gitolite.nix
index c7f2a168f8ab..cbe2c06ab651 100644
--- a/nixpkgs/nixos/modules/services/misc/gitolite.nix
+++ b/nixpkgs/nixos/modules/services/misc/gitolite.nix
@@ -143,21 +143,37 @@ in
     users.users.${cfg.user} = {
       description     = "Gitolite user";
       home            = cfg.dataDir;
-      createHome      = true;
       uid             = config.ids.uids.gitolite;
       group           = cfg.group;
       useDefaultShell = true;
     };
     users.groups."${cfg.group}".gid = config.ids.gids.gitolite;
 
+    systemd.tmpfiles.rules = [
+      "d '${cfg.dataDir}' 0750 ${cfg.user} ${cfg.group} - -"
+      "d '${cfg.dataDir}'/.gitolite - ${cfg.user} ${cfg.group} - -"
+      "d '${cfg.dataDir}'/.gitolite/logs - ${cfg.user} ${cfg.group} - -"
+
+      "Z ${cfg.dataDir} 0750 ${cfg.user} ${cfg.group} - -"
+    ];
+
     systemd.services."gitolite-init" = {
       description = "Gitolite initialization";
       wantedBy    = [ "multi-user.target" ];
       unitConfig.RequiresMountsFor = cfg.dataDir;
 
-      serviceConfig.User = "${cfg.user}";
-      serviceConfig.Type = "oneshot";
-      serviceConfig.RemainAfterExit = true;
+      environment = {
+        GITOLITE_RC = ".gitolite.rc";
+        GITOLITE_RC_DEFAULT = "${rcDir}/gitolite.rc.default";
+      };
+
+      serviceConfig = {
+        Type = "oneshot";
+        User = cfg.user;
+        Group = cfg.group;
+        WorkingDirectory = "~";
+        RemainAfterExit = true;
+      };
 
       path = [ pkgs.gitolite pkgs.git pkgs.perl pkgs.bash pkgs.diffutils config.programs.ssh.package ];
       script =
@@ -187,11 +203,6 @@ in
           '';
       in
         ''
-          cd ${cfg.dataDir}
-          mkdir -p .gitolite/logs
-
-          GITOLITE_RC=.gitolite.rc
-          GITOLITE_RC_DEFAULT=${rcDir}/gitolite.rc.default
           if ( [[ ! -e "$GITOLITE_RC" ]] && [[ ! -L "$GITOLITE_RC" ]] ) ||
              ( [[ -f "$GITOLITE_RC" ]] && diff -q "$GITOLITE_RC" "$GITOLITE_RC_DEFAULT" >/dev/null ) ||
              ( [[ -L "$GITOLITE_RC" ]] && [[ "$(readlink "$GITOLITE_RC")" =~ ^/nix/store/ ]] )
diff --git a/nixpkgs/nixos/modules/services/misc/greenclip.nix b/nixpkgs/nixos/modules/services/misc/greenclip.nix
new file mode 100644
index 000000000000..9152a782d7f0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/greenclip.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.greenclip;
+in {
+
+  options.services.greenclip = {
+    enable = mkEnableOption "Greenclip daemon";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.haskellPackages.greenclip;
+      defaultText = "pkgs.haskellPackages.greenclip";
+      description = "greenclip derivation to use.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.user.services.greenclip = {
+      enable      = true;
+      description = "greenclip daemon";
+      wantedBy = [ "graphical-session.target" ];
+      after    = [ "graphical-session.target" ];
+      serviceConfig.ExecStart = "${cfg.package}/bin/greenclip daemon";
+    };
+
+    environment.systemPackages = [ cfg.package ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/nix-daemon.nix b/nixpkgs/nixos/modules/services/misc/nix-daemon.nix
index d8f90f1539c1..6bc88c66dc19 100644
--- a/nixpkgs/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixpkgs/nixos/modules/services/misc/nix-daemon.nix
@@ -467,7 +467,7 @@ in
         fi
       '';
 
-    nix.nrBuildUsers = mkDefault (lib.max 32 cfg.maxJobs);
+    nix.nrBuildUsers = mkDefault (lib.max 32 (if cfg.maxJobs == "auto" then 0 else cfg.maxJobs));
 
     users.users = nixbldUsers;
 
diff --git a/nixpkgs/nixos/modules/services/misc/redmine.nix b/nixpkgs/nixos/modules/services/misc/redmine.nix
index 91ddf2c3edf3..24b9e27ac2da 100644
--- a/nixpkgs/nixos/modules/services/misc/redmine.nix
+++ b/nixpkgs/nixos/modules/services/misc/redmine.nix
@@ -1,8 +1,10 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
+  inherit (lib) mkDefault mkEnableOption mkIf mkOption types;
+  inherit (lib) concatStringsSep literalExample mapAttrsToList;
+  inherit (lib) optional optionalAttrs optionalString singleton versionAtLeast;
+
   cfg = config.services.redmine;
 
   bundle = "${cfg.package}/share/redmine/bin/bundle";
@@ -11,11 +13,11 @@ let
     production:
       adapter: ${cfg.database.type}
       database: ${cfg.database.name}
-      host: ${cfg.database.host}
+      host: ${if (cfg.database.type == "postgresql" && cfg.database.socket != null) then cfg.database.socket else cfg.database.host}
       port: ${toString cfg.database.port}
       username: ${cfg.database.user}
       password: #dbpass#
-      ${optionalString (cfg.database.socket != null) "socket: ${cfg.database.socket}"}
+      ${optionalString (cfg.database.type == "mysql2" && cfg.database.socket != null) "socket: ${cfg.database.socket}"}
   '';
 
   configurationYml = pkgs.writeText "configuration.yml" ''
@@ -50,16 +52,15 @@ let
       '';
   });
 
+  mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql2";
+  pgsqlLocal = cfg.database.createLocally && cfg.database.type == "postgresql";
+
 in
 
 {
   options = {
     services.redmine = {
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Enable the Redmine service.";
-      };
+      enable = mkEnableOption "Redmine";
 
       # default to the 4.x series not forcing major version upgrade of those on the 3.x series
       package = mkOption {
@@ -107,7 +108,8 @@ in
         description = ''
           Extra configuration in configuration.yml.
 
-          See https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration
+          See <link xlink:href="https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration"/>
+          for details.
         '';
         example = literalExample ''
           email_delivery:
@@ -124,7 +126,8 @@ in
         description = ''
           Extra configuration in additional_environment.rb.
 
-          See https://svn.redmine.org/redmine/trunk/config/additional_environment.rb.example
+          See <link xlink:href="https://svn.redmine.org/redmine/trunk/config/additional_environment.rb.example"/>
+          for details.
         '';
         example = literalExample ''
           config.logger.level = Logger::DEBUG
@@ -169,13 +172,14 @@ in
 
         host = mkOption {
           type = types.str;
-          default = (if cfg.database.socket != null then "localhost" else "127.0.0.1");
+          default = "localhost";
           description = "Database host address.";
         };
 
         port = mkOption {
           type = types.int;
-          default = 3306;
+          default = if cfg.database.type == "postgresql" then 5432 else 3306;
+          defaultText = "3306";
           description = "Database host port.";
         };
 
@@ -213,10 +217,20 @@ in
 
         socket = mkOption {
           type = types.nullOr types.path;
-          default = null;
+          default =
+            if mysqlLocal then "/run/mysqld/mysqld.sock"
+            else if pgsqlLocal then "/run/postgresql"
+            else null;
+          defaultText = "/run/mysqld/mysqld.sock";
           example = "/run/mysqld/mysqld.sock";
           description = "Path to the unix socket file to use for authentication.";
         };
+
+        createLocally = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Create the database and database user locally.";
+        };
       };
     };
   };
@@ -227,12 +241,37 @@ in
       { assertion = cfg.database.passwordFile != null || cfg.database.password != "" || cfg.database.socket != null;
         message = "one of services.redmine.database.socket, services.redmine.database.passwordFile, or services.redmine.database.password must be set";
       }
-      { assertion = cfg.database.socket != null -> (cfg.database.type == "mysql2");
-        message = "Socket authentication is only available for the mysql2 database type";
+      { assertion = cfg.database.createLocally -> cfg.database.user == cfg.user;
+        message = "services.redmine.database.user must be set to ${cfg.user} if services.redmine.database.createLocally is set true";
+      }
+      { assertion = cfg.database.createLocally -> cfg.database.socket != null;
+        message = "services.redmine.database.socket must be set if services.redmine.database.createLocally is set to true";
+      }
+      { assertion = cfg.database.createLocally -> cfg.database.host == "localhost";
+        message = "services.redmine.database.host must be set to localhost if services.redmine.database.createLocally is set to true";
       }
     ];
 
-    environment.systemPackages = [ cfg.package ];
+    services.mysql = mkIf mysqlLocal {
+      enable = true;
+      package = mkDefault pkgs.mariadb;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        { name = cfg.database.user;
+          ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
+        }
+      ];
+    };
+
+    services.postgresql = mkIf pgsqlLocal {
+      enable = true;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        { name = cfg.database.user;
+          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+        }
+      ];
+    };
 
     # create symlinks for the basic directory layout the redmine package expects
     systemd.tmpfiles.rules = [
@@ -259,7 +298,7 @@ in
     ];
 
     systemd.services.redmine = {
-      after = [ "network.target" (if cfg.database.type == "mysql2" then "mysql.service" else "postgresql.service") ];
+      after = [ "network.target" ] ++ optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
       wantedBy = [ "multi-user.target" ];
       environment.RAILS_ENV = "production";
       environment.RAILS_CACHE = "${cfg.stateDir}/cache";
diff --git a/nixpkgs/nixos/modules/services/misc/taskserver/default.nix b/nixpkgs/nixos/modules/services/misc/taskserver/default.nix
index 07dbee69db0c..8a57277fafe7 100644
--- a/nixpkgs/nixos/modules/services/misc/taskserver/default.nix
+++ b/nixpkgs/nixos/modules/services/misc/taskserver/default.nix
@@ -411,7 +411,7 @@ in {
         } else {
           cert = "${cfg.pki.manual.server.cert}";
           key = "${cfg.pki.manual.server.key}";
-          crl = "${cfg.pki.manual.server.crl}";
+          ${mapNullable (_: "crl") cfg.pki.manual.server.crl} = "${cfg.pki.manual.server.crl}";
         });
 
         ca.cert = if needToCreateCA then "${cfg.dataDir}/keys/ca.cert"
diff --git a/nixpkgs/nixos/modules/services/misc/tiddlywiki.nix b/nixpkgs/nixos/modules/services/misc/tiddlywiki.nix
new file mode 100644
index 000000000000..2adc08f6cfed
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/tiddlywiki.nix
@@ -0,0 +1,52 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.tiddlywiki;
+  listenParams = concatStrings (mapAttrsToList (n: v: " '${n}=${toString v}' ") cfg.listenOptions);
+  exe = "${pkgs.nodePackages.tiddlywiki}/lib/node_modules/.bin/tiddlywiki";
+  name = "tiddlywiki";
+  dataDir = "/var/lib/" + name;
+
+in {
+
+  options.services.tiddlywiki = {
+
+    enable = mkEnableOption "TiddlyWiki nodejs server";
+
+    listenOptions = mkOption {
+      type = types.attrs;
+      default = {};
+      example = {
+        credentials = "../credentials.csv";
+        readers="(authenticated)";
+        port = 3456;
+      };
+      description = ''
+        Parameters passed to <literal>--listen</literal> command.
+        Refer to <link xlink:href="https://tiddlywiki.com/#WebServer"/>
+        for details on supported values.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd = {
+      services.tiddlywiki = {
+        description = "TiddlyWiki nodejs server";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          Type = "simple";
+          Restart = "on-failure";
+          DynamicUser = true;
+          StateDirectory = name;
+          ExecStartPre = "-${exe} ${dataDir} --init server";
+          ExecStart = "${exe} ${dataDir} --listen ${listenParams}";
+        };
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/datadog-agent.nix b/nixpkgs/nixos/modules/services/monitoring/datadog-agent.nix
index ce3d53fb2c17..7f78db74677c 100644
--- a/nixpkgs/nixos/modules/services/monitoring/datadog-agent.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/datadog-agent.nix
@@ -42,9 +42,9 @@ let
   # Apply the configured extraIntegrations to the provided agent
   # package. See the documentation of `dd-agent/integrations-core.nix`
   # for detailed information on this.
-  datadogPkg = cfg.package.overrideAttrs(_: {
-    python = (pkgs.datadog-integrations-core cfg.extraIntegrations).python;
-  });
+  datadogPkg = cfg.package.override {
+    pythonPackages = pkgs.datadog-integrations-core cfg.extraIntegrations;
+  };
 in {
   options.services.datadog-agent = {
     enable = mkOption {
@@ -60,7 +60,7 @@ in {
       defaultText = "pkgs.datadog-agent";
       description = ''
         Which DataDog v6 agent package to use. Note that the provided
-        package is expected to have an overridable `python`-attribute
+        package is expected to have an overridable `pythonPackages`-attribute
         which configures the Python environment with the Datadog
         checks.
       '';
diff --git a/nixpkgs/nixos/modules/services/monitoring/grafana.nix b/nixpkgs/nixos/modules/services/monitoring/grafana.nix
index 5d3f2e6ac28f..bf1084eecc3a 100644
--- a/nixpkgs/nixos/modules/services/monitoring/grafana.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/grafana.nix
@@ -503,12 +503,12 @@ in {
         message = "Cannot set both adminPassword and adminPasswordFile";
       }
       {
-        assertion = cfg.security.secretKeyFile != opt.security.secretKeyFile.default -> cfg.security.secretKeyFile == null;
+        assertion = cfg.security.secretKey != opt.security.secretKey.default -> cfg.security.secretKeyFile == null;
         message = "Cannot set both secretKey and secretKeyFile";
       }
       {
         assertion = cfg.smtp.password != opt.smtp.password.default -> cfg.smtp.passwordFile == null;
-        message = "Cannot set both password and secretKeyFile";
+        message = "Cannot set both password and passwordFile";
       }
     ];
 
@@ -552,6 +552,8 @@ in {
       description = "Grafana user";
       home = cfg.dataDir;
       createHome = true;
+      group = "grafana";
     };
+    users.groups.grafana = {};
   };
 }
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
index d8384e0d35b3..647d67533b89 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
@@ -79,12 +79,8 @@ let
       (pkgs.writeText "prometheus.rules" (concatStringsSep "\n" cfg2.rules))
     ]);
     scrape_configs = filterValidPrometheus cfg2.scrapeConfigs;
-    alerting = optionalAttrs (cfg2.alertmanagerURL != []) {
-      alertmanagers = [{
-        static_configs = [{
-          targets = cfg2.alertmanagerURL;
-        }];
-      }];
+    alerting = {
+      inherit (cfg2) alertmanagers;
     };
   };
 
@@ -738,11 +734,23 @@ in {
         '';
       };
 
-      alertmanagerURL = mkOption {
-        type = types.listOf types.str;
+      alertmanagers = mkOption {
+        type = types.listOf types.attrs;
+        example = literalExample ''
+          [ {
+            scheme = "https";
+            path_prefix = "/alertmanager";
+            static_configs = [ {
+              targets = [
+                "prometheus.domain.tld"
+              ];
+            } ];
+          } ]
+        '';
         default = [];
         description = ''
-          List of Alertmanager URLs to send notifications to.
+          A list of alertmanagers to send alerts to.
+          See <link xlink:href="https://prometheus.io/docs/prometheus/latest/configuration/configuration/#alertmanager_config">the official documentation</link> for more information.
         '';
       };
 
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
index 20e7eba43412..2ab8910ff9db 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -1,8 +1,10 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
+{ config, pkgs, lib, options, ... }:
 
 let
+  inherit (lib) concatStrings foldl foldl' genAttrs literalExample maintainers
+                mapAttrsToList mkDefault mkEnableOption mkIf mkMerge mkOption
+                optional types;
+
   cfg = config.services.prometheus.exporters;
 
   # each attribute in `exporterOpts` is expected to have specified:
@@ -17,25 +19,30 @@ let
   #  Note that `extraOpts` is optional, but a script for the exporter's
   #  systemd service must be provided by specifying either
   #  `serviceOpts.script` or `serviceOpts.serviceConfig.ExecStart`
-  exporterOpts = {
-    blackbox  = import ./exporters/blackbox.nix  { inherit config lib pkgs; };
-    collectd  = import ./exporters/collectd.nix  { inherit config lib pkgs; };
-    dnsmasq   = import ./exporters/dnsmasq.nix   { inherit config lib pkgs; };
-    dovecot   = import ./exporters/dovecot.nix   { inherit config lib pkgs; };
-    fritzbox  = import ./exporters/fritzbox.nix  { inherit config lib pkgs; };
-    json      = import ./exporters/json.nix      { inherit config lib pkgs; };
-    minio     = import ./exporters/minio.nix     { inherit config lib pkgs; };
-    nginx     = import ./exporters/nginx.nix     { inherit config lib pkgs; };
-    node      = import ./exporters/node.nix      { inherit config lib pkgs; };
-    postfix   = import ./exporters/postfix.nix   { inherit config lib pkgs; };
-    snmp      = import ./exporters/snmp.nix      { inherit config lib pkgs; };
-    surfboard = import ./exporters/surfboard.nix { inherit config lib pkgs; };
-    tor       = import ./exporters/tor.nix       { inherit config lib pkgs; };
-    unifi     = import ./exporters/unifi.nix     { inherit config lib pkgs; };
-    varnish   = import ./exporters/varnish.nix   { inherit config lib pkgs; };
-    bind      = import ./exporters/bind.nix      { inherit config lib pkgs; };
-    wireguard = import ./exporters/wireguard.nix { inherit config lib pkgs; };
-  };
+
+  exporterOpts = genAttrs [
+    "bind"
+    "blackbox"
+    "collectd"
+    "dnsmasq"
+    "dovecot"
+    "fritzbox"
+    "json"
+    "mail"
+    "minio"
+    "nginx"
+    "node"
+    "postfix"
+    "postgres"
+    "snmp"
+    "surfboard"
+    "tor"
+    "unifi"
+    "varnish"
+    "wireguard"
+  ] (name:
+    import (./. + "/exporters/${name}.nix") { inherit config lib pkgs options; }
+  );
 
   mkExporterOpts = ({ name, port }: {
     enable = mkEnableOption "the prometheus ${name} exporter";
@@ -81,7 +88,7 @@ let
     };
     user = mkOption {
       type = types.str;
-      default = "nobody";
+      default = "${name}-exporter";
       description = ''
         User name under which the ${name} exporter shall be run.
         Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true.
@@ -89,7 +96,7 @@ let
     };
     group = mkOption {
       type = types.str;
-      default = "nobody";
+      default = "${name}-exporter";
       description = ''
         Group under which the ${name} exporter shall be run.
         Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true.
@@ -97,9 +104,10 @@ let
     };
   });
 
-  mkSubModule = { name, port, extraOpts, ... }: {
+  mkSubModule = { name, port, extraOpts, imports }: {
     ${name} = mkOption {
       type = types.submodule {
+        inherit imports;
         options = (mkExporterOpts {
           inherit name port;
         } // extraOpts);
@@ -112,13 +120,30 @@ let
   mkSubModules = (foldl' (a: b: a//b) {}
     (mapAttrsToList (name: opts: mkSubModule {
       inherit name;
-      inherit (opts) port serviceOpts;
+      inherit (opts) port;
       extraOpts = opts.extraOpts or {};
+      imports = opts.imports or [];
     }) exporterOpts)
   );
 
   mkExporterConf = { name, conf, serviceOpts }:
+    let
+      enableDynamicUser = serviceOpts.serviceConfig.DynamicUser or true;
+    in
     mkIf conf.enable {
+      warnings = conf.warnings or [];
+      users.users = (mkIf (conf.user == "${name}-exporter" && !enableDynamicUser) {
+        "${name}-exporter" = {
+          description = ''
+            Prometheus ${name} exporter service user
+          '';
+          isSystemUser = true;
+          inherit (conf) group;
+        };
+      });
+      users.groups = (mkIf (conf.group == "${name}-exporter" && !enableDynamicUser) {
+        "${name}-exporter" = {};
+      });
       networking.firewall.extraCommands = mkIf conf.openFirewall (concatStrings [
         "ip46tables -A nixos-fw ${conf.firewallFilter} "
         "-m comment --comment ${name}-exporter -j nixos-fw-accept"
@@ -129,7 +154,8 @@ let
         serviceConfig.Restart = mkDefault "always";
         serviceConfig.PrivateTmp = mkDefault true;
         serviceConfig.WorkingDirectory = mkDefault /tmp;
-      } serviceOpts ] ++ optional (!(serviceOpts.serviceConfig.DynamicUser or false)) {
+        serviceConfig.DynamicUser = mkDefault enableDynamicUser;
+      } serviceOpts ] ++ optional (!enableDynamicUser) {
         serviceConfig.User = conf.user;
         serviceConfig.Group = conf.group;
       });
@@ -154,13 +180,19 @@ in
   };
 
   config = mkMerge ([{
-    assertions = [{
+    assertions = [ {
       assertion = (cfg.snmp.configurationPath == null) != (cfg.snmp.configuration == null);
       message = ''
         Please ensure you have either `services.prometheus.exporters.snmp.configuration'
           or `services.prometheus.exporters.snmp.configurationPath' set!
       '';
-    }];
+    } {
+      assertion = (cfg.mail.configFile == null) != (cfg.mail.configuration == {});
+      message = ''
+        Please specify either 'services.prometheus.exporters.mail.configuration'
+          or 'services.prometheus.exporters.mail.configFile'.
+      '';
+    } ];
   }] ++ [(mkIf config.services.minio.enable {
     services.prometheus.exporters.minio.minioAddress  = mkDefault "http://localhost:9000";
     services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey;
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.xml b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.xml
index 81ac998729be..c2d4b05996a4 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.xml
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.xml
@@ -17,7 +17,7 @@
    exporter</link>, it provides hardware and OS metrics from the host it's
    running on. The exporter could be configured as follows:
 <programlisting>
-  services.promtheus.exporters.node = {
+  services.prometheus.exporters.node = {
     enable = true;
     enabledCollectors = [
       "logind"
@@ -113,7 +113,7 @@
      specific options and configuration:
 <programlisting>
 # nixpgs/nixos/modules/services/prometheus/exporters/postfix.nix
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -159,8 +159,10 @@ in
   # `serviceOpts.script` and `serviceOpts.serviceConfig.ExecStart`
   # has to be specified here. This will be merged with the default
   # service confiuration.
+  # Note that by default 'DynamicUser' is 'true'.
   serviceOpts = {
     serviceConfig = {
+      DynamicUser = false;
       ExecStart = ''
         ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \
           --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
@@ -184,4 +186,42 @@ in
    </listitem>
   </itemizedlist>
  </section>
+ <section xml:id="module-services-prometheus-exporters-update-exporter-module">
+  <title>Updating an exporter module</title>
+   <para>
+     Should an exporter option change at some point, it is possible to add
+     information about the change to the exporter definition similar to
+     <literal>nixpkgs/nixos/modules/rename.nix</literal>:
+<programlisting>
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.nginx;
+in
+{
+  port = 9113;
+  extraOpts = {
+    # additional module options
+    # ...
+  };
+  serviceOpts = {
+    # service configuration
+    # ...
+  };
+  imports = [
+    # 'services.prometheus.exporters.nginx.telemetryEndpoint' -> 'services.prometheus.exporters.nginx.telemetryPath'
+    (mkRenamedOptionModule [ "telemetryEndpoint" ] [ "telemetryPath" ])
+
+    # removed option 'services.prometheus.exporters.nginx.insecure'
+    (mkRemovedOptionModule [ "insecure" ] ''
+      This option was replaced by 'prometheus.exporters.nginx.sslVerify' which defaults to true.
+    '')
+    ({ options.warnings = options.warnings; })
+  ];
+}
+</programlisting>
+    </para>
+  </section>
 </chapter>
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bind.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bind.nix
index a9746c4d65d5..972632b5a24a 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bind.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bind.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -39,7 +39,6 @@ in
   };
   serviceOpts = {
     serviceConfig = {
-      DynamicUser = true;
       ExecStart = ''
         ${pkgs.prometheus-bind-exporter}/bin/bind_exporter \
           -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
index d09d1c4f3663..ca4366121e12 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
@@ -1,9 +1,16 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
 let
   cfg = config.services.prometheus.exporters.blackbox;
+
+  checkConfig = file: pkgs.runCommand "checked-blackbox-exporter.conf" {
+    preferLocalBuild = true;
+    buildInputs = [ pkgs.buildPackages.prometheus-blackbox-exporter ]; } ''
+    ln -s ${file} $out
+    blackbox_exporter --config.check --config.file $out
+  '';
 in
 {
   port = 9115;
@@ -18,11 +25,10 @@ in
   serviceOpts = {
     serviceConfig = {
       AmbientCapabilities = [ "CAP_NET_RAW" ]; # for ping probes
-      DynamicUser = true;
       ExecStart = ''
         ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \
           --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
-          --config.file ${cfg.configFile} \
+          --config.file ${checkConfig cfg.configFile} \
           ${concatStringsSep " \\\n  " cfg.extraFlags}
       '';
       ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix
index 0eba3527162d..1cc346418091 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -64,7 +64,6 @@ in
     '' else "";
   in {
     serviceConfig = {
-      DynamicUser = true;
       ExecStart = ''
         ${pkgs.prometheus-collectd-exporter}/bin/collectd_exporter \
           -log.format ${cfg.logFormat} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dnsmasq.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dnsmasq.nix
index b1fab85109af..e9fa26cb1f5a 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dnsmasq.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dnsmasq.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -26,7 +26,6 @@ in
   };
   serviceOpts = {
     serviceConfig = {
-      DynamicUser = true;
       ExecStart = ''
         ${pkgs.prometheus-dnsmasq-exporter}/bin/dnsmasq_exporter \
           --listen ${cfg.listenAddress}:${toString cfg.port} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix
index c47e87a3dc35..a01074758ff8 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -39,8 +39,8 @@ in
             mail_plugins = $mail_plugins old_stats
             service old-stats {
               unix_listener old-stats {
-                user = nobody
-                group = nobody
+                user = dovecot-exporter
+                group = dovecot-exporter
               }
             }
           ''';
@@ -59,6 +59,7 @@ in
   };
   serviceOpts = {
     serviceConfig = {
+      DynamicUser = false;
       ExecStart = ''
         ${pkgs.prometheus-dovecot-exporter}/bin/dovecot_exporter \
           --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix
index 530206681d36..9526597b8c96 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -26,7 +26,6 @@ in
   };
   serviceOpts = {
     serviceConfig = {
-      DynamicUser = true;
       ExecStart = ''
         ${pkgs.prometheus-fritzbox-exporter}/bin/exporter \
           -listen-address ${cfg.listenAddress}:${toString cfg.port} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/json.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/json.nix
index a5494e85e016..82a55bafc982 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/json.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/json.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -24,7 +24,6 @@ in
   };
   serviceOpts = {
     serviceConfig = {
-      DynamicUser = true;
       ExecStart = ''
         ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \
           --port ${toString cfg.port} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mail.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mail.nix
new file mode 100644
index 000000000000..7d8c6fb61404
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mail.nix
@@ -0,0 +1,157 @@
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.mail;
+
+  configurationFile = pkgs.writeText "prometheus-mail-exporter.conf" (builtins.toJSON (
+    # removes the _module attribute, null values and converts attrNames to lowercase
+    mapAttrs' (name: value:
+      if name == "servers"
+      then nameValuePair (toLower name)
+        ((map (srv: (mapAttrs' (n: v: nameValuePair (toLower n) v)
+          (filterAttrs (n: v: !(n == "_module" || v == null)) srv)
+        ))) value)
+      else nameValuePair (toLower name) value
+    ) (filterAttrs (n: _: !(n == "_module")) cfg.configuration)
+  ));
+
+  serverOptions.options = {
+    name = mkOption {
+      type = types.str;
+      description = ''
+        Value for label 'configname' which will be added to all metrics.
+      '';
+    };
+    server = mkOption {
+      type = types.str;
+      description = ''
+        Hostname of the server that should be probed.
+      '';
+    };
+    port = mkOption {
+      type = types.int;
+      example = 587;
+      description = ''
+        Port to use for SMTP.
+      '';
+    };
+    from = mkOption {
+      type = types.str;
+      example = "exporteruser@domain.tld";
+      description = ''
+        Content of 'From' Header for probing mails.
+      '';
+    };
+    to = mkOption {
+      type = types.str;
+      example = "exporteruser@domain.tld";
+      description = ''
+        Content of 'To' Header for probing mails.
+      '';
+    };
+    detectionDir = mkOption {
+      type = types.path;
+      example = "/var/spool/mail/exporteruser/new";
+      description = ''
+        Directory in which new mails for the exporter user are placed.
+        Note that this needs to exist when the exporter starts.
+      '';
+    };
+    login = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "exporteruser@domain.tld";
+      description = ''
+        Username to use for SMTP authentication.
+      '';
+    };
+    passphrase = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Password to use for SMTP authentication.
+      '';
+    };
+  };
+
+  exporterOptions.options = {
+    monitoringInterval = mkOption {
+      type = types.str;
+      example = "10s";
+      description = ''
+        Time interval between two probe attempts.
+      '';
+    };
+    mailCheckTimeout = mkOption {
+      type = types.str;
+      description = ''
+        Timeout until mails are considered "didn't make it".
+      '';
+    };
+    disableFileDelition = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Disables the exporter's function to delete probing mails.
+      '';
+    };
+    servers = mkOption {
+      type = types.listOf (types.submodule serverOptions);
+      default = [];
+      example = literalExample ''
+        [ {
+          name = "testserver";
+          server = "smtp.domain.tld";
+          port = 587;
+          from = "exporteruser@domain.tld";
+          to = "exporteruser@domain.tld";
+          detectionDir = "/path/to/Maildir/new";
+        } ]
+      '';
+      description = ''
+        List of servers that should be probed.
+      '';
+    };
+  };
+in
+{
+  port = 9225;
+  extraOpts = {
+    configFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        Specify the mailexporter configuration file to use.
+      '';
+    };
+    configuration = mkOption {
+      type = types.submodule exporterOptions;
+      default = {};
+      description = ''
+        Specify the mailexporter configuration file to use.
+      '';
+    };
+    telemetryPath = mkOption {
+      type = types.str;
+      default = "/metrics";
+      description = ''
+        Path under which to expose metrics.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = false;
+      ExecStart = ''
+        ${pkgs.prometheus-mail-exporter}/bin/mailexporter \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          --config.file ${
+            if cfg.configuration != {} then configurationFile else cfg.configFile
+          } \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/minio.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
index 3cc4ffdbc8fd..ab3e3d7d5d50 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -50,7 +50,6 @@ in
   };
   serviceOpts = {
     serviceConfig = {
-      DynamicUser = true;
       ExecStart = ''
         ${pkgs.prometheus-minio-exporter}/bin/minio-exporter \
           -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
index 431dd8b4ead7..554377df37ba 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -16,32 +16,39 @@ in
         Can be enabled with services.nginx.statusPage = true.
       '';
     };
-    telemetryEndpoint = mkOption {
+    telemetryPath = mkOption {
       type = types.str;
       default = "/metrics";
       description = ''
         Path under which to expose metrics.
       '';
     };
-    insecure = mkOption {
+    sslVerify = mkOption {
       type = types.bool;
       default = true;
       description = ''
-        Ignore server certificate if using https.
+        Whether to perform certificate verification for https.
       '';
     };
+
   };
   serviceOpts = {
     serviceConfig = {
-      DynamicUser = true;
       ExecStart = ''
-        ${pkgs.prometheus-nginx-exporter}/bin/nginx_exporter \
-          --nginx.scrape_uri '${cfg.scrapeUri}' \
-          --telemetry.address ${cfg.listenAddress}:${toString cfg.port} \
-          --telemetry.endpoint ${cfg.telemetryEndpoint} \
-          --insecure ${toString cfg.insecure} \
+        ${pkgs.prometheus-nginx-exporter}/bin/nginx-prometheus-exporter \
+          --nginx.scrape-uri '${cfg.scrapeUri}' \
+          --nginx.ssl-verify ${toString cfg.sslVerify} \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          --web.telemetry-path ${cfg.telemetryPath} \
           ${concatStringsSep " \\\n  " cfg.extraFlags}
       '';
     };
   };
+  imports = [
+    (mkRenamedOptionModule [ "telemetryEndpoint" ] [ "telemetryPath" ])
+    (mkRemovedOptionModule [ "insecure" ] ''
+      This option was replaced by 'prometheus.exporters.nginx.sslVerify'.
+    '')
+    ({ options.warnings = options.warnings; })
+  ];
 }
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/node.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/node.nix
index 8c4128f9b634..7e394e8463e0 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/node.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/node.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -27,6 +27,7 @@ in
   };
   serviceOpts = {
     serviceConfig = {
+      DynamicUser = false;
       RuntimeDirectory = "prometheus-node-exporter";
       ExecStart = ''
         ${pkgs.prometheus-node-exporter}/bin/node_exporter \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
index efe78ebcba86..f40819e826b0 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -62,6 +62,7 @@ in
   };
   serviceOpts = {
     serviceConfig = {
+      DynamicUser = false;
       ExecStart = ''
         ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \
           --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postgres.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postgres.nix
new file mode 100644
index 000000000000..1ece73a1159a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postgres.nix
@@ -0,0 +1,47 @@
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.postgres;
+in
+{
+  port = 9187;
+  extraOpts = {
+    telemetryPath = mkOption {
+      type = types.str;
+      default = "/metrics";
+      description = ''
+        Path under which to expose metrics.
+      '';
+    };
+    dataSourceName = mkOption {
+      type = types.str;
+      default = "user=postgres database=postgres host=/run/postgresql sslmode=disable";
+      example = "postgresql://username:password@localhost:5432/postgres?sslmode=disable";
+      description = ''
+        Accepts PostgreSQL URI form and key=value form arguments.
+      '';
+    };
+    runAsLocalSuperUser = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to run the exporter as the local 'postgres' super user.
+      '';
+    };
+  };
+  serviceOpts = {
+    environment.DATA_SOURCE_NAME = cfg.dataSourceName;
+    serviceConfig = {
+      DynamicUser = false;
+      User = mkIf cfg.runAsLocalSuperUser (mkForce "postgres");
+      ExecStart = ''
+        ${pkgs.prometheus-postgres-exporter}/bin/postgres_exporter \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          --web.telemetry-path ${cfg.telemetryPath} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix
index 0d9194124325..fe7ae8a8ac90 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -57,7 +57,6 @@ in
                  else "${pkgs.writeText "snmp-eporter-conf.yml" (builtins.toJSON cfg.configuration)}";
     in {
     serviceConfig = {
-      DynamicUser = true;
       ExecStart = ''
         ${pkgs.prometheus-snmp-exporter.bin}/bin/snmp_exporter \
           --config.file=${configFile} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/surfboard.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/surfboard.nix
index 715dba06a3dc..81c5c70ed93f 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/surfboard.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/surfboard.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -20,7 +20,6 @@ in
     description = "Prometheus exporter for surfboard cable modem";
     unitConfig.Documentation = "https://github.com/ipstatic/surfboard_exporter";
     serviceConfig = {
-      DynamicUser = true;
       ExecStart = ''
         ${pkgs.prometheus-surfboard-exporter}/bin/surfboard_exporter \
           --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/tor.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/tor.nix
index e0ae83802425..36c473677efa 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/tor.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/tor.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -26,7 +26,6 @@ in
   };
   serviceOpts = {
     serviceConfig = {
-      DynamicUser = true;
       ExecStart = ''
         ${pkgs.prometheus-tor-exporter}/bin/prometheus-tor-exporter \
           -b ${cfg.listenAddress} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix
index 011dcbe208e4..9aa0f1b85aac 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -51,7 +51,6 @@ in
   };
   serviceOpts = {
     serviceConfig = {
-      DynamicUser = true;
       ExecStart = ''
         ${pkgs.prometheus-unifi-exporter}/bin/unifi_exporter \
           -telemetry.addr ${cfg.listenAddress}:${toString cfg.port} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix
index aaed76175b84..12153fa021ec 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -68,8 +68,8 @@ in
   serviceOpts = {
     path = [ pkgs.varnish ];
     serviceConfig = {
-      DynamicUser = true;
       RestartSec = mkDefault 1;
+      DynamicUser = false;
       ExecStart = ''
         ${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \
           --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
index c5b84e574b8d..8ae2c927b58c 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs }:
+{ config, lib, pkgs, options }:
 
 with lib;
 
@@ -23,20 +23,39 @@ in {
         to set the peers up.
       '';
     };
+
+    singleSubnetPerField = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        By default, all allowed IPs and subnets are comma-separated in the
+        <literal>allowed_ips</literal> field. With this option enabled,
+        a single IP and subnet will be listed in fields like <literal>allowed_ip_0</literal>,
+        <literal>allowed_ip_1</literal> and so on.
+      '';
+    };
+
+    withRemoteIp = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether or not the remote IP of a WireGuard peer should be exposed via prometheus.
+      '';
+    };
   };
   serviceOpts = {
-    script = ''
-      ${pkgs.prometheus-wireguard-exporter}/bin/prometheus_wireguard_exporter \
-        -p ${toString cfg.port} \
-        ${optionalString cfg.verbose "-v"} \
-        ${optionalString (cfg.wireguardConfig != null) "-n ${cfg.wireguardConfig}"}
-    '';
-
     path = [ pkgs.wireguard-tools ];
 
     serviceConfig = {
-      DynamicUser = true;
       AmbientCapabilities = [ "CAP_NET_ADMIN" ];
+      ExecStart = ''
+        ${pkgs.prometheus-wireguard-exporter}/bin/prometheus_wireguard_exporter \
+          -p ${toString cfg.port} \
+          ${optionalString cfg.verbose "-v"} \
+          ${optionalString cfg.singleSubnetPerField "-s"} \
+          ${optionalString cfg.withRemoteIp "-r"} \
+          ${optionalString (cfg.wireguardConfig != null) "-n ${cfg.wireguardConfig}"}
+      '';
     };
   };
 }
diff --git a/nixpkgs/nixos/modules/services/monitoring/thanos.nix b/nixpkgs/nixos/modules/services/monitoring/thanos.nix
new file mode 100644
index 000000000000..b41e99b76477
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/thanos.nix
@@ -0,0 +1,801 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.thanos;
+
+  nullOpt = type: description: mkOption {
+    type = types.nullOr type;
+    default = null;
+    inherit description;
+  };
+
+  optionToArgs = opt: v  : optional (v != null)  ''--${opt}="${toString v}"'';
+  flagToArgs   = opt: v  : optional v            ''--${opt}'';
+  listToArgs   = opt: vs : map               (v: ''--${opt}="${v}"'') vs;
+  attrsToArgs  = opt: kvs: mapAttrsToList (k: v: ''--${opt}=${k}=\"${v}\"'') kvs;
+
+  mkParamDef = type: default: description: mkParam type (description + ''
+
+    Defaults to <literal>${toString default}</literal> in Thanos
+    when set to <literal>null</literal>.
+  '');
+
+  mkParam = type: description: {
+    toArgs = optionToArgs;
+    option = nullOpt type description;
+  };
+
+  mkFlagParam = description: {
+    toArgs = flagToArgs;
+    option = mkOption {
+      type = types.bool;
+      default = false;
+      inherit description;
+    };
+  };
+
+  mkListParam = opt: description: {
+    toArgs = _opt: listToArgs opt;
+    option = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      inherit description;
+    };
+  };
+
+  mkAttrsParam = opt: description: {
+    toArgs = _opt: attrsToArgs opt;
+    option = mkOption {
+      type = types.attrsOf types.str;
+      default = {};
+      inherit description;
+    };
+  };
+
+  mkStateDirParam = opt: default: description: {
+    toArgs = _opt: stateDir: optionToArgs opt "/var/lib/${stateDir}";
+    option = mkOption {
+      type = types.str;
+      inherit default;
+      inherit description;
+    };
+  };
+
+  toYAML = name: attrs: pkgs.runCommandNoCC name {
+    preferLocalBuild = true;
+    json = builtins.toFile "${name}.json" (builtins.toJSON attrs);
+    nativeBuildInputs = [ pkgs.remarshal ];
+  } ''json2yaml -i $json -o $out'';
+
+  thanos = cmd: "${cfg.package}/bin/thanos ${cmd}" +
+    (let args = cfg."${cmd}".arguments;
+     in optionalString (length args != 0) (" \\\n  " +
+         concatStringsSep " \\\n  " args));
+
+  argumentsOf = cmd: concatLists (collect isList
+    (flip mapParamsRecursive params."${cmd}" (path: param:
+      let opt = concatStringsSep "." path;
+          v = getAttrFromPath path cfg."${cmd}";
+      in param.toArgs opt v)));
+
+  mkArgumentsOption = cmd: mkOption {
+    type = types.listOf types.str;
+    default = argumentsOf cmd;
+    description = ''
+      Arguments to the <literal>thanos ${cmd}</literal> command.
+
+      Defaults to a list of arguments formed by converting the structured
+      options of <option>services.thanos.${cmd}</option> to a list of arguments.
+
+      Overriding this option will cause none of the structured options to have
+      any effect. So only set this if you know what you're doing!
+    '';
+  };
+
+  mapParamsRecursive =
+    let noParam = attr: !(attr ? "toArgs" && attr ? "option");
+    in mapAttrsRecursiveCond noParam;
+
+  paramsToOptions = mapParamsRecursive (_path: param: param.option);
+
+  params = {
+
+    log = {
+
+      log.level = mkParamDef (types.enum ["debug" "info" "warn" "error" "fatal"]) "info" ''
+        Log filtering level.
+      '';
+
+      log.format = mkParam types.str ''
+        Log format to use.
+      '';
+    };
+
+    tracing = cfg: {
+      tracing.config-file = {
+        toArgs = _opt: path: optionToArgs "tracing.config-file" path;
+        option = mkOption {
+          type = with types; nullOr str;
+          default = if cfg.tracing.config == null then null
+                    else toString (toYAML "tracing.yaml" cfg.tracing.config);
+          defaultText = ''
+            if config.services.thanos.<cmd>.tracing.config == null then null
+            else toString (toYAML "tracing.yaml" config.services.thanos.<cmd>.tracing.config);
+          '';
+          description = ''
+            Path to YAML file that contains tracing configuration.
+          '';
+        };
+      };
+
+      tracing.config =
+        {
+          toArgs = _opt: _attrs: [];
+          option = nullOpt types.attrs ''
+            Tracing configuration.
+
+            When not <literal>null</literal> the attribute set gets converted to
+            a YAML file and stored in the Nix store. The option
+            <option>tracing.config-file</option> will default to its path.
+
+            If <option>tracing.config-file</option> is set this option has no effect.
+          '';
+        };
+    };
+
+    common = cfg: params.log // params.tracing cfg // {
+
+      http-address = mkParamDef types.str "0.0.0.0:10902" ''
+        Listen <literal>host:port</literal> for HTTP endpoints.
+      '';
+
+      grpc-address = mkParamDef types.str "0.0.0.0:10901" ''
+        Listen <literal>ip:port</literal> address for gRPC endpoints (StoreAPI).
+
+        Make sure this address is routable from other components.
+      '';
+
+      grpc-server-tls-cert = mkParam types.str ''
+        TLS Certificate for gRPC server, leave blank to disable TLS
+      '';
+
+      grpc-server-tls-key = mkParam types.str ''
+        TLS Key for the gRPC server, leave blank to disable TLS
+      '';
+
+      grpc-server-tls-client-ca = mkParam types.str ''
+        TLS CA to verify clients against.
+
+        If no client CA is specified, there is no client verification on server side.
+        (tls.NoClientCert)
+      '';
+    };
+
+    objstore = cfg: {
+
+      objstore.config-file = {
+        toArgs = _opt: path: optionToArgs "objstore.config-file" path;
+        option = mkOption {
+          type = with types; nullOr str;
+          default = if cfg.objstore.config == null then null
+                    else toString (toYAML "objstore.yaml" cfg.objstore.config);
+          defaultText = ''
+            if config.services.thanos.<cmd>.objstore.config == null then null
+            else toString (toYAML "objstore.yaml" config.services.thanos.<cmd>.objstore.config);
+          '';
+          description = ''
+            Path to YAML file that contains object store configuration.
+          '';
+        };
+      };
+
+      objstore.config =
+        {
+          toArgs = _opt: _attrs: [];
+          option = nullOpt types.attrs ''
+            Object store configuration.
+
+            When not <literal>null</literal> the attribute set gets converted to
+            a YAML file and stored in the Nix store. The option
+            <option>objstore.config-file</option> will default to its path.
+
+            If <option>objstore.config-file</option> is set this option has no effect.
+          '';
+        };
+    };
+
+    sidecar = params.common cfg.sidecar // params.objstore cfg.sidecar // {
+
+      prometheus.url = mkParamDef types.str "http://localhost:9090" ''
+        URL at which to reach Prometheus's API.
+
+        For better performance use local network.
+      '';
+
+      tsdb.path = {
+        toArgs = optionToArgs;
+        option = mkOption {
+          type = types.str;
+          default = "/var/lib/${config.services.prometheus2.stateDir}/data";
+          defaultText = "/var/lib/\${config.services.prometheus2.stateDir}/data";
+          description = ''
+            Data directory of TSDB.
+          '';
+        };
+      };
+
+      reloader.config-file = mkParam types.str ''
+        Config file watched by the reloader.
+      '';
+
+      reloader.config-envsubst-file = mkParam types.str ''
+        Output file for environment variable substituted config file.
+      '';
+
+      reloader.rule-dirs = mkListParam "reloader.rule-dir" ''
+        Rule directories for the reloader to refresh.
+      '';
+
+    };
+
+    store = params.common cfg.store // params.objstore cfg.store // {
+
+      stateDir = mkStateDirParam "data-dir" "thanos-store" ''
+        Data directory relative to <literal>/var/lib</literal>
+        in which to cache remote blocks.
+      '';
+
+      index-cache-size = mkParamDef types.str "250MB" ''
+        Maximum size of items held in the index cache.
+      '';
+
+      chunk-pool-size = mkParamDef types.str "2GB" ''
+        Maximum size of concurrently allocatable bytes for chunks.
+      '';
+
+      store.grpc.series-sample-limit = mkParamDef types.int 0 ''
+        Maximum amount of samples returned via a single Series call.
+
+        <literal>0</literal> means no limit.
+
+        NOTE: for efficiency we take 120 as the number of samples in chunk (it
+        cannot be bigger than that), so the actual number of samples might be
+        lower, even though the maximum could be hit.
+      '';
+
+      store.grpc.series-max-concurrency = mkParamDef types.int 20 ''
+        Maximum number of concurrent Series calls.
+      '';
+
+      sync-block-duration = mkParamDef types.str "3m" ''
+        Repeat interval for syncing the blocks between local and remote view.
+      '';
+
+      block-sync-concurrency = mkParamDef types.int 20 ''
+        Number of goroutines to use when syncing blocks from object storage.
+      '';
+    };
+
+    query = params.common cfg.query // {
+
+      grpc-client-tls-secure = mkFlagParam ''
+        Use TLS when talking to the gRPC server
+      '';
+
+      grpc-client-tls-cert = mkParam types.str ''
+        TLS Certificates to use to identify this client to the server
+      '';
+
+      grpc-client-tls-key = mkParam types.str ''
+        TLS Key for the client's certificate
+      '';
+
+      grpc-client-tls-ca = mkParam types.str ''
+        TLS CA Certificates to use to verify gRPC servers
+      '';
+
+      grpc-client-server-name = mkParam types.str ''
+        Server name to verify the hostname on the returned gRPC certificates.
+        See <link xlink:href="https://tools.ietf.org/html/rfc4366#section-3.1"/>
+      '';
+
+      web.route-prefix = mkParam types.str ''
+        Prefix for API and UI endpoints.
+
+        This allows thanos UI to be served on a sub-path. This option is
+        analogous to <option>web.route-prefix</option> of Promethus.
+      '';
+
+      web.external-prefix = mkParam types.str ''
+        Static prefix for all HTML links and redirect URLs in the UI query web
+        interface.
+
+        Actual endpoints are still served on / or the
+        <option>web.route-prefix</option>. This allows thanos UI to be served
+        behind a reverse proxy that strips a URL sub-path.
+      '';
+
+      web.prefix-header = mkParam types.str ''
+        Name of HTTP request header used for dynamic prefixing of UI links and
+        redirects.
+
+        This option is ignored if the option
+        <literal>web.external-prefix</literal> is set.
+
+        Security risk: enable this option only if a reverse proxy in front of
+        thanos is resetting the header.
+
+        The setting <literal>web.prefix-header="X-Forwarded-Prefix"</literal>
+        can be useful, for example, if Thanos UI is served via Traefik reverse
+        proxy with <literal>PathPrefixStrip</literal> option enabled, which
+        sends the stripped prefix value in <literal>X-Forwarded-Prefix</literal>
+        header. This allows thanos UI to be served on a sub-path.
+      '';
+
+      query.timeout = mkParamDef types.str "2m" ''
+        Maximum time to process query by query node.
+      '';
+
+      query.max-concurrent = mkParamDef types.int 20 ''
+        Maximum number of queries processed concurrently by query node.
+      '';
+
+      query.replica-label = mkParam types.str ''
+        Label to treat as a replica indicator along which data is
+        deduplicated.
+
+        Still you will be able to query without deduplication using
+        <literal>dedup=false</literal> parameter.
+      '';
+
+      selector-labels = mkAttrsParam "selector-label" ''
+        Query selector labels that will be exposed in info endpoint.
+      '';
+
+      store.addresses = mkListParam "store" ''
+        Addresses of statically configured store API servers.
+
+        The scheme may be prefixed with <literal>dns+</literal> or
+        <literal>dnssrv+</literal> to detect store API servers through
+        respective DNS lookups.
+      '';
+
+      store.sd-files = mkListParam "store.sd-files" ''
+        Path to files that contain addresses of store API servers. The path
+        can be a glob pattern.
+      '';
+
+      store.sd-interval = mkParamDef types.str "5m" ''
+        Refresh interval to re-read file SD files. It is used as a resync fallback.
+      '';
+
+      store.sd-dns-interval = mkParamDef types.str "30s" ''
+        Interval between DNS resolutions.
+      '';
+
+      store.unhealthy-timeout = mkParamDef types.str "5m" ''
+        Timeout before an unhealthy store is cleaned from the store UI page.
+      '';
+
+      query.auto-downsampling = mkFlagParam ''
+        Enable automatic adjustment (step / 5) to what source of data should
+        be used in store gateways if no
+        <literal>max_source_resolution</literal> param is specified.
+      '';
+
+      query.partial-response = mkFlagParam ''
+        Enable partial response for queries if no
+        <literal>partial_response</literal> param is specified.
+      '';
+
+      query.default-evaluation-interval = mkParamDef types.str "1m" ''
+        Set default evaluation interval for sub queries.
+      '';
+
+      store.response-timeout = mkParamDef types.str "0ms" ''
+        If a Store doesn't send any data in this specified duration then a
+        Store will be ignored and partial data will be returned if it's
+        enabled. <literal>0</literal> disables timeout.
+      '';
+    };
+
+    rule = params.common cfg.rule // params.objstore cfg.rule // {
+
+      labels = mkAttrsParam "label" ''
+        Labels to be applied to all generated metrics.
+
+        Similar to external labels for Prometheus,
+        used to identify ruler and its blocks as unique source.
+      '';
+
+      stateDir = mkStateDirParam "data-dir" "thanos-rule" ''
+        Data directory relative to <literal>/var/lib</literal>.
+      '';
+
+      rule-files = mkListParam "rule-file" ''
+        Rule files that should be used by rule manager. Can be in glob format.
+      '';
+
+      eval-interval = mkParamDef types.str "30s" ''
+        The default evaluation interval to use.
+      '';
+
+      tsdb.block-duration = mkParamDef types.str "2h" ''
+        Block duration for TSDB block.
+      '';
+
+      tsdb.retention = mkParamDef types.str "48h" ''
+        Block retention time on local disk.
+      '';
+
+      alertmanagers.urls = mkListParam "alertmanagers.url" ''
+        Alertmanager replica URLs to push firing alerts.
+
+        Ruler claims success if push to at least one alertmanager from
+        discovered succeeds. The scheme may be prefixed with
+        <literal>dns+</literal> or <literal>dnssrv+</literal> to detect
+        Alertmanager IPs through respective DNS lookups. The port defaults to
+        <literal>9093</literal> or the SRV record's value. The URL path is
+        used as a prefix for the regular Alertmanager API path.
+      '';
+
+      alertmanagers.send-timeout = mkParamDef types.str "10s" ''
+        Timeout for sending alerts to alertmanager.
+      '';
+
+      alert.query-url = mkParam types.str ''
+        The external Thanos Query URL that would be set in all alerts 'Source' field.
+      '';
+
+      alert.label-drop = mkListParam "alert.label-drop" ''
+        Labels by name to drop before sending to alertmanager.
+
+        This allows alert to be deduplicated on replica label.
+
+        Similar Prometheus alert relabelling
+      '';
+
+      web.route-prefix = mkParam types.str ''
+        Prefix for API and UI endpoints.
+
+        This allows thanos UI to be served on a sub-path.
+
+        This option is analogous to <literal>--web.route-prefix</literal> of Promethus.
+      '';
+
+      web.external-prefix = mkParam types.str ''
+        Static prefix for all HTML links and redirect URLs in the UI query web
+        interface.
+
+        Actual endpoints are still served on / or the
+        <option>web.route-prefix</option>. This allows thanos UI to be served
+        behind a reverse proxy that strips a URL sub-path.
+      '';
+
+      web.prefix-header = mkParam types.str ''
+        Name of HTTP request header used for dynamic prefixing of UI links and
+        redirects.
+
+        This option is ignored if the option
+        <option>web.external-prefix</option> is set.
+
+        Security risk: enable this option only if a reverse proxy in front of
+        thanos is resetting the header.
+
+        The header <literal>X-Forwarded-Prefix</literal> can be useful, for
+        example, if Thanos UI is served via Traefik reverse proxy with
+        <literal>PathPrefixStrip</literal> option enabled, which sends the
+        stripped prefix value in <literal>X-Forwarded-Prefix</literal>
+        header. This allows thanos UI to be served on a sub-path.
+      '';
+
+      query.addresses = mkListParam "query" ''
+        Addresses of statically configured query API servers.
+
+        The scheme may be prefixed with <literal>dns+</literal> or
+        <literal>dnssrv+</literal> to detect query API servers through
+        respective DNS lookups.
+      '';
+
+      query.sd-files = mkListParam "query.sd-files" ''
+        Path to file that contain addresses of query peers.
+        The path can be a glob pattern.
+      '';
+
+      query.sd-interval = mkParamDef types.str "5m" ''
+        Refresh interval to re-read file SD files. (used as a fallback)
+      '';
+
+      query.sd-dns-interval = mkParamDef types.str "30s" ''
+        Interval between DNS resolutions.
+      '';
+    };
+
+    compact = params.log // params.tracing cfg.compact // params.objstore cfg.compact // {
+
+      http-address = mkParamDef types.str "0.0.0.0:10902" ''
+        Listen <literal>host:port</literal> for HTTP endpoints.
+      '';
+
+      stateDir = mkStateDirParam "data-dir" "thanos-compact" ''
+        Data directory relative to <literal>/var/lib</literal>
+        in which to cache blocks and process compactions.
+      '';
+
+      consistency-delay = mkParamDef types.str "30m" ''
+        Minimum age of fresh (non-compacted) blocks before they are being
+        processed. Malformed blocks older than the maximum of consistency-delay
+        and 30m0s will be removed.
+      '';
+
+      retention.resolution-raw = mkParamDef types.str "0d" ''
+        How long to retain raw samples in bucket.
+
+        <literal>0d</literal> - disables this retention
+      '';
+
+      retention.resolution-5m = mkParamDef types.str "0d" ''
+        How long to retain samples of resolution 1 (5 minutes) in bucket.
+
+        <literal>0d</literal> - disables this retention
+      '';
+
+      retention.resolution-1h = mkParamDef types.str "0d" ''
+        How long to retain samples of resolution 2 (1 hour) in bucket.
+
+        <literal>0d</literal> - disables this retention
+      '';
+
+      startAt = {
+        toArgs = _opt: startAt: flagToArgs "wait" (startAt == null);
+        option = nullOpt types.str ''
+          When this option is set to a <literal>systemd.time</literal>
+          specification the Thanos compactor will run at the specified period.
+
+          When this option is <literal>null</literal> the Thanos compactor service
+          will run continuously. So it will not exit after all compactions have
+          been processed but wait for new work.
+        '';
+      };
+
+      block-sync-concurrency = mkParamDef types.int 20 ''
+        Number of goroutines to use when syncing block metadata from object storage.
+      '';
+
+      compact.concurrency = mkParamDef types.int 1 ''
+        Number of goroutines to use when compacting groups.
+      '';
+    };
+
+    downsample = params.log // params.tracing cfg.downsample // params.objstore cfg.downsample // {
+
+      stateDir = mkStateDirParam "data-dir" "thanos-downsample" ''
+        Data directory relative to <literal>/var/lib</literal>
+        in which to cache blocks and process downsamplings.
+      '';
+
+    };
+
+    receive = params.common cfg.receive // params.objstore cfg.receive // {
+
+      remote-write.address = mkParamDef types.str "0.0.0.0:19291" ''
+        Address to listen on for remote write requests.
+      '';
+
+      stateDir = mkStateDirParam "tsdb.path" "thanos-receive" ''
+        Data directory relative to <literal>/var/lib</literal> of TSDB.
+      '';
+
+      labels = mkAttrsParam "labels" ''
+        External labels to announce.
+
+        This flag will be removed in the future when handling multiple tsdb
+        instances is added.
+      '';
+
+      tsdb.retention = mkParamDef types.str "15d" ''
+        How long to retain raw samples on local storage.
+
+        <literal>0d</literal> - disables this retention
+      '';
+    };
+
+  };
+
+  assertRelativeStateDir = cmd: {
+    assertions = [
+      {
+        assertion = !hasPrefix "/" cfg."${cmd}".stateDir;
+        message =
+          "The option services.thanos.${cmd}.stateDir should not be an absolute directory." +
+          " It should be a directory relative to /var/lib.";
+      }
+    ];
+  };
+
+in {
+
+  options.services.thanos = {
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.thanos;
+      defaultText = "pkgs.thanos";
+      description = ''
+        The thanos package that should be used.
+      '';
+    };
+
+    sidecar = paramsToOptions params.sidecar // {
+      enable = mkEnableOption
+        "the Thanos sidecar for Prometheus server";
+      arguments = mkArgumentsOption "sidecar";
+    };
+
+    store = paramsToOptions params.store // {
+      enable = mkEnableOption
+        "the Thanos store node giving access to blocks in a bucket provider.";
+      arguments = mkArgumentsOption "store";
+    };
+
+    query = paramsToOptions params.query // {
+      enable = mkEnableOption
+        ("the Thanos query node exposing PromQL enabled Query API " +
+         "with data retrieved from multiple store nodes");
+      arguments = mkArgumentsOption "query";
+    };
+
+    rule = paramsToOptions params.rule // {
+      enable = mkEnableOption
+        ("the Thanos ruler service which evaluates Prometheus rules against" +
+        " given Query nodes, exposing Store API and storing old blocks in bucket");
+      arguments = mkArgumentsOption "rule";
+    };
+
+    compact = paramsToOptions params.compact // {
+      enable = mkEnableOption
+        "the Thanos compactor which continuously compacts blocks in an object store bucket";
+      arguments = mkArgumentsOption "compact";
+    };
+
+    downsample = paramsToOptions params.downsample // {
+      enable = mkEnableOption
+        "the Thanos downsampler which continuously downsamples blocks in an object store bucket";
+      arguments = mkArgumentsOption "downsample";
+    };
+
+    receive = paramsToOptions params.receive // {
+      enable = mkEnableOption
+        ("the Thanos receiver which accept Prometheus remote write API requests " +
+         "and write to local tsdb (EXPERIMENTAL, this may change drastically without notice)");
+      arguments = mkArgumentsOption "receive";
+    };
+  };
+
+  config = mkMerge [
+
+    (mkIf cfg.sidecar.enable {
+      assertions = [
+        {
+          assertion = config.services.prometheus2.enable;
+          message =
+            "Please enable services.prometheus2 when enabling services.thanos.sidecar.";
+        }
+        {
+          assertion = !(config.services.prometheus2.globalConfig.external_labels == null ||
+                        config.services.prometheus2.globalConfig.external_labels == {});
+          message =
+            "services.thanos.sidecar requires uniquely identifying external labels " +
+            "to be configured in the Prometheus server. " +
+            "Please set services.prometheus2.globalConfig.external_labels.";
+        }
+      ];
+      systemd.services.thanos-sidecar = {
+        wantedBy = [ "multi-user.target" ];
+        after    = [ "network.target" "prometheus2.service" ];
+        serviceConfig = {
+          User = "prometheus";
+          Restart = "always";
+          ExecStart = thanos "sidecar";
+        };
+      };
+    })
+
+    (mkIf cfg.store.enable (mkMerge [
+      (assertRelativeStateDir "store")
+      {
+        systemd.services.thanos-store = {
+          wantedBy = [ "multi-user.target" ];
+          after    = [ "network.target" ];
+          serviceConfig = {
+            DynamicUser = true;
+            StateDirectory = cfg.store.stateDir;
+            Restart = "always";
+            ExecStart = thanos "store";
+          };
+        };
+      }
+    ]))
+
+    (mkIf cfg.query.enable {
+      systemd.services.thanos-query = {
+        wantedBy = [ "multi-user.target" ];
+        after    = [ "network.target" ];
+        serviceConfig = {
+          DynamicUser = true;
+          Restart = "always";
+          ExecStart = thanos "query";
+        };
+      };
+    })
+
+    (mkIf cfg.rule.enable (mkMerge [
+      (assertRelativeStateDir "rule")
+      {
+        systemd.services.thanos-rule = {
+          wantedBy = [ "multi-user.target" ];
+          after    = [ "network.target" ];
+          serviceConfig = {
+            DynamicUser = true;
+            StateDirectory = cfg.rule.stateDir;
+            Restart = "always";
+            ExecStart = thanos "rule";
+          };
+        };
+      }
+    ]))
+
+    (mkIf cfg.compact.enable (mkMerge [
+      (assertRelativeStateDir "compact")
+      {
+        systemd.services.thanos-compact =
+          let wait = cfg.compact.startAt == null; in {
+            wantedBy = [ "multi-user.target" ];
+            after    = [ "network.target" ];
+            serviceConfig = {
+              Type    = if wait then "simple" else "oneshot";
+              Restart = if wait then "always" else "no";
+              DynamicUser = true;
+              StateDirectory = cfg.compact.stateDir;
+              ExecStart = thanos "compact";
+            };
+          } // optionalAttrs (!wait) { inherit (cfg.compact) startAt; };
+      }
+    ]))
+
+    (mkIf cfg.downsample.enable (mkMerge [
+      (assertRelativeStateDir "downsample")
+      {
+        systemd.services.thanos-downsample = {
+          wantedBy = [ "multi-user.target" ];
+          after    = [ "network.target" ];
+          serviceConfig = {
+            DynamicUser = true;
+            StateDirectory = cfg.downsample.stateDir;
+            Restart = "always";
+            ExecStart = thanos "downsample";
+          };
+        };
+      }
+    ]))
+
+    (mkIf cfg.receive.enable (mkMerge [
+      (assertRelativeStateDir "receive")
+      {
+        systemd.services.thanos-receive = {
+          wantedBy = [ "multi-user.target" ];
+          after    = [ "network.target" ];
+          serviceConfig = {
+            DynamicUser = true;
+            StateDirectory = cfg.receive.stateDir;
+            Restart = "always";
+            ExecStart = thanos "receive";
+          };
+        };
+      }
+    ]))
+
+  ];
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/zabbix-agent.nix b/nixpkgs/nixos/modules/services/monitoring/zabbix-agent.nix
index 0519e7c2ad6a..b1645f861101 100644
--- a/nixpkgs/nixos/modules/services/monitoring/zabbix-agent.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/zabbix-agent.nix
@@ -1,73 +1,118 @@
-# Zabbix agent daemon.
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
-
   cfg = config.services.zabbixAgent;
 
-  zabbix = cfg.package;
-
-  stateDir = "/run/zabbix";
-
-  logDir = "/var/log/zabbix";
-
-  pidFile = "${stateDir}/zabbix_agentd.pid";
+  inherit (lib) mkDefault mkEnableOption mkIf mkOption;
+  inherit (lib) attrValues concatMapStringsSep literalExample optionalString types;
 
-  configFile = pkgs.writeText "zabbix_agentd.conf"
-    ''
-      Server = ${cfg.server}
+  user = "zabbix-agent";
+  group = "zabbix-agent";
 
-      LogFile = ${logDir}/zabbix_agentd
-
-      PidFile = ${pidFile}
-
-      StartAgents = 1
+  moduleEnv = pkgs.symlinkJoin {
+    name = "zabbix-agent-module-env";
+    paths = attrValues cfg.modules;
+  };
 
-      ${config.services.zabbixAgent.extraConfig}
-    '';
+  configFile = pkgs.writeText "zabbix_agent.conf" ''
+    LogType = console
+    Server = ${cfg.server}
+    ListenIP = ${cfg.listen.ip}
+    ListenPort = ${toString cfg.listen.port}
+    ${optionalString (cfg.modules != {}) "LoadModulePath = ${moduleEnv}/lib"}
+    ${concatMapStringsSep "\n" (name: "LoadModule = ${name}") (builtins.attrNames cfg.modules)}
+    ${cfg.extraConfig}
+  '';
 
 in
 
 {
-
-  ###### interface
+  # interface
 
   options = {
 
     services.zabbixAgent = {
+      enable = mkEnableOption "the Zabbix Agent";
 
-      enable = mkOption {
-        default = false;
+      package = mkOption {
+        type = types.package;
+        default = pkgs.zabbix.agent;
+        defaultText = "pkgs.zabbix.agent";
+        description = "The Zabbix package to use.";
+      };
+
+      extraPackages = mkOption {
+        type = types.listOf types.package;
+        default = with pkgs; [ nettools ];
+        defaultText = "[ nettools ]";
+        example = "[ nettools mysql ]";
         description = ''
-          Whether to run the Zabbix monitoring agent on this machine.
-          It will send monitoring data to a Zabbix server.
+          Packages to be added to the Zabbix <envar>PATH</envar>.
+          Typically used to add executables for scripts, but can be anything.
         '';
       };
 
-      package = mkOption {
-        type = types.attrs; # Note: pkgs.zabbixXY isn't a derivation, but an attrset of { server = ...; agent = ...; }.
-        default = pkgs.zabbix;
-        defaultText = "pkgs.zabbix";
-        example = literalExample "pkgs.zabbix34";
-        description = ''
-          The Zabbix package to use.
+      modules = mkOption {
+        type = types.attrsOf types.package;
+        description = "A set of modules to load.";
+        default = {};
+        example = literalExample ''
+          {
+            "dummy.so" = pkgs.stdenv.mkDerivation {
+              name = "zabbix-dummy-module-''${cfg.package.version}";
+              src = cfg.package.src;
+              buildInputs = [ cfg.package ];
+              sourceRoot = "zabbix-''${cfg.package.version}/src/modules/dummy";
+              installPhase = '''
+                mkdir -p $out/lib
+                cp dummy.so $out/lib/
+              ''';
+            };
+          }
         '';
       };
 
       server = mkOption {
-        default = "127.0.0.1";
+        type = types.str;
         description = ''
           The IP address or hostname of the Zabbix server to connect to.
         '';
       };
 
+      listen = {
+        ip = mkOption {
+          type = types.str;
+          default = "0.0.0.0";
+          description = ''
+            List of comma delimited IP addresses that the agent should listen on.
+          '';
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 10050;
+          description = ''
+            Agent will listen on this port for connections from the server.
+          '';
+        };
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open ports in the firewall for the Zabbix Agent.
+        '';
+      };
+
+      # TODO: for bonus points migrate this to https://github.com/NixOS/rfcs/pull/42
       extraConfig = mkOption {
         default = "";
         type = types.lines;
         description = ''
-          Configuration that is injected verbatim into the configuration file.
+          Configuration that is injected verbatim into the configuration file. Refer to
+          <link xlink:href="https://www.zabbix.com/documentation/current/manual/appendix/config/zabbix_agentd"/>
+          for details on supported values.
         '';
       };
 
@@ -75,38 +120,38 @@ in
 
   };
 
-
-  ###### implementation
+  # implementation
 
   config = mkIf cfg.enable {
 
-    users.users = mkIf (!config.services.zabbixServer.enable) (singleton
-      { name = "zabbix";
-        uid = config.ids.uids.zabbix;
-        description = "Zabbix daemon user";
-      });
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.listen.port ];
+    };
 
-    systemd.services."zabbix-agent" =
-      { description = "Zabbix Agent";
+    users.users.${user} = {
+      description = "Zabbix Agent daemon user";
+      inherit group;
+    };
 
-        wantedBy = [ "multi-user.target" ];
+    users.groups.${group} = { };
 
-        path = [ pkgs.nettools ];
+    systemd.services."zabbix-agent" = {
+      description = "Zabbix Agent";
 
-        preStart =
-          ''
-            mkdir -m 0755 -p ${stateDir} ${logDir}
-            chown zabbix ${stateDir} ${logDir}
-          '';
+      wantedBy = [ "multi-user.target" ];
 
-        serviceConfig.ExecStart = "@${zabbix.agent}/sbin/zabbix_agentd zabbix_agentd --config ${configFile}";
-        serviceConfig.Type = "forking";
-        serviceConfig.RemainAfterExit = true;
-        serviceConfig.Restart = "always";
-        serviceConfig.RestartSec = 2;
-      };
+      path = [ "/run/wrappers" ] ++ cfg.extraPackages;
+
+      serviceConfig = {
+        ExecStart = "@${cfg.package}/sbin/zabbix_agentd zabbix_agentd -f --config ${configFile}";
+        Restart = "always";
+        RestartSec = 2;
 
-    environment.systemPackages = [ zabbix.agent ];
+        User = user;
+        Group = group;
+        PrivateTmp = true;
+      };
+    };
 
   };
 
diff --git a/nixpkgs/nixos/modules/services/monitoring/zabbix-proxy.nix b/nixpkgs/nixos/modules/services/monitoring/zabbix-proxy.nix
new file mode 100644
index 000000000000..9cfcd1697c11
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/zabbix-proxy.nix
@@ -0,0 +1,298 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.zabbixProxy;
+  pgsql = config.services.postgresql;
+  mysql = config.services.mysql;
+
+  inherit (lib) mkDefault mkEnableOption mkIf mkOption;
+  inherit (lib) attrValues concatMapStringsSep literalExample optional optionalAttrs optionalString types;
+
+  user = "zabbix";
+  group = "zabbix";
+  runtimeDir = "/run/zabbix";
+  stateDir = "/var/lib/zabbix";
+  passwordFile = "${runtimeDir}/zabbix-dbpassword.conf";
+
+  moduleEnv = pkgs.symlinkJoin {
+    name = "zabbix-proxy-module-env";
+    paths = attrValues cfg.modules;
+  };
+
+  configFile = pkgs.writeText "zabbix_proxy.conf" ''
+    LogType = console
+    ListenIP = ${cfg.listen.ip}
+    ListenPort = ${toString cfg.listen.port}
+    Server = ${cfg.server}
+    # TODO: set to cfg.database.socket if database type is pgsql?
+    DBHost = ${optionalString (cfg.database.createLocally != true) cfg.database.host}
+    ${optionalString (cfg.database.createLocally != true) "DBPort = ${cfg.database.port}"}
+    DBName = ${cfg.database.name}
+    DBUser = ${cfg.database.user}
+    ${optionalString (cfg.database.passwordFile != null) "Include ${passwordFile}"}
+    ${optionalString (mysqlLocal && cfg.database.socket != null) "DBSocket = ${cfg.database.socket}"}
+    SocketDir = ${runtimeDir}
+    FpingLocation = /run/wrappers/bin/fping
+    ${optionalString (cfg.modules != {}) "LoadModulePath = ${moduleEnv}/lib"}
+    ${concatMapStringsSep "\n" (name: "LoadModule = ${name}") (builtins.attrNames cfg.modules)}
+    ${cfg.extraConfig}
+  '';
+
+  mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
+  pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
+
+in
+
+{
+  # interface
+
+  options = {
+
+    services.zabbixProxy = {
+      enable = mkEnableOption "the Zabbix Proxy";
+
+      server = mkOption {
+        type = types.str;
+        description = ''
+          The IP address or hostname of the Zabbix server to connect to.
+          '';
+        };
+
+      package = mkOption {
+        type = types.package;
+        default =
+          if cfg.database.type == "mysql" then pkgs.zabbix.proxy-mysql
+          else if cfg.database.type == "pgsql" then pkgs.zabbix.proxy-pgsql
+          else pkgs.zabbix.proxy-sqlite;
+        defaultText = "pkgs.zabbix.proxy-pgsql";
+        description = "The Zabbix package to use.";
+      };
+
+      extraPackages = mkOption {
+        type = types.listOf types.package;
+        default = with pkgs; [ nettools nmap traceroute ];
+        defaultText = "[ nettools nmap traceroute ]";
+        description = ''
+          Packages to be added to the Zabbix <envar>PATH</envar>.
+          Typically used to add executables for scripts, but can be anything.
+        '';
+      };
+
+      modules = mkOption {
+        type = types.attrsOf types.package;
+        description = "A set of modules to load.";
+        default = {};
+        example = literalExample ''
+          {
+            "dummy.so" = pkgs.stdenv.mkDerivation {
+              name = "zabbix-dummy-module-''${cfg.package.version}";
+              src = cfg.package.src;
+              buildInputs = [ cfg.package ];
+              sourceRoot = "zabbix-''${cfg.package.version}/src/modules/dummy";
+              installPhase = '''
+                mkdir -p $out/lib
+                cp dummy.so $out/lib/
+              ''';
+            };
+          }
+        '';
+      };
+
+      database = {
+        type = mkOption {
+          type = types.enum [ "mysql" "pgsql" "sqlite" ];
+          example = "mysql";
+          default = "pgsql";
+          description = "Database engine to use.";
+        };
+
+        host = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = "Database host address.";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = if cfg.database.type == "mysql" then mysql.port else pgsql.port;
+          description = "Database host port.";
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "zabbix";
+          description = "Database name.";
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "zabbix";
+          description = "Database user.";
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/keys/zabbix-dbpassword";
+          description = ''
+            A file containing the password corresponding to
+            <option>database.user</option>.
+          '';
+        };
+
+        socket = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/postgresql";
+          description = "Path to the unix socket file to use for authentication.";
+        };
+
+        createLocally = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Whether to create a local database automatically.";
+        };
+      };
+
+      listen = {
+        ip = mkOption {
+          type = types.str;
+          default = "0.0.0.0";
+          description = ''
+            List of comma delimited IP addresses that the trapper should listen on.
+            Trapper will listen on all network interfaces if this parameter is missing.
+          '';
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 10051;
+          description = ''
+            Listen port for trapper.
+          '';
+        };
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open ports in the firewall for the Zabbix Proxy.
+        '';
+      };
+
+      # TODO: for bonus points migrate this to https://github.com/NixOS/rfcs/pull/42
+      extraConfig = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''
+          Configuration that is injected verbatim into the configuration file. Refer to
+          <link xlink:href="https://www.zabbix.com/documentation/current/manual/appendix/config/zabbix_proxy"/>
+          for details on supported values.
+        '';
+      };
+
+    };
+
+  };
+
+  # implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = !config.services.zabbixServer.enable;
+        message = "Please choose one of services.zabbixServer or services.zabbixProxy.";
+      }
+      { assertion = cfg.database.createLocally -> cfg.database.user == user;
+        message = "services.zabbixProxy.database.user must be set to ${user} if services.zabbixProxy.database.createLocally is set true";
+      }
+      { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
+        message = "a password cannot be specified if services.zabbixProxy.database.createLocally is set to true";
+      }
+    ];
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.listen.port ];
+    };
+
+    services.mysql = optionalAttrs mysqlLocal {
+      enable = true;
+      package = mkDefault pkgs.mariadb;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        { name = cfg.database.user;
+          ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
+        }
+      ];
+    };
+
+    services.postgresql = optionalAttrs pgsqlLocal {
+      enable = true;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        { name = cfg.database.user;
+          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+        }
+      ];
+    };
+
+    users.users.${user} = {
+      description = "Zabbix daemon user";
+      uid = config.ids.uids.zabbix;
+      inherit group;
+    };
+
+    users.groups.${group} = {
+      gid = config.ids.gids.zabbix;
+    };
+
+    security.wrappers = {
+      fping.source = "${pkgs.fping}/bin/fping";
+    };
+
+    systemd.services."zabbix-proxy" = {
+      description = "Zabbix Proxy";
+
+      wantedBy = [ "multi-user.target" ];
+      after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
+
+      path = [ "/run/wrappers" ] ++ cfg.extraPackages;
+      preStart = optionalString pgsqlLocal ''
+        if ! test -e "${stateDir}/db-created"; then
+          cat ${cfg.package}/share/zabbix/database/postgresql/schema.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/postgresql/images.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/postgresql/data.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+          touch "${stateDir}/db-created"
+        fi
+      '' + optionalString mysqlLocal ''
+        if ! test -e "${stateDir}/db-created"; then
+          cat ${cfg.package}/share/zabbix/database/mysql/schema.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/mysql/images.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/mysql/data.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+          touch "${stateDir}/db-created"
+        fi
+      '' + optionalString (cfg.database.passwordFile != null) ''
+        # create a copy of the supplied password file in a format zabbix can consume
+        touch ${passwordFile}
+        chmod 0600 ${passwordFile}
+        echo -n "DBPassword = " > ${passwordFile}
+        cat ${cfg.database.passwordFile} >> ${passwordFile}
+      '';
+
+      serviceConfig = {
+        ExecStart = "@${cfg.package}/sbin/zabbix_proxy zabbix_proxy -f --config ${configFile}";
+        Restart = "always";
+        RestartSec = 2;
+
+        User = user;
+        Group = group;
+        RuntimeDirectory = "zabbix";
+        StateDirectory = "zabbix";
+        PrivateTmp = true;
+      };
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/zabbix-server.nix b/nixpkgs/nixos/modules/services/monitoring/zabbix-server.nix
index fdeab6af4417..11311b466c3f 100644
--- a/nixpkgs/nixos/modules/services/monitoring/zabbix-server.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/zabbix-server.nix
@@ -1,125 +1,292 @@
-# Zabbix server daemon.
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
-
   cfg = config.services.zabbixServer;
+  pgsql = config.services.postgresql;
+  mysql = config.services.mysql;
 
-  stateDir = "/run/zabbix";
+  inherit (lib) mkDefault mkEnableOption mkIf mkOption;
+  inherit (lib) attrValues concatMapStringsSep literalExample optional optionalAttrs optionalString types;
 
-  logDir = "/var/log/zabbix";
+  user = "zabbix";
+  group = "zabbix";
+  runtimeDir = "/run/zabbix";
+  stateDir = "/var/lib/zabbix";
+  passwordFile = "${runtimeDir}/zabbix-dbpassword.conf";
 
-  libDir = "/var/lib/zabbix";
+  moduleEnv = pkgs.symlinkJoin {
+    name = "zabbix-server-module-env";
+    paths = attrValues cfg.modules;
+  };
 
-  pidFile = "${stateDir}/zabbix_server.pid";
+  configFile = pkgs.writeText "zabbix_server.conf" ''
+    LogType = console
+    ListenIP = ${cfg.listen.ip}
+    ListenPort = ${toString cfg.listen.port}
+    # TODO: set to cfg.database.socket if database type is pgsql?
+    DBHost = ${optionalString (cfg.database.createLocally != true) cfg.database.host}
+    ${optionalString (cfg.database.createLocally != true) "DBPort = ${cfg.database.port}"}
+    DBName = ${cfg.database.name}
+    DBUser = ${cfg.database.user}
+    ${optionalString (cfg.database.passwordFile != null) "Include ${passwordFile}"}
+    ${optionalString (mysqlLocal && cfg.database.socket != null) "DBSocket = ${cfg.database.socket}"}
+    SocketDir = ${runtimeDir}
+    FpingLocation = /run/wrappers/bin/fping
+    ${optionalString (cfg.modules != {}) "LoadModulePath = ${moduleEnv}/lib"}
+    ${concatMapStringsSep "\n" (name: "LoadModule = ${name}") (builtins.attrNames cfg.modules)}
+    ${cfg.extraConfig}
+  '';
+
+  mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
+  pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
 
-  configFile = pkgs.writeText "zabbix_server.conf"
-    ''
-      LogFile = ${logDir}/zabbix_server
+in
 
-      PidFile = ${pidFile}
+{
+  # interface
 
-      ${optionalString (cfg.dbServer != "localhost") ''
-        DBHost = ${cfg.dbServer}
-      ''}
+  options = {
 
-      DBName = zabbix
+    services.zabbixServer = {
+      enable = mkEnableOption "the Zabbix Server";
 
-      DBUser = zabbix
+      package = mkOption {
+        type = types.package;
+        default = if cfg.database.type == "mysql" then pkgs.zabbix.server-mysql else pkgs.zabbix.server-pgsql;
+        defaultText = "pkgs.zabbix.server-pgsql";
+        description = "The Zabbix package to use.";
+      };
 
-      ${optionalString (cfg.dbPassword != "") ''
-        DBPassword = ${cfg.dbPassword}
-      ''}
+      extraPackages = mkOption {
+        type = types.listOf types.package;
+        default = with pkgs; [ nettools nmap traceroute ];
+        defaultText = "[ nettools nmap traceroute ]";
+        description = ''
+          Packages to be added to the Zabbix <envar>PATH</envar>.
+          Typically used to add executables for scripts, but can be anything.
+        '';
+      };
 
-      ${config.services.zabbixServer.extraConfig}
-    '';
+      modules = mkOption {
+        type = types.attrsOf types.package;
+        description = "A set of modules to load.";
+        default = {};
+        example = literalExample ''
+          {
+            "dummy.so" = pkgs.stdenv.mkDerivation {
+              name = "zabbix-dummy-module-''${cfg.package.version}";
+              src = cfg.package.src;
+              buildInputs = [ cfg.package ];
+              sourceRoot = "zabbix-''${cfg.package.version}/src/modules/dummy";
+              installPhase = '''
+                mkdir -p $out/lib
+                cp dummy.so $out/lib/
+              ''';
+            };
+          }
+        '';
+      };
 
-  useLocalPostgres = cfg.dbServer == "localhost" || cfg.dbServer == "";
+      database = {
+        type = mkOption {
+          type = types.enum [ "mysql" "pgsql" ];
+          example = "mysql";
+          default = "pgsql";
+          description = "Database engine to use.";
+        };
+
+        host = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = "Database host address.";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = if cfg.database.type == "mysql" then mysql.port else pgsql.port;
+          description = "Database host port.";
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "zabbix";
+          description = "Database name.";
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "zabbix";
+          description = "Database user.";
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/keys/zabbix-dbpassword";
+          description = ''
+            A file containing the password corresponding to
+            <option>database.user</option>.
+          '';
+        };
+
+        socket = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/postgresql";
+          description = "Path to the unix socket file to use for authentication.";
+        };
+
+        createLocally = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Whether to create a local database automatically.";
+        };
+      };
 
-in
+      listen = {
+        ip = mkOption {
+          type = types.str;
+          default = "0.0.0.0";
+          description = ''
+            List of comma delimited IP addresses that the trapper should listen on.
+            Trapper will listen on all network interfaces if this parameter is missing.
+          '';
+        };
 
-{
+        port = mkOption {
+          type = types.port;
+          default = 10051;
+          description = ''
+            Listen port for trapper.
+          '';
+        };
+      };
 
-  ###### interface
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open ports in the firewall for the Zabbix Server.
+        '';
+      };
 
-  options = {
+      # TODO: for bonus points migrate this to https://github.com/NixOS/rfcs/pull/42
+      extraConfig = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''
+          Configuration that is injected verbatim into the configuration file. Refer to
+          <link xlink:href="https://www.zabbix.com/documentation/current/manual/appendix/config/zabbix_server"/>
+          for details on supported values.
+        '';
+      };
 
-    services.zabbixServer.enable = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        Whether to run the Zabbix server on this machine.
-      '';
     };
 
-    services.zabbixServer.dbServer = mkOption {
-      default = "localhost";
-      type = types.str;
-      description = ''
-        Hostname or IP address of the database server.
-        Use an empty string ("") to use peer authentication.
-      '';
-    };
+  };
 
-    services.zabbixServer.dbPassword = mkOption {
-      default = "";
-      type = types.str;
-      description = "Password used to connect to the database server.";
-    };
+  # implementation
 
-    services.zabbixServer.extraConfig = mkOption {
-      default = "";
-      type = types.lines;
-      description = ''
-        Configuration that is injected verbatim into the configuration file.
-      '';
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = cfg.database.createLocally -> cfg.database.user == user;
+        message = "services.zabbixServer.database.user must be set to ${user} if services.zabbixServer.database.createLocally is set true";
+      }
+      { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
+        message = "a password cannot be specified if services.zabbixServer.database.createLocally is set to true";
+      }
+    ];
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.listen.port ];
     };
 
-  };
+    services.mysql = optionalAttrs mysqlLocal {
+      enable = true;
+      package = mkDefault pkgs.mariadb;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        { name = cfg.database.user;
+          ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
+        }
+      ];
+    };
 
-  ###### implementation
+    services.postgresql = optionalAttrs pgsqlLocal {
+      enable = true;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        { name = cfg.database.user;
+          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+        }
+      ];
+    };
 
-  config = mkIf cfg.enable {
+    users.users.${user} = {
+      description = "Zabbix daemon user";
+      uid = config.ids.uids.zabbix;
+      inherit group;
+    };
 
-    services.postgresql.enable = useLocalPostgres;
+    users.groups.${group} = {
+      gid = config.ids.gids.zabbix;
+    };
 
-    users.users = singleton
-      { name = "zabbix";
-        uid = config.ids.uids.zabbix;
-        description = "Zabbix daemon user";
-      };
+    security.wrappers = {
+      fping.source = "${pkgs.fping}/bin/fping";
+    };
 
-    systemd.services."zabbix-server" =
-      { description = "Zabbix Server";
-
-        wantedBy = [ "multi-user.target" ];
-        after = optional useLocalPostgres "postgresql.service";
-
-        preStart =
-          ''
-            mkdir -m 0755 -p ${stateDir} ${logDir} ${libDir}
-            chown zabbix ${stateDir} ${logDir} ${libDir}
-
-            if ! test -e "${libDir}/db-created"; then
-                ${pkgs.su}/bin/su -s "$SHELL" ${config.services.postgresql.superUser} -c '${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole zabbix' || true
-                ${pkgs.su}/bin/su -s "$SHELL" ${config.services.postgresql.superUser} -c '${pkgs.postgresql}/bin/createdb --owner zabbix zabbix' || true
-                cat ${pkgs.zabbix.server}/share/zabbix/db/schema/postgresql.sql | ${pkgs.su}/bin/su -s "$SHELL" zabbix -c '${pkgs.postgresql}/bin/psql zabbix'
-                cat ${pkgs.zabbix.server}/share/zabbix/db/data/images_pgsql.sql | ${pkgs.su}/bin/su -s "$SHELL" zabbix -c '${pkgs.postgresql}/bin/psql zabbix'
-                cat ${pkgs.zabbix.server}/share/zabbix/db/data/data.sql | ${pkgs.su}/bin/su -s "$SHELL" zabbix -c '${pkgs.postgresql}/bin/psql zabbix'
-                touch "${libDir}/db-created"
-            fi
-          '';
+    systemd.services."zabbix-server" = {
+      description = "Zabbix Server";
+
+      wantedBy = [ "multi-user.target" ];
+      after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
+
+      path = [ "/run/wrappers" ] ++ cfg.extraPackages;
+      preStart = ''
+        # pre 19.09 compatibility
+        if test -e "${runtimeDir}/db-created"; then
+          mv "${runtimeDir}/db-created" "${stateDir}/"
+        fi
+      '' + optionalString pgsqlLocal ''
+        if ! test -e "${stateDir}/db-created"; then
+          cat ${cfg.package}/share/zabbix/database/postgresql/schema.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/postgresql/images.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/postgresql/data.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+          touch "${stateDir}/db-created"
+        fi
+      '' + optionalString mysqlLocal ''
+        if ! test -e "${stateDir}/db-created"; then
+          cat ${cfg.package}/share/zabbix/database/mysql/schema.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/mysql/images.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/mysql/data.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+          touch "${stateDir}/db-created"
+        fi
+      '' + optionalString (cfg.database.passwordFile != null) ''
+        # create a copy of the supplied password file in a format zabbix can consume
+        touch ${passwordFile}
+        chmod 0600 ${passwordFile}
+        echo -n "DBPassword = " > ${passwordFile}
+        cat ${cfg.database.passwordFile} >> ${passwordFile}
+      '';
 
-        path = [ pkgs.nettools ];
+      serviceConfig = {
+        ExecStart = "@${cfg.package}/sbin/zabbix_server zabbix_server -f --config ${configFile}";
+        Restart = "always";
+        RestartSec = 2;
 
-        serviceConfig.ExecStart = "@${pkgs.zabbix.server}/sbin/zabbix_server zabbix_server --config ${configFile}";
-        serviceConfig.Type = "forking";
-        serviceConfig.Restart = "always";
-        serviceConfig.RestartSec = 2;
-        serviceConfig.PIDFile = pidFile;
+        User = user;
+        Group = group;
+        RuntimeDirectory = "zabbix";
+        StateDirectory = "zabbix";
+        PrivateTmp = true;
       };
+    };
+
+    systemd.services.httpd.after =
+      optional (config.services.zabbixWeb.enable && mysqlLocal) "mysql.service" ++
+      optional (config.services.zabbixWeb.enable && pgsqlLocal) "postgresql.service";
 
   };
 
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/kbfs.nix b/nixpkgs/nixos/modules/services/network-filesystems/kbfs.nix
index 7b2eea3b5850..263b70d04a56 100644
--- a/nixpkgs/nixos/modules/services/network-filesystems/kbfs.nix
+++ b/nixpkgs/nixos/modules/services/network-filesystems/kbfs.nix
@@ -48,6 +48,7 @@ in {
       requires = [ "keybase.service" ];
       after = [ "keybase.service" ];
       path = [ "/run/wrappers" ];
+      unitConfig.ConditionUser = "!@system";
       serviceConfig = {
         ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${cfg.mountPoint}";
         ExecStart = "${pkgs.kbfs}/bin/kbfsfuse ${toString cfg.extraFlags} ${cfg.mountPoint}";
diff --git a/nixpkgs/nixos/modules/services/networking/bind.nix b/nixpkgs/nixos/modules/services/networking/bind.nix
index 7f89cff22329..06af4dbcca4e 100644
--- a/nixpkgs/nixos/modules/services/networking/bind.nix
+++ b/nixpkgs/nixos/modules/services/networking/bind.nix
@@ -33,7 +33,7 @@ let
       ${cfg.extraConfig}
 
       ${ concatMapStrings
-          ({ name, file, master ? true, slaves ? [], masters ? [] }:
+          ({ name, file, master ? true, slaves ? [], masters ? [], extraConfig ? "" }:
             ''
               zone "${name}" {
                 type ${if master then "master" else "slave"};
@@ -52,6 +52,7 @@ let
                    ''
                 }
                 allow-query { any; };
+                ${extraConfig}
               };
             '')
           cfg.zones }
@@ -131,6 +132,7 @@ in
           file = "/var/dns/example.com";
           masters = ["192.168.0.1"];
           slaves = [];
+          extraConfig = "";
         }];
       };
 
@@ -168,7 +170,9 @@ in
 
   ###### implementation
 
-  config = mkIf config.services.bind.enable {
+  config = mkIf cfg.enable {
+
+    networking.resolvconf.useLocalResolver = mkDefault true;
 
     users.users = singleton
       { name = bindUser;
diff --git a/nixpkgs/nixos/modules/services/networking/dhcpcd.nix b/nixpkgs/nixos/modules/services/networking/dhcpcd.nix
index c217ccaa405a..7b2786034552 100644
--- a/nixpkgs/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixpkgs/nixos/modules/services/networking/dhcpcd.nix
@@ -162,7 +162,7 @@ in
 
         wantedBy = [ "multi-user.target" ] ++ optional (!hasDefaultGatewaySet) "network-online.target";
         wants = [ "network.target" "systemd-udev-settle.service" ];
-        before = [ "network.target" ];
+        before = [ "network-online.target" ];
         after = [ "systemd-udev-settle.service" ];
 
         # Stopping dhcpcd during a reconfiguration is undesirable
diff --git a/nixpkgs/nixos/modules/services/networking/dnsmasq.nix b/nixpkgs/nixos/modules/services/networking/dnsmasq.nix
index 24d16046c63e..714a5903bff1 100644
--- a/nixpkgs/nixos/modules/services/networking/dnsmasq.nix
+++ b/nixpkgs/nixos/modules/services/networking/dnsmasq.nix
@@ -79,7 +79,7 @@ in
 
   ###### implementation
 
-  config = mkIf config.services.dnsmasq.enable {
+  config = mkIf cfg.enable {
 
     networking.nameservers =
       optional cfg.resolveLocalQueries "127.0.0.1";
@@ -92,6 +92,15 @@ in
       description = "Dnsmasq daemon user";
     };
 
+    networking.resolvconf = mkIf cfg.resolveLocalQueries {
+      useLocalResolver = mkDefault true;
+
+      extraConfig = ''
+        dnsmasq_conf=/etc/dnsmasq-conf.conf
+        dnsmasq_resolv=/etc/dnsmasq-resolv.conf
+      '';
+    };
+
     systemd.services.dnsmasq = {
         description = "Dnsmasq Daemon";
         after = [ "network.target" "systemd-resolved.service" ];
diff --git a/nixpkgs/nixos/modules/services/networking/keybase.nix b/nixpkgs/nixos/modules/services/networking/keybase.nix
index a149f16a84cb..85f52be8a6ac 100644
--- a/nixpkgs/nixos/modules/services/networking/keybase.nix
+++ b/nixpkgs/nixos/modules/services/networking/keybase.nix
@@ -26,6 +26,7 @@ in {
 
     systemd.user.services.keybase = {
       description = "Keybase service";
+      unitConfig.ConditionUser = "!@system";
       serviceConfig = {
         ExecStart = ''
           ${pkgs.keybase}/bin/keybase service --auto-forked
diff --git a/nixpkgs/nixos/modules/services/networking/kresd.nix b/nixpkgs/nixos/modules/services/networking/kresd.nix
index ca34ff9df4ef..fc516c01230a 100644
--- a/nixpkgs/nixos/modules/services/networking/kresd.nix
+++ b/nixpkgs/nixos/modules/services/networking/kresd.nix
@@ -80,8 +80,11 @@ in
         # Syntax depends on being IPv6 or IPv4.
         (iface: if elem ":" (stringToCharacters iface) then "[${iface}]:53" else "${iface}:53")
         cfg.interfaces;
-      socketConfig.ListenDatagram = listenStreams;
-      socketConfig.FreeBind = true;
+      socketConfig = {
+        ListenDatagram = listenStreams;
+        FreeBind = true;
+        FileDescriptorName = "dns";
+      };
     };
 
     systemd.sockets.kresd-tls = mkIf (cfg.listenTLS != []) rec {
diff --git a/nixpkgs/nixos/modules/services/networking/networkmanager.nix b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
index f1f8c9722e02..551636a33d25 100644
--- a/nixpkgs/nixos/modules/services/networking/networkmanager.nix
+++ b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
@@ -1,6 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with pkgs;
 with lib;
 
 let
@@ -12,12 +11,13 @@ let
   # /var/lib/misc is for dnsmasq.leases.
   stateDirs = "/var/lib/NetworkManager /var/lib/dhclient /var/lib/misc";
 
-  configFile = writeText "NetworkManager.conf" ''
+  configFile = pkgs.writeText "NetworkManager.conf" ''
     [main]
     plugins=keyfile
     dhcp=${cfg.dhcp}
     dns=${cfg.dns}
-    rc-manager=${cfg.rc-manager}
+    # If resolvconf is disabled that means that resolv.conf is managed by some other module.
+    rc-manager=${if config.networking.resolvconf.enable then "resolvconf" else "unmanaged"}
 
     [keyfile]
     ${optionalString (cfg.unmanaged != [])
@@ -65,19 +65,19 @@ let
     });
   '';
 
-  ns = xs: writeText "nameservers" (
+  ns = xs: pkgs.writeText "nameservers" (
     concatStrings (map (s: "nameserver ${s}\n") xs)
   );
 
-  overrideNameserversScript = writeScript "02overridedns" ''
+  overrideNameserversScript = pkgs.writeScript "02overridedns" ''
     #!/bin/sh
-    tmp=`${coreutils}/bin/mktemp`
-    ${gnused}/bin/sed '/nameserver /d' /etc/resolv.conf > $tmp
-    ${gnugrep}/bin/grep 'nameserver ' /etc/resolv.conf | \
-      ${gnugrep}/bin/grep -vf ${ns (cfg.appendNameservers ++ cfg.insertNameservers)} > $tmp.ns
-    ${optionalString (cfg.appendNameservers != []) "${coreutils}/bin/cat $tmp $tmp.ns ${ns cfg.appendNameservers} > /etc/resolv.conf"}
-    ${optionalString (cfg.insertNameservers != []) "${coreutils}/bin/cat $tmp ${ns cfg.insertNameservers} $tmp.ns > /etc/resolv.conf"}
-    ${coreutils}/bin/rm -f $tmp $tmp.ns
+    PATH=${with pkgs; makeBinPath [ gnused gnugrep coreutils ]}
+    tmp=$(mktemp)
+    sed '/nameserver /d' /etc/resolv.conf > $tmp
+    grep 'nameserver ' /etc/resolv.conf | \
+      grep -vf ${ns (cfg.appendNameservers ++ cfg.insertNameservers)} > $tmp.ns
+    cat $tmp ${ns cfg.insertNameservers} $tmp.ns ${ns cfg.appendNameservers} > /etc/resolv.conf
+    rm -f $tmp $tmp.ns
   '';
 
   dispatcherTypesSubdirMap = {
@@ -176,7 +176,8 @@ in {
       # Ugly hack for using the correct gnome3 packageSet
       basePackages = mkOption {
         type = types.attrsOf types.package;
-        default = { inherit networkmanager modemmanager wpa_supplicant
+        default = { inherit (pkgs)
+                            networkmanager modemmanager wpa_supplicant crda
                             networkmanager-openvpn networkmanager-vpnc
                             networkmanager-openconnect networkmanager-fortisslvpn
                             networkmanager-l2tp networkmanager-iodine; };
@@ -268,25 +269,6 @@ in {
         '';
       };
 
-      rc-manager = mkOption {
-        type = types.enum [ "symlink" "file" "resolvconf" "netconfig" "unmanaged" "none" ];
-        default = "resolvconf";
-        description = ''
-          Set the <literal>resolv.conf</literal> management mode.
-          </para>
-          <para>
-          A description of these modes can be found in the main section of
-          <link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html">
-            https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
-          </link>
-          or in
-          <citerefentry>
-            <refentrytitle>NetworkManager.conf</refentrytitle>
-            <manvolnum>5</manvolnum>
-          </citerefentry>.
-        '';
-      };
-
       dispatcherScripts = mkOption {
         type = types.listOf (types.submodule {
           options = {
@@ -425,13 +407,10 @@ in {
       { source = "${networkmanager-l2tp}/lib/NetworkManager/VPN/nm-l2tp-service.name";
         target = "NetworkManager/VPN/nm-l2tp-service.name";
       }
-      { source = "${networkmanager_strongswan}/lib/NetworkManager/VPN/nm-strongswan-service.name";
-        target = "NetworkManager/VPN/nm-strongswan-service.name";
-      }
       { source = "${networkmanager-iodine}/lib/NetworkManager/VPN/nm-iodine-service.name";
         target = "NetworkManager/VPN/nm-iodine-service.name";
       }
-    ] ++ optional (cfg.appendNameservers == [] || cfg.insertNameservers == [])
+    ] ++ optional (cfg.appendNameservers != [] || cfg.insertNameservers != [])
            { source = overrideNameserversScript;
              target = "NetworkManager/dispatcher.d/02overridedns";
            }
@@ -440,11 +419,15 @@ in {
         target = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}";
         mode = "0544";
       }) cfg.dispatcherScripts
-      ++ optional (dynamicHostsEnabled)
+      ++ optional dynamicHostsEnabled
            { target = "NetworkManager/dnsmasq.d/dyndns.conf";
              text = concatMapStrings (n: ''
                hostsdir=/run/NetworkManager/hostsdirs/${n}
              '') (attrNames cfg.dynamicHosts.hostsDirs);
+           }
+      ++ optional cfg.enableStrongSwan
+           { source = "${pkgs.networkmanager_strongswan}/lib/NetworkManager/VPN/nm-strongswan-service.name";
+             target = "NetworkManager/VPN/nm-strongswan-service.name";
            };
 
     environment.systemPackages = cfg.packages;
@@ -512,7 +495,7 @@ in {
     networking = {
       useDHCP = false;
       # use mkDefault to trigger the assertion about the conflict above
-      wireless.enable = lib.mkDefault false;
+      wireless.enable = mkDefault false;
     };
 
     security.polkit.extraConfig = polkitConf;
diff --git a/nixpkgs/nixos/modules/services/networking/rdnssd.nix b/nixpkgs/nixos/modules/services/networking/rdnssd.nix
index 887772f6e5f0..bccab805beeb 100644
--- a/nixpkgs/nixos/modules/services/networking/rdnssd.nix
+++ b/nixpkgs/nixos/modules/services/networking/rdnssd.nix
@@ -35,6 +35,11 @@ in
 
   config = mkIf config.services.rdnssd.enable {
 
+    assertions = [{
+      assertion = config.networking.resolvconf.enable;
+      message = "rdnssd needs resolvconf to work (probably something sets up a static resolv.conf)";
+    }];
+
     systemd.services.rdnssd = {
       description = "RDNSS daemon";
       after = [ "network.target" ];
diff --git a/nixpkgs/nixos/modules/services/networking/syncthing.nix b/nixpkgs/nixos/modules/services/networking/syncthing.nix
index d78a54a3327b..126f5b7b527b 100644
--- a/nixpkgs/nixos/modules/services/networking/syncthing.nix
+++ b/nixpkgs/nixos/modules/services/networking/syncthing.nix
@@ -291,7 +291,7 @@ in {
 
       group = mkOption {
         type = types.str;
-        default = "nogroup";
+        default = defaultUser;
         description = ''
           Syncthing will be run under this group (group will not be created if it doesn't exist.
           This can be your user name).
@@ -372,16 +372,18 @@ in {
 
     systemd.packages = [ pkgs.syncthing ];
 
-    users = mkIf (cfg.systemService && cfg.user == defaultUser) {
-      users."${defaultUser}" =
+    users.users = mkIf (cfg.systemService && cfg.user == defaultUser) {
+      "${defaultUser}" =
         { group = cfg.group;
           home  = cfg.dataDir;
           createHome = true;
           uid = config.ids.uids.syncthing;
           description = "Syncthing daemon user";
         };
+    };
 
-      groups."${defaultUser}".gid =
+    users.groups = mkIf (cfg.systemService && cfg.group == defaultUser) {
+      "${defaultUser}".gid =
         config.ids.gids.syncthing;
     };
 
@@ -403,18 +405,12 @@ in {
           Group = cfg.group;
           ExecStartPre = mkIf (cfg.declarative.cert != null || cfg.declarative.key != null)
             "+${pkgs.writers.writeBash "syncthing-copy-keys" ''
-              mkdir -p ${cfg.configDir}
-              chown ${cfg.user}:${cfg.group} ${cfg.configDir}
-              chmod 700 ${cfg.configDir}
+              install -dm700 -o ${cfg.user} -g ${cfg.group} ${cfg.configDir}
               ${optionalString (cfg.declarative.cert != null) ''
-                cp ${toString cfg.declarative.cert} ${cfg.configDir}/cert.pem
-                chown ${cfg.user}:${cfg.group} ${cfg.configDir}/cert.pem
-                chmod 400 ${cfg.configDir}/cert.pem
+                install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.cert} ${cfg.configDir}/cert.pem
               ''}
               ${optionalString (cfg.declarative.key != null) ''
-                cp ${toString cfg.declarative.key} ${cfg.configDir}/key.pem
-                chown ${cfg.user}:${cfg.group} ${cfg.configDir}/key.pem
-                chmod 400 ${cfg.configDir}/key.pem
+                install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.key} ${cfg.configDir}/key.pem
               ''}
             ''}"
           ;
diff --git a/nixpkgs/nixos/modules/services/networking/thelounge.nix b/nixpkgs/nixos/modules/services/networking/thelounge.nix
new file mode 100644
index 000000000000..b1d23372955e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/thelounge.nix
@@ -0,0 +1,75 @@
+{ pkgs, lib, config, ... }:
+
+with lib;
+
+let
+  cfg = config.services.thelounge;
+  dataDir = "/var/lib/thelounge";
+  configJsData = "module.exports = " + builtins.toJSON (
+    { private = cfg.private; port = cfg.port; } // cfg.extraConfig
+  );
+in {
+  options.services.thelounge = {
+    enable = mkEnableOption "The Lounge web IRC client";
+
+    private = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Make your The Lounge instance private. You will need to configure user
+        accounts by using the (<command>thelounge</command>) command or by adding
+        entries in <filename>${dataDir}/users</filename>. You might need to restart
+        The Lounge after making changes to the state directory.
+      '';
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 9000;
+      description = "TCP port to listen on for http connections.";
+    };
+
+    extraConfig = mkOption {
+      default = {};
+      type = types.attrs;
+      example = literalExample ''{
+        reverseProxy = true;
+        defaults = {
+          name = "Your Network";
+          host = "localhost";
+          port = 6697;
+        };
+      }'';
+      description = ''
+        The Lounge's <filename>config.js</filename> contents as attribute set (will be
+        converted to JSON to generate the configuration file).
+
+        The options defined here will be merged to the default configuration file.
+        Note: In case of duplicate configuration, options from <option>extraConfig</option> have priority.
+
+        Documentation: <link xlink:href="https://thelounge.chat/docs/server/configuration" />
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users.thelounge = {
+      description = "thelounge service user";
+      group = "thelounge";
+    };
+    users.groups.thelounge = {};
+    systemd.services.thelounge = {
+      description = "The Lounge web IRC client";
+      wantedBy = [ "multi-user.target" ];
+      environment = { THELOUNGE_HOME = dataDir; };
+      preStart = "ln -sf ${pkgs.writeText "config.js" configJsData} ${dataDir}/config.js";
+      serviceConfig = {
+        User = "thelounge";
+        StateDirectory = baseNameOf dataDir;
+        ExecStart = "${pkgs.thelounge}/bin/thelounge start";
+      };
+    };
+
+    environment.systemPackages = [ pkgs.thelounge ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/unbound.nix b/nixpkgs/nixos/modules/services/networking/unbound.nix
index 1a35979ad44c..3cf82e8839bb 100644
--- a/nixpkgs/nixos/modules/services/networking/unbound.nix
+++ b/nixpkgs/nixos/modules/services/networking/unbound.nix
@@ -101,6 +101,8 @@ in
       isSystemUser = true;
     };
 
+    networking.resolvconf.useLocalResolver = mkDefault true;
+
     systemd.services.unbound = {
       description = "Unbound recursive Domain Name Server";
       after = [ "network.target" ];
diff --git a/nixpkgs/nixos/modules/services/networking/unifi.nix b/nixpkgs/nixos/modules/services/networking/unifi.nix
index 9057a1e12b33..6239c88b7e41 100644
--- a/nixpkgs/nixos/modules/services/networking/unifi.nix
+++ b/nixpkgs/nixos/modules/services/networking/unifi.nix
@@ -148,7 +148,7 @@ in
 
     systemd.tmpfiles.rules = [
       "e '${stateDir}' 0700 unifi - - -"
-      "e '${stateDir}/data' 0700 unifi - - -"
+      "d '${stateDir}/data' 0700 unifi - - -"
     ];
 
     systemd.services.unifi = {
diff --git a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
index 0bd9edf4a41c..63e59e7c8fac 100644
--- a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
@@ -204,6 +204,7 @@ in {
     environment.systemPackages =  [ pkgs.wpa_supplicant ];
 
     services.dbus.packages = [ pkgs.wpa_supplicant ];
+    services.udev.packages = [ pkgs.crda ];
 
     # FIXME: start a separate wpa_supplicant instance per interface.
     systemd.services.wpa_supplicant = let
diff --git a/nixpkgs/nixos/modules/services/security/sshguard.nix b/nixpkgs/nixos/modules/services/security/sshguard.nix
index 3892cd5c72b8..25cec5b5b105 100644
--- a/nixpkgs/nixos/modules/services/security/sshguard.nix
+++ b/nixpkgs/nixos/modules/services/security/sshguard.nix
@@ -107,8 +107,6 @@ in {
       path = with pkgs; [ iptables ipset iproute systemd ];
 
       postStart = ''
-        ${pkgs.ipset}/bin/ipset -quiet create -exist sshguard4 hash:ip family inet
-        ${pkgs.ipset}/bin/ipset -quiet create -exist sshguard6 hash:ip family inet6
         ${pkgs.iptables}/bin/iptables  -I INPUT -m set --match-set sshguard4 src -j DROP
         ${pkgs.iptables}/bin/ip6tables -I INPUT -m set --match-set sshguard6 src -j DROP
       '';
diff --git a/nixpkgs/nixos/modules/services/security/tor.nix b/nixpkgs/nixos/modules/services/security/tor.nix
index 6f4852c3ba1a..abdc0cd78b4d 100644
--- a/nixpkgs/nixos/modules/services/security/tor.nix
+++ b/nixpkgs/nixos/modules/services/security/tor.nix
@@ -81,7 +81,7 @@ let
 
     ${optionalString (elem cfg.relay.role ["bridge" "private-bridge"]) ''
       BridgeRelay 1
-      ServerTransportPlugin ${concatStringsSep "," cfg.relay.bridgeTransports} exec ${obfs4}/bin/obfs4proxy managed
+      ServerTransportPlugin ${concatStringsSep "," cfg.relay.bridgeTransports} exec ${pkgs.obfs4}/bin/obfs4proxy managed
       ExtORPort auto
       ${optionalString (cfg.relay.role == "private-bridge") ''
         ExtraInfoStatistics 0
diff --git a/nixpkgs/nixos/modules/services/system/nscd.conf b/nixpkgs/nixos/modules/services/system/nscd.conf
index 603a5d01acce..2b7523a7346d 100644
--- a/nixpkgs/nixos/modules/services/system/nscd.conf
+++ b/nixpkgs/nixos/modules/services/system/nscd.conf
@@ -7,46 +7,28 @@
 # is not aware of the path in which the nss modules live.  As a workaround, we
 # have `enable-cache yes` with an explicit ttl of 0
 server-user             nscd
-threads                 1
-paranoia                no
-debug-level             0
 
 enable-cache            passwd          yes
 positive-time-to-live   passwd          0
 negative-time-to-live   passwd          0
-suggested-size          passwd          211
-check-files             passwd          yes
-persistent              passwd          no
 shared                  passwd          yes
 
 enable-cache            group           yes
 positive-time-to-live   group           0
 negative-time-to-live   group           0
-suggested-size          group           211
-check-files             group           yes
-persistent              group           no
 shared                  group           yes
 
 enable-cache            netgroup        yes
 positive-time-to-live   netgroup        0
 negative-time-to-live   netgroup        0
-suggested-size          netgroup        211
-check-files             netgroup        yes
-persistent              netgroup        no
 shared                  netgroup        yes
 
 enable-cache            hosts           yes
 positive-time-to-live   hosts           600
 negative-time-to-live   hosts           0
-suggested-size          hosts           211
-check-files             hosts           yes
-persistent              hosts           no
 shared                  hosts           yes
 
 enable-cache            services        yes
 positive-time-to-live   services        0
 negative-time-to-live   services        0
-suggested-size          services        211
-check-files             services        yes
-persistent              services        no
 shared                  services        yes
diff --git a/nixpkgs/nixos/modules/services/system/nscd.nix b/nixpkgs/nixos/modules/services/system/nscd.nix
index fd1570d11980..e11f7e049d8f 100644
--- a/nixpkgs/nixos/modules/services/system/nscd.nix
+++ b/nixpkgs/nixos/modules/services/system/nscd.nix
@@ -39,11 +39,6 @@ in
   config = mkIf cfg.enable {
     environment.etc."nscd.conf".text = cfg.config;
 
-    users.users.nscd =
-      { isSystemUser = true;
-        description = "Name service cache daemon user";
-      };
-
     systemd.services.nscd =
       { description = "Name Service Cache Daemon";
 
@@ -51,22 +46,23 @@ in
 
         environment = { LD_LIBRARY_PATH = nssModulesPath; };
 
-        preStart =
-          ''
-            mkdir -m 0755 -p /run/nscd
-            rm -f /run/nscd/nscd.pid
-            mkdir -m 0755 -p /var/db/nscd
-          '';
-
         restartTriggers = [
           config.environment.etc.hosts.source
           config.environment.etc."nsswitch.conf".source
           config.environment.etc."nscd.conf".source
         ];
 
+        # We use DynamicUser because in default configurations nscd doesn't
+        # create any files that need to survive restarts. However, in some
+        # configurations, nscd needs to be started as root; it will drop
+        # privileges after all the NSS modules have read their configuration
+        # files. So prefix the ExecStart command with "!" to prevent systemd
+        # from dropping privileges early. See ExecStart in systemd.service(5).
         serviceConfig =
-          { ExecStart = "@${pkgs.glibc.bin}/sbin/nscd nscd";
+          { ExecStart = "!@${pkgs.glibc.bin}/sbin/nscd nscd";
             Type = "forking";
+            DynamicUser = true;
+            RuntimeDirectory = "nscd";
             PIDFile = "/run/nscd/nscd.pid";
             Restart = "always";
             ExecReload =
@@ -75,15 +71,6 @@ in
                 "${pkgs.glibc.bin}/sbin/nscd --invalidate hosts"
               ];
           };
-
-        # Urgggggh... Nscd forks before opening its socket and writing
-        # its pid. So wait until it's ready.
-        postStart =
-          ''
-            while ! ${pkgs.glibc.bin}/sbin/nscd -g > /dev/null; do
-              sleep 0.2
-            done
-          '';
       };
 
   };
diff --git a/nixpkgs/nixos/modules/services/torrent/deluge.nix b/nixpkgs/nixos/modules/services/torrent/deluge.nix
index 01a5890a7845..48ec4d692e2f 100644
--- a/nixpkgs/nixos/modules/services/torrent/deluge.nix
+++ b/nixpkgs/nixos/modules/services/torrent/deluge.nix
@@ -118,36 +118,74 @@ in {
             more informations.
           '';
         };
+
+        user = mkOption {
+          type = types.str;
+          default = "deluge";
+          description = ''
+            User account under which deluge runs.
+          '';
+        };
+
+        group = mkOption {
+          type = types.str;
+          default = "deluge";
+          description = ''
+            Group under which deluge runs.
+          '';
+        };
+
+        extraPackages = mkOption {
+          type = types.listOf types.package;
+          default = [];
+          description = ''
+            Extra packages available at runtime to enable Deluge's plugins. For example,
+            extraction utilities are required for the built-in "Extractor" plugin.
+            This always contains unzip, gnutar, xz, p7zip and bzip2.
+          '';
+        };
       };
 
       deluge.web = {
         enable = mkEnableOption "Deluge Web daemon";
+
         port = mkOption {
-        type = types.port;
+          type = types.port;
           default = 8112;
           description = ''
             Deluge web UI port.
           '';
         };
+
+        openFirewall = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Open ports in the firewall for deluge web daemon
+          '';
+        };
       };
     };
   };
 
   config = mkIf cfg.enable {
 
-    systemd.tmpfiles.rules = [ "d '${configDir}' 0770 deluge deluge" ]
+    # Provide a default set of `extraPackages`.
+    services.deluge.extraPackages = with pkgs; [ unzip gnutar xz p7zip bzip2 ];
+
+    systemd.tmpfiles.rules = [ "d '${configDir}' 0770 ${cfg.user} ${cfg.group}" ]
     ++ optional (cfg.config ? "download_location")
-      "d '${cfg.config.download_location}' 0770 deluge deluge"
+      "d '${cfg.config.download_location}' 0770 ${cfg.user} ${cfg.group}"
     ++ optional (cfg.config ? "torrentfiles_location")
-      "d '${cfg.config.torrentfiles_location}' 0770 deluge deluge"
+      "d '${cfg.config.torrentfiles_location}' 0770 ${cfg.user} ${cfg.group}"
     ++ optional (cfg.config ? "move_completed_path")
-      "d '${cfg.config.move_completed_path}' 0770 deluge deluge";
+      "d '${cfg.config.move_completed_path}' 0770 ${cfg.user} ${cfg.group}";
 
     systemd.services.deluged = {
       after = [ "network.target" ];
       description = "Deluge BitTorrent Daemon";
       wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.deluge ];
+      path = [ pkgs.deluge ] ++ cfg.extraPackages;
       serviceConfig = {
         ExecStart = ''
           ${pkgs.deluge}/bin/deluged \
@@ -157,8 +195,8 @@ in {
         # To prevent "Quit & shutdown daemon" from working; we want systemd to
         # manage it!
         Restart = "on-success";
-        User = "deluge";
-        Group = "deluge";
+        User = cfg.user;
+        Group = cfg.group;
         UMask = "0002";
         LimitNOFILE = cfg.openFilesLimit;
       };
@@ -177,26 +215,37 @@ in {
             --config ${configDir} \
             --port ${toString cfg.web.port}
         '';
-        User = "deluge";
-        Group = "deluge";
+        User = cfg.user;
+        Group = cfg.group;
       };
     };
 
-    networking.firewall = mkIf (cfg.declarative && cfg.openFirewall && !(cfg.config.random_port or true)) {
-      allowedTCPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
-      allowedUDPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
-    };
+    networking.firewall = mkMerge [
+      (mkIf (cfg.declarative && cfg.openFirewall && !(cfg.config.random_port or true)) {
+        allowedTCPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
+        allowedUDPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
+      })
+      (mkIf (cfg.web.openFirewall) {
+        allowedTCPPorts = [ cfg.web.port ];
+      })
+    ];
 
     environment.systemPackages = [ pkgs.deluge ];
 
-    users.users.deluge = {
-      group = "deluge";
-      uid = config.ids.uids.deluge;
-      home = cfg.dataDir;
-      createHome = true;
-      description = "Deluge Daemon user";
+    users.users = mkIf (cfg.user == "deluge") {
+      deluge = {
+        group = cfg.group;
+        uid = config.ids.uids.deluge;
+        home = cfg.dataDir;
+        createHome = true;
+        description = "Deluge Daemon user";
+      };
     };
 
-    users.groups.deluge.gid = config.ids.gids.deluge;
+    users.groups = mkIf (cfg.group == "deluge") {
+      deluge = {
+        gid = config.ids.gids.deluge;
+      };
+    };
   };
 }
diff --git a/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix b/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix
new file mode 100644
index 000000000000..5bd5977e592b
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix
@@ -0,0 +1,473 @@
+{ config, pkgs, lib, ... }:
+
+let
+
+  inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption;
+  inherit (lib) concatStringsSep literalExample mapAttrsToList optional optionals optionalString types;
+
+  cfg = config.services.mediawiki;
+  fpm = config.services.phpfpm.pools.mediawiki;
+  user = "mediawiki";
+  group = config.services.httpd.group;
+  cacheDir = "/var/cache/mediawiki";
+  stateDir = "/var/lib/mediawiki";
+
+  pkg = pkgs.stdenv.mkDerivation rec {
+    pname = "mediawiki-full";
+    version = src.version;
+    src = cfg.package;
+
+    installPhase = ''
+      mkdir -p $out
+      cp -r * $out/
+
+      rm -rf $out/share/mediawiki/skins/*
+      rm -rf $out/share/mediawiki/extensions/*
+
+      ${concatStringsSep "\n" (mapAttrsToList (k: v: ''
+        ln -s ${v} $out/share/mediawiki/skins/${k}
+      '') cfg.skins)}
+
+      ${concatStringsSep "\n" (mapAttrsToList (k: v: ''
+        ln -s ${v} $out/share/mediawiki/extensions/${k}
+      '') cfg.extensions)}
+    '';
+  };
+
+  mediawikiScripts = pkgs.runCommand "mediawiki-scripts" {
+    buildInputs = [ pkgs.makeWrapper ];
+    preferLocalBuild = true;
+  } ''
+    mkdir -p $out/bin
+    for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do
+      makeWrapper ${pkgs.php}/bin/php $out/bin/mediawiki-$(basename $i .php) \
+        --set MEDIAWIKI_CONFIG ${mediawikiConfig} \
+        --add-flags ${pkg}/share/mediawiki/maintenance/$i
+    done
+  '';
+
+  mediawikiConfig = pkgs.writeText "LocalSettings.php" ''
+    <?php
+      # Protect against web entry
+      if ( !defined( 'MEDIAWIKI' ) ) {
+        exit;
+      }
+
+      $wgSitename = "${cfg.name}";
+      $wgMetaNamespace = false;
+
+      ## The URL base path to the directory containing the wiki;
+      ## defaults for all runtime URL paths are based off of this.
+      ## For more information on customizing the URLs
+      ## (like /w/index.php/Page_title to /wiki/Page_title) please see:
+      ## https://www.mediawiki.org/wiki/Manual:Short_URL
+      $wgScriptPath = "";
+
+      ## The protocol and server name to use in fully-qualified URLs
+      $wgServer = "${if cfg.virtualHost.enableSSL then "https" else "http"}://${cfg.virtualHost.hostName}";
+
+      ## The URL path to static resources (images, scripts, etc.)
+      $wgResourceBasePath = $wgScriptPath;
+
+      ## The URL path to the logo.  Make sure you change this from the default,
+      ## or else you'll overwrite your logo when you upgrade!
+      $wgLogo = "$wgResourceBasePath/resources/assets/wiki.png";
+
+      ## UPO means: this is also a user preference option
+
+      $wgEnableEmail = true;
+      $wgEnableUserEmail = true; # UPO
+
+      $wgEmergencyContact = "${if cfg.virtualHost.adminAddr != null then cfg.virtualHost.adminAddr else config.services.httpd.adminAddr}";
+      $wgPasswordSender = $wgEmergencyContact;
+
+      $wgEnotifUserTalk = false; # UPO
+      $wgEnotifWatchlist = false; # UPO
+      $wgEmailAuthentication = true;
+
+      ## Database settings
+      $wgDBtype = "${cfg.database.type}";
+      $wgDBserver = "${cfg.database.host}:${if cfg.database.socket != null then cfg.database.socket else toString cfg.database.port}";
+      $wgDBname = "${cfg.database.name}";
+      $wgDBuser = "${cfg.database.user}";
+      ${optionalString (cfg.database.passwordFile != null) "$wgDBpassword = file_get_contents(\"${cfg.database.passwordFile}\");"}
+
+      ${optionalString (cfg.database.type == "mysql" && cfg.database.tablePrefix != null) ''
+        # MySQL specific settings
+        $wgDBprefix = "${cfg.database.tablePrefix}";
+      ''}
+
+      ${optionalString (cfg.database.type == "mysql") ''
+        # MySQL table options to use during installation or update
+        $wgDBTableOptions = "ENGINE=InnoDB, DEFAULT CHARSET=binary";
+      ''}
+
+      ## Shared memory settings
+      $wgMainCacheType = CACHE_NONE;
+      $wgMemCachedServers = [];
+
+      ${optionalString (cfg.uploadsDir != null) ''
+        $wgEnableUploads = true;
+        $wgUploadDirectory = "${cfg.uploadsDir}";
+      ''}
+
+      $wgUseImageMagick = true;
+      $wgImageMagickConvertCommand = "${pkgs.imagemagick}/bin/convert";
+
+      # InstantCommons allows wiki to use images from https://commons.wikimedia.org
+      $wgUseInstantCommons = false;
+
+      # Periodically send a pingback to https://www.mediawiki.org/ with basic data
+      # about this MediaWiki instance. The Wikimedia Foundation shares this data
+      # with MediaWiki developers to help guide future development efforts.
+      $wgPingback = true;
+
+      ## If you use ImageMagick (or any other shell command) on a
+      ## Linux server, this will need to be set to the name of an
+      ## available UTF-8 locale
+      $wgShellLocale = "C.UTF-8";
+
+      ## Set $wgCacheDirectory to a writable directory on the web server
+      ## to make your wiki go slightly faster. The directory should not
+      ## be publically accessible from the web.
+      $wgCacheDirectory = "${cacheDir}";
+
+      # Site language code, should be one of the list in ./languages/data/Names.php
+      $wgLanguageCode = "en";
+
+      $wgSecretKey = file_get_contents("${stateDir}/secret.key");
+
+      # Changing this will log out all existing sessions.
+      $wgAuthenticationTokenVersion = "";
+
+      ## For attaching licensing metadata to pages, and displaying an
+      ## appropriate copyright notice / icon. GNU Free Documentation
+      ## License and Creative Commons licenses are supported so far.
+      $wgRightsPage = ""; # Set to the title of a wiki page that describes your license/copyright
+      $wgRightsUrl = "";
+      $wgRightsText = "";
+      $wgRightsIcon = "";
+
+      # Path to the GNU diff3 utility. Used for conflict resolution.
+      $wgDiff = "${pkgs.diffutils}/bin/diff";
+      $wgDiff3 = "${pkgs.diffutils}/bin/diff3";
+
+      # Enabled skins.
+      ${concatStringsSep "\n" (mapAttrsToList (k: v: "wfLoadSkin('${k}');") cfg.skins)}
+
+      # Enabled extensions.
+      ${concatStringsSep "\n" (mapAttrsToList (k: v: "wfLoadExtension('${k}');") cfg.extensions)}
+
+
+      # End of automatically generated settings.
+      # Add more configuration options below.
+
+      ${cfg.extraConfig}
+  '';
+
+in
+{
+  # interface
+  options = {
+    services.mediawiki = {
+
+      enable = mkEnableOption "MediaWiki";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.mediawiki;
+        description = "Which MediaWiki package to use.";
+      };
+
+      name = mkOption {
+        default = "MediaWiki";
+        example = "Foobar Wiki";
+        description = "Name of the wiki.";
+      };
+
+      uploadsDir = mkOption {
+        type = types.nullOr types.path;
+        default = "${stateDir}/uploads";
+        description = ''
+          This directory is used for uploads of pictures. The directory passed here is automatically
+          created and permissions adjusted as required.
+        '';
+      };
+
+      passwordFile = mkOption {
+        type = types.path;
+        description = "A file containing the initial password for the admin user.";
+        example = "/run/keys/mediawiki-password";
+      };
+
+      skins = mkOption {
+        default = {};
+        type = types.attrsOf types.path;
+        description = ''
+          List of paths whose content is copied to the 'skins'
+          subdirectory of the MediaWiki installation.
+        '';
+      };
+
+      extensions = mkOption {
+        default = {};
+        type = types.attrsOf types.path;
+        description = ''
+          List of paths whose content is copied to the 'extensions'
+          subdirectory of the MediaWiki installation.
+        '';
+      };
+
+      database = {
+        type = mkOption {
+          type = types.enum [ "mysql" "postgres" "sqlite" "mssql" "oracle" ];
+          default = "mysql";
+          description = "Database engine to use. MySQL/MariaDB is the database of choice by MediaWiki developers.";
+        };
+
+        host = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = "Database host address.";
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 3306;
+          description = "Database host port.";
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "mediawiki";
+          description = "Database name.";
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "mediawiki";
+          description = "Database user.";
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/keys/mediawiki-dbpassword";
+          description = ''
+            A file containing the password corresponding to
+            <option>database.user</option>.
+          '';
+        };
+
+        tablePrefix = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            If you only have access to a single database and wish to install more than
+            one version of MediaWiki, or have other applications that also use the
+            database, you can give the table names a unique prefix to stop any naming
+            conflicts or confusion.
+            See <link xlink:href='https://www.mediawiki.org/wiki/Manual:$wgDBprefix'/>.
+          '';
+        };
+
+        socket = mkOption {
+          type = types.nullOr types.path;
+          default = if cfg.database.createLocally then "/run/mysqld/mysqld.sock" else null;
+          defaultText = "/run/mysqld/mysqld.sock";
+          description = "Path to the unix socket file to use for authentication.";
+        };
+
+        createLocally = mkOption {
+          type = types.bool;
+          default = cfg.database.type == "mysql";
+          defaultText = "true";
+          description = ''
+            Create the database and database user locally.
+            This currently only applies if database type "mysql" is selected.
+          '';
+        };
+      };
+
+      virtualHost = mkOption {
+        type = types.submodule ({
+          options = import ../web-servers/apache-httpd/per-server-options.nix {
+            inherit lib;
+            forMainServer = false;
+          };
+        });
+        example = literalExample ''
+          {
+            hostName = "mediawiki.example.org";
+            enableSSL = true;
+            adminAddr = "webmaster@example.org";
+            sslServerCert = "/var/lib/acme/mediawiki.example.org/full.pem";
+            sslServerKey = "/var/lib/acme/mediawiki.example.org/key.pem";
+          }
+        '';
+        description = ''
+          Apache configuration can be done by adapting <option>services.httpd.virtualHosts</option>.
+          See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
+        '';
+      };
+
+      poolConfig = mkOption {
+        type = types.lines;
+        default = ''
+          pm = dynamic
+          pm.max_children = 32
+          pm.start_servers = 2
+          pm.min_spare_servers = 2
+          pm.max_spare_servers = 4
+          pm.max_requests = 500
+        '';
+        description = ''
+          Options for MediaWiki's PHP pool. See the documentation on <literal>php-fpm.conf</literal>
+          for details on configuration directives.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        description = ''
+          Any additional text to be appended to MediaWiki's
+          LocalSettings.php configuration file. For configuration
+          settings, see <link xlink:href="https://www.mediawiki.org/wiki/Manual:Configuration_settings"/>.
+        '';
+        default = "";
+        example = ''
+          $wgEnableEmail = false;
+        '';
+      };
+
+    };
+  };
+
+  # implementation
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = cfg.database.createLocally -> cfg.database.type == "mysql";
+        message = "services.mediawiki.createLocally is currently only supported for database type 'mysql'";
+      }
+      { assertion = cfg.database.createLocally -> cfg.database.user == user;
+        message = "services.mediawiki.database.user must be set to ${user} if services.mediawiki.database.createLocally is set true";
+      }
+      { assertion = cfg.database.createLocally -> cfg.database.socket != null;
+        message = "services.mediawiki.database.socket must be set if services.mediawiki.database.createLocally is set to true";
+      }
+      { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
+        message = "a password cannot be specified if services.mediawiki.database.createLocally is set to true";
+      }
+    ];
+
+    services.mediawiki.skins = {
+      MonoBook = "${cfg.package}/share/mediawiki/skins/MonoBook";
+      Timeless = "${cfg.package}/share/mediawiki/skins/Timeless";
+      Vector = "${cfg.package}/share/mediawiki/skins/Vector";
+    };
+
+    services.mysql = mkIf cfg.database.createLocally {
+      enable = true;
+      package = mkDefault pkgs.mariadb;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        { name = cfg.database.user;
+          ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
+        }
+      ];
+    };
+
+    services.phpfpm.pools.mediawiki = {
+      listen = "/run/phpfpm/mediawiki.sock";
+      extraConfig = ''
+        listen.owner = ${config.services.httpd.user}
+        listen.group = ${config.services.httpd.group}
+        user = ${user}
+        group = ${group}
+
+        env[MEDIAWIKI_CONFIG] = ${mediawikiConfig}
+
+        ${cfg.poolConfig}
+      '';
+    };
+
+    services.httpd = {
+      enable = true;
+      adminAddr = mkDefault cfg.virtualHost.adminAddr;
+      extraModules = [ "proxy_fcgi" ];
+      virtualHosts = [ (mkMerge [
+        cfg.virtualHost {
+          documentRoot = mkForce "${pkg}/share/mediawiki";
+          extraConfig = ''
+            <Directory "${pkg}/share/mediawiki">
+              <FilesMatch "\.php$">
+                <If "-f %{REQUEST_FILENAME}">
+                  SetHandler "proxy:unix:${fpm.listen}|fcgi://localhost/"
+                </If>
+              </FilesMatch>
+
+              Require all granted
+              DirectoryIndex index.php
+              AllowOverride All
+            </Directory>
+          '' + optionalString (cfg.uploadsDir != null) ''
+            Alias "/images" "${cfg.uploadsDir}"
+            <Directory "${cfg.uploadsDir}">
+              Require all granted
+            </Directory>
+          '';
+        }
+      ]) ];
+    };
+
+    systemd.tmpfiles.rules = [
+      "d '${stateDir}' 0750 ${user} ${group} - -"
+      "d '${cacheDir}' 0750 ${user} ${group} - -"
+    ] ++ optionals (cfg.uploadsDir != null) [
+      "d '${cfg.uploadsDir}' 0750 ${user} ${group} - -"
+      "Z '${cfg.uploadsDir}' 0750 ${user} ${group} - -"
+    ];
+
+    systemd.services.mediawiki-init = {
+      wantedBy = [ "multi-user.target" ];
+      before = [ "phpfpm-mediawiki.service" ];
+      after = optional cfg.database.createLocally "mysql.service";
+      script = ''
+        if ! test -e "${stateDir}/secret.key"; then
+          tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c 64 > ${stateDir}/secret.key
+        fi
+
+        echo "exit( wfGetDB( DB_MASTER )->tableExists( 'user' ) ? 1 : 0 );" | \
+        ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/eval.php --conf ${mediawikiConfig} && \
+        ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/install.php \
+          --confpath /tmp \
+          --scriptpath / \
+          --dbserver ${cfg.database.host}${optionalString (cfg.database.socket != null) ":${cfg.database.socket}"} \
+          --dbport ${toString cfg.database.port} \
+          --dbname ${cfg.database.name} \
+          ${optionalString (cfg.database.tablePrefix != null) "--dbprefix ${cfg.database.tablePrefix}"} \
+          --dbuser ${cfg.database.user} \
+          ${optionalString (cfg.database.passwordFile != null) "--dbpassfile ${cfg.database.passwordFile}"} \
+          --passfile ${cfg.passwordFile} \
+          ${cfg.name} \
+          admin
+
+        ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/update.php --conf ${mediawikiConfig} --quick
+      '';
+
+      serviceConfig = {
+        Type = "oneshot";
+        User = user;
+        Group = group;
+        PrivateTmp = true;
+      };
+    };
+
+    systemd.services.httpd.after = optional (cfg.database.createLocally && cfg.database.type == "mysql") "mysql.service";
+
+    users.users.${user}.group = group;
+
+    environment.systemPackages = [ mediawikiScripts ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
index fa9a36d11892..a0214a75d93e 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
@@ -297,8 +297,23 @@ in {
 
       systemd.services = {
         "nextcloud-setup" = let
+          c = cfg.config;
+          writePhpArrary = a: "[${concatMapStringsSep "," (val: ''"${toString val}"'') a}]";
           overrideConfig = pkgs.writeText "nextcloud-config.php" ''
             <?php
+            ${optionalString (c.dbpassFile != null) ''
+              function nix_read_pwd() {
+                $file = "${c.dbpassFile}";
+                if (!file_exists($file)) {
+                  throw new \RuntimeException(sprintf(
+                    "Cannot start Nextcloud, dbpass file %s set by NixOS doesn't exist!",
+                    $file
+                  ));
+                }
+
+                return trim(file_get_contents($file));
+              }
+            ''}
             $CONFIG = [
               'apps_paths' => [
                 [ 'path' => '${cfg.home}/apps', 'url' => '/apps', 'writable' => false ],
@@ -309,19 +324,27 @@ in {
               ${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"}
               'log_type' => 'syslog',
               'log_level' => '${builtins.toString cfg.logLevel}',
-              ${optionalString (cfg.config.overwriteProtocol != null) "'overwriteprotocol' => '${cfg.config.overwriteProtocol}',"}
+              ${optionalString (c.overwriteProtocol != null) "'overwriteprotocol' => '${c.overwriteProtocol}',"}
+              ${optionalString (c.dbname != null) "'dbname' => '${c.dbname}',"}
+              ${optionalString (c.dbhost != null) "'dbhost' => '${c.dbhost}',"}
+              ${optionalString (c.dbport != null) "'dbport' => '${toString c.dbport}',"}
+              ${optionalString (c.dbuser != null) "'dbuser' => '${c.dbuser}',"}
+              ${optionalString (c.dbtableprefix != null) "'dbtableprefix' => '${toString c.dbtableprefix}',"}
+              ${optionalString (c.dbpass != null) "'dbpassword' => '${c.dbpass}',"}
+              ${optionalString (c.dbpassFile != null) "'dbpassword' => nix_read_pwd(),"}
+              'dbtype' => '${c.dbtype}',
+              'trusted_domains' => ${writePhpArrary ([ cfg.hostName ] ++ c.extraTrustedDomains)},
             ];
           '';
           occInstallCmd = let
-            c = cfg.config;
-            adminpass = if c.adminpassFile != null
-              then ''"$(<"${toString c.adminpassFile}")"''
-              else ''"${toString c.adminpass}"'';
             dbpass = if c.dbpassFile != null
               then ''"$(<"${toString c.dbpassFile}")"''
               else if c.dbpass != null
               then ''"${toString c.dbpass}"''
               else null;
+            adminpass = if c.adminpassFile != null
+              then ''"$(<"${toString c.adminpassFile}")"''
+              else ''"${toString c.adminpass}"'';
             installFlags = concatStringsSep " \\\n    "
               (mapAttrsToList (k: v: "${k} ${toString v}") {
               "--database" = ''"${c.dbtype}"'';
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml
index d78d866086a6..d66e0f0c2997 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml
@@ -42,10 +42,12 @@
 
   services.postgresql = {
     <link linkend="opt-services.postgresql.enable">enable</link> = true;
-    <link linkend="opt-services.postgresql.initialScript">initialScript</link> = pkgs.writeText "psql-init" ''
-      CREATE ROLE nextcloud WITH LOGIN;
-      CREATE DATABASE nextcloud WITH OWNER nextcloud;
-    '';
+    <link linkend="opt-services.postgresql.ensureDatabases">ensureDatabases</link> = [ "nextcloud" ];
+    <link linkend="opt-services.postgresql.ensureUsers">ensureUsers</link> = [
+     { name = "nextcloud";
+       ensurePermissions."DATABASE nextcloud" = "ALL PRIVILEGES";
+     }
+    ];
   };
 
   # ensure that postgres is running *before* running the setup
@@ -63,17 +65,22 @@
    are used internally to configure an HTTP server using
    <literal><link xlink:href="https://php-fpm.org/">PHP-FPM</link></literal>
    and <literal>nginx</literal>. The <literal>config</literal> attribute set is
-   used for the <literal>config.php</literal> which is used for the
-   application's configuration. <emphasis>Beware: this isn't entirely pure
-   since the config is modified by the application's runtime!</emphasis>
+   used by the imperative installer and all values are written to an additional file
+   to ensure that changes can be applied by changing the module's options.
   </para>
 
   <para>
-   In case the application serves multiple hosts (those are checked with
+   In case the application serves multiple domains (those are checked with
    <literal><link xlink:href="http://php.net/manual/en/reserved.variables.server.php">$_SERVER['HTTP_HOST']</link></literal>)
-   those can be added using
+   it's needed to add them to
    <literal><link linkend="opt-services.nextcloud.config.extraTrustedDomains">services.nextcloud.config.extraTrustedDomains</link></literal>.
   </para>
+
+  <para>
+   Auto updates for Nextcloud apps can be enabled using
+   <literal><link linkend="opt-services.nextcloud.autoUpdateApps.enable">services.nextcloud.autoUpdateApps</link></literal>.
+</para>
+
  </section>
  <section xml:id="module-services-nextcloud-pitfalls-during-upgrade">
   <title>Pitfalls</title>
@@ -87,35 +94,24 @@
   </para>
 
   <para>
-   Right now changes to the <literal>services.nextcloud.config</literal>
-   attribute set won't take effect after the first install (except
-   <literal><link linkend="opt-services.nextcloud.config.extraTrustedDomains">services.nextcloud.config.extraTrustedDomains</link></literal>)
-   since the actual configuration file is generated by the NextCloud installer
-   which also sets up critical parts such as the database structure.
+   All configuration parameters are also stored in
+   <literal>/var/lib/nextcloud/config/override.config.php</literal> which is generated by
+   the module and linked from the store to ensure that all values from <literal>config.php</literal>
+   can be modified by the module.
+   However <literal>config.php</literal> manages the application's state and shouldn't be touched
+   manually because of that.
   </para>
 
-  <para>
-   <emphasis>Warning: don't delete <literal>config.php</literal>! This file
+  <warning>
+   <para>Don't delete <literal>config.php</literal>! This file
    tracks the application's state and a deletion can cause unwanted
-   side-effects!</emphasis>
-  </para>
+   side-effects!</para>
+  </warning>
 
-  <para>
-   <emphasis>Warning: don't rerun <literal>nextcloud-occ
+  <warning>
+   <para>Don't rerun <literal>nextcloud-occ
    maintenance:install</literal>! This command tries to install the application
-   and can cause unwanted side-effects!</emphasis>
-  </para>
-
-  <para>
-   The issues are known and reported in
-   <link xlink:href="https://github.com/NixOS/nixpkgs/issues/49783">#49783</link>,
-   for now it's unfortunately necessary to manually work around these issues.
-  </para>
-
-  <para>
-   Right now app installation and configuration is done imperatively in the nextcloud web ui or via the <literal>nextcloud-occ</literal> command line utility.
-   You can activate auto updates for your apps via
-   <literal><link linkend="opt-services.nextcloud.autoUpdateApps.enable">services.nextcloud.autoUpdateApps</link></literal>.
-  </para>
+   and can cause unwanted side-effects!</para>
+  </warning>
  </section>
 </chapter>
diff --git a/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix b/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix
index b882f6c2ae7e..1bd9de93735d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix
@@ -16,6 +16,9 @@ let
 
   poolName = "tt-rss";
 
+  mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
+  pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
+
   tt-rss-config = pkgs.writeText "config.php" ''
     <?php
 
@@ -200,6 +203,12 @@ let
             and 3306 for pgsql and mysql respectively).
           '';
         };
+
+        createLocally = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Create the database and database user locally.";
+        };
       };
 
       auth = {
@@ -551,9 +560,13 @@ let
       };
     };
 
-    systemd.services.tt-rss = let
-      dbService = if cfg.database.type == "pgsql" then "postgresql.service" else "mysql.service";
-    in {
+    systemd.tmpfiles.rules = [
+      "d '${cfg.root}' 0755 ${cfg.user} tt_rss - -"
+      "Z '${cfg.root}' 0755 ${cfg.user} tt_rss - -"
+    ];
+
+    systemd.services.tt-rss =
+      {
 
         description = "Tiny Tiny RSS feeds update daemon";
 
@@ -562,14 +575,14 @@ let
               if cfg.database.type == "pgsql" then ''
                   ${optionalString (cfg.database.password != null) "PGPASSWORD=${cfg.database.password}"} \
                   ${optionalString (cfg.database.passwordFile != null) "PGPASSWORD=$(cat ${cfg.database.passwordFile})"} \
-                  ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${config.services.postgresql.package}/bin/psql \
+                  ${config.services.postgresql.package}/bin/psql \
                     -U ${cfg.database.user} \
                     ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} --port ${toString dbPort}"} \
                     -c '${e}' \
                     ${cfg.database.name}''
 
               else if cfg.database.type == "mysql" then ''
-                  echo '${e}' | ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${config.services.mysql.package}/bin/mysql \
+                  echo '${e}' | ${config.services.mysql.package}/bin/mysql \
                     -u ${cfg.database.user} \
                     ${optionalString (cfg.database.password != null) "-p${cfg.database.password}"} \
                     ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} -P ${toString dbPort}"} \
@@ -579,7 +592,6 @@ let
 
         in ''
           rm -rf "${cfg.root}/*"
-          mkdir -m 755 -p "${cfg.root}"
           cp -r "${pkgs.tt-rss}/"* "${cfg.root}"
           ${optionalString (cfg.pluginPackages != []) ''
             for plugin in ${concatStringsSep " " cfg.pluginPackages}; do
@@ -592,19 +604,10 @@ let
             done
           ''}
           ln -sf "${tt-rss-config}" "${cfg.root}/config.php"
-          chown -R "${cfg.user}" "${cfg.root}"
           chmod -R 755 "${cfg.root}"
         ''
 
         + (optionalString (cfg.database.type == "pgsql") ''
-          ${optionalString (cfg.database.host == null && cfg.database.password == null) ''
-            if ! [ -e ${cfg.root}/.db-created ]; then
-              ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createuser ${cfg.database.user}
-              ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -O ${cfg.database.user} ${cfg.database.name}
-              touch ${cfg.root}/.db-created
-            fi
-          ''}
-
           exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \
           | tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//')
 
@@ -628,18 +631,18 @@ let
 
         serviceConfig = {
           User = "${cfg.user}";
+          Group = "tt_rss";
           ExecStart = "${pkgs.php}/bin/php ${cfg.root}/update.php --daemon";
           StandardOutput = "syslog";
           StandardError = "syslog";
-          PermissionsStartOnly = true;
         };
 
         wantedBy = [ "multi-user.target" ];
-        requires = ["${dbService}"];
-        after = ["network.target" "${dbService}"];
+        requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
+        after = [ "network.target" ] ++ optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
     };
 
-    services.mysql = optionalAttrs (cfg.database.type == "mysql") {
+    services.mysql = mkIf mysqlLocal {
       enable = true;
       package = mkDefault pkgs.mysql;
       ensureDatabases = [ cfg.database.name ];
@@ -653,17 +656,22 @@ let
       ];
     };
 
-    services.postgresql = optionalAttrs (cfg.database.type == "pgsql") {
+    services.postgresql = mkIf pgsqlLocal {
       enable = mkDefault true;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        { name = cfg.user;
+          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+        }
+      ];
     };
 
-    users = optionalAttrs (cfg.user == "tt_rss") {
-      users.tt_rss = {
-        description = "tt-rss service user";
-        isSystemUser = true;
-        group = "tt_rss";
-      };
-      groups.tt_rss = {};
+    users.users.tt_rss = optionalAttrs (cfg.user == "tt_rss") {
+      description = "tt-rss service user";
+      isSystemUser = true;
+      group = "tt_rss";
     };
+
+    users.groups.tt_rss = {};
   };
 }
diff --git a/nixpkgs/nixos/modules/services/web-apps/zabbix.nix b/nixpkgs/nixos/modules/services/web-apps/zabbix.nix
new file mode 100644
index 000000000000..4b5334579a99
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/zabbix.nix
@@ -0,0 +1,225 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption types;
+  inherit (lib) literalExample mapAttrs optionalString;
+
+  cfg = config.services.zabbixWeb;
+  fpm = config.services.phpfpm.pools.zabbix;
+
+  user = "zabbix";
+  group = "zabbix";
+  stateDir = "/var/lib/zabbix";
+
+  zabbixConfig = pkgs.writeText "zabbix.conf.php" ''
+    <?php
+    // Zabbix GUI configuration file.
+    global $DB;
+    $DB['TYPE'] = '${ { "mysql" = "MYSQL"; "pgsql" = "POSTGRESQL"; "oracle" = "ORACLE"; }.${cfg.database.type} }';
+    $DB['SERVER'] = '${cfg.database.host}';
+    $DB['PORT'] = '${toString cfg.database.port}';
+    $DB['DATABASE'] = '${cfg.database.name}';
+    $DB['USER'] = '${cfg.database.user}';
+    $DB['PASSWORD'] = ${if cfg.database.passwordFile != null then "file_get_contents('${cfg.database.passwordFile}')" else "''"};
+    // Schema name. Used for IBM DB2 and PostgreSQL.
+    $DB['SCHEMA'] = ''';
+    $ZBX_SERVER = '${cfg.server.address}';
+    $ZBX_SERVER_PORT = '${toString cfg.server.port}';
+    $ZBX_SERVER_NAME = ''';
+    $IMAGE_FORMAT_DEFAULT = IMAGE_FORMAT_PNG;
+  '';
+
+in
+{
+  # interface
+
+  options.services = {
+    zabbixWeb = {
+      enable = mkEnableOption "the Zabbix web interface";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.zabbix.web;
+        defaultText = "zabbix.web";
+        description = "Which Zabbix package to use.";
+      };
+
+      server = {
+        port = mkOption {
+          type = types.int;
+          description = "The port of the Zabbix server to connect to.";
+          default = 10051;
+        };
+
+        address = mkOption {
+          type = types.str;
+          description = "The IP address or hostname of the Zabbix server to connect to.";
+          default = "localhost";
+        };
+      };
+
+      database = {
+        type = mkOption {
+          type = types.enum [ "mysql" "pgsql" "oracle" ];
+          example = "mysql";
+          default = "pgsql";
+          description = "Database engine to use.";
+        };
+
+        host = mkOption {
+          type = types.str;
+          default = "";
+          description = "Database host address.";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default =
+            if cfg.database.type == "mysql" then config.services.mysql.port
+            else if cfg.database.type == "pgsql" then config.services.postgresql.port
+            else 1521;
+          description = "Database host port.";
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "zabbix";
+          description = "Database name.";
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "zabbix";
+          description = "Database user.";
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/keys/zabbix-dbpassword";
+          description = ''
+            A file containing the password corresponding to
+            <option>database.user</option>.
+          '';
+        };
+
+        socket = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/postgresql";
+          description = "Path to the unix socket file to use for authentication.";
+        };
+      };
+
+      virtualHost = mkOption {
+        type = types.submodule ({
+          options = import ../web-servers/apache-httpd/per-server-options.nix {
+            inherit lib;
+            forMainServer = false;
+          };
+        });
+        example = {
+          hostName = "zabbix.example.org";
+          enableSSL = true;
+          adminAddr = "webmaster@example.org";
+          sslServerCert = "/var/lib/acme/zabbix.example.org/full.pem";
+          sslServerKey = "/var/lib/acme/zabbix.example.org/key.pem";
+        };
+        description = ''
+          Apache configuration can be done by adapting <literal>services.httpd.virtualHosts.&lt;name&gt;</literal>.
+          See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
+        '';
+      };
+
+      poolConfig = mkOption {
+        type = types.lines;
+        default = ''
+          pm = dynamic
+          pm.max_children = 32
+          pm.start_servers = 2
+          pm.min_spare_servers = 2
+          pm.max_spare_servers = 4
+          pm.max_requests = 500
+        '';
+        description = ''
+          Options for the Zabbix PHP pool. See the documentation on <literal>php-fpm.conf</literal> for details on configuration directives.
+        '';
+      };
+
+    };
+  };
+
+  # implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.tmpfiles.rules = [
+      "d '${stateDir}' 0750 ${user} ${group} - -"
+      "d '${stateDir}/session' 0750 ${user} ${config.services.httpd.group} - -"
+    ];
+
+    services.phpfpm.pools.zabbix = {
+      phpOptions = ''
+        # https://www.zabbix.com/documentation/current/manual/installation/install
+        memory_limit = 128M
+        post_max_size = 16M
+        upload_max_filesize = 2M
+        max_execution_time = 300
+        max_input_time = 300
+        session.auto_start = 0
+        mbstring.func_overload = 0
+        always_populate_raw_post_data = -1
+        # https://bbs.archlinux.org/viewtopic.php?pid=1745214#p1745214
+        session.save_path = ${stateDir}/session
+      '' + optionalString (config.time.timeZone != null) ''
+        date.timezone = "${config.time.timeZone}"
+      '' + optionalString (cfg.database.type == "oracle") ''
+        extension=${pkgs.phpPackages.oci8}/lib/php/extensions/oci8.so
+      '';
+      listen = "/run/phpfpm/zabbix.sock";
+      extraConfig = ''
+        listen.owner = ${config.services.httpd.user};
+        listen.group = ${config.services.httpd.group};
+        user = ${user};
+        group = ${config.services.httpd.group};
+        env[ZABBIX_CONFIG] = ${zabbixConfig}
+        ${cfg.poolConfig}
+      '';
+    };
+
+    services.httpd = {
+      enable = true;
+      adminAddr = mkDefault cfg.virtualHost.adminAddr;
+      extraModules = [ "proxy_fcgi" ];
+      virtualHosts = [ (mkMerge [
+        cfg.virtualHost {
+          documentRoot = mkForce "${cfg.package}/share/zabbix";
+          extraConfig = ''
+            <Directory "${cfg.package}/share/zabbix">
+              <FilesMatch "\.php$">
+                <If "-f %{REQUEST_FILENAME}">
+                  SetHandler "proxy:unix:${fpm.listen}|fcgi://localhost/"
+                </If>
+              </FilesMatch>
+              AllowOverride all
+              Options -Indexes
+              DirectoryIndex index.php
+            </Directory>
+          '';
+        }
+      ]) ];
+    };
+
+    users.users.${user} = mapAttrs (name: mkDefault) {
+      description = "Zabbix daemon user";
+      uid = config.ids.uids.zabbix;
+      inherit group;
+    };
+
+    users.groups.${group} = mapAttrs (name: mkDefault) {
+      gid = config.ids.gids.zabbix;
+    };
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
index bf99f6c132af..ea9476a7c915 100644
--- a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
@@ -21,10 +21,9 @@ let
     else [{ip = "*"; port = 80;}];
 
   getListen = cfg:
-    let list = (lib.optional (cfg.port != 0) {ip = "*"; port = cfg.port;}) ++ cfg.listen;
-    in if list == []
-        then defaultListen cfg
-        else list;
+    if cfg.listen == []
+      then defaultListen cfg
+      else cfg.listen;
 
   listenToString = l: "${l.ip}:${toString l.port}";
 
@@ -638,7 +637,7 @@ in
                      message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; }
                  ];
 
-    warnings = map (cfg: ''apache-httpd's port option is deprecated. Use listen = [{/*ip = "*"; */ port = ${toString cfg.port};}]; instead'' ) (lib.filter (cfg: cfg.port != 0) allHosts);
+    warnings = map (cfg: "apache-httpd's extraSubservices option is deprecated. Most existing subservices have been ported to the NixOS module system. Please update your configuration accordingly.") (lib.filter (cfg: cfg.extraSubservices != []) allHosts);
 
     users.users = optionalAttrs (mainCfg.user == "wwwrun") (singleton
       { name = "wwwrun";
@@ -672,7 +671,7 @@ in
 
         wantedBy = [ "multi-user.target" ];
         wants = [ "keys.target" ];
-        after = [ "network.target" "fs.target" "postgresql.service" "keys.target" ];
+        after = [ "network.target" "fs.target" "keys.target" ];
 
         path =
           [ httpd pkgs.coreutils pkgs.gnugrep ]
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/foswiki.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/foswiki.nix
deleted file mode 100644
index 8c1ac8935a47..000000000000
--- a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/foswiki.nix
+++ /dev/null
@@ -1,78 +0,0 @@
-{ config, pkgs, lib, serverInfo, ... }:
-let
-  inherit (pkgs) foswiki;
-  inherit (serverInfo.serverConfig) user group;
-  inherit (config) vardir;
-in
-{
-  options.vardir = lib.mkOption {
-    type = lib.types.path;
-    default = "/var/www/foswiki";
-    description = "The directory where variable foswiki data will be stored and served from.";
-  };
-
-  # TODO: this will probably need to be better customizable
-  extraConfig =
-    let httpd-conf = pkgs.runCommand "foswiki-httpd.conf"
-      { preferLocalBuild = true; }
-      ''
-        substitute '${foswiki}/foswiki_httpd_conf.txt' "$out" \
-          --replace /var/www/foswiki/ "${vardir}/"
-      '';
-    in
-      ''
-        RewriteEngine on
-        RewriteRule /foswiki/(.*) ${vardir}/$1
-
-        <Directory "${vardir}">
-          Require all granted
-        </Directory>
-
-        Include ${httpd-conf}
-        <Directory "${vardir}/pub">
-          Options FollowSymlinks
-        </Directory>
-      '';
-
-  /** This handles initial setup and updates.
-      It will probably need some tweaking, maybe per-site.  */
-  startupScript = pkgs.writeScript "foswiki_startup.sh" (
-    let storeLink = "${vardir}/package"; in
-    ''
-      [ -e '${storeLink}' ] || needs_setup=1
-      mkdir -p '${vardir}'
-      cd '${vardir}'
-      ln -sf -T '${foswiki}' '${storeLink}'
-
-      if [ -n "$needs_setup" ]; then # do initial setup
-        mkdir -p bin lib
-        # setup most of data/ as copies only
-        cp -r '${foswiki}'/data '${vardir}/'
-        rm -r '${vardir}'/data/{System,mime.types}
-        ln -sr -t '${vardir}/data/' '${storeLink}'/data/{System,mime.types}
-
-        ln -sr '${storeLink}/locale' .
-
-        mkdir pub
-        ln -sr '${storeLink}/pub/System' pub/
-
-        mkdir templates
-        ln -sr '${storeLink}'/templates/* templates/
-
-        ln -sr '${storeLink}/tools' .
-
-        mkdir -p '${vardir}'/working/{logs,tmp}
-        ln -sr '${storeLink}/working/README' working/ # used to check dir validity
-
-        chown -R '${user}:${group}' .
-        chmod +w -R .
-      fi
-
-      # bin/* and lib/* shall always be overwritten, in case files are added
-      ln -srf '${storeLink}'/bin/* '${vardir}/bin/'
-      ln -srf '${storeLink}'/lib/* '${vardir}/lib/'
-    ''
-    /* Symlinking bin/ one-by-one ensures that ${vardir}/lib/LocalSite.cfg
-        is used instead of ${foswiki}/... */
-  );
-}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
deleted file mode 100644
index 6234478014ce..000000000000
--- a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
+++ /dev/null
@@ -1,349 +0,0 @@
-{ config, lib, pkgs, serverInfo, php, ... }:
-
-with lib;
-
-let
-
-  httpd = serverInfo.serverConfig.package;
-
-  version24 = !versionOlder httpd.version "2.4";
-
-  allGranted = if version24 then ''
-    Require all granted
-  '' else ''
-    Order allow,deny
-    Allow from all
-  '';
-
-  mediawikiConfig = pkgs.writeText "LocalSettings.php"
-    ''
-      <?php
-        # Copied verbatim from the default (generated) LocalSettings.php.
-        if( defined( 'MW_INSTALL_PATH' ) ) {
-                $IP = MW_INSTALL_PATH;
-        } else {
-                $IP = dirname( __FILE__ );
-        }
-
-        $path = array( $IP, "$IP/includes", "$IP/languages" );
-        set_include_path( implode( PATH_SEPARATOR, $path ) . PATH_SEPARATOR . get_include_path() );
-
-        require_once( "$IP/includes/DefaultSettings.php" );
-
-        if ( $wgCommandLineMode ) {
-                if ( isset( $_SERVER ) && array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) {
-                        die( "This script must be run from the command line\n" );
-                }
-        }
-
-        $wgScriptPath = "${config.urlPrefix}";
-
-        # We probably need to set $wgSecretKey and $wgCacheEpoch.
-
-        # Paths to external programs.
-        $wgDiff3 = "${pkgs.diffutils}/bin/diff3";
-        $wgDiff = "${pkgs.diffutils}/bin/diff";
-        $wgImageMagickConvertCommand = "${pkgs.imagemagick.out}/bin/convert";
-
-        #$wgDebugLogFile = "/tmp/mediawiki_debug_log.txt";
-
-        # Database configuration.
-        $wgDBtype = "${config.dbType}";
-        $wgDBserver = "${config.dbServer}";
-        $wgDBuser = "${config.dbUser}";
-        $wgDBpassword = "${config.dbPassword}";
-        $wgDBname = "${config.dbName}";
-
-        # E-mail.
-        $wgEmergencyContact = "${config.emergencyContact}";
-        $wgPasswordSender = "${config.passwordSender}";
-
-        $wgSitename = "${config.siteName}";
-
-        ${optionalString (config.logo != "") ''
-          $wgLogo = "${config.logo}";
-        ''}
-
-        ${optionalString (config.articleUrlPrefix != "") ''
-          $wgArticlePath = "${config.articleUrlPrefix}/$1";
-        ''}
-
-        ${optionalString config.enableUploads ''
-          $wgEnableUploads = true;
-          $wgUploadDirectory = "${config.uploadDir}";
-        ''}
-
-        ${optionalString (config.defaultSkin != "") ''
-          $wgDefaultSkin = "${config.defaultSkin}";
-        ''}
-
-        ${config.extraConfig}
-      ?>
-    '';
-
-  # Unpack Mediawiki and put the config file in its root directory.
-  mediawikiRoot = pkgs.stdenv.mkDerivation rec {
-    name= "mediawiki-1.31.1";
-
-    src = pkgs.fetchurl {
-      url = "https://releases.wikimedia.org/mediawiki/1.31/${name}.tar.gz";
-      sha256 = "13x48clij21cmysjkpnx68vggchrdasqp7b290j87xlfgjhdhnnf";
-    };
-
-    skins = config.skins;
-    extensions = config.extensions;
-
-    buildPhase =
-      ''
-        for skin in $skins; do
-          cp -prvd $skin/* skins/
-        done
-        for extension in $extensions; do
-          cp -prvd $extension/* extensions/
-        done
-      ''; # */
-
-    installPhase =
-      ''
-        mkdir -p $out
-        cp -r * $out
-        cp ${mediawikiConfig} $out/LocalSettings.php
-        sed -i \
-        -e 's|/bin/bash|${pkgs.bash}/bin/bash|g' \
-        -e 's|/usr/bin/timeout|${pkgs.coreutils}/bin/timeout|g' \
-          $out/includes/shell/limit.sh \
-          $out/includes/GlobalFunctions.php
-      '';
-  };
-
-  mediawikiScripts = pkgs.runCommand "mediawiki-${config.id}-scripts" {
-      buildInputs = [ pkgs.makeWrapper ];
-      preferLocalBuild = true;
-    } ''
-      mkdir -p $out/bin
-      for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do
-        makeWrapper ${php}/bin/php $out/bin/mediawiki-${config.id}-$(basename $i .php) \
-          --add-flags ${mediawikiRoot}/maintenance/$i
-      done
-    '';
-
-in
-
-{
-
-  extraConfig =
-    ''
-      ${optionalString config.enableUploads ''
-        Alias ${config.urlPrefix}/images ${config.uploadDir}
-
-        <Directory ${config.uploadDir}>
-            ${allGranted}
-            Options -Indexes
-        </Directory>
-      ''}
-
-      ${if config.urlPrefix != "" then "Alias ${config.urlPrefix} ${mediawikiRoot}" else ''
-        RewriteEngine On
-        RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
-        RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
-        ${concatMapStringsSep "\n" (u: "RewriteCond %{REQUEST_URI} !^${u.urlPath}") serverInfo.vhostConfig.servedDirs}
-        ${concatMapStringsSep "\n" (u: "RewriteCond %{REQUEST_URI} !^${u.urlPath}") serverInfo.vhostConfig.servedFiles}
-        RewriteRule ${if config.enableUploads
-          then "!^/images"
-          else "^.*\$"
-        } %{DOCUMENT_ROOT}/${if config.articleUrlPrefix == ""
-          then ""
-          else "${config.articleUrlPrefix}/"
-        }index.php [L]
-      ''}
-
-      <Directory ${mediawikiRoot}>
-          ${allGranted}
-          DirectoryIndex index.php
-      </Directory>
-
-      ${optionalString (config.articleUrlPrefix != "") ''
-        Alias ${config.articleUrlPrefix} ${mediawikiRoot}/index.php
-      ''}
-    '';
-
-  documentRoot = if config.urlPrefix == "" then mediawikiRoot else null;
-
-  enablePHP = true;
-
-  options = {
-
-    id = mkOption {
-      default = "main";
-      description = ''
-        A unique identifier necessary to keep multiple MediaWiki server
-        instances on the same machine apart.  This is used to
-        disambiguate the administrative scripts, which get names like
-        mediawiki-$id-change-password.
-      '';
-    };
-
-    dbType = mkOption {
-      default = "postgres";
-      example = "mysql";
-      description = "Database type.";
-    };
-
-    dbName = mkOption {
-      default = "mediawiki";
-      description = "Name of the database that holds the MediaWiki data.";
-    };
-
-    dbServer = mkOption {
-      default = ""; # use a Unix domain socket
-      example = "10.0.2.2";
-      description = ''
-        The location of the database server.  Leave empty to use a
-        database server running on the same machine through a Unix
-        domain socket.
-      '';
-    };
-
-    dbUser = mkOption {
-      default = "mediawiki";
-      description = "The user name for accessing the database.";
-    };
-
-    dbPassword = mkOption {
-      default = "";
-      example = "foobar";
-      description = ''
-        The password of the database user.  Warning: this is stored in
-        cleartext in the Nix store!
-      '';
-    };
-
-    emergencyContact = mkOption {
-      default = serverInfo.serverConfig.adminAddr;
-      example = "admin@example.com";
-      description = ''
-        Emergency contact e-mail address.  Defaults to the Apache
-        admin address.
-      '';
-    };
-
-    passwordSender = mkOption {
-      default = serverInfo.serverConfig.adminAddr;
-      example = "password@example.com";
-      description = ''
-        E-mail address from which password confirmations originate.
-        Defaults to the Apache admin address.
-      '';
-    };
-
-    siteName = mkOption {
-      default = "MediaWiki";
-      example = "Foobar Wiki";
-      description = "Name of the wiki";
-    };
-
-    logo = mkOption {
-      default = "";
-      example = "/images/logo.png";
-      description = "The URL of the site's logo (which should be a 135x135px image).";
-    };
-
-    urlPrefix = mkOption {
-      default = "/w";
-      description = ''
-        The URL prefix under which the Mediawiki service appears.
-      '';
-    };
-
-    articleUrlPrefix = mkOption {
-      default = "/wiki";
-      example = "";
-      description = ''
-        The URL prefix under which article pages appear,
-        e.g. http://server/wiki/Page.  Leave empty to use the main URL
-        prefix, e.g. http://server/w/index.php?title=Page.
-      '';
-    };
-
-    enableUploads = mkOption {
-      default = false;
-      description = "Whether to enable file uploads.";
-    };
-
-    uploadDir = mkOption {
-      default = throw "You must specify `uploadDir'.";
-      example = "/data/mediawiki-upload";
-      description = "The directory that stores uploaded files.";
-    };
-
-    defaultSkin = mkOption {
-      default = "";
-      example = "nostalgia";
-      description = "Set this value to change the default skin used by MediaWiki.";
-    };
-
-    skins = mkOption {
-      default = [];
-      type = types.listOf types.path;
-      description =
-        ''
-          List of paths whose content is copied to the ‘skins’
-          subdirectory of the MediaWiki installation.
-        '';
-    };
-
-    extensions = mkOption {
-      default = [];
-      type = types.listOf types.path;
-      description =
-        ''
-          List of paths whose content is copied to the 'extensions'
-          subdirectory of the MediaWiki installation.
-        '';
-    };
-
-    extraConfig = mkOption {
-      type = types.lines;
-      default = "";
-      example =
-        ''
-          $wgEnableEmail = false;
-        '';
-      description = ''
-        Any additional text to be appended to MediaWiki's
-        configuration file.  This is a PHP script.  For configuration
-        settings, see <link xlink:href='https://www.mediawiki.org/wiki/Manual:Configuration_settings'/>.
-      '';
-    };
-
-  };
-
-  extraPath = [ mediawikiScripts ];
-
-  # !!! Need to specify that Apache has a dependency on PostgreSQL!
-
-  startupScript = pkgs.writeScript "mediawiki_startup.sh"
-    # Initialise the database automagically if we're using a Postgres
-    # server on localhost.
-    (optionalString (config.dbType == "postgres" && config.dbServer == "") ''
-      if ! ${pkgs.postgresql}/bin/psql -l | grep -q ' ${config.dbName} ' ; then
-          ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true
-          ${pkgs.postgresql}/bin/createdb "${config.dbName}" -O "${config.dbUser}"
-          ( echo 'CREATE LANGUAGE plpgsql;'
-            cat ${mediawikiRoot}/maintenance/postgres/tables.sql
-            echo 'CREATE TEXT SEARCH CONFIGURATION public.default ( COPY = pg_catalog.english );'
-            echo COMMIT
-          ) | ${pkgs.postgresql}/bin/psql -U "${config.dbUser}" "${config.dbName}"
-      fi
-      ${php}/bin/php ${mediawikiRoot}/maintenance/update.php
-    '');
-
-  robotsEntries = optionalString (config.articleUrlPrefix != "")
-    ''
-      User-agent: *
-      Disallow: ${config.urlPrefix}/
-      Disallow: ${config.articleUrlPrefix}/Special:Search
-      Disallow: ${config.articleUrlPrefix}/Special:Random
-    '';
-
-}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
index 4bbd041b6e04..536e707137c6 100644
--- a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
@@ -24,14 +24,6 @@ with lib;
     '';
   };
 
-  port = mkOption {
-    type = types.int;
-    default = 0;
-    description = ''
-      Port for the server. Option will be removed, use <option>listen</option> instead.
-  '';
-  };
-
   listen = mkOption {
      type = types.listOf (types.submodule (
           {
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix
deleted file mode 100644
index a883bb2b3433..000000000000
--- a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix
+++ /dev/null
@@ -1,103 +0,0 @@
-{ config, pkgs, serverInfo, lib, ... }:
-
-let
-  extraWorkersProperties = lib.optionalString (config ? extraWorkersProperties) config.extraWorkersProperties;
-  
-  workersProperties = pkgs.writeText "workers.properties" ''
-# Define list of workers that will be used
-# for mapping requests
-# The configuration directives are valid
-# for the mod_jk version 1.2.18 and later
-#
-worker.list=loadbalancer,status
-
-# Define Node1
-# modify the host as your host IP or DNS name.
-worker.node1.port=8009
-worker.node1.host=localhost
-worker.node1.type=ajp13
-worker.node1.lbfactor=1
-
-# Load-balancing behaviour
-worker.loadbalancer.type=lb
-worker.loadbalancer.balance_workers=node1
-
-# Status worker for managing load balancer
-worker.status.type=status
-
-${extraWorkersProperties}
-  '';
-in
-{
-
-  options = {
-    extraWorkersProperties = lib.mkOption {
-      default = "";
-      description = "Additional configuration for the workers.properties file.";
-    };
-  };
-
-  extraModules = [
-    { name = "jk"; path = "${pkgs.tomcat_connectors}/modules/mod_jk.so"; }
-  ];
-
-  extraConfig = ''
-# Where to find workers.properties
-JkWorkersFile ${workersProperties}
-
-# Where to put jk logs
-JkLogFile ${serverInfo.serverConfig.logDir}/mod_jk.log
-
-# Set the jk log level [debug/error/info]
-JkLogLevel info
-
-# Select the log format
-JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"
-
-# JkOptions indicates to send SSK KEY SIZE
-# Note: Changed from +ForwardURICompat.
-# See http://tomcat.apache.org/security-jk.html
-JkOptions +ForwardKeySize +ForwardURICompatUnparsed -ForwardDirectories
-
-# JkRequestLogFormat
-JkRequestLogFormat "%w %V %T"
-
-# Mount your applications
-JkMount /__application__/* loadbalancer
-
-# You can use external file for mount points.
-# It will be checked for updates each 60 seconds.
-# The format of the file is: /url=worker
-# /examples/*=loadbalancer
-#JkMountFile uriworkermap.properties
-
-# Add shared memory.
-# This directive is present with 1.2.10 and
-# later versions of mod_jk, and is needed for
-# for load balancing to work properly
-# Note: Replaced JkShmFile logs/jk.shm due to SELinux issues. Refer to
-# https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=225452
-JkShmFile ${serverInfo.serverConfig.stateDir}/jk.shm
-
-# Static files in all Tomcat webapp context directories are served by apache
-JkAutoAlias /var/tomcat/webapps
-
-# All requests go to worker by default
-JkMount /* loadbalancer
-# Serve some static files using httpd
-#JkUnMount /*.html loadbalancer
-#JkUnMount /*.jpg  loadbalancer
-#JkUnMount /*.gif  loadbalancer
-#JkUnMount /*.css  loadbalancer
-#JkUnMount /*.png  loadbalancer
-#JkUnMount /*.js  loadbalancer
-
-# Add jkstatus for managing runtime data
-<Location /jkstatus/>
-JkMount status
-Order deny,allow
-Deny from all
-Allow from 127.0.0.1
-</Location>
-  '';
-}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/trac.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/trac.nix
deleted file mode 100644
index 28b411a64b6f..000000000000
--- a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/trac.nix
+++ /dev/null
@@ -1,121 +0,0 @@
-{ config, lib, pkgs, serverInfo, ... }:
-
-with lib;
-
-let
-
-  # Build a Subversion instance with Apache modules and Swig/Python bindings.
-  subversion = pkgs.subversion.override {
-    bdbSupport = true;
-    httpServer = true;
-    pythonBindings = true;
-    apacheHttpd = httpd;
-  };
-
-  httpd = serverInfo.serverConfig.package;
-
-  versionPre24 = versionOlder httpd.version "2.4";
-
-in
-
-{
-
-  options = {
-
-    projectsLocation = mkOption {
-      description = "URL path in which Trac projects can be accessed";
-      default = "/projects";
-    };
-
-    projects = mkOption {
-      description = "List of projects that should be provided by Trac. If they are not defined yet empty projects are created.";
-      default = [];
-      example =
-        [ { identifier = "myproject";
-            name = "My Project";
-            databaseURL="postgres://root:password@/tracdb";
-            subversionRepository="/data/subversion/myproject";
-          }
-        ];
-    };
-
-    user = mkOption {
-      default = "wwwrun";
-      description = "User account under which Trac runs.";
-    };
-
-    group = mkOption {
-      default = "wwwrun";
-      description = "Group under which Trac runs.";
-    };
-
-    ldapAuthentication = {
-      enable = mkOption {
-        default = false;
-        description = "Enable the ldap authentication in trac";
-      };
-
-      url = mkOption {
-        default = "ldap://127.0.0.1/dc=example,dc=co,dc=ke?uid?sub?(objectClass=inetOrgPerson)";
-        description = "URL of the LDAP authentication";
-      };
-
-      name = mkOption {
-        default = "Trac server";
-        description = "AuthName";
-      };
-    };
-
-  };
-
-  extraModules = singleton
-    { name = "python"; path = "${pkgs.mod_python}/modules/mod_python.so"; };
-
-  extraConfig = ''
-    <Location ${config.projectsLocation}>
-      SetHandler mod_python
-      PythonHandler trac.web.modpython_frontend
-      PythonOption TracEnvParentDir /var/trac/projects
-      PythonOption TracUriRoot ${config.projectsLocation}
-      PythonOption PYTHON_EGG_CACHE /var/trac/egg-cache
-    </Location>
-    ${if config.ldapAuthentication.enable then ''
-      <LocationMatch "^${config.projectsLocation}[^/]+/login$">
-        AuthType Basic
-        AuthName "${config.ldapAuthentication.name}"
-        AuthBasicProvider "ldap"
-        AuthLDAPURL "${config.ldapAuthentication.url}"
-        ${if versionPre24 then "authzldapauthoritative Off" else ""}
-        require valid-user
-      </LocationMatch>
-    '' else ""}
-  '';
-
-  globalEnvVars = singleton
-    { name = "PYTHONPATH";
-      value =
-        makeSearchPathOutput "lib" "lib/${pkgs.python.libPrefix}/site-packages"
-          [ pkgs.mod_python
-            pkgs.pythonPackages.trac
-            pkgs.pythonPackages.setuptools
-            pkgs.pythonPackages.genshi
-            pkgs.pythonPackages.psycopg2
-            subversion
-          ];
-    };
-
-  startupScript = pkgs.writeScript "activateTrac" ''
-    mkdir -p /var/trac
-    chown ${config.user}:${config.group} /var/trac
-
-    ${concatMapStrings (project:
-      ''
-        if [ ! -d /var/trac/${project.identifier} ]
-        then
-            export PYTHONPATH=${pkgs.pythonPackages.psycopg2}/lib/${pkgs.python.libPrefix}/site-packages
-            ${pkgs.pythonPackages.trac}/bin/trac-admin /var/trac/${project.identifier} initenv "${project.name}" "${project.databaseURL}" svn "${project.subversionRepository}"
-        fi
-      '' ) (config.projects)}
-  '';
-
-}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/zabbix.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/zabbix.nix
deleted file mode 100644
index cab16593bcbc..000000000000
--- a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/zabbix.nix
+++ /dev/null
@@ -1,84 +0,0 @@
-{ config, lib, pkgs, serverInfo, ... }:
-
-with lib;
-
-let
-
-  # The Zabbix PHP frontend needs to be able to write its
-  # configuration settings (the connection info to the database) to
-  # the "conf" subdirectory.  So symlink $out/conf to some directory
-  # outside of the Nix store where we want to keep this stateful info.
-  # Note that different instances of the frontend will therefore end
-  # up with their own copies of the PHP sources.  !!! Alternatively,
-  # we could generate zabbix.conf.php declaratively.
-  zabbixPHP = pkgs.runCommand "${pkgs.zabbix.server.name}-php" {}
-    ''
-      cp -rs ${pkgs.zabbix.server}/share/zabbix/php "$out"
-      chmod -R u+w $out
-      ln -s "${if config.configFile == null
-               then "${config.stateDir}/zabbix.conf.php"
-               else config.configFile}" "$out/conf/zabbix.conf.php"
-    '';
-
-in
-
-{
-
-  enablePHP = true;
-
-  phpOptions =
-    ''
-      post_max_size = 32M
-      max_execution_time = 300
-      max_input_time = 300
-    '';
-
-  extraConfig = ''
-    Alias ${config.urlPrefix}/ ${zabbixPHP}/
-
-    <Directory ${zabbixPHP}>
-      DirectoryIndex index.php
-      Order deny,allow
-      Allow from *
-    </Directory>
-  '';
-
-  startupScript = pkgs.writeScript "zabbix-startup-hook" ''
-    mkdir -p ${config.stateDir}
-    chown -R ${serverInfo.serverConfig.user} ${config.stateDir}
-  '';
-
-  # The frontend needs "ps" to find out whether zabbix_server is running.
-  extraServerPath = [ pkgs.procps ];
-
-  options = {
-
-    urlPrefix = mkOption {
-      default = "/zabbix";
-      description = "
-        The URL prefix under which the Zabbix service appears.
-        Use the empty string to have it appear in the server root.
-      ";
-    };
-
-    configFile = mkOption {
-      default = null;
-      type = types.nullOr types.path;
-      description = ''
-        The configuration file (zabbix.conf.php) which contains the database
-        connection settings. If not set, the configuration settings will created
-        by the web installer.
-      '';
-    };
-
-    stateDir = mkOption {
-      default = "/var/lib/zabbix/frontend";
-      description = "
-        Directory where the dynamically generated configuration data
-        of the PHP frontend will be stored.
-      ";
-    };
-
-  };
-
-}
diff --git a/nixpkgs/nixos/modules/services/x11/clight.nix b/nixpkgs/nixos/modules/services/x11/clight.nix
new file mode 100644
index 000000000000..6ec395bb05ec
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/x11/clight.nix
@@ -0,0 +1,115 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.clight;
+
+  toConf = v:
+    if builtins.isFloat v then toString v
+    else if isInt v       then toString v
+    else if isBool v      then boolToString v
+    else if isString v    then ''"${escape [''"''] v}"''
+    else if isList v      then "[ " + concatMapStringsSep ", " toConf v + " ]"
+    else abort "clight.toConf: unexpected type (v = ${v})";
+
+  clightConf = pkgs.writeText "clight.conf"
+    (concatStringsSep "\n" (mapAttrsToList
+      (name: value: "${toString name} = ${toConf value};")
+      (filterAttrs
+        (_: value: value != null)
+        cfg.settings)));
+in {
+  options.services.clight = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable clight or not.
+      '';
+    };
+
+    temperature = {
+      day = mkOption {
+        type = types.int;
+        default = 5500;
+        description = ''
+          Colour temperature to use during the day, between
+          <literal>1000</literal> and <literal>25000</literal> K.
+        '';
+      };
+      night = mkOption {
+        type = types.int;
+        default = 3700;
+        description = ''
+          Colour temperature to use at night, between
+          <literal>1000</literal> and <literal>25000</literal> K.
+        '';
+      };
+    };
+
+    settings = let
+      validConfigTypes = with types; either int (either str (either bool float));
+    in mkOption {
+      type = with types; attrsOf (nullOr (either validConfigTypes (listOf validConfigTypes)));
+      default = {};
+      example = { captures = 20; gamma_long_transition = true; ac_capture_timeouts = [ 120 300 60 ]; };
+      description = ''
+        Additional configuration to extend clight.conf. See
+        <link xlink:href="https://github.com/FedeDP/Clight/blob/master/Extra/clight.conf"/> for a
+        sample configuration file.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    boot.kernelModules = [ "i2c_dev" ];
+    environment.systemPackages = with pkgs; [ clight clightd ];
+    services.dbus.packages = with pkgs; [ clight clightd ];
+    services.upower.enable = true;
+
+    services.clight.settings = {
+      gamma_temp = with cfg.temperature; mkDefault [ day night ];
+    } // (optionalAttrs (config.location.provider == "manual") {
+      latitude = mkDefault config.location.latitude;
+      longitude = mkDefault config.location.longitude;
+    });
+
+    services.geoclue2.appConfig."clightc" = {
+      isAllowed = true;
+      isSystem = true;
+    };
+
+    systemd.services.clightd = {
+      requires = [ "polkit.service" ];
+      wantedBy = [ "multi-user.target" ];
+
+      description = "Bus service to manage various screen related properties (gamma, dpms, backlight)";
+      serviceConfig = {
+        Type = "dbus";
+        BusName = "org.clightd.clightd";
+        Restart = "on-failure";
+        RestartSec = 5;
+        ExecStart = ''
+          ${pkgs.clightd}/bin/clightd
+        '';
+      };
+    };
+
+    systemd.user.services.clight = {
+      after = [ "upower.service" "clightd.service" ];
+      wants = [ "upower.service" "clightd.service" ];
+      partOf = [ "graphical-session.target" ];
+      wantedBy = [ "graphical-session.target" ];
+
+      description = "C daemon to adjust screen brightness to match ambient brightness, as computed capturing frames from webcam";
+      serviceConfig = {
+        Restart = "on-failure";
+        RestartSec = 5;
+        ExecStart = ''
+          ${pkgs.clight}/bin/clight --conf-file ${clightConf}
+        '';
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/x11/compton.nix b/nixpkgs/nixos/modules/services/x11/compton.nix
index d4357324c870..c02c9bfd94e8 100644
--- a/nixpkgs/nixos/modules/services/x11/compton.nix
+++ b/nixpkgs/nixos/modules/services/x11/compton.nix
@@ -7,57 +7,35 @@ let
 
   cfg = config.services.compton;
 
-  literalAttrs = v:
-    if isString v then toString v
-    else if isAttrs v then "{\n"
-      + concatStringsSep "\n" (mapAttrsToList
-        (name: value: "${literalAttrs name} = ${literalAttrs value};")
-        v)
-      + "\n}"
-    else generators.toPretty {} v;
+  pairOf = x: with types; addCheck (listOf x) (y: length y == 2);
 
   floatBetween = a: b: with lib; with types;
     addCheck str (x: versionAtLeast x a && versionOlder x b);
 
-  pairOf = x: with types; addCheck (listOf x) (y: length y == 2);
-
-  opacityRules = optionalString (length cfg.opacityRules != 0)
-    (concatMapStringsSep ",\n" (rule: ''"${rule}"'') cfg.opacityRules);
-
-  configFile = pkgs.writeText "compton.conf"
-    (optionalString cfg.fade ''
-      # fading
-      fading = true;
-      fade-delta    = ${toString cfg.fadeDelta};
-      fade-in-step  = ${elemAt cfg.fadeSteps 0};
-      fade-out-step = ${elemAt cfg.fadeSteps 1};
-      fade-exclude  = ${toJSON cfg.fadeExclude};
-    '' + optionalString cfg.shadow ''
-
-      # shadows
-      shadow = true;
-      shadow-offset-x = ${toString (elemAt cfg.shadowOffsets 0)};
-      shadow-offset-y = ${toString (elemAt cfg.shadowOffsets 1)};
-      shadow-opacity  = ${cfg.shadowOpacity};
-      shadow-exclude  = ${toJSON cfg.shadowExclude};
-    '' + ''
-
-      # opacity
-      active-opacity   = ${cfg.activeOpacity};
-      inactive-opacity = ${cfg.inactiveOpacity};
-
-      wintypes:
-      ${literalAttrs cfg.wintypes};
-
-      opacity-rule = [
-        ${opacityRules}
-      ];
-
-      # other options
-      backend = ${toJSON cfg.backend};
-      vsync = ${boolToString cfg.vSync};
-      refresh-rate = ${toString cfg.refreshRate};
-    '' + cfg.extraOptions);
+  toConf = attrs: concatStringsSep "\n"
+    (mapAttrsToList
+      (k: v: let
+        sep = if isAttrs v then ":" else "=";
+        # Basically a tinkered lib.generators.mkKeyValueDefault
+        mkValueString = v:
+          if isBool v        then boolToString v
+          else if isInt v    then toString v
+          else if isFloat v  then toString v
+          else if isString v then ''"${escape [ ''"'' ] v}"''
+          else if isList v   then "[ "
+            + concatMapStringsSep " , " mkValueString v
+            + " ]"
+          else if isAttrs v  then "{ "
+            + concatStringsSep " "
+              (mapAttrsToList
+                (key: value: "${toString key}=${mkValueString value};")
+                v)
+            + " }"
+          else abort "compton.mkValueString: unexpected type (v = ${v})";
+      in "${escape [ sep ] k}${sep}${mkValueString v};")
+      attrs);
+
+  configFile = pkgs.writeText "compton.conf" (toConf cfg.settings);
 
 in {
 
@@ -236,23 +214,13 @@ in {
       '';
     };
 
-    package = mkOption {
-      type = types.package;
-      default = pkgs.compton;
-      defaultText = "pkgs.compton";
-      example = literalExample "pkgs.compton";
-      description = ''
-        Compton derivation to use.
-      '';
-    };
-
-    extraOptions = mkOption {
-      type = types.lines;
-      default = "";
-      example = ''
-        unredir-if-possible = true;
-        dbe = true;
-      '';
+    settings = let
+      configTypes = with types; either bool (either int (either float str));
+      # types.loaOf converts lists to sets
+      loaOf = t: with types; either (listOf t) (attrsOf t);
+    in mkOption {
+      type = loaOf (types.either configTypes (loaOf (types.either configTypes (loaOf configTypes))));
+      default = {};
       description = ''
         Additional Compton configuration.
       '';
@@ -260,6 +228,42 @@ in {
   };
 
   config = mkIf cfg.enable {
+    services.compton.settings = let
+      # Hard conversion to float, literally lib.toInt but toFloat
+      toFloat = str: let
+        may_be_float = builtins.fromJSON str;
+      in if builtins.isFloat may_be_float
+        then may_be_float
+        else throw "Could not convert ${str} to float.";
+    in {
+      # fading
+      fading           = mkDefault cfg.fade;
+      fade-delta       = mkDefault cfg.fadeDelta;
+      fade-in-step     = mkDefault (toFloat (elemAt cfg.fadeSteps 0));
+      fade-out-step    = mkDefault (toFloat (elemAt cfg.fadeSteps 1));
+      fade-exclude     = mkDefault cfg.fadeExclude;
+
+      # shadows
+      shadow           = mkDefault cfg.shadow;
+      shadow-offset-x  = mkDefault (elemAt cfg.shadowOffsets 0);
+      shadow-offset-y  = mkDefault (elemAt cfg.shadowOffsets 1);
+      shadow-opacity   = mkDefault (toFloat cfg.shadowOpacity);
+      shadow-exclude   = mkDefault cfg.shadowExclude;
+
+      # opacity
+      active-opacity   = mkDefault (toFloat cfg.activeOpacity);
+      inactive-opacity = mkDefault (toFloat cfg.inactiveOpacity);
+
+      wintypes         = mkDefault cfg.wintypes;
+
+      opacity-rule     = mkDefault cfg.opacityRules;
+
+      # other options
+      backend          = mkDefault cfg.backend;
+      vsync            = mkDefault cfg.vSync;
+      refresh-rate     = mkDefault cfg.refreshRate;
+    };
+
     systemd.user.services.compton = {
       description = "Compton composite manager";
       wantedBy = [ "graphical-session.target" ];
@@ -271,13 +275,13 @@ in {
       };
 
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/compton --config ${configFile}";
+        ExecStart = "${pkgs.compton}/bin/compton --config ${configFile}";
         RestartSec = 3;
         Restart = "always";
       };
     };
 
-    environment.systemPackages = [ cfg.package ];
+    environment.systemPackages = [ pkgs.compton ];
   };
 
 }
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix
index 2b1e9169e5f6..671a959cdde1 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix
@@ -20,7 +20,7 @@ in
   imports = [
     ./none.nix ./xterm.nix ./xfce.nix ./plasma5.nix ./lumina.nix
     ./lxqt.nix ./enlightenment.nix ./gnome3.nix ./kodi.nix ./maxx.nix
-    ./mate.nix ./pantheon.nix
+    ./mate.nix ./pantheon.nix ./surf-display.nix
   ];
 
   options = {
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome3.nix
index ef6820d33260..5e1e652a5089 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -123,12 +123,8 @@ in {
     services.dleyna-renderer.enable = mkDefault true;
     services.dleyna-server.enable = mkDefault true;
     services.gnome3.at-spi2-core.enable = true;
-    services.gnome3.evince.enable = mkDefault true;
     services.gnome3.evolution-data-server.enable = true;
-    services.gnome3.file-roller.enable = mkDefault true;
     services.gnome3.glib-networking.enable = true;
-    services.gnome3.gnome-disks.enable = mkDefault true;
-    services.gnome3.gnome-documents.enable = mkDefault true;
     services.gnome3.gnome-keyring.enable = true;
     services.gnome3.gnome-online-accounts.enable = mkDefault true;
     services.gnome3.gnome-remote-desktop.enable = mkDefault true;
@@ -154,7 +150,14 @@ in {
     services.hardware.bolt.enable = mkDefault true;
     services.xserver.libinput.enable = mkDefault true; # for controlling touchpad settings via gnome control center
     systemd.packages = [ pkgs.gnome3.vino ];
-    services.flatpak.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
+    xdg.portal.enable = true;
+    xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
+
+    # Enable default programs
+    programs.evince.enable = mkDefault true;
+    programs.file-roller.enable = mkDefault true;
+    programs.gnome-disks.enable = mkDefault true;
+    programs.gnome-documents.enable = mkDefault true;
 
     # If gnome3 is installed, build vim for gtk3 too.
     nixpkgs.config.vim.gui = "gtk3";
@@ -229,7 +232,7 @@ in {
 
     # Use the correct gnome3 packageSet
     networking.networkmanager.basePackages =
-      { inherit (pkgs) networkmanager modemmanager wpa_supplicant;
+      { inherit (pkgs) networkmanager modemmanager wpa_supplicant crda;
         inherit (pkgs.gnome3) networkmanager-openvpn networkmanager-vpnc
                               networkmanager-openconnect networkmanager-fortisslvpn
                               networkmanager-iodine networkmanager-l2tp; };
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix
index 41903b33fae9..8e1272f3c92b 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix
@@ -102,6 +102,10 @@ in
           # Makes qt applications look less alien
           export QT_QPA_PLATFORMTHEME=gtk3
           export QT_STYLE_OVERRIDE=adwaita
+
+          # Settings from elementary-default-settings
+          export GTK_CSD=1
+          export GTK_MODULES=$GTK_MODULES:pantheon-filechooser-module
       fi
     '';
 
@@ -147,7 +151,7 @@ in
 
     networking.networkmanager.enable = mkDefault true;
     networking.networkmanager.basePackages =
-      { inherit (pkgs) networkmanager modemmanager wpa_supplicant;
+      { inherit (pkgs) networkmanager modemmanager wpa_supplicant crda;
         inherit (pkgs.gnome3) networkmanager-openvpn networkmanager-vpnc
                               networkmanager-openconnect networkmanager-fortisslvpn
                               networkmanager-iodine networkmanager-l2tp; };
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
index dc8bfc7dc172..94a307ae1007 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -21,6 +21,13 @@ in
         description = "Enable the Plasma 5 (KDE 5) desktop environment.";
       };
 
+      phononBackend = mkOption {
+        type = types.enum [ "gstreamer" "vlc" ];
+        default = "gstreamer";
+        example = "vlc";
+        description = "Phonon audio backend to install.";
+      };
+
       enableQt4Support = mkOption {
         type = types.bool;
         default = true;
@@ -64,8 +71,8 @@ in
       };
 
       security.wrappers = {
-        kcheckpass.source = "${lib.getBin plasma5.kscreenlocker}/lib/libexec/kcheckpass";
-        "start_kdeinit".source = "${lib.getBin pkgs.kinit}/lib/libexec/kf5/start_kdeinit";
+        kcheckpass.source = "${lib.getBin plasma5.kscreenlocker}/libexec/kcheckpass";
+        "start_kdeinit".source = "${lib.getBin pkgs.kinit}/libexec/kf5/start_kdeinit";
         kwin_wayland = {
           source = "${lib.getBin plasma5.kwin}/bin/kwin_wayland";
           capabilities = "cap_sys_nice+ep";
@@ -161,15 +168,17 @@ in
 
           qtvirtualkeyboard
 
-          libsForQt5.phonon-backend-gstreamer
-
           xdg-user-dirs # Update user dirs as described in https://freedesktop.org/wiki/Software/xdg-user-dirs/
         ]
 
-        ++ lib.optionals cfg.enableQt4Support [ pkgs.phonon-backend-gstreamer ]
+        # Phonon audio backend
+        ++ lib.optional (cfg.phononBackend == "gstreamer") libsForQt5.phonon-backend-gstreamer
+        ++ lib.optional (cfg.phononBackend == "gstreamer" && cfg.enableQt4Support) pkgs.phonon-backend-gstreamer
+        ++ lib.optional (cfg.phononBackend == "vlc") libsForQt5.phonon-backend-vlc
+        ++ lib.optional (cfg.phononBackend == "vlc" && cfg.enableQt4Support) pkgs.phonon-backend-vlc
 
         # Optional hardware support features
-        ++ lib.optional config.hardware.bluetooth.enable bluedevil
+        ++ lib.optionals config.hardware.bluetooth.enable [ bluedevil bluez-qt ]
         ++ lib.optional config.networking.networkmanager.enable plasma-nm
         ++ lib.optional config.hardware.pulseaudio.enable plasma-pa
         ++ lib.optional config.powerManagement.enable powerdevil
@@ -224,6 +233,9 @@ in
       security.pam.services.sddm.enableKwallet = true;
       security.pam.services.slim.enableKwallet = true;
 
+      xdg.portal.enable = true;
+      xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-kde ];
+
       # Update the start menu for each user that is currently logged in
       system.userActivationScripts.plasmaSetup = ''
         # The KDE icon cache is supposed to update itself
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/surf-display.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/surf-display.nix
new file mode 100644
index 000000000000..232bbf5c55d4
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/surf-display.nix
@@ -0,0 +1,127 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.xserver.desktopManager.surf-display;
+
+  surfDisplayConf = ''
+    # Surf Kiosk Display: Wrap around surf browser and turn your
+    # system into a browser screen in KIOSK-mode.
+
+    # default download URI for all display screens if not configured individually
+    DEFAULT_WWW_URI="${cfg.defaultWwwUri}"
+
+    # Enforce fixed resolution for all displays (default: not set):
+    #DEFAULT_RESOLUTION="1920x1080"
+
+    # HTTP proxy URL, if needed (default: not set).
+    #HTTP_PROXY_URL="http://webcache:3128"
+
+    # Setting for internal inactivity timer to restart surf-display
+    # if the user goes inactive/idle.
+    INACTIVITY_INTERVAL="${builtins.toString cfg.inactivityInterval}"
+
+    # log to syslog instead of .xsession-errors
+    LOG_TO_SYSLOG="yes"
+
+    # Launch pulseaudio daemon if not already running.
+    WITH_PULSEAUDIO="yes"
+
+    # screensaver settings, see "man 1 xset" for possible options
+    SCREENSAVER_SETTINGS="${cfg.screensaverSettings}"
+
+    # disable right and middle pointer device click in browser sessions while keeping
+    # scrolling wheels' functionality intact... (consider "pointer" subcommand on
+    # xmodmap man page for details).
+    POINTER_BUTTON_MAP="${cfg.pointerButtonMap}"
+
+    # Hide idle mouse pointer.
+    HIDE_IDLE_POINTER="${cfg.hideIdlePointer}"
+
+    ${cfg.extraConfig}
+  '';
+
+in {
+  options = {
+    services.xserver.desktopManager.surf-display = {
+      enable = mkEnableOption "surf-display as a kiosk browser session";
+
+      defaultWwwUri = mkOption {
+        type = types.string;
+        default = "${pkgs.surf-display}/share/surf-display/empty-page.html";
+        example = "https://www.example.com/";
+        description = "Default URI to display.";
+      };
+
+      inactivityInterval = mkOption {
+        type = types.int;
+        default = 300;
+        example = "0";
+        description = ''
+          Setting for internal inactivity timer to restart surf-display if the
+          user goes inactive/idle to get a fresh session for the next user of
+          the kiosk.
+
+          If this value is set to zero, the whole feature of restarting due to
+          inactivity is disabled.
+        '';
+      };
+
+      screensaverSettings = mkOption {
+        type = types.string;
+        default = "";
+        description = ''
+          Screensaver settings, see <literal>man 1 xset</literal> for possible options.
+        '';
+      };
+
+      pointerButtonMap = mkOption {
+        type = types.string;
+        default = "1 0 0 4 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+        description = ''
+          Disable right and middle pointer device click in browser sessions
+          while keeping scrolling wheels' functionality intact. See pointer
+          subcommand on <literal>man xmodmap</literal> for details.
+        '';
+      };
+
+      hideIdlePointer = mkOption {
+        type = types.string;
+        default = "yes";
+        example = "no";
+        description = "Hide idle mouse pointer.";
+      };
+
+      extraConfig = mkOption {
+        type = types.string;
+        default = "";
+        example = ''
+          # Enforce fixed resolution for all displays (default: not set):
+          DEFAULT_RESOLUTION="1920x1080"
+
+          # HTTP proxy URL, if needed (default: not set).
+          HTTP_PROXY_URL="http://webcache:3128"
+
+          # Configure individual display screens with host specific parameters:
+          DISPLAYS['display-host-0']="www_uri=https://www.displayserver.comany.net/display-1/index.html"
+          DISPLAYS['display-host-1']="www_uri=https://www.displayserver.comany.net/display-2/index.html"
+          DISPLAYS['display-host-2']="www_uri=https://www.displayserver.comany.net/display-3/index.html|res=1920x1280"
+          DISPLAYS['display-host-3']="www_uri=https://www.displayserver.comany.net/display-4/index.html"|res=1280x1024"
+          DISPLAYS['display-host-local-file']="www_uri=file:///usr/share/doc/surf-display/empty-page.html"
+        '';
+        description = ''
+          Extra configuration options to append to <literal>/etc/default/surf-display</literal>.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.xserver.displayManager.extraSessionFilePackages = [
+      pkgs.surf-display
+    ];
+
+    environment.etc."default/surf-display".text = surfDisplayConf;
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
index ff4b91923e02..3f1669d08516 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
@@ -96,6 +96,14 @@ in
         type = types.bool;
       };
 
+      autoSuspend = mkOption {
+        default = true;
+        description = ''
+          Suspend the machine after inactivity.
+        '';
+        type = types.bool;
+      };
+
     };
 
   };
@@ -176,10 +184,40 @@ in
 
     systemd.user.services.dbus.wantedBy = [ "default.target" ];
 
-    programs.dconf.profiles.gdm = pkgs.writeText "dconf-gdm-profile" ''
-      system-db:local
-      ${gdm}/share/dconf/profile/gdm
-    '';
+    programs.dconf.profiles.gdm =
+    let
+      customDconf = pkgs.writeTextFile {
+        name = "gdm-dconf";
+        destination = "/dconf/gdm-custom";
+        text = ''
+          ${optionalString (!cfg.gdm.autoSuspend) ''
+            [org/gnome/settings-daemon/plugins/power]
+            sleep-inactive-ac-type='nothing'
+            sleep-inactive-battery-type='nothing'
+            sleep-inactive-ac-timeout=0
+            sleep-inactive-battery-timeout=0
+          ''}
+        '';
+      };
+
+      customDconfDb = pkgs.stdenv.mkDerivation {
+        name = "gdm-dconf-db";
+        buildCommand = ''
+          ${pkgs.gnome3.dconf}/bin/dconf compile $out ${customDconf}/dconf
+        '';
+      };
+    in pkgs.stdenv.mkDerivation {
+      name = "dconf-gdm-profile";
+      buildCommand = ''
+        # Check that the GDM profile starts with what we expect.
+        if [ $(head -n 1 ${gdm}/share/dconf/profile/gdm) != "user-db:user" ]; then
+          echo "GDM dconf profile changed, please update gdm.nix"
+          exit 1
+        fi
+        # Insert our custom DB behind it.
+        sed '2ifile-db:${customDconfDb}' ${gdm}/share/dconf/profile/gdm > $out
+      '';
+    };
 
     # Use AutomaticLogin if delay is zero, because it's immediate.
     # Otherwise with TimedLogin with zero seconds the prompt is still
diff --git a/nixpkgs/nixos/modules/services/x11/extra-layouts.nix b/nixpkgs/nixos/modules/services/x11/extra-layouts.nix
new file mode 100644
index 000000000000..5523dd2bf023
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/x11/extra-layouts.nix
@@ -0,0 +1,165 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  layouts = config.services.xserver.extraLayouts;
+
+  layoutOpts = {
+    options = {
+      description = mkOption {
+        type = types.str;
+        description = "A short description of the layout.";
+      };
+
+      languages = mkOption {
+        type = types.listOf types.str;
+        description =
+        ''
+          A list of languages provided by the layout.
+          (Use ISO 639-2 codes, for example: "eng" for english)
+        '';
+      };
+
+      compatFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          The path to the xkb compat file.
+          This file sets the compatibility state, used to preserve
+          compatibility with xkb-unaware programs.
+          It must contain a <literal>xkb_compat "name" { ... }</literal> block.
+        '';
+      };
+
+      geometryFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          The path to the xkb geometry file.
+          This (completely optional) file describes the physical layout of
+          keyboard, which maybe be used by programs to depict it.
+          It must contain a <literal>xkb_geometry "name" { ... }</literal> block.
+        '';
+      };
+
+      keycodesFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          The path to the xkb keycodes file.
+          This file specifies the range and the interpretation of the raw
+          keycodes sent by the keyboard.
+          It must contain a <literal>xkb_keycodes "name" { ... }</literal> block.
+        '';
+      };
+
+      symbolsFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          The path to the xkb symbols file.
+          This is the most important file: it defines which symbol or action
+          maps to each key and must contain a
+          <literal>xkb_symbols "name" { ... }</literal> block.
+        '';
+      };
+
+      typesFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          The path to the xkb types file.
+          This file specifies the key types that can be associated with
+          the various keyboard keys.
+          It must contain a <literal>xkb_types "name" { ... }</literal> block.
+        '';
+      };
+
+    };
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options.services.xserver = {
+    extraLayouts = mkOption {
+      type = types.attrsOf (types.submodule layoutOpts);
+      default = {};
+      example = literalExample
+      ''
+        {
+          mine = {
+            description = "My custom xkb layout.";
+            languages = [ "eng" ];
+            symbolsFile = /path/to/my/layout;
+          };
+        }
+      '';
+      description = ''
+        Extra custom layouts that will be included in the xkb configuration.
+        Information on how to create a new layout can be found here:
+        <link xlink:href="https://www.x.org/releases/current/doc/xorg-docs/input/XKB-Enhancing.html#Defining_New_Layouts"></link>.
+        For more examples see
+        <link xlink:href="https://wiki.archlinux.org/index.php/X_KeyBoard_extension#Basic_examples"></link>
+      '';
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf (layouts != { }) {
+
+    # We don't override xkeyboard_config directly to
+    # reduce the amount of packages to be recompiled.
+    # Only the following packages are necessary to set
+    # a custom layout anyway:
+    nixpkgs.overlays = lib.singleton (self: super: {
+
+      xkb_patched = self.xorg.xkeyboardconfig_custom {
+        layouts = config.services.xserver.extraLayouts;
+      };
+
+      xorg = super.xorg // {
+        xorgserver = super.xorg.xorgserver.overrideAttrs (old: {
+          configureFlags = old.configureFlags ++ [
+            "--with-xkb-bin-directory=${self.xorg.xkbcomp}/bin"
+            "--with-xkb-path=${self.xkb_patched}/share/X11/xkb"
+          ];
+        });
+
+        setxkbmap = super.xorg.setxkbmap.overrideAttrs (old: {
+          postInstall =
+            ''
+              mkdir -p $out/share
+              ln -sfn ${self.xkb_patched}/etc/X11 $out/share/X11
+            '';
+        });
+
+        xkbcomp = super.xorg.xkbcomp.overrideAttrs (old: {
+          configureFlags = "--with-xkb-config-root=${self.xkb_patched}/share/X11/xkb";
+        });
+
+      };
+
+      ckbcomp = super.ckbcomp.override {
+        xkeyboard_config = self.xkb_patched;
+      };
+
+      xkbvalidate = super.xkbvalidate.override {
+        libxkbcommon = self.libxkbcommon.override {
+          xkeyboard_config = self.xkb_patched;
+        };
+      };
+
+    });
+
+    services.xserver.xkbDir = "${pkgs.xkb_patched}/etc/X11/xkb";
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/x11/gdk-pixbuf.nix b/nixpkgs/nixos/modules/services/x11/gdk-pixbuf.nix
index 2dc8eabd95a7..9ad926369ec7 100644
--- a/nixpkgs/nixos/modules/services/x11/gdk-pixbuf.nix
+++ b/nixpkgs/nixos/modules/services/x11/gdk-pixbuf.nix
@@ -5,21 +5,21 @@ with lib;
 let
   cfg = config.services.xserver.gdk-pixbuf;
 
-  # Get packages to generate the cache for. We always include gdk_pixbuf.
-  effectivePackages = unique ([pkgs.gdk_pixbuf] ++ cfg.modulePackages);
+  # Get packages to generate the cache for. We always include gdk-pixbuf.
+  effectivePackages = unique ([pkgs.gdk-pixbuf] ++ cfg.modulePackages);
 
   # Generate the cache file by running gdk-pixbuf-query-loaders for each
   # package and concatenating the results.
   loadersCache = pkgs.runCommand "gdk-pixbuf-loaders.cache" { preferLocalBuild = true; } ''
     (
       for package in ${concatStringsSep " " effectivePackages}; do
-        module_dir="$package/${pkgs.gdk_pixbuf.moduleDir}"
+        module_dir="$package/${pkgs.gdk-pixbuf.moduleDir}"
         if [[ ! -d $module_dir ]]; then
           echo "Warning (services.xserver.gdk-pixbuf): missing module directory $module_dir" 1>&2
           continue
         fi
         GDK_PIXBUF_MODULEDIR="$module_dir" \
-          ${pkgs.gdk_pixbuf.dev}/bin/gdk-pixbuf-query-loaders
+          ${pkgs.gdk-pixbuf.dev}/bin/gdk-pixbuf-query-loaders
       done
     ) > "$out"
   '';
diff --git a/nixpkgs/nixos/modules/services/x11/redshift.nix b/nixpkgs/nixos/modules/services/x11/redshift.nix
index 4345a3348081..55f8f75021bc 100644
--- a/nixpkgs/nixos/modules/services/x11/redshift.nix
+++ b/nixpkgs/nixos/modules/services/x11/redshift.nix
@@ -5,6 +5,7 @@ with lib;
 let
 
   cfg = config.services.redshift;
+  lcfg = config.location;
 
 in {
 
@@ -18,35 +19,6 @@ in {
       '';
     };
 
-    latitude = mkOption {
-      type = types.nullOr types.str;
-      default = null;
-      description = ''
-        Your current latitude, between
-        <literal>-90.0</literal> and <literal>90.0</literal>. Must be provided
-        along with longitude.
-      '';
-    };
-
-    longitude = mkOption {
-      type = types.nullOr types.str;
-      default = null;
-      description = ''
-        Your current longitude, between
-        between <literal>-180.0</literal> and <literal>180.0</literal>. Must be
-        provided along with latitude.
-      '';
-    };
-
-    provider = mkOption {
-      type = types.enum [ "manual" "geoclue2" ];
-      default = "manual";
-      description = ''
-        The location provider to use for determining your location. If set to
-        <literal>manual</literal> you must also provide latitude/longitude.
-      '';
-    };
-
     temperature = {
       day = mkOption {
         type = types.int;
@@ -106,33 +78,19 @@ in {
   };
 
   config = mkIf cfg.enable {
-    assertions = [ 
-      {
-        assertion = 
-          if cfg.provider == "manual"
-          then (cfg.latitude != null && cfg.longitude != null) 
-          else (cfg.latitude == null && cfg.longitude == null);
-        message = "Latitude and longitude must be provided together, and with provider set to null.";
-      }
-    ];
-
     # needed so that .desktop files are installed, which geoclue cares about
     environment.systemPackages = [ cfg.package ];
 
-    services.geoclue2 = mkIf (cfg.provider == "geoclue2") {
-      enable = true;
-      appConfig."redshift" = {
-        isAllowed = true;
-        isSystem = true;
-      };
+    services.geoclue2.appConfig."redshift" = {
+      isAllowed = true;
+      isSystem = true;
     };
 
-    systemd.user.services.redshift = 
+    systemd.user.services.redshift =
     let
-      providerString = 
-        if cfg.provider == "manual"
-        then "${cfg.latitude}:${cfg.longitude}"
-        else cfg.provider;
+      providerString = if lcfg.provider == "manual"
+        then "${toString lcfg.latitude}:${toString lcfg.longitude}"
+        else lcfg.provider;
     in
     {
       description = "Redshift colour temperature adjuster";
diff --git a/nixpkgs/nixos/modules/services/x11/xserver.nix b/nixpkgs/nixos/modules/services/x11/xserver.nix
index a1ed2fd1e97b..b1a316706976 100644
--- a/nixpkgs/nixos/modules/services/x11/xserver.nix
+++ b/nixpkgs/nixos/modules/services/x11/xserver.nix
@@ -14,6 +14,9 @@ let
     # Alias so people can keep using "virtualbox" instead of "vboxvideo".
     virtualbox = { modules = [ xorg.xf86videovboxvideo ]; driverName = "vboxvideo"; };
 
+    # Alias so that "radeon" uses the xf86-video-ati driver.
+    radeon = { modules = [ xorg.xf86videoati ]; driverName = "ati"; };
+
     # modesetting does not have a xf86videomodesetting package as it is included in xorgserver
     modesetting = {};
   };
@@ -241,7 +244,7 @@ in
       videoDrivers = mkOption {
         type = types.listOf types.str;
         # !!! We'd like "nv" here, but it segfaults the X server.
-        default = [ "ati" "cirrus" "vesa" "vmware" "modesetting" ];
+        default = [ "radeon" "cirrus" "vesa" "vmware" "modesetting" ];
         example = [
           "ati_unfree" "amdgpu" "amdgpu-pro"
           "nv" "nvidia" "nvidiaLegacy390" "nvidiaLegacy340" "nvidiaLegacy304"
@@ -662,10 +665,9 @@ in
         restartIfChanged = false;
 
         environment =
-          {
-            LD_LIBRARY_PATH = concatStringsSep ":" ([ "/run/opengl-driver/lib" ]
-              ++ concatLists (catAttrs "libPath" cfg.drivers));
-          } // cfg.displayManager.job.environment;
+          optionalAttrs config.hardware.opengl.setLdLibraryPath
+            { LD_LIBRARY_PATH = pkgs.addOpenGLRunpath.driverLink; }
+          // cfg.displayManager.job.environment;
 
         preStart =
           ''
diff --git a/nixpkgs/nixos/modules/system/boot/binfmt.nix b/nixpkgs/nixos/modules/system/boot/binfmt.nix
index d6c0f0504868..a550ffd6320f 100644
--- a/nixpkgs/nixos/modules/system/boot/binfmt.nix
+++ b/nixpkgs/nixos/modules/system/boot/binfmt.nix
@@ -115,6 +115,14 @@ let
       magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'';
       mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
     };
+    wasm32-wasi = {
+      magicOrExtension = ''\x00asm'';
+      mask = ''\xff\xff\xff\xff'';
+    };
+    wasm64-wasi = {
+      magicOrExtension = ''\x00asm'';
+      mask = ''\xff\xff\xff\xff'';
+    };
     x86_64-windows = {
       magicOrExtension = ".exe";
       recognitionType = "extension";
@@ -226,6 +234,7 @@ in {
 
       emulatedSystems = mkOption {
         default = [];
+        example = [ "wasm32-wasi" "x86_64-windows" "aarch64-linux" ];
         description = ''
           List of systems to emulate. Will also configure Nix to
           support your new systems.
diff --git a/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl
index a36b3c180eb4..a09c5dc47618 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -407,6 +407,29 @@ addEntry("NixOS - Default", $defaultConfig);
 
 $conf .= "$extraEntries\n" unless $extraEntriesBeforeNixOS;
 
+# Find all the children of the current default configuration
+# Do not search for grand children
+my @links = sort (glob "$defaultConfig/fine-tune/*");
+foreach my $link (@links) {
+
+    my $entryName = "";
+
+    my $cfgName = readFile("$link/configuration-name");
+
+    my $date = strftime("%F", localtime(lstat($link)->mtime));
+    my $version =
+        -e "$link/nixos-version"
+        ? readFile("$link/nixos-version")
+        : basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]);
+
+    if ($cfgName) {
+        $entryName = $cfgName;
+    } else {
+        $entryName = "($date - $version)";
+    }
+    addEntry("NixOS - $entryName", $link);
+}
+
 my $grubBootPath = $grubBoot->path;
 # extraEntries could refer to @bootRoot@, which we have to substitute
 $conf =~ s/\@bootRoot\@/$grubBootPath/g;
diff --git a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
index 910a602c61de..22d459ceb04f 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
+++ b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
@@ -22,7 +22,9 @@ let
 
     editor = if cfg.editor then "True" else "False";
 
-    inherit (cfg) consoleMode configurationLimit;
+    configurationLimit = if cfg.configurationLimit == null then 0 else cfg.configurationLimit;
+
+    inherit (cfg) consoleMode;
 
     inherit (efi) efiSysMountPoint canTouchEfiVariables;
 
@@ -58,12 +60,15 @@ in {
     };
 
     configurationLimit = mkOption {
-      default = 100;
+      default = null;
       example = 120;
-      type = types.int;
+      type = types.nullOr types.int;
       description = ''
-        Maximum of configurations in boot menu. Otherwise boot partition could
-        run out of disk space.
+        Maximum number of latest generations in the boot menu. 
+        Useful to prevent boot partition running out of disk space.
+
+        <literal>null</literal> means no limit i.e. all generations 
+        that were not garbage collected yet.
       '';
     };
 
diff --git a/nixpkgs/nixos/modules/system/boot/resolved.nix b/nixpkgs/nixos/modules/system/boot/resolved.nix
index 5c66cf4a6e6e..3ea96f8e4645 100644
--- a/nixpkgs/nixos/modules/system/boot/resolved.nix
+++ b/nixpkgs/nixos/modules/system/boot/resolved.nix
@@ -3,6 +3,10 @@
 with lib;
 let
   cfg = config.services.resolved;
+
+  dnsmasqResolve = config.services.dnsmasq.enable &&
+                   config.services.dnsmasq.resolveLocalQueries;
+
 in
 {
 
@@ -126,6 +130,12 @@ in
 
   config = mkIf cfg.enable {
 
+    assertions = [
+      { assertion = !config.networking.useHostResolvConf;
+        message = "Using host resolv.conf is not supported with systemd-resolved";
+      }
+    ];
+
     systemd.additionalUpstreamSystemUnits = [
       "systemd-resolved.service"
     ];
@@ -135,21 +145,30 @@ in
       restartTriggers = [ config.environment.etc."systemd/resolved.conf".source ];
     };
 
-    environment.etc."systemd/resolved.conf".text = ''
-      [Resolve]
-      ${optionalString (config.networking.nameservers != [])
-        "DNS=${concatStringsSep " " config.networking.nameservers}"}
-      ${optionalString (cfg.fallbackDns != [])
-        "FallbackDNS=${concatStringsSep " " cfg.fallbackDns}"}
-      ${optionalString (cfg.domains != [])
-        "Domains=${concatStringsSep " " cfg.domains}"}
-      LLMNR=${cfg.llmnr}
-      DNSSEC=${cfg.dnssec}
-      ${config.services.resolved.extraConfig}
-    '';
+    environment.etc = {
+      "systemd/resolved.conf".text = ''
+        [Resolve]
+        ${optionalString (config.networking.nameservers != [])
+          "DNS=${concatStringsSep " " config.networking.nameservers}"}
+        ${optionalString (cfg.fallbackDns != [])
+          "FallbackDNS=${concatStringsSep " " cfg.fallbackDns}"}
+        ${optionalString (cfg.domains != [])
+          "Domains=${concatStringsSep " " cfg.domains}"}
+        LLMNR=${cfg.llmnr}
+        DNSSEC=${cfg.dnssec}
+        ${config.services.resolved.extraConfig}
+      '';
+
+      # symlink the dynamic stub resolver of resolv.conf as recommended by upstream:
+      # https://www.freedesktop.org/software/systemd/man/systemd-resolved.html#/etc/resolv.conf
+      "resolv.conf".source = "/run/systemd/resolve/stub-resolv.conf";
+    } // optionalAttrs dnsmasqResolve {
+      "dnsmasq-resolv.conf".source = "/run/systemd/resolve/resolv.conf";
+    };
 
     # If networkmanager is enabled, ask it to interface with resolved.
     networking.networkmanager.dns = "systemd-resolved";
+
   };
 
 }
diff --git a/nixpkgs/nixos/modules/system/boot/stage-2.nix b/nixpkgs/nixos/modules/system/boot/stage-2.nix
index 55e6b19c67fd..6b0b47227301 100644
--- a/nixpkgs/nixos/modules/system/boot/stage-2.nix
+++ b/nixpkgs/nixos/modules/system/boot/stage-2.nix
@@ -4,19 +4,20 @@ with lib;
 
 let
 
+  useHostResolvConf = config.networking.resolvconf.enable && config.networking.useHostResolvConf;
+
   bootStage2 = pkgs.substituteAll {
     src = ./stage-2-init.sh;
     shellDebug = "${pkgs.bashInteractive}/bin/bash";
     shell = "${pkgs.bash}/bin/bash";
     isExecutable = true;
     inherit (config.nix) readOnlyStore;
-    inherit (config.networking) useHostResolvConf;
+    inherit useHostResolvConf;
     inherit (config.system.build) earlyMountScript;
-    path = lib.makeBinPath [
+    path = lib.makeBinPath ([
       pkgs.coreutils
       pkgs.utillinux
-      pkgs.openresolv
-    ];
+    ] ++ lib.optional useHostResolvConf pkgs.openresolv);
     fsPackagesPath = lib.makeBinPath config.system.fsPackages;
     postBootCommands = pkgs.writeText "local-cmds"
       ''
diff --git a/nixpkgs/nixos/modules/system/boot/systemd-unit-options.nix b/nixpkgs/nixos/modules/system/boot/systemd-unit-options.nix
index 63f974b704f3..ee4ae845a7d5 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd-unit-options.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd-unit-options.nix
@@ -6,7 +6,7 @@ with import ./systemd-lib.nix { inherit config lib pkgs; };
 let
   checkService = checkUnitConfig "Service" [
     (assertValueOneOf "Type" [
-      "simple" "forking" "oneshot" "dbus" "notify" "idle"
+      "exec" "simple" "forking" "oneshot" "dbus" "notify" "idle"
     ])
     (assertValueOneOf "Restart" [
       "no" "on-success" "on-failure" "on-abnormal" "on-abort" "always"
diff --git a/nixpkgs/nixos/modules/tasks/auto-upgrade.nix b/nixpkgs/nixos/modules/tasks/auto-upgrade.nix
index 91f4ae79ee91..18753ae0c1ae 100644
--- a/nixpkgs/nixos/modules/tasks/auto-upgrade.nix
+++ b/nixpkgs/nixos/modules/tasks/auto-upgrade.nix
@@ -53,6 +53,16 @@ let cfg = config.system.autoUpgrade; in
         '';
       };
 
+      allowReboot = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Reboot the system into the new generation instead of a switch
+          if the new generation uses a different kernel, kernel modules
+          or initrd than the booted system.
+        '';
+      };
+
     };
 
   };
@@ -78,11 +88,23 @@ let cfg = config.system.autoUpgrade; in
           HOME = "/root";
         } // config.networking.proxy.envVars;
 
-      path = [ pkgs.gnutar pkgs.xz.bin pkgs.gitMinimal config.nix.package.out ];
-
-      script = ''
-        ${config.system.build.nixos-rebuild}/bin/nixos-rebuild switch ${toString cfg.flags}
-      '';
+      path = [ pkgs.coreutils pkgs.gnutar pkgs.xz.bin pkgs.gitMinimal config.nix.package.out ];
+
+      script = let
+          nixos-rebuild = "${config.system.build.nixos-rebuild}/bin/nixos-rebuild";
+        in
+        if cfg.allowReboot then ''
+            ${nixos-rebuild} boot ${toString cfg.flags}
+            booted="$(readlink /run/booted-system/{initrd,kernel,kernel-modules})"
+            built="$(readlink /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})"
+            if [ "$booted" = "$built" ]; then
+              ${nixos-rebuild} switch ${toString cfg.flags}
+            else
+              /run/current-system/sw/bin/shutdown -r +1
+            fi
+          '' else ''
+            ${nixos-rebuild} switch ${toString cfg.flags}
+        '';
 
       startAt = cfg.dates;
     };
diff --git a/nixpkgs/nixos/modules/tasks/filesystems/xfs.nix b/nixpkgs/nixos/modules/tasks/filesystems/xfs.nix
index c6a90bcf1a51..98038701ca58 100644
--- a/nixpkgs/nixos/modules/tasks/filesystems/xfs.nix
+++ b/nixpkgs/nixos/modules/tasks/filesystems/xfs.nix
@@ -18,6 +18,7 @@ in
     boot.initrd.extraUtilsCommands = mkIf inInitrd
       ''
         copy_bin_and_libs ${pkgs.xfsprogs.bin}/bin/fsck.xfs
+        copy_bin_and_libs ${pkgs.xfsprogs.bin}/bin/xfs_repair
       '';
 
     # Trick just to set 'sh' after the extraUtils nuke-refs.
diff --git a/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix b/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix
index f7f07bad9522..ac06b6caee30 100644
--- a/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix
@@ -1,8 +1,6 @@
 { config, lib, pkgs, utils, ... }:
 #
-# todo:
-#   - crontab for scrubs, etc
-#   - zfs tunables
+# TODO: zfs tunables
 
 with utils;
 with lib;
@@ -13,6 +11,7 @@ let
   cfgSnapshots = config.services.zfs.autoSnapshot;
   cfgSnapFlags = cfgSnapshots.flags;
   cfgScrub = config.services.zfs.autoScrub;
+  cfgTrim = config.services.zfs.trim;
 
   inInitrd = any (fs: fs == "zfs") config.boot.initrd.supportedFilesystems;
   inSystem = any (fs: fs == "zfs") config.boot.supportedFilesystems;
@@ -268,14 +267,26 @@ in
       };
     };
 
-    services.zfs.autoScrub = {
-      enable = mkOption {
-        default = false;
-        type = types.bool;
+    services.zfs.trim = {
+      enable = mkEnableOption "Enables periodic TRIM on all ZFS pools.";
+
+      interval = mkOption {
+        default = "weekly";
+        type = types.str;
+        example = "daily";
         description = ''
-          Enables periodic scrubbing of ZFS pools.
+          How often we run trim. For most desktop and server systems
+          a sufficient trimming frequency is once a week.
+
+          The format is described in
+          <citerefentry><refentrytitle>systemd.time</refentrytitle>
+          <manvolnum>7</manvolnum></citerefentry>.
         '';
       };
+    };
+
+    services.zfs.autoScrub = {
+      enable = mkEnableOption "Enables periodic scrubbing of ZFS pools.";
 
       interval = mkOption {
         default = "Sun, 02:00";
@@ -535,5 +546,17 @@ in
         };
       };
     })
+
+    (mkIf cfgTrim.enable {
+      systemd.services.zpool-trim = {
+        description = "ZFS pools trim";
+        after = [ "zfs-import.target" ];
+        path = [ packages.zfsUser ];
+        startAt = cfgTrim.interval;
+        script = ''
+          zpool list -H -o name | xargs -n1 zpool trim
+        '';
+      };
+    })
   ];
 }
diff --git a/nixpkgs/nixos/modules/tasks/network-interfaces-scripted.nix b/nixpkgs/nixos/modules/tasks/network-interfaces-scripted.nix
index c12ada7a030a..2b8a7944dc36 100644
--- a/nixpkgs/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixpkgs/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -103,7 +103,7 @@ let
 
             script =
               ''
-                ${optionalString (!config.environment.etc?"resolv.conf") ''
+                ${optionalString config.networking.resolvconf.enable ''
                   # Set the static DNS configuration, if given.
                   ${pkgs.openresolv}/sbin/resolvconf -m 1 -a static <<EOF
                   ${optionalString (cfg.nameservers != [] && cfg.domain != null) ''
diff --git a/nixpkgs/nixos/modules/tasks/network-interfaces.nix b/nixpkgs/nixos/modules/tasks/network-interfaces.nix
index f9b0eb330bf8..c75d7cbc408b 100644
--- a/nixpkgs/nixos/modules/tasks/network-interfaces.nix
+++ b/nixpkgs/nixos/modules/tasks/network-interfaces.nix
@@ -1017,7 +1017,6 @@ in
         pkgs.iproute
         pkgs.iputils
         pkgs.nettools
-        pkgs.openresolv
       ]
       ++ optionals config.networking.wireless.enable [
         pkgs.wirelesstools # FIXME: obsolete?
@@ -1087,7 +1086,24 @@ in
 
     virtualisation.vswitch = mkIf (cfg.vswitches != { }) { enable = true; };
 
-    services.udev.packages = mkIf (cfg.wlanInterfaces != {}) [
+    services.udev.packages =  [
+      (pkgs.writeTextFile rec {
+        name = "ipv6-privacy-extensions.rules";
+        destination = "/etc/udev/rules.d/98-${name}";
+        text = ''
+          # enable and prefer IPv6 privacy addresses by default
+          ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.procps}/bin/sysctl net.ipv6.conf.%k.use_tempaddr=2"
+        '';
+      })
+      (pkgs.writeTextFile rec {
+        name = "ipv6-privacy-extensions.rules";
+        destination = "/etc/udev/rules.d/99-${name}";
+        text = concatMapStrings (i: ''
+          # enable IPv6 privacy addresses but prefer EUI-64 addresses for ${i.name}
+          ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.procps}/bin/sysctl net.ipv6.conf.${i.name}.use_tempaddr=1"
+        '') (filter (i: !i.preferTempAddress) interfaces);
+      })
+    ] ++ lib.optional (cfg.wlanInterfaces != {})
       (pkgs.writeTextFile {
         name = "99-zzz-40-wlanInterfaces.rules";
         destination = "/etc/udev/rules.d/99-zzz-40-wlanInterfaces.rules";
@@ -1161,8 +1177,7 @@ in
             # Generate the same systemd events for both 'add' and 'move' udev events.
             ACTION=="move", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", NAME=="${device}", ${systemdAttrs curInterface._iName}
           '');
-      }) ];
-
+      });
   };
 
 }
diff --git a/nixpkgs/nixos/modules/virtualisation/parallels-guest.nix b/nixpkgs/nixos/modules/virtualisation/parallels-guest.nix
index 4e0f2cae299e..828419fb4b9d 100644
--- a/nixpkgs/nixos/modules/virtualisation/parallels-guest.nix
+++ b/nixpkgs/nixos/modules/virtualisation/parallels-guest.nix
@@ -47,7 +47,7 @@ in
   config = mkIf config.hardware.parallels.enable {
     services.xserver = {
       drivers = singleton
-        { name = "prlvideo"; modules = [ prl-tools ]; libPath = [ prl-tools ]; };
+        { name = "prlvideo"; modules = [ prl-tools ]; };
 
       screenSection = ''
         Option "NoMTRR"
@@ -65,6 +65,7 @@ in
 
     hardware.opengl.package = prl-tools;
     hardware.opengl.package32 = pkgs.pkgsi686Linux.linuxPackages.prl-tools.override { libsOnly = true; kernel = null; };
+    hardware.opengl.setLdLibraryPath = true;
 
     services.udev.packages = [ prl-tools ];
 
diff --git a/nixpkgs/nixos/release-combined.nix b/nixpkgs/nixos/release-combined.nix
index b9a9515f94ef..7146aebf54be 100644
--- a/nixpkgs/nixos/release-combined.nix
+++ b/nixpkgs/nixos/release-combined.nix
@@ -68,8 +68,9 @@ in rec {
         nixos.tests.chromium.x86_64-linux or []
         (all nixos.tests.firefox)
         (all nixos.tests.firewall)
-        (except ["aarch64-linux"] nixos.tests.gnome3)
-        (except ["aarch64-linux"] nixos.tests.pantheon)
+        (all nixos.tests.gnome3-xorg)
+        (all nixos.tests.gnome3)
+        (all nixos.tests.pantheon)
         nixos.tests.installer.zfsroot.x86_64-linux or [] # ZFS is 64bit only
         (except ["aarch64-linux"] nixos.tests.installer.lvm)
         (except ["aarch64-linux"] nixos.tests.installer.luksroot)
@@ -103,7 +104,7 @@ in rec {
         #(all nixos.tests.keymap.neo)
         #(all nixos.tests.keymap.qwertz)
         (all nixos.tests.plasma5)
-        #(all nixos.tests.lightdm)
+        (all nixos.tests.lightdm)
         (all nixos.tests.login)
         (all nixos.tests.misc)
         (all nixos.tests.mutableUsers)
diff --git a/nixpkgs/nixos/tests/all-tests.nix b/nixpkgs/nixos/tests/all-tests.nix
index 665a75e47dad..c24c8ae61a58 100644
--- a/nixpkgs/nixos/tests/all-tests.nix
+++ b/nixpkgs/nixos/tests/all-tests.nix
@@ -56,7 +56,7 @@ in
   containers-physical_interfaces = handleTest ./containers-physical_interfaces.nix {};
   containers-restart_networking = handleTest ./containers-restart_networking.nix {};
   containers-tmpfs = handleTest ./containers-tmpfs.nix {};
-  #couchdb = handleTest ./couchdb.nix {}; # spidermonkey-1.8.5 is marked as broken
+  couchdb = handleTest ./couchdb.nix {};
   deluge = handleTest ./deluge.nix {};
   dhparams = handleTest ./dhparams.nix {};
   dnscrypt-proxy = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy.nix {};
@@ -85,6 +85,7 @@ in
   flannel = handleTestOn ["x86_64-linux"] ./flannel.nix {};
   flatpak = handleTest ./flatpak.nix {};
   flatpak-builder = handleTest ./flatpak-builder.nix {};
+  fluentd = handleTest ./fluentd.nix {};
   fsck = handleTest ./fsck.nix {};
   fwupd = handleTestOn ["x86_64-linux"] ./fwupd.nix {}; # libsmbios is unsupported on aarch64
   gdk-pixbuf = handleTest ./gdk-pixbuf.nix {};
@@ -92,14 +93,15 @@ in
   gitlab = handleTest ./gitlab.nix {};
   gitolite = handleTest ./gitolite.nix {};
   gjs = handleTest ./gjs.nix {};
-  gnome3 = handleTestOn ["x86_64-linux"] ./gnome3.nix {}; # libsmbios is unsupported on aarch64
-  gnome3-gdm = handleTestOn ["x86_64-linux"] ./gnome3-gdm.nix {}; # libsmbios is unsupported on aarch64
+  gnome3-xorg = handleTest ./gnome3-xorg.nix {};
+  gnome3 = handleTest ./gnome3.nix {};
   gocd-agent = handleTest ./gocd-agent.nix {};
   gocd-server = handleTest ./gocd-server.nix {};
   google-oslogin = handleTest ./google-oslogin {};
   graphene = handleTest ./graphene.nix {};
   grafana = handleTest ./grafana.nix {};
   graphite = handleTest ./graphite.nix {};
+  graylog = handleTest ./graylog.nix {};
   hadoop.hdfs = handleTestOn [ "x86_64-linux" ] ./hadoop/hdfs.nix {};
   hadoop.yarn = handleTestOn [ "x86_64-linux" ] ./hadoop/yarn.nix {};
   handbrake = handleTestOn ["x86_64-linux"] ./handbrake.nix {};
@@ -137,7 +139,7 @@ in
   ldap = handleTest ./ldap.nix {};
   leaps = handleTest ./leaps.nix {};
   lidarr = handleTest ./lidarr.nix {};
-  #lightdm = handleTest ./lightdm.nix {};
+  lightdm = handleTest ./lightdm.nix {};
   limesurvey = handleTest ./limesurvey.nix {};
   login = handleTest ./login.nix {};
   loki = handleTest ./loki.nix {};
@@ -145,6 +147,7 @@ in
   mailcatcher = handleTest ./mailcatcher.nix {};
   mathics = handleTest ./mathics.nix {};
   matrix-synapse = handleTest ./matrix-synapse.nix {};
+  mediawiki = handleTest ./mediawiki.nix {};
   memcached = handleTest ./memcached.nix {};
   mesos = handleTest ./mesos.nix {};
   miniflux = handleTest ./miniflux.nix {};
@@ -180,6 +183,7 @@ in
   nginx = handleTest ./nginx.nix {};
   nginx-sso = handleTest ./nginx-sso.nix {};
   nix-ssh-serve = handleTest ./nix-ssh-serve.nix {};
+  nixos-generate-config = handleTest ./nixos-generate-config.nix {};
   novacomd = handleTestOn ["x86_64-linux"] ./novacomd.nix {};
   nsd = handleTest ./nsd.nix {};
   nzbget = handleTest ./nzbget.nix {};
@@ -206,6 +210,7 @@ in
   plotinus = handleTest ./plotinus.nix {};
   postgis = handleTest ./postgis.nix {};
   postgresql = handleTest ./postgresql.nix {};
+  postgresql-wal-receiver = handleTest ./postgresql-wal-receiver.nix {};
   powerdns = handleTest ./powerdns.nix {};
   predictable-interface-names = handleTest ./predictable-interface-names.nix {};
   printing = handleTest ./printing.nix {};
@@ -247,8 +252,8 @@ in
   pdns-recursor = handleTest ./pdns-recursor.nix {};
   taskserver = handleTest ./taskserver.nix {};
   telegraf = handleTest ./telegraf.nix {};
+  tiddlywiki = handleTest ./tiddlywiki.nix {};
   tinydns = handleTest ./tinydns.nix {};
-  tomcat = handleTest ./tomcat.nix {};
   tor = handleTest ./tor.nix {};
   transmission = handleTest ./transmission.nix {};
   udisks2 = handleTest ./udisks2.nix {};
diff --git a/nixpkgs/nixos/tests/boot.nix b/nixpkgs/nixos/tests/boot.nix
index c9bb1e77c6d0..57d8006d7ac3 100644
--- a/nixpkgs/nixos/tests/boot.nix
+++ b/nixpkgs/nixos/tests/boot.nix
@@ -17,46 +17,33 @@ let
         ];
     }).config.system.build.isoImage;
 
-  makeBootTest = name: machineConfig:
-    makeTest {
-      inherit iso;
-      name = "boot-" + name;
-      nodes = { };
-      testScript =
-        ''
-          my $machine = createMachine({ ${machineConfig}, qemuFlags => '-m 768' });
-          $machine->start;
-          $machine->waitForUnit("multi-user.target");
-          $machine->succeed("nix verify -r --no-trust /run/current-system");
+  perlAttrs = params: "{ ${concatStringsSep ", " (mapAttrsToList (name: param: "${name} => ${builtins.toJSON param}") params)} }";
 
-          # Test whether the channel got installed correctly.
-          $machine->succeed("nix-instantiate --dry-run '<nixpkgs>' -A hello");
-          $machine->succeed("nix-env --dry-run -iA nixos.procps");
-
-          $machine->shutdown;
-        '';
-    };
-in {
-
-    biosCdrom = makeBootTest "bios-cdrom" ''
-        cdrom => glob("${iso}/iso/*.iso")
-      '';
-
-    biosUsb = makeBootTest "bios-usb" ''
-        usb => glob("${iso}/iso/*.iso")
-      '';
+  makeBootTest = name: extraConfig:
+    let
+      machineConfig = perlAttrs ({ qemuFlags = "-m 768"; } // extraConfig);
+    in
+      makeTest {
+        inherit iso;
+        name = "boot-" + name;
+        nodes = { };
+        testScript =
+          ''
+            my $machine = createMachine(${machineConfig});
+            $machine->start;
+            $machine->waitForUnit("multi-user.target");
+            $machine->succeed("nix verify -r --no-trust /run/current-system");
 
-    uefiCdrom = makeBootTest "uefi-cdrom" ''
-        cdrom => glob("${iso}/iso/*.iso"),
-        bios => '${pkgs.OVMF.fd}/FV/OVMF.fd'
-      '';
+            # Test whether the channel got installed correctly.
+            $machine->succeed("nix-instantiate --dry-run '<nixpkgs>' -A hello");
+            $machine->succeed("nix-env --dry-run -iA nixos.procps");
 
-    uefiUsb = makeBootTest "uefi-usb" ''
-        usb => glob("${iso}/iso/*.iso"),
-        bios => '${pkgs.OVMF.fd}/FV/OVMF.fd'
-      '';
+            $machine->shutdown;
+          '';
+      };
 
-    netboot = let
+  makeNetbootTest = name: extraConfig:
+    let
       config = (import ../lib/eval-config.nix {
           inherit system;
           modules =
@@ -65,35 +52,55 @@ in {
               { key = "serial"; }
             ];
         }).config;
-      ipxeScriptDir = pkgs.writeTextFile {
-        name = "ipxeScriptDir";
-        text = ''
-          #!ipxe
-          dhcp
-          kernel bzImage init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} console=ttyS0
-          initrd initrd
-          boot
-        '';
-        destination = "/boot.ipxe";
-      };
       ipxeBootDir = pkgs.symlinkJoin {
         name = "ipxeBootDir";
         paths = [
           config.system.build.netbootRamdisk
           config.system.build.kernel
-          ipxeScriptDir
+          config.system.build.netbootIpxeScript
         ];
       };
+      machineConfig = perlAttrs ({
+        qemuFlags = "-boot order=n -m 2000";
+        netBackendArgs = "tftp=${ipxeBootDir},bootfile=netboot.ipxe";
+      } // extraConfig);
     in
       makeTest {
-        name = "boot-netboot";
+        name = "boot-netboot-" + name;
         nodes = { };
         testScript =
           ''
-            my $machine = createMachine({ qemuFlags => '-boot order=n -net nic,model=e1000 -net user,tftp=${ipxeBootDir}/,bootfile=boot.ipxe -m 2000M' });
+            my $machine = createMachine(${machineConfig});
             $machine->start;
             $machine->waitForUnit("multi-user.target");
             $machine->shutdown;
           '';
       };
+in {
+
+    biosCdrom = makeBootTest "bios-cdrom" {
+      cdrom = "${iso}/iso/${iso.isoName}";
+    };
+
+    biosUsb = makeBootTest "bios-usb" {
+      usb = "${iso}/iso/${iso.isoName}";
+    };
+
+    uefiCdrom = makeBootTest "uefi-cdrom" {
+      cdrom = "${iso}/iso/${iso.isoName}";
+      bios = "${pkgs.OVMF.fd}/FV/OVMF.fd";
+    };
+
+    uefiUsb = makeBootTest "uefi-usb" {
+      usb = "${iso}/iso/${iso.isoName}";
+      bios = "${pkgs.OVMF.fd}/FV/OVMF.fd";
+    };
+
+    biosNetboot = makeNetbootTest "bios" {};
+
+    uefiNetboot = makeNetbootTest "uefi" {
+      bios = "${pkgs.OVMF.fd}/FV/OVMF.fd";
+      # Custom ROM is needed for EFI PXE boot. I failed to understand exactly why, because QEMU should still use iPXE for EFI.
+      netFrontendArgs = "romfile=${pkgs.ipxe}/ipxe.efirom";
+    };
 }
diff --git a/nixpkgs/nixos/tests/cassandra.nix b/nixpkgs/nixos/tests/cassandra.nix
index aea4fa4d1c95..c55733c9be7b 100644
--- a/nixpkgs/nixos/tests/cassandra.nix
+++ b/nixpkgs/nixos/tests/cassandra.nix
@@ -8,11 +8,12 @@ let
   jmxRoles = [{ username = "me"; password = "password"; }];
   jmxRolesFile = ./cassandra-jmx-roles;
   jmxAuthArgs = "-u ${(builtins.elemAt jmxRoles 0).username} -pw ${(builtins.elemAt jmxRoles 0).password}";
+  jmxPort = 7200;  # Non-standard port so it doesn't accidentally work
 
   # Would usually be assigned to 512M
   numMaxHeapSize = "400";
   getHeapLimitCommand = ''
-    nodetool info | grep "^Heap Memory" | awk \'{print $NF}\'
+    nodetool info -p ${toString jmxPort} | grep "^Heap Memory" | awk \'{print $NF}\'
   '';
   checkHeapLimitCommand = ''
     [ 1 -eq "$(echo "$(${getHeapLimitCommand}) < ${numMaxHeapSize}" | ${pkgs.bc}/bin/bc)" ]
@@ -27,19 +28,20 @@ let
       package = testPackage;
       maxHeapSize = "${numMaxHeapSize}M";
       heapNewSize = "100M";
+      inherit jmxPort;
     };
-  nodeCfg = ipAddress: extra: {pkgs, config, ...}:
-    { environment.systemPackages = [ testPackage ];
-      networking = {
-        firewall.allowedTCPPorts = [ 7000 7199 9042 ];
-        useDHCP = false;
-        interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [
-          { address = ipAddress; prefixLength = 24; }
-        ];
-      };
-      services.cassandra = cassandraCfg ipAddress // extra;
-      virtualisation.memorySize = 1024;
+  nodeCfg = ipAddress: extra: {pkgs, config, ...}: rec {
+    environment.systemPackages = [ testPackage ];
+    networking = {
+      firewall.allowedTCPPorts = [ 7000 9042 services.cassandra.jmxPort ];
+      useDHCP = false;
+      interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [
+        { address = ipAddress; prefixLength = 24; }
+      ];
     };
+    services.cassandra = cassandraCfg ipAddress // extra;
+    virtualisation.memorySize = 1024;
+  };
 in
 {
   name = "cassandra-ci";
@@ -50,7 +52,9 @@ in
     cass2 = nodeCfg "192.168.1.3" { jvmOpts = [ "-Dcassandra.replace_address=cass1" ]; };
   };
 
-  testScript = ''
+  testScript = let
+    jmxPortS = toString jmxPort;
+  in ''
     # Check configuration
     subtest "Timers exist", sub {
       $cass0->succeed("systemctl list-timers | grep cassandra-full-repair.timer");
@@ -63,51 +67,51 @@ in
     };
     subtest "Nodetool is operational", sub {
       $cass0->waitForUnit("cassandra.service");
-      $cass0->waitUntilSucceeds("nc -z localhost 7199");
-      $cass0->succeed("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass0'");
+      $cass0->waitUntilSucceeds("nc -z localhost ${jmxPortS}");
+      $cass0->succeed("nodetool status -p ${jmxPortS} --resolve-ip | egrep '^UN[[:space:]]+cass0'");
     };
     subtest "Cluster name was set", sub {
       $cass0->waitForUnit("cassandra.service");
-      $cass0->waitUntilSucceeds("nc -z localhost 7199");
-      $cass0->waitUntilSucceeds("nodetool describecluster | grep 'Name: ${clusterName}'");
+      $cass0->waitUntilSucceeds("nc -z localhost ${jmxPortS}");
+      $cass0->waitUntilSucceeds("nodetool describecluster -p ${jmxPortS} | grep 'Name: ${clusterName}'");
     };
     subtest "Heap limit set correctly", sub {
       # Nodetool takes a while until it can display info
-      $cass0->waitUntilSucceeds('nodetool info');
+      $cass0->waitUntilSucceeds('nodetool info -p ${jmxPortS}');
       $cass0->succeed('${checkHeapLimitCommand}');
     };
 
     # Check cluster interaction
     subtest "Bring up cluster", sub {
       $cass1->waitForUnit("cassandra.service");
-      $cass1->waitUntilSucceeds("nodetool ${jmxAuthArgs} status | egrep -c '^UN' | grep 2");
-      $cass0->succeed("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass1'");
+      $cass1->waitUntilSucceeds("nodetool -p ${jmxPortS} ${jmxAuthArgs} status | egrep -c '^UN' | grep 2");
+      $cass0->succeed("nodetool status -p ${jmxPortS} --resolve-ip | egrep '^UN[[:space:]]+cass1'");
     };
   '' + lib.optionalString testRemoteAuth ''
     subtest "Remote authenticated jmx", sub {
       # Doesn't work if not enabled
-      $cass0->waitUntilSucceeds("nc -z localhost 7199");
-      $cass1->fail("nc -z 192.168.1.1 7199");
-      $cass1->fail("nodetool -h 192.168.1.1 status");
+      $cass0->waitUntilSucceeds("nc -z localhost ${jmxPortS}");
+      $cass1->fail("nc -z 192.168.1.1 ${toString jmxPort}");
+      $cass1->fail("nodetool -p ${jmxPortS} -h 192.168.1.1 status");
 
       # Works if enabled
-      $cass1->waitUntilSucceeds("nc -z localhost 7199");
-      $cass0->succeed("nodetool -h 192.168.1.2 ${jmxAuthArgs} status");
+      $cass1->waitUntilSucceeds("nc -z localhost ${toString jmxPort}");
+      $cass0->succeed("nodetool -p ${jmxPortS} -h 192.168.1.2 ${jmxAuthArgs} status");
     };
   '' + ''
     subtest "Break and fix node", sub {
       $cass1->block;
-      $cass0->waitUntilSucceeds("nodetool status --resolve-ip | egrep -c '^DN[[:space:]]+cass1'");
-      $cass0->succeed("nodetool status | egrep -c '^UN'  | grep 1");
+      $cass0->waitUntilSucceeds("nodetool status -p ${jmxPortS} --resolve-ip | egrep -c '^DN[[:space:]]+cass1'");
+      $cass0->succeed("nodetool status -p ${jmxPortS} | egrep -c '^UN'  | grep 1");
       $cass1->unblock;
-      $cass1->waitUntilSucceeds("nodetool ${jmxAuthArgs} status | egrep -c '^UN'  | grep 2");
-      $cass0->succeed("nodetool status | egrep -c '^UN'  | grep 2");
+      $cass1->waitUntilSucceeds("nodetool -p ${jmxPortS} ${jmxAuthArgs} status | egrep -c '^UN'  | grep 2");
+      $cass0->succeed("nodetool status -p ${jmxPortS} | egrep -c '^UN'  | grep 2");
     };
     subtest "Replace crashed node", sub {
       $cass1->crash;
       $cass2->waitForUnit("cassandra.service");
-      $cass0->waitUntilFails("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass1'");
-      $cass0->waitUntilSucceeds("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass2'");
+      $cass0->waitUntilFails("nodetool status -p ${jmxPortS} --resolve-ip | egrep '^UN[[:space:]]+cass1'");
+      $cass0->waitUntilSucceeds("nodetool status -p ${jmxPortS} --resolve-ip | egrep '^UN[[:space:]]+cass2'");
     };
   '';
 })
diff --git a/nixpkgs/nixos/tests/deluge.nix b/nixpkgs/nixos/tests/deluge.nix
index 22ad84e7bff1..b58030409b5c 100644
--- a/nixpkgs/nixos/tests/deluge.nix
+++ b/nixpkgs/nixos/tests/deluge.nix
@@ -8,9 +8,11 @@ import ./make-test.nix ({ pkgs, ...} : {
     simple = {
       services.deluge = {
         enable = true;
-        web.enable = true;
+        web = {
+          enable = true;
+          openFirewall = true;
+        };
       };
-      networking.firewall.allowedTCPPorts = [ 8112 ];
     };
 
     declarative =
diff --git a/nixpkgs/nixos/tests/flatpak-builder.nix b/nixpkgs/nixos/tests/flatpak-builder.nix
index 2100631ec7f4..49b97e8ca99e 100644
--- a/nixpkgs/nixos/tests/flatpak-builder.nix
+++ b/nixpkgs/nixos/tests/flatpak-builder.nix
@@ -9,6 +9,7 @@ import ./make-test.nix ({ pkgs, ... }:
 
   machine = { pkgs, ... }: {
     services.flatpak.enable = true;
+    xdg.portal.enable = true;
     environment.systemPackages = with pkgs; [ gnome-desktop-testing flatpak-builder ] ++ flatpak-builder.installedTestsDependencies;
     virtualisation.diskSize = 2048;
   };
diff --git a/nixpkgs/nixos/tests/fluentd.nix b/nixpkgs/nixos/tests/fluentd.nix
new file mode 100644
index 000000000000..e5c4c3d21631
--- /dev/null
+++ b/nixpkgs/nixos/tests/fluentd.nix
@@ -0,0 +1,46 @@
+import ./make-test.nix ({ pkgs, lib, ... }: {
+  name = "fluentd";
+
+  machine = { pkgs, ... }: {
+    services.fluentd = {
+      enable = true;
+      config = ''
+        <source>
+          @type http
+          port 9880
+        </source>
+
+        <match **>
+          type copy
+          <store>
+            @type file
+            format json
+            path /tmp/fluentd
+            symlink_path /tmp/current-log
+          </store>
+          <store>
+            @type stdout
+          </store>
+        </match>
+      '';
+    };
+  };
+
+  testScript = let
+    testMessage = "an example log message";
+
+    payload = pkgs.writeText "test-message.json" (builtins.toJSON {
+      inherit testMessage;
+    });
+  in ''
+    $machine->start;
+    $machine->waitForUnit('fluentd.service');
+    $machine->waitForOpenPort(9880);
+
+    $machine->succeed("curl -fsSL -X POST -H 'Content-type: application/json' -d @${payload} http://localhost:9880/test.tag");
+
+    $machine->succeed("systemctl stop fluentd"); # blocking flush
+
+    $machine->succeed("grep '${testMessage}' /tmp/current-log");
+  '';
+})
diff --git a/nixpkgs/nixos/tests/gdk-pixbuf.nix b/nixpkgs/nixos/tests/gdk-pixbuf.nix
index 005c5111da2b..9a62b593f46d 100644
--- a/nixpkgs/nixos/tests/gdk-pixbuf.nix
+++ b/nixpkgs/nixos/tests/gdk-pixbuf.nix
@@ -3,12 +3,12 @@ import ./make-test.nix ({ pkgs, ... }: {
   name = "gdk-pixbuf";
 
   meta = {
-    maintainers = pkgs.gdk_pixbuf.meta.maintainers;
+    maintainers = pkgs.gdk-pixbuf.meta.maintainers;
   };
 
   machine = { pkgs, ... }: {
     environment.systemPackages = with pkgs; [ gnome-desktop-testing ];
-    environment.variables.XDG_DATA_DIRS = [ "${pkgs.gdk_pixbuf.installedTests}/share" ];
+    environment.variables.XDG_DATA_DIRS = [ "${pkgs.gdk-pixbuf.installedTests}/share" ];
 
     # Tests allocate a lot of memory trying to exploit a CVE
     # but qemu-system-i386 has a 2047M memory limit
diff --git a/nixpkgs/nixos/tests/gnome3-gdm.nix b/nixpkgs/nixos/tests/gnome3-gdm.nix
deleted file mode 100644
index c2808d87d99d..000000000000
--- a/nixpkgs/nixos/tests/gnome3-gdm.nix
+++ /dev/null
@@ -1,63 +0,0 @@
-import ./make-test.nix ({ pkgs, ...} : {
-  name = "gnome3-gdm";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ lethalman ];
-  };
-
-  machine =
-    { ... }:
-
-    { imports = [ ./common/user-account.nix ];
-
-      services.xserver.enable = true;
-
-      services.xserver.displayManager.gdm = {
-        enable = true;
-        autoLogin = {
-          enable = true;
-          user = "alice";
-        };
-      };
-      services.xserver.desktopManager.gnome3.enable = true;
-
-      virtualisation.memorySize = 1024;
-    };
-
-  testScript = let
-    # Keep line widths somewhat managable
-    bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus";
-    gdbus = "${bus} gdbus";
-    # Call javascript in gnome shell, returns a tuple (success, output), where
-    # `success` is true if the dbus call was successful and output is what the
-    # javascript evaluates to.
-    eval = "call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval";
-    # False when startup is done
-    startingUp = "${gdbus} ${eval} Main.layoutManager._startingUp";
-    # Hopefully gnome-terminal's wm class
-    wmClass = "${gdbus} ${eval} global.display.focus_window.wm_class";
-  in ''
-      # wait for gdm to start
-      $machine->waitForUnit("display-manager.service");
-
-      # wait for alice to be logged in
-      $machine->waitForUnit("default.target","alice");
-
-      # Check that logging in has given the user ownership of devices.
-      $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
-
-      # Wait for the wayland server
-      $machine->waitForFile("/run/user/1000/wayland-0");
-
-      # Wait for gnome shell, correct output should be "(true, 'false')"
-      $machine->waitUntilSucceeds("su - alice -c '${startingUp} | grep -q true,..false'");
-
-      # open a terminal
-      $machine->succeed("su - alice -c '${bus} gnome-terminal'");
-      # and check it's there
-      $machine->waitUntilSucceeds("su - alice -c '${wmClass} | grep -q gnome-terminal-server'");
-
-      # wait to get a nice screenshot
-      $machine->sleep(20);
-      $machine->screenshot("screen");
-    '';
-})
diff --git a/nixpkgs/nixos/tests/gnome3-xorg.nix b/nixpkgs/nixos/tests/gnome3-xorg.nix
new file mode 100644
index 000000000000..f12361da0372
--- /dev/null
+++ b/nixpkgs/nixos/tests/gnome3-xorg.nix
@@ -0,0 +1,41 @@
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "gnome3-xorg";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = pkgs.gnome3.maintainers;
+  };
+
+  machine =
+    { ... }:
+
+    { imports = [ ./common/user-account.nix ];
+
+      services.xserver.enable = true;
+
+      services.xserver.displayManager.gdm.enable = false;
+      services.xserver.displayManager.lightdm.enable = true;
+      services.xserver.displayManager.lightdm.autoLogin.enable = true;
+      services.xserver.displayManager.lightdm.autoLogin.user = "alice";
+      services.xserver.desktopManager.gnome3.enable = true;
+      services.xserver.desktopManager.default = "gnome-xorg";
+
+      virtualisation.memorySize = 1024;
+    };
+
+  testScript =
+    ''
+      $machine->waitForX;
+
+      # wait for alice to be logged in
+      $machine->waitForUnit("default.target","alice");
+
+      # Check that logging in has given the user ownership of devices.
+      $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
+
+      $machine->succeed("su - alice -c 'DISPLAY=:0.0 gnome-terminal &'");
+      $machine->succeed("xauth merge ~alice/.Xauthority");
+      $machine->waitForWindow(qr/alice.*machine/);
+      $machine->succeed("timeout 900 bash -c 'while read msg; do if [[ \$msg =~ \"GNOME Shell started\" ]]; then break; fi; done < <(journalctl -f)'");
+      $machine->sleep(10);
+      $machine->screenshot("screen");
+    '';
+})
diff --git a/nixpkgs/nixos/tests/gnome3.nix b/nixpkgs/nixos/tests/gnome3.nix
index b58c9e5a0e32..b6fe602a7327 100644
--- a/nixpkgs/nixos/tests/gnome3.nix
+++ b/nixpkgs/nixos/tests/gnome3.nix
@@ -1,7 +1,7 @@
 import ./make-test.nix ({ pkgs, ...} : {
   name = "gnome3";
   meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ domenkozar eelco lethalman ];
+    maintainers = pkgs.gnome3.maintainers;
   };
 
   machine =
@@ -11,19 +11,34 @@ import ./make-test.nix ({ pkgs, ...} : {
 
       services.xserver.enable = true;
 
-      services.xserver.displayManager.gdm.enable = false;
-      services.xserver.displayManager.lightdm.enable = true;
-      services.xserver.displayManager.lightdm.autoLogin.enable = true;
-      services.xserver.displayManager.lightdm.autoLogin.user = "alice";
+      services.xserver.displayManager.gdm = {
+        enable = true;
+        autoLogin = {
+          enable = true;
+          user = "alice";
+        };
+      };
+
       services.xserver.desktopManager.gnome3.enable = true;
-      services.xserver.desktopManager.default = "gnome-xorg";
 
       virtualisation.memorySize = 1024;
     };
 
-  testScript =
-    ''
-      $machine->waitForX;
+  testScript = let
+    # Keep line widths somewhat managable
+    bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus";
+    gdbus = "${bus} gdbus";
+    # Call javascript in gnome shell, returns a tuple (success, output), where
+    # `success` is true if the dbus call was successful and output is what the
+    # javascript evaluates to.
+    eval = "call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval";
+    # False when startup is done
+    startingUp = "${gdbus} ${eval} Main.layoutManager._startingUp";
+    # Hopefully gnome-terminal's wm class
+    wmClass = "${gdbus} ${eval} global.display.focus_window.wm_class";
+  in ''
+      # wait for gdm to start
+      $machine->waitForUnit("display-manager.service");
 
       # wait for alice to be logged in
       $machine->waitForUnit("default.target","alice");
@@ -31,11 +46,19 @@ import ./make-test.nix ({ pkgs, ...} : {
       # Check that logging in has given the user ownership of devices.
       $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
 
-      $machine->succeed("su - alice -c 'DISPLAY=:0.0 gnome-terminal &'");
-      $machine->succeed("xauth merge ~alice/.Xauthority");
-      $machine->waitForWindow(qr/alice.*machine/);
-      $machine->succeed("timeout 900 bash -c 'while read msg; do if [[ \$msg =~ \"GNOME Shell started\" ]]; then break; fi; done < <(journalctl -f)'");
-      $machine->sleep(10);
+      # Wait for the wayland server
+      $machine->waitForFile("/run/user/1000/wayland-0");
+
+      # Wait for gnome shell, correct output should be "(true, 'false')"
+      $machine->waitUntilSucceeds("su - alice -c '${startingUp} | grep -q true,..false'");
+
+      # open a terminal
+      $machine->succeed("su - alice -c '${bus} gnome-terminal'");
+      # and check it's there
+      $machine->waitUntilSucceeds("su - alice -c '${wmClass} | grep -q gnome-terminal-server'");
+
+      # wait to get a nice screenshot
+      $machine->sleep(20);
       $machine->screenshot("screen");
     '';
 })
diff --git a/nixpkgs/nixos/tests/grafana.nix b/nixpkgs/nixos/tests/grafana.nix
index 9dc765a879bc..7a1b4c8ffbbc 100644
--- a/nixpkgs/nixos/tests/grafana.nix
+++ b/nixpkgs/nixos/tests/grafana.nix
@@ -1,25 +1,91 @@
-import ./make-test.nix ({ lib, ... }:
-{
-  name = "grafana";
+import ./make-test.nix ({ lib, pkgs, ... }:
 
-  meta = with lib.maintainers; {
-    maintainers = [ willibutz ];
-  };
+let
+  inherit (lib) mkMerge nameValuePair maintainers;
 
-  machine = { ... }: {
+  baseGrafanaConf = {
     services.grafana = {
       enable = true;
       addr = "localhost";
       analytics.reporting.enable = false;
       domain = "localhost";
-      security.adminUser = "testusername";
+      security = {
+        adminUser = "testadmin";
+        adminPassword = "snakeoilpwd";
+      };
     };
   };
 
+  extraNodeConfs = {
+    postgresql = {
+      services.grafana.database = {
+        host = "127.0.0.1:5432";
+        user = "grafana";
+      };
+      services.postgresql = {
+        enable = true;
+        ensureDatabases = [ "grafana" ];
+        ensureUsers = [{
+          name = "grafana";
+          ensurePermissions."DATABASE grafana" = "ALL PRIVILEGES";
+        }];
+      };
+      systemd.services.grafana.after = [ "postgresql.service" ];
+    };
+
+    mysql = {
+      services.grafana.database.user = "grafana";
+      services.mysql = {
+        enable = true;
+        ensureDatabases = [ "grafana" ];
+        ensureUsers = [{
+          name = "grafana";
+          ensurePermissions."grafana.*" = "ALL PRIVILEGES";
+        }];
+        package = pkgs.mariadb;
+      };
+      systemd.services.grafana.after = [ "mysql.service" ];
+    };
+  };
+
+  nodes = builtins.listToAttrs (map (dbName:
+    nameValuePair dbName (mkMerge [
+    baseGrafanaConf
+    (extraNodeConfs.${dbName} or {})
+  ])) [ "sqlite" "postgresql" "mysql" ]);
+
+in {
+  name = "grafana";
+
+  meta = with maintainers; {
+    maintainers = [ willibutz ];
+  };
+
+  inherit nodes;
+
   testScript = ''
-    $machine->start;
-    $machine->waitForUnit("grafana.service");
-    $machine->waitForOpenPort(3000);
-    $machine->succeed("curl -sSfL http://127.0.0.1:3000/");
+    startAll();
+
+    subtest "Grafana sqlite", sub {
+      $sqlite->waitForUnit("grafana.service");
+      $sqlite->waitForOpenPort(3000);
+      $sqlite->succeed("curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/org/users | grep -q testadmin\@localhost");
+    };
+
+    subtest "Grafana postgresql", sub {
+      $postgresql->waitForUnit("grafana.service");
+      $postgresql->waitForUnit("postgresql.service");
+      $postgresql->waitForOpenPort(3000);
+      $postgresql->waitForOpenPort(5432);
+      $postgresql->succeed("curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/org/users | grep -q testadmin\@localhost");
+    };
+
+    subtest "Grafana mysql", sub {
+      $mysql->waitForUnit("grafana.service");
+      $mysql->waitForUnit("mysql.service");
+      $mysql->waitForOpenPort(3000);
+      $mysql->waitForOpenPort(3306);
+      $mysql->succeed("curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/org/users | grep -q testadmin\@localhost");
+    };
   '';
 })
diff --git a/nixpkgs/nixos/tests/graylog.nix b/nixpkgs/nixos/tests/graylog.nix
new file mode 100644
index 000000000000..dc54afd1d26d
--- /dev/null
+++ b/nixpkgs/nixos/tests/graylog.nix
@@ -0,0 +1,111 @@
+import ./make-test.nix ({ pkgs, lib, ... }: {
+  name = "graylog";
+  meta.maintainers = with lib.maintainers; [ ma27 ];
+
+  machine = { pkgs, ... }: {
+    virtualisation.memorySize = 4096;
+    virtualisation.diskSize = 4096;
+
+    services.mongodb.enable = true;
+    services.elasticsearch.enable = true;
+    services.elasticsearch.package = pkgs.elasticsearch-oss;
+    services.elasticsearch.extraConf = ''
+      network.publish_host: 127.0.0.1
+      network.bind_host: 127.0.0.1
+    '';
+
+    services.graylog = {
+      enable = true;
+      passwordSecret = "YGhZ59wXMrYOojx5xdgEpBpDw2N6FbhM4lTtaJ1KPxxmKrUvSlDbtWArwAWMQ5LKx1ojHEVrQrBMVRdXbRyZLqffoUzHfssc";
+      elasticsearchHosts = [ "http://localhost:9200" ];
+
+      # `echo -n "nixos" | shasum -a 256`
+      rootPasswordSha2 = "6ed332bcfa615381511d4d5ba44a293bb476f368f7e9e304f0dff50230d1a85b";
+    };
+
+    environment.systemPackages = [ pkgs.jq ];
+
+    systemd.services.graylog.path = [ pkgs.netcat ];
+    systemd.services.graylog.preStart = ''
+      until nc -z localhost 9200; do
+        sleep 2
+      done
+    '';
+  };
+
+  testScript = let
+    payloads.login = pkgs.writeText "login.json" (builtins.toJSON {
+      host = "127.0.0.1:9000";
+      username = "admin";
+      password = "nixos";
+    });
+
+    payloads.input = pkgs.writeText "input.json" (builtins.toJSON {
+      title = "Demo";
+      global = false;
+      type = "org.graylog2.inputs.gelf.udp.GELFUDPInput";
+      node = "@node@";
+      configuration = {
+        bind_address = "0.0.0.0";
+        decompress_size_limit = 8388608;
+        number_worker_threads = 1;
+        override_source = null;
+        port = 12201;
+        recv_buffer_size = 262144;
+      };
+    });
+
+    payloads.gelf_message = pkgs.writeText "gelf.json" (builtins.toJSON {
+      host = "example.org";
+      short_message = "A short message";
+      full_message = "A long message";
+      version = "1.1";
+      level = 5;
+      facility = "Test";
+    });
+  in ''
+    $machine->start;
+    $machine->waitForUnit("graylog.service");
+    $machine->waitForOpenPort(9000);
+    $machine->succeed("curl -sSfL http://127.0.0.1:9000/");
+
+    my $session = $machine->succeed("curl -X POST "
+                                  . "-sSfL http://127.0.0.1:9000/api/system/sessions "
+                                  . "-d \$(cat ${payloads.login}) "
+                                  . "-H 'Content-Type: application/json' "
+                                  . "-H 'Accept: application/json' "
+                                  . "-H 'x-requested-by: cli' "
+                                  . "| jq .session_id | xargs echo"
+                                  );
+
+    chomp($session);
+
+    $machine->succeed("curl -X POST "
+                    . "-sSfL http://127.0.0.1:9000/api/system/inputs -u $session:session "
+                    . "-d \$(cat ${payloads.input} | sed -e \"s,\@node\@,\$(cat /var/lib/graylog/server/node-id),\") "
+                    . "-H 'Accept: application/json' "
+                    . "-H 'Content-Type: application/json' "
+                    . "-H 'x-requested-by: cli' "
+                    );
+
+    $machine->waitUntilSucceeds("test \"\$(curl -sSfL 'http://127.0.0.1:9000/api/cluster/inputstates' "
+                              . "-u $session:session "
+                              . "-H 'Accept: application/json' "
+                              . "-H 'Content-Type: application/json' "
+                              . "-H 'x-requested-by: cli'"
+                              . "| jq 'to_entries[]|.value|.[0]|.state' | xargs echo"
+                              . ")\" = \"RUNNING\""
+                              );
+
+    $machine->succeed("echo -n \$(cat ${payloads.gelf_message}) | nc -w10 -u 127.0.0.1 12201");
+
+    $machine->succeed("test \"\$(curl -X GET "
+                    . "-sSfL 'http://127.0.0.1:9000/api/search/universal/relative?query=*' "
+                    . "-u $session:session "
+                    . "-H 'Accept: application/json' "
+                    . "-H 'Content-Type: application/json' "
+                    . "-H 'x-requested-by: cli'"
+                    . " | jq '.total_results' | xargs echo)\" = \"1\""
+                    );
+  '';
+})
diff --git a/nixpkgs/nixos/tests/initdb.nix b/nixpkgs/nixos/tests/initdb.nix
new file mode 100644
index 000000000000..749d7857a134
--- /dev/null
+++ b/nixpkgs/nixos/tests/initdb.nix
@@ -0,0 +1,26 @@
+let
+  pkgs = import <nixpkgs> { };
+in
+with import <nixpkgs/nixos/lib/testing.nix> { inherit pkgs; system = builtins.currentSystem; };
+with pkgs.lib;
+
+makeTest {
+    name = "pg-initdb";
+
+    machine = {...}:
+      {
+        documentation.enable = false;
+        services.postgresql.enable = true;
+        services.postgresql.package = pkgs.postgresql_9_6;
+        environment.pathsToLink = [
+          "/share/postgresql"
+        ];
+      };
+
+    testScript = ''
+      $machine->start;
+      $machine->succeed("sudo -u postgres initdb -D /tmp/testpostgres2");
+      $machine->shutdown;
+    '';
+
+  }
\ No newline at end of file
diff --git a/nixpkgs/nixos/tests/installer.nix b/nixpkgs/nixos/tests/installer.nix
index 07659b60b3b8..a136678c6eff 100644
--- a/nixpkgs/nixos/tests/installer.nix
+++ b/nixpkgs/nixos/tests/installer.nix
@@ -67,6 +67,7 @@ let
   # partitions and filesystems.
   testScriptFun = { bootLoader, createPartitions, grubVersion, grubDevice, grubUseEfi
                   , grubIdentifier, preBootCommands, extraConfig
+                  , testCloneConfig
                   }:
     let
       iface = if grubVersion == 1 then "ide" else "virtio";
@@ -85,6 +86,7 @@ let
     in if !isEfi && !(pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) then
       throw "Non-EFI boot methods are only supported on i686 / x86_64"
     else ''
+
       $machine->start;
 
       # Make sure that we get a login prompt etc.
@@ -185,6 +187,43 @@ let
       ${preBootCommands}
       $machine->waitForUnit("network.target");
       $machine->shutdown;
+
+      # Tests for validating clone configuration entries in grub menu
+      ${optionalString testCloneConfig ''
+        # Reboot Machine
+        $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}", name => "clone-default-config" });
+        ${preBootCommands}
+        $machine->waitForUnit("multi-user.target");
+
+        # Booted configuration name should be Home
+        # This is not the name that shows in the grub menu.
+        # The default configuration is always shown as "Default"
+        $machine->succeed("cat /run/booted-system/configuration-name >&2");
+        $machine->succeed("cat /run/booted-system/configuration-name | grep Home");
+
+        # We should find **not** a file named /etc/gitconfig
+        $machine->fail("test -e /etc/gitconfig");
+
+        # Set grub to boot the second configuration
+        $machine->succeed("grub-reboot 1");
+
+        $machine->shutdown;
+
+        # Reboot Machine
+        $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}", name => "clone-alternate-config" });
+        ${preBootCommands}
+
+        $machine->waitForUnit("multi-user.target");
+        # Booted configuration name should be Work
+        $machine->succeed("cat /run/booted-system/configuration-name >&2");
+        $machine->succeed("cat /run/booted-system/configuration-name | grep Work");
+
+        # We should find a file named /etc/gitconfig
+        $machine->succeed("test -e /etc/gitconfig");
+
+        $machine->shutdown;
+      ''}
+
     '';
 
 
@@ -194,6 +233,7 @@ let
     , bootLoader ? "grub" # either "grub" or "systemd-boot"
     , grubVersion ? 2, grubDevice ? "/dev/vda", grubIdentifier ? "uuid", grubUseEfi ? false
     , enableOCR ? false, meta ? {}
+    , testCloneConfig ? false
     }:
     makeTest {
       inherit enableOCR;
@@ -269,7 +309,8 @@ let
 
       testScript = testScriptFun {
         inherit bootLoader createPartitions preBootCommands
-                grubVersion grubDevice grubIdentifier grubUseEfi extraConfig;
+                grubVersion grubDevice grubIdentifier grubUseEfi extraConfig
+                testCloneConfig;
       };
     };
 
@@ -304,32 +345,24 @@ let
         '';
       };
 
-
-in {
-
-  # !!! `parted mkpart' seems to silently create overlapping partitions.
-
-
   # The (almost) simplest partitioning scheme: a swap partition and
   # one big filesystem partition.
-  simple = makeInstallerTest "simple"
-    { createPartitions =
-        ''
-          $machine->succeed(
-              "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
-              . " mkpart primary linux-swap 1M 1024M"
-              . " mkpart primary ext2 1024M -1s",
-              "udevadm settle",
-              "mkswap /dev/vda1 -L swap",
-              "swapon -L swap",
-              "mkfs.ext3 -L nixos /dev/vda2",
-              "mount LABEL=nixos /mnt",
-          );
-        '';
-    };
-
-  # Simple GPT/UEFI configuration using systemd-boot with 3 partitions: ESP, swap & root filesystem
-  simpleUefiSystemdBoot = makeInstallerTest "simpleUefiSystemdBoot"
+  simple-test-config = { createPartitions =
+       ''
+         $machine->succeed(
+             "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
+             . " mkpart primary linux-swap 1M 1024M"
+             . " mkpart primary ext2 1024M -1s",
+             "udevadm settle",
+             "mkswap /dev/vda1 -L swap",
+             "swapon -L swap",
+             "mkfs.ext3 -L nixos /dev/vda2",
+             "mount LABEL=nixos /mnt",
+         );
+       '';
+   };
+
+  simple-uefi-grub-config =
     { createPartitions =
         ''
           $machine->succeed(
@@ -348,10 +381,45 @@ in {
               "mount LABEL=BOOT /mnt/boot",
           );
         '';
-        bootLoader = "systemd-boot";
+        bootLoader = "grub";
+        grubUseEfi = true;
     };
 
-  simpleUefiGrub = makeInstallerTest "simpleUefiGrub"
+  clone-test-extraconfig = { extraConfig =
+         ''
+         environment.systemPackages = [ pkgs.grub2 ];
+         boot.loader.grub.configurationName = "Home";
+         nesting.clone = [
+         {
+           boot.loader.grub.configurationName = lib.mkForce "Work";
+
+           environment.etc = {
+             "gitconfig".text = "
+               [core]
+                 gitproxy = none for work.com
+                 ";
+           };
+         }
+         ];
+         '';
+       testCloneConfig = true;
+  };
+
+
+in {
+
+  # !!! `parted mkpart' seems to silently create overlapping partitions.
+
+
+  # The (almost) simplest partitioning scheme: a swap partition and
+  # one big filesystem partition.
+  simple = makeInstallerTest "simple" simple-test-config;
+
+  # Test cloned configurations with the simple grub configuration
+  simpleClone = makeInstallerTest "simpleClone" (simple-test-config // clone-test-extraconfig);
+
+  # Simple GPT/UEFI configuration using systemd-boot with 3 partitions: ESP, swap & root filesystem
+  simpleUefiSystemdBoot = makeInstallerTest "simpleUefiSystemdBoot"
     { createPartitions =
         ''
           $machine->succeed(
@@ -370,10 +438,14 @@ in {
               "mount LABEL=BOOT /mnt/boot",
           );
         '';
-        bootLoader = "grub";
-        grubUseEfi = true;
+        bootLoader = "systemd-boot";
     };
 
+  simpleUefiGrub = makeInstallerTest "simpleUefiGrub" simple-uefi-grub-config;
+
+  # Test cloned configurations with the uefi grub configuration
+  simpleUefiGrubClone = makeInstallerTest "simpleUefiGrubClone" (simple-uefi-grub-config // clone-test-extraconfig);
+
   # Same as the previous, but now with a separate /boot partition.
   separateBoot = makeInstallerTest "separateBoot"
     { createPartitions =
diff --git a/nixpkgs/nixos/tests/ipv6.nix b/nixpkgs/nixos/tests/ipv6.nix
index 14f24c29cfe2..d11eba764da3 100644
--- a/nixpkgs/nixos/tests/ipv6.nix
+++ b/nixpkgs/nixos/tests/ipv6.nix
@@ -1,14 +1,16 @@
 # Test of IPv6 functionality in NixOS, including whether router
 # solicication/advertisement using radvd works.
 
-import ./make-test.nix ({ pkgs, ...} : {
+import ./make-test.nix ({ pkgs, lib, ...} : {
   name = "ipv6";
   meta = with pkgs.stdenv.lib.maintainers; {
     maintainers = [ eelco ];
   };
 
   nodes =
-    { client = { ... }: { };
+    # Remove the interface configuration provided by makeTest so that the
+    # interfaces are all configured implicitly
+    { client = { ... }: { networking.interfaces = lib.mkForce {}; };
 
       server =
         { ... }:
@@ -73,6 +75,11 @@ import ./make-test.nix ({ pkgs, ...} : {
           $client->succeed("curl --fail -g http://[$serverIp]");
           $client->fail("curl --fail -g http://[$clientIp]");
       };
+      subtest "privacy extensions", sub {
+          my $ip = waitForAddress $client, "eth1", "global temporary";
+          # Default route should have "src <temporary address>" in it
+          $client->succeed("ip r g ::2 | grep $ip");
+      };
 
       # TODO: test reachability of a machine on another network.
     '';
diff --git a/nixpkgs/nixos/tests/lightdm.nix b/nixpkgs/nixos/tests/lightdm.nix
index 8a9a7408d292..c805f1ed9f3c 100644
--- a/nixpkgs/nixos/tests/lightdm.nix
+++ b/nixpkgs/nixos/tests/lightdm.nix
@@ -1,7 +1,7 @@
 import ./make-test.nix ({ pkgs, ...} : {
   name = "lightdm";
   meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ aszlig ];
+    maintainers = [ aszlig worldofpeace ];
   };
 
   machine = { ... }: {
diff --git a/nixpkgs/nixos/tests/mediawiki.nix b/nixpkgs/nixos/tests/mediawiki.nix
new file mode 100644
index 000000000000..6293e8a2f465
--- /dev/null
+++ b/nixpkgs/nixos/tests/mediawiki.nix
@@ -0,0 +1,19 @@
+import ./make-test.nix ({ pkgs, lib, ... }: {
+  name = "mediawiki";
+  meta.maintainers = [ lib.maintainers.aanderse ];
+
+  machine =
+    { ... }:
+    { services.mediawiki.enable = true;
+      services.mediawiki.virtualHost.hostName = "localhost";
+      services.mediawiki.virtualHost.adminAddr = "root@example.com";
+      services.mediawiki.passwordFile = pkgs.writeText "password" "correcthorsebatterystaple";
+    };
+
+  testScript = ''
+    startAll;
+
+    $machine->waitForUnit('phpfpm-mediawiki.service');
+    $machine->succeed('curl -L http://localhost/') =~ /MediaWiki has been installed/ or die;
+  '';
+})
diff --git a/nixpkgs/nixos/tests/mysql.nix b/nixpkgs/nixos/tests/mysql.nix
index abf9606aa366..05bd968de02d 100644
--- a/nixpkgs/nixos/tests/mysql.nix
+++ b/nixpkgs/nixos/tests/mysql.nix
@@ -19,7 +19,7 @@ import ./make-test.nix ({ pkgs, ...} : {
         services.mysql.initialScript = pkgs.writeText "mysql-init.sql" ''
           CREATE USER 'passworduser'@'localhost' IDENTIFIED BY 'password123';
         '';
-        services.mysql.package = pkgs.mysql;
+        services.mysql.package = pkgs.mysql57;
       };
 
     mariadb =
diff --git a/nixpkgs/nixos/tests/networking.nix b/nixpkgs/nixos/tests/networking.nix
index ed9f287d5582..949d946bdc4c 100644
--- a/nixpkgs/nixos/tests/networking.nix
+++ b/nixpkgs/nixos/tests/networking.nix
@@ -510,7 +510,7 @@ let
           '';
         };
       };
-      nodes.client = { pkgs, ... }: with pkgs.lib; {
+      nodes.clientWithPrivacy = { pkgs, ... }: with pkgs.lib; {
         virtualisation.vlans = [ 1 ];
         networking = {
           useNetworkd = networkd;
@@ -522,21 +522,39 @@ let
           };
         };
       };
+      nodes.client = { pkgs, ... }: with pkgs.lib; {
+        virtualisation.vlans = [ 1 ];
+        networking = {
+          useNetworkd = networkd;
+          useDHCP = true;
+          interfaces.eth1 = {
+            preferTempAddress = false;
+            ipv4.addresses = mkOverride 0 [ ];
+            ipv6.addresses = mkOverride 0 [ ];
+          };
+        };
+      };
       testScript = { ... }:
         ''
           startAll;
 
           $client->waitForUnit("network.target");
+          $clientWithPrivacy->waitForUnit("network.target");
           $router->waitForUnit("network-online.target");
 
           # Wait until we have an ip address
+          $clientWithPrivacy->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'");
           $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'");
 
           # Test vlan 1
+          $clientWithPrivacy->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1");
           $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1");
 
           # Test address used is temporary
-          $client->waitUntilSucceeds("! ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'");
+          $clientWithPrivacy->waitUntilSucceeds("! ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'");
+
+          # Test address used is EUI-64
+          $client->waitUntilSucceeds("ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'");
         '';
     };
     routes = {
diff --git a/nixpkgs/nixos/tests/nextcloud/with-postgresql-and-redis.nix b/nixpkgs/nixos/tests/nextcloud/with-postgresql-and-redis.nix
index 0351d4db69ac..8a840a608753 100644
--- a/nixpkgs/nixos/tests/nextcloud/with-postgresql-and-redis.nix
+++ b/nixpkgs/nixos/tests/nextcloud/with-postgresql-and-redis.nix
@@ -27,10 +27,7 @@ in {
           dbtype = "pgsql";
           dbname = "nextcloud";
           dbuser = "nextcloud";
-          dbhost = "localhost";
-          dbpassFile = toString (pkgs.writeText "db-pass-file" ''
-            hunter2
-          '');
+          dbhost = "/run/postgresql";
           inherit adminuser;
           adminpassFile = toString (pkgs.writeText "admin-pass-file" ''
             ${adminpass}
@@ -84,10 +81,12 @@ in {
 
       services.postgresql = {
         enable = true;
-        initialScript = pkgs.writeText "psql-init" ''
-          create role nextcloud with login password 'hunter2';
-          create database nextcloud with owner nextcloud;
-        '';
+        ensureDatabases = [ "nextcloud" ];
+        ensureUsers = [
+          { name = "nextcloud";
+            ensurePermissions."DATABASE nextcloud" = "ALL PRIVILEGES";
+          }
+        ];
       };
     };
   };
diff --git a/nixpkgs/nixos/tests/nixos-generate-config.nix b/nixpkgs/nixos/tests/nixos-generate-config.nix
new file mode 100644
index 000000000000..15a173e024b4
--- /dev/null
+++ b/nixpkgs/nixos/tests/nixos-generate-config.nix
@@ -0,0 +1,24 @@
+import ./make-test.nix ({ lib, ... } : {
+  name = "nixos-generate-config";
+  meta.maintainers = with lib.maintainers; [ basvandijk ];
+  machine = {
+    system.nixos-generate-config.configuration = ''
+      # OVERRIDDEN
+      { config, pkgs, ... }: {
+        imports = [ ./hardware-configuration.nix ];
+      $bootLoaderConfig
+      }
+    '';
+  };
+  testScript = ''
+    startAll;
+    $machine->waitForUnit("multi-user.target");
+    $machine->succeed("nixos-generate-config");
+
+    # Test if the configuration really is overridden
+    $machine->succeed("grep 'OVERRIDDEN' /etc/nixos/configuration.nix");
+
+    # Test of if the Perl variable $bootLoaderConfig is spliced correctly:
+    $machine->succeed("grep 'boot\\.loader\\.grub\\.enable = true;' /etc/nixos/configuration.nix");
+  '';
+})
diff --git a/nixpkgs/nixos/tests/ostree.nix b/nixpkgs/nixos/tests/ostree.nix
index 8b19004874e7..d7ad84a1a5f0 100644
--- a/nixpkgs/nixos/tests/ostree.nix
+++ b/nixpkgs/nixos/tests/ostree.nix
@@ -12,7 +12,7 @@ import ./make-test.nix ({ pkgs, lib, ... }: {
       gnome-desktop-testing ostree gnupg (python3.withPackages (p: with p; [ pyyaml ]))
     ];
 
-    environment.variables.GI_TYPELIB_PATH = lib.makeSearchPath "lib/girepository-1.0" (with pkgs; [ gtk3 pango.out ostree gdk_pixbuf atk ]); # for GJS tests
+    environment.variables.GI_TYPELIB_PATH = lib.makeSearchPath "lib/girepository-1.0" (with pkgs; [ gtk3 pango.out ostree gdk-pixbuf atk ]); # for GJS tests
   };
 
   testScript = ''
diff --git a/nixpkgs/nixos/tests/postgresql-wal-receiver.nix b/nixpkgs/nixos/tests/postgresql-wal-receiver.nix
new file mode 100644
index 000000000000..791b041ba95b
--- /dev/null
+++ b/nixpkgs/nixos/tests/postgresql-wal-receiver.nix
@@ -0,0 +1,86 @@
+{ system ? builtins.currentSystem
+, config ? { }
+, pkgs ? import ../.. { inherit system config; } }:
+
+with import ../lib/testing.nix { inherit system pkgs; };
+with pkgs.lib;
+
+let
+  postgresqlDataDir = "/var/db/postgresql/test";
+  replicationUser = "wal_receiver_user";
+  replicationSlot = "wal_receiver_slot";
+  replicationConn = "postgresql://${replicationUser}@localhost";
+  baseBackupDir = "/tmp/pg_basebackup";
+  walBackupDir = "/tmp/pg_wal";
+  recoveryConf = pkgs.writeText "recovery.conf" ''
+    restore_command = 'cp ${walBackupDir}/%f %p'
+  '';
+
+  makePostgresqlWalReceiverTest = subTestName: postgresqlPackage: makeTest {
+    name = "postgresql-wal-receiver-${subTestName}";
+    meta.maintainers = with maintainers; [ pacien ];
+
+    machine = { ... }: {
+      services.postgresql = {
+        package = postgresqlPackage;
+        enable = true;
+        dataDir = postgresqlDataDir;
+        extraConfig = ''
+          wal_level = archive # alias for replica on pg >= 9.6
+          max_wal_senders = 10
+          max_replication_slots = 10
+        '';
+        authentication = ''
+          host replication ${replicationUser} all trust
+        '';
+        initialScript = pkgs.writeText "init.sql" ''
+          create user ${replicationUser} replication;
+          select * from pg_create_physical_replication_slot('${replicationSlot}');
+        '';
+      };
+
+      services.postgresqlWalReceiver.receivers.main = {
+        inherit postgresqlPackage;
+        connection = replicationConn;
+        slot = replicationSlot;
+        directory = walBackupDir;
+      };
+    };
+
+    testScript = ''
+      # make an initial base backup
+      $machine->waitForUnit('postgresql');
+      $machine->waitForUnit('postgresql-wal-receiver-main');
+      # WAL receiver healthchecks PG every 5 seconds, so let's be sure they have connected each other
+      # required only for 9.4
+      $machine->sleep(5);
+      $machine->succeed('${postgresqlPackage}/bin/pg_basebackup --dbname=${replicationConn} --pgdata=${baseBackupDir}');
+
+      # create a dummy table with 100 records
+      $machine->succeed('sudo -u postgres psql --command="create table dummy as select * from generate_series(1, 100) as val;"');
+
+      # stop postgres and destroy data
+      $machine->systemctl('stop postgresql');
+      $machine->systemctl('stop postgresql-wal-receiver-main');
+      $machine->succeed('rm -r ${postgresqlDataDir}/{base,global,pg_*}');
+
+      # restore the base backup
+      $machine->succeed('cp -r ${baseBackupDir}/* ${postgresqlDataDir} && chown postgres:postgres -R ${postgresqlDataDir}');
+
+      # prepare WAL and recovery
+      $machine->succeed('chmod a+rX -R ${walBackupDir}');
+      $machine->execute('for part in ${walBackupDir}/*.partial; do mv $part ''${part%%.*}; done'); # make use of partial segments too
+      $machine->succeed('cp ${recoveryConf} ${postgresqlDataDir}/recovery.conf && chmod 666 ${postgresqlDataDir}/recovery.conf');
+
+      # replay WAL
+      $machine->systemctl('start postgresql');
+      $machine->waitForFile('${postgresqlDataDir}/recovery.done');
+      $machine->systemctl('restart postgresql');
+      $machine->waitForUnit('postgresql');
+
+      # check that our records have been restored
+      $machine->succeed('test $(sudo -u postgres psql --pset="pager=off" --tuples-only --command="select count(distinct val) from dummy;") -eq 100');
+    '';
+  };
+
+in mapAttrs makePostgresqlWalReceiverTest (import ../../pkgs/servers/sql/postgresql pkgs)
diff --git a/nixpkgs/nixos/tests/prometheus-2.nix b/nixpkgs/nixos/tests/prometheus-2.nix
index d7035d49ad4d..219c47c73d95 100644
--- a/nixpkgs/nixos/tests/prometheus-2.nix
+++ b/nixpkgs/nixos/tests/prometheus-2.nix
@@ -1,9 +1,44 @@
-import ./make-test.nix {
+let
+  grpcPort   = 19090;
+  queryPort  =  9090;
+  minioPort  =  9000;
+  pushgwPort =  9091;
+
+  s3 = {
+    accessKey = "BKIKJAA5BMMU2RHO6IBB";
+    secretKey = "V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12";
+  };
+
+  objstore.config = {
+    type = "S3";
+    config = {
+      bucket = "thanos-bucket";
+      endpoint = "s3:${toString minioPort}";
+      region =  "us-east-1";
+      access_key = s3.accessKey;
+      secret_key = s3.secretKey;
+      insecure = true;
+      signature_version2 = false;
+      encrypt_sse =  false;
+      put_user_metadata = {};
+      http_config = {
+        idle_conn_timeout = "0s";
+        insecure_skip_verify = false;
+      };
+      trace = {
+        enable = false;
+      };
+    };
+  };
+
+in import ./make-test.nix {
   name = "prometheus-2";
 
   nodes = {
-    one = { pkgs, ... }: {
+    prometheus = { pkgs, ... }: {
+      virtualisation.diskSize = 2 * 1024;
       environment.systemPackages = [ pkgs.jq ];
+      networking.firewall.allowedTCPPorts = [ grpcPort ];
       services.prometheus2 = {
         enable = true;
         scrapeConfigs = [
@@ -11,7 +46,7 @@ import ./make-test.nix {
             job_name = "prometheus";
             static_configs = [
               {
-                targets = [ "127.0.0.1:9090" ];
+                targets = [ "127.0.0.1:${toString queryPort}" ];
                 labels = { instance = "localhost"; };
               }
             ];
@@ -21,7 +56,7 @@ import ./make-test.nix {
             scrape_interval = "1s";
             static_configs = [
               {
-                targets = [ "127.0.0.1:9091" ];
+                targets = [ "127.0.0.1:${toString pushgwPort}" ];
               }
             ];
           }
@@ -35,33 +70,170 @@ import ./make-test.nix {
                     expr: count(up{job="prometheus"})
           ''
         ];
+        globalConfig = {
+          external_labels = {
+            some_label = "required by thanos";
+          };
+        };
+        extraFlags = [
+          # Required by thanos
+          "--storage.tsdb.min-block-duration=5s"
+          "--storage.tsdb.max-block-duration=5s"
+        ];
       };
       services.prometheus.pushgateway = {
         enable = true;
+        web.listen-address = ":${toString pushgwPort}";
         persistMetrics = true;
         persistence.interval = "1s";
         stateDir = "prometheus-pushgateway";
       };
+      services.thanos = {
+        sidecar = {
+          enable = true;
+          grpc-address = "0.0.0.0:${toString grpcPort}";
+          inherit objstore;
+        };
+
+        # TODO: Add some tests for these services:
+        #rule = {
+        #  enable = true;
+        #  http-address = "0.0.0.0:19194";
+        #  grpc-address = "0.0.0.0:19193";
+        #  query.addresses = [
+        #    "localhost:19191"
+        #  ];
+        #  labels = {
+        #    just = "some";
+        #    nice = "labels";
+        #  };
+        #};
+        #
+        #receive = {
+        #  http-address = "0.0.0.0:19195";
+        #  enable = true;
+        #  labels = {
+        #    just = "some";
+        #    nice = "labels";
+        #  };
+        #};
+      };
+    };
+
+    query = { pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+      services.thanos.query = {
+        enable = true;
+        http-address = "0.0.0.0:${toString queryPort}";
+        store.addresses = [
+          "prometheus:${toString grpcPort}"
+        ];
+      };
+    };
+
+    store = { pkgs, ... }: {
+      virtualisation.diskSize = 2 * 1024;
+      environment.systemPackages = with pkgs; [ jq thanos ];
+      services.thanos.store = {
+        enable = true;
+        http-address = "0.0.0.0:10902";
+        grpc-address = "0.0.0.0:${toString grpcPort}";
+        inherit objstore;
+        sync-block-duration = "1s";
+      };
+      services.thanos.compact = {
+        enable = true;
+        http-address = "0.0.0.0:10903";
+        inherit objstore;
+        consistency-delay = "5s";
+      };
+      services.thanos.query = {
+        enable = true;
+        http-address = "0.0.0.0:${toString queryPort}";
+        store.addresses = [
+          "localhost:${toString grpcPort}"
+        ];
+      };
+    };
+
+    s3 = { pkgs, ... } : {
+      # Minio requires at least 1GiB of free disk space to run.
+      virtualisation.diskSize = 2 * 1024;
+      networking.firewall.allowedTCPPorts = [ minioPort ];
+
+      services.minio = {
+        enable = true;
+        inherit (s3) accessKey secretKey;
+      };
+
+      environment.systemPackages = [ pkgs.minio-client ];
     };
   };
 
-  testScript = ''
-    startAll;
-    $one->waitForUnit("prometheus2.service");
-    $one->waitForOpenPort(9090);
-    $one->succeed("curl -s http://127.0.0.1:9090/metrics");
+  testScript = { nodes, ... } : ''
+    # Before starting the other machines we first make sure that our S3 service is online
+    # and has a bucket added for thanos:
+    $s3->start;
+    $s3->waitForUnit("minio.service");
+    $s3->waitForOpenPort(${toString minioPort});
+    $s3->succeed(
+      "mc config host add minio " .
+      "http://localhost:${toString minioPort} ${s3.accessKey} ${s3.secretKey} S3v4");
+    $s3->succeed("mc mb minio/thanos-bucket");
+
+    # Now that s3 has started we can start the other machines:
+    $prometheus->start;
+    $query->start;
+    $store->start;
 
-    # Let's test if pushing a metric to the pushgateway succeeds
-    # and whether that metric gets ingested by prometheus.
-    $one->waitForUnit("pushgateway.service");
-    $one->succeed(
+    # Check if prometheus responds to requests:
+    $prometheus->waitForUnit("prometheus2.service");
+    $prometheus->waitForOpenPort(${toString queryPort});
+    $prometheus->succeed("curl -s http://127.0.0.1:${toString queryPort}/metrics");
+
+    # Let's test if pushing a metric to the pushgateway succeeds:
+    $prometheus->waitForUnit("pushgateway.service");
+    $prometheus->succeed(
       "echo 'some_metric 3.14' | " .
-      "curl --data-binary \@- http://127.0.0.1:9091/metrics/job/some_job");
-    $one->waitUntilSucceeds(
-      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=some_metric' " .
-      "| jq '.data.result[0].value[1]' | grep '\"3.14\"'");
+      "curl --data-binary \@- http://127.0.0.1:${toString pushgwPort}/metrics/job/some_job");
+
+    # Now check whether that metric gets ingested by prometheus.
+    # Since we'll check for the metric several times on different machines
+    # we abstract the test using the following function:
+
+    # Function to check if the metric "some_metric" has been received and returns the correct value.
+    local *Machine::waitForMetric = sub {
+      my ($self) = @_;
+      $self->waitUntilSucceeds(
+        "curl -sf 'http://127.0.0.1:${toString queryPort}/api/v1/query?query=some_metric' " .
+        "| jq '.data.result[0].value[1]' | grep '\"3.14\"'");
+    };
+
+    $prometheus->waitForMetric;
 
     # Let's test if the pushgateway persists metrics to the configured location.
-    $one->waitUntilSucceeds("test -e /var/lib/prometheus-pushgateway/metrics");
+    $prometheus->waitUntilSucceeds("test -e /var/lib/prometheus-pushgateway/metrics");
+
+    # Test thanos
+    $prometheus->waitForUnit("thanos-sidecar.service");
+
+    # Test if the Thanos query service can correctly retrieve the metric that was send above.
+    $query->waitForUnit("thanos-query.service");
+    $query->waitForMetric;
+
+    # Test if the Thanos sidecar has correctly uploaded its TSDB to S3, if the
+    # Thanos storage service has correctly downloaded it from S3 and if the Thanos
+    # query service running on $store can correctly retrieve the metric:
+    $store->waitForUnit("thanos-store.service");
+    $store->waitForMetric;
+
+    $store->waitForUnit("thanos-compact.service");
+
+    # Test if the Thanos bucket command is able to retrieve blocks from the S3 bucket
+    # and check if the blocks have the correct labels:
+    $store->succeed(
+      "thanos bucket ls" .
+      " --objstore.config-file=${nodes.store.config.services.thanos.store.objstore.config-file}" .
+      " --output=json | jq .thanos.labels.some_label | grep 'required by thanos'");
   '';
 }
diff --git a/nixpkgs/nixos/tests/prometheus-exporters.nix b/nixpkgs/nixos/tests/prometheus-exporters.nix
index 90c7c9701f60..02d83f82f338 100644
--- a/nixpkgs/nixos/tests/prometheus-exporters.nix
+++ b/nixpkgs/nixos/tests/prometheus-exporters.nix
@@ -3,10 +3,11 @@
 , pkgs ? import ../.. { inherit system config; }
 }:
 
-with pkgs.lib;
-with import ../lib/testing.nix { inherit system pkgs; };
-
 let
+  inherit (import ../lib/testing.nix { inherit system pkgs; }) makeTest;
+  inherit (pkgs.lib) concatStringsSep maintainers mapAttrs mkMerge
+                     removeSuffix replaceChars singleton splitString;
+
   escape' = str: replaceChars [''"'' "$" "\n"] [''\\\"'' "\\$" ""] str;
 
 /*
@@ -73,7 +74,7 @@ let
       exporterTest = ''
         waitForUnit("prometheus-bind-exporter.service");
         waitForOpenPort(9119);
-        succeed("curl -sSf http://localhost:9119/metrics" | grep -q 'bind_query_recursions_total 0');
+        succeed("curl -sSf http://localhost:9119/metrics | grep -q 'bind_query_recursions_total 0'");
       '';
     };
 
@@ -187,6 +188,47 @@ let
       '';
     };
 
+    mail = {
+      exporterConfig = {
+        enable = true;
+        configuration = {
+          monitoringInterval = "2s";
+          mailCheckTimeout = "10s";
+          servers = [ {
+            name = "testserver";
+            server = "localhost";
+            port = 25;
+            from = "mail-exporter@localhost";
+            to = "mail-exporter@localhost";
+            detectionDir = "/var/spool/mail/mail-exporter/new";
+          } ];
+        };
+      };
+      metricProvider = {
+        services.postfix.enable = true;
+        systemd.services.prometheus-mail-exporter = {
+          after = [ "postfix.service" ];
+          requires = [ "postfix.service" ];
+          preStart = ''
+            mkdir -p 0600 mail-exporter/new
+          '';
+          serviceConfig = {
+            ProtectHome = true;
+            ReadOnlyPaths = "/";
+            ReadWritePaths = "/var/spool/mail";
+            WorkingDirectory = "/var/spool/mail";
+          };
+        };
+        users.users.mailexporter.isSystemUser = true;
+      };
+      exporterTest = ''
+        waitForUnit("postfix.service")
+        waitForUnit("prometheus-mail-exporter.service")
+        waitForOpenPort(9225)
+        waitUntilSucceeds("curl -sSf http://localhost:9225/metrics | grep -q 'mail_deliver_success{configname=\"testserver\"} 1'")
+      '';
+    };
+
     nginx = {
       exporterConfig = {
         enable = true;
@@ -231,6 +273,30 @@ let
       '';
     };
 
+    postgres = {
+      exporterConfig = {
+        enable = true;
+        runAsLocalSuperUser = true;
+      };
+      metricProvider = {
+        services.postgresql.enable = true;
+      };
+      exporterTest = ''
+        waitForUnit("prometheus-postgres-exporter.service");
+        waitForOpenPort(9187);
+        waitForUnit("postgresql.service");
+        succeed("curl -sSf http://localhost:9187/metrics | grep -q 'pg_exporter_last_scrape_error 0'");
+        succeed("curl -sSf http://localhost:9187/metrics | grep -q 'pg_up 1'");
+        systemctl("stop postgresql.service");
+        succeed("curl -sSf http://localhost:9187/metrics | grep -qv 'pg_exporter_last_scrape_error 0'");
+        succeed("curl -sSf http://localhost:9187/metrics | grep -q 'pg_up 0'");
+        systemctl("start postgresql.service");
+        waitForUnit("postgresql.service");
+        succeed("curl -sSf http://localhost:9187/metrics | grep -q 'pg_exporter_last_scrape_error 0'");
+        succeed("curl -sSf http://localhost:9187/metrics | grep -q 'pg_up 1'");
+      '';
+    };
+
     snmp = {
       exporterConfig = {
         enable = true;
@@ -311,6 +377,7 @@ let
       };
       exporterTest = ''
         waitForUnit("prometheus-varnish-exporter.service");
+        waitForOpenPort(6081);
         waitForOpenPort(9131);
         succeed("curl -sSf http://localhost:9131/metrics | grep -q 'varnish_up 1'");
       '';
@@ -331,11 +398,12 @@ let
             inherit (snakeoil.peer1) publicKey;
           };
         };
+        systemd.services.prometheus-wireguard-exporter.after = [ "wireguard-wg0.service" ];
       };
       exporterTest = ''
         waitForUnit("prometheus-wireguard-exporter.service");
         waitForOpenPort(9586);
-        succeed("curl -sSf http://localhost:9586/metrics | grep '${snakeoil.peer1.publicKey}'");
+        waitUntilSucceeds("curl -sSf http://localhost:9586/metrics | grep '${snakeoil.peer1.publicKey}'");
       '';
     };
   };
diff --git a/nixpkgs/nixos/tests/redmine.nix b/nixpkgs/nixos/tests/redmine.nix
index cbdb5c8d2954..2d4df288b055 100644
--- a/nixpkgs/nixos/tests/redmine.nix
+++ b/nixpkgs/nixos/tests/redmine.nix
@@ -10,19 +10,9 @@ let
   mysqlTest = package: makeTest {
     machine =
       { config, pkgs, ... }:
-      { services.mysql.enable = true;
-        services.mysql.package = pkgs.mariadb;
-        services.mysql.ensureDatabases = [ "redmine" ];
-        services.mysql.ensureUsers = [
-          { name = "redmine";
-            ensurePermissions = { "redmine.*" = "ALL PRIVILEGES"; };
-          }
-        ];
-
-        services.redmine.enable = true;
+      { services.redmine.enable = true;
         services.redmine.package = package;
         services.redmine.database.type = "mysql2";
-        services.redmine.database.socket = "/run/mysqld/mysqld.sock";
         services.redmine.plugins = {
           redmine_env_auth = pkgs.fetchurl {
             url = https://github.com/Intera/redmine_env_auth/archive/0.7.zip;
@@ -48,19 +38,9 @@ let
   pgsqlTest = package: makeTest {
     machine =
       { config, pkgs, ... }:
-      { services.postgresql.enable = true;
-        services.postgresql.ensureDatabases = [ "redmine" ];
-        services.postgresql.ensureUsers = [
-          { name = "redmine";
-            ensurePermissions = { "DATABASE redmine" = "ALL PRIVILEGES"; };
-          }
-        ];
-
-        services.redmine.enable = true;
+      { services.redmine.enable = true;
         services.redmine.package = package;
         services.redmine.database.type = "postgresql";
-        services.redmine.database.host = "";
-        services.redmine.database.port = 5432;
         services.redmine.plugins = {
           redmine_env_auth = pkgs.fetchurl {
             url = https://github.com/Intera/redmine_env_auth/archive/0.7.zip;
diff --git a/nixpkgs/nixos/tests/syncthing-init.nix b/nixpkgs/nixos/tests/syncthing-init.nix
index 811a466ff941..0de76b688bdc 100644
--- a/nixpkgs/nixos/tests/syncthing-init.nix
+++ b/nixpkgs/nixos/tests/syncthing-init.nix
@@ -22,9 +22,13 @@ in {
   };
 
   testScript = ''
+    my $config;
+
     $machine->waitForUnit("syncthing-init.service");
-    $machine->succeed("cat /var/lib/syncthing/config.xml") =~ /${testId}/ or die;
-    $machine->succeed("cat /var/lib/syncthing/config.xml") =~ /testFolder/ or die;
+    $config = $machine->succeed("cat /var/lib/syncthing/.config/syncthing/config.xml");
+   
+    $config =~ /${testId}/ or die;
+    $config =~ /testFolder/ or die;
   '';
 })
 
diff --git a/nixpkgs/nixos/tests/tiddlywiki.nix b/nixpkgs/nixos/tests/tiddlywiki.nix
new file mode 100644
index 000000000000..4a2014a4ec91
--- /dev/null
+++ b/nixpkgs/nixos/tests/tiddlywiki.nix
@@ -0,0 +1,67 @@
+import ./make-test.nix ({ ... }: {
+  name = "tiddlywiki";
+  nodes = {
+    default = {
+      services.tiddlywiki.enable = true;
+    };
+    configured = {
+      boot.postBootCommands = ''
+        echo "username,password
+        somelogin,somesecret" > /var/lib/wikiusers.csv
+      '';
+      services.tiddlywiki = {
+        enable = true;
+        listenOptions = {
+          port = 3000;
+          credentials="../wikiusers.csv";
+          readers="(authenticated)";
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    startAll;
+
+    subtest "by default works without configuration", sub {
+      $default->waitForUnit("tiddlywiki.service");
+    };
+
+    subtest "by default available on port 8080 without auth", sub {
+      $default->waitForUnit("tiddlywiki.service");
+      $default->waitForOpenPort(8080);
+      $default->succeed("curl --fail 127.0.0.1:8080");
+    };
+
+    subtest "by default creates empty wiki", sub {
+      $default->succeed("test -f /var/lib/tiddlywiki/tiddlywiki.info");
+    };
+
+    subtest "configured on port 3000 with basic auth", sub {
+      $configured->waitForUnit("tiddlywiki.service");
+      $configured->waitForOpenPort(3000);
+      $configured->fail("curl --fail 127.0.0.1:3000");
+      $configured->succeed("curl --fail 127.0.0.1:3000 --user somelogin:somesecret");
+    };
+
+    subtest "configured with different wikifolder", sub {
+      $configured->succeed("test -f /var/lib/tiddlywiki/tiddlywiki.info");
+    };
+
+    subtest "restart preserves changes", sub {
+      # given running wiki
+      $default->waitForUnit("tiddlywiki.service");
+      # with some changes
+      $default->succeed("curl --fail --request PUT --header 'X-Requested-With:TiddlyWiki' --data '{ \"title\": \"title\", \"text\": \"content\" }' --url 127.0.0.1:8080/recipes/default/tiddlers/somepage ");
+      $default->succeed("sleep 2"); # server syncs to filesystem on timer
+
+      # when wiki is cycled
+      $default->systemctl("restart tiddlywiki.service");
+      $default->waitForUnit("tiddlywiki.service");
+      $default->waitForOpenPort(8080);
+
+      # the change is preserved
+      $default->succeed("curl --fail 127.0.0.1:8080/recipes/default/tiddlers/somepage");
+    };
+  '';
+})
diff --git a/nixpkgs/nixos/tests/tomcat.nix b/nixpkgs/nixos/tests/tomcat.nix
deleted file mode 100644
index 8e7b886dd302..000000000000
--- a/nixpkgs/nixos/tests/tomcat.nix
+++ /dev/null
@@ -1,30 +0,0 @@
-import ./make-test.nix ({ pkgs, ...} : {
-  name = "tomcat";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ eelco ];
-  };
-
-  nodes = {
-    server =
-      { ... }:
-
-      { services.tomcat.enable = true;
-        services.httpd.enable = true;
-        services.httpd.adminAddr = "foo@bar.com";
-        services.httpd.extraSubservices =
-          [ { serviceType = "tomcat-connector"; } ];
-        networking.firewall.allowedTCPPorts = [ 80 ];
-      };
-
-    client = { };
-  };
-
-  testScript = ''
-    startAll;
-
-    $server->waitForUnit("tomcat");
-    $client->waitForUnit("network.target");
-    $client->waitUntilSucceeds("curl --fail http://server/examples/servlets/servlet/HelloWorldExample");
-    $client->waitUntilSucceeds("curl --fail http://server/examples/jsp/jsp2/simpletag/hello.jsp");
-  '';
-})
diff --git a/nixpkgs/nixos/tests/trac.nix b/nixpkgs/nixos/tests/trac.nix
deleted file mode 100644
index 8ec11ebda2cf..000000000000
--- a/nixpkgs/nixos/tests/trac.nix
+++ /dev/null
@@ -1,74 +0,0 @@
-import ./make-test.nix ({ pkgs, ... }: {
-  name = "trac";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ eelco ];
-  };
-
-  nodes = {
-    storage =
-      { ... }:
-      { services.nfs.server.enable = true;
-        services.nfs.server.exports = ''
-          /repos 192.168.1.0/255.255.255.0(rw,no_root_squash)
-        '';
-        services.nfs.server.createMountPoints = true;
-      };
-
-    postgresql =
-      { pkgs, ... }:
-      { services.postgresql.enable = true;
-        services.postgresql.package = pkgs.postgresql;
-        services.postgresql.enableTCPIP = true;
-        services.postgresql.authentication = ''
-          # Generated file; do not edit!
-          local all all                trust
-          host  all all 127.0.0.1/32   trust
-          host  all all ::1/128        trust
-          host  all all 192.168.1.0/24 trust
-        '';
-      };
-
-    webserver =
-      { pkgs, ... }:
-      { fileSystems = pkgs.lib.mkVMOverride
-          [ { mountPoint = "/repos";
-              device = "storage:/repos";
-              fsType = "nfs";
-            }
-          ];
-        services.httpd.enable = true;
-        services.httpd.adminAddr = "root@localhost";
-        services.httpd.extraSubservices = [ { serviceType = "trac"; } ];
-        environment.systemPackages = [ pkgs.pythonPackages.trac pkgs.subversion ];
-      };
-
-    client =
-      { ... }:
-      { imports = [ ./common/x11.nix ];
-        services.xserver.desktopManager.plasma5.enable = true;
-      };
-  };
-
-  testScript =
-    ''
-      startAll;
-
-      $postgresql->waitForUnit("postgresql");
-      $postgresql->succeed("createdb trac");
-
-      $webserver->succeed("mkdir -p /repos/trac");
-      $webserver->succeed("svnadmin create /repos/trac");
-
-      $webserver->waitForUnit("httpd");
-      $webserver->waitForFile("/var/trac");
-      $webserver->succeed("mkdir -p /var/trac/projects/test");
-      $webserver->succeed("PYTHONPATH=${pkgs.pythonPackages.psycopg2}/lib/${pkgs.python.libPrefix}/site-packages trac-admin /var/trac/projects/test initenv Test postgres://root\@postgresql/trac svn /repos/trac");
-
-      $client->waitForX;
-      $client->execute("konqueror http://webserver/projects/test &");
-      $client->waitForWindow(qr/Test.*Konqueror/);
-      $client->sleep(30); # loading takes a long time
-
-      $client->screenshot("screen");
-    '';
-})