about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorParnell Springmeyer <parnell@digitalmentat.com>2018-02-13 17:28:45 -0600
committerParnell Springmeyer <parnell@digitalmentat.com>2018-02-13 17:28:45 -0600
commit0a603ee165c9481476fd9ff3da66573c115fdb73 (patch)
treed24cef418971c3a015dd39918091557c612fc575 /nixos
parente4ec980e9ced7dbea0fb1310f831207bbc7175b0 (diff)
parenta4621c0f684de162023afed671ecc4442ca8fb49 (diff)
Merge remote-tracking branch 'upstream/master' into parnell/fetchdocker
Diffstat (limited to 'nixos')
-rw-r--r--nixos/default.nix4
-rw-r--r--nixos/doc/manual/default.nix57
-rw-r--r--nixos/doc/manual/installation/changing-config.xml18
-rw-r--r--nixos/doc/manual/installation/installing-virtualbox-guest.xml10
-rw-r--r--nixos/doc/manual/installation/installing.xml5
-rw-r--r--nixos/doc/manual/options-to-docbook.xsl9
-rw-r--r--nixos/doc/manual/release-notes/rl-1803.xml69
-rw-r--r--nixos/lib/build-vms.nix2
-rw-r--r--nixos/lib/make-disk-image.nix82
-rw-r--r--nixos/lib/qemu-flags.nix11
-rw-r--r--nixos/lib/testing.nix4
-rw-r--r--nixos/maintainers/scripts/ec2/amazon-image.nix2
-rw-r--r--nixos/modules/config/power-management.nix3
-rw-r--r--nixos/modules/config/shells-environment.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-aarch64.nix10
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix8
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix1
-rw-r--r--nixos/modules/misc/ids.nix6
-rw-r--r--nixos/modules/misc/nixpkgs.nix60
-rw-r--r--nixos/modules/module-list.nix13
-rw-r--r--nixos/modules/profiles/clone-config.nix2
-rw-r--r--nixos/modules/programs/adb.nix1
-rw-r--r--nixos/modules/programs/less.nix118
-rw-r--r--nixos/modules/programs/shadow.nix10
-rw-r--r--nixos/modules/programs/tmux.nix7
-rw-r--r--nixos/modules/programs/yabar.nix149
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.nix16
-rw-r--r--nixos/modules/programs/zsh/zsh.nix5
-rw-r--r--nixos/modules/rename.nix4
-rw-r--r--nixos/modules/security/acme.nix22
-rw-r--r--nixos/modules/security/pam.nix21
-rw-r--r--nixos/modules/security/sudo.nix129
-rw-r--r--nixos/modules/services/computing/slurm/slurm.nix31
-rw-r--r--nixos/modules/services/databases/mysql.nix4
-rw-r--r--nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix27
-rw-r--r--nixos/modules/services/hardware/80-net-setup-link.rules13
-rw-r--r--nixos/modules/services/hardware/acpid.nix32
-rw-r--r--nixos/modules/services/hardware/amd-hybrid-graphics.nix46
-rw-r--r--nixos/modules/services/hardware/nvidia-optimus.nix2
-rw-r--r--nixos/modules/services/hardware/udev.nix2
-rw-r--r--nixos/modules/services/mail/dovecot.nix4
-rw-r--r--nixos/modules/services/mail/postfix.nix139
-rw-r--r--nixos/modules/services/mail/rspamd.nix261
-rw-r--r--nixos/modules/services/misc/home-assistant.nix135
-rw-r--r--nixos/modules/services/misc/matrix-synapse.nix63
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix58
-rw-r--r--nixos/modules/services/misc/zookeeper.nix11
-rw-r--r--nixos/modules/services/monitoring/netdata.nix54
-rw-r--r--nixos/modules/services/monitoring/prometheus/alertmanager.nix10
-rw-r--r--nixos/modules/services/network-filesystems/openafs-client/default.nix99
-rw-r--r--nixos/modules/services/network-filesystems/openafs/client.nix239
-rw-r--r--nixos/modules/services/network-filesystems/openafs/lib.nix28
-rw-r--r--nixos/modules/services/network-filesystems/openafs/server.nix260
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix13
-rw-r--r--nixos/modules/services/networking/aria2.nix10
-rw-r--r--nixos/modules/services/networking/bird.nix23
-rw-r--r--nixos/modules/services/networking/dnscrypt-wrapper.nix10
-rw-r--r--nixos/modules/services/networking/kresd.nix29
-rw-r--r--nixos/modules/services/networking/monero.nix238
-rw-r--r--nixos/modules/services/networking/mosquitto.nix16
-rw-r--r--nixos/modules/services/networking/openvpn.nix28
-rw-r--r--nixos/modules/services/networking/resilio.nix2
-rw-r--r--nixos/modules/services/networking/rxe.nix63
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix15
-rw-r--r--nixos/modules/services/networking/stunnel.nix221
-rw-r--r--nixos/modules/services/search/elasticsearch.nix8
-rw-r--r--nixos/modules/services/security/physlock.nix64
-rw-r--r--nixos/modules/services/security/tor.nix30
-rw-r--r--nixos/modules/services/web-servers/mighttpd2.nix132
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix19
-rw-r--r--nixos/modules/services/web-servers/nginx/vhost-options.nix16
-rw-r--r--nixos/modules/services/web-servers/traefik.nix12
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix12
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix145
-rw-r--r--nixos/modules/services/x11/window-managers/2bwm.nix4
-rw-r--r--nixos/modules/services/x11/xserver.nix2
-rw-r--r--nixos/modules/system/boot/initrd-network.nix4
-rw-r--r--nixos/modules/system/boot/kernel.nix8
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix27
-rw-r--r--nixos/modules/tasks/network-interfaces-scripted.nix4
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix24
-rw-r--r--nixos/modules/tasks/network-interfaces.nix10
-rw-r--r--nixos/modules/testing/test-instrumentation.nix19
-rw-r--r--nixos/modules/virtualisation/container-config.nix2
-rw-r--r--nixos/modules/virtualisation/google-compute-image.nix2
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix1
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix16
-rw-r--r--nixos/modules/virtualisation/virtualbox-image.nix2
-rw-r--r--nixos/modules/virtualisation/xen-dom0.nix25
-rw-r--r--nixos/release-combined.nix9
-rw-r--r--nixos/release-small.nix3
-rw-r--r--nixos/release.nix89
-rw-r--r--nixos/tests/boot.nix1
-rw-r--r--nixos/tests/borgbackup.nix21
-rw-r--r--nixos/tests/cloud-init.nix1
-rw-r--r--nixos/tests/ec2.nix1
-rw-r--r--nixos/tests/elk.nix172
-rw-r--r--nixos/tests/home-assistant.nix46
-rw-r--r--nixos/tests/installer.nix1
-rw-r--r--nixos/tests/keymap.nix1
-rw-r--r--nixos/tests/kubernetes/base.nix1
-rw-r--r--nixos/tests/make-test.nix2
-rw-r--r--nixos/tests/matrix-synapse.nix30
-rw-r--r--nixos/tests/misc.nix5
-rw-r--r--nixos/tests/netdata.nix31
-rw-r--r--nixos/tests/networking.nix43
-rw-r--r--nixos/tests/predictable-interface-names.nix27
-rw-r--r--nixos/tests/rspamd.nix140
-rw-r--r--nixos/tests/rxe.nix53
-rw-r--r--nixos/tests/statsd.nix2
-rw-r--r--nixos/tests/sudo.nix93
-rw-r--r--nixos/tests/yabar.nix25
112 files changed, 3698 insertions, 733 deletions
diff --git a/nixos/default.nix b/nixos/default.nix
index 0e45a1cd75e24..45da78e9261cc 100644
--- a/nixos/default.nix
+++ b/nixos/default.nix
@@ -9,8 +9,6 @@ let
     modules = [ configuration ];
   };
 
-  inherit (eval) pkgs;
-
   # This is for `nixos-rebuild build-vm'.
   vmConfig = (import ./lib/eval-config.nix {
     inherit system;
@@ -30,7 +28,7 @@ let
 in
 
 {
-  inherit (eval) config options;
+  inherit (eval) pkgs config options;
 
   system = eval.config.system.build.toplevel;
 
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index 9bc83be66104a..bbe82066aa0c1 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -6,22 +6,52 @@ let
   lib = pkgs.lib;
 
   # Remove invisible and internal options.
-  optionsList = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList 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 builtins.isFunction x then "<function>"
+    else if lib.isFunction x then "<function>"
     else x;
 
-  # Clean up declaration sites to not refer to the NixOS source tree.
-  optionsList' = lib.flip map optionsList (opt: opt // {
+  # 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
+          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>pkgs.${name} (${package.meta.name})</literal>"
+        + lib.optionalString (!package.meta.evaluates) " <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 ? type) { type = substFunction opt.type; }
+  // lib.optionalAttrs (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,
@@ -32,8 +62,21 @@ 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');
+  optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsList);
 
   optionsDocBook = runCommand "options-db.xml" {} ''
     optionsXML=${optionsXML}
@@ -191,7 +234,7 @@ in rec {
       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'))))
+        (builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList))))
       } $dst/options.json
 
       mkdir -p $out/nix-support
diff --git a/nixos/doc/manual/installation/changing-config.xml b/nixos/doc/manual/installation/changing-config.xml
index 75df307a1b7c8..4db9020b96064 100644
--- a/nixos/doc/manual/installation/changing-config.xml
+++ b/nixos/doc/manual/installation/changing-config.xml
@@ -70,9 +70,21 @@ $ ./result/bin/run-*-vm
 </screen>
 
 The VM does not have any data from your host system, so your existing
-user accounts and home directories will not be available.  You can
-forward ports on the host to the guest.  For instance, the following
-will forward host port 2222 to guest port 22 (SSH):
+user accounts and home directories will not be available unless you
+have set <literal>mutableUsers = false</literal>.  Another way is to
+temporarily add the following to your configuration:
+
+<screen>
+users.extraUsers.your-user.initialPassword = "test"  
+</screen>
+
+<emphasis>Important:</emphasis> delete the $hostname.qcow2 file if you
+have started the virtual machine at least once without the right
+users, otherwise the changes will not get picked up.
+
+You can forward ports on the host to the guest.  For
+instance, the following will forward host port 2222 to guest port 22
+(SSH):
 
 <screen>
 $ QEMU_NET_OPTS="hostfwd=tcp::2222-:22" ./result/bin/run-*-vm
diff --git a/nixos/doc/manual/installation/installing-virtualbox-guest.xml b/nixos/doc/manual/installation/installing-virtualbox-guest.xml
index ee9680ced397e..7fcd22a112cf0 100644
--- a/nixos/doc/manual/installation/installing-virtualbox-guest.xml
+++ b/nixos/doc/manual/installation/installing-virtualbox-guest.xml
@@ -4,18 +4,18 @@
          version="5.0"
          xml:id="sec-instaling-virtualbox-guest">
 
-<title>Installing in a Virtualbox guest</title>
+<title>Installing in a VirtualBox guest</title>
 <para>
-  Installing NixOS into a Virtualbox guest is convenient for users who want to
+  Installing NixOS into a VirtualBox guest is convenient for users who want to
   try NixOS without installing it on bare metal. If you want to use a pre-made
-  Virtualbox appliance, it is available at <link
+  VirtualBox appliance, it is available at <link
   xlink:href="https://nixos.org/nixos/download.html">the downloads page</link>.
-  If you want to set up a Virtualbox guest manually, follow these instructions:
+  If you want to set up a VirtualBox guest manually, follow these instructions:
 </para>
 
 <orderedlist>
 
-  <listitem><para>Add a New Machine in Virtualbox with OS Type "Linux / Other
+  <listitem><para>Add a New Machine in VirtualBox with OS Type "Linux / Other
   Linux"</para></listitem>
 
   <listitem><para>Base Memory Size: 768 MB or higher.</para></listitem>
diff --git a/nixos/doc/manual/installation/installing.xml b/nixos/doc/manual/installation/installing.xml
index ab9108c30a715..d4746f2eb3a88 100644
--- a/nixos/doc/manual/installation/installing.xml
+++ b/nixos/doc/manual/installation/installing.xml
@@ -45,7 +45,10 @@ for a UEFI installation is by and large the same as a BIOS installation. The dif
   using <command>ifconfig</command>.</para>
   <para>To manually configure the network on the graphical installer,
   first disable network-manager with
-  <command>systemctl stop network-manager</command>.</para></listitem>
+  <command>systemctl stop network-manager</command>.</para>
+  <para>To manually configure the wifi on the minimal installer, run
+  <command>wpa_supplicant -B -i interface -c &lt;(wpa_passphrase 'SSID' 'key')</command>.</para></listitem>
+
 
   <listitem><para>If you would like to continue the installation from a different
   machine you need to activate the SSH daemon via <literal>systemctl start sshd</literal>.
diff --git a/nixos/doc/manual/options-to-docbook.xsl b/nixos/doc/manual/options-to-docbook.xsl
index 5387546b59828..7b45b233ab2a5 100644
--- a/nixos/doc/manual/options-to-docbook.xsl
+++ b/nixos/doc/manual/options-to-docbook.xsl
@@ -70,6 +70,15 @@
                 </para>
               </xsl:if>
 
+              <xsl:if test="attr[@name = 'relatedPackages']">
+                <para>
+                  <emphasis>Related packages:</emphasis>
+                  <xsl:text> </xsl:text>
+                  <xsl:value-of disable-output-escaping="yes"
+                                select="attr[@name = 'relatedPackages']/string/@value" />
+                </para>
+              </xsl:if>
+
               <xsl:if test="count(attr[@name = 'declarations']/list/*) != 0">
                 <para>
                   <emphasis>Declared by:</emphasis>
diff --git a/nixos/doc/manual/release-notes/rl-1803.xml b/nixos/doc/manual/release-notes/rl-1803.xml
index 12ff2e39a1bf5..275f74654bed7 100644
--- a/nixos/doc/manual/release-notes/rl-1803.xml
+++ b/nixos/doc/manual/release-notes/rl-1803.xml
@@ -38,6 +38,10 @@ has the following highlights: </para>
       </itemizedlist>
     </para>
   </listitem>
+
+  <listitem>
+    <para>PHP now defaults to PHP 7.2</para>
+  </listitem>
 </itemizedlist>
 
 </section>
@@ -90,6 +94,28 @@ following incompatible changes:</para>
   </listitem>
   <listitem>
     <para>
+      Package attributes starting with a digit have been prefixed with an
+      underscore sign. This is to avoid quoting in the configuration and
+      other issues with command-line tools like <literal>nix-env</literal>.
+      The change affects the following packages:
+      <itemizedlist>
+        <listitem>
+          <para><literal>2048-in-terminal</literal> → <literal>_2048-in-terminal</literal></para>
+        </listitem>
+        <listitem>
+          <para><literal>90secondportraits</literal> → <literal>_90secondportraits</literal></para>
+        </listitem>
+        <listitem>
+          <para><literal>2bwm</literal> → <literal>_2bwm</literal></para>
+        </listitem>
+        <listitem>
+          <para><literal>389-ds-base</literal> → <literal>_389-ds-base</literal></para>
+        </listitem>
+      </itemizedlist>
+    </para>
+  </listitem>
+  <listitem>
+    <para>
       <emphasis role="strong">
         The OpenSSH service no longer enables support for DSA keys by default,
         which could cause a system lock out. Update your keys or, unfavorably,
@@ -113,7 +139,7 @@ following incompatible changes:</para>
   </listitem>
   <listitem>
     <para>
-      <literal>cc-wrapper</literal>has been split in two; there is now also a <literal>bintools-wrapper</literal>.
+      <literal>cc-wrapper</literal> has been split in two; there is now also a <literal>bintools-wrapper</literal>.
       The most commonly used files in <filename>nix-support</filename> are now split between the two wrappers.
       Some commonly used ones, like <filename>nix-support/dynamic-linker</filename>, are duplicated for backwards compatability, even though they rightly belong only in <literal>bintools-wrapper</literal>.
       Other more obscure ones are just moved.
@@ -133,12 +159,53 @@ following incompatible changes:</para>
   </listitem>
   <listitem>
     <para>
+      <literal>lib.addPassthru drv passthru</literal> is removed.  Use <literal>lib.extendDerivation true passthru drv</literal> instead.  <emphasis role="strong">TODO: actually remove it before branching 18.03 off.</emphasis>
+    </para>
+  </listitem>
+  <listitem>
+    <para>
       The <literal>memcached</literal> service no longer accept dynamic socket
       paths via <option>services.memcached.socket</option>. Unix sockets can be
       still enabled by <option>services.memcached.enableUnixSocket</option> and
       will be accessible at <literal>/run/memcached/memcached.sock</literal>.
     </para>
   </listitem>
+  <listitem>
+    <para>
+      The <varname>hardware.amdHybridGraphics.disable</varname> option was removed for lack of a maintainer. If you still need this module, you may wish to include a copy of it from an older version of nixos in your imports.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The merging of config options for <varname>services.postfix.config</varname>
+      was buggy. Previously, if other options in the Postfix module like
+      <varname>services.postfix.useSrs</varname> were set and the user set config
+      options that were also set by such options, the resulting config wouldn't
+      include all options that were needed. They are now merged correctly. If
+      config options need to be overridden, <literal>lib.mkForce</literal> or
+      <literal>lib.mkOverride</literal> can be used.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The following changes apply if the <literal>stateVersion</literal> is changed to 18.03 or higher.
+      For <literal>stateVersion = "17.09"</literal> or lower the old behavior is preserved.
+    </para>
+    <itemizedlist>
+      <listitem>
+        <para>
+          <literal>matrix-synapse</literal> uses postgresql by default instead of sqlite.
+          Migration instructions can be found <link xlink:href="https://github.com/matrix-org/synapse/blob/master/docs/postgres.rst#porting-from-sqlite"> here </link>.
+        </para>
+      </listitem>
+    </itemizedlist>
+  </listitem>
+  <listitem>
+    <para>
+      The <literal>jid</literal> package has been removed, due to maintenance
+      overhead of a go package having non-versioned dependencies.
+    </para>
+  </listitem>
 </itemizedlist>
 
 </section>
diff --git a/nixos/lib/build-vms.nix b/nixos/lib/build-vms.nix
index 1a17a080ba452..4685fe6914a29 100644
--- a/nixos/lib/build-vms.nix
+++ b/nixos/lib/build-vms.nix
@@ -3,7 +3,7 @@
 let pkgs = import ../.. { inherit system config; }; in
 
 with pkgs.lib;
-with import ../lib/qemu-flags.nix;
+with import ../lib/qemu-flags.nix { inherit pkgs; };
 
 rec {
 
diff --git a/nixos/lib/make-disk-image.nix b/nixos/lib/make-disk-image.nix
index 636d0223fb778..8a3d8ed17701a 100644
--- a/nixos/lib/make-disk-image.nix
+++ b/nixos/lib/make-disk-image.nix
@@ -13,10 +13,16 @@
   # grafted in the file system at path `target'.
 , contents ? []
 
-, # Whether the disk should be partitioned (with a single partition
-  # containing the root filesystem) or contain the root filesystem
-  # directly.
-  partitioned ? true
+, # Type of partition table to use; either "legacy", "efi", or "none".
+  # For "efi" images, the GPT partition table is used and a mandatory ESP
+  #   partition of reasonable size is created in addition to the root partition.
+  #   If `installBootLoader` is true, GRUB will be installed in EFI mode.
+  # For "legacy", the msdos partition table is used and a single large root
+  #   partition is created. If `installBootLoader` is true, GRUB will be
+  #   installed in legacy mode.
+  # For "none", no partition table is created. Enabling `installBootLoader`
+  #   most likely fails as GRUB will probably refuse to install.
+  partitionTableType ? "legacy"
 
   # Whether to invoke switch-to-configuration boot during image creation
 , installBootLoader ? true
@@ -37,6 +43,10 @@
   format ? "raw"
 }:
 
+assert partitionTableType == "legacy" || partitionTableType == "efi" || partitionTableType == "none";
+# We use -E offset=X below, which is only supported by e2fsprogs
+assert partitionTableType != "none" -> fsType == "ext4";
+
 with lib;
 
 let format' = format; in let
@@ -51,6 +61,27 @@ let format' = format; in let
     raw   = "img";
   }.${format};
 
+  rootPartition = { # switch-case
+    legacy = "1";
+    efi = "2";
+  }.${partitionTableType};
+
+  partitionDiskScript = { # switch-case
+    legacy = ''
+      parted --script $diskImage -- \
+        mklabel msdos \
+        mkpart primary ext4 1MiB -1
+    '';
+    efi = ''
+      parted --script $diskImage -- \
+        mklabel gpt \
+        mkpart ESP fat32 8MiB 256MiB \
+        set 1 boot on \
+        mkpart primary ext4 256MiB -1
+    '';
+    none = "";
+  }.${partitionTableType};
+
   nixpkgs = cleanSource pkgs.path;
 
   channelSources = pkgs.runCommand "nixos-${config.system.nixosVersion}" {} ''
@@ -79,21 +110,32 @@ let format' = format; in let
   targets = map (x: x.target) contents;
 
   prepareImage = ''
-    export PATH=${makeSearchPathOutput "bin" "bin" prepareImageInputs}
+    export PATH=${makeBinPath prepareImageInputs}
+
+    # Yes, mkfs.ext4 takes different units in different contexts. Fun.
+    sectorsToKilobytes() {
+      echo $(( ( "$1" * 512 ) / 1024 ))
+    }
+
+    sectorsToBytes() {
+      echo $(( "$1" * 512  ))
+    }
 
     mkdir $out
     diskImage=nixos.raw
     truncate -s ${toString diskSize}M $diskImage
 
-    ${if partitioned then ''
-      parted --script $diskImage -- mklabel msdos mkpart primary ext4 1M -1s
-      offset=$((2048*512))
+    ${partitionDiskScript}
+
+    ${if partitionTableType != "none" then ''
+      # Get start & length of the root partition in sectors to $START and $SECTORS.
+      eval $(partx $diskImage -o START,SECTORS --nr ${rootPartition} --pairs)
+
+      mkfs.${fsType} -F -L nixos $diskImage -E offset=$(sectorsToBytes $START) $(sectorsToKilobytes $SECTORS)K
     '' else ''
-      offset=0
+      mkfs.${fsType} -F -L nixos $diskImage
     ''}
 
-    mkfs.${fsType} -F -L nixos -E offset=$offset $diskImage
-
     root="$PWD/root"
     mkdir -p $root
 
@@ -133,12 +175,12 @@ let format' = format; in let
     find $root/nix/store -mindepth 1 -maxdepth 1 -type f -o -type d | xargs chmod -R a-w
 
     echo "copying staging root to image..."
-    cptofs ${optionalString partitioned "-P 1"} -t ${fsType} -i $diskImage $root/* /
+    cptofs -p ${optionalString (partitionTableType != "none") "-P ${rootPartition}"} -t ${fsType} -i $diskImage $root/* /
   '';
 in pkgs.vmTools.runInLinuxVM (
   pkgs.runCommand name
     { preVM = prepareImage;
-      buildInputs = with pkgs; [ utillinux e2fsprogs ];
+      buildInputs = with pkgs; [ utillinux e2fsprogs dosfstools ];
       exportReferencesGraph = [ "closure" metaClosure ];
       postVM = ''
         ${if format == "raw" then ''
@@ -152,11 +194,7 @@ in pkgs.vmTools.runInLinuxVM (
       memSize = 1024;
     }
     ''
-      ${if partitioned then ''
-        rootDisk=/dev/vda1
-      '' else ''
-        rootDisk=/dev/vda
-      ''}
+      rootDisk=${if partitionTableType != "none" then "/dev/vda${rootPartition}" else "/dev/vda"}
 
       # Some tools assume these exist
       ln -s vda /dev/xvda
@@ -166,6 +204,14 @@ in pkgs.vmTools.runInLinuxVM (
       mkdir $mountPoint
       mount $rootDisk $mountPoint
 
+      # Create the ESP and mount it. Unlike e2fsprogs, mkfs.vfat doesn't support an
+      # '-E offset=X' option, so we can't do this outside the VM.
+      ${optionalString (partitionTableType == "efi") ''
+        mkdir -p /mnt/boot
+        mkfs.vfat -n ESP /dev/vda1
+        mount /dev/vda1 /mnt/boot
+      ''}
+
       # Install a configuration.nix
       mkdir -p /mnt/etc/nixos
       ${optionalString (configFile != null) ''
diff --git a/nixos/lib/qemu-flags.nix b/nixos/lib/qemu-flags.nix
index de355b08918ca..fcdcbf1b00770 100644
--- a/nixos/lib/qemu-flags.nix
+++ b/nixos/lib/qemu-flags.nix
@@ -1,4 +1,5 @@
 # QEMU flags shared between various Nix expressions.
+{ pkgs }:
 
 {
 
@@ -7,4 +8,14 @@
       "-net vde,vlan=${toString nic},sock=$QEMU_VDE_SOCKET_${toString net}"
     ];
 
+  qemuSerialDevice = if pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64 then "ttyS0"
+        else if pkgs.stdenv.isArm || pkgs.stdenv.isAarch64 then "ttyAMA0"
+        else throw "Unknown QEMU serial device for system '${pkgs.stdenv.system}'";
+
+  qemuBinary = qemuPkg: {
+    "i686-linux" = "${qemuPkg}/bin/qemu-kvm";
+    "x86_64-linux" = "${qemuPkg}/bin/qemu-kvm -cpu kvm64";
+    "armv7l-linux" = "${qemuPkg}/bin/qemu-system-arm -enable-kvm -machine virt -cpu host";
+    "aarch64-linux" = "${qemuPkg}/bin/qemu-system-aarch64 -enable-kvm -machine virt,gic-version=host -cpu host";
+  }.${pkgs.stdenv.system} or (throw "Unknown QEMU binary for '${pkgs.stdenv.system}'");
 }
diff --git a/nixos/lib/testing.nix b/nixos/lib/testing.nix
index 532fff681d379..ddab23cce3937 100644
--- a/nixos/lib/testing.nix
+++ b/nixos/lib/testing.nix
@@ -29,7 +29,7 @@ rec {
         cp ${./test-driver/Logger.pm} $libDir/Logger.pm
 
         wrapProgram $out/bin/nixos-test-driver \
-          --prefix PATH : "${lib.makeBinPath [ qemu vde2 netpbm coreutils ]}" \
+          --prefix PATH : "${lib.makeBinPath [ qemu_test vde2 netpbm coreutils ]}" \
           --prefix PERL5LIB : "${with perlPackages; lib.makePerlPath [ TermReadLineGnu XMLWriter IOTty FileSlurp ]}:$out/lib/perl5/site_perl"
       '';
   };
@@ -85,7 +85,7 @@ rec {
 
       testScript' =
         # Call the test script with the computed nodes.
-        if builtins.isFunction testScript
+        if lib.isFunction testScript
         then testScript { inherit nodes; }
         else testScript;
 
diff --git a/nixos/maintainers/scripts/ec2/amazon-image.nix b/nixos/maintainers/scripts/ec2/amazon-image.nix
index 2e67edf8ee3d5..972c04453aefc 100644
--- a/nixos/maintainers/scripts/ec2/amazon-image.nix
+++ b/nixos/maintainers/scripts/ec2/amazon-image.nix
@@ -46,7 +46,7 @@ in {
     inherit lib config;
     inherit (cfg) contents format name;
     pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
-    partitioned = config.ec2.hvm;
+    partitionTableType = if config.ec2.hvm then "legacy" else "none";
     diskSize = cfg.sizeMB;
     configFile = pkgs.writeText "configuration.nix"
       ''
diff --git a/nixos/modules/config/power-management.nix b/nixos/modules/config/power-management.nix
index a4a4d6e1a6af3..4c37e8a6208ca 100644
--- a/nixos/modules/config/power-management.nix
+++ b/nixos/modules/config/power-management.nix
@@ -69,9 +69,6 @@ in
 
   config = mkIf cfg.enable {
 
-    # Leftover for old setups, should be set by nixos-generate-config now
-    powerManagement.cpuFreqGovernor = mkDefault "ondemand";
-
     systemd.targets.post-resume = {
       description = "Post-Resume Actions";
       requires = [ "post-resume.service" ];
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
index 65f2e5d7af992..398660967c529 100644
--- a/nixos/modules/config/shells-environment.nix
+++ b/nixos/modules/config/shells-environment.nix
@@ -36,7 +36,7 @@ in
       default = {};
       description = ''
         A set of environment variables used in the global environment.
-        These variables will be set on shell initialisation.
+        These variables will be set on shell initialisation (e.g. in /etc/profile).
         The value of each variable can be either a string or a list of
         strings.  The latter is concatenated, interspersed with colon
         characters.
diff --git a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
index efb9ba39bcd45..3306846b7fa7c 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
@@ -27,6 +27,7 @@ in
   boot.loader.grub.enable = false;
   boot.loader.generic-extlinux-compatible.enable = true;
 
+  boot.consoleLogLevel = lib.mkDefault 7;
   boot.kernelPackages = pkgs.linuxPackages_latest;
 
   # The serial ports listed here are:
@@ -42,8 +43,17 @@ in
     populateBootCommands = let
       configTxt = pkgs.writeText "config.txt" ''
         kernel=u-boot-rpi3.bin
+
+        # Boot in 64-bit mode.
         arm_control=0x200
+
+        # U-Boot used to need this to work, regardless of whether UART is actually used or not.
+        # TODO: check when/if this can be removed.
         enable_uart=1
+
+        # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
+        # when attempting to show low-voltage or overtemperature warnings.
+        avoid_warnings=1
       '';
       in ''
         (cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/boot/)
diff --git a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
index 880a6bf2e1e8e..08903ba397a19 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
@@ -27,6 +27,7 @@ in
   boot.loader.grub.enable = false;
   boot.loader.generic-extlinux-compatible.enable = true;
 
+  boot.consoleLogLevel = lib.mkDefault 7;
   boot.kernelPackages = pkgs.linuxPackages_latest;
   # The serial ports listed here are:
   # - ttyS0: for Tegra (Jetson TK1)
@@ -42,11 +43,18 @@ in
   sdImage = {
     populateBootCommands = let
       configTxt = pkgs.writeText "config.txt" ''
+        # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
+        # when attempting to show low-voltage or overtemperature warnings.
+        avoid_warnings=1
+
         [pi2]
         kernel=u-boot-rpi2.bin
 
         [pi3]
         kernel=u-boot-rpi3.bin
+
+        # U-Boot used to need this to work, regardless of whether UART is actually used or not.
+        # TODO: check when/if this can be removed.
         enable_uart=1
       '';
       in ''
diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
index eb676eae05e89..2833b75b84d87 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
@@ -27,6 +27,7 @@ in
   boot.loader.grub.enable = false;
   boot.loader.generic-extlinux-compatible.enable = true;
 
+  boot.consoleLogLevel = lib.mkDefault 7;
   boot.kernelPackages = pkgs.linuxPackages_rpi;
 
   # FIXME: this probably should be in installation-device.nix
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index c6440dd906fdd..c0c6a6ef92444 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -301,6 +301,9 @@
       pykms = 282;
       kodi = 283;
       restya-board = 284;
+      mighttpd2 = 285;
+      hass = 286;
+      monero = 287;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -570,6 +573,9 @@
       pykms = 282;
       kodi = 283;
       restya-board = 284;
+      mighttpd2 = 285;
+      hass = 286;
+      monero = 287;
 
       # When adding a gid, make sure it doesn't match an existing
       # uid. Users and groups with the same name should have equal
diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix
index 1793c1447d602..e747fbc6755ca 100644
--- a/nixos/modules/misc/nixpkgs.nix
+++ b/nixos/modules/misc/nixpkgs.nix
@@ -3,11 +3,13 @@
 with lib;
 
 let
+  cfg = config.nixpkgs;
+
   isConfig = x:
-    builtins.isAttrs x || builtins.isFunction x;
+    builtins.isAttrs x || lib.isFunction x;
 
   optCall = f: x:
-    if builtins.isFunction f
+    if lib.isFunction f
     then f x
     else f;
 
@@ -38,16 +40,55 @@ let
   overlayType = mkOptionType {
     name = "nixpkgs-overlay";
     description = "nixpkgs overlay";
-    check = builtins.isFunction;
+    check = lib.isFunction;
     merge = lib.mergeOneOption;
   };
 
-  _pkgs = import ../../.. config.nixpkgs;
+  pkgsType = mkOptionType {
+    name = "nixpkgs";
+    description = "An evaluation of Nixpkgs; the top level attribute set of packages";
+    check = builtins.isAttrs;
+  };
 
 in
 
 {
   options.nixpkgs = {
+
+    pkgs = mkOption {
+      defaultText = literalExample
+        ''import "''${nixos}/.." {
+            inherit (config.nixpkgs) config overlays system;
+          }
+        '';
+      default = import ../../.. { inherit (cfg) config overlays system; };
+      type = pkgsType;
+      example = literalExample ''import <nixpkgs> {}'';
+      description = ''
+        This is the evaluation of Nixpkgs that will be provided to
+        all NixOS modules. Defining this option has the effect of
+        ignoring the other options that would otherwise be used to
+        evaluate Nixpkgs, because those are arguments to the default
+        value. The default value imports the Nixpkgs source files
+        relative to the location of this NixOS module, because
+        NixOS and Nixpkgs are distributed together for consistency,
+        so the <code>nixos</code> in the default value is in fact a
+        relative path. The <code>config</code>, <code>overlays</code>
+        and <code>system</code> come from this option's siblings.
+
+        This option can be used by applications like NixOps to increase
+        the performance of evaluation, or to create packages that depend
+        on a container that should be built with the exact same evaluation
+        of Nixpkgs, for example. Applications like this should set
+        their default value using <code>lib.mkDefault</code>, so
+        user-provided configuration can override it without using
+        <code>lib</code>.
+
+        Note that using a distinct version of Nixpkgs with NixOS may
+        be an unexpected source of problems. Use this option with care.
+      '';
+    };
+
     config = mkOption {
       default = {};
       example = literalExample
@@ -59,6 +100,8 @@ in
         The configuration of the Nix Packages collection.  (For
         details, see the Nixpkgs documentation.)  It allows you to set
         package configuration options.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
       '';
     };
 
@@ -69,7 +112,6 @@ in
           [ (self: super: {
               openssh = super.openssh.override {
                 hpnSupport = true;
-                withKerberos = true;
                 kerberos = self.libkrb5;
               };
             };
@@ -83,6 +125,8 @@ in
         takes as an argument the <emphasis>original</emphasis> Nixpkgs.
         The first argument should be used for finding dependencies, and
         the second should be used for overriding recipes.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
       '';
     };
 
@@ -94,14 +138,16 @@ in
         If unset, it defaults to the platform type of your host system.
         Specifying this option is useful when doing distributed
         multi-platform deployment, or when building virtual machines.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
       '';
     };
   };
 
   config = {
     _module.args = {
-      pkgs = _pkgs;
-      pkgs_i686 = _pkgs.pkgsi686Linux;
+      pkgs = cfg.pkgs;
+      pkgs_i686 = cfg.pkgs.pkgsi686Linux;
     };
   };
 }
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 8d329b5b4b25e..2ef8684d7f950 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -84,6 +84,7 @@
   ./programs/info.nix
   ./programs/java.nix
   ./programs/kbdlight.nix
+  ./programs/less.nix
   ./programs/light.nix
   ./programs/man.nix
   ./programs/mosh.nix
@@ -110,6 +111,7 @@
   ./programs/wireshark.nix
   ./programs/xfs_quota.nix
   ./programs/xonsh.nix
+  ./programs/yabar.nix
   ./programs/zsh/oh-my-zsh.nix
   ./programs/zsh/zsh.nix
   ./programs/zsh/zsh-syntax-highlighting.nix
@@ -200,6 +202,7 @@
   ./services/desktops/dleyna-server.nix
   ./services/desktops/geoclue2.nix
   ./services/desktops/gnome3/at-spi2-core.nix
+  ./services/desktops/gnome3/chrome-gnome-shell.nix
   ./services/desktops/gnome3/evolution-data-server.nix
   ./services/desktops/gnome3/gnome-disks.nix
   ./services/desktops/gnome3/gnome-documents.nix
@@ -225,7 +228,6 @@
   ./services/games/terraria.nix
   ./services/hardware/acpid.nix
   ./services/hardware/actkbd.nix
-  ./services/hardware/amd-hybrid-graphics.nix
   ./services/hardware/bluetooth.nix
   ./services/hardware/brltty.nix
   ./services/hardware/freefall.nix
@@ -314,6 +316,7 @@
   ./services/misc/gogs.nix
   ./services/misc/gollum.nix
   ./services/misc/gpsd.nix
+  ./services/misc/home-assistant.nix
   ./services/misc/ihaskell.nix
   ./services/misc/irkerd.nix
   ./services/misc/jackett.nix
@@ -415,7 +418,8 @@
   ./services/network-filesystems/ipfs.nix
   ./services/network-filesystems/netatalk.nix
   ./services/network-filesystems/nfsd.nix
-  ./services/network-filesystems/openafs-client/default.nix
+  ./services/network-filesystems/openafs/client.nix
+  ./services/network-filesystems/openafs/server.nix
   ./services/network-filesystems/rsyncd.nix
   ./services/network-filesystems/samba.nix
   ./services/network-filesystems/tahoe.nix
@@ -424,6 +428,7 @@
   ./services/network-filesystems/yandex-disk.nix
   ./services/network-filesystems/xtreemfs.nix
   ./services/networking/amuled.nix
+  ./services/networking/aria2.nix
   ./services/networking/asterisk.nix
   ./services/networking/atftpd.nix
   ./services/networking/avahi-daemon.nix
@@ -488,6 +493,7 @@
   ./services/networking/minidlna.nix
   ./services/networking/miniupnpd.nix
   ./services/networking/mosquitto.nix
+  ./services/networking/monero.nix
   ./services/networking/miredo.nix
   ./services/networking/mstpd.nix
   ./services/networking/murmur.nix
@@ -525,6 +531,7 @@
   ./services/networking/redsocks.nix
   ./services/networking/resilio.nix
   ./services/networking/rpcbind.nix
+  ./services/networking/rxe.nix
   ./services/networking/sabnzbd.nix
   ./services/networking/searx.nix
   ./services/networking/seeks.nix
@@ -540,6 +547,7 @@
   ./services/networking/ssh/lshd.nix
   ./services/networking/ssh/sshd.nix
   ./services/networking/strongswan.nix
+  ./services/networking/stunnel.nix
   ./services/networking/supplicant.nix
   ./services/networking/supybot.nix
   ./services/networking/syncthing.nix
@@ -634,6 +642,7 @@
   ./services/web-servers/lighttpd/default.nix
   ./services/web-servers/lighttpd/gitweb.nix
   ./services/web-servers/lighttpd/inginious.nix
+  ./services/web-servers/mighttpd2.nix
   ./services/web-servers/minio.nix
   ./services/web-servers/nginx/default.nix
   ./services/web-servers/phpfpm/default.nix
diff --git a/nixos/modules/profiles/clone-config.nix b/nixos/modules/profiles/clone-config.nix
index 77d86f8d74057..5b4e68beb6a69 100644
--- a/nixos/modules/profiles/clone-config.nix
+++ b/nixos/modules/profiles/clone-config.nix
@@ -17,7 +17,7 @@ let
   # you should use files).
   moduleFiles =
     # FIXME: use typeOf (Nix 1.6.1).
-    filter (x: !isAttrs x && !builtins.isFunction x) modules;
+    filter (x: !isAttrs x && !lib.isFunction x) modules;
 
   # Partition module files because between NixOS and non-NixOS files.  NixOS
   # files may change if the repository is updated.
diff --git a/nixos/modules/programs/adb.nix b/nixos/modules/programs/adb.nix
index 18290555b79df..f648d70bd9fa4 100644
--- a/nixos/modules/programs/adb.nix
+++ b/nixos/modules/programs/adb.nix
@@ -16,6 +16,7 @@ with lib;
           To grant access to a user, it must be part of adbusers group:
           <code>users.extraUsers.alice.extraGroups = ["adbusers"];</code>
         '';
+        relatedPackages = [ ["androidenv" "platformTools"] ];
       };
     };
   };
diff --git a/nixos/modules/programs/less.nix b/nixos/modules/programs/less.nix
new file mode 100644
index 0000000000000..c0283c9e68626
--- /dev/null
+++ b/nixos/modules/programs/less.nix
@@ -0,0 +1,118 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.programs.less;
+
+  configFile = ''
+    #command
+    ${concatStringsSep "\n"
+      (mapAttrsToList (command: action: "${command} ${action}") cfg.commands)
+    }
+    ${if cfg.clearDefaultCommands then "#stop" else ""}
+
+    #line-edit
+    ${concatStringsSep "\n"
+      (mapAttrsToList (command: action: "${command} ${action}") cfg.lineEditingKeys)
+    }
+
+    #env
+    ${concatStringsSep "\n"
+      (mapAttrsToList (variable: values: "${variable}=${values}") cfg.envVariables)
+    }
+  '';
+
+  lessKey = pkgs.runCommand "lesskey"
+            { src = pkgs.writeText "lessconfig" configFile; }
+            "${pkgs.less}/bin/lesskey -o $out $src";
+
+in
+
+{
+  options = {
+
+    programs.less = {
+
+      enable = mkEnableOption "less";
+
+      commands = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        example = {
+          "h" = "noaction 5\e(";
+          "l" = "noaction 5\e)";
+        };
+        description = "Defines new command keys.";
+      };
+
+      clearDefaultCommands = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Clear all default commands.
+          You should remember to set the quit key.
+          Otherwise you will not be able to leave less without killing it.
+        '';
+      };
+
+      lineEditingKeys = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        example = {
+          "\e" = "abort";
+        };
+        description = "Defines new line-editing keys.";
+      };
+
+      envVariables = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        example = {
+          LESS = "--quit-if-one-screen";
+        };
+        description = "Defines environment variables.";
+      };
+
+      lessopen = mkOption {
+        type = types.nullOr types.str;
+        default = "|${pkgs.lesspipe}/bin/lesspipe.sh %s";
+        description = ''
+          Before less opens a file, it first gives your input preprocessor a chance to modify the way the contents of the file are displayed.
+        '';
+      };
+
+      lessclose = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          When less closes a file opened in such a way, it will call another program, called the input postprocessor, which may  perform  any  desired  clean-up  action (such  as deleting the replacement file created by LESSOPEN).
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.less ];
+
+    environment.variables = {
+      "LESSKEY_SYSTEM" = toString lessKey;
+    } // optionalAttrs (cfg.lessopen != null) {
+      "LESSOPEN" = cfg.lessopen;
+    } // optionalAttrs (cfg.lessclose != null) {
+      "LESSCLOSE" = cfg.lessclose;
+    };
+
+    warnings = optional (
+      cfg.clearDefaultCommands && (all (x: x != "quit") (attrValues cfg.commands))
+    ) ''
+      config.programs.less.clearDefaultCommands clears all default commands of less but there is no alternative binding for exiting.
+      Consider adding a binding for 'quit'.
+    '';
+  };
+
+  meta.maintainers = with maintainers; [ johnazoidberg ];
+
+}
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
index 0f3f42901bab1..8ec4169207db5 100644
--- a/nixos/modules/programs/shadow.nix
+++ b/nixos/modules/programs/shadow.nix
@@ -26,8 +26,9 @@ let
       # Ensure privacy for newly created home directories.
       UMASK        077
 
-      # Uncomment this to allow non-root users to change their account
-      #information.  This should be made configurable.
+      # Uncomment this and install chfn SUID to allow non-root
+      # users to change their account GECOS information.
+      # This should be made configurable.
       #CHFN_RESTRICT frwh
 
     '';
@@ -103,13 +104,12 @@ in
 
     security.wrappers = {
       su.source        = "${pkgs.shadow.su}/bin/su";
-      chfn.source      = "${pkgs.shadow.out}/bin/chfn";
+      sg.source        = "${pkgs.shadow.out}/bin/sg";
+      newgrp.source    = "${pkgs.shadow.out}/bin/newgrp";
       newuidmap.source = "${pkgs.shadow.out}/bin/newuidmap";
       newgidmap.source = "${pkgs.shadow.out}/bin/newgidmap";
     } // (if config.users.mutableUsers then {
       passwd.source    = "${pkgs.shadow.out}/bin/passwd";
-      sg.source        = "${pkgs.shadow.out}/bin/sg";
-      newgrp.source    = "${pkgs.shadow.out}/bin/newgrp";
     } else {});
   };
 }
diff --git a/nixos/modules/programs/tmux.nix b/nixos/modules/programs/tmux.nix
index 1eb6fa6bf2fa6..4a60403a2827e 100644
--- a/nixos/modules/programs/tmux.nix
+++ b/nixos/modules/programs/tmux.nix
@@ -61,7 +61,12 @@ in {
   options = {
     programs.tmux = {
 
-      enable = mkEnableOption "<command>tmux</command> - a <command>screen</command> replacement.";
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whenever to configure <command>tmux</command> system-wide.";
+        relatedPackages = [ "tmux" ];
+      };
 
       aggressiveResize = mkOption {
         default = false;
diff --git a/nixos/modules/programs/yabar.nix b/nixos/modules/programs/yabar.nix
new file mode 100644
index 0000000000000..a01083c3ace98
--- /dev/null
+++ b/nixos/modules/programs/yabar.nix
@@ -0,0 +1,149 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.yabar;
+
+  mapExtra = v: lib.concatStringsSep "\n" (mapAttrsToList (
+    key: val: "${key} = ${if (isString val) then "\"${val}\"" else "${builtins.toString val}"};"
+  ) v);
+
+  listKeys = r: concatStringsSep "," (map (n: "\"${n}\"") (attrNames r));
+
+  configFile = let
+    bars = mapAttrsToList (
+      name: cfg: ''
+        ${name}: {
+          font: "${cfg.font}";
+          position: "${cfg.position}";
+
+          ${mapExtra cfg.extra}
+
+          block-list: [${listKeys cfg.indicators}]
+
+          ${concatStringsSep "\n" (mapAttrsToList (
+            name: cfg: ''
+              ${name}: {
+                exec: "${cfg.exec}";
+                align: "${cfg.align}";
+                ${mapExtra cfg.extra}
+              };
+            ''
+          ) cfg.indicators)}
+        };
+      ''
+    ) cfg.bars;
+  in pkgs.writeText "yabar.conf" ''
+    bar-list = [${listKeys cfg.bars}];
+    ${concatStringsSep "\n" bars}
+  '';
+in
+  {
+    options.programs.yabar = {
+      enable = mkEnableOption "yabar";
+
+      package = mkOption {
+        default = pkgs.yabar;
+        example = literalExample "pkgs.yabar-unstable";
+        type = types.package;
+
+        description = ''
+          The package which contains the `yabar` binary.
+
+          Nixpkgs provides the `yabar` and `yabar-unstable`
+          derivations since 18.03, so it's possible to choose.
+        '';
+      };
+
+      bars = mkOption {
+        default = {};
+        type = types.attrsOf(types.submodule {
+          options = {
+            font = mkOption {
+              default = "sans bold 9";
+              example = "Droid Sans, FontAwesome Bold 9";
+              type = types.string;
+
+              description = ''
+                The font that will be used to draw the status bar.
+              '';
+            };
+
+            position = mkOption {
+              default = "top";
+              example = "bottom";
+              type = types.enum [ "top" "bottom" ];
+
+              description = ''
+                The position where the bar will be rendered.
+              '';
+            };
+
+            extra = mkOption {
+              default = {};
+              type = types.attrsOf types.string;
+
+              description = ''
+                An attribute set which contains further attributes of a bar.
+              '';
+            };
+
+            indicators = mkOption {
+              default = {};
+              type = types.attrsOf(types.submodule {
+                options.exec = mkOption {
+                  example = "YABAR_DATE";
+                  type = types.string;
+                  description = ''
+                     The type of the indicator to be executed.
+                  '';
+                };
+
+                options.align = mkOption {
+                  default = "left";
+                  example = "right";
+                  type = types.enum [ "left" "center" "right" ];
+
+                  description = ''
+                    Whether to align the indicator at the left or right of the bar.
+                  '';
+                };
+
+                options.extra = mkOption {
+                  default = {};
+                  type = types.attrsOf (types.either types.string types.int);
+
+                  description = ''
+                    An attribute set which contains further attributes of a indicator.
+                  '';
+                };
+              });
+
+              description = ''
+                Indicators that should be rendered by yabar.
+              '';
+            };
+          };
+        });
+
+        description = ''
+          List of bars that should be rendered by yabar.
+        '';
+      };
+    };
+
+    config = mkIf cfg.enable {
+      systemd.user.services.yabar = {
+        description = "yabar service";
+        wantedBy = [ "graphical-session.target" ];
+        partOf = [ "graphical-session.target" ];
+
+        script = ''
+          ${cfg.package}/bin/yabar -c ${configFile}
+        '';
+
+        serviceConfig.Restart = "always";
+      };
+    };
+  }
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.nix b/nixos/modules/programs/zsh/oh-my-zsh.nix
index 9077643c4440c..b995d390b2793 100644
--- a/nixos/modules/programs/zsh/oh-my-zsh.nix
+++ b/nixos/modules/programs/zsh/oh-my-zsh.nix
@@ -48,6 +48,15 @@ in
             Name of the theme to be used by oh-my-zsh.
           '';
         };
+
+        cacheDir = mkOption {
+          default = "$HOME/.cache/oh-my-zsh";
+          type = types.str;
+          description = ''
+            Cache directory to be used by `oh-my-zsh`.
+            Without this option it would default to the read-only nix store.
+          '';
+        };
       };
     };
 
@@ -74,6 +83,13 @@ in
           "ZSH_THEME=\"${cfg.theme}\""
         }
 
+        ${optionalString (cfg.cacheDir != null) ''
+          if [[ ! -d "${cfg.cacheDir}" ]]; then
+            mkdir -p "${cfg.cacheDir}"
+          fi
+          ZSH_CACHE_DIR=${cfg.cacheDir}
+        ''}
+
         source $ZSH/oh-my-zsh.sh
       '';
     };
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index 6fb1346bbb338..5102bfef0325d 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -36,8 +36,9 @@ in
       shellAliases = mkOption {
         default = config.environment.shellAliases;
         description = ''
-          Set of aliases for zsh shell. See <option>environment.shellAliases</option>
-          for an option format description.
+          Set of aliases for zsh shell. Overrides the default value taken from
+           <option>environment.shellAliases</option>.
+          See <option>environment.shellAliases</option> for an option format description.
         '';
         type = types.attrs; # types.attrsOf types.stringOrPath;
       };
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 562be13a3f640..7351482f957ff 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -210,6 +210,7 @@ with lib;
       "Set the option `services.xserver.displayManager.sddm.package' instead.")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "forceAutohint" ] "")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ] "")
+    (mkRemovedOptionModule [ "virtualisation" "xen" "qemu" ] "You don't need this option anymore, it will work without it.")
 
     # ZSH
     (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
@@ -220,5 +221,8 @@ with lib;
     (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "theme" ] [ "programs" "zsh" "ohMyZsh" "theme" ])
     (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "custom" ] [ "programs" "zsh" "ohMyZsh" "custom" ])
     (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "plugins" ] [ "programs" "zsh" "ohMyZsh" "plugins" ])
+
+    # Xen
+    (mkRenamedOptionModule [ "virtualisation" "xen" "qemu-package" ] [ "virtualisation" "xen" "package-qemu" ])
   ];
 }
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index fb011019f7f54..0736239ed2cfb 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -6,10 +6,11 @@ let
 
   cfg = config.security.acme;
 
-  certOpts = { ... }: {
+  certOpts = { name, ... }: {
     options = {
       webroot = mkOption {
         type = types.str;
+        example = "/var/lib/acme/acme-challenges";
         description = ''
           Where the webroot of the HTTP vhost is located.
           <filename>.well-known/acme-challenge/</filename> directory
@@ -20,8 +21,8 @@ let
       };
 
       domain = mkOption {
-        type = types.nullOr types.str;
-        default = null;
+        type = types.str;
+        default = name;
         description = "Domain to fetch certificate for (defaults to the entry name)";
       };
 
@@ -48,7 +49,7 @@ let
         default = false;
         description = ''
           Give read permissions to the specified group
-          (<option>security.acme.group</option>) to read SSL private certificates.
+          (<option>security.acme.cert.&lt;name&gt;.group</option>) to read SSL private certificates.
         '';
       };
 
@@ -87,7 +88,7 @@ let
           }
         '';
         description = ''
-          Extra domain names for which certificates are to be issued, with their
+          A list of extra domain names, which are included in the one certificate to be issued, with their
           own server roots if needed.
         '';
       };
@@ -139,6 +140,14 @@ in
         '';
       };
 
+      tosHash = mkOption {
+        type = types.string;
+        default = "cc88d8d9517f490191401e7b54e9ffd12a2b9082ec7a1d4cec6101f9f1647e7b";
+        description = ''
+          SHA256 of the Terms of Services document. This changes once in a while.
+        '';
+      };
+
       production = mkOption {
         type = types.bool;
         default = true;
@@ -185,10 +194,9 @@ in
           servicesLists = mapAttrsToList certToServices cfg.certs;
           certToServices = cert: data:
               let
-                domain = if data.domain != null then data.domain else cert;
                 cpath = "${cfg.directory}/${cert}";
                 rights = if data.allowKeysForGroup then "750" else "700";
-                cmdline = [ "-v" "-d" domain "--default_root" data.webroot "--valid_min" cfg.validMin ]
+                cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin "--tos_sha256" cfg.tosHash ]
                           ++ optionals (data.email != null) [ "--email" data.email ]
                           ++ concatMap (p: [ "-f" p ]) data.plugins
                           ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains)
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 3fff9e78aa191..f39f64033ca7d 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -46,6 +46,18 @@ let
         '';
       };
 
+      googleAuthenticator = {
+        enable = mkOption {
+          default = false;
+          type = types.bool;
+          description = ''
+            If set, users with enabled Google Authenticator (created
+            <filename>~/.google_authenticator</filename>) will be required
+            to provide Google Authenticator token to log in.
+          '';
+        };
+      };
+
       usbAuth = mkOption {
         default = config.security.pam.usb.enable;
         type = types.bool;
@@ -284,7 +296,12 @@ let
           # prompts the user for password so we run it once with 'required' at an
           # earlier point and it will run again with 'sufficient' further down.
           # We use try_first_pass the second time to avoid prompting password twice
-          (optionalString (cfg.unixAuth && (config.security.pam.enableEcryptfs || cfg.pamMount || cfg.enableKwallet || cfg.enableGnomeKeyring)) ''
+          (optionalString (cfg.unixAuth &&
+          (config.security.pam.enableEcryptfs
+            || cfg.pamMount
+            || cfg.enableKwallet
+            || cfg.enableGnomeKeyring
+            || cfg.googleAuthenticator.enable)) ''
               auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth
               ${optionalString config.security.pam.enableEcryptfs
                 "auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"}
@@ -295,6 +312,8 @@ let
                  " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")}
               ${optionalString cfg.enableGnomeKeyring
                 ("auth optional ${pkgs.gnome3.gnome_keyring}/lib/security/pam_gnome_keyring.so")}
+              ${optionalString cfg.googleAuthenticator.enable
+                  "auth required ${pkgs.googleAuthenticator}/lib/security/pam_google_authenticator.so no_increment_hotp"}
             '') + ''
           ${optionalString cfg.unixAuth
               "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"}
diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix
index cfd0595e63b72..a57f14bb5ae1c 100644
--- a/nixos/modules/security/sudo.nix
+++ b/nixos/modules/security/sudo.nix
@@ -8,6 +8,22 @@ let
 
   inherit (pkgs) sudo;
 
+  toUserString = user: if (isInt user) then "#${toString user}" else "${user}";
+  toGroupString = group: if (isInt group) then "%#${toString group}" else "%${group}";
+
+  toCommandOptionsString = options:
+    "${concatStringsSep ":" options}${optionalString (length options != 0) ":"} ";
+
+  toCommandsString = commands:
+    concatStringsSep ", " (
+      map (command:
+        if (isString command) then
+          command
+        else
+          "${toCommandOptionsString command.options}${command.command}"
+      ) commands
+    );
+
 in
 
 {
@@ -47,6 +63,97 @@ in
         '';
     };
 
+    security.sudo.extraRules = mkOption {
+      description = ''
+        Define specific rules to be in the <filename>sudoers</filename> file.
+      '';
+      default = [];
+      example = [
+        # Allow execution of any command by all users in group sudo,
+        # requiring a password.
+        { groups = [ "sudo" ]; commands = [ "ALL" ]; }
+
+        # Allow execution of "/home/root/secret.sh" by user `backup`, `database`
+        # and the group with GID `1006` without a password.
+        { users = [ "backup" ]; groups = [ 1006 ];
+          commands = [ { command = "/home/root/secret.sh"; options = [ "SETENV" "NOPASSWD" ]; } ]; }
+
+        # Allow all users of group `bar` to run two executables as user `foo`
+        # with arguments being pre-set.
+        { groups = [ "bar" ]; runAs = "foo";
+          commands =
+            [ "/home/baz/cmd1.sh hello-sudo"
+              { command = ''/home/baz/cmd2.sh ""''; options = [ "SETENV" ]; } ]; }
+      ];
+      type = with types; listOf (submodule {
+        options = {
+          users = mkOption {
+            type = with types; listOf (either string int);
+            description = ''
+              The usernames / UIDs this rule should apply for.
+            '';
+            default = [];
+          };
+
+          groups = mkOption {
+            type = with types; listOf (either string int);
+            description = ''
+              The groups / GIDs this rule should apply for.
+            '';
+            default = [];
+          };
+
+          host = mkOption {
+            type = types.string;
+            default = "ALL";
+            description = ''
+              For what host this rule should apply.
+            '';
+          };
+
+          runAs = mkOption {
+            type = with types; string;
+            default = "ALL:ALL";
+            description = ''
+              Under which user/group the specified command is allowed to run.
+
+              A user can be specified using just the username: <code>"foo"</code>.
+              It is also possible to specify a user/group combination using <code>"foo:bar"</code>
+              or to only allow running as a specific group with <code>":bar"</code>.
+            '';
+          };
+
+          commands = mkOption {
+            description = ''
+              The commands for which the rule should apply.
+            '';
+            type = with types; listOf (either string (submodule {
+
+              options = {
+                command = mkOption {
+                  type = with types; string;
+                  description = ''
+                    A command being either just a path to a binary to allow any arguments,
+                    the full command with arguments pre-set or with <code>""</code> used as the argument,
+                    not allowing arguments to the command at all.
+                  '';
+                };
+
+                options = mkOption {
+                  type = with types; listOf (enum [ "NOPASSWD" "PASSWD" "NOEXEC" "EXEC" "SETENV" "NOSETENV" "LOG_INPUT" "NOLOG_INPUT" "LOG_OUTPUT" "NOLOG_OUTPUT" ]);
+                  description = ''
+                    Options for running the command. Refer to the <a href="https://www.sudo.ws/man/1.7.10/sudoers.man.html">sudo manual</a>.
+                  '';
+                  default = [];
+                };
+              };
+
+            }));
+          };
+        };
+      });
+    };
+
     security.sudo.extraConfig = mkOption {
       type = types.lines;
       default = "";
@@ -61,10 +168,16 @@ in
 
   config = mkIf cfg.enable {
 
+    security.sudo.extraRules = [
+      { groups = [ "wheel" ];
+        commands = [ { command = "ALL"; options = (if cfg.wheelNeedsPassword then [ "SETENV" ] else [ "NOPASSWD" "SETENV" ]); } ];
+      }
+    ];
+
     security.sudo.configFile =
       ''
         # Don't edit this file. Set the NixOS options ‘security.sudo.configFile’
-        # or ‘security.sudo.extraConfig’ instead.
+        # or ‘security.sudo.extraRules’ instead.
 
         # Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so can do its magic.
         Defaults env_keep+=SSH_AUTH_SOCK
@@ -72,8 +185,18 @@ in
         # "root" is allowed to do anything.
         root        ALL=(ALL:ALL) SETENV: ALL
 
-        # Users in the "wheel" group can do anything.
-        %wheel      ALL=(ALL:ALL) ${if cfg.wheelNeedsPassword then "" else "NOPASSWD: ALL, "}SETENV: ALL
+        # extraRules
+        ${concatStringsSep "\n" (
+          lists.flatten (
+            map (
+              rule: if (length rule.commands != 0) then [
+                (map (user: "${toUserString user}	${rule.host}=(${rule.runAs})	${toCommandsString rule.commands}") rule.users)
+                (map (group: "${toGroupString group}	${rule.host}=(${rule.runAs})	${toCommandsString rule.commands}") rule.groups)
+              ] else []
+            ) cfg.extraRules
+          )
+        )}
+
         ${cfg.extraConfig}
       '';
 
diff --git a/nixos/modules/services/computing/slurm/slurm.nix b/nixos/modules/services/computing/slurm/slurm.nix
index fb91a29a40002..45d34f5b76f50 100644
--- a/nixos/modules/services/computing/slurm/slurm.nix
+++ b/nixos/modules/services/computing/slurm/slurm.nix
@@ -6,14 +6,20 @@ let
 
   cfg = config.services.slurm;
   # configuration file can be generated by http://slurm.schedmd.com/configurator.html
-  configFile = pkgs.writeText "slurm.conf" 
+  configFile = pkgs.writeText "slurm.conf"
     ''
       ${optionalString (cfg.controlMachine != null) ''controlMachine=${cfg.controlMachine}''}
       ${optionalString (cfg.controlAddr != null) ''controlAddr=${cfg.controlAddr}''}
       ${optionalString (cfg.nodeName != null) ''nodeName=${cfg.nodeName}''}
       ${optionalString (cfg.partitionName != null) ''partitionName=${cfg.partitionName}''}
+      PlugStackConfig=${plugStackConfig}
       ${cfg.extraConfig}
     '';
+
+  plugStackConfig = pkgs.writeText "plugstack.conf"
+    ''
+      ${optionalString cfg.enableSrunX11 ''optional ${pkgs.slurm-spank-x11}/lib/x11.so''}
+    '';
 in
 
 {
@@ -28,7 +34,7 @@ in
         enable = mkEnableOption "slurm control daemon";
 
       };
-      
+
       client = {
         enable = mkEnableOption "slurm rlient daemon";
 
@@ -86,8 +92,19 @@ in
         '';
       };
 
+      enableSrunX11 = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If enabled srun will accept the option "--x11" to allow for X11 forwarding
+          from within an interactive session or a batch job. This activates the
+          slurm-spank-x11 module. Note that this requires 'services.openssh.forwardX11'
+          to be enabled on the compute nodes.
+        '';
+      };
+
       extraConfig = mkOption {
-        default = ""; 
+        default = "";
         type = types.lines;
         description = ''
           Extra configuration options that will be added verbatim at
@@ -134,7 +151,8 @@ in
     environment.systemPackages = [ wrappedSlurm ];
 
     systemd.services.slurmd = mkIf (cfg.client.enable) {
-      path = with pkgs; [ wrappedSlurm coreutils ];
+      path = with pkgs; [ wrappedSlurm coreutils ]
+        ++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
 
       wantedBy = [ "multi-user.target" ];
       after = [ "systemd-tmpfiles-clean.service" ];
@@ -152,8 +170,9 @@ in
     };
 
     systemd.services.slurmctld = mkIf (cfg.server.enable) {
-      path = with pkgs; [ wrappedSlurm munge coreutils ];
-      
+      path = with pkgs; [ wrappedSlurm munge coreutils ]
+        ++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
+
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" "munged.service" ];
       requires = [ "munged.service" ];
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
index 36d5340a306fa..5b73905035520 100644
--- a/nixos/modules/services/databases/mysql.nix
+++ b/nixos/modules/services/databases/mysql.nix
@@ -289,10 +289,10 @@ in
                     # Create initial databases
                     if ! test -e "${cfg.dataDir}/${database.name}"; then
                         echo "Creating initial database: ${database.name}"
-                        ( echo "create database ${database.name};"
+                        ( echo "create database `${database.name}`;"
 
                           ${optionalString (database ? "schema") ''
-                          echo "use ${database.name};"
+                          echo "use `${database.name}`;"
 
                           if [ -f "${database.schema}" ]
                           then
diff --git a/nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix b/nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix
new file mode 100644
index 0000000000000..2740a22c7ca0d
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix
@@ -0,0 +1,27 @@
+# Chrome GNOME Shell native host connector.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+  options = {
+    services.gnome3.chrome-gnome-shell.enable = mkEnableOption ''
+      Chrome GNOME Shell native host connector, a DBus service
+      allowing to install GNOME Shell extensions from a web browser.
+    '';
+  };
+
+
+  ###### implementation
+  config = mkIf config.services.gnome3.chrome-gnome-shell.enable {
+    environment.etc = {
+      "chromium/native-messaging-hosts/org.gnome.chrome_gnome_shell.json".source = "${pkgs.chrome-gnome-shell}/etc/chromium/native-messaging-hosts/org.gnome.chrome_gnome_shell.json";
+      "opt/chrome/native-messaging-hosts/org.gnome.chrome_gnome_shell.json".source = "${pkgs.chrome-gnome-shell}/etc/opt/chrome/native-messaging-hosts/org.gnome.chrome_gnome_shell.json";
+    };
+
+    environment.systemPackages = [ pkgs.chrome-gnome-shell ];
+
+    services.dbus.packages = [ pkgs.chrome-gnome-shell ];
+  };
+}
diff --git a/nixos/modules/services/hardware/80-net-setup-link.rules b/nixos/modules/services/hardware/80-net-setup-link.rules
new file mode 100644
index 0000000000000..18547f170a3f9
--- /dev/null
+++ b/nixos/modules/services/hardware/80-net-setup-link.rules
@@ -0,0 +1,13 @@
+# Copied from systemd 203.
+ACTION=="remove", GOTO="net_name_slot_end"
+SUBSYSTEM!="net", GOTO="net_name_slot_end"
+NAME!="", GOTO="net_name_slot_end"
+
+IMPORT{cmdline}="net.ifnames"
+ENV{net.ifnames}=="0", GOTO="net_name_slot_end"
+
+NAME=="", ENV{ID_NET_NAME_ONBOARD}!="", NAME="$env{ID_NET_NAME_ONBOARD}"
+NAME=="", ENV{ID_NET_NAME_SLOT}!="", NAME="$env{ID_NET_NAME_SLOT}"
+NAME=="", ENV{ID_NET_NAME_PATH}!="", NAME="$env{ID_NET_NAME_PATH}"
+
+LABEL="net_name_slot_end"
diff --git a/nixos/modules/services/hardware/acpid.nix b/nixos/modules/services/hardware/acpid.nix
index bb17c8859d842..f69706ebff347 100644
--- a/nixos/modules/services/hardware/acpid.nix
+++ b/nixos/modules/services/hardware/acpid.nix
@@ -31,7 +31,7 @@ let
           ''
             fn=$out/${name}
             echo "event=${handler.event}" > $fn
-            echo "action=${pkgs.writeScript "${name}.sh" (concatStringsSep "\n" [ "#! ${pkgs.bash}/bin/sh" handler.action ])}" >> $fn
+            echo "action=${pkgs.writeShellScriptBin "${name}.sh" handler.action }/bin/${name}.sh '%e'" >> $fn
           '';
         in concatStringsSep "\n" (mapAttrsToList f (canonicalHandlers // config.services.acpid.handlers))
       }
@@ -69,11 +69,33 @@ in
           };
         });
 
-        description = "Event handlers.";
-        default = {};
-        example = { mute = { event = "button/mute.*"; action = "amixer set Master toggle"; }; };
-
+        description = ''
+          Event handlers.
 
+          <note><para>
+            Handler can be a single command.
+          </para></note>
+        '';
+        default = {};
+        example = {
+          ac-power = {
+            event = "ac_adapter/*";
+            action = ''
+              vals=($1)  # space separated string to array of multiple values
+              case ''${vals[3]} in
+                  00000000)
+                      echo unplugged >> /tmp/acpi.log
+                      ;;
+                  00000001)
+                      echo plugged in >> /tmp/acpi.log
+                      ;;
+                  *)
+                      echo unknown >> /tmp/acpi.log
+                      ;;
+              esac
+            '';
+          };
+        };
       };
 
       powerEventCommands = mkOption {
diff --git a/nixos/modules/services/hardware/amd-hybrid-graphics.nix b/nixos/modules/services/hardware/amd-hybrid-graphics.nix
deleted file mode 100644
index b0f9ff56d1b2c..0000000000000
--- a/nixos/modules/services/hardware/amd-hybrid-graphics.nix
+++ /dev/null
@@ -1,46 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-{
-
-  ###### interface
-
-  options = {
-
-    hardware.amdHybridGraphics.disable = lib.mkOption {
-      default = false;
-      type = lib.types.bool;
-      description = ''
-        Completely disable the AMD graphics card and use the
-        integrated graphics processor instead.
-      '';
-    };
-
-  };
-
-
-  ###### implementation
-
-  config = lib.mkIf config.hardware.amdHybridGraphics.disable {
-    systemd.services."amd-hybrid-graphics" = {
-      path = [ pkgs.bash ];
-      description = "Disable AMD Card";
-      after = [ "sys-kernel-debug.mount" ];
-      before = [ "systemd-vconsole-setup.service" "display-manager.service" ];
-      requires = [ "sys-kernel-debug.mount" "vgaswitcheroo.path" ];
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
-        ExecStart = "${pkgs.bash}/bin/sh -c 'echo -e \"IGD\\nOFF\" > /sys/kernel/debug/vgaswitcheroo/switch'";
-        ExecStop = "${pkgs.bash}/bin/sh -c 'echo ON >/sys/kernel/debug/vgaswitcheroo/switch'";
-      };
-    };
-    systemd.paths."vgaswitcheroo" = {
-      pathConfig = {
-        PathExists = "/sys/kernel/debug/vgaswitcheroo/switch";
-        Unit = "amd-hybrid-graphics.service";
-      };
-      wantedBy = ["multi-user.target"];
-    };
-  };
-
-}
diff --git a/nixos/modules/services/hardware/nvidia-optimus.nix b/nixos/modules/services/hardware/nvidia-optimus.nix
index 9fe4021c42474..eb1713baa140e 100644
--- a/nixos/modules/services/hardware/nvidia-optimus.nix
+++ b/nixos/modules/services/hardware/nvidia-optimus.nix
@@ -23,7 +23,7 @@ let kernel = config.boot.kernelPackages; in
   ###### implementation
 
   config = lib.mkIf config.hardware.nvidiaOptimus.disable {
-    boot.blacklistedKernelModules = ["nouveau" "nvidia" "nvidiafb"];
+    boot.blacklistedKernelModules = ["nouveau" "nvidia" "nvidiafb" "nvidia-drm"];
     boot.kernelModules = [ "bbswitch" ];
     boot.extraModulePackages = [ kernel.bbswitch ];
 
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
index 730e538e72f61..9f42f9e59ad5e 100644
--- a/nixos/modules/services/hardware/udev.nix
+++ b/nixos/modules/services/hardware/udev.nix
@@ -119,7 +119,7 @@ let
       fi
 
       ${optionalString config.networking.usePredictableInterfaceNames ''
-        cp ${udev}/lib/udev/rules.d/80-net-setup-link.rules $out/80-net-setup-link.rules
+        cp ${./80-net-setup-link.rules} $out/80-net-setup-link.rules
       ''}
 
       # If auto-configuration is disabled, then remove
diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix
index 18101a3122544..b42c73b866687 100644
--- a/nixos/modules/services/mail/dovecot.nix
+++ b/nixos/modules/services/mail/dovecot.nix
@@ -104,7 +104,7 @@ let
   };
 
   mailboxConfig = mailbox: ''
-    mailbox ${mailbox.name} {
+    mailbox "${mailbox.name}" {
       auto = ${toString mailbox.auto}
   '' + optionalString (mailbox.specialUse != null) ''
       special_use = \${toString mailbox.specialUse}
@@ -113,7 +113,7 @@ let
   mailboxes = { lib, pkgs, ... }: {
     options = {
       name = mkOption {
-        type = types.str;
+        type = types.strMatching ''[^"]+'';
         example = "Spam";
         description = "The name of the mailbox.";
       };
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
index 867c0ea6761c5..22af7e876af25 100644
--- a/nixos/modules/services/mail/postfix.nix
+++ b/nixos/modules/services/mail/postfix.nix
@@ -15,20 +15,18 @@ let
   haveVirtual = cfg.virtual != "";
 
   clientAccess =
-    if (cfg.dnsBlacklistOverrides != "")
-    then [ "check_client_access hash:/etc/postfix/client_access" ]
-    else [];
+    optional (cfg.dnsBlacklistOverrides != "")
+      "check_client_access hash:/etc/postfix/client_access";
 
   dnsBl =
-    if (cfg.dnsBlacklists != [])
-    then [ (concatStringsSep ", " (map (s: "reject_rbl_client " + s) cfg.dnsBlacklists)) ]
-    else [];
+    optionals (cfg.dnsBlacklists != [])
+      (map (s: "reject_rbl_client " + s) cfg.dnsBlacklists);
 
   clientRestrictions = concatStringsSep ", " (clientAccess ++ dnsBl);
 
   mainCf = let
     escape = replaceStrings ["$"] ["$$"];
-    mkList = items: "\n  " + concatStringsSep "\n  " items;
+    mkList = items: "\n  " + concatStringsSep ",\n  " items;
     mkVal = value:
       if isList value then mkList value
         else " " + (if value == true then "yes"
@@ -36,72 +34,9 @@ let
         else toString value);
     mkEntry = name: value: "${escape name} =${mkVal value}";
   in
-    concatStringsSep "\n" (mapAttrsToList mkEntry (recursiveUpdate defaultConf cfg.config))
+    concatStringsSep "\n" (mapAttrsToList mkEntry cfg.config)
       + "\n" + cfg.extraConfig;
 
-  defaultConf = {
-    compatibility_level  = "9999";
-    mail_owner           = user;
-    default_privs        = "nobody";
-
-    # NixOS specific locations
-    data_directory       = "/var/lib/postfix/data";
-    queue_directory      = "/var/lib/postfix/queue";
-
-    # Default location of everything in package
-    meta_directory       = "${pkgs.postfix}/etc/postfix";
-    command_directory    = "${pkgs.postfix}/bin";
-    sample_directory     = "/etc/postfix";
-    newaliases_path      = "${pkgs.postfix}/bin/newaliases";
-    mailq_path           = "${pkgs.postfix}/bin/mailq";
-    readme_directory     = false;
-    sendmail_path        = "${pkgs.postfix}/bin/sendmail";
-    daemon_directory     = "${pkgs.postfix}/libexec/postfix";
-    manpage_directory    = "${pkgs.postfix}/share/man";
-    html_directory       = "${pkgs.postfix}/share/postfix/doc/html";
-    shlib_directory      = false;
-    relayhost            = if cfg.relayHost == "" then "" else
-                             if cfg.lookupMX
-                             then "${cfg.relayHost}:${toString cfg.relayPort}"
-                             else "[${cfg.relayHost}]:${toString cfg.relayPort}";
-
-    mail_spool_directory = "/var/spool/mail/";
-    setgid_group         = setgidGroup;
-  }
-  // optionalAttrs config.networking.enableIPv6 { inet_protocols = "all"; }
-  // optionalAttrs (cfg.networks != null) { mynetworks = cfg.networks; }
-  // optionalAttrs (cfg.networksStyle != "") { mynetworks_style = cfg.networksStyle; }
-  // optionalAttrs (cfg.hostname != "") { myhostname = cfg.hostname; }
-  // optionalAttrs (cfg.domain != "") { mydomain = cfg.domain; }
-  // optionalAttrs (cfg.origin != "") { myorigin =  cfg.origin; }
-  // optionalAttrs (cfg.destination != null) { mydestination = cfg.destination; }
-  // optionalAttrs (cfg.relayDomains != null) { relay_domains = cfg.relayDomains; }
-  // optionalAttrs (cfg.recipientDelimiter != "") { recipient_delimiter = cfg.recipientDelimiter; }
-  // 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 (cfg.dnsBlacklists != []) { smtpd_client_restrictions = clientRestrictions; }
-  // optionalAttrs cfg.useSrs {
-    sender_canonical_maps = "tcp:127.0.0.1:10001";
-    sender_canonical_classes = "envelope_sender";
-    recipient_canonical_maps = "tcp:127.0.0.1:10002";
-    recipient_canonical_classes= "envelope_recipient";
-  }
-  // optionalAttrs cfg.enableHeaderChecks { header_checks = "regexp:/etc/postfix/header_checks"; }
-  // optionalAttrs (cfg.sslCert != "") {
-    smtp_tls_CAfile = cfg.sslCACert;
-    smtp_tls_cert_file = cfg.sslCert;
-    smtp_tls_key_file = cfg.sslKey;
-
-    smtp_use_tls = true;
-
-    smtpd_tls_CAfile = cfg.sslCACert;
-    smtpd_tls_cert_file = cfg.sslCert;
-    smtpd_tls_key_file = cfg.sslKey;
-
-    smtpd_use_tls = true;
-  };
-
   masterCfOptions = { options, config, name, ... }: {
     options = {
       name = mkOption {
@@ -507,7 +442,6 @@ in
 
       config = mkOption {
         type = with types; attrsOf (either bool (either str (listOf str)));
-        default = defaultConf;
         description = ''
           The main.cf configuration file as key value set.
         '';
@@ -749,6 +683,67 @@ in
           '';
         };
 
+      services.postfix.config = (mapAttrs (_: v: mkDefault v) {
+        compatibility_level  = "9999";
+        mail_owner           = cfg.user;
+        default_privs        = "nobody";
+
+        # NixOS specific locations
+        data_directory       = "/var/lib/postfix/data";
+        queue_directory      = "/var/lib/postfix/queue";
+
+        # Default location of everything in package
+        meta_directory       = "${pkgs.postfix}/etc/postfix";
+        command_directory    = "${pkgs.postfix}/bin";
+        sample_directory     = "/etc/postfix";
+        newaliases_path      = "${pkgs.postfix}/bin/newaliases";
+        mailq_path           = "${pkgs.postfix}/bin/mailq";
+        readme_directory     = false;
+        sendmail_path        = "${pkgs.postfix}/bin/sendmail";
+        daemon_directory     = "${pkgs.postfix}/libexec/postfix";
+        manpage_directory    = "${pkgs.postfix}/share/man";
+        html_directory       = "${pkgs.postfix}/share/postfix/doc/html";
+        shlib_directory      = false;
+        mail_spool_directory = "/var/spool/mail/";
+        setgid_group         = cfg.setgidGroup;
+      })
+      // optionalAttrs (cfg.relayHost != "") { relayhost = if cfg.lookupMX
+                                                           then "${cfg.relayHost}:${toString cfg.relayPort}"
+                                                           else "[${cfg.relayHost}]:${toString cfg.relayPort}"; }
+      // optionalAttrs config.networking.enableIPv6 { inet_protocols = mkDefault "all"; }
+      // optionalAttrs (cfg.networks != null) { mynetworks = cfg.networks; }
+      // optionalAttrs (cfg.networksStyle != "") { mynetworks_style = cfg.networksStyle; }
+      // optionalAttrs (cfg.hostname != "") { myhostname = cfg.hostname; }
+      // optionalAttrs (cfg.domain != "") { mydomain = cfg.domain; }
+      // optionalAttrs (cfg.origin != "") { myorigin =  cfg.origin; }
+      // optionalAttrs (cfg.destination != null) { mydestination = cfg.destination; }
+      // optionalAttrs (cfg.relayDomains != null) { relay_domains = cfg.relayDomains; }
+      // optionalAttrs (cfg.recipientDelimiter != "") { recipient_delimiter = cfg.recipientDelimiter; }
+      // 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 (cfg.dnsBlacklists != []) { smtpd_client_restrictions = clientRestrictions; }
+      // optionalAttrs cfg.useSrs {
+        sender_canonical_maps = [ "tcp:127.0.0.1:10001" ];
+        sender_canonical_classes = [ "envelope_sender" ];
+        recipient_canonical_maps = [ "tcp:127.0.0.1:10002" ];
+        recipient_canonical_classes = [ "envelope_recipient" ];
+      }
+      // optionalAttrs cfg.enableHeaderChecks { header_checks = [ "regexp:/etc/postfix/header_checks" ]; }
+      // optionalAttrs (cfg.sslCert != "") {
+        smtp_tls_CAfile = cfg.sslCACert;
+        smtp_tls_cert_file = cfg.sslCert;
+        smtp_tls_key_file = cfg.sslKey;
+
+        smtp_use_tls = true;
+
+        smtpd_tls_CAfile = cfg.sslCACert;
+        smtpd_tls_cert_file = cfg.sslCert;
+        smtpd_tls_key_file = cfg.sslKey;
+
+        smtpd_use_tls = true;
+      };
+
       services.postfix.masterConfig = {
         smtp_inet = {
           name = "smtp";
diff --git a/nixos/modules/services/mail/rspamd.nix b/nixos/modules/services/mail/rspamd.nix
index b80aa48f2c865..09fb587e74b56 100644
--- a/nixos/modules/services/mail/rspamd.nix
+++ b/nixos/modules/services/mail/rspamd.nix
@@ -1,14 +1,152 @@
-{ config, lib, pkgs, ... }:
+{ config, options, pkgs, lib, ... }:
 
 with lib;
 
 let
 
   cfg = config.services.rspamd;
+  opts = options.services.rspamd;
 
-  mkBindSockets = socks: concatStringsSep "\n" (map (each: "  bind_socket = \"${each}\"") socks);
+  bindSocketOpts = {options, config, ... }: {
+    options = {
+      socket = mkOption {
+        type = types.str;
+        example = "localhost:11333";
+        description = ''
+          Socket for this worker to listen on in a format acceptable by rspamd.
+        '';
+      };
+      mode = mkOption {
+        type = types.str;
+        default = "0644";
+        description = "Mode to set on unix socket";
+      };
+      owner = mkOption {
+        type = types.str;
+        default = "${cfg.user}";
+        description = "Owner to set on unix socket";
+      };
+      group = mkOption {
+        type = types.str;
+        default = "${cfg.group}";
+        description = "Group to set on unix socket";
+      };
+      rawEntry = mkOption {
+        type = types.str;
+        internal = true;
+      };
+    };
+    config.rawEntry = let
+      maybeOption = option:
+        optionalString options.${option}.isDefined " ${option}=${config.${option}}";
+    in
+      if (!(hasPrefix "/" config.socket)) then "${config.socket}"
+      else "${config.socket}${maybeOption "mode"}${maybeOption "owner"}${maybeOption "group"}";
+  };
 
-   rspamdConfFile = pkgs.writeText "rspamd.conf"
+  workerOpts = { name, ... }: {
+    options = {
+      enable = mkOption {
+        type = types.nullOr types.bool;
+        default = null;
+        description = "Whether to run the rspamd worker.";
+      };
+      name = mkOption {
+        type = types.nullOr types.str;
+        default = name;
+        description = "Name of the worker";
+      };
+      type = mkOption {
+        type = types.nullOr (types.enum [
+          "normal" "controller" "fuzzy_storage" "proxy" "lua"
+        ]);
+        description = "The type of this worker";
+      };
+      bindSockets = mkOption {
+        type = types.listOf (types.either types.str (types.submodule bindSocketOpts));
+        default = [];
+        description = ''
+          List of sockets to listen, in format acceptable by rspamd
+        '';
+        example = [{
+          socket = "/run/rspamd.sock";
+          mode = "0666";
+          owner = "rspamd";
+        } "*:11333"];
+        apply = value: map (each: if (isString each)
+          then if (isUnixSocket each)
+            then {socket = each; owner = cfg.user; group = cfg.group; mode = "0644"; rawEntry = "${each}";}
+            else {socket = each; rawEntry = "${each}";}
+          else each) value;
+      };
+      count = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Number of worker instances to run
+        '';
+      };
+      includes = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          List of files to include in configuration
+        '';
+      };
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Additional entries to put verbatim into worker section of rspamd config file.";
+      };
+    };
+    config = mkIf (name == "normal" || name == "controller" || name == "fuzzy") {
+      type = mkDefault name;
+      includes = mkDefault [ "$CONFDIR/worker-${name}.inc" ];
+      bindSockets = mkDefault (if name == "normal"
+        then [{
+              socket = "/run/rspamd/rspamd.sock";
+              mode = "0660";
+              owner = cfg.user;
+              group = cfg.group;
+            }]
+        else if name == "controller"
+        then [ "localhost:11334" ]
+        else [] );
+    };
+  };
+
+  indexOf = default: start: list: e:
+    if list == []
+    then default
+    else if (head list) == e then start
+    else (indexOf default (start + (length (listenStreams (head list).socket))) (tail list) e);
+
+  systemdSocket = indexOf (abort "Socket not found") 0 allSockets;
+
+  isUnixSocket = socket: hasPrefix "/" (if (isString socket) then socket else socket.socket);
+  isPort = hasPrefix "*:";
+  isIPv4Socket = hasPrefix "*v4:";
+  isIPv6Socket = hasPrefix "*v6:";
+  isLocalHost = hasPrefix "localhost:";
+  listenStreams = socket:
+    if (isLocalHost socket) then
+      let port = (removePrefix "localhost:" socket);
+      in [ "127.0.0.1:${port}" ] ++ (if config.networking.enableIPv6 then ["[::1]:${port}"] else [])
+    else if (isIPv6Socket socket) then [removePrefix "*v6:" socket]
+    else if (isPort socket) then [removePrefix "*:" socket]
+    else if (isIPv4Socket socket) then
+      throw "error: IPv4 only socket not supported in rspamd with socket activation"
+    else if (length (splitString " " socket)) != 1 then
+      throw "error: string options not supported in rspamd with socket activation"
+    else [socket];
+
+  mkBindSockets = enabled: socks: concatStringsSep "\n  " (flatten (map (each:
+    if cfg.socketActivation && enabled != false then
+      let systemd = (systemdSocket each);
+      in (imap (idx: e: "bind_socket = \"systemd:${toString (systemd + idx - 1)}\";") (listenStreams each.socket))
+    else "bind_socket = \"${each.rawEntry}\";") socks));
+
+  rspamdConfFile = pkgs.writeText "rspamd.conf"
     ''
       .include "$CONFDIR/common.conf"
 
@@ -22,19 +160,33 @@ let
         .include "$CONFDIR/logging.inc"
       }
 
-      worker {
-      ${mkBindSockets cfg.bindSocket}
-        .include "$CONFDIR/worker-normal.inc"
-      }
-
-      worker {
-      ${mkBindSockets cfg.bindUISocket}
-        .include "$CONFDIR/worker-controller.inc"
-      }
+      ${concatStringsSep "\n" (mapAttrsToList (name: value: ''
+        worker ${optionalString (value.name != "normal" && value.name != "controller") "${value.name}"} {
+          type = "${value.type}";
+          ${optionalString (value.enable != null)
+            "enabled = ${if value.enable != false then "yes" else "no"};"}
+          ${mkBindSockets value.enable value.bindSockets}
+          ${optionalString (value.count != null) "count = ${toString value.count};"}
+          ${concatStringsSep "\n  " (map (each: ".include \"${each}\"") value.includes)}
+          ${value.extraConfig}
+        }
+      '') cfg.workers)}
 
       ${cfg.extraConfig}
    '';
 
+  allMappedSockets = flatten (mapAttrsToList (name: value:
+    if value.enable != false
+    then imap (idx: each: {
+        name = "${name}";
+        index = idx;
+        value = each;
+      }) value.bindSockets
+    else []) cfg.workers);
+  allSockets = map (e: e.value) allMappedSockets;
+
+  allSocketNames = map (each: "rspamd-${each.name}-${toString each.index}.socket") allMappedSockets;
+
 in
 
 {
@@ -48,36 +200,43 @@ in
       enable = mkEnableOption "Whether to run the rspamd daemon.";
 
       debug = mkOption {
+        type = types.bool;
         default = false;
         description = "Whether to run the rspamd daemon in debug mode.";
       };
 
-      bindSocket = mkOption {
-        type = types.listOf types.str;
-        default = [
-          "/run/rspamd/rspamd.sock mode=0660 owner=${cfg.user} group=${cfg.group}"
-        ];
-        defaultText = ''[
-          "/run/rspamd/rspamd.sock mode=0660 owner=${cfg.user} group=${cfg.group}"
-        ]'';
+      socketActivation = mkOption {
+        type = types.bool;
         description = ''
-          List of sockets to listen, in format acceptable by rspamd
-        '';
-        example = ''
-          bindSocket = [
-            "/run/rspamd.sock mode=0666 owner=rspamd"
-            "*:11333"
-          ];
+          Enable systemd socket activation for rspamd.
         '';
       };
 
-      bindUISocket = mkOption {
-        type = types.listOf types.str;
-        default = [
-          "localhost:11334"
-        ];
+      workers = mkOption {
+        type = with types; attrsOf (submodule workerOpts);
         description = ''
-          List of sockets for web interface, in format acceptable by rspamd
+          Attribute set of workers to start.
+        '';
+        default = {
+          normal = {};
+          controller = {};
+        };
+        example = literalExample ''
+          {
+            normal = {
+              includes = [ "$CONFDIR/worker-normal.inc" ];
+              bindSockets = [{
+                socket = "/run/rspamd/rspamd.sock";
+                mode = "0660";
+                owner = "${cfg.user}";
+                group = "${cfg.group}";
+              }];
+            };
+            controller = {
+              includes = [ "$CONFDIR/worker-controller.inc" ];
+              bindSockets = [ "[::1]:11334" ];
+            };
+          }
         '';
       };
 
@@ -113,6 +272,13 @@ in
 
   config = mkIf cfg.enable {
 
+    services.rspamd.socketActivation = mkDefault (!opts.bindSocket.isDefined && !opts.bindUISocket.isDefined);
+
+    assertions = [ {
+      assertion = !cfg.socketActivation || !(opts.bindSocket.isDefined || opts.bindUISocket.isDefined);
+      message = "Can't use socketActivation for rspamd when using renamed bind socket options";
+    } ];
+
     # Allow users to run 'rspamc' and 'rspamadm'.
     environment.systemPackages = [ pkgs.rspamd ];
 
@@ -128,17 +294,22 @@ in
       gid = config.ids.gids.rspamd;
     };
 
+    environment.etc."rspamd.conf".source = rspamdConfFile;
+
     systemd.services.rspamd = {
       description = "Rspamd Service";
 
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
+      wantedBy = mkIf (!cfg.socketActivation) [ "multi-user.target" ];
+      after = [ "network.target" ] ++
+       (if cfg.socketActivation then allSocketNames else []);
+      requires = mkIf cfg.socketActivation allSocketNames;
 
       serviceConfig = {
         ExecStart = "${pkgs.rspamd}/bin/rspamd ${optionalString cfg.debug "-d"} --user=${cfg.user} --group=${cfg.group} --pid=/run/rspamd.pid -c ${rspamdConfFile} -f";
         Restart = "always";
         RuntimeDirectory = "rspamd";
         PrivateTmp = true;
+        Sockets = mkIf cfg.socketActivation (concatStringsSep " " allSocketNames);
       };
 
       preStart = ''
@@ -146,5 +317,25 @@ in
         ${pkgs.coreutils}/bin/chown ${cfg.user}:${cfg.group} /var/lib/rspamd
       '';
     };
+    systemd.sockets = mkIf cfg.socketActivation
+      (listToAttrs (map (each: {
+        name = "rspamd-${each.name}-${toString each.index}";
+        value = {
+          description = "Rspamd socket ${toString each.index} for worker ${each.name}";
+          wantedBy = [ "sockets.target" ];
+          listenStreams = (listenStreams each.value.socket);
+          socketConfig = {
+            BindIPv6Only = mkIf (isIPv6Socket each.value.socket) "ipv6-only";
+            Service = "rspamd.service";
+            SocketUser = mkIf (isUnixSocket each.value.socket) each.value.owner;
+            SocketGroup = mkIf (isUnixSocket each.value.socket) each.value.group;
+            SocketMode = mkIf (isUnixSocket each.value.socket) each.value.mode;
+          };
+        };
+      }) allMappedSockets));
   };
+  imports = [
+    (mkRenamedOptionModule [ "services" "rspamd" "bindSocket" ] [ "services" "rspamd" "workers" "normal" "bindSockets" ])
+    (mkRenamedOptionModule [ "services" "rspamd" "bindUISocket" ] [ "services" "rspamd" "workers" "controller" "bindSockets" ])
+  ];
 }
diff --git a/nixos/modules/services/misc/home-assistant.nix b/nixos/modules/services/misc/home-assistant.nix
new file mode 100644
index 0000000000000..cc60a143fa6c3
--- /dev/null
+++ b/nixos/modules/services/misc/home-assistant.nix
@@ -0,0 +1,135 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.home-assistant;
+
+  configFile = pkgs.writeText "configuration.yaml" (builtins.toJSON cfg.config);
+
+  availableComponents = pkgs.home-assistant.availableComponents;
+
+  # Given component "parentConfig.platform", returns whether config.parentConfig
+  # is a list containing a set with set.platform == "platform".
+  #
+  # For example, the component sensor.luftdaten is used as follows:
+  # config.sensor = [ {
+  #   platform = "luftdaten";
+  #   ...
+  # } ];
+  useComponentPlatform = component:
+    let
+      path = splitString "." component;
+      parentConfig = attrByPath (init path) null cfg.config;
+      platform = last path;
+    in isList parentConfig && any
+      (item: item.platform or null == platform)
+      parentConfig;
+
+  # Returns whether component is used in config
+  useComponent = component:
+    hasAttrByPath (splitString "." component) cfg.config
+    || useComponentPlatform component;
+
+  # List of components used in config
+  extraComponents = filter useComponent availableComponents;
+
+  package = if cfg.autoExtraComponents
+    then (cfg.package.override { inherit extraComponents; })
+    else cfg.package;
+
+in {
+  meta.maintainers = with maintainers; [ dotlambda ];
+
+  options.services.home-assistant = {
+    enable = mkEnableOption "Home Assistant";
+
+    configDir = mkOption {
+      default = "/var/lib/hass";
+      type = types.path;
+      description = "The config directory, where your <filename>configuration.yaml</filename> is located.";
+    };
+
+    config = mkOption {
+      default = null;
+      type = with types; nullOr attrs;
+      example = literalExample ''
+        {
+          homeassistant = {
+            name = "Home";
+            time_zone = "UTC";
+          };
+          frontend = { };
+          http = { };
+          feedreader.urls = [ "https://nixos.org/blogs.xml" ];
+        }
+      '';
+      description = ''
+        Your <filename>configuration.yaml</filename> as a Nix attribute set.
+        Beware that setting this option will delete your previous <filename>configuration.yaml</filename>.
+      '';
+    };
+
+    package = mkOption {
+      default = pkgs.home-assistant;
+      defaultText = "pkgs.home-assistant";
+      type = types.package;
+      example = literalExample ''
+        pkgs.home-assistant.override {
+          extraPackages = ps: with ps; [ colorlog ];
+        }
+      '';
+      description = ''
+        Home Assistant package to use.
+        Override <literal>extraPackages</literal> in order to add additional dependencies.
+      '';
+    };
+
+    autoExtraComponents = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        If set to <literal>true</literal>, the components used in <literal>config</literal>
+        are set as the specified package's <literal>extraComponents</literal>.
+        This in turn adds all packaged dependencies to the derivation.
+        You might still see import errors in your log.
+        In this case, you will need to package the necessary dependencies yourself
+        or ask for someone else to package them.
+        If a dependency is packaged but not automatically added to this list,
+        you might need to specify it in <literal>extraPackages</literal>.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.home-assistant = {
+      description = "Home Assistant";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      preStart = lib.optionalString (cfg.config != null) ''
+        rm -f ${cfg.configDir}/configuration.yaml
+        ln -s ${configFile} ${cfg.configDir}/configuration.yaml
+      '';
+      serviceConfig = {
+        ExecStart = ''
+          ${package}/bin/hass --config "${cfg.configDir}"
+        '';
+        User = "hass";
+        Group = "hass";
+        Restart = "on-failure";
+        ProtectSystem = "strict";
+        ReadWritePaths = "${cfg.configDir}";
+        PrivateTmp = true;
+      };
+    };
+
+    users.extraUsers.hass = {
+      home = cfg.configDir;
+      createHome = true;
+      group = "hass";
+      uid = config.ids.uids.hass;
+    };
+
+    users.extraGroups.hass.gid = config.ids.gids.hass;
+  };
+}
diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix
index 80979547d3392..7e880ad09b896 100644
--- a/nixos/modules/services/misc/matrix-synapse.nix
+++ b/nixos/modules/services/misc/matrix-synapse.nix
@@ -4,6 +4,8 @@ with lib;
 
 let
   cfg = config.services.matrix-synapse;
+  pg = config.services.postgresql;
+  usePostgresql = cfg.database_type == "psycopg2";
   logConfigFile = pkgs.writeText "log_config.yaml" cfg.logConfig;
   mkResource = r: ''{names: ${builtins.toJSON r.names}, compress: ${boolToString r.compress}}'';
   mkListener = l: ''{port: ${toString l.port}, bind_address: "${l.bind_address}", type: ${l.type}, tls: ${boolToString l.tls}, x_forwarded: ${boolToString l.x_forwarded}, resources: [${concatStringsSep "," (map mkResource l.resources)}]}'';
@@ -38,7 +40,7 @@ database: {
   name: "${cfg.database_type}",
   args: {
     ${concatStringsSep ",\n    " (
-      mapAttrsToList (n: v: "\"${n}\": ${v}") cfg.database_args
+      mapAttrsToList (n: v: "\"${n}\": ${builtins.toJSON v}") cfg.database_args
     )}
   }
 }
@@ -155,7 +157,7 @@ in {
       tls_certificate_path = mkOption {
         type = types.nullOr types.str;
         default = null;
-        example = "/var/lib/matrix-synapse/homeserver.tls.crt";
+        example = "${cfg.dataDir}/homeserver.tls.crt";
         description = ''
           PEM encoded X509 certificate for TLS.
           You can replace the self-signed certificate that synapse
@@ -167,7 +169,7 @@ in {
       tls_private_key_path = mkOption {
         type = types.nullOr types.str;
         default = null;
-        example = "/var/lib/matrix-synapse/homeserver.tls.key";
+        example = "${cfg.dataDir}/homeserver.tls.key";
         description = ''
           PEM encoded private key for TLS. Specify null if synapse is not
           speaking TLS directly.
@@ -176,7 +178,7 @@ in {
       tls_dh_params_path = mkOption {
         type = types.nullOr types.str;
         default = null;
-        example = "/var/lib/matrix-synapse/homeserver.tls.dh";
+        example = "${cfg.dataDir}/homeserver.tls.dh";
         description = ''
           PEM dh parameters for ephemeral keys
         '';
@@ -184,6 +186,7 @@ in {
       server_name = mkOption {
         type = types.str;
         example = "example.com";
+        default = config.networking.hostName;
         description = ''
           The domain name of the server, with optional explicit port.
           This is used by remote servers to connect to this server,
@@ -339,16 +342,39 @@ in {
       };
       database_type = mkOption {
         type = types.enum [ "sqlite3" "psycopg2" ];
-        default = "sqlite3";
+        default = if versionAtLeast config.system.stateVersion "18.03"
+          then "psycopg2"
+          else "sqlite3";
         description = ''
           The database engine name. Can be sqlite or psycopg2.
         '';
       };
+      create_local_database = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to create a local database automatically.
+        '';
+      };
+      database_name = mkOption {
+        type = types.str;
+        default = "matrix-synapse";
+        description = "Database name.";
+      };
+      database_user = mkOption {
+        type = types.str;
+        default = "matrix-synapse";
+        description = "Database user name.";
+      };
       database_args = mkOption {
         type = types.attrs;
         default = {
-          database = "${cfg.dataDir}/homeserver.db";
-        };
+          sqlite3 = { database = "${cfg.dataDir}/homeserver.db"; };
+          psycopg2 = {
+            user = cfg.database_user;
+            database = cfg.database_name;
+          };
+        }."${cfg.database_type}";
         description = ''
           Arguments to pass to the engine.
         '';
@@ -623,15 +649,36 @@ in {
         gid = config.ids.gids.matrix-synapse;
       } ];
 
+    services.postgresql.enable = mkIf usePostgresql (mkDefault true);
+
     systemd.services.matrix-synapse = {
       description = "Synapse Matrix homeserver";
-      after = [ "network.target" ];
+      after = [ "network.target" "postgresql.service" ];
       wantedBy = [ "multi-user.target" ];
       preStart = ''
         ${cfg.package}/bin/homeserver \
           --config-path ${configFile} \
           --keys-directory ${cfg.dataDir} \
           --generate-keys
+      '' + optionalString (usePostgresql && cfg.create_local_database) ''
+        if ! test -e "${cfg.dataDir}/db-created"; then
+          ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \
+            ${pg.package}/bin/createuser \
+            --login \
+            --no-createdb \
+            --no-createrole \
+            --encrypted \
+            ${cfg.database_user}
+          ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \
+            ${pg.package}/bin/createdb \
+            --owner=${cfg.database_user} \
+            --encoding=UTF8 \
+            --lc-collate=C \
+            --lc-ctype=C \
+            --template=template0 \
+            ${cfg.database_name}
+          touch "${cfg.dataDir}/db-created"
+        fi
       '';
       serviceConfig = {
         Type = "simple";
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index beca820d2d602..a169b0f2c7841 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -8,7 +8,7 @@ let
 
   nix = cfg.package.out;
 
-  isNix112 = versionAtLeast (getVersion nix) "1.12pre";
+  isNix20 = versionAtLeast (getVersion nix) "2.0pre";
 
   makeNixBuildUser = nr:
     { name = "nixbld${toString nr}";
@@ -26,32 +26,40 @@ let
 
   nixConf =
     let
-      # If we're using sandbox for builds, then provide /bin/sh in
-      # the sandbox as a bind-mount to bash. This means we also need to
-      # include the entire closure of bash.
+      # In Nix < 2.0, If we're using sandbox for builds, then provide
+      # /bin/sh in the sandbox as a bind-mount to bash. This means we
+      # also need to include the entire closure of bash. Nix >= 2.0
+      # provides a /bin/sh by default.
       sh = pkgs.stdenv.shell;
       binshDeps = pkgs.writeReferencesToFile sh;
     in
-      pkgs.runCommand "nix.conf" {extraOptions = cfg.extraOptions; } ''
-        extraPaths=$(for i in $(cat ${binshDeps}); do if test -d $i; then echo $i; fi; done)
+      pkgs.runCommand "nix.conf" { extraOptions = cfg.extraOptions; inherit binshDeps; } ''
+        ${optionalString (!isNix20) ''
+          extraPaths=$(for i in $(cat binshDeps); do if test -d $i; then echo $i; fi; done)
+        ''}
         cat > $out <<END
         # WARNING: this file is generated from the nix.* options in
         # your NixOS configuration, typically
         # /etc/nixos/configuration.nix.  Do not edit it!
         build-users-group = nixbld
-        build-max-jobs = ${toString (cfg.maxJobs)}
-        build-cores = ${toString (cfg.buildCores)}
-        build-use-sandbox = ${if (builtins.isBool cfg.useSandbox) then boolToString cfg.useSandbox else cfg.useSandbox}
-        build-sandbox-paths = ${toString cfg.sandboxPaths} /bin/sh=${sh} $(echo $extraPaths)
-        binary-caches = ${toString cfg.binaryCaches}
-        trusted-binary-caches = ${toString cfg.trustedBinaryCaches}
-        binary-cache-public-keys = ${toString cfg.binaryCachePublicKeys}
+        ${if isNix20 then "max-jobs" else "build-max-jobs"} = ${toString (cfg.maxJobs)}
+        ${if isNix20 then "cores" else "build-cores"} = ${toString (cfg.buildCores)}
+        ${if isNix20 then "sandbox" else "build-use-sandbox"} = ${if (builtins.isBool cfg.useSandbox) then boolToString cfg.useSandbox else cfg.useSandbox}
+        ${if isNix20 then "extra-sandbox-paths" else "build-sandbox-paths"} = ${toString cfg.sandboxPaths} ${optionalString (!isNix20) "/bin/sh=${sh} $(echo $extraPaths)"}
+        ${if isNix20 then "substituters" else "binary-caches"} = ${toString cfg.binaryCaches}
+        ${if isNix20 then "trusted-substituters" else "trusted-binary-caches"} = ${toString cfg.trustedBinaryCaches}
+        ${if isNix20 then "trusted-public-keys" else "binary-cache-public-keys"} = ${toString cfg.binaryCachePublicKeys}
         auto-optimise-store = ${boolToString cfg.autoOptimiseStore}
-        ${optionalString cfg.requireSignedBinaryCaches ''
-          signed-binary-caches = *
+        ${if isNix20 then ''
+          require-sigs = ${if cfg.requireSignedBinaryCaches then "true" else "false"}
+        '' else ''
+          signed-binary-caches = ${if cfg.requireSignedBinaryCaches then "*" else ""}
         ''}
         trusted-users = ${toString cfg.trustedUsers}
         allowed-users = ${toString cfg.allowedUsers}
+        ${optionalString (isNix20 && !cfg.distributedBuilds) ''
+          builders =
+        ''}
         $extraOptions
         END
       '';
@@ -377,8 +385,9 @@ in
     systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ];
 
     systemd.services.nix-daemon =
-      { path = [ nix pkgs.openssl.bin pkgs.utillinux config.programs.ssh.package ]
-          ++ optionals cfg.distributedBuilds [ pkgs.gzip ];
+      { path = [ nix pkgs.utillinux ]
+          ++ optionals cfg.distributedBuilds [ config.programs.ssh.package pkgs.gzip ]
+          ++ optionals (!isNix20) [ pkgs.openssl.bin ];
 
         environment = cfg.envVars
           // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"; }
@@ -396,10 +405,9 @@ in
       };
 
     nix.envVars =
-      { NIX_CONF_DIR = "/etc/nix";
-      }
+      optionalAttrs (!isNix20) {
+        NIX_CONF_DIR = "/etc/nix";
 
-      // optionalAttrs (!isNix112) {
         # Enable the copy-from-other-stores substituter, which allows
         # builds to be sped up by copying build results from remote
         # Nix stores.  To do this, mount the remote file system on a
@@ -407,12 +415,8 @@ in
         NIX_OTHER_STORES = "/run/nix/remote-stores/*/nix";
       }
 
-      // optionalAttrs cfg.distributedBuilds {
-        NIX_BUILD_HOOK =
-          if isNix112 then
-            "${nix}/libexec/nix/build-remote"
-          else
-            "${nix}/libexec/nix/build-remote.pl";
+      // optionalAttrs (cfg.distributedBuilds && !isNix20) {
+        NIX_BUILD_HOOK = "${nix}/libexec/nix/build-remote.pl";
       };
 
     # Set up the environment variables for running Nix.
@@ -420,7 +424,7 @@ in
       { NIX_PATH = concatStringsSep ":" cfg.nixPath;
       };
 
-    environment.extraInit =
+    environment.extraInit = optionalString (!isNix20)
       ''
         # Set up secure multi-user builds: non-root users build through the
         # Nix daemon.
diff --git a/nixos/modules/services/misc/zookeeper.nix b/nixos/modules/services/misc/zookeeper.nix
index d85b5e4ec5076..91539592511c2 100644
--- a/nixos/modules/services/misc/zookeeper.nix
+++ b/nixos/modules/services/misc/zookeeper.nix
@@ -106,10 +106,19 @@ in {
       '';
     };
 
+    package = mkOption {
+      description = "The zookeeper package to use";
+      default = pkgs.zookeeper;
+      defaultText = "pkgs.zookeeper";
+      type = types.package;
+    };
+
   };
 
 
   config = mkIf cfg.enable {
+    environment.systemPackages = [cfg.package];
+
     systemd.services.zookeeper = {
       description = "Zookeeper Daemon";
       wantedBy = [ "multi-user.target" ];
@@ -118,7 +127,7 @@ in {
       serviceConfig = {
         ExecStart = ''
           ${pkgs.jre}/bin/java \
-            -cp "${pkgs.zookeeper}/lib/*:${pkgs.zookeeper}/${pkgs.zookeeper.name}.jar:${configDir}" \
+            -cp "${cfg.package}/lib/*:${cfg.package}/${cfg.package.name}.jar:${configDir}" \
             ${escapeShellArgs cfg.extraCmdLineOptions} \
             -Dzookeeper.datadir.autocreate=false \
             ${optionalString cfg.preferIPv4 "-Djava.net.preferIPv4Stack=true"} \
diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix
index e1fde4fc95002..d23b329eeb258 100644
--- a/nixos/modules/services/monitoring/netdata.nix
+++ b/nixos/modules/services/monitoring/netdata.nix
@@ -5,18 +5,25 @@ with lib;
 let
   cfg = config.services.netdata;
 
-  configFile = pkgs.writeText "netdata.conf" cfg.configText;
+  wrappedPlugins = pkgs.runCommand "wrapped-plugins" {} ''
+    mkdir -p $out/libexec/netdata/plugins.d
+    ln -s /run/wrappers/bin/apps.plugin $out/libexec/netdata/plugins.d/apps.plugin
+  '';
+
+  localConfig = {
+    global = {
+      "plugins directory" = "${wrappedPlugins}/libexec/netdata/plugins.d ${pkgs.netdata}/libexec/netdata/plugins.d";
+    };
+  };
+  mkConfig = generators.toINI {} (recursiveUpdate localConfig cfg.config);
+  configFile = pkgs.writeText "netdata.conf" (if cfg.configText != null then cfg.configText else mkConfig);
 
   defaultUser = "netdata";
 
 in {
   options = {
     services.netdata = {
-      enable = mkOption {
-        default = false;
-        type = types.bool;
-        description = "Whether to enable netdata monitoring.";
-      };
+      enable = mkEnableOption "netdata";
 
       user = mkOption {
         type = types.str;
@@ -31,9 +38,9 @@ in {
       };
 
       configText = mkOption {
-        type = types.lines;
-        default = "";
-        description = "netdata.conf configuration.";
+        type = types.nullOr types.lines;
+        description = "Verbatim netdata.conf, cannot be combined with config.";
+        default = null;
         example = ''
           [global]
           debug log = syslog
@@ -42,11 +49,29 @@ in {
         '';
       };
 
+      config = mkOption {
+        type = types.attrsOf types.attrs;
+        default = {};
+        description = "netdata.conf configuration as nix attributes. cannot be combined with configText.";
+        example = literalExample ''
+          global = {
+            "debug log" = "syslog";
+            "access log" = "syslog";
+            "error log" = "syslog";
+          };
+        '';
+        };
+      };
     };
-  };
 
   config = mkIf cfg.enable {
+    assertions =
+      [ { assertion = cfg.config != {} -> cfg.configText == null ;
+          message = "Cannot specify both config and configText";
+        }
+      ];
     systemd.services.netdata = {
+      path = with pkgs; [ gawk curl ];
       description = "Real time performance monitoring";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
@@ -66,6 +91,15 @@ in {
       };
     };
 
+    security.wrappers."apps.plugin" = {
+      source = "${pkgs.netdata}/libexec/netdata/plugins.d/apps.plugin";
+      capabilities = "cap_dac_read_search,cap_sys_ptrace+ep";
+      owner = cfg.user;
+      group = cfg.group;
+      permissions = "u+rx,g+rx,o-rwx";
+    };
+
+
     users.extraUsers = optional (cfg.user == defaultUser) {
       name = defaultUser;
     };
diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager.nix b/nixos/modules/services/monitoring/prometheus/alertmanager.nix
index cf761edad9260..8a47c9f1e7d89 100644
--- a/nixos/modules/services/monitoring/prometheus/alertmanager.nix
+++ b/nixos/modules/services/monitoring/prometheus/alertmanager.nix
@@ -111,11 +111,11 @@ in {
       after    = [ "network.target" ];
       script = ''
         ${pkgs.prometheus-alertmanager.bin}/bin/alertmanager \
-        -config.file ${alertmanagerYml} \
-        -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
-        -log.level ${cfg.logLevel} \
-        ${optionalString (cfg.webExternalUrl != null) ''-web.external-url ${cfg.webExternalUrl} \''}
-        ${optionalString (cfg.logFormat != null) "-log.format ${cfg.logFormat}"}
+        --config.file ${alertmanagerYml} \
+        --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+        --log.level ${cfg.logLevel} \
+        ${optionalString (cfg.webExternalUrl != null) ''--web.external-url ${cfg.webExternalUrl} \''}
+        ${optionalString (cfg.logFormat != null) "--log.format ${cfg.logFormat}"}
       '';
 
       serviceConfig = {
diff --git a/nixos/modules/services/network-filesystems/openafs-client/default.nix b/nixos/modules/services/network-filesystems/openafs-client/default.nix
deleted file mode 100644
index 0946e379e7967..0000000000000
--- a/nixos/modules/services/network-filesystems/openafs-client/default.nix
+++ /dev/null
@@ -1,99 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-let
-  inherit (lib) mkOption mkIf;
-
-  cfg = config.services.openafsClient;
-
-  cellServDB = pkgs.fetchurl {
-    url = http://dl.central.org/dl/cellservdb/CellServDB.2017-03-14;
-    sha256 = "1197z6c5xrijgf66rhaymnm5cvyg2yiy1i20y4ah4mrzmjx0m7sc";
-  };
-
-  afsConfig = pkgs.runCommand "afsconfig" {} ''
-    mkdir -p $out
-    echo ${cfg.cellName} > $out/ThisCell
-    cp ${cellServDB} $out/CellServDB
-    echo "/afs:${cfg.cacheDirectory}:${cfg.cacheSize}" > $out/cacheinfo
-  '';
-
-  openafsPkgs = config.boot.kernelPackages.openafsClient;
-in
-{
-  ###### interface
-
-  options = {
-
-    services.openafsClient = {
-
-      enable = mkOption {
-        default = false;
-        description = "Whether to enable the OpenAFS client.";
-      };
-
-      cellName = mkOption {
-        default = "grand.central.org";
-        description = "Cell name.";
-      };
-
-      cacheSize = mkOption {
-        default = "100000";
-        description = "Cache size.";
-      };
-
-      cacheDirectory = mkOption {
-        default = "/var/cache/openafs";
-        description = "Cache directory.";
-      };
-
-      crypt = mkOption {
-        default = false;
-        description = "Whether to enable (weak) protocol encryption.";
-      };
-
-      sparse = mkOption {
-        default = false;
-        description = "Minimal cell list in /afs.";
-      };
-
-    };
-  };
-
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-
-    environment.systemPackages = [ openafsPkgs ];
-
-    environment.etc = [
-      { source = afsConfig;
-        target = "openafs";
-      }
-    ];
-
-    systemd.services.afsd = {
-      description = "AFS client";
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
-      serviceConfig = { RemainAfterExit = true; };
-
-      preStart = ''
-        mkdir -p -m 0755 /afs
-        mkdir -m 0700 -p ${cfg.cacheDirectory}
-        ${pkgs.kmod}/bin/insmod ${openafsPkgs}/lib/openafs/libafs-*.ko || true
-        ${openafsPkgs}/sbin/afsd -confdir ${afsConfig} -cachedir ${cfg.cacheDirectory} ${if cfg.sparse then "-dynroot-sparse" else "-dynroot"} -fakestat -afsdb
-        ${openafsPkgs}/bin/fs setcrypt ${if cfg.crypt then "on" else "off"}
-      '';
-
-      # Doing this in preStop, because after these commands AFS is basically
-      # stopped, so systemd has nothing to do, just noticing it.  If done in
-      # postStop, then we get a hang + kernel oops, because AFS can't be
-      # stopped simply by sending signals to processes.
-      preStop = ''
-        ${pkgs.utillinux}/bin/umount /afs
-        ${openafsPkgs}/sbin/afsd -shutdown
-      '';
-    };
-  };
-}
diff --git a/nixos/modules/services/network-filesystems/openafs/client.nix b/nixos/modules/services/network-filesystems/openafs/client.nix
new file mode 100644
index 0000000000000..3826fe3edfd03
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/openafs/client.nix
@@ -0,0 +1,239 @@
+{ config, pkgs, lib, ... }:
+
+with import ./lib.nix { inherit lib; };
+
+let
+  inherit (lib) getBin mkOption mkIf optionalString singleton types;
+
+  cfg = config.services.openafsClient;
+
+  cellServDB = pkgs.fetchurl {
+    url = http://dl.central.org/dl/cellservdb/CellServDB.2017-03-14;
+    sha256 = "1197z6c5xrijgf66rhaymnm5cvyg2yiy1i20y4ah4mrzmjx0m7sc";
+  };
+
+  clientServDB = pkgs.writeText "client-cellServDB-${cfg.cellName}" (mkCellServDB cfg.cellName cfg.cellServDB);
+
+  afsConfig = pkgs.runCommand "afsconfig" {} ''
+    mkdir -p $out
+    echo ${cfg.cellName} > $out/ThisCell
+    cat ${cellServDB} ${clientServDB} > $out/CellServDB
+    echo "${cfg.mountPoint}:${cfg.cache.directory}:${toString cfg.cache.blocks}" > $out/cacheinfo
+  '';
+
+  openafsMod = config.boot.kernelPackages.openafs;
+  openafsBin = lib.getBin pkgs.openafs;
+in
+{
+  ###### interface
+
+  options = {
+
+    services.openafsClient = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Whether to enable the OpenAFS client.";
+      };
+
+      afsdb = mkOption {
+        default = true;
+        type = types.bool;
+        description = "Resolve cells via AFSDB DNS records.";
+      };
+
+      cellName = mkOption {
+        default = "";
+        type = types.str;
+        description = "Cell name.";
+        example = "grand.central.org";
+      };
+
+      cellServDB = mkOption {
+        default = [];
+        type = with types; listOf (submodule { options = cellServDBConfig; });
+        description = ''
+          This cell's database server records, added to the global
+          CellServDB. See CellServDB(5) man page for syntax. Ignored when
+          <literal>afsdb</literal> is set to <literal>true</literal>.
+        '';
+        example = ''
+          [ { ip = "1.2.3.4"; dnsname = "first.afsdb.server.dns.fqdn.org"; }
+            { ip = "2.3.4.5"; dnsname = "second.afsdb.server.dns.fqdn.org"; }
+          ]
+        '';
+      };
+
+      cache = {
+        blocks = mkOption {
+          default = 100000;
+          type = types.int;
+          description = "Cache size in 1KB blocks.";
+        };
+
+        chunksize = mkOption {
+          default = 0;
+          type = types.ints.between 0 30;
+          description = ''
+            Size of each cache chunk given in powers of
+            2. <literal>0</literal> resets the chunk size to its default
+            values (13 (8 KB) for memcache, 18-20 (256 KB to 1 MB) for
+            diskcache). Maximum value is 30. Important performance
+            parameter. Set to higher values when dealing with large files.
+          '';
+        };
+
+        directory = mkOption {
+          default = "/var/cache/openafs";
+          type = types.str;
+          description = "Cache directory.";
+        };
+
+        diskless = mkOption {
+          default = false;
+          type = types.bool;
+          description = ''
+            Use in-memory cache for diskless machines. Has no real
+            performance benefit anymore.
+          '';
+        };
+      };
+
+      crypt = mkOption {
+        default = true;
+        type = types.bool;
+        description = "Whether to enable (weak) protocol encryption.";
+      };
+
+      daemons = mkOption {
+        default = 2;
+        type = types.int;
+        description = ''
+          Number of daemons to serve user requests. Numbers higher than 6
+          usually do no increase performance. Default is sufficient for up
+          to five concurrent users.
+        '';
+      };
+
+      fakestat = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Return fake data on stat() calls. If <literal>true</literal>,
+          always do so. If <literal>false</literal>, only do so for
+          cross-cell mounts (as these are potentially expensive).
+        '';
+      };
+
+      inumcalc = mkOption {
+        default = "compat";
+        type = types.strMatching "compat|md5";
+        description = ''
+          Inode calculation method. <literal>compat</literal> is
+          computationally less expensive, but <literal>md5</literal> greatly
+          reduces the likelihood of inode collisions in larger scenarios
+          involving multiple cells mounted into one AFS space.
+        '';
+      };
+
+      mountPoint = mkOption {
+        default = "/afs";
+        type = types.str;
+        description = ''
+          Mountpoint of the AFS file tree, conventionally
+          <literal>/afs</literal>. When set to a different value, only
+          cross-cells that use the same value can be accessed.
+        '';
+      };
+
+      sparse = mkOption {
+        default = true;
+        type = types.bool;
+        description = "Minimal cell list in /afs.";
+      };
+
+      startDisconnected = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Start up in disconnected mode.  You need to execute
+          <literal>fs disco online</literal> (as root) to switch to
+          connected mode. Useful for roaming devices.
+        '';
+      };
+
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = cfg.afsdb || cfg.cellServDB != [];
+        message = "You should specify all cell-local database servers in config.services.openafsClient.cellServDB or set config.services.openafsClient.afsdb.";
+      }
+      { assertion = cfg.cellName != "";
+        message = "You must specify the local cell name in config.services.openafsClient.cellName.";
+      }
+    ];
+
+    environment.systemPackages = [ pkgs.openafs ];
+
+    environment.etc = {
+      clientCellServDB = {
+        source = pkgs.runCommand "CellServDB" {} ''
+          cat ${cellServDB} ${clientServDB} > $out
+        '';
+        target = "openafs/CellServDB";
+        mode = "0644";
+      };
+      clientCell = {
+        text = ''
+          ${cfg.cellName}
+        '';
+        target = "openafs/ThisCell";
+        mode = "0644";
+      };
+    };
+
+    systemd.services.afsd = {
+      description = "AFS client";
+      wantedBy = [ "multi-user.target" ];
+      after = singleton (if cfg.startDisconnected then  "network.target" else "network-online.target");
+      serviceConfig = { RemainAfterExit = true; };
+      restartIfChanged = false;
+
+      preStart = ''
+        mkdir -p -m 0755 ${cfg.mountPoint}
+        mkdir -m 0700 -p ${cfg.cache.directory}
+        ${pkgs.kmod}/bin/insmod ${openafsMod}/lib/modules/*/extra/openafs/libafs.ko.xz
+        ${openafsBin}/sbin/afsd \
+          -mountdir ${cfg.mountPoint} \
+          -confdir ${afsConfig} \
+          ${optionalString (!cfg.cache.diskless) "-cachedir ${cfg.cache.directory}"} \
+          -blocks ${toString cfg.cache.blocks} \
+          -chunksize ${toString cfg.cache.chunksize} \
+          ${optionalString cfg.cache.diskless "-memcache"} \
+          -inumcalc ${cfg.inumcalc} \
+          ${if cfg.fakestat then "-fakestat-all" else "-fakestat"} \
+          ${if cfg.sparse then "-dynroot-sparse" else "-dynroot"} \
+          ${optionalString cfg.afsdb "-afsdb"}
+        ${openafsBin}/bin/fs setcrypt ${if cfg.crypt then "on" else "off"}
+        ${optionalString cfg.startDisconnected "${openafsBin}/bin/fs discon offline"}
+      '';
+
+      # Doing this in preStop, because after these commands AFS is basically
+      # stopped, so systemd has nothing to do, just noticing it.  If done in
+      # postStop, then we get a hang + kernel oops, because AFS can't be
+      # stopped simply by sending signals to processes.
+      preStop = ''
+        ${pkgs.utillinux}/bin/umount ${cfg.mountPoint}
+        ${openafsBin}/sbin/afsd -shutdown
+        ${pkgs.kmod}/sbin/rmmod libafs
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/openafs/lib.nix b/nixos/modules/services/network-filesystems/openafs/lib.nix
new file mode 100644
index 0000000000000..ecfc72d2eaf91
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/openafs/lib.nix
@@ -0,0 +1,28 @@
+{ lib, ...}:
+
+let
+  inherit (lib) concatStringsSep mkOption types;
+
+in rec {
+
+  mkCellServDB = cellName: db: ''
+    >${cellName}
+  '' + (concatStringsSep "\n" (map (dbm: if (dbm.ip != "" && dbm.dnsname != "") then dbm.ip + " #" + dbm.dnsname else "")
+                                   db));
+
+  # CellServDB configuration type
+  cellServDBConfig = {
+    ip = mkOption {
+      type = types.str;
+      default = "";
+      example = "1.2.3.4";
+      description = "IP Address of a database server";
+    };
+    dnsname = mkOption {
+      type = types.str;
+      default = "";
+      example = "afs.example.org";
+      description = "DNS full-qualified domain name of a database server";
+    };
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/openafs/server.nix b/nixos/modules/services/network-filesystems/openafs/server.nix
new file mode 100644
index 0000000000000..429eb945ac9e8
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/openafs/server.nix
@@ -0,0 +1,260 @@
+{ config, pkgs, lib, ... }:
+
+with import ./lib.nix { inherit lib; };
+
+let
+  inherit (lib) concatStringsSep intersperse mapAttrsToList mkForce mkIf mkMerge mkOption optionalString types;
+
+  bosConfig = pkgs.writeText "BosConfig" (''
+    restrictmode 1
+    restarttime 16 0 0 0 0
+    checkbintime 3 0 5 0 0
+  '' + (optionalString cfg.roles.database.enable ''
+    bnode simple vlserver 1
+    parm ${openafsBin}/libexec/openafs/vlserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} ${cfg.roles.database.vlserverArgs}
+    end
+    bnode simple ptserver 1
+    parm ${openafsBin}/libexec/openafs/ptserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} ${cfg.roles.database.ptserverArgs}
+    end
+  '') + (optionalString cfg.roles.fileserver.enable ''
+    bnode dafs dafs 1
+    parm ${openafsBin}/libexec/openafs/dafileserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} -udpsize ${udpSizeStr} ${cfg.roles.fileserver.fileserverArgs}
+    parm ${openafsBin}/libexec/openafs/davolserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} -udpsize ${udpSizeStr} ${cfg.roles.fileserver.volserverArgs}
+    parm ${openafsBin}/libexec/openafs/salvageserver ${cfg.roles.fileserver.salvageserverArgs}
+    parm ${openafsBin}/libexec/openafs/dasalvager ${cfg.roles.fileserver.salvagerArgs}
+    end
+  '') + (optionalString (cfg.roles.database.enable && cfg.roles.backup.enable) ''
+    bnode simple buserver 1
+    parm ${openafsBin}/libexec/openafs/buserver ${cfg.roles.backup.buserverArgs} ${optionalString (cfg.roles.backup.cellServDB != []) "-cellservdb /etc/openafs/backup/"}
+    end
+  ''));
+
+  netInfo = if (cfg.advertisedAddresses != []) then
+    pkgs.writeText "NetInfo" ((concatStringsSep "\nf " cfg.advertisedAddresses) + "\n")
+  else null;
+
+  buCellServDB = pkgs.writeText "backup-cellServDB-${cfg.cellName}" (mkCellServDB cfg.cellName cfg.roles.backup.cellServDB);
+
+  cfg = config.services.openafsServer;
+
+  udpSizeStr = toString cfg.udpPacketSize;
+
+  openafsBin = lib.getBin pkgs.openafs;
+
+in {
+
+  options = {
+
+    services.openafsServer = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to enable the OpenAFS server. An OpenAFS server needs a
+          complex setup. So, be aware that enabling this service and setting
+          some options does not give you a turn-key-ready solution. You need
+          at least a running Kerberos 5 setup, as OpenAFS relies on it for
+          authentication. See the Guide "QuickStartUnix" coming with
+          <literal>pkgs.openafs.doc</literal> for complete setup
+          instructions.
+        '';
+      };
+
+      advertisedAddresses = mkOption {
+        default = [];
+        description = "List of IP addresses this server is advertised under. See NetInfo(5)";
+      };
+
+      cellName = mkOption {
+        default = "";
+        type = types.str;
+        description = "Cell name, this server will serve.";
+        example = "grand.central.org";
+      };
+
+      cellServDB = mkOption {
+        default = [];
+        type = with types; listOf (submodule [ { options = cellServDBConfig;} ]);
+        description = "Definition of all cell-local database server machines.";
+      };
+
+      roles = {
+        fileserver = {
+          enable = mkOption {
+            default = true;
+            type = types.bool;
+            description = "Fileserver role, serves files and volumes from its local storage.";
+          };
+
+          fileserverArgs = mkOption {
+            default = "-vattachpar 128 -vhashsize 11 -L -rxpck 400 -cb 1000000";
+            type = types.str;
+            description = "Arguments to the dafileserver process. See its man page.";
+          };
+
+          volserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the davolserver process. See its man page.";
+            example = "-sync never";
+          };
+
+          salvageserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the salvageserver process. See its man page.";
+            example = "-showlog";
+          };
+
+          salvagerArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the dasalvager process. See its man page.";
+            example = "-showlog -showmounts";
+          };
+        };
+
+        database = {
+          enable = mkOption {
+            default = true;
+            type = types.bool;
+            description = ''
+              Database server role, maintains the Volume Location Database,
+              Protection Database (and Backup Database, see
+              <literal>backup</literal> role). There can be multiple
+              servers in the database role for replication, which then need
+              reliable network connection to each other.
+
+              Servers in this role appear in AFSDB DNS records or the
+              CellServDB.
+            '';
+          };
+
+          vlserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the vlserver process. See its man page.";
+            example = "-rxbind";
+          };
+
+          ptserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the ptserver process. See its man page.";
+            example = "-restricted -default_access S---- S-M---";
+          };
+        };
+
+        backup = {
+          enable = mkOption {
+            default = false;
+            type = types.bool;
+            description = ''
+              Backup server role. Use in conjunction with the
+              <literal>database</literal> role to maintain the Backup
+              Database. Normally only used in conjunction with tape storage
+              or IBM's Tivoli Storage Manager.
+            '';
+          };
+
+          buserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the buserver process. See its man page.";
+            example = "-p 8";
+          };
+
+          cellServDB = mkOption {
+            default = [];
+            type = with types; listOf (submodule [ { options = cellServDBConfig;} ]);
+            description = ''
+              Definition of all cell-local backup database server machines.
+              Use this when your cell uses less backup database servers than
+              other database server machines.
+            '';
+          };
+        };
+      };
+
+      dottedPrincipals= mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If enabled, allow principal names containing (.) dots. Enabling
+          this has security implications!
+        '';
+      };
+
+      udpPacketSize = mkOption {
+        default = 1310720;
+        type = types.int;
+        description = ''
+          UDP packet size to use in Bytes. Higher values can speed up
+          communications. The default of 1 MB is a sufficient in most
+          cases. Make sure to increase the kernel's UDP buffer size
+          accordingly via <literal>net.core(w|r|opt)mem_max</literal>
+          sysctl.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = cfg.cellServDB != [];
+        message = "You must specify all cell-local database servers in config.services.openafsServer.cellServDB.";
+      }
+      { assertion = cfg.cellName != "";
+        message = "You must specify the local cell name in config.services.openafsServer.cellName.";
+      }
+    ];
+
+    environment.systemPackages = [ pkgs.openafs ];
+
+    environment.etc = {
+      bosConfig = {
+        source = bosConfig;
+        target = "openafs/BosConfig";
+        mode = "0644";
+      };
+      cellServDB = {
+        text = mkCellServDB cfg.cellName cfg.cellServDB;
+        target = "openafs/server/CellServDB";
+        mode = "0644";
+      };
+      thisCell = {
+        text = cfg.cellName;
+        target = "openafs/server/ThisCell";
+        mode = "0644";
+      };
+      buCellServDB = {
+        enable = (cfg.roles.backup.cellServDB != []);
+        text = mkCellServDB cfg.cellName cfg.roles.backup.cellServDB;
+        target = "openafs/backup/CellServDB";
+      };
+    };
+
+    systemd.services = {
+      openafs-server = {
+        description = "OpenAFS server";
+        after = [ "syslog.target" "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        restartIfChanged = false;
+        unitConfig.ConditionPathExists = [ "/etc/openafs/server/rxkad.keytab" ];
+        preStart = ''
+          mkdir -m 0755 -p /var/openafs
+          ${optionalString (netInfo != null) "cp ${netInfo} /var/openafs/netInfo"}
+          ${optionalString (cfg.roles.backup.cellServDB != []) "cp ${buCellServDB}"}
+        '';
+        serviceConfig = {
+          ExecStart = "${openafsBin}/bin/bosserver -nofork";
+          ExecStop = "${openafsBin}/bin/bos shutdown localhost -wait -localauth";
+        };
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix
index 09cd9cb22ca85..b23266e8d43a3 100644
--- a/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -54,10 +54,12 @@ let
       };
 
       serviceConfig = {
-        ExecStart = "${samba}/sbin/${appName} ${args}";
+        ExecStart = "${samba}/sbin/${appName} --foreground --no-process-group ${args}";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
         LimitNOFILE = 16384;
+        PIDFile = "/run/${appName}.pid";
         Type = "notify";
+        NotifyAccess = "all"; #may not do anything...
       };
 
       restartTriggers = [ configFile ];
@@ -231,11 +233,12 @@ in
             after = [ "samba-setup.service" "network.target" ];
             wantedBy = [ "multi-user.target" ];
           };
-
+          # Refer to https://github.com/samba-team/samba/tree/master/packaging/systemd
+          # for correct use with systemd
           services = {
-            "samba-smbd" = daemonService "smbd" "-F";
-            "samba-nmbd" = mkIf cfg.enableNmbd (daemonService "nmbd" "-F");
-            "samba-winbindd" = mkIf cfg.enableWinbindd (daemonService "winbindd" "-F");
+            "samba-smbd" = daemonService "smbd" "";
+            "samba-nmbd" = mkIf cfg.enableNmbd (daemonService "nmbd" "");
+            "samba-winbindd" = mkIf cfg.enableWinbindd (daemonService "winbindd" "");
             "samba-setup" = {
               description = "Samba Setup Task";
               script = setupScript;
diff --git a/nixos/modules/services/networking/aria2.nix b/nixos/modules/services/networking/aria2.nix
index ad4ac9bf45e32..df9c92db2e540 100644
--- a/nixos/modules/services/networking/aria2.nix
+++ b/nixos/modules/services/networking/aria2.nix
@@ -10,9 +10,9 @@ let
   settingsDir = "${homeDir}";
   sessionFile = "${homeDir}/aria2.session";
   downloadDir = "${homeDir}/Downloads";
-  
+
   rangesToStringList = map (x: builtins.toString x.from +"-"+ builtins.toString x.to);
-  
+
   settingsFile = pkgs.writeText "aria2.conf"
   ''
     dir=${cfg.downloadDir}
@@ -110,12 +110,12 @@ in
         mkdir -m 0770 -p "${homeDir}"
         chown aria2:aria2 "${homeDir}"
         if [[ ! -d "${config.services.aria2.downloadDir}" ]]
-        then 
+        then
           mkdir -m 0770 -p "${config.services.aria2.downloadDir}"
           chown aria2:aria2 "${config.services.aria2.downloadDir}"
         fi
         if [[ ! -e "${sessionFile}" ]]
-        then 
+        then
           touch "${sessionFile}"
           chown aria2:aria2 "${sessionFile}"
         fi
@@ -132,4 +132,4 @@ in
       };
     };
   };
-}
\ No newline at end of file
+}
diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix
index 1a7a1e24b702d..c25bd0fdc5411 100644
--- a/nixos/modules/services/networking/bird.nix
+++ b/nixos/modules/services/networking/bird.nix
@@ -7,21 +7,27 @@ let
     let
       cfg = config.services.${variant};
       pkg = pkgs.${variant};
+      birdBin = if variant == "bird6" then "bird6" else "bird";
       birdc = if variant == "bird6" then "birdc6" else "birdc";
+      descr =
+        { bird = "1.9.x with IPv4 suport";
+          bird6 = "1.9.x with IPv6 suport";
+          bird2 = "2.x";
+        }.${variant};
       configFile = pkgs.stdenv.mkDerivation {
         name = "${variant}.conf";
         text = cfg.config;
         preferLocalBuild = true;
         buildCommand = ''
           echo -n "$text" > $out
-          ${pkg}/bin/${variant} -d -p -c $out
+          ${pkg}/bin/${birdBin} -d -p -c $out
         '';
       };
     in {
       ###### interface
       options = {
         services.${variant} = {
-          enable = mkEnableOption "BIRD Internet Routing Daemon";
+          enable = mkEnableOption "BIRD Internet Routing Daemon (${descr})";
           config = mkOption {
             type = types.lines;
             description = ''
@@ -36,12 +42,12 @@ let
       config = mkIf cfg.enable {
         environment.systemPackages = [ pkg ];
         systemd.services.${variant} = {
-          description = "BIRD Internet Routing Daemon";
+          description = "BIRD Internet Routing Daemon (${descr})";
           wantedBy = [ "multi-user.target" ];
           serviceConfig = {
             Type = "forking";
             Restart = "on-failure";
-            ExecStart = "${pkg}/bin/${variant} -c ${configFile} -u ${variant} -g ${variant}";
+            ExecStart = "${pkg}/bin/${birdBin} -c ${configFile} -u ${variant} -g ${variant}";
             ExecReload = "${pkg}/bin/${birdc} configure";
             ExecStop = "${pkg}/bin/${birdc} down";
             CapabilityBoundingSet = [ "CAP_CHOWN" "CAP_FOWNER" "CAP_DAC_OVERRIDE" "CAP_SETUID" "CAP_SETGID"
@@ -56,14 +62,15 @@ let
         users = {
           extraUsers.${variant} = {
             description = "BIRD Internet Routing Daemon user";
-            group = "${variant}";
+            group = variant;
           };
           extraGroups.${variant} = {};
         };
       };
     };
 
-  inherit (config.services) bird bird6;
-in {
-  imports = [(generic "bird") (generic "bird6")];
+in
+
+{
+  imports = map generic [ "bird" "bird6" "bird2" ];
 }
diff --git a/nixos/modules/services/networking/dnscrypt-wrapper.nix b/nixos/modules/services/networking/dnscrypt-wrapper.nix
index 23cc92946e41f..bf13d5c6f5fec 100644
--- a/nixos/modules/services/networking/dnscrypt-wrapper.nix
+++ b/nixos/modules/services/networking/dnscrypt-wrapper.nix
@@ -145,6 +145,16 @@ in {
     };
     users.groups.dnscrypt-wrapper = { };
 
+    security.polkit.extraConfig = ''
+      // Allow dnscrypt-wrapper user to restart dnscrypt-wrapper.service
+      polkit.addRule(function(action, subject) {
+          if (action.id == "org.freedesktop.systemd1.manage-units" &&
+              action.lookup("unit") == "dnscrypt-wrapper.service" &&
+              subject.user == "dnscrypt-wrapper") {
+              return polkit.Result.YES;
+          }
+        });
+    '';
 
     systemd.services.dnscrypt-wrapper = {
       description = "dnscrypt-wrapper daemon";
diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix
index 011a9b2f58ea0..aac02b811d716 100644
--- a/nixos/modules/services/networking/kresd.nix
+++ b/nixos/modules/services/networking/kresd.nix
@@ -43,7 +43,16 @@ in
       type = with types; listOf str;
       default = [ "::1" "127.0.0.1" ];
       description = ''
-        What addresses the server should listen on.
+        What addresses the server should listen on. (UDP+TCP 53)
+      '';
+    };
+    listenTLS = mkOption {
+      type = with types; listOf str;
+      default = [];
+      example = [ "198.51.100.1:853" "[2001:db8::1]:853" "853" ];
+      description = ''
+        Addresses on which kresd should provide DNS over TLS (see RFC 7858).
+        For detailed syntax see ListenStream in man systemd.socket.
       '';
     };
     # TODO: perhaps options for more common stuff like cache size or forwarding
@@ -75,6 +84,18 @@ in
       socketConfig.FreeBind = true;
     };
 
+    systemd.sockets.kresd-tls = mkIf (cfg.listenTLS != []) rec {
+      wantedBy = [ "sockets.target" ];
+      before = wantedBy;
+      partOf = [ "kresd.socket" ];
+      listenStreams = cfg.listenTLS;
+      socketConfig = {
+        FileDescriptorName = "tls";
+        FreeBind = true;
+        Service = "kresd.service";
+      };
+    };
+
     systemd.sockets.kresd-control = rec {
       wantedBy = [ "sockets.target" ];
       before = wantedBy;
@@ -97,11 +118,13 @@ in
         Type = "notify";
         WorkingDirectory = cfg.cacheDir;
         Restart = "on-failure";
+        Sockets = [ "kresd.socket" "kresd-control.socket" ]
+          ++ optional (cfg.listenTLS != []) "kresd-tls.socket";
       };
 
+      # Trust anchor goes from dns-root-data by default.
       script = ''
-        exec '${package}/bin/kresd' --config '${configFile}' \
-          -k '${pkgs.dns-root-data}/root.key'
+        exec '${package}/bin/kresd' --config '${configFile}' --forks=1
       '';
 
       requires = [ "kresd.socket" ];
diff --git a/nixos/modules/services/networking/monero.nix b/nixos/modules/services/networking/monero.nix
new file mode 100644
index 0000000000000..31379189f5de3
--- /dev/null
+++ b/nixos/modules/services/networking/monero.nix
@@ -0,0 +1,238 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg     = config.services.monero;
+  dataDir = "/var/lib/monero";
+
+  listToConf = option: list:
+    concatMapStrings (value: "${option}=${value}\n") list;
+
+  login = (cfg.rpc.user != null && cfg.rpc.password != null);
+
+  configFile = with cfg; pkgs.writeText "monero.conf" ''
+    log-file=/dev/stdout
+    data-dir=${dataDir}
+
+    ${optionalString mining.enable ''
+      start-mining=${mining.address}
+      mining-threads=${toString mining.threads}
+    ''}
+
+    rpc-bind-ip=${rpc.address}
+    rpc-bind-port=${toString rpc.port}
+    ${optionalString login ''
+      rpc-login=${rpc.user}:${rpc.password}
+    ''}
+    ${optionalString rpc.restricted ''
+      restrict-rpc=1
+    ''}
+
+    limit-rate-up=${toString limits.upload}
+    limit-rate-down=${toString limits.download}
+    max-concurrency=${toString limits.threads}
+    block-sync-size=${toString limits.syncSize}
+
+    ${listToConf "add-peer" extraNodes}
+    ${listToConf "add-priority-node" priorityNodes}
+    ${listToConf "add-exclusive-node" exclusiveNodes}
+
+    ${extraConfig}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.monero = {
+
+      enable = mkEnableOption "Monero node daemon.";
+
+      mining.enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to mine moneroj.
+        '';
+      };
+
+      mining.address = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Monero address where to send mining rewards.
+        '';
+      };
+
+      mining.threads = mkOption {
+        type = types.addCheck types.int (x: x>=0);
+        default = 0;
+        description = ''
+          Number of threads used for mining.
+          Set to <literal>0</literal> to use all available.
+        '';
+      };
+
+      rpc.user = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          User name for RPC connections.
+        '';
+      };
+
+      rpc.password = mkOption {
+        type = types.str;
+        default = null;
+        description = ''
+          Password for RPC connections.
+        '';
+      };
+
+      rpc.address = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = ''
+          IP address the RPC server will bind to.
+        '';
+      };
+
+      rpc.port = mkOption {
+        type = types.int;
+        default = 18081;
+        description = ''
+          Port the RPC server will bind to.
+        '';
+      };
+
+      rpc.restricted = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to restrict RPC to view only commands.
+        '';
+      };
+
+      limits.upload = mkOption {
+        type = types.addCheck types.int (x: x>=-1);
+        default = -1;
+        description = ''
+          Limit of the upload rate in kB/s.
+          Set to <literal>-1</literal> to leave unlimited.
+        '';
+      };
+
+      limits.download = mkOption {
+        type = types.addCheck types.int (x: x>=-1);
+        default = -1;
+        description = ''
+          Limit of the download rate in kB/s.
+          Set to <literal>-1</literal> to leave unlimited.
+        '';
+      };
+
+      limits.threads = mkOption {
+        type = types.addCheck types.int (x: x>=0);
+        default = 0;
+        description = ''
+          Maximum number of threads used for a parallel job.
+          Set to <literal>0</literal> to leave unlimited.
+        '';
+      };
+
+      limits.syncSize = mkOption {
+        type = types.addCheck types.int (x: x>=0);
+        default = 0;
+        description = ''
+          Maximum number of blocks to sync at once.
+          Set to <literal>0</literal> for adaptive.
+        '';
+      };
+
+      extraNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of additional peer IP addresses to add to the local list.
+        '';
+      };
+
+      priorityNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of peer IP addresses to connect to and
+          attempt to keep the connection open.
+        '';
+      };
+
+      exclusiveNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of peer IP addresses to connect to *only*.
+          If given the other peer options will be ignored.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra lines to be added verbatim to monerod configuration.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton {
+      name = "monero";
+      uid  = config.ids.uids.monero;
+      description = "Monero daemon user";
+      home = dataDir;
+      createHome = true;
+    };
+
+    users.extraGroups = singleton {
+      name = "monero";
+      gid  = config.ids.gids.monero;
+    };
+
+    systemd.services.monero = {
+      description = "monero daemon";
+      after    = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User  = "monero";
+        Group = "monero";
+        ExecStart = "${pkgs.monero}/bin/monerod --config-file=${configFile} --non-interactive";
+        Restart = "always";
+        SuccessExitStatus = [ 0 1 ];
+      };
+    };
+
+   assertions = singleton {
+     assertion = cfg.mining.enable -> cfg.mining.address != "";
+     message   = ''
+       You need a Monero address to receive mining rewards:
+       specify one using option monero.mining.address.
+    '';
+   };
+
+  };
+
+}
+
diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix
index 81915b5a2ef82..d8135f4d0ffa4 100644
--- a/nixos/modules/services/networking/mosquitto.nix
+++ b/nixos/modules/services/networking/mosquitto.nix
@@ -12,6 +12,10 @@ let
     keyfile ${cfg.ssl.keyfile}
   '';
 
+  passwordConf = optionalString cfg.checkPasswords ''
+    password_file ${cfg.dataDir}/passwd
+  '';
+
   mosquittoConf = pkgs.writeText "mosquitto.conf" ''
     pid_file /run/mosquitto/pid
     acl_file ${aclFile}
@@ -19,6 +23,7 @@ let
     allow_anonymous ${boolToString cfg.allowAnonymous}
     bind_address ${cfg.host}
     port ${toString cfg.port}
+    ${passwordConf}
     ${listenerConf}
     ${cfg.extraConf}
   '';
@@ -153,6 +158,15 @@ in
         '';
       };
 
+      checkPasswords = mkOption {
+        default = false;
+        example = true;
+        type = types.bool;
+        description = ''
+          Refuse connection when clients provide incorrect passwords.
+        '';
+      };
+
       extraConf = mkOption {
         default = "";
         type = types.lines;
@@ -198,7 +212,7 @@ in
       '' + concatStringsSep "\n" (
         mapAttrsToList (n: c:
           if c.hashedPassword != null then
-            "echo '${n}:${c.hashedPassword}' > ${cfg.dataDir}/passwd"
+            "echo '${n}:${c.hashedPassword}' >> ${cfg.dataDir}/passwd"
           else optionalString (c.password != null)
             "${pkgs.mosquitto}/bin/mosquitto_passwd -b ${cfg.dataDir}/passwd ${n} ${c.password}"
         ) cfg.users);
diff --git a/nixos/modules/services/networking/openvpn.nix b/nixos/modules/services/networking/openvpn.nix
index 3fbf5a9f0227a..7a96b673c51e0 100644
--- a/nixos/modules/services/networking/openvpn.nix
+++ b/nixos/modules/services/networking/openvpn.nix
@@ -50,6 +50,11 @@ let
               "up ${pkgs.writeScript "openvpn-${name}-up" upScript}"}
           ${optionalString (cfg.down != "" || cfg.updateResolvConf)
               "down ${pkgs.writeScript "openvpn-${name}-down" downScript}"}
+          ${optionalString (cfg.authUserPass != null)
+              "auth-user-pass ${pkgs.writeText "openvpn-credentials-${name}" ''
+                ${cfg.authUserPass.username}
+                ${cfg.authUserPass.password}
+              ''}"}
         '';
 
     in {
@@ -161,6 +166,29 @@ in
             '';
           };
 
+          authUserPass = mkOption {
+            default = null;
+            description = ''
+              This option can be used to store the username / password credentials
+              with the "auth-user-pass" authentication method.
+
+              WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store!
+            '';
+            type = types.nullOr (types.submodule {
+
+              options = {
+                username = mkOption {
+                  description = "The username to store inside the credentials file.";
+                  type = types.string;
+                };
+
+                password = mkOption {
+                  description = "The password to store inside the credentials file.";
+                  type = types.string;
+                };
+              };
+            });
+          };
         };
 
       });
diff --git a/nixos/modules/services/networking/resilio.nix b/nixos/modules/services/networking/resilio.nix
index 6d2b7bdbca1b5..d1c4101f80bd9 100644
--- a/nixos/modules/services/networking/resilio.nix
+++ b/nixos/modules/services/networking/resilio.nix
@@ -17,7 +17,7 @@ let
 
     search_lan = entry.searchLAN;
     use_sync_trash = entry.useSyncTrash;
-    known_hosts = knownHosts;
+    known_hosts = entry.knownHosts;
   }) cfg.sharedFolders;
 
   configFile = pkgs.writeText "config.json" (builtins.toJSON ({
diff --git a/nixos/modules/services/networking/rxe.nix b/nixos/modules/services/networking/rxe.nix
new file mode 100644
index 0000000000000..a6a069ec50c06
--- /dev/null
+++ b/nixos/modules/services/networking/rxe.nix
@@ -0,0 +1,63 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.networking.rxe;
+
+  runRxeCmd = cmd: ifcs:
+    concatStrings ( map (x: "${pkgs.rdma-core}/bin/rxe_cfg -n ${cmd} ${x};") ifcs);
+
+  startScript = pkgs.writeShellScriptBin "rxe-start" ''
+    ${pkgs.rdma-core}/bin/rxe_cfg -n start
+    ${runRxeCmd "add" cfg.interfaces}
+    ${pkgs.rdma-core}/bin/rxe_cfg
+  '';
+
+  stopScript = pkgs.writeShellScriptBin "rxe-stop" ''
+    ${runRxeCmd "remove" cfg.interfaces }
+    ${pkgs.rdma-core}/bin/rxe_cfg -n stop
+  '';
+
+in {
+  ###### interface
+
+  options = {
+    networking.rxe = {
+      enable = mkEnableOption "RDMA over converged ethernet";
+      interfaces = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "eth0" ];
+        description = ''
+          Enable RDMA on the listed interfaces. The corresponding virtual
+          RDMA interfaces will be named rxe0 ... rxeN where the ordering
+          will be as they are named in the list. UDP port 4791 must be
+          open on the respective ethernet interfaces.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.rxe = {
+      path = with pkgs; [ kmod rdma-core ];
+      description = "RoCE interfaces";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "systemd-modules-load.service" "network-online.target" ];
+      wants = [ "network-pre.target" ];
+
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        ExecStart = "${startScript}/bin/rxe-start";
+        ExecStop = "${stopScript}/bin/rxe-stop";
+      };
+    };
+  };
+}
+
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index aa9c0fa1c09fa..e50c4dbacf36f 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -21,7 +21,7 @@ let
           daemon reads in addition to the the user's authorized_keys file.
           You can combine the <literal>keys</literal> and
           <literal>keyFiles</literal> options.
-          Warning: If you are using <literal>NixOps</literal> then don't use this 
+          Warning: If you are using <literal>NixOps</literal> then don't use this
           option since it will replace the key required for deployment via ssh.
         '';
       };
@@ -137,6 +137,14 @@ in
         '';
       };
 
+      openFirewall = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to automatically open the specified ports in the firewall.
+        '';
+      };
+
       listenAddresses = mkOption {
         type = with types; listOf (submodule {
           options = {
@@ -302,7 +310,7 @@ in
 
       };
 
-    networking.firewall.allowedTCPPorts = cfg.ports;
+    networking.firewall.allowedTCPPorts = if cfg.openFirewall then cfg.ports else [];
 
     security.pam.services.sshd =
       { startSession = true;
@@ -367,9 +375,6 @@ in
         # LogLevel VERBOSE logs user's key fingerprint on login.
         # Needed to have a clear audit track of which key was used to log in.
         LogLevel VERBOSE
-
-        # Use kernel sandbox mechanisms where possible in unprivileged processes.
-        UsePrivilegeSeparation sandbox
       '';
 
     assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true;
diff --git a/nixos/modules/services/networking/stunnel.nix b/nixos/modules/services/networking/stunnel.nix
new file mode 100644
index 0000000000000..89a14966eca76
--- /dev/null
+++ b/nixos/modules/services/networking/stunnel.nix
@@ -0,0 +1,221 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.stunnel;
+  yesNo = val: if val then "yes" else "no";
+
+  verifyChainPathAssert = n: c: {
+    assertion = c.verifyHostname == null || (c.verifyChain || c.verifyPeer);
+    message =  "stunnel: \"${n}\" client configuration - hostname verification " +
+      "is not possible without either verifyChain or verifyPeer enabled";
+  };
+
+  serverConfig = {
+    options = {
+      accept = mkOption {
+        type = types.int;
+        description = "On which port stunnel should listen for incoming TLS connections.";
+      };
+
+      connect = mkOption {
+        type = types.int;
+        description = "To which port the decrypted connection should be forwarded.";
+      };
+
+      cert = mkOption {
+        type = types.path;
+        description = "File containing both the private and public keys.";
+      };
+    };
+  };
+
+  clientConfig = {
+    options = {
+      accept = mkOption {
+        type = types.string;
+        description = "IP:Port on which connections should be accepted.";
+      };
+
+      connect = mkOption {
+        type = types.string;
+        description = "IP:Port destination to connect to.";
+      };
+
+      verifyChain = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Check if the provided certificate has a valid certificate chain (against CAPath).";
+      };
+
+      verifyPeer = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Check if the provided certificate is contained in CAPath.";
+      };
+
+      CAPath = mkOption {
+        type = types.path;
+        default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
+        description = "Path to a file containing certificates to validate against.";
+      };
+
+      verifyHostname = mkOption {
+        type = with types; nullOr string;
+        default = null;
+        description = "If set, stunnel checks if the provided certificate is valid for the given hostname.";
+      };
+    };
+  };
+
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.stunnel = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the stunnel TLS tunneling service.";
+      };
+
+      user = mkOption {
+        type = with types; nullOr string;
+        default = "nobody";
+        description = "The user under which stunnel runs.";
+      };
+
+      group = mkOption {
+        type = with types; nullOr string;
+        default = "nogroup";
+        description = "The group under which stunnel runs.";
+      };
+
+      logLevel = mkOption {
+        type = types.enum [ "emerg" "alert" "crit" "err" "warning" "notice" "info" "debug" ];
+        default = "info";
+        description = "Verbosity of stunnel output.";
+      };
+
+      fipsMode = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable FIPS 140-2 mode required for compliance.";
+      };
+
+      enableInsecureSSLv3 = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable support for the insecure SSLv3 protocol.";
+      };
+
+
+      servers = mkOption {
+        description = "Define the server configuations.";
+        type = with types; attrsOf (submodule serverConfig);
+        example = {
+          fancyWebserver = {
+            enable = true;
+            accept = 443;
+            connect = 8080;
+            cert = "/path/to/pem/file";
+          };
+        };
+        default = { };
+      };
+
+      clients = mkOption {
+        description = "Define the client configurations.";
+        type = with types; attrsOf (submodule clientConfig);
+        example = {
+          foobar = {
+            accept = "0.0.0.0:8080";
+            connect = "nixos.org:443";
+            verifyChain = false;
+          };
+        };
+        default = { };
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = concatLists [
+      (singleton {
+        assertion = (length (attrValues cfg.servers) != 0) || ((length (attrValues cfg.clients)) != 0);
+        message = "stunnel: At least one server- or client-configuration has to be present.";
+      })
+
+      (mapAttrsToList verifyChainPathAssert cfg.clients)
+    ];
+
+    environment.systemPackages = [ pkgs.stunnel ];
+
+    environment.etc."stunnel.cfg".text = ''
+      ${ if cfg.user != null then "setuid = ${cfg.user}" else "" }
+      ${ if cfg.group != null then "setgid = ${cfg.group}" else "" }
+
+      debug = ${cfg.logLevel}
+
+      ${ optionalString cfg.fipsMode "fips = yes" }
+      ${ optionalString cfg.enableInsecureSSLv3 "options = -NO_SSLv3" }
+
+      ; ----- SERVER CONFIGURATIONS -----
+      ${ lib.concatStringsSep "\n"
+           (lib.mapAttrsToList
+             (n: v: ''
+               [${n}]
+               accept = ${toString v.accept}
+               connect = ${toString v.connect}
+               cert = ${v.cert}
+
+             '')
+           cfg.servers)
+      }
+
+      ; ----- CLIENT CONFIGURATIONS -----
+      ${ lib.concatStringsSep "\n"
+           (lib.mapAttrsToList
+             (n: v: ''
+               [${n}]
+               client = yes
+               accept = ${v.accept}
+               connect = ${v.connect}
+               verifyChain = ${yesNo v.verifyChain}
+               verifyPeer = ${yesNo v.verifyPeer}
+               ${optionalString (v.CAPath != null) "CApath = ${v.CAPath}"}
+               ${optionalString (v.verifyHostname != null) "checkHost = ${v.verifyHostname}"}
+               OCSPaia = yes
+
+             '')
+           cfg.clients)
+      }
+    '';
+
+    systemd.services.stunnel = {
+      description = "stunnel TLS tunneling service";
+      after = [ "network.target" ];
+      wants = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      restartTriggers = [ config.environment.etc."stunnel.cfg".source ];
+      serviceConfig = {
+        ExecStart = "${pkgs.stunnel}/bin/stunnel ${config.environment.etc."stunnel.cfg".source}";
+        Type = "forking";
+      };
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix
index c51dd5d946550..adef500b7b5cd 100644
--- a/nixos/modules/services/search/elasticsearch.nix
+++ b/nixos/modules/services/search/elasticsearch.nix
@@ -6,6 +6,7 @@ let
   cfg = config.services.elasticsearch;
 
   es5 = builtins.compareVersions (builtins.parseDrvName cfg.package.name).version "5" >= 0;
+  es6 = builtins.compareVersions (builtins.parseDrvName cfg.package.name).version "6" >= 0;
 
   esConfig = ''
     network.host: ${cfg.listenAddress}
@@ -92,8 +93,6 @@ in {
         node.name: "elasticsearch"
         node.master: true
         node.data: false
-        index.number_of_shards: 5
-        index.number_of_replicas: 1
       '';
     };
 
@@ -165,7 +164,10 @@ in {
       path = [ pkgs.inetutils ];
       environment = {
         ES_HOME = cfg.dataDir;
-        ES_JAVA_OPTS = toString ([ "-Des.path.conf=${configDir}" ] ++ cfg.extraJavaOptions);
+        ES_JAVA_OPTS = toString ( optional (!es6) [ "-Des.path.conf=${configDir}" ]
+                                  ++ cfg.extraJavaOptions);
+      } // optionalAttrs es6 {
+        ES_PATH_CONF = configDir;
       };
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/elasticsearch ${toString cfg.extraCmdLineOptions}";
diff --git a/nixos/modules/services/security/physlock.nix b/nixos/modules/services/security/physlock.nix
index 30224d7fc6bac..97fbd6aae6e02 100644
--- a/nixos/modules/services/security/physlock.nix
+++ b/nixos/modules/services/security/physlock.nix
@@ -30,6 +30,20 @@ in
         '';
       };
 
+      allowAnyUser = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to allow any user to lock the screen. This will install a
+          setuid wrapper to allow any user to start physlock as root, which
+          is a minor security risk. Call the physlock binary to use this instead
+          of using the systemd service.
+
+          Note that you might need to relog to have the correct binary in your
+          PATH upon changing this option.
+        '';
+      };
+
       disableSysRq = mkOption {
         type = types.bool;
         default = true;
@@ -79,28 +93,36 @@ in
 
   ###### implementation
 
-  config = mkIf cfg.enable {
-
-    # for physlock -l and physlock -L
-    environment.systemPackages = [ pkgs.physlock ];
-
-    systemd.services."physlock" = {
-      enable = true;
-      description = "Physlock";
-      wantedBy = optional cfg.lockOn.suspend   "suspend.target"
-              ++ optional cfg.lockOn.hibernate "hibernate.target"
-              ++ cfg.lockOn.extraTargets;
-      before   = optional cfg.lockOn.suspend   "systemd-suspend.service"
-              ++ optional cfg.lockOn.hibernate "systemd-hibernate.service"
-              ++ cfg.lockOn.extraTargets;
-      serviceConfig.Type = "forking";
-      script = ''
-        ${pkgs.physlock}/bin/physlock -d${optionalString cfg.disableSysRq "s"}
-      '';
-    };
+  config = mkIf cfg.enable (mkMerge [
+    {
+
+      # for physlock -l and physlock -L
+      environment.systemPackages = [ pkgs.physlock ];
+
+      systemd.services."physlock" = {
+        enable = true;
+        description = "Physlock";
+        wantedBy = optional cfg.lockOn.suspend   "suspend.target"
+                ++ optional cfg.lockOn.hibernate "hibernate.target"
+                ++ cfg.lockOn.extraTargets;
+        before   = optional cfg.lockOn.suspend   "systemd-suspend.service"
+                ++ optional cfg.lockOn.hibernate "systemd-hibernate.service"
+                ++ cfg.lockOn.extraTargets;
+        serviceConfig = {
+          Type = "forking";
+          ExecStart = "${pkgs.physlock}/bin/physlock -d${optionalString cfg.disableSysRq "s"}";
+        };
+      };
 
-    security.pam.services.physlock = {};
+      security.pam.services.physlock = {};
 
-  };
+    }
+
+    (mkIf cfg.allowAnyUser {
+
+      security.wrappers.physlock = { source = "${pkgs.physlock}/bin/physlock"; user = "root"; };
+
+    })
+  ]);
 
 }
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index fa4aeb22ae9de..fed91756e7696 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -88,6 +88,9 @@ let
     ${flip concatMapStrings v.map (p: ''
       HiddenServicePort ${toString p.port} ${p.destination}
     '')}
+    ${optionalString (v.authorizeClient != null) ''
+      HiddenServiceAuthorizeClient ${v.authorizeClient.authType} ${concatStringsSep "," v.authorizeClient.clientNames}
+    ''}
   ''))
   + cfg.extraConfig;
 
@@ -619,6 +622,33 @@ in
                }));
              };
 
+             authorizeClient = mkOption {
+               default = null;
+               description = "If configured, the hidden service is accessible for authorized clients only.";
+               type = types.nullOr (types.submodule ({config, ...}: {
+
+                 options = {
+
+                   authType = mkOption {
+                     type = types.enum [ "basic" "stealth" ];
+                     description = ''
+                       Either <literal>"basic"</literal> for a general-purpose authorization protocol
+                       or <literal>"stealth"</literal> for a less scalable protocol
+                       that also hides service activity from unauthorized clients.
+                     '';
+                   };
+
+                   clientNames = mkOption {
+                     type = types.nonEmptyListOf (types.strMatching "[A-Za-z0-9+-_]+");
+                     description = ''
+                       Only clients that are listed here are authorized to access the hidden service.
+                       Generated authorization data can be found in <filename>${torDirectory}/onion/$name/hostname</filename>.
+                       Clients need to put this authorization data in their configuration file using <literal>HidServAuth</literal>.
+                     '';
+                   };
+                 };
+               }));
+             };
           };
 
           config = {
diff --git a/nixos/modules/services/web-servers/mighttpd2.nix b/nixos/modules/services/web-servers/mighttpd2.nix
new file mode 100644
index 0000000000000..a888f623616e0
--- /dev/null
+++ b/nixos/modules/services/web-servers/mighttpd2.nix
@@ -0,0 +1,132 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.mighttpd2;
+  configFile = pkgs.writeText "mighty-config" cfg.config;
+  routingFile = pkgs.writeText "mighty-routing" cfg.routing;
+in {
+  options.services.mighttpd2 = {
+    enable = mkEnableOption "Mighttpd2 web server";
+
+    config = mkOption {
+      default = "";
+      example = ''
+        # Example configuration for Mighttpd 2
+        Port: 80
+        # IP address or "*"
+        Host: *
+        Debug_Mode: Yes # Yes or No
+        # If available, "nobody" is much more secure for User:.
+        User: root
+        # If available, "nobody" is much more secure for Group:.
+        Group: root
+        Pid_File: /var/run/mighty.pid
+        Logging: Yes # Yes or No
+        Log_File: /var/log/mighty # The directory must be writable by User:
+        Log_File_Size: 16777216 # bytes
+        Log_Backup_Number: 10
+        Index_File: index.html
+        Index_Cgi: index.cgi
+        Status_File_Dir: /usr/local/share/mighty/status
+        Connection_Timeout: 30 # seconds
+        Fd_Cache_Duration: 10 # seconds
+        # Server_Name: Mighttpd/3.x.y
+        Tls_Port: 443
+        Tls_Cert_File: cert.pem # should change this with an absolute path
+        # should change this with comma-separated absolute paths
+        Tls_Chain_Files: chain.pem
+        # Currently, Tls_Key_File must not be encrypted.
+        Tls_Key_File: privkey.pem # should change this with an absolute path
+        Service: 0 # 0 is HTTP only, 1 is HTTPS only, 2 is both
+      '';
+      type = types.lines;
+      description = ''
+        Verbatim config file to use
+        (see http://www.mew.org/~kazu/proj/mighttpd/en/config.html)
+      '';
+    };
+
+    routing = mkOption {
+      default = "";
+      example = ''
+        # Example routing for Mighttpd 2
+
+        # Domain lists
+        [localhost www.example.com]
+
+        # Entries are looked up in the specified order
+        # All paths must end with "/"
+
+        # A path to CGI scripts should be specified with "=>"
+        /~alice/cgi-bin/ => /home/alice/public_html/cgi-bin/
+
+        # A path to static files should be specified with "->"
+        /~alice/         -> /home/alice/public_html/
+        /cgi-bin/        => /export/cgi-bin/
+
+        # Reverse proxy rules should be specified with ">>"
+        # /path >> host:port/path2
+        # Either "host" or ":port" can be committed, but not both.
+        /app/cal/        >> example.net/calendar/
+        # Yesod app in the same server
+        /app/wiki/       >> 127.0.0.1:3000/
+
+        /                -> /export/www/
+      '';
+      type = types.lines;
+      description = ''
+        Verbatim routing file to use
+        (see http://www.mew.org/~kazu/proj/mighttpd/en/config.html)
+      '';
+    };
+
+    cores = mkOption {
+      default = null;
+      type = types.nullOr types.int;
+      description = ''
+        How many cores to use.
+        If null it will be determined automatically
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    assertions =
+      [ { assertion = cfg.routing != "";
+          message = "You need at least one rule in mighttpd2.routing";
+        }
+      ];
+    systemd.services.mighttpd2 = {
+      description = "Mighttpd2 web server";
+      after = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.haskellPackages.mighttpd2}/bin/mighty \
+            ${configFile} \
+            ${routingFile} \
+            +RTS -N${optionalString (cfg.cores != null) "${cfg.cores}"}
+        '';
+        Type = "simple";
+        User = "mighttpd2";
+        Group = "mighttpd2";
+        Restart = "on-failure";
+        AmbientCapabilities = "cap_net_bind_service";
+        CapabilityBoundingSet = "cap_net_bind_service";
+      };
+    };
+
+    users.extraUsers.mighttpd2 = {
+      group = "mighttpd2";
+      uid = config.ids.uids.mighttpd2;
+      isSystemUser = true;
+    };
+
+    users.extraGroups.mighttpd2.gid = config.ids.gids.mighttpd2;
+  };
+
+  meta.maintainers = with lib.maintainers; [ fgaz ];
+}
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 2951e63e863e1..100fabf902f8e 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -15,6 +15,9 @@ let
     } // (optionalAttrs vhostConfig.enableACME {
       sslCertificate = "/var/lib/acme/${serverName}/fullchain.pem";
       sslCertificateKey = "/var/lib/acme/${serverName}/key.pem";
+    }) // (optionalAttrs (vhostConfig.useACMEHost != null) {
+      sslCertificate = "/var/lib/acme/${vhostConfig.useACMEHost}/fullchain.pem";
+      sslCertificateKey = "/var/lib/acme/${vhostConfig.useACMEHost}/key.pem";
     })
   ) cfg.virtualHosts;
   enableIPv6 = config.networking.enableIPv6;
@@ -174,7 +177,7 @@ let
 
         redirectListen = filter (x: !x.ssl) defaultListen;
 
-        acmeLocation = ''
+        acmeLocation = optionalString (vhost.enableACME || vhost.useACMEHost != null) ''
           location /.well-known/acme-challenge {
             ${optionalString (vhost.acmeFallbackHost != null) "try_files $uri @acme-fallback;"}
             root ${vhost.acmeRoot};
@@ -194,7 +197,7 @@ let
             ${concatMapStringsSep "\n" listenString redirectListen}
 
             server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases};
-            ${optionalString vhost.enableACME acmeLocation}
+            ${acmeLocation}
             location / {
               return 301 https://$host$request_uri;
             }
@@ -204,7 +207,7 @@ let
         server {
           ${concatMapStringsSep "\n" listenString hostListen}
           server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases};
-          ${optionalString vhost.enableACME acmeLocation}
+          ${acmeLocation}
           ${optionalString (vhost.root != null) "root ${vhost.root};"}
           ${optionalString (vhost.globalRedirect != null) ''
             return 301 http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri;
@@ -555,6 +558,14 @@ in
           are mutually exclusive.
         '';
       }
+
+      {
+        assertion = all (conf: !(conf.enableACME && conf.useACMEHost != null)) (attrValues virtualHosts);
+        message = ''
+          Options services.nginx.service.virtualHosts.<name>.enableACME and
+          services.nginx.virtualHosts.<name>.useACMEHost are mutually exclusive.
+        '';
+      }
     ];
 
     systemd.services.nginx = {
@@ -580,7 +591,7 @@ in
     security.acme.certs = filterAttrs (n: v: v != {}) (
       let
         vhostsConfigs = mapAttrsToList (vhostName: vhostConfig: vhostConfig) virtualHosts;
-        acmeEnabledVhosts = filter (vhostConfig: vhostConfig.enableACME) vhostsConfigs;
+        acmeEnabledVhosts = filter (vhostConfig: vhostConfig.enableACME && vhostConfig.useACMEHost == null) vhostsConfigs;
         acmePairs = map (vhostConfig: { name = vhostConfig.serverName; value = {
             user = cfg.user;
             group = lib.mkDefault cfg.group;
diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix
index 29f08cc4f307e..bf18108a1a3cb 100644
--- a/nixos/modules/services/web-servers/nginx/vhost-options.nix
+++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix
@@ -48,7 +48,21 @@ with lib;
     enableACME = mkOption {
       type = types.bool;
       default = false;
-      description = "Whether to ask Let's Encrypt to sign a certificate for this vhost.";
+      description = ''
+        Whether to ask Let's Encrypt to sign a certificate for this vhost.
+        Alternately, you can use an existing certificate through <option>useACMEHost</option>.
+      '';
+    };
+
+    useACMEHost = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        A host of an existing Let's Encrypt certificate to use.
+        This is useful if you have many subdomains and want to avoid hitting the
+        <link xlink:href="https://letsencrypt.org/docs/rate-limits/">rate limit</link>.
+        Alternately, you can generate a certificate through <option>enableACME</option>.
+      '';
     };
 
     acmeRoot = mkOption {
diff --git a/nixos/modules/services/web-servers/traefik.nix b/nixos/modules/services/web-servers/traefik.nix
index 4ede4fc209670..b6c7fef21fb29 100644
--- a/nixos/modules/services/web-servers/traefik.nix
+++ b/nixos/modules/services/web-servers/traefik.nix
@@ -64,6 +64,16 @@ in {
       '';
     };
 
+    group = mkOption {
+      default = "traefik";
+      type = types.string;
+      example = "docker";
+      description = ''
+        Set the group that traefik runs under.
+        For the docker backend this needs to be set to <literal>docker</literal> instead.
+      '';
+    };
+
     package = mkOption {
       default = pkgs.traefik;
       defaultText = "pkgs.traefik";
@@ -87,7 +97,7 @@ in {
         ];
         Type = "simple";
         User = "traefik";
-        Group = "traefik";
+        Group = cfg.group;
         Restart = "on-failure";
         StartLimitInterval = 86400;
         StartLimitBurst = 5;
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index 17a2cde3a65d1..4c76ce0bb1953 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -47,6 +47,18 @@ in
             ${getBin config.hardware.pulseaudio.package}/bin/pactl load-module module-device-manager "do_routing=1"
           ''}
 
+          if [ -f "$HOME/.config/kdeglobals" ]
+          then
+              # Remove extraneous font style names.
+              # See also: https://phabricator.kde.org/D9070
+              ${getBin pkgs.gnused}/bin/sed -i "$HOME/.config/kdeglobals" \
+                  -e '/^fixed=/ s/,Regular$//' \
+                  -e '/^font=/ s/,Regular$//' \
+                  -e '/^menuFont=/ s/,Regular$//' \
+                  -e '/^smallestReadableFont=/ s/,Regular$//' \
+                  -e '/^toolBarFont=/ s/,Regular$//'
+          fi
+
           exec "${getBin plasma5.plasma-workspace}/bin/startkde"
         '';
       };
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 9d5d03638e049..c0c9d7ea47f7f 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -3,9 +3,7 @@
 with lib;
 
 let
-  xcfg = config.services.xserver;
-  pcfg = config.hardware.pulseaudio;
-  cfg = xcfg.desktopManager.xfce;
+  cfg = config.services.xserver.desktopManager.xfce;
 in
 
 {
@@ -52,82 +50,93 @@ in
         description = "Application used by XFCE to lock the screen.";
       };
     };
-
   };
 
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs.xfce // pkgs; [
+      # Get GTK+ themes and gtk-update-icon-cache
+      gtk2.out
+
+      # Supplies some abstract icons such as:
+      # utilities-terminal, accessories-text-editor
+      gnome3.defaultIconTheme
+
+      hicolor_icon_theme
+      tango-icon-theme
+      xfce4-icon-theme
+
+      desktop_file_utils
+      shared_mime_info
+
+      # Needed by Xfce's xinitrc script
+      # TODO: replace with command -v
+      which
+
+      exo
+      garcon
+      gtk-xfce-engine
+      gvfs
+      libxfce4ui
+      tumbler
+      xfconf
+
+      mousepad
+      ristretto
+      xfce4-appfinder
+      xfce4-screenshooter
+      xfce4-session
+      xfce4-settings
+      xfce4-terminal
+
+      (thunar.override { thunarPlugins = cfg.thunarPlugins; })
+      thunar-volman # TODO: drop
+    ] ++ (if config.hardware.pulseaudio.enable
+          then [ xfce4-mixer-pulse xfce4-volumed-pulse ]
+	  else [ xfce4-mixer xfce4-volumed ])
+      # TODO: NetworkManager doesn't belong here
+      ++ optionals config.networking.networkmanager.enable [ networkmanagerapplet ]
+      ++ optionals config.powerManagement.enable [ xfce4-power-manager ]
+      ++ optionals cfg.enableXfwm [ xfwm4 ]
+      ++ optionals (!cfg.noDesktop) [
+        xfce4-panel
+        xfce4-notifyd
+        xfdesktop
+      ];
+
+    environment.pathsToLink = [
+      "/share/xfce4"
+      "/share/themes"
+      "/share/mime"
+      "/share/desktop-directories"
+      "/share/gtksourceview-2.0"
+    ];
+
+    environment.variables = {
+      GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache";
+      GIO_EXTRA_MODULES = [ "${pkgs.xfce.gvfs}/lib/gio/modules" ];
+    };
 
-  config = mkIf (xcfg.enable && cfg.enable) {
-
-    services.xserver.desktopManager.session = singleton
-      { name = "xfce";
-        bgSupport = true;
-        start =
-          ''
-            ${cfg.extraSessionCommands}
+    services.xserver.desktopManager.session = [{
+      name = "xfce";
+      bgSupport = true;
+      start = ''
+        ${cfg.extraSessionCommands}
 
-            # Set GTK_PATH so that GTK+ can find the theme engines.
-            export GTK_PATH="${config.system.path}/lib/gtk-2.0:${config.system.path}/lib/gtk-3.0"
+        # Set GTK_PATH so that GTK+ can find the theme engines.
+        export GTK_PATH="${config.system.path}/lib/gtk-2.0:${config.system.path}/lib/gtk-3.0"
 
-            # Set GTK_DATA_PREFIX so that GTK+ can find the Xfce themes.
-            export GTK_DATA_PREFIX=${config.system.path}
+        # Set GTK_DATA_PREFIX so that GTK+ can find the Xfce themes.
+        export GTK_DATA_PREFIX=${config.system.path}
 
-            ${pkgs.stdenv.shell} ${pkgs.xfce.xinitrc} &
-            waitPID=$!
-          '';
-      };
+        ${pkgs.stdenv.shell} ${pkgs.xfce.xinitrc} &
+        waitPID=$!
+      '';
+    }];
 
     services.xserver.updateDbusEnvironment = true;
 
-    environment.systemPackages =
-      [ pkgs.gtk2.out # To get GTK+'s themes and gtk-update-icon-cache
-        pkgs.hicolor_icon_theme
-        pkgs.tango-icon-theme
-        pkgs.shared_mime_info
-        pkgs.which # Needed by the xfce's xinitrc script.
-        pkgs."${cfg.screenLock}"
-        pkgs.xfce.exo
-        pkgs.xfce.gtk_xfce_engine
-        pkgs.xfce.mousepad
-        pkgs.xfce.ristretto
-        pkgs.xfce.terminal
-       (pkgs.xfce.thunar.override { thunarPlugins = cfg.thunarPlugins; })
-        pkgs.xfce.xfce4icontheme
-        pkgs.xfce.xfce4session
-        pkgs.xfce.xfce4settings
-       (if pcfg.enable then pkgs.xfce.xfce4mixer_pulse else pkgs.xfce.xfce4mixer)
-       (if pcfg.enable then pkgs.xfce.xfce4volumed_pulse else pkgs.xfce.xfce4volumed)
-        pkgs.xfce.xfce4-screenshooter
-        pkgs.xfce.xfconf
-        # This supplies some "abstract" icons such as
-        # "utilities-terminal" and "accessories-text-editor".
-        pkgs.gnome3.defaultIconTheme
-        pkgs.desktop_file_utils
-        pkgs.xfce.libxfce4ui
-        pkgs.xfce.garcon
-        pkgs.xfce.thunar_volman
-        pkgs.xfce.gvfs
-        pkgs.xfce.xfce4_appfinder
-        pkgs.xfce.tumbler       # found via dbus
-      ]
-      ++ optional cfg.enableXfwm pkgs.xfce.xfwm4
-      ++ optional config.powerManagement.enable pkgs.xfce.xfce4_power_manager
-      ++ optional config.networking.networkmanager.enable pkgs.networkmanagerapplet
-      ++ optionals (!cfg.noDesktop)
-         [ pkgs.xfce.xfce4panel
-           pkgs.xfce.xfdesktop
-	   pkgs.xfce.xfce4notifyd  # found via dbus
-         ];
-
-    environment.pathsToLink =
-      [ "/share/xfce4" "/share/themes" "/share/mime" "/share/desktop-directories" "/share/gtksourceview-2.0" ];
-
-    environment.variables.GIO_EXTRA_MODULES = [ "${pkgs.xfce.gvfs}/lib/gio/modules" ];
-    environment.variables.GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache";
-
     # Enable helpful DBus services.
     services.udisks2.enable = true;
     services.upower.enable = config.powerManagement.enable;
-
   };
-
 }
diff --git a/nixos/modules/services/x11/window-managers/2bwm.nix b/nixos/modules/services/x11/window-managers/2bwm.nix
index e3f5ec7dbe674..fdbdf35b0f5af 100644
--- a/nixos/modules/services/x11/window-managers/2bwm.nix
+++ b/nixos/modules/services/x11/window-managers/2bwm.nix
@@ -25,12 +25,12 @@ in
       { name = "2bwm";
         start =
           ''
-            ${pkgs."2bwm"}/bin/2bwm &
+            ${pkgs._2bwm}/bin/2bwm &
             waitPID=$!
           '';
       };
 
-    environment.systemPackages = [ pkgs."2bwm" ];
+    environment.systemPackages = [ pkgs._2bwm ];
 
   };
 
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index 7acd621f53ab3..f96d3c5afbac4 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -548,7 +548,7 @@ in
           knownVideoDrivers;
       in optional (driver != null) ({ inherit name; modules = []; driverName = name; } // driver));
 
-    nixpkgs.config.xorg = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { abiCompat = "1.18"; };
+    nixpkgs.config = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { xorg.abiCompat = "1.18"; };
 
     assertions = [
       { assertion = config.security.polkit.enable;
diff --git a/nixos/modules/system/boot/initrd-network.nix b/nixos/modules/system/boot/initrd-network.nix
index 6e226c1906090..4a6e1c7e56e56 100644
--- a/nixos/modules/system/boot/initrd-network.nix
+++ b/nixos/modules/system/boot/initrd-network.nix
@@ -40,6 +40,10 @@ in
         kernel documentation</link>.  Otherwise, if
         <option>networking.useDHCP</option> is enabled, an IP address
         is acquired using DHCP.
+
+        You should add the module(s) required for your network card to
+        boot.initrd.availableKernelModules. lspci -v -s &lt;ethernet controller&gt;
+        will tell you which.
       '';
     };
 
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index 90074a1ba77b5..d21908f845375 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -206,12 +206,14 @@ in
         "xhci_hcd"
         "xhci_pci"
         "usbhid"
-        "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat" "hid_logitech_hidpp"
+        "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat"
+        "hid_logitech_hidpp" "hid_logitech_dj"
 
-        # Misc. keyboard stuff.
+      ] ++ optionals (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [
+        # Misc. x86 keyboard stuff.
         "pcips2" "atkbd" "i8042"
 
-        # Needed by the stage 2 init script.
+        # x86 RTC needed by the stage 2 init script.
         "rtc_cmos"
       ];
 
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 2c0a165887bd8..30c54ddd0e4e8 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -24,7 +24,11 @@ let
 
   kernel = config.boot.kernelPackages;
 
-  packages = if config.boot.zfs.enableUnstable then {
+  packages = if config.boot.zfs.enableLegacyCrypto then {
+    spl = kernel.splLegacyCrypto;
+    zfs = kernel.zfsLegacyCrypto;
+    zfsUser = pkgs.zfsLegacyCrypto;
+  } else if config.boot.zfs.enableUnstable then {
     spl = kernel.splUnstable;
     zfs = kernel.zfsUnstable;
     zfsUser = pkgs.zfsUnstable;
@@ -75,6 +79,27 @@ in
           '';
       };
 
+      enableLegacyCrypto = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enabling this option will allow you to continue to use the old format for
+          encrypted datasets. With the inclusion of stability patches the format of
+          encrypted datasets has changed. They can still be accessed and mounted but
+          in read-only mode mounted. It is highly recommended to convert them to
+          the new format.
+
+          This option is only for convenience to people that cannot convert their
+          datasets to the new format yet and it will be removed in due time.
+
+          For migration strategies from old format to this new one, check the Wiki:
+          https://nixos.wiki/wiki/NixOS_on_ZFS#Encrypted_Dataset_Format_Change
+
+          See https://github.com/zfsonlinux/zfs/pull/6864 for more details about
+          the stability patches.
+          '';
+      };
+
       extraPools = mkOption {
         type = types.listOf types.str;
         default = [];
diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
index 1f424f84c6e01..63d07832d105f 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -230,9 +230,7 @@ let
               RemainAfterExit = true;
             };
             script = ''
-              ip tuntap add dev "${i.name}" \
-              ${optionalString (i.virtualType != null) "mode ${i.virtualType}"} \
-              user "${i.virtualOwner}"
+              ip tuntap add dev "${i.name}" mode "${i.virtualType}" user "${i.virtualOwner}"
             '';
             postStop = ''
               ip link del ${i.name} || true
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index a365a01bfb1ed..5d72ad0f1bde1 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -74,21 +74,17 @@ in
         networks."99-main" = genericNetwork mkDefault;
       }
       (mkMerge (flip map interfaces (i: {
-        netdevs = mkIf i.virtual (
-          let
-            devType = if i.virtualType != null then i.virtualType
-              else (if hasPrefix "tun" i.name then "tun" else "tap");
-          in {
-            "40-${i.name}" = {
-              netdevConfig = {
-                Name = i.name;
-                Kind = devType;
-              };
-              "${devType}Config" = optionalAttrs (i.virtualOwner != null) {
-                User = i.virtualOwner;
-              };
+        netdevs = mkIf i.virtual ({
+          "40-${i.name}" = {
+            netdevConfig = {
+              Name = i.name;
+              Kind = i.virtualType;
             };
-          });
+            "${i.virtualType}Config" = optionalAttrs (i.virtualOwner != null) {
+              User = i.virtualOwner;
+            };
+          };
+        });
         networks."40-${i.name}" = mkMerge [ (genericNetwork mkDefault) {
           name = mkDefault i.name;
           DHCP = mkForce (dhcpStr
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index b7e85e402aa9e..f4851988d63df 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -273,11 +273,13 @@ let
       };
 
       virtualType = mkOption {
-        default = null;
-        type = with types; nullOr (enum [ "tun" "tap" ]);
+        default = if hasPrefix "tun" name then "tun" else "tap";
+        defaultText = literalExample ''if hasPrefix "tun" name then "tun" else "tap"'';
+        type = with types; enum [ "tun" "tap" ];
         description = ''
-          The explicit type of interface to create. Accepts tun or tap strings.
-          Also accepts null to implicitly detect the type of device.
+          The type of interface to create.
+          The default is TUN for an interface name starting
+          with "tun", otherwise TAP.
         '';
       };
 
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index 9b4136223c0f2..41dec2af9ed43 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -4,13 +4,10 @@
 { config, lib, pkgs, ... }:
 
 with lib;
+with import ../../lib/qemu-flags.nix { inherit pkgs; };
 
 let
   kernel = config.boot.kernelPackages.kernel;
-  # FIXME: figure out a common place for this instead of copy pasting
-  serialDevice = if pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64 then "ttyS0"
-        else if pkgs.stdenv.isArm || pkgs.stdenv.isAarch64 then "ttyAMA0"
-        else throw "Unknown QEMU serial device for system '${pkgs.stdenv.system}'";
 in
 
 {
@@ -28,8 +25,8 @@ in
 
     systemd.services.backdoor =
       { wantedBy = [ "multi-user.target" ];
-        requires = [ "dev-hvc0.device" "dev-${serialDevice}.device" ];
-        after = [ "dev-hvc0.device" "dev-${serialDevice}.device" ];
+        requires = [ "dev-hvc0.device" "dev-${qemuSerialDevice}.device" ];
+        after = [ "dev-hvc0.device" "dev-${qemuSerialDevice}.device" ];
         script =
           ''
             export USER=root
@@ -46,7 +43,7 @@ in
 
             cd /tmp
             exec < /dev/hvc0 > /dev/hvc0
-            while ! exec 2> /dev/${serialDevice}; do sleep 0.1; done
+            while ! exec 2> /dev/${qemuSerialDevice}; do sleep 0.1; done
             echo "connecting to host..." >&2
             stty -F /dev/hvc0 raw -echo # prevent nl -> cr/nl conversion
             echo
@@ -55,10 +52,10 @@ in
         serviceConfig.KillSignal = "SIGHUP";
       };
 
-    # Prevent agetty from being instantiated on ${serialDevice}, since it
-    # interferes with the backdoor (writes to ${serialDevice} will randomly fail
+    # Prevent agetty from being instantiated on the serial device, since it
+    # interferes with the backdoor (writes to it will randomly fail
     # with EIO).  Likewise for hvc0.
-    systemd.services."serial-getty@${serialDevice}".enable = false;
+    systemd.services."serial-getty@${qemuSerialDevice}".enable = false;
     systemd.services."serial-getty@hvc0".enable = false;
 
     boot.initrd.preDeviceCommands =
@@ -94,7 +91,7 @@ in
     # Panic if an error occurs in stage 1 (rather than waiting for
     # user intervention).
     boot.kernelParams =
-      [ "console=${serialDevice}" "panic=1" "boot.panic_on_fail" ];
+      [ "console=${qemuSerialDevice}" "panic=1" "boot.panic_on_fail" ];
 
     # `xwininfo' is used by the test driver to query open windows.
     environment.systemPackages = [ pkgs.xorg.xwininfo ];
diff --git a/nixos/modules/virtualisation/container-config.nix b/nixos/modules/virtualisation/container-config.nix
index b4f9d8b6fc178..5e368acd6d8b5 100644
--- a/nixos/modules/virtualisation/container-config.nix
+++ b/nixos/modules/virtualisation/container-config.nix
@@ -11,7 +11,7 @@ with lib;
     services.udisks2.enable = mkDefault false;
     powerManagement.enable = mkDefault false;
 
-    networking.useHostResolvConf = true;
+    networking.useHostResolvConf = mkDefault true;
 
     # Containers should be light-weight, so start sshd on demand.
     services.openssh.startWhenNeeded = mkDefault true;
diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix
index 75717e08ab2af..2fb38059b261a 100644
--- a/nixos/modules/virtualisation/google-compute-image.nix
+++ b/nixos/modules/virtualisation/google-compute-image.nix
@@ -212,7 +212,7 @@ in
           echo "Obtaining SSH keys..."
           mkdir -m 0700 -p /root/.ssh
           AUTH_KEYS=$(${mktemp})
-          ${wget} -O $AUTH_KEYS http://metadata.google.internal/computeMetadata/v1/project/attributes/sshKeys
+          ${wget} -O $AUTH_KEYS --header="Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/attributes/sshKeys
           if [ -s $AUTH_KEYS ]; then
 
             # Read in key one by one, split in case Google decided
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index 64465ae185223..a369b7ddbe1d8 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -128,6 +128,7 @@ in {
           dmidecode
           dnsmasq
           ebtables
+          cfg.qemuPackage # libvirtd requires qemu-img to manage disk images
         ]
         ++ optional vswitch.enable vswitch.package;
 
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index 26f7945a4eda0..13d0eb7de5c2d 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -10,21 +10,11 @@
 { config, lib, pkgs, ... }:
 
 with lib;
+with import ../../lib/qemu-flags.nix { inherit pkgs; };
 
 let
 
   qemu = config.system.build.qemu or pkgs.qemu_test;
-  qemuKvm = {
-    "i686-linux" = "${qemu}/bin/qemu-kvm";
-    "x86_64-linux" = "${qemu}/bin/qemu-kvm -cpu kvm64";
-    "armv7l-linux" = "${qemu}/bin/qemu-system-arm -enable-kvm -machine virt -cpu host";
-    "aarch64-linux" = "${qemu}/bin/qemu-system-aarch64 -enable-kvm -machine virt,gic-version=host -cpu host";
-  }.${pkgs.stdenv.system};
-
-  # FIXME: figure out a common place for this instead of copy pasting
-  serialDevice = if pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64 then "ttyS0"
-        else if pkgs.stdenv.isArm || pkgs.stdenv.isAarch64 then "ttyAMA0"
-        else throw "Unknown QEMU serial device for system '${pkgs.stdenv.system}'";
 
   vmName =
     if config.networking.hostName == ""
@@ -34,7 +24,7 @@ let
   cfg = config.virtualisation;
 
   qemuGraphics = if cfg.graphics then "" else "-nographic";
-  kernelConsole = if cfg.graphics then "" else "console=${serialDevice}";
+  kernelConsole = if cfg.graphics then "" else "console=${qemuSerialDevice}";
   ttys = [ "tty1" "tty2" "tty3" "tty4" "tty5" "tty6" ];
 
   # Shell script to start the VM.
@@ -83,7 +73,7 @@ let
       '')}
 
       # Start QEMU.
-      exec ${qemuKvm} \
+      exec ${qemuBinary qemu} \
           -name ${vmName} \
           -m ${toString config.virtualisation.memorySize} \
           -smp ${toString config.virtualisation.cores} \
diff --git a/nixos/modules/virtualisation/virtualbox-image.nix b/nixos/modules/virtualisation/virtualbox-image.nix
index 00381c426d23f..a544403e6bed3 100644
--- a/nixos/modules/virtualisation/virtualbox-image.nix
+++ b/nixos/modules/virtualisation/virtualbox-image.nix
@@ -25,7 +25,7 @@ in {
       name = "nixos-ova-${config.system.nixosLabel}-${pkgs.stdenv.system}";
 
       inherit pkgs lib config;
-      partitioned = true;
+      partitionTableType = "legacy";
       diskSize = cfg.baseImageSize;
 
       postVM =
diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix
index c7656bc309c0b..afc5a42f8b4e6 100644
--- a/nixos/modules/virtualisation/xen-dom0.nix
+++ b/nixos/modules/virtualisation/xen-dom0.nix
@@ -35,24 +35,19 @@ in
       description = ''
         The package used for Xen binary.
       '';
+      relatedPackages = [ "xen" "xen-light" ];
     };
 
-    virtualisation.xen.qemu = mkOption {
-      type = types.path;
-      defaultText = "\${pkgs.xen}/lib/xen/bin/qemu-system-i386";
-      example = literalExample "''${pkgs.qemu_xen-light}/bin/qemu-system-i386";
-      description = ''
-        The qemu binary to use for Dom-0 backend.
-      '';
-    };
-
-    virtualisation.xen.qemu-package = mkOption {
+    virtualisation.xen.package-qemu = mkOption {
       type = types.package;
       defaultText = "pkgs.xen";
       example = literalExample "pkgs.qemu_xen-light";
       description = ''
-        The package with qemu binaries for xendomains.
+        The package with qemu binaries for dom0 qemu and xendomains.
       '';
+      relatedPackages = [ "xen"
+                          { name = "qemu_xen-light"; comment = "For use with pkgs.xen-light."; }
+                        ];
     };
 
     virtualisation.xen.bootParams =
@@ -158,8 +153,7 @@ in
     } ];
 
     virtualisation.xen.package = mkDefault pkgs.xen;
-    virtualisation.xen.qemu = mkDefault "${pkgs.xen}/lib/xen/bin/qemu-system-i386";
-    virtualisation.xen.qemu-package = mkDefault pkgs.xen;
+    virtualisation.xen.package-qemu = mkDefault pkgs.xen;
     virtualisation.xen.stored = mkDefault "${cfg.package}/bin/oxenstored";
 
     environment.systemPackages = [ cfg.package ];
@@ -339,7 +333,8 @@ in
       after = [ "xen-console.service" ];
       requires = [ "xen-store.service" ];
       serviceConfig.ExecStart = ''
-        ${cfg.qemu} -xen-attach -xen-domid 0 -name dom0 -M xenpv \
+        ${cfg.package-qemu}/${cfg.package-qemu.qemu-system-i386} \
+           -xen-attach -xen-domid 0 -name dom0 -M xenpv \
            -nographic -monitor /dev/null -serial /dev/null -parallel /dev/null
         '';
     };
@@ -448,7 +443,7 @@ in
       before = [ "dhcpd.service" ];
       restartIfChanged = false;
       serviceConfig.RemainAfterExit = "yes";
-      path = [ cfg.package cfg.qemu-package ];
+      path = [ cfg.package cfg.package-qemu ];
       environment.XENDOM_CONFIG = "${cfg.package}/etc/sysconfig/xendomains";
       preStart = "mkdir -p /var/lock/subsys -m 755";
       serviceConfig.ExecStart = "${cfg.package}/etc/init.d/xendomains start";
diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix
index 34a413f1ac3e6..9d4a551a958b8 100644
--- a/nixos/release-combined.nix
+++ b/nixos/release-combined.nix
@@ -2,7 +2,7 @@
 # and nixos-14.04). The channel is updated every time the ‘tested’ job
 # succeeds, and all other jobs have finished (they may fail).
 
-{ nixpkgs ? { outPath = ./..; revCount = 56789; shortRev = "gfedcba"; }
+{ nixpkgs ? { outPath = (import ../lib).cleanSource ./..; revCount = 56789; shortRev = "gfedcba"; }
 , stableBranch ? false
 , supportedSystems ? [ "x86_64-linux" ]
 , limitedSupportedSystems ? [ "i686-linux" ]
@@ -52,12 +52,13 @@ in rec {
         (all nixos.dummy)
         (all nixos.manual)
 
-        (all nixos.iso_minimal)
+        nixos.iso_minimal.x86_64-linux
+        nixos.iso_minimal.i686-linux
         nixos.iso_graphical.x86_64-linux
         nixos.ova.x86_64-linux
 
         #(all nixos.tests.containers)
-        nixos.tests.chromium
+        nixos.tests.chromium.x86_64-linux
         (all nixos.tests.firefox)
         (all nixos.tests.firewall)
         (all nixos.tests.gnome3)
@@ -80,7 +81,7 @@ in rec {
         (all nixos.tests.boot.uefiUsb)
         (all nixos.tests.boot-stage1)
         (all nixos.tests.hibernate)
-        nixos.tests.docker
+        nixos.tests.docker.x86_64-linux
         (all nixos.tests.ecryptfs)
         (all nixos.tests.env)
         (all nixos.tests.ipv6)
diff --git a/nixos/release-small.nix b/nixos/release-small.nix
index e9f3cfb4de539..2b532c70763fa 100644
--- a/nixos/release-small.nix
+++ b/nixos/release-small.nix
@@ -2,7 +2,7 @@
 # small subset of Nixpkgs, mostly useful for servers that need fast
 # security updates.
 
-{ nixpkgs ? { outPath = ./..; revCount = 56789; shortRev = "gfedcba"; }
+{ nixpkgs ? { outPath = (import ../lib).cleanSource ./..; revCount = 56789; shortRev = "gfedcba"; }
 , stableBranch ? false
 , supportedSystems ? [ "x86_64-linux" ] # no i686-linux
 }:
@@ -41,6 +41,7 @@ in rec {
         nfs3
         openssh
         php-pcre
+        predictable-interface-names
         proxy
         simple;
       installer = {
diff --git a/nixos/release.nix b/nixos/release.nix
index 3dd670c30bade..a34de42297be5 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -1,8 +1,9 @@
-{ nixpkgs ? { outPath = ./..; revCount = 56789; shortRev = "gfedcba"; }
+{ nixpkgs ? { outPath = (import ../lib).cleanSource ./..; revCount = 56789; shortRev = "gfedcba"; }
 , stableBranch ? false
 , supportedSystems ? [ "x86_64-linux" "aarch64-linux" ]
 }:
 
+with import ../pkgs/top-level/release-lib.nix { inherit supportedSystems; };
 with import ../lib;
 
 let
@@ -11,15 +12,15 @@ let
   versionSuffix =
     (if stableBranch then "." else "pre") + "${toString nixpkgs.revCount}.${nixpkgs.shortRev}";
 
-  forAllSystems = genAttrs supportedSystems;
-
   importTest = fn: args: system: import fn ({
     inherit system;
   } // args);
 
-  callTest = fn: args: forAllSystems (system: hydraJob (importTest fn args system));
+  callTestOnTheseSystems = systems: fn: args: forTheseSystems systems (system: hydraJob (importTest fn args system));
+  callTest = callTestOnTheseSystems supportedSystems;
 
-  callSubTests = fn: args: let
+  callSubTests = callSubTestsOnTheseSystems supportedSystems;
+  callSubTestsOnTheseSystems = systems: fn: args: let
     discover = attrs: let
       subTests = filterAttrs (const (hasAttr "test")) attrs;
     in mapAttrs (const (t: hydraJob t.test)) subTests;
@@ -28,10 +29,7 @@ let
       ${system} = test;
     }) (discover (importTest fn args system));
 
-  # If the test is only for a particular system, use only the specified
-  # system instead of generating attributes for all available systems.
-  in if args ? system then discover (import fn args)
-     else foldAttrs mergeAttrs {} (map discoverForSystem supportedSystems);
+  in foldAttrs mergeAttrs {} (map discoverForSystem (intersectLists systems supportedSystems));
 
   pkgs = import nixpkgs { system = "x86_64-linux"; };
 
@@ -91,13 +89,13 @@ let
 
   makeNetboot = config:
     let
-      config_evaled = import lib/eval-config.nix config;
-      build = config_evaled.config.system.build;
-      kernelTarget = config_evaled.pkgs.stdenv.platform.kernelTarget;
+      configEvaled = import lib/eval-config.nix config;
+      build = configEvaled.config.system.build;
+      kernelTarget = configEvaled.pkgs.stdenv.platform.kernelTarget;
     in
       pkgs.symlinkJoin {
-        name="netboot";
-        paths=[
+        name = "netboot";
+        paths = [
           build.netbootRamdisk
           build.kernel
           build.netbootIpxeScript
@@ -108,6 +106,7 @@ let
           echo "file initrd $out/initrd" >> $out/nix-support/hydra-build-products
           echo "file ipxe $out/netboot.ipxe" >> $out/nix-support/hydra-build-products
         '';
+        preferLocalBuild = true;
       };
 
 
@@ -124,22 +123,13 @@ in rec {
   # Build the initial ramdisk so Hydra can keep track of its size over time.
   initialRamdisk = buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.initialRamdisk);
 
-  netboot = {
-    x86_64-linux = makeNetboot {
-      system = "x86_64-linux";
-      modules = [
-        ./modules/installer/netboot/netboot-minimal.nix
-        versionModule
-      ];
-    };
-  } // (optionalAttrs (elem "aarch64-linux" supportedSystems) {
-    aarch64-linux = makeNetboot {
-      system = "aarch64-linux";
-      modules = [
-        ./modules/installer/netboot/netboot-minimal.nix
-        versionModule
-      ];
-    };});
+  netboot = forTheseSystems [ "x86_64-linux" "aarch64-linux" ] (system: makeNetboot {
+    inherit system;
+    modules = [
+      ./modules/installer/netboot/netboot-minimal.nix
+      versionModule
+    ];
+  });
 
   iso_minimal = forAllSystems (system: makeIso {
     module = ./modules/installer/cd-dvd/installation-cd-minimal.nix;
@@ -147,7 +137,7 @@ in rec {
     inherit system;
   });
 
-  iso_graphical = genAttrs [ "x86_64-linux" ] (system: makeIso {
+  iso_graphical = forTheseSystems [ "x86_64-linux" ] (system: makeIso {
     module = ./modules/installer/cd-dvd/installation-cd-graphical-kde.nix;
     type = "graphical";
     inherit system;
@@ -155,7 +145,7 @@ in rec {
 
   # A variant with a more recent (but possibly less stable) kernel
   # that might support more hardware.
-  iso_minimal_new_kernel = genAttrs [ "x86_64-linux" ] (system: makeIso {
+  iso_minimal_new_kernel = forTheseSystems [ "x86_64-linux" ] (system: makeIso {
     module = ./modules/installer/cd-dvd/installation-cd-minimal-new-kernel.nix;
     type = "minimal-new-kernel";
     inherit system;
@@ -163,7 +153,7 @@ in rec {
 
 
   # A bootable VirtualBox virtual appliance as an OVA file (i.e. packaged OVF).
-  ova = genAttrs [ "x86_64-linux" ] (system:
+  ova = forTheseSystems [ "x86_64-linux" ] (system:
 
     with import nixpkgs { inherit system; };
 
@@ -237,8 +227,9 @@ in rec {
   tests.blivet = callTest tests/blivet.nix {};
   tests.boot = callSubTests tests/boot.nix {};
   tests.boot-stage1 = callTest tests/boot-stage1.nix {};
-  tests.cadvisor = hydraJob (import tests/cadvisor.nix { system = "x86_64-linux"; });
-  tests.chromium = (callSubTests tests/chromium.nix { system = "x86_64-linux"; }).stable;
+  tests.borgbackup = callTest tests/borgbackup.nix {};
+  tests.cadvisor = callTestOnTheseSystems ["x86_64-linux"] tests/cadvisor.nix {};
+  tests.chromium = (callSubTestsOnTheseSystems ["x86_64-linux"] tests/chromium.nix {}).stable;
   tests.cjdns = callTest tests/cjdns.nix {};
   tests.cloud-init = callTest tests/cloud-init.nix {};
   tests.containers-ipv4 = callTest tests/containers-ipv4.nix {};
@@ -252,20 +243,20 @@ in rec {
   tests.containers-hosts = callTest tests/containers-hosts.nix {};
   tests.containers-macvlans = callTest tests/containers-macvlans.nix {};
   tests.couchdb = callTest tests/couchdb.nix {};
-  tests.docker = hydraJob (import tests/docker.nix { system = "x86_64-linux"; });
-  tests.docker-edge = hydraJob (import tests/docker-edge.nix { system = "x86_64-linux"; });
+  tests.docker = callTestOnTheseSystems ["x86_64-linux"] tests/docker.nix {};
+  tests.docker-edge = callTestOnTheseSystems ["x86_64-linux"] tests/docker-edge.nix {};
   tests.dovecot = callTest tests/dovecot.nix {};
-  tests.dnscrypt-proxy = callTest tests/dnscrypt-proxy.nix { system = "x86_64-linux"; };
+  tests.dnscrypt-proxy = callTestOnTheseSystems ["x86_64-linux"] tests/dnscrypt-proxy.nix {};
   tests.ecryptfs = callTest tests/ecryptfs.nix {};
-  tests.etcd = hydraJob (import tests/etcd.nix { system = "x86_64-linux"; });
-  tests.ec2-nixops = hydraJob (import tests/ec2.nix { system = "x86_64-linux"; }).boot-ec2-nixops;
-  tests.ec2-config = hydraJob (import tests/ec2.nix { system = "x86_64-linux"; }).boot-ec2-config;
-  tests.elk = hydraJob (import tests/elk.nix { system = "x86_64-linux"; });
+  tests.etcd = callTestOnTheseSystems ["x86_64-linux"] tests/etcd.nix {};
+  tests.ec2-nixops = (callSubTestsOnTheseSystems ["x86_64-linux"] tests/ec2.nix {}).boot-ec2-nixops;
+  tests.ec2-config = (callSubTestsOnTheseSystems ["x86_64-linux"] tests/ec2.nix {}).boot-ec2-config;
+  tests.elk = callSubTestsOnTheseSystems ["x86_64-linux"] tests/elk.nix {};
   tests.env = callTest tests/env.nix {};
   tests.ferm = callTest tests/ferm.nix {};
   tests.firefox = callTest tests/firefox.nix {};
   tests.firewall = callTest tests/firewall.nix {};
-  tests.fleet = hydraJob (import tests/fleet.nix { system = "x86_64-linux"; });
+  tests.fleet = callTestOnTheseSystems ["x86_64-linux"] tests/fleet.nix {};
   #tests.gitlab = callTest tests/gitlab.nix {};
   tests.gitolite = callTest tests/gitolite.nix {};
   tests.gocd-agent = callTest tests/gocd-agent.nix {};
@@ -276,6 +267,7 @@ in rec {
   tests.graphite = callTest tests/graphite.nix {};
   tests.hardened = callTest tests/hardened.nix { };
   tests.hibernate = callTest tests/hibernate.nix {};
+  tests.home-assistant = callTest tests/home-assistant.nix { };
   tests.hound = callTest tests/hound.nix {};
   tests.hocker-fetchdocker = callTest tests/hocker-fetchdocker {};
   tests.i3wm = callTest tests/i3wm.nix {};
@@ -301,6 +293,7 @@ in rec {
   tests.login = callTest tests/login.nix {};
   #tests.logstash = callTest tests/logstash.nix {};
   tests.mathics = callTest tests/mathics.nix {};
+  tests.matrix-synapse = callTest tests/matrix-synapse.nix {};
   tests.mesos = callTest tests/mesos.nix {};
   tests.misc = callTest tests/misc.nix {};
   tests.mongodb = callTest tests/mongodb.nix {};
@@ -313,6 +306,7 @@ in rec {
   tests.nat.firewall = callTest tests/nat.nix { withFirewall = true; };
   tests.nat.firewall-conntrack = callTest tests/nat.nix { withFirewall = true; withConntrackHelpers = true; };
   tests.nat.standalone = callTest tests/nat.nix { withFirewall = false; };
+  tests.netdata = callTest tests/netdata.nix { };
   tests.networking.networkd = callSubTests tests/networking.nix { networkd = true; };
   tests.networking.scripted = callSubTests tests/networking.nix { networkd = false; };
   # TODO: put in networking.nix after the test becomes more complete
@@ -326,13 +320,14 @@ in rec {
   tests.openssh = callTest tests/openssh.nix {};
   tests.owncloud = callTest tests/owncloud.nix {};
   tests.pam-oath-login = callTest tests/pam-oath-login.nix {};
-  #tests.panamax = hydraJob (import tests/panamax.nix { system = "x86_64-linux"; });
+  #tests.panamax = callTestOnTheseSystems ["x86_64-linux"] tests/panamax.nix {};
   tests.peerflix = callTest tests/peerflix.nix {};
   tests.php-pcre = callTest tests/php-pcre.nix {};
   tests.postgresql = callSubTests tests/postgresql.nix {};
   tests.pgmanage = callTest tests/pgmanage.nix {};
   tests.postgis = callTest tests/postgis.nix {};
   #tests.pgjwt = callTest tests/pgjwt.nix {};
+  tests.predictable-interface-names = callSubTests tests/predictable-interface-names.nix {};
   tests.printing = callTest tests/printing.nix {};
   tests.prometheus = callTest tests/prometheus.nix {};
   tests.proxy = callTest tests/proxy.nix {};
@@ -340,7 +335,9 @@ in rec {
   # tests.quagga = callTest tests/quagga.nix {};
   tests.quake3 = callTest tests/quake3.nix {};
   tests.radicale = callTest tests/radicale.nix {};
+  tests.rspamd = callSubTests tests/rspamd.nix {};
   tests.runInMachine = callTest tests/run-in-machine.nix {};
+  tests.rxe = callTest tests/rxe.nix {};
   tests.samba = callTest tests/samba.nix {};
   tests.sddm = callSubTests tests/sddm.nix {};
   tests.simple = callTest tests/simple.nix {};
@@ -348,15 +345,17 @@ in rec {
   tests.smokeping = callTest tests/smokeping.nix {};
   tests.snapper = callTest tests/snapper.nix {};
   tests.statsd = callTest tests/statsd.nix {};
+  tests.sudo = callTest tests/sudo.nix {};
   tests.switchTest = callTest tests/switch-test.nix {};
   tests.taskserver = callTest tests/taskserver.nix {};
   tests.tomcat = callTest tests/tomcat.nix {};
   tests.udisks2 = callTest tests/udisks2.nix {};
   tests.vault = callTest tests/vault.nix {};
-  tests.virtualbox = callSubTests tests/virtualbox.nix { system = "x86_64-linux"; };
+  tests.virtualbox = callSubTestsOnTheseSystems ["x86_64-linux"] tests/virtualbox.nix {};
   tests.wordpress = callTest tests/wordpress.nix {};
   tests.xfce = callTest tests/xfce.nix {};
   tests.xmonad = callTest tests/xmonad.nix {};
+  tests.yabar = callTest tests/yabar.nix {};
   tests.zookeeper = callTest tests/zookeeper.nix {};
 
   /* Build a bunch of typical closures so that Hydra can keep track of
diff --git a/nixos/tests/boot.nix b/nixos/tests/boot.nix
index 69ab4755e4447..fc52cd09f2095 100644
--- a/nixos/tests/boot.nix
+++ b/nixos/tests/boot.nix
@@ -1,7 +1,6 @@
 { system ? builtins.currentSystem }:
 
 with import ../lib/testing.nix { inherit system; };
-with import ../lib/qemu-flags.nix;
 with pkgs.lib;
 
 let
diff --git a/nixos/tests/borgbackup.nix b/nixos/tests/borgbackup.nix
new file mode 100644
index 0000000000000..123b02be72511
--- /dev/null
+++ b/nixos/tests/borgbackup.nix
@@ -0,0 +1,21 @@
+import ./make-test.nix ({ pkgs, ...}: {
+  name = "borgbackup";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ mic92 ];
+  };
+
+  nodes = {
+    machine = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.borgbackup ];
+    };
+  };
+
+  testScript = ''
+    my $borg = "BORG_PASSPHRASE=supersecret borg";
+    $machine->succeed("$borg init --encryption=repokey /tmp/backup");
+    $machine->succeed("mkdir /tmp/data/ && echo 'data' >/tmp/data/file");
+    $machine->succeed("$borg create --stats /tmp/backup::test /tmp/data");
+    $machine->succeed("$borg extract /tmp/backup::test");
+    $machine->succeed('c=$(cat data/file) && echo "c = $c" >&2 && [[ "$c" == "data" ]]');
+  '';
+})
diff --git a/nixos/tests/cloud-init.nix b/nixos/tests/cloud-init.nix
index c0add7eff360f..2a258e4bff542 100644
--- a/nixos/tests/cloud-init.nix
+++ b/nixos/tests/cloud-init.nix
@@ -1,7 +1,6 @@
 { system ? builtins.currentSystem }:
 
 with import ../lib/testing.nix { inherit system; };
-with import ../lib/qemu-flags.nix;
 with pkgs.lib;
 
 let
diff --git a/nixos/tests/ec2.nix b/nixos/tests/ec2.nix
index 4ec7e56cc6cb5..f585fa2ec237b 100644
--- a/nixos/tests/ec2.nix
+++ b/nixos/tests/ec2.nix
@@ -1,7 +1,6 @@
 { system ? builtins.currentSystem }:
 
 with import ../lib/testing.nix { inherit system; };
-with import ../lib/qemu-flags.nix;
 with pkgs.lib;
 
 let
diff --git a/nixos/tests/elk.nix b/nixos/tests/elk.nix
index 65ff1cac070bd..ed656b3628b9e 100644
--- a/nixos/tests/elk.nix
+++ b/nixos/tests/elk.nix
@@ -1,95 +1,107 @@
-# Test the ELK stack: Elasticsearch, Logstash and Kibana.
-
-import ./make-test.nix ({ pkgs, ...} :
+{ system ? builtins.currentSystem }:
+with import ../lib/testing.nix { inherit system; };
+with pkgs.lib;
 let
   esUrl = "http://localhost:9200";
-in {
-  name = "ELK";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ eelco chaoflow offline basvandijk ];
-  };
 
-  nodes = {
-    one =
-      { config, pkgs, ... }: {
-          # Not giving the machine at least 2060MB results in elasticsearch failing with the following error:
-          #
-          #   OpenJDK 64-Bit Server VM warning:
-          #     INFO: os::commit_memory(0x0000000085330000, 2060255232, 0)
-          #     failed; error='Cannot allocate memory' (errno=12)
-          #
-          #   There is insufficient memory for the Java Runtime Environment to continue.
-          #   Native memory allocation (mmap) failed to map 2060255232 bytes for committing reserved memory.
-          #
-          # When setting this to 2500 I got "Kernel panic - not syncing: Out of
-          # memory: compulsory panic_on_oom is enabled" so lets give it even a
-          # bit more room:
-          virtualisation.memorySize = 3000;
+  mkElkTest = name : elk : makeTest {
+    inherit name;
+    meta = with pkgs.stdenv.lib.maintainers; {
+      maintainers = [ eelco chaoflow offline basvandijk ];
+    };
+    nodes = {
+      one =
+        { config, pkgs, ... }: {
+            # Not giving the machine at least 2060MB results in elasticsearch failing with the following error:
+            #
+            #   OpenJDK 64-Bit Server VM warning:
+            #     INFO: os::commit_memory(0x0000000085330000, 2060255232, 0)
+            #     failed; error='Cannot allocate memory' (errno=12)
+            #
+            #   There is insufficient memory for the Java Runtime Environment to continue.
+            #   Native memory allocation (mmap) failed to map 2060255232 bytes for committing reserved memory.
+            #
+            # When setting this to 2500 I got "Kernel panic - not syncing: Out of
+            # memory: compulsory panic_on_oom is enabled" so lets give it even a
+            # bit more room:
+            virtualisation.memorySize = 3000;
 
-          # For querying JSON objects returned from elasticsearch and kibana.
-          environment.systemPackages = [ pkgs.jq ];
+            # For querying JSON objects returned from elasticsearch and kibana.
+            environment.systemPackages = [ pkgs.jq ];
 
-          services = {
-            logstash = {
-              enable = true;
-              package = pkgs.logstash5;
-              inputConfig = ''
-                exec { command => "echo -n flowers" interval => 1 type => "test" }
-                exec { command => "echo -n dragons" interval => 1 type => "test" }
-              '';
-              filterConfig = ''
-                if [message] =~ /dragons/ {
-                  drop {}
-                }
-              '';
-              outputConfig = ''
-                file {
-                  path => "/tmp/logstash.out"
-                  codec => line { format => "%{message}" }
-                }
-                elasticsearch {
-                  hosts => [ "${esUrl}" ]
-                }
-              '';
-            };
+            services = {
+              logstash = {
+                enable = true;
+                package = elk.logstash;
+                inputConfig = ''
+                  exec { command => "echo -n flowers" interval => 1 type => "test" }
+                  exec { command => "echo -n dragons" interval => 1 type => "test" }
+                '';
+                filterConfig = ''
+                  if [message] =~ /dragons/ {
+                    drop {}
+                  }
+                '';
+                outputConfig = ''
+                  file {
+                    path => "/tmp/logstash.out"
+                    codec => line { format => "%{message}" }
+                  }
+                  elasticsearch {
+                    hosts => [ "${esUrl}" ]
+                  }
+                '';
+              };
 
-            elasticsearch = {
-              enable = true;
-              package = pkgs.elasticsearch5;
-            };
+              elasticsearch = {
+                enable = true;
+                package = elk.elasticsearch;
+              };
 
-            kibana = {
-              enable = true;
-              package = pkgs.kibana5;
-              elasticsearch.url = esUrl;
+              kibana = {
+                enable = true;
+                package = elk.kibana;
+                elasticsearch.url = esUrl;
+              };
             };
           };
-        };
-    };
+      };
 
-  testScript = ''
-    startAll;
+    testScript = ''
+      startAll;
 
-    $one->waitForUnit("elasticsearch.service");
+      $one->waitForUnit("elasticsearch.service");
 
-    # Continue as long as the status is not "red". The status is probably
-    # "yellow" instead of "green" because we are using a single elasticsearch
-    # node which elasticsearch considers risky.
-    #
-    # TODO: extend this test with multiple elasticsearch nodes and see if the status turns "green".
-    $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_cluster/health' | jq .status | grep -v red");
+      # Continue as long as the status is not "red". The status is probably
+      # "yellow" instead of "green" because we are using a single elasticsearch
+      # node which elasticsearch considers risky.
+      #
+      # TODO: extend this test with multiple elasticsearch nodes and see if the status turns "green".
+      $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_cluster/health' | jq .status | grep -v red");
 
-    # Perform some simple logstash tests.
-    $one->waitForUnit("logstash.service");
-    $one->waitUntilSucceeds("cat /tmp/logstash.out | grep flowers");
-    $one->waitUntilSucceeds("cat /tmp/logstash.out | grep -v dragons");
+      # Perform some simple logstash tests.
+      $one->waitForUnit("logstash.service");
+      $one->waitUntilSucceeds("cat /tmp/logstash.out | grep flowers");
+      $one->waitUntilSucceeds("cat /tmp/logstash.out | grep -v dragons");
 
-    # See if kibana is healthy.
-    $one->waitForUnit("kibana.service");
-    $one->waitUntilSucceeds("curl --silent --show-error 'http://localhost:5601/api/status' | jq .status.overall.state | grep green");
+      # See if kibana is healthy.
+      $one->waitForUnit("kibana.service");
+      $one->waitUntilSucceeds("curl --silent --show-error 'http://localhost:5601/api/status' | jq .status.overall.state | grep green");
 
-    # See if logstash messages arive in elasticsearch.
-    $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"flowers\"}}}' | jq .hits.total | grep -v 0");
-    $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"dragons\"}}}' | jq .hits.total | grep 0");
-  '';
-})
+      # See if logstash messages arive in elasticsearch.
+      $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"flowers\"}}}' | jq .hits.total | grep -v 0");
+      $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"dragons\"}}}' | jq .hits.total | grep 0");
+    '';
+  };
+in mapAttrs mkElkTest {
+  "ELK-5" = {
+    elasticsearch = pkgs.elasticsearch5;
+    logstash      = pkgs.logstash5;
+    kibana        = pkgs.kibana5;
+  };
+  "ELK-6" = {
+    elasticsearch = pkgs.elasticsearch6;
+    logstash      = pkgs.logstash6;
+    kibana        = pkgs.kibana6;
+  };
+}
diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix
new file mode 100644
index 0000000000000..5d7e0ec65e73c
--- /dev/null
+++ b/nixos/tests/home-assistant.nix
@@ -0,0 +1,46 @@
+import ./make-test.nix ({ pkgs, ... }:
+
+let
+  configDir = "/var/lib/foobar";
+
+in {
+  name = "home-assistant";
+
+  nodes = {
+    hass =
+      { config, pkgs, ... }:
+      {
+        services.home-assistant = {
+          inherit configDir;
+          enable = true;
+          config = {
+            homeassistant = {
+              name = "Home";
+              time_zone = "UTC";
+              latitude = "0.0";
+              longitude = "0.0";
+              elevation = 0;
+            };
+            frontend = { };
+            http = { };
+          };
+        };
+      };
+  };
+
+  testScript = ''
+    startAll;
+    $hass->waitForUnit("home-assistant.service");
+    
+    # Since config is specified using a Nix attribute set,
+    # configuration.yaml is a link to the Nix store
+    $hass->succeed("test -L ${configDir}/configuration.yaml");
+
+    # Check that Home Assistant's web interface and API can be reached
+    $hass->waitForOpenPort(8123);
+    $hass->succeed("curl --fail http://localhost:8123/states");
+    $hass->succeed("curl --fail http://localhost:8123/api/ | grep 'API running'");
+
+    $hass->fail("cat ${configDir}/home-assistant.log | grep -qF ERROR");
+  '';
+})
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 90ac5b933f323..637cbb45709d6 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -1,7 +1,6 @@
 { system ? builtins.currentSystem }:
 
 with import ../lib/testing.nix { inherit system; };
-with import ../lib/qemu-flags.nix;
 with pkgs.lib;
 
 let
diff --git a/nixos/tests/keymap.nix b/nixos/tests/keymap.nix
index c431c1a341744..eec674e227dff 100644
--- a/nixos/tests/keymap.nix
+++ b/nixos/tests/keymap.nix
@@ -46,6 +46,7 @@ let
   in makeTest {
     name = "keymap-${layout}";
 
+    machine.services.xserver.desktopManager.xterm.enable = false;
     machine.i18n.consoleKeyMap = mkOverride 900 layout;
     machine.services.xserver.layout = mkOverride 900 layout;
     machine.imports = [ ./common/x11.nix extraConfig ];
diff --git a/nixos/tests/kubernetes/base.nix b/nixos/tests/kubernetes/base.nix
index acf2e0250819a..f3b930b630ba3 100644
--- a/nixos/tests/kubernetes/base.nix
+++ b/nixos/tests/kubernetes/base.nix
@@ -1,7 +1,6 @@
 { system ? builtins.currentSystem }:
 
 with import ../../lib/testing.nix { inherit system; };
-with import ../../lib/qemu-flags.nix;
 with pkgs.lib;
 
 let
diff --git a/nixos/tests/make-test.nix b/nixos/tests/make-test.nix
index f3e26aa7e74d2..ee4ba310ad50e 100644
--- a/nixos/tests/make-test.nix
+++ b/nixos/tests/make-test.nix
@@ -2,4 +2,4 @@ f: { system ? builtins.currentSystem, ... } @ args:
 
 with import ../lib/testing.nix { inherit system; };
 
-makeTest (if builtins.isFunction f then f (args // { inherit pkgs; inherit (pkgs) lib; }) else f)
+makeTest (if pkgs.lib.isFunction f then f (args // { inherit pkgs; inherit (pkgs) lib; }) else f)
diff --git a/nixos/tests/matrix-synapse.nix b/nixos/tests/matrix-synapse.nix
new file mode 100644
index 0000000000000..113fb622588b3
--- /dev/null
+++ b/nixos/tests/matrix-synapse.nix
@@ -0,0 +1,30 @@
+import ./make-test.nix ({ pkgs, ... } : {
+
+  name = "matrix-synapse";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ corngood ];
+  };
+
+  nodes = {
+    server_postgres = args: {
+      services.matrix-synapse.enable = true;
+      services.matrix-synapse.database_type = "psycopg2";
+    };
+
+    server_sqlite = args: {
+      services.matrix-synapse.enable = true;
+      services.matrix-synapse.database_type = "sqlite3";
+    };
+  };
+
+  testScript = ''
+    startAll;
+    $server_postgres->waitForUnit("matrix-synapse.service");
+    $server_postgres->waitUntilSucceeds("curl -Lk https://localhost:8448/");
+    $server_postgres->requireActiveUnit("postgresql.service");
+    $server_sqlite->waitForUnit("matrix-synapse.service");
+    $server_sqlite->waitUntilSucceeds("curl -Lk https://localhost:8448/");
+    $server_sqlite->mustSucceed("[ -e /var/lib/matrix-synapse/homeserver.db ]");
+  '';
+
+})
diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix
index 79290861cb0b4..6de17518214c6 100644
--- a/nixos/tests/misc.nix
+++ b/nixos/tests/misc.nix
@@ -115,11 +115,6 @@ import ./make-test.nix ({ pkgs, ...} : {
           $machine->succeed("nix-store -qR /run/current-system | grep nixos-");
       };
 
-      # Test sudo
-      subtest "sudo", sub {
-          $machine->succeed("su - sybil -c 'sudo true'");
-      };
-
       # Test sysctl
       subtest "sysctl", sub {
           $machine->waitForUnit("systemd-sysctl.service");
diff --git a/nixos/tests/netdata.nix b/nixos/tests/netdata.nix
new file mode 100644
index 0000000000000..58733c1b3379b
--- /dev/null
+++ b/nixos/tests/netdata.nix
@@ -0,0 +1,31 @@
+# This test runs netdata and checks for data via apps.plugin
+
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "netdata";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ cransom ];
+  };
+
+  nodes = {
+    netdata =
+      { config, pkgs, ... }:
+        {
+          environment.systemPackages = with pkgs; [ curl jq ];
+          services.netdata.enable = true;
+        };
+    };
+
+  testScript = ''
+    startAll;
+
+    $netdata->waitForUnit("netdata.service");
+    # check if netdata can read disk ops for root owned processes. 
+    # if > 0, successful. verifies both netdata working and 
+    # apps.plugin has elevated capabilities.
+    my $cmd = <<'CMD';
+    curl -s http://localhost:19999/api/v1/data\?chart=users.pwrites | \
+       jq -e '[.data[range(10)][.labels | indices("root")[0]]] | add | . > 0'
+    CMD
+    $netdata->waitUntilSucceeds($cmd);
+  '';
+})
diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix
index 7708775f73f3a..182328b329623 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -433,6 +433,49 @@ let
           $client2->succeed("ip addr show dev vlan >&2");
         '';
     };
+    virtual = {
+      name = "Virtual";
+      machine = {
+        networking.interfaces."tap0" = {
+          ip4 = [ { address = "192.168.1.1"; prefixLength = 24; } ];
+          ip6 = [ { address = "2001:1470:fffd:2096::"; prefixLength = 64; } ];
+          virtual = true;
+        };
+        networking.interfaces."tun0" = {
+          ip4 = [ { address = "192.168.1.2"; prefixLength = 24; } ];
+          ip6 = [ { address = "2001:1470:fffd:2097::"; prefixLength = 64; } ];
+          virtual = true;
+        };
+      };
+
+      testScript = ''
+        my $targetList = <<'END';
+        tap0: tap UNKNOWN_FLAGS:800 user 0
+        tun0: tun UNKNOWN_FLAGS:800 user 0
+        END
+
+        # Wait for networking to come up
+        $machine->start;
+        $machine->waitForUnit("network.target");
+
+        # Test interfaces set up
+        my $list = $machine->succeed("ip tuntap list | sort");
+        "$list" eq "$targetList" or die(
+          "The list of virtual interfaces does not match the expected one:\n",
+          "Result:\n", "$list\n",
+          "Expected:\n", "$targetList\n"
+        );
+
+        # Test interfaces clean up
+        $machine->succeed("systemctl stop network-addresses-tap0");
+        $machine->succeed("systemctl stop network-addresses-tun0");
+        my $residue = $machine->succeed("ip tuntap list");
+        $residue eq "" or die(
+          "Some virtual interface has not been properly cleaned:\n",
+          "$residue\n"
+        );
+      '';
+    };
   };
 
 in mapAttrs (const (attrs: makeTest (attrs // {
diff --git a/nixos/tests/predictable-interface-names.nix b/nixos/tests/predictable-interface-names.nix
new file mode 100644
index 0000000000000..b4c2039923cf0
--- /dev/null
+++ b/nixos/tests/predictable-interface-names.nix
@@ -0,0 +1,27 @@
+{ system ? builtins.currentSystem
+, pkgs ? import ../.. { inherit system; }
+}:
+with import ../lib/testing.nix { inherit system; };
+let boolToString = x: if x then "yes" else "no"; in
+let testWhenSetTo = predictable: withNetworkd:
+makeTest {
+  name = "${if predictable then "" else "un"}predictableInterfaceNames${if withNetworkd then "-with-networkd" else ""}";
+  meta = {};
+
+  machine = { config, pkgs, ... }: {
+    networking.usePredictableInterfaceNames = pkgs.stdenv.lib.mkForce predictable;
+    networking.useNetworkd = withNetworkd;
+    networking.dhcpcd.enable = !withNetworkd;
+  };
+
+  testScript = ''
+    print $machine->succeed("ip link");
+    $machine->succeed("ip link show ${if predictable then "ens3" else "eth0"}");
+    $machine->fail("ip link show ${if predictable then "eth0" else "ens3"}");
+  '';
+}; in
+with pkgs.stdenv.lib.lists;
+with pkgs.stdenv.lib.attrsets;
+listToAttrs (map (drv: nameValuePair drv.name drv) (
+crossLists testWhenSetTo [[true false] [true false]]
+))
diff --git a/nixos/tests/rspamd.nix b/nixos/tests/rspamd.nix
new file mode 100644
index 0000000000000..6b2e2dd3a5317
--- /dev/null
+++ b/nixos/tests/rspamd.nix
@@ -0,0 +1,140 @@
+{ system ? builtins.currentSystem }:
+with import ../lib/testing.nix { inherit system; };
+with pkgs.lib;
+let
+  initMachine = ''
+    startAll
+    $machine->waitForUnit("rspamd.service");
+    $machine->succeed("id \"rspamd\" >/dev/null");
+  '';
+  checkSocket = socket: user: group: mode: ''
+    $machine->succeed("ls ${socket} >/dev/null");
+    $machine->succeed("[[ \"\$(stat -c %U ${socket})\" == \"${user}\" ]]");
+    $machine->succeed("[[ \"\$(stat -c %G ${socket})\" == \"${group}\" ]]");
+    $machine->succeed("[[ \"\$(stat -c %a ${socket})\" == \"${mode}\" ]]");
+  '';
+  simple = name: socketActivation: enableIPv6: makeTest {
+    name = "rspamd-${name}";
+    machine = {
+      services.rspamd = {
+        enable = true;
+        socketActivation = socketActivation;
+      };
+      networking.enableIPv6 = enableIPv6;
+    };
+    testScript = ''
+      startAll
+      $machine->waitForUnit("multi-user.target");
+      $machine->waitForOpenPort(11334);
+      $machine->waitForUnit("rspamd.service");
+      $machine->succeed("id \"rspamd\" >/dev/null");
+      ${checkSocket "/run/rspamd/rspamd.sock" "rspamd" "rspamd" "660" }
+      sleep 10;
+      $machine->log($machine->succeed("cat /etc/rspamd.conf"));
+      $machine->log($machine->succeed("systemctl cat rspamd.service"));
+      ${if socketActivation then ''
+        $machine->log($machine->succeed("systemctl cat rspamd-controller-1.socket"));
+        $machine->log($machine->succeed("systemctl cat rspamd-normal-1.socket"));
+      '' else ''
+        $machine->fail("systemctl cat rspamd-controller-1.socket");
+        $machine->fail("systemctl cat rspamd-normal-1.socket");
+      ''}
+      $machine->log($machine->succeed("curl http://localhost:11334/auth"));
+      $machine->log($machine->succeed("curl http://127.0.0.1:11334/auth"));
+      ${optionalString enableIPv6 ''
+        $machine->log($machine->succeed("curl http://[::1]:11334/auth"));
+      ''}
+    '';
+  };
+in
+{
+  simple = simple "simple" false true;
+  ipv4only = simple "ipv4only" false false;
+  simple-socketActivated = simple "simple-socketActivated" true true;
+  ipv4only-socketActivated = simple "ipv4only-socketActivated" true false;
+  deprecated = makeTest {
+    name = "rspamd-deprecated";
+    machine = {
+      services.rspamd = {
+        enable = true;
+        bindSocket = [ "/run/rspamd.sock mode=0600 user=root group=root" ];
+        bindUISocket = [ "/run/rspamd-worker.sock mode=0666 user=root group=root" ];
+      };
+    };
+
+    testScript = ''
+      ${initMachine}
+      $machine->waitForFile("/run/rspamd.sock");
+      ${checkSocket "/run/rspamd.sock" "root" "root" "600" }
+      ${checkSocket "/run/rspamd-worker.sock" "root" "root" "666" }
+      $machine->log($machine->succeed("cat /etc/rspamd.conf"));
+      $machine->fail("systemctl cat rspamd-normal-1.socket");
+      $machine->log($machine->succeed("rspamc -h /run/rspamd-worker.sock stat"));
+      $machine->log($machine->succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping"));
+    '';
+  };
+
+  bindports = makeTest {
+    name = "rspamd-bindports";
+    machine = {
+      services.rspamd = {
+        enable = true;
+        socketActivation = false;
+        workers.normal.bindSockets = [{
+          socket = "/run/rspamd.sock";
+          mode = "0600";
+          owner = "root";
+          group = "root";
+        }];
+        workers.controller.bindSockets = [{
+          socket = "/run/rspamd-worker.sock";
+          mode = "0666";
+          owner = "root";
+          group = "root";
+        }];
+      };
+    };
+
+    testScript = ''
+      ${initMachine}
+      $machine->waitForFile("/run/rspamd.sock");
+      ${checkSocket "/run/rspamd.sock" "root" "root" "600" }
+      ${checkSocket "/run/rspamd-worker.sock" "root" "root" "666" }
+      $machine->log($machine->succeed("cat /etc/rspamd.conf"));
+      $machine->fail("systemctl cat rspamd-normal-1.socket");
+      $machine->log($machine->succeed("rspamc -h /run/rspamd-worker.sock stat"));
+      $machine->log($machine->succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping"));
+    '';
+  };
+  socketActivated = makeTest {
+    name = "rspamd-socketActivated";
+    machine = {
+      services.rspamd = {
+        enable = true;
+        workers.normal.bindSockets = [{
+          socket = "/run/rspamd.sock";
+          mode = "0600";
+          owner = "root";
+          group = "root";
+        }];
+        workers.controller.bindSockets = [{
+          socket = "/run/rspamd-worker.sock";
+          mode = "0666";
+          owner = "root";
+          group = "root";
+        }];
+      };
+    };
+
+    testScript = ''
+      startAll
+      $machine->waitForFile("/run/rspamd.sock");
+      ${checkSocket "/run/rspamd.sock" "root" "root" "600" }
+      ${checkSocket "/run/rspamd-worker.sock" "root" "root" "666" }
+      $machine->log($machine->succeed("cat /etc/rspamd.conf"));
+      $machine->log($machine->succeed("systemctl cat rspamd-normal-1.socket"));
+      $machine->log($machine->succeed("rspamc -h /run/rspamd-worker.sock stat"));
+      $machine->log($machine->succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping"));
+    '';
+  };
+}
diff --git a/nixos/tests/rxe.nix b/nixos/tests/rxe.nix
new file mode 100644
index 0000000000000..cfe64a75a6352
--- /dev/null
+++ b/nixos/tests/rxe.nix
@@ -0,0 +1,53 @@
+import ./make-test.nix ({ pkgs, ... } :
+
+let
+  node = { config, pkgs, lib, ... } : {
+    networking = {
+      firewall = {
+        allowedUDPPorts = [ 4791 ]; # open RoCE port
+        allowedTCPPorts = [ 4800 ]; # port for test utils
+      };
+      rxe = {
+        enable = true;
+        interfaces = [ "eth1" ];
+      };
+    };
+
+    environment.systemPackages = with pkgs; [ rdma-core screen ];
+  };
+
+in {
+  name = "rxe";
+
+  nodes = {
+    server = node;
+    client = node;
+  };
+
+  testScript = ''
+    # Test if rxe interface comes up
+    $server->waitForUnit("default.target");
+    $server->succeed("systemctl status rxe.service");
+    $server->succeed("ibv_devices | grep rxe0");
+
+    $client->waitForUnit("default.target");
+
+    # ping pong test
+    $server->succeed("screen -dmS rc_pingpong ibv_rc_pingpong -p 4800 -g0");
+    $client->succeed("sleep 2; ibv_rc_pingpong -p 4800 -g0 server");
+
+    $server->succeed("screen -dmS uc_pingpong ibv_uc_pingpong -p 4800 -g0");
+    $client->succeed("sleep 2; ibv_uc_pingpong -p 4800 -g0 server");
+
+    $server->succeed("screen -dmS ud_pingpong ibv_ud_pingpong -p 4800 -s 1024 -g0");
+    $client->succeed("sleep 2; ibv_ud_pingpong -p 4800 -s 1024 -g0 server");
+
+    $server->succeed("screen -dmS srq_pingpong ibv_srq_pingpong -p 4800 -g0");
+    $client->succeed("sleep 2; ibv_srq_pingpong -p 4800 -g0 server");
+
+    $server->succeed("screen -dmS rping rping -s -a server -C 10");
+    $client->succeed("sleep 2; rping -c -a server -C 10");
+  '';
+})
+
+
diff --git a/nixos/tests/statsd.nix b/nixos/tests/statsd.nix
index d6bbc3901630a..a9d7dc61cb603 100644
--- a/nixos/tests/statsd.nix
+++ b/nixos/tests/statsd.nix
@@ -1,4 +1,4 @@
-import ./make-test.nix ({ pkgs, lib }:
+import ./make-test.nix ({ pkgs, lib, ... }:
 
 with lib;
 
diff --git a/nixos/tests/sudo.nix b/nixos/tests/sudo.nix
new file mode 100644
index 0000000000000..35addb0ee8056
--- /dev/null
+++ b/nixos/tests/sudo.nix
@@ -0,0 +1,93 @@
+# Some tests to ensure sudo is working properly.
+
+let
+  password = "helloworld";
+
+in
+  import ./make-test.nix ({ pkgs, ...} : {
+    name = "sudo";
+    meta = with pkgs.stdenv.lib.maintainers; {
+      maintainers = [ lschuermann ];
+    };
+
+    machine =
+      { config, lib, pkgs, ... }:
+      with lib;
+      {
+        users.extraGroups = { foobar = {}; barfoo = {}; baz = { gid = 1337; }; };
+        users.users = {
+          test0 = { isNormalUser = true; extraGroups = [ "wheel" ]; };
+          test1 = { isNormalUser = true; password = password; };
+          test2 = { isNormalUser = true; extraGroups = [ "foobar" ]; password = password; };
+          test3 = { isNormalUser = true; extraGroups = [ "barfoo" ]; };
+          test4 = { isNormalUser = true; extraGroups = [ "baz" ]; };
+          test5 = { isNormalUser = true; };
+        };
+
+        security.sudo = {
+          enable = true;
+          wheelNeedsPassword = false;
+
+          extraRules = [
+            # SUDOERS SYNTAX CHECK (Test whether the module produces a valid output;
+            # errors being detected by the visudo checks.
+
+            # These should not create any entries
+            { users = [ "notest1" ]; commands = [ ]; }
+            { commands = [ { command = "ALL"; options = [ ]; } ]; }
+
+            # Test defining commands with the options syntax, though not setting any options
+            { users = [ "notest2" ]; commands = [ { command = "ALL"; options = [ ]; } ]; }
+
+
+            # CONFIGURATION FOR TEST CASES
+            { users = [ "test1" ]; groups = [ "foobar" ]; commands = [ "ALL" ]; }
+            { groups = [ "barfoo" 1337 ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" "NOSETENV" ]; } ]; }
+            { users = [ "test5" ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" "SETENV" ]; } ]; runAs = "test1:barfoo"; }
+          ];
+        };
+      };
+
+    testScript =
+      ''
+        subtest "users in wheel group should have passwordless sudo", sub {
+            $machine->succeed("su - test0 -c \"sudo -u root true\"");
+        };
+
+        subtest "test1 user should have sudo with password", sub {
+            $machine->succeed("su - test1 -c \"echo ${password} | sudo -S -u root true\"");
+        };
+
+        subtest "test1 user should not be able to use sudo without password", sub {
+            $machine->fail("su - test1 -c \"sudo -n -u root true\"");
+        };
+
+        subtest "users in group 'foobar' should be able to use sudo with password", sub {
+            $machine->succeed("sudo -u test2 echo ${password} | sudo -S -u root true");
+        };
+
+        subtest "users in group 'barfoo' should be able to use sudo without password", sub {
+            $machine->succeed("sudo -u test3 sudo -n -u root true");
+        };
+
+        subtest "users in group 'baz' (GID 1337) should be able to use sudo without password", sub {
+            $machine->succeed("sudo -u test4 sudo -n -u root echo true");
+        };
+
+        subtest "test5 user should be able to run commands under test1", sub {
+            $machine->succeed("sudo -u test5 sudo -n -u test1 true");
+        };
+
+        subtest "test5 user should not be able to run commands under root", sub {
+            $machine->fail("sudo -u test5 sudo -n -u root true");
+        };
+
+        subtest "test5 user should be able to keep his environment", sub {
+            $machine->succeed("sudo -u test5 sudo -n -E -u test1 true");
+        };
+
+        subtest "users in group 'barfoo' should not be able to keep their environment", sub {
+            $machine->fail("sudo -u test3 sudo -n -E -u root true");
+        };
+      '';
+  })
diff --git a/nixos/tests/yabar.nix b/nixos/tests/yabar.nix
new file mode 100644
index 0000000000000..40ca91e8064d5
--- /dev/null
+++ b/nixos/tests/yabar.nix
@@ -0,0 +1,25 @@
+import ./make-test.nix ({ pkgs, lib }:
+
+with lib;
+
+{
+  name = "yabar";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ ma27 ];
+  };
+
+  nodes.yabar = {
+    imports = [ ./common/x11.nix ./common/user-account.nix ];
+
+    services.xserver.displayManager.auto.user = "bob";
+
+    programs.yabar.enable = true;
+  };
+
+  testScript = ''
+    $yabar->start;
+    $yabar->waitForX;
+
+    $yabar->waitForUnit("yabar.service", "bob");
+  '';
+})