about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorRobin Gloster <mail@glob.in>2017-09-02 23:29:04 +0200
committerRobin Gloster <mail@glob.in>2017-09-02 23:29:04 +0200
commit0156db2da530a52265fbccc8ad7747b00ee8aaf3 (patch)
treef5facfdd70ac838997ed0538fd9f4e8edffe9936 /nixos
parent891a1662aa6cd6b2bcd2187d97f27822ed5df138 (diff)
parentd784b830051bb86172b9a3669826774cc20e1f41 (diff)
downloadnixlib-0156db2da530a52265fbccc8ad7747b00ee8aaf3.tar
nixlib-0156db2da530a52265fbccc8ad7747b00ee8aaf3.tar.gz
nixlib-0156db2da530a52265fbccc8ad7747b00ee8aaf3.tar.bz2
nixlib-0156db2da530a52265fbccc8ad7747b00ee8aaf3.tar.lz
nixlib-0156db2da530a52265fbccc8ad7747b00ee8aaf3.tar.xz
nixlib-0156db2da530a52265fbccc8ad7747b00ee8aaf3.tar.zst
nixlib-0156db2da530a52265fbccc8ad7747b00ee8aaf3.zip
Merge remote-tracking branch 'upstream/master' into HEAD
Diffstat (limited to 'nixos')
-rwxr-xr-xnixos/doc/manual/development/releases.xml73
-rw-r--r--nixos/doc/manual/installation/installing-usb.xml2
-rw-r--r--nixos/doc/manual/man-nixos-option.xml48
-rw-r--r--nixos/doc/manual/manual.xml2
-rw-r--r--nixos/doc/manual/release-notes/rl-1509.xml2
-rw-r--r--nixos/doc/manual/release-notes/rl-1609.xml6
-rw-r--r--nixos/doc/manual/release-notes/rl-1709.xml107
-rw-r--r--nixos/doc/manual/release-notes/rl-1803.xml46
-rw-r--r--nixos/lib/make-disk-image.nix22
-rw-r--r--nixos/lib/make-ext4-fs.nix4
-rw-r--r--nixos/maintainers/scripts/ec2/amazon-image.nix18
-rw-r--r--nixos/modules/config/ldap.nix1
-rw-r--r--nixos/modules/config/networking.nix35
-rw-r--r--nixos/modules/config/no-x-libs.nix13
-rw-r--r--nixos/modules/config/nsswitch.nix3
-rw-r--r--nixos/modules/config/pulseaudio.nix4
-rw-r--r--nixos/modules/config/swap.nix77
-rw-r--r--nixos/modules/config/system-path.nix3
-rw-r--r--nixos/modules/config/timezone.nix20
-rw-r--r--nixos/modules/config/users-groups.nix2
-rw-r--r--nixos/modules/hardware/mcelog.nix24
-rw-r--r--nixos/modules/hardware/raid/hpsa.nix61
-rw-r--r--nixos/modules/installer/cd-dvd/channel.nix11
-rw-r--r--nixos/modules/installer/tools/auto-upgrade.nix2
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix6
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl10
-rw-r--r--nixos/modules/installer/tools/nixos-install.sh2
-rw-r--r--nixos/modules/installer/tools/nixos-rebuild.sh2
-rw-r--r--nixos/modules/misc/ids.nix8
-rw-r--r--nixos/modules/misc/version.nix2
-rw-r--r--nixos/modules/module-list.nix22
-rw-r--r--nixos/modules/profiles/all-hardware.nix3
-rw-r--r--nixos/modules/profiles/graphical.nix2
-rw-r--r--nixos/modules/profiles/hardened.nix6
-rw-r--r--nixos/modules/profiles/installation-device.nix2
-rw-r--r--nixos/modules/programs/gnupg.nix59
-rw-r--r--nixos/modules/programs/nylas-mail.nix37
-rw-r--r--nixos/modules/programs/oblogout.nix16
-rw-r--r--nixos/modules/programs/qt5ct.nix2
-rw-r--r--nixos/modules/programs/thefuck.nix14
-rw-r--r--nixos/modules/programs/wvdial.nix71
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.nix20
-rw-r--r--nixos/modules/programs/zsh/zsh.nix71
-rw-r--r--nixos/modules/rename.nix33
-rw-r--r--nixos/modules/security/auditd.nix27
-rw-r--r--nixos/modules/security/chromium-suid-sandbox.nix3
-rw-r--r--nixos/modules/security/grsecurity.nix169
-rw-r--r--nixos/modules/security/grsecurity.xml385
-rw-r--r--nixos/modules/security/pam.nix4
-rw-r--r--nixos/modules/services/audio/alsa.nix4
-rw-r--r--nixos/modules/services/audio/mpd.nix52
-rw-r--r--nixos/modules/services/backup/znapzend.nix10
-rw-r--r--nixos/modules/services/computing/slurm/slurm.nix6
-rw-r--r--nixos/modules/services/continuous-integration/buildbot/master.nix6
-rw-r--r--nixos/modules/services/continuous-integration/gitlab-runner.nix89
-rw-r--r--nixos/modules/services/continuous-integration/hail.nix61
-rw-r--r--nixos/modules/services/continuous-integration/hydra/default.nix4
-rw-r--r--nixos/modules/services/databases/influxdb.nix5
-rw-r--r--nixos/modules/services/databases/mongodb.nix2
-rw-r--r--nixos/modules/services/databases/mysql.nix10
-rw-r--r--nixos/modules/services/databases/postage.nix205
-rw-r--r--nixos/modules/services/databases/postgresql.nix19
-rw-r--r--nixos/modules/services/editors/emacs.xml4
-rw-r--r--nixos/modules/services/games/factorio.nix2
-rw-r--r--nixos/modules/services/hardware/interception-tools.nix61
-rw-r--r--nixos/modules/services/hardware/tlp.nix2
-rw-r--r--nixos/modules/services/logging/fluentd.nix13
-rw-r--r--nixos/modules/services/logging/graylog.nix24
-rw-r--r--nixos/modules/services/logging/journalwatch.nix246
-rw-r--r--nixos/modules/services/mail/postfix.nix595
-rw-r--r--nixos/modules/services/misc/airsonic.nix117
-rw-r--r--nixos/modules/services/misc/autofs.nix17
-rw-r--r--nixos/modules/services/misc/autorandr.nix1
-rw-r--r--nixos/modules/services/misc/bepasty.nix2
-rw-r--r--nixos/modules/services/misc/calibre-server.nix2
-rw-r--r--nixos/modules/services/misc/exhibitor.nix418
-rw-r--r--nixos/modules/services/misc/fstrim.nix1
-rw-r--r--nixos/modules/services/misc/gitlab.nix2
-rw-r--r--nixos/modules/services/misc/nixos-manual.nix5
-rw-r--r--nixos/modules/services/misc/ripple-rest.nix110
-rw-r--r--nixos/modules/services/misc/snapper.nix152
-rw-r--r--nixos/modules/services/misc/spice-vdagentd.nix2
-rw-r--r--nixos/modules/services/misc/taskserver/helper-tool.py2
-rw-r--r--nixos/modules/services/misc/zookeeper.nix6
-rw-r--r--nixos/modules/services/monitoring/munin.nix4
-rw-r--r--nixos/modules/services/monitoring/osquery.nix91
-rw-r--r--nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix4
-rw-r--r--nixos/modules/services/monitoring/prometheus/unifi-exporter.nix1
-rw-r--r--nixos/modules/services/network-filesystems/ipfs.nix180
-rw-r--r--nixos/modules/services/network-filesystems/rsyncd.nix54
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix2
-rw-r--r--nixos/modules/services/network-filesystems/tahoe.nix18
-rw-r--r--nixos/modules/services/networking/avahi-daemon.nix10
-rw-r--r--nixos/modules/services/networking/bitlbee.nix27
-rw-r--r--nixos/modules/services/networking/coturn.nix8
-rw-r--r--nixos/modules/services/networking/ddclient.nix100
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.nix9
-rw-r--r--nixos/modules/services/networking/firefox/sync-server.nix61
-rw-r--r--nixos/modules/services/networking/i2pd.nix198
-rw-r--r--nixos/modules/services/networking/lldpd.nix9
-rw-r--r--nixos/modules/services/networking/mosquitto.nix4
-rw-r--r--nixos/modules/services/networking/nat.nix68
-rw-r--r--nixos/modules/services/networking/networkmanager.nix19
-rw-r--r--nixos/modules/services/networking/radicale.nix40
-rw-r--r--nixos/modules/services/networking/searx.nix4
-rw-r--r--nixos/modules/services/networking/strongswan.nix2
-rw-r--r--nixos/modules/services/networking/tinc.nix35
-rw-r--r--nixos/modules/services/networking/tlsdated.nix111
-rw-r--r--nixos/modules/services/networking/unifi.nix31
-rw-r--r--nixos/modules/services/networking/wireguard.nix99
-rw-r--r--nixos/modules/services/printing/cupsd.nix36
-rw-r--r--nixos/modules/services/scheduling/cron.nix2
-rw-r--r--nixos/modules/services/security/frandom.nix31
-rw-r--r--nixos/modules/services/security/oauth2_proxy.nix9
-rw-r--r--nixos/modules/services/security/sks.nix82
-rw-r--r--nixos/modules/services/security/tor.nix394
-rw-r--r--nixos/modules/services/security/usbguard.nix200
-rw-r--r--nixos/modules/services/security/vault.nix143
-rw-r--r--nixos/modules/services/system/saslauthd.nix63
-rw-r--r--nixos/modules/services/torrent/deluge.nix10
-rw-r--r--nixos/modules/services/ttys/agetty.nix23
-rw-r--r--nixos/modules/services/web-apps/atlassian/confluence.nix53
-rw-r--r--nixos/modules/services/web-apps/atlassian/jira.nix51
-rw-r--r--nixos/modules/services/web-apps/pgpkeyserver-lite.nix75
-rw-r--r--nixos/modules/services/web-apps/piwik-doc.xml26
-rw-r--r--nixos/modules/services/web-apps/piwik.nix98
-rw-r--r--nixos/modules/services/web-servers/caddy.nix14
-rw-r--r--nixos/modules/services/web-servers/lighttpd/default.nix29
-rw-r--r--nixos/modules/services/web-servers/minio.nix42
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix222
-rw-r--r--nixos/modules/services/web-servers/nginx/location-options.nix12
-rw-r--r--nixos/modules/services/web-servers/nginx/vhost-options.nix50
-rw-r--r--nixos/modules/services/web-servers/phpfpm/default.nix4
-rw-r--r--nixos/modules/services/web-servers/varnish/default.nix44
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix3
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix5
-rw-r--r--nixos/modules/services/x11/desktop-managers/mate.nix79
-rw-r--r--nixos/modules/services/x11/desktop-managers/maxx.nix25
-rw-r--r--nixos/modules/services/x11/display-managers/gdm.nix19
-rw-r--r--nixos/modules/services/x11/hardware/synaptics.nix2
-rw-r--r--nixos/modules/services/x11/window-managers/compiz.nix60
-rw-r--r--nixos/modules/services/x11/window-managers/default.nix1
-rw-r--r--nixos/modules/services/x11/window-managers/xmonad.nix1
-rw-r--r--nixos/modules/services/x11/xserver.nix48
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl9
-rw-r--r--nixos/modules/system/activation/top-level.nix1
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl4
-rw-r--r--nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py29
-rw-r--r--nixos/modules/system/boot/networkd.nix33
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh4
-rw-r--r--nixos/modules/system/boot/stage-1.nix2
-rw-r--r--nixos/modules/system/boot/systemd.nix34
-rw-r--r--nixos/modules/system/etc/etc.nix26
-rw-r--r--nixos/modules/system/etc/make-etc.sh10
-rw-r--r--nixos/modules/system/etc/setup-etc.pl2
-rw-r--r--nixos/modules/tasks/filesystems.nix2
-rw-r--r--nixos/modules/tasks/filesystems/bcachefs.nix26
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix19
-rw-r--r--nixos/modules/tasks/network-interfaces-scripted.nix2
-rw-r--r--nixos/modules/testing/minimal-kernel.nix2
-rw-r--r--nixos/modules/virtualisation/azure-images.nix5
-rw-r--r--nixos/modules/virtualisation/cloud-image.nix44
-rw-r--r--nixos/modules/virtualisation/containers.nix1
-rw-r--r--nixos/modules/virtualisation/docker.nix48
-rw-r--r--nixos/modules/virtualisation/gce-images.nix8
-rw-r--r--nixos/modules/virtualisation/google-compute-image.nix149
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix39
-rw-r--r--nixos/modules/virtualisation/vmware-guest.nix2
-rw-r--r--nixos/modules/virtualisation/xen-dom0.nix1
-rw-r--r--nixos/release-combined.nix23
-rw-r--r--nixos/release.nix5
-rw-r--r--nixos/tests/ammonite.nix20
-rw-r--r--nixos/tests/containers-reloadable.nix66
-rw-r--r--nixos/tests/gnome3.nix2
-rw-r--r--nixos/tests/hydra.nix32
-rw-r--r--nixos/tests/installer.nix7
-rw-r--r--nixos/tests/ipfs.nix38
-rw-r--r--nixos/tests/keymap.nix32
-rw-r--r--nixos/tests/minio.nix17
-rw-r--r--nixos/tests/misc.nix2
-rw-r--r--nixos/tests/nat.nix59
-rw-r--r--nixos/tests/nginx.nix2
-rw-r--r--nixos/tests/plasma5.nix15
-rw-r--r--nixos/tests/postgresql.nix18
-rw-r--r--nixos/tests/radicale.nix83
-rw-r--r--nixos/tests/snapper.nix43
-rw-r--r--nixos/tests/taskserver.nix4
-rw-r--r--nixos/tests/timezone.nix45
188 files changed, 5746 insertions, 2233 deletions
diff --git a/nixos/doc/manual/development/releases.xml b/nixos/doc/manual/development/releases.xml
index 01c3dbc22f16..afcb970ed700 100755
--- a/nixos/doc/manual/development/releases.xml
+++ b/nixos/doc/manual/development/releases.xml
@@ -10,7 +10,7 @@
   <title>Release process</title>
 
   <para>
-    Going through an example of releasing NixOS 15.09:
+    Going through an example of releasing NixOS 17.09:
   </para>
 
   <section xml:id="one-month-before-the-beta">
@@ -18,13 +18,13 @@
     <itemizedlist spacing="compact">
       <listitem>
         <para>
-          Send an email to nix-dev mailinglist as a warning about upcoming beta "feature freeze" in a month.
+          Send an email to the nix-devel mailinglist as a warning about upcoming beta "feature freeze" in a month.
         </para>
       </listitem>
       <listitem>
         <para>
           Discuss with Eelco Dolstra and the community (via IRC, ML) about what will reach the deadline.
-          Any issue or Pull Request targeting the release should have assigned milestone.
+          Any issue or Pull Request targeting the release should be included in the release milestone.
         </para>
       </listitem>
     </itemizedlist>
@@ -34,91 +34,88 @@
     <itemizedlist spacing="compact">
       <listitem>
         <para>
-          Rename <literal>rl-unstable.xml</literal> -&gt;
-          <literal>rl-1509.xml</literal>.
+          <link xlink:href="https://github.com/NixOS/nixpkgs/issues/13559">Create
+            an issue for tracking Zero Hydra Failures progress. ZHF is an effort
+            to get build failures down to zero.</link>
         </para>
       </listitem>
       <listitem>
         <para>
-          <literal>git tag -a -m &quot;Release 15.09-beta&quot; 15.09-beta &amp;&amp; git push --tags</literal>
+          <literal>git tag -a -s -m &quot;Release 17.09-beta&quot; 17.09-beta &amp;&amp; git push --tags</literal>
         </para>
       </listitem>
       <listitem>
         <para>
-          From the master branch run <literal>git checkout -B release-15.09</literal>.
+          From the master branch run <literal>git checkout -B release-17.09</literal>.
         </para>
       </listitem>
       <listitem>
         <para>
           <link xlink:href="https://github.com/NixOS/nixos-org-configurations/pull/18">
-            Make sure channel is created at http://nixos.org/channels/.
+            Make sure a channel is created at http://nixos.org/channels/.
           </link>
         </para>
       </listitem>
       <listitem>
         <para>
           <link xlink:href="https://github.com/NixOS/nixpkgs/settings/branches">
-            Lock the branch on github (so developers can’t force push)
+            Let a GitHub nixpkgs admin lock the branch on github for you.
+            (so developers can’t force push)
           </link>
         </para>
       </listitem>
       <listitem>
         <para>
-          <link xlink:href="https://github.com/NixOS/nixpkgs/compare/bdf161ed8d21...6b63c4616790">bump
-          <literal>system.defaultChannel</literal> attribute in
-          <literal>nixos/modules/misc/version.nix</literal></link>
+          <link xlink:href="https://github.com/NixOS/nixpkgs/compare/bdf161ed8d21...6b63c4616790">
+            Bump the <literal>system.defaultChannel</literal> attribute in
+            <literal>nixos/modules/misc/version.nix</literal>
+          </link>
         </para>
       </listitem>
       <listitem>
         <para>
-          <link xlink:href="https://github.com/NixOS/nixpkgs/commit/d6b08acd1ccac0d9d502c4b635e00b04d3387f06">update
-          <literal>versionSuffix</literal> in
+          <link xlink:href="https://github.com/NixOS/nixpkgs/commit/d6b08acd1ccac0d9d502c4b635e00b04d3387f06">
+            Update <literal>versionSuffix</literal> in
           <literal>nixos/release.nix</literal></link>, use
-          <literal>git log --format=%an|wc -l</literal> to get commit
+          <literal>git log --format=%an|wc -l</literal> to get the commit
           count
         </para>
       </listitem>
       <listitem>
         <para>
-          <literal>echo -n &quot;16.03&quot; &gt; .version</literal> in
+          <literal>echo -n &quot;18.03&quot; &gt; .version</literal> on
           master.
         </para>
       </listitem>
       <listitem>
         <para>
-          <link xlink:href="https://github.com/NixOS/nixpkgs/commit/b8a4095003e27659092892a4708bb3698231a842">pick
-          a new name for unstable branch.</link>
-        </para>
-      </listitem>
-      <listitem>
-        <para>
-          <link xlink:href="https://github.com/NixOS/nixpkgs/issues/13559">Create
-            an issue for tracking Zero Hydra Failures progress. ZHF is an effort
-            to get build failures down to zero.</link>
+          <link xlink:href="https://github.com/NixOS/nixpkgs/commit/b8a4095003e27659092892a4708bb3698231a842">
+            Pick a new name for the unstable branch.
+          </link>
         </para>
       </listitem>
       <listitem>
         <para>
-          Use https://lwn.net/Vulnerabilities/ and 
-          <link xlink:href="https://github.com/NixOS/nixpkgs/search?utf8=%E2%9C%93&amp;q=vulnerabilities&amp;type=Issues">triage vulnerabilities in an issue</link>.
+          Create a new release notes file for the upcoming release + 1, in this
+          case <literal>rl-1803.xml</literal>.
         </para>
       </listitem>
       <listitem>
         <para>
-          Create two Hydra jobsets: release-15.09 and release-15.09-small with <literal>stableBranch</literal> set to false
+          Create two Hydra jobsets: release-17.09 and release-17.09-small with <literal>stableBranch</literal> set to false.
         </para>
       </listitem>
       <listitem>
         <para>
           Edit changelog at
-          <literal>nixos/doc/manual/release-notes/rl-1509.xml</literal>
+          <literal>nixos/doc/manual/release-notes/rl-1709.xml</literal>
           (double check desktop versions are noted)
         </para>
         <itemizedlist spacing="compact">
           <listitem>
             <para>
               Get all new NixOS modules
-              <literal>git diff release-14.12..release-15.09 nixos/modules/module-list.nix|grep ^+</literal>
+              <literal>git diff release-17.03..release-17.09 nixos/modules/module-list.nix|grep ^+</literal>
             </para>
           </listitem>
           <listitem>
@@ -130,11 +127,27 @@
       </listitem>
     </itemizedlist>
   </section>
+  <section xml:id="during-beta">
+    <title>During Beta</title>
+    <itemizedlist spacing="compact">
+      <listitem>
+        <para>
+          Monitor the master branch for bugfixes and minor updates
+          and cherry-pick them to the release branch.
+        </para>
+      </listitem>
+    </itemizedlist>
+  </section>
   <section xml:id="before-the-final-release">
     <title>Before the final release</title>
     <itemizedlist spacing="compact">
       <listitem>
         <para>
+          Re-check that the release notes are complete.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           Release Nix (currently only Eelco Dolstra can do that).
           <link xlink:href="https://github.com/NixOS/nixpkgs/commit/53710c752a85f00658882531bc90a23a3d1287e4">
             Make sure fallback is updated.
diff --git a/nixos/doc/manual/installation/installing-usb.xml b/nixos/doc/manual/installation/installing-usb.xml
index 4a74e406b14c..b97f989d902c 100644
--- a/nixos/doc/manual/installation/installing-usb.xml
+++ b/nixos/doc/manual/installation/installing-usb.xml
@@ -11,7 +11,7 @@ a USB stick. You can use the <command>dd</command> utility to write the image:
 <command>dd if=<replaceable>path-to-image</replaceable>
 of=<replaceable>/dev/sdb</replaceable></command>. Be careful about specifying the
 correct drive; you can use the <command>lsblk</command> command to get a list of
-block devices. If you're on OS X you can run <command>diskutil list</command>
+block devices. If you're on macOS you can run <command>diskutil list</command>
 to see the list of devices; the device you'll use for the USB must be ejected
 before writing the image.</para>
 
diff --git a/nixos/doc/manual/man-nixos-option.xml b/nixos/doc/manual/man-nixos-option.xml
index 6be8bc780f13..d2b2d5b7965c 100644
--- a/nixos/doc/manual/man-nixos-option.xml
+++ b/nixos/doc/manual/man-nixos-option.xml
@@ -17,11 +17,16 @@
 <refsynopsisdiv>
   <cmdsynopsis>
     <command>nixos-option</command>
-    <arg choice='plain'><replaceable>option.name</replaceable></arg>
+    <arg>
+      <option>-I</option>
+      <replaceable>path</replaceable>
+    </arg>
+    <arg><option>--verbose</option></arg>
+    <arg><option>--xml</option></arg>
+    <arg choice="plain"><replaceable>option.name</replaceable></arg>
   </cmdsynopsis>
 </refsynopsisdiv>
 
-
 <refsection><title>Description</title>
 
 <para>This command evaluates the configuration specified in
@@ -33,6 +38,45 @@ attributes contained in the attribute set.</para>
 
 </refsection>
 
+<refsection><title>Options</title>
+
+<para>This command accepts the following options:</para>
+
+<variablelist>
+
+  <varlistentry>
+    <term><option>-I</option> <replaceable>path</replaceable></term>
+    <listitem>
+      <para>
+        This option is passed to the underlying
+        <command>nix-instantiate</command> invocation.
+      </para>
+    </listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><option>--verbose</option></term>
+    <listitem>
+      <para>
+        This option enables verbose mode, which currently is just
+        the Bash <command>set</command> <option>-x</option> debug mode.
+      </para>
+    </listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><option>--xml</option></term>
+    <listitem>
+      <para>
+        This option causes the output to be rendered as XML.
+      </para>
+    </listitem>
+  </varlistentry>
+
+</variablelist>
+
+</refsection>
+
 <refsection><title>Environment</title>
 
 <variablelist>
diff --git a/nixos/doc/manual/manual.xml b/nixos/doc/manual/manual.xml
index 2c28dd448016..9aa332f026da 100644
--- a/nixos/doc/manual/manual.xml
+++ b/nixos/doc/manual/manual.xml
@@ -18,7 +18,7 @@
 
     <para>If you encounter problems, please report them on the
     <literal
-    xlink:href="http://lists.science.uu.nl/mailman/listinfo/nix-dev">nix-dev@lists.science.uu.nl</literal>
+    xlink:href="https://groups.google.com/forum/#!forum/nix-devel">nix-devel</literal>
     mailing list or on the <link
     xlink:href="irc://irc.freenode.net/#nixos">
     <literal>#nixos</literal> channel on Freenode</link>.  Bugs should
diff --git a/nixos/doc/manual/release-notes/rl-1509.xml b/nixos/doc/manual/release-notes/rl-1509.xml
index 967fbcf869db..6c1c46844ccb 100644
--- a/nixos/doc/manual/release-notes/rl-1509.xml
+++ b/nixos/doc/manual/release-notes/rl-1509.xml
@@ -28,7 +28,7 @@ has the following highlights:</para>
     since version 0.0 as well as the most recent <link
     xlink:href="http://www.stackage.org/">Stackage Nightly</link>
     snapshot. The announcement <link
-    xlink:href="http://lists.science.uu.nl/pipermail/nix-dev/2015-September/018138.html">&quot;Full
+    xlink:href="https://nixos.org/nix-dev/2015-September/018138.html">&quot;Full
     Stackage Support in Nixpkgs&quot;</link> gives additional
     details.</para>
   </listitem>
diff --git a/nixos/doc/manual/release-notes/rl-1609.xml b/nixos/doc/manual/release-notes/rl-1609.xml
index ade7d5581ced..893f894f42fe 100644
--- a/nixos/doc/manual/release-notes/rl-1609.xml
+++ b/nixos/doc/manual/release-notes/rl-1609.xml
@@ -78,13 +78,13 @@ following incompatible changes:</para>
     our package set it loosely based on the latest available LTS release, i.e.
     LTS 7.x at the time of this writing. New releases of NixOS and Nixpkgs will
     drop those old names entirely. <link
-    xlink:href="http://lists.science.uu.nl/pipermail/nix-dev/2016-June/020585.html">The
+    xlink:href="https://nixos.org/nix-dev/2016-June/020585.html">The
     motivation for this change</link> has been discussed at length on the
     <literal>nix-dev</literal> mailing list and in <link
     xlink:href="https://github.com/NixOS/nixpkgs/issues/14897">Github issue
     #14897</link>. Development strategies for Haskell hackers who want to rely
     on Nix and NixOS have been described in <link
-    xlink:href="http://lists.science.uu.nl/pipermail/nix-dev/2016-June/020642.html">another
+    xlink:href="https://nixos.org/nix-dev/2016-June/020642.html">another
     nix-dev article</link>.</para>
   </listitem>
 
@@ -176,7 +176,7 @@ following incompatible changes:</para>
   streamlined.  Desktop users should be able to simply set
   <programlisting>security.grsecurity.enable = true</programlisting> to get
   a reasonably secure system without having to sacrifice too much
-  functionality.  See <xref linkend="sec-grsecurity" /> for documentation
+  functionality.
   </para></listitem>
 
   <listitem><para>Special filesystems, like <literal>/proc</literal>,
diff --git a/nixos/doc/manual/release-notes/rl-1709.xml b/nixos/doc/manual/release-notes/rl-1709.xml
index 34cfe1702e9c..c275fe46d118 100644
--- a/nixos/doc/manual/release-notes/rl-1709.xml
+++ b/nixos/doc/manual/release-notes/rl-1709.xml
@@ -12,6 +12,11 @@ has the following highlights: </para>
 <itemizedlist>
   <listitem>
     <para>
+      The GNOME version is now 3.24.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
       The user handling now keeps track of deallocated UIDs/GIDs. When a user
       or group is revived, this allows it to be allocated the UID/GID it had before.
       A consequence is that UIDs and GIDs are no longer reused.
@@ -86,6 +91,10 @@ rmdir /var/lib/ipfs/.ipfs
   </listitem>
   <listitem>
     <para>
+      The following changes apply if the <literal>stateVersion</literal> is changed to 17.09 or higher.
+      For <literal>stateVersion = "17.03</literal> or lower the old behavior is preserved.
+    </para>
+    <para>
       The <literal>postgres</literal> default version was changed from 9.5 to 9.6.
     </para>
     <para>
@@ -94,6 +103,12 @@ rmdir /var/lib/ipfs/.ipfs
     <para>
       The <literal>postgres</literal> default <literal>dataDir</literal> has changed from <literal>/var/db/postgres</literal> to <literal>/var/lib/postgresql/$psqlSchema</literal> where $psqlSchema is 9.6 for example.
     </para>
+    <para>
+      The <literal>mysql</literal> default <literal>dataDir</literal> has changed from <literal>/var/mysql</literal> to <literal>/var/lib/mysql</literal>.
+    </para>
+    <para>
+      Radicale's default package has changed from 1.x to 2.x. Instructions to migrate can be found <link xlink:href="http://radicale.org/1to2/"> here </link>. It is also possible to use the newer version by setting the <literal>package</literal> to <literal>radicale2</literal>, which is done automatically when <literal>stateVersion</literal> is 17.09 or higher.
+    </para>
   </listitem>
   <listitem>
     <para>
@@ -113,9 +128,73 @@ rmdir /var/lib/ipfs/.ipfs
       also serve as a SSH agent if <literal>enableSSHSupport</literal> is set.
     </para>
   </listitem>
+  <listitem>
+    <para>
+      The <literal>services.tinc.networks.&lt;name&gt;.listenAddress</literal>
+      option had a misleading name that did not correspond to its behavior. It
+      now correctly defines the ip to listen for incoming connections on. To
+      keep the previous behaviour, use
+      <literal>services.tinc.networks.&lt;name&gt;.bindToAddress</literal>
+      instead. Refer to the description of the options for more details.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      <literal>tlsdate</literal> package and module were removed. This is due to the project
+      being dead and not building with openssl 1.1.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      <literal>wvdial</literal> package and module were removed. This is due to the project
+      being dead and not building with openssl 1.1.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      <literal>cc-wrapper</literal>'s setup-hook now exports a number of
+      environment variables corresponding to binutils binaries,
+      (e.g. <envar>LD</envar>, <envar>STRIP</envar>, <envar>RANLIB</envar>,
+      etc). This is done to prevent packages' build systems guessing, which is
+      harder to predict, especially when cross-compiling. However, some packages
+      have broken due to this—their build systems either not supporting, or
+      claiming to support without adequate testing, taking such environment
+      variables as parameters.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      <literal>services.firefox.syncserver</literal> now runs by default as a
+      non-root user. To accomodate this change, the default sqlite database
+      location has also been changed. Migration should work automatically.
+      Refer to the description of the options for more details.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The <literal>compiz</literal> window manager and package was
+      removed. The system support had been broken for several years.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      Touchpad support should now be enabled through
+      <literal>libinput</literal> as <literal>synaptics</literal> is
+      now deprecated. See the option
+      <literal>services.xserver.libinput.enable</literal>.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      grsecurity/PaX support has been dropped, following upstream's
+      decision to cease free support.  See
+      <link xlink:href="https://grsecurity.net/passing_the_baton.php">
+      upstream's announcement</link> for more information.
+      No complete replacement for grsecurity/PaX is available presently.
+    </para>
+  </listitem>
 </itemizedlist>
 
-
 <para>Other notable improvements:</para>
 
 <itemizedlist>
@@ -141,6 +220,32 @@ rmdir /var/lib/ipfs/.ipfs
       module where user Fontconfig settings are available.
     </para>
   </listitem>
+  <listitem>
+    <para>
+      ZFS/SPL have been updated to 0.7.0, <literal>zfsUnstable, splUnstable</literal>
+      have therefore been removed.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      The <option>time.timeZone</option> option now allows the value
+      <literal>null</literal> in addition to timezone strings. This value
+      allows changing the timezone of a system imperatively using
+      <command>timedatectl set-timezone</command>. The default timezone
+      is still UTC.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      Nixpkgs overlays may now be specified with a file as well as a directory. The
+      value of <literal>&lt;nixpkgs-overlays></literal> may be a file, and
+      <filename>~/.config/nixpkgs/overlays.nix</filename> can be used instead of the
+      <filename>~/.config/nixpkgs/overalys</filename> directory.
+    </para>
+    <para>
+      See the overlays chapter of the Nixpkgs manual for more details.
+    </para>
+  </listitem>
 
 </itemizedlist>
 
diff --git a/nixos/doc/manual/release-notes/rl-1803.xml b/nixos/doc/manual/release-notes/rl-1803.xml
new file mode 100644
index 000000000000..ce05cde7b08b
--- /dev/null
+++ b/nixos/doc/manual/release-notes/rl-1803.xml
@@ -0,0 +1,46 @@
+<section xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-release-18.03">
+
+<title>Release 18.03 (“Impala”, 2018/03/??)</title>
+
+<para>In addition to numerous new and upgraded packages, this release
+has the following highlights: </para>
+
+<itemizedlist>
+  <listitem>
+    <para>
+    </para>
+  </listitem>
+</itemizedlist>
+
+<para>The following new services were added since the last release:</para>
+
+<itemizedlist>
+  <listitem>
+    <para></para>
+  </listitem>
+</itemizedlist>
+
+<para>When upgrading from a previous release, please be aware of the
+following incompatible changes:</para>
+
+<itemizedlist>
+  <listitem>
+    <para>
+    </para>
+  </listitem>
+</itemizedlist>
+
+<para>Other notable improvements:</para>
+
+<itemizedlist>
+  <listitem>
+    <para>
+    </para>
+  </listitem>
+</itemizedlist>
+
+</section>
diff --git a/nixos/lib/make-disk-image.nix b/nixos/lib/make-disk-image.nix
index 56766ec9047f..d4b2e338c3ef 100644
--- a/nixos/lib/make-disk-image.nix
+++ b/nixos/lib/make-disk-image.nix
@@ -39,19 +39,13 @@
 with lib;
 
 let
-  # Copied from https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/installer/cd-dvd/channel.nix
-  # TODO: factor out more cleanly
+  extensions = {
+    qcow2 = "qcow2";
+    vpc   = "vhd";
+    raw   = "img";
+  };
 
-  # Do not include these things:
-  #   - The '.git' directory
-  #   - Result symlinks from nix-build ('result', 'result-2', 'result-bin', ...)
-  #   - VIM/Emacs swap/backup files ('.swp', '.swo', '.foo.swp', 'foo~', ...)
-  filterFn = path: type: let basename = baseNameOf (toString path); in
-    if type == "directory" then basename != ".git"
-    else if type == "symlink" then builtins.match "^result(|-.*)$" basename == null
-    else builtins.match "^((|\..*)\.sw[a-z]|.*~)$" basename == null;
-
-  nixpkgs = builtins.filterSource filterFn pkgs.path;
+  nixpkgs = lib.cleanSource pkgs.path;
 
   channelSources = pkgs.runCommand "nixos-${config.system.nixosVersion}" {} ''
     mkdir -p $out
@@ -142,8 +136,8 @@ in pkgs.vmTools.runInLinuxVM (
           mv $diskImage $out/nixos.img
           diskImage=$out/nixos.img
         '' else ''
-          ${pkgs.qemu}/bin/qemu-img convert -f raw -O qcow2 $diskImage $out/nixos.qcow2
-          diskImage=$out/nixos.qcow2
+          ${pkgs.qemu}/bin/qemu-img convert -f raw -O ${format} $diskImage $out/nixos.${extensions.${format}}
+          diskImage=$out/nixos.${extensions.${format}}
         ''}
         ${postVM}
       '';
diff --git a/nixos/lib/make-ext4-fs.nix b/nixos/lib/make-ext4-fs.nix
index 23839ea487db..f06649e1991a 100644
--- a/nixos/lib/make-ext4-fs.nix
+++ b/nixos/lib/make-ext4-fs.nix
@@ -33,7 +33,7 @@ pkgs.stdenv.mkDerivation {
       echo "Creating an EXT4 image of $bytes bytes (numInodes=$numInodes, numDataBlocks=$numDataBlocks)"
 
       truncate -s $bytes $out
-      faketime "1970-01-01 00:00:00" mkfs.ext4 -L ${volumeLabel} -U 44444444-4444-4444-8888-888888888888 $out
+      faketime -f "1970-01-01 00:00:01" mkfs.ext4 -L ${volumeLabel} -U 44444444-4444-4444-8888-888888888888 $out
 
       # Populate the image contents by piping a bunch of commands to the `debugfs` tool from e2fsprogs.
       # For example, to copy /nix/store/abcd...efg-coreutils-8.23/bin/sleep:
@@ -76,7 +76,7 @@ pkgs.stdenv.mkDerivation {
 
           echo sif $file gid 30000 # chgrp to nixbld
         done
-      ) | faketime "1970-01-01 00:00:00" debugfs -w $out -f /dev/stdin > errorlog 2>&1
+      ) | faketime -f "1970-01-01 00:00:01" debugfs -w $out -f /dev/stdin > errorlog 2>&1
 
       # The debugfs tool doesn't terminate on error nor exit with a non-zero status. Check manually.
       if egrep -q 'Could not allocate|File not found' errorlog; then
diff --git a/nixos/maintainers/scripts/ec2/amazon-image.nix b/nixos/maintainers/scripts/ec2/amazon-image.nix
index cdfac71634d4..d76fb644d029 100644
--- a/nixos/maintainers/scripts/ec2/amazon-image.nix
+++ b/nixos/maintainers/scripts/ec2/amazon-image.nix
@@ -22,15 +22,26 @@ in {
         generated image. Glob patterns work.
       '';
     };
+
+    sizeMB = mkOption {
+      type = types.int;
+      default = if config.ec2.hvm then 2048 else 8192;
+      description = "The size in MB of the image";
+    };
+
+    format = mkOption {
+      type = types.enum [ "raw" "qcow2" "vpc" ];
+      default = "qcow2";
+      description = "The image format to output";
+    };
   };
 
   config.system.build.amazonImage = import ../../../lib/make-disk-image.nix {
     inherit lib config;
-    inherit (cfg) contents;
+    inherit (cfg) contents format;
     pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
     partitioned = config.ec2.hvm;
-    diskSize = if config.ec2.hvm then 2048 else 8192;
-    format = "qcow2";
+    diskSize = cfg.sizeMB;
     configFile = pkgs.writeText "configuration.nix"
       ''
         {
@@ -41,5 +52,4 @@ in {
         }
       '';
   };
-
 }
diff --git a/nixos/modules/config/ldap.nix b/nixos/modules/config/ldap.nix
index da875d6e4821..710dfdd01af5 100644
--- a/nixos/modules/config/ldap.nix
+++ b/nixos/modules/config/ldap.nix
@@ -19,7 +19,6 @@ let
       bind_policy ${config.users.ldap.bind.policy}
       ${optionalString config.users.ldap.useTLS ''
         ssl start_tls
-        tls_checkpeer no
       ''}
       ${optionalString (config.users.ldap.bind.distinguishedName != "") ''
         binddn ${config.users.ldap.bind.distinguishedName}
diff --git a/nixos/modules/config/networking.nix b/nixos/modules/config/networking.nix
index d503f5a8b20e..619f36cd5150 100644
--- a/nixos/modules/config/networking.nix
+++ b/nixos/modules/config/networking.nix
@@ -20,12 +20,26 @@ in
 
   options = {
 
+    networking.hosts = lib.mkOption {
+      type = types.attrsOf ( types.listOf types.str );
+      default = {};
+      example = literalExample ''
+        {
+          "127.0.0.1" = [ "foo.bar.baz" ];
+          "192.168.0.2" = [ "fileserver.local" "nameserver.local" ];
+        };
+      '';
+      description = ''
+        Locally defined maps of hostnames to IP addresses.
+      '';
+    };
+
     networking.extraHosts = lib.mkOption {
       type = types.lines;
       default = "";
       example = "192.168.0.1 lanlocalhost";
       description = ''
-        Additional entries to be appended to <filename>/etc/hosts</filename>.
+        Additional verbatim entries to be appended to <filename>/etc/hosts</filename>.
       '';
     };
 
@@ -188,11 +202,22 @@ in
 
         # /etc/hosts: Hostname-to-IP mappings.
         "hosts".text =
+          let oneToString = set : ip : ip + " " + concatStringsSep " " ( getAttr ip set );
+              allToString = set : concatMapStringsSep "\n" ( oneToString set ) ( attrNames set );
+              userLocalHosts = optionalString
+                ( builtins.hasAttr "127.0.0.1" cfg.hosts )
+                ( concatStringsSep " " ( remove "localhost" cfg.hosts."127.0.0.1" ));
+              userLocalHosts6 = optionalString
+                ( builtins.hasAttr "::1" cfg.hosts )
+                ( concatStringsSep " " ( remove "localhost" cfg.hosts."::1" ));
+              otherHosts = allToString ( removeAttrs cfg.hosts [ "127.0.0.1" "::1" ]);
+          in
           ''
-            127.0.0.1 localhost
+            127.0.0.1 ${userLocalHosts} localhost
             ${optionalString cfg.enableIPv6 ''
-              ::1 localhost
+              ::1 ${userLocalHosts6} localhost
             ''}
+            ${otherHosts}
             ${cfg.extraHosts}
           '';
 
@@ -223,7 +248,9 @@ in
             '';
 
       } // optionalAttrs config.services.resolved.enable {
-        "resolv.conf".source = "/run/systemd/resolve/resolv.conf";
+        # symlink the static version of resolv.conf as recommended by upstream:
+        # https://www.freedesktop.org/software/systemd/man/systemd-resolved.html#/etc/resolv.conf
+        "resolv.conf".source = "${pkgs.systemd}/lib/systemd/resolv.conf";
       } // optionalAttrs (config.services.resolved.enable && dnsmasqResolve) {
         "dnsmasq-resolv.conf".source = "/run/systemd/resolve/resolv.conf";
       };
diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix
index 13477337bda5..ae3e17ac27b6 100644
--- a/nixos/modules/config/no-x-libs.nix
+++ b/nixos/modules/config/no-x-libs.nix
@@ -26,7 +26,16 @@ with lib;
 
     fonts.fontconfig.enable = false;
 
-    nixpkgs.config.packageOverrides = pkgs:
-      { dbus = pkgs.dbus.override { x11Support = false; }; };
+    nixpkgs.config.packageOverrides = pkgs: {
+      dbus = pkgs.dbus.override { x11Support = false; };
+      networkmanager_fortisslvpn = pkgs.networkmanager_fortisslvpn.override { withGnome = false; };
+      networkmanager_l2tp = pkgs.networkmanager_l2tp.override { withGnome = false; };
+      networkmanager_openconnect = pkgs.networkmanager_openconnect.override { withGnome = false; };
+      networkmanager_openvpn = pkgs.networkmanager_openvpn.override { withGnome = false; };
+      networkmanager_pptp = pkgs.networkmanager_pptp.override { withGnome = false; };
+      networkmanager_vpnc = pkgs.networkmanager_vpnc.override { withGnome = false; };
+      networkmanager_iodine = pkgs.networkmanager_iodine.override { withGnome = false; };
+      pinentry = pkgs.pinentry.override { gtk2 = null; qt4 = null; };
+    };
   };
 }
diff --git a/nixos/modules/config/nsswitch.nix b/nixos/modules/config/nsswitch.nix
index 16c43a99ad56..97278238dcd5 100644
--- a/nixos/modules/config/nsswitch.nix
+++ b/nixos/modules/config/nsswitch.nix
@@ -28,7 +28,8 @@ let
   passwdArray = [ "files" ]
     ++ optional sssd "sss"
     ++ optionals ldap [ "ldap" ]
-    ++ optionals mymachines [ "mymachines" ];
+    ++ optionals mymachines [ "mymachines" ]
+    ++ [ "systemd" ];
 
   shadowArray = [ "files" ]
     ++ optional sssd "sss"
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
index bd80c8113483..8b9c3570476a 100644
--- a/nixos/modules/config/pulseaudio.nix
+++ b/nixos/modules/config/pulseaudio.nix
@@ -6,6 +6,7 @@ with lib;
 let
 
   cfg = config.hardware.pulseaudio;
+  alsaCfg = config.sound;
 
   systemWide = cfg.enable && cfg.systemWide;
   nonSystemWide = cfg.enable && !cfg.systemWide;
@@ -76,6 +77,7 @@ let
     ctl.!default {
       type pulse
     }
+    ${alsaCfg.extraConfig}
   '');
 
 in {
@@ -222,7 +224,7 @@ in {
       # Allow PulseAudio to get realtime priority using rtkit.
       security.rtkit.enable = true;
 
-      systemd.packages = [ cfg.package ];
+      systemd.packages = [ overriddenPackage ];
     })
 
     (mkIf hasZeroconf {
diff --git a/nixos/modules/config/swap.nix b/nixos/modules/config/swap.nix
index e57ed2565a10..fed3fa3bc7c8 100644
--- a/nixos/modules/config/swap.nix
+++ b/nixos/modules/config/swap.nix
@@ -5,6 +5,52 @@ with lib;
 
 let
 
+  randomEncryptionCoerce = enable: { inherit enable; };
+
+  randomEncryptionOpts = { ... }: {
+
+    options = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Encrypt swap device with a random key. This way you won't have a persistent swap device.
+
+          WARNING: Don't try to hibernate when you have at least one swap partition with
+          this option enabled! We have no way to set the partition into which hibernation image
+          is saved, so if your image ends up on an encrypted one you would lose it!
+
+          WARNING #2: Do not use /dev/disk/by-uuid/… or /dev/disk/by-label/… as your swap device
+          when using randomEncryption as the UUIDs and labels will get erased on every boot when
+          the partition is encrypted. Best to use /dev/disk/by-partuuid/…
+        '';
+      };
+
+      cipher = mkOption {
+        default = "aes-xts-plain64";
+        example = "serpent-xts-plain64";
+        type = types.str;
+        description = ''
+          Use specified cipher for randomEncryption.
+
+          Hint: Run "cryptsetup benchmark" to see which one is fastest on your machine.
+        '';
+      };
+
+      source = mkOption {
+        default = "/dev/urandom";
+        example = "/dev/random";
+        type = types.str;
+        description = ''
+          Define the source of randomness to obtain a random key for encryption.
+        '';
+      };
+
+    };
+
+  };
+
   swapCfg = {config, options, ...}: {
 
     options = {
@@ -47,10 +93,17 @@ let
 
       randomEncryption = mkOption {
         default = false;
-        type = types.bool;
+        example = {
+          enable = true;
+          cipher = "serpent-xts-plain64";
+          source = "/dev/random";
+        };
+        type = types.coercedTo types.bool randomEncryptionCoerce (types.submodule randomEncryptionOpts);
         description = ''
           Encrypt swap device with a random key. This way you won't have a persistent swap device.
 
+          HINT: run "cryptsetup benchmark" to test cipher performance on your machine.
+
           WARNING: Don't try to hibernate when you have at least one swap partition with
           this option enabled! We have no way to set the partition into which hibernation image
           is saved, so if your image ends up on an encrypted one you would lose it!
@@ -77,7 +130,7 @@ let
       device = mkIf options.label.isDefined
         "/dev/disk/by-label/${config.label}";
       deviceName = lib.replaceChars ["\\"] [""] (escapeSystemdPath config.device);
-      realDevice = if config.randomEncryption then "/dev/mapper/${deviceName}" else config.device;
+      realDevice = if config.randomEncryption.enable then "/dev/mapper/${deviceName}" else config.device;
     };
 
   };
@@ -125,14 +178,14 @@ in
 
         createSwapDevice = sw:
           assert sw.device != "";
-          assert !(sw.randomEncryption && lib.hasPrefix "/dev/disk/by-uuid"  sw.device);
-          assert !(sw.randomEncryption && lib.hasPrefix "/dev/disk/by-label" sw.device);
+          assert !(sw.randomEncryption.enable && lib.hasPrefix "/dev/disk/by-uuid"  sw.device);
+          assert !(sw.randomEncryption.enable && lib.hasPrefix "/dev/disk/by-label" sw.device);
           let realDevice' = escapeSystemdPath sw.realDevice;
           in nameValuePair "mkswap-${sw.deviceName}"
           { description = "Initialisation of swap device ${sw.device}";
             wantedBy = [ "${realDevice'}.swap" ];
             before = [ "${realDevice'}.swap" ];
-            path = [ pkgs.utillinux ] ++ optional sw.randomEncryption pkgs.cryptsetup;
+            path = [ pkgs.utillinux ] ++ optional sw.randomEncryption.enable pkgs.cryptsetup;
 
             script =
               ''
@@ -145,13 +198,11 @@ in
                       truncate --size "${toString sw.size}M" "${sw.device}"
                     fi
                     chmod 0600 ${sw.device}
-                    ${optionalString (!sw.randomEncryption) "mkswap ${sw.realDevice}"}
+                    ${optionalString (!sw.randomEncryption.enable) "mkswap ${sw.realDevice}"}
                   fi
                 ''}
-                ${optionalString sw.randomEncryption ''
-                  echo "secretkey" | cryptsetup luksFormat --batch-mode ${sw.device}
-                  echo "secretkey" | cryptsetup luksOpen ${sw.device} ${sw.deviceName}
-                  cryptsetup luksErase --batch-mode ${sw.device}
+                ${optionalString sw.randomEncryption.enable ''
+                  cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} ${sw.device} ${sw.deviceName}
                   mkswap ${sw.realDevice}
                 ''}
               '';
@@ -159,12 +210,12 @@ in
             unitConfig.RequiresMountsFor = [ "${dirOf sw.device}" ];
             unitConfig.DefaultDependencies = false; # needed to prevent a cycle
             serviceConfig.Type = "oneshot";
-            serviceConfig.RemainAfterExit = sw.randomEncryption;
-            serviceConfig.ExecStop = optionalString sw.randomEncryption "${pkgs.cryptsetup}/bin/cryptsetup luksClose ${sw.deviceName}";
+            serviceConfig.RemainAfterExit = sw.randomEncryption.enable;
+            serviceConfig.ExecStop = optionalString sw.randomEncryption.enable "${pkgs.cryptsetup}/bin/cryptsetup luksClose ${sw.deviceName}";
             restartIfChanged = false;
           };
 
-      in listToAttrs (map createSwapDevice (filter (sw: sw.size != null || sw.randomEncryption) config.swapDevices));
+      in listToAttrs (map createSwapDevice (filter (sw: sw.size != null || sw.randomEncryption.enable) config.swapDevices));
 
   };
 
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
index cf3cc2f22628..5d339eaea485 100644
--- a/nixos/modules/config/system-path.nix
+++ b/nixos/modules/config/system-path.nix
@@ -118,6 +118,9 @@ in
         "/share/themes"
         "/share/vim-plugins"
         "/share/vulkan"
+        "/share/kservices5"
+        "/share/kservicetypes5"
+        "/share/kxmlgui5"
       ];
 
     system.path = pkgs.buildEnv {
diff --git a/nixos/modules/config/timezone.nix b/nixos/modules/config/timezone.nix
index 39a45042c6cc..aa030a816d04 100644
--- a/nixos/modules/config/timezone.nix
+++ b/nixos/modules/config/timezone.nix
@@ -14,13 +14,16 @@ in
     time = {
 
       timeZone = mkOption {
-        default = "UTC";
-        type = types.str;
+        default = null;
+        type = types.nullOr types.str;
         example = "America/New_York";
         description = ''
           The time zone used when displaying times and dates. See <link
           xlink:href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones"/>
           for a comprehensive list of possible values for this setting.
+
+          If null, the timezone will default to UTC and can be set imperatively
+          using timedatectl.
         '';
       };
 
@@ -40,13 +43,14 @@ in
     # This way services are restarted when tzdata changes.
     systemd.globalEnvironment.TZDIR = tzdir;
 
-    environment.etc.localtime =
-      { source = "/etc/zoneinfo/${config.time.timeZone}";
-        mode = "direct-symlink";
-      };
-
-    environment.etc.zoneinfo.source = tzdir;
+    systemd.services.systemd-timedated.environment = lib.optionalAttrs (config.time.timeZone != null) { NIXOS_STATIC_TIMEZONE = "1"; };
 
+    environment.etc = {
+      zoneinfo.source = tzdir;
+    } // lib.optionalAttrs (config.time.timeZone != null) {
+        localtime.source = "/etc/zoneinfo/${config.time.timeZone}";
+        localtime.mode = "direct-symlink";
+      };
   };
 
 }
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 0e7ffbd3c2e1..a4715175cc95 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -527,7 +527,7 @@ in {
       input.gid = ids.gids.input;
     };
 
-    system.activationScripts.users = stringAfter [ "etc" ]
+    system.activationScripts.users = stringAfter [ "stdio" ]
       ''
         ${pkgs.perl}/bin/perl -w \
           -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl \
diff --git a/nixos/modules/hardware/mcelog.nix b/nixos/modules/hardware/mcelog.nix
index e4ac7d39053f..13ad238870c2 100644
--- a/nixos/modules/hardware/mcelog.nix
+++ b/nixos/modules/hardware/mcelog.nix
@@ -3,7 +3,7 @@
 with lib;
 
 {
-  meta.maintainers = [ maintainers.grahamc ];
+  meta.maintainers = with maintainers; [ grahamc ];
   options = {
 
     hardware.mcelog = {
@@ -19,19 +19,17 @@ with lib;
   };
 
   config = mkIf config.hardware.mcelog.enable {
-    systemd.services.mcelog = {
-      description = "Machine Check Exception Logging Daemon";
-      wantedBy = [ "multi-user.target" ];
-
-      serviceConfig = {
-        ExecStart = "${pkgs.mcelog}/bin/mcelog --daemon --foreground";
-        SuccessExitStatus = [ 0 15 ];
-
-        ProtectHome = true;
-        PrivateNetwork = true;
-        PrivateTmp = true;
+    systemd = {
+      packages = [ pkgs.mcelog ];
+
+      services.mcelog = {
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          ProtectHome = true;
+          PrivateNetwork = true;
+          PrivateTmp = true;
+        };
       };
     };
   };
-
 }
diff --git a/nixos/modules/hardware/raid/hpsa.nix b/nixos/modules/hardware/raid/hpsa.nix
new file mode 100644
index 000000000000..1b4b1fa1954f
--- /dev/null
+++ b/nixos/modules/hardware/raid/hpsa.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  hpssacli = pkgs.stdenv.mkDerivation rec {
+    name = "hpssacli-${version}";
+    version = "2.40-13.0";
+
+    src = pkgs.fetchurl {
+      url = "http://downloads.linux.hpe.com/SDR/downloads/MCP/Ubuntu/pool/non-free/${name}_amd64.deb";
+      sha256 = "11w7fwk93lmfw0yya4jpjwdmgjimqxx6412sqa166g1pz4jil4sw";
+    };
+
+    nativeBuildInputs = [ pkgs.dpkg ];
+
+    unpackPhase = "dpkg -x $src ./";
+
+    installPhase = ''
+      mkdir -p $out/bin $out/share/doc $out/share/man
+      mv opt/hp/hpssacli/bld/{hpssascripting,hprmstr,hpssacli} $out/bin/
+      mv opt/hp/hpssacli/bld/*.{license,txt}                   $out/share/doc/
+      mv usr/man                                               $out/share/
+
+      for file in $out/bin/*; do
+        chmod +w $file
+        patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+                 --set-rpath ${lib.makeLibraryPath [ pkgs.stdenv.cc.cc ]} \
+                 $file
+      done
+    '';
+
+    dontStrip = true;
+
+    meta = with lib; {
+      description = "HP Smart Array CLI";
+      homepage = http://downloads.linux.hpe.com/SDR/downloads/MCP/Ubuntu/pool/non-free/;
+      license = licenses.unfreeRedistributable;
+      platforms = [ "x86_64-linux" ];
+      maintainers = with maintainers; [ volth ];
+    };
+  };
+in {
+  ###### interface
+
+  options = {
+    hardware.raid.HPSmartArray = {
+      enable = mkEnableOption "HP Smart Array kernel modules and CLI utility";
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf config.hardware.raid.HPSmartArray.enable {
+
+    boot.initrd.kernelModules = [ "sg" ]; /* hpssacli wants it */
+    boot.initrd.availableKernelModules = [ "hpsa" ];
+
+    environment.systemPackages = [ hpssacli ];
+  };
+}
diff --git a/nixos/modules/installer/cd-dvd/channel.nix b/nixos/modules/installer/cd-dvd/channel.nix
index 663ff24c81f1..ddb00f174d1a 100644
--- a/nixos/modules/installer/cd-dvd/channel.nix
+++ b/nixos/modules/installer/cd-dvd/channel.nix
@@ -6,16 +6,7 @@
 with lib;
 
 let
-  # Do not include these things:
-  #   - The '.git' directory
-  #   - Result symlinks from nix-build ('result', 'result-2', 'result-bin', ...)
-  #   - VIM/Emacs swap/backup files ('.swp', '.swo', '.foo.swp', 'foo~', ...)
-  filterFn = path: type: let basename = baseNameOf (toString path); in
-    if type == "directory" then basename != ".git"
-    else if type == "symlink" then builtins.match "^result(|-.*)$" basename == null
-    else builtins.match "^((|\..*)\.sw[a-z]|.*~)$" basename == null;
-
-  nixpkgs = builtins.filterSource filterFn pkgs.path;
+  nixpkgs = lib.cleanSource pkgs.path;
 
   # We need a copy of the Nix expressions for Nixpkgs and NixOS on the
   # CD.  These are installed into the "nixos" channel of the root
diff --git a/nixos/modules/installer/tools/auto-upgrade.nix b/nixos/modules/installer/tools/auto-upgrade.nix
index a4d4f16d1d96..7b756b70e2fc 100644
--- a/nixos/modules/installer/tools/auto-upgrade.nix
+++ b/nixos/modules/installer/tools/auto-upgrade.nix
@@ -76,7 +76,7 @@ let cfg = config.system.autoUpgrade; in
       environment = config.nix.envVars //
         { inherit (config.environment.sessionVariables) NIX_PATH;
           HOME = "/root";
-        };
+        } // config.networking.proxy.envVars;
 
       path = [ pkgs.gnutar pkgs.xz.bin config.nix.package.out ];
 
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index 80241cd3ebec..62c292eea6dd 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,5 +1,5 @@
 {
-  x86_64-linux = "/nix/store/crqd5wmrqipl4n1fcm5kkc1zg4sj80js-nix-1.11.11";
-  i686-linux = "/nix/store/wsjn14xp5ja509d4dxb1c78zhirw0b5x-nix-1.11.11";
-  x86_64-darwin = "/nix/store/zqkqnhk85g2shxlpb04y72h1i3db3gpl-nix-1.11.11";
+  x86_64-linux = "/nix/store/xrqssm90gsrnqdn79rpfcs6dwx8597d2-nix-1.11.14";
+  i686-linux = "/nix/store/3vjphivqs2iy6m9yb3bd80nd3518510k-nix-1.11.14";
+  x86_64-darwin = "/nix/store/4j9jacx8mjd4jlj53wvymyhxq7dqyj5d-nix-1.11.14";
 }
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index 29b447912e70..3af76f898f9a 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -605,6 +605,9 @@ $bootLoaderConfig
   # services.xserver.layout = "us";
   # services.xserver.xkbOptions = "eurosign:e";
 
+  # Enable touchpad support.
+  # services.xserver.libinput.enable = true;
+
   # Enable the KDE Desktop Environment.
   # services.xserver.displayManager.sddm.enable = true;
   # services.xserver.desktopManager.plasma5.enable = true;
@@ -615,8 +618,11 @@ $bootLoaderConfig
   #   uid = 1000;
   # };
 
-  # The NixOS release to be compatible with for stateful data such as databases.
-  system.stateVersion = "${\(qw(@nixosRelease@))}";
+  # This value determines the NixOS release with which your system is to be
+  # compatible, in order to avoid breaking some software such as database
+  # servers. You should change this only after NixOS release notes say you
+  # should.
+  system.stateVersion = "${\(qw(@nixosRelease@))}"; # Did you read the comment?
 
 }
 EOF
diff --git a/nixos/modules/installer/tools/nixos-install.sh b/nixos/modules/installer/tools/nixos-install.sh
index e2ae2ee9fdf8..087fbcd4512f 100644
--- a/nixos/modules/installer/tools/nixos-install.sh
+++ b/nixos/modules/installer/tools/nixos-install.sh
@@ -140,7 +140,7 @@ channel_closure="$tmpdir/channel.closure"
 nix-store --export $channel_root > $channel_closure
 
 # Populate the target root directory with the basics
-@prepare_root@/bin/nixos-prepare-root $mountPoint $channel_root $system_root @nixClosure@ $system_closure $channel_closure
+@prepare_root@/bin/nixos-prepare-root "$mountPoint" "$channel_root" "$system_root" @nixClosure@ "$system_closure" "$channel_closure"
 
 # nixos-prepare-root doesn't currently do anything with file ownership, so we set it up here instead
 chown @root_uid@:@nixbld_gid@ $mountPoint/nix/store
diff --git a/nixos/modules/installer/tools/nixos-rebuild.sh b/nixos/modules/installer/tools/nixos-rebuild.sh
index 4b5e7b3230c8..9ede74a54cd7 100644
--- a/nixos/modules/installer/tools/nixos-rebuild.sh
+++ b/nixos/modules/installer/tools/nixos-rebuild.sh
@@ -250,7 +250,7 @@ trap cleanup EXIT
 # If --repair is given, don't try to use the Nix daemon, because the
 # flag can only be used directly.
 if [ -z "$repair" ] && systemctl show nix-daemon.socket nix-daemon.service | grep -q ActiveState=active; then
-    export NIX_REMOTE=${NIX_REMOTE:-daemon}
+    export NIX_REMOTE=${NIX_REMOTE-daemon}
 fi
 
 
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 5ac5764cd7cb..859838182ed4 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -139,6 +139,7 @@
       btsync = 113;
       minecraft = 114;
       #monetdb = 115; # unused (not packaged), removed 2016-09-19
+      vault = 115;
       rippled = 116;
       murmur = 117;
       foundationdb = 118;
@@ -213,7 +214,7 @@
       plex = 193;
       grafana = 196;
       skydns = 197;
-      ripple-rest = 198;
+      # ripple-rest = 198; # unused, removed 2017-08-12
       nix-serve = 199;
       tvheadend = 200;
       uwsgi = 201;
@@ -334,7 +335,7 @@
       dialout = 27;
       #polkituser = 28; # currently unused, polkitd doesn't need a group
       utmp = 29;
-      #ddclient = 30; # unused
+      ddclient = 30;
       davfs2 = 31;
       disnix = 33;
       osgi = 34;
@@ -415,6 +416,7 @@
       btsync = 113;
       #minecraft = 114; # unused
       #monetdb = 115; # unused (not packaged), removed 2016-09-19
+      vault = 115;
       #ripped = 116; # unused
       #murmur = 117; # unused
       foundationdb = 118;
@@ -487,7 +489,7 @@
       sabnzbd = 194;
       #grafana = 196; #unused
       #skydns = 197; #unused
-      #ripple-rest = 198; #unused
+      # ripple-rest = 198; # unused, removed 2017-08-12
       #nix-serve = 199; #unused
       #tvheadend = 200; #unused
       uwsgi = 201;
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index 315c33a462c6..48cde2ebbc8a 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -95,7 +95,7 @@ in
       nixosVersionSuffix = mkIf (pathIsDirectory gitRepo) (mkDefault (".git." + gitCommitId));
 
       # Note: code names must only increase in alphabetical order.
-      nixosCodeName = "Hummingbird";
+      nixosCodeName = "Impala";
     };
 
     # Generate /etc/os-release.  See
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index a2add1b3e435..cc7aa519478e 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -43,6 +43,7 @@
   ./hardware/nitrokey.nix
   ./hardware/opengl.nix
   ./hardware/pcmcia.nix
+  ./hardware/raid/hpsa.nix
   ./hardware/usb-wwan.nix
   ./hardware/video/amdgpu.nix
   ./hardware/video/amdgpu-pro.nix
@@ -105,7 +106,6 @@
   ./programs/venus.nix
   ./programs/vim.nix
   ./programs/wireshark.nix
-  ./programs/wvdial.nix
   ./programs/xfs_quota.nix
   ./programs/xonsh.nix
   ./programs/zsh/oh-my-zsh.nix
@@ -116,11 +116,11 @@
   ./security/apparmor.nix
   ./security/apparmor-suid.nix
   ./security/audit.nix
+  ./security/auditd.nix
   ./security/ca.nix
   ./security/chromium-suid-sandbox.nix
   ./security/dhparams.nix
   ./security/duosec.nix
-  ./security/grsecurity.nix
   ./security/hidepid.nix
   ./security/lock-kernel-modules.nix
   ./security/oath.nix
@@ -165,6 +165,7 @@
   ./services/continuous-integration/buildbot/master.nix
   ./services/continuous-integration/buildbot/worker.nix
   ./services/continuous-integration/buildkite-agent.nix
+  ./services/continuous-integration/hail.nix
   ./services/continuous-integration/hydra/default.nix
   ./services/continuous-integration/gitlab-runner.nix
   ./services/continuous-integration/gocd-agent/default.nix
@@ -185,6 +186,7 @@
   ./services/databases/neo4j.nix
   ./services/databases/openldap.nix
   ./services/databases/opentsdb.nix
+  ./services/databases/postage.nix
   ./services/databases/postgresql.nix
   ./services/databases/redis.nix
   ./services/databases/riak.nix
@@ -223,6 +225,7 @@
   ./services/hardware/brltty.nix
   ./services/hardware/freefall.nix
   ./services/hardware/illum.nix
+  ./services/hardware/interception-tools.nix
   ./services/hardware/irqbalance.nix
   ./services/hardware/nvidia-optimus.nix
   ./services/hardware/pcscd.nix
@@ -242,6 +245,7 @@
   ./services/logging/graylog.nix
   ./services/logging/heartbeat.nix
   ./services/logging/journalbeat.nix
+  ./services/logging/journalwatch.nix
   ./services/logging/klogd.nix
   ./services/logging/logcheck.nix
   ./services/logging/logrotate.nix
@@ -266,6 +270,7 @@
   ./services/mail/rspamd.nix
   ./services/mail/rmilter.nix
   ./services/mail/nullmailer.nix
+  ./services/misc/airsonic.nix
   ./services/misc/apache-kafka.nix
   ./services/misc/autofs.nix
   ./services/misc/autorandr.nix
@@ -285,6 +290,7 @@
   ./services/misc/emby.nix
   ./services/misc/errbot.nix
   ./services/misc/etcd.nix
+  ./services/misc/exhibitor.nix
   ./services/misc/felix.nix
   ./services/misc/folding-at-home.nix
   ./services/misc/fstrim.nix
@@ -321,10 +327,10 @@
   ./services/misc/radarr.nix
   ./services/misc/redmine.nix
   ./services/misc/rippled.nix
-  ./services/misc/ripple-rest.nix
   ./services/misc/ripple-data-api.nix
   ./services/misc/rogue.nix
   ./services/misc/siproxd.nix
+  ./services/misc/snapper.nix
   ./services/misc/sonarr.nix
   ./services/misc/spice-vdagentd.nix
   ./services/misc/ssm-agent.nix
@@ -352,6 +358,7 @@
   ./services/monitoring/munin.nix
   ./services/monitoring/nagios.nix
   ./services/monitoring/netdata.nix
+  ./services/monitoring/osquery.nix
   ./services/monitoring/prometheus/default.nix
   ./services/monitoring/prometheus/alertmanager.nix
   ./services/monitoring/prometheus/blackbox-exporter.nix
@@ -512,7 +519,6 @@
   ./services/networking/teamspeak3.nix
   ./services/networking/tinc.nix
   ./services/networking/tftpd.nix
-  ./services/networking/tlsdated.nix
   ./services/networking/tox-bootstrapd.nix
   ./services/networking/toxvpn.nix
   ./services/networking/tvheadend.nix
@@ -544,7 +550,6 @@
   ./services/security/fail2ban.nix
   ./services/security/fprintd.nix
   ./services/security/fprot.nix
-  ./services/security/frandom.nix
   ./services/security/haka.nix
   ./services/security/haveged.nix
   ./services/security/hologram-server.nix
@@ -553,16 +558,20 @@
   ./services/security/oauth2_proxy.nix
   ./services/security/physlock.nix
   ./services/security/shibboleth-sp.nix
+  ./services/security/sks.nix
   ./services/security/sshguard.nix
   ./services/security/tor.nix
   ./services/security/torify.nix
   ./services/security/torsocks.nix
+  ./services/security/usbguard.nix
+  ./services/security/vault.nix
   ./services/system/cgmanager.nix
   ./services/system/cloud-init.nix
   ./services/system/dbus.nix
   ./services/system/earlyoom.nix
   ./services/system/kerberos.nix
   ./services/system/nscd.nix
+  ./services/system/saslauthd.nix
   ./services/system/uptimed.nix
   ./services/torrent/deluge.nix
   ./services/torrent/flexget.nix
@@ -578,6 +587,7 @@
   ./services/web-apps/frab.nix
   ./services/web-apps/mattermost.nix
   ./services/web-apps/nixbot.nix
+  ./services/web-apps/pgpkeyserver-lite.nix
   ./services/web-apps/piwik.nix
   ./services/web-apps/pump.io.nix
   ./services/web-apps/tt-rss.nix
@@ -620,7 +630,6 @@
   ./services/x11/redshift.nix
   ./services/x11/urxvtd.nix
   ./services/x11/window-managers/awesome.nix
-  #./services/x11/window-managers/compiz.nix
   ./services/x11/window-managers/default.nix
   ./services/x11/window-managers/fluxbox.nix
   ./services/x11/window-managers/icewm.nix
@@ -670,6 +679,7 @@
   ./tasks/cpu-freq.nix
   ./tasks/encrypted-devices.nix
   ./tasks/filesystems.nix
+  ./tasks/filesystems/bcachefs.nix
   ./tasks/filesystems/btrfs.nix
   ./tasks/filesystems/cifs.nix
   ./tasks/filesystems/exfat.nix
diff --git a/nixos/modules/profiles/all-hardware.nix b/nixos/modules/profiles/all-hardware.nix
index 530b2fbffd1c..6e6ae98e19fc 100644
--- a/nixos/modules/profiles/all-hardware.nix
+++ b/nixos/modules/profiles/all-hardware.nix
@@ -41,6 +41,9 @@
 
       # Virtio (QEMU, KVM etc.) support.
       "virtio_net" "virtio_pci" "virtio_blk" "virtio_scsi" "virtio_balloon" "virtio_console"
+      
+      # VMware support.
+      "mptspi" "vmw_balloon" "vmwgfx" "vmw_vmci" "vmw_vsock_vmci_transport" "vmxnet3" "vsock"
 
       # Hyper-V support.
       "hv_storvsc"
diff --git a/nixos/modules/profiles/graphical.nix b/nixos/modules/profiles/graphical.nix
index e23375375188..fe9851e79a6d 100644
--- a/nixos/modules/profiles/graphical.nix
+++ b/nixos/modules/profiles/graphical.nix
@@ -8,7 +8,7 @@
     enable = true;
     displayManager.sddm.enable = true;
     desktopManager.plasma5.enable = true;
-    synaptics.enable = true; # for touchpad support on many laptops
+    libinput.enable = true; # for touchpad support on many laptops
   };
 
   environment.systemPackages = [ pkgs.glxinfo ];
diff --git a/nixos/modules/profiles/hardened.nix b/nixos/modules/profiles/hardened.nix
index 0a0838431da7..0ab210cc4c39 100644
--- a/nixos/modules/profiles/hardened.nix
+++ b/nixos/modules/profiles/hardened.nix
@@ -59,4 +59,10 @@ with lib;
   # the feature at runtime.  Attempting to create a user namespace
   # with unshare will then fail with "no space left on device".
   boot.kernel.sysctl."user.max_user_namespaces" = mkDefault 0;
+
+  # Raise ASLR entropy for 64bit & 32bit, respectively.
+  #
+  # Note: mmap_rnd_compat_bits may not exist on 64bit.
+  boot.kernel.sysctl."vm.mmap_rnd_bits" = mkDefault 32;
+  boot.kernel.sysctl."vm.mmap_rnd_compat_bits" = mkDefault 16;
 }
diff --git a/nixos/modules/profiles/installation-device.nix b/nixos/modules/profiles/installation-device.nix
index a24fa75e01db..8e1482f5533f 100644
--- a/nixos/modules/profiles/installation-device.nix
+++ b/nixos/modules/profiles/installation-device.nix
@@ -28,7 +28,7 @@ with lib;
     services.nixosManual.showManual = true;
 
     # Let the user play Rogue on TTY 8 during the installation.
-    services.rogue.enable = true;
+    #services.rogue.enable = true;
 
     # Disable some other stuff we don't need.
     security.sudo.enable = false;
diff --git a/nixos/modules/programs/gnupg.nix b/nixos/modules/programs/gnupg.nix
index 68adee94f79e..addc9dcca87e 100644
--- a/nixos/modules/programs/gnupg.nix
+++ b/nixos/modules/programs/gnupg.nix
@@ -55,79 +55,24 @@ in
   };
 
   config = mkIf cfg.agent.enable {
-    systemd.user.services.gpg-agent = {
-      serviceConfig = {
-        ExecStart = [
-          ""
-          ("${pkgs.gnupg}/bin/gpg-agent --supervised "
-            + optionalString cfg.agent.enableSSHSupport "--enable-ssh-support")
-        ];
-        ExecReload = "${pkgs.gnupg}/bin/gpgconf --reload gpg-agent";
-      };
-    };
-
     systemd.user.sockets.gpg-agent = {
       wantedBy = [ "sockets.target" ];
-      listenStreams = [ "%t/gnupg/S.gpg-agent" ];
-      socketConfig = {
-        FileDescriptorName = "std";
-        SocketMode = "0600";
-        DirectoryMode = "0700";
-      };
     };
 
     systemd.user.sockets.gpg-agent-ssh = mkIf cfg.agent.enableSSHSupport {
       wantedBy = [ "sockets.target" ];
-      listenStreams = [ "%t/gnupg/S.gpg-agent.ssh" ];
-      socketConfig = {
-        FileDescriptorName = "ssh";
-        Service = "gpg-agent.service";
-        SocketMode = "0600";
-        DirectoryMode = "0700";
-      };
     };
 
     systemd.user.sockets.gpg-agent-extra = mkIf cfg.agent.enableExtraSocket {
       wantedBy = [ "sockets.target" ];
-      listenStreams = [ "%t/gnupg/S.gpg-agent.extra" ];
-      socketConfig = {
-        FileDescriptorName = "extra";
-        Service = "gpg-agent.service";
-        SocketMode = "0600";
-        DirectoryMode = "0700";
-      };
     };
 
     systemd.user.sockets.gpg-agent-browser = mkIf cfg.agent.enableBrowserSocket {
       wantedBy = [ "sockets.target" ];
-      listenStreams = [ "%t/gnupg/S.gpg-agent.browser" ];
-      socketConfig = {
-        FileDescriptorName = "browser";
-        Service = "gpg-agent.service";
-        SocketMode = "0600";
-        DirectoryMode = "0700";
-      };
-    };
-
-    systemd.user.services.dirmngr = {
-      requires = [ "dirmngr.socket" ];
-      after = [ "dirmngr.socket" ];
-      unitConfig = {
-        RefuseManualStart = "true";
-      };
-      serviceConfig = {
-        ExecStart = "${pkgs.gnupg}/bin/dirmngr --supervised";
-        ExecReload = "${pkgs.gnupg}/bin/gpgconf --reload dirmngr";
-      };
     };
 
-    systemd.user.sockets.dirmngr = {
+    systemd.user.sockets.dirmngr = mkIf cfg.dirmngr.enable {
       wantedBy = [ "sockets.target" ];
-      listenStreams = [ "%t/gnupg/S.dirmngr" ];
-      socketConfig = {
-        SocketMode = "0600";
-        DirectoryMode = "0700";
-      };
     };
 
     systemd.packages = [ pkgs.gnupg ];
@@ -147,7 +92,7 @@ in
     '');
 
     assertions = [
-      { assertion = cfg.agent.enableSSHSupport && !config.programs.ssh.startAgent;
+      { assertion = cfg.agent.enableSSHSupport -> !config.programs.ssh.startAgent;
         message = "You can't use ssh-agent and GnuPG agent with SSH support enabled at the same time!";
       }
     ];
diff --git a/nixos/modules/programs/nylas-mail.nix b/nixos/modules/programs/nylas-mail.nix
new file mode 100644
index 000000000000..9a6cf755f2a2
--- /dev/null
+++ b/nixos/modules/programs/nylas-mail.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.nylas-mail;
+  defaultUser = "nylas-mail";
+in {
+  ###### interface
+  options = {
+    services.nylas-mail = {
+
+      enable = mkEnableOption ''
+        nylas-mail - Open-source mail client built on the modern web with Electron, React, and Flux
+      '';
+
+      gnome3-keyring = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Enable gnome3 keyring for nylas-mail.";
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.nylas-mail-bin ];
+
+    services.gnome3.gnome-keyring = mkIf cfg.gnome3-keyring {
+      enable = true;
+    };
+
+  };
+}
diff --git a/nixos/modules/programs/oblogout.nix b/nixos/modules/programs/oblogout.nix
index 79a8ddb7ce37..720c29b1eaee 100644
--- a/nixos/modules/programs/oblogout.nix
+++ b/nixos/modules/programs/oblogout.nix
@@ -27,6 +27,7 @@ in
         type = types.int;
         default = 70;
         description = ''
+          Opacity percentage of Cairo rendered backgrounds.
         '';
       };
 
@@ -34,6 +35,7 @@ in
         type = types.str;
         default = "black";
         description = ''
+          Colour name or hex code (#ffffff) of the background color.
         '';
       };
 
@@ -41,6 +43,9 @@ in
         type = types.str;
         default = "simplistic";
         description = ''
+          Icon theme for the buttons, must be in the themes folder of
+          the package, or in
+          <filename>~/.themes/&lt;name&gt;/oblogout/</filename>.
         '';
       };
 
@@ -48,6 +53,7 @@ in
         type = types.str;
         default =  "cancel, logout, restart, shutdown, suspend, hibernate";
         description = ''
+          List and order of buttons to show.
         '';
       };
 
@@ -55,6 +61,7 @@ in
         type = types.str;
         default =  "Escape";
         description = ''
+          Cancel logout/shutdown shortcut.
         '';
       };
 
@@ -62,6 +69,7 @@ in
         type = types.str;
         default = "S";
         description = ''
+          Shutdown shortcut.
         '';
       };
 
@@ -69,6 +77,7 @@ in
         type = types.str;
         default = "R";
         description = ''
+          Restart shortcut.
         '';
       };
 
@@ -76,6 +85,7 @@ in
         type = types.str;
         default = "U";
         description = ''
+          Suspend shortcut.
         '';
       };
 
@@ -83,6 +93,7 @@ in
         type = types.str;
         default = "L";
         description = ''
+          Logout shortcut.
         '';
       };
 
@@ -90,6 +101,7 @@ in
         type = types.str;
         default = "K";
         description = ''
+          Lock session shortcut.
         '';
       };
 
@@ -97,6 +109,7 @@ in
         type = types.str;
         default =  "H";
         description = ''
+          Hibernate shortcut.
         '';
       };
 
@@ -104,6 +117,7 @@ in
         type = types.str;
         default = "openbox --exit";
         description = ''
+          Command to logout.
         '';
       };
 
@@ -111,6 +125,7 @@ in
         type = types.str;
         default = "";
         description = ''
+          Command to lock screen.
         '';
       };
 
@@ -118,6 +133,7 @@ in
         type = types.str;
         default = "";
         description = ''
+          Command to switch user.
         '';
       };
     };
diff --git a/nixos/modules/programs/qt5ct.nix b/nixos/modules/programs/qt5ct.nix
index 550634e65be9..aeb7fc508495 100644
--- a/nixos/modules/programs/qt5ct.nix
+++ b/nixos/modules/programs/qt5ct.nix
@@ -26,6 +26,6 @@ with lib;
   ###### implementation
   config = mkIf config.programs.qt5ct.enable {
     environment.variables.QT_QPA_PLATFORMTHEME = "qt5ct";
-    environment.systemPackages = [ pkgs.qt5ct ];
+    environment.systemPackages = with pkgs; [ qt5ct libsForQt5.qtstyleplugins ];
   };
 }
diff --git a/nixos/modules/programs/thefuck.nix b/nixos/modules/programs/thefuck.nix
index 433a0ca95fef..eb913477cf05 100644
--- a/nixos/modules/programs/thefuck.nix
+++ b/nixos/modules/programs/thefuck.nix
@@ -3,7 +3,12 @@
 with lib;
 
 let
-  cfg = config.programs.thefuck;
+  prg = config.programs;
+  cfg = prg.thefuck;
+
+  initScript = ''
+    eval $(${pkgs.thefuck}/bin/thefuck --alias ${cfg.alias})
+  '';
 in
   {
     options = {
@@ -24,8 +29,11 @@ in
 
     config = mkIf cfg.enable {
       environment.systemPackages = with pkgs; [ thefuck ];
-      environment.shellInit = ''
-        eval $(${pkgs.thefuck}/bin/thefuck --alias ${cfg.alias})
+      environment.shellInit = initScript;
+
+      programs.zsh.shellInit = mkIf prg.zsh.enable initScript;
+      programs.fish.shellInit = mkIf prg.fish.enable ''
+        ${pkgs.thefuck}/bin/thefuck --alias | source
       '';
     };
   }
diff --git a/nixos/modules/programs/wvdial.nix b/nixos/modules/programs/wvdial.nix
deleted file mode 100644
index 1ed929ed4afa..000000000000
--- a/nixos/modules/programs/wvdial.nix
+++ /dev/null
@@ -1,71 +0,0 @@
-# Global configuration for wvdial.
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  configFile = ''
-    [Dialer Defaults]
-    PPPD PATH = ${pkgs.ppp}/sbin/pppd
-    ${config.environment.wvdial.dialerDefaults}
-  '';
-
-  cfg = config.environment.wvdial;
-
-in
-{
-  ###### interface
-
-  options = {
-
-    environment.wvdial = {
-
-      dialerDefaults = mkOption {
-        default = "";
-        type = types.str;
-        example = ''Init1 = AT+CGDCONT=1,"IP","internet.t-mobile"'';
-        description = ''
-          Contents of the "Dialer Defaults" section of
-          <filename>/etc/wvdial.conf</filename>.
-        '';
-      };
-
-      pppDefaults = mkOption {
-        default = ''
-          noipdefault
-          usepeerdns
-          defaultroute
-          persist
-          noauth
-        '';
-        type = types.str;
-        description = "Default ppp settings for wvdial.";
-      };
-
-    };
-
-  };
-
-  ###### implementation
-
-  config = mkIf (cfg.dialerDefaults != "") {
-
-    environment = {
-
-      etc =
-      [
-        { source = pkgs.writeText "wvdial.conf" configFile;
-          target = "wvdial.conf";
-        }
-        { source = pkgs.writeText "wvdial" cfg.pppDefaults;
-          target = "ppp/peers/wvdial";
-        }
-      ];
-
-    };
-
-  };
-
-}
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.nix b/nixos/modules/programs/zsh/oh-my-zsh.nix
index 446c05da39d0..9077643c4440 100644
--- a/nixos/modules/programs/zsh/oh-my-zsh.nix
+++ b/nixos/modules/programs/zsh/oh-my-zsh.nix
@@ -15,6 +15,16 @@ in
           '';
         };
 
+        package = mkOption {
+          default = pkgs.oh-my-zsh;
+          defaultText = "pkgs.oh-my-zsh";
+          description = ''
+            Package to install for `oh-my-zsh` usage.
+          '';
+
+          type = types.package;
+        };
+
         plugins = mkOption {
           default = [];
           type = types.listOf(types.str);
@@ -42,11 +52,15 @@ in
     };
 
     config = mkIf cfg.enable {
-      environment.systemPackages = with pkgs; [ oh-my-zsh ];
 
-      programs.zsh.interactiveShellInit = with pkgs; with builtins; ''
+      # Prevent zsh from overwriting oh-my-zsh's prompt
+      programs.zsh.promptInit = mkDefault "";
+
+      environment.systemPackages = [ cfg.package ];
+
+      programs.zsh.interactiveShellInit = with builtins; ''
         # oh-my-zsh configuration generated by NixOS
-        export ZSH=${oh-my-zsh}/share/oh-my-zsh
+        export ZSH=${cfg.package}/share/oh-my-zsh
 
         ${optionalString (length(cfg.plugins) > 0)
           "plugins=(${concatStringsSep " " cfg.plugins})"
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index b276bf9bb73c..615e54c326b7 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -97,45 +97,6 @@ in
 
   config = mkIf cfg.enable {
 
-    programs.zsh = {
-
-      shellInit = ''
-        . ${config.system.build.setEnvironment}
-
-        ${cfge.shellInit}
-      '';
-
-      loginShellInit = cfge.loginShellInit;
-
-      interactiveShellInit = ''
-        # history defaults
-        SAVEHIST=2000
-        HISTSIZE=2000
-        HISTFILE=$HOME/.zsh_history
-
-        setopt HIST_IGNORE_DUPS SHARE_HISTORY HIST_FCNTL_LOCK
-
-        # Tell zsh how to find installed completions
-        for p in ''${(z)NIX_PROFILES}; do
-          fpath+=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions)
-        done
-
-        ${if cfg.enableCompletion then "autoload -U compinit && compinit" else ""}
-
-        ${optionalString (cfg.enableAutosuggestions)
-          "source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh"
-        }
-
-        ${zshAliases}
-        ${cfg.promptInit}
-
-        ${cfge.interactiveShellInit}
-
-        HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help"
-      '';
-
-    };
-
     environment.etc."zshenv".text =
       ''
         # /etc/zshenv: DO NOT EDIT -- this file has been generated automatically.
@@ -146,6 +107,10 @@ in
         if [ -n "$__ETC_ZSHENV_SOURCED" ]; then return; fi
         export __ETC_ZSHENV_SOURCED=1
 
+        . ${config.system.build.setEnvironment}
+
+        ${cfge.shellInit}
+
         ${cfg.shellInit}
 
         # Read system-wide modifications.
@@ -163,6 +128,8 @@ in
         if [ -n "$__ETC_ZPROFILE_SOURCED" ]; then return; fi
         __ETC_ZPROFILE_SOURCED=1
 
+        ${cfge.loginShellInit}
+
         ${cfg.loginShellInit}
 
         # Read system-wide modifications.
@@ -182,8 +149,34 @@ in
 
         . /etc/zinputrc
 
+        # history defaults
+        SAVEHIST=2000
+        HISTSIZE=2000
+        HISTFILE=$HOME/.zsh_history
+
+        setopt HIST_IGNORE_DUPS SHARE_HISTORY HIST_FCNTL_LOCK
+
+        HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help"
+
+        # Tell zsh how to find installed completions
+        for p in ''${(z)NIX_PROFILES}; do
+          fpath+=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions)
+        done
+
+        ${optionalString cfg.enableCompletion "autoload -U compinit && compinit"}
+
+        ${optionalString (cfg.enableAutosuggestions)
+          "source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh"
+        }
+
+        ${cfge.interactiveShellInit}
+
         ${cfg.interactiveShellInit}
 
+        ${zshAliases}
+
+        ${cfg.promptInit}
+
         # Read system-wide modifications.
         if test -f /etc/zshrc.local; then
           . /etc/zshrc.local
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index c3fb5758edeb..fcf4c32d2778 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -1,4 +1,4 @@
-{ lib, ... }:
+{ lib, pkgs, ... }:
 
 with lib;
 
@@ -14,17 +14,23 @@ with lib;
     (mkRenamedOptionModule [ "networking" "enableRT73Firmware" ] [ "networking" "enableRalinkFirmware" ])
 
     (mkRenamedOptionModule [ "services" "cadvisor" "host" ] [ "services" "cadvisor" "listenAddress" ])
+    (mkChangedOptionModule [ "services" "printing" "gutenprint" ] [ "services" "printing" "drivers" ]
+      (config:
+        let enabled = getAttrFromPath [ "services" "printing" "gutenprint" ] config;
+        in if enabled then [ pkgs.gutenprint ] else [ ]))
     (mkRenamedOptionModule [ "services" "elasticsearch" "host" ] [ "services" "elasticsearch" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "graphite" "api" "host" ] [ "services" "graphite" "api" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "graphite" "web" "host" ] [ "services" "graphite" "web" "listenAddress" ])
-    (mkRenamedOptionModule [ "services" "logstash" "address" ] [ "services" "logstash" "listenAddress" ])
+    (mkRenamedOptionModule [ "services" "i2pd" "extIp" ] [ "services" "i2pd" "address" ])
     (mkRenamedOptionModule [ "services" "kibana" "host" ] [ "services" "kibana" "listenAddress" ])
+    (mkRenamedOptionModule [ "services" "logstash" "address" ] [ "services" "logstash" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "mpd" "network" "host" ] [ "services" "mpd" "network" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "neo4j" "host" ] [ "services" "neo4j" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "shout" "host" ] [ "services" "shout" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "sslh" "host" ] [ "services" "sslh" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "statsd" "host" ] [ "services" "statsd" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "subsonic" "host" ] [ "services" "subsonic" "listenAddress" ])
+    (mkRenamedOptionModule [ "services" "tor" "relay" "portSpec" ] [ "services" "tor" "relay" "port" ])
     (mkRenamedOptionModule [ "jobs" ] [ "systemd" "services" ])
 
     (mkRenamedOptionModule [ "services" "gitlab" "stateDir" ] [ "services" "gitlab" "statePath" ])
@@ -118,26 +124,6 @@ with lib;
     (mkRenamedOptionModule [ "services" "iodined" "extraConfig" ] [ "services" "iodine" "server" "extraConfig" ])
     (mkRemovedOptionModule [ "services" "iodined" "client" ] "")
 
-    # Grsecurity
-    (mkRemovedOptionModule [ "security" "grsecurity" "kernelPatch" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "mode" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "priority" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "system" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "virtualisationConfig" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "hardwareVirtualisation" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "virtualisationSoftware" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "sysctl" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "denyChrootChmod" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "denyChrootCaps" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "denyUSB" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "restrictProc" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "restrictProcWithGroup" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "unrestrictProcGid" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "disableRBAC" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "disableSimultConnect" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "verboseVersion" ] "")
-    (mkRemovedOptionModule [ "security" "grsecurity" "config" "kernelExtraConfig" ] "")
-
     # Unity3D
     (mkRenamedOptionModule [ "programs" "unity3d" "enable" ] [ "security" "chromiumSuidSandbox" "enable" ])
 
@@ -195,6 +181,8 @@ with lib;
     (mkRemovedOptionModule [ "services" "openvpn" "enable" ] "")
     (mkRemovedOptionModule [ "services" "printing" "cupsFilesConf" ] "")
     (mkRemovedOptionModule [ "services" "printing" "cupsdConf" ] "")
+    (mkRemovedOptionModule [ "services" "tor" "relay" "isBridge" ] "Use services.tor.relay.role instead.")
+    (mkRemovedOptionModule [ "services" "tor" "relay" "isExit" ] "Use services.tor.relay.role instead.")
     (mkRemovedOptionModule [ "services" "xserver" "startGnuPGAgent" ]
       "See the 16.09 release notes for more information.")
     (mkRemovedOptionModule [ "services" "phpfpm" "phpIni" ] "")
@@ -204,6 +192,7 @@ with lib;
       "Set the option `services.xserver.displayManager.sddm.package' instead.")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "forceAutohint" ] "")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ] "")
+    (mkRemovedOptionModule [ "boot" "zfs" "enableUnstable" ] "0.7.0 is now the default")
 
     # ZSH
     (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
diff --git a/nixos/modules/security/auditd.nix b/nixos/modules/security/auditd.nix
new file mode 100644
index 000000000000..6abac244dac2
--- /dev/null
+++ b/nixos/modules/security/auditd.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  options.security.auditd.enable = mkEnableOption "the Linux Audit daemon";
+
+  config = mkIf config.security.auditd.enable {
+    systemd.services.auditd = {
+      description = "Linux Audit daemon";
+      wantedBy = [ "basic.target" ];
+
+      unitConfig = {
+        ConditionVirtualization = "!container";
+        ConditionSecurity = [ "audit" ];
+        DefaultDependencies = false;
+      };
+
+      path = [ pkgs.audit ];
+
+      serviceConfig = {
+        ExecStartPre="${pkgs.coreutils}/bin/mkdir -p /var/log/audit";
+        ExecStart = "${pkgs.audit}/bin/auditd -l -n -s nochange";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/security/chromium-suid-sandbox.nix b/nixos/modules/security/chromium-suid-sandbox.nix
index 0458ffb6c46c..be6acb3f1f53 100644
--- a/nixos/modules/security/chromium-suid-sandbox.nix
+++ b/nixos/modules/security/chromium-suid-sandbox.nix
@@ -19,9 +19,6 @@ in
 
       Also, if the URL chrome://sandbox tells you that "You are not adequately
       sandboxed!", turning this on might resolve the issue.
-
-      Finally, if you have <option>security.grsecurity</option> enabled and you
-      use Chromium, you probably need this.
     '';
   };
 
diff --git a/nixos/modules/security/grsecurity.nix b/nixos/modules/security/grsecurity.nix
deleted file mode 100644
index d23c7f2e86de..000000000000
--- a/nixos/modules/security/grsecurity.nix
+++ /dev/null
@@ -1,169 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.security.grsecurity;
-  grsecLockPath = "/proc/sys/kernel/grsecurity/grsec_lock";
-
-  # Ascertain whether NixOS container support is required
-  containerSupportRequired =
-    config.boot.enableContainers && config.containers != {};
-in
-
-{
-  meta = {
-    maintainers = with maintainers; [ ];
-    doc = ./grsecurity.xml;
-  };
-
-  options.security.grsecurity = {
-
-    enable = mkOption {
-      type = types.bool;
-      default = false;
-      description = ''
-        Enable grsecurity/PaX.
-      '';
-    };
-
-    lockTunables = mkOption {
-      type = types.bool;
-      default = true;
-      description = ''
-        Whether to automatically lock grsecurity tunables
-        (<option>boot.kernel.sysctl."kernel.grsecurity.*"</option>).  Disable
-        this to allow runtime configuration of grsecurity features.  Activate
-        the <literal>grsec-lock</literal> service unit to prevent further
-        configuration until the next reboot.
-      '';
-    };
-
-    disableEfiRuntimeServices = mkOption {
-      type = types.bool;
-      default = true;
-      description = ''
-        Whether to disable access to EFI runtime services.  Enabling EFI runtime
-        services creates a venue for code injection attacks on the kernel and
-        should be disabled if at all possible.  Changing this option enters into
-        effect upon reboot.
-      '';
-    };
-
-  };
-
-  config = mkIf cfg.enable {
-
-    boot.kernelPackages = mkForce pkgs.linuxPackages_grsec_nixos;
-
-    boot.kernelParams = [ "grsec_sysfs_restrict=0" ]
-      ++ optional cfg.disableEfiRuntimeServices "noefi";
-
-    nixpkgs.config.grsecurity = true;
-
-    # Install PaX related utillities into the system profile.
-    environment.systemPackages = with pkgs; [ gradm paxctl pax-utils ];
-
-    # Install rules for the grsec device node
-    services.udev.packages = [ pkgs.gradm ];
-
-    # This service unit is responsible for locking the grsecurity tunables.  The
-    # unit is always defined, but only activated on bootup if lockTunables is
-    # toggled.  When lockTunables is toggled, failure to activate the unit will
-    # enter emergency mode.  The intent is to make it difficult to silently
-    # enter multi-user mode without having locked the tunables.  Some effort is
-    # made to ensure that starting the unit is an idempotent operation.
-    systemd.services.grsec-lock = {
-      description = "Lock grsecurity tunables";
-
-      wantedBy = optional cfg.lockTunables "multi-user.target";
-
-      wants = [ "local-fs.target" "systemd-sysctl.service" ];
-      after = [ "local-fs.target" "systemd-sysctl.service" ];
-      conflicts = [ "shutdown.target" ];
-
-      restartIfChanged = false;
-
-      script = ''
-        if ${pkgs.gnugrep}/bin/grep -Fq 0 ${grsecLockPath} ; then
-          echo -n 1 > ${grsecLockPath}
-        fi
-      '';
-
-      unitConfig = {
-        ConditionPathIsReadWrite = grsecLockPath;
-        DefaultDependencies = false;
-      } // optionalAttrs cfg.lockTunables {
-        OnFailure = "emergency.target";
-      };
-
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
-      };
-    };
-
-    # Configure system tunables
-    boot.kernel.sysctl = {
-      # Read-only under grsecurity
-      "kernel.kptr_restrict" = mkForce null;
-
-      # All grsec tunables default to off, those not enabled below are
-      # *disabled*.  We use mkDefault to allow expert users to override
-      # our choices, but use mkForce where tunables would outright
-      # conflict with other settings.
-
-      # Enable all chroot restrictions by default (overwritten as
-      # necessary below)
-      "kernel.grsecurity.chroot_caps" = mkDefault 1;
-      "kernel.grsecurity.chroot_deny_bad_rename" = mkDefault 1;
-      "kernel.grsecurity.chroot_deny_chmod" = mkDefault 1;
-      "kernel.grsecurity.chroot_deny_chroot" = mkDefault 1;
-      "kernel.grsecurity.chroot_deny_fchdir" = mkDefault 1;
-      "kernel.grsecurity.chroot_deny_mknod" = mkDefault 1;
-      "kernel.grsecurity.chroot_deny_mount" = mkDefault 1;
-      "kernel.grsecurity.chroot_deny_pivot" = mkDefault 1;
-      "kernel.grsecurity.chroot_deny_shmat" = mkDefault 1;
-      "kernel.grsecurity.chroot_deny_sysctl" = mkDefault 1;
-      "kernel.grsecurity.chroot_deny_unix" = mkDefault 1;
-      "kernel.grsecurity.chroot_enforce_chdir" = mkDefault 1;
-      "kernel.grsecurity.chroot_findtask" = mkDefault 1;
-      "kernel.grsecurity.chroot_restrict_nice" = mkDefault 1;
-
-      # Enable various grsec protections
-      "kernel.grsecurity.consistent_setxid" = mkDefault 1;
-      "kernel.grsecurity.deter_bruteforce" = mkDefault 1;
-      "kernel.grsecurity.fifo_restrictions" = mkDefault 1;
-      "kernel.grsecurity.harden_ipc" = mkDefault 1;
-      "kernel.grsecurity.harden_ptrace" = mkDefault 1;
-      "kernel.grsecurity.harden_tty" = mkDefault 1;
-      "kernel.grsecurity.ip_blackhole" = mkDefault 1;
-      "kernel.grsecurity.linking_restrictions" = mkDefault 1;
-      "kernel.grsecurity.ptrace_readexec" = mkDefault 1;
-
-      # Enable auditing
-      "kernel.grsecurity.audit_ptrace" = mkDefault 1;
-      "kernel.grsecurity.forkfail_logging" = mkDefault 1;
-      "kernel.grsecurity.rwxmap_logging" = mkDefault 1;
-      "kernel.grsecurity.signal_logging" = mkDefault 1;
-      "kernel.grsecurity.timechange_logging" = mkDefault 1;
-    } // optionalAttrs config.nix.useSandbox {
-      # chroot(2) restrictions that conflict with sandboxed Nix builds
-      "kernel.grsecurity.chroot_caps" = mkForce 0;
-      "kernel.grsecurity.chroot_deny_chmod" = mkForce 0;
-      "kernel.grsecurity.chroot_deny_chroot" = mkForce 0;
-      "kernel.grsecurity.chroot_deny_mount" = mkForce 0;
-      "kernel.grsecurity.chroot_deny_pivot" = mkForce 0;
-    } // optionalAttrs containerSupportRequired {
-      # chroot(2) restrictions that conflict with NixOS lightweight containers
-      "kernel.grsecurity.chroot_caps" = mkForce 0;
-      "kernel.grsecurity.chroot_deny_chmod" = mkForce 0;
-      "kernel.grsecurity.chroot_deny_mount" = mkForce 0;
-      "kernel.grsecurity.chroot_restrict_nice" = mkForce 0;
-      # Disable privileged IO by default, unless X is enabled
-    } // optionalAttrs (!config.services.xserver.enable) {
-      "kernel.grsecurity.disable_priv_io" = mkDefault 1;
-    };
-
-  };
-}
diff --git a/nixos/modules/security/grsecurity.xml b/nixos/modules/security/grsecurity.xml
deleted file mode 100644
index 0a884b3f9b55..000000000000
--- a/nixos/modules/security/grsecurity.xml
+++ /dev/null
@@ -1,385 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-         xmlns:xlink="http://www.w3.org/1999/xlink"
-         xmlns:xi="http://www.w3.org/2001/XInclude"
-         version="5.0"
-         xml:id="sec-grsecurity">
-
-  <title>Grsecurity/PaX</title>
-
-  <para>
-    Grsecurity/PaX is a set of patches against the Linux kernel that
-    implements an extensive suite of
-    <link xlink:href="https://grsecurity.net/features.php">features</link>
-    designed to increase the difficulty of exploiting kernel and
-    application bugs.
-  </para>
-
-  <para>
-    The NixOS grsecurity/PaX module is designed with casual users in mind and is
-    intended to be compatible with normal desktop usage, without
-    <emphasis>unnecessarily</emphasis> compromising security.  The
-    following sections describe the configuration and administration of
-    a grsecurity/PaX enabled NixOS system.  For more comprehensive
-    coverage, please refer to the
-    <link xlink:href="https://en.wikibooks.org/wiki/Grsecurity">grsecurity wikibook</link>
-    and the
-    <link xlink:href="https://wiki.archlinux.org/index.php/Grsecurity">Arch
-    Linux wiki page on grsecurity</link>.
-
-    <warning><para>Upstream has ceased free support for grsecurity/PaX.  See
-    <link xlink:href="https://grsecurity.net/passing_the_baton.php">
-    the announcement</link> for more information.  Consequently, NixOS
-    support for grsecurity/PaX also must cease.  Enabling this module will
-    result in a build error.</para></warning>
-    <note><para>We standardise on a desktop oriented configuration primarily due
-    to lack of resources.  The grsecurity/PaX configuration state space is huge
-    and each configuration requires quite a bit of testing to ensure that the
-    resulting packages work as advertised.  Defining additional package sets
-    would likely result in a large number of functionally broken packages, to
-    nobody's benefit.</para></note>
-  </para>
-
-  <sect1 xml:id="sec-grsec-enable"><title>Enabling grsecurity/PaX</title>
-
-  <para>
-    To make use of grsecurity/PaX on NixOS, add the following to your
-    <filename>configuration.nix</filename>:
-    <programlisting>
-      security.grsecurity.enable = true;
-    </programlisting>
-    followed by
-    <programlisting>
-      # nixos-rebuild boot
-      # reboot
-    </programlisting>
-    <note><para>
-      Enabling the grsecurity module overrides
-      <option>boot.kernelPackages</option>, to reduce the risk of
-      misconfiguration.  <xref linkend="sec-grsec-custom-kernel" />
-      describes how to use a custom kernel package set.
-    </para></note>
-
-    For most users, further configuration should be unnecessary.  All users
-    are encouraged to look over <xref linkend="sec-grsec-security" /> before
-    using the system, however.  If you experience problems, please refer to
-    <xref linkend="sec-grsec-issues" />.
-  </para>
-
-  <para>
-    Once booted into the new system, you can optionally use
-    <command>paxtest</command> to exercise various PaX features:
-    <screen><![CDATA[
-    # nix-shell -p paxtest --command 'paxtest blackhat'
-    Executable anonymous mapping             : Killed
-    Executable bss                           : Killed
-    # ... remaining output truncated for brevity
-    ]]></screen>
-  </para>
-
-  </sect1>
-
-  <sect1 xml:id="sec-grsec-declarative-tuning"><title>Declarative tuning</title>
-
-  <para>
-    The default configuration mode is strictly declarative.  Some features
-    simply cannot be changed at all after boot, while others are locked once the
-    system is up and running.  Moreover, changes to the configuration enter
-    into effect only upon booting into the new system.
-  </para>
-
-  <para>
-    The NixOS module exposes a limited number of options for tuning the behavior
-    of grsecurity/PaX.  These are options thought to be of particular interest
-    to most users.  For experts, further tuning is possible via
-    <option>boot.kernelParams</option> (see
-    <xref linkend="sec-grsec-kernel-params" />) and
-    <option>boot.kernel.sysctl."kernel.grsecurity.*"</option> (the wikibook
-    contains an <link xlink:href="https://en.wikibooks.org/wiki/Grsecurity/Appendix/Sysctl_Options">
-    exhaustive listing of grsecurity sysctl tunables</link>).
-  </para>
-
-  </sect1>
-
-  <sect1 xml:id="sec-grsec-manual-tuning"><title>Manual tuning</title>
-
-  <para>
-    To permit manual tuning of grsecurity runtime parameters, set:
-    <programlisting>
-      security.grsecurity.lockTunables = false;
-    </programlisting>
-    Once booted into this system, grsecurity features that have a corresponding
-    sysctl tunable can be changed without rebooting, either by switching into
-    a new system profile or via the <command>sysctl</command> utility.
-  </para>
-
-  <para>
-    To lock all grsecurity tunables until the next boot, do:
-    <screen>
-      # systemctl start grsec-lock
-    </screen>
-  </para>
-
-  </sect1>
-
-  <sect1 xml:id="sec-grsec-security"><title>Security considerations</title>
-
-  <para>
-    The NixOS kernel is built using upstream's recommended settings for a
-    desktop deployment that generally favours security over performance.  This
-    section details deviations from upstream's recommendations that may
-    compromise security.
-
-    <warning><para>There may be additional problems not covered here!</para>
-    </warning>
-  </para>
-
-  <itemizedlist>
-
-    <listitem><para>
-      The following hardening features are disabled in the NixOS kernel:
-      <itemizedlist>
-        <listitem><para>Kernel symbol hiding: rendered useless by redistributing
-        kernel objects.</para></listitem>
-
-        <listitem><para>Randomization of kernel structures: rendered useless by
-        redistributing kernel objects.</para></listitem>
-
-        <listitem><para>TCP simultaneous OPEN connection is permitted: breaking
-        strict TCP conformance is inappropriate for a general purpose kernel.
-        The trade-off is that an attacker may be able to deny outgoing
-        connections if they are able to guess the source port allocated by your
-        OS for that connection <emphasis>and</emphasis> also manage to initiate
-        a TCP simultaneous OPEN on that port before the connection is actually
-        established.</para></listitem>
-
-        <listitem><para>Trusted path execution: a desirable feature, but
-        requires some more work to operate smoothly on NixOS.</para></listitem>
-      </itemizedlist>
-    </para></listitem>
-
-    <listitem><para>
-      The NixOS module conditionally weakens <command>chroot</command>
-      restrictions to accommodate NixOS lightweight containers and sandboxed Nix
-      builds.  This can be problematic if the deployment also runs privileged
-      network facing processes that <emphasis>rely</emphasis> on
-      <command>chroot</command> for isolation.
-    </para></listitem>
-
-    <listitem><para>
-      The NixOS kernel is patched to allow usermode helpers from anywhere in the
-      Nix store.  A usermode helper is an executable called by the kernel in
-      certain circumstances, e.g., <command>modprobe</command>.  Vanilla
-      grsecurity only allows usermode helpers from paths typically owned by the
-      super user.  The NixOS kernel allows an attacker to inject malicious code
-      into the Nix store which could then be executed by the kernel as a
-      usermode helper.
-    </para></listitem>
-
-    <listitem><para>
-      The following features are disabled because they overlap with
-      vanilla kernel mechanisms:
-
-      <itemizedlist>
-        <listitem><para><filename class="directory">/proc</filename> hardening:
-        use <option>security.hideProcessInformation</option> instead.  This
-        trades weaker protection for greater compatibility.
-        </para></listitem>
-
-        <listitem><para><command>dmesg</command> restrictions:
-        use <option>boot.kernel.sysctl."kernel.dmesg_restrict"</option> instead
-        </para></listitem>
-      </itemizedlist>
-    </para></listitem>
-
-  </itemizedlist>
-
-  </sect1>
-
-  <sect1 xml:id="sec-grsec-custom-kernel"><title>Using a custom grsecurity/PaX kernel</title>
-
-  <para>
-    The NixOS kernel is likely to be either too permissive or too restrictive
-    for many deployment scenarios.  In addition to producing a kernel more
-    suitable for a particular deployment, a custom kernel may improve security
-    by depriving an attacker the ability to study the kernel object code, adding
-    yet more guesswork to successfully carry out certain exploits.
-  </para>
-
-  <para>
-    To build a custom kernel using upstream's recommended settings for server
-    deployments, while still using the NixOS module:
-    <programlisting>
-      nixpkgs.config.packageOverrides = super: {
-        linux_grsec_nixos = super.linux_grsec_nixos.override {
-          extraConfig = ''
-            GRKERNSEC_CONFIG_AUTO y
-            GRKERNSEC_CONFIG_SERVER y
-            GRKERNSEC_CONFIG_SECURITY y
-          '';
-        };
-      };
-    </programlisting>
-  </para>
-
-  <para>
-    The grsecurity/PaX wikibook provides an exhaustive listing of
-    <link xlink:href="https://en.wikibooks.org/wiki/Grsecurity/Appendix/Grsecurity_and_PaX_Configuration_Options">kernel configuration options</link>.
-  </para>
-
-  <para>
-    The NixOS module makes several assumptions about the kernel and so
-    may be incompatible with your customised kernel. Currently, the only way
-    to work around these incompatibilities is to eschew the NixOS
-    module.
-  </para>
-
-  <para>
-    If not using the NixOS module, a custom grsecurity package set can
-    be specified inline instead, as in
-    <programlisting>
-      boot.kernelPackages =
-        let
-          kernel = pkgs.linux_grsec_nixos.override {
-            extraConfig = /* as above */;
-          };
-          self = pkgs.linuxPackagesFor kernel self;
-        in self;
-    </programlisting>
-  </para>
-
-  </sect1>
-
-  <sect1 xml:id="sec-grsec-pax-flags"><title>Per-executable PaX flags</title>
-
-  <para>
-    Manual tuning of per-file PaX flags for executables in the Nix store is
-    impossible on a properly configured system.  If a package in Nixpkgs fails
-    due to PaX, that is a bug in the package recipe and should be reported to
-    the maintainer (including relevant <command>dmesg</command> output).
-  </para>
-
-  <para>
-    For executables installed outside of the Nix store, PaX flags can be set
-    using the <command>paxctl</command> utility:
-    <programlisting>
-      paxctl -czem <replaceable>foo</replaceable>
-    </programlisting>
-
-    <warning>
-      <para><command>paxctl</command> overwrites files in-place.</para>
-    </warning>
-
-    Equivalently, on file systems that support extended attributes:
-    <programlisting>
-      setfattr -n user.pax.flags -v em <replaceable>foo</replaceable>
-    </programlisting>
-
-    <!-- TODO: PaX flags via RBAC policy -->
-  </para>
-
-  </sect1>
-
-  <sect1 xml:id="sec-grsec-issues"><title>Issues and work-arounds</title>
-
-  <itemizedlist>
-    <listitem><para>User namespaces require <literal>CAP_SYS_ADMIN</literal>:
-    consequently, unprivileged namespaces are unsupported. Applications that
-    rely on namespaces for sandboxing must use a privileged helper. For chromium
-    there is <option>security.chromiumSuidSandbox.enable</option>.</para></listitem>
-
-    <listitem><para>Access to EFI runtime services is disabled by default:
-    this plugs a potential code injection attack vector; use
-    <option>security.grsecurity.disableEfiRuntimeServices</option> to override
-    this behavior.</para></listitem>
-
-    <listitem><para>User initiated autoloading of modules (e.g., when
-    using fuse or loop devices) is disallowed; either load requisite modules
-    as root or add them to <option>boot.kernelModules</option>.</para></listitem>
-
-    <listitem><para>Virtualization: KVM is the preferred virtualization
-    solution. Xen, Virtualbox, and VMWare are
-    <emphasis>unsupported</emphasis> and most likely require a custom kernel.
-    </para></listitem>
-
-    <listitem><para>
-      Attaching <command>gdb</command> to a running process is disallowed by
-      default: unprivileged users can only ptrace processes that are children of
-      the ptracing process.  To relax this restriction, set
-      <programlisting>
-        boot.kernel.sysctl."kernel.grsecurity.harden_ptrace" = 0;
-      </programlisting>
-    </para></listitem>
-
-    <listitem><para>
-      Overflows in boot critical code (e.g., the root filesystem module) can
-      render the system unbootable.  Work around by setting
-      <programlisting>
-        boot.kernelParams = [ "pax_size_overflow_report_only" ];
-      </programlisting>
-    </para></listitem>
-
-    <listitem><para>
-      The <citerefentry><refentrytitle>modify_ldt
-      </refentrytitle><manvolnum>2</manvolnum></citerefentry> syscall is disabled
-      by default.  This restriction can interfere with programs designed to run
-      legacy 16-bit or segmented 32-bit code.  To support applications that rely
-      on this syscall, set
-      <programlisting>
-        boot.kernel.sysctl."kernel.modify_ldt" = 1;
-      </programlisting>
-    </para></listitem>
-
-    <listitem><para>
-      The gitlab service (<xref linkend="module-services-gitlab" />)
-      requires a variant of the <literal>ruby</literal> interpreter
-      built without `mprotect()` hardening, as in
-      <programlisting>
-        services.gitlab.packages.gitlab = pkgs.gitlab.override {
-          ruby = pkgs.ruby.overrideAttrs (attrs: {
-            postFixup = "paxmark m $out/bin/ruby";
-          });
-        };
-      </programlisting>
-    </para></listitem>
-
-  </itemizedlist>
-
-  </sect1>
-
-  <sect1 xml:id="sec-grsec-kernel-params"><title>Grsecurity/PaX kernel parameters</title>
-
-  <para>
-    The NixOS kernel supports the following kernel command line parameters:
-    <itemizedlist>
-      <listitem><para>
-        <literal>pax_nouderef</literal>: disable UDEREF (separate kernel and
-        user address spaces).
-      </para></listitem>
-
-      <listitem><para>
-        <literal>pax_weakuderef</literal>: enable a faster but
-        weaker variant of UDEREF on 64-bit processors with PCID support
-        (check <code>grep pcid /proc/cpuinfo</code>).
-      </para></listitem>
-
-      <listitem><para>
-        <literal>pax_sanitize_slab={off|fast|full}</literal>: control kernel
-        slab object sanitization. Defaults to <literal>fast</literal>
-      </para></listitem>
-
-      <listitem><para>
-        <literal>pax_size_overflow_report_only</literal>: log size overflow
-        violations but leave the violating task running
-      </para></listitem>
-
-      <listitem><para>
-        <literal>grsec_sysfs_restrict=[0|1]</literal>: toggle sysfs
-        restrictions. The NixOS module sets this to <literal>0</literal>
-        for systemd compatibility
-      </para></listitem>
-    </itemizedlist>
-  </para>
-
-  </sect1>
-
-</chapter>
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 5632500df2e0..ede4ace5ed03 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -281,7 +281,7 @@ let
                 "auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so"}
               ${optionalString cfg.enableKwallet
                 ("auth optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
-                 " kwalletd=${pkgs.libsForQt5.kwallet}/bin/kwalletd5")}
+                 " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")}
             '') + ''
           ${optionalString cfg.unixAuth
               "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"}
@@ -350,7 +350,7 @@ let
               "session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug"}
           ${optionalString (cfg.enableKwallet)
               ("session optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
-               " kwalletd=${pkgs.libsForQt5.kwallet}/bin/kwalletd5")}
+               " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")}
         '');
     };
 
diff --git a/nixos/modules/services/audio/alsa.nix b/nixos/modules/services/audio/alsa.nix
index 53786dbc6270..acf48d3c3d03 100644
--- a/nixos/modules/services/audio/alsa.nix
+++ b/nixos/modules/services/audio/alsa.nix
@@ -7,6 +7,8 @@ let
 
   inherit (pkgs) alsaUtils;
 
+  pulseaudioEnabled = config.hardware.pulseaudio.enable;
+
 in
 
 {
@@ -80,7 +82,7 @@ in
 
     environment.systemPackages = [ alsaUtils ];
 
-    environment.etc = mkIf (config.sound.extraConfig != "")
+    environment.etc = mkIf (!pulseaudioEnabled && config.sound.extraConfig != "")
       [
         { source = pkgs.writeText "asound.conf" config.sound.extraConfig;
           target = "asound.conf";
diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix
index 11628781bbd8..5f379b392ea8 100644
--- a/nixos/modules/services/audio/mpd.nix
+++ b/nixos/modules/services/audio/mpd.nix
@@ -10,11 +10,9 @@ let
   gid = config.ids.gids.mpd;
   cfg = config.services.mpd;
 
-  playlistDir = "${cfg.dataDir}/playlists";
-
   mpdConf = pkgs.writeText "mpd.conf" ''
     music_directory     "${cfg.musicDirectory}"
-    playlist_directory  "${playlistDir}"
+    playlist_directory  "${cfg.playlistDirectory}"
     db_file             "${cfg.dbFile}"
     state_file          "${cfg.dataDir}/state"
     sticker_file        "${cfg.dataDir}/sticker.sql"
@@ -44,14 +42,34 @@ in {
         '';
       };
 
+      startWhenNeeded = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If set, <command>mpd</command> is socket-activated; that
+          is, instead of having it permanently running as a daemon,
+          systemd will start it on the first incoming connection.
+        '';
+      };
+
       musicDirectory = mkOption {
         type = types.path;
         default = "${cfg.dataDir}/music";
+        defaultText = ''''${dataDir}/music'';
         description = ''
           The directory where mpd reads music from.
         '';
       };
 
+      playlistDirectory = mkOption {
+        type = types.path;
+        default = "${cfg.dataDir}/playlists";
+        defaultText = ''''${dataDir}/playlists'';
+        description = ''
+          The directory where mpd stores playlists.
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
@@ -110,6 +128,7 @@ in {
       dbFile = mkOption {
         type = types.str;
         default = "${cfg.dataDir}/tag_cache";
+        defaultText = ''''${dataDir}/tag_cache'';
         description = ''
           The path to MPD's database.
         '';
@@ -123,19 +142,42 @@ in {
 
   config = mkIf cfg.enable {
 
+    systemd.sockets.mpd = mkIf cfg.startWhenNeeded {
+      description = "Music Player Daemon Socket";
+      wantedBy = [ "sockets.target" ];
+      listenStreams = [
+        "${optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:"}${toString cfg.network.port}"
+      ];
+      socketConfig = {
+        Backlog = 5;
+        KeepAlive = true;
+        PassCredentials = true;
+      };
+    };
+
     systemd.services.mpd = {
       after = [ "network.target" "sound.target" ];
       description = "Music Player Daemon";
-      wantedBy = [ "multi-user.target" ];
+      wantedBy = optional (!cfg.startWhenNeeded) "multi-user.target";
 
       preStart = ''
         mkdir -p "${cfg.dataDir}" && chown -R ${cfg.user}:${cfg.group} "${cfg.dataDir}"
-        mkdir -p "${playlistDir}" && chown -R ${cfg.user}:${cfg.group} "${playlistDir}"
+        mkdir -p "${cfg.playlistDirectory}" && chown -R ${cfg.user}:${cfg.group} "${cfg.playlistDirectory}"
       '';
       serviceConfig = {
         User = "${cfg.user}";
         PermissionsStartOnly = true;
         ExecStart = "${pkgs.mpd}/bin/mpd --no-daemon ${mpdConf}";
+        Type = "notify";
+        LimitRTPRIO = 50;
+        LimitRTTIME = "infinity";
+        ProtectSystem = true;
+        NoNewPrivileges = true;
+        ProtectKernelTunables = true;
+        ProtectControlGroups = true;
+        ProtectKernelModules = true;
+        RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX AF_NETLINK";
+        RestrictNamespaces = true;
       };
     };
 
diff --git a/nixos/modules/services/backup/znapzend.nix b/nixos/modules/services/backup/znapzend.nix
index 8c5af80c8fb7..baf99930e3eb 100644
--- a/nixos/modules/services/backup/znapzend.nix
+++ b/nixos/modules/services/backup/znapzend.nix
@@ -27,7 +27,13 @@ in
       noDestroy = mkOption {
         type = types.bool;
         default = false;
-        description = "Does all changes to the filesystem except destroy";
+        description = "Does all changes to the filesystem except destroy.";
+      };
+
+      autoCreation = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Automatically create the dataset on dest if it does not exists.";
       };
     };
   };
@@ -44,7 +50,7 @@ in
         path = with pkgs; [ zfs mbuffer openssh ];
 
         serviceConfig = {
-          ExecStart = "${pkgs.znapzend}/bin/znapzend --logto=${cfg.logTo} --loglevel=${cfg.logLevel} ${optionalString cfg.noDestroy "--nodestroy"}";
+          ExecStart = "${pkgs.znapzend}/bin/znapzend --logto=${cfg.logTo} --loglevel=${cfg.logLevel} ${optionalString cfg.noDestroy "--nodestroy"} ${optionalString cfg.autoCreation "--autoCreation"}";
           ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
           Restart = "on-failure";
         };
diff --git a/nixos/modules/services/computing/slurm/slurm.nix b/nixos/modules/services/computing/slurm/slurm.nix
index ee38a42199ee..fb91a29a4000 100644
--- a/nixos/modules/services/computing/slurm/slurm.nix
+++ b/nixos/modules/services/computing/slurm/slurm.nix
@@ -36,9 +36,9 @@ in
 
       package = mkOption {
         type = types.package;
-        default = pkgs.slurm-llnl;
-        defaultText = "pkgs.slurm-llnl";
-        example = literalExample "pkgs.slurm-llnl-full";
+        default = pkgs.slurm;
+        defaultText = "pkgs.slurm";
+        example = literalExample "pkgs.slurm-full";
         description = ''
           The package to use for slurm binaries.
         '';
diff --git a/nixos/modules/services/continuous-integration/buildbot/master.nix b/nixos/modules/services/continuous-integration/buildbot/master.nix
index fe0c452d0672..846efc8b5b9a 100644
--- a/nixos/modules/services/continuous-integration/buildbot/master.nix
+++ b/nixos/modules/services/continuous-integration/buildbot/master.nix
@@ -225,11 +225,7 @@ in {
         User = cfg.user;
         Group = cfg.group;
         WorkingDirectory = cfg.home;
-        Environment = "PYTHONPATH=${cfg.package}/lib/python2.7/site-packages:${pkgs.buildbot-plugins.www}/lib/python2.7/site-packages:${pkgs.buildbot-plugins.waterfall-view}/lib/python2.7/site-packages:${pkgs.buildbot-plugins.console-view}/lib/python2.7/site-packages:${pkgs.python27Packages.future}/lib/python2.7/site-packages:${pkgs.python27Packages.dateutil}/lib/python2.7/site-packages:${pkgs.python27Packages.six}/lib/python2.7/site-packages:${pkgs.python27Packages.sqlalchemy}/lib/python2.7/site-packages:${pkgs.python27Packages.jinja2}/lib/python2.7/site-packages:${pkgs.python27Packages.markupsafe}/lib/python2.7/site-packages:${pkgs.python27Packages.sqlalchemy_migrate}/lib/python2.7/site-packages:${pkgs.python27Packages.tempita}/lib/python2.7/site-packages:${pkgs.python27Packages.decorator}/lib/python2.7/site-packages:${pkgs.python27Packages.sqlparse}/lib/python2.7/site-packages:${pkgs.python27Packages.txaio}/lib/python2.7/site-packages:${pkgs.python27Packages.autobahn}/lib/python2.7/site-packages:${pkgs.python27Packages.pyjwt}/lib/python2.7/site-packages:${pkgs.python27Packages.distro}/lib/python2.7/site-packages:${pkgs.python27Packages.pbr}/lib/python2.7/site-packages:${pkgs.python27Packages.urllib3}/lib/python2.7/site-packages";
-
-        # NOTE: call twistd directly with stdout logging for systemd
-        #ExecStart = "${cfg.package}/bin/buildbot start --nodaemon ${cfg.buildbotDir}";
-        ExecStart = "${pkgs.python27Packages.twisted}/bin/twistd -n -l - -y ${cfg.buildbotDir}/buildbot.tac";
+        ExecStart = "${cfg.package}/bin/buildbot start --nodaemon ${cfg.buildbotDir}";
       };
 
     };
diff --git a/nixos/modules/services/continuous-integration/gitlab-runner.nix b/nixos/modules/services/continuous-integration/gitlab-runner.nix
index 048343b3360c..6d5cea4f77a5 100644
--- a/nixos/modules/services/continuous-integration/gitlab-runner.nix
+++ b/nixos/modules/services/continuous-integration/gitlab-runner.nix
@@ -4,15 +4,82 @@ with lib;
 
 let
   cfg = config.services.gitlab-runner;
-  configFile = pkgs.writeText "config.toml" cfg.configText;
+  configFile =
+    if (cfg.configFile == null) then
+      (pkgs.runCommand "config.toml" {
+        buildInputs = [ pkgs.remarshal ];
+      } ''
+        remarshal -if json -of toml \
+          < ${pkgs.writeText "config.json" (builtins.toJSON cfg.configOptions)} \
+          > $out
+      '')
+    else
+      cfg.configFile;
   hasDocker = config.virtualisation.docker.enable;
 in
 {
   options.services.gitlab-runner = {
     enable = mkEnableOption "Gitlab Runner";
 
-    configText = mkOption {
-      description = "Verbatim config.toml to use";
+    configFile = mkOption {
+      default = null;
+      description = ''
+        Configuration file for gitlab-runner.
+        Use this option in favor of configOptions to avoid placing CI tokens in the nix store.
+
+        <option>configFile</option> takes precedence over <option>configOptions</option>.
+
+        Warning: Not using <option>configFile</option> will potentially result in secrets
+        leaking into the WORLD-READABLE nix store.
+      '';
+      type = types.nullOr types.path;
+    };
+
+    configOptions = mkOption {
+      description = ''
+        Configuration for gitlab-runner
+        <option>configFile</option> will take precedence over this option.
+
+        Warning: all Configuration, especially CI token, will be stored in a
+        WORLD-READABLE file in the Nix Store.
+
+        If you want to protect your CI token use <option>configFile</option> instead.
+      '';
+      type = types.attrs;
+      example = {
+        concurrent = 2;
+        runners = [{
+          name = "docker-nix-1.11";
+          url = "https://CI/";
+          token = "TOKEN";
+          executor = "docker";
+          builds_dir = "";
+          docker = {
+            host = "";
+            image = "nixos/nix:1.11";
+            privileged = true;
+            disable_cache = true;
+            cache_dir = "";
+          };
+        }];
+      };
+    };
+
+    gracefulTermination = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Finish all remaining jobs before stopping, restarting or reconfiguring.
+        If not set gitlab-runner will stop immediatly without waiting for jobs to finish,
+        which will lead to failed builds.
+      '';
+    };
+
+    gracefulTimeout = mkOption {
+      default = "infinity";
+      type = types.str;
+      example = "5min 20s";
+      description = ''Time to wait until a graceful shutdown is turned into a forceful one.'';
     };
 
     workDir = mkOption {
@@ -29,10 +96,21 @@ in
       example = literalExample "pkgs.gitlab-runner_1_11";
     };
 
+    packages = mkOption {
+      default = [ pkgs.bash pkgs.docker-machine ];
+      defaultText = "[ pkgs.bash pkgs.docker-machine ]";
+      type = types.listOf types.package;
+      description = ''
+        Packages to add to PATH for the gitlab-runner process.
+      '';
+    };
+
   };
 
   config = mkIf cfg.enable {
     systemd.services.gitlab-runner = {
+      path = cfg.packages;
+      environment = config.networking.proxy.envVars;
       description = "Gitlab Runner";
       after = [ "network.target" ]
         ++ optional hasDocker "docker.service";
@@ -45,6 +123,11 @@ in
           --service gitlab-runner \
           --user gitlab-runner \
         '';
+
+      } //  optionalAttrs (cfg.gracefulTermination) {
+        TimeoutStopSec = "${cfg.gracefulTimeout}";
+        KillSignal = "SIGQUIT";
+        KillMode = "process";
       };
     };
 
diff --git a/nixos/modules/services/continuous-integration/hail.nix b/nixos/modules/services/continuous-integration/hail.nix
new file mode 100644
index 000000000000..5d0c3f7b4ab3
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/hail.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ...}:
+
+with lib;
+
+let
+  cfg = config.services.hail;
+in {
+
+
+  ###### interface
+
+  options.services.hail = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enables the Hail Auto Update Service. Hail can automatically deploy artifacts
+        built by a Hydra Continous Integration server. A common use case is to provide
+        continous deployment for single services or a full NixOS configuration.'';
+    };
+    profile = mkOption {
+      type = types.str;
+      default = "hail-profile";
+      description = "The name of the Nix profile used by Hail.";
+    };
+    hydraJobUri = mkOption {
+      type = types.str;
+      description = "The URI of the Hydra Job.";
+    };
+    netrc = mkOption {
+      type = types.nullOr types.path;
+      description = "The netrc file to use when fetching data from Hydra.";
+      default = null;
+    };
+    package = mkOption {
+      type = types.package;
+      default = pkgs.haskellPackages.hail;
+      defaultText = "pkgs.haskellPackages.hail";
+      description = "Hail package to use.";
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.hail = {
+      description = "Hail Auto Update Service";
+      wants = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = with pkgs; [ nix ];
+      environment = {
+        HOME = "/var/lib/empty";
+      };
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/hail --profile ${cfg.profile} --job-uri ${cfg.hydraJobUri}"
+          + lib.optionalString (cfg.netrc != null) " --netrc-file ${cfg.netrc}";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix
index fcc0f58637c4..43fec5ff5bb2 100644
--- a/nixos/modules/services/continuous-integration/hydra/default.nix
+++ b/nixos/modules/services/continuous-integration/hydra/default.nix
@@ -270,8 +270,8 @@ in
 
           ${optionalString haveLocalDB ''
             if ! [ -e ${baseDir}/.db-created ]; then
-              ${config.services.postgresql.package}/bin/createuser hydra
-              ${config.services.postgresql.package}/bin/createdb -O hydra hydra
+              ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createuser hydra
+              ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -O hydra hydra
               touch ${baseDir}/.db-created
             fi
           ''}
diff --git a/nixos/modules/services/databases/influxdb.nix b/nixos/modules/services/databases/influxdb.nix
index dd88624f406c..9ffe9fdea2ce 100644
--- a/nixos/modules/services/databases/influxdb.nix
+++ b/nixos/modules/services/databases/influxdb.nix
@@ -68,9 +68,9 @@ let
 
     collectd = [{
       enabled = false;
-      typesdb = "${pkgs.collectd}/share/collectd/types.db";
+      typesdb = "${pkgs.collectd-data}/share/collectd/types.db";
       database = "collectd_db";
-      port = 25826;
+      bind-address = ":25826";
     }];
 
     opentsdb = [{
@@ -149,7 +149,6 @@ in
         type = types.attrs;
       };
     };
-
   };
 
 
diff --git a/nixos/modules/services/databases/mongodb.nix b/nixos/modules/services/databases/mongodb.nix
index c56564f57f36..78dbf0d784cf 100644
--- a/nixos/modules/services/databases/mongodb.nix
+++ b/nixos/modules/services/databases/mongodb.nix
@@ -108,7 +108,7 @@ in
         after = [ "network.target" ];
 
         serviceConfig = {
-          ExecStart = "${mongodb}/bin/mongod --quiet --config ${mongoCnf} --fork --pidfilepath ${cfg.pidFile}";
+          ExecStart = "${mongodb}/bin/mongod --config ${mongoCnf} --fork --pidfilepath ${cfg.pidFile}";
           User = cfg.user;
           PIDFile = cfg.pidFile;
           Type = "forking";
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
index 6027f109285a..50766093307d 100644
--- a/nixos/modules/services/databases/mysql.nix
+++ b/nixos/modules/services/databases/mysql.nix
@@ -108,10 +108,13 @@ in
 
       initialDatabases = mkOption {
         default = [];
-        description = "List of database names and their initial schemas that should be used to create databases on the first startup of MySQL";
+        description = ''
+          List of database names and their initial schemas that should be used to create databases on the first startup
+          of MySQL. The schema attribute is optional: If not specified, an empty database is created.
+        '';
         example = [
           { name = "foodatabase"; schema = literalExample "./foodatabase.sql"; }
-          { name = "bardatabase"; schema = literalExample "./bardatabase.sql"; }
+          { name = "bardatabase"; }
         ];
       };
 
@@ -247,6 +250,8 @@ in
                     if ! test -e "${cfg.dataDir}/${database.name}"; then
                         echo "Creating initial database: ${database.name}"
                         ( echo "create database ${database.name};"
+
+                          ${optionalString (database ? "schema") ''
                           echo "use ${database.name};"
 
                           if [ -f "${database.schema}" ]
@@ -256,6 +261,7 @@ in
                           then
                               cat ${database.schema}/mysql-databases/*.sql
                           fi
+                          ''}
                         ) | ${mysql}/bin/mysql -u root -N
                     fi
                   '') cfg.initialDatabases}
diff --git a/nixos/modules/services/databases/postage.nix b/nixos/modules/services/databases/postage.nix
new file mode 100644
index 000000000000..d49c9a83a46f
--- /dev/null
+++ b/nixos/modules/services/databases/postage.nix
@@ -0,0 +1,205 @@
+{ lib, pkgs, config, ... } :
+
+with lib;
+
+let
+  cfg = config.services.postage;
+
+  confFile = pkgs.writeTextFile {
+    name = "postage.conf";
+    text =  ''
+      connection_file = ${postageConnectionsFile}
+
+      allow_custom_connections = ${builtins.toJSON cfg.allowCustomConnections}
+
+      postage_port = ${toString cfg.port}
+
+      super_only = ${builtins.toJSON cfg.superOnly}
+
+      ${optionalString (!isNull cfg.loginGroup) "login_group = ${cfg.loginGroup}"}
+
+      login_timeout = ${toString cfg.loginTimeout}
+
+      web_root = ${cfg.package}/etc/postage/web_root
+
+      data_root = ${cfg.dataRoot}
+
+      ${optionalString (!isNull cfg.tls) ''
+      tls_cert = ${cfg.tls.cert}
+      tls_key = ${cfg.tls.key}
+      ''}
+
+      log_level = ${cfg.logLevel}
+    '';
+  };
+
+  postageConnectionsFile = pkgs.writeTextFile {
+    name = "postage-connections.conf";
+    text = concatStringsSep "\n"
+      (mapAttrsToList (name : conn : "${name}: ${conn}") cfg.connections);
+  };
+
+  postage = "postage";
+in {
+
+  options.services.postage = {
+    enable = mkEnableOption "PostgreSQL Administration for the web";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.postage;
+      defaultText = "pkgs.postage";
+      description = ''
+        The postage package to use.
+      '';
+    };
+
+    connections = mkOption {
+      type = types.attrsOf types.str;
+      default = {};
+      example = {
+        "nuc-server"  = "hostaddr=192.168.0.100 port=5432 dbname=postgres";
+        "mini-server" = "hostaddr=127.0.0.1 port=5432 dbname=postgres sslmode=require";
+      };
+      description = ''
+        Postage requires at least one PostgreSQL server be defined.
+        </para><para>
+        Detailed information about PostgreSQL connection strings is available at:
+        <link xlink:href="http://www.postgresql.org/docs/current/static/libpq-connect.html"/>
+        </para><para>
+        Note that you should not specify your user name or password. That
+        information will be entered on the login screen. If you specify a
+        username or password, it will be removed by Postage before attempting to
+        connect to a database.
+      '';
+    };
+
+    allowCustomConnections = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        This tells Postage whether or not to allow anyone to use a custom
+        connection from the login screen.
+      '';
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = 8080;
+      description = ''
+        This tells Postage what port to listen on for browser requests.
+      '';
+    };
+
+    localOnly = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        This tells Postage whether or not to set the listening socket to local
+        addresses only.
+      '';
+    };
+
+    superOnly = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        This tells Postage whether or not to only allow super users to
+        login. The recommended value is true and will restrict users who are not
+        super users from logging in to any PostgreSQL instance through
+        Postage. Note that a connection will be made to PostgreSQL in order to
+        test if the user is a superuser.
+      '';
+    };
+
+    loginGroup = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        This tells Postage to only allow users in a certain PostgreSQL group to
+        login to Postage. Note that a connection will be made to PostgreSQL in
+        order to test if the user is a member of the login group.
+      '';
+    };
+
+    loginTimeout = mkOption {
+      type = types.int;
+      default = 3600;
+      description = ''
+        Number of seconds of inactivity before user is automatically logged
+        out.
+      '';
+    };
+
+    dataRoot = mkOption {
+      type = types.str;
+      default = "/var/lib/postage";
+      description = ''
+        This tells Postage where to put the SQL file history. All tabs are saved
+        to this location so that if you get disconnected from Postage you
+        don't lose your work.
+      '';
+    };
+
+    tls = mkOption {
+      type = types.nullOr (types.submodule {
+        options = {
+          cert = mkOption {
+            type = types.str;
+            description = "TLS certificate";
+          };
+          key = mkOption {
+            type = types.str;
+            description = "TLS key";
+          };
+        };
+      });
+      default = null;
+      description = ''
+        These options tell Postage where the TLS Certificate and Key files
+        reside. If you use these options then you'll only be able to access
+        Postage through a secure TLS connection. These options are only
+        necessary if you wish to connect directly to Postage using a secure TLS
+        connection. As an alternative, you can set up Postage in a reverse proxy
+        configuration. This allows your web server to terminate the secure
+        connection and pass on the request to Postage. You can find help to set
+        up this configuration in:
+        <link xlink:href="https://github.com/workflowproducts/postage/blob/master/INSTALL_NGINX.md"/>
+      '';
+    };
+
+    logLevel = mkOption {
+      type = types.enum ["error" "warn" "notice" "info"];
+      default = "error";
+      description = ''
+        Verbosity of logs
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.postage = {
+      description = "postage - PostgreSQL Administration for the web";
+      wants    = [ "postgresql.service" ];
+      after    = [ "postgresql.service" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User         = postage;
+        Group        = postage;
+        ExecStart    = "${pkgs.postage}/sbin/postage -c ${confFile}" +
+                       optionalString cfg.localOnly " --local-only=true";
+      };
+    };
+    users = {
+      users."${postage}" = {
+        name  = postage;
+        group = postage;
+        home  = cfg.dataRoot;
+        createHome = true;
+      };
+      groups."${postage}" = {
+        name = postage;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index d06e03a52978..1bbab3296005 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -38,9 +38,6 @@ let
 
   pre84 = versionOlder (builtins.parseDrvName postgresql.name).version "8.4";
 
-  # NixOS traditionally used `root` as superuser, most other distros use `postgres`. From 17.09
-  # we also try to follow this standard
-  superuser = (if versionAtLeast config.system.stateVersion "17.09" then "postgres" else "root");
 
 in
 
@@ -151,6 +148,16 @@ in
           Contents of the <filename>recovery.conf</filename> file.
         '';
       };
+      superUser = mkOption {
+        type = types.str;
+        default= if versionAtLeast config.system.stateVersion "17.09" then "postgres" else "root";
+        internal = true;
+        description = ''
+          NixOS traditionally used `root` as superuser, most other distros use `postgres`.
+          From 17.09 we also try to follow this standard. Internal since changing this value
+          would lead to breakage while setting up databases.
+        '';
+        };
     };
 
   };
@@ -215,7 +222,7 @@ in
           ''
             # Initialise the database.
             if ! test -e ${cfg.dataDir}/PG_VERSION; then
-              initdb -U ${superuser}
+              initdb -U ${cfg.superUser}
               # See postStart!
               touch "${cfg.dataDir}/.first_startup"
             fi
@@ -247,14 +254,14 @@ in
         # Wait for PostgreSQL to be ready to accept connections.
         postStart =
           ''
-            while ! ${pkgs.sudo}/bin/sudo -u ${superuser} psql --port=${toString cfg.port} -d postgres -c "" 2> /dev/null; do
+            while ! ${pkgs.sudo}/bin/sudo -u ${cfg.superUser} psql --port=${toString cfg.port} -d postgres -c "" 2> /dev/null; do
                 if ! kill -0 "$MAINPID"; then exit 1; fi
                 sleep 0.1
             done
 
             if test -e "${cfg.dataDir}/.first_startup"; then
               ${optionalString (cfg.initialScript != null) ''
-                ${pkgs.sudo}/bin/sudo -u ${superuser} psql -f "${cfg.initialScript}" --port=${toString cfg.port} -d postgres
+                ${pkgs.sudo}/bin/sudo -u ${cfg.superUser} psql -f "${cfg.initialScript}" --port=${toString cfg.port} -d postgres
               ''}
               rm -f "${cfg.dataDir}/.first_startup"
             fi
diff --git a/nixos/modules/services/editors/emacs.xml b/nixos/modules/services/editors/emacs.xml
index 9d6395ebd74c..dd66bac442c6 100644
--- a/nixos/modules/services/editors/emacs.xml
+++ b/nixos/modules/services/editors/emacs.xml
@@ -24,7 +24,7 @@
   <para>
     Emacs runs within a graphical desktop environment using the X
     Window System, but works equally well on a text terminal. Under
-    <productname>OS X</productname>, a "Mac port" edition is
+    <productname>macOS</productname>, a "Mac port" edition is
     available, which uses Apple's native GUI frameworks.
   </para>
 
@@ -84,7 +84,7 @@
             <listitem>
               <para>
                 Emacs 25 with the "Mac port" patches, providing a more
-                native look and feel under OS X.
+                native look and feel under macOS.
               </para>
             </listitem>
           </varlistentry>
diff --git a/nixos/modules/services/games/factorio.nix b/nixos/modules/services/games/factorio.nix
index e7f070d08773..1dc8ce93a0e5 100644
--- a/nixos/modules/services/games/factorio.nix
+++ b/nixos/modules/services/games/factorio.nix
@@ -39,7 +39,7 @@ let
     admins = [];
   };
   serverSettingsFile = pkgs.writeText "server-settings.json" (builtins.toJSON (filterAttrsRecursive (n: v: v != null) serverSettings));
-  modDir = pkgs.factorio-mkModDirDrv cfg.mods;
+  modDir = pkgs.factorio-utils.mkModDirDrv cfg.mods;
 in
 {
   options = {
diff --git a/nixos/modules/services/hardware/interception-tools.nix b/nixos/modules/services/hardware/interception-tools.nix
new file mode 100644
index 000000000000..fadcb19a016f
--- /dev/null
+++ b/nixos/modules/services/hardware/interception-tools.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.interception-tools;
+in {
+  options.services.interception-tools = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Whether to enable the interception tools service.";
+    };
+
+    plugins = mkOption {
+      type = types.listOf types.package;
+      default = [ pkgs.interception-tools-plugins.caps2esc ];
+      description = ''
+        A list of interception tools plugins that will be made available to use
+        inside the udevmon configuration.
+      '';
+    };
+
+    udevmonConfig = mkOption {
+      type = types.either types.str types.path;
+      default = ''
+        - JOB: "intercept -g $DEVNODE | caps2esc | uinput -d $DEVNODE"
+          DEVICE:
+            EVENTS:
+              EV_KEY: [KEY_CAPSLOCK, KEY_ESC]
+      '';
+      example = ''
+        - JOB: "intercept -g $DEVNODE | y2z | x2y | uinput -d $DEVNODE"
+          DEVICE:
+            EVENTS:
+              EV_KEY: [KEY_X, KEY_Y]
+      '';
+      description = ''
+        String of udevmon YAML configuration, or path to a udevmon YAML
+        configuration file.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.interception-tools = {
+      description = "Interception tools";
+      path = [ pkgs.bash pkgs.interception-tools ] ++ cfg.plugins;
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.interception-tools}/bin/udevmon -c \
+          ${if builtins.typeOf cfg.udevmonConfig == "path"
+          then cfg.udevmonConfig
+          else pkgs.writeText "udevmon.yaml" cfg.udevmonConfig}
+        '';
+        Nice = -20;
+      };
+      wantedBy = [ "multi-user.target" ];
+    };
+  };
+}
diff --git a/nixos/modules/services/hardware/tlp.nix b/nixos/modules/services/hardware/tlp.nix
index 3b108c87edd2..68425822a884 100644
--- a/nixos/modules/services/hardware/tlp.nix
+++ b/nixos/modules/services/hardware/tlp.nix
@@ -57,6 +57,8 @@ in
     powerManagement.scsiLinkPolicy = null;
     powerManagement.cpuFreqGovernor = null;
 
+    systemd.sockets."systemd-rfkill".enable = false;
+
     systemd.services = {
       "systemd-rfkill@".enable = false;
       "systemd-rfkill".enable = false;
diff --git a/nixos/modules/services/logging/fluentd.nix b/nixos/modules/services/logging/fluentd.nix
index 9fbec2457371..95825705d9d7 100644
--- a/nixos/modules/services/logging/fluentd.nix
+++ b/nixos/modules/services/logging/fluentd.nix
@@ -4,6 +4,8 @@ with lib;
 
 let
   cfg = config.services.fluentd;
+
+  pluginArgs = concatStringsSep " " (map (x: "-p ${x}") cfg.plugins);
 in {
   ###### interface
 
@@ -28,6 +30,15 @@ in {
         defaultText = "pkgs.fluentd";
         description = "The fluentd package to use.";
       };
+
+      plugins = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = ''
+          A list of plugin paths to pass into fluentd. It will make plugins defined in ruby files
+          there available in your config.
+        '';
+      };
     };
   };
 
@@ -39,7 +50,7 @@ in {
       description = "Fluentd Daemon";
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/fluentd -c ${pkgs.writeText "fluentd.conf" cfg.config}";
+        ExecStart = "${cfg.package}/bin/fluentd -c ${pkgs.writeText "fluentd.conf" cfg.config} ${pluginArgs}";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
       };
     };
diff --git a/nixos/modules/services/logging/graylog.nix b/nixos/modules/services/logging/graylog.nix
index 9f0fb11f0252..a0dc0d6d089d 100644
--- a/nixos/modules/services/logging/graylog.nix
+++ b/nixos/modules/services/logging/graylog.nix
@@ -11,9 +11,7 @@ let
     password_secret = ${cfg.passwordSecret}
     root_username = ${cfg.rootUsername}
     root_password_sha2 = ${cfg.rootPasswordSha2}
-    elasticsearch_cluster_name = ${cfg.elasticsearchClusterName}
-    elasticsearch_discovery_zen_ping_multicast_enabled = ${boolToString cfg.elasticsearchDiscoveryZenPingMulticastEnabled}
-    elasticsearch_discovery_zen_ping_unicast_hosts = ${cfg.elasticsearchDiscoveryZenPingUnicastHosts}
+    elasticsearch_hosts = ${concatStringsSep "," cfg.elasticsearchHosts}
     message_journal_dir = ${cfg.messageJournalDir}
     mongodb_uri = ${cfg.mongodbUri}
     plugin_dir = /var/lib/graylog/plugins
@@ -91,22 +89,10 @@ in
         '';
       };
 
-      elasticsearchClusterName = mkOption {
-        type = types.str;
-        example = "graylog";
-        description = "This must be the same as for your Elasticsearch cluster";
-      };
-
-      elasticsearchDiscoveryZenPingMulticastEnabled = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Whether to use elasticsearch multicast discovery";
-      };
-
-      elasticsearchDiscoveryZenPingUnicastHosts = mkOption {
-        type = types.str;
-        default = "127.0.0.1:9300";
-        description = "Tells Graylogs Elasticsearch client how to find other cluster members. See Elasticsearch documentation for details";
+      elasticsearchHosts = mkOption {
+        type = types.listOf types.str;
+        example = literalExample ''[ "http://node1:9200" "http://user:password@node2:19200" ]'';
+        description = "List of valid URIs of the http ports of your elastic nodes. If one or more of your elasticsearch hosts require authentication, include the credentials in each node URI that requires authentication";
       };
 
       messageJournalDir = mkOption {
diff --git a/nixos/modules/services/logging/journalwatch.nix b/nixos/modules/services/logging/journalwatch.nix
new file mode 100644
index 000000000000..d49795fe2b77
--- /dev/null
+++ b/nixos/modules/services/logging/journalwatch.nix
@@ -0,0 +1,246 @@
+{ config, lib, pkgs, services, ... }:
+with lib;
+
+let
+  cfg = config.services.journalwatch;
+  user = "journalwatch";
+  dataDir = "/var/lib/${user}";
+
+  journalwatchConfig = pkgs.writeText "config" (''
+    # (File Generated by NixOS journalwatch module.)
+    [DEFAULT]
+    mail_binary = ${cfg.mailBinary}
+    priority = ${toString cfg.priority}
+    mail_from = ${cfg.mailFrom}
+  ''
+  + optionalString (cfg.mailTo != null) ''
+    mail_to = ${cfg.mailTo}
+  ''
+  + cfg.extraConfig);
+
+  journalwatchPatterns = pkgs.writeText "patterns" ''
+    # (File Generated by NixOS journalwatch module.)
+
+    ${mkPatterns cfg.filterBlocks}
+  '';
+
+  # empty line at the end needed to to separate the blocks
+  mkPatterns = filterBlocks: concatStringsSep "\n" (map (block: ''
+    ${block.match}
+    ${block.filters}
+
+  '') filterBlocks);
+
+
+in {
+  options = {
+    services.journalwatch = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If enabled, periodically check the journal with journalwatch and report the results by mail.
+        '';
+      };
+
+      priority = mkOption {
+        type = types.int;
+        default = 6;
+        description = ''
+          Lowest priority of message to be considered.
+          A value between 7 ("debug"), and 0 ("emerg"). Defaults to 6 ("info").
+          If you don't care about anything with "info" priority, you can reduce
+          this to e.g. 5 ("notice") to considerably reduce the amount of
+          messages without needing many <option>filterBlocks</option>.
+        '';
+      };
+
+      # HACK: this is a workaround for journalwatch's usage of socket.getfqdn() which always returns localhost if
+      # there's an alias for the localhost on a separate line in /etc/hosts, or take for ages if it's not present and
+      # then return something right-ish in the direction of /etc/hostname. Just bypass it completely.
+      mailFrom = mkOption {
+        type = types.str;
+        default = "journalwatch@${config.networking.hostName}";
+        description = ''
+          Mail address to send journalwatch reports from.
+        '';
+      };
+
+      mailTo = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Mail address to send journalwatch reports to.
+        '';
+      };
+
+      mailBinary = mkOption {
+        type = types.path;
+        default = "/run/wrappers/bin/sendmail";
+        description = ''
+          Sendmail-compatible binary to be used to send the messages.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Extra lines to be added verbatim to the journalwatch/config configuration file.
+          You can add any commandline argument to the config, without the '--'.
+          See <literal>journalwatch --help</literal> for all arguments and their description.
+          '';
+      };
+
+      filterBlocks = mkOption {
+        type = types.listOf (types.submodule {
+          options = {
+           match = mkOption {
+              type = types.str;
+              example = "SYSLOG_IDENTIFIER = systemd";
+              description = ''
+                Syntax: <literal>field = value</literal>
+                Specifies the log entry <literal>field</literal> this block should apply to.
+                If the <literal>field</literal> of a message matches this <literal>value</literal>,
+                this patternBlock's <option>filters</option> are applied.
+                If <literal>value</literal> starts and ends with a slash, it is interpreted as
+                an extended python regular expression, if not, it's an exact match.
+                The journal fields are explained in systemd.journal-fields(7).
+              '';
+            };
+
+            filters = mkOption {
+              type = types.str;
+              example = ''
+                (Stopped|Stopping|Starting|Started) .*
+                (Reached target|Stopped target) .*
+              '';
+              description = ''
+                The filters to apply on all messages which satisfy <option>match</option>.
+                Any of those messages that match any specified filter will be removed from journalwatch's output.
+                Each filter is an extended Python regular expression.
+                You can specify multiple filters and separate them by newlines.
+                Lines starting with '#' are comments. Inline-comments are not permitted.
+              '';
+            };
+          };
+        });
+
+        example = [
+          # examples taken from upstream
+          {
+            match = "_SYSTEMD_UNIT = systemd-logind.service";
+            filters = ''
+              New session [a-z]?\d+ of user \w+\.
+              Removed session [a-z]?\d+\.
+            '';
+          }
+
+          {
+            match = "SYSLOG_IDENTIFIER = /(CROND|crond)/";
+            filters = ''
+              pam_unix\(crond:session\): session (opened|closed) for user \w+
+              \(\w+\) CMD .*
+            '';
+          }
+        ];
+
+        # another example from upstream.
+        # very useful on priority = 6, and required as journalwatch throws an error when no pattern is defined at all.
+        default = [
+          {
+            match = "SYSLOG_IDENTIFIER = systemd";
+            filters = ''
+              (Stopped|Stopping|Starting|Started) .*
+              (Created slice|Removed slice) user-\d*\.slice\.
+              Received SIGRTMIN\+24 from PID .*
+              (Reached target|Stopped target) .*
+              Startup finished in \d*ms\.
+            '';
+          }
+        ];
+
+
+        description = ''
+          filterBlocks can be defined to blacklist journal messages which are not errors.
+          Each block matches on a log entry field, and the filters in that block then are matched
+          against all messages with a matching log entry field.
+
+          All messages whose PRIORITY is at least 6 (INFO) are processed by journalwatch.
+          If you don't specify any filterBlocks, PRIORITY is reduced to 5 (NOTICE) by default.
+
+          All regular expressions are extended Python regular expressions, for details
+          see: http://doc.pyschools.com/html/regex.html
+        '';
+      };
+
+      interval = mkOption {
+        type = types.str;
+        default = "hourly";
+        description = ''
+          How often to run journalwatch.
+
+          The format is described in systemd.time(7).
+        '';
+      };
+      accuracy = mkOption {
+        type = types.str;
+        default = "10min";
+        description = ''
+          The time window around the interval in which the journalwatch run will be scheduled.
+
+          The format is described in systemd.time(7).
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers.${user} = {
+      isSystemUser = true;
+      createHome = true;
+      home = dataDir;
+      # for journal access
+      group = "systemd-journal";
+    };
+
+    systemd.services.journalwatch = {
+      environment = {
+        XDG_DATA_HOME = "${dataDir}/share";
+        XDG_CONFIG_HOME = "${dataDir}/config";
+      };
+      serviceConfig = {
+        User = user;
+        Type = "oneshot";
+        PermissionsStartOnly = true;
+        ExecStart = "${pkgs.python3Packages.journalwatch}/bin/journalwatch mail";
+        # lowest CPU and IO priority, but both still in best-effort class to prevent starvation
+        Nice=19;
+        IOSchedulingPriority=7;
+      };
+      preStart = ''
+        chown -R ${user}:systemd-journal ${dataDir}
+        chmod -R u+rwX,go-w ${dataDir}
+        mkdir -p ${dataDir}/config/journalwatch
+        ln -sf ${journalwatchConfig} ${dataDir}/config/journalwatch/config
+        ln -sf ${journalwatchPatterns} ${dataDir}/config/journalwatch/patterns
+      '';
+    };
+
+    systemd.timers.journalwatch = {
+      description = "Periodic journalwatch run";
+      wantedBy = [ "timers.target" ];
+      timerConfig = {
+        OnCalendar = cfg.interval;
+        AccuracySec = cfg.accuracy;
+        Persistent = true;
+      };
+    };
+
+  };
+
+  meta = {
+    maintainers = with stdenv.lib.maintainers; [ florianjacob ];
+  };
+}
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
index caaa87b94d61..01ae49d49090 100644
--- a/nixos/modules/services/mail/postfix.nix
+++ b/nixos/modules/services/mail/postfix.nix
@@ -9,7 +9,8 @@ let
   group = cfg.group;
   setgidGroup = cfg.setgidGroup;
 
-  haveAliases = cfg.postmasterAlias != "" || cfg.rootAlias != "" || cfg.extraAliases != "";
+  haveAliases = cfg.postmasterAlias != "" || cfg.rootAlias != ""
+                      || cfg.extraAliases != "";
   haveTransport = cfg.transport != "";
   haveVirtual = cfg.virtual != "";
 
@@ -25,149 +26,275 @@ let
 
   clientRestrictions = concatStringsSep ", " (clientAccess ++ dnsBl);
 
-  mainCf =
-    ''
-      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 = no
-      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 = no
+  mainCf = let
+    escape = replaceStrings ["$"] ["$$"];
+    mkList = items: "\n  " + concatMapStringsSep "\n  " escape items;
+    mkVal = value:
+      if isList value then mkList value
+        else " " + (if value == true then "yes"
+        else if value == false then "no"
+        else toString value);
+    mkEntry = name: value: "${escape name} =${mkVal value}";
+  in
+    concatStringsSep "\n" (mapAttrsToList mkEntry (recursiveUpdate defaultConf 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.lookupMX || cfg.relayHost == ""
+                             then cfg.relayHost
+                             else "[${cfg.relayHost}]";
+    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.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;
+  };
 
-    ''
-    + optionalString config.networking.enableIPv6 ''
-      inet_protocols = all
-    ''
-    + (if cfg.networks != null then
-        ''
-          mynetworks = ${concatStringsSep ", " cfg.networks}
-        ''
-      else if cfg.networksStyle != "" then
-        ''
-          mynetworks_style = ${cfg.networksStyle}
-        ''
-      else
-        "")
-    + optionalString (cfg.hostname != "") ''
-      myhostname = ${cfg.hostname}
-    ''
-    + optionalString (cfg.domain != "") ''
-      mydomain = ${cfg.domain}
-    ''
-    + optionalString (cfg.origin != "") ''
-      myorigin = ${cfg.origin}
-    ''
-    + optionalString (cfg.destination != null) ''
-      mydestination = ${concatStringsSep ", " cfg.destination}
-    ''
-    + optionalString (cfg.relayDomains != null) ''
-      relay_domains = ${concatStringsSep ", " cfg.relayDomains}
-    ''
-    + ''
-      relayhost = ${if cfg.lookupMX || cfg.relayHost == "" then
-          cfg.relayHost
-        else
-          "[" + cfg.relayHost + "]"}
+  masterCfOptions = { options, config, name, ... }: {
+    options = {
+      name = mkOption {
+        type = types.str;
+        default = name;
+        example = "smtp";
+        description = ''
+          The name of the service to run. Defaults to the attribute set key.
+        '';
+      };
 
-      mail_spool_directory = /var/spool/mail/
+      type = mkOption {
+        type = types.enum [ "inet" "unix" "fifo" "pass" ];
+        default = "unix";
+        example = "inet";
+        description = "The type of the service";
+      };
 
-      setgid_group = ${setgidGroup}
-    ''
-    + optionalString (cfg.sslCert != "") ''
+      private = mkOption {
+        type = types.bool;
+        example = false;
+        description = ''
+          Whether the service's sockets and storage directory is restricted to
+          be only available via the mail system. If <literal>null</literal> is
+          given it uses the postfix default <literal>true</literal>.
+        '';
+      };
 
-      smtp_tls_CAfile = ${cfg.sslCACert}
-      smtp_tls_cert_file = ${cfg.sslCert}
-      smtp_tls_key_file = ${cfg.sslKey}
+      privileged = mkOption {
+        type = types.bool;
+        example = true;
+        description = "";
+      };
 
-      smtp_use_tls = yes
+      chroot = mkOption {
+        type = types.bool;
+        example = true;
+        description = ''
+          Whether the service is chrooted to have only access to the
+          <option>services.postfix.queueDir</option> and the closure of
+          store paths specified by the <option>program</option> option.
+        '';
+      };
 
-      smtpd_tls_CAfile = ${cfg.sslCACert}
-      smtpd_tls_cert_file = ${cfg.sslCert}
-      smtpd_tls_key_file = ${cfg.sslKey}
+      wakeup = mkOption {
+        type = types.int;
+        example = 60;
+        description = ''
+          Automatically wake up the service after the specified number of
+          seconds. If <literal>0</literal> is given, never wake the service
+          up.
+        '';
+      };
 
-      smtpd_use_tls = yes
-    ''
-    + optionalString (cfg.recipientDelimiter != "") ''
-      recipient_delimiter = ${cfg.recipientDelimiter}
-    ''
-    + optionalString haveAliases ''
-      alias_maps = hash:/etc/postfix/aliases
-    ''
-    + optionalString haveTransport ''
-      transport_maps = hash:/etc/postfix/transport
-    ''
-    + optionalString haveVirtual ''
-      virtual_alias_maps = hash:/etc/postfix/virtual
-    ''
-    + optionalString (cfg.dnsBlacklists != []) ''
-      smtpd_client_restrictions = ${clientRestrictions}
-    ''
-    + cfg.extraConfig;
-
-  masterCf = ''
-    # ==========================================================================
-    # service type  private unpriv  chroot  wakeup  maxproc command + args
-    #               (yes)   (yes)   (no)    (never) (100)
-    # ==========================================================================
-    smtp      inet  n       -       n       -       -       smtpd
-  '' + optionalString cfg.enableSubmission ''
-    submission inet n       -       n       -       -       smtpd
-      ${concatStringsSep "\n  " (mapAttrsToList (x: y: "-o " + x + "=" + y) cfg.submissionOptions)}
-  ''
-  + ''
-    pickup    unix  n       -       n       60      1       pickup
-    cleanup   unix  n       -       n       -       0       cleanup
-    qmgr      unix  n       -       n       300     1       qmgr
-    tlsmgr    unix  -       -       n       1000?   1       tlsmgr
-    rewrite   unix  -       -       n       -       -       trivial-rewrite
-    bounce    unix  -       -       n       -       0       bounce
-    defer     unix  -       -       n       -       0       bounce
-    trace     unix  -       -       n       -       0       bounce
-    verify    unix  -       -       n       -       1       verify
-    flush     unix  n       -       n       1000?   0       flush
-    proxymap  unix  -       -       n       -       -       proxymap
-    proxywrite unix -       -       n       -       1       proxymap
-  ''
-  + optionalString cfg.enableSmtp ''
-    smtp      unix  -       -       n       -       -       smtp
-    relay     unix  -       -       n       -       -       smtp
-    	      -o smtp_fallback_relay=
-    #       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
-  ''
-  + ''
-    showq     unix  n       -       n       -       -       showq
-    error     unix  -       -       n       -       -       error
-    retry     unix  -       -       n       -       -       error
-    discard   unix  -       -       n       -       -       discard
-    local     unix  -       n       n       -       -       local
-    virtual   unix  -       n       n       -       -       virtual
-    lmtp      unix  -       -       n       -       -       lmtp
-    anvil     unix  -       -       n       -       1       anvil
-    scache    unix  -       -       n       -       1       scache
-    ${cfg.extraMasterConf}
-  '';
-
-  aliases =
+      wakeupUnusedComponent = mkOption {
+        type = types.bool;
+        example = false;
+        description = ''
+          If set to <literal>false</literal> the component will only be woken
+          up if it is used. This is equivalent to postfix' notion of adding a
+          question mark behind the wakeup time in
+          <filename>master.cf</filename>
+        '';
+      };
+
+      maxproc = mkOption {
+        type = types.int;
+        example = 1;
+        description = ''
+          The maximum number of processes to spawn for this service. If the
+          value is <literal>0</literal> it doesn't have any limit. If
+          <literal>null</literal> is given it uses the postfix default of
+          <literal>100</literal>.
+        '';
+      };
+
+      command = mkOption {
+        type = types.str;
+        default = name;
+        example = "smtpd";
+        description = ''
+          A program name specifying a Postfix service/daemon process.
+          By default it's the attribute <option>name</option>.
+        '';
+      };
+
+      args = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "-o" "smtp_helo_timeout=5" ];
+        description = ''
+          Arguments to pass to the <option>command</option>. There is no shell
+          processing involved and shell syntax is passed verbatim to the
+          process.
+        '';
+      };
+
+      rawEntry = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        internal = true;
+        description = ''
+          The raw configuration line for the <filename>master.cf</filename>.
+        '';
+      };
+    };
+
+    config.rawEntry = let
+      mkBool = bool: if bool then "y" else "n";
+      mkArg = arg: "${optionalString (hasPrefix "-" arg) "\n  "}${arg}";
+
+      maybeOption = fun: option:
+        if options.${option}.isDefined then fun config.${option} else "-";
+
+      # This is special, because we have two options for this value.
+      wakeup = let
+        wakeupDefined = options.wakeup.isDefined;
+        wakeupUCDefined = options.wakeupUnusedComponent.isDefined;
+        finalValue = toString config.wakeup
+                   + optionalString (wakeupUCDefined && !config.wakeupUnusedComponent) "?";
+      in if wakeupDefined then finalValue else "-";
+
+    in [
+      config.name
+      config.type
+      (maybeOption mkBool "private")
+      (maybeOption (b: mkBool (!b)) "privileged")
+      (maybeOption mkBool "chroot")
+      wakeup
+      (maybeOption toString "maxproc")
+      (config.command + " " + concatMapStringsSep " " mkArg config.args)
+    ];
+  };
+
+  masterCfContent = let
+
+    labels = [
+      "# service" "type" "private" "unpriv" "chroot" "wakeup" "maxproc"
+      "command + args"
+    ];
+
+    labelDefaults = [
+      "# " "" "(yes)" "(yes)" "(no)" "(never)" "(100)" "" ""
+    ];
+
+    masterCf = mapAttrsToList (const (getAttr "rawEntry")) cfg.masterConfig;
+
+    # A list of the maximum width of the columns across all lines and labels
+    maxWidths = let
+      foldLine = line: acc: let
+        columnLengths = map stringLength line;
+      in zipListsWith max acc columnLengths;
+      # We need to handle the last column specially here, because it's
+      # open-ended (command + args).
+      lines = [ labels labelDefaults ] ++ (map (l: init l ++ [""]) masterCf);
+    in fold foldLine (genList (const 0) (length labels)) lines;
+
+    # Pad a string with spaces from the right (opposite of fixedWidthString).
+    pad = width: str: let
+      padWidth = width - stringLength str;
+      padding = concatStrings (genList (const " ") padWidth);
+    in str + optionalString (padWidth > 0) padding;
+
+    # It's + 2 here, because that's the amount of spacing between columns.
+    fullWidth = fold (width: acc: acc + width + 2) 0 maxWidths;
+
+    formatLine = line: concatStringsSep "  " (zipListsWith pad maxWidths line);
+
+    formattedLabels = let
+      sep = "# " + concatStrings (genList (const "=") (fullWidth + 5));
+      lines = [ sep (formatLine labels) (formatLine labelDefaults) sep ];
+    in concatStringsSep "\n" lines;
+
+  in formattedLabels + "\n" + concatMapStringsSep "\n" formatLine masterCf + "\n" + cfg.extraMasterConf;
+
+  headerCheckOptions = { ... }:
+  {
+    options = {
+      pattern = mkOption {
+        type = types.str;
+        default = "/^.*/";
+        example = "/^X-Mailer:/";
+        description = "A regexp pattern matching the header";
+      };
+      action = mkOption {
+        type = types.str;
+        default = "DUNNO";
+        example = "BCC mail@example.com";
+        description = "The action to be executed when the pattern is matched";
+      };
+    };
+  };
+
+  headerChecks = concatStringsSep "\n" (map (x: "${x.pattern} ${x.action}") cfg.headerChecks) + cfg.extraHeaderChecks;
+
+  aliases = let seperator = if cfg.aliasMapType == "hash" then ":" else ""; in
     optionalString (cfg.postmasterAlias != "") ''
-      postmaster: ${cfg.postmasterAlias}
+      postmaster${seperator} ${cfg.postmasterAlias}
     ''
     + optionalString (cfg.rootAlias != "") ''
-      root: ${cfg.rootAlias}
+      root${seperator} ${cfg.rootAlias}
     ''
     + cfg.extraAliases
   ;
@@ -176,8 +303,9 @@ let
   virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual;
   checkClientAccessFile = pkgs.writeText "postfix-check-client-access" cfg.dnsBlacklistOverrides;
   mainCfFile = pkgs.writeText "postfix-main.cf" mainCf;
-  masterCfFile = pkgs.writeText "postfix-master.cf" masterCf;
+  masterCfFile = pkgs.writeText "postfix-master.cf" masterCfContent;
   transportFile = pkgs.writeText "postfix-transport" cfg.transport;
+  headerChecksFile = pkgs.writeText "postfix-header-checks" headerChecks;
 
 in
 
@@ -199,27 +327,29 @@ in
         default = true;
         description = "Whether to enable smtp in master.cf.";
       };
-      
+
       enableSubmission = mkOption {
         type = types.bool;
         default = false;
-        description = "Whether to enable smtp submission";
+        description = "Whether to enable smtp submission.";
       };
 
       submissionOptions = mkOption {
         type = types.attrs;
-        default = { "smtpd_tls_security_level" = "encrypt";
-                    "smtpd_sasl_auth_enable" = "yes";
-                    "smtpd_client_restrictions" = "permit_sasl_authenticated,reject";
-                    "milter_macro_daemon_name" = "ORIGINATING";
-                  };
+        default = {
+          smtpd_tls_security_level = "encrypt";
+          smtpd_sasl_auth_enable = "yes";
+          smtpd_client_restrictions = "permit_sasl_authenticated,reject";
+          milter_macro_daemon_name = "ORIGINATING";
+        };
+        example = {
+          smtpd_tls_security_level = "encrypt";
+          smtpd_sasl_auth_enable = "yes";
+          smtpd_sasl_type = "dovecot";
+          smtpd_client_restrictions = "permit_sasl_authenticated,reject";
+          milter_macro_daemon_name = "ORIGINATING";
+        };
         description = "Options for the submission config in master.cf";
-        example = { "smtpd_tls_security_level" = "encrypt";
-                    "smtpd_sasl_auth_enable" = "yes";
-                    "smtpd_sasl_type" = "dovecot";
-                    "smtpd_client_restrictions" = "permit_sasl_authenticated,reject";
-                    "milter_macro_daemon_name" = "ORIGINATING";
-                  };
       };
 
       setSendmail = mkOption {
@@ -352,6 +482,25 @@ in
         ";
       };
 
+      aliasMapType = mkOption {
+        type = with types; enum [ "hash" "regexp" "pcre" ];
+        default = "hash";
+        example = "regexp";
+        description = "The format the alias map should have. Use regexp if you want to use regular expressions.";
+      };
+
+      config = mkOption {
+        type = with types; attrsOf (either bool (either str (listOf str)));
+        default = defaultConf;
+        description = ''
+          The main.cf configuration file as key value set.
+        '';
+        example = {
+          mail_owner = "postfix";
+          smtp_use_tls = true;
+        };
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
@@ -395,6 +544,14 @@ in
         ";
       };
 
+      virtualMapType = mkOption {
+        type = types.enum ["hash" "regexp" "pcre"];
+        default = "hash";
+        description = ''
+          What type of virtual alias map file to use. Use <literal>"regexp"</literal> for regular expressions.
+        '';
+      };
+
       transport = mkOption {
         default = "";
         description = "
@@ -413,6 +570,22 @@ in
         description = "contents of check_client_access for overriding dnsBlacklists";
       };
 
+      masterConfig = mkOption {
+        type = types.attrsOf (types.submodule masterCfOptions);
+        default = {};
+        example =
+          { submission = {
+              type = "inet";
+              args = [ "-o" "smtpd_tls_security_level=encrypt" ];
+            };
+          };
+        description = ''
+          An attribute set of service options, which correspond to the service
+          definitions usually done within the Postfix
+          <filename>master.cf</filename> file.
+        '';
+      };
+
       extraMasterConf = mkOption {
         type = types.lines;
         default = "";
@@ -420,6 +593,27 @@ in
         description = "Extra lines to append to the generated master.cf file.";
       };
 
+      enableHeaderChecks = mkOption {
+        type = types.bool;
+        default = false;
+        example = true;
+        description = "Whether to enable postfix header checks";
+      };
+
+      headerChecks = mkOption {
+        type = types.listOf (types.submodule headerCheckOptions);
+        default = [];
+        example = [ { pattern = "/^X-Spam-Flag:/"; action = "REDIRECT spam@example.com"; } ];
+        description = "Postfix header checks.";
+      };
+
+      extraHeaderChecks = mkOption {
+        type = types.lines;
+        default = "";
+        example = "/^X-Spam-Flag:/ REDIRECT spam@example.com";
+        description = "Extra lines to /etc/postfix/header_checks file.";
+      };
+
       aliasFiles = mkOption {
         type = types.attrsOf types.path;
         default = {};
@@ -530,6 +724,101 @@ in
             ${pkgs.postfix}/bin/postfix set-permissions config_directory=/var/lib/postfix/conf
           '';
         };
+
+      services.postfix.masterConfig = {
+        smtp_inet = {
+          name = "smtp";
+          type = "inet";
+          private = false;
+          command = "smtpd";
+        };
+        pickup = {
+          private = false;
+          wakeup = 60;
+          maxproc = 1;
+        };
+        cleanup = {
+          private = false;
+          maxproc = 0;
+        };
+        qmgr = {
+          private = false;
+          wakeup = 300;
+          maxproc = 1;
+        };
+        tlsmgr = {
+          wakeup = 1000;
+          wakeupUnusedComponent = false;
+          maxproc = 1;
+        };
+        rewrite = {
+          command = "trivial-rewrite";
+        };
+        bounce = {
+          maxproc = 0;
+        };
+        defer = {
+          maxproc = 0;
+          command = "bounce";
+        };
+        trace = {
+          maxproc = 0;
+          command = "bounce";
+        };
+        verify = {
+          maxproc = 1;
+        };
+        flush = {
+          private = false;
+          wakeup = 1000;
+          wakeupUnusedComponent = false;
+          maxproc = 0;
+        };
+        proxymap = {
+          command = "proxymap";
+        };
+        proxywrite = {
+          maxproc = 1;
+          command = "proxymap";
+        };
+        showq = {
+          private = false;
+        };
+        error = {};
+        retry = {
+          command = "error";
+        };
+        discard = {};
+        local = {
+          privileged = true;
+        };
+        virtual = {
+          privileged = true;
+        };
+        lmtp = {
+        };
+        anvil = {
+          maxproc = 1;
+        };
+        scache = {
+          maxproc = 1;
+        };
+      } // optionalAttrs cfg.enableSubmission {
+        submission = {
+          type = "inet";
+          private = false;
+          command = "smtpd";
+          args = let
+            mkKeyVal = opt: val: [ "-o" (opt + "=" + val) ];
+          in concatLists (mapAttrsToList mkKeyVal cfg.submissionOptions);
+        };
+      } // optionalAttrs cfg.enableSmtp {
+        smtp = {};
+        relay = {
+          command = "smtp";
+          args = [ "-o" "smtp_fallback_relay=" ];
+        };
+      };
     }
 
     (mkIf haveAliases {
@@ -541,9 +830,17 @@ in
     (mkIf haveVirtual {
       services.postfix.mapFiles."virtual" = virtualFile;
     })
+    (mkIf cfg.enableHeaderChecks {
+      services.postfix.mapFiles."header_checks" = headerChecksFile;
+    })
     (mkIf (cfg.dnsBlacklists != []) {
       services.postfix.mapFiles."client_access" = checkClientAccessFile;
     })
+    (mkIf (cfg.extraConfig != "") {
+      warnings = [ "The services.postfix.extraConfig option was deprecated. Please use services.postfix.config instead." ];
+    })
+    (mkIf (cfg.extraMasterConf != "") {
+      warnings = [ "The services.postfix.extraMasterConf option was deprecated. Please use services.postfix.masterConfig instead." ];
+    })
   ]);
-
 }
diff --git a/nixos/modules/services/misc/airsonic.nix b/nixos/modules/services/misc/airsonic.nix
new file mode 100644
index 000000000000..b92104787a56
--- /dev/null
+++ b/nixos/modules/services/misc/airsonic.nix
@@ -0,0 +1,117 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.airsonic;
+in {
+  options = {
+
+    services.airsonic = {
+      enable = mkEnableOption "Airsonic, the Free and Open Source media streaming server (fork of Subsonic and Libresonic)";
+
+      user = mkOption {
+        type = types.str;
+        default = "airsonic";
+        description = "User account under which airsonic runs.";
+      };
+
+      home = mkOption {
+        type = types.path;
+        default = "/var/lib/airsonic";
+        description = ''
+          The directory where Airsonic will create files.
+          Make sure it is writable.
+        '';
+      };
+
+      listenAddress = mkOption {
+        type = types.string;
+        default = "127.0.0.1";
+        description = ''
+          The host name or IP address on which to bind Airsonic.
+          Only relevant if you have multiple network interfaces and want
+          to make Airsonic available on only one of them. The default value
+          will bind Airsonic to all available network interfaces.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 4040;
+        description = ''
+          The port on which Airsonic will listen for
+          incoming HTTP traffic. Set to 0 to disable.
+        '';
+      };
+
+      contextPath = mkOption {
+        type = types.path;
+        default = "/";
+        description = ''
+          The context path, i.e., the last part of the Airsonic
+          URL. Typically '/' or '/airsonic'. Default '/'
+        '';
+      };
+
+      maxMemory = mkOption {
+        type = types.int;
+        default = 100;
+        description = ''
+          The memory limit (max Java heap size) in megabytes.
+          Default: 100
+        '';
+      };
+
+      transcoders = mkOption {
+        type = types.listOf types.path;
+        default = [ "${pkgs.ffmpeg.bin}/bin/ffmpeg" ];
+        defaultText= [ "\${pkgs.ffmpeg.bin}/bin/ffmpeg" ];
+        description = ''
+          List of paths to transcoder executables that should be accessible
+          from Airsonic. Symlinks will be created to each executable inside
+          ${cfg.home}/transcoders.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.airsonic = {
+      description = "Airsonic Media Server";
+      after = [ "local-fs.target" "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        # Install transcoders.
+        rm -rf ${cfg.home}/transcode
+        mkdir -p ${cfg.home}/transcode
+        for exe in ${toString cfg.transcoders}; do
+          ln -sf "$exe" ${cfg.home}/transcode
+        done
+      '';
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.jre}/bin/java -Xmx${toString cfg.maxMemory}m \
+          -Dairsonic.home=${cfg.home} \
+          -Dserver.address=${cfg.listenAddress} \
+          -Dserver.port=${toString cfg.port} \
+          -Dairsonic.contextPath=${cfg.contextPath} \
+          -Djava.awt.headless=true \
+          -verbose:gc \
+          -jar ${pkgs.airsonic}/webapps/airsonic.war
+        '';
+        Restart = "always";
+        User = "airsonic";
+        UMask = "0022";
+      };
+    };
+
+    users.extraUsers.airsonic = {
+      description = "Airsonic service user";
+      name = cfg.user;
+      home = cfg.home;
+      createHome = true;
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/autofs.nix b/nixos/modules/services/misc/autofs.nix
index 40b48f70f7ed..f1742177326a 100644
--- a/nixos/modules/services/misc/autofs.nix
+++ b/nixos/modules/services/misc/autofs.nix
@@ -20,10 +20,10 @@ in
 
       enable = mkOption {
         default = false;
-        description = "
+        description = ''
           Mount filesystems on demand. Unmount them automatically.
           You may also be interested in afuse.
-        ";
+        '';
       };
 
       autoMaster = mkOption {
@@ -45,10 +45,9 @@ in
             /auto file:''${mapConf}
           '''
         '';
-        description = "
-          file contents of /etc/auto.master. See man auto.master
-          See man 5 auto.master and man 5 autofs.
-        ";
+        description = ''
+          Contents of <literal>/etc/auto.master</literal> file. See <command>auto.master(5)</command> and <command>autofs(5)</command>.
+        '';
       };
 
       timeout = mkOption {
@@ -58,9 +57,9 @@ in
 
       debug = mkOption {
         default = false;
-        description = "
-        pass -d and -7 to automount and write log to /var/log/autofs
-        ";
+        description = ''
+          Pass -d and -7 to automount and write log to the system journal.
+        '';
       };
 
     };
diff --git a/nixos/modules/services/misc/autorandr.nix b/nixos/modules/services/misc/autorandr.nix
index 792a4c8375d9..3020130ad1f6 100644
--- a/nixos/modules/services/misc/autorandr.nix
+++ b/nixos/modules/services/misc/autorandr.nix
@@ -30,4 +30,5 @@ in {
 
   };
 
+  meta.maintainers = with maintainers; [ gnidorah ];
 }
diff --git a/nixos/modules/services/misc/bepasty.nix b/nixos/modules/services/misc/bepasty.nix
index 4d78cddcb54f..c499e428af35 100644
--- a/nixos/modules/services/misc/bepasty.nix
+++ b/nixos/modules/services/misc/bepasty.nix
@@ -3,7 +3,7 @@
 with lib;
 let
   gunicorn = pkgs.pythonPackages.gunicorn;
-  bepasty = pkgs.pythonPackages.bepasty-server;
+  bepasty = pkgs.bepasty;
   gevent = pkgs.pythonPackages.gevent;
   python = pkgs.pythonPackages.python;
   cfg = config.services.bepasty;
diff --git a/nixos/modules/services/misc/calibre-server.nix b/nixos/modules/services/misc/calibre-server.nix
index a920aa22ccdf..6b19f780ec0c 100644
--- a/nixos/modules/services/misc/calibre-server.nix
+++ b/nixos/modules/services/misc/calibre-server.nix
@@ -42,7 +42,7 @@ in
         serviceConfig = {
           User = "calibre-server";
           Restart = "always";
-          ExecStart = "${pkgs.calibre}/bin/calibre-server --with-library=${cfg.libraryDir}";
+          ExecStart = "${pkgs.calibre}/bin/calibre-server ${cfg.libraryDir}";
         };
 
       };
diff --git a/nixos/modules/services/misc/exhibitor.nix b/nixos/modules/services/misc/exhibitor.nix
new file mode 100644
index 000000000000..600bd780e7b0
--- /dev/null
+++ b/nixos/modules/services/misc/exhibitor.nix
@@ -0,0 +1,418 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.exhibitor;
+  exhibitor = cfg.package;
+  exhibitorConfig = ''
+    zookeeper-install-directory=${cfg.baseDir}/zookeeper
+    zookeeper-data-directory=${cfg.zkDataDir}
+    zookeeper-log-directory=${cfg.zkLogDir}
+    zoo-cfg-extra=${cfg.zkExtraCfg}
+    client-port=${toString cfg.zkClientPort}
+    connect-port=${toString cfg.zkConnectPort}
+    election-port=${toString cfg.zkElectionPort}
+    cleanup-period-ms=${toString cfg.zkCleanupPeriod}
+    servers-spec=${concatStringsSep "," cfg.zkServersSpec}
+    auto-manage-instances=${toString cfg.autoManageInstances}
+    ${cfg.extraConf}
+  '';
+  # NB: toString rather than lib.boolToString on cfg.autoManageInstances is intended.
+  # Exhibitor tests if it's an integer not equal to 0, so the empty string (toString false)
+  # will operate in the same fashion as a 0.
+  configDir = pkgs.writeTextDir "exhibitor.properties" exhibitorConfig;
+  cliOptionsCommon = {
+    configtype = cfg.configType;
+    defaultconfig = "${configDir}/exhibitor.properties";
+    port = toString cfg.port;
+    hostname = cfg.hostname;
+    headingtext = if (cfg.headingText != null) then (lib.escapeShellArg cfg.headingText) else null;
+    nodemodification = lib.boolToString cfg.nodeModification;
+    configcheckms = toString cfg.configCheckMs;
+    jquerystyle = cfg.jqueryStyle;
+    loglines = toString cfg.logLines;
+    servo = lib.boolToString cfg.servo;
+    timeout = toString cfg.timeout;
+  };
+  s3CommonOptions = { s3region = cfg.s3Region; s3credentials = cfg.s3Credentials; };
+  cliOptionsPerConfig = {
+    s3 = {
+      s3config = "${cfg.s3Config.bucketName}:${cfg.s3Config.objectKey}";
+      s3configprefix = cfg.s3Config.configPrefix;
+    };
+    zookeeper = {
+      zkconfigconnect = concatStringsSep "," cfg.zkConfigConnect;
+      zkconfigexhibitorpath = cfg.zkConfigExhibitorPath;
+      zkconfigpollms = toString cfg.zkConfigPollMs;
+      zkconfigretry = "${toString cfg.zkConfigRetry.sleepMs}:${toString cfg.zkConfigRetry.retryQuantity}";
+      zkconfigzpath = cfg.zkConfigZPath;
+      zkconfigexhibitorport = toString cfg.zkConfigExhibitorPort; # NB: This might be null
+    };
+    file = {
+      fsconfigdir = cfg.fsConfigDir;
+      fsconfiglockprefix = cfg.fsConfigLockPrefix;
+      fsConfigName = fsConfigName;
+    };
+    none = {
+      noneconfigdir = configDir;
+    };
+  };
+  cliOptions = concatStringsSep " " (mapAttrsToList (k: v: "--${k} ${v}") (filterAttrs (k: v: v != null && v != "") (cliOptionsCommon //
+               cliOptionsPerConfig."${cfg.configType}" //
+               s3CommonOptions //
+               optionalAttrs cfg.s3Backup { s3backup = "true"; } //
+               optionalAttrs cfg.fileSystemBackup { filesystembackup = "true"; }
+               )));
+in
+{
+  options = {
+    services.exhibitor = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "
+          Whether to enable the exhibitor server.
+        ";
+      };
+      # See https://github.com/soabase/exhibitor/wiki/Running-Exhibitor for what these mean
+      # General options for any type of config
+      port = mkOption {
+        type = types.int;
+        default = 8080;
+        description = ''
+          The port for exhibitor to listen on and communicate with other exhibitors.
+        '';
+      };
+      baseDir = mkOption {
+        type = types.str;
+        default = "/var/exhibitor";
+        description = ''
+          Baseline directory for exhibitor runtime config.
+        '';
+      };
+      configType = mkOption {
+        type = types.enum [ "file" "s3" "zookeeper" "none" ];
+        description = ''
+          Which configuration type you want to use. Additional config will be
+          required depending on which type you are using.
+        '';
+      };
+      hostname = mkOption {
+        type = types.nullOr types.str;
+        description = ''
+          Hostname to use and advertise
+        '';
+        default = null;
+      };
+      nodeModification = mkOption {
+        type = types.bool;
+        description = ''
+          Whether the Explorer UI will allow nodes to be modified (use with caution).
+        '';
+        default = true;
+      };
+      configCheckMs = mkOption {
+        type = types.int;
+        description = ''
+          Period (ms) to check for shared config updates.
+        '';
+        default = 30000;
+      };
+      headingText = mkOption {
+        type = types.nullOr types.str;
+        description = ''
+          Extra text to display in UI header
+        '';
+        default = null;
+      };
+      jqueryStyle = mkOption {
+        type = types.enum [ "red" "black" "custom" ];
+        description = ''
+          Styling used for the JQuery-based UI.
+        '';
+        default = "red";
+      };
+      logLines = mkOption {
+        type = types.int;
+        description = ''
+        Max lines of logging to keep in memory for display.
+        '';
+        default = 1000;
+      };
+      servo = mkOption {
+        type = types.bool;
+        description = ''
+          ZooKeeper will be queried once a minute for its state via the 'mntr' four
+          letter word (this requires ZooKeeper 3.4.x+). Servo will be used to publish
+          this data via JMX.
+        '';
+        default = false;
+      };
+      timeout = mkOption {
+        type = types.int;
+        description = ''
+          Connection timeout (ms) for ZK connections.
+        '';
+        default = 30000;
+      };
+      autoManageInstances = mkOption {
+        type = types.bool;
+        description = ''
+          Automatically manage ZooKeeper instances in the ensemble
+        '';
+        default = false;
+      };
+      zkDataDir = mkOption {
+        type = types.str;
+        default = "${cfg.baseDir}/zkData";
+        description = ''
+          The Zookeeper data directory
+        '';
+      };
+      zkLogDir = mkOption {
+        type = types.path;
+        default = "${cfg.baseDir}/zkLogs";
+        description = ''
+          The Zookeeper logs directory
+        '';
+      };
+      extraConf = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Extra Exhibitor configuration to put in the ZooKeeper config file.
+        '';
+      };
+      zkExtraCfg = mkOption {
+        type = types.str;
+        default = ''initLimit=5&syncLimit=2&tickTime=2000'';
+        description = ''
+          Extra options to pass into Zookeeper
+        '';
+      };
+      zkClientPort = mkOption {
+        type = types.int;
+        default = 2181;
+        description = ''
+          Zookeeper client port
+        '';
+      };
+      zkConnectPort = mkOption {
+        type = types.int;
+        default = 2888;
+        description = ''
+          The port to use for followers to talk to each other.
+        '';
+      };
+      zkElectionPort = mkOption {
+        type = types.int;
+        default = 3888;
+        description = ''
+          The port for Zookeepers to use for leader election.
+        '';
+      };
+      zkCleanupPeriod = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          How often (in milliseconds) to run the Zookeeper log cleanup task.
+        '';
+      };
+      zkServersSpec = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Zookeeper server spec for all servers in the ensemble.
+        '';
+        example = [ "S:1:zk1.example.com" "S:2:zk2.example.com" "S:3:zk3.example.com" "O:4:zk-observer.example.com" ];
+      };
+
+      # Backup options
+      s3Backup = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable backups to S3
+        '';
+      };
+      fileSystemBackup = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enables file system backup of ZooKeeper log files
+        '';
+      };
+
+      # Options for using zookeeper configType
+      zkConfigConnect = mkOption {
+        type = types.listOf types.str;
+        description = ''
+          The initial connection string for ZooKeeper shared config storage
+        '';
+        example = ["host1:2181" "host2:2181"];
+      };
+      zkConfigExhibitorPath = mkOption {
+        type = types.string;
+        description = ''
+          If the ZooKeeper shared config is also running Exhibitor, the URI path for the REST call
+        '';
+        default = "/";
+      };
+      zkConfigExhibitorPort = mkOption {
+        type = types.nullOr types.int;
+        description = ''
+          If the ZooKeeper shared config is also running Exhibitor, the port that
+          Exhibitor is listening on. IMPORTANT: if this value is not set it implies
+          that Exhibitor is not being used on the ZooKeeper shared config.
+        '';
+      };
+      zkConfigPollMs = mkOption {
+        type = types.int;
+        description = ''
+          The period in ms to check for changes in the config ensemble
+        '';
+        default = 10000;
+      };
+      zkConfigRetry = {
+        sleepMs = mkOption {
+          type = types.int;
+          default = 1000;
+          description = ''
+            Retry sleep time connecting to the ZooKeeper config
+          '';
+        };
+        retryQuantity = mkOption {
+          type = types.int;
+          default = 3;
+          description = ''
+            Retries connecting to the ZooKeeper config
+          '';
+        };
+      };
+      zkConfigZPath = mkOption {
+        type = types.str;
+        description = ''
+          The base ZPath that Exhibitor should use
+        '';
+        example = "/exhibitor/config";
+      };
+
+      # Config options for s3 configType
+      s3Config = {
+        bucketName = mkOption {
+          type = types.str;
+          description = ''
+            Bucket name to store config
+          '';
+        };
+        objectKey = mkOption {
+          type = types.str;
+          description = ''
+            S3 key name to store the config
+          '';
+        };
+        configPrefix = mkOption {
+          type = types.str;
+          description = ''
+            When using AWS S3 shared config files, the prefix to use for values such as locks
+          '';
+          default = "exhibitor-";
+        };
+      };
+
+      # The next two are used for either s3backup or s3 configType
+      s3Credentials = mkOption {
+        type = types.nullOr types.path;
+        description = ''
+          Optional credentials to use for s3backup or s3config. Argument is the path
+          to an AWS credential properties file with two properties:
+          com.netflix.exhibitor.s3.access-key-id and com.netflix.exhibitor.s3.access-secret-key
+        '';
+        default = null;
+      };
+      s3Region = mkOption {
+        type = types.nullOr types.str;
+        description = ''
+          Optional region for S3 calls
+        '';
+        default = null;
+      };
+
+      # Config options for file config type
+      fsConfigDir = mkOption {
+        type = types.path;
+        description = ''
+          Directory to store Exhibitor properties (cannot be used with s3config).
+          Exhibitor uses file system locks so you can specify a shared location
+          so as to enable complete ensemble management.
+        '';
+      };
+      fsConfigLockPrefix = mkOption {
+        type = types.str;
+        description = ''
+          A prefix for a locking mechanism used in conjunction with fsconfigdir
+        '';
+        default = "exhibitor-lock-";
+      };
+      fsConfigName = mkOption {
+        type = types.str;
+        description = ''
+          The name of the file to store config in
+        '';
+        default = "exhibitor.properties";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.exhibitor = {
+      description = "Exhibitor Daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      environment = {
+        ZOO_LOG_DIR = cfg.baseDir;
+      };
+      serviceConfig = {
+        /***
+          Exhibitor is a bit un-nixy. It wants to present to you a user interface in order to
+          mutate the configuration of both itself and ZooKeeper, and to coordinate changes
+          among the members of the Zookeeper ensemble. I'm going for a different approach here,
+          which is to manage all the configuration via nix and have it write out the configuration
+          files that exhibitor will use, and to reduce the amount of inter-exhibitor orchestration.
+        ***/
+        ExecStart = ''
+          ${pkgs.exhibitor}/bin/startExhibitor.sh ${cliOptions}
+        '';
+        User = "zookeeper";
+        PermissionsStartOnly = true;
+      };
+      # This is a bit wonky, but the reason for this is that Exhibitor tries to write to
+      # ${cfg.baseDir}/zookeeper/bin/../conf/zoo.cfg
+      # I want everything but the conf directory to be in the immutable nix store, and I want defaults
+      # from the nix store
+      # If I symlink the bin directory in, then bin/../ will resolve to the parent of the symlink in the
+      # immutable nix store. Bind mounting a writable conf over the existing conf might work, but it gets very
+      # messy with trying to copy the existing out into a mutable store.
+      # Another option is to try to patch upstream exhibitor, but the current package just pulls down the
+      # prebuild JARs off of Maven, rather than building them ourselves, as Maven support in Nix isn't
+      # very mature. So, it seems like a reasonable compromise is to just copy out of the immutable store
+      # just before starting the service, so we're running binaries from the immutable store, but we work around
+      # Exhibitor's desire to mutate its current installation.
+      preStart = ''
+        mkdir -m 0700 -p ${cfg.baseDir}/zookeeper
+        # Not doing a chown -R to keep the base ZK files owned by root
+        chown zookeeper ${cfg.baseDir} ${cfg.baseDir}/zookeeper
+        cp -Rf ${pkgs.zookeeper}/* ${cfg.baseDir}/zookeeper
+        chown -R zookeeper ${cfg.baseDir}/zookeeper/conf
+        chmod -R u+w ${cfg.baseDir}/zookeeper/conf
+      '';
+    };
+    users.extraUsers = singleton {
+      name = "zookeeper";
+      uid = config.ids.uids.zookeeper;
+      description = "Zookeeper daemon user";
+      home = cfg.baseDir;
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/fstrim.nix b/nixos/modules/services/misc/fstrim.nix
index e89366cbafed..15f283f093c0 100644
--- a/nixos/modules/services/misc/fstrim.nix
+++ b/nixos/modules/services/misc/fstrim.nix
@@ -42,4 +42,5 @@ in {
 
   };
 
+  meta.maintainers = with maintainers; [ gnidorah ];
 }
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index dcff9bc783a2..d6e82214a5e5 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -490,6 +490,8 @@ in {
       environment.GITLAB_SHELL_CONFIG_PATH = gitlabEnv.GITLAB_SHELL_CONFIG_PATH;
       path = with pkgs; [
         gitAndTools.git
+        gnutar
+        gzip
         openssh
         gitlab-workhorse
       ];
diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix
index 622607f3b32d..515864ec2e2d 100644
--- a/nixos/modules/services/misc/nixos-manual.nix
+++ b/nixos/modules/services/misc/nixos-manual.nix
@@ -62,8 +62,7 @@ let
     name = "nixos-manual";
     desktopName = "NixOS Manual";
     genericName = "View NixOS documentation in a web browser";
-    # TODO: find a better icon (Nix logo + help overlay?)
-    icon = "system-help";
+    icon = "nix-snowflake";
     exec = "${helpScript}/bin/nixos-help";
     categories = "System";
   };
@@ -115,7 +114,7 @@ in
 
     environment.systemPackages =
       [ manual.manual helpScript ]
-      ++ optional config.services.xserver.enable desktopItem
+      ++ optionals config.services.xserver.enable [desktopItem pkgs.nixos-icons]
       ++ optional config.programs.man.enable manual.manpages;
 
     boot.extraTTYs = mkIf cfg.showManual ["tty${toString cfg.ttyNumber}"];
diff --git a/nixos/modules/services/misc/ripple-rest.nix b/nixos/modules/services/misc/ripple-rest.nix
deleted file mode 100644
index 49520f68a50a..000000000000
--- a/nixos/modules/services/misc/ripple-rest.nix
+++ /dev/null
@@ -1,110 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.rippleRest;
-
-  configFile = pkgs.writeText "ripple-rest-config.json" (builtins.toJSON {
-    config_version = "2.0.3";
-    debug = cfg.debug;
-    port = cfg.port;
-    host = cfg.host;
-    ssl_enabled = cfg.ssl.enable;
-    ssl = {
-      key_path = cfg.ssl.keyPath;
-      cert_path = cfg.ssl.certPath;
-      reject_unathorized = cfg.ssl.rejectUnathorized;
-    };
-    db_path = cfg.dbPath;
-    max_transaction_fee = cfg.maxTransactionFee;
-    rippled_servers = cfg.rippleds;
-  });
-
-in {
-  options.services.rippleRest = {
-    enable = mkEnableOption "ripple rest";
-
-    debug = mkEnableOption "debug for ripple-rest";
-
-    host = mkOption {
-      description = "Ripple rest host.";
-      default = "localhost";
-      type = types.str;
-    };
-
-    port = mkOption {
-      description = "Ripple rest port.";
-      default = 5990;
-      type = types.int;
-    };
-
-    ssl = {
-      enable = mkEnableOption "ssl";
-
-      keyPath = mkOption {
-        description = "Path to the ripple rest key file.";
-        default = null;
-        type = types.nullOr types.path;
-      };
-
-
-      certPath = mkOption {
-        description = "Path to the ripple rest cert file.";
-        default = null;
-        type = types.nullOr types.path;
-      };
-
-      rejectUnathorized = mkOption {
-        description = "Whether to reject unatohroized.";
-        default = true;
-        type = types.bool;
-      };
-    };
-
-    dbPath = mkOption {
-      description = "Ripple rest database path.";
-      default = "${cfg.dataDir}/ripple-rest.db";
-      type = types.path;
-    };
-
-    maxTransactionFee = mkOption {
-      description = "Ripple rest max transaction fee.";
-      default = 1000000;
-      type = types.int;
-    };
-
-    rippleds = mkOption {
-      description = "List of rippled servers.";
-      default = [
-        "wss://s1.ripple.com:443"
-      ];
-      type = types.listOf types.str;
-    };
-
-    dataDir = mkOption {
-      description = "Ripple rest data directory.";
-      default = "/var/lib/ripple-rest";
-      type = types.path;
-    };
-  };
-
-  config = mkIf (cfg.enable) {
-    systemd.services.ripple-rest = {
-      wantedBy = [ "multi-user.target"];
-      after = ["network.target" ];
-      environment.NODE_PATH="${pkgs.ripple-rest}/lib/node_modules/ripple-rest/node_modules";
-      serviceConfig = {
-        ExecStart = "${pkgs.nodejs}/bin/node ${pkgs.ripple-rest}/lib/node_modules/ripple-rest/server/server.js --config ${configFile}";
-        User = "ripple-rest";
-      };
-    };
-
-    users.extraUsers.postgres = {
-      name = "ripple-rest";
-      uid = config.ids.uids.ripple-rest;
-      createHome = true;
-      home = cfg.dataDir;
-    };
-  };
-}
diff --git a/nixos/modules/services/misc/snapper.nix b/nixos/modules/services/misc/snapper.nix
new file mode 100644
index 000000000000..62b344d11b06
--- /dev/null
+++ b/nixos/modules/services/misc/snapper.nix
@@ -0,0 +1,152 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.snapper;
+in
+
+{
+  options.services.snapper = {
+
+    snapshotInterval = mkOption {
+      type = types.str;
+      default = "hourly";
+      description = ''
+        Snapshot interval.
+
+        The format is described in
+        <citerefentry><refentrytitle>systemd.time</refentrytitle>
+        <manvolnum>7</manvolnum></citerefentry>.
+      '';
+    };
+
+    cleanupInterval = mkOption {
+      type = types.str;
+      default = "1d";
+      description = ''
+        Cleanup interval.
+
+        The format is described in
+        <citerefentry><refentrytitle>systemd.time</refentrytitle>
+        <manvolnum>7</manvolnum></citerefentry>.
+      '';
+    };
+
+    filters = mkOption {
+      type = types.nullOr types.lines;
+      default = null;
+      description = ''
+        Global display difference filter. See man:snapper(8) for more details.
+      '';
+    };
+
+    configs = mkOption {
+      default = { };
+      example = literalExample {
+        "home" = {
+          subvolume = "/home";
+          extraConfig = ''
+            ALLOW_USERS="alice"
+          '';
+        };
+      };
+
+      description = ''
+        Subvolume configuration
+      '';
+
+      type = types.attrsOf (types.submodule {
+        options = {
+          subvolume = mkOption {
+            type = types.path;
+            description = ''
+              Path of the subvolume or mount point.
+              This path is a subvolume and has to contain a subvolume named
+              .snapshots.
+              See also man:snapper(8) section PERMISSIONS.
+            '';
+          };
+
+          fstype = mkOption {
+            type = types.enum [ "btrfs" ];
+            default = "btrfs";
+            description = ''
+              Filesystem type. Only btrfs is stable and tested.
+            '';
+          };
+
+          extraConfig = mkOption {
+            type = types.lines;
+            default = "";
+            description = ''
+              Additional configuration next to SUBVOLUME and FSTYPE.
+              See man:snapper-configs(5).
+            '';
+          };
+        };
+      });
+    };
+  };
+
+  config = mkIf (cfg.configs != {}) (let
+    documentation = [ "man:snapper(8)" "man:snapper-configs(5)" ];
+  in {
+
+    environment = {
+
+      systemPackages = [ pkgs.snapper ];
+
+      # Note: snapper/config-templates/default is only needed for create-config
+      #       which is not the NixOS way to configure.
+      etc = {
+
+        "sysconfig/snapper".text = ''
+          SNAPPER_CONFIGS="${lib.concatStringsSep " " (builtins.attrNames cfg.configs)}"
+        '';
+
+      }
+      // (mapAttrs' (name: subvolume: nameValuePair "snapper/configs/${name}" ({
+        text = ''
+          ${subvolume.extraConfig}
+          FSTYPE="${subvolume.fstype}"
+          SUBVOLUME="${subvolume.subvolume}"
+        '';
+      })) cfg.configs)
+      // (lib.optionalAttrs (cfg.filters != null) {
+        "snapper/filters/default.txt".text = cfg.filters;
+      });
+
+    };
+
+    services.dbus.packages = [ pkgs.snapper ];
+
+    systemd.services.snapper-timeline = {
+      description = "Timeline of Snapper Snapshots";
+      inherit documentation;
+      serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --timeline";
+    };
+
+    systemd.timers.snapper-timeline = {
+      description = "Timeline of Snapper Snapshots";
+      inherit documentation;
+      wantedBy = [ "basic.target" ];
+      timerConfig.OnCalendar = cfg.snapshotInterval;
+    };
+
+    systemd.services.snapper-cleanup = {
+      description = "Cleanup of Snapper Snapshots";
+      inherit documentation;
+      serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --cleanup";
+    };
+
+    systemd.timers.snapper-cleanup = {
+      description = "Cleanup of Snapper Snapshots";
+      inherit documentation;
+      wantedBy = [ "basic.target" ];
+      timerConfig.OnBootSec = "10m";
+      timerConfig.OnUnitActiveSec = cfg.cleanupInterval;
+    };
+  });
+}
+
diff --git a/nixos/modules/services/misc/spice-vdagentd.nix b/nixos/modules/services/misc/spice-vdagentd.nix
index f8133394ffd3..f322ba4cbd58 100644
--- a/nixos/modules/services/misc/spice-vdagentd.nix
+++ b/nixos/modules/services/misc/spice-vdagentd.nix
@@ -23,7 +23,7 @@ in
       '';
       serviceConfig = {
         Type = "forking";
-        ExecStart = "/bin/sh -c '${pkgs.spice-vdagent}/bin/spice-vdagentd'";
+        ExecStart = "${pkgs.spice-vdagent}/bin/spice-vdagentd";
       };
     };
   };
diff --git a/nixos/modules/services/misc/taskserver/helper-tool.py b/nixos/modules/services/misc/taskserver/helper-tool.py
index b97bc1df74f7..22a3d8d5311b 100644
--- a/nixos/modules/services/misc/taskserver/helper-tool.py
+++ b/nixos/modules/services/misc/taskserver/helper-tool.py
@@ -448,6 +448,8 @@ def cli(ctx):
     """
     Manage Taskserver users and certificates
     """
+    if not IS_AUTO_CONFIG:
+        return
     for path in (CA_KEY, CA_CERT, CRL_FILE):
         if not os.path.exists(path):
             msg = "CA setup not done or incomplete, missing file {}."
diff --git a/nixos/modules/services/misc/zookeeper.nix b/nixos/modules/services/misc/zookeeper.nix
index b7bca8b56b28..d85b5e4ec507 100644
--- a/nixos/modules/services/misc/zookeeper.nix
+++ b/nixos/modules/services/misc/zookeeper.nix
@@ -4,7 +4,7 @@ with lib;
 
 let
   cfg = config.services.zookeeper;
-  
+
   zookeeperConfig = ''
     dataDir=${cfg.dataDir}
     clientPort=${toString cfg.port}
@@ -49,7 +49,7 @@ in {
       default = 1;
       type = types.int;
     };
- 
+
     extraConf = mkOption {
       description = "Extra configuration for Zookeeper.";
       type = types.lines;
@@ -119,7 +119,7 @@ in {
         ExecStart = ''
           ${pkgs.jre}/bin/java \
             -cp "${pkgs.zookeeper}/lib/*:${pkgs.zookeeper}/${pkgs.zookeeper.name}.jar:${configDir}" \
-            ${toString cfg.extraCmdLineOptions} \
+            ${escapeShellArgs cfg.extraCmdLineOptions} \
             -Dzookeeper.datadir.autocreate=false \
             ${optionalString cfg.preferIPv4 "-Djava.net.preferIPv4Stack=true"} \
             org.apache.zookeeper.server.quorum.QuorumPeerMain \
diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix
index b26bcba64059..723b04dc0fe9 100644
--- a/nixos/modules/services/monitoring/munin.nix
+++ b/nixos/modules/services/monitoring/munin.nix
@@ -26,7 +26,9 @@ let
 
       for file in $out/*; do
         case "$file" in
-            plugin.sh) continue;;
+            */plugin.sh|*/plugins.history)
+              chmod +x "$file"
+              continue;;
         esac
 
         # read magic makers from the file
diff --git a/nixos/modules/services/monitoring/osquery.nix b/nixos/modules/services/monitoring/osquery.nix
new file mode 100644
index 000000000000..ba0dc4c21768
--- /dev/null
+++ b/nixos/modules/services/monitoring/osquery.nix
@@ -0,0 +1,91 @@
+{ config, lib, pkgs, ... }:
+
+with builtins;
+with lib;
+
+let
+  cfg = config.services.osquery;
+
+in
+
+{
+
+  options = {
+
+    services.osquery = {
+
+      enable = mkEnableOption "osquery";
+
+      loggerPath = mkOption {
+        type = types.path;
+        description = "Base directory used for logging.";
+        default = "/var/log/osquery";
+      };
+
+      pidfile = mkOption {
+        type = types.path;
+        description = "Path used for pid file.";
+        default = "/var/osquery/osqueryd.pidfile";
+      };
+
+      utc = mkOption {
+        type = types.bool;
+        description = "Attempt to convert all UNIX calendar times to UTC.";
+        default = true;
+      };
+
+      databasePath = mkOption {
+        type = types.path;
+        description = "Path used for database file.";
+        default = "/var/osquery/osquery.db";
+      };
+
+      extraConfig = mkOption {
+        type = types.attrs // {
+          merge = loc: foldl' (res: def: recursiveUpdate res def.value) {};
+        };
+        description = "Extra config to be recursively merged into the JSON config file.";
+        default = { };
+      };
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.osquery ];
+
+    environment.etc."osquery/osquery.conf".text = toJSON (
+      recursiveUpdate {
+        options = {
+          config_plugin = "filesystem";
+          logger_plugin = "filesystem";
+          logger_path = cfg.loggerPath;
+          database_path = cfg.databasePath;
+          utc = cfg.utc;
+        };
+      } cfg.extraConfig
+    );
+
+    systemd.services.osqueryd = {
+      description = "The osquery Daemon";
+      after = [ "network.target" "syslog.service" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.osquery ];
+      preStart = ''
+        mkdir -p ${escapeShellArg cfg.loggerPath}
+        mkdir -p "$(dirname ${escapeShellArg cfg.pidfile})"
+        mkdir -p "$(dirname ${escapeShellArg cfg.databasePath})"
+      '';
+      serviceConfig = {
+        TimeoutStartSec = 0;
+        ExecStart = "${pkgs.osquery}/bin/osqueryd --logger_path ${escapeShellArg cfg.loggerPath} --pidfile ${escapeShellArg cfg.pidfile} --database_path ${escapeShellArg cfg.databasePath}";
+        KillMode = "process";
+        KillSignal = "SIGTERM";
+        Restart = "on-failure";
+      };
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix b/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
index 388e4d4ac01d..ce2e1cf2d74b 100644
--- a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
+++ b/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
@@ -57,8 +57,8 @@ in {
         AmbientCapabilities = [ "CAP_NET_RAW" ]; # for ping probes
         ExecStart = ''
           ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \
-            -web.listen-address :${toString cfg.port} \
-            -config.file ${cfg.configFile} \
+            --web.listen-address :${toString cfg.port} \
+            --config.file ${cfg.configFile} \
             ${concatStringsSep " \\\n  " cfg.extraFlags}
         '';
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
diff --git a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix b/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
index e3059e485098..0a56d6ae95a5 100644
--- a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
+++ b/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
@@ -83,6 +83,7 @@ in {
       description = "Prometheus exporter for UniFi Controller metrics";
       unitConfig.Documentation = "https://github.com/mdlayher/unifi_exporter";
       wantedBy = [ "multi-user.target" ];
+      after = optional config.services.unifi.enable "unifi.service";
       serviceConfig = {
         User = "nobody";
         Restart = "always";
diff --git a/nixos/modules/services/network-filesystems/ipfs.nix b/nixos/modules/services/network-filesystems/ipfs.nix
index 10c1d751ac5d..cd320c5c4620 100644
--- a/nixos/modules/services/network-filesystems/ipfs.nix
+++ b/nixos/modules/services/network-filesystems/ipfs.nix
@@ -1,15 +1,19 @@
 { config, lib, pkgs, ... }:
-
 with lib;
-
 let
   inherit (pkgs) ipfs runCommand makeWrapper;
 
   cfg = config.services.ipfs;
 
-  ipfsFlags = ''${if cfg.autoMigrate then "--migrate" else ""} ${if cfg.enableGC then "--enable-gc" else ""} ${toString cfg.extraFlags}'';
+  ipfsFlags = toString ([
+    #(optionalString  cfg.autoMount                   "--mount")
+    (optionalString  cfg.autoMigrate                 "--migrate")
+    (optionalString  cfg.enableGC                    "--enable-gc")
+    (optionalString (cfg.serviceFdlimit != null)     "--manage-fdlimit=false")
+    (optionalString (cfg.defaultMode == "offline")   "--offline")
+    (optionalString (cfg.defaultMode == "norouting") "--routing=none")
+  ] ++ cfg.extraFlags);
 
-  # Before Version 17.09, ipfs would always use "/var/lib/ipfs/.ipfs" as it's dataDir
   defaultDataDir = if versionAtLeast config.system.stateVersion "17.09" then
     "/var/lib/ipfs" else
     "/var/lib/ipfs/.ipfs";
@@ -17,11 +21,48 @@ let
   # Wrapping the ipfs binary with the environment variable IPFS_PATH set to dataDir because we can't set it in the user environment
   wrapped = runCommand "ipfs" { buildInputs = [ makeWrapper ]; } ''
     mkdir -p "$out/bin"
-    makeWrapper "${ipfs}/bin/ipfs" "$out/bin/ipfs" --set IPFS_PATH ${cfg.dataDir}
+    makeWrapper "${ipfs}/bin/ipfs" "$out/bin/ipfs" \
+      --set IPFS_PATH ${cfg.dataDir} \
+      --prefix PATH : /run/wrappers/bin
   '';
-in
 
-{
+
+  commonEnv = {
+    environment.IPFS_PATH = cfg.dataDir;
+    path = [ wrapped ];
+    serviceConfig.User = cfg.user;
+    serviceConfig.Group = cfg.group;
+  };
+
+  baseService = recursiveUpdate commonEnv {
+    wants = [ "ipfs-init.service" ];
+    preStart = ''
+      ipfs --local config Addresses.API ${cfg.apiAddress}
+      ipfs --local config Addresses.Gateway ${cfg.gatewayAddress}
+    '' + optionalString false/*cfg.autoMount*/ ''
+      ipfs --local config Mounts.FuseAllowOther --json true
+      ipfs --local config Mounts.IPFS ${cfg.ipfsMountDir}
+      ipfs --local config Mounts.IPNS ${cfg.ipnsMountDir}
+    '' + concatStringsSep "\n" (collect
+          isString
+          (mapAttrsRecursive
+            (path: value:
+            # Using heredoc below so that the value is never improperly quoted
+            ''
+              read value <<EOF
+              ${builtins.toJSON value}
+              EOF
+              ipfs --local config --json "${concatStringsSep "." path}" "$value"
+            '')
+            cfg.extraConfig)
+        );
+    serviceConfig = {
+      ExecStart = "${wrapped}/bin/ipfs daemon ${ipfsFlags}";
+      Restart = "on-failure";
+      RestartSec = 1;
+    } // optionalAttrs (cfg.serviceFdlimit != null) { LimitNOFILE = cfg.serviceFdlimit; };
+  };
+in {
 
   ###### interface
 
@@ -49,6 +90,12 @@ in
         description = "The data dir for IPFS";
       };
 
+      defaultMode = mkOption {
+        description = "systemd service that is enabled by default";
+        type = types.enum [ "online" "offline" "norouting" ];
+        default = "online";
+      };
+
       autoMigrate = mkOption {
         type = types.bool;
         default = false;
@@ -57,6 +104,24 @@ in
         '';
       };
 
+      #autoMount = mkOption {
+      #  type = types.bool;
+      #  default = false;
+      #  description = "Whether IPFS should try to mount /ipfs and /ipns at startup.";
+      #};
+
+      ipfsMountDir = mkOption {
+        type = types.str;
+        default = "/ipfs";
+        description = "Where to mount the IPFS namespace to";
+      };
+
+      ipnsMountDir = mkOption {
+        type = types.str;
+        default = "/ipns";
+        description = "Where to mount the IPNS namespace to";
+      };
+
       gatewayAddress = mkOption {
         type = types.str;
         default = "/ip4/127.0.0.1/tcp/8080";
@@ -85,11 +150,41 @@ in
         '';
       };
 
+      extraConfig = mkOption {
+        type = types.attrs;
+        description = toString [
+          "Attrset of daemon configuration to set using `ipfs config`, every time the daemon starts."
+          "These are applied last, so may override configuration set by other options in this module."
+          "Keep in mind that this configuration is stateful; i.e., unsetting anything in here does not reset the value to the default!"
+        ];
+        default = {};
+        example = {
+          Datastore.StorageMax = "100GB";
+          Discovery.MDNS.Enabled = false;
+          Bootstrap = [
+            "/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu"
+            "/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm"
+          ];
+          Swarm.AddrFilters = null;
+        };
+
+      };
+
       extraFlags = mkOption {
         type = types.listOf types.str;
         description = "Extra flags passed to the IPFS daemon";
         default = [];
       };
+
+      serviceFdlimit = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          The fdlimit for the IPFS systemd unit or `null` to have the daemon attempt to manage it.
+        '';
+        example = 256*1024;
+      };
+
     };
   };
 
@@ -109,82 +204,57 @@ in
     };
 
     users.extraGroups = mkIf (cfg.group == "ipfs") {
-      ipfs = {
-        gid = config.ids.gids.ipfs;
-      };
+      ipfs.gid = config.ids.gids.ipfs;
     };
 
-    systemd.services.ipfs-init = {
+    systemd.services.ipfs-init = recursiveUpdate commonEnv {
       description = "IPFS Initializer";
 
       after = [ "local-fs.target" ];
       before = [ "ipfs.service" "ipfs-offline.service" ];
 
-      environment.IPFS_PATH = cfg.dataDir;
-
-      path  = [ pkgs.ipfs pkgs.su pkgs.bash ];
-
       preStart = ''
         install -m 0755 -o ${cfg.user} -g ${cfg.group} -d ${cfg.dataDir}
+      '' + optionalString false/*cfg.autoMount*/ ''
+        install -m 0755 -o ${cfg.user} -g ${cfg.group} -d ${cfg.ipfsMountDir}
+        install -m 0755 -o ${cfg.user} -g ${cfg.group} -d ${cfg.ipnsMountDir}
       '';
-      script =  ''
+      script = ''
         if [[ ! -f ${cfg.dataDir}/config ]]; then
-          ${ipfs}/bin/ipfs init ${optionalString cfg.emptyRepo "-e"}
+          ipfs init ${optionalString cfg.emptyRepo "-e"}
         fi
-        ${ipfs}/bin/ipfs --local config Addresses.API ${cfg.apiAddress}
-        ${ipfs}/bin/ipfs --local config Addresses.Gateway ${cfg.gatewayAddress}
       '';
 
       serviceConfig = {
-        User = cfg.user;
-        Group = cfg.group;
         Type = "oneshot";
         RemainAfterExit = true;
         PermissionsStartOnly = true;
       };
     };
 
-    systemd.services.ipfs = {
-      description = "IPFS Daemon";
+    # TODO These 3 definitions possibly be further abstracted through use of a function
+    # like: mutexServices "ipfs" [ "", "offline", "norouting" ] { ... shared conf here ... }
 
-      wantedBy = [ "multi-user.target" ];
+    systemd.services.ipfs = recursiveUpdate baseService {
+      description = "IPFS Daemon";
+      wantedBy = mkIf (cfg.defaultMode == "online") [ "multi-user.target" ];
       after = [ "network.target" "local-fs.target" "ipfs-init.service" ];
-
-      conflicts = [ "ipfs-offline.service" ];
-      wants = [ "ipfs-init.service" ];
-
-      environment.IPFS_PATH = cfg.dataDir;
-
-      path  = [ pkgs.ipfs ];
-
-      serviceConfig = {
-        ExecStart = "${ipfs}/bin/ipfs daemon ${ipfsFlags}";
-        User = cfg.user;
-        Group = cfg.group;
-        Restart = "on-failure";
-        RestartSec = 1;
-      };
+      conflicts = [ "ipfs-offline.service" "ipfs-norouting.service"];
     };
 
-    systemd.services.ipfs-offline = {
+    systemd.services.ipfs-offline = recursiveUpdate baseService {
       description = "IPFS Daemon (offline mode)";
-
+      wantedBy = mkIf (cfg.defaultMode == "offline") [ "multi-user.target" ];
       after = [ "local-fs.target" "ipfs-init.service" ];
+      conflicts = [ "ipfs.service" "ipfs-norouting.service"];
+    };
 
-      conflicts = [ "ipfs.service" ];
-      wants = [ "ipfs-init.service" ];
-
-      environment.IPFS_PATH = cfg.dataDir;
-
-      path  = [ pkgs.ipfs ];
-
-      serviceConfig = {
-        ExecStart = "${ipfs}/bin/ipfs daemon ${ipfsFlags} --offline";
-        User = cfg.user;
-        Group = cfg.group;
-        Restart = "on-failure";
-        RestartSec = 1;
-      };
+    systemd.services.ipfs-norouting = recursiveUpdate baseService {
+      description = "IPFS Daemon (no routing mode)";
+      wantedBy = mkIf (cfg.defaultMode == "norouting") [ "multi-user.target" ];
+      after = [ "local-fs.target" "ipfs-init.service" ];
+      conflicts = [ "ipfs.service" "ipfs-offline.service"];
     };
+
   };
 }
diff --git a/nixos/modules/services/network-filesystems/rsyncd.nix b/nixos/modules/services/network-filesystems/rsyncd.nix
index 2018bfa14a57..054057d52ab1 100644
--- a/nixos/modules/services/network-filesystems/rsyncd.nix
+++ b/nixos/modules/services/network-filesystems/rsyncd.nix
@@ -8,22 +8,21 @@ let
 
   motdFile = builtins.toFile "rsyncd-motd" cfg.motd;
 
-  moduleConfig = name:
-    let module = getAttr name cfg.modules; in
-    "[${name}]\n " + (toString (
-       map
-         (key: "${key} = ${toString (getAttr key module)}\n")
-         (attrNames module)
-    ));
-
-  cfgFile = builtins.toFile "rsyncd.conf"
-    ''
+  foreach = attrs: f:
+    concatStringsSep "\n" (mapAttrsToList f attrs);
+
+  cfgFile = ''
     ${optionalString (cfg.motd != "") "motd file = ${motdFile}"}
     ${optionalString (cfg.address != "") "address = ${cfg.address}"}
     ${optionalString (cfg.port != 873) "port = ${toString cfg.port}"}
     ${cfg.extraConfig}
-    ${toString (map moduleConfig (attrNames cfg.modules))}
-    '';
+    ${foreach cfg.modules (name: module: ''
+      [${name}]
+      ${foreach module (k: v:
+        "${k} = ${v}"
+      )}
+    '')}
+  '';
 in
 
 {
@@ -84,6 +83,24 @@ in
           };
       };
 
+      user = mkOption {
+        type = types.str;
+        default = "root";
+        description = ''
+          The user to run the daemon as.
+          By default the daemon runs as root.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "root";
+        description = ''
+          The group to run the daemon as.
+          By default the daemon runs as root.
+        '';
+      };
+
     };
   };
 
@@ -91,16 +108,17 @@ in
 
   config = mkIf cfg.enable {
 
-    environment.etc = singleton {
-      source = cfgFile;
-      target = "rsyncd.conf";
-    };
+    environment.etc."rsyncd.conf".text = cfgFile;
 
     systemd.services.rsyncd = {
       description = "Rsync daemon";
       wantedBy = [ "multi-user.target" ];
-      serviceConfig.ExecStart = "${pkgs.rsync}/bin/rsync --daemon --no-detach";
+      restartTriggers = [ config.environment.etc."rsyncd.conf".source ];
+      serviceConfig = {
+        ExecStart = "${pkgs.rsync}/bin/rsync --daemon --no-detach";
+        User = cfg.user;
+        Group = cfg.group;
+      };
     };
-
   };
 }
diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix
index 87c4f7a8ebcb..b3b4d0c915ac 100644
--- a/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -243,7 +243,7 @@ in
           };
         };
 
-        security.pam.services.sambda = {};
+        security.pam.services.samba = {};
 
       })
     ];
diff --git a/nixos/modules/services/network-filesystems/tahoe.nix b/nixos/modules/services/network-filesystems/tahoe.nix
index 9815a5434ee3..80b34c48f1d2 100644
--- a/nixos/modules/services/network-filesystems/tahoe.nix
+++ b/nixos/modules/services/network-filesystems/tahoe.nix
@@ -237,13 +237,13 @@ in
               # arguments to $(tahoe start). The node directory must come first,
               # and arguments which alter Twisted's behavior come afterwards.
               ExecStart = ''
-                ${settings.package}/bin/tahoe start ${nodedir} -n -l- --pidfile=${pidfile}
+                ${settings.package}/bin/tahoe start ${lib.escapeShellArg nodedir} -n -l- --pidfile=${lib.escapeShellArg pidfile}
               '';
             };
             preStart = ''
-              if [ \! -d ${nodedir} ]; then
+              if [ ! -d ${lib.escapeShellArg nodedir} ]; then
                 mkdir -p /var/db/tahoe-lafs
-                tahoe create-introducer ${nodedir}
+                tahoe create-introducer ${lib.escapeShellArg nodedir}
               fi
 
               # Tahoe has created a predefined tahoe.cfg which we must now
@@ -252,7 +252,7 @@ in
               # we must do this on every prestart. Fixes welcome.
               # rm ${nodedir}/tahoe.cfg
               # ln -s /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg
-              cp /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg
+              cp /etc/tahoe-lafs/introducer-"${node}".cfg ${lib.escapeShellArg nodedir}/tahoe.cfg
             '';
           });
         users.extraUsers = flip mapAttrs' cfg.introducers (node: _:
@@ -337,13 +337,13 @@ in
               # arguments to $(tahoe start). The node directory must come first,
               # and arguments which alter Twisted's behavior come afterwards.
               ExecStart = ''
-                ${settings.package}/bin/tahoe start ${nodedir} -n -l- --pidfile=${pidfile}
+                ${settings.package}/bin/tahoe start ${lib.escapeShellArg nodedir} -n -l- --pidfile=${lib.escapeShellArg pidfile}
               '';
             };
             preStart = ''
-              if [ \! -d ${nodedir} ]; then
+              if [ ! -d ${lib.escapeShellArg nodedir} ]; then
                 mkdir -p /var/db/tahoe-lafs
-                tahoe create-node --hostname=localhost ${nodedir}
+                tahoe create-node --hostname=localhost ${lib.escapeShellArg nodedir}
               fi
 
               # Tahoe has created a predefined tahoe.cfg which we must now
@@ -351,8 +351,8 @@ in
               # XXX I thought that a symlink would work here, but it doesn't, so
               # we must do this on every prestart. Fixes welcome.
               # rm ${nodedir}/tahoe.cfg
-              # ln -s /etc/tahoe-lafs/${node}.cfg ${nodedir}/tahoe.cfg
-              cp /etc/tahoe-lafs/${node}.cfg ${nodedir}/tahoe.cfg
+              # ln -s /etc/tahoe-lafs/${lib.escapeShellArg node}.cfg ${nodedir}/tahoe.cfg
+              cp /etc/tahoe-lafs/${lib.escapeShellArg node}.cfg ${lib.escapeShellArg nodedir}/tahoe.cfg
             '';
           });
         users.extraUsers = flip mapAttrs' cfg.nodes (node: _:
diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix
index f4d0fc822dea..9ccdacb20e91 100644
--- a/nixos/modules/services/networking/avahi-daemon.nix
+++ b/nixos/modules/services/networking/avahi-daemon.nix
@@ -22,6 +22,7 @@ let
     ${optionalString (interfaces!=null) "allow-interfaces=${concatStringsSep "," interfaces}"}
     ${optionalString (domainName!=null) "domain-name=${domainName}"}
     allow-point-to-point=${yesNo allowPointToPoint}
+    ${optionalString (cacheEntriesMax!=null) "cache-entries-max=${toString cacheEntriesMax}"}
 
     [wide-area]
     enable-wide-area=${yesNo wideArea}
@@ -166,6 +167,15 @@ in
         '';
       };
 
+      cacheEntriesMax = mkOption {
+        default = null;
+        type = types.nullOr types.int;
+        description = ''
+          Number of resource records to be cached per interface. Use 0 to
+          disable caching. Avahi daemon defaults to 4096 if not set.
+        '';
+      };
+
     };
 
   };
diff --git a/nixos/modules/services/networking/bitlbee.nix b/nixos/modules/services/networking/bitlbee.nix
index e72ea20cccee..bd26804788f3 100644
--- a/nixos/modules/services/networking/bitlbee.nix
+++ b/nixos/modules/services/networking/bitlbee.nix
@@ -7,6 +7,10 @@ let
   cfg = config.services.bitlbee;
   bitlbeeUid = config.ids.uids.bitlbee;
 
+  bitlbeePkg = if cfg.libpurple_plugins == []
+  then pkgs.bitlbee
+  else pkgs.bitlbee.override { enableLibPurple = true; };
+
   bitlbeeConfig = pkgs.writeText "bitlbee.conf"
     ''
     [settings]
@@ -25,6 +29,12 @@ let
     ${cfg.extraDefaults}
     '';
 
+  purple_plugin_path =
+    lib.concatMapStringsSep ":"
+      (plugin: "${plugin}/lib/pidgin/")
+      cfg.libpurple_plugins
+    ;
+
 in
 
 {
@@ -90,6 +100,15 @@ in
         '';
       };
 
+      libpurple_plugins = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExample "[ pkgs.purple-matrix ]";
+        description = ''
+          The list of libpurple plugins to install.
+        '';
+      };
+
       configDir = mkOption {
         default = "/var/lib/bitlbee";
         type = types.path;
@@ -144,14 +163,16 @@ in
       };
 
     systemd.services.bitlbee =
-      { description = "BitlBee IRC to other chat networks gateway";
+      {
+        environment.PURPLE_PLUGIN_PATH = purple_plugin_path;
+        description = "BitlBee IRC to other chat networks gateway";
         after = [ "network.target" ];
         wantedBy = [ "multi-user.target" ];
         serviceConfig.User = "bitlbee";
-        serviceConfig.ExecStart = "${pkgs.bitlbee}/sbin/bitlbee -F -n -c ${bitlbeeConfig}";
+        serviceConfig.ExecStart = "${bitlbeePkg}/sbin/bitlbee -F -n -c ${bitlbeeConfig}";
       };
 
-    environment.systemPackages = [ pkgs.bitlbee ];
+    environment.systemPackages = [ bitlbeePkg ];
 
   };
 
diff --git a/nixos/modules/services/networking/coturn.nix b/nixos/modules/services/networking/coturn.nix
index 14e6932d868b..65273a4bf939 100644
--- a/nixos/modules/services/networking/coturn.nix
+++ b/nixos/modules/services/networking/coturn.nix
@@ -320,6 +320,14 @@ in {
         RuntimeDirectory = "turnserver";
         User = "turnserver";
         Group = "turnserver";
+        AmbientCapabilities =
+          mkIf (
+            cfg.listening-port < 1024 ||
+            cfg.alt-listening-port < 1024 ||
+            cfg.tls-listening-port < 1024 ||
+            cfg.alt-tls-listening-port < 1024 ||
+            cfg.min-port < 1024
+          ) "cap_net_bind_service";
         Restart = "on-abort";
       };
     };
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
index 28c96a9baefc..9e56545f746c 100644
--- a/nixos/modules/services/networking/ddclient.nix
+++ b/nixos/modules/services/networking/ddclient.nix
@@ -1,17 +1,33 @@
 { config, pkgs, lib, ... }:
 
 let
-
-  inherit (lib) mkOption mkIf singleton;
-  inherit (pkgs) ddclient;
-
-  stateDir = "/var/spool/ddclient";
-  ddclientUser = "ddclient";
-  ddclientFlags = "-foreground -file ${config.services.ddclient.configFile}";
-  ddclientPIDFile = "${stateDir}/ddclient.pid";
+  cfg = config.services.ddclient;
+  boolToStr = bool: if bool then "yes" else "no";
+
+  configText = ''
+    # This file can be used as a template for configFile or is automatically generated by Nix options.
+    daemon=${toString cfg.interval}
+    cache=${cfg.homeDir}/ddclient.cache
+    pid=/run/ddclient/ddclient.pid
+    foreground=NO
+    use=${cfg.use}
+    login=${cfg.username}
+    password=${cfg.password}
+    protocol=${cfg.protocol}
+    ${let server = cfg.server; in
+      lib.optionalString (server != "") "server=${server}"}
+    ssl=${boolToStr cfg.ssl}
+    wildcard=YES
+    quiet=${boolToStr cfg.quiet}
+    verbose=${boolToStr cfg.verbose}
+    ${cfg.domain}
+    ${cfg.extraConfig}
+  '';
 
 in
 
+with lib;
+
 {
 
   ###### interface
@@ -28,6 +44,12 @@ in
         '';
       };
 
+      homeDir = mkOption {
+        default = "/var/lib/ddclient";
+        type = str;
+        description = "Home directory for the daemon user.";
+      };
+
       domain = mkOption {
         default = "";
         type = str;
@@ -52,6 +74,12 @@ in
         '';
       };
 
+      interval = mkOption {
+        default = 600;
+        type = int;
+        description = "The interval at which to run the check and update.";
+      };
+
       configFile = mkOption {
         default = "/etc/ddclient.conf";
         type = path;
@@ -126,37 +154,24 @@ in
 
   config = mkIf config.services.ddclient.enable {
 
-    environment.systemPackages = [ ddclient ];
+    users = {
+      extraGroups.ddclient.gid = config.ids.gids.ddclient;
 
-    users.extraUsers = singleton {
-      name = ddclientUser;
-      uid = config.ids.uids.ddclient;
-      description = "ddclient daemon user";
-      home = stateDir;
+      extraUsers.ddclient = {
+        uid = config.ids.uids.ddclient;
+        description = "ddclient daemon user";
+        group = "ddclient";
+        home = cfg.homeDir;
+        createHome = true;
+      };
     };
 
     environment.etc."ddclient.conf" = {
-      enable = config.services.ddclient.configFile == "/etc/ddclient.conf";
+      enable = cfg.configFile == "/etc/ddclient.conf";
       uid = config.ids.uids.ddclient;
+      gid = config.ids.gids.ddclient;
       mode = "0600";
-      text = ''
-        # This file can be used as a template for configFile or is automatically generated by Nix options.
-        daemon=600
-        cache=${stateDir}/ddclient.cache
-        pid=${ddclientPIDFile}
-        use=${config.services.ddclient.use}
-        login=${config.services.ddclient.username}
-        password=${config.services.ddclient.password}
-        protocol=${config.services.ddclient.protocol}
-        ${let server = config.services.ddclient.server; in
-          lib.optionalString (server != "") "server=${server}"}
-        ssl=${if config.services.ddclient.ssl then "yes" else "no"}
-        wildcard=YES
-        quiet=${if config.services.ddclient.quiet then "yes" else "no"}
-        verbose=${if config.services.ddclient.verbose then "yes" else "no"}
-        ${config.services.ddclient.domain}
-        ${config.services.ddclient.extraConfig}
-      '';
+      text = configText;
     };
 
     systemd.services.ddclient = {
@@ -166,17 +181,14 @@ in
       restartTriggers = [ config.environment.etc."ddclient.conf".source ];
 
       serviceConfig = {
-        # Uncomment this if too many problems occur:
-        # Type = "forking";
-        User = ddclientUser;
-        Group = "nogroup"; #TODO get this to work
-        PermissionsStartOnly = "true";
-        PIDFile = ddclientPIDFile;
-        ExecStartPre = ''
-          ${pkgs.stdenv.shell} -c "${pkgs.coreutils}/bin/mkdir -m 0755 -p ${stateDir} && ${pkgs.coreutils}/bin/chown ${ddclientUser} ${stateDir}"
-        '';
-        ExecStart = "${ddclient}/bin/ddclient ${ddclientFlags}";
-        #ExecStartPost = "${pkgs.coreutils}/bin/rm -r ${stateDir}"; # Should we have this?
+        RuntimeDirectory = "ddclient";
+        # we cannot run in forking mode as it swallows all the program output
+        Type = "simple";
+        User = "ddclient";
+        Group = "ddclient";
+        ExecStart = "${lib.getBin pkgs.ddclient}/bin/ddclient -foreground -file ${cfg.configFile}";
+        ProtectSystem = "full";
+        PrivateTmp = true;
       };
     };
   };
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix
index 077aeca457ea..ed658258c7f9 100644
--- a/nixos/modules/services/networking/dnscrypt-proxy.nix
+++ b/nixos/modules/services/networking/dnscrypt-proxy.nix
@@ -82,14 +82,13 @@ in
       };
 
       resolverName = mkOption {
-        default = "dnscrypt.eu-nl";
+        default = "random";
+        example = "dnscrypt.eu-nl";
         type = types.nullOr types.str;
         description = ''
           The name of the DNSCrypt resolver to use, taken from
-          <filename>${resolverList}</filename>.  The default
-          resolver is located in Holland, supports DNS security
-          extensions, and <emphasis>claims</emphasis> to not
-          keep logs.
+          <filename>${resolverList}</filename>.  The default is to
+          pick a random non-logging resolver that supports DNSSEC.
         '';
       };
 
diff --git a/nixos/modules/services/networking/firefox/sync-server.nix b/nixos/modules/services/networking/firefox/sync-server.nix
index 3a95b9c4ec94..a9f3fd65d76b 100644
--- a/nixos/modules/services/networking/firefox/sync-server.nix
+++ b/nixos/modules/services/networking/firefox/sync-server.nix
@@ -4,6 +4,10 @@ with lib;
 
 let
   cfg = config.services.firefox.syncserver;
+
+  defaultDbLocation = "/var/db/firefox-sync-server/firefox-sync-server.db";
+  defaultSqlUri = "sqlite:///${defaultDbLocation}";
+
   syncServerIni = pkgs.writeText "syncserver.ini" ''
     [DEFAULT]
     overrides = ${cfg.privateConfig}
@@ -25,6 +29,7 @@ let
     backend = tokenserver.verifiers.LocalVerifier
     audiences = ${removeSuffix "/" cfg.publicUrl}
   '';
+
 in
 
 {
@@ -65,6 +70,18 @@ in
         '';
       };
 
+      user = mkOption {
+        type = types.str;
+        default = "syncserver";
+        description = "User account under which syncserver runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "syncserver";
+        description = "Group account under which syncserver runs.";
+      };
+
       publicUrl = mkOption {
         type = types.str;
         default = "http://localhost:5000/";
@@ -85,7 +102,7 @@ in
 
       sqlUri = mkOption {
         type = types.str;
-        default = "sqlite:////var/db/firefox-sync-server.db";
+        default = defaultSqlUri;
         example = "postgresql://scott:tiger@localhost/test";
         description = ''
           The location of the database. This URL is composed of
@@ -119,22 +136,52 @@ in
 
   config = mkIf cfg.enable {
 
-    systemd.services.syncserver = {
+    systemd.services.syncserver = let
+      syncServerEnv = pkgs.python.withPackages(ps: with ps; [ syncserver pasteScript ]);
+    in {
       after = [ "network.target" ];
       description = "Firefox Sync Server";
       wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.pythonPackages.pasteScript pkgs.coreutils ];
-      environment.PYTHONPATH = "${pkgs.pythonPackages.syncserver}/lib/${pkgs.pythonPackages.python.libPrefix}/site-packages";
+      path = [ pkgs.coreutils syncServerEnv ];
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        PermissionsStartOnly = true;
+      };
+
       preStart = ''
         if ! test -e ${cfg.privateConfig}; then
-          umask u=rwx,g=x,o=x
-          mkdir -p $(dirname ${cfg.privateConfig})
+          mkdir -m 700 -p $(dirname ${cfg.privateConfig})
           echo  > ${cfg.privateConfig} '[syncserver]'
           echo >> ${cfg.privateConfig} "secret = $(head -c 20 /dev/urandom | sha1sum | tr -d ' -')"
         fi
+        chown ${cfg.user}:${cfg.group} ${cfg.privateConfig}
+      '' + optionalString (cfg.sqlUri == defaultSqlUri) ''
+        if ! test -e $(dirname ${defaultDbLocation}); then
+          mkdir -m 700 -p $(dirname ${defaultDbLocation})
+          chown ${cfg.user}:${cfg.group} $(dirname ${defaultDbLocation})
+        fi
+        # Move previous database file if it exists
+        oldDb="/var/db/firefox-sync-server.db"
+        if test -f $oldDb; then
+          mv $oldDb ${defaultDbLocation}
+          chown ${cfg.user}:${cfg.group} ${defaultDbLocation}
+        fi
       '';
-      serviceConfig.ExecStart = "${pkgs.pythonPackages.pasteScript}/bin/paster serve ${syncServerIni}";
+      serviceConfig.ExecStart = "${syncServerEnv}/bin/paster serve ${syncServerIni}";
     };
 
+    users.extraUsers = optionalAttrs (cfg.user == "syncserver")
+      (singleton {
+        name = "syncserver";
+        group = cfg.group;
+        isSystemUser = true;
+      });
+
+    users.extraGroups = optionalAttrs (cfg.group == "syncserver")
+      (singleton {
+        name = "syncserver";
+      });
   };
 }
diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix
index 7622f030f832..5c4932075fed 100644
--- a/nixos/modules/services/networking/i2pd.nix
+++ b/nixos/modules/services/networking/i2pd.nix
@@ -28,15 +28,15 @@ let
   };
 
   mkKeyedEndpointOpt = name: addr: port: keyFile:
-  (mkEndpointOpt name addr port) // {
-    keys = mkOption {
-      type = types.str;
-      default = "";
-      description = ''
-        File to persist ${lib.toUpper name} keys.
-      '';
+    (mkEndpointOpt name addr port) // {
+      keys = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          File to persist ${lib.toUpper name} keys.
+        '';
+      };
     };
-  };
 
   commonTunOpts = let
     i2cpOpts = {
@@ -59,7 +59,7 @@ let
       description = "Number of ElGamal/AES tags to send.";
       default = 40;
     };
-   destination = mkOption {
+    destination = mkOption {
       type = types.str;
       description = "Remote endpoint, I2P hostname or b32.i2p address.";
     };
@@ -70,88 +70,91 @@ let
     };
   } // mkEndpointOpt name "127.0.0.1" 0;
 
-  i2pdConf = pkgs.writeText "i2pd.conf"
-  ''
-  ipv4 = ${boolToString cfg.enableIPv4}
-  ipv6 = ${boolToString cfg.enableIPv6}
-  notransit = ${boolToString cfg.notransit}
-  floodfill = ${boolToString cfg.floodfill}
-  netid = ${toString cfg.netid}
-  ${if isNull cfg.bandwidth then "" else "bandwidth = ${toString cfg.bandwidth}" }
-  ${if isNull cfg.port then "" else "port = ${toString cfg.port}"}
-
-  [limits]
-  transittunnels = ${toString cfg.limits.transittunnels}
-
-  [upnp]
-  enabled = ${boolToString cfg.upnp.enable}
-  name = ${cfg.upnp.name}
-
-  [precomputation]
-  elgamal = ${boolToString cfg.precomputation.elgamal}
-
-  [reseed]
-  verify = ${boolToString cfg.reseed.verify}
-  file = ${cfg.reseed.file}
-  urls = ${builtins.concatStringsSep "," cfg.reseed.urls}
-
-  [addressbook]
-  defaulturl = ${cfg.addressbook.defaulturl}
-  subscriptions = ${builtins.concatStringsSep "," cfg.addressbook.subscriptions}
-  ${flip concatMapStrings
+  i2pdConf = pkgs.writeText "i2pd.conf" ''
+    # DO NOT EDIT -- this file has been generated automatically.
+    loglevel = ${cfg.logLevel}
+
+    ipv4 = ${boolToString cfg.enableIPv4}
+    ipv6 = ${boolToString cfg.enableIPv6}
+    notransit = ${boolToString cfg.notransit}
+    floodfill = ${boolToString cfg.floodfill}
+    netid = ${toString cfg.netid}
+    ${if isNull cfg.bandwidth then "" else "bandwidth = ${toString cfg.bandwidth}" }
+    ${if isNull cfg.port then "" else "port = ${toString cfg.port}"}
+
+    [limits]
+    transittunnels = ${toString cfg.limits.transittunnels}
+
+    [upnp]
+    enabled = ${boolToString cfg.upnp.enable}
+    name = ${cfg.upnp.name}
+
+    [precomputation]
+    elgamal = ${boolToString cfg.precomputation.elgamal}
+
+    [reseed]
+    verify = ${boolToString cfg.reseed.verify}
+    file = ${cfg.reseed.file}
+    urls = ${builtins.concatStringsSep "," cfg.reseed.urls}
+
+    [addressbook]
+    defaulturl = ${cfg.addressbook.defaulturl}
+    subscriptions = ${builtins.concatStringsSep "," cfg.addressbook.subscriptions}
+
+    ${flip concatMapStrings
       (collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto)
-      (proto: let portStr = toString proto.port; in
-        ''
-          [${proto.name}]
-          enabled = ${boolToString proto.enable}
-          address = ${proto.address}
-          port = ${toString proto.port}
-          ${if proto ? keys then "keys = ${proto.keys}" else ""}
-          ${if proto ? auth then "auth = ${boolToString proto.auth}" else ""}
-          ${if proto ? user then "user = ${proto.user}" else ""}
-          ${if proto ? pass then "pass = ${proto.pass}" else ""}
-          ${if proto ? outproxy then "outproxy = ${proto.outproxy}" else ""}
-          ${if proto ? outproxyPort then "outproxyport = ${toString proto.outproxyPort}" else ""}
-        '')
-      }
+      (proto: let portStr = toString proto.port; in ''
+        [${proto.name}]
+        enabled = ${boolToString proto.enable}
+        address = ${proto.address}
+        port = ${toString proto.port}
+        ${if proto ? keys then "keys = ${proto.keys}" else ""}
+        ${if proto ? auth then "auth = ${boolToString proto.auth}" else ""}
+        ${if proto ? user then "user = ${proto.user}" else ""}
+        ${if proto ? pass then "pass = ${proto.pass}" else ""}
+        ${if proto ? outproxy then "outproxy = ${proto.outproxy}" else ""}
+        ${if proto ? outproxyPort then "outproxyport = ${toString proto.outproxyPort}" else ""}
+      '')
+    }
   '';
 
   i2pdTunnelConf = pkgs.writeText "i2pd-tunnels.conf" ''
-  ${flip concatMapStrings
-    (collect (tun: tun ? port && tun ? destination) cfg.outTunnels)
-    (tun: let portStr = toString tun.port; in ''
-  [${tun.name}]
-  type = client
-  destination = ${tun.destination}
-  keys = ${tun.keys}
-  address = ${tun.address}
-  port = ${toString tun.port}
-  inbound.length = ${toString tun.inbound.length}
-  outbound.length = ${toString tun.outbound.length}
-  inbound.quantity = ${toString tun.inbound.quantity}
-  outbound.quantity = ${toString tun.outbound.quantity}
-  crypto.tagsToSend = ${toString tun.crypto.tagsToSend}
-  '')
-  }
-  ${flip concatMapStrings
-    (collect (tun: tun ? port && tun ? host) cfg.inTunnels)
-    (tun: let portStr = toString tun.port; in ''
-  [${tun.name}]
-  type = server
-  destination = ${tun.destination}
-  keys = ${tun.keys}
-  host = ${tun.address}
-  port = ${tun.port}
-  inport = ${tun.inPort}
-  accesslist = ${builtins.concatStringsSep "," tun.accessList}
-  '')
-  }
+    # DO NOT EDIT -- this file has been generated automatically.
+    ${flip concatMapStrings
+      (collect (tun: tun ? port && tun ? destination) cfg.outTunnels)
+      (tun: let portStr = toString tun.port; in ''
+        [${tun.name}]
+        type = client
+        destination = ${tun.destination}
+        keys = ${tun.keys}
+        address = ${tun.address}
+        port = ${toString tun.port}
+        inbound.length = ${toString tun.inbound.length}
+        outbound.length = ${toString tun.outbound.length}
+        inbound.quantity = ${toString tun.inbound.quantity}
+        outbound.quantity = ${toString tun.outbound.quantity}
+        crypto.tagsToSend = ${toString tun.crypto.tagsToSend}
+      '')
+    }
+    ${flip concatMapStrings
+      (collect (tun: tun ? port && tun ? host) cfg.inTunnels)
+      (tun: let portStr = toString tun.port; in ''
+        [${tun.name}]
+        type = server
+        destination = ${tun.destination}
+        keys = ${tun.keys}
+        host = ${tun.address}
+        port = ${tun.port}
+        inport = ${tun.inPort}
+        accesslist = ${builtins.concatStringsSep "," tun.accessList}
+      '')
+    }
   '';
 
   i2pdSh = pkgs.writeScriptBin "i2pd" ''
     #!/bin/sh
-    ${pkgs.i2pd}/bin/i2pd \
-      ${if isNull cfg.extIp then "" else "--host="+cfg.extIp} \
+    exec ${pkgs.i2pd}/bin/i2pd \
+      ${if isNull cfg.address then "" else "--host="+cfg.address} \
       --conf=${i2pdConf} \
       --tunconf=${i2pdTunnelConf}
   '';
@@ -176,11 +179,23 @@ in
         '';
       };
 
-      extIp = mkOption {
+      logLevel = mkOption {
+        type = types.enum ["debug" "info" "warn" "error"];
+        default = "error";
+        description = ''
+          The log level. <command>i2pd</command> defaults to "info"
+          but that generates copious amounts of log messages.
+
+          We default to "error" which is similar to the default log
+          level of <command>tor</command>.
+        '';
+      };
+
+      address = mkOption {
         type = with types; nullOr str;
         default = null;
         description = ''
-          Your external IP.
+          Your external IP or hostname.
         '';
       };
 
@@ -213,7 +228,7 @@ in
         default = null;
         description = ''
            Set a router bandwidth limit integer in KBps.
-           If not set, i2pd defaults to 32KBps.
+           If not set, <command>i2pd</command> defaults to 32KBps.
         '';
       };
 
@@ -261,9 +276,14 @@ in
 
       precomputation.elgamal = mkOption {
         type = types.bool;
-        default = false;
+        default = true;
         description = ''
-          Use ElGamal precomputated tables.
+          Whenever to use precomputated tables for ElGamal.
+          <command>i2pd</command> defaults to <literal>false</literal>
+          to save 64M of memory (and looses some performance).
+
+          We default to <literal>true</literal> as that is what most
+          users want anyway.
         '';
       };
 
@@ -353,7 +373,7 @@ in
         };
       };
 
-      proto.httpProxy = mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4446 "";
+      proto.httpProxy = mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4444 "";
       proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "")
       // {
         outproxy = mkOption {
diff --git a/nixos/modules/services/networking/lldpd.nix b/nixos/modules/services/networking/lldpd.nix
index 4f951d843e2c..ba4e1b1542fe 100644
--- a/nixos/modules/services/networking/lldpd.nix
+++ b/nixos/modules/services/networking/lldpd.nix
@@ -28,16 +28,11 @@ in
     users.extraGroups._lldpd = {};
 
     environment.systemPackages = [ pkgs.lldpd ];
+    systemd.packages = [ pkgs.lldpd ];
 
     systemd.services.lldpd = {
       wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
-      requires = [ "network.target" ];
-      serviceConfig = {
-        ExecStart = "${pkgs.lldpd}/bin/lldpd -d ${concatStringsSep " " cfg.extraArgs}";
-        PrivateTmp = true;
-        PrivateDevices = true;
-      };
+      environment.LLDPD_OPTIONS = concatStringsSep " " cfg.extraArgs;
     };
   };
 }
diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix
index 5451500b56f6..81915b5a2ef8 100644
--- a/nixos/modules/services/networking/mosquitto.nix
+++ b/nixos/modules/services/networking/mosquitto.nix
@@ -125,8 +125,8 @@ in
               description = ''
                 Specifies the hashed password for the MQTT User.
                 <option>hashedPassword</option> overrides <option>password</option>.
-                To generate hashed password install <literal>mkpasswd</literal>
-                package and run <literal>mkpasswd -m sha-512</literal>.
+                To generate hashed password install <literal>mosquitto</literal>
+                package and use <literal>mosquitto_passwd</literal>.
               '';
             };
 
diff --git a/nixos/modules/services/networking/nat.nix b/nixos/modules/services/networking/nat.nix
index 08ba2fdb1646..366bb2ed7a80 100644
--- a/nixos/modules/services/networking/nat.nix
+++ b/nixos/modules/services/networking/nat.nix
@@ -48,7 +48,7 @@ let
     # NAT from external ports to internal ports.
     ${concatMapStrings (fwd: ''
       iptables -w -t nat -A nixos-nat-pre \
-        -i ${cfg.externalInterface} -p tcp \
+        -i ${cfg.externalInterface} -p ${fwd.proto} \
         --dport ${builtins.toString fwd.sourcePort} \
         -j DNAT --to-destination ${fwd.destination}
     '') cfg.forwardPorts}
@@ -133,12 +133,19 @@ in
           destination = mkOption {
             type = types.str;
             example = "10.0.0.1:80";
-            description = "Forward tcp connection to destination ip:port";
+            description = "Forward connection to destination ip:port";
+          };
+
+          proto = mkOption {
+            type = types.str;
+            default = "tcp";
+            example = "udp";
+            description = "Protocol of forwarded connection";
           };
         };
       });
       default = [];
-      example = [ { sourcePort = 8080; destination = "10.0.0.1:80"; } ];
+      example = [ { sourcePort = 8080; destination = "10.0.0.1:80"; proto = "tcp"; } ];
       description =
         ''
           List of forwarded ports from the external interface to
@@ -151,38 +158,41 @@ in
 
   ###### implementation
 
-  config = mkIf config.networking.nat.enable {
+  config = mkMerge [
+    { networking.firewall.extraCommands = mkBefore flushNat; }
+    (mkIf config.networking.nat.enable {
 
-    environment.systemPackages = [ pkgs.iptables ];
+      environment.systemPackages = [ pkgs.iptables ];
 
-    boot = {
-      kernelModules = [ "nf_nat_ftp" ];
-      kernel.sysctl = {
-        "net.ipv4.conf.all.forwarding" = mkOverride 99 true;
-        "net.ipv4.conf.default.forwarding" = mkOverride 99 true;
+      boot = {
+        kernelModules = [ "nf_nat_ftp" ];
+        kernel.sysctl = {
+          "net.ipv4.conf.all.forwarding" = mkOverride 99 true;
+          "net.ipv4.conf.default.forwarding" = mkOverride 99 true;
+        };
       };
-    };
 
-    networking.firewall = mkIf config.networking.firewall.enable {
-      extraCommands = mkMerge [ (mkBefore flushNat) setupNat ];
-      extraStopCommands = flushNat;
-    };
+      networking.firewall = mkIf config.networking.firewall.enable {
+        extraCommands = setupNat;
+        extraStopCommands = flushNat;
+      };
 
-    systemd.services = mkIf (!config.networking.firewall.enable) { nat = {
-      description = "Network Address Translation";
-      wantedBy = [ "network.target" ];
-      after = [ "network-pre.target" "systemd-modules-load.service" ];
-      path = [ pkgs.iptables ];
-      unitConfig.ConditionCapability = "CAP_NET_ADMIN";
+      systemd.services = mkIf (!config.networking.firewall.enable) { nat = {
+        description = "Network Address Translation";
+        wantedBy = [ "network.target" ];
+        after = [ "network-pre.target" "systemd-modules-load.service" ];
+        path = [ pkgs.iptables ];
+        unitConfig.ConditionCapability = "CAP_NET_ADMIN";
 
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
-      };
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+        };
 
-      script = flushNat + setupNat;
+        script = flushNat + setupNat;
 
-      postStop = flushNat;
-    }; };
-  };
+        postStop = flushNat;
+      }; };
+    })
+  ];
 }
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index f1b3d298fecb..e03309c87299 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -9,11 +9,17 @@ let
   # /var/lib/misc is for dnsmasq.leases.
   stateDirs = "/var/lib/NetworkManager /var/lib/dhclient /var/lib/misc";
 
+  dns =
+    if cfg.useDnsmasq then "dnsmasq"
+    else if config.services.resolved.enable then "systemd-resolved"
+    else if config.services.unbound.enable then "unbound"
+    else "default";
+
   configFile = writeText "NetworkManager.conf" ''
     [main]
     plugins=keyfile
     dhcp=${cfg.dhcp}
-    dns=${if cfg.useDnsmasq then "dnsmasq" else "default"}
+    dns=${dns}
 
     [keyfile]
     ${optionalString (cfg.unmanaged != [])
@@ -124,7 +130,8 @@ in {
         default = { inherit networkmanager modemmanager wpa_supplicant
                             networkmanager_openvpn networkmanager_vpnc
                             networkmanager_openconnect networkmanager_fortisslvpn
-                            networkmanager_pptp networkmanager_l2tp; };
+                            networkmanager_pptp networkmanager_l2tp
+                            networkmanager_iodine; };
         internal = true;
       };
 
@@ -249,6 +256,9 @@ in {
       { source = "${networkmanager_strongswan}/etc/NetworkManager/VPN/nm-strongswan-service.name";
         target = "NetworkManager/VPN/nm-strongswan-service.name";
       }
+      { source = "${networkmanager_iodine}/etc/NetworkManager/VPN/nm-iodine-service.name";
+        target = "NetworkManager/VPN/nm-iodine-service.name";
+      }
     ] ++ optional (cfg.appendNameservers == [] || cfg.insertNameservers == [])
            { source = overrideNameserversScript;
              target = "NetworkManager/dispatcher.d/02overridedns";
@@ -272,6 +282,11 @@ in {
       name = "nm-openvpn";
       uid = config.ids.uids.nm-openvpn;
       extraGroups = [ "networkmanager" ];
+    }
+    {
+      name = "nm-iodine";
+      isSystemUser = true;
+      group = "networkmanager";
     }];
 
     systemd.packages = cfg.packages;
diff --git a/nixos/modules/services/networking/radicale.nix b/nixos/modules/services/networking/radicale.nix
index ef860e7e5df4..f1b8edf43713 100644
--- a/nixos/modules/services/networking/radicale.nix
+++ b/nixos/modules/services/networking/radicale.nix
@@ -1,4 +1,4 @@
-{config, lib, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
 with lib;
 
@@ -8,17 +8,35 @@ let
 
   confFile = pkgs.writeText "radicale.conf" cfg.config;
 
+  # This enables us to default to version 2 while still not breaking configurations of people with version 1
+  defaultPackage = if versionAtLeast "17.09" config.system.stateVersion then {
+    pkg = pkgs.radicale2;
+    text = "pkgs.radicale2";
+  } else {
+    pkg = pkgs.radicale1;
+    text = "pkgs.radicale1";
+  };
 in
 
 {
 
   options = {
-
     services.radicale.enable = mkOption {
       type = types.bool;
       default = false;
       description = ''
-          Enable Radicale CalDAV and CardDAV server
+          Enable Radicale CalDAV and CardDAV server.
+      '';
+    };
+
+    services.radicale.package = mkOption {
+      type = types.package;
+      default = defaultPackage.pkg;
+      defaultText = defaultPackage.text;
+      description = ''
+        Radicale package to use. This defaults to version 1.x if
+        <literal>system.stateVersion &lt; 17.09</literal> and version 2.x
+        otherwise.
       '';
     };
 
@@ -27,13 +45,13 @@ in
       default = "";
       description = ''
         Radicale configuration, this will set the service
-        configuration file
+        configuration file.
       '';
-      };
+    };
   };
 
   config = mkIf cfg.enable {
-    environment.systemPackages = [ pkgs.radicale ];
+    environment.systemPackages = [ cfg.package ];
 
     users.extraUsers = singleton
       { name = "radicale";
@@ -52,11 +70,13 @@ in
       description = "A Simple Calendar and Contact Server";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      script = "${pkgs.radicale}/bin/radicale -C ${confFile} -f";
-      serviceConfig.User = "radicale";
-      serviceConfig.Group = "radicale";
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/radicale -C ${confFile} -f";
+        User = "radicale";
+        Group = "radicale";
+      };
     };
   };
 
-  meta.maintainers = with lib.maintainers; [ aneeshusa ];
+  meta.maintainers = with lib.maintainers; [ aneeshusa infinisil ];
 }
diff --git a/nixos/modules/services/networking/searx.nix b/nixos/modules/services/networking/searx.nix
index e0eef9ed96f6..c7a128ae212d 100644
--- a/nixos/modules/services/networking/searx.nix
+++ b/nixos/modules/services/networking/searx.nix
@@ -33,8 +33,8 @@ in
 
       package = mkOption {
         type = types.package;
-        default = pkgs.pythonPackages.searx;
-        defaultText = "pkgs.pythonPackages.searx";
+        default = pkgs.searx;
+        defaultText = "pkgs.searx";
         description = "searx package to use.";
       };
 
diff --git a/nixos/modules/services/networking/strongswan.nix b/nixos/modules/services/networking/strongswan.nix
index 8778b0364f9a..b0eb0460b9ba 100644
--- a/nixos/modules/services/networking/strongswan.nix
+++ b/nixos/modules/services/networking/strongswan.nix
@@ -120,7 +120,7 @@ in
       wantedBy = [ "multi-user.target" ];
       path = with pkgs; [ kmod iproute iptables utillinux ]; # XXX Linux
       wants = [ "keys.target" ];
-      after = [ "network.target" "keys.target" ];
+      after = [ "network-online.target" "keys.target" ];
       environment = {
         STRONGSWAN_CONF = strongswanConf { inherit setup connections ca secrets; };
       };
diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix
index 79a0aa953feb..7a786b54ccbb 100644
--- a/nixos/modules/services/networking/tinc.nix
+++ b/nixos/modules/services/networking/tinc.nix
@@ -79,7 +79,15 @@ in
               default = null;
               type = types.nullOr types.str;
               description = ''
-                The ip adress to bind to.
+                The ip address to listen on for incoming connections.
+              '';
+            };
+
+            bindToAddress = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The ip address to bind to (both listen on and send packets from).
               '';
             };
 
@@ -123,7 +131,7 @@ in
       (flip mapAttrsToList cfg.networks (network: data:
         flip mapAttrs' data.hosts (host: text: nameValuePair
           ("tinc/${network}/hosts/${host}")
-          ({ mode = "0444"; inherit text; })
+          ({ mode = "0644"; user = "tinc.${network}"; inherit text; })
         ) // {
           "tinc/${network}/tinc.conf" = {
             mode = "0444";
@@ -131,7 +139,8 @@ in
               Name = ${if data.name == null then "$HOST" else data.name}
               DeviceType = ${data.interfaceType}
               ${optionalString (data.ed25519PrivateKeyFile != null) "Ed25519PrivateKeyFile = ${data.ed25519PrivateKeyFile}"}
-              ${optionalString (data.listenAddress != null) "BindToAddress = ${data.listenAddress}"}
+              ${optionalString (data.listenAddress != null) "ListenAddress = ${data.listenAddress}"}
+              ${optionalString (data.bindToAddress != null) "BindToAddress = ${data.bindToAddress}"}
               Device = /dev/net/tun
               Interface = tinc.${network}
               ${data.extraConfig}
@@ -155,15 +164,14 @@ in
         wantedBy = [ "multi-user.target" ];
         after = [ "network.target" ];
         path = [ data.package ];
-        restartTriggers = [ config.environment.etc."tinc/${network}/tinc.conf".source ]
-          ++ mapAttrsToList (host: _ : config.environment.etc."tinc/${network}/hosts/${host}".source) data.hosts;
         serviceConfig = {
           Type = "simple";
-          PIDFile = "/run/tinc.${network}.pid";
-          Restart = "on-failure";
+          Restart = "always";
+          RestartSec = "3";
         };
         preStart = ''
           mkdir -p /etc/tinc/${network}/hosts
+          chown tinc.${network} /etc/tinc/${network}/hosts
 
           # Determine how we should generate our keys
           if type tinc >/dev/null 2>&1; then
@@ -185,6 +193,19 @@ in
       })
     );
 
+    environment.systemPackages = let
+      cli-wrappers = pkgs.stdenv.mkDerivation {
+        name = "tinc-cli-wrappers";
+        buildInputs = [ pkgs.makeWrapper ];
+        buildCommand = ''
+          mkdir -p $out/bin
+          ${concatStringsSep "\n" (mapAttrsToList (network: data: ''
+              makeWrapper ${data.package}/bin/tinc "$out/bin/tinc.${network}" --add-flags "--pidfile=/run/tinc.${network}.pid"
+            '') cfg.networks)}
+        '';
+      };
+    in [ cli-wrappers ];
+
     users.extraUsers = flip mapAttrs' cfg.networks (network: _:
       nameValuePair ("tinc.${network}") ({
         description = "Tinc daemon user for ${network}";
diff --git a/nixos/modules/services/networking/tlsdated.nix b/nixos/modules/services/networking/tlsdated.nix
deleted file mode 100644
index 757cce287607..000000000000
--- a/nixos/modules/services/networking/tlsdated.nix
+++ /dev/null
@@ -1,111 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  inherit (pkgs) coreutils tlsdate;
-
-  cfg = config.services.tlsdated;
-in
-
-{
-
-  ###### interface
-
-  options = {
-
-    services.tlsdated = {
-
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Enable tlsdated daemon.
-        '';
-      };
-
-      extraOptions = mkOption {
-        type = types.string;
-        default = "";
-        description = ''
-          Additional command line arguments to pass to tlsdated.
-        '';
-      };
-
-      sources = mkOption {
-        type = types.listOf (types.submodule {
-          options = {
-            host = mkOption {
-              type = types.string;
-              description = ''
-                Remote hostname.
-              '';
-            };
-            port = mkOption {
-              type = types.int;
-              description = ''
-                Remote port.
-              '';
-            };
-            proxy = mkOption {
-              type = types.nullOr types.string;
-              default = null;
-              description = ''
-                The proxy argument expects HTTP, SOCKS4A or SOCKS5 formatted as followed:
-
-                 http://127.0.0.1:8118
-                 socks4a://127.0.0.1:9050
-                 socks5://127.0.0.1:9050
-
-                The proxy support should not leak DNS requests and is suitable for use with Tor.
-              '';
-            };
-          };
-        });
-        default = [
-          {
-            host = "encrypted.google.com";
-            port = 443;
-            proxy = null;
-          }
-        ];
-        description = ''
-          You can list one or more sources to fetch time from.
-        '';
-      };
-
-    };
-
-  };
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-
-    # Make tools such as tlsdate available in the system path
-    environment.systemPackages = [ tlsdate ];
-
-    systemd.services.tlsdated = {
-      description = "tlsdated daemon";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        # XXX because pkgs.tlsdate is compiled to run as nobody:nogroup, we
-        # hard-code base-path to /tmp and use PrivateTmp.
-        ExecStart = "${tlsdate}/bin/tlsdated -f ${pkgs.writeText "tlsdated.confg" ''
-          base-path /tmp
-
-          ${concatMapStrings (src: ''
-          source
-              host    ${src.host}
-              port    ${toString src.port}
-              proxy   ${if src.proxy == null then "none" else src.proxy}
-          end
-          '') cfg.sources}
-        ''} ${cfg.extraOptions}";
-        PrivateTmp = "yes";
-      };
-    };
-
-  };
-
-}
diff --git a/nixos/modules/services/networking/unifi.nix b/nixos/modules/services/networking/unifi.nix
index a8cff638d3b2..8e5f0bfc070d 100644
--- a/nixos/modules/services/networking/unifi.nix
+++ b/nixos/modules/services/networking/unifi.nix
@@ -3,7 +3,12 @@ with lib;
 let
   cfg = config.services.unifi;
   stateDir = "/var/lib/unifi";
-  cmd = "@${pkgs.jre}/bin/java java -jar ${stateDir}/lib/ace.jar";
+  cmd = ''
+    @${pkgs.jre}/bin/java java \
+        ${optionalString (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m"} \
+        ${optionalString (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m"} \
+        -jar ${stateDir}/lib/ace.jar
+  '';
   mountPoints = [
     {
       what = "${pkgs.unifi}/dl";
@@ -58,6 +63,26 @@ in
       '';
     };
 
+    services.unifi.initialJavaHeapSize = mkOption {
+      type = types.nullOr types.int;
+      default = null;
+      example = 1024;
+      description = ''
+        Set the initial heap size for the JVM in MB. If this option isn't set, the
+        JVM will decide this value at runtime.
+      '';
+    };
+
+    services.unifi.maximumJavaHeapSize = mkOption {
+      type = types.nullOr types.int;
+      default = null;
+      example = 4096;
+      description = ''
+        Set the maximimum heap size for the JVM in MB. If this option isn't set, the
+        JVM will decide this value at runtime.
+      '';
+    };
+
   };
 
   config = mkIf cfg.enable {
@@ -121,8 +146,8 @@ in
 
       serviceConfig = {
         Type = "simple";
-        ExecStart = "${cmd} start";
-        ExecStop = "${cmd} stop";
+        ExecStart = "${(removeSuffix "\n" cmd)} start";
+        ExecStop = "${(removeSuffix "\n" cmd)} stop";
         User = "unifi";
         PermissionsStartOnly = true;
         UMask = "0077";
diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix
index 62ff708d244c..4f54b45639f6 100644
--- a/nixos/modules/services/networking/wireguard.nix
+++ b/nixos/modules/services/networking/wireguard.nix
@@ -23,8 +23,23 @@ let
 
       privateKey = mkOption {
         example = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=";
-        type = types.str;
-        description = "Base64 private key generated by wg genkey.";
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Base64 private key generated by wg genkey.
+
+          Warning: Consider using privateKeyFile instead if you do not
+          want to store the key in the world-readable Nix store.
+        '';
+      };
+
+      privateKeyFile = mkOption {
+        example = "/private/wireguard_key";
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Private key file as generated by wg genkey.
+        '';
       };
 
       listenPort = mkOption {
@@ -64,6 +79,16 @@ let
         description = "A list of commands called after shutting down the interface.";
       };
 
+      table = mkOption {
+        default = "main";
+        type = types.str;
+        description = ''The kernel routing table to add this interface's
+        associated routes to. Setting this is useful for e.g. policy routing
+        ("ip rule") or virtual routing and forwarding ("ip vrf"). Both numeric
+        table IDs and table names (/etc/rt_tables) can be used. Defaults to
+        "main".'';
+      };
+
       peers = mkOption {
         default = [];
         description = "Peers linked to the interface.";
@@ -91,7 +116,22 @@ let
         example = "rVXs/Ni9tu3oDBLS4hOyAUAa1qTWVA3loR8eL20os3I=";
         type = with types; nullOr str;
         description = ''
-          base64 preshared key generated by wg genpsk. Optional,
+          Base64 preshared key generated by wg genpsk. Optional,
+          and may be omitted. This option adds an additional layer of
+          symmetric-key cryptography to be mixed into the already existing
+          public-key cryptography, for post-quantum resistance.
+
+          Warning: Consider using presharedKeyFile instead if you do not
+          want to store the key in the world-readable Nix store.
+        '';
+      };
+
+      presharedKeyFile = mkOption {
+        default = null;
+        example = "/private/wireguard_psk";
+        type = with types; nullOr str;
+        description = ''
+          File pointing to preshared key as generated by wg pensk. Optional,
           and may be omitted. This option adds an additional layer of
           symmetric-key cryptography to be mixed into the already existing
           public-key cryptography, for post-quantum resistance.
@@ -134,54 +174,61 @@ let
 
   };
 
-  generateConf = name: values: pkgs.writeText "wireguard-${name}.conf" ''
-    [Interface]
-    PrivateKey = ${values.privateKey}
-    ${optionalString (values.listenPort != null)   "ListenPort = ${toString values.listenPort}"}
-
-    ${concatStringsSep "\n\n" (map (peer: ''
-    [Peer]
-    PublicKey = ${peer.publicKey}
-    ${optionalString (peer.presharedKey != null) "PresharedKey = ${peer.presharedKey}"}
-    ${optionalString (peer.allowedIPs != []) "AllowedIPs = ${concatStringsSep ", " peer.allowedIPs}"}
-    ${optionalString (peer.endpoint != null) "Endpoint = ${peer.endpoint}"}
-    ${optionalString (peer.persistentKeepalive != null) "PersistentKeepalive = ${toString peer.persistentKeepalive}"}
-    '') values.peers)}
-  '';
-
   ipCommand = "${pkgs.iproute}/bin/ip";
   wgCommand = "${pkgs.wireguard}/bin/wg";
 
   generateUnit = name: values:
+    # exactly one way to specify the private key must be set
+    assert (values.privateKey != null) != (values.privateKeyFile != null);
+    let privKey = if values.privateKeyFile != null then values.privateKeyFile else pkgs.writeText "wg-key" values.privateKey;
+    in
     nameValuePair "wireguard-${name}"
       {
         description = "WireGuard Tunnel - ${name}";
         after = [ "network.target" ];
         wantedBy = [ "multi-user.target" ];
+
         serviceConfig = {
           Type = "oneshot";
           RemainAfterExit = true;
-          ExecStart = lib.flatten([
+          ExecStart = flatten([
             values.preSetup
 
             "-${ipCommand} link del dev ${name}"
             "${ipCommand} link add dev ${name} type wireguard"
-            "${wgCommand} setconf ${name} ${generateConf name values}"
 
             (map (ip:
-            ''${ipCommand} address add ${ip} dev ${name}''
+            "${ipCommand} address add ${ip} dev ${name}"
             ) values.ips)
 
+            ("${wgCommand} set ${name} private-key ${privKey}" +
+            optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}")
+
+            (map (peer:
+            assert (peer.presharedKeyFile == null) || (peer.presharedKey == null); # at most one of the two must be set
+            let psk = if peer.presharedKey != null then pkgs.writeText "wg-psk" peer.presharedKey else peer.presharedKeyFile;
+            in
+            "${wgCommand} set ${name} peer ${peer.publicKey}" +
+            optionalString (psk != null) " preshared-key ${psk}" +
+            optionalString (peer.endpoint != null) " endpoint ${peer.endpoint}" +
+            optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" +
+            optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}"
+            ) values.peers)
+
             "${ipCommand} link set up dev ${name}"
 
-            (flatten (map (peer: (map (ip:
-            "${ipCommand} route add ${ip} dev ${name}"
-            ) peer.allowedIPs)) values.peers))
+            (map (peer:
+            (map (allowedIP:
+            "${ipCommand} route replace ${allowedIP} dev ${name} table ${values.table}"
+            ) peer.allowedIPs)
+            ) values.peers)
 
             values.postSetup
           ]);
-
-          ExecStop = [ ''${ipCommand} link del dev "${name}"'' ] ++ values.postShutdown;
+          ExecStop = flatten([
+            "${ipCommand} link del dev ${name}"
+            values.postShutdown
+          ]);
         };
       };
 
diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix
index 7ce2ae38fb36..4c7f58d1d8bc 100644
--- a/nixos/modules/services/printing/cupsd.nix
+++ b/nixos/modules/services/printing/cupsd.nix
@@ -4,7 +4,7 @@ with lib;
 
 let
 
-  inherit (pkgs) cups cups-pk-helper cups-filters gutenprint;
+  inherit (pkgs) cups cups-pk-helper cups-filters;
 
   cfg = config.services.printing;
 
@@ -35,9 +35,8 @@ let
     name = "cups-progs";
     paths =
       [ cups.out additionalBackends cups-filters pkgs.ghostscript ]
-      ++ optional cfg.gutenprint gutenprint
       ++ cfg.drivers;
-    pathsToLink = [ "/lib/cups" "/share/cups" "/bin" ];
+    pathsToLink = [ "/lib" "/share/cups" "/bin" ];
     postBuild = cfg.bindirCmds;
     ignoreCollisions = true;
   };
@@ -97,12 +96,15 @@ let
       (writeConf "client.conf" cfg.clientConf)
       (writeConf "snmp.conf" cfg.snmpConf)
     ] ++ optional avahiEnabled browsedFile
-      ++ optional cfg.gutenprint gutenprint
       ++ cfg.drivers;
     pathsToLink = [ "/etc/cups" ];
     ignoreCollisions = true;
   };
 
+  filterGutenprint = pkgs: filter (pkg: pkg.meta.isGutenprint or false == true) pkgs;
+  containsGutenprint = pkgs: length (filterGutenprint pkgs) > 0;
+  getGutenprint = pkgs: head (filterGutenprint pkgs);
+
 in
 
 {
@@ -224,23 +226,17 @@ in
         '';
       };
 
-      gutenprint = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Whether to enable Gutenprint drivers for CUPS. This includes auto-updating
-          Gutenprint PPD files.
-        '';
-      };
-
       drivers = mkOption {
         type = types.listOf types.path;
         default = [];
-        example = literalExample "[ pkgs.splix ]";
+        example = literalExample "[ pkgs.gutenprint pkgs.hplip pkgs.splix ]";
         description = ''
-          CUPS drivers to use. Drivers provided by CUPS, cups-filters, Ghostscript
-          and Samba are added unconditionally. For adding Gutenprint, see
-          <literal>gutenprint</literal>.
+          CUPS drivers to use. Drivers provided by CUPS, cups-filters,
+          Ghostscript and Samba are added unconditionally. If this list contains
+          Gutenprint (i.e. a derivation with
+          <literal>meta.isGutenprint = true</literal>) the PPD files in
+          <filename>/var/lib/cups/ppd</filename> will be updated automatically
+          to avoid errors due to incompatible versions.
         '';
       };
 
@@ -318,12 +314,14 @@ in
             [ ! -e /var/lib/cups/path ] && \
               ln -s ${bindir} /var/lib/cups/path
 
-            ${optionalString cfg.gutenprint ''
+            ${optionalString (containsGutenprint cfg.drivers) ''
               if [ -d /var/lib/cups/ppd ]; then
-                ${gutenprint}/bin/cups-genppdupdate -p /var/lib/cups/ppd
+                ${getGutenprint cfg.drivers}/bin/cups-genppdupdate -p /var/lib/cups/ppd
               fi
             ''}
           '';
+
+          serviceConfig.PrivateTmp = true;
       };
 
     systemd.services.cups-browsed = mkIf avahiEnabled
diff --git a/nixos/modules/services/scheduling/cron.nix b/nixos/modules/services/scheduling/cron.nix
index 7b1a1599e141..6f6977b38a1d 100644
--- a/nixos/modules/services/scheduling/cron.nix
+++ b/nixos/modules/services/scheduling/cron.nix
@@ -122,7 +122,7 @@ in
               fi
             '';
 
-          restartTriggers = [ config.environment.etc.localtime.source ];
+          restartTriggers = [ config.time.timeZone ];
           serviceConfig.ExecStart = "${cronNixosPkg}/bin/cron -n";
         };
 
diff --git a/nixos/modules/services/security/frandom.nix b/nixos/modules/services/security/frandom.nix
deleted file mode 100644
index 2d43d12e541d..000000000000
--- a/nixos/modules/services/security/frandom.nix
+++ /dev/null
@@ -1,31 +0,0 @@
-{lib, config, ...}:
-
-let kernel = config.boot.kernelPackages;
-in
-
-{
-
-  ###### interface
-
-  options = {
-
-    services.frandom.enable = lib.mkOption {
-      default = false;
-      type = lib.types.bool;
-      description = ''
-        enable the /dev/frandom device (a very fast random number generator)
-      '';
-    };
-
-  };
-
-
-  ###### implementation
-
-  config = lib.mkIf config.services.frandom.enable {
-    boot.kernelModules = [ "frandom" ];
-    boot.extraModulePackages = [ kernel.frandom ];
-    services.udev.packages = [ kernel.frandom ];
-  };
-
-}
diff --git a/nixos/modules/services/security/oauth2_proxy.nix b/nixos/modules/services/security/oauth2_proxy.nix
index e292fd9851e3..3e5087766b1c 100644
--- a/nixos/modules/services/security/oauth2_proxy.nix
+++ b/nixos/modules/services/security/oauth2_proxy.nix
@@ -21,21 +21,20 @@ let
     '';
 
     github = cfg: ''
-      $(optionalString (!isNull cfg.github.org) "--github-org=${cfg.github.org}") \
-      $(optionalString (!isNull cfg.github.team) "--github-org=${cfg.github.team}") \
+      ${optionalString (!isNull cfg.github.org) "--github-org=${cfg.github.org}"} \
+      ${optionalString (!isNull cfg.github.team) "--github-org=${cfg.github.team}"} \
     '';
 
     google = cfg: ''
       --google-admin-email=${cfg.google.adminEmail} \
       --google-service-account=${cfg.google.serviceAccountJSON} \
-      $(repeatedArgs (group: "--google-group=${group}") cfg.google.groups) \
+      ${repeatedArgs (group: "--google-group=${group}") cfg.google.groups} \
     '';
   };
 
   authenticatedEmailsFile = pkgs.writeText "authenticated-emails" cfg.email.addresses;
 
-  getProviderOptions = cfg: provider:
-    if providerSpecificOptions ? provider then providerSpecificOptions.provider cfg else "";
+  getProviderOptions = cfg: provider: providerSpecificOptions.${provider} or (_: "") cfg;
 
   mkCommandLine = cfg: ''
     --provider='${cfg.provider}' \
diff --git a/nixos/modules/services/security/sks.nix b/nixos/modules/services/security/sks.nix
new file mode 100644
index 000000000000..62308428f326
--- /dev/null
+++ b/nixos/modules/services/security/sks.nix
@@ -0,0 +1,82 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.sks;
+
+  sksPkg = cfg.package;
+
+in
+
+{
+
+  options = {
+
+    services.sks = {
+
+      enable = mkEnableOption "sks";
+
+      package = mkOption {
+        default = pkgs.sks;
+        defaultText = "pkgs.sks";
+        type = types.package;
+        description = "
+          Which sks derivation to use.
+        ";
+      };
+
+      hkpAddress = mkOption {
+        default = [ "127.0.0.1" "::1" ];
+        type = types.listOf types.str;
+        description = "
+          Wich ip addresses the sks-keyserver is listening on.
+        ";
+      };
+
+      hkpPort = mkOption {
+        default = 11371;
+        type = types.int;
+        description = "
+          Which port the sks-keyserver is listening on.
+        ";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ sksPkg ];
+    
+    users.users.sks = {
+      createHome = true;
+      home = "/var/db/sks";
+      isSystemUser = true;
+      shell = "${pkgs.coreutils}/bin/true";
+    };
+
+    systemd.services = let
+      hkpAddress = "'" + (builtins.concatStringsSep " " cfg.hkpAddress) + "'" ;
+      hkpPort = builtins.toString cfg.hkpPort;
+      home = config.users.users.sks.home;
+      user = config.users.users.sks.name;
+    in {
+      sks-keyserver = {
+        wantedBy = [ "multi-user.target" ];
+        preStart = ''
+          mkdir -p ${home}/dump
+          ${pkgs.sks}/bin/sks build ${home}/dump/*.gpg -n 10 -cache 100 || true #*/
+          ${pkgs.sks}/bin/sks cleandb || true
+          ${pkgs.sks}/bin/sks pbuild -cache 20 -ptree_cache 70 || true
+        '';
+        serviceConfig = {
+          WorkingDirectory = home;
+          User = user;
+          Restart = "always";
+          ExecStart = "${pkgs.sks}/bin/sks db -hkp_address ${hkpAddress} -hkp_port ${hkpPort}";
+        };
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index 10596d6431d0..04b065f6ae4b 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -7,7 +7,7 @@ let
   torDirectory = "/var/lib/tor";
 
   opt    = name: value: optionalString (value != null) "${name} ${value}";
-  optint = name: value: optionalString (value != 0)    "${name} ${toString value}";
+  optint = name: value: optionalString (value != null && value != 0)    "${name} ${toString value}";
 
   torRc = ''
     User tor
@@ -17,7 +17,7 @@ let
       GeoIPv6File ${pkgs.tor.geoip}/share/tor/geoip6
     ''}
 
-    ${optint "ControlPort" cfg.controlPort}
+    ${optint "ControlPort" (toString cfg.controlPort)}
   ''
   # Client connection config
   + optionalString cfg.client.enable  ''
@@ -27,7 +27,8 @@ let
   ''
   # Relay config
   + optionalString cfg.relay.enable ''
-    ORPort ${cfg.relay.portSpec}
+    ORPort ${toString cfg.relay.port}
+    ${opt "Address" cfg.relay.address}
     ${opt "Nickname" cfg.relay.nickname}
     ${opt "ContactInfo" cfg.relay.contactInfo}
 
@@ -36,19 +37,32 @@ let
     ${opt "AccountingMax" cfg.relay.accountingMax}
     ${opt "AccountingStart" cfg.relay.accountingStart}
 
-    ${if cfg.relay.isExit then
+    ${if (cfg.relay.role == "exit") then
         opt "ExitPolicy" cfg.relay.exitPolicy
       else
         "ExitPolicy reject *:*"}
 
-    ${optionalString cfg.relay.isBridge ''
+    ${optionalString (elem cfg.relay.role ["bridge" "private-bridge"]) ''
       BridgeRelay 1
       ServerTransportPlugin obfs2,obfs3 exec ${pkgs.pythonPackages.obfsproxy}/bin/obfsproxy managed
+      ExtORPort auto
+      ${optionalString (cfg.relay.role == "private-bridge") ''
+        ExtraInfoStatistics 0
+        PublishServerDescriptor 0
+      ''}
     ''}
   ''
+  # Hidden services
+  + concatStrings (flip mapAttrsToList cfg.hiddenServices (n: v: ''
+    HiddenServiceDir ${torDirectory}/onion/${v.name}
+    ${flip concatMapStrings v.map (p: ''
+      HiddenServicePort ${toString p.port} ${p.destination}
+    '')}
+  ''))
   + cfg.extraConfig;
 
   torRcFile = pkgs.writeText "torrc" torRc;
+
 in
 {
   options = {
@@ -84,8 +98,8 @@ in
       };
 
       controlPort = mkOption {
-        type = types.int;
-        default = 0;
+        type = types.nullOr (types.either types.int types.str);
+        default = null;
         example = 9051;
         description = ''
           If set, Tor will accept connections on the specified port
@@ -121,9 +135,10 @@ in
           example = "192.168.0.1:9101";
           description = ''
             Bind to this address to listen for connections from
-            Socks-speaking applications. Same as socksListenAddress
-            but uses weaker circuit isolation to provide performance
-            suitable for a web browser.
+            Socks-speaking applications. Same as
+            <option>socksListenAddress</option> but uses weaker
+            circuit isolation to provide performance suitable for a
+            web browser.
            '';
          };
 
@@ -133,9 +148,9 @@ in
           example = "accept 192.168.0.0/16, reject *";
           description = ''
             Entry policies to allow/deny SOCKS requests based on IP
-            address.  First entry that matches wins. If no SocksPolicy
+            address. First entry that matches wins. If no SocksPolicy
             is set, we accept all (and only) requests from
-            SocksListenAddress.
+            <option>socksListenAddress</option>.
           '';
         };
 
@@ -164,45 +179,147 @@ in
           description = ''
             Whether to enable relaying TOR traffic for others.
 
-            See https://www.torproject.org/docs/tor-doc-relay for details.
-          '';
-        };
-
-        isBridge = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Bridge relays (or "bridges") are Tor relays that aren't
-            listed in the main directory. Since there is no complete
-            public list of them, even if an ISP is filtering
-            connections to all the known Tor relays, they probably
-            won't be able to block all the bridges.
-
-            A bridge relay can't be an exit relay.
-
-            You need to set relay.enable to true for this option to
-            take effect.
+            See <link xlink:href="https://www.torproject.org/docs/tor-doc-relay" />
+            for details.
 
-            The bridge is set up with an obfuscated transport proxy.
-
-            See https://www.torproject.org/bridges.html.en for more info.
+            Setting this to true requires setting
+            <option>services.tor.relay.role</option>
+            and
+            <option>services.tor.relay.port</option>
+            options.
           '';
         };
 
-        isExit = mkOption {
-          type = types.bool;
-          default = false;
+        role = mkOption {
+          type = types.enum [ "exit" "relay" "bridge" "private-bridge" ];
           description = ''
-            An exit relay allows Tor users to access regular Internet
-            services.
-
-            Unlike running a non-exit relay, running an exit relay may
-            expose you to abuse complaints. See
-            https://www.torproject.org/faq.html.en#ExitPolicies for
-            more info.
-
-            You can specify which services Tor users may access via
-            your exit relay using exitPolicy option.
+            Your role in Tor network. There're several options:
+
+            <variablelist>
+            <varlistentry>
+              <term><literal>exit</literal></term>
+              <listitem>
+                <para>
+                  An exit relay. This allows Tor users to access regular
+                  Internet services through your public IP.
+                </para>
+
+                <important><para>
+                  Running an exit relay may expose you to abuse
+                  complaints. See
+                  <link xlink:href="https://www.torproject.org/faq.html.en#ExitPolicies" />
+                  for more info.
+                </para></important>
+
+                <para>
+                  You can specify which services Tor users may access via
+                  your exit relay using <option>exitPolicy</option> option.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><literal>relay</literal></term>
+              <listitem>
+                <para>
+                  Regular relay. This allows Tor users to relay onion
+                  traffic to other Tor nodes, but not to public
+                  Internet.
+                </para>
+
+                <important><para>
+                  Note that some misconfigured and/or disrespectful
+                  towards privacy sites will block you even if your
+                  relay is not an exit relay. That is, just being listed
+                  in a public relay directory can have unwanted
+                  consequences.
+
+                  Which means you might not want to use
+                  this role if you browse public Internet from the same
+                  network as your relay, unless you want to write
+                  e-mails to those sites (you should!).
+                </para></important>
+
+                <para>
+                  See
+                  <link xlink:href="https://www.torproject.org/docs/tor-doc-relay.html.en" />
+                  for more info.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><literal>bridge</literal></term>
+              <listitem>
+                <para>
+                  Regular bridge. Works like a regular relay, but
+                  doesn't list you in the public relay directory and
+                  hides your Tor node behind obfsproxy.
+                </para>
+
+                <para>
+                  Using this option will make Tor advertise your bridge
+                  to users through various mechanisms like
+                  <link xlink:href="https://bridges.torproject.org/" />, though.
+                </para>
+
+                <important>
+                  <para>
+                    WARNING: THE FOLLOWING PARAGRAPH IS NOT LEGAL ADVISE.
+                    Consult with your lawer when in doubt.
+                  </para>
+
+                  <para>
+                    This role should be safe to use in most situations
+                    (unless the act of forwarding traffic for others is
+                    a punishable offence under your local laws, which
+                    would be pretty insane as it would make ISP
+                    illegal).
+                  </para>
+                </important>
+
+                <para>
+                  See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" />
+                  for more info.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><literal>private-bridge</literal></term>
+              <listitem>
+                <para>
+                  Private bridge. Works like regular bridge, but does
+                  not advertise your node in any way.
+                </para>
+
+                <para>
+                  Using this role means that you won't contribute to Tor
+                  network in any way unless you advertise your node
+                  yourself in some way.
+                </para>
+
+                <para>
+                  Use this if you want to run a private bridge, for
+                  example because you'll give out your bridge address
+                  manually to your friends.
+                </para>
+
+                <para>
+                  Switching to this role after measurable time in
+                  "bridge" role is pretty useless as some Tor users
+                  would have learned about your node already. In the
+                  latter case you can still change
+                  <option>port</option> option.
+                </para>
+
+                <para>
+                  See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" />
+                  for more info.
+                </para>
+              </listitem>
+            </varlistentry>
+            </variablelist>
           '';
         };
 
@@ -229,11 +346,11 @@ in
           default = null;
           example = "450 GBytes";
           description = ''
-            Specify maximum bandwidth allowed during an accounting
-            period. This allows you to limit overall tor bandwidth
-            over some time period. See the
-            <literal>AccountingMax</literal> option by looking at the
-            tor manual (<literal>man tor</literal>) for more.
+            Specify maximum bandwidth allowed during an accounting period. This
+            allows you to limit overall tor bandwidth over some time period.
+            See the <literal>AccountingMax</literal> option by looking at the
+            tor manual <citerefentry><refentrytitle>tor</refentrytitle>
+            <manvolnum>1</manvolnum></citerefentry> for more.
 
             Note this limit applies individually to upload and
             download; if you specify <literal>"500 GBytes"</literal>
@@ -247,16 +364,17 @@ in
           default = null;
           example = "month 1 1:00";
           description = ''
-            Specify length of an accounting period. This allows you to
-            limit overall tor bandwidth over some time period. See the
-            <literal>AccountingStart</literal> option by looking at
-            the tor manual (<literal>man tor</literal>) for more.
+            Specify length of an accounting period. This allows you to limit
+            overall tor bandwidth over some time period. See the
+            <literal>AccountingStart</literal> option by looking at the tor
+            manual <citerefentry><refentrytitle>tor</refentrytitle>
+            <manvolnum>1</manvolnum></citerefentry> for more.
           '';
         };
 
         bandwidthRate = mkOption {
-          type = types.int;
-          default = 0;
+          type = types.nullOr types.int;
+          default = null;
           example = 100;
           description = ''
             Specify this to limit the bandwidth usage of relayed (server)
@@ -265,7 +383,7 @@ in
         };
 
         bandwidthBurst = mkOption {
-          type = types.int;
+          type = types.nullOr types.int;
           default = cfg.relay.bandwidthRate;
           example = 200;
           description = ''
@@ -275,13 +393,24 @@ in
           '';
         };
 
-        portSpec = mkOption {
-          type    = types.str;
-          example = "143";
+        address = mkOption {
+          type    = types.nullOr types.str;
+          default = null;
+          example = "noname.example.com";
+          description = ''
+            The IP address or full DNS name for advertised address of your relay.
+            Leave unset and Tor will guess.
+          '';
+        };
+
+        port = mkOption {
+          type    = types.either types.int types.str;
+          example = 143;
           description = ''
-            What port to advertise for Tor connections. This corresponds
-            to the <literal>ORPort</literal> section in the Tor manual; see
-            <literal>man tor</literal> for more details.
+            What port to advertise for Tor connections. This corresponds to the
+            <literal>ORPort</literal> section in the Tor manual; see
+            <citerefentry><refentrytitle>tor</refentrytitle>
+            <manvolnum>1</manvolnum></citerefentry> for more details.
 
             At a minimum, you should just specify the port for the
             relay to listen on; a common one like 143, 22, 80, or 443
@@ -299,13 +428,15 @@ in
             considered first to last, and the first match wins. If you
             want to _replace_ the default exit policy, end this with
             either a reject *:* or an accept *:*. Otherwise, you're
-            _augmenting_ (prepending to) the default exit
-            policy. Leave commented to just use the default, which is
+            _augmenting_ (prepending to) the default exit policy.
+            Leave commented to just use the default, which is
             available in the man page or at
-            https://www.torproject.org/documentation.html
+            <link xlink:href="https://www.torproject.org/documentation.html" />.
 
-            Look at https://www.torproject.org/faq-abuse.html#TypicalAbuses
-            for issues you might encounter if you use the default exit policy.
+            Look at
+            <link xlink:href="https://www.torproject.org/faq-abuse.html#TypicalAbuses" />
+            for issues you might encounter if you use the default
+            exit policy.
 
             If certain IPs and ports are blocked externally, e.g. by
             your firewall, you should update your exit policy to
@@ -314,15 +445,124 @@ in
           '';
         };
       };
+
+      hiddenServices = mkOption {
+        description = ''
+          A set of static hidden services that terminate their Tor
+          circuits at this node.
+
+          Every element in this set declares a virtual onion host.
+
+          You can specify your onion address by putting corresponding
+          private key to an appropriate place in ${torDirectory}.
+
+          For services without private keys in ${torDirectory} Tor
+          daemon will generate random key pairs (which implies random
+          onion addresses) on restart. The latter could take a while,
+          please be patient.
+
+          <note><para>
+            Hidden services can be useful even if you don't intend to
+            actually <emphasis>hide</emphasis> them, since they can
+            also be seen as a kind of NAT traversal mechanism.
+
+            E.g. the example will make your sshd, whatever runs on
+            "8080" and your mail server available from anywhere where
+            the Tor network is available (which, with the help from
+            bridges, is pretty much everywhere), even if both client
+            and server machines are behind NAT you have no control
+            over.
+          </para></note>
+        '';
+        default = {};
+        example = literalExample ''
+          { "my-hidden-service-example".map = [
+              { port = 22; }                # map ssh port to this machine's ssh
+              { port = 80; toPort = 8080; } # map http port to whatever runs on 8080
+              { port = "sip"; toHost = "mail.example.com"; toPort = "imap"; } # because we can
+            ];
+          }
+        '';
+        type = types.loaOf (types.submodule ({name, config, ...}: {
+          options = {
+
+             name = mkOption {
+               type = types.str;
+               description = ''
+                 Name of this tor hidden service.
+
+                 This is purely descriptive.
+
+                 After restarting Tor daemon you should be able to
+                 find your .onion address in
+                 <literal>${torDirectory}/onion/$name/hostname</literal>.
+               '';
+             };
+
+             map = mkOption {
+               default = [];
+               description = "Port mapping for this hidden service.";
+               type = types.listOf (types.submodule ({config, ...}: {
+                 options = {
+
+                   port = mkOption {
+                     type = types.either types.int types.str;
+                     example = 80;
+                     description = ''
+                       Hidden service port to "bind to".
+                     '';
+                   };
+
+                   destination = mkOption {
+                     internal = true;
+                     type = types.str;
+                     description = "Forward these connections where?";
+                   };
+
+                   toHost = mkOption {
+                     type = types.str;
+                     default = "127.0.0.1";
+                     description = "Mapping destination host.";
+                   };
+
+                   toPort = mkOption {
+                     type = types.either types.int types.str;
+                     example = 8080;
+                     description = "Mapping destination port.";
+                   };
+
+                 };
+
+                 config = {
+                   toPort = mkDefault config.port;
+                   destination = mkDefault "${config.toHost}:${toString config.toPort}";
+                 };
+               }));
+             };
+
+          };
+
+          config = {
+            name = mkDefault name;
+          };
+        }));
+      };
     };
   };
 
   config = mkIf cfg.enable {
-    assertions = singleton
-      { message = "Can't be both an exit and a bridge relay at the same time";
-        assertion =
-          cfg.relay.enable -> !(cfg.relay.isBridge && cfg.relay.isExit);
-      };
+    # Not sure if `cfg.relay.role == "private-bridge"` helps as tor
+    # sends a lot of stats
+    warnings = optional (cfg.relay.enable && cfg.hiddenServices != {})
+      ''
+        Running Tor hidden services on a public relay makes the
+        presence of hidden services visible through simple statistical
+        analysis of publicly available data.
+
+        You can safely ignore this warning if you don't intend to
+        actually hide your hidden services. In either case, you can
+        always create a container/VM with a separate Tor daemon instance.
+      '';
 
     users.extraGroups.tor.gid = config.ids.gids.tor;
     users.extraUsers.tor =
@@ -342,9 +582,13 @@ in
         restartTriggers = [ torRcFile ];
 
         # Translated from the upstream contrib/dist/tor.service.in
+        preStart = ''
+          install -o tor -g tor -d ${torDirectory}/onion
+          ${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config
+        '';
+
         serviceConfig =
           { Type         = "simple";
-            ExecStartPre = "${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config";
             ExecStart    = "${pkgs.tor}/bin/tor -f ${torRcFile} --RunAsDaemon 0";
             ExecReload   = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
             KillSignal   = "SIGINT";
diff --git a/nixos/modules/services/security/usbguard.nix b/nixos/modules/services/security/usbguard.nix
new file mode 100644
index 000000000000..1f2c56a9efa1
--- /dev/null
+++ b/nixos/modules/services/security/usbguard.nix
@@ -0,0 +1,200 @@
+{config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.usbguard;
+
+  # valid policy options
+  policy = (types.enum [ "allow" "block" "reject" "keep" "apply-policy" ]);
+
+  # decide what file to use for rules
+  ruleFile = if cfg.rules != null then pkgs.writeText "usbguard-rules" cfg.rules else cfg.ruleFile;
+
+  daemonConf = ''
+      # generated by nixos/modules/services/security/usbguard.nix
+      RuleFile=${ruleFile}
+      ImplicitPolicyTarget=${cfg.implictPolicyTarget}
+      PresentDevicePolicy=${cfg.presentDevicePolicy}
+      PresentControllerPolicy=${cfg.presentControllerPolicy}
+      InsertedDevicePolicy=${cfg.insertedDevicePolicy}
+      RestoreControllerDeviceState=${if cfg.restoreControllerDeviceState then "true" else "false"}
+      # this does not seem useful for endusers to change
+      DeviceManagerBackend=uevent
+      IPCAllowedUsers=${concatStringsSep " " cfg.IPCAllowedUsers}
+      IPCAllowedGroups=${concatStringsSep " " cfg.IPCAllowedGroups}
+      IPCAccessControlFiles=${cfg.IPCAccessControlFiles}
+      DeviceRulesWithPort=${if cfg.deviceRulesWithPort then "true" else "false"}
+      AuditFilePath=${cfg.auditFilePath}
+    '';
+
+    daemonConfFile = pkgs.writeText "usbguard-daemon-conf" daemonConf;
+
+in {
+
+  ###### interface
+
+  options = {
+    services.usbguard = {
+      enable = mkEnableOption "USBGuard daemon";
+
+      ruleFile = mkOption {
+        type = types.path;
+        default = "/var/lib/usbguard/rules.conf";
+        description = ''
+          The USBGuard daemon will use this file to load the policy rule set
+          from it and to write new rules received via the IPC interface.
+
+          Running the command <literal>usbguard generate-policy</literal> as
+          root will generate a config for your currently plugged in devices.
+          For a in depth guide consult the official documentation.
+
+          Setting the <literal>rules</literal> option will ignore the
+          <literal>ruleFile</literal> option.
+        '';
+      };
+
+      rules = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = ''
+          allow with-interface equals { 08:*:* }
+        '';
+        description = ''
+          The USBGuard daemon will load this policy rule set. Modifying it via
+          the IPC interface won't work if you use this option, since the
+          contents of this option will be written into the nix-store it will be
+          read-only.
+
+          You can still use <literal> usbguard generate-policy</literal> to
+          generate rules, but you would have to insert them here.
+
+          Setting the <literal>rules</literal> option will ignore the
+          <literal>ruleFile</literal> option.
+        '';
+      };
+
+      implictPolicyTarget = mkOption {
+        type = policy;
+        default = "block";
+        description = ''
+          How to treat USB devices that don't match any rule in the policy.
+          Target should be one of allow, block or reject (logically remove the
+          device node from the system).
+        '';
+      };
+
+      presentDevicePolicy = mkOption {
+        type = policy;
+        default = "apply-policy";
+        description = ''
+          How to treat USB devices that are already connected when the daemon
+          starts. Policy should be one of allow, block, reject, keep (keep
+          whatever state the device is currently in) or apply-policy (evaluate
+          the rule set for every present device).
+        '';
+      };
+
+      presentControllerPolicy = mkOption {
+        type = policy;
+        default = "keep";
+        description = ''
+          How to treat USB controller devices that are already connected when
+          the daemon starts. One of allow, block, reject, keep or apply-policy.
+        '';
+      };
+
+      insertedDevicePolicy = mkOption {
+        type = policy;
+        default = "apply-policy";
+        description = ''
+          How to treat USB devices that are already connected after the daemon
+          starts. One of block, reject, apply-policy.
+        '';
+      };
+
+      restoreControllerDeviceState = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          The  USBGuard  daemon  modifies  some attributes of controller
+          devices like the default authorization state of new child device
+          instances. Using this setting, you can controll whether the daemon
+          will try to restore the attribute values to the state before
+          modificaton on shutdown.
+        '';
+      };
+
+      IPCAllowedUsers = mkOption {
+        type = types.listOf types.str;
+        default = [ "root" ];
+        example = [ "root" "yourusername" ];
+        description = ''
+          A list of usernames that the daemon will accept IPC connections from.
+        '';
+      };
+
+      IPCAllowedGroups = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "wheel" ];
+        description = ''
+          A list of groupnames that the daemon will accept IPC connections
+          from.
+        '';
+      };
+
+      IPCAccessControlFiles = mkOption {
+        type = types.path;
+        default = "/var/lib/usbguard/IPCAccessControl.d/";
+        description = ''
+          The files at this location will be interpreted by the daemon as IPC
+          access control definition files. See the IPC ACCESS CONTROL section
+          in <citerefentry><refentrytitle>usbguard-daemon.conf</refentrytitle>
+          <manvolnum>5</manvolnum></citerefentry> for more details.
+        '';
+      };
+
+      deviceRulesWithPort = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Generate device specific rules including the "via-port" attribute.
+        '';
+      };
+
+      auditFilePath = mkOption {
+        type = types.path;
+        default = "/var/log/usbguard/usbguard-audit.log";
+        description = ''
+          USBGuard audit events log file path.
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.usbguard ];
+
+    systemd.services.usbguard = {
+      description = "USBGuard daemon";
+
+      wantedBy = [ "basic.target" ];
+      wants = [ "systemd-udevd.service" "local-fs.target" ];
+
+      # make sure an empty rule file and required directories exist
+      preStart = ''mkdir -p $(dirname "${cfg.ruleFile}") "${cfg.IPCAccessControlFiles}" && ([ -f "${cfg.ruleFile}" ] || touch ${cfg.ruleFile})'';
+
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = ''${pkgs.usbguard}/bin/usbguard-daemon -d -k -c ${daemonConfFile}'';
+        Restart = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/security/vault.nix b/nixos/modules/services/security/vault.nix
new file mode 100644
index 000000000000..146afec344ab
--- /dev/null
+++ b/nixos/modules/services/security/vault.nix
@@ -0,0 +1,143 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.vault;
+
+  configFile = pkgs.writeText "vault.hcl" ''
+    listener "tcp" {
+      address = "${cfg.address}"
+      ${if (cfg.tlsCertFile == null || cfg.tlsKeyFile == null) then ''
+          tls_disable = "true"
+        '' else ''
+          tls_cert_file = "${cfg.tlsCertFile}"
+          tls_key_file = "${cfg.tlsKeyFile}"
+        ''}
+      ${cfg.listenerExtraConfig}
+    }
+    storage "${cfg.storageBackend}" {
+      ${optionalString (cfg.storagePath   != null) ''path = "${cfg.storagePath}"''}
+      ${optionalString (cfg.storageConfig != null) cfg.storageConfig}
+    }
+    ${optionalString (cfg.telemetryConfig != "") ''
+        telemetry {
+          ${cfg.telemetryConfig}
+        }
+      ''}
+  '';
+in
+{
+  options = {
+
+    services.vault = {
+
+      enable = mkEnableOption "Vault daemon";
+
+      address = mkOption {
+        type = types.str;
+        default = "127.0.0.1:8200";
+        description = "The name of the ip interface to listen to";
+      };
+
+      tlsCertFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/path/to/your/cert.pem";
+        description = "TLS certificate file. TLS will be disabled unless this option is set";
+      };
+
+      tlsKeyFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/path/to/your/key.pem";
+        description = "TLS private key file. TLS will be disabled unless this option is set";
+      };
+
+      listenerExtraConfig = mkOption {
+        type = types.lines;
+        default = ''
+          tls_min_version = "tls12"
+        '';
+        description = "extra configuration";
+      };
+
+      storageBackend = mkOption {
+        type = types.enum [ "inmem" "file" "consul" "zookeeper" "s3" "azure" "dynamodb" "etcd" "mssql" "mysql" "postgresql" "swift" "gcs" ];
+        default = "inmem";
+        description = "The name of the type of storage backend";
+      };
+
+      storagePath = mkOption {
+        type = types.nullOr types.path;
+        default = if cfg.storageBackend == "file" then "/var/lib/vault" else null;
+        description = "Data directory for file backend";
+      };
+
+      storageConfig = mkOption {
+        type = types.nullOr types.lines;
+        default = null;
+        description = "Storage configuration";
+      };
+
+      telemetryConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Telemetry configuration";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      { assertion = cfg.storageBackend == "inmem" -> (cfg.storagePath == null && cfg.storageConfig == null);
+        message = ''The "inmem" storage expects no services.vault.storagePath nor services.vault.storageConfig'';
+      }
+      { assertion = (cfg.storageBackend == "file" -> (cfg.storagePath != null && cfg.storageConfig == null)) && (cfg.storagePath != null -> cfg.storageBackend == "file");
+        message = ''You must set services.vault.storagePath only when using the "file" backend'';
+      }
+    ];
+
+    users.extraUsers.vault = {
+      name = "vault";
+      group = "vault";
+      uid = config.ids.uids.vault;
+      description = "Vault daemon user";
+    };
+    users.extraGroups.vault.gid = config.ids.gids.vault;
+
+    systemd.services.vault = {
+      description = "Vault server daemon";
+
+      wantedBy = ["multi-user.target"];
+      after = [ "network.target" ]
+           ++ optional (config.services.consul.enable && cfg.storageBackend == "consul") "consul.service";
+
+      restartIfChanged = false; # do not restart on "nixos-rebuild switch". It would seal the storage and disrupt the clients.
+
+      preStart = optionalString (cfg.storagePath != null) ''
+        install -d -m0700 -o vault -g vault "${cfg.storagePath}"
+      '';
+
+      serviceConfig = {
+        User = "vault";
+        Group = "vault";
+        PermissionsStartOnly = true;
+        ExecStart = "${pkgs.vault}/bin/vault server -config ${configFile}";
+        PrivateDevices = true;
+        PrivateTmp = true;
+        ProtectSystem = "full";
+        ProtectHome = "read-only";
+        AmbientCapabilities = "cap_ipc_lock";
+        NoNewPrivileges = true;
+        KillSignal = "SIGINT";
+        TimeoutStopSec = "30s";
+        Restart = "on-failure";
+        StartLimitInterval = "60s";
+        StartLimitBurst = 3;
+      };
+
+      unitConfig.RequiresMountsFor = optional (cfg.storagePath != null) cfg.storagePath;
+    };
+  };
+
+}
diff --git a/nixos/modules/services/system/saslauthd.nix b/nixos/modules/services/system/saslauthd.nix
new file mode 100644
index 000000000000..281716cf1860
--- /dev/null
+++ b/nixos/modules/services/system/saslauthd.nix
@@ -0,0 +1,63 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  nssModulesPath = config.system.nssModules.path;
+  cfg = config.services.saslauthd;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.saslauthd = {
+
+      enable = mkEnableOption "Whether to enable the Cyrus SASL authentication daemon.";
+
+      package = mkOption {
+        default = pkgs.cyrus_sasl.bin;
+        defaultText = "pkgs.cyrus_sasl.bin";
+        type = types.package;
+        description = "Cyrus SASL package to use.";
+      };
+
+      mechanism = mkOption {
+        type = types.str;
+        default = "pam";
+        description = "Auth mechanism to use";
+      };
+
+      config = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Configuration to use for Cyrus SASL authentication daemon.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.saslauthd = {
+      description = "Cyrus SASL authentication daemon";
+
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = "@${cfg.package}/sbin/saslauthd saslauthd -a ${cfg.mechanism} -O ${pkgs.writeText "saslauthd.conf" cfg.config}";
+        Type = "forking";
+        PIDFile = "/run/saslauthd/saslauthd.pid";
+        Restart = "always";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/torrent/deluge.nix b/nixos/modules/services/torrent/deluge.nix
index 2534f09b555a..ec1e97f4125e 100644
--- a/nixos/modules/services/torrent/deluge.nix
+++ b/nixos/modules/services/torrent/deluge.nix
@@ -42,9 +42,9 @@ in {
       after = [ "network.target" ];
       description = "Deluge BitTorrent Daemon";
       wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.pythonPackages.deluge ];
+      path = [ pkgs.deluge ];
       serviceConfig = {
-        ExecStart = "${pkgs.pythonPackages.deluge}/bin/deluged -d";
+        ExecStart = "${pkgs.deluge}/bin/deluged -d";
         # To prevent "Quit & shutdown daemon" from working; we want systemd to manage it!
         Restart = "on-success";
         User = "deluge";
@@ -57,13 +57,13 @@ in {
       after = [ "network.target" ];
       description = "Deluge BitTorrent WebUI";
       wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.pythonPackages.deluge ];
-      serviceConfig.ExecStart = "${pkgs.pythonPackages.deluge}/bin/deluge --ui web";
+      path = [ pkgs.deluge ];
+      serviceConfig.ExecStart = "${pkgs.deluge}/bin/deluge --ui web";
       serviceConfig.User = "deluge";
       serviceConfig.Group = "deluge";
     };
 
-    environment.systemPackages = [ pkgs.pythonPackages.deluge ];
+    environment.systemPackages = [ pkgs.deluge ];
 
     users.extraUsers.deluge = {
       group = "deluge";
diff --git a/nixos/modules/services/ttys/agetty.nix b/nixos/modules/services/ttys/agetty.nix
index 051d54e932fb..3429397d2cc2 100644
--- a/nixos/modules/services/ttys/agetty.nix
+++ b/nixos/modules/services/ttys/agetty.nix
@@ -68,24 +68,35 @@ in
     services.mingetty.greetingLine = mkDefault ''<<< Welcome to NixOS ${config.system.nixosLabel} (\m) - \l >>>'';
 
     systemd.services."getty@" =
-      { serviceConfig.ExecStart = gettyCmd "--noclear --keep-baud %I 115200,38400,9600 $TERM";
+      { serviceConfig.ExecStart = [
+          "" # override upstream default with an empty ExecStart
+          (gettyCmd "--noclear --keep-baud %I 115200,38400,9600 $TERM")
+        ];
         restartIfChanged = false;
       };
 
     systemd.services."serial-getty@" =
-      { serviceConfig.ExecStart =
-          let speeds = concatStringsSep "," (map toString config.services.mingetty.serialSpeed);
-          in gettyCmd "%I ${speeds} $TERM";
+      let speeds = concatStringsSep "," (map toString config.services.mingetty.serialSpeed); in
+      { serviceConfig.ExecStart = [
+          "" # override upstream default with an empty ExecStart
+          (gettyCmd "%I ${speeds} $TERM")
+        ];
         restartIfChanged = false;
       };
 
     systemd.services."container-getty@" =
-      { serviceConfig.ExecStart = gettyCmd "--noclear --keep-baud pts/%I 115200,38400,9600 $TERM";
+      { serviceConfig.ExecStart = [
+          "" # override upstream default with an empty ExecStart
+          (gettyCmd "--noclear --keep-baud pts/%I 115200,38400,9600 $TERM")
+        ];
         restartIfChanged = false;
       };
 
     systemd.services."console-getty" =
-      { serviceConfig.ExecStart = gettyCmd "--noclear --keep-baud console 115200,38400,9600 $TERM";
+      { serviceConfig.ExecStart = [
+          "" # override upstream default with an empty ExecStart
+          (gettyCmd "--noclear --keep-baud console 115200,38400,9600 $TERM")
+        ];
         serviceConfig.Restart = "always";
         restartIfChanged = false;
         enable = mkDefault config.boot.isContainer;
diff --git a/nixos/modules/services/web-apps/atlassian/confluence.nix b/nixos/modules/services/web-apps/atlassian/confluence.nix
index c1d7d4ea06d4..2344b9922d0b 100644
--- a/nixos/modules/services/web-apps/atlassian/confluence.nix
+++ b/nixos/modules/services/web-apps/atlassian/confluence.nix
@@ -6,7 +6,22 @@ let
 
   cfg = config.services.confluence;
 
-  pkg = pkgs.atlassian-confluence;
+  pkg = pkgs.atlassian-confluence.override (optionalAttrs cfg.sso.enable {
+    enableSSO = cfg.sso.enable;
+    crowdProperties = ''
+      application.name                        ${cfg.sso.applicationName}
+      application.password                    ${cfg.sso.applicationPassword}
+      application.login.url                   ${cfg.sso.crowd}/console/
+
+      crowd.server.url                        ${cfg.sso.crowd}/services/
+      crowd.base.url                          ${cfg.sso.crowd}/
+
+      session.isauthenticated                 session.isauthenticated
+      session.tokenkey                        session.tokenkey
+      session.validationinterval              ${toString cfg.sso.validationInterval}
+      session.lastvalidation                  session.lastvalidation
+    '';
+  });
 
 in
 
@@ -76,6 +91,42 @@ in
         };
       };
 
+      sso = {
+        enable = mkEnableOption "SSO with Atlassian Crowd";
+
+        crowd = mkOption {
+          type = types.str;
+          example = "http://localhost:8095/crowd";
+          description = "Crowd Base URL without trailing slash";
+        };
+
+        applicationName = mkOption {
+          type = types.str;
+          example = "jira";
+          description = "Exact name of this Confluence instance in Crowd";
+        };
+
+        applicationPassword = mkOption {
+          type = types.str;
+          description = "Application password of this Confluence instance in Crowd";
+        };
+
+        validationInterval = mkOption {
+          type = types.int;
+          default = 2;
+          example = 0;
+          description = ''
+            Set to 0, if you want authentication checks to occur on each
+            request. Otherwise set to the number of minutes between request
+            to validate if the user is logged in or out of the Crowd SSO
+            server. Setting this value to 1 or higher will increase the
+            performance of Crowd's integration.
+          '';
+        };
+      };
+
+
+
       jrePackage = let
         jreSwitch = unfree: free: if config.nixpkgs.config.allowUnfree or false then unfree else free;
       in mkOption {
diff --git a/nixos/modules/services/web-apps/atlassian/jira.nix b/nixos/modules/services/web-apps/atlassian/jira.nix
index 0ab94d95eee8..19b6a8d30851 100644
--- a/nixos/modules/services/web-apps/atlassian/jira.nix
+++ b/nixos/modules/services/web-apps/atlassian/jira.nix
@@ -6,7 +6,22 @@ let
 
   cfg = config.services.jira;
 
-  pkg = pkgs.atlassian-jira;
+  pkg = pkgs.atlassian-jira.override {
+    enableSSO = cfg.sso.enable;
+    crowdProperties = ''
+      application.name                        ${cfg.sso.applicationName}
+      application.password                    ${cfg.sso.applicationPassword}
+      application.login.url                   ${cfg.sso.crowd}/console/
+
+      crowd.server.url                        ${cfg.sso.crowd}/services/
+      crowd.base.url                          ${cfg.sso.crowd}/
+
+      session.isauthenticated                 session.isauthenticated
+      session.tokenkey                        session.tokenkey
+      session.validationinterval              ${toString cfg.sso.validationInterval}
+      session.lastvalidation                  session.lastvalidation
+    '';
+  };
 
 in
 
@@ -82,6 +97,40 @@ in
         };
       };
 
+      sso = {
+        enable = mkEnableOption "SSO with Atlassian Crowd";
+
+        crowd = mkOption {
+          type = types.str;
+          example = "http://localhost:8095/crowd";
+          description = "Crowd Base URL without trailing slash";
+        };
+
+        applicationName = mkOption {
+          type = types.str;
+          example = "jira";
+          description = "Exact name of this JIRA instance in Crowd";
+        };
+
+        applicationPassword = mkOption {
+          type = types.str;
+          description = "Application password of this JIRA instance in Crowd";
+        };
+
+        validationInterval = mkOption {
+          type = types.int;
+          default = 2;
+          example = 0;
+          description = ''
+            Set to 0, if you want authentication checks to occur on each
+            request. Otherwise set to the number of minutes between request
+            to validate if the user is logged in or out of the Crowd SSO
+            server. Setting this value to 1 or higher will increase the
+            performance of Crowd's integration.
+          '';
+        };
+      };
+
       jrePackage = let
         jreSwitch = unfree: free: if config.nixpkgs.config.allowUnfree or false then unfree else free;
       in mkOption {
diff --git a/nixos/modules/services/web-apps/pgpkeyserver-lite.nix b/nixos/modules/services/web-apps/pgpkeyserver-lite.nix
new file mode 100644
index 000000000000..93f69bd12651
--- /dev/null
+++ b/nixos/modules/services/web-apps/pgpkeyserver-lite.nix
@@ -0,0 +1,75 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.pgpkeyserver-lite;
+  sksCfg = config.services.sks;
+
+  webPkg = cfg.package;
+
+in
+
+{
+
+  options = {
+
+    services.pgpkeyserver-lite = {
+
+      enable = mkEnableOption "pgpkeyserver-lite on a nginx vHost proxying to a gpg keyserver";
+
+      package = mkOption {
+        default = pkgs.pgpkeyserver-lite;
+        defaultText = "pkgs.pgpkeyserver-lite";
+        type = types.package;
+        description = "
+          Which webgui derivation to use.
+        ";
+      };
+
+      hostname = mkOption {
+        type = types.str;
+        description = "
+          Which hostname to set the vHost to that is proxying to sks.
+        ";
+      };     
+
+      hkpAddress = mkOption {
+        default = builtins.head sksCfg.hkpAddress;
+        type = types.str;
+        description = "
+          Wich ip address the sks-keyserver is listening on.
+        ";
+      };
+
+      hkpPort = mkOption {
+        default = sksCfg.hkpPort;
+        type = types.int;
+        description = "
+          Which port the sks-keyserver is listening on.
+        ";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    services.nginx.enable = true;
+
+    services.nginx.virtualHosts = let
+      hkpPort = builtins.toString cfg.hkpPort;
+    in {
+      "${cfg.hostname}" = {
+        root = webPkg;
+        locations = {
+          "/pks".extraConfig = ''
+            proxy_pass         http://${cfg.hkpAddress}:${hkpPort};
+            proxy_pass_header  Server;
+            add_header         Via "1.1 ${cfg.hostname}";
+          '';
+        };
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/piwik-doc.xml b/nixos/modules/services/web-apps/piwik-doc.xml
index a1d8a5b7556a..a393a182d36a 100644
--- a/nixos/modules/services/web-apps/piwik-doc.xml
+++ b/nixos/modules/services/web-apps/piwik-doc.xml
@@ -23,16 +23,24 @@
       and enter those credentials in your browser.
       You can use passwordless database authentication via the UNIX_SOCKET authentication plugin
       with the following SQL commands:
+
       <programlisting>
+        # For MariaDB
         INSTALL PLUGIN unix_socket SONAME 'auth_socket';
-        ALTER USER root IDENTIFIED VIA unix_socket;
         CREATE DATABASE piwik;
-        CREATE USER 'piwik'@'localhost' IDENTIFIED VIA unix_socket;
+        CREATE USER 'piwik'@'localhost' IDENTIFIED WITH unix_socket;
+        GRANT ALL PRIVILEGES ON piwik.* TO 'piwik'@'localhost';
+
+        # For MySQL
+        INSTALL PLUGIN auth_socket SONAME 'auth_socket.so';
+        CREATE DATABASE piwik;
+        CREATE USER 'piwik'@'localhost' IDENTIFIED WITH auth_socket;
         GRANT ALL PRIVILEGES ON piwik.* TO 'piwik'@'localhost';
       </programlisting>
+
       Then fill in <literal>piwik</literal> as database user and database name, and leave the password field blank.
-      This works with MariaDB and MySQL. This authentication works by allowing only the <literal>piwik</literal> unix
-      user to authenticate as <literal>piwik</literal> database (without needing a password), but no other users.
+      This authentication works by allowing only the <literal>piwik</literal> unix user to authenticate as the 
+      <literal>piwik</literal> database user (without needing a password), but no other users.
       For more information on passwordless login, see
       <link xlink:href="https://mariadb.com/kb/en/mariadb/unix_socket-authentication-plugin/" />.
     </para>
@@ -71,16 +79,6 @@
           You can safely ignore this, unless you need a plugin that needs JavaScript tracker access.
         </para>
       </listitem>
-
-      <listitem>
-        <para>
-          Sending mail from piwik, e.g. for the password reset function, might not work out of the box:
-          There's a problem with using <command>sendmail</command> from <literal>php-fpm</literal> that is
-          being investigated at <link xlink:href="https://github.com/NixOS/nixpkgs/issues/26611" />.
-          If you have (or don't have) this problem as well, please report it. You can enable SMTP as method
-          to send mail in piwik's <quote>General Settings</quote> > <quote>Mail Server Settings</quote> instead.
-        </para>
-      </listitem>
     </itemizedlist>
   </section>
 
diff --git a/nixos/modules/services/web-apps/piwik.nix b/nixos/modules/services/web-apps/piwik.nix
index 26342a9c5f00..ce86c6873dd4 100644
--- a/nixos/modules/services/web-apps/piwik.nix
+++ b/nixos/modules/services/web-apps/piwik.nix
@@ -24,14 +24,17 @@ in {
         default = false;
         description = ''
           Enable piwik web analytics with php-fpm backend.
+          Either the nginx option or the webServerUser option is mandatory.
         '';
       };
 
       webServerUser = mkOption {
-        type = types.str;
-        example = "nginx";
+        type = types.nullOr types.str;
+        default = null;
+        example = "lighttpd";
         description = ''
-          Name of the owner of the ${phpSocket} fastcgi socket for piwik.
+          Name of the web server user that forwards requests to the ${phpSocket} fastcgi socket for piwik if the nginx
+          option is not used. Either this option or the nginx option is mandatory.
           If you want to use another webserver than nginx, you need to set this to that server's user
           and pass fastcgi requests to `index.php` and `piwik.php` to this socket.
         '';
@@ -57,47 +60,43 @@ in {
       };
 
       nginx = mkOption {
-        # TODO: for maximum flexibility, it would be nice to use nginx's vhost_options module
-        #       but this only makes sense if we can somehow specify defaults suitable for piwik.
-        #       But users can always copy the piwik nginx config to their configuration.nix and customize it.
-        type = types.nullOr (types.submodule {
-          options = {
-            virtualHost = mkOption {
-              type = types.str;
-              default = "piwik.${config.networking.hostName}";
-              example = "piwik.$\{config.networking.hostName\}";
-              description = ''
-                  Name of the nginx virtualhost to use and set up.
-              '';
-            };
-            enableSSL = mkOption {
-              type = types.bool;
-              default = true;
-              description = "Whether to enable https.";
-            };
-            forceSSL = mkOption {
-              type = types.bool;
-              default = true;
-              description = "Whether to always redirect to https.";
-            };
-            enableACME = mkOption {
-              type = types.bool;
-              default = true;
-              description = "Whether to ask Let's Encrypt to sign a certificate for this vhost.";
-            };
-          };
-        });
+        type = types.nullOr (types.submodule (
+          recursiveUpdate
+            (import ../web-servers/nginx/vhost-options.nix { inherit config lib; })
+            {
+              # enable encryption by default,
+              # as sensitive login and piwik data should not be transmitted in clear text.
+              options.forceSSL.default = true;
+              options.enableACME.default = true;
+            }
+        )
+        );
         default = null;
-        example = { virtualHost = "stats.$\{config.networking.hostName\}"; };
+        example = {
+          serverName = "stats.$\{config.networking.hostName\}";
+          enableACME = false;
+        };
         description = ''
-            The options to use to configure an nginx virtualHost.
-            If null (the default), no nginx virtualHost will be configured.
+            With this option, you can customize an nginx virtualHost which already has sensible defaults for piwik.
+            Either this option or the webServerUser option is mandatory.
+            Set this to {} to just enable the virtualHost if you don't need any customization.
+            If enabled, then by default, the serverName is piwik.$\{config.networking.hostName\}, SSL is active,
+            and certificates are acquired via ACME.
+            If this is set to null (the default), no nginx virtualHost will be configured.
         '';
       };
     };
   };
 
   config = mkIf cfg.enable {
+    warnings = mkIf (cfg.nginx != null && cfg.webServerUser != null) [
+      "If services.piwik.nginx is set, services.piwik.nginx.webServerUser is ignored and should be removed."
+    ];
+
+    assertions = [ {
+        assertion = cfg.nginx != null || cfg.webServerUser != null;
+        message = "Either services.piwik.nginx or services.piwik.nginx.webServerUser is mandatory";
+    }];
 
     users.extraUsers.${user} = {
       isSystemUser = true;
@@ -153,10 +152,16 @@ in {
       serviceConfig.UMask = "0007";
     };
 
-    services.phpfpm.poolConfigs = {
+    services.phpfpm.poolConfigs = let
+      # workaround for when both are null and need to generate a string,
+      # which is illegal, but as assertions apparently are being triggered *after* config generation,
+      # we have to avoid already throwing errors at this previous stage.
+      socketOwner = if (cfg.nginx != null) then config.services.nginx.user
+      else if (cfg.webServerUser != null) then cfg.webServerUser else "";
+    in {
       ${pool} = ''
         listen = "${phpSocket}"
-        listen.owner = ${cfg.webServerUser}
+        listen.owner = ${socketOwner}
         listen.group = root
         listen.mode = 0600
         user = ${user}
@@ -170,12 +175,15 @@ in {
       # References:
       # https://fralef.me/piwik-hardening-with-nginx-and-php-fpm.html
       # https://github.com/perusio/piwik-nginx
-      ${cfg.nginx.virtualHost} = {
-        root = "${pkgs.piwik}/share";
-        enableSSL  = cfg.nginx.enableSSL;
-        enableACME = cfg.nginx.enableACME;
-        forceSSL   = cfg.nginx.forceSSL;
-
+      "${user}.${config.networking.hostName}" = mkMerge [ cfg.nginx {
+        # don't allow to override the root easily, as it will almost certainly break piwik.
+        # disadvantage: not shown as default in docs.
+        root = mkForce "${pkgs.piwik}/share";
+
+        # define locations here instead of as the submodule option's default
+        # so that they can easily be extended with additional locations if required
+        # without needing to redefine the piwik ones.
+        # disadvantage: not shown as default in docs.
         locations."/" = {
           index = "index.php";
         };
@@ -208,7 +216,7 @@ in {
         locations."= /piwik.js".extraConfig = ''
           expires 1M;
         '';
-      };
+      }];
     };
   };
 
diff --git a/nixos/modules/services/web-servers/caddy.nix b/nixos/modules/services/web-servers/caddy.nix
index ee32a1c86d4d..d8efa24bc6d5 100644
--- a/nixos/modules/services/web-servers/caddy.nix
+++ b/nixos/modules/services/web-servers/caddy.nix
@@ -5,12 +5,22 @@ with lib;
 let
   cfg = config.services.caddy;
   configFile = pkgs.writeText "Caddyfile" cfg.config;
-in
-{
+in {
   options.services.caddy = {
     enable = mkEnableOption "Caddy web server";
 
     config = mkOption {
+      default = "";
+      example = ''
+        example.com {
+        gzip
+        minify
+        log syslog
+
+        root /srv/http
+        }
+      '';
+      type = types.lines;
       description = "Verbatim Caddyfile to use";
     };
 
diff --git a/nixos/modules/services/web-servers/lighttpd/default.nix b/nixos/modules/services/web-servers/lighttpd/default.nix
index 84cd0a4095d9..45a65965112a 100644
--- a/nixos/modules/services/web-servers/lighttpd/default.nix
+++ b/nixos/modules/services/web-servers/lighttpd/default.nix
@@ -37,8 +37,10 @@ let
     "mod_rrdtool"
     "mod_accesslog"
     # Remaining list of modules, order assumed to be unimportant.
+    "mod_authn_file"
+    "mod_authn_mysql"
     "mod_cml"
-    "mod_dirlisting"
+    "mod_deflate"
     "mod_evasive"
     "mod_extforward"
     "mod_flv_streaming"
@@ -47,6 +49,7 @@ let
     "mod_scgi"
     "mod_setenv"
     "mod_trigger_b4_dl"
+    "mod_uploadprogress"
     "mod_webdav"
   ];
 
@@ -86,14 +89,9 @@ let
       accesslog.use-syslog = "enable"
       server.errorlog-use-syslog = "enable"
 
-      mimetype.assign = (
-          ".html" => "text/html",
-          ".htm" => "text/html",
-          ".txt" => "text/plain",
-          ".jpg" => "image/jpeg",
-          ".png" => "image/png",
-          ".css" => "text/css"
-          )
+      ${lib.optionalString cfg.enableUpstreamMimeTypes ''
+      include "${pkgs.lighttpd}/share/lighttpd/doc/config/conf.d/mime.conf"
+      ''}
 
       static-file.exclude-extensions = ( ".fcgi", ".php", ".rb", "~", ".inc" )
       index-file.names = ( "index.html" )
@@ -165,6 +163,17 @@ in
         '';
       };
 
+      enableUpstreamMimeTypes = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to include the list of mime types bundled with lighttpd
+          (upstream). If you disable this, no mime types will be added by
+          NixOS and you will have to add your own mime types in
+          <option>services.lighttpd.extraConfig</option>.
+        '';
+      };
+
       mod_status = mkOption {
         default = false;
         type = types.bool;
@@ -177,7 +186,7 @@ in
       configText = mkOption {
         default = "";
         type = types.lines;
-	example = ''...verbatim config file contents...'';
+        example = ''...verbatim config file contents...'';
         description = ''
           Overridable config file contents to use for lighttpd. By default, use
           the contents automatically generated by NixOS.
diff --git a/nixos/modules/services/web-servers/minio.nix b/nixos/modules/services/web-servers/minio.nix
index 1893edf3a776..843f0d986877 100644
--- a/nixos/modules/services/web-servers/minio.nix
+++ b/nixos/modules/services/web-servers/minio.nix
@@ -29,6 +29,40 @@ in
       description = "The config directory, for the access keys and other settings.";
     };
 
+    accessKey = mkOption {
+      default = "";
+      type = types.str;
+      description = ''
+        Access key of 5 to 20 characters in length that clients use to access the server.
+        This overrides the access key that is generated by minio on first startup and stored inside the
+        <literal>configDir</literal> directory.
+      '';
+    };
+
+    secretKey = mkOption {
+      default = "";
+      type = types.str;
+      description = ''
+        Specify the Secret key of 8 to 40 characters in length that clients use to access the server.
+        This overrides the secret key that is generated by minio on first startup and stored inside the
+        <literal>configDir</literal> directory.
+      '';
+    };
+
+    region = mkOption {
+      default = "us-east-1";
+      type = types.str;
+      description = ''
+        The physical location of the server. By default it is set to us-east-1, which is same as AWS S3's and Minio's default region.
+      '';
+    };
+
+    browser = mkOption {
+      default = true;
+      type = types.bool;
+      description = "Enable or disable access to web UI.";
+    };
+
     package = mkOption {
       default = pkgs.minio;
       defaultText = "pkgs.minio";
@@ -57,6 +91,14 @@ in
         Group = "minio";
         LimitNOFILE = 65536;
       };
+      environment = {
+        MINIO_REGION = "${cfg.region}";
+        MINIO_BROWSER = "${if cfg.browser then "on" else "off"}";
+      } // optionalAttrs (cfg.accessKey != "") {
+        MINIO_ACCESS_KEY = "${cfg.accessKey}";
+      } // optionalAttrs (cfg.secretKey != "") {
+        MINIO_SECRET_KEY = "${cfg.secretKey}";
+      };
     };
 
     users.extraUsers.minio = {
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index ae14aa28ae34..a9ee6255fd40 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -19,6 +19,24 @@ let
   ) cfg.virtualHosts;
   enableIPv6 = config.networking.enableIPv6;
 
+  recommendedProxyConfig = pkgs.writeText "nginx-recommended-proxy-headers.conf" ''
+    proxy_set_header        Host $host;
+    proxy_set_header        X-Real-IP $remote_addr;
+    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
+    proxy_set_header        X-Forwarded-Proto $scheme;
+    proxy_set_header        X-Forwarded-Host $host;
+    proxy_set_header        X-Forwarded-Server $host;
+    proxy_set_header        Accept-Encoding "";
+  '';
+
+  upstreamConfig = toString (flip mapAttrsToList cfg.upstreams (name: upstream: ''
+    upstream ${name} {
+      ${toString (flip mapAttrsToList upstream.servers (name: server: ''
+        server ${name} ${optionalString server.backup "backup"};
+      ''))}
+    }
+  ''));
+
   configFile = pkgs.writeText "nginx.conf" ''
     user ${cfg.user} ${cfg.group};
     error_log stderr;
@@ -36,6 +54,12 @@ let
     http {
       include ${cfg.package}/conf/mime.types;
       include ${cfg.package}/conf/fastcgi.conf;
+      include ${cfg.package}/conf/uwsgi_params;
+
+      ${optionalString (cfg.resolver.addresses != []) ''
+        resolver ${toString cfg.resolver.addresses} ${optionalString (cfg.resolver.valid != "") "valid=${cfg.resolver.valid}"};
+      ''}
+      ${upstreamConfig}
 
       ${optionalString (cfg.recommendedOptimisation) ''
         # optimisation
@@ -65,24 +89,23 @@ let
         gzip_proxied any;
         gzip_comp_level 9;
         gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
+        gzip_vary on;
       ''}
 
       ${optionalString (cfg.recommendedProxySettings) ''
-        proxy_set_header        Host $host;
-        proxy_set_header        X-Real-IP $remote_addr;
-        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
-        proxy_set_header        X-Forwarded-Proto $scheme;
-        proxy_set_header        X-Forwarded-Host $host;
-        proxy_set_header        X-Forwarded-Server $host;
-        proxy_set_header        Accept-Encoding "";
-
         proxy_redirect          off;
         proxy_connect_timeout   90;
         proxy_send_timeout      90;
         proxy_read_timeout      90;
         proxy_http_version      1.0;
+        include ${recommendedProxyConfig};
       ''}
 
+      # $connection_upgrade is used for websocket proxying
+      map $http_upgrade $connection_upgrade {
+          default upgrade;
+          '''      close;
+      }
       client_max_body_size ${cfg.clientMaxBodySize};
 
       server_tokens ${if cfg.serverTokens then "on" else "off"};
@@ -115,6 +138,7 @@ let
     http {
       include ${cfg.package}/conf/mime.types;
       include ${cfg.package}/conf/fastcgi.conf;
+      include ${cfg.package}/conf/uwsgi_params;
       ${cfg.httpConfig}
     }''}
 
@@ -122,51 +146,69 @@ let
   '';
 
   vhosts = concatStringsSep "\n" (mapAttrsToList (vhostName: vhost:
-      let
-        serverName = vhost.serverName;
-        ssl = vhost.enableSSL || vhost.forceSSL;
-        port = if vhost.port != null then vhost.port else (if ssl then 443 else 80);
-        listenString = toString port + optionalString ssl " ssl http2"
-          + optionalString vhost.default " default_server";
-        acmeLocation = optionalString vhost.enableACME (''
+    let
+        onlySSL = vhost.onlySSL || vhost.enableSSL;
+        hasSSL = onlySSL || vhost.addSSL || vhost.forceSSL;
+
+        defaultListen =
+          if vhost.listen != [] then vhost.listen
+          else ((optionals hasSSL (
+            singleton                    { addr = "0.0.0.0"; port = 443; ssl = true; }
+            ++ optional enableIPv6 { addr = "[::]";    port = 443; ssl = true; }
+          )) ++ optionals (!onlySSL) (
+            singleton                    { addr = "0.0.0.0"; port = 80;  ssl = false; }
+            ++ optional enableIPv6 { addr = "[::]";    port = 80;  ssl = false; }
+          ));
+
+        hostListen =
+          if vhost.forceSSL
+            then filter (x: x.ssl) defaultListen
+            else defaultListen;
+
+        listenString = { addr, port, ssl, ... }:
+          "listen ${addr}:${toString port} "
+          + optionalString ssl "ssl http2 "
+          + optionalString vhost.default "default_server "
+          + ";";
+
+        redirectListen = filter (x: !x.ssl) defaultListen;
+
+        acmeLocation = ''
           location /.well-known/acme-challenge {
             ${optionalString (vhost.acmeFallbackHost != null) "try_files $uri @acme-fallback;"}
             root ${vhost.acmeRoot};
             auth_basic off;
           }
-        '' + (optionalString (vhost.acmeFallbackHost != null) ''
-          location @acme-fallback {
-            auth_basic off;
-            proxy_pass http://${vhost.acmeFallbackHost};
-          }
-        ''));
+          ${optionalString (vhost.acmeFallbackHost != null) ''
+            location @acme-fallback {
+              auth_basic off;
+              proxy_pass http://${vhost.acmeFallbackHost};
+            }
+          ''}
+        '';
+
       in ''
         ${optionalString vhost.forceSSL ''
           server {
-            listen 80 ${optionalString vhost.default "default_server"};
-            ${optionalString enableIPv6
-              ''listen [::]:80 ${optionalString vhost.default "default_server"};''
-            }
+            ${concatMapStringsSep "\n" listenString redirectListen}
 
-            server_name ${serverName} ${concatStringsSep " " vhost.serverAliases};
-            ${acmeLocation}
+            server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases};
+            ${optionalString vhost.enableACME acmeLocation}
             location / {
-              return 301 https://$host${optionalString (port != 443) ":${toString port}"}$request_uri;
+              return 301 https://$host$request_uri;
             }
           }
         ''}
 
         server {
-          listen ${listenString};
-          ${optionalString enableIPv6 "listen [::]:${listenString};"}
-
-          server_name ${serverName} ${concatStringsSep " " vhost.serverAliases};
-          ${acmeLocation}
+          ${concatMapStringsSep "\n" listenString hostListen}
+          server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases};
+          ${optionalString vhost.enableACME acmeLocation}
           ${optionalString (vhost.root != null) "root ${vhost.root};"}
           ${optionalString (vhost.globalRedirect != null) ''
-            return 301 http${optionalString ssl "s"}://${vhost.globalRedirect}$request_uri;
+            return 301 http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri;
           ''}
-          ${optionalString ssl ''
+          ${optionalString hasSSL ''
             ssl_certificate ${vhost.sslCertificate};
             ssl_certificate_key ${vhost.sslCertificateKey};
           ''}
@@ -181,12 +223,24 @@ let
   ) virtualHosts);
   mkLocations = locations: concatStringsSep "\n" (mapAttrsToList (location: config: ''
     location ${location} {
-      ${optionalString (config.proxyPass != null) "proxy_pass ${config.proxyPass};"}
+      ${optionalString (config.proxyPass != null && !cfg.proxyResolveWhileRunning)
+        "proxy_pass ${config.proxyPass};"
+      }
+      ${optionalString (config.proxyPass != null && cfg.proxyResolveWhileRunning) ''
+        set $nix_proxy_target "${config.proxyPass}";
+        proxy_pass $nix_proxy_target;
+      ''}
+      ${optionalString config.proxyWebsockets ''
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection $connection_upgrade;
+      ''}
       ${optionalString (config.index != null) "index ${config.index};"}
       ${optionalString (config.tryFiles != null) "try_files ${config.tryFiles};"}
       ${optionalString (config.root != null) "root ${config.root};"}
       ${optionalString (config.alias != null) "alias ${config.alias};"}
       ${config.extraConfig}
+      ${optionalString (config.proxyPass != null && cfg.recommendedProxySettings) "include ${recommendedProxyConfig};"}
     }
   '') locations);
   mkBasicAuth = vhostName: authDef: let
@@ -378,9 +432,74 @@ in
         description = "Path to DH parameters file.";
       };
 
+      proxyResolveWhileRunning = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Resolves domains of proxyPass targets at runtime
+          and not only at start, you have to set
+          services.nginx.resolver, too.
+        '';
+      };
+
+      resolver = mkOption {
+        type = types.submodule {
+          options = {
+            addresses = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              example = literalExample ''[ "[::1]" "127.0.0.1:5353" ]'';
+              description = "List of resolvers to use";
+            };
+            valid = mkOption {
+              type = types.str;
+              default = "";
+              example = "30s";
+              description = ''
+                By default, nginx caches answers using the TTL value of a response.
+                An optional valid parameter allows overriding it
+              '';
+            };
+          };
+        };
+        description = ''
+          Configures name servers used to resolve names of upstream servers into addresses
+        '';
+        default = {};
+      };
+
+      upstreams = mkOption {
+        type = types.attrsOf (types.submodule {
+          options = {
+            servers = mkOption {
+              type = types.attrsOf (types.submodule {
+                options = {
+                  backup = mkOption {
+                    type = types.bool;
+                    default = false;
+                    description = ''
+                      Marks the server as a backup server. It will be passed
+                      requests when the primary servers are unavailable.
+                    '';
+                  };
+                };
+              });
+              description = ''
+                Defines the address and other parameters of the upstream servers.
+              '';
+              default = {};
+            };
+          };
+        });
+        description = ''
+          Defines a group of servers to use as proxy target.
+        '';
+        default = {};
+      };
+
       virtualHosts = mkOption {
         type = types.attrsOf (types.submodule (import ./vhost-options.nix {
-          inherit lib;
+          inherit config lib;
         }));
         default = {
           localhost = {};
@@ -388,6 +507,7 @@ in
         example = literalExample ''
           {
             "hydra.example.com" = {
+              addSSL = true;
               forceSSL = true;
               enableACME = true;
               locations."/" = {
@@ -404,11 +524,37 @@ in
   config = mkIf cfg.enable {
     # TODO: test user supplied config file pases syntax test
 
-    assertions = let hostOrAliasIsNull = l: l.root == null || l.alias == null; in [
+    warnings =
+    let
+      deprecatedSSL = name: config: optional config.enableSSL
+      ''
+        config.services.nginx.virtualHosts.<name>.enableSSL is deprecated,
+        use config.services.nginx.virtualHosts.<name>.onlySSL instead.
+      '';
+
+    in flatten (mapAttrsToList deprecatedSSL virtualHosts);
+
+    assertions =
+    let
+      hostOrAliasIsNull = l: l.root == null || l.alias == null;
+    in [
       {
         assertion = all (host: all hostOrAliasIsNull (attrValues host.locations)) (attrValues virtualHosts);
         message = "Only one of nginx root or alias can be specified on a location.";
       }
+
+      {
+        assertion = all (conf: with conf;
+          !(addSSL && (onlySSL || enableSSL)) &&
+          !(forceSSL && (onlySSL || enableSSL)) &&
+          !(addSSL && forceSSL)
+        ) (attrValues virtualHosts);
+        message = ''
+          Options services.nginx.service.virtualHosts.<name>.addSSL,
+          services.nginx.virtualHosts.<name>.onlySSL and services.nginx.virtualHosts.<name>.forceSSL
+          are mutually exclusive.
+        '';
+      }
     ];
 
     systemd.services.nginx = {
diff --git a/nixos/modules/services/web-servers/nginx/location-options.nix b/nixos/modules/services/web-servers/nginx/location-options.nix
index 83ce0f717341..4c772734a749 100644
--- a/nixos/modules/services/web-servers/nginx/location-options.nix
+++ b/nixos/modules/services/web-servers/nginx/location-options.nix
@@ -14,7 +14,17 @@ with lib;
       default = null;
       example = "http://www.example.org/";
       description = ''
-        Adds proxy_pass directive.
+        Adds proxy_pass directive and sets recommended proxy headers if
+        recommendedProxySettings is enabled.
+      '';
+    };
+
+    proxyWebsockets = mkOption {
+      type = types.bool;
+      default = false;
+      example = true;
+      description = ''
+        Whether to supporty proxying websocket connections with HTTP/1.1.
       '';
     };
 
diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix
index c0ea645b3dfe..8a04e07eeeac 100644
--- a/nixos/modules/services/web-servers/nginx/vhost-options.nix
+++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix
@@ -3,7 +3,7 @@
 # has additional options that affect the web server as a whole, like
 # the user/group to run under.)
 
-{ lib }:
+{ config, lib }:
 
 with lib;
 {
@@ -26,12 +26,22 @@ with lib;
       '';
     };
 
-    port = mkOption {
-      type = types.nullOr types.int;
-      default = null;
+    listen = mkOption {
+      type = with types; listOf (submodule { options = {
+        addr = mkOption { type = str;  description = "IP address.";  };
+        port = mkOption { type = int;  description = "Port number."; default = 80; };
+        ssl  = mkOption { type = bool; description = "Enable SSL.";  default = false; };
+      }; });
+      default = [];
+      example = [
+        { addr = "195.154.1.1"; port = 443; ssl = true;}
+        { addr = "192.154.1.1"; port = 80; }
+      ];
       description = ''
-        Port for the server. Defaults to 80 for http
-        and 443 for https (i.e. when enableSSL is set).
+        Listen addresses and ports for this virtual host.
+        IPv6 addresses must be enclosed in square brackets.
+        Note: this option overrides <literal>addSSL</literal>
+        and <literal>onlySSL</literal>.
       '';
     };
 
@@ -56,16 +66,40 @@ with lib;
       '';
     };
 
+    addSSL = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable HTTPS in addition to plain HTTP. This will set defaults for
+        <literal>listen</literal> to listen on all interfaces on the respective default
+        ports (80, 443).
+      '';
+    };
+
+    onlySSL = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable HTTPS and reject plain HTTP connections. This will set
+        defaults for <literal>listen</literal> to listen on all interfaces on port 443.
+      '';
+    };
+
     enableSSL = mkOption {
       type = types.bool;
+      visible = false;
       default = false;
-      description = "Whether to enable SSL (https) support.";
     };
 
     forceSSL = mkOption {
       type = types.bool;
       default = false;
-      description = "Whether to always redirect to https.";
+      description = ''
+        Whether to add a separate nginx server block that permanently redirects (301)
+        all plain HTTP traffic to HTTPS. This will set defaults for
+        <literal>listen</literal> to listen on all interfaces on the respective default
+        ports (80, 443), where the non-SSL listens are used for the redirect vhosts.
+      '';
     };
 
     sslCertificate = mkOption {
diff --git a/nixos/modules/services/web-servers/phpfpm/default.nix b/nixos/modules/services/web-servers/phpfpm/default.nix
index 26f546022035..e1f4ff5db7f2 100644
--- a/nixos/modules/services/web-servers/phpfpm/default.nix
+++ b/nixos/modules/services/web-servers/phpfpm/default.nix
@@ -150,8 +150,8 @@ in {
           PrivateDevices = true;
           ProtectSystem = "full";
           ProtectHome = true;
-          NoNewPrivileges = true;
-          RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
+          # XXX: We need AF_NETLINK to make the sendmail SUID binary from postfix work
+          RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
           Type = "notify";
           ExecStart = "${cfg.phpPackage}/bin/php-fpm -y ${cfgFile} -c ${phpIni}";
           ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID";
diff --git a/nixos/modules/services/web-servers/varnish/default.nix b/nixos/modules/services/web-servers/varnish/default.nix
index 5433db3b91c8..c3bc065d4651 100644
--- a/nixos/modules/services/web-servers/varnish/default.nix
+++ b/nixos/modules/services/web-servers/varnish/default.nix
@@ -7,14 +7,10 @@ with lib;
 {
   options = {
     services.varnish = {
-      enable = mkOption {
-        default = false;
-        description = "
-          Enable the Varnish Server.
-        ";
-      };
+      enable = mkEnableOption "Varnish Server";
 
       http_address = mkOption {
+        type = types.str;
         default = "*:6081";
         description = "
           HTTP listen address and port.
@@ -22,17 +18,37 @@ with lib;
       };
 
       config = mkOption {
+        type = types.lines;
         description = "
           Verbatim default.vcl configuration.
         ";
       };
 
       stateDir = mkOption {
+        type = types.path;
         default = "/var/spool/varnish/${config.networking.hostName}";
         description = "
           Directory holding all state for Varnish to run.
         ";
       };
+
+      extraModules = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExample "[ pkgs.varnish-geoip ]";
+        description = "
+          Varnish modules (except 'std').
+        ";
+      };
+
+      extraCommandLine = mkOption {
+        type = types.str;
+        default = "";
+        example = "-s malloc,256M";
+        description = "
+          Command line switches for varnishd (run 'varnishd -?' to get list of options)
+        ";
+      };
     };
 
   };
@@ -42,6 +58,7 @@ with lib;
     systemd.services.varnish = {
       description = "Varnish";
       wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
       preStart = ''
         mkdir -p ${cfg.stateDir}
         chown -R varnish:varnish ${cfg.stateDir}
@@ -49,8 +66,19 @@ with lib;
       postStop = ''
         rm -rf ${cfg.stateDir}
       '';
-      serviceConfig.ExecStart = "${pkgs.varnish}/sbin/varnishd -a ${cfg.http_address} -f ${pkgs.writeText "default.vcl" cfg.config} -n ${cfg.stateDir} -u varnish";
-      serviceConfig.Type = "forking";
+      serviceConfig = {
+        Type = "simple";
+        PermissionsStartOnly = true;
+        ExecStart = "${pkgs.varnish}/sbin/varnishd -a ${cfg.http_address} -f ${pkgs.writeText "default.vcl" cfg.config} -n ${cfg.stateDir} -F ${cfg.extraCommandLine}"
+          + optionalString (cfg.extraModules != []) " -p vmod_path='${makeSearchPathOutput "lib" "lib/varnish/vmods" ([pkgs.varnish] ++ cfg.extraModules)}' -r vmod_path";
+        Restart = "always";
+        RestartSec = "5s";
+        User = "varnish";
+        Group = "varnish";
+        AmbientCapabilities = "cap_net_bind_service";
+        NoNewPrivileges = true;
+        LimitNOFILE = 131072;
+      };
     };
 
     environment.systemPackages = [ pkgs.varnish ];
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index c207aab5de0a..13f339e3fbf3 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -19,7 +19,8 @@ in
   # E.g., if Plasma 5 is enabled, it supersedes xterm.
   imports = [
     ./none.nix ./xterm.nix ./xfce.nix ./plasma5.nix ./lumina.nix
-    ./lxqt.nix ./enlightenment.nix ./gnome3.nix ./kodi.nix
+    ./lxqt.nix ./enlightenment.nix ./gnome3.nix ./kodi.nix ./maxx.nix
+    ./mate.nix
   ];
 
   options = {
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index c043884f8e0b..0abdc27bbfc6 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -176,7 +176,7 @@ in {
 
     services.xserver.updateDbusEnvironment = true;
 
-    environment.variables.GIO_EXTRA_MODULES = [ "${gnome3.dconf}/lib/gio/modules"
+    environment.variables.GIO_EXTRA_MODULES = [ "${lib.getLib gnome3.dconf}/lib/gio/modules"
                                                 "${gnome3.glib_networking.out}/lib/gio/modules"
                                                 "${gnome3.gvfs}/lib/gio/modules" ];
     environment.systemPackages = gnome3.corePackages ++ cfg.sessionPath
@@ -186,7 +186,8 @@ in {
     networking.networkmanager.basePackages =
       { inherit (pkgs) networkmanager modemmanager wpa_supplicant;
         inherit (gnome3) networkmanager_openvpn networkmanager_vpnc
-                         networkmanager_openconnect networkmanager_fortisslvpn networkmanager_pptp
+                         networkmanager_openconnect networkmanager_fortisslvpn
+                         networkmanager_pptp networkmanager_iodine
                          networkmanager_l2tp; };
 
     # Needed for themes and backgrounds
diff --git a/nixos/modules/services/x11/desktop-managers/mate.nix b/nixos/modules/services/x11/desktop-managers/mate.nix
new file mode 100644
index 000000000000..7a95ac6549d8
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/mate.nix
@@ -0,0 +1,79 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  # Remove packages of ys from xs, based on their names
+  removePackagesByName = xs: ys:
+    let
+      pkgName = drv: (builtins.parseDrvName drv.name).name;
+      ysNames = map pkgName ys;
+    in
+      filter (x: !(builtins.elem (pkgName x) ysNames)) xs;
+
+  xcfg = config.services.xserver;
+  cfg = xcfg.desktopManager.mate;
+
+in
+
+{
+  options = {
+
+    services.xserver.desktopManager.mate.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable the MATE desktop environment";
+    };
+
+    environment.mate.excludePackages = mkOption {
+      default = [];
+      example = literalExample "[ pkgs.mate.mate-terminal pkgs.mate.pluma ]";
+      type = types.listOf types.package;
+      description = "Which MATE packages to exclude from the default environment";
+    };
+
+  };
+
+  config = mkIf (xcfg.enable && cfg.enable) {
+
+    services.xserver.desktopManager.session = singleton {
+      name = "mate";
+      bgSupport = true;
+      start = ''
+        # Set GTK_DATA_PREFIX so that GTK+ can find the themes
+        export GTK_DATA_PREFIX=${config.system.path}
+
+        # Find theme engines
+        export GTK_PATH=${config.system.path}/lib/gtk-3.0:${config.system.path}/lib/gtk-2.0
+
+        export XDG_MENU_PREFIX=mate
+
+        # Find the mouse
+        export XCURSOR_PATH=~/.icons:${config.system.path}/share/icons
+
+        # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
+        ${pkgs.xdg-user-dirs}/bin/xdg-user-dirs-update
+
+        ${pkgs.mate.mate-session-manager}/bin/mate-session &
+        waitPID=$!
+      '';
+    };
+
+    environment.systemPackages =
+      pkgs.mate.basePackages ++
+      (removePackagesByName
+        pkgs.mate.extraPackages
+        config.environment.mate.excludePackages);
+
+    services.dbus.packages = [
+      pkgs.gnome3.dconf
+      pkgs.at_spi2_core
+    ];
+
+    services.gnome3.gnome-keyring.enable = true;
+
+    environment.pathsToLink = [ "/share" ];
+  };
+
+}
diff --git a/nixos/modules/services/x11/desktop-managers/maxx.nix b/nixos/modules/services/x11/desktop-managers/maxx.nix
new file mode 100644
index 000000000000..d7bd2fc5eb0c
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/maxx.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  xcfg = config.services.xserver;
+  cfg = xcfg.desktopManager.maxx;
+in {
+  options.services.xserver.desktopManager.maxx = {
+    enable = mkEnableOption "MaXX desktop environment";
+  };
+
+  config = mkIf (xcfg.enable && cfg.enable) {
+    environment.systemPackages = [ pkgs.maxx ];
+
+    services.xserver.desktopManager.session = [
+    { name = "MaXX";
+      start = ''
+        exec ${pkgs.maxx}/opt/MaXX/etc/skel/Xsession.dt
+      '';
+    }];
+  };
+
+  meta.maintainers = [ maintainers.gnidorah ];
+}
diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix
index 6c63fede857f..bd16f2210592 100644
--- a/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixos/modules/services/x11/display-managers/gdm.nix
@@ -103,14 +103,29 @@ in
             (filter (arg: arg != "-terminate") cfg.xserverArgs);
           GDM_SESSIONS_DIR = "${cfg.session.desktops}";
           # Find the mouse
-          XCURSOR_PATH = "~/.icons:${config.system.path}/share/icons";
+          XCURSOR_PATH = "~/.icons:${gnome3.adwaita-icon-theme}/share/icons";
         };
         execCmd = "exec ${gdm}/bin/gdm";
       };
 
     # Because sd_login_monitor_new requires /run/systemd/machines
     systemd.services.display-manager.wants = [ "systemd-machined.service" ];
-    systemd.services.display-manager.after = [ "systemd-machined.service" ];
+    systemd.services.display-manager.after = [
+      "rc-local.service"
+      "systemd-machined.service"
+      "systemd-user-sessions.service"
+      "getty@tty1.service"
+    ];
+
+    systemd.services.display-manager.conflicts = [ "getty@tty1.service" ];
+    systemd.services.display-manager.serviceConfig = {
+      # Restart = "always"; - already defined in xserver.nix
+      KillMode = "mixed";
+      IgnoreSIGPIPE = "no";
+      BusName = "org.gnome.DisplayManager";
+      StandardOutput = "syslog";
+      StandardError = "inherit";
+    };
 
     systemd.services.display-manager.path = [ gnome3.gnome_session ];
 
diff --git a/nixos/modules/services/x11/hardware/synaptics.nix b/nixos/modules/services/x11/hardware/synaptics.nix
index 54454c736c1d..f032c5938852 100644
--- a/nixos/modules/services/x11/hardware/synaptics.nix
+++ b/nixos/modules/services/x11/hardware/synaptics.nix
@@ -29,7 +29,7 @@ in {
       enable = mkOption {
         type = types.bool;
         default = false;
-        description = "Whether to enable touchpad support.";
+        description = "Whether to enable touchpad support. Deprecated: Consider services.xserver.libinput.enable.";
       };
 
       dev = mkOption {
diff --git a/nixos/modules/services/x11/window-managers/compiz.nix b/nixos/modules/services/x11/window-managers/compiz.nix
deleted file mode 100644
index 539a83f99068..000000000000
--- a/nixos/modules/services/x11/window-managers/compiz.nix
+++ /dev/null
@@ -1,60 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  cfg = config.services.xserver.windowManager.compiz;
-  xorg = config.services.xserver.package;
-
-in
-
-{
-
-  options = {
-
-    services.xserver.windowManager.compiz = {
-
-      enable = mkEnableOption "compiz";
-
-      renderingFlag = mkOption {
-        default = "";
-        example = "--indirect-rendering";
-        description = "Pass the <option>--indirect-rendering</option> flag to Compiz.";
-      };
-
-    };
-
-  };
-
-
-  config = mkIf cfg.enable {
-
-    services.xserver.windowManager.session = singleton
-      { name = "compiz";
-        start =
-          ''
-            # Start Compiz using the flat-file configuration backend
-            # (ccp).
-            export COMPIZ_PLUGINDIR=${config.system.path}/lib/compiz
-            export COMPIZ_METADATADIR=${config.system.path}/share/compiz
-            ${pkgs.compiz}/bin/compiz ccp ${cfg.renderingFlag} &
-
-            # Start GTK-style window decorator.
-            ${pkgs.compiz}/bin/gtk-window-decorator &
-          '';
-      };
-
-    environment.systemPackages =
-      [ pkgs.compiz
-        pkgs.compiz_ccsm
-        pkgs.compiz_plugins_main
-        pkgs.compiz_plugins_extra
-        pkgs.libcompizconfig # for the "ccp" plugin
-      ];
-
-    environment.pathsToLink = [ "/lib/compiz" "/share/compiz" ];
-
-  };
-
-}
diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix
index 32ef34bdad2b..d12003768a67 100644
--- a/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixos/modules/services/x11/window-managers/default.nix
@@ -11,7 +11,6 @@ in
     ./2bwm.nix
     ./afterstep.nix
     ./bspwm.nix
-    ./compiz.nix
     ./dwm.nix
     ./exwm.nix
     ./fluxbox.nix
diff --git a/nixos/modules/services/x11/window-managers/xmonad.nix b/nixos/modules/services/x11/window-managers/xmonad.nix
index e25a8ae22823..43de746ab1f1 100644
--- a/nixos/modules/services/x11/window-managers/xmonad.nix
+++ b/nixos/modules/services/x11/window-managers/xmonad.nix
@@ -29,6 +29,7 @@ in
 
       extraPackages = mkOption {
         default = self: [];
+        defaultText = "self: []";
         example = literalExample ''
           haskellPackages: [
             haskellPackages.xmonad-contrib
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index 638509e710be..3ce124d3da27 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -648,51 +648,11 @@ in
 
     services.xserver.xkbDir = mkDefault "${pkgs.xkeyboard_config}/etc/X11/xkb";
 
-    system.extraDependencies = singleton (pkgs.runCommand "xkb-layouts-exist" {
-      inherit (cfg) layout xkbDir;
+    system.extraDependencies = singleton (pkgs.runCommand "xkb-validated" {
+      inherit (cfg) xkbModel layout xkbVariant xkbOptions;
+      nativeBuildInputs = [ pkgs.xkbvalidate ];
     } ''
-      # We can use the default IFS here, because the layouts won't contain
-      # spaces or tabs and are ruled out by the sed expression below.
-      availableLayouts="$(
-        sed -n -e ':i /^! \(layout\|variant\) *$/ {
-          # Loop through all of the layouts/variants until we hit another ! at
-          # the start of the line or the line is empty ('t' branches only if
-          # the last substitution was successful, so if the line is empty the
-          # substition will fail).
-          :l; n; /^!/bi; s/^ *\([^ ]\+\).*/\1/p; tl
-        }' "$xkbDir/rules/base.lst" | sort -u
-      )"
-
-      layoutNotFound() {
-        echo >&2
-        echo "The following layouts and variants are available:" >&2
-        echo >&2
-
-        # While an output width of 80 is more desirable for small terminals, we
-        # really don't know the amount of columns of the terminal from within
-        # the builder. The content in $availableLayouts however is pretty
-        # large, so let's opt for a larger width here, because it will print a
-        # smaller amount of lines on modern KMS/framebuffer terminals and won't
-        # lose information even in smaller terminals (it only will look a bit
-        # ugly).
-        echo "$availableLayouts" | ${pkgs.utillinux}/bin/column -c 150 >&2
-
-        echo >&2
-        echo "However, the keyboard layout definition in" \
-             "\`services.xserver.layout' contains the layout \`$1', which" \
-             "isn't a valid layout or variant." >&2
-        echo >&2
-        exit 1
-      }
-
-      # Again, we don't need to take care of IFS, see the comment for
-      # $availableLayouts.
-      for l in ''${layout//,/ }; do
-        if ! echo "$availableLayouts" | grep -qxF "$l"; then
-          layoutNotFound "$l"
-        fi
-      done
-
+      validate "$xkbModel" "$layout" "$xkbVariant" "$xkbOptions"
       touch "$out"
     '');
 
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index 88e7847cf8c8..29cc60b00324 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -147,11 +147,16 @@ my $activePrev = getActiveUnits;
 while (my ($unit, $state) = each %{$activePrev}) {
     my $baseUnit = $unit;
 
-    # Recognise template instances.
-    $baseUnit = "$1\@.$2" if $unit =~ /^(.*)@[^\.]*\.(.*)$/;
     my $prevUnitFile = "/etc/systemd/system/$baseUnit";
     my $newUnitFile = "$out/etc/systemd/system/$baseUnit";
 
+    # Detect template instances.
+    if (!-e $prevUnitFile && !-e $newUnitFile && $unit =~ /^(.*)@[^\.]*\.(.*)$/) {
+      $baseUnit = "$1\@.$2";
+      $prevUnitFile = "/etc/systemd/system/$baseUnit";
+      $newUnitFile = "$out/etc/systemd/system/$baseUnit";
+    }
+
     my $baseName = $baseUnit;
     $baseName =~ s/\.[a-z]*$//;
 
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index e9897cc01b6a..67cb2264e3f3 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -141,6 +141,7 @@ in
     system.build = mkOption {
       internal = true;
       default = {};
+      type = types.attrs;
       description = ''
         Attribute set of derivations used to setup the system.
       '';
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index 1edb9e0d229b..82b5bcda9217 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -121,8 +121,8 @@ sub GetFs {
         my $device = $fields[$n + 1];
         my @superOptions = split /,/, $fields[$n + 2];
 
-        # Skip the read-only bind-mount on /nix/store.
-        next if $mountPoint eq "/nix/store" && (grep { $_ eq "rw" } @superOptions) && (grep { $_ eq "ro" } @mountOptions);
+        # Skip the bind-mount on /nix/store.
+        next if $mountPoint eq "/nix/store" && (grep { $_ eq "rw" } @superOptions);
         # Skip mount point generated by systemd-efi-boot-generator?
         next if $fsType eq "autofs";
 
diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
index 779005c0df52..3333569c36be 100644
--- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
+++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
@@ -12,6 +12,9 @@ import warnings
 import ctypes
 libc = ctypes.CDLL("libc.so.6")
 import re
+import datetime
+import glob
+import os.path
 
 def copy_if_not_exists(source, dest):
     if not os.path.exists(dest):
@@ -24,7 +27,7 @@ def system_dir(profile, generation):
         return "/nix/var/nix/profiles/system-%d-link" % (generation)
 
 BOOT_ENTRY = """title NixOS{profile}
-version Generation {generation}
+version Generation {generation} {description}
 linux {kernel}
 initrd {initrd}
 options {kernel_params}
@@ -54,6 +57,26 @@ def copy_from_profile(profile, generation, name, dry_run=False):
         copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
     return efi_file_path
 
+def describe_generation(generation_dir):
+    try:
+        with open("%s/nixos-version" % generation_dir) as f:
+            nixos_version = f.read()
+    except IOError:
+        nixos_version = "Unknown"
+
+    kernel_dir = os.path.dirname(os.path.realpath("%s/kernel" % generation_dir))
+    module_dir = glob.glob("%s/lib/modules/*" % kernel_dir)[0]
+    kernel_version = os.path.basename(module_dir)
+
+    build_time = int(os.path.getctime(generation_dir))
+    build_date = datetime.datetime.fromtimestamp(build_time).strftime('%F')
+
+    description = "NixOS {}, Linux Kernel {}, Built on {}".format(
+        nixos_version, kernel_version, build_date
+    )
+
+    return description
+
 def write_entry(profile, generation, machine_id):
     kernel = copy_from_profile(profile, generation, "kernel")
     initrd = copy_from_profile(profile, generation, "initrd")
@@ -69,6 +92,7 @@ def write_entry(profile, generation, machine_id):
     generation_dir = os.readlink(system_dir(profile, generation))
     tmp_path = "%s.tmp" % (entry_file)
     kernel_params = "systemConfig=%s init=%s/init " % (generation_dir, generation_dir)
+
     with open("%s/kernel-params" % (generation_dir)) as params_file:
         kernel_params = kernel_params + params_file.read()
     with open(tmp_path, 'w') as f:
@@ -76,7 +100,8 @@ def write_entry(profile, generation, machine_id):
                     generation=generation,
                     kernel=kernel,
                     initrd=initrd,
-                    kernel_params=kernel_params))
+                    kernel_params=kernel_params,
+                    description=describe_generation(generation_dir)))
         if machine_id is not None:
             f.write("machine-id %s\n" % machine_id)
     os.rename(tmp_path, entry_file)
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index f96dde153610..9d2cea3ad165 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -94,7 +94,7 @@ let
   checkNetwork = checkUnitConfig "Network" [
     (assertOnlyFields [
       "Description" "DHCP" "DHCPServer" "IPForward" "IPMasquerade" "IPv4LL" "IPv4LLRoute"
-      "LLMNR" "Domains" "Bridge" "Bond"
+      "LLMNR" "MulticastDNS" "Domains" "Bridge" "Bond"
     ])
     (assertValueOneOf "DHCP" ["both" "none" "v4" "v6"])
     (assertValueOneOf "DHCPServer" boolValues)
@@ -103,6 +103,7 @@ let
     (assertValueOneOf "IPv4LL" boolValues)
     (assertValueOneOf "IPv4LLRoute" boolValues)
     (assertValueOneOf "LLMNR" boolValues)
+    (assertValueOneOf "MulticastDNS" boolValues)
   ];
 
   checkAddress = checkUnitConfig "Address" [
@@ -141,6 +142,18 @@ let
     (assertValueOneOf "EmitTimezone" boolValues)
   ];
 
+  # .network files have a [Link] section with different options than in .netlink files
+  checkNetworkLink = checkUnitConfig "Link" [
+    (assertOnlyFields [
+      "MACAddress" "MTUBytes" "ARP" "Unmanaged"
+    ])
+    (assertMacAddress "MACAddress")
+    (assertByteFormat "MTUBytes")
+    (assertValueOneOf "ARP" boolValues)
+    (assertValueOneOf "Unmanaged" boolValues)
+  ];
+
+
   commonNetworkOptions = {
 
     enable = mkOption {
@@ -370,6 +383,18 @@ let
       '';
     };
 
+    linkConfig = mkOption {
+      default = {};
+      example = { Unmanaged = true; };
+      type = types.addCheck (types.attrsOf unitOption) checkNetworkLink;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Link]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.network</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
     name = mkOption {
       type = types.nullOr types.str;
       default = null;
@@ -580,6 +605,12 @@ let
     { inherit (def) enable;
       text = commonMatchText def +
         ''
+          ${optionalString (def.linkConfig != { }) ''
+            [Link]
+            ${attrsToSection def.linkConfig}
+
+          ''}
+
           [Network]
           ${attrsToSection def.networkConfig}
           ${concatStringsSep "\n" (map (s: "Address=${s}") def.address)}
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index 9a125dcb0aeb..b442386914ad 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -221,6 +221,9 @@ checkFS() {
     # Don't check resilient COWs as they validate the fs structures at mount time
     if [ "$fsType" = btrfs -o "$fsType" = zfs ]; then return 0; fi
 
+    # Skip fsck for bcachefs - not implemented yet.
+    if [ "$fsType" = bcachefs ]; then return 0; fi
+
     # Skip fsck for inherently readonly filesystems.
     if [ "$fsType" = squashfs ]; then return 0; fi
 
@@ -301,6 +304,7 @@ mountFS() {
         *x-nixos.autoresize*)
             if [ "$fsType" = ext2 -o "$fsType" = ext3 -o "$fsType" = ext4 ]; then
                 echo "resizing $device..."
+                e2fsck -fp "$device"
                 resize2fs "$device"
             fi
             ;;
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 02870878c0f3..d6e3e3a87d01 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -207,7 +207,7 @@ let
       preLVMCommands preDeviceCommands postDeviceCommands postMountCommands preFailCommands kernelModules;
 
     resumeDevices = map (sd: if sd ? device then sd.device else "/dev/disk/by-label/${sd.label}")
-                    (filter (sd: hasPrefix "/dev/" sd.device && !sd.randomEncryption
+                    (filter (sd: hasPrefix "/dev/" sd.device && !sd.randomEncryption.enable
                              # Don't include zram devices
                              && !(hasPrefix "/dev/zram" sd.device)
                             ) config.swapDevices);
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 00d6d370dd75..d12d0a06f444 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -593,7 +593,7 @@ in
     services.logind.extraConfig = mkOption {
       default = "";
       type = types.lines;
-      example = "HandleLidSwitch=ignore";
+      example = "IdleAction=lock";
       description = ''
         Extra config options for systemd-logind. See man logind.conf for
         available options.
@@ -659,16 +659,22 @@ in
         }));
     };
 
+    systemd.user.paths = mkOption {
+      default = {};
+      type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
+      description = "Definition of systemd per-user path units.";
+    };
+
     systemd.user.services = mkOption {
       default = {};
       type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ] );
       description = "Definition of systemd per-user service units.";
     };
 
-    systemd.user.timers = mkOption {
+    systemd.user.slices = mkOption {
       default = {};
-      type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ] );
-      description = "Definition of systemd per-user timer units.";
+      type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig ] );
+      description = "Definition of systemd per-user slice units.";
     };
 
     systemd.user.sockets = mkOption {
@@ -683,6 +689,12 @@ in
       description = "Definition of systemd per-user target units.";
     };
 
+    systemd.user.timers = mkOption {
+      default = {};
+      type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ] );
+      description = "Definition of systemd per-user timer units.";
+    };
+
     systemd.additionalUpstreamSystemUnits = mkOption {
       default = [ ];
       type = types.listOf types.str;
@@ -799,12 +811,12 @@ in
       };
 
     systemd.units =
-      mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets
+         mapAttrs' (n: v: nameValuePair "${n}.path"    (pathToUnit    n v)) cfg.paths
       // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
-      // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets
-      // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers
-      // mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths
-      // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.slices
+      // mapAttrs' (n: v: nameValuePair "${n}.slice"   (sliceToUnit   n v)) cfg.slices
+      // mapAttrs' (n: v: nameValuePair "${n}.socket"  (socketToUnit  n v)) cfg.sockets
+      // mapAttrs' (n: v: nameValuePair "${n}.target"  (targetToUnit  n v)) cfg.targets
+      // mapAttrs' (n: v: nameValuePair "${n}.timer"   (timerToUnit   n v)) cfg.timers
       // listToAttrs (map
                    (v: let n = escapeSystemdPath v.where;
                        in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts)
@@ -813,7 +825,9 @@ in
                        in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
 
     systemd.user.units =
-         mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services
+         mapAttrs' (n: v: nameValuePair "${n}.path"    (pathToUnit    n v)) cfg.user.paths
+      // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services
+      // mapAttrs' (n: v: nameValuePair "${n}.slice"   (sliceToUnit   n v)) cfg.user.slices
       // mapAttrs' (n: v: nameValuePair "${n}.socket"  (socketToUnit  n v)) cfg.user.sockets
       // mapAttrs' (n: v: nameValuePair "${n}.target"  (targetToUnit  n v)) cfg.user.targets
       // mapAttrs' (n: v: nameValuePair "${n}.timer"   (timerToUnit   n v)) cfg.user.timers;
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
index fd6e58cd5b43..7d43ba07ca57 100644
--- a/nixos/modules/system/etc/etc.nix
+++ b/nixos/modules/system/etc/etc.nix
@@ -20,8 +20,8 @@ let
     sources = map (x: x.source) etc';
     targets = map (x: x.target) etc';
     modes = map (x: x.mode) etc';
-    uids  = map (x: x.uid) etc';
-    gids  = map (x: x.gid) etc';
+    users  = map (x: x.user) etc';
+    groups  = map (x: x.group) etc';
   };
 
 in
@@ -108,6 +108,26 @@ in
               '';
             };
 
+            user = mkOption {
+              default = "+${toString config.uid}";
+              type = types.str;
+              description = ''
+                User name of created file.
+                Only takes affect when the file is copied (that is, the mode is not 'symlink').
+                Changing this option takes precedence over <literal>uid</literal>.
+              '';
+            };
+
+            group = mkOption {
+              default = "+${toString config.gid}";
+              type = types.str;
+              description = ''
+                Group name of created file.
+                Only takes affect when the file is copied (that is, the mode is not 'symlink').
+                Changing this option takes precedence over <literal>gid</literal>.
+              '';
+            };
+
           };
 
           config = {
@@ -130,7 +150,7 @@ in
 
     system.build.etc = etc;
 
-    system.activationScripts.etc = stringAfter [ "stdio" ]
+    system.activationScripts.etc = stringAfter [ "users" "groups" ]
       ''
         # Set up the statically computed bits of /etc.
         echo "setting up /etc..."
diff --git a/nixos/modules/system/etc/make-etc.sh b/nixos/modules/system/etc/make-etc.sh
index 60d4ba1301a3..1ca4c3046f0e 100644
--- a/nixos/modules/system/etc/make-etc.sh
+++ b/nixos/modules/system/etc/make-etc.sh
@@ -6,8 +6,8 @@ set -f
 sources_=($sources)
 targets_=($targets)
 modes_=($modes)
-uids_=($uids)
-gids_=($gids)
+users_=($users)
+groups_=($groups)
 set +f
 
 for ((i = 0; i < ${#targets_[@]}; i++)); do
@@ -36,9 +36,9 @@ for ((i = 0; i < ${#targets_[@]}; i++)); do
         fi
         
         if test "${modes_[$i]}" != symlink; then
-            echo "${modes_[$i]}" > $out/etc/$target.mode
-            echo "${uids_[$i]}" > $out/etc/$target.uid
-            echo "${gids_[$i]}" > $out/etc/$target.gid
+            echo "${modes_[$i]}"  > $out/etc/$target.mode
+            echo "${users_[$i]}"  > $out/etc/$target.uid
+            echo "${groups_[$i]}" > $out/etc/$target.gid
         fi
         
     fi
diff --git a/nixos/modules/system/etc/setup-etc.pl b/nixos/modules/system/etc/setup-etc.pl
index efda74161ff8..eed20065087f 100644
--- a/nixos/modules/system/etc/setup-etc.pl
+++ b/nixos/modules/system/etc/setup-etc.pl
@@ -108,6 +108,8 @@ sub link {
             my $uid = read_file("$_.uid"); chomp $uid;
             my $gid = read_file("$_.gid"); chomp $gid;
             copy "$static/$fn", "$target.tmp" or warn;
+            $uid = getpwnam $uid unless $uid =~ /^\+/;
+            $gid = getgrnam $gid unless $gid =~ /^\+/;
             chown int($uid), int($gid), "$target.tmp" or warn;
             chmod oct($mode), "$target.tmp" or warn;
             rename "$target.tmp", $target or warn;
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index 3951d617f6f1..1922d2924bc5 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -294,7 +294,7 @@ in
       "/run" = { fsType = "tmpfs"; options = [ "nosuid" "nodev" "strictatime" "mode=755" "size=${config.boot.runSize}" ]; };
       "/dev" = { fsType = "devtmpfs"; options = [ "nosuid" "strictatime" "mode=755" "size=${config.boot.devSize}" ]; };
       "/dev/shm" = { fsType = "tmpfs"; options = [ "nosuid" "nodev" "strictatime" "mode=1777" "size=${config.boot.devShmSize}" ]; };
-      "/dev/pts" = { fsType = "devpts"; options = [ "nosuid" "noexec" "mode=620" "gid=${toString config.ids.gids.tty}" ]; };
+      "/dev/pts" = { fsType = "devpts"; options = [ "nosuid" "noexec" "mode=620" "ptmxmode=0666" "gid=${toString config.ids.gids.tty}" ]; };
 
       # To hold secrets that shouldn't be written to disk (generally used for NixOps, harmless elsewhere)
       "/run/keys" = { fsType = "ramfs"; options = [ "nosuid" "nodev" "mode=750" "gid=${toString config.ids.gids.keys}" ]; };
diff --git a/nixos/modules/tasks/filesystems/bcachefs.nix b/nixos/modules/tasks/filesystems/bcachefs.nix
new file mode 100644
index 000000000000..227707173a3d
--- /dev/null
+++ b/nixos/modules/tasks/filesystems/bcachefs.nix
@@ -0,0 +1,26 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  inInitrd = any (fs: fs == "bcachefs") config.boot.initrd.supportedFilesystems;
+
+in
+
+{
+  config = mkIf (any (fs: fs == "bcachefs") config.boot.supportedFilesystems) {
+
+    system.fsPackages = [ pkgs.bcachefs-tools ];
+
+    # use kernel package with bcachefs support until it's in mainline
+    boot.kernelPackages = pkgs.linuxPackages_testing_bcachefs;
+    boot.initrd.availableKernelModules = mkIf inInitrd [ "bcachefs" ];
+
+    boot.initrd.extraUtilsCommands = mkIf inInitrd
+      ''
+        copy_bin_and_libs ${pkgs.bcachefs-tools}/bin/fsck.bcachefs
+      '';
+
+  };
+}
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 2de3a3d8a330..f300091b11ee 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -24,11 +24,7 @@ let
 
   kernel = config.boot.kernelPackages;
 
-  packages = if config.boot.zfs.enableUnstable then {
-    spl = kernel.splUnstable;
-    zfs = kernel.zfsUnstable;
-    zfsUser = pkgs.zfsUnstable;
-  } else {
+  packages = {
     spl = kernel.spl;
     zfs = kernel.zfs;
     zfsUser = pkgs.zfs;
@@ -62,19 +58,6 @@ in
 
   options = {
     boot.zfs = {
-      enableUnstable = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Use the unstable zfs package. This might be an option, if the latest
-          kernel is not yet supported by a published release of ZFS. Enabling
-          this option will install a development version of ZFS on Linux. The
-          version will have already passed an extensive test suite, but it is
-          more likely to hit an undiscovered bug compared to running a released
-          version of ZFS on Linux.
-        '';
-      };
-
       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 f6f104ce7a68..15b36cfcb113 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -71,7 +71,7 @@ let
              (hasAttr dev cfg.vswitches) ||
              (hasAttr dev cfg.wlanInterfaces)
           then [ "${dev}-netdev.service" ]
-          else optional (dev != null && !config.boot.isContainer) (subsystemDevice dev);
+          else optional (dev != null && dev != "lo" && !config.boot.isContainer) (subsystemDevice dev);
 
         networkLocalCommands = {
           after = [ "network-setup.service" ];
diff --git a/nixos/modules/testing/minimal-kernel.nix b/nixos/modules/testing/minimal-kernel.nix
index a463cb803ade..7c2b9c05cf9a 100644
--- a/nixos/modules/testing/minimal-kernel.nix
+++ b/nixos/modules/testing/minimal-kernel.nix
@@ -6,7 +6,7 @@ let
   );
 
   origKernel = pkgs.buildLinux {
-    inherit (pkgs.linux) src version;
+    inherit (pkgs.linux) src version stdenv;
     inherit configfile;
     allowImportFromDerivation = true;
     kernelPatches = [ pkgs.kernelPatches.cifs_timeout_2_6_38 ];
diff --git a/nixos/modules/virtualisation/azure-images.nix b/nixos/modules/virtualisation/azure-images.nix
new file mode 100644
index 000000000000..22c82fc14f65
--- /dev/null
+++ b/nixos/modules/virtualisation/azure-images.nix
@@ -0,0 +1,5 @@
+let self = {
+  "16.09" = "https://nixos.blob.core.windows.net/images/nixos-image-16.09.1694.019dcc3-x86_64-linux.vhd";
+
+  latest = self."16.09";
+}; in self
diff --git a/nixos/modules/virtualisation/cloud-image.nix b/nixos/modules/virtualisation/cloud-image.nix
new file mode 100644
index 000000000000..0f0141abfb16
--- /dev/null
+++ b/nixos/modules/virtualisation/cloud-image.nix
@@ -0,0 +1,44 @@
+# Usage:
+# $ NIX_PATH=`pwd`:nixos-config=`pwd`/nixpkgs/nixos/modules/virtualisation/cloud-image.nix nix-build '<nixpkgs/nixos>' -A config.system.build.cloudImage
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  system.build.cloudImage = import ../../lib/make-disk-image.nix {
+    inherit pkgs lib config;
+    partitioned = true;
+    diskSize = 1 * 1024;
+    configFile = pkgs.writeText "configuration.nix"
+      ''
+        { config, lib, pkgs, ... }:
+
+        with lib;
+
+        {
+          imports = [ <nixpkgs/nixos/modules/virtualisation/cloud-image.nix> ];
+        }
+      '';
+  };
+
+  imports = [ ../profiles/qemu-guest.nix ];
+
+  fileSystems."/".device = "/dev/disk/by-label/nixos";
+
+  boot = {
+    kernelParams = [ "console=ttyS0" ];
+    loader.grub.device = "/dev/vda";
+    loader.timeout = 0;
+  };
+
+  networking.hostName = mkDefault "";
+
+  services.openssh = {
+    enable = true;
+    permitRootLogin = "without-password";
+    passwordAuthentication = mkDefault false;
+  };
+
+  services.cloud-init.enable = true;
+}
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index 6adb2c1681a2..001c6473a98e 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -120,7 +120,6 @@ let
 
       # Run systemd-nspawn without startup notification (we'll
       # wait for the container systemd to signal readiness).
-      EXIT_ON_REBOOT=1 \
       exec ${config.systemd.package}/bin/systemd-nspawn \
         --keep-unit \
         -M "$INSTANCE" -D "$root" $extraFlags \
diff --git a/nixos/modules/virtualisation/docker.nix b/nixos/modules/virtualisation/docker.nix
index c26cae06cd1d..5a8a0e27436f 100644
--- a/nixos/modules/virtualisation/docker.nix
+++ b/nixos/modules/virtualisation/docker.nix
@@ -94,6 +94,38 @@ in
             <command>docker</command> daemon.
           '';
       };
+
+    autoPrune = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to periodically prune Docker resources. If enabled, a
+          systemd timer will run <literal>docker system prune -f</literal>
+          as specified by the <literal>dates</literal> option.
+        '';
+      };
+
+      flags = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "--all" ];
+        description = ''
+          Any additional flags passed to <command>docker system prune</command>.
+        '';
+      };
+
+      dates = mkOption {
+        default = "weekly";
+        type = types.str;
+        description = ''
+          Specification (in the format described by
+          <citerefentry><refentrytitle>systemd.time</refentrytitle>
+          <manvolnum>7</manvolnum></citerefentry>) of the time at
+          which the prune will occur.
+        '';
+      };
+    };
   };
 
   ###### implementation
@@ -137,6 +169,22 @@ in
           SocketGroup = "docker";
         };
       };
+
+
+      systemd.services.docker-prune = {
+        description = "Prune docker resources";
+
+        restartIfChanged = false;
+        unitConfig.X-StopOnRemoval = false;
+
+        serviceConfig.Type = "oneshot";
+
+        script = ''
+          ${pkgs.docker}/bin/docker system prune -f ${toString cfg.autoPrune.flags}
+        '';
+
+        startAt = optional cfg.autoPrune.enable cfg.autoPrune.dates;
+      };
     }
   ]);
 
diff --git a/nixos/modules/virtualisation/gce-images.nix b/nixos/modules/virtualisation/gce-images.nix
new file mode 100644
index 000000000000..8a9bda1b60c2
--- /dev/null
+++ b/nixos/modules/virtualisation/gce-images.nix
@@ -0,0 +1,8 @@
+let self = {
+  "14.12" = "gs://nixos-cloud-images/nixos-14.12.471.1f09b77-x86_64-linux.raw.tar.gz";
+  "15.09" = "gs://nixos-cloud-images/nixos-15.09.425.7870f20-x86_64-linux.raw.tar.gz";
+  "16.03" = "gs://nixos-cloud-images/nixos-image-16.03.847.8688c17-x86_64-linux.raw.tar.gz";
+  "17.03" = "gs://nixos-cloud-images/nixos-image-17.03.1082.4aab5c5798-x86_64-linux.raw.tar.gz";
+
+  latest = self."17.03";
+}; in self
diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix
index 3943a62f8a45..4a8dadaa281d 100644
--- a/nixos/modules/virtualisation/google-compute-image.nix
+++ b/nixos/modules/virtualisation/google-compute-image.nix
@@ -3,13 +3,11 @@
 with lib;
 let
   diskSize = 1024; # MB
+  gce = pkgs.google-compute-engine;
 in
 {
   imports = [ ../profiles/headless.nix ../profiles/qemu-guest.nix ./grow-partition.nix ];
 
-  # https://cloud.google.com/compute/docs/tutorials/building-images
-  networking.firewall.enable = mkDefault false;
-
   system.build.googleComputeImage = import ../../lib/make-disk-image.nix {
     name = "google-compute-image";
     postVM = ''
@@ -49,12 +47,18 @@ in
   services.openssh.permitRootLogin = "prohibit-password";
   services.openssh.passwordAuthentication = mkDefault false;
 
+  # Use GCE udev rules for dynamic disk volumes
+  services.udev.packages = [ gce ];
+
   # Force getting the hostname from Google Compute.
   networking.hostName = mkDefault "";
 
   # Always include cryptsetup so that NixOps can use it.
   environment.systemPackages = [ pkgs.cryptsetup ];
 
+  # Rely on GCP's firewall instead
+  networking.firewall.enable = mkDefault false;
+
   # Configure default metadata hostnames
   networking.extraHosts = ''
     169.254.169.254 metadata.google.internal metadata
@@ -64,6 +68,132 @@ in
 
   networking.usePredictableInterfaceNames = false;
 
+  # allow the google-accounts-daemon to manage users
+  users.mutableUsers = true;
+  # and allow users to sudo without password
+  security.sudo.enable = true;
+  security.sudo.extraConfig = ''
+  %google-sudoers ALL=(ALL:ALL) NOPASSWD:ALL
+  '';
+
+  # NOTE: google-accounts tries to write to /etc/sudoers.d but the folder doesn't exist
+  # FIXME: not such file or directory on dynamic SSH provisioning
+  systemd.services.google-accounts-daemon = {
+    description = "Google Compute Engine Accounts Daemon";
+    # This daemon creates dynamic users
+    enable = config.users.mutableUsers;
+    after = [
+      "network.target"
+      "google-instance-setup.service"
+      "google-network-setup.service"
+    ];
+    wantedBy = [ "multi-user.target" ];
+    requires = ["network.target"];
+    path = with pkgs; [ shadow ];
+    serviceConfig = {
+      Type = "simple";
+      ExecStart = "${gce}/bin/google_accounts_daemon --debug";
+    };
+  };
+
+  systemd.services.google-clock-skew-daemon = {
+    description = "Google Compute Engine Clock Skew Daemon";
+    after = [
+      "network.target"
+      "google-instance-setup.service"
+      "google-network-setup.service"
+    ];
+    requires = [ "network.target" ];
+    wantedBy = [ "multi-user.target" ];
+    serviceConfig = {
+      Type = "simple";
+      ExecStart = "${gce}/bin/google_clock_skew_daemon --debug";
+    };
+  };
+
+  systemd.services.google-instance-setup = {
+    description = "Google Compute Engine Instance Setup";
+    after = ["fs.target" "network-online.target" "network.target" "rsyslog.service"];
+    before = ["sshd.service"];
+    wants = ["local-fs.target" "network-online.target" "network.target"];
+    wantedBy = [ "sshd.service" "multi-user.target" ];
+    path = with pkgs; [ ethtool ];
+    serviceConfig = {
+      ExecStart = "${gce}/bin/google_instance_setup --debug";
+      Type = "oneshot";
+    };
+  };
+
+  systemd.services.google-ip-forwarding-daemon = {
+    description = "Google Compute Engine IP Forwarding Daemon";
+    after = ["network.target" "google-instance-setup.service" "google-network-setup.service"];
+    requires = ["network.target"];
+    wantedBy = [ "multi-user.target" ];
+    path = with pkgs; [ iproute ];
+    serviceConfig = {
+      Type = "simple";
+      ExecStart = "${gce}/bin/google_ip_forwarding_daemon --debug";
+    };
+  };
+
+  systemd.services.google-shutdown-scripts = {
+    description = "Google Compute Engine Shutdown Scripts";
+    after = [
+      "local-fs.target"
+      "network-online.target"
+      "network.target"
+      "rsyslog.service"
+      "google-instance-setup.service"
+      "google-network-setup.service"
+    ];
+    wants = [ "local-fs.target" "network-online.target" "network.target"];
+    wantedBy = [ "multi-user.target" ];
+    serviceConfig = {
+      ExecStart = "${pkgs.coreutils}/bin/true";
+      ExecStop = "${gce}/bin/google_metadata_script_runner --debug --script-type shutdown";
+      Type = "oneshot";
+      RemainAfterExit = true;
+      TimeoutStopSec = 0;
+    };
+  };
+
+  systemd.services.google-network-setup = {
+    description = "Google Compute Engine Network Setup";
+    after = [
+      "local-fs.target"
+      "network-online.target"
+      "network.target"
+      "rsyslog.service"
+    ];
+    wants = [ "local-fs.target" "network-online.target" "network.target"];
+    wantedBy = [ "multi-user.target" ];
+    serviceConfig = {
+      ExecStart = "${gce}/bin/google_network_setup --debug";
+      KillMode = "process";
+      Type = "oneshot";
+    };
+  };
+
+  systemd.services.google-startup-scripts = {
+    description = "Google Compute Engine Startup Scripts";
+    after = [
+      "local-fs.target"
+      "network-online.target"
+      "network.target"
+      "rsyslog.service"
+      "google-instance-setup.service"
+      "google-network-setup.service"
+    ];
+    wants = [ "local-fs.target" "network-online.target" "network.target"];
+    wantedBy = [ "multi-user.target" ];
+    serviceConfig = {
+      ExecStart = "${gce}/bin/google_metadata_script_runner --debug --script-type startup";
+      KillMode = "process";
+      Type = "oneshot";
+    };
+  };
+
+  # TODO: remove this
   systemd.services.fetch-ssh-keys =
     { description = "Fetch host keys and authorized_keys for root user";
 
@@ -113,9 +243,13 @@ in
       serviceConfig.StandardOutput = "journal+console";
     };
 
-  # Setings taken from https://cloud.google.com/compute/docs/tutorials/building-images#providedkernel
+  # Settings taken from https://github.com/GoogleCloudPlatform/compute-image-packages/blob/master/google_config/sysctl/11-gce-network-security.conf
   boot.kernel.sysctl = {
-    # enables syn flood protection
+    # Turn on SYN-flood protections.  Starting with 2.6.26, there is no loss
+    # of TCP functionality/features under normal conditions.  When flood
+    # protections kick in under high unanswered-SYN load, the system
+    # should remain more stable, with a trade off of some loss of TCP
+    # functionality/features (e.g. TCP Window scaling).
     "net.ipv4.tcp_syncookies" = mkDefault "1";
 
     # ignores source-routed packets
@@ -169,6 +303,11 @@ in
     # randomizes addresses of mmap base, heap, stack and VDSO page
     "kernel.randomize_va_space" = mkDefault "2";
 
+    # Reboot the machine soon after a kernel panic.
+    "kernel.panic" = mkDefault "10";
+
+    ## Not part of the original config
+
     # provides protection from ToCToU races
     "fs.protected_hardlinks" = mkDefault "1";
 
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index 10a38b99f871..8aa7ad8e3911 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -15,7 +15,7 @@ let
   '';
   qemuConfigFile = pkgs.writeText "qemu.conf" ''
     ${optionalString cfg.qemuOvmf ''
-      nvram = ["${pkgs.OVMF.fd}/FV/OVMF_CODE.fd:${pkgs.OVMF.fd}/FV/OVMF_VARS.fd"]
+      nvram = ["/run/libvirt/nix-ovmf/OVMF_CODE.fd:/run/libvirt/nix-ovmf/OVMF_VARS.fd"]
     ''}
     ${cfg.qemuVerbatimConfig}
   '';
@@ -102,9 +102,7 @@ in {
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = with pkgs;
-      [ libvirt netcat-openbsd ]
-       ++ optional cfg.enableKVM qemu_kvm;
+    environment.systemPackages = with pkgs; [ libvirt netcat-openbsd qemu_kvm ];
 
     boot.kernelModules = [ "tun" ];
 
@@ -129,7 +127,6 @@ in {
           dnsmasq
           ebtables
         ]
-        ++ optional cfg.enableKVM qemu_kvm
         ++ optional vswitch.enable vswitch.package;
 
       preStart = ''
@@ -155,31 +152,31 @@ in {
         # Copy generated qemu config to libvirt directory
         cp -f ${qemuConfigFile} /var/lib/libvirt/qemu.conf
 
-        # libvirtd puts the full path of the emulator binary in the machine
-        # config file. But this path can unfortunately be garbage collected
-        # while still being used by the virtual machine. So update the
-        # emulator path on each startup to something valid (re-scan $PATH).
-        for file in /var/lib/libvirt/qemu/*.xml /var/lib/libvirt/lxc/*.xml; do
-            test -f "$file" || continue
-            # get (old) emulator path from config file
-            emulator=$(grep "^[[:space:]]*<emulator>" "$file" | sed 's,^[[:space:]]*<emulator>\(.*\)</emulator>.*,\1,')
-            # get a (definitely) working emulator path by re-scanning $PATH
-            new_emulator=$(PATH=${pkgs.libvirt}/libexec:$PATH command -v $(basename "$emulator"))
-            # write back
-            sed -i "s,^[[:space:]]*<emulator>.*,    <emulator>$new_emulator</emulator> <!-- WARNING: emulator dirname is auto-updated by the nixos libvirtd module -->," "$file"
-        done
-      ''; # */
+        # stable (not GC'able as in /nix/store) paths for using in <emulator> section of xml configs
+        mkdir -p /run/libvirt/nix-emulators
+        ln -s --force ${pkgs.libvirt}/libexec/libvirt_lxc /run/libvirt/nix-emulators/
+        ${optionalString pkgs.stdenv.isAarch64 "ln -s --force ${pkgs.qemu}/bin/qemu-system-aarch64 /run/libvirt/nix-emulators/"}
+        ${optionalString cfg.enableKVM         "ln -s --force ${pkgs.qemu_kvm}/bin/qemu-kvm        /run/libvirt/nix-emulators/"}
+
+        ${optionalString cfg.qemuOvmf ''
+            mkdir -p /run/libvirt/nix-ovmf
+            ln -s --force ${pkgs.OVMF.fd}/FV/OVMF_CODE.fd /run/libvirt/nix-ovmf/
+            ln -s --force ${pkgs.OVMF.fd}/FV/OVMF_VARS.fd /run/libvirt/nix-ovmf/
+        ''}
+      '';
 
       serviceConfig = {
         Type = "notify";
         KillMode = "process"; # when stopping, leave the VMs alone
-        Restart = "on-failure";
+        Restart = "no";
       };
+      restartIfChanged = false;
     };
 
     systemd.services.libvirt-guests = {
       wantedBy = [ "multi-user.target" ];
       path = with pkgs; [ coreutils libvirt gawk ];
+      restartIfChanged = false;
     };
 
     systemd.sockets.virtlogd = {
@@ -191,6 +188,7 @@ in {
     systemd.services.virtlogd = {
       description = "Virtual machine log manager";
       serviceConfig.ExecStart = "@${pkgs.libvirt}/sbin/virtlogd virtlogd";
+      restartIfChanged = false;
     };
 
     systemd.sockets.virtlockd = {
@@ -202,6 +200,7 @@ in {
     systemd.services.virtlockd = {
       description = "Virtual machine lock manager";
       serviceConfig.ExecStart = "@${pkgs.libvirt}/sbin/virtlockd virtlockd";
+      restartIfChanged = false;
     };
   };
 }
diff --git a/nixos/modules/virtualisation/vmware-guest.nix b/nixos/modules/virtualisation/vmware-guest.nix
index ce1224a8f131..68930a0e3254 100644
--- a/nixos/modules/virtualisation/vmware-guest.nix
+++ b/nixos/modules/virtualisation/vmware-guest.nix
@@ -33,7 +33,7 @@ in
         serviceConfig.ExecStart = "${open-vm-tools}/bin/vmtoolsd";
       };
 
-    environment.etc."vmware-tools".source = "${pkgs.open-vm-tools}/etc/vmware-tools/*";
+    environment.etc."vmware-tools".source = "${open-vm-tools}/etc/vmware-tools/*";
 
     services.xserver = mkIf (!cfg.headless) {
       videoDrivers = mkOverride 50 [ "vmware" ];
diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix
index 5239652d4075..c7656bc309c0 100644
--- a/nixos/modules/virtualisation/xen-dom0.nix
+++ b/nixos/modules/virtualisation/xen-dom0.nix
@@ -16,6 +16,7 @@ in
     virtualisation.xen.enable =
       mkOption {
         default = false;
+        type = types.bool;
         description =
           ''
             Setting this option enables the Xen hypervisor, a
diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix
index ecbd317cb9a4..54fd4a15ffcc 100644
--- a/nixos/release-combined.nix
+++ b/nixos/release-combined.nix
@@ -4,7 +4,8 @@
 
 { nixpkgs ? { outPath = ./..; revCount = 56789; shortRev = "gfedcba"; }
 , stableBranch ? false
-, supportedSystems ? [ "x86_64-linux" "i686-linux" ]
+, supportedSystems ? [ "x86_64-linux" ]
+, limitedSupportedSystems ? [ "i686-linux" ]
 }:
 
 let
@@ -19,10 +20,16 @@ let
       else pkgs.lib.mapAttrs (n: v: removeMaintainers v) set
     else set;
 
+  allSupportedNixpkgs = builtins.removeAttrs (removeMaintainers (import ../pkgs/top-level/release.nix {
+    supportedSystems = supportedSystems ++ limitedSupportedSystems;
+    nixpkgs = nixpkgsSrc;
+  })) [ "unstable" ];
+
 in rec {
 
   nixos = removeMaintainers (import ./release.nix {
-    inherit stableBranch supportedSystems;
+    inherit stableBranch;
+    supportedSystems = supportedSystems ++ limitedSupportedSystems;
     nixpkgs = nixpkgsSrc;
   });
 
@@ -38,8 +45,11 @@ in rec {
       maintainers = [ pkgs.lib.maintainers.eelco ];
     };
     constituents =
-      let all = x: map (system: x.${system}) supportedSystems; in
-      [ nixos.channel
+      let
+        all = x: map (system: x.${system})
+          (supportedSystems ++ limitedSupportedSystems);
+      in [
+        nixos.channel
         (all nixos.dummy)
         (all nixos.manual)
 
@@ -60,6 +70,7 @@ in rec {
         (all nixos.tests.installer.simple)
         (all nixos.tests.installer.simpleLabels)
         (all nixos.tests.installer.simpleProvided)
+        (all nixos.tests.installer.simpleUefiSystemdBoot)
         (all nixos.tests.installer.swraid)
         (all nixos.tests.installer.btrfsSimple)
         (all nixos.tests.installer.btrfsSubvols)
@@ -106,8 +117,8 @@ in rec {
         (all nixos.tests.xfce)
 
         nixpkgs.tarball
-        (all nixpkgs.emacs)
-        (all nixpkgs.jdk)
+        (all allSupportedNixpkgs.emacs)
+        (all allSupportedNixpkgs.jdk)
       ];
   });
 
diff --git a/nixos/release.nix b/nixos/release.nix
index 467e3bb8cd61..ca2a164bb6c8 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -1,6 +1,6 @@
 { nixpkgs ? { outPath = ./..; revCount = 56789; shortRev = "gfedcba"; }
 , stableBranch ? false
-, supportedSystems ? [ "x86_64-linux" "i686-linux" ]
+, supportedSystems ? [ "x86_64-linux" ]
 }:
 
 with import ../lib;
@@ -239,6 +239,7 @@ in rec {
   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 = callTest tests/elk.nix {};
   tests.ferm = callTest tests/ferm.nix {};
   tests.firefox = callTest tests/firefox.nix {};
   tests.firewall = callTest tests/firewall.nix {};
@@ -297,12 +298,14 @@ in rec {
   tests.pumpio = callTest tests/pump.io.nix {};
   # tests.quagga = callTest tests/quagga.nix {};
   tests.quake3 = callTest tests/quake3.nix {};
+  tests.radicale = callTest tests/radicale.nix {};
   tests.runInMachine = callTest tests/run-in-machine.nix {};
   tests.samba = callTest tests/samba.nix {};
   tests.sddm = callSubTests tests/sddm.nix {};
   tests.simple = callTest tests/simple.nix {};
   tests.slim = callTest tests/slim.nix {};
   tests.smokeping = callTest tests/smokeping.nix {};
+  tests.snapper = callTest tests/snapper.nix {};
   tests.taskserver = callTest tests/taskserver.nix {};
   tests.tomcat = callTest tests/tomcat.nix {};
   tests.udisks2 = callTest tests/udisks2.nix {};
diff --git a/nixos/tests/ammonite.nix b/nixos/tests/ammonite.nix
new file mode 100644
index 000000000000..e1dee71fddf2
--- /dev/null
+++ b/nixos/tests/ammonite.nix
@@ -0,0 +1,20 @@
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "ammonite";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ nequissimus ];
+  };
+
+  nodes = {
+    amm =
+      { config, pkgs, ... }:
+        {
+          environment.systemPackages = [ pkgs.ammonite ];
+        };
+    };
+
+  testScript = ''
+    startAll;
+
+    $amm->succeed("amm -c 'val foo = 21; println(foo * 2)' | grep 42")
+  '';
+})
diff --git a/nixos/tests/containers-reloadable.nix b/nixos/tests/containers-reloadable.nix
new file mode 100644
index 000000000000..b5867c6f6ab1
--- /dev/null
+++ b/nixos/tests/containers-reloadable.nix
@@ -0,0 +1,66 @@
+import ./make-test.nix ({ pkgs, lib, ...} :
+let
+  client_base = rec {
+    
+    containers.test1 = {
+      autoStart = true;
+      config = {
+        environment.etc."check".text = "client_base";
+      };
+    };
+
+    # prevent make-test.nix to change IP
+    networking.interfaces = {
+      eth1.ip4 = lib.mkOverride 0 [ ];
+    };
+  };
+in {
+  name = "cotnainers-reloadable";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ danbst ];
+  };
+
+  nodes = {
+    client = { lib, pkgs, ... }: {
+      imports = [ client_base ];
+    };
+
+    client_c1 = { lib, pkgs, ... }: {
+      imports = [ client_base ];
+
+      containers.test1.config = {
+        environment.etc."check".text = lib.mkForce "client_c1";
+        services.httpd.enable = true;
+        services.httpd.adminAddr = "nixos@example.com";
+      };
+    };
+    client_c2 = { lib, pkgs, ... }: {
+      imports = [ client_base ];
+
+      containers.test1.config = {
+        environment.etc."check".text = lib.mkForce "client_c2";
+        services.nginx.enable = true;
+      };
+    };
+  };
+
+  testScript = {nodes, ...}: let
+    originalSystem = nodes.client.config.system.build.toplevel;
+    c1System = nodes.client_c1.config.system.build.toplevel;
+    c2System = nodes.client_c2.config.system.build.toplevel;
+  in ''
+    $client->start();
+    $client->waitForUnit("default.target");
+    $client->succeed("[[ \$(nixos-container run test1 cat /etc/check) == client_base ]] >&2");
+
+    $client->succeed("${c1System}/bin/switch-to-configuration test >&2");
+    $client->succeed("[[ \$(nixos-container run test1 cat /etc/check) == client_c1 ]] >&2");
+    $client->succeed("systemctl status httpd -M test1 >&2");
+
+    $client->succeed("${c2System}/bin/switch-to-configuration test >&2");
+    $client->succeed("[[ \$(nixos-container run test1 cat /etc/check) == client_c2 ]] >&2");
+    $client->fail("systemctl status httpd -M test1 >&2");
+    $client->succeed("systemctl status nginx -M test1 >&2");
+  '';
+
+})
diff --git a/nixos/tests/gnome3.nix b/nixos/tests/gnome3.nix
index 4787d42d695a..492fa61484a0 100644
--- a/nixos/tests/gnome3.nix
+++ b/nixos/tests/gnome3.nix
@@ -15,7 +15,7 @@ import ./make-test.nix ({ pkgs, ...} : {
       services.xserver.displayManager.auto.user = "alice";
       services.xserver.desktopManager.gnome3.enable = true;
 
-      virtualisation.memorySize = 512;
+      virtualisation.memorySize = 1024;
     };
 
   testScript =
diff --git a/nixos/tests/hydra.nix b/nixos/tests/hydra.nix
new file mode 100644
index 000000000000..6abd7a5ad300
--- /dev/null
+++ b/nixos/tests/hydra.nix
@@ -0,0 +1,32 @@
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "hydra-init-localdb";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ pstn ];
+  };
+
+  machine =
+    { config, pkgs, ... }:
+
+    {
+      services.hydra = {
+        enable = true;
+
+        #Hydra needs those settings to start up, so we add something not harmfull.
+        hydraURL = "example.com";
+        notificationSender = "example@example.com";
+      };
+    };
+
+  testScript =
+    ''
+      # let the system boot up
+      $machine->waitForUnit("multi-user.target");
+      # test whether the database is running
+      $machine->succeed("systemctl status postgresql.service");
+      # test whether the actual hydra daemons are running
+      $machine->succeed("systemctl status hydra-queue-runner.service");
+      $machine->succeed("systemctl status hydra-init.service");
+      $machine->succeed("systemctl status hydra-evaluator.service");
+      $machine->succeed("systemctl status hydra-send-stats.service");
+     '';
+})
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index a6afe8a4b72a..c1dae2a34fa3 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -18,6 +18,9 @@ let
             <nixpkgs/nixos/modules/testing/test-instrumentation.nix>
           ];
 
+        # To ensure that we can rebuild the grub configuration on the nixos-rebuild
+        system.extraDependencies = with pkgs; [ stdenvNoCC ];
+
         ${optionalString (bootLoader == "grub") ''
           boot.loader.grub.version = ${toString grubVersion};
           ${optionalString (grubVersion == 1) ''
@@ -71,7 +74,7 @@ let
       # Make sure that we get a login prompt etc.
       $machine->succeed("echo hello");
       #$machine->waitForUnit('getty@tty2');
-      $machine->waitForUnit("rogue");
+      #$machine->waitForUnit("rogue");
       $machine->waitForUnit("nixos-manual");
 
       # Wait for hard disks to appear in /dev
@@ -270,7 +273,7 @@ in {
     };
 
   # Simple GPT/UEFI configuration using systemd-boot with 3 partitions: ESP, swap & root filesystem
-  simpleUefiGummiboot = makeInstallerTest "simpleUefiGummiboot"
+  simpleUefiSystemdBoot = makeInstallerTest "simpleUefiSystemdBoot"
     { createPartitions =
         ''
           $machine->succeed(
diff --git a/nixos/tests/ipfs.nix b/nixos/tests/ipfs.nix
index 92d742e4f37f..a93f8f175c55 100644
--- a/nixos/tests/ipfs.nix
+++ b/nixos/tests/ipfs.nix
@@ -11,27 +11,47 @@ import ./make-test.nix ({ pkgs, ...} : {
       {
         services.ipfs = {
           enable = true;
+          defaultMode = "norouting";
           gatewayAddress = "/ip4/127.0.0.1/tcp/2323";
           apiAddress = "/ip4/127.0.0.1/tcp/2324";
         };
+        networking.firewall.allowedTCPPorts = [ 4001 ];
       };
     getter =
       { config, pkgs, ... }:
       {
-         services.ipfs.enable = true;
+        services.ipfs = {
+          enable = true;
+          defaultMode = "norouting";
+          # not yet. See #28621
+          #autoMount = true;
+        };
+        networking.firewall.allowedTCPPorts = [ 4001 ];
       };
   };
 
   testScript = ''
     startAll;
-    $adder->waitForUnit("ipfs");
-    # * => needs ipfs dht (internet)
-    # $getter->waitForUnit("ipfs");
+    $adder->waitForUnit("ipfs-norouting");
+    $getter->waitForUnit("ipfs-norouting");
+
+    # wait until api is available
     $adder->waitUntilSucceeds("ipfs --api /ip4/127.0.0.1/tcp/2324 id");
-    $adder->mustSucceed("([[ -n '$(ipfs --api /ip4/127.0.0.1/tcp/2324 config Addresses.gatewayAddress | grep /ip4/127.0.0.1/tcp/2323)' ]])");
-    # * $getter->waitUntilSucceeds("ipfs --api /ip4/127.0.0.1/tcp/5001 id");
-    # * my $ipfsHash = $adder->mustSucceed("echo fnord | ipfs --api /ip4/127.0.0.1/tcp/2324 add | cut -d' ' -f2");
-    $adder->mustSucceed("([[ -n '$(echo fnord | ipfs --api /ip4/127.0.0.1/tcp/2324 add | grep added)' ]])");
-    # * $getter->mustSucceed("ipfs --api /ip4/127.0.0.1/tcp/5001 cat $ipfsHash");
+    my $addrId = $adder->succeed("ipfs --api /ip4/127.0.0.1/tcp/2324 id -f=\"<id>\"");
+    my $addrIp = (split /[ \/]+/, $adder->succeed("ip -o -4 addr show dev eth1"))[3];
+
+    $adder->mustSucceed("[ -n \"\$(ipfs --api /ip4/127.0.0.1/tcp/2324 config Addresses.Gateway | grep /ip4/127.0.0.1/tcp/2323)\" ]");
+
+    # wait until api is available
+    $getter->waitUntilSucceeds("ipfs --api /ip4/127.0.0.1/tcp/5001 id");
+    my $ipfsHash = $adder->mustSucceed("echo fnord | ipfs --api /ip4/127.0.0.1/tcp/2324 add | cut -d' ' -f2");
+    chomp($ipfsHash);
+
+    $adder->mustSucceed("[ -n \"\$(echo fnord | ipfs --api /ip4/127.0.0.1/tcp/2324 add | grep added)\" ]");
+
+    $getter->mustSucceed("ipfs --api /ip4/127.0.0.1/tcp/5001 swarm connect /ip4/$addrIp/tcp/4001/ipfs/$addrId");
+    $getter->mustSucceed("[ -n \"\$(ipfs --api /ip4/127.0.0.1/tcp/5001 cat /ipfs/$ipfsHash | grep fnord)\" ]");
+    # not yet. See #28621
+    # $getter->mustSucceed("[ -n \"$(cat /ipfs/$ipfsHash | grep fnord)\" ]");
     '';
 })
diff --git a/nixos/tests/keymap.nix b/nixos/tests/keymap.nix
index 55a0e7603882..c431c1a34174 100644
--- a/nixos/tests/keymap.nix
+++ b/nixos/tests/keymap.nix
@@ -49,6 +49,38 @@ let
     machine.i18n.consoleKeyMap = mkOverride 900 layout;
     machine.services.xserver.layout = mkOverride 900 layout;
     machine.imports = [ ./common/x11.nix extraConfig ];
+    machine.services.xserver.displayManager.slim = {
+      enable = true;
+
+      # Use a custom theme in order to get best OCR results
+      theme = pkgs.runCommand "slim-theme-ocr" {
+        nativeBuildInputs = [ pkgs.imagemagick ];
+      } ''
+        mkdir "$out"
+        convert -size 1x1 xc:white "$out/background.jpg"
+        convert -size 200x100 xc:white "$out/panel.jpg"
+        cat > "$out/slim.theme" <<EOF
+        background_color #ffffff
+        background_style tile
+
+        input_fgcolor #000000
+        msg_color #000000
+
+        session_color #000000
+        session_font Verdana:size=16:bold
+
+        username_msg Username:
+        username_font Verdana:size=16:bold
+        username_color #000000
+        username_x 50%
+        username_y 40%
+
+        password_msg Password:
+        password_x 50%
+        password_y 40%
+        EOF
+      '';
+    };
 
     testScript = ''
       sub waitCatAndDelete ($) {
diff --git a/nixos/tests/minio.nix b/nixos/tests/minio.nix
index 462a3bc4768c..a349265b2f57 100644
--- a/nixos/tests/minio.nix
+++ b/nixos/tests/minio.nix
@@ -4,8 +4,15 @@ import ./make-test.nix ({ pkgs, ...} : {
     maintainers = [ bachp ];
   };
 
-  machine = { config, pkgs, ... }: {
-    services.minio.enable = true;
+  nodes = {
+    machine = { config, pkgs, ... }: {
+      services.minio = {
+        enable = true;
+        accessKey = "BKIKJAA5BMMU2RHO6IBB";
+        secretKey = "V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12";
+      };
+      environment.systemPackages = [ pkgs.minio-client ];
+    };
   };
 
   testScript =
@@ -14,6 +21,12 @@ import ./make-test.nix ({ pkgs, ...} : {
       $machine->waitForUnit("minio.service");
       $machine->waitForOpenPort(9000);
       $machine->succeed("curl --fail http://localhost:9000/minio/index.html");
+
+      # Create a test bucket on the server
+      $machine->succeed("mc config host add minio http://localhost:9000 BKIKJAA5BMMU2RHO6IBB V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12 S3v4");
+      $machine->succeed("mc mb minio/test-bucket");
+      $machine->succeed("mc ls minio") =~ /test-bucket/ or die;
       $machine->shutdown;
+
     '';
 })
diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix
index b926a62194b4..1b24551009c9 100644
--- a/nixos/tests/misc.nix
+++ b/nixos/tests/misc.nix
@@ -34,7 +34,7 @@ import ./make-test.nix ({ pkgs, ...} : {
       };
 
       subtest "nixos-rebuild", sub {
-          $machine->succeed("nixos-rebuild --help | grep SYNOPSIS");
+          $machine->succeed("nixos-rebuild --help | grep 'NixOS module' ");
       };
 
       # Sanity check for uid/gid assignment.
diff --git a/nixos/tests/nat.nix b/nixos/tests/nat.nix
index 74e20bff8d81..a12b7645bc28 100644
--- a/nixos/tests/nat.nix
+++ b/nixos/tests/nat.nix
@@ -6,6 +6,20 @@
 import ./make-test.nix ({ pkgs, lib, withFirewall, withConntrackHelpers ? false, ... }:
   let
     unit = if withFirewall then "firewall" else "nat";
+
+    routerBase =
+      lib.mkMerge [
+        { virtualisation.vlans = [ 2 1 ];
+          networking.firewall.enable = withFirewall;
+          networking.firewall.allowPing = true;
+          networking.nat.internalIPs = [ "192.168.1.0/24" ];
+          networking.nat.externalInterface = "eth1";
+        }
+        (lib.optionalAttrs withConntrackHelpers {
+          networking.firewall.connectionTrackingModules = [ "ftp" ];
+          networking.firewall.autoLoadConntrackHelpers = true;
+        })
+      ];
   in
   {
     name = "nat" + (if withFirewall then "WithFirewall" else "Standalone")
@@ -30,20 +44,16 @@ import ./make-test.nix ({ pkgs, lib, withFirewall, withConntrackHelpers ? false,
           ];
 
         router =
-          { config, pkgs, ... }:
-          lib.mkMerge [
-            { virtualisation.vlans = [ 2 1 ];
-              networking.firewall.enable = withFirewall;
-              networking.firewall.allowPing = true;
-              networking.nat.enable = true;
-              networking.nat.internalIPs = [ "192.168.1.0/24" ];
-              networking.nat.externalInterface = "eth1";
-            }
-            (lib.optionalAttrs withConntrackHelpers {
-              networking.firewall.connectionTrackingModules = [ "ftp" ];
-              networking.firewall.autoLoadConntrackHelpers = true;
-            })
-          ];
+        { config, pkgs, ... }: lib.mkMerge [
+          routerBase
+          { networking.nat.enable = true; }
+        ];
+
+        routerDummyNoNat =
+        { config, pkgs, ... }: lib.mkMerge [
+          routerBase
+          { networking.nat.enable = false; }
+        ];
 
         server =
           { config, pkgs, ... }:
@@ -57,9 +67,13 @@ import ./make-test.nix ({ pkgs, lib, withFirewall, withConntrackHelpers ? false,
       };
 
     testScript =
-      { nodes, ... }:
-      ''
-        startAll;
+      { nodes, ... }: let
+        routerDummyNoNatClosure = nodes.routerDummyNoNat.config.system.build.toplevel;
+        routerClosure = nodes.router.config.system.build.toplevel;
+      in ''
+        $client->start;
+        $router->start;
+        $server->start;
 
         # The router should have access to the server.
         $server->waitForUnit("network.target");
@@ -87,13 +101,18 @@ import ./make-test.nix ({ pkgs, lib, withFirewall, withConntrackHelpers ? false,
         $router->succeed("ping -c 1 client >&2");
 
         # If we turn off NAT, the client shouldn't be able to reach the server.
-        $router->succeed("iptables -t nat -D PREROUTING -j nixos-nat-pre");
-        $router->succeed("iptables -t nat -D POSTROUTING -j nixos-nat-post");
+        $router->succeed("${routerDummyNoNatClosure}/bin/switch-to-configuration test 2>&1");
         $client->fail("curl --fail --connect-timeout 5 http://server/ >&2");
         $client->fail("ping -c 1 server >&2");
 
         # And make sure that reloading the NAT job works.
-        $router->succeed("systemctl restart ${unit}");
+        $router->succeed("${routerClosure}/bin/switch-to-configuration test 2>&1");
+        # FIXME: this should not be necessary, but nat.service is not started because
+        #        network.target is not triggered
+        #        (https://github.com/NixOS/nixpkgs/issues/16230#issuecomment-226408359)
+        ${lib.optionalString (!withFirewall) ''
+          $router->succeed("systemctl start nat.service");
+        ''}
         $client->succeed("curl --fail http://server/ >&2");
         $client->succeed("ping -c 1 server >&2");
       '';
diff --git a/nixos/tests/nginx.nix b/nixos/tests/nginx.nix
index c2beb5590ef7..7f7bc0f0b4fe 100644
--- a/nixos/tests/nginx.nix
+++ b/nixos/tests/nginx.nix
@@ -3,7 +3,7 @@
 #      generated virtual hosts config.
 
 import ./make-test.nix ({ pkgs, ...} : {
-  name = "jenkins";
+  name = "nginx";
   meta = with pkgs.stdenv.lib.maintainers; {
     maintainers = [ mbbx6spp ];
   };
diff --git a/nixos/tests/plasma5.nix b/nixos/tests/plasma5.nix
index f97544b5ea52..f3bd4c5915b0 100644
--- a/nixos/tests/plasma5.nix
+++ b/nixos/tests/plasma5.nix
@@ -16,17 +16,10 @@ import ./make-test.nix ({ pkgs, ...} :
 
     # fontconfig-penultimate-0.3.3 -> 0.3.4 broke OCR apparently, but no idea why.
     nixpkgs.config.packageOverrides = superPkgs: {
-      fontconfig-penultimate = superPkgs.fontconfig-penultimate.overrideAttrs
-        (_attrs: rec {
-          version = "0.3.3";
-          name = "fontconfig-penultimate-${version}";
-          src = pkgs.fetchFromGitHub {
-            owner = "ttuegel";
-            repo = "fontconfig-penultimate";
-            rev = version;
-            sha256 = "0392lw31jps652dcjazln77ihb6bl7gk201gb7wb9i223avp86w9";
-          };
-        });
+      fontconfig-penultimate = superPkgs.fontconfig-penultimate.override {
+        version = "0.3.3";
+        sha256 = "1z76jbkb0nhf4w7fy647yyayqr4q02fgk6w58k0yi700p0m3h4c9";
+      };
     };
   };
 
diff --git a/nixos/tests/postgresql.nix b/nixos/tests/postgresql.nix
index 1f4f43a26669..0ce37b55bb7b 100644
--- a/nixos/tests/postgresql.nix
+++ b/nixos/tests/postgresql.nix
@@ -13,8 +13,10 @@ let
     INSERT INTO sth (id) VALUES (1);
     INSERT INTO sth (id) VALUES (1);
     INSERT INTO sth (id) VALUES (1);
+    CREATE TABLE xmltest ( doc xml );
+    INSERT INTO xmltest (doc) VALUES ('<test>ok</test>'); -- check if libxml2 enabled
   '';
-  make-postgresql-test = postgresql-name: postgresql-package: {
+  make-postgresql-test = postgresql-name: postgresql-package: makeTest {
     name = postgresql-name;
     meta = with pkgs.stdenv.lib.maintainers; {
       maintainers = [ zagy ];
@@ -27,17 +29,23 @@ let
       };
 
     testScript = ''
+      sub check_count {
+        my ($select, $nlines) = @_;
+        return 'test $(sudo -u postgres psql postgres -tAc "' . $select . '"|wc -l) -eq ' . $nlines;
+      }
+
       $machine->start;
       $machine->waitForUnit("postgresql");
       # postgresql should be available just after unit start
-      $machine->succeed("cat ${test-sql} | psql postgres");
+      $machine->succeed("cat ${test-sql} | sudo -u postgres psql");
       $machine->shutdown; # make sure that postgresql survive restart (bug #1735)
       sleep(2);
       $machine->start;
       $machine->waitForUnit("postgresql");
-      $machine->fail('test $(psql postgres -tAc "SELECT * FROM sth;"|wc -l) -eq 3');
-      $machine->succeed('test $(psql postgres -tAc "SELECT * FROM sth;"|wc -l) -eq 5');
-      $machine->fail('test $(psql postgres -tAc "SELECT * FROM sth;"|wc -l) -eq 4');
+      $machine->fail(check_count("SELECT * FROM sth;", 3));
+      $machine->succeed(check_count("SELECT * FROM sth;", 5));
+      $machine->fail(check_count("SELECT * FROM sth;", 4));
+      $machine->succeed(check_count("SELECT xpath(\'/test/text()\', doc) FROM xmltest;", 1));
       $machine->shutdown;
     '';
 
diff --git a/nixos/tests/radicale.nix b/nixos/tests/radicale.nix
index 4c2ed8456ddd..bec86d2cb284 100644
--- a/nixos/tests/radicale.nix
+++ b/nixos/tests/radicale.nix
@@ -1,80 +1,39 @@
 let
-  port = 5232;
-  radicaleOverlay = self: super: {
-    radicale = super.radicale.overrideAttrs (oldAttrs: {
-      propagatedBuildInputs = with self.pythonPackages;
-        (oldAttrs.propagatedBuildInputs or []) ++ [
-          passlib
-        ];
-    });
-  };
-  common = { config, pkgs, ...}: {
+  user = "someuser";
+  password = "some_password";
+  port = builtins.toString 5232;
+in
+  import ./make-test.nix ({ pkgs, lib, ... }: {
+  name = "radicale";
+  meta.maintainers = with lib.maintainers; [ aneeshusa infinisil ];
+
+  machine = {
     services.radicale = {
       enable = true;
-      config = let home = config.users.extraUsers.radicale.home; in ''
-        [server]
-        hosts = 127.0.0.1:${builtins.toString port}
-        daemon = False
-        [encoding]
-        [well-known]
+      config = ''
         [auth]
         type = htpasswd
         htpasswd_filename = /etc/radicale/htpasswd
         htpasswd_encryption = bcrypt
-        [git]
-        [rights]
+
         [storage]
-        type = filesystem
-        filesystem_folder = ${home}/collections
+        filesystem_folder = /tmp/collections
+
         [logging]
-        [headers]
+        debug = True
       '';
     };
     # WARNING: DON'T DO THIS IN PRODUCTION!
     # This puts secrets (albeit hashed) directly into the Nix store for ease of testing.
-    environment.etc."radicale/htpasswd".source = with pkgs; let
-      py = python.withPackages(ps: with ps; [ passlib ]);
-    in runCommand "htpasswd" {} ''
-        ${py}/bin/python -c "
-from passlib.apache import HtpasswdFile
-ht = HtpasswdFile(
-    '$out',
-    new=True,
-    default_scheme='bcrypt'
-)
-ht.set_password('someuser', 'really_secret_password')
-ht.save()
-"
+    environment.etc."radicale/htpasswd".source = pkgs.runCommand "htpasswd" {} ''
+      ${pkgs.apacheHttpd}/bin/htpasswd -bcB "$out" ${user} ${password}
     '';
   };
-
-in import ./make-test.nix ({ lib, ... }: {
-  name = "radicale";
-  meta.maintainers = with lib.maintainers; [ aneeshusa ];
-
-  # Test radicale with bcrypt-based htpasswd authentication
-  nodes = {
-    py2 = { config, pkgs, ... }@args: (common args) // {
-      nixpkgs.overlays = [
-        radicaleOverlay
-      ];
-    };
-    py3 = { config, pkgs, ... }@args: (common args) // {
-      nixpkgs.overlays = [
-        (self: super: {
-          python = self.python3;
-          pythonPackages = self.python3.pkgs;
-        })
-        radicaleOverlay
-      ];
-    };
-  };
-
+  
+  # This tests whether the web interface is accessible to an authenticated user
   testScript = ''
-    for my $machine ($py2, $py3) {
-      $machine->waitForUnit('radicale.service');
-      $machine->waitForOpenPort(${builtins.toString port});
-      $machine->succeed('curl -s http://someuser:really_secret_password@127.0.0.1:${builtins.toString port}/someuser/calendar.ics/');
-    }
+    $machine->waitForUnit('radicale.service');
+    $machine->waitForOpenPort(${port});
+    $machine->succeed('curl --fail http://${user}:${password}@localhost:${port}/.web/');
   '';
 })
diff --git a/nixos/tests/snapper.nix b/nixos/tests/snapper.nix
new file mode 100644
index 000000000000..74ec22fd3499
--- /dev/null
+++ b/nixos/tests/snapper.nix
@@ -0,0 +1,43 @@
+import ./make-test.nix ({ ... }:
+{
+  name = "snapper";
+
+  machine = { pkgs, lib, ... }: {
+    boot.initrd.postDeviceCommands = ''
+      ${pkgs.btrfs-progs}/bin/mkfs.btrfs -f -L aux /dev/vdb
+    '';
+
+    virtualisation.emptyDiskImages = [ 4096 ];
+
+    fileSystems = lib.mkVMOverride {
+      "/home" = {
+        device = "/dev/disk/by-label/aux";
+        fsType = "btrfs";
+      };
+    };
+    services.snapper.configs.home.subvolume = "/home";
+    services.snapper.filters = "/nix";
+  };
+
+  testScript = ''
+    $machine->succeed("btrfs subvolume create /home/.snapshots");
+
+    $machine->succeed("snapper -c home list");
+
+    $machine->succeed("snapper -c home create --description empty");
+
+    $machine->succeed("echo test > /home/file");
+    $machine->succeed("snapper -c home create --description file");
+
+    $machine->succeed("snapper -c home status 1..2");
+
+    $machine->succeed("snapper -c home undochange 1..2");
+    $machine->fail("ls /home/file");
+
+    $machine->succeed("snapper -c home delete 2");
+
+    $machine->succeed("systemctl --wait start snapper-timeline.service");
+
+    $machine->succeed("systemctl --wait start snapper-cleanup.service");
+  '';
+})
diff --git a/nixos/tests/taskserver.nix b/nixos/tests/taskserver.nix
index cdccb11d8887..75be97a507d0 100644
--- a/nixos/tests/taskserver.nix
+++ b/nixos/tests/taskserver.nix
@@ -246,6 +246,10 @@ in {
     };
 
     subtest "check manual configuration", sub {
+      # Remove the keys from automatic CA creation, to make sure the new
+      # generation doesn't use keys from before.
+      $server->succeed('rm -rf ${cfg.dataDir}/keys/* >&2');
+
       $server->succeed('${switchToNewServer} >&2');
       $server->waitForUnit("taskserver.service");
       $server->waitForOpenPort(${portStr});
diff --git a/nixos/tests/timezone.nix b/nixos/tests/timezone.nix
new file mode 100644
index 000000000000..2204649a3fc4
--- /dev/null
+++ b/nixos/tests/timezone.nix
@@ -0,0 +1,45 @@
+{
+  timezone-static = import ./make-test.nix ({ pkgs, ... }: {
+    name = "timezone-static";
+    meta.maintainers = with pkgs.lib.maintainers; [ lheckemann ];
+
+    machine.time.timeZone = "Europe/Amsterdam";
+
+    testScript = ''
+      $machine->waitForUnit("dbus.socket");
+      $machine->fail("timedatectl set-timezone Asia/Tokyo");
+      my @dateResult = $machine->execute('date -d @0 "+%Y-%m-%d %H:%M:%S"');
+      $dateResult[1] eq "1970-01-01 01:00:00\n" or die "Timezone seems to be wrong";
+    '';
+  });
+
+  timezone-imperative = import ./make-test.nix ({ pkgs, ... }: {
+    name = "timezone-imperative";
+    meta.maintainers = with pkgs.lib.maintainers; [ lheckemann ];
+
+    machine.time.timeZone = null;
+
+    testScript = ''
+      $machine->waitForUnit("dbus.socket");
+
+      # Should default to UTC
+      my @dateResult = $machine->execute('date -d @0 "+%Y-%m-%d %H:%M:%S"');
+      print $dateResult[1];
+      $dateResult[1] eq "1970-01-01 00:00:00\n" or die "Timezone seems to be wrong";
+
+      $machine->succeed("timedatectl set-timezone Asia/Tokyo");
+
+      # Adjustment should be taken into account
+      my @dateResult = $machine->execute('date -d @0 "+%Y-%m-%d %H:%M:%S"');
+      print $dateResult[1];
+      $dateResult[1] eq "1970-01-01 09:00:00\n" or die "Timezone was not adjusted";
+
+      # Adjustment should persist across a reboot
+      $machine->shutdown;
+      $machine->waitForUnit("dbus.socket");
+      my @dateResult = $machine->execute('date -d @0 "+%Y-%m-%d %H:%M:%S"');
+      print $dateResult[1];
+      $dateResult[1] eq "1970-01-01 09:00:00\n" or die "Timezone adjustment was not persisted";
+    '';
+  });
+}