summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/default.nix10
-rw-r--r--nixos/doc/manual/release-notes/rl-1803.xml37
-rw-r--r--nixos/modules/config/no-x-libs.nix1
-rw-r--r--nixos/modules/hardware/video/nvidia.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix2
-rw-r--r--nixos/modules/misc/ids.nix4
-rw-r--r--nixos/modules/module-list.nix14
-rw-r--r--nixos/modules/profiles/base.nix2
-rw-r--r--nixos/modules/rename.nix8
-rw-r--r--nixos/modules/security/acme.nix10
-rw-r--r--nixos/modules/security/duosec.nix4
-rw-r--r--nixos/modules/services/backup/almir.nix173
-rw-r--r--nixos/modules/services/backup/tarsnap.nix67
-rw-r--r--nixos/modules/services/backup/znapzend.nix2
-rw-r--r--nixos/modules/services/cluster/kubernetes/default.nix12
-rw-r--r--nixos/modules/services/logging/graylog.nix2
-rw-r--r--nixos/modules/services/misc/defaultUnicornConfig.rb240
-rw-r--r--nixos/modules/services/misc/gitlab.nix6
-rw-r--r--nixos/modules/services/misc/serviio.nix92
-rw-r--r--nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix68
-rw-r--r--nixos/modules/services/monitoring/prometheus/collectd-exporter.nix128
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.nix172
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.xml135
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix31
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/collectd.nix78
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix39
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/json.nix36
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/minio.nix65
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nginx.nix31
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/node.nix39
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/postfix.nix46
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/snmp.nix71
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/unifi.nix67
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/varnish.nix21
-rw-r--r--nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix76
-rw-r--r--nixos/modules/services/monitoring/prometheus/json-exporter.nix74
-rw-r--r--nixos/modules/services/monitoring/prometheus/minio-exporter.nix117
-rw-r--r--nixos/modules/services/monitoring/prometheus/nginx-exporter.nix78
-rw-r--r--nixos/modules/services/monitoring/prometheus/node-exporter.nix87
-rw-r--r--nixos/modules/services/monitoring/prometheus/snmp-exporter.nix127
-rw-r--r--nixos/modules/services/monitoring/prometheus/unifi-exporter.nix105
-rw-r--r--nixos/modules/services/monitoring/prometheus/varnish-exporter.nix61
-rw-r--r--nixos/modules/services/networking/networkmanager.nix8
-rw-r--r--nixos/modules/services/networking/prosody.nix214
-rw-r--r--nixos/modules/services/security/hologram-server.nix11
-rw-r--r--nixos/modules/services/security/tor.nix17
-rw-r--r--nixos/modules/services/web-apps/nixbot.nix149
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/per-server-options.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix3
-rw-r--r--nixos/modules/system/boot/kexec.nix2
-rw-r--r--nixos/modules/system/boot/systemd.nix15
-rw-r--r--nixos/release.nix1
-rw-r--r--nixos/tests/installer.nix15
-rw-r--r--nixos/tests/misc.nix2
-rw-r--r--nixos/tests/openldap.nix2
-rw-r--r--nixos/tests/prosody.nix75
56 files changed, 1431 insertions, 1525 deletions
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index 3b01f4fed35..c51c30065a3 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -139,7 +139,7 @@ let
 
   manual-combined = runCommand "nixos-manual-combined"
     { inherit sources;
-      nativeBuildInputs = [ buildPackages.libxml2 buildPackages.libxslt ];
+      nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ];
       meta.description = "The NixOS manual as plain docbook XML";
     }
     ''
@@ -194,7 +194,7 @@ let
 
   olinkDB = runCommand "manual-olinkdb"
     { inherit sources;
-      nativeBuildInputs = [ buildPackages.libxml2 buildPackages.libxslt ];
+      nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ];
     }
     ''
       xsltproc \
@@ -244,7 +244,7 @@ in rec {
   # Generate the NixOS manual.
   manual = runCommand "nixos-manual"
     { inherit sources;
-      nativeBuildInputs = [ buildPackages.libxml2 buildPackages.libxslt ];
+      nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ];
       meta.description = "The NixOS manual in HTML format";
       allowedReferences = ["out"];
     }
@@ -272,7 +272,7 @@ in rec {
 
   manualEpub = runCommand "nixos-manual-epub"
     { inherit sources;
-      buildInputs = [ libxml2 libxslt zip ];
+      buildInputs = [ libxml2.bin libxslt.bin zip ];
     }
     ''
       # Generate the epub manual.
@@ -302,7 +302,7 @@ in rec {
   # Generate the NixOS manpages.
   manpages = runCommand "nixos-manpages"
     { inherit sources;
-      nativeBuildInputs = [ buildPackages.libxml2 buildPackages.libxslt ];
+      nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ];
       allowedReferences = ["out"];
     }
     ''
diff --git a/nixos/doc/manual/release-notes/rl-1803.xml b/nixos/doc/manual/release-notes/rl-1803.xml
index b755245a69f..60c17c60413 100644
--- a/nixos/doc/manual/release-notes/rl-1803.xml
+++ b/nixos/doc/manual/release-notes/rl-1803.xml
@@ -322,6 +322,43 @@ following incompatible changes:</para>
       <link xlink:href="https://github.com/rvl/pump.io-nixos">external module</link>.
     </para>
   </listitem>
+  <listitem>
+    <para>
+      The Prosody XMPP server has received a major update. The following modules were renamed:
+      <itemizedlist>
+        <listitem>
+          <para>
+            <option>services.prosody.modules.httpserver</option> is now <option>services.prosody.modules.http_files</option>
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            <option>services.prosody.modules.console</option> is now <option>services.prosody.modules.admin_telnet</option>
+          </para>
+        </listitem>
+      </itemizedlist>
+    </para>
+
+    <para>
+      Many new modules are now core modules, most notably <option>services.prosody.modules.carbons</option>
+      and <option>services.prosody.modules.mam</option>.
+    </para>
+
+    <para>
+      The better-performing <literal>libevent</literal> backend is now enabled by default.
+    </para>
+
+    <para>
+      <literal>withCommunityModules</literal> now passes through the modules to <option>services.prosody.extraModules</option>.
+      Use <literal>withOnlyInstalledCommunityModules</literal> for modules that should not be enabled directly, e.g <literal>lib_ldap</literal>.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      All prometheus exporter modules are now defined as submodules.
+      The exporters are configured using <literal>services.prometheus.exporters</literal>.
+    </para>
+  </listitem>
 </itemizedlist>
 
 </section>
diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix
index b9d5b2b903e..a20910353f3 100644
--- a/nixos/modules/config/no-x-libs.nix
+++ b/nixos/modules/config/no-x-libs.nix
@@ -32,7 +32,6 @@ with lib;
       networkmanager-l2tp = pkgs.networkmanager-l2tp.override { withGnome = false; };
       networkmanager-openconnect = pkgs.networkmanager-openconnect.override { withGnome = false; };
       networkmanager-openvpn = pkgs.networkmanager-openvpn.override { withGnome = false; };
-      networkmanager-pptp = pkgs.networkmanager-pptp.override { withGnome = false; };
       networkmanager-vpnc = pkgs.networkmanager-vpnc.override { withGnome = false; };
       networkmanager-iodine = pkgs.networkmanager-iodine.override { withGnome = false; };
       pinentry = pkgs.pinentry_ncurses;
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index eafc9869315..a1f1cef07ad 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -16,8 +16,6 @@ let
         kernelPackages.nvidia_x11
     else if elem "nvidiaBeta" drivers then
         kernelPackages.nvidia_x11_beta
-    else if elem "nvidiaLegacy173" drivers then
-      kernelPackages.nvidia_x11_legacy173
     else if elem "nvidiaLegacy304" drivers then
       kernelPackages.nvidia_x11_legacy304
     else if elem "nvidiaLegacy340" drivers then
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
index ba84cd51098..6d4ba96dba0 100644
--- a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
+++ b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
@@ -54,7 +54,7 @@ in
   environment.systemPackages =
     [ pkgs.w3m # needed for the manual anyway
       pkgs.testdisk # useful for repairing boot problems
-      pkgs.mssys # for writing Microsoft boot sectors / MBRs
+      pkgs.ms-sys # for writing Microsoft boot sectors / MBRs
       pkgs.parted
       pkgs.ddrescue
       pkgs.ccrypt
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 39a24cfecc5..c5dee2ca97a 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -106,7 +106,7 @@
       freenet = 79;
       ircd = 80;
       bacula = 81;
-      almir = 82;
+      #almir = 82; # removed 2018-03-25, the almir package was removed in 30291227f2411abaca097773eedb49b8f259e297 during 2017-08
       deluge = 83;
       mysql = 84;
       rabbitmq = 85;
@@ -393,7 +393,7 @@
       freenet = 79;
       ircd = 80;
       bacula = 81;
-      almir = 82;
+      #almir = 82; # removed 2018-03-25, the almir package was removed in 30291227f2411abaca097773eedb49b8f259e297 during 2017-08
       deluge = 83;
       mysql = 84;
       rabbitmq = 85;
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index e0c0ec2711b..9e232ce1f4e 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -158,7 +158,6 @@
   ./services/audio/slimserver.nix
   ./services/audio/squeezelite.nix
   ./services/audio/ympd.nix
-  ./services/backup/almir.nix
   ./services/backup/bacula.nix
   ./services/backup/borgbackup.nix
   ./services/backup/crashplan.nix
@@ -363,6 +362,7 @@
   ./services/misc/rippled.nix
   ./services/misc/ripple-data-api.nix
   ./services/misc/rogue.nix
+  ./services/misc/serviio.nix
   ./services/misc/siproxd.nix
   ./services/misc/snapper.nix
   ./services/misc/sonarr.nix
@@ -398,16 +398,7 @@
   ./services/monitoring/osquery.nix
   ./services/monitoring/prometheus/default.nix
   ./services/monitoring/prometheus/alertmanager.nix
-  ./services/monitoring/prometheus/blackbox-exporter.nix
-  ./services/monitoring/prometheus/collectd-exporter.nix
-  ./services/monitoring/prometheus/fritzbox-exporter.nix
-  ./services/monitoring/prometheus/json-exporter.nix
-  ./services/monitoring/prometheus/minio-exporter.nix
-  ./services/monitoring/prometheus/nginx-exporter.nix
-  ./services/monitoring/prometheus/node-exporter.nix
-  ./services/monitoring/prometheus/snmp-exporter.nix
-  ./services/monitoring/prometheus/unifi-exporter.nix
-  ./services/monitoring/prometheus/varnish-exporter.nix
+  ./services/monitoring/prometheus/exporters.nix
   ./services/monitoring/riemann.nix
   ./services/monitoring/riemann-dash.nix
   ./services/monitoring/riemann-tools.nix
@@ -640,7 +631,6 @@
   ./services/web-apps/atlassian/jira.nix
   ./services/web-apps/frab.nix
   ./services/web-apps/mattermost.nix
-  ./services/web-apps/nixbot.nix
   ./services/web-apps/nexus.nix
   ./services/web-apps/pgpkeyserver-lite.nix
   ./services/web-apps/matomo.nix
diff --git a/nixos/modules/profiles/base.nix b/nixos/modules/profiles/base.nix
index 39b8553976e..3bf06a95119 100644
--- a/nixos/modules/profiles/base.nix
+++ b/nixos/modules/profiles/base.nix
@@ -9,7 +9,7 @@
   environment.systemPackages = [
     pkgs.w3m-nox # needed for the manual anyway
     pkgs.testdisk # useful for repairing boot problems
-    pkgs.mssys # for writing Microsoft boot sectors / MBRs
+    pkgs.ms-sys # for writing Microsoft boot sectors / MBRs
     pkgs.efibootmgr
     pkgs.efivar
     pkgs.parted
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 489d7d8b9b5..b0ca274b939 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -240,5 +240,11 @@ with lib;
 
     # Xen
     (mkRenamedOptionModule [ "virtualisation" "xen" "qemu-package" ] [ "virtualisation" "xen" "package-qemu" ])
-  ];
+  ] ++ (flip map [ "blackboxExporter" "collectdExporter" "fritzboxExporter"
+                   "jsonExporter" "minioExporter" "nginxExporter" "nodeExporter"
+                   "snmpExporter" "unifiExporter" "varnishExporter" ]
+       (opt: mkRemovedOptionModule [ "services" "prometheus" "${opt}" ] ''
+         The prometheus exporters are now configured using `services.prometheus.exporters'.
+         See the 18.03 release notes for more information.
+       '' ));
 }
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index 0736239ed2c..e9676d9dfb1 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -140,14 +140,6 @@ in
         '';
       };
 
-      tosHash = mkOption {
-        type = types.string;
-        default = "cc88d8d9517f490191401e7b54e9ffd12a2b9082ec7a1d4cec6101f9f1647e7b";
-        description = ''
-          SHA256 of the Terms of Services document. This changes once in a while.
-        '';
-      };
-
       production = mkOption {
         type = types.bool;
         default = true;
@@ -196,7 +188,7 @@ in
               let
                 cpath = "${cfg.directory}/${cert}";
                 rights = if data.allowKeysForGroup then "750" else "700";
-                cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin "--tos_sha256" cfg.tosHash ]
+                cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin ]
                           ++ optionals (data.email != null) [ "--email" data.email ]
                           ++ concatMap (p: [ "-f" p ]) data.plugins
                           ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains)
diff --git a/nixos/modules/security/duosec.nix b/nixos/modules/security/duosec.nix
index 9ca818e86ff..df6108dede7 100644
--- a/nixos/modules/security/duosec.nix
+++ b/nixos/modules/security/duosec.nix
@@ -25,14 +25,14 @@ let
   loginCfgFile = optional cfg.ssh.enable
     { source = pkgs.writeText "login_duo.conf" configFile;
       mode   = "0600";
-      uid    = config.ids.uids.sshd;
+      user   = "sshd";
       target = "duo/login_duo.conf";
     };
 
   pamCfgFile = optional cfg.pam.enable
     { source = pkgs.writeText "pam_duo.conf" configFile;
       mode   = "0600";
-      uid    = config.ids.uids.sshd;
+      user   = "sshd";
       target = "duo/pam_duo.conf";
     };
 in
diff --git a/nixos/modules/services/backup/almir.nix b/nixos/modules/services/backup/almir.nix
deleted file mode 100644
index fbb4ff4034f..00000000000
--- a/nixos/modules/services/backup/almir.nix
+++ /dev/null
@@ -1,173 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.almir;
-
-  bconsoleconf = pkgs.writeText "bconsole.conf"
-    ''
-      Director {
-        Name = ${cfg.director_name}
-        DIRport = ${toString cfg.director_port}
-        address = ${cfg.director_address}
-        Password = "${cfg.director_password}"
-      }
-    '';
-
-  productionini = pkgs.writeText "production.ini"
-    ''
-[app:main]
-use = egg:almir
-
-pyramid.reload_templates = false
-pyramid.debug_authorization = false
-pyramid.debug_notfound = false
-pyramid.debug_routematch = false
-pyramid.debug_templates = false
-pyramid.default_locale_name = en
-pyramid.includes =
-    pyramid_exclog
-exclog.extra_info = true
-
-sqlalchemy.url = ${cfg.sqlalchemy_engine_url}
-timezone = ${cfg.timezone}
-bconsole_config = ${bconsoleconf}
-
-[server:main]
-use = egg:waitress#main
-host = 127.0.0.1
-port = ${toString cfg.port}
-
-
-# Begin logging configuration
-
-[loggers]
-keys = root, almir, sqlalchemy, exc_logger
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = WARN
-handlers = console
-
-[logger_almir]
-level = WARN
-handlers =
-qualname = almir
-
-[logger_exc_logger]
-level = ERROR
-handlers =
-qualname = exc_logger
-
-[logger_sqlalchemy]
-level = WARN
-handlers =
-qualname = sqlalchemy.engine
-# "level = INFO" logs SQL queries.
-# "level = DEBUG" logs SQL queries and results.
-# "level = WARN" logs neither.  (Recommended for production systems.)
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-    '';
-in {
-  options = {
-    services.almir = {
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Enable Almir web server. Also configures postgresql database and installs bacula.
-        '';
-      };
-
-      port = mkOption {
-        default = 35000;
-        type = types.int;
-        description = ''
-          Port for Almir web server to listen on.
-        '';
-      };
-
-      timezone = mkOption {
-	description = ''
-         Timezone as specified in https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
-        '';
-        example = "Europe/Ljubljana";
-      };
-
-      sqlalchemy_engine_url = mkOption {
-        default = "postgresql:///bacula";
-        example = ''
-          postgresql://bacula:bacula@localhost:5432/bacula
-          mysql+mysqlconnector://<user>:<password>@<hostname>/<database>'
-          sqlite:////var/lib/bacula/bacula.db'
-        '';
-	description = ''
-         Define SQL database connection to bacula catalog as specified in http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls
-        '';
-      };
-
-      director_name = mkOption {
-        description = ''
-          Name of the Director to connect with bconsole.
-        '';
-      };
-
-      director_password = mkOption {
-        description = ''
-          Password for Director to connect with bconsole.
-        '';
-      };
-
-      director_port = mkOption {
-        default = 9101;
-        type = types.int;
-        description = ''
-          Port for Director to connect with bconsole.
-        '';
-      };
-
-      director_address = mkOption {
-        default = "127.0.0.1";
-        description = ''
-          IP/Hostname for Director to connect with bconsole.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    systemd.services.almir = {
-      after = [ "network.target" "postgresql.service" ];
-      description = "Almir web app";
-      wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.pythonPackages.almir ];
-      environment.PYTHONPATH = "${pkgs.pythonPackages.almir}/lib/${pkgs.pythonPackages.python.libPrefix}/site-packages";
-      serviceConfig.ExecStart = "${pkgs.pythonPackages.pyramid}/bin/pserve ${productionini}";
-    };
-
-    environment.systemPackages = [ pkgs.pythonPackages.almir ];
-
-    users.extraUsers.almir = {
-      group = "almir";
-      uid = config.ids.uids.almir;
-      createHome = true;
-      shell = "${pkgs.bash}/bin/bash";
-    };
-
-    users.extraGroups.almir.gid = config.ids.gids.almir;
-  };
-}
diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix
index 59e9d122fb5..4fc7c24813a 100644
--- a/nixos/modules/services/backup/tarsnap.nix
+++ b/nixos/modules/services/backup/tarsnap.nix
@@ -238,6 +238,20 @@ in
                   Whether to produce verbose logging output.
                 '';
               };
+              explicitSymlinks = mkOption {
+                type = types.bool;
+                default = false;
+                description = ''
+                  Whether to follow symlinks specified as archives.
+                '';
+              };
+              followSymlinks = mkOption {
+                type = types.bool;
+                default = false;
+                description = ''
+                  Whether to follow all symlinks in archive trees.
+                '';
+              };
             };
           }
         ));
@@ -285,12 +299,12 @@ in
         }) gcfg.archives);
 
     systemd.services =
-      mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" {
+      (mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" {
         description = "Tarsnap archive '${name}'";
         requires    = [ "network-online.target" ];
         after       = [ "network-online.target" ];
 
-        path = [ pkgs.iputils pkgs.tarsnap pkgs.utillinux ];
+        path = with pkgs; [ iputils tarsnap utillinux ];
 
         # In order for the persistent tarsnap timer to work reliably, we have to
         # make sure that the tarsnap server is reachable after systemd starts up
@@ -300,10 +314,12 @@ in
           while ! ping -q -c 1 v1-0-0-server.tarsnap.com &> /dev/null; do sleep 3; done
         '';
 
-        script =
-          let run = ''tarsnap --configfile "/etc/tarsnap/${name}.conf" \
-                        -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \
+        script = let
+          tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"'';
+          run = ''${tarsnap} -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \
                         ${optionalString cfg.verbose "-v"} \
+                        ${optionalString cfg.explicitSymlinks "-H"} \
+                        ${optionalString cfg.followSymlinks "-L"} \
                         ${concatStringsSep " " cfg.directories}'';
           in if (cfg.cachedir != null) then ''
             mkdir -p ${cfg.cachedir}
@@ -313,7 +329,7 @@ in
               if [ ! -e ${cfg.cachedir}/firstrun ]; then
                 ( flock 10
                   flock -u 9
-                  tarsnap --configfile "/etc/tarsnap/${name}.conf" --fsck
+                  ${tarsnap} --fsck
                   flock 9
                 ) 10>${cfg.cachedir}/firstrun
               fi
@@ -329,7 +345,44 @@ in
           CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ];
           PermissionsStartOnly = "true";
         };
-      }) gcfg.archives;
+      }) gcfg.archives) //
+
+      (mapAttrs' (name: cfg: nameValuePair "tarsnap-restore-${name}"{
+        description = "Tarsnap restore '${name}'";
+        requires    = [ "network-online.target" ];
+
+        path = with pkgs; [ iputils tarsnap utillinux ];
+
+        script = let
+          tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"'';
+          lastArchive = ''$(${tarsnap} --list-archives | sort | tail -1)'';
+          run = ''${tarsnap} -x -f "${lastArchive}" ${optionalString cfg.verbose "-v"}'';
+
+        in if (cfg.cachedir != null) then ''
+          mkdir -p ${cfg.cachedir}
+          chmod 0700 ${cfg.cachedir}
+
+          ( flock 9
+            if [ ! -e ${cfg.cachedir}/firstrun ]; then
+              ( flock 10
+                flock -u 9
+                ${tarsnap} --fsck
+                flock 9
+              ) 10>${cfg.cachedir}/firstrun
+            fi
+          ) 9>${cfg.cachedir}/lockf
+
+           exec flock ${cfg.cachedir}/firstrun ${run}
+        '' else "exec ${run}";
+
+        serviceConfig = {
+          Type = "oneshot";
+          IOSchedulingClass = "idle";
+          NoNewPrivileges = "true";
+          CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ];
+          PermissionsStartOnly = "true";
+        };
+      }) gcfg.archives);
 
     # Note: the timer must be Persistent=true, so that systemd will start it even
     # if e.g. your laptop was asleep while the latest interval occurred.
diff --git a/nixos/modules/services/backup/znapzend.nix b/nixos/modules/services/backup/znapzend.nix
index 762bb4b3867..3d133f82d20 100644
--- a/nixos/modules/services/backup/znapzend.nix
+++ b/nixos/modules/services/backup/znapzend.nix
@@ -386,7 +386,7 @@ in
           echo Resetting znapzend zetups
           ${pkgs.znapzend}/bin/znapzendzetup list \
             | grep -oP '(?<=\*\*\* backup plan: ).*(?= \*\*\*)' \
-            | xargs ${pkgs.znapzend}/bin/znapzendzetup delete
+            | xargs -I{} ${pkgs.znapzend}/bin/znapzendzetup delete "{}"
         '' + concatStringsSep "\n" (mapAttrsToList (dataset: config: ''
           echo Importing znapzend zetup ${config} for dataset ${dataset}
           ${pkgs.znapzend}/bin/znapzendzetup import --write ${dataset} ${config}
diff --git a/nixos/modules/services/cluster/kubernetes/default.nix b/nixos/modules/services/cluster/kubernetes/default.nix
index 4a2c6f0833e..39c5d78d419 100644
--- a/nixos/modules/services/cluster/kubernetes/default.nix
+++ b/nixos/modules/services/cluster/kubernetes/default.nix
@@ -279,7 +279,7 @@ in {
       tokenAuthFile = mkOption {
         description = ''
           Kubernetes apiserver token authentication file. See
-          <link xlink:href="http://kubernetes.io/docs/admin/authentication.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/authentication.html"/>
         '';
         default = null;
         type = types.nullOr types.path;
@@ -288,7 +288,7 @@ in {
       basicAuthFile = mkOption {
         description = ''
           Kubernetes apiserver basic authentication file. See
-          <link xlink:href="http://kubernetes.io/docs/admin/authentication.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/authentication.html"/>
         '';
         default = pkgs.writeText "users" ''
           kubernetes,admin,0
@@ -299,7 +299,7 @@ in {
       authorizationMode = mkOption {
         description = ''
           Kubernetes apiserver authorization mode (AlwaysAllow/AlwaysDeny/ABAC/RBAC). See
-          <link xlink:href="http://kubernetes.io/docs/admin/authorization.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/authorization.html"/>
         '';
         default = ["RBAC" "Node"];
         type = types.listOf (types.enum ["AlwaysAllow" "AlwaysDeny" "ABAC" "RBAC" "Node"]);
@@ -308,7 +308,7 @@ in {
       authorizationPolicy = mkOption {
         description = ''
           Kubernetes apiserver authorization policy file. See
-          <link xlink:href="http://kubernetes.io/docs/admin/authorization.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/authorization.html"/>
         '';
         default = [];
         type = types.listOf types.attrs;
@@ -332,7 +332,7 @@ in {
       runtimeConfig = mkOption {
         description = ''
           Api runtime configuration. See
-          <link xlink:href="http://kubernetes.io/docs/admin/cluster-management.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/cluster-management.html"/>
         '';
         default = "authentication.k8s.io/v1beta1=true";
         example = "api/all=false,api/v1=true";
@@ -342,7 +342,7 @@ in {
       admissionControl = mkOption {
         description = ''
           Kubernetes admission control plugins to use. See
-          <link xlink:href="http://kubernetes.io/docs/admin/admission-controllers/"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/admission-controllers/"/>
         '';
         default = ["NamespaceLifecycle" "LimitRanger" "ServiceAccount" "ResourceQuota" "DefaultStorageClass" "DefaultTolerationSeconds" "NodeRestriction"];
         example = [
diff --git a/nixos/modules/services/logging/graylog.nix b/nixos/modules/services/logging/graylog.nix
index a0dc0d6d089..95f31829882 100644
--- a/nixos/modules/services/logging/graylog.nix
+++ b/nixos/modules/services/logging/graylog.nix
@@ -141,7 +141,7 @@ in
         JAVA_HOME = jre;
         GRAYLOG_CONF = "${confFile}";
       };
-      path = [ pkgs.openjdk8 pkgs.which pkgs.procps ];
+      path = [ pkgs.jre_headless pkgs.which pkgs.procps ];
       preStart = ''
         mkdir -p /var/lib/graylog -m 755
 
diff --git a/nixos/modules/services/misc/defaultUnicornConfig.rb b/nixos/modules/services/misc/defaultUnicornConfig.rb
index 84622622db7..0b58c59c7a5 100644
--- a/nixos/modules/services/misc/defaultUnicornConfig.rb
+++ b/nixos/modules/services/misc/defaultUnicornConfig.rb
@@ -1,205 +1,69 @@
-# The following was taken from github.com/crohr/syslogger and is BSD
-# licensed.
-require 'syslog'
-require 'logger'
-require 'thread'
+worker_processes 3
 
-class Syslogger
-
-  VERSION = "1.6.0"
-
-  attr_reader :level, :ident, :options, :facility, :max_octets
-  attr_accessor :formatter
-
-  MAPPING = {
-    Logger::DEBUG => Syslog::LOG_DEBUG,
-    Logger::INFO => Syslog::LOG_INFO,
-    Logger::WARN => Syslog::LOG_WARNING,
-    Logger::ERROR => Syslog::LOG_ERR,
-    Logger::FATAL => Syslog::LOG_CRIT,
-    Logger::UNKNOWN => Syslog::LOG_ALERT
-  }
-
-  #
-  # Initializes default options for the logger
-  # <tt>ident</tt>:: the name of your program [default=$0].
-  # <tt>options</tt>::  syslog options [default=<tt>Syslog::LOG_PID | Syslog::LOG_CONS</tt>].
-  #                     Correct values are:
-  #                       LOG_CONS    : writes the message on the console if an error occurs when sending the message;
-  #                       LOG_NDELAY  : no delay before sending the message;
-  #                       LOG_PERROR  : messages will also be written on STDERR;
-  #                       LOG_PID     : adds the process number to the message (just after the program name)
-  # <tt>facility</tt>:: the syslog facility [default=nil] Correct values include:
-  #                       Syslog::LOG_DAEMON
-  #                       Syslog::LOG_USER
-  #                       Syslog::LOG_SYSLOG
-  #                       Syslog::LOG_LOCAL2
-  #                       Syslog::LOG_NEWS
-  #                       etc.
-  #
-  # Usage:
-  #   logger = Syslogger.new("my_app", Syslog::LOG_PID | Syslog::LOG_CONS, Syslog::LOG_LOCAL0)
-  #   logger.level = Logger::INFO # use Logger levels
-  #   logger.warn "warning message"
-  #   logger.debug "debug message"
-  #
-  def initialize(ident = $0, options = Syslog::LOG_PID | Syslog::LOG_CONS, facility = nil)
-    @ident = ident
-    @options = options || (Syslog::LOG_PID | Syslog::LOG_CONS)
-    @facility = facility
-    @level = Logger::INFO
-    @mutex = Mutex.new
-    @formatter = Logger::Formatter.new
-  end
-
-  %w{debug info warn error fatal unknown}.each do |logger_method|
-    # Accepting *args as message could be nil.
-    #  Default params not supported in ruby 1.8.7
-    define_method logger_method.to_sym do |*args, &block|
-      return true if @level > Logger.const_get(logger_method.upcase)
-      message = args.first || block && block.call
-      add(Logger.const_get(logger_method.upcase), message)
-    end
-
-    unless logger_method == 'unknown'
-      define_method "#{logger_method}?".to_sym do
-        @level <= Logger.const_get(logger_method.upcase)
-      end
-    end
-  end
-
-  # Log a message at the Logger::INFO level. Useful for use with Rack::CommonLogger
-  def write(msg)
-    add(Logger::INFO, msg)
-  end
-
-  # Logs a message at the Logger::INFO level.
-  def <<(msg)
-    add(Logger::INFO, msg)
-  end
-
-  # Low level method to add a message.
-  # +severity+::  the level of the message. One of Logger::DEBUG, Logger::INFO, Logger::WARN, Logger::ERROR, Logger::FATAL, Logger::UNKNOWN
-  # +message+:: the message string.
-  #             If nil, the method will call the block and use the result as the message string.
-  #             If both are nil or no block is given, it will use the progname as per the behaviour of both the standard Ruby logger, and the Rails BufferedLogger.
-  # +progname+:: optionally, overwrite the program name that appears in the log message.
-  def add(severity, message = nil, progname = nil, &block)
-    if message.nil? && block.nil? && !progname.nil?
-      message, progname = progname, nil
-    end
-    progname ||= @ident
-
-    @mutex.synchronize do
-      Syslog.open(progname, @options, @facility) do |s|
-        s.mask = Syslog::LOG_UPTO(MAPPING[@level])
-        communication = clean(message || block && block.call)
-        if self.max_octets
-          buffer = "#{tags_text}"
-          communication.bytes do |byte|
-            buffer.concat(byte)
-            # if the last byte we added is potentially part of an escape, we'll go ahead and add another byte
-            if buffer.bytesize >= self.max_octets && !['%'.ord,'\\'.ord].include?(byte)
-              s.log(MAPPING[severity],buffer)
-              buffer = ""
-            end
-          end
-          s.log(MAPPING[severity],buffer) unless buffer.empty?
-        else
-          s.log(MAPPING[severity],"#{tags_text}#{communication}")
-        end
-      end
-    end
-  end
-
-  # Set the max octets of the messages written to the log
-  def max_octets=(max_octets)
-    @max_octets = max_octets
-  end
-
-  # Sets the minimum level for messages to be written in the log.
-  # +level+:: one of <tt>Logger::DEBUG</tt>, <tt>Logger::INFO</tt>, <tt>Logger::WARN</tt>, <tt>Logger::ERROR</tt>, <tt>Logger::FATAL</tt>, <tt>Logger::UNKNOWN</tt>
-  def level=(level)
-    level = Logger.const_get(level.to_s.upcase) if level.is_a?(Symbol)
-
-    unless level.is_a?(Fixnum)
-      raise ArgumentError.new("Invalid logger level `#{level.inspect}`")
-    end
-
-    @level = level
-  end
-
-  # Sets the ident string passed along to Syslog
-  def ident=(ident)
-    @ident = ident
-  end
-
-  # Tagging code borrowed from ActiveSupport gem
-  def tagged(*tags)
-    new_tags = push_tags(*tags)
-    yield self
-  ensure
-    pop_tags(new_tags.size)
-  end
-
-  def push_tags(*tags)
-    tags.flatten.reject{ |i| i.respond_to?(:empty?) ? i.empty? : !i }.tap do |new_tags|
-      current_tags.concat new_tags
-    end
-  end
-
-  def pop_tags(size = 1)
-    current_tags.pop size
-  end
-
-  def clear_tags!
-    current_tags.clear
-  end
-
-  protected
-
-  # Borrowed from SyslogLogger.
-  def clean(message)
-    message = message.to_s.dup
-    message.strip! # remove whitespace
-    message.gsub!(/\n/, '\\n') # escape newlines
-    message.gsub!(/%/, '%%') # syslog(3) freaks on % (printf)
-    message.gsub!(/\e\[[^m]*m/, '') # remove useless ansi color codes
-    message
-  end
-
-  private
-
-  def tags_text
-    tags = current_tags
-    if tags.any?
-      tags.collect { |tag| "[#{tag}] " }.join
-    end
-  end
-
-  def current_tags
-    Thread.current[:syslogger_tagged_logging_tags] ||= []
-  end
-end
+listen ENV["UNICORN_PATH"] + "/tmp/sockets/gitlab.socket", :backlog => 1024
+listen "/run/gitlab/gitlab.socket", :backlog => 1024
 
-worker_processes 2
 working_directory ENV["GITLAB_PATH"]
-pid ENV["UNICORN_PATH"] + "/tmp/pids/unicorn.pid"
 
-listen ENV["UNICORN_PATH"] + "/tmp/sockets/gitlab.socket", :backlog => 1024
+pid ENV["UNICORN_PATH"] + "/tmp/pids/unicorn.pid"
 
 timeout 60
 
-logger Syslogger.new
-
+# combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings
+# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
 preload_app true
-
 GC.respond_to?(:copy_on_write_friendly=) and
   GC.copy_on_write_friendly = true
 
 check_client_connection false
 
+before_fork do |server, worker|
+  # the following is highly recommended for Rails + "preload_app true"
+  # as there's no need for the master process to hold a connection
+  defined?(ActiveRecord::Base) and
+    ActiveRecord::Base.connection.disconnect!
+
+  # The following is only recommended for memory/DB-constrained
+  # installations.  It is not needed if your system can house
+  # twice as many worker_processes as you have configured.
+  #
+  # This allows a new master process to incrementally
+  # phase out the old master process with SIGTTOU to avoid a
+  # thundering herd (especially in the "preload_app false" case)
+  # when doing a transparent upgrade.  The last worker spawned
+  # will then kill off the old master process with a SIGQUIT.
+  old_pid = "#{server.config[:pid]}.oldbin"
+  if old_pid != server.pid
+    begin
+      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
+      Process.kill(sig, File.read(old_pid).to_i)
+    rescue Errno::ENOENT, Errno::ESRCH
+    end
+  end
+
+  # Throttle the master from forking too quickly by sleeping.  Due
+  # to the implementation of standard Unix signal handlers, this
+  # helps (but does not completely) prevent identical, repeated signals
+  # from being lost when the receiving process is busy.
+  # sleep 1
+end
+
 after_fork do |server, worker|
+  # per-process listener ports for debugging/admin/migrations
+  # addr = "127.0.0.1:#{9293 + worker.nr}"
+  # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
+
+  # the following is *required* for Rails + "preload_app true",
   defined?(ActiveRecord::Base) and
     ActiveRecord::Base.establish_connection
+
+  # reset prometheus client, this will cause any opened metrics files to be closed
+  defined?(::Prometheus::Client.reinitialize_on_pid_change) &&
+    Prometheus::Client.reinitialize_on_pid_change
+
+  # if preload_app is true, then you may also want to check and
+  # restart any other shared sockets/descriptors such as Memcached,
+  # and Redis.  TokyoCabinet file handles are safe to reuse
+  # between any number of forked children (assuming your kernel
+  # correctly implements pread()/pwrite() system calls)
 end
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index 9ed5875a019..20d7ec90dcc 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -143,6 +143,7 @@ let
     GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/";
     GITLAB_STATE_PATH = "${cfg.statePath}";
     GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads";
+    SCHEMA = "${cfg.statePath}/db/schema.rb";
     GITLAB_LOG_PATH = "${cfg.statePath}/log";
     GITLAB_SHELL_PATH = "${cfg.packages.gitlab-shell}";
     GITLAB_SHELL_CONFIG_PATH = "${cfg.statePath}/shell/config.yml";
@@ -500,7 +501,7 @@ in {
         Type = "simple";
         User = cfg.user;
         Group = cfg.group;
-        TimeoutSec = "300";
+        TimeoutSec = "infinity";
         Restart = "on-failure";
         WorkingDirectory = gitlabEnv.HOME;
         ExecStart = "${cfg.packages.gitaly}/bin/gitaly ${gitalyToml}";
@@ -566,6 +567,7 @@ in {
         mkdir -p ${cfg.statePath}/tmp/pids
         mkdir -p ${cfg.statePath}/tmp/sockets
         mkdir -p ${cfg.statePath}/shell
+        mkdir -p ${cfg.statePath}/db
 
         rm -rf ${cfg.statePath}/config ${cfg.statePath}/shell/hooks
         mkdir -p ${cfg.statePath}/config
@@ -580,6 +582,7 @@ in {
         ln -sf ${cfg.statePath}/log /run/gitlab/log
         ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads
         ln -sf ${cfg.statePath}/tmp /run/gitlab/tmp
+        ln -sf $GITLAB_SHELL_CONFIG_PATH /run/gitlab/shell-config.yml
         chown -R ${cfg.user}:${cfg.group} /run/gitlab
 
         # Prepare home directory
@@ -587,6 +590,7 @@ in {
         touch ${gitlabEnv.HOME}/.ssh/authorized_keys
         chown -R ${cfg.user}:${cfg.group} ${gitlabEnv.HOME}/
 
+        cp -rf ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db
         cp -rf ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
         ${optionalString cfg.smtp.enable ''
           ln -sf ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb
diff --git a/nixos/modules/services/misc/serviio.nix b/nixos/modules/services/misc/serviio.nix
new file mode 100644
index 00000000000..a6612e9c6ad
--- /dev/null
+++ b/nixos/modules/services/misc/serviio.nix
@@ -0,0 +1,92 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.serviio;
+
+  serviioStart = pkgs.writeScript "serviio.sh" ''
+    #!${pkgs.bash}/bin/sh
+
+    SERVIIO_HOME=${pkgs.serviio}
+    
+    # Setup the classpath
+    SERVIIO_CLASS_PATH="$SERVIIO_HOME/lib/*:$SERVIIO_HOME/config"
+
+    # Setup Serviio specific properties
+    JAVA_OPTS="-Djava.net.preferIPv4Stack=true -Djava.awt.headless=true -Dorg.restlet.engine.loggerFacadeClass=org.restlet.ext.slf4j.Slf4jLoggerFacade
+               -Dderby.system.home=${cfg.dataDir}/library -Dserviio.home=${cfg.dataDir} -Dffmpeg.location=${pkgs.ffmpeg}/bin/ffmpeg -Ddcraw.location=${pkgs.dcraw}/bin/dcraw"
+
+    # Execute the JVM in the foreground
+    exec ${pkgs.jre}/bin/java -Xmx512M -Xms20M -XX:+UseG1GC -XX:GCTimeRatio=1 -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 $JAVA_OPTS -classpath "$SERVIIO_CLASS_PATH" org.serviio.MediaServer "$@"
+  '';
+  
+in {
+
+  ###### interface
+  options = {
+    services.serviio = {
+      
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the Serviio Media Server.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/serviio";
+        description = ''
+          The directory where serviio stores its state, data, etc.
+        '';
+      };
+
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.serviio = {
+      description = "Serviio Media Server";
+      after = [ "local-fs.target" "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.serviio ];
+      serviceConfig = {
+        User = "serviio";
+        Group = "serviio";
+        ExecStart = "${serviioStart}";
+        ExecStop = "${serviioStart} -stop";
+      };
+    };
+
+    users.extraUsers = [
+      { 
+        name = "serviio";
+        group = "serviio";
+        home = cfg.dataDir;
+        description = "Serviio Media Server User";
+        createHome = true;
+        isSystemUser = true;
+      }
+    ];
+
+    users.extraGroups = [
+      { name = "serviio";} 
+    ];
+
+    networking.firewall = {
+      allowedTCPPorts = [ 
+        8895  # serve UPnP responses
+        23423 # console
+        23424 # mediabrowser
+      ];
+      allowedUDPPorts = [ 
+        1900 # UPnP service discovey
+      ];
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix b/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
deleted file mode 100644
index ce2e1cf2d74..00000000000
--- a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
+++ /dev/null
@@ -1,68 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.blackboxExporter;
-in {
-  options = {
-    services.prometheus.blackboxExporter = {
-      enable = mkEnableOption "prometheus blackbox exporter";
-
-      configFile = mkOption {
-        type = types.path;
-        description = ''
-          Path to configuration file.
-        '';
-      };
-
-      port = mkOption {
-        type = types.int;
-        default = 9115;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the blackbox exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-blackbox-exporter = {
-      description = "Prometheus exporter for blackbox probes";
-      unitConfig.Documentation = "https://github.com/prometheus/blackbox_exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        AmbientCapabilities = [ "CAP_NET_RAW" ]; # for ping probes
-        ExecStart = ''
-          ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \
-            --web.listen-address :${toString cfg.port} \
-            --config.file ${cfg.configFile} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/collectd-exporter.nix b/nixos/modules/services/monitoring/prometheus/collectd-exporter.nix
deleted file mode 100644
index f8a5b9576a1..00000000000
--- a/nixos/modules/services/monitoring/prometheus/collectd-exporter.nix
+++ /dev/null
@@ -1,128 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.collectdExporter;
-
-  collectSettingsArgs = if (cfg.collectdBinary.enable) then ''
-    -collectd.listen-address ${optionalString (cfg.collectdBinary.listenAddress != null) cfg.collectdBinary.listenAddress}:${toString cfg.collectdBinary.port} \
-    -collectd.security-level ${cfg.collectdBinary.securityLevel} \
-  '' else "";
-
-in {
-  options = {
-    services.prometheus.collectdExporter = {
-      enable = mkEnableOption "prometheus collectd exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9103;
-        description = ''
-          Port to listen on.
-          This is used for scraping as well as the to receive collectd data via the write_http plugin.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "0.0.0.0";
-        description = ''
-          Address to listen on for web interface, telemetry and collectd JSON data.
-        '';
-      };
-
-      collectdBinary = {
-        enable = mkEnableOption "collectd binary protocol receiver";
-
-        authFile = mkOption {
-          default = null;
-          type = types.nullOr types.path;
-          description = "File mapping user names to pre-shared keys (passwords).";
-        };
-
-        port = mkOption {
-          type = types.int;
-          default = 25826;
-          description = ''Network address on which to accept collectd binary network packets.'';
-        };
-
-        listenAddress = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "0.0.0.0";
-          description = ''
-            Address to listen on for binary network packets.
-            '';
-        };
-
-        securityLevel = mkOption {
-          type = types.enum ["None" "Sign" "Encrypt"];
-          default = "None";
-          description = ''
-            Minimum required security level for accepted packets.
-            '';
-        };
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the collectd exporter.
-        '';
-      };
-
-      logFormat = mkOption {
-        type = types.str;
-        default = "logger:stderr";
-        example = "logger:syslog?appname=bob&local=7 or logger:stdout?json=true";
-        description = ''
-          Set the log target and format.
-        '';
-      };
-
-      logLevel = mkOption {
-        type = types.enum ["debug" "info" "warn" "error" "fatal"];
-        default = "info";
-        description = ''
-          Only log messages with the given severity or above.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = (optional cfg.openFirewall cfg.port) ++
-      (optional (cfg.openFirewall && cfg.collectdBinary.enable) cfg.collectdBinary.port);
-
-    systemd.services.prometheus-collectd-exporter = {
-      description = "Prometheus exporter for Collectd metrics";
-      unitConfig.Documentation = "https://github.com/prometheus/collectd_exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        DynamicUser = true;
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-collectd-exporter}/bin/collectd_exporter \
-            -log.format ${cfg.logFormat} \
-            -log.level ${cfg.logLevel} \
-            -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port} \
-            ${collectSettingsArgs} \
-            ${concatStringsSep " " cfg.extraFlags}
-        '';
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
new file mode 100644
index 00000000000..8d6ca1e40a1
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -0,0 +1,172 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters;
+
+  # each attribute in `exporterOpts` is expected to have specified:
+  #   - port        (types.int):   port on which the exporter listens
+  #   - serviceOpts (types.attrs): config that is merged with the
+  #                                default definition of the exporter's
+  #                                systemd service
+  #   - extraOpts   (types.attrs): extra configuration options to
+  #                                configure the exporter with, which
+  #                                are appended to the default options
+  #
+  #  Note that `extraOpts` is optional, but a script for the exporter's
+  #  systemd service must be provided by specifying either
+  #  `serviceOpts.script` or `serviceOpts.serviceConfig.ExecStart`
+  exporterOpts = {
+    blackbox = import ./exporters/blackbox.nix { inherit config lib pkgs; };
+    collectd = import ./exporters/collectd.nix { inherit config lib pkgs; };
+    fritzbox = import ./exporters/fritzbox.nix { inherit config lib pkgs; };
+    json     = import ./exporters/json.nix     { inherit config lib pkgs; };
+    minio    = import ./exporters/minio.nix    { inherit config lib pkgs; };
+    nginx    = import ./exporters/nginx.nix    { inherit config lib pkgs; };
+    node     = import ./exporters/node.nix     { inherit config lib pkgs; };
+    postfix  = import ./exporters/postfix.nix  { inherit config lib pkgs; };
+    snmp     = import ./exporters/snmp.nix     { inherit config lib pkgs; };
+    unifi    = import ./exporters/unifi.nix    { inherit config lib pkgs; };
+    varnish  = import ./exporters/varnish.nix  { inherit config lib pkgs; };
+  };
+
+  mkExporterOpts = ({ name, port }: {
+    enable = mkEnableOption "the prometheus ${name} exporter";
+    port = mkOption {
+      type = types.int;
+      default = port;
+      description = ''
+        Port to listen on.
+      '';
+    };
+    listenAddress = mkOption {
+      type = types.str;
+      default = "0.0.0.0";
+      description = ''
+        Address to listen on.
+      '';
+    };
+    extraFlags = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        Extra commandline options to pass to the ${name} exporter.
+      '';
+    };
+    openFirewall = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Open port in firewall for incoming connections.
+      '';
+    };
+    firewallFilter = mkOption {
+      type = types.str;
+      default = "-p tcp -m tcp --dport ${toString port}";
+      example = literalExample ''
+        "-i eth0 -p tcp -m tcp --dport ${toString port}"
+      '';
+      description = ''
+        Specify a filter for iptables to use when
+        <option>services.prometheus.exporters.${name}.openFirewall</option>
+        is true. It is used as `ip46tables -I INPUT <option>firewallFilter</option> -j ACCEPT`.
+      '';
+    };
+    user = mkOption {
+      type = types.str;
+      default = "nobody";
+      description = ''
+        User name under which the ${name} exporter shall be run.
+        Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true.
+      '';
+    };
+    group = mkOption {
+      type = types.str;
+      default = "nobody";
+      description = ''
+        Group under which the ${name} exporter shall be run.
+        Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true.
+      '';
+    };
+  });
+
+  mkSubModule = { name, port, extraOpts, serviceOpts }: {
+    ${name} = mkOption {
+      type = types.submodule {
+        options = (mkExporterOpts {
+          inherit name port;
+        } // extraOpts);
+      };
+      internal = true;
+      default = {};
+    };
+  };
+
+  mkSubModules = (foldl' (a: b: a//b) {}
+    (mapAttrsToList (name: opts: mkSubModule {
+      inherit name;
+      inherit (opts) port serviceOpts;
+      extraOpts = opts.extraOpts or {};
+    }) exporterOpts)
+  );
+
+  mkExporterConf = { name, conf, serviceOpts }:
+    mkIf conf.enable {
+      networking.firewall.extraCommands = mkIf conf.openFirewall ''
+        ip46tables -I INPUT ${conf.firewallFilter} -j ACCEPT
+      '';
+      systemd.services."prometheus-${name}-exporter" = mkMerge ([{
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        serviceConfig = {
+          Restart = mkDefault "always";
+          PrivateTmp = mkDefault true;
+          WorkingDirectory = mkDefault /tmp;
+        } // mkIf (!(serviceOpts.serviceConfig.DynamicUser or false)) {
+          User = conf.user;
+          Group = conf.group;
+        };
+      } serviceOpts ]);
+  };
+in
+{
+  options.services.prometheus.exporters = mkOption {
+    type = types.submodule {
+      options = (mkSubModules);
+    };
+    description = "Prometheus exporter configuration";
+    default = {};
+    example = literalExample ''
+      {
+        node = {
+          enable = true;
+          enabledCollectors = [ "systemd" ];
+        };
+        varnish.enable = true;
+      }
+    '';
+  };
+
+  config = mkMerge ([{
+    assertions = [{
+      assertion = (cfg.snmp.configurationPath == null) != (cfg.snmp.configuration == null);
+      message = ''
+        Please ensure you have either `services.prometheus.exporters.snmp.configuration'
+          or `services.prometheus.exporters.snmp.configurationPath' set!
+      '';
+    }];
+  }] ++ [(mkIf config.services.minio.enable {
+    services.prometheus.exporters.minio.minioAddress  = mkDefault "http://localhost:9000";
+    services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey;
+    services.prometheus.exporters.minio.minioAccessSecret = mkDefault config.services.minio.secretKey;
+  })] ++ (mapAttrsToList (name: conf:
+    mkExporterConf {
+      inherit name;
+      inherit (conf) serviceOpts;
+      conf = cfg.${name};
+    }) exporterOpts)
+  );
+
+  meta.doc = ./exporters.xml;
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.xml b/nixos/modules/services/monitoring/prometheus/exporters.xml
new file mode 100644
index 00000000000..4f0bcb29810
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters.xml
@@ -0,0 +1,135 @@
+<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-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><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:
+<programlisting>
+  services.promtheus.exporters.node = {
+    enable = true;
+    enabledCollectors = [
+      "logind"
+      "systemd"
+    ];
+    disabledCollectors = [
+      "textfile"
+    ];
+    openFirewall = true;
+    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><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>:
+    </para>
+  </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>
+    </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>
+        # nixpgs/nixos/modules/services/prometheus/exporters/postfix.nix
+        { config, lib, pkgs }:
+
+        with lib;
+
+        let
+          # for convenience we define cfg here
+          cfg = config.services.prometheus.exporters.postfix;
+        in
+        {
+          port = 9154; # The postfix exporter listens on this port by default
+
+          # `extraOpts` is an attribute set which contains additional options
+          # (and optional overrides for default options).
+          # Note that this attribute is optional.
+          extraOpts = {
+            telemetryPath = mkOption {
+              type = types.str;
+              default = "/metrics";
+              description = ''
+                Path under which to expose metrics.
+              '';
+            };
+            logfilePath = mkOption {
+              type = types.path;
+              default = /var/log/postfix_exporter_input.log;
+              example = /var/log/mail.log;
+              description = ''
+                Path where Postfix writes log entries.
+                This file will be truncated by this exporter!
+              '';
+            };
+            showqPath = mkOption {
+              type = types.path;
+              default = /var/spool/postfix/public/showq;
+              example = /var/lib/postfix/queue/public/showq;
+              description = ''
+                Path at which Postfix places its showq socket.
+              '';
+            };
+          };
+
+          # `serviceOpts` is an attribute set which contains configuration
+          # for the exporter's systemd service. One of
+          # `serviceOpts.script` and `serviceOpts.serviceConfig.ExecStart`
+          # has to be specified here. This will be merged with the default
+          # service confiuration.
+          serviceOpts = {
+            serviceConfig = {
+              ExecStart = ''
+                ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \
+                  --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+                  --web.telemetry-path ${cfg.telemetryPath} \
+                  ${concatStringsSep " \\\n  " cfg.extraFlags}
+              '';
+            };
+          };
+        }
+      </programlisting>
+    </para>
+  </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>
+    </para>
+  </listitem>
+</itemizedlist>
+</section>
+</chapter>
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix b/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
new file mode 100644
index 00000000000..d09d1c4f366
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.blackbox;
+in
+{
+  port = 9115;
+  extraOpts = {
+    configFile = mkOption {
+      type = types.path;
+      description = ''
+        Path to configuration file.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      AmbientCapabilities = [ "CAP_NET_RAW" ]; # for ping probes
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          --config.file ${cfg.configFile} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+      ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix b/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix
new file mode 100644
index 00000000000..0eba3527162
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix
@@ -0,0 +1,78 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.collectd;
+in
+{
+  port = 9103;
+  extraOpts = {
+    collectdBinary = {
+      enable = mkEnableOption "collectd binary protocol receiver";
+
+      authFile = mkOption {
+        default = null;
+        type = types.nullOr types.path;
+        description = "File mapping user names to pre-shared keys (passwords).";
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 25826;
+        description = ''Network address on which to accept collectd binary network packets.'';
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        description = ''
+          Address to listen on for binary network packets.
+          '';
+      };
+
+      securityLevel = mkOption {
+        type = types.enum ["None" "Sign" "Encrypt"];
+        default = "None";
+        description = ''
+          Minimum required security level for accepted packets.
+        '';
+      };
+    };
+
+    logFormat = mkOption {
+      type = types.str;
+      default = "logger:stderr";
+      example = "logger:syslog?appname=bob&local=7 or logger:stdout?json=true";
+      description = ''
+        Set the log target and format.
+      '';
+    };
+
+    logLevel = mkOption {
+      type = types.enum ["debug" "info" "warn" "error" "fatal"];
+      default = "info";
+      description = ''
+        Only log messages with the given severity or above.
+      '';
+    };
+  };
+  serviceOpts = let
+    collectSettingsArgs = if (cfg.collectdBinary.enable) then ''
+      -collectd.listen-address ${cfg.collectdBinary.listenAddress}:${toString cfg.collectdBinary.port} \
+      -collectd.security-level ${cfg.collectdBinary.securityLevel} \
+    '' else "";
+  in {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-collectd-exporter}/bin/collectd_exporter \
+          -log.format ${cfg.logFormat} \
+          -log.level ${cfg.logLevel} \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${collectSettingsArgs} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix b/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix
new file mode 100644
index 00000000000..a3f1d9d3132
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.fritzbox;
+in
+{
+  port = 9133;
+  extraOpts = {
+    gatewayAddress = mkOption {
+      type = types.str;
+      default = "fritz.box";
+      description = ''
+        The hostname or IP of the FRITZ!Box.
+      '';
+    };
+
+    gatewayPort = mkOption {
+      type = types.int;
+      default = 49000;
+      description = ''
+        The port of the FRITZ!Box UPnP service.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-fritzbox-exporter}/bin/fritzbox_exporter \
+          -listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          -gateway-address ${cfg.gatewayAddress} \
+          -gateway-port ${toString cfg.gatewayPort} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/json.nix b/nixos/modules/services/monitoring/prometheus/exporters/json.nix
new file mode 100644
index 00000000000..a5494e85e01
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/json.nix
@@ -0,0 +1,36 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.json;
+in
+{
+  port = 7979;
+  extraOpts = {
+    url = mkOption {
+      type = types.str;
+      description = ''
+        URL to scrape JSON from.
+      '';
+    };
+    configFile = mkOption {
+      type = types.path;
+      description = ''
+        Path to configuration file.
+      '';
+    };
+    listenAddress = {}; # not used
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \
+          --port ${toString cfg.port} \
+          ${cfg.url} ${cfg.configFile} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/minio.nix b/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
new file mode 100644
index 00000000000..3cc4ffdbc8f
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
@@ -0,0 +1,65 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.minio;
+in
+{
+  port = 9290;
+  extraOpts = {
+    minioAddress = mkOption {
+      type = types.str;
+      example = "https://10.0.0.1:9000";
+      description = ''
+        The URL of the minio server.
+        Use HTTPS if Minio accepts secure connections only.
+        By default this connects to the local minio server if enabled.
+      '';
+    };
+
+    minioAccessKey = mkOption {
+      type = types.str;
+      example = "yourMinioAccessKey";
+      description = ''
+        The value of the Minio access key.
+        It is required in order to connect to the server.
+        By default this uses the one from the local minio server if enabled
+        and <literal>config.services.minio.accessKey</literal>.
+      '';
+    };
+
+    minioAccessSecret = mkOption {
+      type = types.str;
+      description = ''
+        The value of the Minio access secret.
+        It is required in order to connect to the server.
+        By default this uses the one from the local minio server if enabled
+        and <literal>config.services.minio.secretKey</literal>.
+      '';
+    };
+
+    minioBucketStats = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Collect statistics about the buckets and files in buckets.
+        It requires more computation, use it carefully in case of large buckets..
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-minio-exporter}/bin/minio-exporter \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          -minio.server ${cfg.minioAddress} \
+          -minio.access-key ${cfg.minioAccessKey} \
+          -minio.access-secret ${cfg.minioAccessSecret} \
+          ${optionalString cfg.minioBucketStats "-minio.bucket-stats"} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
new file mode 100644
index 00000000000..6a3ba2d0457
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.nginx;
+in
+{
+  port = 9113;
+  extraOpts = {
+    scrapeUri = mkOption {
+      type = types.string;
+      default = "http://localhost/nginx_status";
+      description = ''
+        Address to access the nginx status page.
+        Can be enabled with services.nginx.statusPage = true.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-nginx-exporter}/bin/nginx_exporter \
+          -nginx.scrape_uri '${cfg.scrapeUri}' \
+          -telemetry.address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/node.nix b/nixos/modules/services/monitoring/prometheus/exporters/node.nix
new file mode 100644
index 00000000000..c85f5f9cfb2
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/node.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.node;
+in
+{
+  port = 9100;
+  extraOpts = {
+    enabledCollectors = mkOption {
+      type = types.listOf types.string;
+      default = [];
+      example = ''[ "systemd" ]'';
+      description = ''
+        Collectors to enable. The collectors listed here are enabled in addition to the default ones.
+      '';
+    };
+    disabledCollectors = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = ''[ "timex" ]'';
+      description = ''
+        Collectors to disable which are enabled by default.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      ExecStart = ''
+        ${pkgs.prometheus-node-exporter}/bin/node_exporter \
+          ${concatMapStringsSep " " (x: "--collector." + x) cfg.enabledCollectors} \
+          ${concatMapStringsSep " " (x: "--no-collector." + x) cfg.disabledCollectors} \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
new file mode 100644
index 00000000000..1e909aa27d8
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
@@ -0,0 +1,46 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.postfix;
+in
+{
+  port = 9154;
+  extraOpts = {
+    telemetryPath = mkOption {
+      type = types.str;
+      default = "/metrics";
+      description = ''
+        Path under which to expose metrics.
+      '';
+    };
+    logfilePath = mkOption {
+      type = types.path;
+      default = "/var/log/postfix_exporter_input.log";
+      example = "/var/log/mail.log";
+      description = ''
+        Path where Postfix writes log entries.
+        This file will be truncated by this exporter!
+      '';
+    };
+    showqPath = mkOption {
+      type = types.path;
+      default = "/var/spool/postfix/public/showq";
+      example = "/var/lib/postfix/queue/public/showq";
+      description = ''
+        Path where Postfix places it's showq socket.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      ExecStart = ''
+        ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          --web.telemetry-path ${cfg.telemetryPath} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix b/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix
new file mode 100644
index 00000000000..404cd0a1896
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix
@@ -0,0 +1,71 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.snmp;
+in
+{
+  port = 9116;
+  extraOpts = {
+    configurationPath = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        Path to a snmp exporter configuration file. Mutually exclusive with 'configuration' option.
+      '';
+      example = "./snmp.yml";
+    };
+
+    configuration = mkOption {
+      type = types.nullOr types.attrs;
+      default = {};
+      description = ''
+        Snmp exporter configuration as nix attribute set. Mutually exclusive with 'configurationPath' option.
+      '';
+      example = ''
+        {
+          "default" = {
+            "version" = 2;
+            "auth" = {
+              "community" = "public";
+            };
+          };
+        };
+      '';
+    };
+
+    logFormat = mkOption {
+      type = types.str;
+      default = "logger:stderr";
+      description = ''
+        Set the log target and format.
+      '';
+    };
+
+    logLevel = mkOption {
+      type = types.enum ["debug" "info" "warn" "error" "fatal"];
+      default = "info";
+      description = ''
+        Only log messages with the given severity or above.
+      '';
+    };
+  };
+  serviceOpts = let
+    configFile = if cfg.configurationPath != null
+                 then cfg.configurationPath
+                 else "${pkgs.writeText "snmp-eporter-conf.yml" (builtins.toJSON cfg.configuration)}";
+    in {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-snmp-exporter.bin}/bin/snmp_exporter \
+          -config.file ${configFile} \
+          -log.format ${cfg.logFormat} \
+          -log.level ${cfg.logLevel} \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix b/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix
new file mode 100644
index 00000000000..011dcbe208e
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix
@@ -0,0 +1,67 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.unifi;
+in
+{
+  port = 9130;
+  extraOpts = {
+    unifiAddress = mkOption {
+      type = types.str;
+      example = "https://10.0.0.1:8443";
+      description = ''
+        URL of the UniFi Controller API.
+      '';
+    };
+
+    unifiInsecure = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        If enabled skip the verification of the TLS certificate of the UniFi Controller API.
+        Use with caution.
+      '';
+    };
+
+    unifiUsername = mkOption {
+      type = types.str;
+      example = "ReadOnlyUser";
+      description = ''
+        username for authentication against UniFi Controller API.
+      '';
+    };
+
+    unifiPassword = mkOption {
+      type = types.str;
+      description = ''
+        Password for authentication against UniFi Controller API.
+      '';
+    };
+
+    unifiTimeout = mkOption {
+      type = types.str;
+      default = "5s";
+      example = "2m";
+      description = ''
+        Timeout including unit for UniFi Controller API requests.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-unifi-exporter}/bin/unifi_exporter \
+          -telemetry.addr ${cfg.listenAddress}:${toString cfg.port} \
+          -unifi.addr ${cfg.unifiAddress} \
+          -unifi.username ${cfg.unifiUsername} \
+          -unifi.password ${cfg.unifiPassword} \
+          -unifi.timeout ${cfg.unifiTimeout} \
+          ${optionalString cfg.unifiInsecure "-unifi.insecure" } \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix b/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix
new file mode 100644
index 00000000000..b439a83e7aa
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix
@@ -0,0 +1,21 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.varnish;
+in
+{
+  port = 9131;
+  serviceOpts = {
+    path = [ pkgs.varnish ];
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix b/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix
deleted file mode 100644
index 6da39b6519c..00000000000
--- a/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix
+++ /dev/null
@@ -1,76 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.fritzboxExporter;
-in {
-  options = {
-    services.prometheus.fritzboxExporter = {
-      enable = mkEnableOption "prometheus fritzbox exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9133;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      gatewayAddress = mkOption {
-        type = types.str;
-        default = "fritz.box";
-        description = ''
-          The hostname or IP of the FRITZ!Box.
-        '';
-      };
-
-      gatewayPort = mkOption {
-        type = types.int;
-        default = 49000;
-        description = ''
-          The port of the FRITZ!Box UPnP service.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the fritzbox exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-fritzbox-exporter = {
-      description = "Prometheus exporter for FRITZ!Box via UPnP";
-      unitConfig.Documentation = "https://github.com/ndecker/fritzbox_exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-fritzbox-exporter}/bin/fritzbox_exporter \
-            -listen-address :${toString cfg.port} \
-            -gateway-address ${cfg.gatewayAddress} \
-            -gateway-port ${toString cfg.gatewayPort} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/json-exporter.nix b/nixos/modules/services/monitoring/prometheus/json-exporter.nix
deleted file mode 100644
index 6bc56df9834..00000000000
--- a/nixos/modules/services/monitoring/prometheus/json-exporter.nix
+++ /dev/null
@@ -1,74 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.jsonExporter;
-in {
-  options = {
-    services.prometheus.jsonExporter = {
-      enable = mkEnableOption "prometheus JSON exporter";
-
-      url = mkOption {
-        type = types.str;
-        description = ''
-          URL to scrape JSON from.
-        '';
-      };
-
-      configFile = mkOption {
-        type = types.path;
-        description = ''
-          Path to configuration file.
-        '';
-      };
-
-      port = mkOption {
-        type = types.int;
-        default = 7979;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the JSON exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-json-exporter = {
-      description = "Prometheus exporter for JSON over HTTP";
-      unitConfig.Documentation = "https://github.com/kawamuray/prometheus-json-exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \
-            --port ${toString cfg.port} \
-            ${cfg.url} ${cfg.configFile} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/minio-exporter.nix b/nixos/modules/services/monitoring/prometheus/minio-exporter.nix
deleted file mode 100644
index 4314671523c..00000000000
--- a/nixos/modules/services/monitoring/prometheus/minio-exporter.nix
+++ /dev/null
@@ -1,117 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.minioExporter;
-in {
-  options = {
-    services.prometheus.minioExporter = {
-      enable = mkEnableOption "prometheus minio exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9290;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "0.0.0.0";
-        description = ''
-          Address to listen on for web interface and telemetry.
-        '';
-      };
-
-      minioAddress = mkOption {
-        type = types.str;
-        example = "https://10.0.0.1:9000";
-        default = if config.services.minio.enable then "http://localhost:9000" else null;
-        description = ''
-          The URL of the minio server.
-          Use HTTPS if Minio accepts secure connections only.
-          By default this connects to the local minio server if enabled.
-        '';
-      };
-
-      minioAccessKey = mkOption ({
-        type = types.str;
-        example = "BKIKJAA5BMMU2RHO6IBB";
-        description = ''
-          The value of the Minio access key.
-          It is required in order to connect to the server.
-          By default this uses the one from the local minio server if enabled
-          and <literal>config.services.minio.accessKey</literal>.
-        '';
-      } // optionalAttrs (config.services.minio.enable && config.services.minio.accessKey != "") {
-        default = config.services.minio.accessKey;
-      });
-
-      minioAccessSecret = mkOption ({
-        type = types.str;
-        description = ''
-          The calue of the Minio access secret.
-          It is required in order to connect to the server.
-          By default this uses the one from the local minio server if enabled
-          and <literal>config.services.minio.secretKey</literal>.
-        '';
-      } // optionalAttrs (config.services.minio.enable && config.services.minio.secretKey != "") {
-        default = config.services.minio.secretKey;
-      });
-
-      minioBucketStats = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Collect statistics about the buckets and files in buckets.
-          It requires more computation, use it carefully in case of large buckets..
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the minio exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-minio-exporter = {
-      description = "Prometheus exporter for Minio server metrics";
-      unitConfig.Documentation = "https://github.com/joe-pll/minio-exporter";
-      wantedBy = [ "multi-user.target" ];
-      after = optional config.services.minio.enable "minio.service";
-      serviceConfig = {
-        DynamicUser = true;
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-minio-exporter}/bin/minio-exporter \
-            -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port} \
-            -minio.server ${cfg.minioAddress} \
-            -minio.access-key ${cfg.minioAccessKey} \
-            -minio.access-secret ${cfg.minioAccessSecret} \
-            ${optionalString cfg.minioBucketStats "-minio.bucket-stats"} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix b/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix
deleted file mode 100644
index 1ccafee3b18..00000000000
--- a/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix
+++ /dev/null
@@ -1,78 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.nginxExporter;
-in {
-  options = {
-    services.prometheus.nginxExporter = {
-      enable = mkEnableOption "prometheus nginx exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9113;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.string;
-        default = "0.0.0.0";
-        description = ''
-          Address to listen on.
-        '';
-      };
-
-      scrapeUri = mkOption {
-        type = types.string;
-        default = "http://localhost/nginx_status";
-        description = ''
-          Address to access the nginx status page.
-          Can be enabled with services.nginx.statusPage = true.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the nginx exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-nginx-exporter = {
-      after = [ "network.target" "nginx.service" ];
-      description = "Prometheus exporter for nginx metrics";
-      unitConfig.Documentation = "https://github.com/discordianfish/nginx_exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        User = "nobody";
-        Restart  = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-nginx-exporter}/bin/nginx_exporter \
-            -nginx.scrape_uri '${cfg.scrapeUri}' \
-            -telemetry.address ${cfg.listenAddress}:${toString cfg.port} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/node-exporter.nix b/nixos/modules/services/monitoring/prometheus/node-exporter.nix
deleted file mode 100644
index bad4389ce79..00000000000
--- a/nixos/modules/services/monitoring/prometheus/node-exporter.nix
+++ /dev/null
@@ -1,87 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.nodeExporter;
-in {
-  options = {
-    services.prometheus.nodeExporter = {
-      enable = mkEnableOption "prometheus node exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9100;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.string;
-        default = "0.0.0.0";
-        description = ''
-          Address to listen on.
-        '';
-      };
-
-      enabledCollectors = mkOption {
-        type = types.listOf types.string;
-        default = [];
-        example = ''[ "systemd" ]'';
-        description = ''
-          Collectors to enable. The collectors listed here are enabled in addition to the default ones.
-        '';
-      };
-
-      disabledCollectors = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = ''[ "timex" ]'';
-        description = ''
-          Collectors to disable which are enabled by default.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the node exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-node-exporter = {
-      description = "Prometheus exporter for machine metrics";
-      unitConfig.Documentation = "https://github.com/prometheus/node_exporter";
-      wantedBy = [ "multi-user.target" ];
-      script = ''
-        exec ${pkgs.prometheus-node-exporter}/bin/node_exporter \
-          ${concatMapStringsSep " " (x: "--collector." + x) cfg.enabledCollectors} \
-          ${concatMapStringsSep " " (x: "--no-collector." + x) cfg.disabledCollectors} \
-          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
-          ${concatStringsSep " \\\n  " cfg.extraFlags}
-      '';
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix b/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix
deleted file mode 100644
index fe33f8c1f04..00000000000
--- a/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix
+++ /dev/null
@@ -1,127 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.snmpExporter;
-  mkConfigFile = pkgs.writeText "snmp.yml" (if cfg.configurationPath == null then builtins.toJSON cfg.configuration else builtins.readFile cfg.configurationPath);
-in {
-  options = {
-    services.prometheus.snmpExporter = {
-      enable = mkEnableOption "Prometheus snmp exporter";
-
-      user = mkOption {
-        type = types.str;
-        default = "nobody";
-        description = ''
-          User name under which snmp exporter shall be run.
-        '';
-      };
-
-      group = mkOption {
-        type = types.str;
-        default = "nogroup";
-        description = ''
-          Group under which snmp exporter shall be run.
-        '';
-      };
-
-      port = mkOption {
-        type = types.int;
-        default = 9116;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Address to listen on for web interface and telemetry.
-        '';
-      };
-
-      configurationPath = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        description = ''
-          Path to a snmp exporter configuration file. Mutually exclusive with 'configuration' option.
-        '';
-        example = "./snmp.yml";
-      };
-
-      configuration = mkOption {
-        type = types.nullOr types.attrs;
-        default = {};
-        description = ''
-          Snmp exporter configuration as nix attribute set. Mutually exclusive with 'configurationPath' option.
-        '';
-        example = ''
-          {
-            "default" = {
-              "version" = 2;
-              "auth" = {
-                "community" = "public";
-              };
-            };
-          };
-        '';
-      };
-
-      logFormat = mkOption {
-        type = types.str;
-        default = "logger:stderr";
-        description = ''
-          Set the log target and format.
-        '';
-      };
-
-      logLevel = mkOption {
-        type = types.enum ["debug" "info" "warn" "error" "fatal"];
-        default = "info";
-        description = ''
-          Only log messages with the given severity or above.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    assertions = singleton
-      {
-        assertion = (cfg.configurationPath == null) != (cfg.configuration == null);
-        message = "Please ensure you have either 'configuration' or 'configurationPath' set!";
-      };
-
-    systemd.services.prometheus-snmp-exporter = {
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
-      script = ''
-        ${pkgs.prometheus-snmp-exporter.bin}/bin/snmp_exporter \
-          -config.file ${mkConfigFile} \
-          -log.format ${cfg.logFormat} \
-          -log.level ${cfg.logLevel} \
-          -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port}
-      '';
-
-      serviceConfig = {
-        User = cfg.user;
-        Group = cfg.group;
-        Restart  = "always";
-        PrivateTmp = true;
-        WorkingDirectory = "/tmp";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix b/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
deleted file mode 100644
index 0a56d6ae95a..00000000000
--- a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
+++ /dev/null
@@ -1,105 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.unifiExporter;
-in {
-  options = {
-    services.prometheus.unifiExporter = {
-      enable = mkEnableOption "prometheus unifi exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9130;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      unifiAddress = mkOption {
-        type = types.str;
-        example = "https://10.0.0.1:8443";
-        description = ''
-          URL of the UniFi Controller API.
-        '';
-      };
-
-      unifiInsecure = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          If enabled skip the verification of the TLS certificate of the UniFi Controller API.
-          Use with caution.
-        '';
-      };
-      
-      unifiUsername = mkOption {
-        type = types.str;
-        example = "ReadOnlyUser";
-        description = ''
-          username for authentication against UniFi Controller API.
-        '';
-      };
-      
-      unifiPassword = mkOption {
-        type = types.str;
-        description = ''
-          Password for authentication against UniFi Controller API.
-        '';
-      };
-      
-      unifiTimeout = mkOption {
-        type = types.str;
-        default = "5s";
-        example = "2m";
-        description = ''
-          Timeout including unit for UniFi Controller API requests.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the unifi exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-unifi-exporter = {
-      description = "Prometheus exporter for UniFi Controller metrics";
-      unitConfig.Documentation = "https://github.com/mdlayher/unifi_exporter";
-      wantedBy = [ "multi-user.target" ];
-      after = optional config.services.unifi.enable "unifi.service";
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-unifi-exporter}/bin/unifi_exporter \
-            -telemetry.addr :${toString cfg.port} \
-            -unifi.addr ${cfg.unifiAddress} \
-            -unifi.username ${cfg.unifiUsername} \
-            -unifi.password ${cfg.unifiPassword} \
-            -unifi.timeout ${cfg.unifiTimeout} \
-            ${optionalString cfg.unifiInsecure "-unifi.insecure" } \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix b/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix
deleted file mode 100644
index 143ebb62aea..00000000000
--- a/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix
+++ /dev/null
@@ -1,61 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-# Shamelessly cribbed from nginx-exporter.nix. ~ C.
-with lib;
-
-let
-  cfg = config.services.prometheus.varnishExporter;
-in {
-  options = {
-    services.prometheus.varnishExporter = {
-      enable = mkEnableOption "prometheus Varnish exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9131;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the Varnish exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-varnish-exporter = {
-      description = "Prometheus exporter for Varnish metrics";
-      unitConfig.Documentation = "https://github.com/jonnenauha/prometheus_varnish_exporter";
-      wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.varnish ];
-      script = ''
-        exec ${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \
-          -web.listen-address :${toString cfg.port} \
-          ${concatStringsSep " \\\n  " cfg.extraFlags}
-      '';
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index f83fb7a6d5d..10e96eb4036 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -135,8 +135,7 @@ in {
         default = { inherit networkmanager modemmanager wpa_supplicant
                             networkmanager-openvpn networkmanager-vpnc
                             networkmanager-openconnect networkmanager-fortisslvpn
-                            networkmanager-pptp networkmanager-l2tp
-                            networkmanager-iodine; };
+                            networkmanager-l2tp networkmanager-iodine; };
         internal = true;
       };
 
@@ -267,8 +266,6 @@ in {
       message = "You can not use networking.networkmanager with networking.wireless";
     }];
 
-    boot.kernelModules = [ "ppp_mppe" ]; # Needed for most (all?) PPTP VPN connections.
-
     environment.etc = with cfg.basePackages; [
       { source = configFile;
         target = "NetworkManager/NetworkManager.conf";
@@ -285,9 +282,6 @@ in {
       { source = "${networkmanager-fortisslvpn}/etc/NetworkManager/VPN/nm-fortisslvpn-service.name";
         target = "NetworkManager/VPN/nm-fortisslvpn-service.name";
       }
-      { source = "${networkmanager-pptp}/etc/NetworkManager/VPN/nm-pptp-service.name";
-        target = "NetworkManager/VPN/nm-pptp-service.name";
-      }
       { source = "${networkmanager-l2tp}/etc/NetworkManager/VPN/nm-l2tp-service.name";
         target = "NetworkManager/VPN/nm-l2tp-service.name";
       }
diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix
index 9d7e6d6018a..d57ebb61f63 100644
--- a/nixos/modules/services/networking/prosody.nix
+++ b/nixos/modules/services/networking/prosody.nix
@@ -15,6 +15,7 @@ let
         description = "Path to the key file.";
       };
 
+      # TODO: rename to certificate to match the prosody config
       cert = mkOption {
         type = types.path;
         description = "Path to the certificate file.";
@@ -30,7 +31,7 @@ let
   };
 
   moduleOpts = {
-
+    # Generally required
     roster = mkOption {
       type = types.bool;
       default = true;
@@ -61,12 +62,38 @@ let
       description = "Service discovery";
     };
 
-    legacyauth = mkOption {
+    # Not essential, but recommended
+    carbons = mkOption {
       type = types.bool;
       default = true;
-      description = "Legacy authentication. Only used by some old clients and bots";
+      description = "Keep multiple clients in sync";
+    };
+
+    pep = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Enables users to publish their mood, activity, playing music and more";
+    };
+
+    private = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Private XML storage (for room bookmarks, etc.)";
+    };
+
+    blocklist = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allow users to block communications with other users";
     };
 
+    vcard = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allow users to set vCards";
+    };
+
+    # Nice to have
     version = mkOption {
       type = types.bool;
       default = true;
@@ -91,36 +118,112 @@ let
       description = "Replies to XMPP pings with pongs";
     };
 
-    console = mkOption {
+    register = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allow users to register on this server using a client and change passwords";
+    };
+
+    mam = mkOption {
       type = types.bool;
       default = false;
-      description = "telnet to port 5582";
+      description = "Store messages in an archive and allow users to access it";
     };
 
+    # Admin interfaces
+    admin_adhoc = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allows administration via an XMPP client that supports ad-hoc commands";
+    };
+
+    admin_telnet = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Opens telnet console interface on localhost port 5582";
+    };
+
+    # HTTP modules
     bosh = mkOption {
       type = types.bool;
       default = false;
       description = "Enable BOSH clients, aka 'Jabber over HTTP'";
     };
 
-    httpserver = mkOption {
+    websocket = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable WebSocket support";
+    };
+
+    http_files = mkOption {
       type = types.bool;
       default = false;
       description = "Serve static files from a directory over HTTP";
     };
 
-    websocket = mkOption {
+    # Other specific functionality
+    limits = mkOption {
       type = types.bool;
       default = false;
-      description = "Enable WebSocket support";
+      description = "Enable bandwidth limiting for XMPP connections";
+    };
+
+    groups = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Shared roster support";
+    };
+
+    server_contact_info = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Publish contact information for this service";
+    };
+
+    announce = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Send announcement to all online users";
+    };
+
+    welcome = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Welcome users who register accounts";
+    };
+
+    watchregistrations = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Alert admins of registrations";
+    };
+
+    motd = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Send a message to users when they log in";
+    };
+
+    legacyauth = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Legacy authentication. Only used by some old clients and bots";
+    };
+
+    proxy65 = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enables a file transfer proxy service which clients behind NAT can use";
     };
 
   };
 
   toLua = x:
     if builtins.isString x then ''"${x}"''
-    else if builtins.isBool x then toString x
+    else if builtins.isBool x then (if x == true then "true" else "false")
     else if builtins.isInt x then toString x
+    else if builtins.isList x then ''{ ${lib.concatStringsSep ", " (map (n: toLua n) x) } }''
     else throw "Invalid Lua value";
 
   createSSLOptsStr = o: ''
@@ -198,6 +301,59 @@ in
         description = "Allow account creation";
       };
 
+      c2sRequireEncryption = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Force clients to use encrypted connections? This option will
+          prevent clients from authenticating unless they are using encryption.
+        '';
+      };
+
+      s2sRequireEncryption = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Force servers to use encrypted connections? This option will
+          prevent servers from authenticating unless they are using encryption.
+          Note that this is different from authentication.
+        '';
+      };
+
+      s2sSecureAuth = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Force certificate authentication for server-to-server connections?
+          This provides ideal security, but requires servers you communicate
+          with to support encryption AND present valid, trusted certificates.
+          For more information see https://prosody.im/doc/s2s#security
+        '';
+      };
+
+      s2sInsecureDomains = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "insecure.example.com" ];
+        description = ''
+          Some servers have invalid or self-signed certificates. You can list
+          remote domains here that will not be required to authenticate using
+          certificates. They will be authenticated using DNS instead, even
+          when s2s_secure_auth is enabled.
+        '';
+      };
+
+      s2sSecureDomains = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "jabber.org" ];
+        description = ''
+          Even if you leave s2s_secure_auth disabled, you can still require valid
+          certificates for some domains by specifying a list here.
+        '';
+      };
+
+
       modules = moduleOpts;
 
       extraModules = mkOption {
@@ -206,6 +362,12 @@ in
         description = "Enable custom modules";
       };
 
+      extraPluginPaths = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = "Addtional path in which to look find plugins/modules";
+      };
+
       virtualHosts = mkOption {
 
         description = "Define the virtual hosts";
@@ -255,37 +417,47 @@ in
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = [ pkgs.prosody ];
+    environment.systemPackages = [ cfg.package ];
 
     environment.etc."prosody/prosody.cfg.lua".text = ''
 
       pidfile = "/var/lib/prosody/prosody.pid"
 
-
       log = "*syslog"
 
       data_path = "/var/lib/prosody"
-
-      allow_registration = ${boolToString cfg.allowRegistration};
-
-      ${ optionalString cfg.modules.console "console_enabled = true;" }
+      plugin_paths = {
+        ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.extraPluginPaths) }
+      }
 
       ${ optionalString  (cfg.ssl != null) (createSSLOptsStr cfg.ssl) }
 
-      admins = { ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.admins) } };
+      admins = ${toLua cfg.admins}
+
+      -- we already build with libevent, so we can just enable it for a more performant server
+      use_libevent = true
 
       modules_enabled = {
 
         ${ lib.concatStringsSep "\n\ \ " (lib.mapAttrsToList
-          (name: val: optionalString val ''"${name}";'')
+          (name: val: optionalString val "${toLua name};")
         cfg.modules) }
+        ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.package.communityModules)}
+        ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.extraModules)}
+      };
 
-        ${ optionalString cfg.allowRegistration "\"register\"\;" }
+      allow_registration = ${toLua cfg.allowRegistration}
 
-        ${ lib.concatStringsSep "\n" (map (x: "\"${x}\";") cfg.extraModules)}
+      c2s_require_encryption = ${toLua cfg.c2sRequireEncryption}
+
+      s2s_require_encryption = ${toLua cfg.s2sRequireEncryption}
+
+      s2s_secure_auth = ${toLua cfg.s2sSecureAuth}
+
+      s2s_insecure_domains = ${toLua cfg.s2sInsecureDomains}
+
+      s2s_secure_domains = ${toLua cfg.s2sSecureDomains}
 
-        "posix";
-      };
 
       ${ cfg.extraConfig }
 
diff --git a/nixos/modules/services/security/hologram-server.nix b/nixos/modules/services/security/hologram-server.nix
index bb56e2df09b..bad02c7440b 100644
--- a/nixos/modules/services/security/hologram-server.nix
+++ b/nixos/modules/services/security/hologram-server.nix
@@ -23,8 +23,9 @@ let
       account     = cfg.awsAccount;
       defaultrole = cfg.awsDefaultRole;
     };
-    stats  = cfg.statsAddress;
-    listen = cfg.listenAddress;
+    stats        = cfg.statsAddress;
+    listen       = cfg.listenAddress;
+    cachetimeout = cfg.cacheTimeoutSeconds;
   });
 in {
   options = {
@@ -106,6 +107,12 @@ in {
         default     = "";
         description = "Address of statsd server";
       };
+
+      cacheTimeoutSeconds = mkOption {
+        type        = types.int;
+        default     = 3600;
+        description = "How often (in seconds) to refresh the LDAP cache";
+      };
     };
   };
 
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index fed91756e76..2c727de2102 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -5,6 +5,7 @@ with lib;
 let
   cfg = config.services.tor;
   torDirectory = "/var/lib/tor";
+  torRunDirectory = "/run/tor";
 
   opt    = name: value: optionalString (value != null) "${name} ${value}";
   optint = name: value: optionalString (value != null && value != 0)    "${name} ${toString value}";
@@ -38,6 +39,7 @@ let
     ''}
 
     ${optint "ControlPort" cfg.controlPort}
+    ${optionalString cfg.controlSocket.enable "ControlSocket ${torRunDirectory}/control GroupWritable RelaxDirModeCheck"}
   ''
   # Client connection config
   + optionalString cfg.client.enable ''
@@ -140,6 +142,17 @@ in
         '';
       };
 
+      controlSocket = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Wheter to enable Tor control socket. Control socket is created
+            in <literal>${torRunDirectory}/control</literal>
+          '';
+        };
+      };
+
       client = {
         enable = mkOption {
           type = types.bool;
@@ -692,7 +705,7 @@ in
 
         # Translated from the upstream contrib/dist/tor.service.in
         preStart = ''
-          install -o tor -g tor -d ${torDirectory}/onion
+          install -o tor -g tor -d ${torDirectory}/onion ${torRunDirectory}
           ${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config
         '';
 
@@ -716,7 +729,7 @@ in
             DevicePolicy            = "closed";
             InaccessibleDirectories = "/home";
             ReadOnlyDirectories     = "/";
-            ReadWriteDirectories    = torDirectory;
+            ReadWriteDirectories    = [torDirectory torRunDirectory];
             NoNewPrivileges         = "yes";
           };
       };
diff --git a/nixos/modules/services/web-apps/nixbot.nix b/nixos/modules/services/web-apps/nixbot.nix
deleted file mode 100644
index 0592d01bf36..00000000000
--- a/nixos/modules/services/web-apps/nixbot.nix
+++ /dev/null
@@ -1,149 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.nixbot;
-  pyramidIni = ''
-    ###
-    # app configuration
-    # http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/environment.html
-    ###
-
-    [app:main]
-    use = egg:nixbot
-
-    nixbot.github_token = ${cfg.githubToken}
-    nixbot.bot_name = ${cfg.botName}
-    nixbot.repo = ${cfg.repo}
-    nixbot.pr_repo = ${cfg.prRepo}
-    nixbot.hydra_jobsets_repo = ${cfg.hydraJobsetsRepo}
-    nixbot.github_secret = justnotsorandom
-    nixbot.public_url = ${cfg.publicUrl}
-    nixbot.repo_dir = ${cfg.repoDir}
-
-    pyramid.reload_templates = false
-    pyramid.debug_authorization = false
-    pyramid.debug_notfound = false
-    pyramid.debug_routematch = false
-    pyramid.default_locale_name = en
-
-    # By default, the toolbar only appears for clients from IP addresses
-    # '127.0.0.1' and '::1'.
-    # debugtoolbar.hosts = 127.0.0.1 ::1
-
-    ###
-    # wsgi server configuration
-    ###
-
-    [server:main]
-    use = egg:waitress#main
-    host = 0.0.0.0
-    port = 6543
-
-    ###
-    # logging configuration
-    # http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/logging.html
-    ###
-
-    [loggers]
-    keys = root, nixbot
-
-    [handlers]
-    keys = console
-
-    [formatters]
-    keys = generic
-
-    [logger_root]
-    level = INFO
-    handlers = console
-
-    [logger_nixbot]
-    level = INFO
-    handlers =
-    qualname = nixbot
-
-    [handler_console]
-    class = StreamHandler
-    args = (sys.stderr,)
-    level = NOTSET
-    formatter = generic
-
-    [formatter_generic]
-    format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
-  '';
-in {
-  options = {
-    services.nixbot = {
-      enable = mkEnableOption "nixbot";
-
-      botName = mkOption {
-        type = types.str;
-        description = "The bot's github user account name.";
-        default = "nixbot";
-      };
-
-      githubToken = mkOption {
-        type = types.str;
-        description = "The bot's github user account token.";
-        example = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
-      };
-
-      repo = mkOption {
-        type = types.str;
-        description = "The github repository to check for PRs.";
-        example = "nixos/nixpkgs";
-      };
-
-      prRepo = mkOption {
-        type = types.str;
-        description = "The github repository to push the testing branches to.";
-        example = "nixos/nixpkgs-pr";
-      };
-
-      hydraJobsetsRepo = mkOption {
-        type = types.str;
-        description = "The github repository to push the hydra jobset definitions to.";
-        example = "nixos/hydra-jobsets";
-      };
-
-      publicUrl = mkOption {
-        type = types.str;
-        description = "The public URL the bot is reachable at (Github hook endpoint).";
-        example = "https://nixbot.nixos.org";
-      };
-
-      repoDir = mkOption {
-        type = types.path;
-        description = "The directory the repositories are stored in.";
-        default = "/var/lib/nixbot";
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    users.extraUsers.nixbot = {
-      createHome = true;
-      home = cfg.repoDir;
-    };
-
-    systemd.services.nixbot = let
-      env = pkgs.python3.buildEnv.override {
-        extraLibs = [ pkgs.nixbot ];
-      };
-    in {
-      after = [ "network.target" ];
-      wantedBy = [ "multi-user.target" ];
-      script = ''
-        ${env}/bin/pserve ${pkgs.writeText "production.ini" pyramidIni}
-      '';
-
-      serviceConfig = {
-        User = "nixbot";
-        Group = "nogroup";
-        PermissionsStartOnly = true;
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
index 1d53ce65900..4bbd041b6e0 100644
--- a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
@@ -118,7 +118,7 @@ with lib;
     default = [];
     example = [
       { urlPath = "/foo/bar.png";
-        files = "/home/eelco/some-file.png";
+        file = "/home/eelco/some-file.png";
       }
     ];
     description = ''
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index 7256013d5d8..10e8ef0ed38 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -182,8 +182,7 @@ in {
       { inherit (pkgs) networkmanager modemmanager wpa_supplicant;
         inherit (pkgs.gnome3) networkmanager-openvpn networkmanager-vpnc
                               networkmanager-openconnect networkmanager-fortisslvpn
-                              networkmanager-pptp networkmanager-iodine
-                              networkmanager-l2tp; };
+                              networkmanager-iodine networkmanager-l2tp; };
 
     # Needed for themes and backgrounds
     environment.pathsToLink = [ "/share" ];
diff --git a/nixos/modules/system/boot/kexec.nix b/nixos/modules/system/boot/kexec.nix
index 14ebe66e632..3fc1af28f62 100644
--- a/nixos/modules/system/boot/kexec.nix
+++ b/nixos/modules/system/boot/kexec.nix
@@ -1,7 +1,7 @@
 { config, pkgs, lib, ... }:
 
 {
-  config = lib.mkIf (pkgs.kexectools != null) {
+  config = lib.mkIf (pkgs.kexectools.meta.available) {
     environment.systemPackages = [ pkgs.kexectools ];
 
     systemd.services."prepare-kexec" =
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 92c9ee0c469..b8a2d42e0fb 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -524,6 +524,14 @@ in
       '';
     };
 
+    systemd.enableCgroupAccounting = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Whether to enable cgroup accounting.
+      '';
+    };
+
     systemd.extraConfig = mkOption {
       default = "";
       type = types.lines;
@@ -725,6 +733,13 @@ in
 
       "systemd/system.conf".text = ''
         [Manager]
+        ${optionalString config.systemd.enableCgroupAccounting ''
+          DefaultCPUAccounting=yes
+          DefaultIOAccounting=yes
+          DefaultBlockIOAccounting=yes
+          DefaultMemoryAccounting=yes
+          DefaultTasksAccounting=yes
+        ''}
         ${config.systemd.extraConfig}
       '';
 
diff --git a/nixos/release.nix b/nixos/release.nix
index 0b8d7318cd8..9b4aa4b0399 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -344,6 +344,7 @@ in rec {
   tests.predictable-interface-names = callSubTests tests/predictable-interface-names.nix {};
   tests.printing = callTest tests/printing.nix {};
   tests.prometheus = callTest tests/prometheus.nix {};
+  tests.prosody = callTest tests/prosody.nix {};
   tests.proxy = callTest tests/proxy.nix {};
   # tests.quagga = callTest tests/quagga.nix {};
   tests.quake3 = callTest tests/quake3.nix {};
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index c12919540a3..acf248d0a5a 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -69,13 +69,20 @@ let
     let
       iface = if grubVersion == 1 then "ide" else "virtio";
       isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
+
+      # FIXME don't duplicate the -enable-kvm etc. flags here yet again!
       qemuFlags =
         (if system == "x86_64-linux" then "-m 768 " else "-m 512 ") +
-        (optionalString (system == "x86_64-linux") "-cpu kvm64 ");
+        (optionalString (system == "x86_64-linux") "-cpu kvm64 ") +
+        (optionalString (system == "aarch64-linux") "-enable-kvm -machine virt,gic-version=host -cpu host ");
+
       hdFlags = ''hda => "vm-state-machine/machine.qcow2", hdaInterface => "${iface}", ''
-        + optionalString isEfi ''bios => "${pkgs.OVMF.fd}/FV/OVMF.fd", '';
-    in
-    ''
+        + optionalString isEfi (if pkgs.stdenv.isAarch64
+            then ''bios => "${pkgs.OVMF.fd}/FV/QEMU_EFI.fd", ''
+            else ''bios => "${pkgs.OVMF.fd}/FV/OVMF.fd", '');
+    in if !isEfi && !(pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) then
+      throw "Non-EFI boot methods are only supported on i686 / x86_64"
+    else ''
       $machine->start;
 
       # Make sure that we get a login prompt etc.
diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix
index 4fd9466dc50..179c95e7643 100644
--- a/nixos/tests/misc.nix
+++ b/nixos/tests/misc.nix
@@ -96,7 +96,7 @@ import ./make-test.nix ({ pkgs, ...} : rec {
       $machine->succeed("systemctl start systemd-udev-settle.service");
       subtest "udev-auto-load", sub {
           $machine->waitForUnit('systemd-udev-settle.service');
-          $machine->succeed('lsmod | grep psmouse');
+          $machine->succeed('lsmod | grep mousedev');
       };
 
       # Test whether systemd-tmpfiles-clean works.
diff --git a/nixos/tests/openldap.nix b/nixos/tests/openldap.nix
index 1178701c609..1bef867c57b 100644
--- a/nixos/tests/openldap.nix
+++ b/nixos/tests/openldap.nix
@@ -28,8 +28,8 @@ import ./make-test.nix {
   };
 
   testScript = ''
-    $machine->succeed('systemctl status openldap.service');
     $machine->waitForUnit('openldap.service');
+    $machine->succeed('systemctl status openldap.service');
     $machine->succeed('ldapsearch -LLL -D "cn=root,dc=example" -w notapassword -b "dc=example"');
   '';
 }
diff --git a/nixos/tests/prosody.nix b/nixos/tests/prosody.nix
new file mode 100644
index 00000000000..fcebfaf74e1
--- /dev/null
+++ b/nixos/tests/prosody.nix
@@ -0,0 +1,75 @@
+import ./make-test.nix {
+  name = "prosody";
+
+  machine = { config, pkgs, ... }: {
+    services.prosody = {
+      enable = true;
+      # TODO: use a self-signed certificate
+      c2sRequireEncryption = false;
+    };
+    environment.systemPackages = let
+      sendMessage = pkgs.writeScriptBin "send-message" ''
+        #!/usr/bin/env python3
+        # Based on the sleekxmpp send_client example, look there for more details:
+        # https://github.com/fritzy/SleekXMPP/blob/develop/examples/send_client.py
+        import sleekxmpp
+
+        class SendMsgBot(sleekxmpp.ClientXMPP):
+            """
+            A basic SleekXMPP bot that will log in, send a message,
+            and then log out.
+            """
+            def __init__(self, jid, password, recipient, message):
+                sleekxmpp.ClientXMPP.__init__(self, jid, password)
+
+                self.recipient = recipient
+                self.msg = message
+
+                self.add_event_handler("session_start", self.start, threaded=True)
+
+            def start(self, event):
+                self.send_presence()
+                self.get_roster()
+
+                self.send_message(mto=self.recipient,
+                                  mbody=self.msg,
+                                  mtype='chat')
+
+                self.disconnect(wait=True)
+
+
+        if __name__ == '__main__':
+            xmpp = SendMsgBot("test1@localhost", "test1", "test2@localhost", "Hello World!")
+            xmpp.register_plugin('xep_0030') # Service Discovery
+            xmpp.register_plugin('xep_0199') # XMPP Ping
+
+            # TODO: verify certificate
+            # If you want to verify the SSL certificates offered by a server:
+            # xmpp.ca_certs = "path/to/ca/cert"
+
+            if xmpp.connect(('localhost', 5222)):
+                xmpp.process(block=True)
+            else:
+                print("Unable to connect.")
+                sys.exit(1)
+      '';
+    in [ (pkgs.python3.withPackages (ps: [ ps.sleekxmpp ])) sendMessage ];
+  };
+
+  testScript = ''
+    $machine->waitForUnit('prosody.service');
+    $machine->succeed('prosodyctl status') =~ /Prosody is running/;
+
+    # set password to 'test' (it's asked twice)
+    $machine->succeed('yes test1 | prosodyctl adduser test1@localhost');
+    # set password to 'y'
+    $machine->succeed('yes | prosodyctl adduser test2@localhost');
+    # correct password to 'test2'
+    $machine->succeed('yes test2 | prosodyctl passwd test2@localhost');
+
+    $machine->succeed("send-message");
+
+    $machine->succeed('prosodyctl deluser test1@localhost');
+    $machine->succeed('prosodyctl deluser test2@localhost');
+  '';
+}