summary refs log tree commit diff
path: root/nixos/modules/services
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services')
-rw-r--r--nixos/modules/services/continuous-integration/buildkite-agent.nix64
-rw-r--r--nixos/modules/services/continuous-integration/hydra/default.nix13
-rw-r--r--nixos/modules/services/databases/mysql.nix9
-rw-r--r--nixos/modules/services/databases/postgresql.nix2
-rw-r--r--nixos/modules/services/desktops/dleyna-renderer.nix28
-rw-r--r--nixos/modules/services/desktops/dleyna-server.nix28
-rw-r--r--nixos/modules/services/desktops/gnome3/tracker-miners.nix41
-rw-r--r--nixos/modules/services/games/factorio.nix20
-rw-r--r--nixos/modules/services/hardware/fwupd.nix90
-rw-r--r--nixos/modules/services/mail/dovecot.nix4
-rw-r--r--nixos/modules/services/misc/matrix-synapse.nix25
-rw-r--r--nixos/modules/services/monitoring/fusion-inventory.nix3
-rw-r--r--nixos/modules/services/monitoring/grafana.nix2
-rw-r--r--nixos/modules/services/monitoring/munin.nix68
-rw-r--r--nixos/modules/services/monitoring/prometheus/default.nix33
-rw-r--r--nixos/modules/services/monitoring/smartd.nix6
-rw-r--r--nixos/modules/services/network-filesystems/ipfs.nix14
-rw-r--r--nixos/modules/services/networking/babeld.nix8
-rw-r--r--nixos/modules/services/networking/firewall.nix3
-rw-r--r--nixos/modules/services/networking/nghttpx/backend-params-submodule.nix131
-rw-r--r--nixos/modules/services/networking/nghttpx/backend-submodule.nix50
-rw-r--r--nixos/modules/services/networking/nghttpx/default.nix117
-rw-r--r--nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix64
-rw-r--r--nixos/modules/services/networking/nghttpx/frontend-submodule.nix36
-rw-r--r--nixos/modules/services/networking/nghttpx/nghttpx-options.nix142
-rw-r--r--nixos/modules/services/networking/nghttpx/server-options.nix18
-rw-r--r--nixos/modules/services/networking/nghttpx/tls-submodule.nix21
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix26
-rw-r--r--nixos/modules/services/security/clamav.nix9
-rw-r--r--nixos/modules/services/web-apps/restya-board.nix384
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/owncloud.nix48
-rw-r--r--nixos/modules/services/web-servers/lighttpd/inginious.nix2
-rw-r--r--nixos/modules/services/x11/compton.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix6
-rw-r--r--nixos/modules/services/x11/desktop-managers/enlightenment.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix5
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix7
-rw-r--r--nixos/modules/services/x11/display-managers/default.nix45
-rw-r--r--nixos/modules/services/x11/display-managers/gdm.nix3
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/sddm.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/xpra.nix2
-rw-r--r--nixos/modules/services/x11/fractalart.nix36
-rw-r--r--nixos/modules/services/x11/hardware/libinput.nix7
-rw-r--r--nixos/modules/services/x11/window-managers/default.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/evilwm.nix25
-rw-r--r--nixos/modules/services/x11/xserver.nix15
47 files changed, 1510 insertions, 164 deletions
diff --git a/nixos/modules/services/continuous-integration/buildkite-agent.nix b/nixos/modules/services/continuous-integration/buildkite-agent.nix
index 9c06e1d43bbe..dcc5e7174601 100644
--- a/nixos/modules/services/continuous-integration/buildkite-agent.nix
+++ b/nixos/modules/services/continuous-integration/buildkite-agent.nix
@@ -4,14 +4,6 @@ with lib;
 
 let
   cfg = config.services.buildkite-agent;
-  configFile = pkgs.writeText "buildkite-agent.cfg"
-    ''
-      token="${cfg.token}"
-      name="${cfg.name}"
-      meta-data="${cfg.meta-data}"
-      hooks-path="${cfg.package}/share/hooks"
-      build-path="${cfg.dataDir}"
-    '';
 in
 
 {
@@ -39,10 +31,13 @@ in
         type = types.listOf types.package;
       };
 
-      token = mkOption {
-        type = types.str;
+      tokenPath = mkOption {
+        type = types.path;
         description = ''
           The token from your Buildkite "Agents" page.
+
+          A run-time path to the token file, which is supposed to be provisioned
+          outside of Nix store.
         '';
       };
 
@@ -62,16 +57,22 @@ in
       };
 
       openssh =
-        { privateKey = mkOption {
-            type = types.str;
+        { privateKeyPath = mkOption {
+            type = types.path;
             description = ''
               Private agent key.
+
+              A run-time path to the key file, which is supposed to be provisioned
+              outside of Nix store.
             '';
           };
-          publicKey = mkOption {
-            type = types.str;
+          publicKeyPath = mkOption {
+            type = types.path;
             description = ''
               Public agent key.
+
+              A run-time path to the key file, which is supposed to be provisioned
+              outside of Nix store.
             '';
           };
         };
@@ -84,11 +85,15 @@ in
         home = cfg.dataDir;
         createHome = true;
         description = "Buildkite agent user";
+        extraGroups = [ "keys" ];
       };
 
     environment.systemPackages = [ cfg.package ];
 
     systemd.services.buildkite-agent =
+      let copy = x: target: perms:
+                 "cp -f ${x} ${target}; ${pkgs.coreutils}/bin/chmod ${toString perms} ${target}; ";
+      in
       { description = "Buildkite Agent";
         wantedBy = [ "multi-user.target" ];
         after = [ "network.target" ];
@@ -97,18 +102,26 @@ in
           HOME = cfg.dataDir;
           NIX_REMOTE = "daemon";
         };
-        preStart = ''
-          ${pkgs.coreutils}/bin/mkdir -m 0700 -p ${cfg.dataDir}/.ssh
-
-          echo "${cfg.openssh.privateKey}" > ${cfg.dataDir}/.ssh/id_rsa
-          ${pkgs.coreutils}/bin/chmod 600 ${cfg.dataDir}/.ssh/id_rsa
 
-          echo "${cfg.openssh.publicKey}" > ${cfg.dataDir}/.ssh/id_rsa.pub
-          ${pkgs.coreutils}/bin/chmod 600 ${cfg.dataDir}/.ssh/id_rsa.pub
-        '';
+        ## NB: maximum care is taken so that secrets (ssh keys and the CI token)
+        ##     don't end up in the Nix store.
+        preStart = ''
+            ${pkgs.coreutils}/bin/mkdir -m 0700 -p ${cfg.dataDir}/.ssh
+            ${copy (toString cfg.openssh.privateKeyPath) "${cfg.dataDir}/.ssh/id_rsa"     600}
+            ${copy (toString cfg.openssh.publicKeyPath)  "${cfg.dataDir}/.ssh/id_rsa.pub" 600}
+
+            cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF
+            token="$(cat ${toString cfg.tokenPath})"
+            name="${cfg.name}"
+            meta-data="${cfg.meta-data}"
+            hooks-path="${pkgs.buildkite-agent}/share/hooks"
+            build-path="${cfg.dataDir}/builds"
+            bootstrap-script="${pkgs.buildkite-agent}/share/bootstrap.sh"
+            EOF
+          '';
 
         serviceConfig =
-          { ExecStart = "${pkgs.buildkite-agent}/bin/buildkite-agent start --config ${configFile}";
+          { ExecStart = "${pkgs.buildkite-agent}/bin/buildkite-agent start --config /var/lib/buildkite-agent/buildkite-agent.cfg";
             User = "buildkite-agent";
             RestartSec = 5;
             Restart = "on-failure";
@@ -116,4 +129,9 @@ in
           };
       };
   };
+  imports = [
+    (mkRenamedOptionModule [ "services" "buildkite-agent" "token" ]                [ "services" "buildkite-agent" "tokenPath" ])
+    (mkRenamedOptionModule [ "services" "buildkite-agent" "openssh" "privateKey" ] [ "services" "buildkite-agent" "openssh" "privateKeyPath" ])
+    (mkRenamedOptionModule [ "services" "buildkite-agent" "openssh" "publicKey" ]  [ "services" "buildkite-agent" "openssh" "publicKeyPath" ])
+  ];
 }
diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix
index 43fec5ff5bb2..2fa7c59a965d 100644
--- a/nixos/modules/services/continuous-integration/hydra/default.nix
+++ b/nixos/modules/services/continuous-integration/hydra/default.nix
@@ -28,6 +28,7 @@ let
 
   serverEnv = env //
     { HYDRA_TRACKER = cfg.tracker;
+      XDG_CACHE_HOME = "${baseDir}/www/.cache";
       COLUMNS = "80";
       PGPASSFILE = "${baseDir}/pgpass-www"; # grrr
     } // (optionalAttrs cfg.debugServer { DBIC_TRACE = "1"; });
@@ -225,14 +226,14 @@ in
 
     services.hydra.extraConfig =
       ''
-        using_frontend_proxy 1
-        base_uri ${cfg.hydraURL}
-        notification_sender ${cfg.notificationSender}
-        max_servers 25
+        using_frontend_proxy = 1
+        base_uri = ${cfg.hydraURL}
+        notification_sender = ${cfg.notificationSender}
+        max_servers = 25
         ${optionalString (cfg.logo != null) ''
-          hydra_logo ${cfg.logo}
+          hydra_logo = ${cfg.logo}
         ''}
-        gc_roots_dir ${cfg.gcRootsDir}
+        gc_roots_dir = ${cfg.gcRootsDir}
         use-substitutes = ${if cfg.useSubstitutes then "1" else "0"}
       '';
 
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
index c6c463bce7d5..a3bf4f9ba925 100644
--- a/nixos/modules/services/databases/mysql.nix
+++ b/nixos/modules/services/databases/mysql.nix
@@ -7,6 +7,11 @@ let
   cfg = config.services.mysql;
 
   mysql = cfg.package;
+  
+  isMariaDB = 
+    let
+      pName = _p: (builtins.parseDrvName (_p.name)).name;
+    in pName mysql == pName pkgs.mariadb;
 
   atLeast55 = versionAtLeast mysql.mysqlVersion "5.5";
 
@@ -59,7 +64,7 @@ in
         type = types.package;
         example = literalExample "pkgs.mysql";
         description = "
-          Which MySQL derivation to use.
+          Which MySQL derivation to use. MariaDB packages are supported too.
         ";
       };
 
@@ -360,7 +365,7 @@ in
 
             ${concatMapStrings (user:
               ''
-                ( echo "CREATE USER IF NOT EXISTS '${user.name}'@'localhost' IDENTIFIED WITH ${if mysql == pkgs.mariadb then "unix_socket" else "auth_socket"};"
+                ( echo "CREATE USER IF NOT EXISTS '${user.name}'@'localhost' IDENTIFIED WITH ${if isMariaDB then "unix_socket" else "auth_socket"};"
                   ${concatStringsSep "\n" (mapAttrsToList (database: permission: ''
                   echo "GRANT ${permission} ON ${database} TO '${user.name}'@'localhost';"
                   '') user.ensurePermissions)}
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index a23a0ebc7366..9b5e3735239f 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -153,7 +153,7 @@ in
         default= if versionAtLeast config.system.stateVersion "17.09" then "postgres" else "root";
         internal = true;
         description = ''
-          NixOS traditionally used `root` as superuser, most other distros use `postgres`.
+          NixOS traditionally used 'root' as superuser, most other distros use 'postgres'.
           From 17.09 we also try to follow this standard. Internal since changing this value
           would lead to breakage while setting up databases.
         '';
diff --git a/nixos/modules/services/desktops/dleyna-renderer.nix b/nixos/modules/services/desktops/dleyna-renderer.nix
new file mode 100644
index 000000000000..7f88605f627c
--- /dev/null
+++ b/nixos/modules/services/desktops/dleyna-renderer.nix
@@ -0,0 +1,28 @@
+# dleyna-renderer service.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+  options = {
+    services.dleyna-renderer = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable dleyna-renderer service, a DBus service
+          for handling DLNA renderers.
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+  config = mkIf config.services.dleyna-renderer.enable {
+    environment.systemPackages = [ pkgs.dleyna-renderer ];
+
+    services.dbus.packages = [ pkgs.dleyna-renderer ];
+  };
+}
diff --git a/nixos/modules/services/desktops/dleyna-server.nix b/nixos/modules/services/desktops/dleyna-server.nix
new file mode 100644
index 000000000000..9a131a5e700f
--- /dev/null
+++ b/nixos/modules/services/desktops/dleyna-server.nix
@@ -0,0 +1,28 @@
+# dleyna-server service.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+  options = {
+    services.dleyna-server = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable dleyna-server service, a DBus service
+          for handling DLNA servers.
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+  config = mkIf config.services.dleyna-server.enable {
+    environment.systemPackages = [ pkgs.dleyna-server ];
+
+    services.dbus.packages = [ pkgs.dleyna-server ];
+  };
+}
diff --git a/nixos/modules/services/desktops/gnome3/tracker-miners.nix b/nixos/modules/services/desktops/gnome3/tracker-miners.nix
new file mode 100644
index 000000000000..20154fc2fed3
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/tracker-miners.nix
@@ -0,0 +1,41 @@
+# Tracker Miners daemons.
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnome3.tracker-miners = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable Tracker miners, indexing services for Tracker
+          search engine and metadata storage system.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnome3.tracker-miners.enable {
+
+    environment.systemPackages = [ pkgs.gnome3.tracker-miners ];
+
+    services.dbus.packages = [ pkgs.gnome3.tracker-miners ];
+
+    systemd.packages = [ pkgs.gnome3.tracker-miners ];
+
+  };
+
+}
diff --git a/nixos/modules/services/games/factorio.nix b/nixos/modules/services/games/factorio.nix
index 1dc8ce93a0e5..3f6bf9de8931 100644
--- a/nixos/modules/services/games/factorio.nix
+++ b/nixos/modules/services/games/factorio.nix
@@ -6,7 +6,7 @@ let
   cfg = config.services.factorio;
   factorio = pkgs.factorio-headless;
   name = "Factorio";
-  stateDir = "/var/lib/factorio";
+  stateDir = cfg.stateDir;
   mkSavePath = name: "${stateDir}/saves/${name}.zip";
   configFile = pkgs.writeText "factorio.conf" ''
     use-system-read-write-data-directories=true
@@ -25,7 +25,7 @@ let
     password = cfg.password;
     token = cfg.token;
     game_password = cfg.game-password;
-    require_user_verification = true;
+    require_user_verification = cfg.requireUserVerification;
     max_upload_in_kilobytes_per_second = 0;
     minimum_latency_in_ticks = 0;
     ignore_player_limit_for_returning_players = false;
@@ -80,6 +80,15 @@ in
           customizations.
         '';
       };
+      stateDir = mkOption {
+        type = types.path;
+        default = "/var/lib/factorio";
+        description = ''
+          The server's data directory.
+
+          The configuration and map will be stored here.
+        '';
+      };
       mods = mkOption {
         type = types.listOf types.package;
         default = [];
@@ -148,6 +157,13 @@ in
           Game password.
         '';
       };
+      requireUserVerification = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          When set to true, the server will only allow clients that have a valid factorio.com account.
+        '';
+      };
       autosave-interval = mkOption {
         type = types.nullOr types.int;
         default = null;
diff --git a/nixos/modules/services/hardware/fwupd.nix b/nixos/modules/services/hardware/fwupd.nix
new file mode 100644
index 000000000000..14113fe01bb4
--- /dev/null
+++ b/nixos/modules/services/hardware/fwupd.nix
@@ -0,0 +1,90 @@
+# fwupd daemon.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.fwupd;
+  originalEtc =
+    let
+      isRegular = v: v == "regular";
+      listFiles = d: builtins.attrNames (filterAttrs (const isRegular) (builtins.readDir d));
+      copiedDirs = [ "fwupd/remotes.d" "pki/fwupd" "pki/fwupd-metadata" ];
+      originalFiles = concatMap (d: map (f: "${d}/${f}") (listFiles "${pkgs.fwupd}/etc/${d}")) copiedDirs;
+      mkEtcFile = n: nameValuePair n { source = "${pkgs.fwupd}/etc/${n}"; };
+    in listToAttrs (map mkEtcFile originalFiles);
+  extraTrustedKeys =
+    let
+      mkName = p: "pki/fwupd/${baseNameOf (toString p)}";
+      mkEtcFile = p: nameValuePair (mkName p) { source = p; };
+    in listToAttrs (map mkEtcFile cfg.extraTrustedKeys);
+in {
+
+  ###### interface
+  options = {
+    services.fwupd = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable fwupd, a DBus service that allows
+          applications to update firmware.
+        '';
+      };
+
+      blacklistDevices = mkOption {
+        type = types.listOf types.string;
+        default = [];
+        example = [ "2082b5e0-7a64-478a-b1b2-e3404fab6dad" ];
+        description = ''
+          Allow blacklisting specific devices by their GUID
+        '';
+      };
+
+      blacklistPlugins = mkOption {
+        type = types.listOf types.string;
+        default = [];
+        example = [ "udev" ];
+        description = ''
+          Allow blacklisting specific plugins
+        '';
+      };
+
+      extraTrustedKeys = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        example = literalExample "[ /etc/nixos/fwupd/myfirmware.pem ]";
+        description = ''
+          Installing a public key allows firmware signed with a matching private key to be recognized as trusted, which may require less authentication to install than for untrusted files. By default trusted firmware can be upgraded (but not downgraded) without the user or administrator password. Only very few keys are installed by default.
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.fwupd ];
+
+    environment.etc = {
+      "fwupd/daemon.conf" = {
+        source = pkgs.writeText "daemon.conf" ''
+          [fwupd]
+          BlacklistDevices=${lib.concatStringsSep ";" cfg.blacklistDevices}
+          BlacklistPlugins=${lib.concatStringsSep ";" cfg.blacklistPlugins}
+        '';
+      };
+    } // originalEtc // extraTrustedKeys;
+
+    services.dbus.packages = [ pkgs.fwupd ];
+
+    services.udev.packages = [ pkgs.fwupd ];
+
+    systemd.packages = [ pkgs.fwupd ];
+
+    systemd.tmpfiles.rules = [
+      "d /var/lib/fwupd 0755 root root -"
+    ];
+  };
+}
diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix
index 6057acc531a3..18101a312254 100644
--- a/nixos/modules/services/mail/dovecot.nix
+++ b/nixos/modules/services/mail/dovecot.nix
@@ -62,14 +62,14 @@ let
       }
     '')
 
-    ''
+    (optionalString (cfg.mailboxes != []) ''
       protocol imap {
         namespace inbox {
           inbox=yes
           ${concatStringsSep "\n" (map mailboxConfig cfg.mailboxes)}
         }
       }
-    ''
+    '')
 
     (optionalString cfg.enableQuota ''
       mail_plugins = $mail_plugins quota
diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix
index a3ec0ea59f8f..11463cf4500a 100644
--- a/nixos/modules/services/misc/matrix-synapse.nix
+++ b/nixos/modules/services/misc/matrix-synapse.nix
@@ -44,7 +44,6 @@ database: {
 }
 event_cache_size: "${cfg.event_cache_size}"
 verbose: ${cfg.verbose}
-log_file: "/var/log/matrix-synapse/homeserver.log"
 log_config: "${logConfigFile}"
 rc_messages_per_second: ${cfg.rc_messages_per_second}
 rc_message_burst_count: ${cfg.rc_message_burst_count}
@@ -53,8 +52,8 @@ federation_rc_sleep_limit: ${cfg.federation_rc_sleep_limit}
 federation_rc_sleep_delay: ${cfg.federation_rc_sleep_delay}
 federation_rc_reject_limit: ${cfg.federation_rc_reject_limit}
 federation_rc_concurrent: ${cfg.federation_rc_concurrent}
-media_store_path: "/var/lib/matrix-synapse/media"
-uploads_path: "/var/lib/matrix-synapse/uploads"
+media_store_path: "${cfg.dataDir}/media"
+uploads_path: "${cfg.dataDir}/uploads"
 max_upload_size: "${cfg.max_upload_size}"
 max_image_pixels: "${cfg.max_image_pixels}"
 dynamic_thumbnails: ${boolToString cfg.dynamic_thumbnails}
@@ -86,7 +85,7 @@ ${optionalString (cfg.macaroon_secret_key != null) ''
 expire_access_token: ${boolToString cfg.expire_access_token}
 enable_metrics: ${boolToString cfg.enable_metrics}
 report_stats: ${boolToString cfg.report_stats}
-signing_key_path: "/var/lib/matrix-synapse/homeserver.signing.key"
+signing_key_path: "${cfg.dataDir}/homeserver.signing.key"
 key_refresh_interval: "${cfg.key_refresh_interval}"
 perspectives:
   servers: {
@@ -348,7 +347,7 @@ in {
       database_args = mkOption {
         type = types.attrs;
         default = {
-          database = "/var/lib/matrix-synapse/homeserver.db";
+          database = "${cfg.dataDir}/homeserver.db";
         };
         description = ''
           Arguments to pass to the engine.
@@ -586,6 +585,14 @@ in {
           A yaml python logging config file
         '';
       };
+      dataDir = mkOption {
+        type = types.str;
+        default = "/var/lib/matrix-synapse";
+        description = ''
+          The directory where matrix-synapse stores its stateful data such as
+          certificates, media and uploads.
+        '';
+      };
     };
   };
 
@@ -593,7 +600,7 @@ in {
     users.extraUsers = [
       { name = "matrix-synapse";
         group = "matrix-synapse";
-        home = "/var/lib/matrix-synapse";
+        home = cfg.dataDir;
         createHome = true;
         shell = "${pkgs.bash}/bin/bash";
         uid = config.ids.uids.matrix-synapse;
@@ -611,16 +618,16 @@ in {
       preStart = ''
         ${cfg.package}/bin/homeserver \
           --config-path ${configFile} \
-          --keys-directory /var/lib/matrix-synapse \
+          --keys-directory ${cfg.dataDir} \
           --generate-keys
       '';
       serviceConfig = {
         Type = "simple";
         User = "matrix-synapse";
         Group = "matrix-synapse";
-        WorkingDirectory = "/var/lib/matrix-synapse";
+        WorkingDirectory = cfg.dataDir;
         PermissionsStartOnly = true;
-        ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory /var/lib/matrix-synapse";
+        ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory ${cfg.dataDir}";
         Restart = "on-failure";
       };
     };
diff --git a/nixos/modules/services/monitoring/fusion-inventory.nix b/nixos/modules/services/monitoring/fusion-inventory.nix
index 1c00f3c299e9..c3b869e00880 100644
--- a/nixos/modules/services/monitoring/fusion-inventory.nix
+++ b/nixos/modules/services/monitoring/fusion-inventory.nix
@@ -55,9 +55,6 @@ in {
       description = "Fusion Inventory Agent";
       wantedBy = [ "multi-user.target" ];
 
-      environment = {
-        OPTIONS = "--no-category=software";
-      };
       serviceConfig = {
         ExecStart = "${pkgs.fusionInventory}/bin/fusioninventory-agent --conf-file=${configFile} --daemon --no-fork";
       };
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index 4fbacef788f9..d48b78ae6d02 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -111,7 +111,7 @@ in {
       type = mkOption {
         description = "Database type.";
         default = "sqlite3";
-        type = types.enum ["mysql" "sqlite3" "postgresql"];
+        type = types.enum ["mysql" "sqlite3" "postgres"];
       };
 
       host = mkOption {
diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix
index cc6d51f0ef1b..358ffd431dd4 100644
--- a/nixos/modules/services/monitoring/munin.nix
+++ b/nixos/modules/services/monitoring/munin.nix
@@ -17,40 +17,6 @@ let
   nodeCfg = config.services.munin-node;
   cronCfg = config.services.munin-cron;
 
-  muninPlugins = pkgs.stdenv.mkDerivation {
-    name = "munin-available-plugins";
-    buildCommand = ''
-      mkdir -p $out
-
-      cp --preserve=mode ${pkgs.munin}/lib/plugins/* $out/
-
-      for file in $out/*; do
-        case "$file" in
-            */plugin.sh|*/plugins.history)
-              chmod +x "$file"
-              continue;;
-        esac
-
-        # read magic makers from the file
-        family=$(sed -nr 's/.*#%#\s+family\s*=\s*(\S+)\s*/\1/p' $file)
-        cap=$(sed -nr 's/.*#%#\s+capabilities\s*=\s*(.+)/\1/p' $file)
-
-        wrapProgram $file \
-          --set PATH "/run/wrappers/bin:/run/current-system/sw/bin" \
-          --set MUNIN_LIBDIR "${pkgs.munin}/lib" \
-          --set MUNIN_PLUGSTATE "/var/run/munin"
-
-        # munin uses markers to tell munin-node-configure what a plugin can do
-        echo "#%# family=$family" >> $file
-        echo "#%# capabilities=$cap" >> $file
-      done
-
-      # NOTE: we disable disktstats because plugin seems to fail and it hangs html generation (100% CPU + memory leak)
-      rm -f $out/diskstats
-    '';
-    buildInputs = [ pkgs.makeWrapper ];
-  };
-
   muninConf = pkgs.writeText "munin.conf"
     ''
       dbdir     /var/lib/munin
@@ -83,6 +49,29 @@ let
 
       ${nodeCfg.extraConfig}
     '';
+
+  pluginConf = pkgs.writeText "munin-plugin-conf"
+    ''
+      [hddtemp_smartctl]
+      user root
+      group root
+
+      [meminfo]
+      user root
+      group root
+
+      [ipmi*]
+      user root
+      group root
+    '';
+
+  pluginConfDir = pkgs.stdenv.mkDerivation {
+    name = "munin-plugin-conf.d";
+    buildCommand = ''
+      mkdir $out
+      ln -s ${pluginConf} $out/nixos-config
+    '';
+  };
 in
 
 {
@@ -179,17 +168,22 @@ in
       description = "Munin Node";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.munin ];
+      path = with pkgs; [ munin smartmontools "/run/current-system/sw" "/run/wrappers" ];
+      environment.MUNIN_LIBDIR = "${pkgs.munin}/lib";
       environment.MUNIN_PLUGSTATE = "/var/run/munin";
+      environment.MUNIN_LOGDIR = "/var/log/munin";
       preStart = ''
         echo "updating munin plugins..."
 
         mkdir -p /etc/munin/plugins
         rm -rf /etc/munin/plugins/*
-        PATH="/run/wrappers/bin:/run/current-system/sw/bin" ${pkgs.munin}/sbin/munin-node-configure --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${muninPlugins} --servicedir=/etc/munin/plugins 2>/dev/null | ${pkgs.bash}/bin/bash
+        ${pkgs.munin}/bin/munin-node-configure --suggest --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${pkgs.munin}/lib/plugins --servicedir=/etc/munin/plugins --sconfdir=${pluginConfDir} 2>/dev/null | ${pkgs.bash}/bin/bash
+
+        # NOTE: we disable disktstats because plugin seems to fail and it hangs html generation (100% CPU + memory leak)
+        rm /etc/munin/plugins/diskstats || true
       '';
       serviceConfig = {
-        ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/";
+        ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/ --sconfdir=${pluginConfDir}";
       };
     };
 
diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix
index c12b5f35dea3..80122e69d167 100644
--- a/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixos/modules/services/monitoring/prometheus/default.nix
@@ -66,6 +66,16 @@ let
           How frequently to evaluate rules by default.
         '';
       };
+
+      external_labels = mkOption {
+        type = types.attrsOf types.str;
+        description = ''
+          The labels to add to any time series or alerts when
+          communicating with external systems (federation, remote
+          storage, Alertmanager).
+        '';
+        default = {};
+      };
     };
   };
 
@@ -100,6 +110,29 @@ let
           The HTTP resource path on which to fetch metrics from targets.
         '';
       };
+      honor_labels = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Controls how Prometheus handles conflicts between labels
+          that are already present in scraped data and labels that
+          Prometheus would attach server-side ("job" and "instance"
+          labels, manually configured target labels, and labels
+          generated by service discovery implementations).
+
+          If honor_labels is set to "true", label conflicts are
+          resolved by keeping label values from the scraped data and
+          ignoring the conflicting server-side labels.
+
+          If honor_labels is set to "false", label conflicts are
+          resolved by renaming conflicting labels in the scraped data
+          to "exported_&lt;original-label&gt;" (for example
+          "exported_instance", "exported_job") and then attaching
+          server-side labels. This is useful for use cases such as
+          federation, where all labels specified in the target should
+          be preserved.
+        '';
+      };
       scheme = mkOption {
         type = types.enum ["http" "https"];
         default = "http";
diff --git a/nixos/modules/services/monitoring/smartd.nix b/nixos/modules/services/monitoring/smartd.nix
index 4d10299a9879..1d6940c516a9 100644
--- a/nixos/modules/services/monitoring/smartd.nix
+++ b/nixos/modules/services/monitoring/smartd.nix
@@ -17,7 +17,7 @@ let
     #! ${pkgs.stdenv.shell}
     ${optionalString nm.enable ''
       {
-      cat << EOF
+      ${pkgs.coreutils}/bin/cat << EOF
       From: smartd on ${host} <root>
       To: undisclosed-recipients:;
       Subject: SMART error on $SMARTD_DEVICESTRING: $SMARTD_FAILTYPE
@@ -30,7 +30,7 @@ let
     ''}
     ${optionalString nw.enable ''
       {
-      cat << EOF
+      ${pkgs.coreutils}/bin/cat << EOF
       Problem detected with disk: $SMARTD_DEVICESTRING
       Warning message from smartd is:
 
@@ -41,7 +41,7 @@ let
     ${optionalString nx.enable ''
       export DISPLAY=${nx.display}
       {
-      cat << EOF
+      ${pkgs.coreutils}/bin/cat << EOF
       Problem detected with disk: $SMARTD_DEVICESTRING
       Warning message from smartd is:
 
diff --git a/nixos/modules/services/network-filesystems/ipfs.nix b/nixos/modules/services/network-filesystems/ipfs.nix
index d4a695ef5880..39a4fd6beff8 100644
--- a/nixos/modules/services/network-filesystems/ipfs.nix
+++ b/nixos/modules/services/network-filesystems/ipfs.nix
@@ -39,8 +39,6 @@ let
     # NB: migration must be performed prior to pre-start, else we get the failure message!
     preStart = ''
       ipfs repo fsck # workaround for BUG #4212 (https://github.com/ipfs/go-ipfs/issues/4214)
-      ipfs --local config Addresses.API ${cfg.apiAddress}
-      ipfs --local config Addresses.Gateway ${cfg.gatewayAddress}
     '' + optionalString cfg.autoMount ''
       ipfs --local config Mounts.FuseAllowOther --json true
       ipfs --local config Mounts.IPFS ${cfg.ipfsMountDir}
@@ -56,7 +54,11 @@ let
               EOF
               ipfs --local config --json "${concatStringsSep "." path}" "$value"
             '')
-            cfg.extraConfig)
+            ({ Addresses.API = cfg.apiAddress;
+               Addresses.Gateway = cfg.gatewayAddress;
+               Addresses.Swarm = cfg.swarmAddress;
+            } //
+            cfg.extraConfig))
         );
     serviceConfig = {
       ExecStart = "${wrapped}/bin/ipfs daemon ${ipfsFlags}";
@@ -140,6 +142,12 @@ in {
         description = "Where IPFS exposes its API to";
       };
 
+      swarmAddress = mkOption {
+        type = types.listOf types.str;
+        default = [ "/ip4/0.0.0.0/tcp/4001" "/ip6/::/tcp/4001" ];
+        description = "Where IPFS listens for incoming p2p connections";
+      };
+
       enableGC = mkOption {
         type = types.bool;
         default = false;
diff --git a/nixos/modules/services/networking/babeld.nix b/nixos/modules/services/networking/babeld.nix
index dd76bac9df76..3dfd80f6ff52 100644
--- a/nixos/modules/services/networking/babeld.nix
+++ b/nixos/modules/services/networking/babeld.nix
@@ -6,8 +6,10 @@ let
 
   cfg = config.services.babeld;
 
+  conditionalBoolToString = value: if (isBool value) then (boolToString value) else (toString value);
+
   paramsString = params:
-    concatMapStringsSep "" (name: "${name} ${boolToString (getAttr name params)}")
+    concatMapStringsSep " " (name: "${name} ${conditionalBoolToString (getAttr name params)}")
                    (attrNames params);
 
   interfaceConfig = name:
@@ -49,7 +51,7 @@ in
         type = types.nullOr (types.attrsOf types.unspecified);
         example =
           {
-            wired = true;
+            type = "tunnel";
             "split-horizon" = true;
           };
       };
@@ -63,7 +65,7 @@ in
         type = types.attrsOf (types.attrsOf types.unspecified);
         example =
           { enp0s2 =
-            { wired = true;
+            { type = "wired";
               "hello-interval" = 5;
               "split-horizon" = "auto";
             };
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index 9bd88ca1707b..bce48c8f65e5 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -125,6 +125,9 @@ let
       ip46tables -t raw -N nixos-fw-rpfilter 2> /dev/null || true
       ip46tables -t raw -A nixos-fw-rpfilter -m rpfilter ${optionalString (cfg.checkReversePath == "loose") "--loose"} -j RETURN
 
+      # Allows this host to act as a DHCP4 client without first having to use APIPA
+      iptables -t raw -A nixos-fw-rpfilter -p udp --sport 67 --dport 68 -j RETURN
+
       # Allows this host to act as a DHCPv4 server
       iptables -t raw -A nixos-fw-rpfilter -s 0.0.0.0 -d 255.255.255.255 -p udp --sport 68 --dport 67 -j RETURN
 
diff --git a/nixos/modules/services/networking/nghttpx/backend-params-submodule.nix b/nixos/modules/services/networking/nghttpx/backend-params-submodule.nix
new file mode 100644
index 000000000000..6523f4b8b9e0
--- /dev/null
+++ b/nixos/modules/services/networking/nghttpx/backend-params-submodule.nix
@@ -0,0 +1,131 @@
+{ lib, ...}:
+{ options = {
+    proto = lib.mkOption {
+      type        = lib.types.enum [ "h2" "http/1.1" ];
+      default     = "http/1.1";
+      description = ''
+        This option configures the protocol the backend server expects
+        to use.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    tls = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        This option determines whether nghttpx will negotiate its
+        connection with a backend server using TLS or not. The burden
+        is on the backend server to provide the TLS certificate!
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    sni = lib.mkOption {
+      type        = lib.types.nullOr lib.types.str;
+      default     = null;
+      description = ''
+        Override the TLS SNI field value. This value (in nghttpx)
+        defaults to the host value of the backend configuration.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    fall = lib.mkOption {
+      type        = lib.types.int;
+      default     = 0;
+      description = ''
+        If nghttpx cannot connect to the backend N times in a row, the
+        backend is assumed to be offline and is excluded from load
+        balancing. If N is 0 the backend is never excluded from load
+        balancing.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    rise = lib.mkOption {
+      type        = lib.types.int;
+      default     = 0;
+      description = ''
+        If the backend is excluded from load balancing, nghttpx will
+        periodically attempt to make a connection to the backend. If
+        the connection is successful N times in a row the backend is
+        re-included in load balancing. If N is 0 a backend is never
+        reconsidered for load balancing once it falls.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    affinity = lib.mkOption {
+      type        = lib.types.enum [ "ip" "none" ];
+      default     = "none";
+      description = ''
+        If "ip" is given, client IP based session affinity is
+        enabled. If "none" is given, session affinity is disabled.
+
+        Session affinity is enabled (by nghttpx) per-backend
+        pattern. If at least one backend has a non-"none" affinity,
+        then session affinity is enabled for all backend servers
+        sharing the same pattern.
+
+        It is advised to set affinity on all backends explicitly if
+        session affinity is desired. The session affinity may break if
+        one of the backend gets unreachable, or backend settings are
+        reloaded or replaced by API.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    dns = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        Name resolution of a backends host name is done at start up,
+        or configuration reload. If "dns" is true, name resolution
+        takes place dynamically.
+
+        This is useful if a backends address changes frequently. If
+        "dns" is true, name resolution of a backend's host name at
+        start up, or configuration reload is skipped.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    redirect-if-not-tls = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        If true, a backend match requires the frontend connection be
+        TLS encrypted. If it is not, nghttpx responds to the request
+        with a 308 status code and https URI the client should use
+        instead in the Location header.
+
+        The port number in the redirect URI is 443 by default and can
+        be changed using 'services.nghttpx.redirect-https-port'
+        option.
+
+        If at least one backend has "redirect-if-not-tls" set to true,
+        this feature is enabled for all backend servers with the same
+        pattern. It is advised to set "redirect-if-no-tls" parameter
+        to all backends explicitly if this feature is desired.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/nghttpx/backend-submodule.nix b/nixos/modules/services/networking/nghttpx/backend-submodule.nix
new file mode 100644
index 000000000000..eb559e926e76
--- /dev/null
+++ b/nixos/modules/services/networking/nghttpx/backend-submodule.nix
@@ -0,0 +1,50 @@
+{ lib, ... }:
+{ options = {
+    server = lib.mkOption {
+      type =
+        lib.types.either
+          (lib.types.submodule (import ./server-options.nix))
+          (lib.types.path);
+      example = {
+        host = "127.0.0.1";
+        port = 8888;
+      };
+      default = {
+        host = "127.0.0.1";
+        port = 80;
+      };
+      description = ''
+        Backend server location specified as either a host:port pair
+        or a unix domain docket.
+      '';
+    };
+
+    patterns = lib.mkOption {
+      type    = lib.types.listOf lib.types.str;
+      example = [
+        "*.host.net/v1/"
+        "host.org/v2/mypath"
+        "/somepath"
+      ];
+      default     = [];
+      description = ''
+        List of nghttpx backend patterns.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more information on the pattern syntax and nghttpxs behavior.
+      '';
+    };
+
+    params = lib.mkOption {
+      type    = lib.types.nullOr (lib.types.submodule (import ./backend-params-submodule.nix));
+      example = {
+        proto = "h2";
+        tls   = true;
+      };
+      default     = null;
+      description = ''
+        Parameters to configure a backend.
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/nghttpx/default.nix b/nixos/modules/services/networking/nghttpx/default.nix
new file mode 100644
index 000000000000..d6e1906e3881
--- /dev/null
+++ b/nixos/modules/services/networking/nghttpx/default.nix
@@ -0,0 +1,117 @@
+{config, pkgs, lib, ...}:
+let
+  cfg = config.services.nghttpx;
+
+  # renderHost :: Either ServerOptions Path -> String
+  renderHost = server:
+    if builtins.isString server
+    then "unix://${server}"
+    else "${server.host},${builtins.toString server.port}";
+
+  # Filter out submodule parameters whose value is null or false or is
+  # the key _module.
+  #
+  # filterParams :: ParamsSubmodule -> ParamsSubmodule
+  filterParams = p:
+    lib.filterAttrs
+      (n: v: ("_module" != n) && (null != v) && (false != v))
+      (lib.optionalAttrs (null != p) p);
+
+  # renderBackend :: BackendSubmodule -> String
+  renderBackend = backend:
+    let
+      host = renderHost backend.server;
+      patterns = lib.concatStringsSep ":" backend.patterns;
+
+      # Render a set of backend parameters, this is somewhat
+      # complicated because nghttpx backend patterns can be entirely
+      # omitted and the params may be given as a mixed collection of
+      # 'key=val' pairs or atoms (e.g: 'proto=h2;tls')
+      params =
+        lib.mapAttrsToList
+          (n: v:
+            if builtins.isBool v
+            then n
+            else if builtins.isString v
+            then "${n}=${v}"
+            else "${n}=${builtins.toString v}")
+          (filterParams backend.params);
+
+      # NB: params are delimited by a ";" which is the same delimiter
+      # to separate the host;[pattern];[params] sections of a backend
+      sections =
+        builtins.filter (e: "" != e) ([
+          host
+          patterns
+        ]++params);
+      formattedSections = lib.concatStringsSep ";" sections;
+    in
+      "backend=${formattedSections}";
+
+  # renderFrontend :: FrontendSubmodule -> String
+  renderFrontend = frontend:
+    let
+      host   = renderHost frontend.server;
+      params0 =
+        lib.mapAttrsToList
+          (n: v: if builtins.isBool v then n else v)
+          (filterParams frontend.params);
+
+      # NB: nghttpx doesn't accept "tls", you must omit "no-tls" for
+      # the default behavior of turning on TLS.
+      params1 = lib.remove "tls" params0;
+          
+      sections          = [ host] ++ params1;
+      formattedSections = lib.concatStringsSep ";" sections;
+    in
+      "frontend=${formattedSections}";
+
+  configurationFile = pkgs.writeText "nghttpx.conf" ''
+    ${lib.optionalString (null != cfg.tls) ("private-key-file="+cfg.tls.key)}
+    ${lib.optionalString (null != cfg.tls) ("certificate-file="+cfg.tls.crt)}
+
+    user=nghttpx
+
+    ${lib.concatMapStringsSep "\n" renderFrontend cfg.frontends}
+    ${lib.concatMapStringsSep "\n" renderBackend  cfg.backends}
+
+    backlog=${builtins.toString cfg.backlog}
+    backend-address-family=${cfg.backend-address-family}
+
+    workers=${builtins.toString cfg.workers}
+    rlimit-nofile=${builtins.toString cfg.rlimit-nofile}
+
+    ${lib.optionalString cfg.single-thread "single-thread=yes"}
+    ${lib.optionalString cfg.single-process "single-process=yes"}
+
+    ${cfg.extraConfig}
+  '';
+in
+{ imports = [
+    ./nghttpx-options.nix
+  ];
+  
+  config = lib.mkIf cfg.enable {
+
+    users.groups.nghttpx = { };
+    users.users.nghttpx = {
+      group = config.users.groups.nghttpx.name;
+    };
+      
+
+    systemd.services = {
+      nghttpx = {
+        wantedBy = [ "multi-user.target" ];
+        after    = [ "network.target" ];
+        script   = ''
+          ${pkgs.nghttp2}/bin/nghttpx --conf=${configurationFile}
+        '';
+
+        serviceConfig = {
+          Restart    = "on-failure";
+          RestartSec = 60;
+        };
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix b/nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix
new file mode 100644
index 000000000000..33c8572bd14f
--- /dev/null
+++ b/nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix
@@ -0,0 +1,64 @@
+{ lib, ...}:
+{ options = {
+    tls = lib.mkOption {
+      type        = lib.types.enum [ "tls" "no-tls" ];
+      default     = "tls";
+      description = ''
+        Enable or disable TLS. If true (enabled) the key and
+        certificate must be configured for nghttpx.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f
+        for more detail.
+      '';
+    };
+
+    sni-fwd = lib.mkOption {
+      type    = lib.types.bool;
+      default = false;
+      description = ''
+        When performing a match to select a backend server, SNI host
+        name received from the client is used instead of the request
+        host. See --backend option about the pattern match.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f
+        for more detail.
+      '';
+    };
+
+    api = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        Enable API access for this frontend. This enables you to
+        dynamically modify nghttpx at run-time therefore this feature
+        is disabled by default and should be turned on with care.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f
+        for more detail.
+      '';
+    };
+
+    healthmon = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        Make this frontend a health monitor endpoint. Any request
+        received on this frontend is responded to with a 200 OK.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f
+        for more detail.
+      '';
+    };
+
+    proxyproto = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        Accept PROXY protocol version 1 on frontend connection.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f
+        for more detail.
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/nghttpx/frontend-submodule.nix b/nixos/modules/services/networking/nghttpx/frontend-submodule.nix
new file mode 100644
index 000000000000..887ef4502131
--- /dev/null
+++ b/nixos/modules/services/networking/nghttpx/frontend-submodule.nix
@@ -0,0 +1,36 @@
+{ lib, ... }:
+{ options = {
+    server = lib.mkOption {
+      type =
+        lib.types.either
+          (lib.types.submodule (import ./server-options.nix))
+          (lib.types.path);
+      example = {
+        host = "127.0.0.1";
+        port = 8888;
+      };
+      default = {
+        host = "127.0.0.1";
+        port = 80;
+      };
+      description = ''
+        Frontend server interface binding specification as either a
+        host:port pair or a unix domain docket.
+
+        NB: a host of "*" listens on all interfaces and includes IPv6
+        addresses.
+      '';
+    };
+
+    params = lib.mkOption {
+      type    = lib.types.nullOr (lib.types.submodule (import ./frontend-params-submodule.nix));
+      example = {
+        tls   = "tls";
+      };
+      default     = null;
+      description = ''
+        Parameters to configure a backend.
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/nghttpx/nghttpx-options.nix b/nixos/modules/services/networking/nghttpx/nghttpx-options.nix
new file mode 100644
index 000000000000..cce65be321a8
--- /dev/null
+++ b/nixos/modules/services/networking/nghttpx/nghttpx-options.nix
@@ -0,0 +1,142 @@
+{ config, lib, ... }:
+{ options.services.nghttpx = {
+    enable = lib.mkEnableOption "nghttpx";
+
+    frontends = lib.mkOption {
+      type        = lib.types.listOf (lib.types.submodule (import ./frontend-submodule.nix));
+      description = ''
+        A list of frontend listener specifications.
+      '';
+      example = [
+        { server = {
+            host = "*";
+            port = 80;
+          };
+
+          params = {
+            tls = "no-tls";
+          };
+        }
+      ];
+    };
+
+    backends  = lib.mkOption {
+      type = lib.types.listOf (lib.types.submodule (import ./backend-submodule.nix));
+      description = ''
+        A list of backend specifications.
+      '';
+      example = [
+        { server = {
+            host = "172.16.0.22";
+            port = 8443;
+          };
+          patterns = [ "/" ];
+          params   = {
+            proto               = "http/1.1";
+            redirect-if-not-tls = true;
+          };
+        }
+      ];
+    };
+
+    tls = lib.mkOption {
+      type        = lib.types.nullOr (lib.types.submodule (import ./tls-submodule.nix));
+      default     = null;
+      description = ''
+        TLS certificate and key paths. Note that this does not enable
+        TLS for a frontend listener, to do so, a frontend
+        specification must set <literal>params.tls</literal> to true.
+      '';
+      example = {
+        key = "/etc/ssl/keys/server.key";
+        crt = "/etc/ssl/certs/server.crt";
+      };
+    };
+
+    extraConfig = lib.mkOption {
+      type        = lib.types.lines;
+      default     = "";
+      description = ''
+        Extra configuration options to be appended to the generated
+        configuration file.
+      '';
+    };
+
+    single-process = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        Run this program in a single process mode for debugging
+        purpose. Without this option, nghttpx creates at least 2
+        processes: master and worker processes. If this option is
+        used, master and worker are unified into a single
+        process. nghttpx still spawns additional process if neverbleed
+        is used. In the single process mode, the signal handling
+        feature is disabled.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--single-process
+      '';
+    };
+
+    backlog = lib.mkOption {
+      type        = lib.types.int;
+      default     = 65536;
+      description = ''
+        Listen backlog size.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--backlog
+      '';
+    };
+
+    backend-address-family = lib.mkOption {
+      type = lib.types.enum [
+        "auto"
+        "IPv4"
+        "IPv6"
+      ];
+      default = "auto";
+      description = ''
+        Specify address family of backend connections. If "auto" is
+        given, both IPv4 and IPv6 are considered. If "IPv4" is given,
+        only IPv4 address is considered. If "IPv6" is given, only IPv6
+        address is considered.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--backend-address-family
+      '';
+    };
+
+    workers = lib.mkOption {
+      type        = lib.types.int;
+      default     = 1;
+      description = ''
+        Set the number of worker threads.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-n
+      '';
+    };
+
+    single-thread = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        Run everything in one thread inside the worker process. This
+        feature is provided for better debugging experience, or for
+        the platforms which lack thread support. If threading is
+        disabled, this option is always enabled.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--single-thread
+      '';
+    };
+
+    rlimit-nofile = lib.mkOption {
+      type        = lib.types.int;
+      default     = 0;
+      description = ''
+        Set maximum number of open files (RLIMIT_NOFILE) to &lt;N&gt;. If 0
+        is given, nghttpx does not set the limit.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--rlimit-nofile
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/nghttpx/server-options.nix b/nixos/modules/services/networking/nghttpx/server-options.nix
new file mode 100644
index 000000000000..ef23bfd793c5
--- /dev/null
+++ b/nixos/modules/services/networking/nghttpx/server-options.nix
@@ -0,0 +1,18 @@
+{ lib, ... }:
+{ options = {
+    host = lib.mkOption {
+      type        = lib.types.str;
+      example     = "127.0.0.1";
+      description = ''
+        Server host address.
+      '';
+    };
+    port = lib.mkOption {
+      type        = lib.types.int;
+      example     = 5088;
+      description = ''
+        Server host port.
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/nghttpx/tls-submodule.nix b/nixos/modules/services/networking/nghttpx/tls-submodule.nix
new file mode 100644
index 000000000000..8f3cdaae2c81
--- /dev/null
+++ b/nixos/modules/services/networking/nghttpx/tls-submodule.nix
@@ -0,0 +1,21 @@
+{lib, ...}:
+{ options = {
+    key = lib.mkOption {
+      type        = lib.types.str;
+      example     = "/etc/ssl/keys/mykeyfile.key";
+      default     = "/etc/ssl/keys/server.key";
+      description = ''
+        Path to the TLS key file.
+      '';
+    };
+
+    crt = lib.mkOption {
+      type        = lib.types.str;
+      example     = "/etc/ssl/certs/mycert.crt";
+      default     = "/etc/ssl/certs/server.crt";
+      description = ''
+        Path to the TLS certificate file.
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index 8828429a8178..f0fddcca766f 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -54,8 +54,6 @@ let
     ));
   in listToAttrs (map mkAuthKeyFile usersWithKeys);
 
-  supportOldHostKeys = !versionAtLeast config.system.stateVersion "15.07";
-
 in
 
 {
@@ -191,9 +189,6 @@ in
         default =
           [ { type = "rsa"; bits = 4096; path = "/etc/ssh/ssh_host_rsa_key"; }
             { type = "ed25519"; path = "/etc/ssh/ssh_host_ed25519_key"; }
-          ] ++ optionals supportOldHostKeys
-          [ { type = "dsa"; path = "/etc/ssh/ssh_host_dsa_key"; }
-            { type = "ecdsa"; bits = 521; path = "/etc/ssh/ssh_host_ecdsa_key"; }
           ];
         description = ''
           NixOS can automatically generate SSH host keys.  This option
@@ -363,14 +358,21 @@ in
           HostKey ${k.path}
         '')}
 
-        # Allow DSA client keys for now. (These were deprecated
-        # in OpenSSH 7.0.)
-        PubkeyAcceptedKeyTypes +ssh-dss
+        ### Recommended settings from both:
+        # https://stribika.github.io/2015/01/04/secure-secure-shell.html
+        # and
+        # https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29
 
-        # Re-enable DSA host keys for now.
-        ${optionalString supportOldHostKeys ''
-          HostKeyAlgorithms +ssh-dss
-        ''}
+        KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
+        Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
+        MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com
+
+        # LogLevel VERBOSE logs user's key fingerprint on login.
+        # Needed to have a clear audit track of which key was used to log in.
+        LogLevel VERBOSE
+
+        # Use kernel sandbox mechanisms where possible in unprivileged processes.
+        UsePrivilegeSeparation sandbox
       '';
 
     assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true;
diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix
index f71f30fee97a..7de2d121e76c 100644
--- a/nixos/modules/services/security/clamav.nix
+++ b/nixos/modules/services/security/clamav.nix
@@ -76,8 +76,9 @@ in
     };
   };
 
-  config = mkIf cfg.updater.enable or cfg.daemon.enable {
+  config = mkIf (cfg.updater.enable || cfg.daemon.enable) {
     environment.systemPackages = [ pkg ];
+
     users.extraUsers = singleton {
       name = clamavUser;
       uid = config.ids.uids.clamav;
@@ -94,7 +95,7 @@ in
     environment.etc."clamav/freshclam.conf".source = freshclamConfigFile;
     environment.etc."clamav/clamd.conf".source = clamdConfigFile;
 
-    systemd.services.clamav-daemon = mkIf cfg.daemon.enable {
+    systemd.services.clamav-daemon = optionalAttrs cfg.daemon.enable {
       description = "ClamAV daemon (clamd)";
       after = mkIf cfg.updater.enable [ "clamav-freshclam.service" ];
       requires = mkIf cfg.updater.enable [ "clamav-freshclam.service" ];
@@ -115,7 +116,7 @@ in
       };
     };
 
-    systemd.timers.clamav-freshclam = mkIf cfg.updater.enable {
+    systemd.timers.clamav-freshclam = optionalAttrs cfg.updater.enable {
       description = "Timer for ClamAV virus database updater (freshclam)";
       wantedBy = [ "timers.target" ];
       timerConfig = {
@@ -124,7 +125,7 @@ in
       };
     };
 
-    systemd.services.clamav-freshclam = mkIf cfg.updater.enable {
+    systemd.services.clamav-freshclam = optionalAttrs cfg.updater.enable {
       description = "ClamAV virus database updater (freshclam)";
       restartTriggers = [ freshclamConfigFile ];
 
diff --git a/nixos/modules/services/web-apps/restya-board.nix b/nixos/modules/services/web-apps/restya-board.nix
new file mode 100644
index 000000000000..cee725e8fe5f
--- /dev/null
+++ b/nixos/modules/services/web-apps/restya-board.nix
@@ -0,0 +1,384 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+# TODO: are these php-packages needed?
+#imagick
+#php-geoip -> php.ini: extension = geoip.so
+#expat
+
+let
+  cfg = config.services.restya-board;
+
+  runDir = "/run/restya-board";
+
+  poolName = "restya-board";
+  phpfpmSocketName = "/var/run/phpfpm/${poolName}.sock";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.restya-board = {
+
+      enable = mkEnableOption "restya-board";
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/restya-board";
+        example = "/var/lib/restya-board";
+        description = ''
+          Data of the application.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "restya-board";
+        example = "restya-board";
+        description = ''
+          User account under which the web-application runs.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "nginx";
+        example = "nginx";
+        description = ''
+          Group account under which the web-application runs.
+        '';
+      };
+
+      virtualHost = {
+        serverName = mkOption {
+          type = types.str;
+          default = "restya.board";
+          description = ''
+            Name of the nginx virtualhost to use.
+          '';
+        };
+
+        listenHost = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = ''
+            Listen address for the virtualhost to use.
+          '';
+        };
+
+        listenPort = mkOption {
+          type = types.int;
+          default = 3000;
+          description = ''
+            Listen port for the virtualhost to use.
+          '';
+        };
+      };
+
+      database = {
+        host = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            Host of the database. Leave 'null' to use a local PostgreSQL database.
+            A local PostgreSQL database is initialized automatically.
+          '';
+        };
+
+        port = mkOption {
+          type = types.nullOr types.int;
+          default = 5432;
+          description = ''
+            The database's port.
+          '';
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "restya_board";
+          description = ''
+            Name of the database. The database must exist.
+          '';
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "restya_board";
+          description = ''
+            The database user. The user must exist and have access to
+            the specified database.
+          '';
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            The database user's password. 'null' if no password is set.
+          '';
+        };
+      };
+
+      email = {
+        server = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          example = "localhost";
+          description = ''
+            Hostname to send outgoing mail. Null to use the system MTA.
+          '';
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = 25;
+          description = ''
+            Port used to connect to SMTP server.
+          '';
+        };
+
+        login = mkOption {
+          type = types.str;
+          default = "";
+          description = ''
+            SMTP authentication login used when sending outgoing mail.
+          '';
+        };
+
+        password = mkOption {
+          type = types.str;
+          default = "";
+          description = ''
+            SMTP authentication password used when sending outgoing mail.
+
+            ATTENTION: The password is stored world-readable in the nix-store!
+          '';
+        };
+      };
+
+      timezone = mkOption {
+        type = types.lines;
+        default = "GMT";
+        description = ''
+          Timezone the web-app runs in.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.phpfpm.poolConfigs = {
+      "${poolName}" = ''
+        listen = "${phpfpmSocketName}";
+        listen.owner = nginx
+        listen.group = nginx
+        listen.mode = 0600
+        user = ${cfg.user}
+        group = ${cfg.group}
+        pm = dynamic
+        pm.max_children = 75
+        pm.start_servers = 10
+        pm.min_spare_servers = 5
+        pm.max_spare_servers = 20
+        pm.max_requests = 500
+        catch_workers_output = 1
+      '';
+    };
+
+    services.phpfpm.phpOptions = ''
+      date.timezone = "CET"
+
+      ${optionalString (!isNull cfg.email.server) ''
+        SMTP = ${cfg.email.server}
+        smtp_port = ${toString cfg.email.port}
+        auth_username = ${cfg.email.login}
+        auth_password = ${cfg.email.password}
+      ''}
+    '';
+
+    services.nginx.enable = true;
+    services.nginx.virtualHosts."${cfg.virtualHost.serverName}" = {
+      listen = [ { addr = cfg.virtualHost.listenHost; port = cfg.virtualHost.listenPort; } ];
+      serverName = cfg.virtualHost.serverName;
+      root = runDir;
+      extraConfig = ''
+        index index.html index.php;
+
+        gzip on;
+        gzip_disable "msie6";
+
+        gzip_comp_level 6;
+        gzip_min_length  1100;
+        gzip_buffers 16 8k;
+        gzip_proxied any;
+        gzip_types text/plain application/xml text/css text/js text/xml application/x-javascript text/javascript application/json application/xml+rss;
+
+        client_max_body_size 300M;
+
+        rewrite ^/oauth/authorize$ /server/php/authorize.php last;
+        rewrite ^/oauth_callback/([a-zA-Z0-9_\.]*)/([a-zA-Z0-9_\.]*)$ /server/php/oauth_callback.php?plugin=$1&code=$2 last;
+        rewrite ^/download/([0-9]*)/([a-zA-Z0-9_\.]*)$ /server/php/download.php?id=$1&hash=$2 last;
+        rewrite ^/ical/([0-9]*)/([0-9]*)/([a-z0-9]*).ics$ /server/php/ical.php?board_id=$1&user_id=$2&hash=$3 last;
+        rewrite ^/api/(.*)$ /server/php/R/r.php?_url=$1&$args last;
+        rewrite ^/api_explorer/api-docs/$ /client/api_explorer/api-docs/index.php last;
+      '';
+
+      locations."/".root = "${runDir}/client";
+
+      locations."~ \.php$" = {
+        tryFiles = "$uri =404";
+        extraConfig = ''
+          include ${pkgs.nginx}/conf/fastcgi_params;
+          fastcgi_pass    unix:${phpfpmSocketName};
+          fastcgi_index   index.php;
+          fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
+          fastcgi_param   PHP_VALUE "upload_max_filesize=9G \n post_max_size=9G \n max_execution_time=200 \n max_input_time=200 \n memory_limit=256M";
+        '';
+      };
+
+      locations."~* \.(css|js|less|html|ttf|woff|jpg|jpeg|gif|png|bmp|ico)" = {
+        root = "${runDir}/client";
+        extraConfig = ''
+          if (-f $request_filename) {
+                  break;
+          }
+          rewrite ^/img/([a-zA-Z_]*)/([a-zA-Z_]*)/([a-zA-Z0-9_\.]*)$ /server/php/image.php?size=$1&model=$2&filename=$3 last;
+          add_header        Cache-Control public;
+          add_header        Cache-Control must-revalidate;
+          expires           7d;
+        '';
+      };
+    };
+
+    systemd.services.restya-board-init = {
+      description = "Restya board initialization";
+      serviceConfig.Type = "oneshot";
+      serviceConfig.RemainAfterExit = true;
+
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "postgresql.service" ];
+      after = [ "network.target" "postgresql.service" ];
+
+      script = ''
+        rm -rf "${runDir}"
+        mkdir -m 750 -p "${runDir}"
+        cp -r "${pkgs.restya-board}/"* "${runDir}"
+        sed -i "s/@restya.com/@${cfg.virtualHost.serverName}/g" "${runDir}/sql/restyaboard_with_empty_data.sql"
+        rm -rf "${runDir}/media"
+        rm -rf "${runDir}/client/img"
+        chmod -R 0750 "${runDir}"
+
+        sed -i "s@^php@${config.services.phpfpm.phpPackage}/bin/php@" "${runDir}/server/php/shell/"*.sh
+
+        ${if (isNull cfg.database.host) then ''
+          sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', 'localhost');/g" "${runDir}/server/php/config.inc.php"
+          sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', 'restya');/g" "${runDir}/server/php/config.inc.php"
+        '' else ''
+          sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', '${cfg.database.host}');/g" "${runDir}/server/php/config.inc.php"
+          sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', '$(<${cfg.database.dbPassFile})');/g" "${runDir}/server/php/config.inc.php"
+        ''}
+        sed -i "s/^.*'R_DB_PORT'.*$/define('R_DB_PORT', '${toString cfg.database.port}');/g" "${runDir}/server/php/config.inc.php"
+        sed -i "s/^.*'R_DB_NAME'.*$/define('R_DB_NAME', '${cfg.database.name}');/g" "${runDir}/server/php/config.inc.php"
+        sed -i "s/^.*'R_DB_USER'.*$/define('R_DB_USER', '${cfg.database.user}');/g" "${runDir}/server/php/config.inc.php"
+
+        chmod 0400 "${runDir}/server/php/config.inc.php"
+
+        ln -sf "${cfg.dataDir}/media" "${runDir}/media"
+        ln -sf "${cfg.dataDir}/client/img" "${runDir}/client/img"
+
+        chmod g+w "${runDir}/tmp/cache"
+        chown -R "${cfg.user}"."${cfg.group}" "${runDir}"
+
+
+        mkdir -m 0750 -p "${cfg.dataDir}"
+        mkdir -m 0750 -p "${cfg.dataDir}/media"
+        mkdir -m 0750 -p "${cfg.dataDir}/client/img"
+        cp -r "${pkgs.restya-board}/media/"* "${cfg.dataDir}/media"
+        cp -r "${pkgs.restya-board}/client/img/"* "${cfg.dataDir}/client/img"
+        chown "${cfg.user}"."${cfg.group}" "${cfg.dataDir}"
+        chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/media"
+        chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/client/img"
+
+        ${optionalString (isNull cfg.database.host) ''
+          if ! [ -e "${cfg.dataDir}/.db-initialized" ]; then
+            ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
+              ${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \
+              -c "CREATE USER ${cfg.database.user} WITH ENCRYPTED PASSWORD 'restya'"
+
+            ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
+              ${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \
+              -c "CREATE DATABASE ${cfg.database.name} OWNER ${cfg.database.user} ENCODING 'UTF8' TEMPLATE template0"
+
+            ${pkgs.sudo}/bin/sudo -u ${cfg.user} \
+              ${config.services.postgresql.package}/bin/psql -U ${cfg.database.user} \
+              -d ${cfg.database.name} -f "${runDir}/sql/restyaboard_with_empty_data.sql"
+
+            touch "${cfg.dataDir}/.db-initialized"
+          fi
+        ''}
+      '';
+    };
+
+    systemd.timers.restya-board = {
+      description = "restya-board scripts for e.g. email notification";
+      wantedBy = [ "timers.target" ];
+      after = [ "restya-board-init.service" ];
+      requires = [ "restya-board-init.service" ];
+      timerConfig = {
+        OnUnitInactiveSec = "60s";
+        Unit = "restya-board-timers.service";
+      };
+    };
+
+    systemd.services.restya-board-timers = {
+      description = "restya-board scripts for e.g. email notification";
+      serviceConfig.Type = "oneshot";
+      serviceConfig.User = cfg.user;
+
+      after = [ "restya-board-init.service" ];
+      requires = [ "restya-board-init.service" ];
+
+      script = ''
+        /bin/sh ${runDir}/server/php/shell/instant_email_notification.sh 2> /dev/null || true
+        /bin/sh ${runDir}/server/php/shell/periodic_email_notification.sh 2> /dev/null || true
+        /bin/sh ${runDir}/server/php/shell/imap.sh 2> /dev/null || true
+        /bin/sh ${runDir}/server/php/shell/webhook.sh 2> /dev/null || true
+        /bin/sh ${runDir}/server/php/shell/card_due_notification.sh 2> /dev/null || true
+      '';
+    };
+
+    users.extraUsers.restya-board = {
+      isSystemUser = true;
+      createHome = false;
+      home = runDir;
+      group  = "restya-board";
+    };
+    users.extraGroups.restya-board = {};
+
+    services.postgresql.enable = mkIf (isNull cfg.database.host) true;
+
+    services.postgresql.identMap = optionalString (isNull cfg.database.host)
+      ''
+        restya-board-users restya-board restya_board
+      '';
+
+    services.postgresql.authentication = optionalString (isNull cfg.database.host)
+      ''
+        local restya_board all ident map=restya-board-users
+      '';
+
+  };
+
+}
+
diff --git a/nixos/modules/services/web-servers/apache-httpd/owncloud.nix b/nixos/modules/services/web-servers/apache-httpd/owncloud.nix
index 94e85f1f4289..cfddab2f5047 100644
--- a/nixos/modules/services/web-servers/apache-httpd/owncloud.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/owncloud.nix
@@ -188,8 +188,7 @@ let
       /* date format to be used while writing to the owncloud logfile */
       'logdateformat' => 'F d, Y H:i:s',
 
-      /* timezone used while writing to the owncloud logfile (default: UTC) */
-      'logtimezone' => '${serverInfo.fullConfig.time.timeZone}',
+      ${tzSetting}
 
       /* Append all database queries and parameters to the log file.
        (watch out, this option can increase the size of your log file)*/
@@ -339,6 +338,31 @@ let
 
     '';
 
+  tzSetting = let tz = serverInfo.fullConfig.time.timeZone; in optionalString (!isNull tz) ''
+    /* timezone used while writing to the owncloud logfile (default: UTC) */
+    'logtimezone' => '${tz}',
+  '';
+
+  postgresql = serverInfo.fullConfig.services.postgresql.package;
+
+  setupDb = pkgs.writeScript "setup-owncloud-db" ''
+    #!${pkgs.stdenv.shell}
+    PATH="${postgresql}/bin"
+    createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true
+    createdb "${config.dbName}" -O "${config.dbUser}" || true
+    psql -U postgres -d postgres -c "alter user ${config.dbUser} with password '${config.dbPassword}';" || true
+
+    QUERY="CREATE TABLE appconfig
+             ( appid       VARCHAR( 255 ) NOT NULL
+             , configkey   VARCHAR( 255 ) NOT NULL
+             , configvalue VARCHAR( 255 ) NOT NULL
+             );
+           GRANT ALL ON appconfig TO ${config.dbUser};
+           ALTER TABLE appconfig OWNER TO ${config.dbUser};"
+
+    psql -h "/tmp" -U postgres -d ${config.dbName} -Atw -c "$QUERY" || true
+  '';
+
 in
 
 rec {
@@ -353,7 +377,7 @@ rec {
       ''}
 
       <Directory ${config.package}>
-        ${builtins.readFile "${config.package}/.htaccess"}
+        Include ${config.package}/.htaccess
       </Directory>
     '';
 
@@ -373,7 +397,7 @@ rec {
       defaultText = "pkgs.owncloud70";
       example = literalExample "pkgs.owncloud70";
       description = ''
-          PostgreSQL package to use.
+          ownCloud package to use.
       '';
     };
 
@@ -574,13 +598,7 @@ rec {
       chmod -R o-rwx ${config.dataDir}
       chown -R wwwrun:wwwrun ${config.dataDir}
 
-      ${pkgs.postgresql}/bin/createuser -s -r postgres
-      ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true
-      ${pkgs.postgresql}/bin/createdb "${config.dbName}" -O "${config.dbUser}" || true
-      ${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql}/bin/psql -U postgres -d postgres -c "alter user ${config.dbUser} with password '${config.dbPassword}';" || true
-
-      QUERY="CREATE TABLE appconfig (appid VARCHAR( 255 ) NOT NULL ,configkey VARCHAR( 255 ) NOT NULL ,configvalue VARCHAR( 255 ) NOT NULL); GRANT ALL ON appconfig TO ${config.dbUser}; ALTER TABLE appconfig OWNER TO ${config.dbUser};"
-      ${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql}/bin/psql -h "/tmp" -U postgres -d ${config.dbName} -Atw -c "$QUERY" || true
+      ${pkgs.sudo}/bin/sudo -u postgres ${setupDb}
     fi
 
     if [ -e ${config.package}/config/ca-bundle.crt ]; then
@@ -591,7 +609,11 @@ rec {
 
     chown wwwrun:wwwrun ${config.dataDir}/owncloud.log || true
 
-    QUERY="INSERT INTO groups (gid) values('admin'); INSERT INTO users (uid,password) values('${config.adminUser}','${builtins.hashString "sha1" config.adminPassword}'); INSERT INTO group_user (gid,uid) values('admin','${config.adminUser}');"
-    ${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql}/bin/psql -h "/tmp" -U postgres -d ${config.dbName} -Atw -c "$QUERY" || true
+    QUERY="INSERT INTO groups (gid) values('admin');
+           INSERT INTO users (uid,password)
+             values('${config.adminUser}','${builtins.hashString "sha1" config.adminPassword}');
+           INSERT INTO group_user (gid,uid)
+             values('admin','${config.adminUser}');"
+    ${pkgs.sudo}/bin/sudo -u postgres ${postgresql}/bin/psql -h "/tmp" -U postgres -d ${config.dbName} -Atw -c "$QUERY" || true
   '';
 }
diff --git a/nixos/modules/services/web-servers/lighttpd/inginious.nix b/nixos/modules/services/web-servers/lighttpd/inginious.nix
index 669e81d0f14b..8c813d116a52 100644
--- a/nixos/modules/services/web-servers/lighttpd/inginious.nix
+++ b/nixos/modules/services/web-servers/lighttpd/inginious.nix
@@ -113,7 +113,6 @@ in
 
     tasksDirectory = mkOption {
       type = types.path;
-      default = "${inginious}/lib/python2.7/site-packages/inginious/tasks";
       example = "/var/lib/INGInious/tasks";
       description = ''
         Path to the tasks folder.
@@ -218,6 +217,7 @@ in
 
       # Common
       {
+        services.lighttpd.inginious.tasksDirectory = mkDefault "${inginious}/lib/python2.7/site-packages/inginious/tasks";
         # To access inginous tools (like inginious-test-task)
         environment.systemPackages = [ inginious ];
 
diff --git a/nixos/modules/services/x11/compton.nix b/nixos/modules/services/x11/compton.nix
index 11cbcac6fa85..8641c05de52e 100644
--- a/nixos/modules/services/x11/compton.nix
+++ b/nixos/modules/services/x11/compton.nix
@@ -181,10 +181,10 @@ in {
     };
 
     backend = mkOption {
-      type = types.enum [ "glx" "xrender" ];
+      type = types.enum [ "glx" "xrender" "xr_glx_hybrid" ];
       default = "xrender";
       description = ''
-        Backend to use: <literal>glx</literal> or <literal>xrender</literal>.
+        Backend to use: <literal>glx</literal>, <literal>xrender</literal> or <literal>xr_glx_hybrid</literal>.
       '';
     };
 
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index 13f339e3fbf3..39b27d4ceb61 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -87,11 +87,11 @@ in
 
       default = mkOption {
         type = types.str;
-        default = "";
-        example = "none";
+        default = "none";
+        example = "plasma5";
         description = "Default desktop manager loaded if none have been chosen.";
         apply = defaultDM:
-          if defaultDM == "" && cfg.session.list != [] then
+          if defaultDM == "none" && cfg.session.list != [] then
             (head cfg.session.list).name
           else if any (w: w.name == defaultDM) cfg.session.list then
             defaultDM
diff --git a/nixos/modules/services/x11/desktop-managers/enlightenment.nix b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
index b02eaf861a0d..8a523f0d8036 100644
--- a/nixos/modules/services/x11/desktop-managers/enlightenment.nix
+++ b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
@@ -61,7 +61,7 @@ in
       '';
     }];
 
-    security.wrappers.e_freqset.source = "${e.enlightenment.out}/bin/e_freqset";
+    security.wrappers = (import (builtins.toPath "${e.enlightenment}/e-wrappers.nix")).security.wrappers;
 
     environment.etc = singleton
       { source = xcfg.xkbDir;
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index ecf0abb1efd2..d2c856fc9332 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -94,6 +94,8 @@ in {
     services.udisks2.enable = true;
     services.accounts-daemon.enable = true;
     services.geoclue2.enable = mkDefault true;
+    services.dleyna-renderer.enable = mkDefault true;
+    services.dleyna-server.enable = mkDefault true;
     services.gnome3.at-spi2-core.enable = true;
     services.gnome3.evolution-data-server.enable = true;
     services.gnome3.gnome-disks.enable = mkDefault true;
@@ -106,6 +108,7 @@ in {
     services.gnome3.seahorse.enable = mkDefault true;
     services.gnome3.sushi.enable = mkDefault true;
     services.gnome3.tracker.enable = mkDefault true;
+    services.gnome3.tracker-miners.enable = mkDefault true;
     hardware.pulseaudio.enable = mkDefault true;
     services.telepathy.enable = mkDefault true;
     networking.networkmanager.enable = mkDefault true;
@@ -150,7 +153,7 @@ in {
           export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${mimeAppsList}/share
 
           # Override gsettings-desktop-schema
-          export XDG_DATA_DIRS=${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides''${XDG_DATA_DIRS:+:}$XDG_DATA_DIRS
+          export NIX_GSETTINGS_OVERRIDES_DIR=${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas
 
           # Let nautilus find extensions
           export NAUTILUS_EXTENSION_DIR=${config.system.path}/lib/nautilus/extensions-3.0/
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index 7f54bb182fe9..bb4f4e868fea 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -195,7 +195,12 @@ in
 
       boot.plymouth = {
         theme = mkDefault "breeze";
-        themePackages = mkDefault [ pkgs.breeze-plymouth ];
+        themePackages = mkDefault [
+          (pkgs.breeze-plymouth.override {
+            nixosBranding = true;
+            nixosVersion = config.system.nixosRelease;
+          })
+        ];
       };
 
       security.pam.services.kde = { allowNullPassword = true; };
diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix
index 3fa482fb6722..43ed21c95fee 100644
--- a/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixos/modules/services/x11/display-managers/default.nix
@@ -59,12 +59,6 @@ let
       # Now it should be safe to assume that the script was called with the
       # expected parameters.
 
-      ${optionalString cfg.displayManager.logToJournal ''
-        if [ -z "$_DID_SYSTEMD_CAT" ]; then
-          _DID_SYSTEMD_CAT=1 exec ${config.systemd.package}/bin/systemd-cat -t xsession -- "$0" "$@"
-        fi
-      ''}
-
       . /etc/profile
       cd "$HOME"
 
@@ -72,16 +66,23 @@ let
       sessionType="$1"
       if [ "$sessionType" = default ]; then sessionType=""; fi
 
-      ${optionalString (!cfg.displayManager.job.logsXsession && !cfg.displayManager.logToJournal) ''
-        exec > ~/.xsession-errors 2>&1
-      ''}
-
       ${optionalString cfg.startDbusSession ''
         if test -z "$DBUS_SESSION_BUS_ADDRESS"; then
           exec ${pkgs.dbus.dbus-launch} --exit-with-session "$0" "$sessionType"
         fi
       ''}
 
+      ${optionalString cfg.displayManager.job.logToJournal ''
+        if [ -z "$_DID_SYSTEMD_CAT" ]; then
+          export _DID_SYSTEMD_CAT=1
+          exec ${config.systemd.package}/bin/systemd-cat -t xsession "$0" "$sessionType"
+        fi
+      ''}
+
+      ${optionalString cfg.displayManager.job.logToFile ''
+        exec &> >(tee ~/.xsession-errors)
+      ''}
+
       # Start PulseAudio if enabled.
       ${optionalString (config.hardware.pulseaudio.enable) ''
         ${optionalString (!config.hardware.pulseaudio.systemWide)
@@ -306,26 +307,24 @@ in
           description = "Additional environment variables needed by the display manager.";
         };
 
-        logsXsession = mkOption {
+        logToFile = mkOption {
           type = types.bool;
           default = false;
           description = ''
-            Whether the display manager redirects the
-            output of the session script to
-            <filename>~/.xsession-errors</filename>.
+            Whether the display manager redirects the output of the
+            session script to <filename>~/.xsession-errors</filename>.
           '';
         };
 
-      };
+        logToJournal = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Whether the display manager redirects the output of the
+            session script to the systemd journal.
+          '';
+        };
 
-      logToJournal = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          By default, the stdout/stderr of sessions is written
-          to <filename>~/.xsession-errors</filename>. When this option
-          is enabled, it will instead be written to the journal.
-        '';
       };
 
     };
diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix
index e83f26516f5f..07642a8b13db 100644
--- a/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixos/modules/services/x11/display-managers/gdm.nix
@@ -122,11 +122,8 @@ in
       "rc-local.service"
       "systemd-machined.service"
       "systemd-user-sessions.service"
-      "getty@tty1.service"
     ];
 
-    systemd.services."getty@tty1".enable = false;
-    systemd.services.display-manager.conflicts = [ "getty@tty1.service" ];
     systemd.services.display-manager.serviceConfig = {
       # Restart = "always"; - already defined in xserver.nix
       KillMode = "mixed";
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index 1733f2fd39b2..b5e936830918 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -190,7 +190,7 @@ in
     services.xserver.displayManager.slim.enable = false;
 
     services.xserver.displayManager.job = {
-      logsXsession = true;
+      logToFile = true;
 
       # lightdm relaunches itself via just `lightdm`, so needs to be on the PATH
       execCmd = ''
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix
index facaea131ae5..2d4cb8aa20a5 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/x11/display-managers/sddm.nix
@@ -205,7 +205,7 @@ in
     services.xserver.displayManager.slim.enable = false;
 
     services.xserver.displayManager.job = {
-      logsXsession = true;
+      logToFile = true;
 
       environment = {
         # Load themes from system environment
diff --git a/nixos/modules/services/x11/display-managers/xpra.nix b/nixos/modules/services/x11/display-managers/xpra.nix
index 8f5ce3dccc6a..b46ede550c16 100644
--- a/nixos/modules/services/x11/display-managers/xpra.nix
+++ b/nixos/modules/services/x11/display-managers/xpra.nix
@@ -220,7 +220,7 @@ in
     '';
 
     services.xserver.displayManager.job = {
-      logsXsession = true;
+      logToFile = true;
 
       execCmd = ''
         ${optionalString (cfg.pulseaudio)
diff --git a/nixos/modules/services/x11/fractalart.nix b/nixos/modules/services/x11/fractalart.nix
new file mode 100644
index 000000000000..448248a58794
--- /dev/null
+++ b/nixos/modules/services/x11/fractalart.nix
@@ -0,0 +1,36 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.fractalart;
+in {
+  options.services.fractalart = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      example = true;
+      description = "Enable FractalArt for generating colorful wallpapers on login";
+    };
+
+    width = mkOption {
+      type = types.nullOr types.int;
+      default = null;
+      example = 1920;
+      description = "Screen width";
+    };
+
+    height = mkOption {
+      type = types.nullOr types.int;
+      default = null;
+      example = 1080;
+      description = "Screen height";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.haskellPackages.FractalArt ];
+    services.xserver.displayManager.sessionCommands =
+      "${pkgs.haskellPackages.FractalArt}/bin/FractalArt --no-bg -f .background-image"
+        + optionalString (cfg.width  != null) " -w ${toString cfg.width}"
+        + optionalString (cfg.height != null) " -h ${toString cfg.height}";
+  };
+}
diff --git a/nixos/modules/services/x11/hardware/libinput.nix b/nixos/modules/services/x11/hardware/libinput.nix
index 44555cb6e2a9..5aecdef812e6 100644
--- a/nixos/modules/services/x11/hardware/libinput.nix
+++ b/nixos/modules/services/x11/hardware/libinput.nix
@@ -198,6 +198,13 @@ in {
 
     environment.systemPackages = [ pkgs.xorg.xf86inputlibinput ];
 
+    environment.etc = [
+      (let cfgPath = "X11/xorg.conf.d/40-libinput.conf"; in {
+        source = pkgs.xorg.xf86inputlibinput.out + "/share/" + cfgPath;
+        target = cfgPath;
+      })
+    ];
+
     services.udev.packages = [ pkgs.libinput ];
 
     services.xserver.config =
diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix
index d12003768a67..25ba95fccd75 100644
--- a/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixos/modules/services/x11/window-managers/default.nix
@@ -61,7 +61,9 @@ in
         example = "wmii";
         description = "Default window manager loaded if none have been chosen.";
         apply = defaultWM:
-          if any (w: w.name == defaultWM) cfg.session then
+          if defaultWM == "none" && cfg.session != []  then
+            (head cfg.session).name
+          else if any (w: w.name == defaultWM) cfg.session then
             defaultWM
           else
             throw "Default window manager (${defaultWM}) not found.";
diff --git a/nixos/modules/services/x11/window-managers/evilwm.nix b/nixos/modules/services/x11/window-managers/evilwm.nix
new file mode 100644
index 000000000000..6e19e3572c79
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/evilwm.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.xserver.windowManager.evilwm;
+in
+{
+  ###### interface
+  options = {
+    services.xserver.windowManager.evilwm.enable = mkEnableOption "evilwm";
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    services.xserver.windowManager.session = singleton {
+      name = "evilwm";
+      start = ''
+	${pkgs.evilwm}/bin/evilwm &
+	waitPID=$!
+      '';
+    };
+    environment.systemPackages = [ pkgs.evilwm ];
+  };
+}
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index d4fe475690ce..1049f810ad2a 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -161,6 +161,15 @@ in
         '';
       };
 
+      plainX = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether the X11 session can be plain (without DM/WM) and
+          the Xsession script will be used as fallback or not.
+        '';
+      };
+
       autorun = mkOption {
         type = types.bool;
         default = true;
@@ -552,6 +561,11 @@ in
                 + "${toString (length primaryHeads)} heads set to primary: "
                 + concatMapStringsSep ", " (x: x.output) primaryHeads;
       })
+      { assertion = cfg.desktopManager.default == "none" && cfg.windowManager.default == "none" -> cfg.plainX;
+        message = "Either the desktop manager or the window manager shouldn't be `none`! "
+                + "To explicitly allow this, you can also set `services.xserver.plainX` to `true`. "
+                + "The `default` value looks for enabled WMs/DMs and select the first one.";
+      }
     ];
 
     environment.etc =
@@ -686,7 +700,6 @@ in
         Section "InputClass"
           Identifier "Keyboard catchall"
           MatchIsKeyboard "on"
-          Option "XkbRules" "base"
           Option "XkbModel" "${cfg.xkbModel}"
           Option "XkbLayout" "${cfg.layout}"
           Option "XkbOptions" "${cfg.xkbOptions}"