summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
authorFrederik Rietdijk <fridh@fridh.nl>2019-07-13 09:45:40 +0200
committerFrederik Rietdijk <fridh@fridh.nl>2019-07-13 09:45:40 +0200
commit54065ae20d6c91cab54aa706d6e819baf673b80c (patch)
tree4b2efeafb2b34de1153ccfb5cf01387842c86143 /nixos/modules
parent22cb7f25f2f939a5e38f5e0c0920a743c37a9480 (diff)
parent7803ff314c707ee11a6d8d1c9ac4cde70737d22e (diff)
downloadnixpkgs-54065ae20d6c91cab54aa706d6e819baf673b80c.tar
nixpkgs-54065ae20d6c91cab54aa706d6e819baf673b80c.tar.gz
nixpkgs-54065ae20d6c91cab54aa706d6e819baf673b80c.tar.bz2
nixpkgs-54065ae20d6c91cab54aa706d6e819baf673b80c.tar.lz
nixpkgs-54065ae20d6c91cab54aa706d6e819baf673b80c.tar.xz
nixpkgs-54065ae20d6c91cab54aa706d6e819baf673b80c.tar.zst
nixpkgs-54065ae20d6c91cab54aa706d6e819baf673b80c.zip
Merge master into staging-next
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/hardware/opengl.nix20
-rw-r--r--nixos/modules/hardware/video/amdgpu-pro.nix3
-rw-r--r--nixos/modules/hardware/video/ati.nix3
-rw-r--r--nixos/modules/hardware/video/nvidia.nix1
-rw-r--r--nixos/modules/misc/ids.nix2
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/modules/rename.nix4
-rw-r--r--nixos/modules/services/monitoring/zabbix-agent.nix163
-rw-r--r--nixos/modules/services/monitoring/zabbix-proxy.nix290
-rw-r--r--nixos/modules/services/monitoring/zabbix-server.nix341
-rw-r--r--nixos/modules/services/networking/dhcpcd.nix2
-rw-r--r--nixos/modules/services/networking/kresd.nix7
-rw-r--r--nixos/modules/services/torrent/deluge.nix91
-rw-r--r--nixos/modules/services/web-apps/zabbix.nix225
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/foswiki.nix78
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/trac.nix121
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/zabbix.nix84
-rw-r--r--nixos/modules/services/x11/xserver.nix7
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl23
-rw-r--r--nixos/modules/tasks/auto-upgrade.nix32
-rw-r--r--nixos/modules/virtualisation/parallels-guest.nix3
21 files changed, 1030 insertions, 472 deletions
diff --git a/nixos/modules/hardware/opengl.nix b/nixos/modules/hardware/opengl.nix
index 2defab51bc3..57cac56bd8a 100644
--- a/nixos/modules/hardware/opengl.nix
+++ b/nixos/modules/hardware/opengl.nix
@@ -118,6 +118,19 @@ in
           set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc.
         '';
       };
+
+      setLdLibraryPath = mkOption {
+        type = types.bool;
+        internal = true;
+        default = false;
+        description = ''
+          Whether the <literal>LD_LIBRARY_PATH</literal> environment variable
+          should be set to the locations of driver libraries. Drivers which
+          rely on overriding libraries should set this to true. Drivers which
+          support <literal>libglvnd</literal> and other dispatch libraries
+          instead of overriding libraries should not set this.
+        '';
+      };
     };
 
   };
@@ -145,11 +158,8 @@ in
       )
     ];
 
-    environment.sessionVariables.LD_LIBRARY_PATH =
-      [ "/run/opengl-driver/lib" ] ++ optional cfg.driSupport32Bit "/run/opengl-driver-32/lib";
-
-    environment.variables.XDG_DATA_DIRS =
-      [ "/run/opengl-driver/share" ] ++ optional cfg.driSupport32Bit "/run/opengl-driver-32/share";
+    environment.sessionVariables.LD_LIBRARY_PATH = mkIf cfg.setLdLibraryPath
+      ([ "/run/opengl-driver/lib" ] ++ optional cfg.driSupport32Bit "/run/opengl-driver-32/lib");
 
     hardware.opengl.package = mkDefault (makePackage pkgs);
     hardware.opengl.package32 = mkDefault (makePackage pkgs.pkgsi686Linux);
diff --git a/nixos/modules/hardware/video/amdgpu-pro.nix b/nixos/modules/hardware/video/amdgpu-pro.nix
index ab9e0c92020..8e91e9d2baa 100644
--- a/nixos/modules/hardware/video/amdgpu-pro.nix
+++ b/nixos/modules/hardware/video/amdgpu-pro.nix
@@ -30,10 +30,11 @@ in
     nixpkgs.config.xorg.abiCompat = "1.19";
 
     services.xserver.drivers = singleton
-      { name = "amdgpu"; modules = [ package ]; libPath = [ package ]; };
+      { name = "amdgpu"; modules = [ package ]; };
 
     hardware.opengl.package = package;
     hardware.opengl.package32 = package32;
+    hardware.opengl.setLdLibraryPath = true;
 
     boot.extraModulePackages = [ package ];
 
diff --git a/nixos/modules/hardware/video/ati.nix b/nixos/modules/hardware/video/ati.nix
index 6102919f015..f867bba8063 100644
--- a/nixos/modules/hardware/video/ati.nix
+++ b/nixos/modules/hardware/video/ati.nix
@@ -21,10 +21,11 @@ in
     nixpkgs.config.xorg.abiCompat = "1.17";
 
     services.xserver.drivers = singleton
-      { name = "fglrx"; modules = [ ati_x11 ]; libPath = [ "${ati_x11}/lib" ]; };
+      { name = "fglrx"; modules = [ ati_x11 ]; };
 
     hardware.opengl.package = ati_x11;
     hardware.opengl.package32 = pkgs.pkgsi686Linux.linuxPackages.ati_drivers_x11.override { libsOnly = true; kernel = null; };
+    hardware.opengl.setLdLibraryPath = true;
 
     environment.systemPackages = [ ati_x11 ];
 
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index a5740929a31..da3c8ee5a9f 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -138,7 +138,6 @@ in
     services.xserver.drivers = singleton {
       name = "nvidia";
       modules = [ nvidia_x11.bin ];
-      libPath = [ nvidia_x11 ];
       deviceSection = optionalString optimusCfg.enable
         ''
           BusID "${optimusCfg.nvidiaBusId}"
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 14ba5a573b1..1047df95cdf 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -516,7 +516,7 @@
       tss = 176;
       #memcached = 177; # unused, removed 2018-01-03
       #ntp = 179; # unused
-      #zabbix = 180; # unused
+      zabbix = 180;
       #redis = 181; # unused, removed 2018-01-03
       #unifi = 183; # unused
       #uptimed = 184; # unused
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 1d1995eda25..db47e69bd4a 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -516,6 +516,7 @@
   ./services/monitoring/uptime.nix
   ./services/monitoring/vnstat.nix
   ./services/monitoring/zabbix-agent.nix
+  ./services/monitoring/zabbix-proxy.nix
   ./services/monitoring/zabbix-server.nix
   ./services/network-filesystems/beegfs.nix
   ./services/network-filesystems/cachefilesd.nix
@@ -782,6 +783,7 @@
   ./services/web-apps/virtlyst.nix
   ./services/web-apps/wordpress.nix
   ./services/web-apps/youtrack.nix
+  ./services/web-apps/zabbix.nix
   ./services/web-servers/apache-httpd/default.nix
   ./services/web-servers/caddy.nix
   ./services/web-servers/fcgiwrap.nix
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 1b77a895d71..e127782e85f 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -171,6 +171,9 @@ with lib;
        The starting time can be configured via <literal>services.postgresqlBackup.startAt</literal>.
     '')
 
+    # zabbixServer
+    (mkRenamedOptionModule [ "services" "zabbixServer" "dbServer" ] [ "services" "zabbixServer" "database" "host" ])
+
     # Profile splitting
     (mkRenamedOptionModule [ "virtualisation" "growPartition" ] [ "boot" "growPartition" ])
 
@@ -214,6 +217,7 @@ with lib;
     (mkRemovedOptionModule [ "services" "winstone" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "services" "mysql" "pidDir" ] "Don't wait for pidfiles, describe dependencies through systemd")
     (mkRemovedOptionModule [ "services" "mysql" "rootPassword" ] "Use socket authentication or set the password outside of the nix store.")
+    (mkRemovedOptionModule [ "services" "zabbixServer" "dbPassword" ] "Use services.zabbixServer.database.passwordFile instead.")
 
     # ZSH
     (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
diff --git a/nixos/modules/services/monitoring/zabbix-agent.nix b/nixos/modules/services/monitoring/zabbix-agent.nix
index 0519e7c2ad6..b1645f86110 100644
--- a/nixos/modules/services/monitoring/zabbix-agent.nix
+++ b/nixos/modules/services/monitoring/zabbix-agent.nix
@@ -1,73 +1,118 @@
-# Zabbix agent daemon.
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
-
   cfg = config.services.zabbixAgent;
 
-  zabbix = cfg.package;
-
-  stateDir = "/run/zabbix";
-
-  logDir = "/var/log/zabbix";
-
-  pidFile = "${stateDir}/zabbix_agentd.pid";
+  inherit (lib) mkDefault mkEnableOption mkIf mkOption;
+  inherit (lib) attrValues concatMapStringsSep literalExample optionalString types;
 
-  configFile = pkgs.writeText "zabbix_agentd.conf"
-    ''
-      Server = ${cfg.server}
+  user = "zabbix-agent";
+  group = "zabbix-agent";
 
-      LogFile = ${logDir}/zabbix_agentd
-
-      PidFile = ${pidFile}
-
-      StartAgents = 1
+  moduleEnv = pkgs.symlinkJoin {
+    name = "zabbix-agent-module-env";
+    paths = attrValues cfg.modules;
+  };
 
-      ${config.services.zabbixAgent.extraConfig}
-    '';
+  configFile = pkgs.writeText "zabbix_agent.conf" ''
+    LogType = console
+    Server = ${cfg.server}
+    ListenIP = ${cfg.listen.ip}
+    ListenPort = ${toString cfg.listen.port}
+    ${optionalString (cfg.modules != {}) "LoadModulePath = ${moduleEnv}/lib"}
+    ${concatMapStringsSep "\n" (name: "LoadModule = ${name}") (builtins.attrNames cfg.modules)}
+    ${cfg.extraConfig}
+  '';
 
 in
 
 {
-
-  ###### interface
+  # interface
 
   options = {
 
     services.zabbixAgent = {
+      enable = mkEnableOption "the Zabbix Agent";
 
-      enable = mkOption {
-        default = false;
+      package = mkOption {
+        type = types.package;
+        default = pkgs.zabbix.agent;
+        defaultText = "pkgs.zabbix.agent";
+        description = "The Zabbix package to use.";
+      };
+
+      extraPackages = mkOption {
+        type = types.listOf types.package;
+        default = with pkgs; [ nettools ];
+        defaultText = "[ nettools ]";
+        example = "[ nettools mysql ]";
         description = ''
-          Whether to run the Zabbix monitoring agent on this machine.
-          It will send monitoring data to a Zabbix server.
+          Packages to be added to the Zabbix <envar>PATH</envar>.
+          Typically used to add executables for scripts, but can be anything.
         '';
       };
 
-      package = mkOption {
-        type = types.attrs; # Note: pkgs.zabbixXY isn't a derivation, but an attrset of { server = ...; agent = ...; }.
-        default = pkgs.zabbix;
-        defaultText = "pkgs.zabbix";
-        example = literalExample "pkgs.zabbix34";
-        description = ''
-          The Zabbix package to use.
+      modules = mkOption {
+        type = types.attrsOf types.package;
+        description = "A set of modules to load.";
+        default = {};
+        example = literalExample ''
+          {
+            "dummy.so" = pkgs.stdenv.mkDerivation {
+              name = "zabbix-dummy-module-''${cfg.package.version}";
+              src = cfg.package.src;
+              buildInputs = [ cfg.package ];
+              sourceRoot = "zabbix-''${cfg.package.version}/src/modules/dummy";
+              installPhase = '''
+                mkdir -p $out/lib
+                cp dummy.so $out/lib/
+              ''';
+            };
+          }
         '';
       };
 
       server = mkOption {
-        default = "127.0.0.1";
+        type = types.str;
         description = ''
           The IP address or hostname of the Zabbix server to connect to.
         '';
       };
 
+      listen = {
+        ip = mkOption {
+          type = types.str;
+          default = "0.0.0.0";
+          description = ''
+            List of comma delimited IP addresses that the agent should listen on.
+          '';
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 10050;
+          description = ''
+            Agent will listen on this port for connections from the server.
+          '';
+        };
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open ports in the firewall for the Zabbix Agent.
+        '';
+      };
+
+      # TODO: for bonus points migrate this to https://github.com/NixOS/rfcs/pull/42
       extraConfig = mkOption {
         default = "";
         type = types.lines;
         description = ''
-          Configuration that is injected verbatim into the configuration file.
+          Configuration that is injected verbatim into the configuration file. Refer to
+          <link xlink:href="https://www.zabbix.com/documentation/current/manual/appendix/config/zabbix_agentd"/>
+          for details on supported values.
         '';
       };
 
@@ -75,38 +120,38 @@ in
 
   };
 
-
-  ###### implementation
+  # implementation
 
   config = mkIf cfg.enable {
 
-    users.users = mkIf (!config.services.zabbixServer.enable) (singleton
-      { name = "zabbix";
-        uid = config.ids.uids.zabbix;
-        description = "Zabbix daemon user";
-      });
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.listen.port ];
+    };
 
-    systemd.services."zabbix-agent" =
-      { description = "Zabbix Agent";
+    users.users.${user} = {
+      description = "Zabbix Agent daemon user";
+      inherit group;
+    };
 
-        wantedBy = [ "multi-user.target" ];
+    users.groups.${group} = { };
 
-        path = [ pkgs.nettools ];
+    systemd.services."zabbix-agent" = {
+      description = "Zabbix Agent";
 
-        preStart =
-          ''
-            mkdir -m 0755 -p ${stateDir} ${logDir}
-            chown zabbix ${stateDir} ${logDir}
-          '';
+      wantedBy = [ "multi-user.target" ];
 
-        serviceConfig.ExecStart = "@${zabbix.agent}/sbin/zabbix_agentd zabbix_agentd --config ${configFile}";
-        serviceConfig.Type = "forking";
-        serviceConfig.RemainAfterExit = true;
-        serviceConfig.Restart = "always";
-        serviceConfig.RestartSec = 2;
-      };
+      path = [ "/run/wrappers" ] ++ cfg.extraPackages;
+
+      serviceConfig = {
+        ExecStart = "@${cfg.package}/sbin/zabbix_agentd zabbix_agentd -f --config ${configFile}";
+        Restart = "always";
+        RestartSec = 2;
 
-    environment.systemPackages = [ zabbix.agent ];
+        User = user;
+        Group = group;
+        PrivateTmp = true;
+      };
+    };
 
   };
 
diff --git a/nixos/modules/services/monitoring/zabbix-proxy.nix b/nixos/modules/services/monitoring/zabbix-proxy.nix
new file mode 100644
index 00000000000..c1a45fba4af
--- /dev/null
+++ b/nixos/modules/services/monitoring/zabbix-proxy.nix
@@ -0,0 +1,290 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.zabbixProxy;
+  pgsql = config.services.postgresql;
+  mysql = config.services.mysql;
+
+  inherit (lib) mkDefault mkEnableOption mkIf mkOption;
+  inherit (lib) attrValues concatMapStringsSep literalExample optional optionalAttrs optionalString types;
+
+  user = "zabbix";
+  group = "zabbix";
+  runtimeDir = "/run/zabbix";
+  stateDir = "/var/lib/zabbix";
+  passwordFile = "${runtimeDir}/zabbix-dbpassword.conf";
+
+  moduleEnv = pkgs.symlinkJoin {
+    name = "zabbix-proxy-module-env";
+    paths = attrValues cfg.modules;
+  };
+
+  configFile = pkgs.writeText "zabbix_proxy.conf" ''
+    LogType = console
+    ListenIP = ${cfg.listen.ip}
+    ListenPort = ${toString cfg.listen.port}
+    # TODO: set to cfg.database.socket if database type is pgsql?
+    DBHost = ${optionalString (cfg.database.createLocally != true) cfg.database.host}
+    ${optionalString (cfg.database.createLocally != true) "DBPort = ${cfg.database.port}"}
+    DBName = ${cfg.database.name}
+    DBUser = ${cfg.database.user}
+    ${optionalString (cfg.database.passwordFile != null) "Include ${passwordFile}"}
+    ${optionalString (mysqlLocal && cfg.database.socket != null) "DBSocket = ${cfg.database.socket}"}
+    SocketDir = ${runtimeDir}
+    FpingLocation = /run/wrappers/bin/fping
+    ${optionalString (cfg.modules != {}) "LoadModulePath = ${moduleEnv}/lib"}
+    ${concatMapStringsSep "\n" (name: "LoadModule = ${name}") (builtins.attrNames cfg.modules)}
+    ${cfg.extraConfig}
+  '';
+
+  mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
+  pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
+
+in
+
+{
+  # interface
+
+  options = {
+
+    services.zabbixProxy = {
+      enable = mkEnableOption "the Zabbix Proxy";
+
+      package = mkOption {
+        type = types.package;
+        default =
+          if cfg.database.type == "mysql" then pkgs.zabbix.proxy-mysql
+          else if cfg.database.type == "pgsql" then pkgs.zabbix.proxy-pgsql
+          else pkgs.zabbix.proxy-sqlite;
+        defaultText = "pkgs.zabbix.proxy-pgsql";
+        description = "The Zabbix package to use.";
+      };
+
+      extraPackages = mkOption {
+        type = types.listOf types.package;
+        default = with pkgs; [ nettools nmap traceroute ];
+        defaultText = "[ nettools nmap traceroute ]";
+        description = ''
+          Packages to be added to the Zabbix <envar>PATH</envar>.
+          Typically used to add executables for scripts, but can be anything.
+        '';
+      };
+
+      modules = mkOption {
+        type = types.attrsOf types.package;
+        description = "A set of modules to load.";
+        default = {};
+        example = literalExample ''
+          {
+            "dummy.so" = pkgs.stdenv.mkDerivation {
+              name = "zabbix-dummy-module-''${cfg.package.version}";
+              src = cfg.package.src;
+              buildInputs = [ cfg.package ];
+              sourceRoot = "zabbix-''${cfg.package.version}/src/modules/dummy";
+              installPhase = '''
+                mkdir -p $out/lib
+                cp dummy.so $out/lib/
+              ''';
+            };
+          }
+        '';
+      };
+
+      database = {
+        type = mkOption {
+          type = types.enum [ "mysql" "pgsql" "sqlite" ];
+          example = "mysql";
+          default = "pgsql";
+          description = "Database engine to use.";
+        };
+
+        host = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = "Database host address.";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = if cfg.database.type == "mysql" then mysql.port else pgsql.port;
+          description = "Database host port.";
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "zabbix";
+          description = "Database name.";
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "zabbix";
+          description = "Database user.";
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/keys/zabbix-dbpassword";
+          description = ''
+            A file containing the password corresponding to
+            <option>database.user</option>.
+          '';
+        };
+
+        socket = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/postgresql";
+          description = "Path to the unix socket file to use for authentication.";
+        };
+
+        createLocally = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Whether to create a local database automatically.";
+        };
+      };
+
+      listen = {
+        ip = mkOption {
+          type = types.str;
+          default = "0.0.0.0";
+          description = ''
+            List of comma delimited IP addresses that the trapper should listen on.
+            Trapper will listen on all network interfaces if this parameter is missing.
+          '';
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 10051;
+          description = ''
+            Listen port for trapper.
+          '';
+        };
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open ports in the firewall for the Zabbix Proxy.
+        '';
+      };
+
+      # TODO: for bonus points migrate this to https://github.com/NixOS/rfcs/pull/42
+      extraConfig = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''
+          Configuration that is injected verbatim into the configuration file. Refer to
+          <link xlink:href="https://www.zabbix.com/documentation/current/manual/appendix/config/zabbix_proxy"/>
+          for details on supported values.
+        '';
+      };
+
+    };
+
+  };
+
+  # implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = !config.services.zabbixServer.enable;
+        message = "Please choose one of services.zabbixServer or services.zabbixProxy.";
+      }
+      { assertion = cfg.database.createLocally -> cfg.database.user == user;
+        message = "services.zabbixProxy.database.user must be set to ${user} if services.zabbixProxy.database.createLocally is set true";
+      }
+      { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
+        message = "a password cannot be specified if services.zabbixProxy.database.createLocally is set to true";
+      }
+    ];
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.listen.port ];
+    };
+
+    services.mysql = optionalAttrs mysqlLocal {
+      enable = true;
+      package = mkDefault pkgs.mariadb;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        { name = cfg.database.user;
+          ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
+        }
+      ];
+    };
+
+    services.postgresql = optionalAttrs pgsqlLocal {
+      enable = true;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        { name = cfg.database.user;
+          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+        }
+      ];
+    };
+
+    users.users.${user} = {
+      description = "Zabbix daemon user";
+      uid = config.ids.uids.zabbix;
+      inherit group;
+    };
+
+    users.groups.${group} = {
+      gid = config.ids.gids.zabbix;
+    };
+
+    security.wrappers = {
+      fping.source = "${pkgs.fping}/bin/fping";
+    };
+
+    systemd.services."zabbix-proxy" = {
+      description = "Zabbix Proxy";
+
+      wantedBy = [ "multi-user.target" ];
+      after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
+
+      path = [ "/run/wrappers" ] ++ cfg.extraPackages;
+      preStart = optionalString pgsqlLocal ''
+        if ! test -e "${stateDir}/db-created"; then
+          cat ${cfg.package}/share/zabbix/database/postgresql/schema.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/postgresql/images.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/postgresql/data.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+          touch "${stateDir}/db-created"
+        fi
+      '' + optionalString mysqlLocal ''
+        if ! test -e "${stateDir}/db-created"; then
+          cat ${cfg.package}/share/zabbix/database/mysql/schema.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/mysql/images.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/mysql/data.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+          touch "${stateDir}/db-created"
+        fi
+      '' + optionalString (cfg.database.passwordFile != null) ''
+        # create a copy of the supplied password file in a format zabbix can consume
+        touch ${passwordFile}
+        chmod 0600 ${passwordFile}
+        echo -n "DBPassword = " > ${passwordFile}
+        cat ${cfg.database.passwordFile} >> ${passwordFile}
+      '';
+
+      serviceConfig = {
+        ExecStart = "@${cfg.package}/sbin/zabbix_proxy zabbix_proxy -f --config ${configFile}";
+        Restart = "always";
+        RestartSec = 2;
+
+        User = user;
+        Group = group;
+        RuntimeDirectory = "zabbix";
+        StateDirectory = "zabbix";
+        PrivateTmp = true;
+      };
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/monitoring/zabbix-server.nix b/nixos/modules/services/monitoring/zabbix-server.nix
index fdeab6af441..11311b466c3 100644
--- a/nixos/modules/services/monitoring/zabbix-server.nix
+++ b/nixos/modules/services/monitoring/zabbix-server.nix
@@ -1,125 +1,292 @@
-# Zabbix server daemon.
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
-
   cfg = config.services.zabbixServer;
+  pgsql = config.services.postgresql;
+  mysql = config.services.mysql;
 
-  stateDir = "/run/zabbix";
+  inherit (lib) mkDefault mkEnableOption mkIf mkOption;
+  inherit (lib) attrValues concatMapStringsSep literalExample optional optionalAttrs optionalString types;
 
-  logDir = "/var/log/zabbix";
+  user = "zabbix";
+  group = "zabbix";
+  runtimeDir = "/run/zabbix";
+  stateDir = "/var/lib/zabbix";
+  passwordFile = "${runtimeDir}/zabbix-dbpassword.conf";
 
-  libDir = "/var/lib/zabbix";
+  moduleEnv = pkgs.symlinkJoin {
+    name = "zabbix-server-module-env";
+    paths = attrValues cfg.modules;
+  };
 
-  pidFile = "${stateDir}/zabbix_server.pid";
+  configFile = pkgs.writeText "zabbix_server.conf" ''
+    LogType = console
+    ListenIP = ${cfg.listen.ip}
+    ListenPort = ${toString cfg.listen.port}
+    # TODO: set to cfg.database.socket if database type is pgsql?
+    DBHost = ${optionalString (cfg.database.createLocally != true) cfg.database.host}
+    ${optionalString (cfg.database.createLocally != true) "DBPort = ${cfg.database.port}"}
+    DBName = ${cfg.database.name}
+    DBUser = ${cfg.database.user}
+    ${optionalString (cfg.database.passwordFile != null) "Include ${passwordFile}"}
+    ${optionalString (mysqlLocal && cfg.database.socket != null) "DBSocket = ${cfg.database.socket}"}
+    SocketDir = ${runtimeDir}
+    FpingLocation = /run/wrappers/bin/fping
+    ${optionalString (cfg.modules != {}) "LoadModulePath = ${moduleEnv}/lib"}
+    ${concatMapStringsSep "\n" (name: "LoadModule = ${name}") (builtins.attrNames cfg.modules)}
+    ${cfg.extraConfig}
+  '';
+
+  mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
+  pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
 
-  configFile = pkgs.writeText "zabbix_server.conf"
-    ''
-      LogFile = ${logDir}/zabbix_server
+in
 
-      PidFile = ${pidFile}
+{
+  # interface
 
-      ${optionalString (cfg.dbServer != "localhost") ''
-        DBHost = ${cfg.dbServer}
-      ''}
+  options = {
 
-      DBName = zabbix
+    services.zabbixServer = {
+      enable = mkEnableOption "the Zabbix Server";
 
-      DBUser = zabbix
+      package = mkOption {
+        type = types.package;
+        default = if cfg.database.type == "mysql" then pkgs.zabbix.server-mysql else pkgs.zabbix.server-pgsql;
+        defaultText = "pkgs.zabbix.server-pgsql";
+        description = "The Zabbix package to use.";
+      };
 
-      ${optionalString (cfg.dbPassword != "") ''
-        DBPassword = ${cfg.dbPassword}
-      ''}
+      extraPackages = mkOption {
+        type = types.listOf types.package;
+        default = with pkgs; [ nettools nmap traceroute ];
+        defaultText = "[ nettools nmap traceroute ]";
+        description = ''
+          Packages to be added to the Zabbix <envar>PATH</envar>.
+          Typically used to add executables for scripts, but can be anything.
+        '';
+      };
 
-      ${config.services.zabbixServer.extraConfig}
-    '';
+      modules = mkOption {
+        type = types.attrsOf types.package;
+        description = "A set of modules to load.";
+        default = {};
+        example = literalExample ''
+          {
+            "dummy.so" = pkgs.stdenv.mkDerivation {
+              name = "zabbix-dummy-module-''${cfg.package.version}";
+              src = cfg.package.src;
+              buildInputs = [ cfg.package ];
+              sourceRoot = "zabbix-''${cfg.package.version}/src/modules/dummy";
+              installPhase = '''
+                mkdir -p $out/lib
+                cp dummy.so $out/lib/
+              ''';
+            };
+          }
+        '';
+      };
 
-  useLocalPostgres = cfg.dbServer == "localhost" || cfg.dbServer == "";
+      database = {
+        type = mkOption {
+          type = types.enum [ "mysql" "pgsql" ];
+          example = "mysql";
+          default = "pgsql";
+          description = "Database engine to use.";
+        };
+
+        host = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = "Database host address.";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = if cfg.database.type == "mysql" then mysql.port else pgsql.port;
+          description = "Database host port.";
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "zabbix";
+          description = "Database name.";
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "zabbix";
+          description = "Database user.";
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/keys/zabbix-dbpassword";
+          description = ''
+            A file containing the password corresponding to
+            <option>database.user</option>.
+          '';
+        };
+
+        socket = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/postgresql";
+          description = "Path to the unix socket file to use for authentication.";
+        };
+
+        createLocally = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Whether to create a local database automatically.";
+        };
+      };
 
-in
+      listen = {
+        ip = mkOption {
+          type = types.str;
+          default = "0.0.0.0";
+          description = ''
+            List of comma delimited IP addresses that the trapper should listen on.
+            Trapper will listen on all network interfaces if this parameter is missing.
+          '';
+        };
 
-{
+        port = mkOption {
+          type = types.port;
+          default = 10051;
+          description = ''
+            Listen port for trapper.
+          '';
+        };
+      };
 
-  ###### interface
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open ports in the firewall for the Zabbix Server.
+        '';
+      };
 
-  options = {
+      # TODO: for bonus points migrate this to https://github.com/NixOS/rfcs/pull/42
+      extraConfig = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''
+          Configuration that is injected verbatim into the configuration file. Refer to
+          <link xlink:href="https://www.zabbix.com/documentation/current/manual/appendix/config/zabbix_server"/>
+          for details on supported values.
+        '';
+      };
 
-    services.zabbixServer.enable = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        Whether to run the Zabbix server on this machine.
-      '';
     };
 
-    services.zabbixServer.dbServer = mkOption {
-      default = "localhost";
-      type = types.str;
-      description = ''
-        Hostname or IP address of the database server.
-        Use an empty string ("") to use peer authentication.
-      '';
-    };
+  };
 
-    services.zabbixServer.dbPassword = mkOption {
-      default = "";
-      type = types.str;
-      description = "Password used to connect to the database server.";
-    };
+  # implementation
 
-    services.zabbixServer.extraConfig = mkOption {
-      default = "";
-      type = types.lines;
-      description = ''
-        Configuration that is injected verbatim into the configuration file.
-      '';
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = cfg.database.createLocally -> cfg.database.user == user;
+        message = "services.zabbixServer.database.user must be set to ${user} if services.zabbixServer.database.createLocally is set true";
+      }
+      { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
+        message = "a password cannot be specified if services.zabbixServer.database.createLocally is set to true";
+      }
+    ];
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.listen.port ];
     };
 
-  };
+    services.mysql = optionalAttrs mysqlLocal {
+      enable = true;
+      package = mkDefault pkgs.mariadb;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        { name = cfg.database.user;
+          ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
+        }
+      ];
+    };
 
-  ###### implementation
+    services.postgresql = optionalAttrs pgsqlLocal {
+      enable = true;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        { name = cfg.database.user;
+          ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+        }
+      ];
+    };
 
-  config = mkIf cfg.enable {
+    users.users.${user} = {
+      description = "Zabbix daemon user";
+      uid = config.ids.uids.zabbix;
+      inherit group;
+    };
 
-    services.postgresql.enable = useLocalPostgres;
+    users.groups.${group} = {
+      gid = config.ids.gids.zabbix;
+    };
 
-    users.users = singleton
-      { name = "zabbix";
-        uid = config.ids.uids.zabbix;
-        description = "Zabbix daemon user";
-      };
+    security.wrappers = {
+      fping.source = "${pkgs.fping}/bin/fping";
+    };
 
-    systemd.services."zabbix-server" =
-      { description = "Zabbix Server";
-
-        wantedBy = [ "multi-user.target" ];
-        after = optional useLocalPostgres "postgresql.service";
-
-        preStart =
-          ''
-            mkdir -m 0755 -p ${stateDir} ${logDir} ${libDir}
-            chown zabbix ${stateDir} ${logDir} ${libDir}
-
-            if ! test -e "${libDir}/db-created"; then
-                ${pkgs.su}/bin/su -s "$SHELL" ${config.services.postgresql.superUser} -c '${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole zabbix' || true
-                ${pkgs.su}/bin/su -s "$SHELL" ${config.services.postgresql.superUser} -c '${pkgs.postgresql}/bin/createdb --owner zabbix zabbix' || true
-                cat ${pkgs.zabbix.server}/share/zabbix/db/schema/postgresql.sql | ${pkgs.su}/bin/su -s "$SHELL" zabbix -c '${pkgs.postgresql}/bin/psql zabbix'
-                cat ${pkgs.zabbix.server}/share/zabbix/db/data/images_pgsql.sql | ${pkgs.su}/bin/su -s "$SHELL" zabbix -c '${pkgs.postgresql}/bin/psql zabbix'
-                cat ${pkgs.zabbix.server}/share/zabbix/db/data/data.sql | ${pkgs.su}/bin/su -s "$SHELL" zabbix -c '${pkgs.postgresql}/bin/psql zabbix'
-                touch "${libDir}/db-created"
-            fi
-          '';
+    systemd.services."zabbix-server" = {
+      description = "Zabbix Server";
+
+      wantedBy = [ "multi-user.target" ];
+      after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
+
+      path = [ "/run/wrappers" ] ++ cfg.extraPackages;
+      preStart = ''
+        # pre 19.09 compatibility
+        if test -e "${runtimeDir}/db-created"; then
+          mv "${runtimeDir}/db-created" "${stateDir}/"
+        fi
+      '' + optionalString pgsqlLocal ''
+        if ! test -e "${stateDir}/db-created"; then
+          cat ${cfg.package}/share/zabbix/database/postgresql/schema.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/postgresql/images.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/postgresql/data.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+          touch "${stateDir}/db-created"
+        fi
+      '' + optionalString mysqlLocal ''
+        if ! test -e "${stateDir}/db-created"; then
+          cat ${cfg.package}/share/zabbix/database/mysql/schema.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/mysql/images.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+          cat ${cfg.package}/share/zabbix/database/mysql/data.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+          touch "${stateDir}/db-created"
+        fi
+      '' + optionalString (cfg.database.passwordFile != null) ''
+        # create a copy of the supplied password file in a format zabbix can consume
+        touch ${passwordFile}
+        chmod 0600 ${passwordFile}
+        echo -n "DBPassword = " > ${passwordFile}
+        cat ${cfg.database.passwordFile} >> ${passwordFile}
+      '';
 
-        path = [ pkgs.nettools ];
+      serviceConfig = {
+        ExecStart = "@${cfg.package}/sbin/zabbix_server zabbix_server -f --config ${configFile}";
+        Restart = "always";
+        RestartSec = 2;
 
-        serviceConfig.ExecStart = "@${pkgs.zabbix.server}/sbin/zabbix_server zabbix_server --config ${configFile}";
-        serviceConfig.Type = "forking";
-        serviceConfig.Restart = "always";
-        serviceConfig.RestartSec = 2;
-        serviceConfig.PIDFile = pidFile;
+        User = user;
+        Group = group;
+        RuntimeDirectory = "zabbix";
+        StateDirectory = "zabbix";
+        PrivateTmp = true;
       };
+    };
+
+    systemd.services.httpd.after =
+      optional (config.services.zabbixWeb.enable && mysqlLocal) "mysql.service" ++
+      optional (config.services.zabbixWeb.enable && pgsqlLocal) "postgresql.service";
 
   };
 
diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
index c217ccaa405..7b278603455 100644
--- a/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -162,7 +162,7 @@ in
 
         wantedBy = [ "multi-user.target" ] ++ optional (!hasDefaultGatewaySet) "network-online.target";
         wants = [ "network.target" "systemd-udev-settle.service" ];
-        before = [ "network.target" ];
+        before = [ "network-online.target" ];
         after = [ "systemd-udev-settle.service" ];
 
         # Stopping dhcpcd during a reconfiguration is undesirable
diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix
index ca34ff9df4e..fc516c01230 100644
--- a/nixos/modules/services/networking/kresd.nix
+++ b/nixos/modules/services/networking/kresd.nix
@@ -80,8 +80,11 @@ in
         # Syntax depends on being IPv6 or IPv4.
         (iface: if elem ":" (stringToCharacters iface) then "[${iface}]:53" else "${iface}:53")
         cfg.interfaces;
-      socketConfig.ListenDatagram = listenStreams;
-      socketConfig.FreeBind = true;
+      socketConfig = {
+        ListenDatagram = listenStreams;
+        FreeBind = true;
+        FileDescriptorName = "dns";
+      };
     };
 
     systemd.sockets.kresd-tls = mkIf (cfg.listenTLS != []) rec {
diff --git a/nixos/modules/services/torrent/deluge.nix b/nixos/modules/services/torrent/deluge.nix
index 01a5890a784..48ec4d692e2 100644
--- a/nixos/modules/services/torrent/deluge.nix
+++ b/nixos/modules/services/torrent/deluge.nix
@@ -118,36 +118,74 @@ in {
             more informations.
           '';
         };
+
+        user = mkOption {
+          type = types.str;
+          default = "deluge";
+          description = ''
+            User account under which deluge runs.
+          '';
+        };
+
+        group = mkOption {
+          type = types.str;
+          default = "deluge";
+          description = ''
+            Group under which deluge runs.
+          '';
+        };
+
+        extraPackages = mkOption {
+          type = types.listOf types.package;
+          default = [];
+          description = ''
+            Extra packages available at runtime to enable Deluge's plugins. For example,
+            extraction utilities are required for the built-in "Extractor" plugin.
+            This always contains unzip, gnutar, xz, p7zip and bzip2.
+          '';
+        };
       };
 
       deluge.web = {
         enable = mkEnableOption "Deluge Web daemon";
+
         port = mkOption {
-        type = types.port;
+          type = types.port;
           default = 8112;
           description = ''
             Deluge web UI port.
           '';
         };
+
+        openFirewall = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Open ports in the firewall for deluge web daemon
+          '';
+        };
       };
     };
   };
 
   config = mkIf cfg.enable {
 
-    systemd.tmpfiles.rules = [ "d '${configDir}' 0770 deluge deluge" ]
+    # Provide a default set of `extraPackages`.
+    services.deluge.extraPackages = with pkgs; [ unzip gnutar xz p7zip bzip2 ];
+
+    systemd.tmpfiles.rules = [ "d '${configDir}' 0770 ${cfg.user} ${cfg.group}" ]
     ++ optional (cfg.config ? "download_location")
-      "d '${cfg.config.download_location}' 0770 deluge deluge"
+      "d '${cfg.config.download_location}' 0770 ${cfg.user} ${cfg.group}"
     ++ optional (cfg.config ? "torrentfiles_location")
-      "d '${cfg.config.torrentfiles_location}' 0770 deluge deluge"
+      "d '${cfg.config.torrentfiles_location}' 0770 ${cfg.user} ${cfg.group}"
     ++ optional (cfg.config ? "move_completed_path")
-      "d '${cfg.config.move_completed_path}' 0770 deluge deluge";
+      "d '${cfg.config.move_completed_path}' 0770 ${cfg.user} ${cfg.group}";
 
     systemd.services.deluged = {
       after = [ "network.target" ];
       description = "Deluge BitTorrent Daemon";
       wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.deluge ];
+      path = [ pkgs.deluge ] ++ cfg.extraPackages;
       serviceConfig = {
         ExecStart = ''
           ${pkgs.deluge}/bin/deluged \
@@ -157,8 +195,8 @@ in {
         # To prevent "Quit & shutdown daemon" from working; we want systemd to
         # manage it!
         Restart = "on-success";
-        User = "deluge";
-        Group = "deluge";
+        User = cfg.user;
+        Group = cfg.group;
         UMask = "0002";
         LimitNOFILE = cfg.openFilesLimit;
       };
@@ -177,26 +215,37 @@ in {
             --config ${configDir} \
             --port ${toString cfg.web.port}
         '';
-        User = "deluge";
-        Group = "deluge";
+        User = cfg.user;
+        Group = cfg.group;
       };
     };
 
-    networking.firewall = mkIf (cfg.declarative && cfg.openFirewall && !(cfg.config.random_port or true)) {
-      allowedTCPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
-      allowedUDPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
-    };
+    networking.firewall = mkMerge [
+      (mkIf (cfg.declarative && cfg.openFirewall && !(cfg.config.random_port or true)) {
+        allowedTCPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
+        allowedUDPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
+      })
+      (mkIf (cfg.web.openFirewall) {
+        allowedTCPPorts = [ cfg.web.port ];
+      })
+    ];
 
     environment.systemPackages = [ pkgs.deluge ];
 
-    users.users.deluge = {
-      group = "deluge";
-      uid = config.ids.uids.deluge;
-      home = cfg.dataDir;
-      createHome = true;
-      description = "Deluge Daemon user";
+    users.users = mkIf (cfg.user == "deluge") {
+      deluge = {
+        group = cfg.group;
+        uid = config.ids.uids.deluge;
+        home = cfg.dataDir;
+        createHome = true;
+        description = "Deluge Daemon user";
+      };
     };
 
-    users.groups.deluge.gid = config.ids.gids.deluge;
+    users.groups = mkIf (cfg.group == "deluge") {
+      deluge = {
+        gid = config.ids.gids.deluge;
+      };
+    };
   };
 }
diff --git a/nixos/modules/services/web-apps/zabbix.nix b/nixos/modules/services/web-apps/zabbix.nix
new file mode 100644
index 00000000000..4b5334579a9
--- /dev/null
+++ b/nixos/modules/services/web-apps/zabbix.nix
@@ -0,0 +1,225 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption types;
+  inherit (lib) literalExample mapAttrs optionalString;
+
+  cfg = config.services.zabbixWeb;
+  fpm = config.services.phpfpm.pools.zabbix;
+
+  user = "zabbix";
+  group = "zabbix";
+  stateDir = "/var/lib/zabbix";
+
+  zabbixConfig = pkgs.writeText "zabbix.conf.php" ''
+    <?php
+    // Zabbix GUI configuration file.
+    global $DB;
+    $DB['TYPE'] = '${ { "mysql" = "MYSQL"; "pgsql" = "POSTGRESQL"; "oracle" = "ORACLE"; }.${cfg.database.type} }';
+    $DB['SERVER'] = '${cfg.database.host}';
+    $DB['PORT'] = '${toString cfg.database.port}';
+    $DB['DATABASE'] = '${cfg.database.name}';
+    $DB['USER'] = '${cfg.database.user}';
+    $DB['PASSWORD'] = ${if cfg.database.passwordFile != null then "file_get_contents('${cfg.database.passwordFile}')" else "''"};
+    // Schema name. Used for IBM DB2 and PostgreSQL.
+    $DB['SCHEMA'] = ''';
+    $ZBX_SERVER = '${cfg.server.address}';
+    $ZBX_SERVER_PORT = '${toString cfg.server.port}';
+    $ZBX_SERVER_NAME = ''';
+    $IMAGE_FORMAT_DEFAULT = IMAGE_FORMAT_PNG;
+  '';
+
+in
+{
+  # interface
+
+  options.services = {
+    zabbixWeb = {
+      enable = mkEnableOption "the Zabbix web interface";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.zabbix.web;
+        defaultText = "zabbix.web";
+        description = "Which Zabbix package to use.";
+      };
+
+      server = {
+        port = mkOption {
+          type = types.int;
+          description = "The port of the Zabbix server to connect to.";
+          default = 10051;
+        };
+
+        address = mkOption {
+          type = types.str;
+          description = "The IP address or hostname of the Zabbix server to connect to.";
+          default = "localhost";
+        };
+      };
+
+      database = {
+        type = mkOption {
+          type = types.enum [ "mysql" "pgsql" "oracle" ];
+          example = "mysql";
+          default = "pgsql";
+          description = "Database engine to use.";
+        };
+
+        host = mkOption {
+          type = types.str;
+          default = "";
+          description = "Database host address.";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default =
+            if cfg.database.type == "mysql" then config.services.mysql.port
+            else if cfg.database.type == "pgsql" then config.services.postgresql.port
+            else 1521;
+          description = "Database host port.";
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "zabbix";
+          description = "Database name.";
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "zabbix";
+          description = "Database user.";
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/keys/zabbix-dbpassword";
+          description = ''
+            A file containing the password corresponding to
+            <option>database.user</option>.
+          '';
+        };
+
+        socket = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/postgresql";
+          description = "Path to the unix socket file to use for authentication.";
+        };
+      };
+
+      virtualHost = mkOption {
+        type = types.submodule ({
+          options = import ../web-servers/apache-httpd/per-server-options.nix {
+            inherit lib;
+            forMainServer = false;
+          };
+        });
+        example = {
+          hostName = "zabbix.example.org";
+          enableSSL = true;
+          adminAddr = "webmaster@example.org";
+          sslServerCert = "/var/lib/acme/zabbix.example.org/full.pem";
+          sslServerKey = "/var/lib/acme/zabbix.example.org/key.pem";
+        };
+        description = ''
+          Apache configuration can be done by adapting <literal>services.httpd.virtualHosts.&lt;name&gt;</literal>.
+          See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
+        '';
+      };
+
+      poolConfig = mkOption {
+        type = types.lines;
+        default = ''
+          pm = dynamic
+          pm.max_children = 32
+          pm.start_servers = 2
+          pm.min_spare_servers = 2
+          pm.max_spare_servers = 4
+          pm.max_requests = 500
+        '';
+        description = ''
+          Options for the Zabbix PHP pool. See the documentation on <literal>php-fpm.conf</literal> for details on configuration directives.
+        '';
+      };
+
+    };
+  };
+
+  # implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.tmpfiles.rules = [
+      "d '${stateDir}' 0750 ${user} ${group} - -"
+      "d '${stateDir}/session' 0750 ${user} ${config.services.httpd.group} - -"
+    ];
+
+    services.phpfpm.pools.zabbix = {
+      phpOptions = ''
+        # https://www.zabbix.com/documentation/current/manual/installation/install
+        memory_limit = 128M
+        post_max_size = 16M
+        upload_max_filesize = 2M
+        max_execution_time = 300
+        max_input_time = 300
+        session.auto_start = 0
+        mbstring.func_overload = 0
+        always_populate_raw_post_data = -1
+        # https://bbs.archlinux.org/viewtopic.php?pid=1745214#p1745214
+        session.save_path = ${stateDir}/session
+      '' + optionalString (config.time.timeZone != null) ''
+        date.timezone = "${config.time.timeZone}"
+      '' + optionalString (cfg.database.type == "oracle") ''
+        extension=${pkgs.phpPackages.oci8}/lib/php/extensions/oci8.so
+      '';
+      listen = "/run/phpfpm/zabbix.sock";
+      extraConfig = ''
+        listen.owner = ${config.services.httpd.user};
+        listen.group = ${config.services.httpd.group};
+        user = ${user};
+        group = ${config.services.httpd.group};
+        env[ZABBIX_CONFIG] = ${zabbixConfig}
+        ${cfg.poolConfig}
+      '';
+    };
+
+    services.httpd = {
+      enable = true;
+      adminAddr = mkDefault cfg.virtualHost.adminAddr;
+      extraModules = [ "proxy_fcgi" ];
+      virtualHosts = [ (mkMerge [
+        cfg.virtualHost {
+          documentRoot = mkForce "${cfg.package}/share/zabbix";
+          extraConfig = ''
+            <Directory "${cfg.package}/share/zabbix">
+              <FilesMatch "\.php$">
+                <If "-f %{REQUEST_FILENAME}">
+                  SetHandler "proxy:unix:${fpm.listen}|fcgi://localhost/"
+                </If>
+              </FilesMatch>
+              AllowOverride all
+              Options -Indexes
+              DirectoryIndex index.php
+            </Directory>
+          '';
+        }
+      ]) ];
+    };
+
+    users.users.${user} = mapAttrs (name: mkDefault) {
+      description = "Zabbix daemon user";
+      uid = config.ids.uids.zabbix;
+      inherit group;
+    };
+
+    users.groups.${group} = mapAttrs (name: mkDefault) {
+      gid = config.ids.gids.zabbix;
+    };
+
+  };
+}
diff --git a/nixos/modules/services/web-servers/apache-httpd/foswiki.nix b/nixos/modules/services/web-servers/apache-httpd/foswiki.nix
deleted file mode 100644
index 8c1ac8935a4..00000000000
--- a/nixos/modules/services/web-servers/apache-httpd/foswiki.nix
+++ /dev/null
@@ -1,78 +0,0 @@
-{ config, pkgs, lib, serverInfo, ... }:
-let
-  inherit (pkgs) foswiki;
-  inherit (serverInfo.serverConfig) user group;
-  inherit (config) vardir;
-in
-{
-  options.vardir = lib.mkOption {
-    type = lib.types.path;
-    default = "/var/www/foswiki";
-    description = "The directory where variable foswiki data will be stored and served from.";
-  };
-
-  # TODO: this will probably need to be better customizable
-  extraConfig =
-    let httpd-conf = pkgs.runCommand "foswiki-httpd.conf"
-      { preferLocalBuild = true; }
-      ''
-        substitute '${foswiki}/foswiki_httpd_conf.txt' "$out" \
-          --replace /var/www/foswiki/ "${vardir}/"
-      '';
-    in
-      ''
-        RewriteEngine on
-        RewriteRule /foswiki/(.*) ${vardir}/$1
-
-        <Directory "${vardir}">
-          Require all granted
-        </Directory>
-
-        Include ${httpd-conf}
-        <Directory "${vardir}/pub">
-          Options FollowSymlinks
-        </Directory>
-      '';
-
-  /** This handles initial setup and updates.
-      It will probably need some tweaking, maybe per-site.  */
-  startupScript = pkgs.writeScript "foswiki_startup.sh" (
-    let storeLink = "${vardir}/package"; in
-    ''
-      [ -e '${storeLink}' ] || needs_setup=1
-      mkdir -p '${vardir}'
-      cd '${vardir}'
-      ln -sf -T '${foswiki}' '${storeLink}'
-
-      if [ -n "$needs_setup" ]; then # do initial setup
-        mkdir -p bin lib
-        # setup most of data/ as copies only
-        cp -r '${foswiki}'/data '${vardir}/'
-        rm -r '${vardir}'/data/{System,mime.types}
-        ln -sr -t '${vardir}/data/' '${storeLink}'/data/{System,mime.types}
-
-        ln -sr '${storeLink}/locale' .
-
-        mkdir pub
-        ln -sr '${storeLink}/pub/System' pub/
-
-        mkdir templates
-        ln -sr '${storeLink}'/templates/* templates/
-
-        ln -sr '${storeLink}/tools' .
-
-        mkdir -p '${vardir}'/working/{logs,tmp}
-        ln -sr '${storeLink}/working/README' working/ # used to check dir validity
-
-        chown -R '${user}:${group}' .
-        chmod +w -R .
-      fi
-
-      # bin/* and lib/* shall always be overwritten, in case files are added
-      ln -srf '${storeLink}'/bin/* '${vardir}/bin/'
-      ln -srf '${storeLink}'/lib/* '${vardir}/lib/'
-    ''
-    /* Symlinking bin/ one-by-one ensures that ${vardir}/lib/LocalSite.cfg
-        is used instead of ${foswiki}/... */
-  );
-}
diff --git a/nixos/modules/services/web-servers/apache-httpd/trac.nix b/nixos/modules/services/web-servers/apache-httpd/trac.nix
deleted file mode 100644
index 28b411a64b6..00000000000
--- a/nixos/modules/services/web-servers/apache-httpd/trac.nix
+++ /dev/null
@@ -1,121 +0,0 @@
-{ config, lib, pkgs, serverInfo, ... }:
-
-with lib;
-
-let
-
-  # Build a Subversion instance with Apache modules and Swig/Python bindings.
-  subversion = pkgs.subversion.override {
-    bdbSupport = true;
-    httpServer = true;
-    pythonBindings = true;
-    apacheHttpd = httpd;
-  };
-
-  httpd = serverInfo.serverConfig.package;
-
-  versionPre24 = versionOlder httpd.version "2.4";
-
-in
-
-{
-
-  options = {
-
-    projectsLocation = mkOption {
-      description = "URL path in which Trac projects can be accessed";
-      default = "/projects";
-    };
-
-    projects = mkOption {
-      description = "List of projects that should be provided by Trac. If they are not defined yet empty projects are created.";
-      default = [];
-      example =
-        [ { identifier = "myproject";
-            name = "My Project";
-            databaseURL="postgres://root:password@/tracdb";
-            subversionRepository="/data/subversion/myproject";
-          }
-        ];
-    };
-
-    user = mkOption {
-      default = "wwwrun";
-      description = "User account under which Trac runs.";
-    };
-
-    group = mkOption {
-      default = "wwwrun";
-      description = "Group under which Trac runs.";
-    };
-
-    ldapAuthentication = {
-      enable = mkOption {
-        default = false;
-        description = "Enable the ldap authentication in trac";
-      };
-
-      url = mkOption {
-        default = "ldap://127.0.0.1/dc=example,dc=co,dc=ke?uid?sub?(objectClass=inetOrgPerson)";
-        description = "URL of the LDAP authentication";
-      };
-
-      name = mkOption {
-        default = "Trac server";
-        description = "AuthName";
-      };
-    };
-
-  };
-
-  extraModules = singleton
-    { name = "python"; path = "${pkgs.mod_python}/modules/mod_python.so"; };
-
-  extraConfig = ''
-    <Location ${config.projectsLocation}>
-      SetHandler mod_python
-      PythonHandler trac.web.modpython_frontend
-      PythonOption TracEnvParentDir /var/trac/projects
-      PythonOption TracUriRoot ${config.projectsLocation}
-      PythonOption PYTHON_EGG_CACHE /var/trac/egg-cache
-    </Location>
-    ${if config.ldapAuthentication.enable then ''
-      <LocationMatch "^${config.projectsLocation}[^/]+/login$">
-        AuthType Basic
-        AuthName "${config.ldapAuthentication.name}"
-        AuthBasicProvider "ldap"
-        AuthLDAPURL "${config.ldapAuthentication.url}"
-        ${if versionPre24 then "authzldapauthoritative Off" else ""}
-        require valid-user
-      </LocationMatch>
-    '' else ""}
-  '';
-
-  globalEnvVars = singleton
-    { name = "PYTHONPATH";
-      value =
-        makeSearchPathOutput "lib" "lib/${pkgs.python.libPrefix}/site-packages"
-          [ pkgs.mod_python
-            pkgs.pythonPackages.trac
-            pkgs.pythonPackages.setuptools
-            pkgs.pythonPackages.genshi
-            pkgs.pythonPackages.psycopg2
-            subversion
-          ];
-    };
-
-  startupScript = pkgs.writeScript "activateTrac" ''
-    mkdir -p /var/trac
-    chown ${config.user}:${config.group} /var/trac
-
-    ${concatMapStrings (project:
-      ''
-        if [ ! -d /var/trac/${project.identifier} ]
-        then
-            export PYTHONPATH=${pkgs.pythonPackages.psycopg2}/lib/${pkgs.python.libPrefix}/site-packages
-            ${pkgs.pythonPackages.trac}/bin/trac-admin /var/trac/${project.identifier} initenv "${project.name}" "${project.databaseURL}" svn "${project.subversionRepository}"
-        fi
-      '' ) (config.projects)}
-  '';
-
-}
diff --git a/nixos/modules/services/web-servers/apache-httpd/zabbix.nix b/nixos/modules/services/web-servers/apache-httpd/zabbix.nix
deleted file mode 100644
index cab16593bcb..00000000000
--- a/nixos/modules/services/web-servers/apache-httpd/zabbix.nix
+++ /dev/null
@@ -1,84 +0,0 @@
-{ config, lib, pkgs, serverInfo, ... }:
-
-with lib;
-
-let
-
-  # The Zabbix PHP frontend needs to be able to write its
-  # configuration settings (the connection info to the database) to
-  # the "conf" subdirectory.  So symlink $out/conf to some directory
-  # outside of the Nix store where we want to keep this stateful info.
-  # Note that different instances of the frontend will therefore end
-  # up with their own copies of the PHP sources.  !!! Alternatively,
-  # we could generate zabbix.conf.php declaratively.
-  zabbixPHP = pkgs.runCommand "${pkgs.zabbix.server.name}-php" {}
-    ''
-      cp -rs ${pkgs.zabbix.server}/share/zabbix/php "$out"
-      chmod -R u+w $out
-      ln -s "${if config.configFile == null
-               then "${config.stateDir}/zabbix.conf.php"
-               else config.configFile}" "$out/conf/zabbix.conf.php"
-    '';
-
-in
-
-{
-
-  enablePHP = true;
-
-  phpOptions =
-    ''
-      post_max_size = 32M
-      max_execution_time = 300
-      max_input_time = 300
-    '';
-
-  extraConfig = ''
-    Alias ${config.urlPrefix}/ ${zabbixPHP}/
-
-    <Directory ${zabbixPHP}>
-      DirectoryIndex index.php
-      Order deny,allow
-      Allow from *
-    </Directory>
-  '';
-
-  startupScript = pkgs.writeScript "zabbix-startup-hook" ''
-    mkdir -p ${config.stateDir}
-    chown -R ${serverInfo.serverConfig.user} ${config.stateDir}
-  '';
-
-  # The frontend needs "ps" to find out whether zabbix_server is running.
-  extraServerPath = [ pkgs.procps ];
-
-  options = {
-
-    urlPrefix = mkOption {
-      default = "/zabbix";
-      description = "
-        The URL prefix under which the Zabbix service appears.
-        Use the empty string to have it appear in the server root.
-      ";
-    };
-
-    configFile = mkOption {
-      default = null;
-      type = types.nullOr types.path;
-      description = ''
-        The configuration file (zabbix.conf.php) which contains the database
-        connection settings. If not set, the configuration settings will created
-        by the web installer.
-      '';
-    };
-
-    stateDir = mkOption {
-      default = "/var/lib/zabbix/frontend";
-      description = "
-        Directory where the dynamically generated configuration data
-        of the PHP frontend will be stored.
-      ";
-    };
-
-  };
-
-}
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index a1ed2fd1e97..82730c5e80c 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -662,10 +662,9 @@ in
         restartIfChanged = false;
 
         environment =
-          {
-            LD_LIBRARY_PATH = concatStringsSep ":" ([ "/run/opengl-driver/lib" ]
-              ++ concatLists (catAttrs "libPath" cfg.drivers));
-          } // cfg.displayManager.job.environment;
+          optionalAttrs config.hardware.opengl.setLdLibraryPath
+            { LD_LIBRARY_PATH = pkgs.addOpenGLRunpath.driverLink; }
+          // cfg.displayManager.job.environment;
 
         preStart =
           ''
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index a36b3c180eb..a09c5dc4761 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -407,6 +407,29 @@ addEntry("NixOS - Default", $defaultConfig);
 
 $conf .= "$extraEntries\n" unless $extraEntriesBeforeNixOS;
 
+# Find all the children of the current default configuration
+# Do not search for grand children
+my @links = sort (glob "$defaultConfig/fine-tune/*");
+foreach my $link (@links) {
+
+    my $entryName = "";
+
+    my $cfgName = readFile("$link/configuration-name");
+
+    my $date = strftime("%F", localtime(lstat($link)->mtime));
+    my $version =
+        -e "$link/nixos-version"
+        ? readFile("$link/nixos-version")
+        : basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]);
+
+    if ($cfgName) {
+        $entryName = $cfgName;
+    } else {
+        $entryName = "($date - $version)";
+    }
+    addEntry("NixOS - $entryName", $link);
+}
+
 my $grubBootPath = $grubBoot->path;
 # extraEntries could refer to @bootRoot@, which we have to substitute
 $conf =~ s/\@bootRoot\@/$grubBootPath/g;
diff --git a/nixos/modules/tasks/auto-upgrade.nix b/nixos/modules/tasks/auto-upgrade.nix
index 91f4ae79ee9..18753ae0c1a 100644
--- a/nixos/modules/tasks/auto-upgrade.nix
+++ b/nixos/modules/tasks/auto-upgrade.nix
@@ -53,6 +53,16 @@ let cfg = config.system.autoUpgrade; in
         '';
       };
 
+      allowReboot = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Reboot the system into the new generation instead of a switch
+          if the new generation uses a different kernel, kernel modules
+          or initrd than the booted system.
+        '';
+      };
+
     };
 
   };
@@ -78,11 +88,23 @@ let cfg = config.system.autoUpgrade; in
           HOME = "/root";
         } // config.networking.proxy.envVars;
 
-      path = [ pkgs.gnutar pkgs.xz.bin pkgs.gitMinimal config.nix.package.out ];
-
-      script = ''
-        ${config.system.build.nixos-rebuild}/bin/nixos-rebuild switch ${toString cfg.flags}
-      '';
+      path = [ pkgs.coreutils pkgs.gnutar pkgs.xz.bin pkgs.gitMinimal config.nix.package.out ];
+
+      script = let
+          nixos-rebuild = "${config.system.build.nixos-rebuild}/bin/nixos-rebuild";
+        in
+        if cfg.allowReboot then ''
+            ${nixos-rebuild} boot ${toString cfg.flags}
+            booted="$(readlink /run/booted-system/{initrd,kernel,kernel-modules})"
+            built="$(readlink /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})"
+            if [ "$booted" = "$built" ]; then
+              ${nixos-rebuild} switch ${toString cfg.flags}
+            else
+              /run/current-system/sw/bin/shutdown -r +1
+            fi
+          '' else ''
+            ${nixos-rebuild} switch ${toString cfg.flags}
+        '';
 
       startAt = cfg.dates;
     };
diff --git a/nixos/modules/virtualisation/parallels-guest.nix b/nixos/modules/virtualisation/parallels-guest.nix
index 4e0f2cae299..828419fb4b9 100644
--- a/nixos/modules/virtualisation/parallels-guest.nix
+++ b/nixos/modules/virtualisation/parallels-guest.nix
@@ -47,7 +47,7 @@ in
   config = mkIf config.hardware.parallels.enable {
     services.xserver = {
       drivers = singleton
-        { name = "prlvideo"; modules = [ prl-tools ]; libPath = [ prl-tools ]; };
+        { name = "prlvideo"; modules = [ prl-tools ]; };
 
       screenSection = ''
         Option "NoMTRR"
@@ -65,6 +65,7 @@ in
 
     hardware.opengl.package = prl-tools;
     hardware.opengl.package32 = pkgs.pkgsi686Linux.linuxPackages.prl-tools.override { libsOnly = true; kernel = null; };
+    hardware.opengl.setLdLibraryPath = true;
 
     services.udev.packages = [ prl-tools ];