summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/config/iproute2.nix35
-rw-r--r--nixos/modules/config/shells-environment.nix21
-rw-r--r--nixos/modules/config/system-path.nix2
-rw-r--r--nixos/modules/config/xdg/mime.nix14
-rw-r--r--nixos/modules/i18n/input-method/default.xml319
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix49
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix63
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix49
-rw-r--r--nixos/modules/installer/cd-dvd/iso-image.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix2
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix8
-rw-r--r--nixos/modules/misc/documentation.nix99
-rw-r--r--nixos/modules/misc/ids.nix10
-rw-r--r--nixos/modules/misc/nixpkgs.nix12
-rw-r--r--nixos/modules/module-list.nix11
-rw-r--r--nixos/modules/profiles/graphical.nix7
-rw-r--r--nixos/modules/profiles/installation-device.nix3
-rw-r--r--nixos/modules/profiles/minimal.nix1
-rw-r--r--nixos/modules/programs/bash/bash.nix12
-rw-r--r--nixos/modules/programs/dconf.nix2
-rw-r--r--nixos/modules/programs/digitalbitbox/doc.xml97
-rw-r--r--nixos/modules/programs/fish.nix19
-rw-r--r--nixos/modules/programs/plotinus.xml37
-rw-r--r--nixos/modules/programs/yabar.nix17
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.xml182
-rw-r--r--nixos/modules/programs/zsh/zsh.nix16
-rw-r--r--nixos/modules/rename.nix2
-rw-r--r--nixos/modules/security/acme.nix8
-rw-r--r--nixos/modules/security/acme.xml104
-rw-r--r--nixos/modules/security/hidepid.xml37
-rw-r--r--nixos/modules/services/amqp/rabbitmq.nix90
-rw-r--r--nixos/modules/services/cluster/kubernetes/default.nix8
-rw-r--r--nixos/modules/services/computing/slurm/slurm.nix10
-rw-r--r--nixos/modules/services/databases/foundationdb.xml621
-rw-r--r--nixos/modules/services/databases/postgresql.nix2
-rw-r--r--nixos/modules/services/databases/postgresql.xml94
-rw-r--r--nixos/modules/services/desktops/flatpak.xml71
-rw-r--r--nixos/modules/services/desktops/gnome3/rygel.nix30
-rw-r--r--nixos/modules/services/editors/emacs.xml821
-rw-r--r--nixos/modules/services/hardware/lirc.nix85
-rw-r--r--nixos/modules/services/hardware/ratbagd.nix32
-rw-r--r--nixos/modules/services/hardware/trezord.nix17
-rw-r--r--nixos/modules/services/mail/exim.nix18
-rw-r--r--nixos/modules/services/mail/rmilter.nix5
-rw-r--r--nixos/modules/services/mail/rspamd.nix83
-rw-r--r--nixos/modules/services/misc/airsonic.nix19
-rw-r--r--nixos/modules/services/misc/emby.nix17
-rw-r--r--nixos/modules/services/misc/gitlab.nix15
-rw-r--r--nixos/modules/services/misc/gitlab.xml124
-rw-r--r--nixos/modules/services/misc/lidarr.nix46
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix7
-rw-r--r--nixos/modules/services/misc/nixos-manual.nix133
-rw-r--r--nixos/modules/services/misc/sickbeard.nix92
-rw-r--r--nixos/modules/services/misc/taskserver/doc.xml209
-rw-r--r--nixos/modules/services/misc/weechat.nix56
-rw-r--r--nixos/modules/services/misc/weechat.xml66
-rw-r--r--nixos/modules/services/monitoring/datadog-agent.nix19
-rw-r--r--nixos/modules/services/monitoring/grafana.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.xml148
-rw-r--r--nixos/modules/services/monitoring/riemann.nix22
-rw-r--r--nixos/modules/services/networking/chrony.nix52
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.xml69
-rw-r--r--nixos/modules/services/networking/epmd.nix56
-rw-r--r--nixos/modules/services/networking/hylafax/default.nix29
-rw-r--r--nixos/modules/services/networking/hylafax/faxq-default.nix12
-rwxr-xr-xnixos/modules/services/networking/hylafax/faxq-wait.sh29
-rw-r--r--nixos/modules/services/networking/hylafax/hfaxd-default.nix10
-rw-r--r--nixos/modules/services/networking/hylafax/modem-default.nix22
-rw-r--r--nixos/modules/services/networking/hylafax/options.nix375
-rwxr-xr-xnixos/modules/services/networking/hylafax/spool.sh111
-rw-r--r--nixos/modules/services/networking/hylafax/systemd.nix249
-rw-r--r--nixos/modules/services/networking/i2pd.nix604
-rw-r--r--nixos/modules/services/networking/iperf3.nix87
-rw-r--r--nixos/modules/services/networking/iwd.nix2
-rw-r--r--nixos/modules/services/networking/networkmanager.nix14
-rw-r--r--nixos/modules/services/networking/nullidentdmod.nix34
-rw-r--r--nixos/modules/services/networking/shairport-sync.nix3
-rw-r--r--nixos/modules/services/networking/teamspeak3.nix2
-rw-r--r--nixos/modules/services/networking/zeronet.nix20
-rw-r--r--nixos/modules/services/security/sks.nix100
-rw-r--r--nixos/modules/services/security/tor.nix2
-rw-r--r--nixos/modules/services/system/kerberos.nix6
-rw-r--r--nixos/modules/services/web-apps/codimd.nix920
-rw-r--r--nixos/modules/services/web-apps/matomo-doc.xml131
-rw-r--r--nixos/modules/services/x11/desktop-managers/enlightenment.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix1
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix1
-rw-r--r--nixos/modules/services/x11/display-managers/default.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix13
-rw-r--r--nixos/modules/services/x11/display-managers/sddm.nix4
-rw-r--r--nixos/modules/services/x11/hardware/libinput.nix2
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl2
-rw-r--r--nixos/modules/system/activation/top-level.nix62
-rw-r--r--nixos/modules/system/boot/kexec.nix2
-rw-r--r--nixos/modules/system/boot/luksroot.nix15
-rw-r--r--nixos/modules/system/boot/networkd.nix6
-rw-r--r--nixos/modules/system/boot/systemd.nix3
-rw-r--r--nixos/modules/tasks/network-interfaces.nix2
-rw-r--r--nixos/modules/testing/test-instrumentation.nix3
-rw-r--r--nixos/modules/virtualisation/google-compute-image.nix44
100 files changed, 5109 insertions, 2196 deletions
diff --git a/nixos/modules/config/iproute2.nix b/nixos/modules/config/iproute2.nix
index 881ad671a627..a1d9ebcec66b 100644
--- a/nixos/modules/config/iproute2.nix
+++ b/nixos/modules/config/iproute2.nix
@@ -4,20 +4,29 @@ with lib;
 
 let
   cfg = config.networking.iproute2;
-  confDir = "/run/iproute2";
 in
 {
-  options.networking.iproute2.enable = mkEnableOption "copy IP route configuration files";
-
-  config = mkMerge [
-    ({ nixpkgs.config.iproute2.confDir = confDir; })
-
-    (mkIf cfg.enable {
-      system.activationScripts.iproute2 = ''
-        cp -R ${pkgs.iproute}/etc/iproute2 ${confDir}
-        chmod -R 664 ${confDir}
-        chmod +x ${confDir}
+  options.networking.iproute2 = {
+    enable = mkEnableOption "copy IP route configuration files";
+    rttablesExtraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Verbatim lines to add to /etc/iproute2/rt_tables
       '';
-    })
-  ];
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.etc."iproute2/bpf_pinning" = { mode = "0644"; text = fileContents "${pkgs.iproute}/etc/iproute2/bpf_pinning"; };
+    environment.etc."iproute2/ematch_map"  = { mode = "0644"; text = fileContents "${pkgs.iproute}/etc/iproute2/ematch_map";  };
+    environment.etc."iproute2/group"       = { mode = "0644"; text = fileContents "${pkgs.iproute}/etc/iproute2/group";       };
+    environment.etc."iproute2/nl_protos"   = { mode = "0644"; text = fileContents "${pkgs.iproute}/etc/iproute2/nl_protos";   };
+    environment.etc."iproute2/rt_dsfield"  = { mode = "0644"; text = fileContents "${pkgs.iproute}/etc/iproute2/rt_dsfield";  };
+    environment.etc."iproute2/rt_protos"   = { mode = "0644"; text = fileContents "${pkgs.iproute}/etc/iproute2/rt_protos";   };
+    environment.etc."iproute2/rt_realms"   = { mode = "0644"; text = fileContents "${pkgs.iproute}/etc/iproute2/rt_realms";   };
+    environment.etc."iproute2/rt_scopes"   = { mode = "0644"; text = fileContents "${pkgs.iproute}/etc/iproute2/rt_scopes";   };
+    environment.etc."iproute2/rt_tables"   = { mode = "0644"; text = (fileContents "${pkgs.iproute}/etc/iproute2/rt_tables")
+                                                                   + (optionalString (cfg.rttablesExtraConfig != "") "\n\n${cfg.rttablesExtraConfig}"); };
+  };
 }
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
index 31adc9b82620..555db459f57a 100644
--- a/nixos/modules/config/shells-environment.nix
+++ b/nixos/modules/config/shells-environment.nix
@@ -163,15 +163,24 @@ in
         /bin/sh
       '';
 
+    # For resetting environment with `. /etc/set-environment` when needed
+    # and discoverability (see motivation of #30418).
+    environment.etc."set-environment".source = config.system.build.setEnvironment;
+
     system.build.setEnvironment = pkgs.writeText "set-environment"
-       ''
-         ${exportedEnvVars}
+      ''
+        # DO NOT EDIT -- this file has been generated automatically.
+
+        # Prevent this file from being sourced by child shells.
+        export __NIXOS_SET_ENVIRONMENT_DONE=1
 
-         ${cfg.extraInit}
+        ${exportedEnvVars}
 
-         # ~/bin if it exists overrides other bin directories.
-         export PATH="$HOME/bin:$PATH"
-       '';
+        ${cfg.extraInit}
+
+        # ~/bin if it exists overrides other bin directories.
+        export PATH="$HOME/bin:$PATH"
+      '';
 
     system.activationScripts.binsh = stringAfter [ "stdio" ]
       ''
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
index ffb437491f6c..3c24dab85e4d 100644
--- a/nixos/modules/config/system-path.nix
+++ b/nixos/modules/config/system-path.nix
@@ -13,7 +13,7 @@ let
       pkgs.attr
       pkgs.bashInteractive # bash with ncurses support
       pkgs.bzip2
-      pkgs.coreutils
+      pkgs.coreutils-full
       pkgs.cpio
       pkgs.curl
       pkgs.diffutils
diff --git a/nixos/modules/config/xdg/mime.nix b/nixos/modules/config/xdg/mime.nix
index f1b672234a34..4323a49ea1dd 100644
--- a/nixos/modules/config/xdg/mime.nix
+++ b/nixos/modules/config/xdg/mime.nix
@@ -7,7 +7,7 @@ with lib;
       type = types.bool;
       default = true;
       description = ''
-        Whether to install files to support the 
+        Whether to install files to support the
         <link xlink:href="https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html">XDG Shared MIME-info specification</link> and the
         <link xlink:href="https://specifications.freedesktop.org/mime-apps-spec/mime-apps-spec-latest.html">XDG MIME Applications specification</link>.
       '';
@@ -17,18 +17,18 @@ with lib;
   config = mkIf config.xdg.mime.enable {
     environment.pathsToLink = [ "/share/mime" ];
 
-    environment.systemPackages = [ 
-      # this package also installs some useful data, as well as its utilities 
-      pkgs.shared-mime-info 
+    environment.systemPackages = [
+      # this package also installs some useful data, as well as its utilities
+      pkgs.shared-mime-info
     ];
 
     environment.extraSetup = ''
-      if [ -w $out/share/mime ]; then
-        XDG_DATA_DIRS=$out/share ${pkgs.shared-mime-info}/bin/update-mime-database -V $out/share/mime > /dev/null
+      if [ -w $out/share/mime ] && [ -d $out/share/mime/packages ]; then
+          XDG_DATA_DIRS=$out/share ${pkgs.shared-mime-info}/bin/update-mime-database -V $out/share/mime > /dev/null
       fi
 
       if [ -w $out/share/applications ]; then
-        ${pkgs.desktop-file-utils}/bin/update-desktop-database $out/share/applications
+          ${pkgs.desktop-file-utils}/bin/update-desktop-database $out/share/applications
       fi
     '';
   };
diff --git a/nixos/modules/i18n/input-method/default.xml b/nixos/modules/i18n/input-method/default.xml
index ab918a9fb23e..117482fb0d57 100644
--- a/nixos/modules/i18n/input-method/default.xml
+++ b/nixos/modules/i18n/input-method/default.xml
@@ -3,32 +3,50 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="module-services-input-methods">
-
-<title>Input Methods</title>
-
-<para>Input methods are an operating system component that allows any data, such
-  as keyboard strokes or mouse movements, to be received as input. In this way
-  users can enter characters and symbols not found on their input devices. Using
-  an input method is obligatory for any language that has more graphemes than
-  there are keys on the keyboard.</para>
-
-<para>The following input methods are available in NixOS:</para>
-
-<itemizedlist>
-  <listitem><para>IBus: The intelligent input bus.</para></listitem>
-  <listitem><para>Fcitx: A customizable lightweight input
-      method.</para></listitem>
-  <listitem><para>Nabi: A Korean input method based on XIM.</para></listitem>
-  <listitem><para>Uim: The universal input method, is a library with a XIM
-      bridge.</para></listitem>
-</itemizedlist>
-
-<section xml:id="module-services-input-methods-ibus"><title>IBus</title>
-
-<para>IBus is an Intelligent Input Bus. It provides full featured and user
-  friendly input method user interface.</para>
-
-<para>The following snippet can be used to configure IBus:</para>
+ <title>Input Methods</title>
+ <para>
+  Input methods are an operating system component that allows any data, such as
+  keyboard strokes or mouse movements, to be received as input. In this way
+  users can enter characters and symbols not found on their input devices.
+  Using an input method is obligatory for any language that has more graphemes
+  than there are keys on the keyboard.
+ </para>
+ <para>
+  The following input methods are available in NixOS:
+ </para>
+ <itemizedlist>
+  <listitem>
+   <para>
+    IBus: The intelligent input bus.
+   </para>
+  </listitem>
+  <listitem>
+   <para>
+    Fcitx: A customizable lightweight input method.
+   </para>
+  </listitem>
+  <listitem>
+   <para>
+    Nabi: A Korean input method based on XIM.
+   </para>
+  </listitem>
+  <listitem>
+   <para>
+    Uim: The universal input method, is a library with a XIM bridge.
+   </para>
+  </listitem>
+ </itemizedlist>
+ <section xml:id="module-services-input-methods-ibus">
+  <title>IBus</title>
+
+  <para>
+   IBus is an Intelligent Input Bus. It provides full featured and user
+   friendly input method user interface.
+  </para>
+
+  <para>
+   The following snippet can be used to configure IBus:
+  </para>
 
 <programlisting>
 i18n.inputMethod = {
@@ -37,57 +55,89 @@ i18n.inputMethod = {
 };
 </programlisting>
 
-<para><literal>i18n.inputMethod.ibus.engines</literal> is optional and can be
-  used to add extra IBus engines.</para>
-
-<para>Available extra IBus engines are:</para>
-
-<itemizedlist>
-  <listitem><para>Anthy (<literal>ibus-engines.anthy</literal>): Anthy is a
-      system for Japanese input method. It converts Hiragana text to Kana Kanji
-      mixed text.</para></listitem>
-  <listitem><para>Hangul (<literal>ibus-engines.hangul</literal>): Korean input
-      method.</para></listitem>
-  <listitem><para>m17n (<literal>ibus-engines.m17n</literal>): m17n is an input
-      method that uses input methods and corresponding icons in the m17n
-      database.</para></listitem>
-  <listitem><para>mozc (<literal>ibus-engines.mozc</literal>): A Japanese input
-      method from Google.</para></listitem>
-  <listitem><para>Table (<literal>ibus-engines.table</literal>): An input method
-      that load tables of input methods.</para></listitem>
-  <listitem><para>table-others (<literal>ibus-engines.table-others</literal>):
-      Various table-based input methods. To use this, and any other table-based
-      input methods, it must appear in the list of engines along with
-      <literal>table</literal>. For example:
+  <para>
+   <literal>i18n.inputMethod.ibus.engines</literal> is optional and can be used
+   to add extra IBus engines.
+  </para>
+
+  <para>
+   Available extra IBus engines are:
+  </para>
+
+  <itemizedlist>
+   <listitem>
+    <para>
+     Anthy (<literal>ibus-engines.anthy</literal>): Anthy is a system for
+     Japanese input method. It converts Hiragana text to Kana Kanji mixed text.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Hangul (<literal>ibus-engines.hangul</literal>): Korean input method.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     m17n (<literal>ibus-engines.m17n</literal>): m17n is an input method that
+     uses input methods and corresponding icons in the m17n database.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     mozc (<literal>ibus-engines.mozc</literal>): A Japanese input method from
+     Google.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Table (<literal>ibus-engines.table</literal>): An input method that load
+     tables of input methods.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     table-others (<literal>ibus-engines.table-others</literal>): Various
+     table-based input methods. To use this, and any other table-based input
+     methods, it must appear in the list of engines along with
+     <literal>table</literal>. For example:
 <programlisting>
 ibus.engines = with pkgs.ibus-engines; [ table table-others ];
 </programlisting>
-  </para></listitem>
-</itemizedlist>
-
-<para>To use any input method, the package must be added in the configuration,
-  as shown above, and also (after running <literal>nixos-rebuild</literal>) the
-  input method must be added from IBus' preference dialog.</para>
-
-<simplesect xml:id="module-services-input-methods-troubleshooting">
-  <title>Troubleshooting</title>
-  <para>If IBus works in some applications but not others, a likely cause of
-  this is that IBus is depending on a different version of
-  <literal>glib</literal> to what the applications are depending on. This can
-  be checked by running <literal>nix-store -q --requisites &lt;path&gt; | grep
-  glib</literal>, where <literal>&lt;path&gt;</literal> is the path of either
-  IBus or an application in the Nix store. The <literal>glib</literal>
-  packages must match exactly. If they do not, uninstalling and reinstalling
-  the application is a likely fix.</para>
-</simplesect>
-</section>
-
-<section xml:id="module-services-input-methods-fcitx"><title>Fcitx</title>
-
-<para>Fcitx is an input method framework with extension support. It has three
-  built-in Input Method Engine, Pinyin, QuWei and Table-based input
-  methods.</para>
-<para>The following snippet can be used to configure Fcitx:</para>
+    </para>
+   </listitem>
+  </itemizedlist>
+
+  <para>
+   To use any input method, the package must be added in the configuration, as
+   shown above, and also (after running <literal>nixos-rebuild</literal>) the
+   input method must be added from IBus' preference dialog.
+  </para>
+
+  <simplesect xml:id="module-services-input-methods-troubleshooting">
+   <title>Troubleshooting</title>
+   <para>
+    If IBus works in some applications but not others, a likely cause of this
+    is that IBus is depending on a different version of <literal>glib</literal>
+    to what the applications are depending on. This can be checked by running
+    <literal>nix-store -q --requisites &lt;path&gt; | grep glib</literal>,
+    where <literal>&lt;path&gt;</literal> is the path of either IBus or an
+    application in the Nix store. The <literal>glib</literal> packages must
+    match exactly. If they do not, uninstalling and reinstalling the
+    application is a likely fix.
+   </para>
+  </simplesect>
+ </section>
+ <section xml:id="module-services-input-methods-fcitx">
+  <title>Fcitx</title>
+
+  <para>
+   Fcitx is an input method framework with extension support. It has three
+   built-in Input Method Engine, Pinyin, QuWei and Table-based input methods.
+  </para>
+
+  <para>
+   The following snippet can be used to configure Fcitx:
+  </para>
 
 <programlisting>
 i18n.inputMethod = {
@@ -96,51 +146,89 @@ i18n.inputMethod = {
 };
 </programlisting>
 
-<para><literal>i18n.inputMethod.fcitx.engines</literal> is optional and can be
-  used to add extra Fcitx engines.</para>
-
-<para>Available extra Fcitx engines are:</para>
-
-<itemizedlist>
-  <listitem><para>Anthy (<literal>fcitx-engines.anthy</literal>): Anthy is a
-      system for Japanese input method. It converts Hiragana text to Kana Kanji
-      mixed text.</para></listitem>
-  <listitem><para>Chewing (<literal>fcitx-engines.chewing</literal>): Chewing is
-      an intelligent Zhuyin input method. It is one of the most popular input
-      methods among Traditional Chinese Unix users.</para></listitem>
-  <listitem><para>Hangul (<literal>fcitx-engines.hangul</literal>): Korean input
-      method.</para></listitem>
-  <listitem><para>Unikey (<literal>fcitx-engines.unikey</literal>): Vietnamese input
-      method.</para></listitem>
-  <listitem><para>m17n (<literal>fcitx-engines.m17n</literal>): m17n is an input
-      method that uses input methods and corresponding icons in the m17n
-      database.</para></listitem>
-  <listitem><para>mozc (<literal>fcitx-engines.mozc</literal>): A Japanese input
-      method from Google.</para></listitem>
-  <listitem><para>table-others (<literal>fcitx-engines.table-others</literal>):
-      Various table-based input methods.</para></listitem>
-</itemizedlist>
-</section>
-
-<section xml:id="module-services-input-methods-nabi"><title>Nabi</title>
-
-<para>Nabi is an easy to use Korean X input method. It allows you to enter
-  phonetic Korean characters (hangul) and pictographic Korean characters
-  (hanja).</para>
-<para>The following snippet can be used to configure Nabi:</para>
+  <para>
+   <literal>i18n.inputMethod.fcitx.engines</literal> is optional and can be
+   used to add extra Fcitx engines.
+  </para>
+
+  <para>
+   Available extra Fcitx engines are:
+  </para>
+
+  <itemizedlist>
+   <listitem>
+    <para>
+     Anthy (<literal>fcitx-engines.anthy</literal>): Anthy is a system for
+     Japanese input method. It converts Hiragana text to Kana Kanji mixed text.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Chewing (<literal>fcitx-engines.chewing</literal>): Chewing is an
+     intelligent Zhuyin input method. It is one of the most popular input
+     methods among Traditional Chinese Unix users.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Hangul (<literal>fcitx-engines.hangul</literal>): Korean input method.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Unikey (<literal>fcitx-engines.unikey</literal>): Vietnamese input method.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     m17n (<literal>fcitx-engines.m17n</literal>): m17n is an input method that
+     uses input methods and corresponding icons in the m17n database.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     mozc (<literal>fcitx-engines.mozc</literal>): A Japanese input method from
+     Google.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     table-others (<literal>fcitx-engines.table-others</literal>): Various
+     table-based input methods.
+    </para>
+   </listitem>
+  </itemizedlist>
+ </section>
+ <section xml:id="module-services-input-methods-nabi">
+  <title>Nabi</title>
+
+  <para>
+   Nabi is an easy to use Korean X input method. It allows you to enter
+   phonetic Korean characters (hangul) and pictographic Korean characters
+   (hanja).
+  </para>
+
+  <para>
+   The following snippet can be used to configure Nabi:
+  </para>
 
 <programlisting>
 i18n.inputMethod = {
   <link linkend="opt-i18n.inputMethod.enabled">enabled</link> = "nabi";
 };
 </programlisting>
-</section>
+ </section>
+ <section xml:id="module-services-input-methods-uim">
+  <title>Uim</title>
 
-<section xml:id="module-services-input-methods-uim"><title>Uim</title>
+  <para>
+   Uim (short for "universal input method") is a multilingual input method
+   framework. Applications can use it through so-called bridges.
+  </para>
 
-<para>Uim (short for "universal input method") is a multilingual input method
-  framework. Applications can use it through so-called bridges.</para>
-<para>The following snippet can be used to configure uim:</para>
+  <para>
+   The following snippet can be used to configure uim:
+  </para>
 
 <programlisting>
 i18n.inputMethod = {
@@ -148,8 +236,9 @@ i18n.inputMethod = {
 };
 </programlisting>
 
-<para>Note: The <xref linkend="opt-i18n.inputMethod.uim.toolbar"/> option can be
-  used to choose uim toolbar.</para>
-
-</section>
+  <para>
+   Note: The <xref linkend="opt-i18n.inputMethod.uim.toolbar"/> option can be
+   used to choose uim toolbar.
+  </para>
+ </section>
 </chapter>
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix
new file mode 100644
index 000000000000..228ef371d252
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix
@@ -0,0 +1,49 @@
+# This module contains the basic configuration for building a graphical NixOS
+# installation CD.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  imports = [ ./installation-cd-base.nix ];
+
+  services.xserver = {
+    enable = true;
+
+    # Don't start the X server by default.
+    autorun = mkForce false;
+
+    # Automatically login as root.
+    displayManager.slim = {
+      enable = true;
+      defaultUser = "root";
+      autoLogin = true;
+    };
+
+  };
+
+  # Provide networkmanager for easy wireless configuration.
+  networking.networkmanager.enable = true;
+  networking.wireless.enable = mkForce false;
+
+  # KDE complains if power management is disabled (to be precise, if
+  # there is no power management backend such as upower).
+  powerManagement.enable = true;
+
+  environment.systemPackages = [
+    # Include gparted for partitioning disks.
+    pkgs.gparted
+
+    # Include some editors.
+    pkgs.vim
+    pkgs.bvi # binary editor
+    pkgs.joe
+
+    # Firefox for reading the manual.
+    pkgs.firefox
+
+    pkgs.glxinfo
+  ];
+
+}
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
index 4c4e69d60d9c..42b5ec882272 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
@@ -6,47 +6,11 @@
 with lib;
 
 {
-  imports = [ ./installation-cd-base.nix ];
+  imports = [ ./installation-cd-graphical-base.nix ];
 
-  services.xserver = {
-    enable = true;
-    # GDM doesn't start in virtual machines with ISO
-    displayManager.slim = {
-      enable = true;
-      defaultUser = "root";
-      autoLogin = true;
-    };
-    desktopManager.gnome3 = {
-      enable = true;
-      extraGSettingsOverrides = ''
-        [org.gnome.desktop.background]
-        show-desktop-icons=true
-
-        [org.gnome.nautilus.desktop]
-        trash-icon-visible=false
-        volumes-visible=false
-        home-icon-visible=false
-        network-icon-visible=false
-      '';
-
-      extraGSettingsOverridePackages = [ pkgs.gnome3.nautilus ];
-    };
-  };
-
-  environment.systemPackages =
-    [ # Include gparted for partitioning disks.
-      pkgs.gparted
+  services.xserver.desktopManager.gnome3.enable = true;
 
-      # Include some editors.
-      pkgs.vim
-      pkgs.bvi # binary editor
-      pkgs.joe
-
-      pkgs.glxinfo
-    ];
-
-  # Don't start the X server by default.
-  services.xserver.autorun = mkForce false;
+  services.xserver.displayManager.slim.enable = mkForce false;
 
   # Auto-login as root.
   services.xserver.displayManager.gdm.autoLogin = {
@@ -54,25 +18,4 @@ with lib;
     user = "root";
   };
 
-  system.activationScripts.installerDesktop = let
-    # Must be executable
-    desktopFile = pkgs.writeScript "nixos-manual.desktop" ''
-      [Desktop Entry]
-      Version=1.0
-      Type=Link
-      Name=NixOS Manual
-      URL=${config.system.build.manual.manual}/share/doc/nixos/index.html
-      Icon=system-help
-    '';
-
-  # use cp and chmod +x, we must be sure the apps are in the nix store though
-  in ''
-    mkdir -p /root/Desktop
-    ln -sfT ${desktopFile} /root/Desktop/nixos-manual.desktop
-    cp ${pkgs.gnome3.gnome-terminal}/share/applications/gnome-terminal.desktop /root/Desktop/gnome-terminal.desktop
-    chmod a+rx /root/Desktop/gnome-terminal.desktop
-    cp ${pkgs.gparted}/share/applications/gparted.desktop /root/Desktop/gparted.desktop
-    chmod a+rx /root/Desktop/gparted.desktop
-  '';
-
 }
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix
index 63227d573495..1c3c9cb30b41 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix
@@ -1,23 +1,14 @@
 # This module defines a NixOS installation CD that contains X11 and
-# KDE 5.
+# Plasma5.
 
 { config, lib, pkgs, ... }:
 
 with lib;
 
 {
-  imports = [ ./installation-cd-base.nix ];
+  imports = [ ./installation-cd-graphical-base.nix ];
 
   services.xserver = {
-    enable = true;
-
-    # Automatically login as root.
-    displayManager.slim = {
-      enable = true;
-      defaultUser = "root";
-      autoLogin = true;
-    };
-
     desktopManager.plasma5 = {
       enable = true;
       enableQt4Support = false;
@@ -27,45 +18,25 @@ with lib;
     synaptics.enable = true;
   };
 
-  environment.systemPackages =
-    [ pkgs.glxinfo
-
-      # Include gparted for partitioning disks.
-      pkgs.gparted
-
-      # Firefox for reading the manual.
-      pkgs.firefox
-
-      # Include some editors.
-      pkgs.vim
-      pkgs.bvi # binary editor
-      pkgs.joe
-    ];
-
-  # Provide networkmanager for easy wireless configuration.
-  networking.networkmanager.enable = true;
-  networking.wireless.enable = mkForce false;
-
-  # KDE complains if power management is disabled (to be precise, if
-  # there is no power management backend such as upower).
-  powerManagement.enable = true;
-
-  # Don't start the X server by default.
-  services.xserver.autorun = mkForce false;
+  environment.systemPackages = with pkgs; [
+    # Graphical text editor
+    kate
+  ];
 
   system.activationScripts.installerDesktop = let
-    desktopFile = pkgs.writeText "nixos-manual.desktop" ''
+
+    manualDesktopFile = pkgs.writeScript "nixos-manual.desktop" ''
       [Desktop Entry]
       Version=1.0
       Type=Application
       Name=NixOS Manual
-      Exec=firefox ${config.system.build.manual.manual}/share/doc/nixos/index.html
+      Exec=firefox ${config.system.build.manual.manualHTMLIndex}
       Icon=text-html
     '';
 
   in ''
     mkdir -p /root/Desktop
-    ln -sfT ${desktopFile} /root/Desktop/nixos-manual.desktop
+    ln -sfT ${manualDesktopFile} /root/Desktop/nixos-manual.desktop
     ln -sfT ${pkgs.konsole}/share/applications/org.kde.konsole.desktop /root/Desktop/org.kde.konsole.desktop
     ln -sfT ${pkgs.gparted}/share/applications/gparted.desktop /root/Desktop/gparted.desktop
   '';
diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix
index 98712f0759a9..96fdb997b2c0 100644
--- a/nixos/modules/installer/cd-dvd/iso-image.nix
+++ b/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -233,7 +233,7 @@ let
             "
     # Make our own efi program, we can't rely on "grub-install" since it seems to
     # probe for devices, even with --skip-fs-probe.
-    ${pkgs.grub2_efi}/bin/grub-mkimage -o $out/EFI/boot/${if targetArch == "x64" then "bootx64" else "bootx32"}.efi -p /EFI/boot -O ${if targetArch == "x64" then "x86_64" else "i386"}-efi \
+    ${pkgs.grub2_efi}/bin/grub-mkimage -o $out/EFI/boot/${if targetArch == "x64" then "bootx64" else "bootia32"}.efi -p /EFI/boot -O ${if targetArch == "x64" then "x86_64" else "i386"}-efi \
       $MODULES
     cp ${pkgs.grub2_efi}/share/grub/unicode.pf2 $out/EFI/boot/
 
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
index 7ec09acd5919..90a5128c02a5 100644
--- a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
+++ b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
@@ -137,7 +137,7 @@ in
   # Setting vesa, we don't get the nvidia driver, which can't work in arm.
   services.xserver.videoDrivers = [ "vesa" ];
 
-  services.nixosManual.enable = false;
+  documentation.nixos.enable = false;
 
   # Include the firmware for various wireless cards.
   networking.enableRalinkFirmware = true;
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index 6611a6ca0790..adde237c07c9 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,6 +1,6 @@
 {
-  x86_64-linux = "/nix/store/r9i30v8nasafg2851wflg71ln49fw03y-nix-2.1";
-  i686-linux = "/nix/store/dsg3pr7wwrk51f7la9wgby173j18llqh-nix-2.1";
-  aarch64-linux = "/nix/store/m3qgnch4xin21pmd1azas8kkcp9rhkr6-nix-2.1";
-  x86_64-darwin = "/nix/store/n7fvy0k555gwkkdszdkhi3h0aahca8h3-nix-2.1";
+  x86_64-linux = "/nix/store/mxg4bbblxfns96yrz0nalxyiyjl7gj98-nix-2.1.2";
+  i686-linux = "/nix/store/bgjgmbwirx63mwwychpikd7yc4k4lbjv-nix-2.1.2";
+  aarch64-linux = "/nix/store/yi18azn4nwrcwvaiag04jnxc1qs38fy5-nix-2.1.2";
+  x86_64-darwin = "/nix/store/fpivmcck2qpw5plrp599iraw2x9jp18k-nix-2.1.2";
 }
diff --git a/nixos/modules/misc/documentation.nix b/nixos/modules/misc/documentation.nix
index e6ccda5d7f40..6a7105e9cdaa 100644
--- a/nixos/modules/misc/documentation.nix
+++ b/nixos/modules/misc/documentation.nix
@@ -1,8 +1,72 @@
-{ config, lib, pkgs, ... }:
+{ config, lib, pkgs, baseModules, ... }:
 
 with lib;
 
-let cfg = config.documentation; in
+let
+
+  cfg = config.documentation;
+
+  /* For the purpose of generating docs, evaluate options with each derivation
+    in `pkgs` (recursively) replaced by a fake with path "\${pkgs.attribute.path}".
+    It isn't perfect, but it seems to cover a vast majority of use cases.
+    Caveat: even if the package is reached by a different means,
+    the path above will be shown and not e.g. `${config.services.foo.package}`. */
+  manual = import ../../doc/manual rec {
+    inherit pkgs config;
+    version = config.system.nixos.release;
+    revision = "release-${version}";
+    options =
+      let
+        scrubbedEval = evalModules {
+          modules = [ { nixpkgs.localSystem = config.nixpkgs.localSystem; } ] ++ baseModules;
+          args = (config._module.args) // { modules = [ ]; };
+          specialArgs = { pkgs = scrubDerivations "pkgs" pkgs; };
+        };
+        scrubDerivations = namePrefix: pkgSet: mapAttrs
+          (name: value:
+            let wholeName = "${namePrefix}.${name}"; in
+            if isAttrs value then
+              scrubDerivations wholeName value
+              // (optionalAttrs (isDerivation value) { outPath = "\${${wholeName}}"; })
+            else value
+          )
+          pkgSet;
+      in scrubbedEval.options;
+  };
+
+  helpScript = pkgs.writeScriptBin "nixos-help"
+    ''
+      #! ${pkgs.runtimeShell} -e
+      # Finds first executable browser in a colon-separated list.
+      # (see how xdg-open defines BROWSER)
+      browser="$(
+        IFS=: ; for b in $BROWSER; do
+          [ -n "$(type -P "$b" || true)" ] && echo "$b" && break
+        done
+      )"
+      if [ -z "$browser" ]; then
+        browser="$(type -P xdg-open || true)"
+        if [ -z "$browser" ]; then
+          browser="$(type -P w3m || true)"
+          if [ -z "$browser" ]; then
+            echo "$0: unable to start a web browser; please set \$BROWSER"
+            exit 1
+          fi
+        fi
+      fi
+      exec "$browser" ${manual.manualHTMLIndex}
+    '';
+
+  desktopItem = pkgs.makeDesktopItem {
+    name = "nixos-manual";
+    desktopName = "NixOS Manual";
+    genericName = "View NixOS documentation in a web browser";
+    icon = "nix-snowflake";
+    exec = "${helpScript}/bin/nixos-help";
+    categories = "System";
+  };
+
+in
 
 {
 
@@ -66,6 +130,22 @@ let cfg = config.documentation; in
         '';
       };
 
+      nixos.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to install NixOS's own documentation.
+          <itemizedlist>
+          <listitem><para>This includes man pages like
+                    <citerefentry><refentrytitle>configuration.nix</refentrytitle>
+                    <manvolnum>5</manvolnum></citerefentry> if <option>man.enable</option> is
+                    set.</para></listitem>
+          <listitem><para>This includes the HTML manual and the <command>nixos-help</command> command if
+                    <option>doc.enable</option> is set.</para></listitem>
+          </itemizedlist>
+        '';
+      };
+
     };
 
   };
@@ -99,6 +179,21 @@ let cfg = config.documentation; in
       environment.extraOutputsToInstall = [ "doc" ] ++ optional cfg.dev.enable "devdoc";
     })
 
+    (mkIf cfg.nixos.enable {
+      system.build.manual = manual;
+
+      environment.systemPackages = []
+        ++ optional cfg.man.enable manual.manpages
+        ++ optionals cfg.doc.enable ([ manual.manualHTML helpScript ]
+           ++ optionals config.services.xserver.enable [ desktopItem pkgs.nixos-icons ]);
+
+      services.mingetty.helpLine = mkIf cfg.doc.enable (
+          "\nRun `nixos-help` "
+        + optionalString config.services.nixosManual.showManual "or press <Alt-F${toString config.services.nixosManual.ttyNumber}> "
+        + "for the NixOS manual."
+      );
+    })
+
   ]);
 
 }
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 8292cdc995e0..0b4ed6d3b628 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -53,7 +53,7 @@
       tomcat = 16;
       #audio = 17; # unused
       #floppy = 18; # unused
-      #uucp = 19; # unused
+      uucp = 19;
       #lp = 20; # unused
       #proc = 21; # unused
       pulseaudio = 22; # must match `pulseaudio' GID
@@ -289,7 +289,7 @@
       stanchion = 262;
       riak-cs = 263;
       infinoted = 264;
-      # keystone = 265; # unused, removed 2017-12-13
+      sickbeard = 265;
       # glance = 266; # unused, removed 2017-12-13
       couchpotato = 267;
       gogs = 268;
@@ -329,6 +329,8 @@
       # kvm = 302; # unused
       # render = 303; # unused
       zeronet = 304;
+      lirc = 305;
+      lidarr = 306;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -579,7 +581,7 @@
       stanchion = 262;
       riak-cs = 263;
       infinoted = 264;
-      # keystone = 265; # unused, removed 2017-12-13
+      sickbeard = 265;
       # glance = 266; # unused, removed 2017-12-13
       couchpotato = 267;
       gogs = 268;
@@ -618,6 +620,8 @@
       kvm = 302; # default udev rules from systemd requires these
       render = 303; # default udev rules from systemd requires these
       zeronet = 304;
+      lirc = 305;
+      lidarr = 306;
 
       # When adding a gid, make sure it doesn't match an existing
       # uid. Users and groups with the same name should have equal
diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix
index 8fbe218b232a..7f9833e184ab 100644
--- a/nixos/modules/misc/nixpkgs.nix
+++ b/nixos/modules/misc/nixpkgs.nix
@@ -62,12 +62,11 @@ in
     pkgs = mkOption {
       defaultText = literalExample
         ''import "''${nixos}/.." {
-            inherit (config.nixpkgs) config overlays localSystem crossSystem;
+            inherit (cfg) config overlays localSystem crossSystem;
           }
         '';
       default = import ../../.. {
-        localSystem = { inherit (cfg) system; } // cfg.localSystem;
-        inherit (cfg) config overlays crossSystem;
+        inherit (cfg) config overlays localSystem crossSystem;
       };
       type = pkgsType;
       example = literalExample ''import <nixpkgs> {}'';
@@ -140,8 +139,11 @@ in
 
     localSystem = mkOption {
       type = types.attrs; # TODO utilize lib.systems.parsedPlatform
-      default = { system = builtins.currentSystem; };
+      default = { inherit (cfg) system; };
       example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; };
+      # Make sure that the final value has all fields for sake of other modules
+      # referring to this. TODO make `lib.systems` itself use the module system.
+      apply = lib.systems.elaborate;
       defaultText = literalExample
         ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
       description = ''
@@ -180,6 +182,7 @@ in
     system = mkOption {
       type = types.str;
       example = "i686-linux";
+      default = { system = builtins.currentSystem; };
       description = ''
         Specifies the Nix platform type on which NixOS should be built.
         It is better to specify <code>nixpkgs.localSystem</code> instead.
@@ -196,6 +199,7 @@ in
         </programlisting>
         See <code>nixpkgs.localSystem</code> for more information.
 
+        Ignored when <code>nixpkgs.localSystem</code> is set.
         Ignored when <code>nixpkgs.pkgs</code> is set.
       '';
     };
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 4795922abcfb..f4c7cf601bf1 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -245,6 +245,7 @@
   ./services/desktops/gnome3/gnome-user-share.nix
   ./services/desktops/gnome3/gpaste.nix
   ./services/desktops/gnome3/gvfs.nix
+  ./services/desktops/gnome3/rygel.nix
   ./services/desktops/gnome3/seahorse.nix
   ./services/desktops/gnome3/sushi.nix
   ./services/desktops/gnome3/tracker.nix
@@ -271,9 +272,11 @@
   ./services/hardware/interception-tools.nix
   ./services/hardware/irqbalance.nix
   ./services/hardware/lcd.nix
+  ./services/hardware/lirc.nix
   ./services/hardware/nvidia-optimus.nix
   ./services/hardware/pcscd.nix
   ./services/hardware/pommed.nix
+  ./services/hardware/ratbagd.nix
   ./services/hardware/sane.nix
   ./services/hardware/sane_extra_backends/brscan4.nix
   ./services/hardware/tcsd.nix
@@ -362,6 +365,7 @@
   ./services/misc/jackett.nix
   ./services/misc/logkeys.nix
   ./services/misc/leaps.nix
+  ./services/misc/lidarr.nix
   ./services/misc/mantisbt.nix
   ./services/misc/mathics.nix
   ./services/misc/matrix-synapse.nix
@@ -392,6 +396,7 @@
   ./services/misc/rogue.nix
   ./services/misc/serviio.nix
   ./services/misc/safeeyes.nix
+  ./services/misc/sickbeard.nix
   ./services/misc/siproxd.nix
   ./services/misc/snapper.nix
   ./services/misc/sonarr.nix
@@ -406,6 +411,7 @@
   ./services/misc/taskserver
   ./services/misc/tzupdate.nix
   ./services/misc/uhub.nix
+  ./services/misc/weechat.nix
   ./services/misc/xmr-stak.nix
   ./services/misc/zookeeper.nix
   ./services/monitoring/apcupsd.nix
@@ -494,6 +500,7 @@
   ./services/networking/dnsdist.nix
   ./services/networking/dnsmasq.nix
   ./services/networking/ejabberd.nix
+  ./services/networking/epmd.nix
   ./services/networking/fakeroute.nix
   ./services/networking/ferm.nix
   ./services/networking/firefox/sync-server.nix
@@ -515,9 +522,11 @@
   ./services/networking/heyefi.nix
   ./services/networking/hostapd.nix
   ./services/networking/htpdate.nix
+  ./services/networking/hylafax/default.nix
   ./services/networking/i2pd.nix
   ./services/networking/i2p.nix
   ./services/networking/iodine.nix
+  ./services/networking/iperf3.nix
   ./services/networking/ircd-hybrid/default.nix
   ./services/networking/iwd.nix
   ./services/networking/keepalived/default.nix
@@ -552,6 +561,7 @@
   ./services/networking/nsd.nix
   ./services/networking/ntopng.nix
   ./services/networking/ntpd.nix
+  ./services/networking/nullidentdmod.nix
   ./services/networking/nylon.nix
   ./services/networking/ocserv.nix
   ./services/networking/oidentd.nix
@@ -676,6 +686,7 @@
   ./services/web-apps/atlassian/confluence.nix
   ./services/web-apps/atlassian/crowd.nix
   ./services/web-apps/atlassian/jira.nix
+  ./services/web-apps/codimd.nix
   ./services/web-apps/frab.nix
   ./services/web-apps/mattermost.nix
   ./services/web-apps/nexus.nix
diff --git a/nixos/modules/profiles/graphical.nix b/nixos/modules/profiles/graphical.nix
index 332cf58aa538..fba756391b11 100644
--- a/nixos/modules/profiles/graphical.nix
+++ b/nixos/modules/profiles/graphical.nix
@@ -7,9 +7,12 @@
   services.xserver = {
     enable = true;
     displayManager.sddm.enable = true;
-    desktopManager.plasma5.enable = true;
+    desktopManager.plasma5 = {
+      enable = true;
+      enableQt4Support = false;
+    };
     libinput.enable = true; # for touchpad support on many laptops
   };
 
-  environment.systemPackages = [ pkgs.glxinfo ];
+  environment.systemPackages = [ pkgs.glxinfo pkgs.firefox ];
 }
diff --git a/nixos/modules/profiles/installation-device.nix b/nixos/modules/profiles/installation-device.nix
index ff4a23a18d06..d51ed195580d 100644
--- a/nixos/modules/profiles/installation-device.nix
+++ b/nixos/modules/profiles/installation-device.nix
@@ -22,9 +22,10 @@ with lib;
   config = {
 
     # Enable in installer, even if the minimal profile disables it.
-    services.nixosManual.enable = mkForce true;
+    documentation.enable = mkForce true;
 
     # Show the manual.
+    documentation.nixos.enable = mkForce true;
     services.nixosManual.showManual = true;
 
     # Let the user play Rogue on TTY 8 during the installation.
diff --git a/nixos/modules/profiles/minimal.nix b/nixos/modules/profiles/minimal.nix
index ed04e46c77d1..138eda117c74 100644
--- a/nixos/modules/profiles/minimal.nix
+++ b/nixos/modules/profiles/minimal.nix
@@ -12,7 +12,6 @@ with lib;
   i18n.supportedLocales = [ (config.i18n.defaultLocale + "/UTF-8") ];
 
   documentation.enable = mkDefault false;
-  services.nixosManual.enable = mkDefault false;
 
   sound.enable = mkDefault false;
 }
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
index 69a1a482d074..424e1506b4c5 100644
--- a/nixos/modules/programs/bash/bash.nix
+++ b/nixos/modules/programs/bash/bash.nix
@@ -126,7 +126,9 @@ in
     programs.bash = {
 
       shellInit = ''
-        ${config.system.build.setEnvironment.text}
+        if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]; then
+            . ${config.system.build.setEnvironment}
+        fi
 
         ${cfge.shellInit}
       '';
@@ -166,11 +168,11 @@ in
 
         # Read system-wide modifications.
         if test -f /etc/profile.local; then
-          . /etc/profile.local
+            . /etc/profile.local
         fi
 
         if [ -n "''${BASH_VERSION:-}" ]; then
-          . /etc/bashrc
+            . /etc/bashrc
         fi
       '';
 
@@ -191,12 +193,12 @@ in
 
         # We are not always an interactive shell.
         if [ -n "$PS1" ]; then
-          ${cfg.interactiveShellInit}
+            ${cfg.interactiveShellInit}
         fi
 
         # Read system-wide modifications.
         if test -f /etc/bashrc.local; then
-          . /etc/bashrc.local
+            . /etc/bashrc.local
         fi
       '';
 
diff --git a/nixos/modules/programs/dconf.nix b/nixos/modules/programs/dconf.nix
index b7d8a345e65c..9c9765b06b6f 100644
--- a/nixos/modules/programs/dconf.nix
+++ b/nixos/modules/programs/dconf.nix
@@ -32,6 +32,8 @@ in
     environment.etc = optionals (cfg.profiles != {})
       (mapAttrsToList mkDconfProfile cfg.profiles);
 
+    services.dbus.packages = [ pkgs.gnome3.dconf ];
+
     environment.variables.GIO_EXTRA_MODULES = optional cfg.enable
       "${pkgs.gnome3.dconf.lib}/lib/gio/modules";
     # https://github.com/NixOS/nixpkgs/pull/31891
diff --git a/nixos/modules/programs/digitalbitbox/doc.xml b/nixos/modules/programs/digitalbitbox/doc.xml
index a26653dda535..c63201628dbd 100644
--- a/nixos/modules/programs/digitalbitbox/doc.xml
+++ b/nixos/modules/programs/digitalbitbox/doc.xml
@@ -3,75 +3,64 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="module-programs-digitalbitbox">
-
-  <title>Digital Bitbox</title>
-
-  <para>
-    Digital Bitbox is a hardware wallet and second-factor authenticator.
-  </para>
-
-  <para>
-    The <literal>digitalbitbox</literal> programs module may be
-    installed by setting <literal>programs.digitalbitbox</literal>
-    to <literal>true</literal> in a manner similar to
-
+ <title>Digital Bitbox</title>
+ <para>
+  Digital Bitbox is a hardware wallet and second-factor authenticator.
+ </para>
+ <para>
+  The <literal>digitalbitbox</literal> programs module may be installed by
+  setting <literal>programs.digitalbitbox</literal> to <literal>true</literal>
+  in a manner similar to
 <programlisting>
 <xref linkend="opt-programs.digitalbitbox.enable"/> = true;
 </programlisting>
-
-    and bundles the <literal>digitalbitbox</literal> package (see <xref
+  and bundles the <literal>digitalbitbox</literal> package (see
+  <xref
       linkend="sec-digitalbitbox-package" />), which contains the
-    <literal>dbb-app</literal> and <literal>dbb-cli</literal> binaries,
-    along with the hardware module (see <xref
+  <literal>dbb-app</literal> and <literal>dbb-cli</literal> binaries, along
+  with the hardware module (see
+  <xref
       linkend="sec-digitalbitbox-hardware-module" />) which sets up the
-    necessary udev rules to access the device.
-  </para>
-
-  <para>
-    Enabling the digitalbitbox module is pretty much the easiest way to
-    get a Digital Bitbox device working on your system.
-  </para>
+  necessary udev rules to access the device.
+ </para>
+ <para>
+  Enabling the digitalbitbox module is pretty much the easiest way to get a
+  Digital Bitbox device working on your system.
+ </para>
+ <para>
+  For more information, see
+  <link xlink:href="https://digitalbitbox.com/start_linux" />.
+ </para>
+ <section xml:id="sec-digitalbitbox-package">
+  <title>Package</title>
 
   <para>
-    For more information, see
-    <link xlink:href="https://digitalbitbox.com/start_linux" />.
-  </para>
-
-  <section xml:id="sec-digitalbitbox-package">
-    <title>Package</title>
-
-    <para>
-      The binaries, <literal>dbb-app</literal> (a GUI tool) and
-      <literal>dbb-cli</literal> (a CLI tool), are available through the
-      <literal>digitalbitbox</literal> package which could be installed
-      as follows:
-
+   The binaries, <literal>dbb-app</literal> (a GUI tool) and
+   <literal>dbb-cli</literal> (a CLI tool), are available through the
+   <literal>digitalbitbox</literal> package which could be installed as
+   follows:
 <programlisting>
 <xref linkend="opt-environment.systemPackages"/> = [
   pkgs.digitalbitbox
 ];
 </programlisting>
-    </para>
-  </section>
-
-
-  <section xml:id="sec-digitalbitbox-hardware-module">
-    <title>Hardware</title>
-
-    <para>
-      The digitalbitbox hardware package enables the udev rules for
-      Digital Bitbox devices and may be installed as follows:
+  </para>
+ </section>
+ <section xml:id="sec-digitalbitbox-hardware-module">
+  <title>Hardware</title>
 
+  <para>
+   The digitalbitbox hardware package enables the udev rules for Digital Bitbox
+   devices and may be installed as follows:
 <programlisting>
 <xref linkend="opt-hardware.digitalbitbox.enable"/> = true;
 </programlisting>
-    </para>
-
-    <para>
-      In order to alter the udev rules, one may provide different values for
-      the <literal>udevRule51</literal> and <literal>udevRule52</literal>
-      attributes by means of overriding as follows:
+  </para>
 
+  <para>
+   In order to alter the udev rules, one may provide different values for the
+   <literal>udevRule51</literal> and <literal>udevRule52</literal> attributes
+   by means of overriding as follows:
 <programlisting>
 programs.digitalbitbox = {
   <link linkend="opt-programs.digitalbitbox.enable">enable</link> = true;
@@ -80,6 +69,6 @@ programs.digitalbitbox = {
   };
 };
 </programlisting>
-    </para>
-  </section>
+  </para>
+ </section>
 </chapter>
diff --git a/nixos/modules/programs/fish.nix b/nixos/modules/programs/fish.nix
index c8d94a47be28..c3f742acde2e 100644
--- a/nixos/modules/programs/fish.nix
+++ b/nixos/modules/programs/fish.nix
@@ -27,7 +27,7 @@ in
         '';
         type = types.bool;
       };
-      
+
       vendor.config.enable = mkOption {
         type = types.bool;
         default = true;
@@ -43,7 +43,7 @@ in
           Whether fish should use completion files provided by other packages.
         '';
       };
-      
+
       vendor.functions.enable = mkOption {
         type = types.bool;
         default = true;
@@ -107,9 +107,11 @@ in
       # This happens before $__fish_datadir/config.fish sets fish_function_path, so it is currently
       # unset. We set it and then completely erase it, leaving its configuration to $__fish_datadir/config.fish
       set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $__fish_datadir/functions
-      
+
       # source the NixOS environment config
-      fenv source ${config.system.build.setEnvironment}
+      if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
+          fenv source ${config.system.build.setEnvironment}
+      end
 
       # clear fish_function_path so that it will be correctly set when we return to $__fish_datadir/config.fish
       set -e fish_function_path
@@ -123,7 +125,7 @@ in
         set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $fish_function_path
         fenv source /etc/fish/foreign-env/shellInit > /dev/null
         set -e fish_function_path[1]
-        
+
         ${cfg.shellInit}
 
         # and leave a note so we don't source this config section again from
@@ -137,7 +139,7 @@ in
         set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $fish_function_path
         fenv source /etc/fish/foreign-env/loginShellInit > /dev/null
         set -e fish_function_path[1]
-        
+
         ${cfg.loginShellInit}
 
         # and leave a note so we don't source this config section again from
@@ -149,12 +151,11 @@ in
       status --is-interactive; and not set -q __fish_nixos_interactive_config_sourced
       and begin
         ${fishAliases}
-        
 
         set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $fish_function_path
         fenv source /etc/fish/foreign-env/interactiveShellInit > /dev/null
         set -e fish_function_path[1]
-        
+
         ${cfg.promptInit}
         ${cfg.interactiveShellInit}
 
@@ -170,7 +171,7 @@ in
       ++ optional cfg.vendor.config.enable "/share/fish/vendor_conf.d"
       ++ optional cfg.vendor.completions.enable "/share/fish/vendor_completions.d"
       ++ optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d";
-    
+
     environment.systemPackages = [ pkgs.fish ];
 
     environment.shells = [
diff --git a/nixos/modules/programs/plotinus.xml b/nixos/modules/programs/plotinus.xml
index 91740ee16ec2..902cd89e0c49 100644
--- a/nixos/modules/programs/plotinus.xml
+++ b/nixos/modules/programs/plotinus.xml
@@ -3,23 +3,28 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="module-program-plotinus">
-
-<title>Plotinus</title>
-
-<para><emphasis>Source:</emphasis> <filename>modules/programs/plotinus.nix</filename></para>
-
-<para><emphasis>Upstream documentation:</emphasis> <link xlink:href="https://github.com/p-e-w/plotinus"/></para>
-
-<para>Plotinus is a searchable command palette in every modern GTK+ application.</para>
-
-<para>When in a GTK+3 application and Plotinus is enabled, you can press <literal>Ctrl+Shift+P</literal> to open the command palette.  The command palette provides a searchable list of of all menu items in the application.</para>
-
-<para>To enable Plotinus, add the following to your <filename>configuration.nix</filename>:
-
+ <title>Plotinus</title>
+ <para>
+  <emphasis>Source:</emphasis>
+  <filename>modules/programs/plotinus.nix</filename>
+ </para>
+ <para>
+  <emphasis>Upstream documentation:</emphasis>
+  <link xlink:href="https://github.com/p-e-w/plotinus"/>
+ </para>
+ <para>
+  Plotinus is a searchable command palette in every modern GTK+ application.
+ </para>
+ <para>
+  When in a GTK+3 application and Plotinus is enabled, you can press
+  <literal>Ctrl+Shift+P</literal> to open the command palette. The command
+  palette provides a searchable list of of all menu items in the application.
+ </para>
+ <para>
+  To enable Plotinus, add the following to your
+  <filename>configuration.nix</filename>:
 <programlisting>
 <xref linkend="opt-programs.plotinus.enable"/> = true;
 </programlisting>
-
-</para>
-
+ </para>
 </chapter>
diff --git a/nixos/modules/programs/yabar.nix b/nixos/modules/programs/yabar.nix
index a01083c3ace9..db085211366e 100644
--- a/nixos/modules/programs/yabar.nix
+++ b/nixos/modules/programs/yabar.nix
@@ -44,10 +44,23 @@ in
       enable = mkEnableOption "yabar";
 
       package = mkOption {
-        default = pkgs.yabar;
-        example = literalExample "pkgs.yabar-unstable";
+        default = pkgs.yabar-unstable;
+        example = literalExample "pkgs.yabar";
         type = types.package;
 
+        # `yabar-stable` segfaults under certain conditions.
+        apply = x: if x == pkgs.yabar-unstable then x else flip warn x ''
+          It's not recommended to use `yabar' with `programs.yabar', the (old) stable release
+          tends to segfault under certain circumstances:
+
+          * https://github.com/geommer/yabar/issues/86
+          * https://github.com/geommer/yabar/issues/68
+          * https://github.com/geommer/yabar/issues/143
+
+          Most of them don't occur on master anymore, until a new release is published, it's recommended
+          to use `yabar-unstable'.
+        '';
+
         description = ''
           The package which contains the `yabar` binary.
 
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.xml b/nixos/modules/programs/zsh/oh-my-zsh.xml
index 6567d4a42fac..5cf690c1a560 100644
--- a/nixos/modules/programs/zsh/oh-my-zsh.xml
+++ b/nixos/modules/programs/zsh/oh-my-zsh.xml
@@ -3,18 +3,20 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="module-programs-zsh-ohmyzsh">
-
-<title>Oh my ZSH</title>
-
-<para><literal><link xlink:href="https://ohmyz.sh/">oh-my-zsh</link></literal> is a framework
-to manage your <link xlink:href="https://www.zsh.org/">ZSH</link> configuration
-including completion scripts for several CLI tools or custom prompt themes.</para>
-
-<section xml:id="module-programs-oh-my-zsh-usage"><title>Basic usage</title>
-<para>The module uses the <literal>oh-my-zsh</literal> package with all available features.  The
-initial setup using Nix expressions is fairly similar to the configuration format
-of <literal>oh-my-zsh</literal>.
-
+ <title>Oh my ZSH</title>
+ <para>
+  <literal><link xlink:href="https://ohmyz.sh/">oh-my-zsh</link></literal> is a
+  framework to manage your <link xlink:href="https://www.zsh.org/">ZSH</link>
+  configuration including completion scripts for several CLI tools or custom
+  prompt themes.
+ </para>
+ <section xml:id="module-programs-oh-my-zsh-usage">
+  <title>Basic usage</title>
+
+  <para>
+   The module uses the <literal>oh-my-zsh</literal> package with all available
+   features. The initial setup using Nix expressions is fairly similar to the
+   configuration format of <literal>oh-my-zsh</literal>.
 <programlisting>
 {
   programs.ohMyZsh = {
@@ -24,39 +26,50 @@ of <literal>oh-my-zsh</literal>.
   };
 }
 </programlisting>
-
-For a detailed explanation of these arguments please refer to the
-<link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki"><literal>oh-my-zsh</literal> docs</link>.
-</para>
-<para>The expression generates the needed
-configuration and writes it into your <literal>/etc/zshrc</literal>.
-</para></section>
-
-<section xml:id="module-programs-oh-my-zsh-additions"><title>Custom additions</title>
-
-<para>Sometimes third-party or custom scripts such as a modified theme may be needed.
-<literal>oh-my-zsh</literal> provides the
-<link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki/Customization#overriding-internals"><literal>ZSH_CUSTOM</literal></link> 
-environment variable for this which points to a directory with additional scripts.</para>
-
-<para>The module can do this as well:
-
+   For a detailed explanation of these arguments please refer to the
+   <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki"><literal>oh-my-zsh</literal>
+   docs</link>.
+  </para>
+
+  <para>
+   The expression generates the needed configuration and writes it into your
+   <literal>/etc/zshrc</literal>.
+  </para>
+ </section>
+ <section xml:id="module-programs-oh-my-zsh-additions">
+  <title>Custom additions</title>
+
+  <para>
+   Sometimes third-party or custom scripts such as a modified theme may be
+   needed. <literal>oh-my-zsh</literal> provides the
+   <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki/Customization#overriding-internals"><literal>ZSH_CUSTOM</literal></link>
+   environment variable for this which points to a directory with additional
+   scripts.
+  </para>
+
+  <para>
+   The module can do this as well:
 <programlisting>
 {
   programs.ohMyZsh.custom = "~/path/to/custom/scripts";
 }
 </programlisting>
-</para></section>
-
-<section xml:id="module-programs-oh-my-zsh-environments"><title>Custom environments</title>
-
-<para>There are several extensions for <literal>oh-my-zsh</literal> packaged in <literal>nixpkgs</literal>.
-One of them is <link xlink:href="https://github.com/spwhitt/nix-zsh-completions">nix-zsh-completions</link>
-which bundles completion scripts and a plugin for <literal>oh-my-zsh</literal>.</para>
-
-<para>Rather than using a single mutable path for <literal>ZSH_CUSTOM</literal>, it's also possible to
-generate this path from a list of Nix packages:
-
+  </para>
+ </section>
+ <section xml:id="module-programs-oh-my-zsh-environments">
+  <title>Custom environments</title>
+
+  <para>
+   There are several extensions for <literal>oh-my-zsh</literal> packaged in
+   <literal>nixpkgs</literal>. One of them is
+   <link xlink:href="https://github.com/spwhitt/nix-zsh-completions">nix-zsh-completions</link>
+   which bundles completion scripts and a plugin for
+   <literal>oh-my-zsh</literal>.
+  </para>
+
+  <para>
+   Rather than using a single mutable path for <literal>ZSH_CUSTOM</literal>,
+   it's also possible to generate this path from a list of Nix packages:
 <programlisting>
 { pkgs, ... }:
 {
@@ -66,42 +79,59 @@ generate this path from a list of Nix packages:
   ];
 }
 </programlisting>
-
-Internally a single store path will be created using <literal>buildEnv</literal>.
-Please refer to the docs of
-<link xlink:href="https://nixos.org/nixpkgs/manual/#sec-building-environment"><literal>buildEnv</literal></link>
-for further reference.</para>
-
-<para><emphasis>Please keep in mind that this is not compatible with <literal>programs.ohMyZsh.custom</literal>
-as it requires an immutable store path while <literal>custom</literal> shall remain mutable! An evaluation failure
-will be thrown if both <literal>custom</literal> and <literal>customPkgs</literal> are set.</emphasis>
-</para></section>
-
-<section xml:id="module-programs-oh-my-zsh-packaging-customizations"><title>Package your own customizations</title>
-
-<para>If third-party customizations (e.g. new themes) are supposed to be added to <literal>oh-my-zsh</literal>
-there are several pitfalls to keep in mind:</para>
-
-<itemizedlist>
-  <listitem>
-    <para>To comply with the default structure of <literal>ZSH</literal> the entire output needs to be written to
-    <literal>$out/share/zsh.</literal></para>
-  </listitem>
-  <listitem>
-    <para>Completion scripts are supposed to be stored at <literal>$out/share/zsh/site-functions</literal>. This directory
-    is part of the <literal><link xlink:href="http://zsh.sourceforge.net/Doc/Release/Functions.html">fpath</link></literal>
-    and the package should be compatible with pure <literal>ZSH</literal> setups. The module will automatically link
-    the contents of <literal>site-functions</literal> to completions directory in the proper store path.</para>
-  </listitem>
-  <listitem>
-    <para>The <literal>plugins</literal> directory needs the structure <literal>pluginname/pluginname.plugin.zsh</literal>
-    as structured in the <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/tree/91b771914bc7c43dd7c7a43b586c5de2c225ceb7/plugins">upstream repo.</link>
+   Internally a single store path will be created using
+   <literal>buildEnv</literal>. Please refer to the docs of
+   <link xlink:href="https://nixos.org/nixpkgs/manual/#sec-building-environment"><literal>buildEnv</literal></link>
+   for further reference.
+  </para>
+
+  <para>
+   <emphasis>Please keep in mind that this is not compatible with
+   <literal>programs.ohMyZsh.custom</literal> as it requires an immutable store
+   path while <literal>custom</literal> shall remain mutable! An evaluation
+   failure will be thrown if both <literal>custom</literal> and
+   <literal>customPkgs</literal> are set.</emphasis>
+  </para>
+ </section>
+ <section xml:id="module-programs-oh-my-zsh-packaging-customizations">
+  <title>Package your own customizations</title>
+
+  <para>
+   If third-party customizations (e.g. new themes) are supposed to be added to
+   <literal>oh-my-zsh</literal> there are several pitfalls to keep in mind:
+  </para>
+
+  <itemizedlist>
+   <listitem>
+    <para>
+     To comply with the default structure of <literal>ZSH</literal> the entire
+     output needs to be written to <literal>$out/share/zsh.</literal>
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Completion scripts are supposed to be stored at
+     <literal>$out/share/zsh/site-functions</literal>. This directory is part
+     of the
+     <literal><link xlink:href="http://zsh.sourceforge.net/Doc/Release/Functions.html">fpath</link></literal>
+     and the package should be compatible with pure <literal>ZSH</literal>
+     setups. The module will automatically link the contents of
+     <literal>site-functions</literal> to completions directory in the proper
+     store path.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     The <literal>plugins</literal> directory needs the structure
+     <literal>pluginname/pluginname.plugin.zsh</literal> as structured in the
+     <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/tree/91b771914bc7c43dd7c7a43b586c5de2c225ceb7/plugins">upstream
+     repo.</link>
     </para>
-  </listitem>
-</itemizedlist>
+   </listitem>
+  </itemizedlist>
 
-<para>
-A derivation for <literal>oh-my-zsh</literal> may look like this:
+  <para>
+   A derivation for <literal>oh-my-zsh</literal> may look like this:
 <programlisting>
 { stdenv, fetchFromGitHub }:
 
@@ -120,6 +150,6 @@ stdenv.mkDerivation rec {
   '';
 }
 </programlisting>
-</para>
-</section>
+  </para>
+ </section>
 </chapter>
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index d30b3415411f..865d148c1629 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -11,7 +11,7 @@ let
   cfg = config.programs.zsh;
 
   zshAliases = concatStringsSep "\n" (
-    mapAttrsFlatten (k: v: "alias ${k}='${v}'") cfg.shellAliases
+    mapAttrsFlatten (k: v: "alias ${k}=${escapeShellArg v}") cfg.shellAliases
   );
 
 in
@@ -70,7 +70,7 @@ in
       promptInit = mkOption {
         default = ''
           if [ "$TERM" != dumb ]; then
-            autoload -U promptinit && promptinit && prompt walters
+              autoload -U promptinit && promptinit && prompt walters
           fi
         '';
         description = ''
@@ -116,7 +116,9 @@ in
         if [ -n "$__ETC_ZSHENV_SOURCED" ]; then return; fi
         export __ETC_ZSHENV_SOURCED=1
 
-        ${config.system.build.setEnvironment.text}
+        if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]; then
+            . ${config.system.build.setEnvironment}
+        fi
 
         ${cfge.shellInit}
 
@@ -124,7 +126,7 @@ in
 
         # Read system-wide modifications.
         if test -f /etc/zshenv.local; then
-          . /etc/zshenv.local
+            . /etc/zshenv.local
         fi
       '';
 
@@ -143,7 +145,7 @@ in
 
         # Read system-wide modifications.
         if test -f /etc/zprofile.local; then
-          . /etc/zprofile.local
+            . /etc/zprofile.local
         fi
       '';
 
@@ -169,7 +171,7 @@ in
 
         # 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)
+            fpath+=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions)
         done
 
         ${optionalString cfg.enableGlobalCompInit "autoload -U compinit && compinit"}
@@ -184,7 +186,7 @@ in
 
         # Read system-wide modifications.
         if test -f /etc/zshrc.local; then
-          . /etc/zshrc.local
+            . /etc/zshrc.local
         fi
       '';
 
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 4a6bdfe83dd2..eb74b9bcac12 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -39,6 +39,7 @@ with lib;
     (mkRenamedOptionModule [ "services" "kubernetes" "apiserver" "address" ] ["services" "kubernetes" "apiserver" "bindAddress"])
     (mkRemovedOptionModule [ "services" "kubernetes" "apiserver" "publicAddress" ] "")
     (mkRenamedOptionModule [ "services" "kubernetes" "addons" "dashboard" "enableRBAC" ] [ "services" "kubernetes" "addons" "dashboard" "rbac" "enable" ])
+    (mkRemovedOptionModule [ "services" "kubernetes" "kubelet" "cadvisorPort" ] "")
     (mkRenamedOptionModule [ "services" "logstash" "address" ] [ "services" "logstash" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "mpd" "network" "host" ] [ "services" "mpd" "network" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "neo4j" "host" ] [ "services" "neo4j" "defaultListenAddress" ])
@@ -276,6 +277,7 @@ with lib;
 
     (mkRenamedOptionModule [ "programs" "info" "enable" ] [ "documentation" "info" "enable" ])
     (mkRenamedOptionModule [ "programs" "man"  "enable" ] [ "documentation" "man"  "enable" ])
+    (mkRenamedOptionModule [ "services" "nixosManual" "enable" ] [ "documentation" "nixos" "enable" ])
 
   ] ++ (flip map [ "blackboxExporter" "collectdExporter" "fritzboxExporter"
                    "jsonExporter" "minioExporter" "nginxExporter" "nodeExporter"
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index 946da92d80e7..092704c6fc3f 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -302,15 +302,15 @@ in
                       workdir="$(mktemp -d)"
 
                       # Create CA
-                      openssl genrsa -des3 -passout pass:x -out $workdir/ca.pass.key 2048
-                      openssl rsa -passin pass:x -in $workdir/ca.pass.key -out $workdir/ca.key
+                      openssl genrsa -des3 -passout pass:xxxx -out $workdir/ca.pass.key 2048
+                      openssl rsa -passin pass:xxxx -in $workdir/ca.pass.key -out $workdir/ca.key
                       openssl req -new -key $workdir/ca.key -out $workdir/ca.csr \
                         -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=Security Department/CN=example.com"
                       openssl x509 -req -days 1 -in $workdir/ca.csr -signkey $workdir/ca.key -out $workdir/ca.crt
 
                       # Create key
-                      openssl genrsa -des3 -passout pass:x -out $workdir/server.pass.key 2048
-                      openssl rsa -passin pass:x -in $workdir/server.pass.key -out $workdir/server.key
+                      openssl genrsa -des3 -passout pass:xxxx -out $workdir/server.pass.key 2048
+                      openssl rsa -passin pass:xxxx -in $workdir/server.pass.key -out $workdir/server.key
                       openssl req -new -key $workdir/server.key -out $workdir/server.csr \
                         -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=IT Department/CN=example.com"
                       openssl x509 -req -days 1 -in $workdir/server.csr -CA $workdir/ca.crt \
diff --git a/nixos/modules/security/acme.xml b/nixos/modules/security/acme.xml
index b4cd83f6632c..ef71fe53d0c7 100644
--- a/nixos/modules/security/acme.xml
+++ b/nixos/modules/security/acme.xml
@@ -3,23 +3,25 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="module-security-acme">
-
-<title>SSL/TLS Certificates with ACME</title>
-
-<para>NixOS supports automatic domain validation &amp; certificate
-retrieval and renewal using the ACME protocol. This is currently only
-implemented by and for Let's Encrypt. The alternative ACME client
-<literal>simp_le</literal> is used under the hood.</para>
-
-<section xml:id="module-security-acme-prerequisites"><title>Prerequisites</title>
-
-<para>You need to have a running HTTP server for verification. The server must
-have a webroot defined that can serve
-<filename>.well-known/acme-challenge</filename>. This directory must be
-writeable by the user that will run the ACME client.</para>
-
-<para>For instance, this generic snippet could be used for Nginx:
-
+ <title>SSL/TLS Certificates with ACME</title>
+ <para>
+  NixOS supports automatic domain validation &amp; certificate retrieval and
+  renewal using the ACME protocol. This is currently only implemented by and
+  for Let's Encrypt. The alternative ACME client <literal>simp_le</literal> is
+  used under the hood.
+ </para>
+ <section xml:id="module-security-acme-prerequisites">
+  <title>Prerequisites</title>
+
+  <para>
+   You need to have a running HTTP server for verification. The server must
+   have a webroot defined that can serve
+   <filename>.well-known/acme-challenge</filename>. This directory must be
+   writeable by the user that will run the ACME client.
+  </para>
+
+  <para>
+   For instance, this generic snippet could be used for Nginx:
 <programlisting>
 http {
   server {
@@ -37,43 +39,47 @@ http {
   }
 }
 </programlisting>
-</para>
-
-</section>
-
-<section xml:id="module-security-acme-configuring"><title>Configuring</title>
-
-<para>To enable ACME certificate retrieval &amp; renewal for a certificate for
-<literal>foo.example.com</literal>, add the following in your
-<filename>configuration.nix</filename>:
-
+  </para>
+ </section>
+ <section xml:id="module-security-acme-configuring">
+  <title>Configuring</title>
+
+  <para>
+   To enable ACME certificate retrieval &amp; renewal for a certificate for
+   <literal>foo.example.com</literal>, add the following in your
+   <filename>configuration.nix</filename>:
 <programlisting>
 <xref linkend="opt-security.acme.certs"/>."foo.example.com" = {
   <link linkend="opt-security.acme.certs._name_.webroot">webroot</link> = "/var/www/challenges";
   <link linkend="opt-security.acme.certs._name_.email">email</link> = "foo@example.com";
 };
 </programlisting>
-</para>
-
-<para>The private key <filename>key.pem</filename> and certificate
-<filename>fullchain.pem</filename> will be put into
-<filename>/var/lib/acme/foo.example.com</filename>. The target directory can
-be configured with the option <xref linkend="opt-security.acme.directory"/>.
-</para>
-
-<para>Refer to <xref linkend="ch-options" /> for all available configuration
-options for the <link linkend="opt-security.acme.certs">security.acme</link> module.</para>
-
-</section>
-
-<section xml:id="module-security-acme-nginx"><title>Using ACME certificates in Nginx</title>
-<para>NixOS supports fetching ACME certificates for you by setting
-  <literal><link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link> = true;</literal> in a virtualHost config. We
-first create self-signed placeholder certificates in place of the
-real ACME certs. The placeholder certs are overwritten when the ACME
-certs arrive. For <literal>foo.example.com</literal> the config would
-look like.
-</para>
+  </para>
+
+  <para>
+   The private key <filename>key.pem</filename> and certificate
+   <filename>fullchain.pem</filename> will be put into
+   <filename>/var/lib/acme/foo.example.com</filename>. The target directory can
+   be configured with the option <xref linkend="opt-security.acme.directory"/>.
+  </para>
+
+  <para>
+   Refer to <xref linkend="ch-options" /> for all available configuration
+   options for the <link linkend="opt-security.acme.certs">security.acme</link>
+   module.
+  </para>
+ </section>
+ <section xml:id="module-security-acme-nginx">
+  <title>Using ACME certificates in Nginx</title>
+
+  <para>
+   NixOS supports fetching ACME certificates for you by setting
+   <literal><link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link>
+   = true;</literal> in a virtualHost config. We first create self-signed
+   placeholder certificates in place of the real ACME certs. The placeholder
+   certs are overwritten when the ACME certs arrive. For
+   <literal>foo.example.com</literal> the config would look like.
+  </para>
 
 <programlisting>
 services.nginx = {
@@ -89,5 +95,5 @@ services.nginx = {
   };
 }
 </programlisting>
-</section>
+ </section>
 </chapter>
diff --git a/nixos/modules/security/hidepid.xml b/nixos/modules/security/hidepid.xml
index d69341eb3cde..5a17cb1da412 100644
--- a/nixos/modules/security/hidepid.xml
+++ b/nixos/modules/security/hidepid.xml
@@ -3,31 +3,26 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="sec-hidepid">
-
-  <title>Hiding process information</title>
-
-  <para>
-    Setting
+ <title>Hiding process information</title>
+ <para>
+  Setting
 <programlisting>
 <xref linkend="opt-security.hideProcessInformation"/> = true;
 </programlisting>
-    ensures that access to process information is restricted to the
-    owning user.  This implies, among other things, that command-line
-    arguments remain private.  Unless your deployment relies on unprivileged
-    users being able to inspect the process information of other users, this
-    option should be safe to enable.
-  </para>
-
-  <para>
-    Members of the <literal>proc</literal> group are exempt from process
-    information hiding.
-  </para>
-
-  <para>
-    To allow a service <replaceable>foo</replaceable> to run without process information hiding, set
+  ensures that access to process information is restricted to the owning user.
+  This implies, among other things, that command-line arguments remain private.
+  Unless your deployment relies on unprivileged users being able to inspect the
+  process information of other users, this option should be safe to enable.
+ </para>
+ <para>
+  Members of the <literal>proc</literal> group are exempt from process
+  information hiding.
+ </para>
+ <para>
+  To allow a service <replaceable>foo</replaceable> to run without process
+  information hiding, set
 <programlisting>
 <link linkend="opt-systemd.services._name_.serviceConfig">systemd.services.<replaceable>foo</replaceable>.serviceConfig</link>.SupplementaryGroups = [ "proc" ];
 </programlisting>
-  </para>
-
+ </para>
 </chapter>
diff --git a/nixos/modules/services/amqp/rabbitmq.nix b/nixos/modules/services/amqp/rabbitmq.nix
index bb6fc0a104df..c6878dd67dbf 100644
--- a/nixos/modules/services/amqp/rabbitmq.nix
+++ b/nixos/modules/services/amqp/rabbitmq.nix
@@ -4,14 +4,18 @@ with lib;
 
 let
   cfg = config.services.rabbitmq;
-  config_file = pkgs.writeText "rabbitmq.config" cfg.config;
-  config_file_wo_suffix = builtins.substring 0 ((builtins.stringLength config_file) - 7) config_file;
+
+  inherit (builtins) concatStringsSep;
+
+  config_file_content = lib.generators.toKeyValue {} cfg.configItems;
+  config_file = pkgs.writeText "rabbitmq.conf" config_file_content;
+
+  advanced_config_file = pkgs.writeText "advanced.config" cfg.config;
 
 in {
   ###### interface
   options = {
     services.rabbitmq = {
-
       enable = mkOption {
         default = false;
         description = ''
@@ -20,6 +24,15 @@ in {
         '';
       };
 
+      package = mkOption {
+        default = pkgs.rabbitmq-server;
+        type = types.package;
+        defaultText = "pkgs.rabbitmq-server";
+        description = ''
+          Which rabbitmq package to use.
+        '';
+      };
+
       listenAddress = mkOption {
         default = "127.0.0.1";
         example = "";
@@ -30,6 +43,10 @@ in {
           <literal>guest</literal> with password
           <literal>guest</literal> by default, so you should delete
           this user if you intend to allow external access.
+
+          Together with 'port' setting it's mostly an alias for
+          configItems."listeners.tcp.1" and it's left for backwards
+          compatibility with previous version of this module.
         '';
         type = types.str;
       };
@@ -60,11 +77,29 @@ in {
         '';
       };
 
+      configItems = mkOption {
+        default = {};
+        type = types.attrsOf types.str;
+        example = ''
+          {
+            "auth_backends.1.authn" = "rabbit_auth_backend_ldap";
+            "auth_backends.1.authz" = "rabbit_auth_backend_internal";
+          }
+        '';
+        description = ''
+          New style config options.
+
+          See http://www.rabbitmq.com/configure.html
+        '';
+      };
+
       config = mkOption {
         default = "";
         type = types.str;
         description = ''
-          Verbatim configuration file contents.
+          Verbatim advanced configuration file contents.
+          Prefered way is to use configItems.
+
           See http://www.rabbitmq.com/configure.html
         '';
       };
@@ -74,6 +109,12 @@ in {
         type = types.listOf types.str;
         description = "The names of plugins to enable";
       };
+
+      pluginDirs = mkOption {
+        default = [];
+        type = types.listOf types.path;
+        description = "The list of directories containing external plugins";
+      };
     };
   };
 
@@ -81,7 +122,10 @@ in {
   ###### implementation
   config = mkIf cfg.enable {
 
-    environment.systemPackages = [ pkgs.rabbitmq_server ];
+    # This is needed so we will have 'rabbitmqctl' in our PATH
+    environment.systemPackages = [ cfg.package ];
+
+    services.epmd.enable = true;
 
     users.users.rabbitmq = {
       description = "RabbitMQ server user";
@@ -93,44 +137,54 @@ in {
 
     users.groups.rabbitmq.gid = config.ids.gids.rabbitmq;
 
+    services.rabbitmq.configItems = {
+      "listeners.tcp.1" = mkDefault "${cfg.listenAddress}:${toString cfg.port}";
+    };
+
     systemd.services.rabbitmq = {
       description = "RabbitMQ Server";
 
       wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
+      after = [ "network.target" "epmd.socket" ];
+      wants = [ "network.target" "epmd.socket" ];
 
-      path = [ pkgs.rabbitmq_server pkgs.procps ];
+      path = [ cfg.package pkgs.procps ];
 
       environment = {
         RABBITMQ_MNESIA_BASE = "${cfg.dataDir}/mnesia";
-        RABBITMQ_NODE_IP_ADDRESS = cfg.listenAddress;
-        RABBITMQ_NODE_PORT = toString cfg.port;
         RABBITMQ_LOGS = "-";
-        RABBITMQ_SASL_LOGS = "-";
-        RABBITMQ_PID_FILE = "${cfg.dataDir}/pid";
         SYS_PREFIX = "";
+        RABBITMQ_CONFIG_FILE = config_file;
+        RABBITMQ_PLUGINS_DIR = concatStringsSep ":" cfg.pluginDirs;
         RABBITMQ_ENABLED_PLUGINS_FILE = pkgs.writeText "enabled_plugins" ''
           [ ${concatStringsSep "," cfg.plugins} ].
         '';
-      } //  optionalAttrs (cfg.config != "") { RABBITMQ_CONFIG_FILE = config_file_wo_suffix; };
+      } //  optionalAttrs (cfg.config != "") { RABBITMQ_ADVANCED_CONFIG_FILE = advanced_config_file; };
 
       serviceConfig = {
-        ExecStart = "${pkgs.rabbitmq_server}/sbin/rabbitmq-server";
-        ExecStop = "${pkgs.rabbitmq_server}/sbin/rabbitmqctl stop";
+        PermissionsStartOnly = true; # preStart must be run as root
+        ExecStart = "${cfg.package}/sbin/rabbitmq-server";
+        ExecStop = "${cfg.package}/sbin/rabbitmqctl shutdown";
         User = "rabbitmq";
         Group = "rabbitmq";
         WorkingDirectory = cfg.dataDir;
+        Type = "notify";
+        NotifyAccess = "all";
+        UMask = "0027";
+        LimitNOFILE = "100000";
+        Restart = "on-failure";
+        RestartSec = "10";
+        TimeoutStartSec = "3600";
       };
 
-      postStart = ''
-        rabbitmqctl wait ${cfg.dataDir}/pid
-      '';
-
       preStart = ''
         ${optionalString (cfg.cookie != "") ''
             echo -n ${cfg.cookie} > ${cfg.dataDir}/.erlang.cookie
+            chown rabbitmq:rabbitmq ${cfg.dataDir}/.erlang.cookie
             chmod 600 ${cfg.dataDir}/.erlang.cookie
         ''}
+        mkdir -p /var/log/rabbitmq
+        chown rabbitmq:rabbitmq /var/log/rabbitmq
       '';
     };
 
diff --git a/nixos/modules/services/cluster/kubernetes/default.nix b/nixos/modules/services/cluster/kubernetes/default.nix
index aeaa6bd66c99..e63d91eb9aca 100644
--- a/nixos/modules/services/cluster/kubernetes/default.nix
+++ b/nixos/modules/services/cluster/kubernetes/default.nix
@@ -622,13 +622,6 @@ in {
         type = types.bool;
       };
 
-      # TODO: remove this deprecated flag
-      cadvisorPort = mkOption {
-        description = "Kubernetes kubelet local cadvisor port.";
-        default = 4194;
-        type = types.int;
-      };
-
       clusterDns = mkOption {
         description = "Use alternative DNS.";
         default = "10.1.0.1";
@@ -862,7 +855,6 @@ in {
             --hostname-override=${cfg.kubelet.hostname} \
             --allow-privileged=${boolToString cfg.kubelet.allowPrivileged} \
             --root-dir=${cfg.dataDir} \
-            --cadvisor_port=${toString cfg.kubelet.cadvisorPort} \
             ${optionalString (cfg.kubelet.clusterDns != "")
               "--cluster-dns=${cfg.kubelet.clusterDns}"} \
             ${optionalString (cfg.kubelet.clusterDomain != "")
diff --git a/nixos/modules/services/computing/slurm/slurm.nix b/nixos/modules/services/computing/slurm/slurm.nix
index 1e1c5bc9f035..09174ed39f5e 100644
--- a/nixos/modules/services/computing/slurm/slurm.nix
+++ b/nixos/modules/services/computing/slurm/slurm.nix
@@ -8,6 +8,7 @@ let
   # configuration file can be generated by http://slurm.schedmd.com/configurator.html
   configFile = pkgs.writeTextDir "slurm.conf"
     ''
+      ClusterName=${cfg.clusterName}
       ${optionalString (cfg.controlMachine != null) ''controlMachine=${cfg.controlMachine}''}
       ${optionalString (cfg.controlAddr != null) ''controlAddr=${cfg.controlAddr}''}
       ${optionalString (cfg.nodeName != null) ''nodeName=${cfg.nodeName}''}
@@ -105,6 +106,15 @@ in
         '';
       };
 
+      clusterName = mkOption {
+        type = types.str;
+        default = "default";
+        example = "myCluster";
+        description = ''
+          Necessary to distinguish accounting records in a multi-cluster environment.
+        '';
+      };
+
       nodeName = mkOption {
         type = types.nullOr types.str;
         default = null;
diff --git a/nixos/modules/services/databases/foundationdb.xml b/nixos/modules/services/databases/foundationdb.xml
index 7883680d46cc..bf4b644c9b86 100644
--- a/nixos/modules/services/databases/foundationdb.xml
+++ b/nixos/modules/services/databases/foundationdb.xml
@@ -3,42 +3,50 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="module-services-foundationdb">
-
-<title>FoundationDB</title>
-
-<para><emphasis>Source:</emphasis> <filename>modules/services/databases/foundationdb.nix</filename></para>
-
-<para><emphasis>Upstream documentation:</emphasis> <link xlink:href="https://apple.github.io/foundationdb/"/></para>
-
-<para><emphasis>Maintainer:</emphasis> Austin Seipp</para>
-
-<para><emphasis>Available version(s):</emphasis> 5.1.x, 5.2.x, 6.0.x</para>
-
-<para>FoundationDB (or "FDB") is an open source, distributed, transactional
-key-value store.</para>
-
-<section xml:id="module-services-foundationdb-configuring"><title>Configuring and basic setup</title>
-
-<para>To enable FoundationDB, add the following to your
-<filename>configuration.nix</filename>:
-
+ <title>FoundationDB</title>
+ <para>
+  <emphasis>Source:</emphasis>
+  <filename>modules/services/databases/foundationdb.nix</filename>
+ </para>
+ <para>
+  <emphasis>Upstream documentation:</emphasis>
+  <link xlink:href="https://apple.github.io/foundationdb/"/>
+ </para>
+ <para>
+  <emphasis>Maintainer:</emphasis> Austin Seipp
+ </para>
+ <para>
+  <emphasis>Available version(s):</emphasis> 5.1.x, 5.2.x, 6.0.x
+ </para>
+ <para>
+  FoundationDB (or "FDB") is an open source, distributed, transactional
+  key-value store.
+ </para>
+ <section xml:id="module-services-foundationdb-configuring">
+  <title>Configuring and basic setup</title>
+
+  <para>
+   To enable FoundationDB, add the following to your
+   <filename>configuration.nix</filename>:
 <programlisting>
 services.foundationdb.enable = true;
 services.foundationdb.package = pkgs.foundationdb52; # FoundationDB 5.2.x
 </programlisting>
-</para>
-
-<para>The <option>services.foundationdb.package</option> option is required,
-and must always be specified. Due to the fact FoundationDB network protocols and
-on-disk storage formats may change between (major) versions, and upgrades must
-be explicitly handled by the user, you must always manually specify this
-yourself so that the NixOS module will use the proper version. Note that minor,
-bugfix releases are always compatible.</para>
-
-<para>After running <command>nixos-rebuild</command>, you can verify whether
-FoundationDB is running by executing <command>fdbcli</command> (which is added
-to <option>environment.systemPackages</option>):
-
+  </para>
+
+  <para>
+   The <option>services.foundationdb.package</option> option is required, and
+   must always be specified. Due to the fact FoundationDB network protocols and
+   on-disk storage formats may change between (major) versions, and upgrades
+   must be explicitly handled by the user, you must always manually specify
+   this yourself so that the NixOS module will use the proper version. Note
+   that minor, bugfix releases are always compatible.
+  </para>
+
+  <para>
+   After running <command>nixos-rebuild</command>, you can verify whether
+   FoundationDB is running by executing <command>fdbcli</command> (which is
+   added to <option>environment.systemPackages</option>):
 <programlisting>
 $ sudo -u foundationdb fdbcli
 Using cluster file `/etc/foundationdb/fdb.cluster'.
@@ -66,14 +74,14 @@ Cluster:
 
 fdb>
 </programlisting>
-</para>
-
-<para>You can also write programs using the available client libraries.
-For example, the following Python program can be run in order to grab the
-cluster status, as a quick example. (This example uses
-<command>nix-shell</command> shebang support to automatically supply the
-necessary Python modules).
-
+  </para>
+
+  <para>
+   You can also write programs using the available client libraries. For
+   example, the following Python program can be run in order to grab the
+   cluster status, as a quick example. (This example uses
+   <command>nix-shell</command> shebang support to automatically supply the
+   necessary Python modules).
 <programlisting>
 a@link> cat fdb-status.py
 #! /usr/bin/env nix-shell
@@ -100,255 +108,336 @@ a@link> ./fdb-status.py
 FoundationDB available: True
 a@link>
 </programlisting>
-</para>
-
-<para>FoundationDB is run under the <command>foundationdb</command> user and
-group by default, but this may be changed in the NixOS configuration. The
-systemd unit <command>foundationdb.service</command> controls the
-<command>fdbmonitor</command> process.</para>
-
-<para>By default, the NixOS module for FoundationDB creates a single
-SSD-storage based database for development and basic usage. This storage engine
-is designed for SSDs and will perform poorly on HDDs; however it can handle far
-more data than the alternative "memory" engine and is a better default choice
-for most deployments. (Note that you can change the storage backend on-the-fly
-for a given FoundationDB cluster using <command>fdbcli</command>.)</para>
-
-<para>Furthermore, only 1 server process and 1 backup agent are started in the
-default configuration. See below for more on scaling to increase this.</para>
-
-<para>FoundationDB stores all data for all server processes under
-<filename>/var/lib/foundationdb</filename>. You can override this using
-<option>services.foundationdb.dataDir</option>, e.g.
-
+  </para>
+
+  <para>
+   FoundationDB is run under the <command>foundationdb</command> user and group
+   by default, but this may be changed in the NixOS configuration. The systemd
+   unit <command>foundationdb.service</command> controls the
+   <command>fdbmonitor</command> process.
+  </para>
+
+  <para>
+   By default, the NixOS module for FoundationDB creates a single SSD-storage
+   based database for development and basic usage. This storage engine is
+   designed for SSDs and will perform poorly on HDDs; however it can handle far
+   more data than the alternative "memory" engine and is a better default
+   choice for most deployments. (Note that you can change the storage backend
+   on-the-fly for a given FoundationDB cluster using
+   <command>fdbcli</command>.)
+  </para>
+
+  <para>
+   Furthermore, only 1 server process and 1 backup agent are started in the
+   default configuration. See below for more on scaling to increase this.
+  </para>
+
+  <para>
+   FoundationDB stores all data for all server processes under
+   <filename>/var/lib/foundationdb</filename>. You can override this using
+   <option>services.foundationdb.dataDir</option>, e.g.
 <programlisting>
 services.foundationdb.dataDir = "/data/fdb";
 </programlisting>
-
-</para>
-
-<para>Similarly, logs are stored under
-<filename>/var/log/foundationdb</filename> by default, and there is a
-corresponding <option>services.foundationdb.logDir</option> as well.</para>
-
-</section>
-
-<section xml:id="module-services-foundationdb-scaling"><title>Scaling processes and backup agents</title>
-
-<para>Scaling the number of server processes is quite easy; simply specify
-<option>services.foundationdb.serverProcesses</option> to be the number of
-FoundationDB worker processes that should be started on the machine.</para>
-
-<para>FoundationDB worker processes typically require 4GB of RAM per-process at
-minimum for good performance, so this option is set to 1 by default since the
-maximum amount of RAM is unknown. You're advised to abide by this restriction,
-so pick a number of processes so that each has 4GB or more.</para>
-
-<para>A similar option exists in order to scale backup agent processes,
-<option>services.foundationdb.backupProcesses</option>. Backup agents are not
-as performance/RAM sensitive, so feel free to experiment with the number of
-available backup processes.</para>
-
-</section>
-
-<section xml:id="module-services-foundationdb-clustering"><title>Clustering</title>
-
-<para>FoundationDB on NixOS works similarly to other Linux systems, so this
-section will be brief. Please refer to the full FoundationDB documentation for
-more on clustering.</para>
-
-<para>FoundationDB organizes clusters using a set of
-<emphasis>coordinators</emphasis>, which are just specially-designated worker
-processes. By default, every installation of FoundationDB on NixOS will start
-as its own individual cluster, with a single coordinator: the first worker
-process on <command>localhost</command>.</para>
-
-<para>Coordinators are specified globally using the
-<command>/etc/foundationdb/fdb.cluster</command> file, which all servers and
-client applications will use to find and join coordinators. Note that this file
-<emphasis>can not</emphasis> be managed by NixOS so easily: FoundationDB is
-designed so that it will rewrite the file at runtime for all clients and nodes
-when cluster coordinators change, with clients transparently handling this
-without intervention. It is fundamentally a mutable file, and you should not
-try to manage it in any way in NixOS.</para>
-
-<para>When dealing with a cluster, there are two main things you want to
-do:</para>
-
-<itemizedlist>
-  <listitem><para>Add a node to the cluster for storage/compute.</para></listitem>
-  <listitem><para>Promote an ordinary worker to a coordinator.</para></listitem>
-</itemizedlist>
-
-<para>A node must already be a member of the cluster in order to properly be
-promoted to a coordinator, so you must always add it first if you wish to
-promote it.</para>
-
-<para>To add a machine to a FoundationDB cluster:</para>
-
-<itemizedlist>
-  <listitem><para>Choose one of the servers to start as the initial coordinator.
-      </para></listitem>
-  <listitem><para>Copy the <command>/etc/foundationdb/fdb.cluster</command> file
-      from this server to all the other servers. Restart FoundationDB on all of
-      these other servers, so they join the cluster.</para></listitem>
-  <listitem><para>All of these servers are now connected and working together
-      in the cluster, under the chosen coordinator.</para></listitem>
-</itemizedlist>
-
-<para>At this point, you can add as many nodes as you want by just repeating
-the above steps. By default there will still be a single coordinator: you can
-use <command>fdbcli</command> to change this and add new coordinators.</para>
-
-<para>As a convenience, FoundationDB can automatically assign coordinators
-based on the redundancy mode you wish to achieve for the cluster. Once all the
-nodes have been joined, simply set the replication policy, and then issue the
-<command>coordinators auto</command> command</para>
-
-<para>For example, assuming we have 3 nodes available, we can enable double
-redundancy mode, then auto-select coordinators. For double redundancy, 3
-coordinators is ideal: therefore FoundationDB will make
-<emphasis>every</emphasis> node a coordinator automatically:</para>
+  </para>
+
+  <para>
+   Similarly, logs are stored under <filename>/var/log/foundationdb</filename>
+   by default, and there is a corresponding
+   <option>services.foundationdb.logDir</option> as well.
+  </para>
+ </section>
+ <section xml:id="module-services-foundationdb-scaling">
+  <title>Scaling processes and backup agents</title>
+
+  <para>
+   Scaling the number of server processes is quite easy; simply specify
+   <option>services.foundationdb.serverProcesses</option> to be the number of
+   FoundationDB worker processes that should be started on the machine.
+  </para>
+
+  <para>
+   FoundationDB worker processes typically require 4GB of RAM per-process at
+   minimum for good performance, so this option is set to 1 by default since
+   the maximum amount of RAM is unknown. You're advised to abide by this
+   restriction, so pick a number of processes so that each has 4GB or more.
+  </para>
+
+  <para>
+   A similar option exists in order to scale backup agent processes,
+   <option>services.foundationdb.backupProcesses</option>. Backup agents are
+   not as performance/RAM sensitive, so feel free to experiment with the number
+   of available backup processes.
+  </para>
+ </section>
+ <section xml:id="module-services-foundationdb-clustering">
+  <title>Clustering</title>
+
+  <para>
+   FoundationDB on NixOS works similarly to other Linux systems, so this
+   section will be brief. Please refer to the full FoundationDB documentation
+   for more on clustering.
+  </para>
+
+  <para>
+   FoundationDB organizes clusters using a set of
+   <emphasis>coordinators</emphasis>, which are just specially-designated
+   worker processes. By default, every installation of FoundationDB on NixOS
+   will start as its own individual cluster, with a single coordinator: the
+   first worker process on <command>localhost</command>.
+  </para>
+
+  <para>
+   Coordinators are specified globally using the
+   <command>/etc/foundationdb/fdb.cluster</command> file, which all servers and
+   client applications will use to find and join coordinators. Note that this
+   file <emphasis>can not</emphasis> be managed by NixOS so easily:
+   FoundationDB is designed so that it will rewrite the file at runtime for all
+   clients and nodes when cluster coordinators change, with clients
+   transparently handling this without intervention. It is fundamentally a
+   mutable file, and you should not try to manage it in any way in NixOS.
+  </para>
+
+  <para>
+   When dealing with a cluster, there are two main things you want to do:
+  </para>
+
+  <itemizedlist>
+   <listitem>
+    <para>
+     Add a node to the cluster for storage/compute.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Promote an ordinary worker to a coordinator.
+    </para>
+   </listitem>
+  </itemizedlist>
+
+  <para>
+   A node must already be a member of the cluster in order to properly be
+   promoted to a coordinator, so you must always add it first if you wish to
+   promote it.
+  </para>
+
+  <para>
+   To add a machine to a FoundationDB cluster:
+  </para>
+
+  <itemizedlist>
+   <listitem>
+    <para>
+     Choose one of the servers to start as the initial coordinator.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Copy the <command>/etc/foundationdb/fdb.cluster</command> file from this
+     server to all the other servers. Restart FoundationDB on all of these
+     other servers, so they join the cluster.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     All of these servers are now connected and working together in the
+     cluster, under the chosen coordinator.
+    </para>
+   </listitem>
+  </itemizedlist>
+
+  <para>
+   At this point, you can add as many nodes as you want by just repeating the
+   above steps. By default there will still be a single coordinator: you can
+   use <command>fdbcli</command> to change this and add new coordinators.
+  </para>
+
+  <para>
+   As a convenience, FoundationDB can automatically assign coordinators based
+   on the redundancy mode you wish to achieve for the cluster. Once all the
+   nodes have been joined, simply set the replication policy, and then issue
+   the <command>coordinators auto</command> command
+  </para>
+
+  <para>
+   For example, assuming we have 3 nodes available, we can enable double
+   redundancy mode, then auto-select coordinators. For double redundancy, 3
+   coordinators is ideal: therefore FoundationDB will make
+   <emphasis>every</emphasis> node a coordinator automatically:
+  </para>
 
 <programlisting>
 fdbcli> configure double ssd
 fdbcli> coordinators auto
 </programlisting>
 
-<para>This will transparently update all the servers within seconds, and
-appropriately rewrite the <command>fdb.cluster</command> file, as well as
-informing all client processes to do the same.</para>
-
-</section>
-
-<section xml:id="module-services-foundationdb-connectivity"><title>Client connectivity</title>
-
-<para>By default, all clients must use the current
-<command>fdb.cluster</command> file to access a given FoundationDB cluster.
-This file is located by default in
-<command>/etc/foundationdb/fdb.cluster</command> on all machines with the
-FoundationDB service enabled, so you may copy the active one from your cluster
-to a new node in order to connect, if it is not part of the cluster.</para>
-
-</section>
-
-<section xml:id="module-services-foundationdb-authorization"><title>Client authorization and TLS</title>
-
-<para>By default, any user who can connect to a FoundationDB process with the
-correct cluster configuration can access anything. FoundationDB uses a
-pluggable design to transport security, and out of the box it supports a
-LibreSSL-based plugin for TLS support. This plugin not only does in-flight
-encryption, but also performs client authorization based on the given
-endpoint's certificate chain. For example, a FoundationDB server may be
-configured to only accept client connections over TLS, where the client TLS
-certificate is from organization <emphasis>Acme Co</emphasis> in the
-<emphasis>Research and Development</emphasis> unit.</para>
-
-<para>Configuring TLS with FoundationDB is done using the
-<option>services.foundationdb.tls</option> options in order to control the peer
-verification string, as well as the certificate and its private key.</para>
-
-<para>Note that the certificate and its private key must be accessible to the
-FoundationDB user account that the server runs under. These files are also NOT
-managed by NixOS, as putting them into the store may reveal private
-information.</para>
-
-<para>After you have a key and certificate file in place, it is not enough to
-simply set the NixOS module options -- you must also configure the
-<command>fdb.cluster</command> file to specify that a given set of coordinators
-use TLS. This is as simple as adding the suffix <command>:tls</command> to your
-cluster coordinator configuration, after the port number. For example, assuming
-you have a coordinator on localhost with the default configuration, simply
-specifying:</para>
+  <para>
+   This will transparently update all the servers within seconds, and
+   appropriately rewrite the <command>fdb.cluster</command> file, as well as
+   informing all client processes to do the same.
+  </para>
+ </section>
+ <section xml:id="module-services-foundationdb-connectivity">
+  <title>Client connectivity</title>
+
+  <para>
+   By default, all clients must use the current <command>fdb.cluster</command>
+   file to access a given FoundationDB cluster. This file is located by default
+   in <command>/etc/foundationdb/fdb.cluster</command> on all machines with the
+   FoundationDB service enabled, so you may copy the active one from your
+   cluster to a new node in order to connect, if it is not part of the cluster.
+  </para>
+ </section>
+ <section xml:id="module-services-foundationdb-authorization">
+  <title>Client authorization and TLS</title>
+
+  <para>
+   By default, any user who can connect to a FoundationDB process with the
+   correct cluster configuration can access anything. FoundationDB uses a
+   pluggable design to transport security, and out of the box it supports a
+   LibreSSL-based plugin for TLS support. This plugin not only does in-flight
+   encryption, but also performs client authorization based on the given
+   endpoint's certificate chain. For example, a FoundationDB server may be
+   configured to only accept client connections over TLS, where the client TLS
+   certificate is from organization <emphasis>Acme Co</emphasis> in the
+   <emphasis>Research and Development</emphasis> unit.
+  </para>
+
+  <para>
+   Configuring TLS with FoundationDB is done using the
+   <option>services.foundationdb.tls</option> options in order to control the
+   peer verification string, as well as the certificate and its private key.
+  </para>
+
+  <para>
+   Note that the certificate and its private key must be accessible to the
+   FoundationDB user account that the server runs under. These files are also
+   NOT managed by NixOS, as putting them into the store may reveal private
+   information.
+  </para>
+
+  <para>
+   After you have a key and certificate file in place, it is not enough to
+   simply set the NixOS module options -- you must also configure the
+   <command>fdb.cluster</command> file to specify that a given set of
+   coordinators use TLS. This is as simple as adding the suffix
+   <command>:tls</command> to your cluster coordinator configuration, after the
+   port number. For example, assuming you have a coordinator on localhost with
+   the default configuration, simply specifying:
+  </para>
 
 <programlisting>
 XXXXXX:XXXXXX@127.0.0.1:4500:tls
 </programlisting>
 
-<para>will configure all clients and server processes to use TLS from now
-on.</para>
-
-</section>
-
-<section xml:id="module-services-foundationdb-disaster-recovery"><title>Backups and Disaster Recovery</title>
-
-<para>The usual rules for doing FoundationDB backups apply on NixOS as written
-in the FoundationDB manual. However, one important difference is the security
-profile for NixOS: by default, the <command>foundationdb</command> systemd unit
-uses <emphasis>Linux namespaces</emphasis> to restrict write access to the
-system, except for the log directory, data directory, and the
-<command>/etc/foundationdb/</command> directory. This is enforced by default
-and cannot be disabled.</para>
-
-<para>However, a side effect of this is that the <command>fdbbackup</command>
-command doesn't work properly for local filesystem backups: FoundationDB uses a
-server process alongside the database processes to perform backups and copy the
-backups to the filesystem. As a result, this process is put under the
-restricted namespaces above: the backup process can only write to a limited
-number of paths.</para>
-
-<para>In order to allow flexible backup locations on local disks, the
-FoundationDB NixOS module supports a
-<option>services.foundationdb.extraReadWritePaths</option> option. This option
-takes a list of paths, and adds them to the systemd unit, allowing the
-processes inside the service to write (and read) the specified
-directories.</para>
-
-<para>For example, to create backups in <command>/opt/fdb-backups</command>,
-first set up the paths in the module options:</para>
+  <para>
+   will configure all clients and server processes to use TLS from now on.
+  </para>
+ </section>
+ <section xml:id="module-services-foundationdb-disaster-recovery">
+  <title>Backups and Disaster Recovery</title>
+
+  <para>
+   The usual rules for doing FoundationDB backups apply on NixOS as written in
+   the FoundationDB manual. However, one important difference is the security
+   profile for NixOS: by default, the <command>foundationdb</command> systemd
+   unit uses <emphasis>Linux namespaces</emphasis> to restrict write access to
+   the system, except for the log directory, data directory, and the
+   <command>/etc/foundationdb/</command> directory. This is enforced by default
+   and cannot be disabled.
+  </para>
+
+  <para>
+   However, a side effect of this is that the <command>fdbbackup</command>
+   command doesn't work properly for local filesystem backups: FoundationDB
+   uses a server process alongside the database processes to perform backups
+   and copy the backups to the filesystem. As a result, this process is put
+   under the restricted namespaces above: the backup process can only write to
+   a limited number of paths.
+  </para>
+
+  <para>
+   In order to allow flexible backup locations on local disks, the FoundationDB
+   NixOS module supports a
+   <option>services.foundationdb.extraReadWritePaths</option> option. This
+   option takes a list of paths, and adds them to the systemd unit, allowing
+   the processes inside the service to write (and read) the specified
+   directories.
+  </para>
+
+  <para>
+   For example, to create backups in <command>/opt/fdb-backups</command>, first
+   set up the paths in the module options:
+  </para>
 
 <programlisting>
 services.foundationdb.extraReadWritePaths = [ "/opt/fdb-backups" ];
 </programlisting>
 
-<para>Restart the FoundationDB service, and it will now be able to write to
-this directory (even if it does not yet exist.) Note: this path
-<emphasis>must</emphasis> exist before restarting the unit. Otherwise, systemd
-will not include it in the private FoundationDB namespace (and it will not add
-it dynamically at runtime).</para>
+  <para>
+   Restart the FoundationDB service, and it will now be able to write to this
+   directory (even if it does not yet exist.) Note: this path
+   <emphasis>must</emphasis> exist before restarting the unit. Otherwise,
+   systemd will not include it in the private FoundationDB namespace (and it
+   will not add it dynamically at runtime).
+  </para>
 
-<para>You can now perform a backup:</para>
+  <para>
+   You can now perform a backup:
+  </para>
 
 <programlisting>
 $ sudo -u foundationdb fdbbackup start  -t default -d file:///opt/fdb-backups
 $ sudo -u foundationdb fdbbackup status -t default
 </programlisting>
-
-</section>
-
-<section xml:id="module-services-foundationdb-limitations"><title>Known limitations</title>
-
-<para>The FoundationDB setup for NixOS should currently be considered beta.
-FoundationDB is not new software, but the NixOS compilation and integration has
-only undergone fairly basic testing of all the available functionality.</para>
-
-<itemizedlist>
-  <listitem><para>There is no way to specify individual parameters for
-      individual <command>fdbserver</command> processes. Currently, all server
-      processes inherit all the global <command>fdbmonitor</command> settings.
-      </para></listitem>
-  <listitem><para>Ruby bindings are not currently installed.</para></listitem>
-  <listitem><para>Go bindings are not currently installed.</para></listitem>
-</itemizedlist>
-
-</section>
-
-<section xml:id="module-services-foundationdb-options"><title>Options</title>
-
-<para>NixOS's FoundationDB module allows you to configure all of the most
-relevant configuration options for <command>fdbmonitor</command>, matching it
-quite closely. A complete list of options for the FoundationDB module may be
-found <link linkend="opt-services.foundationdb.enable">here</link>. You should
-also read the FoundationDB documentation as well.</para>
-
-</section>
-
-<section xml:id="module-services-foundationdb-full-docs"><title>Full documentation</title>
-
-<para>FoundationDB is a complex piece of software, and requires careful
-administration to properly use. Full documentation for administration can be
-found here: <link xlink:href="https://apple.github.io/foundationdb/"/>.</para>
-
-</section>
-
+ </section>
+ <section xml:id="module-services-foundationdb-limitations">
+  <title>Known limitations</title>
+
+  <para>
+   The FoundationDB setup for NixOS should currently be considered beta.
+   FoundationDB is not new software, but the NixOS compilation and integration
+   has only undergone fairly basic testing of all the available functionality.
+  </para>
+
+  <itemizedlist>
+   <listitem>
+    <para>
+     There is no way to specify individual parameters for individual
+     <command>fdbserver</command> processes. Currently, all server processes
+     inherit all the global <command>fdbmonitor</command> settings.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Ruby bindings are not currently installed.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Go bindings are not currently installed.
+    </para>
+   </listitem>
+  </itemizedlist>
+ </section>
+ <section xml:id="module-services-foundationdb-options">
+  <title>Options</title>
+
+  <para>
+   NixOS's FoundationDB module allows you to configure all of the most relevant
+   configuration options for <command>fdbmonitor</command>, matching it quite
+   closely. A complete list of options for the FoundationDB module may be found
+   <link linkend="opt-services.foundationdb.enable">here</link>. You should
+   also read the FoundationDB documentation as well.
+  </para>
+ </section>
+ <section xml:id="module-services-foundationdb-full-docs">
+  <title>Full documentation</title>
+
+  <para>
+   FoundationDB is a complex piece of software, and requires careful
+   administration to properly use. Full documentation for administration can be
+   found here: <link xlink:href="https://apple.github.io/foundationdb/"/>.
+  </para>
+ </section>
 </chapter>
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index f59fb1c81772..de2a757196a5 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -188,6 +188,8 @@ in
         uid = config.ids.uids.postgres;
         group = "postgres";
         description = "PostgreSQL server user";
+        home = "${cfg.dataDir}";
+        useDefaultShell = true;
       };
 
     users.groups.postgres.gid = config.ids.gids.postgres;
diff --git a/nixos/modules/services/databases/postgresql.xml b/nixos/modules/services/databases/postgresql.xml
index 1aaf33963245..f89f0d653164 100644
--- a/nixos/modules/services/databases/postgresql.xml
+++ b/nixos/modules/services/databases/postgresql.xml
@@ -3,36 +3,39 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="module-postgresql">
-
-<title>PostgreSQL</title>
-
+ <title>PostgreSQL</title>
 <!-- FIXME: render nicely -->
-
 <!-- FIXME: source can be added automatically -->
-<para><emphasis>Source:</emphasis> <filename>modules/services/databases/postgresql.nix</filename></para>
-
-<para><emphasis>Upstream documentation:</emphasis> <link xlink:href="http://www.postgresql.org/docs/"/></para>
-
+ <para>
+  <emphasis>Source:</emphasis>
+  <filename>modules/services/databases/postgresql.nix</filename>
+ </para>
+ <para>
+  <emphasis>Upstream documentation:</emphasis>
+  <link xlink:href="http://www.postgresql.org/docs/"/>
+ </para>
 <!-- FIXME: more stuff, like maintainer? -->
-
-<para>PostgreSQL is an advanced, free relational database.<!-- MORE --></para>
-
-<section xml:id="module-services-postgres-configuring"><title>Configuring</title>
-
-<para>To enable PostgreSQL, add the following to your
-<filename>configuration.nix</filename>:
-
+ <para>
+  PostgreSQL is an advanced, free relational database.
+<!-- MORE -->
+ </para>
+ <section xml:id="module-services-postgres-configuring">
+  <title>Configuring</title>
+
+  <para>
+   To enable PostgreSQL, add the following to your
+   <filename>configuration.nix</filename>:
 <programlisting>
 <xref linkend="opt-services.postgresql.enable"/> = true;
 <xref linkend="opt-services.postgresql.package"/> = pkgs.postgresql94;
 </programlisting>
-
-Note that you are required to specify the desired version of
-PostgreSQL (e.g. <literal>pkgs.postgresql94</literal>). Since
-upgrading your PostgreSQL version requires a database dump and reload
-(see below), NixOS cannot provide a default value for
-<xref linkend="opt-services.postgresql.package"/> such as the most recent
-release of PostgreSQL.</para>
+   Note that you are required to specify the desired version of PostgreSQL
+   (e.g. <literal>pkgs.postgresql94</literal>). Since upgrading your PostgreSQL
+   version requires a database dump and reload (see below), NixOS cannot
+   provide a default value for
+   <xref linkend="opt-services.postgresql.package"/> such as the most recent
+   release of PostgreSQL.
+  </para>
 
 <!--
 <para>After running <command>nixos-rebuild</command>, you can verify
@@ -47,31 +50,28 @@ alice=>
 </screen>
 -->
 
-<para>By default, PostgreSQL stores its databases in
-<filename>/var/db/postgresql</filename>. You can override this using
-<xref linkend="opt-services.postgresql.dataDir"/>, e.g.
-
+  <para>
+   By default, PostgreSQL stores its databases in
+   <filename>/var/db/postgresql</filename>. You can override this using
+   <xref linkend="opt-services.postgresql.dataDir"/>, e.g.
 <programlisting>
 <xref linkend="opt-services.postgresql.dataDir"/> = "/data/postgresql";
 </programlisting>
-
-</para>
-
-</section>
-
-
-<section xml:id="module-services-postgres-upgrading"><title>Upgrading</title>
-
-<para>FIXME: document dump/upgrade/load cycle.</para>
-
-</section>
-
-
-<section xml:id="module-services-postgres-options"><title>Options</title>
-
-  <para>A complete list of options for the PostgreSQL module may be found <link linkend="opt-services.postgresql.enable">here</link>.</para>
-
-</section>
-
-
+  </para>
+ </section>
+ <section xml:id="module-services-postgres-upgrading">
+  <title>Upgrading</title>
+
+  <para>
+   FIXME: document dump/upgrade/load cycle.
+  </para>
+ </section>
+ <section xml:id="module-services-postgres-options">
+  <title>Options</title>
+
+  <para>
+   A complete list of options for the PostgreSQL module may be found
+   <link linkend="opt-services.postgresql.enable">here</link>.
+  </para>
+ </section>
 </chapter>
diff --git a/nixos/modules/services/desktops/flatpak.xml b/nixos/modules/services/desktops/flatpak.xml
index d9c8b711c450..8045d5fa14f8 100644
--- a/nixos/modules/services/desktops/flatpak.xml
+++ b/nixos/modules/services/desktops/flatpak.xml
@@ -3,51 +3,54 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="module-services-flatpak">
-
-<title>Flatpak</title>
-
-<para><emphasis>Source:</emphasis> <filename>modules/services/desktop/flatpak.nix</filename></para>
-
-<para><emphasis>Upstream documentation:</emphasis> <link xlink:href="https://github.com/flatpak/flatpak/wiki"/></para>
-
-<para>Flatpak is a system for building, distributing, and running sandboxed desktop applications on Linux.</para>
-
-<para>
-  To enable Flatpak, add the following to your <filename>configuration.nix</filename>:
-
-  <programlisting>
+ <title>Flatpak</title>
+ <para>
+  <emphasis>Source:</emphasis>
+  <filename>modules/services/desktop/flatpak.nix</filename>
+ </para>
+ <para>
+  <emphasis>Upstream documentation:</emphasis>
+  <link xlink:href="https://github.com/flatpak/flatpak/wiki"/>
+ </para>
+ <para>
+  Flatpak is a system for building, distributing, and running sandboxed desktop
+  applications on Linux.
+ </para>
+ <para>
+  To enable Flatpak, add the following to your
+  <filename>configuration.nix</filename>:
+<programlisting>
   <xref linkend="opt-services.flatpak.enable"/> = true;
   </programlisting>
-</para>
-
-<para>
-  For the sandboxed apps to work correctly, desktop integration portals need to be installed. If you run GNOME, this will be handled automatically for you; in other cases, you will need to add something like the following to your <filename>configuration.nix</filename>:
-
-  <programlisting>
+ </para>
+ <para>
+  For the sandboxed apps to work correctly, desktop integration portals need to
+  be installed. If you run GNOME, this will be handled automatically for you;
+  in other cases, you will need to add something like the following to your
+  <filename>configuration.nix</filename>:
+<programlisting>
   <xref linkend="opt-services.flatpak.extraPortals"/> = [ pkgs.xdg-desktop-portal-gtk ];
   </programlisting>
-</para>
-
-<para>
-  Then, you will need to add a repository, for example, <link xlink:href="https://github.com/flatpak/flatpak/wiki">Flathub</link>, either using the following commands:
-
-  <programlisting>
+ </para>
+ <para>
+  Then, you will need to add a repository, for example,
+  <link xlink:href="https://github.com/flatpak/flatpak/wiki">Flathub</link>,
+  either using the following commands:
+<programlisting>
   flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
   flatpak update
   </programlisting>
-
-  or by opening the <link xlink:href="https://flathub.org/repo/flathub.flatpakrepo">repository file</link> in GNOME Software.
-</para>
-
-<para>
+  or by opening the
+  <link xlink:href="https://flathub.org/repo/flathub.flatpakrepo">repository
+  file</link> in GNOME Software.
+ </para>
+ <para>
   Finally, you can search and install programs:
-
-  <programlisting>
+<programlisting>
   flatpak search bustle
   flatpak install flathub org.freedesktop.Bustle
   flatpak run org.freedesktop.Bustle
   </programlisting>
-
   Again, GNOME Software offers graphical interface for these tasks.
-</para>
+ </para>
 </chapter>
diff --git a/nixos/modules/services/desktops/gnome3/rygel.nix b/nixos/modules/services/desktops/gnome3/rygel.nix
new file mode 100644
index 000000000000..55d5e703aa19
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/rygel.nix
@@ -0,0 +1,30 @@
+# rygel service.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+  options = {
+    services.gnome3.rygel = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable Rygel UPnP Mediaserver.
+
+          You will need to also allow UPnP connections in firewall, see the following <link xlink:href="https://github.com/NixOS/nixpkgs/pull/45045#issuecomment-416030795">comment</link>.
+        '';
+        type = types.bool;
+      };
+    };
+  };
+
+  ###### implementation
+  config = mkIf config.services.gnome3.rygel.enable {
+    environment.systemPackages = [ pkgs.gnome3.rygel ];
+
+    services.dbus.packages = [ pkgs.gnome3.rygel ];
+
+    systemd.packages = [ pkgs.gnome3.rygel ];
+  };
+}
diff --git a/nixos/modules/services/editors/emacs.xml b/nixos/modules/services/editors/emacs.xml
index 94eb2e6a77bf..6cf20cf4aa7e 100644
--- a/nixos/modules/services/editors/emacs.xml
+++ b/nixos/modules/services/editors/emacs.xml
@@ -3,150 +3,148 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="module-services-emacs">
-
-  <title>Emacs</title>
-
-  <!--
+ <title>Emacs</title>
+<!--
     Documentation contributors:
       Damien Cassou @DamienCassou
       Thomas Tuegel @ttuegel
       Rodney Lorrimar @rvl
   -->
+ <para>
+  <link xlink:href="http://www.gnu.org/software/emacs/">Emacs</link> is an
+  extensible, customizable, self-documenting real-time display editor — and
+  more. At its core is an interpreter for Emacs Lisp, a dialect of the Lisp
+  programming language with extensions to support text editing.
+ </para>
+ <para>
+  Emacs runs within a graphical desktop environment using the X Window System,
+  but works equally well on a text terminal. Under
+  <productname>macOS</productname>, a "Mac port" edition is available, which
+  uses Apple's native GUI frameworks.
+ </para>
+ <para>
+  <productname>Nixpkgs</productname> provides a superior environment for
+  running <application>Emacs</application>. It's simple to create custom builds
+  by overriding the default packages. Chaotic collections of Emacs Lisp code
+  and extensions can be brought under control using declarative package
+  management. <productname>NixOS</productname> even provides a
+  <command>systemd</command> user service for automatically starting the Emacs
+  daemon.
+ </para>
+ <section xml:id="module-services-emacs-installing">
+  <title>Installing <application>Emacs</application></title>
 
   <para>
-    <link xlink:href="http://www.gnu.org/software/emacs/">Emacs</link>
-    is an extensible, customizable, self-documenting real-time display
-    editor — and more. At its core is an interpreter for Emacs Lisp, a
-    dialect of the Lisp programming language with extensions to
-    support text editing.
+   Emacs can be installed in the normal way for Nix (see
+   <xref linkend="sec-package-management" />). In addition, a NixOS
+   <emphasis>service</emphasis> can be enabled.
   </para>
 
-  <para>
-    Emacs runs within a graphical desktop environment using the X
-    Window System, but works equally well on a text terminal. Under
-    <productname>macOS</productname>, a "Mac port" edition is
-    available, which uses Apple's native GUI frameworks.
-  </para>
+  <section xml:id="module-services-emacs-releases">
+   <title>The Different Releases of Emacs</title>
+
+   <para>
+    <productname>Nixpkgs</productname> defines several basic Emacs packages.
+    The following are attributes belonging to the <varname>pkgs</varname> set:
+    <variablelist>
+     <varlistentry>
+      <term>
+       <varname>emacs</varname>
+      </term>
+      <term>
+       <varname>emacs25</varname>
+      </term>
+      <listitem>
+       <para>
+        The latest stable version of Emacs 25 using the
+        <link
+                xlink:href="http://www.gtk.org">GTK+ 2</link>
+        widget toolkit.
+       </para>
+      </listitem>
+     </varlistentry>
+     <varlistentry>
+      <term>
+       <varname>emacs25-nox</varname>
+      </term>
+      <listitem>
+       <para>
+        Emacs 25 built without any dependency on X11 libraries.
+       </para>
+      </listitem>
+     </varlistentry>
+     <varlistentry>
+      <term>
+       <varname>emacsMacport</varname>
+      </term>
+      <term>
+       <varname>emacs25Macport</varname>
+      </term>
+      <listitem>
+       <para>
+        Emacs 25 with the "Mac port" patches, providing a more native look and
+        feel under macOS.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+
+   <para>
+    If those aren't suitable, then the following imitation Emacs editors are
+    also available in Nixpkgs:
+    <link xlink:href="https://www.gnu.org/software/zile/">Zile</link>,
+    <link xlink:href="http://homepage.boetes.org/software/mg/">mg</link>,
+    <link xlink:href="http://yi-editor.github.io/">Yi</link>.
+   </para>
+  </section>
 
-  <para>
-    <productname>Nixpkgs</productname> provides a superior environment
-    for running <application>Emacs</application>. It's simple to
-    create custom builds by overriding the default packages. Chaotic
-    collections of Emacs Lisp code and extensions can be brought under
-    control using declarative package
-    management. <productname>NixOS</productname> even provides a
-    <command>systemd</command> user service for automatically
-    starting the Emacs daemon.
-  </para>
+  <section xml:id="module-services-emacs-adding-packages">
+   <title>Adding Packages to Emacs</title>
 
-  <section xml:id="module-services-emacs-installing">
-    <title>Installing <application>Emacs</application></title>
+   <para>
+    Emacs includes an entire ecosystem of functionality beyond text editing,
+    including a project planner, mail and news reader, debugger interface,
+    calendar, and more.
+   </para>
 
+   <para>
+    Most extensions are gotten with the Emacs packaging system
+    (<filename>package.el</filename>) from
+    <link
+        xlink:href="https://elpa.gnu.org/">Emacs Lisp Package Archive
+    (<acronym>ELPA</acronym>)</link>,
+    <link xlink:href="https://melpa.org/"><acronym>MELPA</acronym></link>,
+    <link xlink:href="https://stable.melpa.org/">MELPA Stable</link>, and
+    <link xlink:href="http://orgmode.org/elpa.html">Org ELPA</link>. Nixpkgs is
+    regularly updated to mirror all these archives.
+   </para>
+
+   <para>
+    Under NixOS, you can continue to use
+    <function>package-list-packages</function> and
+    <function>package-install</function> to install packages. You can also
+    declare the set of Emacs packages you need using the derivations from
+    Nixpkgs. The rest of this section discusses declarative installation of
+    Emacs packages through nixpkgs.
+   </para>
+
+   <note>
     <para>
-      Emacs can be installed in the normal way for Nix (see
-      <xref linkend="sec-package-management" />).
-      In addition, a NixOS <emphasis>service</emphasis>
-      can be enabled.
+     This documentation describes the new Emacs packages framework in NixOS
+     16.03 (<varname>emacsPackagesNg</varname>) which should not be confused
+     with the previous and deprecated framework
+     (<varname>emacs24Packages</varname>).
     </para>
-
-    <section xml:id="module-services-emacs-releases">
-      <title>The Different Releases of Emacs</title>
-
-      <para>
-        <productname>Nixpkgs</productname> defines several basic Emacs
-        packages. The following are attributes belonging to the
-        <varname>pkgs</varname> set:
-
-        <variablelist>
-          <varlistentry>
-            <term><varname>emacs</varname></term>
-            <term><varname>emacs25</varname></term>
-            <listitem>
-              <para>
-                The latest stable version of Emacs 25 using the <link
-                xlink:href="http://www.gtk.org">GTK+ 2</link> widget
-                toolkit.
-              </para>
-            </listitem>
-          </varlistentry>
-          <varlistentry>
-            <term><varname>emacs25-nox</varname></term>
-            <listitem>
-              <para>
-                Emacs 25 built without any dependency on X11
-                libraries.
-              </para>
-            </listitem>
-          </varlistentry>
-          <varlistentry>
-            <term><varname>emacsMacport</varname></term>
-            <term><varname>emacs25Macport</varname></term>
-            <listitem>
-              <para>
-                Emacs 25 with the "Mac port" patches, providing a more
-                native look and feel under macOS.
-              </para>
-            </listitem>
-          </varlistentry>
-        </variablelist>
-      </para>
-
-      <para>
-        If those aren't suitable, then the following imitation Emacs
-        editors are also available in Nixpkgs:
-        <link xlink:href="https://www.gnu.org/software/zile/">Zile</link>,
-        <link xlink:href="http://homepage.boetes.org/software/mg/">mg</link>,
-        <link xlink:href="http://yi-editor.github.io/">Yi</link>.
-      </para>
-
-    </section>
-    <section xml:id="module-services-emacs-adding-packages">
-      <title>Adding Packages to Emacs</title>
-      <para>
-        Emacs includes an entire ecosystem of functionality beyond
-        text editing, including a project planner, mail and news
-        reader, debugger interface, calendar, and more.
-      </para>
-
-      <para>
-        Most extensions are gotten with the Emacs packaging system
-        (<filename>package.el</filename>) from <link
-        xlink:href="https://elpa.gnu.org/">Emacs Lisp Package Archive
-        (<acronym>ELPA</acronym>)</link>,
-        <link xlink:href="https://melpa.org/"><acronym>MELPA</acronym></link>,
-        <link xlink:href="https://stable.melpa.org/">MELPA Stable</link>,
-        and <link xlink:href="http://orgmode.org/elpa.html">Org ELPA</link>.
-        Nixpkgs is regularly updated to mirror all these archives.
-      </para>
-
-      <para>
-        Under NixOS, you can continue to use
-        <function>package-list-packages</function> and
-        <function>package-install</function> to install packages. You
-        can also declare the set of Emacs packages you need using the
-        derivations from Nixpkgs. The rest of this section discusses
-        declarative installation of Emacs packages through nixpkgs.
-      </para>
-
-      <note>
-        <para>
-          This documentation describes the new Emacs packages
-          framework in NixOS 16.03
-          (<varname>emacsPackagesNg</varname>) which should not be
-          confused with the previous and deprecated framework
-          (<varname>emacs24Packages</varname>).
-        </para>
-      </note>
-
-      <para>
-        The first step to declare the list of packages you want in
-        your Emacs installation is to create a dedicated
-        derivation. This can be done in a dedicated
-        <filename>emacs.nix</filename> file such as:
-
-      <example xml:id="ex-emacsNix">
-        <title>Nix expression to build Emacs with packages (<filename>emacs.nix</filename>)</title>
-        <programlisting language="nix">
+   </note>
+
+   <para>
+    The first step to declare the list of packages you want in your Emacs
+    installation is to create a dedicated derivation. This can be done in a
+    dedicated <filename>emacs.nix</filename> file such as:
+    <example xml:id="ex-emacsNix">
+     <title>Nix expression to build Emacs with packages (<filename>emacs.nix</filename>)</title>
+<programlisting language="nix">
 /*
 This is a nix expression to build Emacs and some Emacs packages I like
 from source on any distribution where Nix is installed. This will install
@@ -181,119 +179,104 @@ in
     pkgs.notmuch   # From main packages set <co xml:id="ex-emacsNix-7" />
   ])
 </programlisting>
-      </example>
-
-      <calloutlist>
-        <callout arearefs="ex-emacsNix-1">
-          <para>
-            The first non-comment line in this file
-            (<literal>{ pkgs ? ... }</literal>)
-            indicates that the whole file represents a function.
-          </para>
-        </callout>
-
-        <callout arearefs="ex-emacsNix-2">
-          <para>
-            The <varname>let</varname> expression below defines a
-            <varname>myEmacs</varname> binding pointing to the current
-            stable version of Emacs. This binding is here to separate the
-            choice of the Emacs binary from the specification of the
-            required packages.
-          </para>
-        </callout>
-
-        <callout arearefs="ex-emacsNix-3">
-          <para>
-            This generates an <varname>emacsWithPackages</varname>
-            function. It takes a single argument: a function from a
-            package set to a list of packages (the packages that will
-            be available in Emacs).
-          </para>
-        </callout>
-
-        <callout arearefs="ex-emacsNix-4">
-          <para>
-            The rest of the file specifies the list of packages to
-            install. In the example, two packages
-            (<varname>magit</varname> and
-            <varname>zerodark-theme</varname>) are taken from MELPA
-            stable.
-          </para>
-        </callout>
-
-        <callout arearefs="ex-emacsNix-5">
-          <para>
-            Two packages (<varname>undo-tree</varname> and
-            <varname>zoom-frm</varname>) are taken from MELPA.
-          </para>
-        </callout>
-
-        <callout arearefs="ex-emacsNix-6">
-          <para>Three packages are taken from GNU ELPA.</para>
-        </callout>
-
-        <callout arearefs="ex-emacsNix-7">
-          <para>
-            <varname>notmuch</varname> is taken from a nixpkgs derivation
-            which contains an Emacs mode.
-          </para>
-        </callout>
-
-      </calloutlist>
+    </example>
+    <calloutlist>
+     <callout arearefs="ex-emacsNix-1">
+      <para>
+       The first non-comment line in this file (<literal>{ pkgs ? ...
+       }</literal>) indicates that the whole file represents a function.
       </para>
-
+     </callout>
+     <callout arearefs="ex-emacsNix-2">
       <para>
-        The result of this configuration will be an
-        <command>emacs</command> command which launches Emacs with all
-        of your chosen packages in the <varname>load-path</varname>.
+       The <varname>let</varname> expression below defines a
+       <varname>myEmacs</varname> binding pointing to the current stable
+       version of Emacs. This binding is here to separate the choice of the
+       Emacs binary from the specification of the required packages.
       </para>
-
+     </callout>
+     <callout arearefs="ex-emacsNix-3">
       <para>
-        You can check that it works by executing this in a terminal:
-
+       This generates an <varname>emacsWithPackages</varname> function. It
+       takes a single argument: a function from a package set to a list of
+       packages (the packages that will be available in Emacs).
+      </para>
+     </callout>
+     <callout arearefs="ex-emacsNix-4">
+      <para>
+       The rest of the file specifies the list of packages to install. In the
+       example, two packages (<varname>magit</varname> and
+       <varname>zerodark-theme</varname>) are taken from MELPA stable.
+      </para>
+     </callout>
+     <callout arearefs="ex-emacsNix-5">
+      <para>
+       Two packages (<varname>undo-tree</varname> and
+       <varname>zoom-frm</varname>) are taken from MELPA.
+      </para>
+     </callout>
+     <callout arearefs="ex-emacsNix-6">
+      <para>
+       Three packages are taken from GNU ELPA.
+      </para>
+     </callout>
+     <callout arearefs="ex-emacsNix-7">
+      <para>
+       <varname>notmuch</varname> is taken from a nixpkgs derivation which
+       contains an Emacs mode.
+      </para>
+     </callout>
+    </calloutlist>
+   </para>
+
+   <para>
+    The result of this configuration will be an <command>emacs</command>
+    command which launches Emacs with all of your chosen packages in the
+    <varname>load-path</varname>.
+   </para>
+
+   <para>
+    You can check that it works by executing this in a terminal:
 <screen>
 $ nix-build emacs.nix
 $ ./result/bin/emacs -q
 </screen>
+    and then typing <literal>M-x package-initialize</literal>. Check that you
+    can use all the packages you want in this Emacs instance. For example, try
+    switching to the zerodark theme through <literal>M-x load-theme &lt;RET&gt;
+    zerodark &lt;RET&gt; y</literal>.
+   </para>
 
-        and then typing <literal>M-x package-initialize</literal>.
-        Check that you can use all the packages you want in this
-        Emacs instance. For example, try switching to the zerodark
-        theme through
-        <literal>M-x load-theme &lt;RET&gt; zerodark &lt;RET&gt; y</literal>.
-      </para>
-
-      <tip>
-        <para>
-          A few popular extensions worth checking out are: auctex,
-          company, edit-server, flycheck, helm, iedit, magit,
-          multiple-cursors, projectile, and yasnippet.
-        </para>
-      </tip>
-
-      <para>
-        The list of available packages in the various ELPA
-        repositories can be seen with the following commands:
-        <example xml:id="module-services-emacs-querying-packages">
-          <title>Querying Emacs packages</title>
-          <programlisting><![CDATA[
+   <tip>
+    <para>
+     A few popular extensions worth checking out are: auctex, company,
+     edit-server, flycheck, helm, iedit, magit, multiple-cursors, projectile,
+     and yasnippet.
+    </para>
+   </tip>
+
+   <para>
+    The list of available packages in the various ELPA repositories can be seen
+    with the following commands:
+    <example xml:id="module-services-emacs-querying-packages">
+     <title>Querying Emacs packages</title>
+<programlisting><![CDATA[
 nix-env -f "<nixpkgs>" -qaP -A emacsPackagesNg.elpaPackages
 nix-env -f "<nixpkgs>" -qaP -A emacsPackagesNg.melpaPackages
 nix-env -f "<nixpkgs>" -qaP -A emacsPackagesNg.melpaStablePackages
 nix-env -f "<nixpkgs>" -qaP -A emacsPackagesNg.orgPackages
 ]]></programlisting>
-        </example>
-      </para>
-
-      <para>
-        If you are on NixOS, you can install this particular Emacs for
-        all users by adding it to the list of system packages
-        (see <xref linkend="sec-declarative-package-mgmt" />). Simply
-        modify your file <filename>configuration.nix</filename> to
-        make it contain:
-        <example xml:id="module-services-emacs-configuration-nix">
-          <title>Custom Emacs in <filename>configuration.nix</filename></title>
-          <programlisting><![CDATA[
+    </example>
+   </para>
+
+   <para>
+    If you are on NixOS, you can install this particular Emacs for all users by
+    adding it to the list of system packages (see
+    <xref linkend="sec-declarative-package-mgmt" />). Simply modify your file
+    <filename>configuration.nix</filename> to make it contain:
+    <example xml:id="module-services-emacs-configuration-nix">
+     <title>Custom Emacs in <filename>configuration.nix</filename></title>
+<programlisting><![CDATA[
 {
  environment.systemPackages = [
    # [...]
@@ -301,60 +284,59 @@ nix-env -f "<nixpkgs>" -qaP -A emacsPackagesNg.orgPackages
   ];
 }
 ]]></programlisting>
-        </example>
-      </para>
+    </example>
+   </para>
 
-      <para>
-        In this case, the next <command>nixos-rebuild switch</command>
-        will take care of adding your <command>emacs</command> to the
-        <varname>PATH</varname> environment variable
-        (see <xref linkend="sec-changing-config" />).
-      </para>
+   <para>
+    In this case, the next <command>nixos-rebuild switch</command> will take
+    care of adding your <command>emacs</command> to the <varname>PATH</varname>
+    environment variable (see <xref linkend="sec-changing-config" />).
+   </para>
 
 <!-- fixme: i think the following is better done with config.nix
 https://nixos.org/nixpkgs/manual/#sec-modify-via-packageOverrides
 -->
-      <para>
-        If you are not on NixOS or want to install this particular
-        Emacs only for yourself, you can do so by adding it to your
-        <filename>~/.config/nixpkgs/config.nix</filename>
-        (see <link xlink:href="http://nixos.org/nixpkgs/manual/#sec-modify-via-packageOverrides">Nixpkgs manual</link>):
-        <example xml:id="module-services-emacs-config-nix">
-          <title>Custom Emacs in <filename>~/.config/nixpkgs/config.nix</filename></title>
-          <programlisting><![CDATA[
+
+   <para>
+    If you are not on NixOS or want to install this particular Emacs only for
+    yourself, you can do so by adding it to your
+    <filename>~/.config/nixpkgs/config.nix</filename> (see
+    <link xlink:href="http://nixos.org/nixpkgs/manual/#sec-modify-via-packageOverrides">Nixpkgs
+    manual</link>):
+    <example xml:id="module-services-emacs-config-nix">
+     <title>Custom Emacs in <filename>~/.config/nixpkgs/config.nix</filename></title>
+<programlisting><![CDATA[
 {
   packageOverrides = super: let self = super.pkgs; in {
     myemacs = import /path/to/emacs.nix { pkgs = self; };
   };
 }
 ]]></programlisting>
-        </example>
-      </para>
-
-      <para>
-        In this case, the next
-        <literal>nix-env -f '&lt;nixpkgs&gt;' -iA myemacs</literal>
-        will take care of adding your emacs to the
-        <varname>PATH</varname> environment variable.
-      </para>
-    </section>
-
-    <section xml:id="module-services-emacs-advanced">
-      <title>Advanced Emacs Configuration</title>
+    </example>
+   </para>
 
-      <para>
-        If you want, you can tweak the Emacs package itself from your
-        <filename>emacs.nix</filename>. For example, if you want to
-        have a GTK+3-based Emacs instead of the default GTK+2-based
-        binary and remove the automatically generated
-        <filename>emacs.desktop</filename> (useful is you only use
-        <command>emacsclient</command>), you can change your file
-        <filename>emacs.nix</filename> in this way:
-      </para>
+   <para>
+    In this case, the next <literal>nix-env -f '&lt;nixpkgs&gt;' -iA
+    myemacs</literal> will take care of adding your emacs to the
+    <varname>PATH</varname> environment variable.
+   </para>
+  </section>
 
-      <example xml:id="ex-emacsGtk3Nix">
-        <title>Custom Emacs build</title>
-        <programlisting><![CDATA[
+  <section xml:id="module-services-emacs-advanced">
+   <title>Advanced Emacs Configuration</title>
+
+   <para>
+    If you want, you can tweak the Emacs package itself from your
+    <filename>emacs.nix</filename>. For example, if you want to have a
+    GTK+3-based Emacs instead of the default GTK+2-based binary and remove the
+    automatically generated <filename>emacs.desktop</filename> (useful is you
+    only use <command>emacsclient</command>), you can change your file
+    <filename>emacs.nix</filename> in this way:
+   </para>
+
+   <example xml:id="ex-emacsGtk3Nix">
+    <title>Custom Emacs build</title>
+<programlisting><![CDATA[
 { pkgs ? import <nixpkgs> {} }:
 let
   myEmacs = (pkgs.emacs.override {
@@ -370,161 +352,143 @@ let
   });
 in [...]
 ]]></programlisting>
-      </example>
+   </example>
 
-      <para>
-        After building this file as shown in <xref linkend="ex-emacsNix" />,
-        you will get an GTK3-based Emacs binary pre-loaded with your
-        favorite packages.
-      </para>
-    </section>
+   <para>
+    After building this file as shown in <xref linkend="ex-emacsNix" />, you
+    will get an GTK3-based Emacs binary pre-loaded with your favorite packages.
+   </para>
   </section>
-
-<section xml:id="module-services-emacs-running">
+ </section>
+ <section xml:id="module-services-emacs-running">
   <title>Running Emacs as a Service</title>
+
   <para>
-    <productname>NixOS</productname> provides an optional
-    <command>systemd</command> service which launches
-    <link xlink:href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html">
-      Emacs daemon
-    </link>
-    with the user's login session.
+   <productname>NixOS</productname> provides an optional
+   <command>systemd</command> service which launches
+   <link xlink:href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html">
+   Emacs daemon </link> with the user's login session.
   </para>
 
   <para>
-    <emphasis>Source:</emphasis>
-    <filename>modules/services/editors/emacs.nix</filename>
+   <emphasis>Source:</emphasis>
+   <filename>modules/services/editors/emacs.nix</filename>
   </para>
 
   <section xml:id="module-services-emacs-enabling">
-    <title>Enabling the Service</title>
-
-    <para>
-      To install and enable the <command>systemd</command>
-      user service for Emacs daemon, add the following to your
-      <filename>configuration.nix</filename>:
+   <title>Enabling the Service</title>
 
+   <para>
+    To install and enable the <command>systemd</command> user service for Emacs
+    daemon, add the following to your <filename>configuration.nix</filename>:
 <programlisting>
 <xref linkend="opt-services.emacs.enable"/> = true;
 <xref linkend="opt-services.emacs.package"/> = import /home/cassou/.emacs.d { pkgs = pkgs; };
 </programlisting>
-    </para>
-
-    <para>
-      The <varname>services.emacs.package</varname> option allows a
-      custom derivation to be used, for example, one created by
-      <function>emacsWithPackages</function>.
-    </para>
-
-    <para>
-      Ensure that the Emacs server is enabled for your user's Emacs
-      configuration, either by customizing the
-      <varname>server-mode</varname> variable, or by adding
-      <literal>(server-start)</literal> to
-      <filename>~/.emacs.d/init.el</filename>.
-    </para>
-
-    <para>
-      To start the daemon, execute the following:
-
+   </para>
+
+   <para>
+    The <varname>services.emacs.package</varname> option allows a custom
+    derivation to be used, for example, one created by
+    <function>emacsWithPackages</function>.
+   </para>
+
+   <para>
+    Ensure that the Emacs server is enabled for your user's Emacs
+    configuration, either by customizing the <varname>server-mode</varname>
+    variable, or by adding <literal>(server-start)</literal> to
+    <filename>~/.emacs.d/init.el</filename>.
+   </para>
+
+   <para>
+    To start the daemon, execute the following:
 <screen>
 $ nixos-rebuild switch  # to activate the new configuration.nix
 $ systemctl --user daemon-reload        # to force systemd reload
 $ systemctl --user start emacs.service  # to start the Emacs daemon
 </screen>
-
-      The server should now be ready to serve Emacs clients.
-    </para>
-
+    The server should now be ready to serve Emacs clients.
+   </para>
   </section>
 
   <section xml:id="module-services-emacs-starting-client">
-    <title>Starting the client</title>
-    <para>
-      Ensure that the emacs server is enabled, either by customizing
-      the <varname>server-mode</varname> variable, or by adding
-      <literal>(server-start)</literal> to
-      <filename>~/.emacs</filename>.
-    </para>
+   <title>Starting the client</title>
 
-    <para>
-      To connect to the emacs daemon, run one of the following:
-      <programlisting><![CDATA[
+   <para>
+    Ensure that the emacs server is enabled, either by customizing the
+    <varname>server-mode</varname> variable, or by adding
+    <literal>(server-start)</literal> to <filename>~/.emacs</filename>.
+   </para>
+
+   <para>
+    To connect to the emacs daemon, run one of the following:
+<programlisting><![CDATA[
 emacsclient FILENAME
 emacsclient --create-frame  # opens a new frame (window)
 emacsclient --create-frame --tty  # opens a new frame on the current terminal
 ]]></programlisting>
-    </para>
+   </para>
   </section>
 
   <section xml:id="module-services-emacs-editor-variable">
-    <title>Configuring the <varname>EDITOR</varname> variable</title>
-    <!--<title><command>emacsclient</command> as the Default Editor</title>-->
-
-    <para>
-      If <xref linkend="opt-services.emacs.defaultEditor"/> is
-      <literal>true</literal>, the <varname>EDITOR</varname> variable
-      will be set to a wrapper script which launches
-      <command>emacsclient</command>.
-    </para>
-
-    <para>
-      Any setting of <varname>EDITOR</varname> in the shell config
-      files will override
-      <varname>services.emacs.defaultEditor</varname>.
-      To make sure <varname>EDITOR</varname> refers to the Emacs
-      wrapper script, remove any existing <varname>EDITOR</varname>
-      assignment from <filename>.profile</filename>,
-      <filename>.bashrc</filename>, <filename>.zshenv</filename> or
-      any other shell config file.
-    </para>
-
-    <para>
-      If you have formed certain bad habits when editing files,
-      these can be corrected with a shell alias to the wrapper
-      script:
-      <programlisting>alias vi=$EDITOR</programlisting>
-    </para>
+   <title>Configuring the <varname>EDITOR</varname> variable</title>
+
+<!--<title><command>emacsclient</command> as the Default Editor</title>-->
+
+   <para>
+    If <xref linkend="opt-services.emacs.defaultEditor"/> is
+    <literal>true</literal>, the <varname>EDITOR</varname> variable will be set
+    to a wrapper script which launches <command>emacsclient</command>.
+   </para>
+
+   <para>
+    Any setting of <varname>EDITOR</varname> in the shell config files will
+    override <varname>services.emacs.defaultEditor</varname>. To make sure
+    <varname>EDITOR</varname> refers to the Emacs wrapper script, remove any
+    existing <varname>EDITOR</varname> assignment from
+    <filename>.profile</filename>, <filename>.bashrc</filename>,
+    <filename>.zshenv</filename> or any other shell config file.
+   </para>
+
+   <para>
+    If you have formed certain bad habits when editing files, these can be
+    corrected with a shell alias to the wrapper script:
+<programlisting>alias vi=$EDITOR</programlisting>
+   </para>
   </section>
 
   <section xml:id="module-services-emacs-per-user">
-    <title>Per-User Enabling of the Service</title>
-
-    <para>
-      In general, <command>systemd</command> user services
-      are globally enabled by symlinks in
-      <filename>/etc/systemd/user</filename>. In the case where
-      Emacs daemon is not wanted for all users, it is possible to
-      install the service but not globally enable it:
+   <title>Per-User Enabling of the Service</title>
 
+   <para>
+    In general, <command>systemd</command> user services are globally enabled
+    by symlinks in <filename>/etc/systemd/user</filename>. In the case where
+    Emacs daemon is not wanted for all users, it is possible to install the
+    service but not globally enable it:
 <programlisting>
 <xref linkend="opt-services.emacs.enable"/> = false;
 <xref linkend="opt-services.emacs.install"/> = true;
 </programlisting>
-    </para>
-
-    <para>
-      To enable the <command>systemd</command> user service for just
-      the currently logged in user, run:
-
-      <programlisting>systemctl --user enable emacs</programlisting>
-
-      This will add the symlink
-      <filename>~/.config/systemd/user/emacs.service</filename>.
-    </para>
+   </para>
+
+   <para>
+    To enable the <command>systemd</command> user service for just the
+    currently logged in user, run:
+<programlisting>systemctl --user enable emacs</programlisting>
+    This will add the symlink
+    <filename>~/.config/systemd/user/emacs.service</filename>.
+   </para>
   </section>
-</section>
-
-<section xml:id="module-services-emacs-configuring">
+ </section>
+ <section xml:id="module-services-emacs-configuring">
   <title>Configuring Emacs</title>
 
   <para>
-    The Emacs init file should be changed to load the extension
-    packages at startup:
-
-    <example xml:id="module-services-emacs-package-initialisation">
-      <title>Package initialization in <filename>.emacs</filename></title>
-      <programlisting><![CDATA[
+   The Emacs init file should be changed to load the extension packages at
+   startup:
+   <example xml:id="module-services-emacs-package-initialisation">
+    <title>Package initialization in <filename>.emacs</filename></title>
+<programlisting><![CDATA[
 (require 'package)
 
 ;; optional. makes unpure packages archives unavailable
@@ -533,66 +497,71 @@ emacsclient --create-frame --tty  # opens a new frame on the current terminal
 (setq package-enable-at-startup nil)
 (package-initialize)
 ]]></programlisting>
-    </example>
+   </example>
   </para>
 
   <para>
-    After the declarative emacs package configuration has been
-    tested, previously downloaded packages can be cleaned up by
-    removing <filename>~/.emacs.d/elpa</filename> (do make a backup
-    first, in case you forgot a package).
+   After the declarative emacs package configuration has been tested,
+   previously downloaded packages can be cleaned up by removing
+   <filename>~/.emacs.d/elpa</filename> (do make a backup first, in case you
+   forgot a package).
   </para>
 
-  <!--
+<!--
       todo: is it worth documenting customizations for
       server-switch-hook, server-done-hook?
   -->
 
   <section xml:id="module-services-emacs-major-mode">
-    <title>A Major Mode for Nix Expressions</title>
+   <title>A Major Mode for Nix Expressions</title>
 
-    <para>
-      Of interest may be <varname>melpaPackages.nix-mode</varname>,
-      which provides syntax highlighting for the Nix language. This is
-      particularly convenient if you regularly edit Nix files.
-    </para>
+   <para>
+    Of interest may be <varname>melpaPackages.nix-mode</varname>, which
+    provides syntax highlighting for the Nix language. This is particularly
+    convenient if you regularly edit Nix files.
+   </para>
   </section>
 
   <section xml:id="module-services-emacs-man-pages">
-    <title>Accessing man pages</title>
-    <para>
-      You can use <function>woman</function> to get completion of all
-      available man pages. For example, type <literal>M-x woman
-      &lt;RET&gt; nixos-rebuild &lt;RET&gt;.</literal>
-    </para>
+   <title>Accessing man pages</title>
+
+   <para>
+    You can use <function>woman</function> to get completion of all available
+    man pages. For example, type <literal>M-x woman &lt;RET&gt; nixos-rebuild
+    &lt;RET&gt;.</literal>
+   </para>
   </section>
 
   <section xml:id="sec-emacs-docbook-xml">
-    <title>Editing DocBook 5 XML Documents</title>
-    <para>
-      Emacs includes <link
-      xlink:href="https://www.gnu.org/software/emacs/manual/html_node/nxml-mode/Introduction.html">nXML</link>,
-      a major-mode for validating and editing XML documents.
-      When editing DocBook 5.0 documents, such as
-      <link linkend="book-nixos-manual">this one</link>,
-      nXML needs to be configured with the relevant schema, which is
-      not included.
-    </para>
+   <title>Editing DocBook 5 XML Documents</title>
 
-    <para>
-      To install the DocBook 5.0 schemas, either add
-      <varname>pkgs.docbook5</varname> to
-      <xref linkend="opt-environment.systemPackages"/> (<link
+   <para>
+    Emacs includes
+    <link
+      xlink:href="https://www.gnu.org/software/emacs/manual/html_node/nxml-mode/Introduction.html">nXML</link>,
+    a major-mode for validating and editing XML documents. When editing DocBook
+    5.0 documents, such as <link linkend="book-nixos-manual">this one</link>,
+    nXML needs to be configured with the relevant schema, which is not
+    included.
+   </para>
+
+   <para>
+    To install the DocBook 5.0 schemas, either add
+    <varname>pkgs.docbook5</varname> to
+    <xref linkend="opt-environment.systemPackages"/>
+    (<link
       linkend="sec-declarative-package-mgmt">NixOS</link>), or run
-      <literal>nix-env -i pkgs.docbook5</literal>
-      (<link linkend="sec-ad-hoc-packages">Nix</link>).
-    </para>
-
-    <para>
-      Then customize the variable <varname>rng-schema-locating-files</varname> to include <filename>~/.emacs.d/schemas.xml</filename> and put the following text into that file:
-      <example xml:id="ex-emacs-docbook-xml">
-        <title>nXML Schema Configuration (<filename>~/.emacs.d/schemas.xml</filename>)</title>
-        <programlisting language="xml"><![CDATA[
+    <literal>nix-env -i pkgs.docbook5</literal>
+    (<link linkend="sec-ad-hoc-packages">Nix</link>).
+   </para>
+
+   <para>
+    Then customize the variable <varname>rng-schema-locating-files</varname> to
+    include <filename>~/.emacs.d/schemas.xml</filename> and put the following
+    text into that file:
+    <example xml:id="ex-emacs-docbook-xml">
+     <title>nXML Schema Configuration (<filename>~/.emacs.d/schemas.xml</filename>)</title>
+<programlisting language="xml"><![CDATA[
 <?xml version="1.0"?>
 <!--
   To let emacs find this file, evaluate:
@@ -612,9 +581,7 @@ emacsclient --create-frame --tty  # opens a new frame on the current terminal
 </locatingRules>
 ]]></programlisting>
     </example>
-  </para>
-
+   </para>
   </section>
-</section>
-
+ </section>
 </chapter>
diff --git a/nixos/modules/services/hardware/lirc.nix b/nixos/modules/services/hardware/lirc.nix
new file mode 100644
index 000000000000..a66a7fbf495f
--- /dev/null
+++ b/nixos/modules/services/hardware/lirc.nix
@@ -0,0 +1,85 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.lirc;
+in {
+
+  ###### interface
+
+  options = {
+    services.lirc = {
+
+      enable = mkEnableOption "LIRC daemon";
+
+      options = mkOption {
+        type = types.lines;
+        example = ''
+          [lircd]
+          nodaemon = False
+        '';
+        description = "LIRC default options descriped in man:lircd(8) (<filename>lirc_options.conf</filename>)";
+      };
+
+      configs = mkOption {
+        type = types.listOf types.lines;
+        description = "Configurations for lircd to load, see man:lircd.conf(5) for details (<filename>lircd.conf</filename>)";
+      };
+
+      extraArguments = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = "Extra arguments to lircd.";
+      };
+
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    # Note: LIRC executables raises a warning, if lirc_options.conf do not exists
+    environment.etc."lirc/lirc_options.conf".text = cfg.options;
+
+    environment.systemPackages = [ pkgs.lirc ];
+
+    systemd.sockets.lircd = {
+      description = "LIRC daemon socket";
+      wantedBy = [ "sockets.target" ];
+      socketConfig = {
+        ListenStream = "/run/lirc/lircd";
+        SocketUser = "lirc";
+        SocketMode = "0660";
+      };
+    };
+
+    systemd.services.lircd = let
+      configFile = pkgs.writeText "lircd.conf" (builtins.concatStringsSep "\n" cfg.configs);
+    in {
+      description = "LIRC daemon service";
+      after = [ "network.target" ];
+
+      unitConfig.Documentation = [ "man:lircd(8)" ];
+
+      serviceConfig = {
+        RuntimeDirectory = "lirc";
+        ExecStart = ''
+          ${pkgs.lirc}/bin/lircd --nodaemon \
+            ${escapeShellArgs cfg.extraArguments} \
+            ${configFile}
+        '';
+        User = "lirc";
+      };
+    };
+
+    users.users.lirc = {
+      uid = config.ids.uids.lirc;
+      group = "lirc";
+      description = "LIRC user for lircd";
+    };
+
+    users.groups.lirc.gid = config.ids.gids.lirc;
+  };
+}
diff --git a/nixos/modules/services/hardware/ratbagd.nix b/nixos/modules/services/hardware/ratbagd.nix
new file mode 100644
index 000000000000..103e1d2315ae
--- /dev/null
+++ b/nixos/modules/services/hardware/ratbagd.nix
@@ -0,0 +1,32 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.ratbagd;
+in
+{
+  ###### interface
+
+  options = {
+    services.ratbagd = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable ratbagd for configuring gaming mice.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    # Give users access to the "ratbagctl" tool
+    environment.systemPackages = [ pkgs.libratbag ];
+
+    services.dbus.packages = [ pkgs.libratbag ];
+
+    systemd.packages = [ pkgs.libratbag ];
+  };
+}
diff --git a/nixos/modules/services/hardware/trezord.nix b/nixos/modules/services/hardware/trezord.nix
index f2ec00a7d3e1..dfefc1171e62 100644
--- a/nixos/modules/services/hardware/trezord.nix
+++ b/nixos/modules/services/hardware/trezord.nix
@@ -26,15 +26,14 @@ in {
       name = "trezord-udev-rules";
       destination = "/etc/udev/rules.d/51-trezor.rules";
       text = ''
-        # Trezor 1
-        SUBSYSTEM=="usb",  ATTR{idVendor}=="534c",  ATTR{idProduct}=="0001",  MODE="0666", GROUP="dialout", SYMLINK+="trezor%n"
-        KERNEL=="hidraw*", ATTRS{idVendor}=="534c", ATTRS{idProduct}=="0001", MODE="0666", GROUP="dialout"
-
-        # Trezor 2 (Model-T)
-        SUBSYSTEM=="usb",  ATTR{idVendor}=="1209",  ATTR{idProduct}=="53c0",  MODE="0661", GROUP="dialout", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n"
-        SUBSYSTEM=="usb",  ATTR{idVendor}=="1209",  ATTR{idProduct}=="53c1",  MODE="0660", GROUP="dialout", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n"
-        KERNEL=="hidraw*", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="53c1", MODE="0660", GROUP="dialout", TAG+="uaccess", TAG+="udev-acl"
-  ];
+        # TREZOR v1 (One)
+        SUBSYSTEM=="usb", ATTR{idVendor}=="534c", ATTR{idProduct}=="0001", MODE="0666", GROUP="dialout", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n"
+        KERNEL=="hidraw*", ATTRS{idVendor}=="534c", ATTRS{idProduct}=="0001",  MODE="0666", GROUP="dialout", TAG+="uaccess", TAG+="udev-acl"
+
+        # TREZOR v2 (T)
+        SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="53c0", MODE="0661", GROUP="dialout", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n"
+        SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="53c1", MODE="0666", GROUP="dialout", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n"
+        KERNEL=="hidraw*", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="53c1", MODE="0666", GROUP="dialout", TAG+="uaccess", TAG+="udev-acl"
       '';
     });
 
diff --git a/nixos/modules/services/mail/exim.nix b/nixos/modules/services/mail/exim.nix
index 06c4b2811b3f..c05811291359 100644
--- a/nixos/modules/services/mail/exim.nix
+++ b/nixos/modules/services/mail/exim.nix
@@ -2,7 +2,7 @@
 
 let
   inherit (lib) mkIf mkOption singleton types;
-  inherit (pkgs) coreutils exim;
+  inherit (pkgs) coreutils;
   cfg = config.services.exim;
 in
 
@@ -57,6 +57,16 @@ in
         '';
       };
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.exim;
+        defaultText = "pkgs.exim";
+        description = ''
+          The Exim derivation to use.
+          This can be used to enable features such as LDAP or PAM support.
+        '';
+      };
+
     };
 
   };
@@ -74,7 +84,7 @@ in
         spool_directory = ${cfg.spoolDir}
         ${cfg.config}
       '';
-      systemPackages = [ exim ];
+      systemPackages = [ cfg.package ];
     };
 
     users.users = singleton {
@@ -89,14 +99,14 @@ in
       gid = config.ids.gids.exim;
     };
 
-    security.wrappers.exim.source = "${exim}/bin/exim";
+    security.wrappers.exim.source = "${cfg.package}/bin/exim";
 
     systemd.services.exim = {
       description = "Exim Mail Daemon";
       wantedBy = [ "multi-user.target" ];
       restartTriggers = [ config.environment.etc."exim.conf".source ];
       serviceConfig = {
-        ExecStart   = "${exim}/bin/exim -bdf -q30m";
+        ExecStart   = "${cfg.package}/bin/exim -bdf -q30m";
         ExecReload  = "${coreutils}/bin/kill -HUP $MAINPID";
       };
       preStart = ''
diff --git a/nixos/modules/services/mail/rmilter.nix b/nixos/modules/services/mail/rmilter.nix
index 7f38d7570132..0d91b247cd34 100644
--- a/nixos/modules/services/mail/rmilter.nix
+++ b/nixos/modules/services/mail/rmilter.nix
@@ -89,7 +89,7 @@ in
 
       bindSocket.path = mkOption {
        type = types.str;
-       default = "/run/rmilter/rmilter.sock";
+       default = "/run/rmilter.sock";
        description = ''
           Path to Unix domain socket to listen on.
         '';
@@ -193,6 +193,9 @@ in
   config = mkMerge [
 
     (mkIf cfg.enable {
+      warnings = [
+        ''`config.services.rmilter' is deprecated, `rmilter' deprecated and unsupported by upstream, and will be removed from next releases. Use built-in rspamd milter instead.''
+      ];
 
       users.users = singleton {
         name = cfg.user;
diff --git a/nixos/modules/services/mail/rspamd.nix b/nixos/modules/services/mail/rspamd.nix
index b3dae60c2c7e..bba11796a3d3 100644
--- a/nixos/modules/services/mail/rspamd.nix
+++ b/nixos/modules/services/mail/rspamd.nix
@@ -115,36 +115,10 @@ let
     };
   };
 
-  indexOf = default: start: list: e:
-    if list == []
-    then default
-    else if (head list) == e then start
-    else (indexOf default (start + (length (listenStreams (head list).socket))) (tail list) e);
-
-  systemdSocket = indexOf (abort "Socket not found") 0 allSockets;
-
   isUnixSocket = socket: hasPrefix "/" (if (isString socket) then socket else socket.socket);
-  isPort = hasPrefix "*:";
-  isIPv4Socket = hasPrefix "*v4:";
-  isIPv6Socket = hasPrefix "*v6:";
-  isLocalHost = hasPrefix "localhost:";
-  listenStreams = socket:
-    if (isLocalHost socket) then
-      let port = (removePrefix "localhost:" socket);
-      in [ "127.0.0.1:${port}" ] ++ (if config.networking.enableIPv6 then ["[::1]:${port}"] else [])
-    else if (isIPv6Socket socket) then [removePrefix "*v6:" socket]
-    else if (isPort socket) then [removePrefix "*:" socket]
-    else if (isIPv4Socket socket) then
-      throw "error: IPv4 only socket not supported in rspamd with socket activation"
-    else if (length (splitString " " socket)) != 1 then
-      throw "error: string options not supported in rspamd with socket activation"
-    else [socket];
-
-  mkBindSockets = enabled: socks: concatStringsSep "\n  " (flatten (map (each:
-    if cfg.socketActivation && enabled != false then
-      let systemd = (systemdSocket each);
-      in (imap (idx: e: "bind_socket = \"systemd:${toString (systemd + idx - 1)}\";") (listenStreams each.socket))
-    else "bind_socket = \"${each.rawEntry}\";") socks));
+
+  mkBindSockets = enabled: socks: concatStringsSep "\n  "
+    (flatten (map (each: "bind_socket = \"${each.rawEntry}\";") socks));
 
   rspamdConfFile = pkgs.writeText "rspamd.conf"
     ''
@@ -175,18 +149,6 @@ let
       ${cfg.extraConfig}
    '';
 
-  allMappedSockets = flatten (mapAttrsToList (name: value:
-    if value.enable != false
-    then imap (idx: each: {
-        name = "${name}";
-        index = idx;
-        value = each;
-      }) value.bindSockets
-    else []) cfg.workers);
-  allSockets = map (e: e.value) allMappedSockets;
-
-  allSocketNames = map (each: "rspamd-${each.name}-${toString each.index}.socket") allMappedSockets;
-
 in
 
 {
@@ -205,13 +167,6 @@ in
         description = "Whether to run the rspamd daemon in debug mode.";
       };
 
-      socketActivation = mkOption {
-        type = types.bool;
-        description = ''
-          Enable systemd socket activation for rspamd.
-        '';
-      };
-
       workers = mkOption {
         type = with types; attrsOf (submodule workerOpts);
         description = ''
@@ -272,13 +227,6 @@ in
 
   config = mkIf cfg.enable {
 
-    services.rspamd.socketActivation = mkDefault (!opts.bindSocket.isDefined && !opts.bindUISocket.isDefined);
-
-    assertions = [ {
-      assertion = !cfg.socketActivation || !(opts.bindSocket.isDefined || opts.bindUISocket.isDefined);
-      message = "Can't use socketActivation for rspamd when using renamed bind socket options";
-    } ];
-
     # Allow users to run 'rspamc' and 'rspamadm'.
     environment.systemPackages = [ pkgs.rspamd ];
 
@@ -299,17 +247,14 @@ in
     systemd.services.rspamd = {
       description = "Rspamd Service";
 
-      wantedBy = mkIf (!cfg.socketActivation) [ "multi-user.target" ];
-      after = [ "network.target" ] ++
-       (if cfg.socketActivation then allSocketNames else []);
-      requires = mkIf cfg.socketActivation allSocketNames;
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
 
       serviceConfig = {
         ExecStart = "${pkgs.rspamd}/bin/rspamd ${optionalString cfg.debug "-d"} --user=${cfg.user} --group=${cfg.group} --pid=/run/rspamd.pid -c ${rspamdConfFile} -f";
         Restart = "always";
         RuntimeDirectory = "rspamd";
         PrivateTmp = true;
-        Sockets = mkIf cfg.socketActivation (concatStringsSep " " allSocketNames);
       };
 
       preStart = ''
@@ -317,24 +262,10 @@ in
         ${pkgs.coreutils}/bin/chown ${cfg.user}:${cfg.group} /var/lib/rspamd
       '';
     };
-    systemd.sockets = mkIf cfg.socketActivation
-      (listToAttrs (map (each: {
-        name = "rspamd-${each.name}-${toString each.index}";
-        value = {
-          description = "Rspamd socket ${toString each.index} for worker ${each.name}";
-          wantedBy = [ "sockets.target" ];
-          listenStreams = (listenStreams each.value.socket);
-          socketConfig = {
-            BindIPv6Only = mkIf (isIPv6Socket each.value.socket) "ipv6-only";
-            Service = "rspamd.service";
-            SocketUser = mkIf (isUnixSocket each.value.socket) each.value.owner;
-            SocketGroup = mkIf (isUnixSocket each.value.socket) each.value.group;
-            SocketMode = mkIf (isUnixSocket each.value.socket) each.value.mode;
-          };
-        };
-      }) allMappedSockets));
   };
   imports = [
+    (mkRemovedOptionModule [ "services" "rspamd" "socketActivation" ]
+	     "Socket activation never worked correctly and could at this time not be fixed and so was removed")
     (mkRenamedOptionModule [ "services" "rspamd" "bindSocket" ] [ "services" "rspamd" "workers" "normal" "bindSockets" ])
     (mkRenamedOptionModule [ "services" "rspamd" "bindUISocket" ] [ "services" "rspamd" "workers" "controller" "bindSockets" ])
   ];
diff --git a/nixos/modules/services/misc/airsonic.nix b/nixos/modules/services/misc/airsonic.nix
index 083587b8ebb1..01d7b3cf6b9d 100644
--- a/nixos/modules/services/misc/airsonic.nix
+++ b/nixos/modules/services/misc/airsonic.nix
@@ -73,6 +73,24 @@ in {
           ${cfg.home}/transcoders.
         '';
       };
+
+      jvmOptions = mkOption {
+        description = ''
+          Extra command line options for the JVM running AirSonic.
+          Useful for sending jukebox output to non-default alsa
+          devices.
+        '';
+        default = [
+        ];
+        type = types.listOf types.str;
+        example = [
+          "-Djavax.sound.sampled.Clip='#CODEC [plughw:1,0]'"
+          "-Djavax.sound.sampled.Port='#Port CODEC [hw:1]'"
+          "-Djavax.sound.sampled.SourceDataLine='#CODEC [plughw:1,0]'"
+          "-Djavax.sound.sampled.TargetDataLine='#CODEC [plughw:1,0]'"
+        ];
+      };
+
     };
   };
 
@@ -98,6 +116,7 @@ in {
           -Dserver.port=${toString cfg.port} \
           -Dairsonic.contextPath=${cfg.contextPath} \
           -Djava.awt.headless=true \
+          ${toString cfg.jvmOptions} \
           -verbose:gc \
           -jar ${pkgs.airsonic}/webapps/airsonic.war
         '';
diff --git a/nixos/modules/services/misc/emby.nix b/nixos/modules/services/misc/emby.nix
index 64cc9c610ac3..ff68b850cd91 100644
--- a/nixos/modules/services/misc/emby.nix
+++ b/nixos/modules/services/misc/emby.nix
@@ -36,11 +36,18 @@ in
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       preStart = ''
-        test -d ${cfg.dataDir} || {
-          echo "Creating initial Emby data directory in ${cfg.dataDir}"
-          mkdir -p ${cfg.dataDir}
-          chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir}
-          }
+        if [ -d ${cfg.dataDir} ]
+        then
+            for plugin in ${cfg.dataDir}/plugins/*
+            do
+                echo "Correcting permissions of plugin: $plugin"
+                chmod u+w $plugin
+            done
+        else
+            echo "Creating initial Emby data directory in ${cfg.dataDir}"
+            mkdir -p ${cfg.dataDir}
+            chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir}
+        fi
       '';
 
       serviceConfig = {
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index 5bf66354f487..d81aa5643e53 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -162,7 +162,7 @@ let
       makeWrapper ${cfg.packages.gitlab.rubyEnv}/bin/rake $out/bin/gitlab-rake \
           ${concatStrings (mapAttrsToList (name: value: "--set ${name} '${value}' ") gitlabEnv)} \
           --set GITLAB_CONFIG_PATH '${cfg.statePath}/config' \
-          --set PATH '${lib.makeBinPath [ pkgs.nodejs pkgs.gzip pkgs.git pkgs.gnutar config.services.postgresql.package ]}:$PATH' \
+          --set PATH '${lib.makeBinPath [ pkgs.nodejs pkgs.gzip pkgs.git pkgs.gnutar config.services.postgresql.package pkgs.coreutils pkgs.procps ]}:$PATH' \
           --set RAKEOPT '-f ${cfg.packages.gitlab}/share/gitlab/Rakefile' \
           --run 'cd ${cfg.packages.gitlab}/share/gitlab'
      '';
@@ -203,6 +203,7 @@ in {
         default = pkgs.gitlab;
         defaultText = "pkgs.gitlab";
         description = "Reference to the gitlab package";
+        example = "pkgs.gitlab-ee";
       };
 
       packages.gitlab-shell = mkOption {
@@ -501,7 +502,7 @@ in {
     };
 
     systemd.services.gitlab-workhorse = {
-      after = [ "network.target" "gitlab.service" ];
+      after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       environment.HOME = gitlabEnv.HOME;
       environment.GITLAB_SHELL_CONFIG_PATH = gitlabEnv.GITLAB_SHELL_CONFIG_PATH;
@@ -569,9 +570,9 @@ in {
 
         mkdir -p /run/gitlab
         mkdir -p ${cfg.statePath}/log
-        ln -sf ${cfg.statePath}/log /run/gitlab/log
-        ln -sf ${cfg.statePath}/tmp /run/gitlab/tmp
-        ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads
+        [ -d /run/gitlab/log ] || ln -sf ${cfg.statePath}/log /run/gitlab/log
+        [ -d /run/gitlab/tmp ] || ln -sf ${cfg.statePath}/tmp /run/gitlab/tmp
+        [ -d /run/gitlab/uploads ] || ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads
         ln -sf $GITLAB_SHELL_CONFIG_PATH /run/gitlab/shell-config.yml
         chown -R ${cfg.user}:${cfg.group} /run/gitlab
 
@@ -629,6 +630,10 @@ in {
           touch "${cfg.statePath}/db-seeded"
         fi
 
+        # The gitlab:shell:setup regenerates the authorized_keys file so that
+        # the store path to the gitlab-shell in it gets updated
+        ${pkgs.sudo}/bin/sudo -u ${cfg.user} force=yes ${gitlab-rake}/bin/gitlab-rake gitlab:shell:setup RAILS_ENV=production
+
         # The gitlab:shell:create_hooks task seems broken for fixing links
         # so we instead delete all the hooks and create them anew
         rm -f ${cfg.statePath}/repositories/**/*.git/hooks
diff --git a/nixos/modules/services/misc/gitlab.xml b/nixos/modules/services/misc/gitlab.xml
index 67b9f1d3e474..ab99d7bd3a60 100644
--- a/nixos/modules/services/misc/gitlab.xml
+++ b/nixos/modules/services/misc/gitlab.xml
@@ -3,20 +3,22 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="module-services-gitlab">
-
-<title>Gitlab</title>
-
-<para>Gitlab is a feature-rich git hosting service.</para>
-
-<section xml:id="module-services-gitlab-prerequisites"><title>Prerequisites</title>
-
-<para>The gitlab service exposes only an Unix socket at
-<literal>/run/gitlab/gitlab-workhorse.socket</literal>. You need to configure a
-webserver to proxy HTTP requests to the socket.</para>
-
-<para>For instance, the following configuration could be used to use nginx as
-    frontend proxy:
-
+ <title>Gitlab</title>
+ <para>
+  Gitlab is a feature-rich git hosting service.
+ </para>
+ <section xml:id="module-services-gitlab-prerequisites">
+  <title>Prerequisites</title>
+
+  <para>
+   The gitlab service exposes only an Unix socket at
+   <literal>/run/gitlab/gitlab-workhorse.socket</literal>. You need to
+   configure a webserver to proxy HTTP requests to the socket.
+  </para>
+
+  <para>
+   For instance, the following configuration could be used to use nginx as
+   frontend proxy:
 <programlisting>
 <link linkend="opt-services.nginx.enable">services.nginx</link> = {
   <link linkend="opt-services.nginx.enable">enable</link> = true;
@@ -31,21 +33,24 @@ webserver to proxy HTTP requests to the socket.</para>
   };
 };
 </programlisting>
-</para>
-
-</section>
-
-<section xml:id="module-services-gitlab-configuring"><title>Configuring</title>
-
-<para>Gitlab depends on both PostgreSQL and Redis and will automatically enable
-both services. In the case of PostgreSQL, a database and a role will be created.
-</para>
-
-<para>The default state dir is <literal>/var/gitlab/state</literal>. This is where
-all data like the repositories and uploads will be stored.</para>
-
-<para>A basic configuration with some custom settings could look like this:
-
+  </para>
+ </section>
+ <section xml:id="module-services-gitlab-configuring">
+  <title>Configuring</title>
+
+  <para>
+   Gitlab depends on both PostgreSQL and Redis and will automatically enable
+   both services. In the case of PostgreSQL, a database and a role will be
+   created.
+  </para>
+
+  <para>
+   The default state dir is <literal>/var/gitlab/state</literal>. This is where
+   all data like the repositories and uploads will be stored.
+  </para>
+
+  <para>
+   A basic configuration with some custom settings could look like this:
 <programlisting>
 services.gitlab = {
   <link linkend="opt-services.gitlab.enable">enable</link> = true;
@@ -105,40 +110,41 @@ services.gitlab = {
   };
 };
 </programlisting>
-</para>
-
-<para>If you're setting up a new Gitlab instance, generate new secrets. You
-for instance use <literal>tr -dc A-Za-z0-9 &lt; /dev/urandom | head -c 128</literal>
-to generate a new secret. Gitlab encrypts sensitive data stored in the database.
-If you're restoring an existing Gitlab instance, you must specify the secrets
-secret from <literal>config/secrets.yml</literal> located in your Gitlab state
-folder.</para>
-
-<para>Refer to <xref linkend="ch-options" /> for all available configuration
-options for the <link linkend="opt-services.gitlab.enable">services.gitlab</link> module.</para>
-
-</section>
-
-<section xml:id="module-services-gitlab-maintenance"><title>Maintenance</title>
-
-<para>You can run Gitlab's rake tasks with <literal>gitlab-rake</literal>
-which will be available on the system when gitlab is enabled. You will
-have to run the command as the user that you configured to run gitlab
-with.</para>
-
-<para>For example, to backup a Gitlab instance:
-
+  </para>
+
+  <para>
+   If you're setting up a new Gitlab instance, generate new secrets. You for
+   instance use <literal>tr -dc A-Za-z0-9 &lt; /dev/urandom | head -c
+   128</literal> to generate a new secret. Gitlab encrypts sensitive data
+   stored in the database. If you're restoring an existing Gitlab instance, you
+   must specify the secrets secret from <literal>config/secrets.yml</literal>
+   located in your Gitlab state folder.
+  </para>
+
+  <para>
+   Refer to <xref linkend="ch-options" /> for all available configuration
+   options for the
+   <link linkend="opt-services.gitlab.enable">services.gitlab</link> module.
+  </para>
+ </section>
+ <section xml:id="module-services-gitlab-maintenance">
+  <title>Maintenance</title>
+
+  <para>
+   You can run Gitlab's rake tasks with <literal>gitlab-rake</literal> which
+   will be available on the system when gitlab is enabled. You will have to run
+   the command as the user that you configured to run gitlab with.
+  </para>
+
+  <para>
+   For example, to backup a Gitlab instance:
 <programlisting>
 $ sudo -u git -H gitlab-rake gitlab:backup:create
 </programlisting>
-
-A list of all availabe rake tasks can be obtained by running:
-
+   A list of all availabe rake tasks can be obtained by running:
 <programlisting>
 $ sudo -u git -H gitlab-rake -T
 </programlisting>
-</para>
-
-</section>
-
+  </para>
+ </section>
 </chapter>
diff --git a/nixos/modules/services/misc/lidarr.nix b/nixos/modules/services/misc/lidarr.nix
new file mode 100644
index 000000000000..627f22334fe8
--- /dev/null
+++ b/nixos/modules/services/misc/lidarr.nix
@@ -0,0 +1,46 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.lidarr;
+in
+{
+  options = {
+    services.lidarr = {
+      enable = mkEnableOption "Lidarr";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.lidarr = {
+      description = "Lidarr";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        [ ! -d /var/lib/lidarr ] && mkdir -p /var/lib/lidarr
+        chown -R lidarr:lidarr /var/lib/lidarr
+      '';
+
+      serviceConfig = {
+        Type = "simple";
+        User = "lidarr";
+        Group = "lidarr";
+        PermissionsStartOnly = "true";
+        ExecStart = "${pkgs.lidarr}/bin/Lidarr";
+        Restart = "on-failure";
+
+        StateDirectory = "/var/lib/lidarr/";
+        StateDirectoryMode = "0770";
+      };
+    };
+
+    users.users.lidarr = {
+      uid = config.ids.uids.lidarr;
+      home = "/var/lib/lidarr";
+      group = "lidarr";
+    };
+
+    users.groups.lidarr.gid = config.ids.gids.lidarr;
+  };
+}
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index c0eb882c58f3..24379ec27354 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -345,7 +345,6 @@ in
         type = types.listOf types.str;
         default =
           [
-            "$HOME/.nix-defexpr/channels"
             "nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
             "nixos-config=/etc/nixos/configuration.nix"
             "/nix/var/nix/profiles/per-user/root/channels"
@@ -436,7 +435,7 @@ in
 
     # Set up the environment variables for running Nix.
     environment.sessionVariables = cfg.envVars //
-      { NIX_PATH = concatStringsSep ":" cfg.nixPath;
+      { NIX_PATH = cfg.nixPath;
       };
 
     environment.extraInit = optionalString (!isNix20)
@@ -446,6 +445,10 @@ in
         if [ "$USER" != root -o ! -w /nix/var/nix/db ]; then
             export NIX_REMOTE=daemon
         fi
+      '' + ''
+        if [ -e "$HOME/.nix-defexpr/channels" ]; then
+          export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
+        fi
       '';
 
     nix.nrBuildUsers = mkDefault (lib.max 32 cfg.maxJobs);
diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix
index 993b59590bb0..df3e71c80dea 100644
--- a/nixos/modules/services/misc/nixos-manual.nix
+++ b/nixos/modules/services/misc/nixos-manual.nix
@@ -1,91 +1,21 @@
-# This module includes the NixOS man-pages in the system environment,
-# and optionally starts a browser that shows the NixOS manual on one
-# of the virtual consoles.  The latter is useful for the installation
+# This module optionally starts a browser that shows the NixOS manual
+# on one of the virtual consoles which is useful for the installation
 # CD.
 
-{ config, lib, pkgs, baseModules, ... }:
+{ config, lib, pkgs, ... }:
 
 with lib;
 
 let
-
   cfg = config.services.nixosManual;
-
-  /* For the purpose of generating docs, evaluate options with each derivation
-    in `pkgs` (recursively) replaced by a fake with path "\${pkgs.attribute.path}".
-    It isn't perfect, but it seems to cover a vast majority of use cases.
-    Caveat: even if the package is reached by a different means,
-    the path above will be shown and not e.g. `${config.services.foo.package}`. */
-  manual = import ../../../doc/manual rec {
-    inherit pkgs config;
-    version = config.system.nixos.release;
-    revision = "release-${version}";
-    options =
-      let
-        scrubbedEval = evalModules {
-          modules = [ { nixpkgs.localSystem = config.nixpkgs.localSystem; } ] ++ baseModules;
-          args = (config._module.args) // { modules = [ ]; };
-          specialArgs = { pkgs = scrubDerivations "pkgs" pkgs; };
-        };
-        scrubDerivations = namePrefix: pkgSet: mapAttrs
-          (name: value:
-            let wholeName = "${namePrefix}.${name}"; in
-            if isAttrs value then
-              scrubDerivations wholeName value
-              // (optionalAttrs (isDerivation value) { outPath = "\${${wholeName}}"; })
-            else value
-          )
-          pkgSet;
-      in scrubbedEval.options;
-  };
-
-  entry = "${manual.manual}/share/doc/nixos/index.html";
-
-  helpScript = pkgs.writeScriptBin "nixos-help"
-    ''
-      #! ${pkgs.runtimeShell} -e
-      # Finds first executable browser in a colon-separated list.
-      # (see how xdg-open defines BROWSER)
-      browser="$(
-        IFS=: ; for b in $BROWSER; do
-          [ -n "$(type -P "$b" || true)" ] && echo "$b" && break
-        done
-      )"
-      if [ -z "$browser" ]; then
-        browser="$(type -P xdg-open || true)"
-        if [ -z "$browser" ]; then
-          browser="$(type -P w3m || true)"
-          if [ -z "$browser" ]; then
-            echo "$0: unable to start a web browser; please set \$BROWSER"
-            exit 1
-          fi
-        fi
-      fi
-      exec "$browser" ${entry}
-    '';
-
-  desktopItem = pkgs.makeDesktopItem {
-    name = "nixos-manual";
-    desktopName = "NixOS Manual";
-    genericName = "View NixOS documentation in a web browser";
-    icon = "nix-snowflake";
-    exec = "${helpScript}/bin/nixos-help";
-    categories = "System";
-  };
+  cfgd = config.documentation;
 in
 
 {
 
   options = {
 
-    services.nixosManual.enable = mkOption {
-      type = types.bool;
-      default = true;
-      description = ''
-        Whether to build the NixOS manual pages.
-      '';
-    };
-
+    # TODO(@oxij): rename this to `.enable` eventually.
     services.nixosManual.showManual = mkOption {
       type = types.bool;
       default = false;
@@ -114,37 +44,30 @@ in
   };
 
 
-  config = mkIf cfg.enable {
-
-    system.build.manual = manual;
-
-    environment.systemPackages = []
-      ++ optionals config.services.xserver.enable [ desktopItem pkgs.nixos-icons ]
-      ++ optional  config.documentation.man.enable manual.manpages
-      ++ optionals config.documentation.doc.enable [ manual.manual helpScript ];
-
-    boot.extraTTYs = mkIf cfg.showManual ["tty${toString cfg.ttyNumber}"];
-
-    systemd.services = optionalAttrs cfg.showManual
-      { "nixos-manual" =
-        { description = "NixOS Manual";
-          wantedBy = [ "multi-user.target" ];
-          serviceConfig =
-            { ExecStart = "${cfg.browser} ${entry}";
-              StandardInput = "tty";
-              StandardOutput = "tty";
-              TTYPath = "/dev/tty${toString cfg.ttyNumber}";
-              TTYReset = true;
-              TTYVTDisallocate = true;
-              Restart = "always";
-            };
+  config = mkMerge [
+    (mkIf cfg.showManual {
+      assertions = singleton {
+        assertion = cfgd.enable && cfgd.nixos.enable;
+        message   = "Can't enable `services.nixosManual.showManual` without `documentation.nixos.enable`";
+      };
+    })
+    (mkIf (cfg.showManual && cfgd.enable && cfgd.nixos.enable) {
+      boot.extraTTYs = [ "tty${toString cfg.ttyNumber}" ];
+
+      systemd.services."nixos-manual" = {
+        description = "NixOS Manual";
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          ExecStart = "${cfg.browser} ${config.system.build.manual.manualHTMLIndex}";
+          StandardInput = "tty";
+          StandardOutput = "tty";
+          TTYPath = "/dev/tty${toString cfg.ttyNumber}";
+          TTYReset = true;
+          TTYVTDisallocate = true;
+          Restart = "always";
         };
       };
-
-      services.mingetty.helpLine = "\nRun `nixos-help` "
-        + lib.optionalString cfg.showManual "or press <Alt-F${toString cfg.ttyNumber}> "
-        + "for the NixOS manual.";
-
-  };
+    })
+  ];
 
 }
diff --git a/nixos/modules/services/misc/sickbeard.nix b/nixos/modules/services/misc/sickbeard.nix
new file mode 100644
index 000000000000..5cfbbe516ae1
--- /dev/null
+++ b/nixos/modules/services/misc/sickbeard.nix
@@ -0,0 +1,92 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  name = "sickbeard";
+
+  cfg = config.services.sickbeard;
+  sickbeard = cfg.package;
+
+in
+{
+
+  ###### interface
+
+  options = {
+    services.sickbeard = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the sickbeard server.";
+      };
+      package = mkOption {
+        type = types.package;
+        default = pkgs.sickbeard;
+        example = literalExample "pkgs.sickrage";
+        description =''
+          Enable <literal>pkgs.sickrage</literal> or <literal>pkgs.sickgear</literal>
+          as an alternative to SickBeard
+        '';
+      };
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/${name}";
+        description = "Path where to store data files.";
+      };
+      configFile = mkOption {
+        type = types.path;
+        default = "${cfg.dataDir}/config.ini";
+        description = "Path to config file.";
+      };
+      port = mkOption {
+        type = types.ints.u16;
+        default = 8081;
+        description = "Port to bind to.";
+      };
+      user = mkOption {
+        type = types.str;
+        default = name;
+        description = "User to run the service as";
+      };
+      group = mkOption {
+        type = types.str;
+        default = name;
+        description = "Group to run the service as";
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.users = optionalAttrs (cfg.user == name) (singleton {
+      name = name;
+      uid = config.ids.uids.sickbeard;
+      group = cfg.group;
+      description = "sickbeard user";
+      home = cfg.dataDir;
+      createHome = true;
+    });
+
+    users.groups = optionalAttrs (cfg.group == name) (singleton {
+      name = name;
+      gid = config.ids.gids.sickbeard;
+    });
+
+    systemd.services.sickbeard = {
+      description = "Sickbeard Server";
+      wantedBy    = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStart = "${sickbeard}/SickBeard.py --datadir ${cfg.dataDir} --config ${cfg.configFile} --port ${toString cfg.port}";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/taskserver/doc.xml b/nixos/modules/services/misc/taskserver/doc.xml
index 21d25ecf391f..5eac8d9ef784 100644
--- a/nixos/modules/services/misc/taskserver/doc.xml
+++ b/nixos/modules/services/misc/taskserver/doc.xml
@@ -2,101 +2,93 @@
     xmlns:xlink="http://www.w3.org/1999/xlink"
     version="5.0"
     xml:id="module-taskserver">
-
-  <title>Taskserver</title>
+ <title>Taskserver</title>
+ <para>
+  Taskserver is the server component of
+  <link xlink:href="https://taskwarrior.org/">Taskwarrior</link>, a free and
+  open source todo list application.
+ </para>
+ <para>
+  <emphasis>Upstream documentation:</emphasis>
+  <link xlink:href="https://taskwarrior.org/docs/#taskd"/>
+ </para>
+ <section xml:id="module-services-taskserver-configuration">
+  <title>Configuration</title>
 
   <para>
-    Taskserver is the server component of
-    <link xlink:href="https://taskwarrior.org/">Taskwarrior</link>, a free and
-    open source todo list application.
+   Taskserver does all of its authentication via TLS using client certificates,
+   so you either need to roll your own CA or purchase a certificate from a
+   known CA, which allows creation of client certificates. These certificates
+   are usually advertised as <quote>server certificates</quote>.
   </para>
 
   <para>
-    <emphasis>Upstream documentation:</emphasis>
-    <link xlink:href="https://taskwarrior.org/docs/#taskd"/>
+   So in order to make it easier to handle your own CA, there is a helper tool
+   called <command>nixos-taskserver</command> which manages the custom CA along
+   with Taskserver organisations, users and groups.
   </para>
 
-  <section xml:id="module-services-taskserver-configuration">
-    <title>Configuration</title>
-
-    <para>
-      Taskserver does all of its authentication via TLS using client
-      certificates, so you either need to roll your own CA or purchase a
-      certificate from a known CA, which allows creation of client
-      certificates.
-
-      These certificates are usually advertised as
-      <quote>server certificates</quote>.
-    </para>
-
-    <para>
-      So in order to make it easier to handle your own CA, there is a helper
-      tool called <command>nixos-taskserver</command> which manages the custom
-      CA along with Taskserver organisations, users and groups.
-    </para>
-
-    <para>
-      While the client certificates in Taskserver only authenticate whether a
-      user is allowed to connect, every user has its own UUID which identifies
-      it as an entity.
-    </para>
-
-    <para>
-      With <command>nixos-taskserver</command> the client certificate is created
-      along with the UUID of the user, so it handles all of the credentials
-      needed in order to setup the Taskwarrior client to work with a Taskserver.
-    </para>
-  </section>
+  <para>
+   While the client certificates in Taskserver only authenticate whether a user
+   is allowed to connect, every user has its own UUID which identifies it as an
+   entity.
+  </para>
 
-  <section xml:id="module-services-taskserver-nixos-taskserver-tool">
-    <title>The nixos-taskserver tool</title>
+  <para>
+   With <command>nixos-taskserver</command> the client certificate is created
+   along with the UUID of the user, so it handles all of the credentials needed
+   in order to setup the Taskwarrior client to work with a Taskserver.
+  </para>
+ </section>
+ <section xml:id="module-services-taskserver-nixos-taskserver-tool">
+  <title>The nixos-taskserver tool</title>
 
-    <para>
-      Because Taskserver by default only provides scripts to setup users
-      imperatively, the <command>nixos-taskserver</command> tool is used for
-      addition and deletion of organisations along with users and groups defined
-      by <xref linkend="opt-services.taskserver.organisations"/> and as well for
-      imperative set up.
-    </para>
+  <para>
+   Because Taskserver by default only provides scripts to setup users
+   imperatively, the <command>nixos-taskserver</command> tool is used for
+   addition and deletion of organisations along with users and groups defined
+   by <xref linkend="opt-services.taskserver.organisations"/> and as well for
+   imperative set up.
+  </para>
 
-    <para>
-      The tool is designed to not interfere if the command is used to manually
-      set up some organisations, users or groups.
-    </para>
+  <para>
+   The tool is designed to not interfere if the command is used to manually set
+   up some organisations, users or groups.
+  </para>
 
-    <para>
-      For example if you add a new organisation using
-      <command>nixos-taskserver org add foo</command>, the organisation is not
-      modified and deleted no matter what you define in
-      <option>services.taskserver.organisations</option>, even if you're adding
-      the same organisation in that option.
-    </para>
+  <para>
+   For example if you add a new organisation using <command>nixos-taskserver
+   org add foo</command>, the organisation is not modified and deleted no
+   matter what you define in
+   <option>services.taskserver.organisations</option>, even if you're adding
+   the same organisation in that option.
+  </para>
 
-    <para>
-      The tool is modelled to imitate the official <command>taskd</command>
-      command, documentation for each subcommand can be shown by using the
-      <option>--help</option> switch.
-    </para>
-  </section>
-  <section xml:id="module-services-taskserver-declarative-ca-management">
-    <title>Declarative/automatic CA management</title>
+  <para>
+   The tool is modelled to imitate the official <command>taskd</command>
+   command, documentation for each subcommand can be shown by using the
+   <option>--help</option> switch.
+  </para>
+ </section>
+ <section xml:id="module-services-taskserver-declarative-ca-management">
+  <title>Declarative/automatic CA management</title>
 
-    <para>
-      Everything is done according to what you specify in the module options,
-      however in order to set up a Taskwarrior client for synchronisation with a
-      Taskserver instance, you have to transfer the keys and certificates to the
-      client machine.
-    </para>
+  <para>
+   Everything is done according to what you specify in the module options,
+   however in order to set up a Taskwarrior client for synchronisation with a
+   Taskserver instance, you have to transfer the keys and certificates to the
+   client machine.
+  </para>
 
-    <para>
-      This is done using
-      <command>nixos-taskserver user export $orgname $username</command> which
-      is printing a shell script fragment to stdout which can either be used
-      verbatim or adjusted to import the user on the client machine.
-    </para>
+  <para>
+   This is done using <command>nixos-taskserver user export $orgname
+   $username</command> which is printing a shell script fragment to stdout
+   which can either be used verbatim or adjusted to import the user on the
+   client machine.
+  </para>
 
-    <para>
-      For example, let's say you have the following configuration:
+  <para>
+   For example, let's say you have the following configuration:
 <screen>
 {
   <xref linkend="opt-services.taskserver.enable"/> = true;
@@ -105,40 +97,39 @@
   <link linkend="opt-services.taskserver.organisations._name_.users">services.taskserver.organisations.my-company.users</link> = [ "alice" ];
 }
 </screen>
-      This creates an organisation called <literal>my-company</literal> with the
-      user <literal>alice</literal>.
-    </para>
+   This creates an organisation called <literal>my-company</literal> with the
+   user <literal>alice</literal>.
+  </para>
 
-    <para>
-      Now in order to import the <literal>alice</literal> user to another
-      machine <literal>alicebox</literal>, all we need to do is something like
-      this:
+  <para>
+   Now in order to import the <literal>alice</literal> user to another machine
+   <literal>alicebox</literal>, all we need to do is something like this:
 <screen>
 $ ssh server nixos-taskserver user export my-company alice | sh
 </screen>
-      Of course, if no SSH daemon is available on the server you can also copy
-      &amp; paste it directly into a shell.
-    </para>
+   Of course, if no SSH daemon is available on the server you can also copy
+   &amp; paste it directly into a shell.
+  </para>
 
-    <para>
-      After this step the user should be set up and you can start synchronising
-      your tasks for the first time with <command>task sync init</command> on
-      <literal>alicebox</literal>.
-    </para>
+  <para>
+   After this step the user should be set up and you can start synchronising
+   your tasks for the first time with <command>task sync init</command> on
+   <literal>alicebox</literal>.
+  </para>
 
-    <para>
-      Subsequent synchronisation requests merely require the command
-      <command>task sync</command> after that stage.
-    </para>
-  </section>
-  <section xml:id="module-services-taskserver-manual-ca-management">
-    <title>Manual CA management</title>
+  <para>
+   Subsequent synchronisation requests merely require the command <command>task
+   sync</command> after that stage.
+  </para>
+ </section>
+ <section xml:id="module-services-taskserver-manual-ca-management">
+  <title>Manual CA management</title>
 
-    <para>
-      If you set any options within
-      <link linkend="opt-services.taskserver.pki.manual.ca.cert">service.taskserver.pki.manual</link>.*,
-      <command>nixos-taskserver</command> won't issue certificates, but you can
-      still use it for adding or removing user accounts.
-    </para>
-  </section>
+  <para>
+   If you set any options within
+   <link linkend="opt-services.taskserver.pki.manual.ca.cert">service.taskserver.pki.manual</link>.*,
+   <command>nixos-taskserver</command> won't issue certificates, but you can
+   still use it for adding or removing user accounts.
+  </para>
+ </section>
 </chapter>
diff --git a/nixos/modules/services/misc/weechat.nix b/nixos/modules/services/misc/weechat.nix
new file mode 100644
index 000000000000..1fcfb440485d
--- /dev/null
+++ b/nixos/modules/services/misc/weechat.nix
@@ -0,0 +1,56 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.weechat;
+in
+
+{
+  options.services.weechat = {
+    enable = mkEnableOption "weechat";
+    root = mkOption {
+      description = "Weechat state directory.";
+      type = types.str;
+      default = "/var/lib/weechat";
+    };
+    sessionName = mkOption {
+      description = "Name of the `screen' session for weechat.";
+      default = "weechat-screen";
+      type = types.str;
+    };
+    binary = mkOption {
+      description = "Binary to execute (by default \${weechat}/bin/weechat).";
+      example = literalExample ''
+        ''${pkgs.weechat}/bin/weechat-headless
+      '';
+      default = "${pkgs.weechat}/bin/weechat";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users = {
+      groups.weechat = {};
+      users.weechat = {
+        createHome = true;
+        group = "weechat";
+        home = cfg.root;
+        isSystemUser = true;
+      };
+    };
+
+    systemd.services.weechat = {
+      environment.WEECHAT_HOME = cfg.root;
+      serviceConfig = {
+        User = "weechat";
+        Group = "weechat";
+        RemainAfterExit = "yes";
+      };
+      script = "exec ${pkgs.screen}/bin/screen -Dm -S ${cfg.sessionName} ${cfg.binary}";
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network.target" ];
+    };
+  };
+
+  meta.doc = ./weechat.xml;
+}
diff --git a/nixos/modules/services/misc/weechat.xml b/nixos/modules/services/misc/weechat.xml
new file mode 100644
index 000000000000..9c9ee0448c92
--- /dev/null
+++ b/nixos/modules/services/misc/weechat.xml
@@ -0,0 +1,66 @@
+<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="module-services-weechat">
+ <title>WeeChat</title>
+ <para>
+  <link xlink:href="https://weechat.org/">WeeChat</link> is a fast and
+  extensible IRC client.
+ </para>
+ <section>
+  <title>Basic Usage</title>
+
+  <para>
+   By default, the module creates a
+   <literal><link xlink:href="https://www.freedesktop.org/wiki/Software/systemd/">systemd</link></literal>
+   unit which runs the chat client in a detached
+   <literal><link xlink:href="https://www.gnu.org/software/screen/">screen</link></literal>
+   session.
+  </para>
+
+  <para>
+   This can be done by enabling the <literal>weechat</literal> service:
+<programlisting>
+{ ... }:
+
+{
+  <link linkend="opt-services.weechat.enable">services.weechat.enable</link> = true;
+}
+</programlisting>
+  </para>
+
+  <para>
+   The service is managed by a dedicated user named <literal>weechat</literal>
+   in the state directory <literal>/var/lib/weechat</literal>.
+  </para>
+ </section>
+ <section>
+  <title>Re-attaching to WeeChat</title>
+
+  <para>
+   WeeChat runs in a screen session owned by a dedicated user. To explicitly
+   allow your another user to attach to this session, the
+   <literal>screenrc</literal> needs to be tweaked by adding
+   <link xlink:href="https://www.gnu.org/software/screen/manual/html_node/Multiuser.html#Multiuser">multiuser</link>
+   support:
+<programlisting>
+{
+  <link linkend="opt-programs.screen.screenrc">programs.screen.screenrc</link> = ''
+    multiuser on
+    acladd normal_user
+  '';
+}
+</programlisting>
+   Now, the session can be re-attached like this:
+<programlisting>
+screen -r weechat-screen
+</programlisting>
+  </para>
+
+  <para>
+   <emphasis>The session name can be changed using
+   <link linkend="opt-services.weechat.sessionName">services.weechat.sessionName.</link></emphasis>
+  </para>
+ </section>
+</chapter>
diff --git a/nixos/modules/services/monitoring/datadog-agent.nix b/nixos/modules/services/monitoring/datadog-agent.nix
index e545e06b3495..8fd3455a238f 100644
--- a/nixos/modules/services/monitoring/datadog-agent.nix
+++ b/nixos/modules/services/monitoring/datadog-agent.nix
@@ -8,7 +8,6 @@ let
   ddConf = {
     dd_url              = "https://app.datadoghq.com";
     skip_ssl_validation = "no";
-    api_key             = "";
     confd_path          = "/etc/datadog-agent/conf.d";
     additional_checksd  = "/etc/datadog-agent/checks.d";
     use_dogstatsd       = true;
@@ -16,6 +15,7 @@ let
   // optionalAttrs (cfg.logLevel != null) { log_level = cfg.logLevel; }
   // optionalAttrs (cfg.hostname != null) { inherit (cfg) hostname; }
   // optionalAttrs (cfg.tags != null ) { tags = concatStringsSep ", " cfg.tags; }
+  // optionalAttrs (cfg.enableLiveProcessCollection) { process_config = { enabled = "true"; }; }
   // cfg.extraConfig;
 
   # Generate Datadog configuration files for each configured checks.
@@ -125,6 +125,13 @@ in {
       '';
      };
 
+    enableLiveProcessCollection = mkOption {
+      description = ''
+        Whether to enable the live process collection agent.
+      '';
+      default = false;
+      type = types.bool;
+    };
     checks = mkOption {
       description = ''
         Configuration for all Datadog checks. Keys of this attribute
@@ -206,7 +213,6 @@ in {
           Group = "datadog";
           Restart = "always";
           RestartSec = 2;
-          PrivateTmp = true;
         };
         restartTriggers = [ datadogPkg ] ++ map (etc: etc.source) etcfiles;
       } attrs;
@@ -229,6 +235,15 @@ in {
         path = [ datadogPkg pkgs.python pkgs.sysstat pkgs.procps pkgs.jdk ];
         serviceConfig.ExecStart = "${datadogPkg}/bin/dd-jmxfetch";
       });
+
+      datadog-process-agent = lib.mkIf cfg.enableLiveProcessCollection (makeService {
+        description = "Datadog Live Process Agent";
+        path = [ ];
+        script = ''
+          export DD_API_KEY=$(head -n 1 ${cfg.apiKeyFile})
+          ${pkgs.datadog-process-agent}/bin/agent --config /etc/datadog-agent/datadog.yaml
+        '';
+      });
     };
 
     environment.etc = etcfiles;
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index 3e801f9b838d..c30647f5460b 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -235,7 +235,7 @@ in {
         but without GF_ prefix
       '';
       default = {};
-      type = types.attrsOf types.str;
+      type = with types; attrsOf (either str path);
     };
   };
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.xml b/nixos/modules/services/monitoring/prometheus/exporters.xml
index be86abb74b44..7a0a1bdf2c14 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.xml
+++ b/nixos/modules/services/monitoring/prometheus/exporters.xml
@@ -3,13 +3,19 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="module-services-prometheus-exporters">
+ <title>Prometheus exporters</title>
+ <para>
+  Prometheus exporters provide metrics for the
+  <link xlink:href="https://prometheus.io">prometheus monitoring system</link>.
+ </para>
+ <section xml:id="module-services-prometheus-exporters-configuration">
+  <title>Configuration</title>
 
-<title>Prometheus exporters</title>
-
-<para>Prometheus exporters provide metrics for the <link xlink:href="https://prometheus.io">prometheus monitoring system</link>.</para>
-
-<section xml:id="module-services-prometheus-exporters-configuration"><title>Configuration</title>
-  <para>One of the most common exporters is the <link xlink:href="https://github.com/prometheus/node_exporter">node exporter</link>, it provides hardware and OS metrics from the host it's running on. The exporter could be configured as follows:
+  <para>
+   One of the most common exporters is the
+   <link xlink:href="https://github.com/prometheus/node_exporter">node
+   exporter</link>, it provides hardware and OS metrics from the host it's
+   running on. The exporter could be configured as follows:
 <programlisting>
   services.promtheus.exporters.node = {
     enable = true;
@@ -24,43 +30,88 @@
     firewallFilter = "-i br0 -p tcp -m tcp --dport 9100";
   };
 </programlisting>
-It should now serve all metrics from the collectors
-that are explicitly enabled and the ones that are
-<link xlink:href="https://github.com/prometheus/node_exporter#enabled-by-default">enabled by default</link>, via http under <literal>/metrics</literal>. In this example the firewall should just
-allow incoming connections to the exporter's port on the bridge interface <literal>br0</literal>
-(this would have to be configured seperately of course).
-For more information about configuration see <literal>man configuration.nix</literal> or
-search through the <link xlink:href="https://nixos.org/nixos/options.html#prometheus.exporters">available options</link>.
-</para>
-</section>
-<section xml:id="module-services-prometheus-exporters-new-exporter"><title>Adding a new exporter</title>
-  <para>To add a new exporter, it has to be packaged first (see <literal>nixpkgs/pkgs/servers/monitoring/prometheus/</literal> for examples), then a module can be added. The postfix exporter is used in this example:</para>
-<itemizedlist>
-  <listitem>
+   It should now serve all metrics from the collectors that are explicitly
+   enabled and the ones that are
+   <link xlink:href="https://github.com/prometheus/node_exporter#enabled-by-default">enabled
+   by default</link>, via http under <literal>/metrics</literal>. In this
+   example the firewall should just allow incoming connections to the
+   exporter's port on the bridge interface <literal>br0</literal> (this would
+   have to be configured seperately of course). For more information about
+   configuration see <literal>man configuration.nix</literal> or search through
+   the
+   <link xlink:href="https://nixos.org/nixos/options.html#prometheus.exporters">available
+   options</link>.
+  </para>
+ </section>
+ <section xml:id="module-services-prometheus-exporters-new-exporter">
+  <title>Adding a new exporter</title>
+
+  <para>
+   To add a new exporter, it has to be packaged first (see
+   <literal>nixpkgs/pkgs/servers/monitoring/prometheus/</literal> for
+   examples), then a module can be added. The postfix exporter is used in this
+   example:
+  </para>
+
+  <itemizedlist>
+   <listitem>
     <para>
-      Some default options for all exporters are provided by
-      <literal>nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix</literal>:
+     Some default options for all exporters are provided by
+     <literal>nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix</literal>:
     </para>
-  </listitem>
-  <listitem override='none'>
+   </listitem>
+   <listitem override='none'>
     <itemizedlist>
-      <listitem><para><literal>enable</literal></para></listitem>
-      <listitem><para><literal>port</literal></para></listitem>
-      <listitem><para><literal>listenAddress</literal></para></listitem>
-      <listitem><para><literal>extraFlags</literal></para></listitem>
-      <listitem><para><literal>openFirewall</literal></para></listitem>
-      <listitem><para><literal>firewallFilter</literal></para></listitem>
-      <listitem><para><literal>user</literal></para></listitem>
-      <listitem><para><literal>group</literal></para></listitem>
+     <listitem>
+      <para>
+       <literal>enable</literal>
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <literal>port</literal>
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <literal>listenAddress</literal>
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <literal>extraFlags</literal>
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <literal>openFirewall</literal>
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <literal>firewallFilter</literal>
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <literal>user</literal>
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <literal>group</literal>
+      </para>
+     </listitem>
     </itemizedlist>
-  </listitem>
-  <listitem>
-    <para>As there is already a package available, the module can now be added.
-      This is accomplished by adding a new file to the
-      <literal>nixos/modules/services/monitoring/prometheus/exporters/</literal> directory,
-      which will be called postfix.nix and contains all exporter specific options
-      and configuration:
-      <programlisting>
+   </listitem>
+   <listitem>
+    <para>
+     As there is already a package available, the module can now be added. This
+     is accomplished by adding a new file to the
+     <literal>nixos/modules/services/monitoring/prometheus/exporters/</literal>
+     directory, which will be called postfix.nix and contains all exporter
+     specific options and configuration:
+<programlisting>
         # nixpgs/nixos/modules/services/prometheus/exporters/postfix.nix
         { config, lib, pkgs }:
 
@@ -121,15 +172,16 @@ search through the <link xlink:href="https://nixos.org/nixos/options.html#promet
         }
       </programlisting>
     </para>
-  </listitem>
-  <listitem>
+   </listitem>
+   <listitem>
     <para>
-      This should already be enough for the postfix exporter. Additionally one could
-      now add assertions and conditional default values. This can be done in the
-      'meta-module' that combines all exporter definitions and generates the submodules:
-      <literal>nixpkgs/nixos/modules/services/prometheus/exporters.nix</literal>
+     This should already be enough for the postfix exporter. Additionally one
+     could now add assertions and conditional default values. This can be done
+     in the 'meta-module' that combines all exporter definitions and generates
+     the submodules:
+     <literal>nixpkgs/nixos/modules/services/prometheus/exporters.nix</literal>
     </para>
-  </listitem>
-</itemizedlist>
-</section>
+   </listitem>
+  </itemizedlist>
+ </section>
 </chapter>
diff --git a/nixos/modules/services/monitoring/riemann.nix b/nixos/modules/services/monitoring/riemann.nix
index 237de53456f9..13d2b1cc0602 100644
--- a/nixos/modules/services/monitoring/riemann.nix
+++ b/nixos/modules/services/monitoring/riemann.nix
@@ -17,9 +17,9 @@ let
 
   launcher = writeScriptBin "riemann" ''
     #!/bin/sh
-    exec ${jdk}/bin/java ${concatStringsSep "\n" cfg.extraJavaOpts} \
+    exec ${jdk}/bin/java ${concatStringsSep " " cfg.extraJavaOpts} \
       -cp ${classpath} \
-      riemann.bin ${writeText "riemann-config.clj" riemannConfig}
+      riemann.bin ${cfg.configFile}
   '';
 
 in {
@@ -37,7 +37,8 @@ in {
       config = mkOption {
         type = types.lines;
         description = ''
-          Contents of the Riemann configuration file.
+          Contents of the Riemann configuration file. For more complicated
+          config you should use configFile.
         '';
       };
       configFiles = mkOption {
@@ -47,7 +48,15 @@ in {
           Extra files containing Riemann configuration. These files will be
           loaded at runtime by Riemann (with Clojure's
           <literal>load-file</literal> function) at the end of the
-          configuration.
+          configuration if you use the config option, this is ignored if you
+          use configFile.
+        '';
+      };
+      configFile = mkOption {
+        type = types.str;
+        description = ''
+          A Riemann config file. Any files in the same directory as this file
+          will be added to the classpath by Riemann.
         '';
       };
       extraClasspathEntries = mkOption {
@@ -77,6 +86,10 @@ in {
       group = "riemann";
     };
 
+    services.riemann.configFile = mkDefault (
+      writeText "riemann-config.clj" riemannConfig
+    );
+
     systemd.services.riemann = {
       wantedBy = [ "multi-user.target" ];
       path = [ inetutils ];
@@ -84,6 +97,7 @@ in {
         User = "riemann";
         ExecStart = "${launcher}/bin/riemann";
       };
+      serviceConfig.LimitNOFILE = 65536;
     };
 
   };
diff --git a/nixos/modules/services/networking/chrony.nix b/nixos/modules/services/networking/chrony.nix
index cef30661cc33..a363b545d649 100644
--- a/nixos/modules/services/networking/chrony.nix
+++ b/nixos/modules/services/networking/chrony.nix
@@ -3,12 +3,10 @@
 with lib;
 
 let
+  cfg = config.services.chrony;
 
   stateDir = "/var/lib/chrony";
-
-  keyFile = "/etc/chrony.keys";
-
-  cfg = config.services.chrony;
+  keyFile = "${stateDir}/chrony.keys";
 
   configFile = pkgs.writeText "chrony.conf" ''
     ${concatMapStringsSep "\n" (server: "server " + server) cfg.servers}
@@ -19,7 +17,6 @@ let
     }
 
     driftfile ${stateDir}/chrony.drift
-
     keyfile ${keyFile}
 
     ${optionalString (!config.time.hardwareClockInLocalTime) "rtconutc"}
@@ -27,18 +24,11 @@ let
     ${cfg.extraConfig}
   '';
 
-  chronyFlags = "-n -m -u chrony -f ${configFile} ${toString cfg.extraFlags}";
-
+  chronyFlags = "-m -u chrony -f ${configFile} ${toString cfg.extraFlags}";
 in
-
 {
-
-  ###### interface
-
   options = {
-
     services.chrony = {
-
       enable = mkOption {
         default = false;
         description = ''
@@ -83,15 +73,9 @@ in
         description = "Extra flags passed to the chronyd command.";
       };
     };
-
   };
 
-
-  ###### implementation
-
   config = mkIf cfg.enable {
-
-    # Make chronyc available in the system path
     environment.systemPackages = [ pkgs.chrony ];
 
     users.groups = singleton
@@ -113,26 +97,30 @@ in
       { description = "chrony NTP daemon";
 
         wantedBy = [ "multi-user.target" ];
-        wants = [ "time-sync.target" ];
-        before = [ "time-sync.target" ];
-        after = [ "network.target" ];
+        wants    = [ "time-sync.target" ];
+        before   = [ "time-sync.target" ];
+        after    = [ "network.target" ];
         conflicts = [ "ntpd.service" "systemd-timesyncd.service" ];
 
         path = [ pkgs.chrony ];
 
-        preStart =
-          ''
-            mkdir -m 0755 -p ${stateDir}
-            touch ${keyFile}
-            chmod 0640 ${keyFile}
-            chown chrony:chrony ${stateDir} ${keyFile}
-          '';
+        preStart = ''
+          mkdir -m 0755 -p ${stateDir}
+          touch ${keyFile}
+          chmod 0640 ${keyFile}
+          chown chrony:chrony ${stateDir} ${keyFile}
+        '';
 
         serviceConfig =
-          { ExecStart = "${pkgs.chrony}/bin/chronyd ${chronyFlags}";
+          { Type = "forking";
+            ExecStart = "${pkgs.chrony}/bin/chronyd ${chronyFlags}";
+
+            ProtectHome = "yes";
+            ProtectSystem = "full";
+            PrivateTmp = "yes";
+
+            ConditionCapability = "CAP_SYS_TIME";
           };
       };
-
   };
-
 }
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.xml b/nixos/modules/services/networking/dnscrypt-proxy.xml
index a97579202523..f90eef69848c 100644
--- a/nixos/modules/services/networking/dnscrypt-proxy.xml
+++ b/nixos/modules/services/networking/dnscrypt-proxy.xml
@@ -3,67 +3,64 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="sec-dnscrypt-proxy">
-
-  <title>DNSCrypt client proxy</title>
+ <title>DNSCrypt client proxy</title>
+ <para>
+  The DNSCrypt client proxy relays DNS queries to a DNSCrypt enabled upstream
+  resolver. The traffic between the client and the upstream resolver is
+  encrypted and authenticated, mitigating the risk of MITM attacks, DNS
+  poisoning attacks, and third-party snooping (assuming the upstream is
+  trustworthy).
+ </para>
+ <sect1 xml:id="sec-dnscrypt-proxy-configuration">
+  <title>Basic configuration</title>
 
   <para>
-    The DNSCrypt client proxy relays DNS queries to a DNSCrypt enabled
-    upstream resolver. The traffic between the client and the upstream
-    resolver is encrypted and authenticated, mitigating the risk of MITM
-    attacks, DNS poisoning attacks, and third-party snooping (assuming the
-    upstream is trustworthy).
-  </para>
-
-  <sect1 xml:id="sec-dnscrypt-proxy-configuration"><title>Basic configuration</title>
-
-  <para>
-    To enable the client proxy, set
-    <programlisting>
+   To enable the client proxy, set
+<programlisting>
 <xref linkend="opt-services.dnscrypt-proxy.enable"/> = true;
     </programlisting>
   </para>
 
   <para>
-    Enabling the client proxy does not alter the system nameserver; to
-    relay local queries, prepend <literal>127.0.0.1</literal> to
-    <option>networking.nameservers</option>.
+   Enabling the client proxy does not alter the system nameserver; to relay
+   local queries, prepend <literal>127.0.0.1</literal> to
+   <option>networking.nameservers</option>.
   </para>
-
-  </sect1>
-
-  <sect1 xml:id="sec-dnscrypt-proxy-forwarder"><title>As a forwarder for another DNS client</title>
+ </sect1>
+ <sect1 xml:id="sec-dnscrypt-proxy-forwarder">
+  <title>As a forwarder for another DNS client</title>
 
   <para>
-    To run the DNSCrypt proxy client as a forwarder for another
-    DNS client, change the default proxy listening port to a
-    non-standard value and point the other client to it:
-    <programlisting>
+   To run the DNSCrypt proxy client as a forwarder for another DNS client,
+   change the default proxy listening port to a non-standard value and point
+   the other client to it:
+<programlisting>
 <xref linkend="opt-services.dnscrypt-proxy.localPort"/> = 43;
     </programlisting>
   </para>
 
-  <sect2 xml:id="sec-dnscrypt-proxy-forwarder-dsnmasq"><title>dnsmasq</title>
-  <para>
-    <programlisting>
+  <sect2 xml:id="sec-dnscrypt-proxy-forwarder-dsnmasq">
+   <title>dnsmasq</title>
+   <para>
+<programlisting>
 {
   <xref linkend="opt-services.dnsmasq.enable"/> = true;
   <xref linkend="opt-services.dnsmasq.servers"/> = [ "127.0.0.1#43" ];
 }
     </programlisting>
-  </para>
+   </para>
   </sect2>
 
-  <sect2 xml:id="sec-dnscrypt-proxy-forwarder-unbound"><title>unbound</title>
-  <para>
-    <programlisting>
+  <sect2 xml:id="sec-dnscrypt-proxy-forwarder-unbound">
+   <title>unbound</title>
+   <para>
+<programlisting>
 {
   <xref linkend="opt-services.unbound.enable"/> = true;
   <xref linkend="opt-services.unbound.forwardAddresses"/> = [ "127.0.0.1@43" ];
 }
     </programlisting>
-  </para>
+   </para>
   </sect2>
-
-  </sect1>
-
+ </sect1>
 </chapter>
diff --git a/nixos/modules/services/networking/epmd.nix b/nixos/modules/services/networking/epmd.nix
new file mode 100644
index 000000000000..692b75e4f086
--- /dev/null
+++ b/nixos/modules/services/networking/epmd.nix
@@ -0,0 +1,56 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.epmd;
+
+in
+
+{
+  ###### interface
+  options.services.epmd = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable socket activation for Erlang Port Mapper Daemon (epmd),
+        which acts as a name server on all hosts involved in distributed
+        Erlang computations.
+      '';
+    };
+    package = mkOption {
+      type = types.package;
+      default = pkgs.erlang;
+      description = ''
+        The Erlang package to use to get epmd binary. That way you can re-use
+        an Erlang runtime that is already installed for other purposes.
+      '';
+    };
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    systemd.sockets.epmd = rec {
+      description = "Erlang Port Mapper Daemon Activation Socket";
+      wantedBy = [ "sockets.target" ];
+      before = wantedBy;
+      socketConfig = {
+        ListenStream = "4369";
+        Accept = "false";
+      };
+    };
+
+    systemd.services.epmd = {
+      description = "Erlang Port Mapper Daemon";
+      after = [ "network.target" ];
+      requires = [ "epmd.socket" ];
+
+      serviceConfig = {
+        DynamicUser = true;
+        ExecStart = "${cfg.package}/bin/epmd -systemd";
+        Type = "notify";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/hylafax/default.nix b/nixos/modules/services/networking/hylafax/default.nix
new file mode 100644
index 000000000000..4c63b822d165
--- /dev/null
+++ b/nixos/modules/services/networking/hylafax/default.nix
@@ -0,0 +1,29 @@
+{ config, lib, pkgs, ... }:
+
+{
+
+  imports = [
+    ./options.nix
+    ./systemd.nix
+  ];
+
+  config = lib.modules.mkIf config.services.hylafax.enable {
+    environment.systemPackages = [ pkgs.hylafaxplus ];
+    users.users.uucp = {
+      uid = config.ids.uids.uucp;
+      group = "uucp";
+      description = "Unix-to-Unix CoPy system";
+      isSystemUser = true;
+      inherit (config.users.users.nobody) home;
+    };
+    assertions = [{
+      assertion = config.services.hylafax.modems != {};
+      message = ''
+        HylaFAX cannot be used without modems.
+        Please define at least one modem with
+        <option>config.services.hylafax.modems</option>.
+      '';
+    }];
+  };
+
+}
diff --git a/nixos/modules/services/networking/hylafax/faxq-default.nix b/nixos/modules/services/networking/hylafax/faxq-default.nix
new file mode 100644
index 000000000000..a2630ce66b71
--- /dev/null
+++ b/nixos/modules/services/networking/hylafax/faxq-default.nix
@@ -0,0 +1,12 @@
+{ ... }:
+
+# see man:hylafax-config(5)
+
+{
+
+  ModemGroup = [ ''"any:.*"'' ];
+  ServerTracing = "0x78701";
+  SessionTracing = "0x78701";
+  UUCPLockDir = "/var/lock";
+
+}
diff --git a/nixos/modules/services/networking/hylafax/faxq-wait.sh b/nixos/modules/services/networking/hylafax/faxq-wait.sh
new file mode 100755
index 000000000000..8c39e9d20c18
--- /dev/null
+++ b/nixos/modules/services/networking/hylafax/faxq-wait.sh
@@ -0,0 +1,29 @@
+#! @shell@ -e
+
+# skip this if there are no modems at all
+if ! stat -t "@spoolAreaPath@"/etc/config.* >/dev/null 2>&1
+then
+  exit 0
+fi
+
+echo "faxq started, waiting for modem(s) to initialize..."
+
+for i in `seq @timeoutSec@0 -1 0`  # gracefully timeout
+do
+  sleep 0.1
+  # done if status files exist, but don't mention initialization
+  if \
+    stat -t "@spoolAreaPath@"/status/* >/dev/null 2>&1 \
+    && \
+    ! grep --silent --ignore-case 'initializing server' \
+    "@spoolAreaPath@"/status/*
+  then
+    echo "modem(s) apparently ready"
+    exit 0
+  fi
+  # if i reached 0, modems probably failed to initialize
+  if test $i -eq 0
+  then
+    echo "warning: modem initialization timed out"
+  fi
+done
diff --git a/nixos/modules/services/networking/hylafax/hfaxd-default.nix b/nixos/modules/services/networking/hylafax/hfaxd-default.nix
new file mode 100644
index 000000000000..8999dae57f41
--- /dev/null
+++ b/nixos/modules/services/networking/hylafax/hfaxd-default.nix
@@ -0,0 +1,10 @@
+{ ... }:
+
+# see man:hfaxd(8)
+
+{
+
+  ServerTracing = "0x91";
+  XferLogFile = "/clientlog";
+
+}
diff --git a/nixos/modules/services/networking/hylafax/modem-default.nix b/nixos/modules/services/networking/hylafax/modem-default.nix
new file mode 100644
index 000000000000..7529b5b0aafd
--- /dev/null
+++ b/nixos/modules/services/networking/hylafax/modem-default.nix
@@ -0,0 +1,22 @@
+{ pkgs, ... }:
+
+# see man:hylafax-config(5)
+
+{
+
+  TagLineFont = "etc/LiberationSans-25.pcf";
+  TagLineLocale = ''en_US.UTF-8'';
+
+  AdminGroup = "root";  # groups that can change server config
+  AnswerRotary = "fax";  # don't accept anything else but faxes
+  LogFileMode = "0640";
+  PriorityScheduling = true;
+  RecvFileMode = "0640";
+  ServerTracing = "0x78701";
+  SessionTracing = "0x78701";
+  UUCPLockDir = "/var/lock";
+
+  SendPageCmd = ''${pkgs.coreutils}/bin/false'';  # prevent pager transmit
+  SendUUCPCmd = ''${pkgs.coreutils}/bin/false'';  # prevent UUCP transmit
+
+}
diff --git a/nixos/modules/services/networking/hylafax/options.nix b/nixos/modules/services/networking/hylafax/options.nix
new file mode 100644
index 000000000000..4ac6d3fa8432
--- /dev/null
+++ b/nixos/modules/services/networking/hylafax/options.nix
@@ -0,0 +1,375 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  inherit (lib.options) literalExample mkEnableOption mkOption;
+  inherit (lib.types) bool enum int lines loaOf nullOr path str submodule;
+  inherit (lib.modules) mkDefault mkIf mkMerge;
+
+  commonDescr = ''
+    Values can be either strings or integers
+    (which will be added to the config file verbatimly)
+    or lists thereof
+    (which will be translated to multiple
+    lines with the same configuration key).
+    Boolean values are translated to "Yes" or "No".
+    The default contains some reasonable
+    configuration to yield an operational system.
+  '';
+
+  str1 = lib.types.addCheck str (s: s!="");  # non-empty string
+  int1 = lib.types.addCheck int (i: i>0);  # positive integer
+
+  configAttrType =
+    # Options in HylaFAX configuration files can be
+    # booleans, strings, integers, or list thereof
+    # representing multiple config directives with the same key.
+    # This type definition resolves all
+    # those types into a list of strings.
+    let
+      inherit (lib.types) attrsOf coercedTo listOf;
+      innerType = coercedTo bool (x: if x then "Yes" else "No")
+        (coercedTo int (toString) str);
+    in
+      attrsOf (coercedTo innerType lib.singleton (listOf innerType));
+
+  cfg = config.services.hylafax;
+
+  modemConfigOptions = { name, config, ... }: {
+    options = {
+      name = mkOption {
+        type = str1;
+        example = "ttyS1";
+        description = ''
+          Name of modem device,
+          will be searched for in <filename>/dev</filename>.
+        '';
+      };
+      type = mkOption {
+        type = str1;
+        example = "cirrus";
+        description = ''
+          Name of modem configuration file,
+          will be searched for in <filename>config</filename>
+          in the spooling area directory.
+        '';
+      };
+      config = mkOption {
+        type = configAttrType;
+        example = {
+          AreaCode = "49";
+          LocalCode = "30";
+          FAXNumber = "123456";
+          LocalIdentifier = "LostInBerlin";
+        };
+        description = ''
+          Attribute set of values for the given modem.
+          ${commonDescr}
+          Options defined here override options in
+          <option>commonModemConfig</option> for this modem.
+        '';
+      };
+    };
+    config.name = mkDefault name;
+    config.config.Include = [ "config/${config.type}" ];
+  };
+
+  defaultConfig =
+    let
+      inherit (config.security) wrapperDir;
+      inherit (config.services.mail.sendmailSetuidWrapper) program;
+      mkIfDefault = cond: value: mkIf cond (mkDefault value);
+      noWrapper = config.services.mail.sendmailSetuidWrapper==null;
+      # If a sendmail setuid wrapper exists,
+      # we add the path to the default configuration file.
+      # Otherwise, we use `false` to provoke
+      # an error if hylafax tries to use it.
+      c.sendmailPath = mkMerge [
+        (mkIfDefault noWrapper ''${pkgs.coreutils}/bin/false'')
+        (mkIfDefault (!noWrapper) ''${wrapperDir}/${program}'')
+      ];
+      importDefaultConfig = file:
+        lib.attrsets.mapAttrs
+        (lib.trivial.const mkDefault)
+        (import file { inherit pkgs; });
+      c.commonModemConfig = importDefaultConfig ./modem-default.nix;
+      c.faxqConfig = importDefaultConfig ./faxq-default.nix;
+      c.hfaxdConfig = importDefaultConfig ./hfaxd-default.nix;
+    in
+      c;
+
+  localConfig =
+    let
+      c.hfaxdConfig.UserAccessFile = cfg.userAccessFile;
+      c.faxqConfig = lib.attrsets.mapAttrs
+        (lib.trivial.const (v: mkIf (v!=null) v))
+        {
+          AreaCode = cfg.areaCode;
+          CountryCode = cfg.countryCode;
+          LongDistancePrefix = cfg.longDistancePrefix;
+          InternationalPrefix = cfg.internationalPrefix;
+        };
+      c.commonModemConfig = c.faxqConfig;
+    in
+      c;
+
+in
+
+
+{
+
+
+  options.services.hylafax = {
+
+    enable = mkEnableOption ''HylaFAX server'';
+
+    autostart = mkOption {
+      type = bool;
+      default = true;
+      example = false;
+      description = ''
+        Autostart the HylaFAX queue manager at system start.
+        If this is <literal>false</literal>, the queue manager
+        will still be started if there are pending
+        jobs or if a user tries to connect to it.
+      '';
+    };
+
+    countryCode = mkOption {
+      type = nullOr str1;
+      default = null;
+      example = "49";
+      description = ''Country code for server and all modems.'';
+    };
+
+    areaCode = mkOption {
+      type = nullOr str1;
+      default = null;
+      example = "30";
+      description = ''Area code for server and all modems.'';
+    };
+
+    longDistancePrefix = mkOption {
+      type = nullOr str;
+      default = null;
+      example = "0";
+      description = ''Long distance prefix for server and all modems.'';
+    };
+
+    internationalPrefix = mkOption {
+      type = nullOr str;
+      default = null;
+      example = "00";
+      description = ''International prefix for server and all modems.'';
+    };
+
+    spoolAreaPath = mkOption {
+      type = path;
+      default = "/var/spool/fax";
+      description = ''
+        The spooling area will be created/maintained
+        at the location given here.
+      '';
+    };
+
+    userAccessFile = mkOption {
+      type = path;
+      default = "/etc/hosts.hfaxd";
+      description = ''
+        The <filename>hosts.hfaxd</filename>
+        file entry in the spooling area
+        will be symlinked to the location given here.
+        This file must exist and be
+        readable only by the <literal>uucp</literal> user.
+        See hosts.hfaxd(5) for details.
+        This configuration permits access for all users:
+        <literal>
+          environment.etc."hosts.hfaxd" = {
+            mode = "0600";
+            user = "uucp";
+            text = ".*";
+          };
+        </literal>
+        Note that host-based access can be controlled with
+        <option>config.systemd.sockets.hylafax-hfaxd.listenStreams</option>;
+        by default, only 127.0.0.1 is permitted to connect.
+      '';
+    };
+
+    sendmailPath = mkOption {
+      type = path;
+      example = literalExample "''${pkgs.postfix}/bin/sendmail";
+      # '' ;  # fix vim
+      description = ''
+        Path to <filename>sendmail</filename> program.
+        The default uses the local sendmail wrapper
+        (see <option>config.services.mail.sendmailSetuidWrapper</option>),
+        otherwise the <filename>false</filename>
+        binary to cause an error if used.
+      '';
+    };
+
+    hfaxdConfig = mkOption {
+      type = configAttrType;
+      example.RecvqProtection = "0400";
+      description = ''
+        Attribute set of lines for the global
+        hfaxd config file <filename>etc/hfaxd.conf</filename>.
+        ${commonDescr}
+      '';
+    };
+
+    faxqConfig = mkOption {
+      type = configAttrType;
+      example = {
+        InternationalPrefix = "00";
+        LongDistancePrefix = "0";
+      };
+      description = ''
+        Attribute set of lines for the global
+        faxq config file <filename>etc/config</filename>.
+        ${commonDescr}
+      '';
+    };
+
+    commonModemConfig = mkOption {
+      type = configAttrType;
+      example = {
+        InternationalPrefix = "00";
+        LongDistancePrefix = "0";
+      };
+      description = ''
+        Attribute set of default values for
+        modem config files <filename>etc/config.*</filename>.
+        ${commonDescr}
+        Think twice before changing
+        paths of fax-processing scripts.
+      '';
+    };
+
+    modems = mkOption {
+      type = loaOf (submodule [ modemConfigOptions ]);
+      default = {};
+      example.ttyS1 = {
+        type = "cirrus";
+        config = {
+          FAXNumber = "123456";
+          LocalIdentifier = "Smith";
+        };
+      };
+      description = ''
+        Description of installed modems.
+        At least on modem must be defined
+        to enable the HylaFAX server.
+      '';
+    };
+
+    spoolExtraInit = mkOption {
+      type = lines;
+      default = "";
+      example = ''chmod 0755 .  # everyone may read my faxes'';
+      description = ''
+        Additional shell code that is executed within the
+        spooling area directory right after its setup.
+      '';
+    };
+
+    faxcron.enable.spoolInit = mkEnableOption ''
+      Purge old files from the spooling area with
+      <filename>faxcron</filename>
+      each time the spooling area is initialized.
+    '';
+    faxcron.enable.frequency = mkOption {
+      type = nullOr str1;
+      default = null;
+      example = "daily";
+      description = ''
+        Purge old files from the spooling area with
+        <filename>faxcron</filename> with the given frequency
+        (see systemd.time(7)).
+      '';
+    };
+    faxcron.infoDays = mkOption {
+      type = int1;
+      default = 30;
+      description = ''
+        Set the expiration time for data in the
+        remote machine information directory in days.
+      '';
+    };
+    faxcron.logDays = mkOption {
+      type = int1;
+      default = 30;
+      description = ''
+        Set the expiration time for
+        session trace log files in days.
+      '';
+    };
+    faxcron.rcvDays = mkOption {
+      type = int1;
+      default = 7;
+      description = ''
+        Set the expiration time for files in
+        the received facsimile queue in days.
+      '';
+    };
+
+    faxqclean.enable.spoolInit = mkEnableOption ''
+      Purge old files from the spooling area with
+      <filename>faxqclean</filename>
+      each time the spooling area is initialized.
+    '';
+    faxqclean.enable.frequency = mkOption {
+      type = nullOr str1;
+      default = null;
+      example = "daily";
+      description = ''
+        Purge old files from the spooling area with
+        <filename>faxcron</filename> with the given frequency
+        (see systemd.time(7)).
+      '';
+    };
+    faxqclean.archiving = mkOption {
+      type = enum [ "never" "as-flagged" "always" ];
+      default = "as-flagged";
+      example = "always";
+      description = ''
+        Enable or suppress job archiving:
+        <literal>never</literal> disables job archiving,
+        <literal>as-flagged</literal> archives jobs that
+        have been flagged for archiving by sendfax,
+        <literal>always</literal> forces archiving of all jobs.
+        See also sendfax(1) and faxqclean(8).
+      '';
+    };
+    faxqclean.doneqMinutes = mkOption {
+      type = int1;
+      default = 15;
+      example = literalExample ''24*60'';
+      description = ''
+        Set the job
+        age threshold (in minutes) that controls how long
+        jobs may reside in the doneq directory.
+      '';
+    };
+    faxqclean.docqMinutes = mkOption {
+      type = int1;
+      default = 60;
+      example = literalExample ''24*60'';
+      description = ''
+        Set the document
+        age threshold (in minutes) that controls how long
+        unreferenced files may reside in the docq directory.
+      '';
+    };
+
+  };
+
+
+  config.services.hylafax =
+    mkIf
+    (config.services.hylafax.enable)
+    (mkMerge [ defaultConfig localConfig ])
+  ;
+
+}
diff --git a/nixos/modules/services/networking/hylafax/spool.sh b/nixos/modules/services/networking/hylafax/spool.sh
new file mode 100755
index 000000000000..31e930e8c597
--- /dev/null
+++ b/nixos/modules/services/networking/hylafax/spool.sh
@@ -0,0 +1,111 @@
+#! @shell@ -e
+
+# The following lines create/update the HylaFAX spool directory:
+# Subdirectories/files with persistent data are kept,
+# other directories/files are removed/recreated,
+# mostly from the template spool
+# directory in the HylaFAX package.
+
+# This block explains how the spool area is
+# derived from the spool template in the HylaFAX package:
+#
+#                  + capital letter: directory; file otherwise
+#                  + P/p: persistent directory
+#                  + F/f: directory with symlinks per entry
+#                  + T/t: temporary data
+#                  + S/s: single symlink into package
+#                  |
+#                  | + u: change ownership to uucp:uucp
+#                  | + U: ..also change access mode to user-only
+#                  | |
+# archive          P U
+# bin              S
+# client           T u  (client connection info)
+# config           S
+# COPYRIGHT        s
+# dev              T u  (maybe some FIFOs)
+# docq             P U
+# doneq            P U
+# etc              F    contains customized config files!
+# etc/hosts.hfaxd  f
+# etc/xferfaxlog   f
+# info             P u  (database of called devices)
+# log              P u  (communication logs)
+# pollq            P U
+# recvq            P u
+# sendq            P U
+# status           T u  (modem status info files)
+# tmp              T U
+
+
+shopt -s dotglob  # if bash sees "*", it also includes dot files
+lnsym () { ln --symbol "$@" ; }
+lnsymfrc () { ln --symbolic --force "$@" ; }
+cprd () { cp --remove-destination "$@" ; }
+update () { install --owner=@faxuser@ --group=@faxgroup@ "$@" ; }
+
+
+## create/update spooling area
+
+update --mode=0750 -d "@spoolAreaPath@"
+cd "@spoolAreaPath@"
+
+persist=(archive docq doneq info log pollq recvq sendq)
+
+# remove entries that don't belong here
+touch dummy  # ensure "*" resolves to something
+for k in *
+do
+  keep=0
+  for j in "${persist[@]}" xferfaxlog clientlog faxcron.lastrun
+  do
+    if test "$k" == "$j"
+    then
+      keep=1
+      break
+    fi
+  done
+  if test "$keep" == "0"
+  then
+    rm --recursive "$k"
+  fi
+done
+
+# create persistent data directories (unless they exist already)
+update --mode=0700 -d "${persist[@]}"
+chmod 0755 info log recvq
+
+# create ``xferfaxlog``, ``faxcron.lastrun``, ``clientlog``
+touch clientlog faxcron.lastrun xferfaxlog
+chown @faxuser@:@faxgroup@ clientlog faxcron.lastrun xferfaxlog
+
+# create symlinks for frozen directories/files
+lnsym --target-directory=. "@hylafax@"/spool/{COPYRIGHT,bin,config}
+
+# create empty temporary directories
+update --mode=0700 -d client dev status
+update -d tmp
+
+
+## create and fill etc
+
+install -d "@spoolAreaPath@/etc"
+cd "@spoolAreaPath@/etc"
+
+# create symlinks to all files in template's etc
+lnsym --target-directory=. "@hylafax@/spool/etc"/*
+
+# set LOCKDIR in setup.cache
+sed --regexp-extended 's|^(UUCP_LOCKDIR=).*$|\1'"'@lockPath@'|g" --in-place setup.cache
+
+# etc/{xferfaxlog,lastrun} are stored in the spool root
+lnsymfrc --target-directory=. ../xferfaxlog
+lnsymfrc --no-target-directory ../faxcron.lastrun lastrun
+
+# etc/hosts.hfaxd is provided by the NixOS configuration
+lnsymfrc --no-target-directory "@userAccessFile@" hosts.hfaxd
+
+# etc/config and etc/config.${DEVID} must be copied:
+# hfaxd reads these file after locking itself up in a chroot
+cprd --no-target-directory "@globalConfigPath@" config
+cprd --target-directory=. "@modemConfigPath@"/*
diff --git a/nixos/modules/services/networking/hylafax/systemd.nix b/nixos/modules/services/networking/hylafax/systemd.nix
new file mode 100644
index 000000000000..91d9c1a37da6
--- /dev/null
+++ b/nixos/modules/services/networking/hylafax/systemd.nix
@@ -0,0 +1,249 @@
+{ config, lib, pkgs, ... }:
+
+
+let
+
+  inherit (lib) mkIf mkMerge;
+  inherit (lib) concatStringsSep optionalString;
+
+  cfg = config.services.hylafax;
+  mapModems = lib.flip map (lib.attrValues cfg.modems);
+
+  mkConfigFile = name: conf:
+    # creates hylafax config file,
+    # makes sure "Include" is listed *first*
+    let
+      mkLines = conf:
+        (lib.concatLists
+        (lib.flip lib.mapAttrsToList conf
+        (k: map (v: ''${k}: ${v}'')
+      )));
+      include = mkLines { Include = conf.Include or []; };
+      other = mkLines ( conf // { Include = []; } );
+    in
+      pkgs.writeText ''hylafax-config${name}''
+      (concatStringsSep "\n" (include ++ other));
+
+  globalConfigPath = mkConfigFile "" cfg.faxqConfig;
+
+  modemConfigPath =
+    let
+      mkModemConfigFile = { config, name, ... }:
+        mkConfigFile ''.${name}''
+        (cfg.commonModemConfig // config);
+      mkLine = { name, type, ... }@modem: ''
+        # check if modem config file exists:
+        test -f "${pkgs.hylafaxplus}/spool/config/${type}"
+        ln \
+          --symbolic \
+          --no-target-directory \
+          "${mkModemConfigFile modem}" \
+          "$out/config.${name}"
+      '';
+    in
+      pkgs.runCommand "hylafax-config-modems" {}
+      ''mkdir --parents "$out/" ${concatStringsSep "\n" (mapModems mkLine)}'';
+
+  setupSpoolScript = pkgs.substituteAll {
+    name = "hylafax-setup-spool.sh";
+    src = ./spool.sh;
+    isExecutable = true;
+    inherit (pkgs.stdenv) shell;
+    hylafax = pkgs.hylafaxplus;
+    faxuser = "uucp";
+    faxgroup = "uucp";
+    lockPath = "/var/lock";
+    inherit globalConfigPath modemConfigPath;
+    inherit (cfg) sendmailPath spoolAreaPath userAccessFile;
+  };
+
+  waitFaxqScript = pkgs.substituteAll {
+    # This script checks the modems status files
+    # and waits until all modems report readiness.
+    name = "hylafax-faxq-wait-start.sh";
+    src = ./faxq-wait.sh;
+    isExecutable = true;
+    timeoutSec = toString 10;
+    inherit (pkgs.stdenv) shell;
+    inherit (cfg) spoolAreaPath;
+  };
+
+  sockets."hylafax-hfaxd" = {
+    description = "HylaFAX server socket";
+    documentation = [ "man:hfaxd(8)" ];
+    wantedBy = [ "multi-user.target" ];
+    listenStreams = [ "127.0.0.1:4559" ];
+    socketConfig.FreeBind = true;
+    socketConfig.Accept = true;
+  };
+
+  paths."hylafax-faxq" = {
+    description = "HylaFAX queue manager sendq watch";
+    documentation = [ "man:faxq(8)" "man:sendq(5)" ];
+    wantedBy = [ "multi-user.target" ];
+    pathConfig.PathExistsGlob = [ ''${cfg.spoolAreaPath}/sendq/q*'' ];
+  };
+
+  timers = mkMerge [
+    (
+      mkIf (cfg.faxcron.enable.frequency!=null)
+      { "hylafax-faxcron".timerConfig.Persistent = true; }
+    )
+    (
+      mkIf (cfg.faxqclean.enable.frequency!=null)
+      { "hylafax-faxqclean".timerConfig.Persistent = true; }
+    )
+  ];
+
+  hardenService =
+    # Add some common systemd service hardening settings,
+    # but allow each service (here) to override
+    # settings by explicitely setting those to `null`.
+    # More hardening would be nice but makes
+    # customizing hylafax setups very difficult.
+    # If at all, it should only be added along
+    # with some options to customize it.
+    let
+      hardening = {
+        PrivateDevices = true;  # breaks /dev/tty...
+        PrivateNetwork = true;
+        PrivateTmp = true;
+        ProtectControlGroups = true;
+        #ProtectHome = true;  # breaks custom spool dirs
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        #ProtectSystem = "strict";  # breaks custom spool dirs
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+      };
+      filter = key: value: (value != null) || ! (lib.hasAttr key hardening);
+      apply = service: lib.filterAttrs filter (hardening // (service.serviceConfig or {}));
+    in
+      service: service // { serviceConfig = apply service; };
+
+  services."hylafax-spool" = {
+    description = "HylaFAX spool area preparation";
+    documentation = [ "man:hylafax-server(4)" ];
+    script = ''
+      ${setupSpoolScript}
+      cd "${cfg.spoolAreaPath}"
+      ${cfg.spoolExtraInit}
+      if ! test -f "${cfg.spoolAreaPath}/etc/hosts.hfaxd"
+      then
+        echo hosts.hfaxd is missing
+        exit 1
+      fi
+    '';
+    serviceConfig.ExecStop = ''${setupSpoolScript}'';
+    serviceConfig.RemainAfterExit = true;
+    serviceConfig.Type = "oneshot";
+    unitConfig.RequiresMountsFor = [ cfg.spoolAreaPath ];
+  };
+
+  services."hylafax-faxq" = {
+    description = "HylaFAX queue manager";
+    documentation = [ "man:faxq(8)" ];
+    requires = [ "hylafax-spool.service" ];
+    after = [ "hylafax-spool.service" ];
+    wants = mapModems ( { name, ... }: ''hylafax-faxgetty@${name}.service'' );
+    wantedBy = mkIf cfg.autostart [ "multi-user.target" ];
+    serviceConfig.Type = "forking";
+    serviceConfig.ExecStart = ''${pkgs.hylafaxplus}/spool/bin/faxq -q "${cfg.spoolAreaPath}"'';
+    # This delays the "readiness" of this service until
+    # all modems are initialized (or a timeout is reached).
+    # Otherwise, sending a fax with the fax service
+    # stopped will always yield a failed send attempt:
+    # The fax service is started when the job is created with
+    # `sendfax`, but modems need some time to initialize.
+    serviceConfig.ExecStartPost = [ ''${waitFaxqScript}'' ];
+    # faxquit fails if the pipe is already gone
+    # (e.g. the service is already stopping)
+    serviceConfig.ExecStop = ''-${pkgs.hylafaxplus}/spool/bin/faxquit -q "${cfg.spoolAreaPath}"'';
+    # disable some systemd hardening settings
+    serviceConfig.PrivateDevices = null;
+    serviceConfig.RestrictRealtime = null;
+  };
+
+  services."hylafax-hfaxd@" = {
+    description = "HylaFAX server";
+    documentation = [ "man:hfaxd(8)" ];
+    after = [ "hylafax-faxq.service" ];
+    requires = [ "hylafax-faxq.service" ];
+    serviceConfig.StandardInput = "socket";
+    serviceConfig.StandardOutput = "socket";
+    serviceConfig.ExecStart = ''${pkgs.hylafaxplus}/spool/bin/hfaxd -q "${cfg.spoolAreaPath}" -d -I'';
+    unitConfig.RequiresMountsFor = [ cfg.userAccessFile ];
+    # disable some systemd hardening settings
+    serviceConfig.PrivateDevices = null;
+    serviceConfig.PrivateNetwork = null;
+  };
+
+  services."hylafax-faxcron" = rec {
+    description = "HylaFAX spool area maintenance";
+    documentation = [ "man:faxcron(8)" ];
+    after = [ "hylafax-spool.service" ];
+    requires = [ "hylafax-spool.service" ];
+    wantedBy = mkIf cfg.faxcron.enable.spoolInit requires;
+    startAt = mkIf (cfg.faxcron.enable.frequency!=null) cfg.faxcron.enable.frequency;
+    serviceConfig.ExecStart = concatStringsSep " " [
+      ''${pkgs.hylafaxplus}/spool/bin/faxcron''
+      ''-q "${cfg.spoolAreaPath}"''
+      ''-info ${toString cfg.faxcron.infoDays}''
+      ''-log  ${toString cfg.faxcron.logDays}''
+      ''-rcv  ${toString cfg.faxcron.rcvDays}''
+    ];
+  };
+
+  services."hylafax-faxqclean" = rec {
+    description = "HylaFAX spool area queue cleaner";
+    documentation = [ "man:faxqclean(8)" ];
+    after = [ "hylafax-spool.service" ];
+    requires = [ "hylafax-spool.service" ];
+    wantedBy = mkIf cfg.faxqclean.enable.spoolInit requires;
+    startAt = mkIf (cfg.faxqclean.enable.frequency!=null) cfg.faxqclean.enable.frequency;
+    serviceConfig.ExecStart = concatStringsSep " " [
+      ''${pkgs.hylafaxplus}/spool/bin/faxqclean''
+      ''-q "${cfg.spoolAreaPath}"''
+      ''-v''
+      (optionalString (cfg.faxqclean.archiving!="never") ''-a'')
+      (optionalString (cfg.faxqclean.archiving=="always")  ''-A'')
+      ''-j ${toString (cfg.faxqclean.doneqMinutes*60)}''
+      ''-d ${toString (cfg.faxqclean.docqMinutes*60)}''
+    ];
+  };
+
+  mkFaxgettyService = { name, ... }:
+    lib.nameValuePair ''hylafax-faxgetty@${name}'' rec {
+      description = "HylaFAX faxgetty for %I";
+      documentation = [ "man:faxgetty(8)" ];
+      bindsTo = [ "dev-%i.device" ];
+      requires = [ "hylafax-spool.service" ];
+      after = bindsTo ++ requires;
+      before = [ "hylafax-faxq.service" "getty.target" ];
+      unitConfig.StopWhenUnneeded = true;
+      unitConfig.AssertFileNotEmpty = ''${cfg.spoolAreaPath}/etc/config.%I'';
+      serviceConfig.UtmpIdentifier = "%I";
+      serviceConfig.TTYPath = "/dev/%I";
+      serviceConfig.Restart = "always";
+      serviceConfig.KillMode = "process";
+      serviceConfig.IgnoreSIGPIPE = false;
+      serviceConfig.ExecStart = ''-${pkgs.hylafaxplus}/spool/bin/faxgetty -q "${cfg.spoolAreaPath}" /dev/%I'';
+      # faxquit fails if the pipe is already gone
+      # (e.g. the service is already stopping)
+      serviceConfig.ExecStop = ''-${pkgs.hylafaxplus}/spool/bin/faxquit -q "${cfg.spoolAreaPath}" %I'';
+      # disable some systemd hardening settings
+      serviceConfig.PrivateDevices = null;
+      serviceConfig.RestrictRealtime = null;
+    };
+
+  modemServices =
+    lib.listToAttrs (mapModems mkFaxgettyService);
+
+in
+
+{
+  config.systemd = mkIf cfg.enable {
+    inherit sockets timers paths;
+    services = lib.mapAttrs (lib.const hardenService) (services // modemServices);
+  };
+}
diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix
index 3afafaf3fed5..0e9b354cfcaf 100644
--- a/nixos/modules/services/networking/i2pd.nix
+++ b/nixos/modules/services/networking/i2pd.nix
@@ -8,6 +8,17 @@ let
 
   homeDir = "/var/lib/i2pd";
 
+  strOpt = k: v: k + " = " + v;
+  boolOpt = k: v: k + " = " + boolToString v;
+  intOpt = k: v: k + " = " + toString v;
+  lstOpt = k: xs: k + " = " + concatStringsSep "," xs;
+  optionalNullString = o: s: optional (! isNull s) (strOpt o s);
+  optionalNullBool = o: b: optional (! isNull b) (boolOpt o b);
+  optionalNullInt = o: i: optional (! isNull i) (intOpt o i);
+  optionalEmptyList = o: l: optional ([] != l) (lstOpt o l);
+
+  mkEnableTrueOption = name: mkEnableOption name // { default = true; };
+
   mkEndpointOpt = name: addr: port: {
     enable = mkEnableOption name;
     name = mkOption {
@@ -18,42 +29,54 @@ let
     address = mkOption {
       type = types.str;
       default = addr;
-      description = "Bind address for ${name} endpoint. Default: " + addr;
+      description = "Bind address for ${name} endpoint.";
     };
     port = mkOption {
       type = types.int;
       default = port;
-      description = "Bind port for ${name} endoint. Default: " + toString port;
+      description = "Bind port for ${name} endoint.";
     };
   };
 
-  mkKeyedEndpointOpt = name: addr: port: keyFile:
+  i2cpOpts = name: {
+    length = mkOption {
+      type = types.int;
+      description = "Guaranteed minimum hops for ${name} tunnels.";
+      default = 3;
+    };
+    quantity = mkOption {
+      type = types.int;
+      description = "Number of simultaneous ${name} tunnels.";
+      default = 5;
+    };
+  };
+
+  mkKeyedEndpointOpt = name: addr: port: keyloc:
     (mkEndpointOpt name addr port) // {
       keys = mkOption {
-        type = types.str;
-        default = "";
+        type = with types; nullOr str;
+        default = keyloc;
         description = ''
           File to persist ${lib.toUpper name} keys.
         '';
       };
-    };
-
-  commonTunOpts = let
-    i2cpOpts = {
-      length = mkOption {
-        type = types.int;
-        description = "Guaranteed minimum hops.";
-        default = 3;
+      inbound = i2cpOpts name;
+      outbound = i2cpOpts name;
+      latency.min = mkOption {
+        type = with types; nullOr int;
+        description = "Min latency for tunnels.";
+        default = null;
       };
-      quantity = mkOption {
-        type = types.int;
-        description = "Number of simultaneous tunnels.";
-        default = 5;
+      latency.max = mkOption {
+        type = with types; nullOr int;
+        description = "Max latency for tunnels.";
+        default = null;
       };
     };
-  in name: {
-    outbound = i2cpOpts;
-    inbound = i2cpOpts;
+
+  commonTunOpts = name: {
+    outbound = i2cpOpts name;
+    inbound = i2cpOpts name;
     crypto.tagsToSend = mkOption {
       type = types.int;
       description = "Number of ElGamal/AES tags to send.";
@@ -70,94 +93,142 @@ let
     };
   } // mkEndpointOpt name "127.0.0.1" 0;
 
-  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
+  sec = name: "\n[" + name + "]";
+  notice = "# DO NOT EDIT -- this file has been generated automatically.";
+  i2pdConf = let
+    opts = [
+      notice
+      (strOpt "loglevel" cfg.logLevel)
+      (boolOpt "logclftime" cfg.logCLFTime)
+      (boolOpt "ipv4" cfg.enableIPv4)
+      (boolOpt "ipv6" cfg.enableIPv6)
+      (boolOpt "notransit" cfg.notransit)
+      (boolOpt "floodfill" cfg.floodfill)
+      (intOpt "netid" cfg.netid)
+    ] ++ (optionalNullInt "bandwidth" cfg.bandwidth)
+      ++ (optionalNullInt "port" cfg.port)
+      ++ (optionalNullString "family" cfg.family)
+      ++ (optionalNullString "datadir" cfg.dataDir)
+      ++ (optionalNullInt "share" cfg.share)
+      ++ (optionalNullBool "ssu" cfg.ssu)
+      ++ (optionalNullBool "ntcp" cfg.ntcp)
+      ++ (optionalNullString "ntcpproxy" cfg.ntcpProxy)
+      ++ (optionalNullString "ifname" cfg.ifname)
+      ++ (optionalNullString "ifname4" cfg.ifname4)
+      ++ (optionalNullString "ifname6" cfg.ifname6)
+      ++ [
+      (sec "limits")
+      (intOpt "transittunnels" cfg.limits.transittunnels)
+      (intOpt "coresize" cfg.limits.coreSize)
+      (intOpt "openfiles" cfg.limits.openFiles)
+      (intOpt "ntcphard" cfg.limits.ntcpHard)
+      (intOpt "ntcpsoft" cfg.limits.ntcpSoft)
+      (intOpt "ntcpthreads" cfg.limits.ntcpThreads)
+      (sec "upnp")
+      (boolOpt "enabled" cfg.upnp.enable)
+      (sec "precomputation")
+      (boolOpt "elgamal" cfg.precomputation.elgamal)
+      (sec "reseed")
+      (boolOpt "verify" cfg.reseed.verify)
+    ] ++ (optionalNullString "file" cfg.reseed.file)
+      ++ (optionalEmptyList "urls" cfg.reseed.urls)
+      ++ (optionalNullString "floodfill" cfg.reseed.floodfill)
+      ++ (optionalNullString "zipfile" cfg.reseed.zipfile)
+      ++ (optionalNullString "proxy" cfg.reseed.proxy)
+      ++ [
+      (sec "trust")
+      (boolOpt "enabled" cfg.trust.enable)
+      (boolOpt "hidden" cfg.trust.hidden)
+    ] ++ (optionalEmptyList "routers" cfg.trust.routers)
+      ++ (optionalNullString "family" cfg.trust.family)
+      ++ [
+      (sec "websockets")
+      (boolOpt "enabled" cfg.websocket.enable)
+      (strOpt "address" cfg.websocket.address)
+      (intOpt "port" cfg.websocket.port)
+      (sec "exploratory")
+      (intOpt "inbound.length" cfg.exploratory.inbound.length)
+      (intOpt "inbound.quantity" cfg.exploratory.inbound.quantity)
+      (intOpt "outbound.length" cfg.exploratory.outbound.length)
+      (intOpt "outbound.quantity" cfg.exploratory.outbound.quantity)
+      (sec "ntcp2")
+      (boolOpt "enabled" cfg.ntcp2.enable)
+      (boolOpt "published" cfg.ntcp2.published)
+      (intOpt "port" cfg.ntcp2.port)
+      (sec "addressbook")
+      (strOpt "defaulturl" cfg.addressbook.defaulturl)
+    ] ++ (optionalEmptyList "subscriptions" cfg.addressbook.subscriptions)
+      ++ (flip map
       (collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto)
-      (proto: ''
-        [${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" ''
-    # DO NOT EDIT -- this file has been generated automatically.
-    ${flip concatMapStrings
+      (proto: let protoOpts = [
+        (sec proto.name)
+        (boolOpt "enabled" proto.enable)
+        (strOpt "address" proto.address)
+        (intOpt "port" proto.port)
+        ] ++ (if proto ? keys then optionalNullString "keys" proto.keys else [])
+        ++ (if proto ? auth then optionalNullBool "auth" proto.auth else [])
+        ++ (if proto ? user then optionalNullString "user" proto.user else [])
+        ++ (if proto ? pass then optionalNullString "pass" proto.pass else [])
+        ++ (if proto ? strictHeaders then optionalNullBool "strictheaders" proto.strictHeaders else [])
+        ++ (if proto ? hostname then optionalNullString "hostname" proto.hostname else [])
+        ++ (if proto ? outproxy then optionalNullString "outproxy" proto.outproxy else [])
+        ++ (if proto ? outproxyPort then optionalNullInt "outproxyport" proto.outproxyPort else [])
+        ++ (if proto ? outproxyEnable then optionalNullBool "outproxy.enabled" proto.outproxyEnable else []);
+        in (concatStringsSep "\n" protoOpts)
+      ));
+  in
+    pkgs.writeText "i2pd.conf" (concatStringsSep "\n" opts);
+
+  tunnelConf = let opts = [
+    notice
+    (flip map
       (collect (tun: tun ? port && tun ? destination) cfg.outTunnels)
-      (tun: ''
-        [${tun.name}]
-        type = client
-        destination = ${tun.destination}
-        destinationport = ${toString tun.destinationPort}
-        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
+      (tun: let outTunOpts = [
+        (sec tun.name)
+        "type = client"
+        (intOpt "port" tun.port)
+        (strOpt "destination" tun.destination)
+        ] ++ (if tun ? destinationPort then optionalNullInt "destinationport" tun.destinationPort else [])
+        ++ (if tun ? keys then
+            optionalNullString "keys" tun.keys else [])
+        ++ (if tun ? address then
+            optionalNullString "address" tun.address else [])
+        ++ (if tun ? inbound.length then
+            optionalNullInt "inbound.length" tun.inbound.length else [])
+        ++ (if tun ? inbound.quantity then
+            optionalNullInt "inbound.quantity" tun.inbound.quantity else [])
+        ++ (if tun ? outbound.length then
+            optionalNullInt "outbound.length" tun.outbound.length else [])
+        ++ (if tun ? outbound.quantity then
+            optionalNullInt "outbound.quantity" tun.outbound.quantity else [])
+        ++ (if tun ? crypto.tagsToSend then
+            optionalNullInt "crypto.tagstosend" tun.crypto.tagsToSend else []);
+        in concatStringsSep "\n" outTunOpts))
+    (flip map
       (collect (tun: tun ? port && tun ? address) cfg.inTunnels)
-      (tun: ''
-        [${tun.name}]
-        type = server
-        destination = ${tun.destination}
-        keys = ${tun.keys}
-        host = ${tun.address}
-        port = ${toString tun.port}
-        inport = ${toString tun.inPort}
-        accesslist = ${builtins.concatStringsSep "," tun.accessList}
-      '')
-    }
-  '';
+      (tun: let inTunOpts = [
+        (sec tun.name)
+        "type = server"
+        (intOpt "port" tun.port)
+        (strOpt "host" tun.address)
+      ] ++ (if tun ? destination then
+            optionalNullString "destination" tun.destination else [])
+        ++ (if tun ? keys then
+            optionalNullString "keys" tun.keys else [])
+        ++ (if tun ? inPort then
+            optionalNullInt "inport" tun.inPort else [])
+        ++ (if tun ? accessList then
+            optionalEmptyList "accesslist" tun.accessList else []);
+        in concatStringsSep "\n" inTunOpts))];
+    in pkgs.writeText "i2pd-tunnels.conf" opts;
 
   i2pdSh = pkgs.writeScriptBin "i2pd" ''
     #!/bin/sh
     exec ${pkgs.i2pd}/bin/i2pd \
       ${if isNull cfg.address then "" else "--host="+cfg.address} \
+      --service \
       --conf=${i2pdConf} \
-      --tunconf=${i2pdTunnelConf}
+      --tunconf=${tunnelConf}
   '';
 
 in
@@ -170,9 +241,7 @@ in
 
     services.i2pd = {
 
-      enable = mkOption {
-        type = types.bool;
-        default = false;
+      enable = mkEnableOption "I2Pd daemon" // {
         description = ''
           Enables I2Pd as a running service upon activation.
           Please read http://i2pd.readthedocs.io/en/latest/ for further
@@ -192,6 +261,8 @@ in
         '';
       };
 
+      logCLFTime = mkEnableOption "Full CLF-formatted date and time to log";
+
       address = mkOption {
         type = with types; nullOr str;
         default = null;
@@ -200,17 +271,72 @@ in
         '';
       };
 
-      notransit = mkOption {
-        type = types.bool;
-        default = false;
+      family = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Specify a family the router belongs to.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Alternative path to storage of i2pd data (RI, keys, peer profiles, ...)
+        '';
+      };
+
+      share = mkOption {
+        type = types.int;
+        default = 100;
+        description = ''
+          Limit of transit traffic from max bandwidth in percents.
+        '';
+      };
+
+      ifname = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Network interface to bind to.
+        '';
+      };
+
+      ifname4 = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          IPv4 interface to bind to.
+        '';
+      };
+
+      ifname6 = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          IPv6 interface to bind to.
+        '';
+      };
+
+      ntcpProxy = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Proxy URL for NTCP transport.
+        '';
+      };
+
+      ntcp = mkEnableTrueOption "ntcp";
+      ssu = mkEnableTrueOption "ssu";
+
+      notransit = mkEnableOption "notransit" // {
         description = ''
           Tells the router to not accept transit tunnels during startup.
         '';
       };
 
-      floodfill = mkOption {
-        type = types.bool;
-        default = false;
+      floodfill = mkEnableOption "floodfill" // {
         description = ''
           If the router is declared to be unreachable and needs introduction nodes.
         '';
@@ -241,131 +367,178 @@ in
         '';
       };
 
-      enableIPv4 = mkOption {
-        type = types.bool;
-        default = true;
+      enableIPv4 = mkEnableTrueOption "IPv4 connectivity";
+      enableIPv6 = mkEnableOption "IPv6 connectivity";
+      nat = mkEnableTrueOption "NAT bypass";
+
+      upnp.enable = mkEnableOption "UPnP service discovery";
+      upnp.name = mkOption {
+        type = types.str;
+        default = "I2Pd";
         description = ''
-          Enables IPv4 connectivity. Enabled by default.
+          Name i2pd appears in UPnP forwardings list.
         '';
       };
 
-      enableIPv6 = mkOption {
-        type = types.bool;
-        default = false;
+      precomputation.elgamal = mkEnableTrueOption "Precomputed ElGamal tables" // {
         description = ''
-          Enables IPv6 connectivity. Disabled by default.
+          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.
         '';
       };
 
-      nat = mkOption {
-        type = types.bool;
-        default = true;
+      reseed.verify = mkEnableOption "SU3 signature verification";
+
+      reseed.file = mkOption {
+        type = with types; nullOr str;
+        default = null;
         description = ''
-          Assume router is NATed. Enabled by default.
+          Full path to SU3 file to reseed from.
         '';
       };
 
-      upnp = {
-        enable = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Enables UPnP.
-          '';
-        };
+      reseed.urls = mkOption {
+        type = with types; listOf str;
+        default = [];
+        description = ''
+          Reseed URLs.
+        '';
+      };
 
-        name = mkOption {
-          type = types.str;
-          default = "I2Pd";
-          description = ''
-            Name i2pd appears in UPnP forwardings list.
-          '';
-        };
+      reseed.floodfill = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Path to router info of floodfill to reseed from.
+        '';
       };
 
-      precomputation.elgamal = mkOption {
-        type = types.bool;
-        default = true;
+      reseed.zipfile = mkOption {
+        type = with types; nullOr str;
+        default = null;
         description = ''
-          Whenever to use precomputated tables for ElGamal.
-          <command>i2pd</command> defaults to <literal>false</literal>
-          to save 64M of memory (and looses some performance).
+          Path to local .zip file to reseed from.
+        '';
+      };
 
-          We default to <literal>true</literal> as that is what most
-          users want anyway.
+      reseed.proxy = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          URL for reseed proxy, supports http/socks.
         '';
       };
 
-      reseed = {
-        verify = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Request SU3 signature verification
-          '';
-        };
+     addressbook.defaulturl = mkOption {
+        type = types.str;
+        default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
+        description = ''
+          AddressBook subscription URL for initial setup
+        '';
+      };
+     addressbook.subscriptions = mkOption {
+        type = with types; listOf str;
+        default = [
+          "http://inr.i2p/export/alive-hosts.txt"
+          "http://i2p-projekt.i2p/hosts.txt"
+          "http://stats.i2p/cgi-bin/newhosts.txt"
+        ];
+        description = ''
+          AddressBook subscription URLs
+        '';
+      };
 
-        file = mkOption {
-          type = types.str;
-          default = "";
-          description = ''
-            Full path to SU3 file to reseed from
-          '';
-        };
+      trust.enable = mkEnableOption "Explicit trust options";
 
-        urls = mkOption {
-          type = with types; listOf str;
-          default = [
-            "https://reseed.i2p-project.de/"
-            "https://i2p.mooo.com/netDb/"
-            "https://netdb.i2p2.no/"
-            "https://us.reseed.i2p2.no:444/"
-            "https://uk.reseed.i2p2.no:444/"
-            "https://i2p.manas.ca:8443/"
-          ];
-          description = ''
-            Reseed URLs
-          '';
-        };
+      trust.family = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Router Familiy to trust for first hops.
+        '';
       };
 
-      addressbook = {
-       defaulturl = mkOption {
-          type = types.str;
-          default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
-          description = ''
-            AddressBook subscription URL for initial setup
-          '';
-        };
-       subscriptions = mkOption {
-          type = with types; listOf str;
-          default = [
-            "http://inr.i2p/export/alive-hosts.txt"
-            "http://i2p-projekt.i2p/hosts.txt"
-            "http://stats.i2p/cgi-bin/newhosts.txt"
-          ];
-          description = ''
-            AddressBook subscription URLs
-          '';
-        };
+      trust.routers = mkOption {
+        type = with types; listOf str;
+        default = [];
+        description = ''
+          Only connect to the listed routers.
+        '';
+      };
+
+      trust.hidden = mkEnableOption "Router concealment.";
+
+      websocket = mkEndpointOpt "websockets" "127.0.0.1" 7666;
+
+      exploratory.inbound = i2cpOpts "exploratory";
+      exploratory.outbound = i2cpOpts "exploratory";
+
+      ntcp2.enable = mkEnableTrueOption "NTCP2.";
+      ntcp2.published = mkEnableOption "NTCP2 publication.";
+      ntcp2.port = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Port to listen for incoming NTCP2 connections (0=auto).
+        '';
       };
 
       limits.transittunnels = mkOption {
         type = types.int;
         default = 2500;
         description = ''
-          Maximum number of active transit sessions
+          Maximum number of active transit sessions.
+        '';
+      };
+
+      limits.coreSize = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Maximum size of corefile in Kb (0 - use system limit).
+        '';
+      };
+
+      limits.openFiles = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Maximum number of open files (0 - use system default).
+        '';
+      };
+
+      limits.ntcpHard = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Maximum number of active transit sessions.
+        '';
+      };
+
+      limits.ntcpSoft = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Threshold to start probabalistic backoff with ntcp sessions (default: use system limit).
+        '';
+      };
+
+      limits.ntcpThreads = mkOption {
+        type = types.int;
+        default = 1;
+        description = ''
+          Maximum number of threads used by NTCP DH worker.
         '';
       };
 
       proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // {
-        auth = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Enable authentication for webconsole.
-          '';
-        };
+
+        auth = mkEnableOption "Webconsole authentication";
+
         user = mkOption {
           type = types.str;
           default = "i2pd";
@@ -373,6 +546,7 @@ in
             Username for webconsole access
           '';
         };
+
         pass = mkOption {
           type = types.str;
           default = "i2pd";
@@ -380,11 +554,35 @@ in
             Password for webconsole access.
           '';
         };
+
+        strictHeaders = mkOption {
+          type = with types; nullOr bool;
+          default = null;
+          description = ''
+            Enable strict host checking on WebUI.
+          '';
+        };
+
+        hostname = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = ''
+            Expected hostname for WebUI.
+          '';
+        };
       };
 
-      proto.httpProxy = mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4444 "";
-      proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "")
+      proto.httpProxy = (mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4444 "httpproxy-keys.dat")
+      // {
+        outproxy = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = "Upstream outproxy bind address.";
+        };
+      };
+      proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "socksproxy-keys.dat")
       // {
+        outproxyEnable = mkEnableOption "SOCKS outproxy";
         outproxy = mkOption {
           type = types.str;
           default = "127.0.0.1";
@@ -408,8 +606,8 @@ in
           { name, ... }: {
             options = {
               destinationPort = mkOption {
-                type = types.int;
-                default = 0;
+                type = with types; nullOr int;
+                default = null;
                 description = "Connect to particular port at destination.";
               };
             } // commonTunOpts name;
diff --git a/nixos/modules/services/networking/iperf3.nix b/nixos/modules/services/networking/iperf3.nix
new file mode 100644
index 000000000000..742404a5692f
--- /dev/null
+++ b/nixos/modules/services/networking/iperf3.nix
@@ -0,0 +1,87 @@
+{ config, lib, pkgs, ... }: with lib;
+let
+  cfg = config.services.iperf3;
+
+  api = {
+    enable = mkEnableOption "iperf3 network throughput testing server";
+    port = mkOption {
+      type        = types.ints.u16;
+      default     = 5201;
+      description = "Server port to listen on for iperf3 client requsts.";
+    };
+    affinity = mkOption {
+      type        = types.nullOr types.ints.unsigned;
+      default     = null;
+      description = "CPU affinity for the process.";
+    };
+    bind = mkOption {
+      type        = types.nullOr types.str;
+      default     = null;
+      description = "Bind to the specific interface associated with the given address.";
+    };
+    verbose = mkOption {
+      type        = types.bool;
+      default     = false;
+      description = "Give more detailed output.";
+    };
+    forceFlush = mkOption {
+      type        = types.bool;
+      default     = false;
+      description = "Force flushing output at every interval.";
+    };
+    debug = mkOption {
+      type        = types.bool;
+      default     = false;
+      description = "Emit debugging output.";
+    };
+    rsaPrivateKey = mkOption {
+      type        = types.nullOr types.path;
+      default     = null;
+      description = "Path to the RSA private key (not password-protected) used to decrypt authentication credentials from the client.";
+    };
+    authorizedUsersFile = mkOption {
+      type        = types.nullOr types.path;
+      default     = null;
+      description = "Path to the configuration file containing authorized users credentials to run iperf tests.";
+    };
+    extraFlags = mkOption {
+      type        = types.listOf types.str;
+      default     = [ ];
+      description = "Extra flags to pass to iperf3(1).";
+    };
+  };
+
+  imp = {
+    systemd.services.iperf3 = {
+      description = "iperf3 daemon";
+      unitConfig.Documentation = "man:iperf3(1) https://iperf.fr/iperf-doc.php";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        Restart = "on-failure";
+        RestartSec = 2;
+        DynamicUser = true;
+        PrivateDevices = true;
+        CapabilityBoundingSet = "";
+        NoNewPrivileges = true;
+        ExecStart = ''
+          ${pkgs.iperf3}/bin/iperf \
+            --server \
+            --port ${toString cfg.port} \
+            ${optionalString (cfg.affinity != null) "--affinity ${toString cfg.affinity}"} \
+            ${optionalString (cfg.bind != null) "--bind ${cfg.bind}"} \
+            ${optionalString (cfg.rsaPrivateKey != null) "--rsa-private-key-path ${cfg.rsaPrivateKey}"} \
+            ${optionalString (cfg.authorizedUsersFile != null) "--authorized-users-path ${cfg.authorizedUsersFile}"} \
+            ${optionalString cfg.verbose "--verbose"} \
+            ${optionalString cfg.debug "--debug"} \
+            ${optionalString cfg.forceFlush "--forceflush"} \
+            ${escapeShellArgs cfg.extraFlags}
+        '';
+      };
+    };
+  };
+in {
+  options.services.iperf3 = api;
+  config = mkIf cfg.enable imp;
+}
diff --git a/nixos/modules/services/networking/iwd.nix b/nixos/modules/services/networking/iwd.nix
index eb03d2e1d632..18ed20e28886 100644
--- a/nixos/modules/services/networking/iwd.nix
+++ b/nixos/modules/services/networking/iwd.nix
@@ -22,6 +22,8 @@ in {
 
     systemd.packages = [ pkgs.iwd ];
 
+    systemd.services.iwd.wantedBy = [ "multi-user.target" ];
+
     systemd.tmpfiles.rules = [
       "d /var/lib/iwd 0700 root root -"
     ];
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index d5af4648e8f9..2d76e0676b24 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -406,25 +406,25 @@ in {
       { source = configFile;
         target = "NetworkManager/NetworkManager.conf";
       }
-      { source = "${networkmanager-openvpn}/etc/NetworkManager/VPN/nm-openvpn-service.name";
+      { source = "${networkmanager-openvpn}/lib/NetworkManager/VPN/nm-openvpn-service.name";
         target = "NetworkManager/VPN/nm-openvpn-service.name";
       }
-      { source = "${networkmanager-vpnc}/etc/NetworkManager/VPN/nm-vpnc-service.name";
+      { source = "${networkmanager-vpnc}/lib/NetworkManager/VPN/nm-vpnc-service.name";
         target = "NetworkManager/VPN/nm-vpnc-service.name";
       }
-      { source = "${networkmanager-openconnect}/etc/NetworkManager/VPN/nm-openconnect-service.name";
+      { source = "${networkmanager-openconnect}/lib/NetworkManager/VPN/nm-openconnect-service.name";
         target = "NetworkManager/VPN/nm-openconnect-service.name";
       }
-      { source = "${networkmanager-fortisslvpn}/etc/NetworkManager/VPN/nm-fortisslvpn-service.name";
+      { source = "${networkmanager-fortisslvpn}/lib/NetworkManager/VPN/nm-fortisslvpn-service.name";
         target = "NetworkManager/VPN/nm-fortisslvpn-service.name";
       }
-      { source = "${networkmanager-l2tp}/etc/NetworkManager/VPN/nm-l2tp-service.name";
+      { source = "${networkmanager-l2tp}/lib/NetworkManager/VPN/nm-l2tp-service.name";
         target = "NetworkManager/VPN/nm-l2tp-service.name";
       }
-      { source = "${networkmanager_strongswan}/etc/NetworkManager/VPN/nm-strongswan-service.name";
+      { source = "${networkmanager_strongswan}/lib/NetworkManager/VPN/nm-strongswan-service.name";
         target = "NetworkManager/VPN/nm-strongswan-service.name";
       }
-      { source = "${networkmanager-iodine}/etc/NetworkManager/VPN/nm-iodine-service.name";
+      { source = "${networkmanager-iodine}/lib/NetworkManager/VPN/nm-iodine-service.name";
         target = "NetworkManager/VPN/nm-iodine-service.name";
       }
     ] ++ optional (cfg.appendNameservers == [] || cfg.insertNameservers == [])
diff --git a/nixos/modules/services/networking/nullidentdmod.nix b/nixos/modules/services/networking/nullidentdmod.nix
new file mode 100644
index 000000000000..786b5227dbad
--- /dev/null
+++ b/nixos/modules/services/networking/nullidentdmod.nix
@@ -0,0 +1,34 @@
+{ config, lib, pkgs, ... }: with lib; let
+  cfg = config.services.nullidentdmod;
+
+in {
+  options.services.nullidentdmod = with types; {
+    enable = mkEnableOption "Enable the nullidentdmod identd daemon";
+
+    userid = mkOption {
+      type = nullOr str;
+      description = "User ID to return. Set to null to return a random string each time.";
+      default = null;
+      example = "alice";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.sockets.nullidentdmod = {
+      description = "Socket for identd (NullidentdMod)";
+      listenStreams = [ "113" ];
+      socketConfig.Accept = true;
+      wantedBy = [ "sockets.target" ];
+    };
+
+    systemd.services."nullidentdmod@" = {
+      description = "NullidentdMod service";
+      serviceConfig = {
+        DynamicUser = true;
+        ExecStart = "${pkgs.nullidentdmod}/bin/nullidentdmod${optionalString (cfg.userid != null) " ${cfg.userid}"}";
+        StandardInput = "socket";
+        StandardOutput = "socket";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/shairport-sync.nix b/nixos/modules/services/networking/shairport-sync.nix
index 0b87140b0d8d..36ecb74ffc95 100644
--- a/nixos/modules/services/networking/shairport-sync.nix
+++ b/nixos/modules/services/networking/shairport-sync.nix
@@ -27,7 +27,7 @@ in
       };
 
       arguments = mkOption {
-        default = "-v -o pulse";
+        default = "-v -d pulse";
         description = ''
           Arguments to pass to the daemon. Defaults to a local pulseaudio
           server.
@@ -72,6 +72,7 @@ in
         serviceConfig = {
           User = cfg.user;
           ExecStart = "${pkgs.shairport-sync}/bin/shairport-sync ${cfg.arguments}";
+          RuntimeDirectory = "shairport-sync";
         };
       };
 
diff --git a/nixos/modules/services/networking/teamspeak3.nix b/nixos/modules/services/networking/teamspeak3.nix
index 3703921ff703..410d650b1f64 100644
--- a/nixos/modules/services/networking/teamspeak3.nix
+++ b/nixos/modules/services/networking/teamspeak3.nix
@@ -124,7 +124,7 @@ in
             dbsqlpath=${ts3}/lib/teamspeak/sql/ logpath=${cfg.logPath} \
             voice_ip=${cfg.voiceIP} default_voice_port=${toString cfg.defaultVoicePort} \
             filetransfer_ip=${cfg.fileTransferIP} filetransfer_port=${toString cfg.fileTransferPort} \
-            query_ip=${cfg.queryIP} query_port=${toString cfg.queryPort}
+            query_ip=${cfg.queryIP} query_port=${toString cfg.queryPort} license_accepted=1
         '';
         WorkingDirectory = cfg.dataDir;
         User = user;
diff --git a/nixos/modules/services/networking/zeronet.nix b/nixos/modules/services/networking/zeronet.nix
index 2377cb2c8f11..8b60799891ca 100644
--- a/nixos/modules/services/networking/zeronet.nix
+++ b/nixos/modules/services/networking/zeronet.nix
@@ -12,6 +12,8 @@ let
       log_dir = ${cfg.logDir}
     '' + lib.optionalString (cfg.port != null) ''
       ui_port = ${toString cfg.port}
+    '' + lib.optionalString (cfg.torAlways) ''
+      tor = always
     '' + cfg.extraConfig;
   };
 in with lib; {
@@ -35,13 +37,19 @@ in with lib; {
     port = mkOption {
       type = types.nullOr types.int;
       default = null;
-      example = 15441;
-      description = "Optional zeronet port.";
+      example = 43110;
+      description = "Optional zeronet web UI port.";
     };
 
     tor = mkOption {
       type = types.bool;
       default = false;
+      description = "Use TOR for zeronet traffic where possible.";
+    };
+
+    torAlways = mkOption {
+      type = types.bool;
+      default = false;
       description = "Use TOR for all zeronet traffic.";
     };
 
@@ -60,9 +68,13 @@ in with lib; {
     services.tor = mkIf cfg.tor {
       enable = true;
       controlPort = 9051;
-      extraConfig = "CookieAuthentication 1";
+      extraConfig = ''
+        CacheDirectoryGroupReadable 1
+        CookieAuthentication 1
+        CookieAuthFileGroupReadable 1
+      '';
     };
-    
+
     systemd.services.zeronet = {
       description = "zeronet";
       after = [ "network.target" (optionalString cfg.tor "tor.service") ];
diff --git a/nixos/modules/services/security/sks.nix b/nixos/modules/services/security/sks.nix
index 62308428f326..9f0261038d5b 100644
--- a/nixos/modules/services/security/sks.nix
+++ b/nixos/modules/services/security/sks.nix
@@ -3,78 +3,112 @@
 with lib;
 
 let
-
   cfg = config.services.sks;
-
   sksPkg = cfg.package;
 
-in
-
-{
+in {
+  meta.maintainers = with maintainers; [ primeos calbrecht jcumming ];
 
   options = {
 
     services.sks = {
 
-      enable = mkEnableOption "sks";
+      enable = mkEnableOption ''
+        SKS (synchronizing key server for OpenPGP) and start the database
+        server. You need to create "''${dataDir}/dump/*.gpg" for the initial
+        import'';
 
       package = mkOption {
         default = pkgs.sks;
         defaultText = "pkgs.sks";
         type = types.package;
-        description = "
-          Which sks derivation to use.
-        ";
+        description = "Which SKS derivation to use.";
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/db/sks";
+        example = "/var/lib/sks";
+        # TODO: The default might change to "/var/lib/sks" as this is more
+        # common. There's also https://github.com/NixOS/nixpkgs/issues/26256
+        # and "/var/db" is not FHS compliant (seems to come from BSD).
+        description = ''
+          Data directory (-basedir) for SKS, where the database and all
+          configuration files are located (e.g. KDB, PTree, membership and
+          sksconf).
+        '';
       };
 
       hkpAddress = mkOption {
         default = [ "127.0.0.1" "::1" ];
         type = types.listOf types.str;
-        description = "
-          Wich ip addresses the sks-keyserver is listening on.
-        ";
+        description = ''
+          Domain names, IPv4 and/or IPv6 addresses to listen on for HKP
+          requests.
+        '';
       };
 
       hkpPort = mkOption {
         default = 11371;
-        type = types.int;
-        description = "
-          Which port the sks-keyserver is listening on.
-        ";
+        type = types.ints.u16;
+        description = "HKP port to listen on.";
+      };
+
+      webroot = mkOption {
+        type = types.nullOr types.path;
+        default = "${sksPkg.webSamples}/OpenPKG";
+        defaultText = "\${pkgs.sks.webSamples}/OpenPKG";
+        description = ''
+          Source directory (will be symlinked, if not null) for the files the
+          built-in webserver should serve. SKS (''${pkgs.sks.webSamples})
+          provides the following examples: "HTML5", "OpenPKG", and "XHTML+ES".
+          The index file can be named index.html, index.htm, index.xhtm, or
+          index.xhtml. Files with the extensions .css, .es, .js, .jpg, .jpeg,
+          .png, or .gif are supported. Subdirectories and filenames with
+          anything other than alphanumeric characters and the '.' character
+          will be ignored.
+        '';
       };
     };
   };
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = [ sksPkg ];
-    
-    users.users.sks = {
-      createHome = true;
-      home = "/var/db/sks";
-      isSystemUser = true;
-      shell = "${pkgs.coreutils}/bin/true";
+    users = {
+      users.sks = {
+        isSystemUser = true;
+        description = "SKS user";
+        home = cfg.dataDir;
+        createHome = true;
+        group = "sks";
+        useDefaultShell = true;
+        packages = [ sksPkg pkgs.db ];
+      };
+      groups.sks = { };
     };
 
     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 = {
+      "sks-db" = {
+        description = "SKS database server";
+        after = [ "network.target" ];
         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
+          ${lib.optionalString (cfg.webroot != null)
+            "ln -sfT \"${cfg.webroot}\" web"}
+          mkdir -p dump
+          ${sksPkg}/bin/sks build dump/*.gpg -n 10 -cache 100 || true #*/
+          ${sksPkg}/bin/sks cleandb || true
+          ${sksPkg}/bin/sks pbuild -cache 20 -ptree_cache 70 || true
         '';
         serviceConfig = {
-          WorkingDirectory = home;
-          User = user;
+          WorkingDirectory = "~";
+          User = "sks";
+          Group = "sks";
           Restart = "always";
-          ExecStart = "${pkgs.sks}/bin/sks db -hkp_address ${hkpAddress} -hkp_port ${hkpPort}";
+          ExecStart = "${sksPkg}/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 def77ba69e58..9b6d4be9bda8 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -208,7 +208,7 @@ in
           enable = mkOption {
             type = types.bool;
             default = false;
-            description = "Whether to enable tor transaprent proxy";
+            description = "Whether to enable tor transparent proxy";
           };
 
           listenAddress = mkOption {
diff --git a/nixos/modules/services/system/kerberos.nix b/nixos/modules/services/system/kerberos.nix
index d151385d2f9b..e2c45ed64ac0 100644
--- a/nixos/modules/services/system/kerberos.nix
+++ b/nixos/modules/services/system/kerberos.nix
@@ -42,7 +42,7 @@ in
         protocol = "tcp";
         user = "root";
         server = "${pkgs.tcp_wrappers}/bin/tcpd";
-        serverArgs = "${pkgs.heimdalFull}/bin/kadmind";
+        serverArgs = "${pkgs.heimdalFull}/libexec/heimdal/kadmind";
       };
 
     systemd.services.kdc = {
@@ -51,13 +51,13 @@ in
       preStart = ''
         mkdir -m 0755 -p ${stateDir}
       '';
-      script = "${heimdalFull}/bin/kdc";
+      script = "${heimdalFull}/libexec/heimdal/kdc";
     };
 
     systemd.services.kpasswdd = {
       description = "Kerberos Password Changing daemon";
       wantedBy = [ "multi-user.target" ];
-      script = "${heimdalFull}/bin/kpasswdd";
+      script = "${heimdalFull}/libexec/heimdal/kpasswdd";
     };
   };
 
diff --git a/nixos/modules/services/web-apps/codimd.nix b/nixos/modules/services/web-apps/codimd.nix
new file mode 100644
index 000000000000..a0af28eac7cc
--- /dev/null
+++ b/nixos/modules/services/web-apps/codimd.nix
@@ -0,0 +1,920 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.codimd;
+
+  prettyJSON = conf:
+    pkgs.runCommand "codimd-config.json" { } ''
+      echo '${builtins.toJSON conf}' | ${pkgs.jq}/bin/jq \
+        '{production:del(.[]|nulls)|del(.[][]?|nulls)}' > $out
+    '';
+in
+{
+  options.services.codimd = {
+    enable = mkEnableOption "the CodiMD Markdown Editor";
+
+    groups = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        Groups to which the codimd user should be added.
+      '';
+    };
+
+    workDir = mkOption {
+      type = types.path;
+      default = "/var/lib/codimd";
+      description = ''
+        Working directory for the CodiMD service.
+      '';
+    };
+
+    configuration = {
+      debug = mkEnableOption "debug mode";
+      domain = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "codimd.org";
+        description = ''
+          Domain name for the CodiMD instance.
+        '';
+      };
+      urlPath = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/url/path/to/codimd";
+        description = ''
+          Path under which CodiMD is accessible.
+        '';
+      };
+      host = mkOption {
+        type = types.str;
+        default = "localhost";
+        description = ''
+          Address to listen on.
+        '';
+      };
+      port = mkOption {
+        type = types.int;
+        default = 3000;
+        example = "80";
+        description = ''
+          Port to listen on.
+        '';
+      };
+      path = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/run/codimd.sock";
+        description = ''
+          Specify where a UNIX domain socket should be placed.
+        '';
+      };
+      allowOrigin = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "localhost" "codimd.org" ];
+        description = ''
+          List of domains to whitelist.
+        '';
+      };
+      useSSL = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable to use SSL server. This will also enable
+          <option>protocolUseSSL</option>.
+        '';
+      };
+      hsts = {
+        enable = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Wheter to enable HSTS if HTTPS is also enabled.
+          '';
+        };
+        maxAgeSeconds = mkOption {
+          type = types.int;
+          default = 31536000;
+          description = ''
+            Max duration for clients to keep the HSTS status.
+          '';
+        };
+        includeSubdomains = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Whether to include subdomains in HSTS.
+          '';
+        };
+        preload = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Whether to allow preloading of the site's HSTS status.
+          '';
+        };
+      };
+      csp = mkOption {
+        type = types.nullOr types.attrs;
+        default = null;
+        example = literalExample ''
+          {
+            enable = true;
+            directives = {
+              scriptSrc = "trustworthy.scripts.example.com";
+            };
+            upgradeInsecureRequest = "auto";
+            addDefaults = true;
+          }
+        '';
+        description = ''
+          Specify the Content Security Policy which is passed to Helmet.
+          For configuration details see <link xlink:href="https://helmetjs.github.io/docs/csp/"
+          >https://helmetjs.github.io/docs/csp/</link>.
+        '';
+      };
+      protocolUseSSL = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable to use TLS for resource paths.
+          This only applies when <option>domain</option> is set.
+        '';
+      };
+      urlAddPort = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable to add the port to callback URLs.
+          This only applies when <option>domain</option> is set
+          and only for ports other than 80 and 443.
+        '';
+      };
+      useCDN = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to use CDN resources or not.
+        '';
+      };
+      allowAnonymous = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to allow anonymous usage.
+        '';
+      };
+      allowAnonymousEdits = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to allow guests to edit existing notes with the `freely' permission,
+          when <option>allowAnonymous</option> is enabled.
+        '';
+      };
+      allowFreeURL = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to allow note creation by accessing a nonexistent note URL.
+        '';
+      };
+      defaultPermission = mkOption {
+        type = types.enum [ "freely" "editable" "limited" "locked" "private" ];
+        default = "editable";
+        description = ''
+          Default permissions for notes.
+          This only applies for signed-in users.
+        '';
+      };
+      dbURL = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = ''
+          postgres://user:pass@host:5432/dbname
+        '';
+        description = ''
+          Specify which database to use.
+          CodiMD supports mysql, postgres, sqlite and mssql.
+          See <link xlink:href="https://sequelize.readthedocs.io/en/v3/">
+          https://sequelize.readthedocs.io/en/v3/</link> for more information.
+          Note: This option overrides <option>db</option>.
+        '';
+      };
+      db = mkOption {
+        type = types.attrs;
+        default = {};
+        example = literalExample ''
+          {
+            dialect = "sqlite";
+            storage = "/var/lib/codimd/db.codimd.sqlite";
+          }
+        '';
+        description = ''
+          Specify the configuration for sequelize.
+          CodiMD supports mysql, postgres, sqlite and mssql.
+          See <link xlink:href="https://sequelize.readthedocs.io/en/v3/">
+          https://sequelize.readthedocs.io/en/v3/</link> for more information.
+          Note: This option overrides <option>db</option>.
+        '';
+      };
+      sslKeyPath= mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/lib/codimd/codimd.key";
+        description = ''
+          Path to the SSL key. Needed when <option>useSSL</option> is enabled.
+        '';
+      };
+      sslCertPath = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/lib/codimd/codimd.crt";
+        description = ''
+          Path to the SSL cert. Needed when <option>useSSL</option> is enabled.
+        '';
+      };
+      sslCAPath = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "/var/lib/codimd/ca.crt" ];
+        description = ''
+          SSL ca chain. Needed when <option>useSSL</option> is enabled.
+        '';
+      };
+      dhParamPath = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/lib/codimd/dhparam.pem";
+        description = ''
+          Path to the SSL dh params. Needed when <option>useSSL</option> is enabled.
+        '';
+      };
+      tmpPath = mkOption {
+        type = types.str;
+        default = "/tmp";
+        description = ''
+          Path to the temp directory CodiMD should use.
+          Note that <option>serviceConfig.PrivateTmp</option> is enabled for
+          the CodiMD systemd service by default.
+          (Non-canonical paths are relative to CodiMD's base directory)
+        '';
+      };
+      defaultNotePath = mkOption {
+        type = types.nullOr types.str;
+        default = "./public/default.md";
+        description = ''
+          Path to the default Note file.
+          (Non-canonical paths are relative to CodiMD's base directory)
+        '';
+      };
+      docsPath = mkOption {
+        type = types.nullOr types.str;
+        default = "./public/docs";
+        description = ''
+          Path to the docs directory.
+          (Non-canonical paths are relative to CodiMD's base directory)
+        '';
+      };
+      indexPath = mkOption {
+        type = types.nullOr types.str;
+        default = "./public/views/index.ejs";
+        description = ''
+          Path to the index template file.
+          (Non-canonical paths are relative to CodiMD's base directory)
+        '';
+      };
+      hackmdPath = mkOption {
+        type = types.nullOr types.str;
+        default = "./public/views/hackmd.ejs";
+        description = ''
+          Path to the hackmd template file.
+          (Non-canonical paths are relative to CodiMD's base directory)
+        '';
+      };
+      errorPath = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        defaultText = "./public/views/error.ejs";
+        description = ''
+          Path to the error template file.
+          (Non-canonical paths are relative to CodiMD's base directory)
+        '';
+      };
+      prettyPath = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        defaultText = "./public/views/pretty.ejs";
+        description = ''
+          Path to the pretty template file.
+          (Non-canonical paths are relative to CodiMD's base directory)
+        '';
+      };
+      slidePath = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        defaultText = "./public/views/slide.hbs";
+        description = ''
+          Path to the slide template file.
+          (Non-canonical paths are relative to CodiMD's base directory)
+        '';
+      };
+      uploadsPath = mkOption {
+        type = types.str;
+        default = "${cfg.workDir}/uploads";
+        defaultText = "/var/lib/codimd/uploads";
+        description = ''
+          Path under which uploaded files are saved.
+        '';
+      };
+      sessionName = mkOption {
+        type = types.str;
+        default = "connect.sid";
+        description = ''
+          Specify the name of the session cookie.
+        '';
+      };
+      sessionSecret = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Specify the secret used to sign the session cookie.
+          If unset, one will be generated on startup.
+        '';
+      };
+      sessionLife = mkOption {
+        type = types.int;
+        default = 1209600000;
+        description = ''
+          Session life time in milliseconds.
+        '';
+      };
+      heartbeatInterval = mkOption {
+        type = types.int;
+        default = 5000;
+        description = ''
+          Specify the socket.io heartbeat interval.
+        '';
+      };
+      heartbeatTimeout = mkOption {
+        type = types.int;
+        default = 10000;
+        description = ''
+          Specify the socket.io heartbeat timeout.
+        '';
+      };
+      documentMaxLength = mkOption {
+        type = types.int;
+        default = 100000;
+        description = ''
+          Specify the maximum document length.
+        '';
+      };
+      email = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable email sign-in.
+        '';
+      };
+      allowEmailRegister = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Wether to enable email registration.
+        '';
+      };
+      allowGravatar = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to use gravatar as profile picture source.
+        '';
+      };
+      imageUploadType = mkOption {
+        type = types.enum [ "imgur" "s3" "minio" "filesystem" ];
+        default = "filesystem";
+        description = ''
+          Specify where to upload images.
+        '';
+      };
+      minio = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            accessKey = mkOption {
+              type = types.str;
+              description = ''
+                Minio access key.
+              '';
+            };
+            secretKey = mkOption {
+              type = types.str;
+              description = ''
+                Minio secret key.
+              '';
+            };
+            endpoint = mkOption {
+              type = types.str;
+              description = ''
+                Minio endpoint.
+              '';
+            };
+            port = mkOption {
+              type = types.int;
+              default = 9000;
+              description = ''
+                Minio listen port.
+              '';
+            };
+            secure = mkOption {
+              type = types.bool;
+              default = true;
+              description = ''
+                Whether to use HTTPS for Minio.
+              '';
+            };
+          };
+        });
+        default = null;
+        description = "Configure the minio third-party integration.";
+      };
+      s3 = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            accessKeyId = mkOption {
+              type = types.str;
+              description = ''
+                AWS access key id.
+              '';
+            };
+            secretAccessKey = mkOption {
+              type = types.str;
+              description = ''
+                AWS access key.
+              '';
+            };
+            region = mkOption {
+              type = types.str;
+              description = ''
+                AWS S3 region.
+              '';
+            };
+          };
+        });
+        default = null;
+        description = "Configure the s3 third-party integration.";
+      };
+      s3bucket = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Specify the bucket name for upload types <literal>s3</literal> and <literal>minio</literal>.
+        '';
+      };
+      allowPDFExport = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable PDF exports.
+        '';
+      };
+      imgur.clientId = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Imgur API client ID.
+        '';
+      };
+      azure = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            connectionString = mkOption {
+              type = types.str;
+              description = ''
+                Azure Blob Storage connection string.
+              '';
+            };
+            container = mkOption {
+              type = types.str;
+              description = ''
+                Azure Blob Storage container name.
+                It will be created if non-existent.
+              '';
+            };
+          };
+        });
+        default = null;
+        description = "Configure the azure third-party integration.";
+      };
+      oauth2 = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            authorizationURL = mkOption {
+              type = types.str;
+              description = ''
+                Specify the OAuth authorization URL.
+              '';
+            };
+            tokenURL = mkOption {
+              type = types.str;
+              description = ''
+                Specify the OAuth token URL.
+              '';
+            };
+            clientID = mkOption {
+              type = types.str;
+              description = ''
+                Specify the OAuth client ID.
+              '';
+            };
+            clientSecret = mkOption {
+              type = types.str;
+              description = ''
+                Specify the OAuth client secret.
+              '';
+            };
+          };
+        });
+        default = null;
+        description = "Configure the OAuth integration.";
+      };
+      facebook = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            clientID = mkOption {
+              type = types.str;
+              description = ''
+                Facebook API client ID.
+              '';
+            };
+            clientSecret = mkOption {
+              type = types.str;
+              description = ''
+                Facebook API client secret.
+              '';
+            };
+          };
+        });
+        default = null;
+        description = "Configure the facebook third-party integration";
+      };
+      twitter = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            consumerKey = mkOption {
+              type = types.str;
+              description = ''
+                Twitter API consumer key.
+              '';
+            };
+            consumerSecret = mkOption {
+              type = types.str;
+              description = ''
+                Twitter API consumer secret.
+              '';
+            };
+          };
+        });
+        default = null;
+        description = "Configure the Twitter third-party integration.";
+      };
+      github = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            clientID = mkOption {
+              type = types.str;
+              description = ''
+                GitHub API client ID.
+              '';
+            };
+            clientSecret = mkOption {
+              type = types.str;
+              description = ''
+                Github API client secret.
+              '';
+            };
+          };
+        });
+        default = null;
+        description = "Configure the GitHub third-party integration.";
+      };
+      gitlab = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            baseURL = mkOption {
+              type = types.str;
+              default = "";
+              description = ''
+                GitLab API authentication endpoint.
+                Only needed for other endpoints than gitlab.com.
+              '';
+            };
+            clientID = mkOption {
+              type = types.str;
+              description = ''
+                GitLab API client ID.
+              '';
+            };
+            clientSecret = mkOption {
+              type = types.str;
+              description = ''
+                GitLab API client secret.
+              '';
+            };
+            scope = mkOption {
+              type = types.enum [ "api" "read_user" ];
+              default = "api";
+              description = ''
+                GitLab API requested scope.
+                GitLab snippet import/export requires api scope.
+              '';
+            };
+          };
+        });
+        default = null;
+        description = "Configure the GitLab third-party integration.";
+      };
+      mattermost = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            baseURL = mkOption {
+              type = types.str;
+              description = ''
+                Mattermost authentication endpoint.
+              '';
+            };
+            clientID = mkOption {
+              type = types.str;
+              description = ''
+                Mattermost API client ID.
+              '';
+            };
+            clientSecret = mkOption {
+              type = types.str;
+              description = ''
+                Mattermost API client secret.
+              '';
+            };
+          };
+        });
+        default = null;
+        description = "Configure the Mattermost third-party integration.";
+      };
+      dropbox = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            clientID = mkOption {
+              type = types.str;
+              description = ''
+                Dropbox API client ID.
+              '';
+            };
+            clientSecret = mkOption {
+              type = types.str;
+              description = ''
+                Dropbox API client secret.
+              '';
+            };
+            appKey = mkOption {
+              type = types.str;
+              description = ''
+                Dropbox app key.
+              '';
+            };
+          };
+        });
+        default = null;
+        description = "Configure the Dropbox third-party integration.";
+      };
+      google = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            clientID = mkOption {
+              type = types.str;
+              description = ''
+                Google API client ID.
+              '';
+            };
+            clientSecret = mkOption {
+              type = types.str;
+              description = ''
+                Google API client secret.
+              '';
+            };
+          };
+        });
+        default = null;
+        description = "Configure the Google third-party integration.";
+      };
+      ldap = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            providerName = mkOption {
+              type = types.str;
+              default = "";
+              description = ''
+                Optional name to be displayed at login form, indicating the LDAP provider.
+              '';
+            };
+            url = mkOption {
+              type = types.str;
+              example = "ldap://localhost";
+              description = ''
+                URL of LDAP server.
+              '';
+            };
+            bindDn = mkOption {
+              type = types.str;
+              description = ''
+                Bind DN for LDAP access.
+              '';
+            };
+            bindCredentials = mkOption {
+              type = types.str;
+              description = ''
+                Bind credentials for LDAP access.
+              '';
+            };
+            searchBase = mkOption {
+              type = types.str;
+              example = "o=users,dc=example,dc=com";
+              description = ''
+                LDAP directory to begin search from.
+              '';
+            };
+            searchFilter = mkOption {
+              type = types.str;
+              example = "(uid={{username}})";
+              description = ''
+                LDAP filter to search with.
+              '';
+            };
+            searchAttributes = mkOption {
+              type = types.listOf types.str;
+              example = [ "displayName" "mail" ];
+              description = ''
+                LDAP attributes to search with.
+              '';
+            };
+            userNameField = mkOption {
+              type = types.str;
+              default = "";
+              description = ''
+                LDAP field which is used as the username on CodiMD.
+                By default <option>useridField</option> is used.
+              '';
+            };
+            useridField = mkOption {
+              type = types.str;
+              example = "uid";
+              description = ''
+                LDAP field which is a unique identifier for users on CodiMD.
+              '';
+            };
+            tlsca = mkOption {
+              type = types.str;
+              example = "server-cert.pem,root.pem";
+              description = ''
+                Root CA for LDAP TLS in PEM format.
+              '';
+            };
+          };
+        });
+        default = null;
+        description = "Configure the LDAP integration.";
+      };
+      saml = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            idpSsoUrl = mkOption {
+              type = types.str;
+              example = "https://idp.example.com/sso";
+              description = ''
+                IdP authentication endpoint.
+              '';
+            };
+            idpCert = mkOption {
+              type = types.path;
+              example = "/path/to/cert.pem";
+              description = ''
+                Path to IdP certificate file in PEM format.
+              '';
+            };
+            issuer = mkOption {
+              type = types.str;
+              default = "";
+              description = ''
+                Optional identity of the service provider.
+                This defaults to the server URL.
+              '';
+            };
+            identifierFormat = mkOption {
+              type = types.str;
+              default = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress";
+              description = ''
+                Optional name identifier format.
+              '';
+            };
+            groupAttribute = mkOption {
+              type = types.str;
+              default = "";
+              example = "memberOf";
+              description = ''
+                Optional attribute name for group list.
+              '';
+            };
+            externalGroups = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              example = [ "Temporary-staff" "External-users" ];
+              description = ''
+                Excluded group names.
+              '';
+            };
+            requiredGroups = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              example = [ "Hackmd-users" "Codimd-users" ];
+              description = ''
+                Required group names.
+              '';
+            };
+            attribute = {
+              id = mkOption {
+                type = types.str;
+                default = "";
+                description = ''
+                  Attribute map for `id'.
+                  Defaults to `NameID' of SAML response.
+                '';
+              };
+              username = mkOption {
+                type = types.str;
+                default = "";
+                description = ''
+                  Attribute map for `username'.
+                  Defaults to `NameID' of SAML response.
+                '';
+              };
+              email = mkOption {
+                type = types.str;
+                default = "";
+                description = ''
+                  Attribute map for `email'.
+                  Defaults to `NameID' of SAML response if
+                  <option>identifierFormat</option> has
+                  the default value.
+                '';
+              };
+            };
+          };
+        });
+        default = null;
+        description = "Configure the SAML integration.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      { assertion = cfg.configuration.db == {} -> (
+          cfg.configuration.dbURL != "" && cfg.configuration.dbURL != null
+        );
+        message = "Database configuration for CodiMD missing."; }
+    ];
+    users.groups.codimd = {};
+    users.users.codimd = {
+      description = "CodiMD service user";
+      group = "codimd";
+      extraGroups = cfg.groups;
+      home = cfg.workDir;
+      createHome = true;
+    };
+
+    systemd.services.codimd = {
+      description = "CodiMD Service";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "networking.target" ];
+      preStart = ''
+        mkdir -p ${cfg.workDir}
+        chown -R codimd: ${cfg.workDir}
+      '';
+      serviceConfig = {
+        WorkingDirectory = cfg.workDir;
+        ExecStart = "${pkgs.codimd}/bin/codimd";
+        Environment = [
+          "CMD_CONFIG_FILE=${prettyJSON cfg.configuration}"
+          "NODE_ENV=production"
+        ];
+        Restart = "always";
+        User = "codimd";
+        PermissionsStartOnly = true;
+        PrivateTmp = true;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/matomo-doc.xml b/nixos/modules/services/web-apps/matomo-doc.xml
index 6f878015c514..510a335edc3b 100644
--- a/nixos/modules/services/web-apps/matomo-doc.xml
+++ b/nixos/modules/services/web-apps/matomo-doc.xml
@@ -3,28 +3,24 @@
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
          xml:id="module-services-matomo">
+ <title>Matomo</title>
+ <para>
+  Matomo is a real-time web analytics application. This module configures
+  php-fpm as backend for Matomo, optionally configuring an nginx vhost as well.
+ </para>
+ <para>
+  An automatic setup is not suported by Matomo, so you need to configure Matomo
+  itself in the browser-based Matomo setup.
+ </para>
+ <section xml:id="module-services-matomo-database-setup">
+  <title>Database Setup</title>
 
-  <title>Matomo</title>
   <para>
-    Matomo is a real-time web analytics application.
-    This module configures php-fpm as backend for Matomo, optionally configuring an nginx vhost as well.
-  </para>
-
-  <para>
-    An automatic setup is not suported by Matomo, so you need to configure Matomo itself in the browser-based Matomo setup.
-  </para>
-
-
-  <section xml:id="module-services-matomo-database-setup">
-    <title>Database Setup</title>
-
-    <para>
-      You also need to configure a MariaDB or MySQL database and -user for Matomo yourself,
-      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>
+   You also need to configure a MariaDB or MySQL database and -user for Matomo
+   yourself, 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';
         CREATE DATABASE matomo;
@@ -37,59 +33,58 @@
         CREATE USER 'matomo'@'localhost' IDENTIFIED WITH auth_socket;
         GRANT ALL PRIVILEGES ON matomo.* TO 'matomo'@'localhost';
       </programlisting>
+   Then fill in <literal>matomo</literal> as database user and database name,
+   and leave the password field blank. This authentication works by allowing
+   only the <literal>matomo</literal> unix user to authenticate as the
+   <literal>matomo</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>
 
-      Then fill in <literal>matomo</literal> as database user and database name, and leave the password field blank.
-      This authentication works by allowing only the <literal>matomo</literal> unix user to authenticate as the
-      <literal>matomo</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>
-
-    <para>
-      Of course, you can use password based authentication as well, e.g. when the database is not on the same host.
-    </para>
-  </section>
+  <para>
+   Of course, you can use password based authentication as well, e.g. when the
+   database is not on the same host.
+  </para>
+ </section>
+ <section xml:id="module-services-matomo-backups">
+  <title>Backup</title>
 
+  <para>
+   You only need to take backups of your MySQL database and the
+   <filename>/var/lib/matomo/config/config.ini.php</filename> file. Use a user
+   in the <literal>matomo</literal> group or root to access the file. For more
+   information, see
+   <link xlink:href="https://matomo.org/faq/how-to-install/faq_138/" />.
+  </para>
+ </section>
+ <section xml:id="module-services-matomo-issues">
+  <title>Issues</title>
 
-  <section xml:id="module-services-matomo-backups">
-    <title>Backup</title>
+  <itemizedlist>
+   <listitem>
     <para>
-      You only need to take backups of your MySQL database and the
-      <filename>/var/lib/matomo/config/config.ini.php</filename> file.
-      Use a user in the <literal>matomo</literal> group or root to access the file.
-      For more information, see <link xlink:href="https://matomo.org/faq/how-to-install/faq_138/" />.
+     Matomo's file integrity check will warn you. This is due to the patches
+     necessary for NixOS, you can safely ignore this.
     </para>
-  </section>
-
-
-  <section xml:id="module-services-matomo-issues">
-    <title>Issues</title>
-    <itemizedlist>
-      <listitem>
-        <para>
-          Matomo's file integrity check will warn you.
-          This is due to the patches necessary for NixOS, you can safely ignore this.
-        </para>
-      </listitem>
-
-      <listitem>
-        <para>
-          Matomo will warn you that the JavaScript tracker is not writable.
-          This is because it's located in the read-only nix store.
-          You can safely ignore this, unless you need a plugin that needs JavaScript tracker access.
-        </para>
-      </listitem>
-    </itemizedlist>
-  </section>
-
-
-  <section xml:id="module-services-matomo-other-web-servers">
-    <title>Using other Web Servers than nginx</title>
-
+   </listitem>
+   <listitem>
     <para>
-      You can use other web servers by forwarding calls for <filename>index.php</filename> and
-      <filename>piwik.php</filename> to the <literal>/run/phpfpm-matomo.sock</literal> fastcgi unix socket.
-      You can use the nginx configuration in the module code as a reference to what else should be configured.
+     Matomo will warn you that the JavaScript tracker is not writable. This is
+     because it's located in the read-only nix store. You can safely ignore
+     this, unless you need a plugin that needs JavaScript tracker access.
     </para>
-  </section>
+   </listitem>
+  </itemizedlist>
+ </section>
+ <section xml:id="module-services-matomo-other-web-servers">
+  <title>Using other Web Servers than nginx</title>
+
+  <para>
+   You can use other web servers by forwarding calls for
+   <filename>index.php</filename> and <filename>piwik.php</filename> to the
+   <literal>/run/phpfpm-matomo.sock</literal> fastcgi unix socket. You can use
+   the nginx configuration in the module code as a reference to what else
+   should be configured.
+  </para>
+ </section>
 </chapter>
diff --git a/nixos/modules/services/x11/desktop-managers/enlightenment.nix b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
index 6fa3ec3b9255..04e380b61530 100644
--- a/nixos/modules/services/x11/desktop-managers/enlightenment.nix
+++ b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
@@ -66,7 +66,7 @@ in
       '';
     }];
 
-    security.wrappers = (import (builtins.toPath "${e.enlightenment}/e-wrappers.nix")).security.wrappers;
+    security.wrappers = (import "${e.enlightenment}/e-wrappers.nix").security.wrappers;
 
     environment.etc = singleton
       { source = xcfg.xkbDir;
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index faf5214130db..eb86f7b53bb6 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -110,6 +110,7 @@ in {
     services.gnome3.gnome-terminal-server.enable = mkDefault true;
     services.gnome3.gnome-user-share.enable = mkDefault true;
     services.gnome3.gvfs.enable = true;
+    services.gnome3.rygel.enable = mkDefault true;
     services.gnome3.seahorse.enable = mkDefault true;
     services.gnome3.sushi.enable = mkDefault true;
     services.gnome3.tracker.enable = mkDefault true;
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index d1cb962f6ff8..e759f69db897 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -81,6 +81,7 @@ in
           kconfig
           kconfigwidgets
           kcoreaddons
+          kdoctools
           kdbusaddons
           kdeclarative
           kded
diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix
index f561c5f8b7a9..357fa8ce8f36 100644
--- a/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixos/modules/services/x11/display-managers/default.nix
@@ -266,7 +266,7 @@ in
           session.  Each session script can set the
           <varname>waitPID</varname> shell variable to make this script
           wait until the end of the user session.  Each script is used
-          to define either a windows manager or a desktop manager.  These
+          to define either a window manager or a desktop manager.  These
           can be differentiated by setting the attribute
           <varname>manage</varname> either to <literal>"window"</literal>
           or <literal>"desktop"</literal>.
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index cd9c3d81a0fb..8078b93a7574 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -197,7 +197,7 @@ in
       # lightdm relaunches itself via just `lightdm`, so needs to be on the PATH
       execCmd = ''
         export PATH=${lightdm}/sbin:$PATH
-        exec ${lightdm}/sbin/lightdm --log-dir=/var/log --run-dir=/run
+        exec ${lightdm}/sbin/lightdm
       '';
     };
 
@@ -246,12 +246,19 @@ in
     '';
 
     users.users.lightdm = {
-      createHome = true;
-      home = "/var/lib/lightdm-data";
+      home = "/var/lib/lightdm";
       group = "lightdm";
       uid = config.ids.uids.lightdm;
     };
 
+    systemd.tmpfiles.rules = [
+      "d /run/lightdm 0711 lightdm lightdm 0"
+      "d /var/cache/lightdm 0711 root lightdm -"
+      "d /var/lib/lightdm 1770 lightdm lightdm -"
+      "d /var/lib/lightdm-data 1775 lightdm lightdm -"
+      "d /var/log/lightdm 0711 root lightdm -"
+    ];
+
     users.groups.lightdm.gid = config.ids.gids.lightdm;
     services.xserver.tty     = null; # We might start multiple X servers so let the tty increment themselves..
     services.xserver.display = null; # We specify our own display (and logfile) in xserver-wrapper up there
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix
index 1635c0f9acc5..2a9826177737 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/x11/display-managers/sddm.nix
@@ -263,7 +263,9 @@ in
     };
 
     environment.etc."sddm.conf".source = cfgFile;
-    environment.pathsToLink = [ "/share/sddm/themes" ];
+    environment.pathsToLink = [ 
+      "/share/sddm" 
+    ];
 
     users.groups.sddm.gid = config.ids.gids.sddm;
 
diff --git a/nixos/modules/services/x11/hardware/libinput.nix b/nixos/modules/services/x11/hardware/libinput.nix
index 072004d5dd91..58fe702d4969 100644
--- a/nixos/modules/services/x11/hardware/libinput.nix
+++ b/nixos/modules/services/x11/hardware/libinput.nix
@@ -205,7 +205,7 @@ in {
       })
     ];
 
-    services.udev.packages = [ pkgs.libinput ];
+    services.udev.packages = [ pkgs.libinput.out ];
 
     services.xserver.config =
       ''
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index b3fe6caf62dc..c3e469e4b8a1 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -419,7 +419,7 @@ while (my $f = <$listActiveUsers>) {
     my ($uid, $name) = ($+{uid}, $+{user});
     print STDERR "reloading user units for $name...\n";
 
-    system("su", "-l", $name, "-c", "XDG_RUNTIME_DIR=/run/user/$uid @systemd@/bin/systemctl --user daemon-reload");
+    system("su", "-s", "@shell@", "-l", $name, "-c", "XDG_RUNTIME_DIR=/run/user/$uid @systemd@/bin/systemctl --user daemon-reload");
 }
 
 close $listActiveUsers;
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index fff88e2c2bf3..413543df88c6 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -93,48 +93,52 @@ let
       ${config.system.extraSystemBuilderCmds}
     '';
 
-  # Handle assertions
-
-  failed = map (x: x.message) (filter (x: !x.assertion) config.assertions);
-
-  showWarnings = res: fold (w: x: builtins.trace "warning: ${w}" x) res config.warnings;
-
   # Putting it all together.  This builds a store path containing
   # symlinks to the various parts of the built configuration (the
   # kernel, systemd units, init scripts, etc.) as well as a script
   # `switch-to-configuration' that activates the configuration and
   # makes it bootable.
-  baseSystem = showWarnings (
-    if [] == failed then pkgs.stdenvNoCC.mkDerivation {
-      name = let hn = config.networking.hostName;
-                 nn = if (hn != "") then hn else "unnamed";
-          in "nixos-system-${nn}-${config.system.nixos.label}";
-      preferLocalBuild = true;
-      allowSubstitutes = false;
-      buildCommand = systemBuilder;
-
-      inherit (pkgs) utillinux coreutils;
-      systemd = config.systemd.package;
+  baseSystem = pkgs.stdenvNoCC.mkDerivation {
+    name = let hn = config.networking.hostName;
+               nn = if (hn != "") then hn else "unnamed";
+        in "nixos-system-${nn}-${config.system.nixos.label}";
+    preferLocalBuild = true;
+    allowSubstitutes = false;
+    buildCommand = systemBuilder;
+
+    inherit (pkgs) utillinux coreutils;
+    systemd = config.systemd.package;
+    shell = "${pkgs.bash}/bin/sh";
+
+    inherit children;
+    kernelParams = config.boot.kernelParams;
+    installBootLoader =
+      config.system.build.installBootLoader
+      or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
+    activationScript = config.system.activationScripts.script;
+    nixosLabel = config.system.nixos.label;
+
+    configurationName = config.boot.loader.grub.configurationName;
+
+    # Needed by switch-to-configuration.
+
+    perl = "${pkgs.perl}/bin/perl " + (concatMapStringsSep " " (lib: "-I${lib}/${pkgs.perl.libPrefix}") (with pkgs.perlPackages; [ FileSlurp NetDBus XMLParser XMLTwig ]));
+  };
 
-      inherit children;
-      kernelParams = config.boot.kernelParams;
-      installBootLoader =
-        config.system.build.installBootLoader
-        or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
-      activationScript = config.system.activationScripts.script;
-      nixosLabel = config.system.nixos.label;
+  # Handle assertions and warnings
 
-      configurationName = config.boot.loader.grub.configurationName;
+  failedAssertions = map (x: x.message) (filter (x: !x.assertion) config.assertions);
 
-      # Needed by switch-to-configuration.
+  showWarnings = res: fold (w: x: builtins.trace "warning: ${w}" x) res config.warnings;
 
-      perl = "${pkgs.perl}/bin/perl " + (concatMapStringsSep " " (lib: "-I${lib}/${pkgs.perl.libPrefix}") (with pkgs.perlPackages; [ FileSlurp NetDBus XMLParser XMLTwig ]));
-  } else throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failed)}");
+  baseSystemAssertWarn = if failedAssertions != []
+    then throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
+    else showWarnings baseSystem;
 
   # Replace runtime dependencies
   system = fold ({ oldDependency, newDependency }: drv:
       pkgs.replaceDependency { inherit oldDependency newDependency drv; }
-    ) baseSystem config.system.replaceRuntimeDependencies;
+    ) baseSystemAssertWarn config.system.replaceRuntimeDependencies;
 
 in
 
diff --git a/nixos/modules/system/boot/kexec.nix b/nixos/modules/system/boot/kexec.nix
index 3e5d7b40f2c5..61f9c6d0e7eb 100644
--- a/nixos/modules/system/boot/kexec.nix
+++ b/nixos/modules/system/boot/kexec.nix
@@ -1,7 +1,7 @@
 { pkgs, lib, ... }:
 
 {
-  config = lib.mkIf (pkgs.kexectools.meta.available) {
+  config = lib.mkIf (lib.any (lib.meta.platformMatch pkgs.stdenv.hostPlatform) pkgs.kexectools.meta.platforms) {
     environment.systemPackages = [ pkgs.kexectools ];
 
     systemd.services."prepare-kexec" =
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index ed8b9f01e275..1079089bc5a8 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -11,19 +11,30 @@ let
       exit 1
     }
 
+    dev_exist() {
+      local target="$1"
+      if [ -e $target ]; then
+        return 0
+      else
+        local uuid=$(echo -n $target | sed -e 's,UUID=\(.*\),\1,g')
+        local dev=$(blkid --uuid $uuid)
+        return $?
+      fi
+    }
+
     wait_target() {
         local name="$1"
         local target="$2"
         local secs="''${3:-10}"
         local desc="''${4:-$name $target to appear}"
 
-        if [ ! -e $target ]; then
+        if ! dev_exist $target; then
             echo -n "Waiting $secs seconds for $desc..."
             local success=false;
             for try in $(seq $secs); do
                 echo -n "."
                 sleep 1
-                if [ -e $target ]; then
+                if dev_exist $target; then
                     success=true
                     break
                 fi
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index 4bacf0f126a4..63a6f7fbe099 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -208,7 +208,6 @@ let
       "InitialCongestionWindow" "InitialAdvertisedReceiveWindow" "QuickAck"
       "MTUBytes"
     ])
-    (assertHasField "Gateway")
   ];
 
   checkDhcp = checkUnitConfig "DHCP" [
@@ -249,13 +248,14 @@ let
   # .network files have a [Link] section with different options than in .netlink files
   checkNetworkLink = checkUnitConfig "Link" [
     (assertOnlyFields [
-      "MACAddress" "MTUBytes" "ARP" "Unmanaged" "RequiredForOnline"
+      "MACAddress" "MTUBytes" "ARP" "Multicast" "Unmanaged" "RequiredForOnline"
     ])
     (assertMacAddress "MACAddress")
     (assertByteFormat "MTUBytes")
     (assertValueOneOf "ARP" boolValues)
+    (assertValueOneOf "Multicast" boolValues)
     (assertValueOneOf "Unmanaged" boolValues)
-    (assertValueOneOf "RquiredForOnline" boolValues)
+    (assertValueOneOf "RequiredForOnline" boolValues)
   ];
 
 
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 12e029ae57f8..3ac4c02b61f5 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -886,6 +886,9 @@ in
     #systemd.services.systemd-logind.restartTriggers = [ config.environment.etc."systemd/logind.conf".source ];
     systemd.services.systemd-logind.restartIfChanged = false;
     systemd.services.systemd-logind.stopIfChanged = false;
+    # The user-runtime-dir@ service is managed by systemd-logind we should not touch it or else we break the users' sessions.
+    systemd.services."user-runtime-dir@".stopIfChanged = false;
+    systemd.services."user-runtime-dir@".restartIfChanged = false;
     systemd.services.systemd-journald.restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
     systemd.services.systemd-journald.stopIfChanged = false;
     systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true;
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 20a740ce1f0c..815523093dde 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -341,7 +341,7 @@ in
         You should try to make this ID unique among your machines. You can
         generate a random 32-bit ID using the following commands:
 
-        <literal>cksum /etc/machine-id | while read c rest; do printf "%x" $c; done</literal>
+        <literal>head -c 8 /etc/machine-id</literal>
 
         (this derives it from the machine-id that systemd generates) or
 
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index ed4cfa7805e2..aa0db4afd978 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -55,7 +55,8 @@ with import ../../lib/qemu-flags.nix { inherit pkgs; };
     systemd.services."serial-getty@hvc0".enable = false;
 
     # Only use a serial console, no TTY.
-    virtualisation.qemu.consoles = [ qemuSerialDevice ];
+    # hvc1: socket backdoor, see "Debugging NixOS tests" section in NixOS manual
+    virtualisation.qemu.consoles = [ "hvc1" qemuSerialDevice ];
 
     boot.initrd.preDeviceCommands =
       ''
diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix
index 4c7cffcf4557..caaf6c0aa59d 100644
--- a/nixos/modules/virtualisation/google-compute-image.nix
+++ b/nixos/modules/virtualisation/google-compute-image.nix
@@ -97,8 +97,8 @@ in
       "google-instance-setup.service"
       "google-network-setup.service"
     ];
-    wantedBy = [ "multi-user.target" ];
     requires = ["network.target"];
+    wantedBy = ["multi-user.target"];
     path = with pkgs; [ shadow ];
     serviceConfig = {
       Type = "simple";
@@ -113,8 +113,8 @@ in
       "google-instance-setup.service"
       "google-network-setup.service"
     ];
-    requires = [ "network.target" ];
-    wantedBy = [ "multi-user.target" ];
+    requires = ["network.target"];
+    wantedBy = ["multi-user.target"];
     serviceConfig = {
       Type = "simple";
       ExecStart = "${gce}/bin/google_clock_skew_daemon --debug";
@@ -123,7 +123,7 @@ in
 
   systemd.services.google-instance-setup = {
     description = "Google Compute Engine Instance Setup";
-    after = ["fs.target" "network-online.target" "network.target" "rsyslog.service"];
+    after = ["local-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" ];
@@ -134,15 +134,17 @@ in
     };
   };
 
-  systemd.services.google-ip-forwarding-daemon = {
-    description = "Google Compute Engine IP Forwarding Daemon";
-    after = ["network.target" "google-instance-setup.service" "google-network-setup.service"];
+  systemd.services.google-network-daemon = {
+    description = "Google Compute Engine Network Daemon";
+    after = ["local-fs.target" "network-online.target" "network.target" "rsyslog.service" "google-instance-setup.service"];
+    wants = ["local-fs.target" "network-online.target" "network.target"];
     requires = ["network.target"];
+    partOf = ["network.target"];
     wantedBy = [ "multi-user.target" ];
     path = with pkgs; [ iproute ];
     serviceConfig = {
-      Type = "simple";
-      ExecStart = "${gce}/bin/google_ip_forwarding_daemon --debug";
+      ExecStart = "${gce}/bin/google_network_daemon --debug";
+      Type = "oneshot";
     };
   };
 
@@ -153,8 +155,9 @@ in
       "network-online.target"
       "network.target"
       "rsyslog.service"
+      "systemd-resolved.service"
       "google-instance-setup.service"
-      "google-network-setup.service"
+      "google-network-daemon.service"
     ];
     wants = [ "local-fs.target" "network-online.target" "network.target"];
     wantedBy = [ "multi-user.target" ];
@@ -167,23 +170,6 @@ in
     };
   };
 
-  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 = [
@@ -192,9 +178,9 @@ in
       "network.target"
       "rsyslog.service"
       "google-instance-setup.service"
-      "google-network-setup.service"
+      "google-network-daemon.service"
     ];
-    wants = [ "local-fs.target" "network-online.target" "network.target"];
+    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";