summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
authorBas van Dijk <v.dijk.bas@gmail.com>2018-08-23 23:41:27 +0200
committerBas van Dijk <v.dijk.bas@gmail.com>2018-08-23 23:41:27 +0200
commit551fec44673ea023c303335e762f880d3c28646e (patch)
tree4d726dc291badc45899c225e36f5315ef7f938ee /nixos/modules
parent3f889f30bee92c3f2d5eaf53e56aef68f61efc4a (diff)
parent291de70ed6b736285ae4de6e3e556c0fc86c2348 (diff)
downloadnixlib-551fec44673ea023c303335e762f880d3c28646e.tar
nixlib-551fec44673ea023c303335e762f880d3c28646e.tar.gz
nixlib-551fec44673ea023c303335e762f880d3c28646e.tar.bz2
nixlib-551fec44673ea023c303335e762f880d3c28646e.tar.lz
nixlib-551fec44673ea023c303335e762f880d3c28646e.tar.xz
nixlib-551fec44673ea023c303335e762f880d3c28646e.tar.zst
nixlib-551fec44673ea023c303335e762f880d3c28646e.zip
Merge branch 'master' into es6
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/config/shells-environment.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-base.nix3
-rw-r--r--nixos/modules/installer/cd-dvd/iso-image.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-aarch64.nix3
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix3
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix3
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image.nix11
-rw-r--r--nixos/modules/installer/netboot/netboot-base.nix3
-rw-r--r--nixos/modules/installer/netboot/netboot.nix3
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl7
-rw-r--r--nixos/modules/misc/ids.nix6
-rw-r--r--nixos/modules/misc/version.nix3
-rw-r--r--nixos/modules/module-list.nix6
-rw-r--r--nixos/modules/profiles/all-hardware.nix2
-rw-r--r--nixos/modules/profiles/installation-device.nix7
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.nix52
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.xml125
-rw-r--r--nixos/modules/rename.nix2
-rw-r--r--nixos/modules/services/audio/mpd.nix4
-rw-r--r--nixos/modules/services/databases/cassandra.nix643
-rw-r--r--nixos/modules/services/databases/foundationdb.xml52
-rw-r--r--nixos/modules/services/desktops/accountsservice.nix10
-rw-r--r--nixos/modules/services/desktops/geoclue2.nix39
-rw-r--r--nixos/modules/services/desktops/zeitgeist.nix26
-rw-r--r--nixos/modules/services/editors/infinoted.nix8
-rw-r--r--nixos/modules/services/games/terraria.nix16
-rw-r--r--nixos/modules/services/hardware/fwupd.nix7
-rw-r--r--nixos/modules/services/hardware/thermald.nix36
-rw-r--r--nixos/modules/services/hardware/undervolt.nix134
-rw-r--r--nixos/modules/services/logging/syslog-ng.nix2
-rw-r--r--nixos/modules/services/misc/disnix.nix2
-rw-r--r--nixos/modules/services/misc/docker-registry.nix37
-rw-r--r--nixos/modules/services/misc/dysnomia.nix10
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix2
-rw-r--r--nixos/modules/services/misc/redmine.nix294
-rw-r--r--nixos/modules/services/misc/synergy.nix12
-rw-r--r--nixos/modules/services/monitoring/datadog-agent.nix236
-rw-r--r--nixos/modules/services/monitoring/dd-agent/dd-agent.nix80
-rw-r--r--nixos/modules/services/monitoring/graphite.nix20
-rw-r--r--nixos/modules/services/monitoring/netdata.nix4
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.nix9
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix10
-rw-r--r--nixos/modules/services/networking/dhcpcd.nix5
-rw-r--r--nixos/modules/services/networking/wpa_supplicant.nix12
-rw-r--r--nixos/modules/services/networking/zerotierone.nix15
-rw-r--r--nixos/modules/services/security/certmgr.nix194
-rw-r--r--nixos/modules/services/security/cfssl.nix209
-rw-r--r--nixos/modules/services/security/vault.nix22
-rw-r--r--nixos/modules/services/system/cloud-init.nix13
-rw-r--r--nixos/modules/services/system/localtime.nix10
-rw-r--r--nixos/modules/services/web-apps/youtrack.nix4
-rw-r--r--nixos/modules/services/web-servers/hydron.nix78
-rw-r--r--nixos/modules/services/web-servers/meguca.nix81
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix12
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix56
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/default.nix93
-rw-r--r--nixos/modules/services/x11/display-managers/gdm.nix4
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix21
-rw-r--r--nixos/modules/services/x11/display-managers/sddm.nix5
-rw-r--r--nixos/modules/services/x11/display-managers/slim.nix4
-rw-r--r--nixos/modules/services/x11/redshift.nix3
-rw-r--r--nixos/modules/services/x11/window-managers/metacity.nix12
-rw-r--r--nixos/modules/system/activation/top-level.nix6
-rw-r--r--nixos/modules/system/boot/loader/generations-dir/generations-dir.nix2
-rw-r--r--nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix2
-rw-r--r--nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py3
-rw-r--r--nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix34
-rw-r--r--nixos/modules/system/boot/luksroot.nix337
-rw-r--r--nixos/modules/system/boot/networkd.nix189
-rw-r--r--nixos/modules/system/boot/stage-1.nix10
-rw-r--r--nixos/modules/system/boot/systemd.nix6
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix35
-rw-r--r--nixos/modules/tasks/trackpoint.nix15
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix24
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix3
-rw-r--r--nixos/modules/virtualisation/virtualbox-host.nix25
78 files changed, 2410 insertions, 1074 deletions
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
index 398660967c52..9dc4749b08d1 100644
--- a/nixos/modules/config/shells-environment.nix
+++ b/nixos/modules/config/shells-environment.nix
@@ -70,7 +70,7 @@ in
       description = ''
         Shell script code called during global environment initialisation
         after all variables and profileVariables have been set.
-        This code is asumed to be shell-independent, which means you should
+        This code is assumed to be shell-independent, which means you should
         stick to pure sh without sh word split.
       '';
       type = types.lines;
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-base.nix b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
index 36024ce9f45e..1453e8082b0d 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-base.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
@@ -29,8 +29,5 @@ with lib;
   # Add Memtest86+ to the CD.
   boot.loader.grub.memtest86.enable = true;
 
-  # Allow the user to log in as root without a password.
-  users.users.root.initialHashedPassword = "";
-
   system.stateVersion = mkDefault "18.03";
 }
diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix
index 08923970cd38..1ac9e5c5c74b 100644
--- a/nixos/modules/installer/cd-dvd/iso-image.nix
+++ b/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -318,7 +318,7 @@ in
         options = [ "allow_other" "cow" "nonempty" "chroot=/mnt-root" "max_files=32768" "hide_meta_files" "dirs=/nix/.rw-store=rw:/nix/.ro-store=ro" ];
       };
 
-    boot.initrd.availableKernelModules = [ "squashfs" "iso9660" "usb-storage" "uas" ];
+    boot.initrd.availableKernelModules = [ "squashfs" "iso9660" "uas" ];
 
     boot.blacklistedKernelModules = [ "nouveau" ];
 
diff --git a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
index 4eb28434e192..bd6cf029967c 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
@@ -33,9 +33,6 @@ in
   # Also increase the amount of CMA to ensure the virtual console on the RPi3 works.
   boot.kernelParams = ["cma=32M" "console=ttyS0,115200n8" "console=ttyAMA0,115200n8" "console=tty0"];
 
-  # FIXME: this probably should be in installation-device.nix
-  users.users.root.initialHashedPassword = "";
-
   sdImage = {
     populateBootCommands = let
       configTxt = pkgs.writeText "config.txt" ''
diff --git a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
index 0d595503f193..0c89eb533359 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
@@ -34,9 +34,6 @@ in
   # - ttySAC2: for Exynos (ODROID-XU3)
   boot.kernelParams = ["console=ttyS0,115200n8" "console=ttymxc0,115200n8" "console=ttyAMA0,115200n8" "console=ttyO0,115200n8" "console=ttySAC2,115200n8" "console=tty0"];
 
-  # FIXME: this probably should be in installation-device.nix
-  users.users.root.initialHashedPassword = "";
-
   sdImage = {
     populateBootCommands = let
       configTxt = pkgs.writeText "config.txt" ''
diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
index aa52844288ca..78ea3f1a205c 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
@@ -27,9 +27,6 @@ in
   boot.consoleLogLevel = lib.mkDefault 7;
   boot.kernelPackages = pkgs.linuxPackages_rpi;
 
-  # FIXME: this probably should be in installation-device.nix
-  users.users.root.initialHashedPassword = "";
-
   sdImage = {
     populateBootCommands = let
       configTxt = pkgs.writeText "config.txt" ''
diff --git a/nixos/modules/installer/cd-dvd/sd-image.nix b/nixos/modules/installer/cd-dvd/sd-image.nix
index 311a5ff69670..2371be9d89a1 100644
--- a/nixos/modules/installer/cd-dvd/sd-image.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image.nix
@@ -12,13 +12,12 @@
 with lib;
 
 let
-  rootfsImage = import ../../../lib/make-ext4-fs.nix {
-    inherit pkgs;
+  rootfsImage = pkgs.callPackage ../../../lib/make-ext4-fs.nix ({
     inherit (config.sdImage) storePaths;
     volumeLabel = "NIXOS_SD";
   } // optionalAttrs (config.sdImage.rootPartitionUUID != null) {
     uuid = config.sdImage.rootPartitionUUID;
-  };
+  });
 in
 {
   options.sdImage = {
@@ -94,10 +93,10 @@ in
 
     sdImage.storePaths = [ config.system.build.toplevel ];
 
-    system.build.sdImage = pkgs.stdenv.mkDerivation {
+    system.build.sdImage = pkgs.callPackage ({ stdenv, dosfstools, e2fsprogs, mtools, libfaketime, utillinux }: stdenv.mkDerivation {
       name = config.sdImage.imageName;
 
-      buildInputs = with pkgs; [ dosfstools e2fsprogs mtools libfaketime utillinux ];
+      nativeBuildInputs = [ dosfstools e2fsprogs mtools libfaketime utillinux ];
 
       buildCommand = ''
         mkdir -p $out/nix-support $out/sd-image
@@ -138,7 +137,7 @@ in
         (cd boot; mcopy -bpsvm -i ../bootpart.img ./* ::)
         dd conv=notrunc if=bootpart.img of=$img seek=$START count=$SECTORS
       '';
-    };
+    }) {};
 
     boot.postBootCommands = ''
       # On the first boot do some maintenance tasks
diff --git a/nixos/modules/installer/netboot/netboot-base.nix b/nixos/modules/installer/netboot/netboot-base.nix
index da7d760ad2fc..7e66a49c7391 100644
--- a/nixos/modules/installer/netboot/netboot-base.nix
+++ b/nixos/modules/installer/netboot/netboot-base.nix
@@ -14,7 +14,4 @@ with lib;
       ../../profiles/base.nix
       ../../profiles/installation-device.nix
     ];
-
-  # Allow the user to log in as root without a password.
-  users.users.root.initialHashedPassword = "";
 }
diff --git a/nixos/modules/installer/netboot/netboot.nix b/nixos/modules/installer/netboot/netboot.nix
index a4eda3c52dce..ea640173c6dd 100644
--- a/nixos/modules/installer/netboot/netboot.nix
+++ b/nixos/modules/installer/netboot/netboot.nix
@@ -28,7 +28,6 @@ with lib;
       ++ (if pkgs.stdenv.system == "aarch64-linux"
           then []
           else [ pkgs.grub2 pkgs.syslinux ]);
-    system.boot.loader.kernelFile = pkgs.stdenv.platform.kernelTarget;
 
     fileSystems."/" =
       { fsType = "tmpfs";
@@ -86,7 +85,7 @@ with lib;
 
     system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" ''
       #!ipxe
-      kernel ${pkgs.stdenv.platform.kernelTarget} init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
+      kernel ${pkgs.stdenv.hostPlatform.platform.kernelTarget} init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
       initrd initrd
       boot
     '';
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index cbe145d5a330..bb201d97ded1 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -537,6 +537,13 @@ if ($showHardwareConfig) {
   boot.loader.systemd-boot.enable = true;
   boot.loader.efi.canTouchEfiVariables = true;
 EOF
+        } elsif (-e "/boot/extlinux") {
+            $bootLoaderConfig = <<EOF;
+  # Use the extlinux boot loader. (NixOS wants to enable GRUB by default)
+  boot.loader.grub.enable = false;
+  # Enables the generation of /boot/extlinux/extlinux.conf
+  boot.loader.generic-extlinux-compatible.enable = true;
+EOF
         } elsif ($virt ne "systemd-nspawn") {
             $bootLoaderConfig = <<EOF;
   # Use the GRUB 2 boot loader.
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 782f6c8f69df..bffd8aff78b9 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -323,6 +323,9 @@
       mapred = 296;
       hadoop = 297;
       hydron = 298;
+      cfssl = 299;
+      cassandra = 300;
+      qemu-libvirtd = 301;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -606,6 +609,9 @@
       mapred = 296;
       hadoop = 297;
       hydron = 298;
+      cfssl = 299;
+      cassandra = 300;
+      qemu-libvirtd = 301;
 
       # When adding a gid, make sure it doesn't match an existing
       # uid. Users and groups with the same name should have equal
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index 3be12e4a6375..63717e0c6a81 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -76,9 +76,6 @@ in
 
   config = {
 
-    warnings = lib.optional (options.system.stateVersion.highestPrio > 1000)
-      "You don't have `system.stateVersion` explicitly set. Expect things to break.";
-
     system.nixos = {
       # These defaults are set here rather than up there so that
       # changing them would not rebuild the manual
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index f5d94baf173c..2846afea8fbc 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -201,6 +201,7 @@
   ./services/databases/4store-endpoint.nix
   ./services/databases/4store.nix
   ./services/databases/aerospike.nix
+  ./services/databases/cassandra.nix
   ./services/databases/clickhouse.nix
   ./services/databases/couchdb.nix
   ./services/databases/firebird.nix
@@ -246,6 +247,7 @@
   ./services/desktops/gnome3/tracker-miners.nix
   ./services/desktops/profile-sync-daemon.nix
   ./services/desktops/telepathy.nix
+  ./services/desktops/zeitgeist.nix
   ./services/development/bloop.nix
   ./services/development/hoogle.nix
   ./services/editors/emacs.nix
@@ -279,6 +281,7 @@
   ./services/hardware/upower.nix
   ./services/hardware/usbmuxd.nix
   ./services/hardware/thermald.nix
+  ./services/hardware/undervolt.nix
   ./services/logging/SystemdJournal2Gelf.nix
   ./services/logging/awstats.nix
   ./services/logging/fluentd.nix
@@ -406,6 +409,7 @@
   ./services/monitoring/cadvisor.nix
   ./services/monitoring/collectd.nix
   ./services/monitoring/das_watchdog.nix
+  ./services/monitoring/datadog-agent.nix
   ./services/monitoring/dd-agent/dd-agent.nix
   ./services/monitoring/fusion-inventory.nix
   ./services/monitoring/grafana.nix
@@ -622,6 +626,8 @@
   ./services/search/hound.nix
   ./services/search/kibana.nix
   ./services/search/solr.nix
+  ./services/security/certmgr.nix
+  ./services/security/cfssl.nix
   ./services/security/clamav.nix
   ./services/security/fail2ban.nix
   ./services/security/fprintd.nix
diff --git a/nixos/modules/profiles/all-hardware.nix b/nixos/modules/profiles/all-hardware.nix
index 0d7124be0a5c..19f821ae17f3 100644
--- a/nixos/modules/profiles/all-hardware.nix
+++ b/nixos/modules/profiles/all-hardware.nix
@@ -33,7 +33,7 @@
 
       # USB support, especially for booting from USB CD-ROM
       # drives.
-      "usb_storage"
+      "uas"
 
       # Firewire support.  Not tested.
       "ohci1394" "sbp2"
diff --git a/nixos/modules/profiles/installation-device.nix b/nixos/modules/profiles/installation-device.nix
index 43f06c219f82..ff4a23a18d06 100644
--- a/nixos/modules/profiles/installation-device.nix
+++ b/nixos/modules/profiles/installation-device.nix
@@ -31,7 +31,8 @@ with lib;
     #services.rogue.enable = true;
 
     # Disable some other stuff we don't need.
-    security.sudo.enable = false;
+    security.sudo.enable = mkDefault false;
+    services.udisks2.enable = mkDefault false;
 
     # Automatically log in at the virtual consoles.
     services.mingetty.autologinUser = "root";
@@ -86,5 +87,9 @@ with lib;
     networking.firewall.logRefusedConnections = mkDefault false;
 
     environment.systemPackages = [ pkgs.vim ];
+
+
+    # Allow the user to log in as root without a password.
+    users.users.root.initialHashedPassword = "";
   };
 }
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.nix b/nixos/modules/programs/zsh/oh-my-zsh.nix
index b995d390b279..f4df4e983e42 100644
--- a/nixos/modules/programs/zsh/oh-my-zsh.nix
+++ b/nixos/modules/programs/zsh/oh-my-zsh.nix
@@ -3,7 +3,30 @@
 with lib;
 
 let
+
   cfg = config.programs.zsh.ohMyZsh;
+
+  mkLinkFarmEntry = name: dir:
+    let
+      env = pkgs.buildEnv {
+        name = "zsh-${name}-env";
+        paths = cfg.customPkgs;
+        pathsToLink = "/share/zsh/${dir}";
+      };
+    in
+      { inherit name; path = "${env}/share/zsh/${dir}"; };
+
+  mkLinkFarmEntry' = name: mkLinkFarmEntry name name;
+
+  custom =
+    if cfg.custom != null then cfg.custom
+    else if length cfg.customPkgs == 0 then null
+    else pkgs.linkFarm "oh-my-zsh-custom" [
+      (mkLinkFarmEntry' "themes")
+      (mkLinkFarmEntry "completions" "site-functions")
+      (mkLinkFarmEntry' "plugins")
+    ];
+
 in
   {
     options = {
@@ -34,10 +57,19 @@ in
         };
 
         custom = mkOption {
-          default = "";
-          type = types.str;
+          default = null;
+          type = with types; nullOr str;
           description = ''
             Path to a custom oh-my-zsh package to override config of oh-my-zsh.
+            (Can't be used along with `customPkgs`).
+          '';
+        };
+
+        customPkgs = mkOption {
+          default = [];
+          type = types.listOf types.package;
+          description = ''
+            List of custom packages that should be loaded into `oh-my-zsh`.
           '';
         };
 
@@ -67,7 +99,7 @@ in
 
       environment.systemPackages = [ cfg.package ];
 
-      programs.zsh.interactiveShellInit = with builtins; ''
+      programs.zsh.interactiveShellInit = ''
         # oh-my-zsh configuration generated by NixOS
         export ZSH=${cfg.package}/share/oh-my-zsh
 
@@ -75,8 +107,8 @@ in
           "plugins=(${concatStringsSep " " cfg.plugins})"
         }
 
-        ${optionalString (stringLength(cfg.custom) > 0)
-          "ZSH_CUSTOM=\"${cfg.custom}\""
+        ${optionalString (custom != null)
+          "ZSH_CUSTOM=\"${custom}\""
         }
 
         ${optionalString (stringLength(cfg.theme) > 0)
@@ -92,5 +124,15 @@ in
 
         source $ZSH/oh-my-zsh.sh
       '';
+
+      assertions = [
+        {
+          assertion = cfg.custom != null -> cfg.customPkgs == [];
+          message = "If `cfg.custom` is set for `ZSH_CUSTOM`, `customPkgs` can't be used!";
+        }
+      ];
+
     };
+
+    meta.doc = ./oh-my-zsh.xml;
   }
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.xml b/nixos/modules/programs/zsh/oh-my-zsh.xml
new file mode 100644
index 000000000000..b74da8630ee7
--- /dev/null
+++ b/nixos/modules/programs/zsh/oh-my-zsh.xml
@@ -0,0 +1,125 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-programs-zsh-ohmyzsh">
+
+<title>Oh my ZSH</title>
+
+<para><literal><link xlink:href="https://ohmyz.sh/">oh-my-zsh</link></literal> is a framework
+to manage your <link xlink:href="https://www.zsh.org/">ZSH</link> configuration
+including completion scripts for several CLI tools or custom prompt themes.</para>
+
+<section><title>Basic usage</title>
+<para>The module uses the <literal>oh-my-zsh</literal> package with all available features.  The
+initial setup using Nix expressions is fairly similar to the configuration format
+of <literal>oh-my-zsh</literal>.
+
+<programlisting>
+{
+  programs.ohMyZsh = {
+    enable = true;
+    plugins = [ "git" "python" "man" ];
+    theme = "agnoster";
+  };
+}
+</programlisting>
+
+For a detailed explanation of these arguments please refer to the
+<link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki"><literal>oh-my-zsh</literal> docs</link>.
+</para>
+<para>The expression generates the needed
+configuration and writes it into your <literal>/etc/zshrc</literal>.
+</para></section>
+
+<section><title>Custom additions</title>
+
+<para>Sometimes third-party or custom scripts such as a modified theme may be needed.
+<literal>oh-my-zsh</literal> provides the
+<link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki/Customization#overriding-internals"><literal>ZSH_CUSTOM</literal></link> 
+environment variable for this which points to a directory with additional scripts.</para>
+
+<para>The module can do this as well:
+
+<programlisting>
+{
+  programs.ohMyZsh.custom = "~/path/to/custom/scripts";
+}
+</programlisting>
+</para></section>
+
+<section><title>Custom environments</title>
+
+<para>There are several extensions for <literal>oh-my-zsh</literal> packaged in <literal>nixpkgs</literal>.
+One of them is <link xlink:href="https://github.com/spwhitt/nix-zsh-completions">nix-zsh-completions</link>
+which bundles completion scripts and a plugin for <literal>oh-my-zsh</literal>.</para>
+
+<para>Rather than using a single mutable path for <literal>ZSH_CUSTOM</literal>, it's also possible to
+generate this path from a list of Nix packages:
+
+<programlisting>
+{ pkgs, ... }:
+{
+  programs.ohMyZsh.customPkgs = with pkgs; [
+    pkgs.nix-zsh-completions
+    # and even more...
+  ];
+}
+</programlisting>
+
+Internally a single store path will be created using <literal>buildEnv</literal>.
+Please refer to the docs of
+<link xlink:href="https://nixos.org/nixpkgs/manual/#sec-building-environment"><literal>buildEnv</literal></link>
+for further reference.</para>
+
+<para><emphasis>Please keep in mind that this is not compatible with <literal>programs.ohMyZsh.custom</literal>
+as it requires an immutable store path while <literal>custom</literal> shall remain mutable! An evaluation failure
+will be thrown if both <literal>custom</literal> and <literal>customPkgs</literal> are set.</emphasis>
+</para></section>
+
+<section><title>Package your own customizations</title>
+
+<para>If third-party customizations (e.g. new themes) are supposed to be added to <literal>oh-my-zsh</literal>
+there are several pitfalls to keep in mind:</para>
+
+<itemizedlist>
+  <listitem>
+    <para>To comply with the default structure of <literal>ZSH</literal> the entire output needs to be written to
+    <literal>$out/share/zsh.</literal></para>
+  </listitem>
+  <listitem>
+    <para>Completion scripts are supposed to be stored at <literal>$out/share/zsh/site-functions</literal>. This directory
+    is part of the <literal><link xlink:href="http://zsh.sourceforge.net/Doc/Release/Functions.html">fpath</link></literal>
+    and the package should be compatible with pure <literal>ZSH</literal> setups. The module will automatically link
+    the contents of <literal>site-functions</literal> to completions directory in the proper store path.</para>
+  </listitem>
+  <listitem>
+    <para>The <literal>plugins</literal> directory needs the structure <literal>pluginname/pluginname.plugin.zsh</literal>
+    as structured in the <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/tree/91b771914bc7c43dd7c7a43b586c5de2c225ceb7/plugins">upstream repo.</link>
+    </para>
+  </listitem>
+</itemizedlist>
+
+<para>
+A derivation for <literal>oh-my-zsh</literal> may look like this:
+<programlisting>
+{ stdenv, fetchFromGitHub }:
+
+stdenv.mkDerivation rec {
+  name = "exemplary-zsh-customization-${version}";
+  version = "1.0.0";
+  src = fetchFromGitHub {
+    # path to the upstream repository
+  };
+
+  dontBuild = true;
+  installPhase = ''
+    mkdir -p $out/share/zsh/site-functions
+    cp {themes,plugins} $out/share/zsh
+    cp completions $out/share/zsh/site-functions
+  '';
+}
+</programlisting>
+</para>
+</section>
+</chapter>
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index fe395c4256bd..b51dcd2976f4 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -9,7 +9,6 @@ with lib;
     (mkRenamedOptionModule [ "system" "nixos" "stateVersion" ] [ "system" "stateVersion" ])
     (mkRenamedOptionModule [ "system" "nixos" "defaultChannel" ] [ "system" "defaultChannel" ])
 
-    (mkRenamedOptionModule [ "dysnomia" ] [ "services" "dysnomia" ])
     (mkRenamedOptionModule [ "environment" "x11Packages" ] [ "environment" "systemPackages" ])
     (mkRenamedOptionModule [ "environment" "enableBashCompletion" ] [ "programs" "bash" "enableCompletion" ])
     (mkRenamedOptionModule [ "environment" "nix" ] [ "nix" "package" ])
@@ -258,6 +257,7 @@ with lib;
     (mkRemovedOptionModule [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ] "")
     (mkRemovedOptionModule [ "virtualisation" "xen" "qemu" ] "You don't need this option anymore, it will work without it.")
     (mkRemovedOptionModule [ "services" "logstash" "enableWeb" ] "The web interface was removed from logstash")
+    (mkRemovedOptionModule [ "boot" "zfs" "enableLegacyCrypto" ] "The corresponding package was removed from nixpkgs.")
 
     # ZSH
     (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix
index 3add6556d0df..5bfe2b6a22ad 100644
--- a/nixos/modules/services/audio/mpd.nix
+++ b/nixos/modules/services/audio/mpd.nix
@@ -55,11 +55,11 @@ in {
       };
 
       musicDirectory = mkOption {
-        type = types.path;
+        type = with types; either path (strMatching "(http|https|nfs|smb)://.+");
         default = "${cfg.dataDir}/music";
         defaultText = ''''${dataDir}/music'';
         description = ''
-          The directory where mpd reads music from.
+          The directory or NFS/SMB network share where mpd reads music from.
         '';
       };
 
diff --git a/nixos/modules/services/databases/cassandra.nix b/nixos/modules/services/databases/cassandra.nix
index 09b3fbd8a62a..86e74d5d5ab4 100644
--- a/nixos/modules/services/databases/cassandra.nix
+++ b/nixos/modules/services/databases/cassandra.nix
@@ -4,445 +4,288 @@ with lib;
 
 let
   cfg = config.services.cassandra;
-  cassandraPackage = cfg.package.override {
-    jre = cfg.jre;
-  };
-  cassandraUser = {
-    name = cfg.user;
-    home = "/var/lib/cassandra";
-    description = "Cassandra role user";
-  };
-
-  cassandraRackDcProperties = ''
-    dc=${cfg.dc}
-    rack=${cfg.rack}
-  '';
-
-  cassandraConf = ''
-    cluster_name: ${cfg.clusterName}
-    num_tokens: 256
-    auto_bootstrap: ${boolToString cfg.autoBootstrap}
-    hinted_handoff_enabled: ${boolToString cfg.hintedHandOff}
-    hinted_handoff_throttle_in_kb: ${builtins.toString cfg.hintedHandOffThrottle}
-    max_hints_delivery_threads: 2
-    max_hint_window_in_ms: 10800000 # 3 hours
-    authenticator: ${cfg.authenticator}
-    authorizer: ${cfg.authorizer}
-    permissions_validity_in_ms: 2000
-    partitioner: org.apache.cassandra.dht.Murmur3Partitioner
-    data_file_directories:
-    ${builtins.concatStringsSep "\n" (map (v: "  - "+v) cfg.dataDirs)}
-    commitlog_directory: ${cfg.commitLogDirectory}
-    disk_failure_policy: stop
-    key_cache_size_in_mb:
-    key_cache_save_period: 14400
-    row_cache_size_in_mb: 0
-    row_cache_save_period: 0
-    saved_caches_directory: ${cfg.savedCachesDirectory}
-    commitlog_sync: ${cfg.commitLogSync}
-    commitlog_sync_period_in_ms: ${builtins.toString cfg.commitLogSyncPeriod}
-    commitlog_segment_size_in_mb: 32
-    seed_provider:
-      - class_name: org.apache.cassandra.locator.SimpleSeedProvider
-        parameters:
-          - seeds: "${builtins.concatStringsSep "," cfg.seeds}"
-    concurrent_reads: ${builtins.toString cfg.concurrentReads}
-    concurrent_writes: ${builtins.toString cfg.concurrentWrites}
-    memtable_flush_queue_size: 4
-    trickle_fsync: false
-    trickle_fsync_interval_in_kb: 10240
-    storage_port: 7000
-    ssl_storage_port: 7001
-    listen_address: ${cfg.listenAddress}
-    start_native_transport: true
-    native_transport_port: 9042
-    start_rpc: true
-    rpc_address: ${cfg.rpcAddress}
-    rpc_port: 9160
-    rpc_keepalive: true
-    rpc_server_type: sync
-    thrift_framed_transport_size_in_mb: 15
-    incremental_backups: ${boolToString cfg.incrementalBackups}
-    snapshot_before_compaction: false
-    auto_snapshot: true
-    column_index_size_in_kb: 64
-    in_memory_compaction_limit_in_mb: 64
-    multithreaded_compaction: false
-    compaction_throughput_mb_per_sec: 16
-    compaction_preheat_key_cache: true
-    read_request_timeout_in_ms: 10000
-    range_request_timeout_in_ms: 10000
-    write_request_timeout_in_ms: 10000
-    cas_contention_timeout_in_ms: 1000
-    truncate_request_timeout_in_ms: 60000
-    request_timeout_in_ms: 10000
-    cross_node_timeout: false
-    endpoint_snitch: ${cfg.snitch}
-    dynamic_snitch_update_interval_in_ms: 100
-    dynamic_snitch_reset_interval_in_ms: 600000
-    dynamic_snitch_badness_threshold: 0.1
-    request_scheduler: org.apache.cassandra.scheduler.NoScheduler
-    server_encryption_options:
-      internode_encryption: ${cfg.internodeEncryption}
-      keystore: ${cfg.keyStorePath}
-      keystore_password: ${cfg.keyStorePassword}
-      truststore: ${cfg.trustStorePath}
-      truststore_password: ${cfg.trustStorePassword}
-    client_encryption_options:
-      enabled: ${boolToString cfg.clientEncryption}
-      keystore: ${cfg.keyStorePath}
-      keystore_password: ${cfg.keyStorePassword}
-    internode_compression: all
-    inter_dc_tcp_nodelay: false
-    preheat_kernel_page_cache: false
-    streaming_socket_timeout_in_ms: ${toString cfg.streamingSocketTimoutInMS}
-  '';
-
-  cassandraLog = ''
-    log4j.rootLogger=${cfg.logLevel},stdout
-    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] %d{HH:mm:ss,SSS} %m%n
-  '';
-
-  cassandraConfFile = pkgs.writeText "cassandra.yaml" cassandraConf;
-  cassandraLogFile = pkgs.writeText "log4j-server.properties" cassandraLog;
-  cassandraRackFile = pkgs.writeText "cassandra-rackdc.properties" cassandraRackDcProperties;
-
-  cassandraEnvironment = {
-    CASSANDRA_HOME = cassandraPackage;
-    JAVA_HOME = cfg.jre;
-    CASSANDRA_CONF = "/etc/cassandra";
-  };
+  defaultUser = "cassandra";
+  cassandraConfig = flip recursiveUpdate cfg.extraConfig
+    ({ commitlog_sync = "batch";
+       commitlog_sync_batch_window_in_ms = 2;
+       partitioner = "org.apache.cassandra.dht.Murmur3Partitioner";
+       endpoint_snitch = "SimpleSnitch";
+       seed_provider =
+         [{ class_name = "org.apache.cassandra.locator.SimpleSeedProvider";
+            parameters = [ { seeds = "127.0.0.1"; } ];
+         }];
+       data_file_directories = [ "${cfg.homeDir}/data" ];
+       commitlog_directory = "${cfg.homeDir}/commitlog";
+       saved_caches_directory = "${cfg.homeDir}/saved_caches";
+     } // (if builtins.compareVersions cfg.package.version "3" >= 0
+             then { hints_directory = "${cfg.homeDir}/hints"; }
+             else {})
+    );
+  cassandraConfigWithAddresses = cassandraConfig //
+    ( if isNull cfg.listenAddress
+        then { listen_interface = cfg.listenInterface; }
+        else { listen_address = cfg.listenAddress; }
+    ) // (
+      if isNull cfg.rpcAddress
+        then { rpc_interface = cfg.rpcInterface; }
+        else { rpc_address = cfg.rpcAddress; }
+    );
+  cassandraEtc = pkgs.stdenv.mkDerivation
+    { name = "cassandra-etc";
+      cassandraYaml = builtins.toJSON cassandraConfigWithAddresses;
+      cassandraEnvPkg = "${cfg.package}/conf/cassandra-env.sh";
+      buildCommand = ''
+        mkdir -p "$out"
 
+        echo "$cassandraYaml" > "$out/cassandra.yaml"
+        ln -s "$cassandraEnvPkg" "$out/cassandra-env.sh"
+      '';
+    };
 in {
-
-  ###### interface
-
   options.services.cassandra = {
-    enable = mkOption {
-      description = "Whether to enable cassandra.";
-      default = false;
-      type = types.bool;
-    };
-    package = mkOption {
-      description = "Cassandra package to use.";
-      default = pkgs.cassandra;
-      defaultText = "pkgs.cassandra";
-      type = types.package;
-    };
-    jre = mkOption {
-      description = "JRE package to run cassandra service.";
-      default = pkgs.jre;
-      defaultText = "pkgs.jre";
-      type = types.package;
-    };
+    enable = mkEnableOption ''
+      Apache Cassandra – Scalable and highly available database.
+    '';
     user = mkOption {
-      description = "User that runs cassandra service.";
-      default = "cassandra";
-      type = types.string;
+      type = types.str;
+      default = defaultUser;
+      description = "Run Apache Cassandra under this user.";
     };
     group = mkOption {
-      description = "Group that runs cassandra service.";
-      default = "cassandra";
-      type = types.string;
-    };
-    envFile = mkOption {
-      description = "path to cassandra-env.sh";
-      default = "${cassandraPackage}/conf/cassandra-env.sh";
-      defaultText = "\${cassandraPackage}/conf/cassandra-env.sh";
-      type = types.path;
-    };
-    clusterName = mkOption {
-      description = "set cluster name";
-      default = "cassandra";
-      example = "prod-cluster0";
-      type = types.string;
-    };
-    commitLogDirectory = mkOption {
-      description = "directory for commit logs";
-      default = "/var/lib/cassandra/commit_log";
-      type = types.string;
-    };
-    savedCachesDirectory = mkOption {
-      description = "directory for saved caches";
-      default = "/var/lib/cassandra/saved_caches";
-      type = types.string;
-    };
-    hintedHandOff = mkOption {
-      description = "enable hinted handoff";
-      default = true;
-      type = types.bool;
-    };
-    hintedHandOffThrottle = mkOption {
-      description = "hinted hand off throttle rate in kb";
-      default = 1024;
-      type = types.int;
-    };
-    commitLogSync = mkOption {
-      description = "commitlog sync method";
-      default = "periodic";
       type = types.str;
-      example = "batch";
-    };
-    commitLogSyncPeriod = mkOption {
-      description = "commitlog sync period in ms ";
-      default = 10000;
-      type = types.int;
+      default = defaultUser;
+      description = "Run Apache Cassandra under this group.";
     };
-    envScript = mkOption {
-      default = "${cassandraPackage}/conf/cassandra-env.sh";
-      defaultText = "\${cassandraPackage}/conf/cassandra-env.sh";
+    homeDir = mkOption {
       type = types.path;
-      description = "Supply your own cassandra-env.sh rather than using the default";
-    };
-    extraParams = mkOption {
-      description = "add additional lines to cassandra-env.sh";
-      default = [];
-      example = [''JVM_OPTS="$JVM_OPTS -Dcassandra.available_processors=1"''];
-      type = types.listOf types.str;
-    };
-    dataDirs = mkOption {
-      type = types.listOf types.path;
-      default = [ "/var/lib/cassandra/data" ];
-      description = "Data directories for cassandra";
-    };
-    logLevel = mkOption {
-      type = types.str;
-      default = "INFO";
-      description = "default logging level for log4j";
-    };
-    internodeEncryption = mkOption {
-      description = "enable internode encryption";
-      default = "none";
-      example = "all";
-      type = types.str;
-    };
-    clientEncryption = mkOption {
-      description = "enable client encryption";
-      default = false;
-      type = types.bool;
-    };
-    trustStorePath = mkOption {
-      description = "path to truststore";
-      default = ".conf/truststore";
-      type = types.str;
-    };
-    keyStorePath = mkOption {
-      description = "path to keystore";
-      default = ".conf/keystore";
-      type = types.str;
-    };
-    keyStorePassword = mkOption {
-      description = "password to keystore";
-      default = "cassandra";
-      type = types.str;
+      default = "/var/lib/cassandra";
+      description = ''
+        Home directory for Apache Cassandra.
+      '';
     };
-    trustStorePassword = mkOption {
-      description = "password to truststore";
-      default = "cassandra";
-      type = types.str;
+    package = mkOption {
+      type = types.package;
+      default = pkgs.cassandra;
+      defaultText = "pkgs.cassandra";
+      example = literalExample "pkgs.cassandra_3_11";
+      description = ''
+        The Apache Cassandra package to use.
+      '';
     };
-    seeds = mkOption {
-      description = "password to truststore";
-      default = [ "127.0.0.1" ];
+    jvmOpts = mkOption {
       type = types.listOf types.str;
-    };
-    concurrentWrites = mkOption {
-      description = "number of concurrent writes allowed";
-      default = 32;
-      type = types.int;
-    };
-    concurrentReads = mkOption {
-      description = "number of concurrent reads allowed";
-      default = 32;
-      type = types.int;
+      default = [];
+      description = ''
+        Populate the JVM_OPT environment variable.
+      '';
     };
     listenAddress = mkOption {
-      description = "listen address";
-      default = "localhost";
-      type = types.str;
-    };
-    rpcAddress = mkOption {
-      description = "rpc listener address";
-      default = "localhost";
-      type = types.str;
-    };
-    incrementalBackups = mkOption {
-      description = "enable incremental backups";
-      default = false;
-      type = types.bool;
-    };
-    snitch = mkOption {
-      description = "snitch to use for topology discovery";
-      default = "GossipingPropertyFileSnitch";
-      example = "Ec2Snitch";
-      type = types.str;
-    };
-    dc = mkOption {
-      description = "datacenter for use in topology configuration";
-      default = "DC1";
-      example = "DC1";
-      type = types.str;
-    };
-    rack = mkOption {
-      description = "rack for use in topology configuration";
-      default = "RAC1";
-      example = "RAC1";
-      type = types.str;
-    };
-    authorizer = mkOption {
-      description = "
-        Authorization backend, implementing IAuthorizer; used to limit access/provide permissions
-      ";
-      default = "AllowAllAuthorizer";
-      example = "CassandraAuthorizer";
-      type = types.str;
-    };
-    authenticator = mkOption {
-      description = "
-        Authentication backend, implementing IAuthenticator; used to identify users
-      ";
-      default = "AllowAllAuthenticator";
-      example = "PasswordAuthenticator";
-      type = types.str;
-    };
-    autoBootstrap = mkOption {
-      description = "It makes new (non-seed) nodes automatically migrate the right data to themselves.";
-      default = true;
-      type = types.bool;
-    };
-    streamingSocketTimoutInMS = mkOption {
-      description = "Enable or disable socket timeout for streaming operations";
-      default = 3600000; #CASSANDRA-8611
-      example = 120;
-      type = types.int;
-    };
-    repairStartAt = mkOption {
-      default = "Sun";
-      type = types.string;
+      type = types.nullOr types.str;
+      default = "127.0.0.1";
+      example = literalExample "null";
       description = ''
-      Defines realtime (i.e. wallclock) timers with calendar event
-      expressions. For more details re: systemd OnCalendar at
-      https://www.freedesktop.org/software/systemd/man/systemd.time.html#Displaying%20Time%20Spans
+        Address or interface to bind to and tell other Cassandra nodes
+        to connect to. You _must_ change this if you want multiple
+        nodes to be able to communicate!
+
+        Set listenAddress OR listenInterface, not both.
+
+        Leaving it blank leaves it up to
+        InetAddress.getLocalHost(). This will always do the Right
+        Thing _if_ the node is properly configured (hostname, name
+        resolution, etc), and the Right Thing is to use the address
+        associated with the hostname (it might not be).
+
+        Setting listen_address to 0.0.0.0 is always wrong.
       '';
-      example = ["weekly" "daily" "08:05:40" "mon,fri *-1/2-1,3 *:30:45"];
     };
-    repairRandomizedDelayInSec = mkOption {
-      default = 0;
-      type = types.int;
-      description = ''Delay the timer by a randomly selected, evenly distributed
-      amount of time between 0 and the specified time value. re: systemd timer
-      RandomizedDelaySec for more details
+    listenInterface = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "eth1";
+      description = ''
+        Set listenAddress OR listenInterface, not both. Interfaces
+        must correspond to a single address, IP aliasing is not
+        supported.
       '';
     };
-    repairPostStop = mkOption {
-      default = null;
-      type = types.nullOr types.string;
+    rpcAddress = mkOption {
+      type = types.nullOr types.str;
+      default = "127.0.0.1";
+      example = literalExample "null";
       description = ''
-      Run a script when repair is over. One can use it to send statsd events, email, etc.
+        The address or interface to bind the native transport server to.
+
+        Set rpcAddress OR rpcInterface, not both.
+
+        Leaving rpcAddress blank has the same effect as on
+        listenAddress (i.e. it will be based on the configured hostname
+        of the node).
+
+        Note that unlike listenAddress, you can specify 0.0.0.0, but you
+        must also set extraConfig.broadcast_rpc_address to a value other
+        than 0.0.0.0.
+
+        For security reasons, you should not expose this port to the
+        internet. Firewall it if needed.
       '';
     };
-    repairPostStart = mkOption {
+    rpcInterface = mkOption {
+      type = types.nullOr types.str;
       default = null;
-      type = types.nullOr types.string;
+      example = "eth1";
       description = ''
-      Run a script when repair starts. One can use it to send statsd events, email, etc.
-      It has same semantics as systemd ExecStopPost; So, if it fails, unit is consisdered
-      failed.
+        Set rpcAddress OR rpcInterface, not both. Interfaces must
+        correspond to a single address, IP aliasing is not supported.
       '';
     };
-  };
 
-  ###### implementation
-
-  config = mkIf cfg.enable {
+    extraConfig = mkOption {
+      type = types.attrs;
+      default = {};
+      example =
+        { commitlog_sync_batch_window_in_ms = 3;
+        };
+      description = ''
+        Extra options to be merged into cassandra.yaml as nix attribute set.
+      '';
+    };
+    fullRepairInterval = mkOption {
+      type = types.nullOr types.str;
+      default = "3w";
+      example = literalExample "null";
+      description = ''
+          Set the interval how often full repairs are run, i.e.
+          `nodetool repair --full` is executed. See
+          https://cassandra.apache.org/doc/latest/operating/repair.html
+          for more information.
 
-    environment.etc."cassandra/cassandra-rackdc.properties" = {
-      source = cassandraRackFile;
+          Set to `null` to disable full repairs.
+        '';
     };
-    environment.etc."cassandra/cassandra.yaml" = {
-      source = cassandraConfFile;
+    fullRepairOptions = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "--partitioner-range" ];
+      description = ''
+          Options passed through to the full repair command.
+        '';
     };
-    environment.etc."cassandra/log4j-server.properties" = {
-      source = cassandraLogFile;
+    incrementalRepairInterval = mkOption {
+      type = types.nullOr types.str;
+      default = "3d";
+      example = literalExample "null";
+      description = ''
+          Set the interval how often incremental repairs are run, i.e.
+          `nodetool repair` is executed. See
+          https://cassandra.apache.org/doc/latest/operating/repair.html
+          for more information.
+
+          Set to `null` to disable incremental repairs.
+        '';
     };
-    environment.etc."cassandra/cassandra-env.sh" = {
-      text = ''
-        ${builtins.readFile cfg.envFile}
-        ${concatStringsSep "\n" cfg.extraParams}
-      '';
+    incrementalRepairOptions = mkOption {
+      type = types.listOf types.string;
+      default = [];
+      example = [ "--partitioner-range" ];
+      description = ''
+          Options passed through to the incremental repair command.
+        '';
     };
-    systemd.services.cassandra = {
-      description = "Cassandra Daemon";
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
-      environment = cassandraEnvironment;
-      restartTriggers = [ cassandraConfFile cassandraLogFile cassandraRackFile ];
-      serviceConfig = {
-
-        User = cfg.user;
-        PermissionsStartOnly = true;
-        LimitAS = "infinity";
-        LimitNOFILE = "100000";
-        LimitNPROC = "32768";
-        LimitMEMLOCK = "infinity";
+  };
 
-      };
-      script = ''
-         ${cassandraPackage}/bin/cassandra -f
-        '';
-      path = [
-        cfg.jre
-        cassandraPackage
-        pkgs.coreutils
+  config = mkIf cfg.enable {
+    assertions =
+      [ { assertion =
+            ((isNull cfg.listenAddress)
+             || (isNull cfg.listenInterface)
+            ) && !((isNull cfg.listenAddress)
+                   && (isNull cfg.listenInterface)
+                  );
+          message = "You have to set either listenAddress or listenInterface";
+        }
+        { assertion =
+            ((isNull cfg.rpcAddress)
+             || (isNull cfg.rpcInterface)
+            ) && !((isNull cfg.rpcAddress)
+                   && (isNull cfg.rpcInterface)
+                  );
+          message = "You have to set either rpcAddress or rpcInterface";
+        }
       ];
-      preStart = ''
-        mkdir -m 0700 -p /etc/cassandra/triggers
-        mkdir -m 0700 -p /var/lib/cassandra /var/log/cassandra
-        chown ${cfg.user} /var/lib/cassandra /var/log/cassandra /etc/cassandra/triggers
-      '';
-      postStart = ''
-        sleep 2
-        while ! nodetool status >/dev/null 2>&1; do
-          sleep 2
-        done
-        nodetool status
-      '';
+    users = mkIf (cfg.user == defaultUser) {
+      extraUsers."${defaultUser}" =
+        {  group = cfg.group;
+           home = cfg.homeDir;
+           createHome = true;
+           uid = config.ids.uids.cassandra;
+           description = "Cassandra service user";
+        };
+      extraGroups."${defaultUser}".gid = config.ids.gids.cassandra;
     };
 
-    environment.systemPackages = [ cassandraPackage ];
-
-    networking.firewall.allowedTCPPorts = [
-      7000
-      7001
-      9042
-      9160
-    ];
-
-    users.users.cassandra =
-      if config.ids.uids ? "cassandra"
-      then { uid = config.ids.uids.cassandra; } // cassandraUser
-      else cassandraUser ;
-
-    boot.kernel.sysctl."vm.swappiness" = pkgs.lib.mkOptionDefault 0;
+    systemd.services.cassandra =
+      { description = "Apache Cassandra service";
+        after = [ "network.target" ];
+        environment =
+          { CASSANDRA_CONF = "${cassandraEtc}";
+            JVM_OPTS = builtins.concatStringsSep " " cfg.jvmOpts;
+          };
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig =
+          { User = cfg.user;
+            Group = cfg.group;
+            ExecStart = "${cfg.package}/bin/cassandra -f";
+            SuccessExitStatus = 143;
+          };
+      };
 
-    systemd.timers."cassandra-repair" = {
-      timerConfig = {
-        OnCalendar = "${toString cfg.repairStartAt}";
-        RandomizedDelaySec = cfg.repairRandomizedDelayInSec;
+    systemd.services.cassandra-full-repair =
+      { description = "Perform a full repair on this Cassandra node";
+        after = [ "cassandra.service" ];
+        requires = [ "cassandra.service" ];
+        serviceConfig =
+          { User = cfg.user;
+            Group = cfg.group;
+            ExecStart =
+              lib.concatStringsSep " "
+                ([ "${cfg.package}/bin/nodetool" "repair" "--full"
+                 ] ++ cfg.fullRepairOptions);
+          };
+      };
+    systemd.timers.cassandra-full-repair =
+      mkIf (!isNull cfg.fullRepairInterval) {
+        description = "Schedule full repairs on Cassandra";
+        wantedBy = [ "timers.target" ];
+        timerConfig =
+          { OnBootSec = cfg.fullRepairInterval;
+            OnUnitActiveSec = cfg.fullRepairInterval;
+            Persistent = true;
+          };
       };
-    };
 
-    systemd.services."cassandra-repair" = {
-      description = "Cassandra repair daemon";
-      environment = cassandraEnvironment;
-      script = "${cassandraPackage}/bin/nodetool repair -pr";
-      postStop = mkIf (cfg.repairPostStop != null) cfg.repairPostStop;
-      postStart = mkIf (cfg.repairPostStart != null) cfg.repairPostStart;
-      serviceConfig = {
-        User = cfg.user;
+    systemd.services.cassandra-incremental-repair =
+      { description = "Perform an incremental repair on this cassandra node.";
+        after = [ "cassandra.service" ];
+        requires = [ "cassandra.service" ];
+        serviceConfig =
+          { User = cfg.user;
+            Group = cfg.group;
+            ExecStart =
+              lib.concatStringsSep " "
+                ([ "${cfg.package}/bin/nodetool" "repair"
+                 ] ++ cfg.incrementalRepairOptions);
+          };
+      };
+    systemd.timers.cassandra-incremental-repair =
+      mkIf (!isNull cfg.incrementalRepairInterval) {
+        description = "Schedule incremental repairs on Cassandra";
+        wantedBy = [ "timers.target" ];
+        timerConfig =
+          { OnBootSec = cfg.incrementalRepairInterval;
+            OnUnitActiveSec = cfg.incrementalRepairInterval;
+            Persistent = true;
+          };
       };
-    };
   };
 }
diff --git a/nixos/modules/services/databases/foundationdb.xml b/nixos/modules/services/databases/foundationdb.xml
index 51fe9ae3f910..f4090c492764 100644
--- a/nixos/modules/services/databases/foundationdb.xml
+++ b/nixos/modules/services/databases/foundationdb.xml
@@ -12,12 +12,10 @@
 
 <para><emphasis>Maintainer:</emphasis> Austin Seipp</para>
 
-<para><emphasis>Available version(s):</emphasis> 5.1.x</para>
+<para><emphasis>Available version(s):</emphasis> 5.1.x, 5.2.x, 6.0.x</para>
 
-<para>FoundationDB (or "FDB") is a distributed, open source, high performance,
-transactional key-value store. It can store petabytes of data and deliver
-exceptional performance while maintaining consistency and ACID semantics
-(serializable transactions) over a large cluster.</para>
+<para>FoundationDB (or "FDB") is an open source, distributed, transactional
+key-value store.</para>
 
 <section><title>Configuring and basic setup</title>
 
@@ -26,12 +24,12 @@ exceptional performance while maintaining consistency and ACID semantics
 
 <programlisting>
 services.foundationdb.enable = true;
-services.foundationdb.package = pkgs.foundationdb51; # FoundationDB 5.1.x
+services.foundationdb.package = pkgs.foundationdb52; # FoundationDB 5.2.x
 </programlisting>
 </para>
 
 <para>The <option>services.foundationdb.package</option> option is required,
-and must always be specified. Because FoundationDB network protocols and
+and must always be specified. Due to the fact FoundationDB network protocols and
 on-disk storage formats may change between (major) versions, and upgrades must
 be explicitly handled by the user, you must always manually specify this
 yourself so that the NixOS module will use the proper version. Note that minor,
@@ -70,6 +68,40 @@ fdb>
 </programlisting>
 </para>
 
+<para>You can also write programs using the available client libraries.
+For example, the following Python program can be run in order to grab the
+cluster status, as a quick example. (This example uses
+<command>nix-shell</command> shebang support to automatically supply the
+necessary Python modules).
+
+<programlisting>
+a@link> cat fdb-status.py
+#! /usr/bin/env nix-shell
+#! nix-shell -i python -p python pythonPackages.foundationdb52
+
+import fdb
+import json
+
+def main():
+    fdb.api_version(520)
+    db = fdb.open()
+
+    @fdb.transactional
+    def get_status(tr):
+        return str(tr['\xff\xff/status/json'])
+
+    obj = json.loads(get_status(db))
+    print('FoundationDB available: %s' % obj['client']['database_status']['available'])
+
+if __name__ == "__main__":
+    main()
+a@link> chmod +x fdb-status.py
+a@link> ./fdb-status.py
+FoundationDB available: True
+a@link>
+</programlisting>
+</para>
+
 <para>FoundationDB is run under the <command>foundationdb</command> user and
 group by default, but this may be changed in the NixOS configuration. The
 systemd unit <command>foundationdb.service</command> controls the
@@ -295,7 +327,6 @@ only undergone fairly basic testing of all the available functionality.</para>
       individual <command>fdbserver</command> processes. Currently, all server
       processes inherit all the global <command>fdbmonitor</command> settings.
       </para></listitem>
-  <listitem><para>Python bindings are not currently installed.</para></listitem>
   <listitem><para>Ruby bindings are not currently installed.</para></listitem>
   <listitem><para>Go bindings are not currently installed.</para></listitem>
 </itemizedlist>
@@ -306,8 +337,9 @@ only undergone fairly basic testing of all the available functionality.</para>
 
 <para>NixOS's FoundationDB module allows you to configure all of the most
 relevant configuration options for <command>fdbmonitor</command>, matching it
-quite closely. For a complete list of all options, check <command>man
-configuration.nix</command>.</para>
+quite closely. A complete list of options for the FoundationDB module may be
+found <link linkend="opt-services.foundationdb.enable">here</link>. You should
+also read the FoundationDB documentation as well.</para>
 
 </section>
 
diff --git a/nixos/modules/services/desktops/accountsservice.nix b/nixos/modules/services/desktops/accountsservice.nix
index 2a7450669ea0..933b9da2c83c 100644
--- a/nixos/modules/services/desktops/accountsservice.nix
+++ b/nixos/modules/services/desktops/accountsservice.nix
@@ -32,15 +32,21 @@ with lib;
 
     environment.systemPackages = [ pkgs.accountsservice ];
 
+    # Accounts daemon looks for dbus interfaces in $XDG_DATA_DIRS/accountsservice
+    environment.pathsToLink = [ "/share/accountsservice" ];
+
     services.dbus.packages = [ pkgs.accountsservice ];
 
     systemd.packages = [ pkgs.accountsservice ];
 
-    systemd.services.accounts-daemon= {
+    systemd.services.accounts-daemon = {
 
       wantedBy = [ "graphical.target" ];
 
-    } // (mkIf (!config.users.mutableUsers) {
+      # Accounts daemon looks for dbus interfaces in $XDG_DATA_DIRS/accountsservice
+      environment.XDG_DATA_DIRS = "${config.system.path}/share";
+
+    } // (optionalAttrs (!config.users.mutableUsers) {
       environment.NIXOS_USERS_PURE = "true";
     });
   };
diff --git a/nixos/modules/services/desktops/geoclue2.nix b/nixos/modules/services/desktops/geoclue2.nix
index c5a000d5c6a7..dafb0af20756 100644
--- a/nixos/modules/services/desktops/geoclue2.nix
+++ b/nixos/modules/services/desktops/geoclue2.nix
@@ -4,6 +4,10 @@
 
 with lib;
 
+let
+  # the demo agent isn't built by default, but we need it here
+  package = pkgs.geoclue2.override { withDemoAgent = config.services.geoclue2.enableDemoAgent; };
+in
 {
 
   ###### interface
@@ -21,21 +25,42 @@ with lib;
         '';
       };
 
+      enableDemoAgent = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to use the GeoClue demo agent. This should be
+          overridden by desktop environments that provide their own
+          agent.
+        '';
+      };
+
     };
 
   };
 
 
   ###### implementation
-
   config = mkIf config.services.geoclue2.enable {
 
-    environment.systemPackages = [ pkgs.geoclue2 ];
-
-    services.dbus.packages = [ pkgs.geoclue2 ];
-
-    systemd.packages = [ pkgs.geoclue2 ];
-
+    environment.systemPackages = [ package ];
+
+    services.dbus.packages = [ package ];
+
+    systemd.packages = [ package ];
+  
+    # this needs to run as a user service, since it's associated with the
+    # user who is making the requests
+    systemd.user.services = mkIf config.services.geoclue2.enableDemoAgent { 
+      "geoclue-agent" = {
+        description = "Geoclue agent";
+        script = "${package}/libexec/geoclue-2.0/demos/agent";
+        # this should really be `partOf = [ "geoclue.service" ]`, but
+        # we can't be part of a system service, and the agent should
+        # be okay with the main service coming and going
+        wantedBy = [ "default.target" ];
+      };
+    };
   };
 
 }
diff --git a/nixos/modules/services/desktops/zeitgeist.nix b/nixos/modules/services/desktops/zeitgeist.nix
new file mode 100644
index 000000000000..20c82ccdd56c
--- /dev/null
+++ b/nixos/modules/services/desktops/zeitgeist.nix
@@ -0,0 +1,26 @@
+# Zeitgeist
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+
+  options = {
+    services.zeitgeist = {
+      enable = mkEnableOption "zeitgeist";
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf config.services.zeitgeist.enable {
+
+    environment.systemPackages = [ pkgs.zeitgeist ];
+
+    services.dbus.packages = [ pkgs.zeitgeist ];
+
+    systemd.packages = [ pkgs.zeitgeist ];
+  };
+}
diff --git a/nixos/modules/services/editors/infinoted.nix b/nixos/modules/services/editors/infinoted.nix
index bba21caca85d..9cc8d421270e 100644
--- a/nixos/modules/services/editors/infinoted.nix
+++ b/nixos/modules/services/editors/infinoted.nix
@@ -10,8 +10,8 @@ in {
 
     package = mkOption {
       type = types.package;
-      default = pkgs.libinfinity.override { daemon = true; };
-      defaultText = "pkgs.libinfinity.override { daemon = true; }";
+      default = pkgs.libinfinity;
+      defaultText = "pkgs.libinfinity";
       description = ''
         Package providing infinoted
       '';
@@ -119,7 +119,7 @@ in {
     users.groups = optional (cfg.group == "infinoted")
       { name = "infinoted";
       };
-  
+
     systemd.services.infinoted =
       { description = "Gobby Dedicated Server";
 
@@ -129,7 +129,7 @@ in {
         serviceConfig = {
           Type = "simple";
           Restart = "always";
-          ExecStart = "${cfg.package}/bin/infinoted-${versions.majorMinor cfg.package.version} --config-file=/var/lib/infinoted/infinoted.conf";
+          ExecStart = "${cfg.package.infinoted} --config-file=/var/lib/infinoted/infinoted.conf";
           User = cfg.user;
           Group = cfg.group;
           PermissionsStartOnly = true;
diff --git a/nixos/modules/services/games/terraria.nix b/nixos/modules/services/games/terraria.nix
index ddf17599296a..31f8edca20ce 100644
--- a/nixos/modules/services/games/terraria.nix
+++ b/nixos/modules/services/games/terraria.nix
@@ -18,6 +18,16 @@ let
     (boolFlag "secure" cfg.secure)
     (boolFlag "noupnp" cfg.noUPnP)
   ];
+  stopScript = pkgs.writeScript "terraria-stop" ''
+    #!${pkgs.runtimeShell}
+
+    if ! [ -d "/proc/$1" ]; then
+      exit 0
+    fi
+
+    ${getBin pkgs.tmux}/bin/tmux -S /var/lib/terraria/terraria.sock send-keys Enter exit Enter
+    ${getBin pkgs.coreutils}/bin/tail --pid="$1" -f /dev/null
+  '';
 in
 {
   options = {
@@ -124,10 +134,10 @@ in
 
       serviceConfig = {
         User    = "terraria";
-        Type = "oneshot";
-        RemainAfterExit = true;
+        Type = "forking";
+        GuessMainPID = true;
         ExecStart = "${getBin pkgs.tmux}/bin/tmux -S /var/lib/terraria/terraria.sock new -d ${pkgs.terraria-server}/bin/TerrariaServer ${concatStringsSep " " flags}";
-        ExecStop = "${getBin pkgs.tmux}/bin/tmux -S /var/lib/terraria/terraria.sock send-keys Enter \"exit\" Enter";
+        ExecStop = "${stopScript} $MAINPID";
       };
 
       postStart = ''
diff --git a/nixos/modules/services/hardware/fwupd.nix b/nixos/modules/services/hardware/fwupd.nix
index d97d690920a6..7743f81fd622 100644
--- a/nixos/modules/services/hardware/fwupd.nix
+++ b/nixos/modules/services/hardware/fwupd.nix
@@ -71,6 +71,13 @@ in {
           BlacklistPlugins=${lib.concatStringsSep ";" cfg.blacklistPlugins}
         '';
       };
+      "fwupd/uefi.conf" = {
+        source = pkgs.writeText "uefi.conf" ''
+          [uefi]
+          OverrideESPMountPoint=${config.boot.loader.efi.efiSysMountPoint}
+        '';
+      };
+
     } // originalEtc // extraTrustedKeys;
 
     services.dbus.packages = [ pkgs.fwupd ];
diff --git a/nixos/modules/services/hardware/thermald.nix b/nixos/modules/services/hardware/thermald.nix
index 88c3f99aed4e..69577bbe0181 100644
--- a/nixos/modules/services/hardware/thermald.nix
+++ b/nixos/modules/services/hardware/thermald.nix
@@ -6,16 +6,30 @@ let
   cfg = config.services.thermald;
 in {
   ###### interface
-  options = { 
-    services.thermald = { 
+  options = {
+    services.thermald = {
       enable = mkOption {
         default = false;
         description = ''
           Whether to enable thermald, the temperature management daemon.
-        ''; 
-      };  
-    };  
-  };  
+        '';
+      };
+
+      debug = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable debug logging.
+        '';
+      };
+
+      configFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = "the thermald manual configuration file.";
+      };
+    };
+  };
 
   ###### implementation
   config = mkIf cfg.enable {
@@ -24,7 +38,15 @@ in {
     systemd.services.thermald = {
       description = "Thermal Daemon Service";
       wantedBy = [ "multi-user.target" ];
-      script = "exec ${pkgs.thermald}/sbin/thermald --no-daemon --dbus-enable";
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.thermald}/sbin/thermald \
+            --no-daemon \
+            ${optionalString cfg.debug "--loglevel=debug"} \
+            ${optionalString (cfg.configFile != null) "--config-file ${cfg.configFile}"} \
+            --dbus-enable
+        '';
+      };
     };
   };
 }
diff --git a/nixos/modules/services/hardware/undervolt.nix b/nixos/modules/services/hardware/undervolt.nix
new file mode 100644
index 000000000000..e5ef0601de3c
--- /dev/null
+++ b/nixos/modules/services/hardware/undervolt.nix
@@ -0,0 +1,134 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.undervolt;
+in {
+  options.services.undervolt = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to undervolt intel cpus.
+      '';
+    };
+
+    verbose = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable verbose logging.
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.undervolt;
+      defaultText = "pkgs.undervolt";
+      description = ''
+        undervolt derivation to use.
+      '';
+    };
+
+    coreOffset = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The amount of voltage to offset the CPU cores by. Accepts a floating point number.
+      '';
+    };
+
+    gpuOffset = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The amount of voltage to offset the GPU by. Accepts a floating point number.
+      '';
+    };
+
+    uncoreOffset = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The amount of voltage to offset uncore by. Accepts a floating point number.
+      '';
+    };
+
+    analogioOffset = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The amount of voltage to offset analogio by. Accepts a floating point number.
+      '';
+    };
+
+    temp = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The temperature target. Accepts a floating point number.
+      '';
+    };
+
+    tempAc = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The temperature target on AC power. Accepts a floating point number.
+      '';
+    };
+
+    tempBat = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The temperature target on battery power. Accepts a floating point number.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    boot.kernelModules = [ "msr" ];
+
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.services.undervolt = {
+      path = [ pkgs.undervolt ];
+
+      description = "Intel Undervolting Service";
+      serviceConfig = {
+        Type = "oneshot";
+        Restart = "no";
+
+        # `core` and `cache` are both intentionally set to `cfg.coreOffset` as according to the undervolt docs:
+        #
+        #     Core or Cache offsets have no effect. It is not possible to set different offsets for
+        #     CPU Core and Cache. The CPU will take the smaller of the two offsets, and apply that to
+        #     both CPU and Cache. A warning message will be displayed if you attempt to set different offsets.
+        ExecStart = ''
+          ${pkgs.undervolt}/bin/undervolt \
+            ${optionalString cfg.verbose "--verbose"} \
+            ${optionalString (cfg.coreOffset != null) "--core ${cfg.coreOffset}"} \
+            ${optionalString (cfg.coreOffset != null) "--cache ${cfg.coreOffset}"} \
+            ${optionalString (cfg.gpuOffset != null) "--gpu ${cfg.gpuOffset}"} \
+            ${optionalString (cfg.uncoreOffset != null) "--uncore ${cfg.uncoreOffset}"} \
+            ${optionalString (cfg.analogioOffset != null) "--analogio ${cfg.analogioOffset}"} \
+            ${optionalString (cfg.temp != null) "--temp ${cfg.temp}"} \
+            ${optionalString (cfg.tempAc != null) "--temp-ac ${cfg.tempAc}"} \
+            ${optionalString (cfg.tempBat != null) "--temp-bat ${cfg.tempBat}"}
+        '';
+      };
+    };
+
+    systemd.timers.undervolt = {
+      description = "Undervolt timer to ensure voltage settings are always applied";
+      partOf = [ "undervolt.service" ];
+      wantedBy = [ "multi-user.target" ];
+      timerConfig = {
+        OnBootSec = "2min";
+        OnUnitActiveSec = "30";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/logging/syslog-ng.nix b/nixos/modules/services/logging/syslog-ng.nix
index 21be286a6e98..65e103ac2ba5 100644
--- a/nixos/modules/services/logging/syslog-ng.nix
+++ b/nixos/modules/services/logging/syslog-ng.nix
@@ -85,9 +85,11 @@ in {
       after = [ "multi-user.target" ]; # makes sure hostname etc is set
       serviceConfig = {
         Type = "notify";
+        PIDFile = pidFile;
         StandardOutput = "null";
         Restart = "on-failure";
         ExecStart = "${cfg.package}/sbin/syslog-ng ${concatStringsSep " " syslogngOptions}";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
       };
     };
   };
diff --git a/nixos/modules/services/misc/disnix.nix b/nixos/modules/services/misc/disnix.nix
index bb3ac1ecf075..c21cb2afc3ca 100644
--- a/nixos/modules/services/misc/disnix.nix
+++ b/nixos/modules/services/misc/disnix.nix
@@ -47,7 +47,7 @@ in
   ###### implementation
 
   config = mkIf cfg.enable {
-    services.dysnomia.enable = true;
+    dysnomia.enable = true;
 
     environment.systemPackages = [ pkgs.disnix ] ++ optional cfg.useWebServiceInterface pkgs.DisnixWebService;
 
diff --git a/nixos/modules/services/misc/docker-registry.nix b/nixos/modules/services/misc/docker-registry.nix
index 08031d33c131..9a3966ab30aa 100644
--- a/nixos/modules/services/misc/docker-registry.nix
+++ b/nixos/modules/services/misc/docker-registry.nix
@@ -5,6 +5,43 @@ with lib;
 let
   cfg = config.services.dockerRegistry;
 
+  blobCache = if cfg.enableRedisCache
+    then "redis"
+    else "inmemory";
+
+  registryConfig = {
+    version =  "0.1";
+    log.fields.service = "registry";
+    storage = {
+      cache.blobdescriptor = blobCache;
+      filesystem.rootdirectory = cfg.storagePath;
+      delete.enabled = cfg.enableDelete;
+    };
+    http = {
+      addr = ":${builtins.toString cfg.port}";
+      headers.X-Content-Type-Options = ["nosniff"];
+    };
+    health.storagedriver = {
+      enabled = true;
+      interval = "10s";
+      threshold = 3;
+    };
+  };
+
+  registryConfig.redis = mkIf cfg.enableRedisCache {
+    addr = "${cfg.redisUrl}";
+    password = "${cfg.redisPassword}";
+    db = 0;
+    dialtimeout = "10ms";
+    readtimeout = "10ms";
+    writetimeout = "10ms";
+    pool = {
+      maxidle = 16;
+      maxactive = 64;
+      idletimeout = "300s";
+    };
+  };
+
   configFile = pkgs.writeText "docker-registry-config.yml" (builtins.toJSON (recursiveUpdate registryConfig cfg.extraConfig));
 
 in {
diff --git a/nixos/modules/services/misc/dysnomia.nix b/nixos/modules/services/misc/dysnomia.nix
index ba74b18b6970..61ea822890ed 100644
--- a/nixos/modules/services/misc/dysnomia.nix
+++ b/nixos/modules/services/misc/dysnomia.nix
@@ -3,7 +3,7 @@
 with lib;
 
 let
-  cfg = config.services.dysnomia;
+  cfg = config.dysnomia;
 
   printProperties = properties:
     concatMapStrings (propertyName:
@@ -69,7 +69,7 @@ let
 in
 {
   options = {
-    services.dysnomia = {
+    dysnomia = {
 
       enable = mkOption {
         type = types.bool;
@@ -142,7 +142,7 @@ in
 
     environment.systemPackages = [ cfg.package ];
 
-    services.dysnomia.package = pkgs.dysnomia.override (origArgs: {
+    dysnomia.package = pkgs.dysnomia.override (origArgs: {
       enableApacheWebApplication = config.services.httpd.enable;
       enableAxis2WebService = config.services.tomcat.axis2.enable;
       enableEjabberdDump = config.services.ejabberd.enable;
@@ -153,7 +153,7 @@ in
       enableMongoDatabase = config.services.mongodb.enable;
     });
 
-    services.dysnomia.properties = {
+    dysnomia.properties = {
       hostname = config.networking.hostName;
       inherit (config.nixpkgs.localSystem) system;
 
@@ -171,7 +171,7 @@ in
       }}");
     };
 
-    services.dysnomia.containers = lib.recursiveUpdate ({
+    dysnomia.containers = lib.recursiveUpdate ({
       process = {};
       wrapper = {};
     }
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index 5ca879bf2664..c0eb882c58f3 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -88,7 +88,7 @@ in
       };
 
       maxJobs = mkOption {
-        type = types.int;
+        type = types.either types.int (types.enum ["auto"]);
         default = 1;
         example = 64;
         description = ''
diff --git a/nixos/modules/services/misc/redmine.nix b/nixos/modules/services/misc/redmine.nix
index 9a9424449f80..f763ba21d0b2 100644
--- a/nixos/modules/services/misc/redmine.nix
+++ b/nixos/modules/services/misc/redmine.nix
@@ -1,121 +1,124 @@
 { config, lib, pkgs, ... }:
 
-# TODO: support non-postgresql
-
 with lib;
 
 let
   cfg = config.services.redmine;
 
-  ruby = pkgs.ruby;
+  bundle = "${pkgs.redmine}/share/redmine/bin/bundle";
 
-  databaseYml = ''
+  databaseYml = pkgs.writeText "database.yml" ''
     production:
-      adapter: postgresql
-      database: ${cfg.databaseName}
-      host: ${cfg.databaseHost}
-      password: ${cfg.databasePassword}
-      username: ${cfg.databaseUsername}
-      encoding: utf8
+      adapter: ${cfg.database.type}
+      database: ${cfg.database.name}
+      host: ${cfg.database.host}
+      port: ${toString cfg.database.port}
+      username: ${cfg.database.user}
+      password: #dbpass#
   '';
 
-  configurationYml = ''
+  configurationYml = pkgs.writeText "configuration.yml" ''
     default:
-      # Absolute path to the directory where attachments are stored.
-      # The default is the 'files' directory in your Redmine instance.
-      # Your Redmine instance needs to have write permission on this
-      # directory.
-      # Examples:
-      # attachments_storage_path: /var/redmine/files
-      # attachments_storage_path: D:/redmine/files
-      attachments_storage_path: ${cfg.stateDir}/files
-
-      # Absolute path to the SCM commands errors (stderr) log file.
-      # The default is to log in the 'log' directory of your Redmine instance.
-      # Example:
-      # scm_stderr_log_file: /var/log/redmine_scm_stderr.log
-      scm_stderr_log_file: ${cfg.stateDir}/redmine_scm_stderr.log
-
-      ${cfg.extraConfig}
+      scm_subversion_command: ${pkgs.subversion}/bin/svn
+      scm_mercurial_command: ${pkgs.mercurial}/bin/hg
+      scm_git_command: ${pkgs.gitAndTools.git}/bin/git
+      scm_cvs_command: ${pkgs.cvs}/bin/cvs
+      scm_bazaar_command: ${pkgs.bazaar}/bin/bzr
+      scm_darcs_command: ${pkgs.darcs}/bin/darcs
+
+    ${cfg.extraConfig}
   '';
 
-  unpackTheme = unpack "theme";
-  unpackPlugin = unpack "plugin";
-  unpack = id: (name: source:
-    pkgs.stdenv.mkDerivation {
-      name = "redmine-${id}-${name}";
-      buildInputs = [ pkgs.unzip ];
-      buildCommand = ''
-        mkdir -p $out
-        cd $out
-        unpackFile ${source}
-      '';
-    });
-
-in {
+in
 
+{
   options = {
     services.redmine = {
       enable = mkOption {
         type = types.bool;
         default = false;
-        description = ''
-          Enable the redmine service.
-        '';
+        description = "Enable the Redmine service.";
       };
 
-      stateDir = mkOption {
+      user = mkOption {
         type = types.str;
-        default = "/var/redmine";
-        description = "The state directory, logs and plugins are stored here";
-      };
-
-      extraConfig = mkOption {
-        type = types.lines;
-        default = "";
-        description = "Extra configuration in configuration.yml";
-      };
-
-      themes = mkOption {
-        type = types.attrsOf types.path;
-        default = {};
-        description = "Set of themes";
+        default = "redmine";
+        description = "User under which Redmine is ran.";
       };
 
-      plugins = mkOption {
-        type = types.attrsOf types.path;
-        default = {};
-        description = "Set of plugins";
+      group = mkOption {
+        type = types.str;
+        default = "redmine";
+        description = "Group under which Redmine is ran.";
       };
 
-      #databaseType = mkOption {
-      #  type = types.str;
-      #  default = "postgresql";
-      #  description = "Type of database";
-      #};
-
-      databaseHost = mkOption {
+      stateDir = mkOption {
         type = types.str;
-        default = "127.0.0.1";
-        description = "Database hostname";
+        default = "/var/lib/redmine";
+        description = "The state directory, logs and plugins are stored here.";
       };
 
-      databasePassword = mkOption {
-        type = types.str;
+      extraConfig = mkOption {
+        type = types.lines;
         default = "";
-        description = "Database user password";
-      };
+        description = ''
+          Extra configuration in configuration.yml.
 
-      databaseName = mkOption {
-        type = types.str;
-        default = "redmine";
-        description = "Database name";
+          See https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration
+        '';
       };
 
-      databaseUsername = mkOption {
-        type = types.str;
-        default = "redmine";
-        description = "Database user";
+      database = {
+        type = mkOption {
+          type = types.enum [ "mysql2" "postgresql" ];
+          example = "postgresql";
+          default = "mysql2";
+          description = "Database engine to use.";
+        };
+
+        host = mkOption {
+          type = types.str;
+          default = "127.0.0.1";
+          description = "Database host address.";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = 3306;
+          description = "Database host port.";
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "redmine";
+          description = "Database name.";
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "redmine";
+          description = "Database user.";
+        };
+
+        password = mkOption {
+          type = types.str;
+          default = "";
+          description = ''
+            The password corresponding to <option>database.user</option>.
+            Warning: this is stored in cleartext in the Nix store!
+            Use <option>database.passwordFile</option> instead.
+          '';
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/keys/redmine-dbpassword";
+          description = ''
+            A file containing the password corresponding to
+            <option>database.user</option>.
+          '';
+        };
       };
     };
   };
@@ -123,99 +126,106 @@ in {
   config = mkIf cfg.enable {
 
     assertions = [
-      { assertion = cfg.databasePassword != "";
-        message = "services.redmine.databasePassword must be set";
+      { assertion = cfg.database.passwordFile != null || cfg.database.password != "";
+        message = "either services.redmine.database.passwordFile or services.redmine.database.password must be set";
       }
     ];
 
-    users.users = [
-      { name = "redmine";
-        group = "redmine";
-        uid = config.ids.uids.redmine;
-      } ];
-
-    users.groups = [
-      { name = "redmine";
-        gid = config.ids.gids.redmine;
-      } ];
+    environment.systemPackages = [ pkgs.redmine ];
 
     systemd.services.redmine = {
-      after = [ "network.target" "postgresql.service" ];
+      after = [ "network.target" (if cfg.database.type == "mysql2" then "mysql.service" else "postgresql.service") ];
       wantedBy = [ "multi-user.target" ];
+      environment.HOME = "${pkgs.redmine}/share/redmine";
       environment.RAILS_ENV = "production";
-      environment.RAILS_ETC = "${cfg.stateDir}/config";
-      environment.RAILS_LOG = "${cfg.stateDir}/log";
-      environment.RAILS_VAR = "${cfg.stateDir}/var";
       environment.RAILS_CACHE = "${cfg.stateDir}/cache";
-      environment.RAILS_PLUGINS = "${cfg.stateDir}/plugins";
-      environment.RAILS_PUBLIC = "${cfg.stateDir}/public";
-      environment.RAILS_TMP = "${cfg.stateDir}/tmp";
-      environment.SCHEMA = "${cfg.stateDir}/cache/schema.db";
-      environment.HOME = "${pkgs.redmine}/share/redmine";
       environment.REDMINE_LANG = "en";
-      environment.GEM_HOME = "${pkgs.redmine}/share/redmine/vendor/bundle/ruby/1.9.1";
-      environment.GEM_PATH = "${pkgs.bundler}/${pkgs.bundler.ruby.gemPath}";
+      environment.SCHEMA = "${cfg.stateDir}/cache/schema.db";
       path = with pkgs; [
         imagemagickBig
-        subversion
-        mercurial
-        cvs
-        config.services.postgresql.package
         bazaar
+        cvs
+        darcs
         gitAndTools.git
-        # once we build binaries for darc enable it
-        #darcs
+        mercurial
+        subversion
       ];
       preStart = ''
-        # TODO: use env vars
-        for i in plugins public/plugin_assets db files log config cache var/files tmp; do
+        # start with a fresh config directory every time
+        rm -rf ${cfg.stateDir}/config
+        cp -r ${pkgs.redmine}/share/redmine/config.dist ${cfg.stateDir}/config
+
+        # create the basic state directory layout pkgs.redmine expects
+        mkdir -p /run/redmine
+
+        for i in config files log plugins tmp; do
           mkdir -p ${cfg.stateDir}/$i
+          ln -fs ${cfg.stateDir}/$i /run/redmine/$i
         done
 
-        chown -R redmine:redmine ${cfg.stateDir}
-        chmod -R 755 ${cfg.stateDir}
+        # ensure cache directory exists for db:migrate command
+        mkdir -p ${cfg.stateDir}/cache
 
-        rm -rf ${cfg.stateDir}/public/*
-        cp -R ${pkgs.redmine}/share/redmine/public/* ${cfg.stateDir}/public/
-        for theme in ${concatStringsSep " " (mapAttrsToList unpackTheme cfg.themes)}; do
-          ln -fs $theme/* ${cfg.stateDir}/public/themes/
-        done
+        # link in the application configuration
+        ln -fs ${configurationYml} ${cfg.stateDir}/config/configuration.yml
 
-        rm -rf ${cfg.stateDir}/plugins/*
-        for plugin in ${concatStringsSep " " (mapAttrsToList unpackPlugin cfg.plugins)}; do
-          ln -fs $plugin/* ${cfg.stateDir}/plugins/''${plugin##*-redmine-plugin-}
-        done
+        chmod -R ug+rwX,o-rwx+x ${cfg.stateDir}/
 
-        ln -fs ${pkgs.writeText "database.yml" databaseYml} ${cfg.stateDir}/config/database.yml
-        ln -fs ${pkgs.writeText "configuration.yml" configurationYml} ${cfg.stateDir}/config/configuration.yml
+        # handle database.passwordFile
+        DBPASS=$(head -n1 ${cfg.database.passwordFile})
+        cp -f ${databaseYml} ${cfg.stateDir}/config/database.yml
+        sed -e "s,#dbpass#,$DBPASS,g" -i ${cfg.stateDir}/config/database.yml
+        chmod 440 ${cfg.stateDir}/config/database.yml
 
-        if [ "${cfg.databaseHost}" = "127.0.0.1" ]; then
-          if ! test -e "${cfg.stateDir}/db-created"; then
-            psql postgres -c "CREATE ROLE redmine WITH LOGIN NOCREATEDB NOCREATEROLE ENCRYPTED PASSWORD '${cfg.databasePassword}'"
-            ${config.services.postgresql.package}/bin/createdb --owner redmine redmine || true
-            touch "${cfg.stateDir}/db-created"
-          fi
+        # generate a secret token if required
+        if ! test -e "${cfg.stateDir}/config/initializers/secret_token.rb"; then
+          ${bundle} exec rake generate_secret_token
+          chmod 440 ${cfg.stateDir}/config/initializers/secret_token.rb
         fi
 
-        cd ${pkgs.redmine}/share/redmine/
-        ${ruby}/bin/rake db:migrate
-        ${ruby}/bin/rake redmine:plugins:migrate
-        ${ruby}/bin/rake redmine:load_default_data
-        ${ruby}/bin/rake generate_secret_token
+        # ensure everything is owned by ${cfg.user}
+        chown -R ${cfg.user}:${cfg.group} ${cfg.stateDir}
+
+        ${bundle} exec rake db:migrate
+        ${bundle} exec rake redmine:load_default_data
       '';
 
       serviceConfig = {
         PermissionsStartOnly = true; # preStart must be run as root
         Type = "simple";
-        User = "redmine";
-        Group = "redmine";
+        User = cfg.user;
+        Group = cfg.group;
         TimeoutSec = "300";
         WorkingDirectory = "${pkgs.redmine}/share/redmine";
-        ExecStart="${ruby}/bin/ruby ${pkgs.redmine}/share/redmine/script/rails server webrick -e production -P ${cfg.stateDir}/redmine.pid";
+        ExecStart="${bundle} exec rails server webrick -e production -P ${cfg.stateDir}/redmine.pid";
       };
 
     };
 
+    users.extraUsers = optionalAttrs (cfg.user == "redmine") (singleton
+      { name = "redmine";
+        group = cfg.group;
+        home = cfg.stateDir;
+        createHome = true;
+        uid = config.ids.uids.redmine;
+      });
+
+    users.extraGroups = optionalAttrs (cfg.group == "redmine") (singleton
+      { name = "redmine";
+        gid = config.ids.gids.redmine;
+      });
+
+    warnings = optional (cfg.database.password != "")
+      ''config.services.redmine.database.password will be stored as plaintext
+      in the Nix store. Use database.passwordFile instead.'';
+
+    # Create database passwordFile default when password is configured.
+    services.redmine.database.passwordFile =
+      (mkDefault (toString (pkgs.writeTextFile {
+        name = "redmine-database-password";
+        text = cfg.database.password;
+      })));
+
   };
 
 }
diff --git a/nixos/modules/services/misc/synergy.nix b/nixos/modules/services/misc/synergy.nix
index 7e8eadbe5f37..b89cb41ac3ad 100644
--- a/nixos/modules/services/misc/synergy.nix
+++ b/nixos/modules/services/misc/synergy.nix
@@ -83,20 +83,20 @@ in
 
   config = mkMerge [
     (mkIf cfgC.enable {
-      systemd.services."synergy-client" = {
-        after = [ "network.target" ];
+      systemd.user.services."synergy-client" = {
+        after = [ "network.target" "graphical-session.target" ];
         description = "Synergy client";
-        wantedBy = optional cfgC.autoStart "multi-user.target";
+        wantedBy = optional cfgC.autoStart "graphical-session.target";
         path = [ pkgs.synergy ];
         serviceConfig.ExecStart = ''${pkgs.synergy}/bin/synergyc -f ${optionalString (cfgC.screenName != "") "-n ${cfgC.screenName}"} ${cfgC.serverAddress}'';
         serviceConfig.Restart = "on-failure";
       };
     })
     (mkIf cfgS.enable {
-      systemd.services."synergy-server" = {
-        after = [ "network.target" ];
+      systemd.user.services."synergy-server" = {
+        after = [ "network.target" "graphical-session.target" ];
         description = "Synergy server";
-        wantedBy = optional cfgS.autoStart "multi-user.target";
+        wantedBy = optional cfgS.autoStart "graphical-session.target";
         path = [ pkgs.synergy ];
         serviceConfig.ExecStart = ''${pkgs.synergy}/bin/synergys -c ${cfgS.configFile} -f ${optionalString (cfgS.address != "") "-a ${cfgS.address}"} ${optionalString (cfgS.screenName != "") "-n ${cfgS.screenName}" }'';
         serviceConfig.Restart = "on-failure";
diff --git a/nixos/modules/services/monitoring/datadog-agent.nix b/nixos/modules/services/monitoring/datadog-agent.nix
new file mode 100644
index 000000000000..f8ee34ebdf88
--- /dev/null
+++ b/nixos/modules/services/monitoring/datadog-agent.nix
@@ -0,0 +1,236 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.datadog-agent;
+
+  ddConf = {
+    dd_url              = "https://app.datadoghq.com";
+    skip_ssl_validation = "no";
+    api_key             = "";
+    confd_path          = "/etc/datadog-agent/conf.d";
+    additional_checksd  = "/etc/datadog-agent/checks.d";
+    use_dogstatsd       = "yes";
+  }
+  // optionalAttrs (cfg.logLevel != null) { log_level = cfg.logLevel; }
+  // optionalAttrs (cfg.hostname != null) { inherit (cfg) hostname; }
+  // optionalAttrs (cfg.tags != null ) { tags = concatStringsSep ", " cfg.tags; }
+  // cfg.extraConfig;
+
+  # Generate Datadog configuration files for each configured checks.
+  # This works because check configurations have predictable paths,
+  # and because JSON is a valid subset of YAML.
+  makeCheckConfigs = entries: mapAttrsToList (name: conf: {
+    source = pkgs.writeText "${name}-check-conf.yaml" (builtins.toJSON conf);
+    target = "datadog-agent/conf.d/${name}.d/conf.yaml";
+  }) entries;
+
+  defaultChecks = {
+    disk = cfg.diskCheck;
+    network = cfg.networkCheck;
+  };
+
+  # Assemble all check configurations and the top-level agent
+  # configuration.
+  etcfiles = with pkgs; with builtins; [{
+    source = writeText "datadog.yaml" (toJSON ddConf);
+    target = "datadog-agent/datadog.yaml";
+  }] ++ makeCheckConfigs (cfg.checks // defaultChecks);
+
+  # Apply the configured extraIntegrations to the provided agent
+  # package. See the documentation of `dd-agent/integrations-core.nix`
+  # for detailed information on this.
+  datadogPkg = cfg.package.overrideAttrs(_: {
+    python = (pkgs.datadog-integrations-core cfg.extraIntegrations).python;
+  });
+in {
+  options.services.datadog-agent = {
+    enable = mkOption {
+      description = ''
+        Whether to enable the datadog-agent v6 monitoring service
+      '';
+      default = false;
+      type = types.bool;
+    };
+
+    package = mkOption {
+      default = pkgs.datadog-agent;
+      defaultText = "pkgs.datadog-agent";
+      description = ''
+        Which DataDog v6 agent package to use. Note that the provided
+        package is expected to have an overridable `python`-attribute
+        which configures the Python environment with the Datadog
+        checks.
+      '';
+      type = types.package;
+    };
+
+    apiKeyFile = mkOption {
+      description = ''
+        Path to a file containing the Datadog API key to associate the
+        agent with your account.
+      '';
+      example = "/run/keys/datadog_api_key";
+      type = types.path;
+    };
+
+    tags = mkOption {
+      description = "The tags to mark this Datadog agent";
+      example = [ "test" "service" ];
+      default = null;
+      type = types.nullOr (types.listOf types.str);
+    };
+
+    hostname = mkOption {
+      description = "The hostname to show in the Datadog dashboard (optional)";
+      default = null;
+      example = "mymachine.mydomain";
+      type = types.uniq (types.nullOr types.string);
+    };
+
+    logLevel = mkOption {
+      description = "Logging verbosity.";
+      default = null;
+      type = types.nullOr (types.enum ["DEBUG" "INFO" "WARN" "ERROR"]);
+    };
+
+    extraIntegrations = mkOption {
+      default = {};
+      type    = types.attrs;
+
+      description = ''
+        Extra integrations from the Datadog core-integrations
+        repository that should be built and included.
+
+        By default the included integrations are disk, mongo, network,
+        nginx and postgres.
+
+        To include additional integrations the name of the derivation
+        and a function to filter its dependencies from the Python
+        package set must be provided.
+      '';
+
+      example = {
+        ntp = (pythonPackages: [ pythonPackages.ntplib ]);
+      };
+    };
+
+    extraConfig = mkOption {
+      default = {};
+      type = types.attrs;
+      description = ''
+        Extra configuration options that will be merged into the
+        main config file <filename>datadog.yaml</filename>.
+      '';
+     };
+
+    checks = mkOption {
+      description = ''
+        Configuration for all Datadog checks. Keys of this attribute
+        set will be used as the name of the check to create the
+        appropriate configuration in `conf.d/$check.d/conf.yaml`.
+
+        The configuration is converted into JSON from the plain Nix
+        language configuration, meaning that you should write
+        configuration adhering to Datadog's documentation - but in Nix
+        language.
+
+        Refer to the implementation of this module (specifically the
+        definition of `defaultChecks`) for an example.
+
+        Note: The 'disk' and 'network' check are configured in
+        separate options because they exist by default. Attempting to
+        override their configuration here will have no effect.
+      '';
+
+      example = {
+        http_check = {
+          init_config = null; # sic!
+          instances = [
+            {
+              name = "some-service";
+              url = "http://localhost:1337/healthz";
+              tags = [ "some-service" ];
+            }
+          ];
+        };
+      };
+
+      default = {};
+
+      # sic! The structure of the values is up to the check, so we can
+      # not usefully constrain the type further.
+      type = with types; attrsOf attrs;
+    };
+
+    diskCheck = mkOption {
+      description = "Disk check config";
+      type = types.attrs;
+      default = {
+        init_config = {};
+        instances = [ { use-mount = "no"; } ];
+      };
+    };
+
+    networkCheck = mkOption {
+      description = "Network check config";
+      type = types.attrs;
+      default = {
+        init_config = {};
+        # Network check only supports one configured instance
+        instances = [ { collect_connection_state = false;
+          excluded_interfaces = [ "lo" "lo0" ]; } ];
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ datadogPkg pkgs.sysstat pkgs.procps ];
+
+    users.extraUsers.datadog = {
+      description = "Datadog Agent User";
+      uid = config.ids.uids.datadog;
+      group = "datadog";
+      home = "/var/log/datadog/";
+      createHome = true;
+    };
+
+    users.extraGroups.datadog.gid = config.ids.gids.datadog;
+
+    systemd.services = let
+      makeService = attrs: recursiveUpdate {
+        path = [ datadogPkg pkgs.python pkgs.sysstat pkgs.procps ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          User = "datadog";
+          Group = "datadog";
+          Restart = "always";
+          RestartSec = 2;
+          PrivateTmp = true;
+        };
+        restartTriggers = [ datadogPkg ] ++ map (etc: etc.source) etcfiles;
+      } attrs;
+    in {
+      datadog-agent = makeService {
+        description = "Datadog agent monitor";
+        preStart = ''
+          chown -R datadog: /etc/datadog-agent
+          rm -f /etc/datadog-agent/auth_token
+        '';
+        script = ''
+          export DD_API_KEY=$(head -n 1 ${cfg.apiKeyFile})
+          exec ${datadogPkg}/bin/agent start -c /etc/datadog-agent/datadog.yaml
+        '';
+        serviceConfig.PermissionsStartOnly = true;
+      };
+
+      dd-jmxfetch = lib.mkIf (lib.hasAttr "jmx" cfg.checks) (makeService {
+        description = "Datadog JMX Fetcher";
+        path = [ datadogPkg pkgs.python pkgs.sysstat pkgs.procps pkgs.jdk ];
+        serviceConfig.ExecStart = "${datadogPkg}/bin/dd-jmxfetch";
+      });
+    };
+
+    environment.etc = etcfiles;
+  };
+}
diff --git a/nixos/modules/services/monitoring/dd-agent/dd-agent.nix b/nixos/modules/services/monitoring/dd-agent/dd-agent.nix
index cf65b6c28cf2..abc8d65d58f2 100644
--- a/nixos/modules/services/monitoring/dd-agent/dd-agent.nix
+++ b/nixos/modules/services/monitoring/dd-agent/dd-agent.nix
@@ -114,13 +114,22 @@ let
 in {
   options.services.dd-agent = {
     enable = mkOption {
-      description = "Whether to enable the dd-agent montioring service";
+      description = ''
+        Whether to enable the dd-agent v5 monitoring service.
+        For datadog-agent v6, see <option>services.datadog-agent.enable</option>.
+      '';
       default = false;
       type = types.bool;
     };
 
     api_key = mkOption {
-      description = "The Datadog API key to associate the agent with your account";
+      description = ''
+        The Datadog API key to associate the agent with your account.
+
+        Warning: this key is stored in cleartext within the world-readable
+        Nix store! Consider using the new v6
+        <option>services.datadog-agent</option> module instead.
+      '';
       example = "ae0aa6a8f08efa988ba0a17578f009ab";
       type = types.str;
     };
@@ -188,48 +197,41 @@ in {
 
     users.groups.datadog.gid = config.ids.gids.datadog;
 
-    systemd.services.dd-agent = {
-      description = "Datadog agent monitor";
-      path = [ pkgs."dd-agent" pkgs.python pkgs.sysstat pkgs.procps pkgs.gohai ];
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        ExecStart = "${pkgs.dd-agent}/bin/dd-agent foreground";
-        User = "datadog";
-        Group = "datadog";
-        Restart = "always";
-        RestartSec = 2;
+    systemd.services = let
+      makeService = attrs: recursiveUpdate {
+        path = [ pkgs.dd-agent pkgs.python pkgs.sysstat pkgs.procps pkgs.gohai ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          User = "datadog";
+          Group = "datadog";
+          Restart = "always";
+          RestartSec = 2;
+          PrivateTmp = true;
+        };
+        restartTriggers = [ pkgs.dd-agent ddConf diskConfig networkConfig postgresqlConfig nginxConfig mongoConfig jmxConfig processConfig ];
+      } attrs;
+    in {
+      dd-agent = makeService {
+        description = "Datadog agent monitor";
+        serviceConfig.ExecStart = "${pkgs.dd-agent}/bin/dd-agent foreground";
       };
-      restartTriggers = [ pkgs.dd-agent ddConf diskConfig networkConfig postgresqlConfig nginxConfig mongoConfig jmxConfig processConfig ];
-    };
 
-    systemd.services.dogstatsd = {
-      description = "Datadog statsd";
-      path = [ pkgs."dd-agent" pkgs.python pkgs.procps ];
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        ExecStart = "${pkgs.dd-agent}/bin/dogstatsd start";
-        User = "datadog";
-        Group = "datadog";
-        Type = "forking";
-        PIDFile = "/tmp/dogstatsd.pid";
-        Restart = "always";
-        RestartSec = 2;
+      dogstatsd = makeService {
+        description = "Datadog statsd";
+        environment.TMPDIR = "/run/dogstatsd";
+        serviceConfig = {
+          ExecStart = "${pkgs.dd-agent}/bin/dogstatsd start";
+          Type = "forking";
+          PIDFile = "/run/dogstatsd/dogstatsd.pid";
+          RuntimeDirectory = "dogstatsd";
+        };
       };
-      restartTriggers = [ pkgs.dd-agent ddConf diskConfig networkConfig postgresqlConfig nginxConfig mongoConfig jmxConfig processConfig ];
-    };
 
-    systemd.services.dd-jmxfetch = lib.mkIf (cfg.jmxConfig != null) {
-      description = "Datadog JMX Fetcher";
-      path = [ pkgs."dd-agent" pkgs.python pkgs.sysstat pkgs.procps pkgs.jdk ];
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        ExecStart = "${pkgs.dd-agent}/bin/dd-jmxfetch";
-        User = "datadog";
-        Group = "datadog";
-        Restart = "always";
-        RestartSec = 2;
+      dd-jmxfetch = lib.mkIf (cfg.jmxConfig != null) {
+        description = "Datadog JMX Fetcher";
+        path = [ pkgs.dd-agent pkgs.python pkgs.sysstat pkgs.procps pkgs.jdk ];
+        serviceConfig.ExecStart = "${pkgs.dd-agent}/bin/dd-jmxfetch";
       };
-      restartTriggers = [ pkgs.dd-agent ddConf diskConfig networkConfig postgresqlConfig nginxConfig mongoConfig jmxConfig ];
     };
 
     environment.etc = etcfiles;
diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix
index cdfd746bc5a3..cdc98b407e90 100644
--- a/nixos/modules/services/monitoring/graphite.nix
+++ b/nixos/modules/services/monitoring/graphite.nix
@@ -57,12 +57,6 @@ let
     --nodaemon --syslog --prefix=${name} --pidfile /run/${name}/${name}.pid ${name}
   '';
 
-  mkPidFileDir = name: ''
-    mkdir -p /run/${name}
-    chmod 0700 /run/${name}
-    chown -R graphite:graphite /run/${name}
-  '';
-
   carbonEnv = {
     PYTHONPATH = let
       cenv = pkgs.python.buildEnv.override {
@@ -412,18 +406,16 @@ in {
         after = [ "network.target" ];
         environment = carbonEnv;
         serviceConfig = {
+          RuntimeDirectory = name;
           ExecStart = "${pkgs.pythonPackages.twisted}/bin/twistd ${carbonOpts name}";
           User = "graphite";
           Group = "graphite";
           PermissionsStartOnly = true;
           PIDFile="/run/${name}/${name}.pid";
         };
-        preStart = mkPidFileDir name + ''
-
-          mkdir -p ${cfg.dataDir}/whisper
-          chmod 0700 ${cfg.dataDir}/whisper
-          chown graphite:graphite ${cfg.dataDir}
-          chown graphite:graphite ${cfg.dataDir}/whisper
+        preStart = ''
+          install -dm0700 -o graphite -g graphite ${cfg.dataDir}
+          install -dm0700 -o graphite -g graphite ${cfg.dataDir}/whisper
         '';
       };
     })
@@ -436,12 +428,12 @@ in {
         after = [ "network.target" ];
         environment = carbonEnv;
         serviceConfig = {
+          RuntimeDirectory = name;
           ExecStart = "${pkgs.pythonPackages.twisted}/bin/twistd ${carbonOpts name}";
           User = "graphite";
           Group = "graphite";
           PIDFile="/run/${name}/${name}.pid";
         };
-        preStart = mkPidFileDir name;
       };
     })
 
@@ -452,12 +444,12 @@ in {
         after = [ "network.target" ];
         environment = carbonEnv;
         serviceConfig = {
+          RuntimeDirectory = name;
           ExecStart = "${pkgs.pythonPackages.twisted}/bin/twistd ${carbonOpts name}";
           User = "graphite";
           Group = "graphite";
           PIDFile="/run/${name}/${name}.pid";
         };
-        preStart = mkPidFileDir name;
       };
     })
 
diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix
index eefddf5a206b..edcaa10d969d 100644
--- a/nixos/modules/services/monitoring/netdata.nix
+++ b/nixos/modules/services/monitoring/netdata.nix
@@ -14,6 +14,10 @@ let
     global = {
       "plugins directory" = "${wrappedPlugins}/libexec/netdata/plugins.d ${pkgs.netdata}/libexec/netdata/plugins.d";
     };
+    web = {
+      "web files owner" = "root";
+      "web files group" = "root";
+    };
   };
   mkConfig = generators.toINI {} (recursiveUpdate localConfig cfg.config);
   configFile = pkgs.writeText "netdata.conf" (if cfg.configText != null then cfg.configText else mkConfig);
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
index 9c58a15bcd73..1d5f400250fd 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -73,7 +73,7 @@ let
       description = ''
         Specify a filter for iptables to use when
         <option>services.prometheus.exporters.${name}.openFirewall</option>
-        is true. It is used as `ip46tables -I INPUT <option>firewallFilter</option> -j ACCEPT`.
+        is true. It is used as `ip46tables -I nixos-fw <option>firewallFilter</option> -j nixos-fw-accept`.
       '';
     };
     user = mkOption {
@@ -116,9 +116,10 @@ let
 
   mkExporterConf = { name, conf, serviceOpts }:
     mkIf conf.enable {
-      networking.firewall.extraCommands = mkIf conf.openFirewall ''
-        ip46tables -I INPUT ${conf.firewallFilter} -j ACCEPT
-      '';
+      networking.firewall.extraCommands = mkIf conf.openFirewall (concatStrings [
+        "ip46tables -I nixos-fw ${conf.firewallFilter} "
+        "-m comment --comment ${name}-exporter -j nixos-fw-accept"
+      ]);
       systemd.services."prometheus-${name}-exporter" = mkMerge ([{
         wantedBy = [ "multi-user.target" ];
         after = [ "network.target" ];
diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix
index b23266e8d43a..10dc58311212 100644
--- a/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -214,12 +214,10 @@ in
             }
           ];
         # Always provide a smb.conf to shut up programs like smbclient and smbspool.
-        environment.etc = singleton
-          { source =
-              if cfg.enable then configFile
-              else pkgs.writeText "smb-dummy.conf" "# Samba is disabled.";
-            target = "samba/smb.conf";
-          };
+        environment.etc."samba/smb.conf".source = mkOptionDefault (
+          if cfg.enable then configFile
+          else pkgs.writeText "smb-dummy.conf" "# Samba is disabled."
+        );
       }
 
       (mkIf cfg.enable {
diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
index de0aa1a2c2c3..efdbca5d52e8 100644
--- a/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -161,8 +161,9 @@ in
       { description = "DHCP Client";
 
         wantedBy = [ "multi-user.target" ] ++ optional (!hasDefaultGatewaySet) "network-online.target";
-        after = [ "network.target" ];
-        wants = [ "network.target" ];
+        wants = [ "network.target" "systemd-udev-settle.service" ];
+        before = [ "network.target" ];
+        after = [ "systemd-udev-settle.service" ];
 
         # Stopping dhcpcd during a reconfiguration is undesirable
         # because it brings down the network interfaces configured by
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix
index 4bae05b6dd30..c788528fa47b 100644
--- a/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixos/modules/services/networking/wpa_supplicant.nix
@@ -8,6 +8,7 @@ let
     ${optionalString cfg.userControlled.enable ''
       ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=${cfg.userControlled.group}
       update_config=1''}
+    ${cfg.extraConfig}
     ${concatStringsSep "\n" (mapAttrsToList (ssid: config: with config; let
       key = if psk != null
         then ''"${psk}"''
@@ -165,6 +166,17 @@ in {
           description = "Members of this group can control wpa_supplicant.";
         };
       };
+      extraConfig = mkOption {
+        type = types.str;
+        default = "";
+        example = ''
+          p2p_disabled=1
+        '';
+        description = ''
+          Extra lines appended to the configuration file.
+          See wpa_supplicant.conf(5) for available options.
+        '';
+      };
     };
   };
 
diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix
index 4c1ee75d536c..a4cd368397e7 100644
--- a/nixos/modules/services/networking/zerotierone.nix
+++ b/nixos/modules/services/networking/zerotierone.nix
@@ -17,6 +17,15 @@ in
     '';
   };
 
+  options.services.zerotierone.port = mkOption {
+    default = 9993;
+    example = 9993;
+    type = types.int;
+    description = ''
+      Network port used by ZeroTier.
+    '';
+  };
+
   options.services.zerotierone.package = mkOption {
     default = pkgs.zerotierone;
     defaultText = "pkgs.zerotierone";
@@ -40,7 +49,7 @@ in
         touch "/var/lib/zerotier-one/networks.d/${netId}.conf"
       '') cfg.joinNetworks);
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/zerotier-one";
+        ExecStart = "${cfg.package}/bin/zerotier-one -p${toString cfg.port}";
         Restart = "always";
         KillMode = "process";
       };
@@ -49,8 +58,8 @@ in
     # ZeroTier does not issue DHCP leases, but some strangers might...
     networking.dhcpcd.denyInterfaces = [ "zt*" ];
 
-    # ZeroTier receives UDP transmissions on port 9993 by default
-    networking.firewall.allowedUDPPorts = [ 9993 ];
+    # ZeroTier receives UDP transmissions
+    networking.firewall.allowedUDPPorts = [ cfg.port ];
 
     environment.systemPackages = [ cfg.package ];
   };
diff --git a/nixos/modules/services/security/certmgr.nix b/nixos/modules/services/security/certmgr.nix
new file mode 100644
index 000000000000..22d5817ec4f0
--- /dev/null
+++ b/nixos/modules/services/security/certmgr.nix
@@ -0,0 +1,194 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.certmgr;
+
+  specs = mapAttrsToList (n: v: rec {
+    name = n + ".json";
+    path = if isAttrs v then pkgs.writeText name (builtins.toJSON v) else v;
+  }) cfg.specs;
+
+  allSpecs = pkgs.linkFarm "certmgr.d" specs;
+
+  certmgrYaml = pkgs.writeText "certmgr.yaml" (builtins.toJSON {
+    dir = allSpecs;
+    default_remote = cfg.defaultRemote;
+    svcmgr = cfg.svcManager;
+    before = cfg.validMin;
+    interval = cfg.renewInterval;
+    inherit (cfg) metricsPort metricsAddress;
+  });
+
+  specPaths = map dirOf (concatMap (spec:
+    if isAttrs spec then
+      collect isString (filterAttrsRecursive (n: v: isAttrs v || n == "path") spec)
+    else
+      [ spec ]
+  ) (attrValues cfg.specs));
+
+  preStart = ''
+    ${concatStringsSep " \\\n" (["mkdir -p"] ++ map escapeShellArg specPaths)}
+    ${pkgs.certmgr}/bin/certmgr -f ${certmgrYaml} check
+  '';
+in
+{
+  options.services.certmgr = {
+    enable = mkEnableOption "certmgr";
+
+    defaultRemote = mkOption {
+      type = types.str;
+      default = "127.0.0.1:8888";
+      description = "The default CA host:port to use.";
+    };
+
+    validMin = mkOption {
+      default = "72h";
+      type = types.str;
+      description = "The interval before a certificate expires to start attempting to renew it.";
+    };
+
+    renewInterval = mkOption {
+      default = "30m";
+      type = types.str;
+      description = "How often to check certificate expirations and how often to update the cert_next_expires metric.";
+    };
+
+    metricsAddress = mkOption {
+      default = "127.0.0.1";
+      type = types.str;
+      description = "The address for the Prometheus HTTP endpoint.";
+    };
+
+    metricsPort = mkOption {
+      default = 9488;
+      type = types.ints.u16;
+      description = "The port for the Prometheus HTTP endpoint.";
+    };
+
+    specs = mkOption {
+      default = {};
+      example = literalExample ''
+      {
+        exampleCert =
+        let
+          domain = "example.com";
+          secret = name: "/var/lib/secrets/''${name}.pem";
+        in {
+          service = "nginx";
+          action = "reload";
+          authority = {
+            file.path = secret "ca";
+          };
+          certificate = {
+            path = secret domain;
+          };
+          private_key = {
+            owner = "root";
+            group = "root";
+            mode = "0600";
+            path = secret "''${domain}-key";
+          };
+          request = {
+            CN = domain;
+            hosts = [ "mail.''${domain}" "www.''${domain}" ];
+            key = {
+              algo = "rsa";
+              size = 2048;
+            };
+            names = {
+              O = "Example Organization";
+              C = "USA";
+            };
+          };
+        };
+        otherCert = "/var/certmgr/specs/other-cert.json";
+      }
+      '';
+      type = with types; attrsOf (either (submodule {
+        options = {
+          service = mkOption {
+            type = nullOr str;
+            default = null;
+            description = "The service on which to perform &lt;action&gt; after fetching.";
+          };
+
+          action = mkOption {
+            type = addCheck str (x: cfg.svcManager == "command" || elem x ["restart" "reload" "nop"]);
+            default = "nop";
+            description = "The action to take after fetching.";
+          };
+
+          # These ought all to be specified according to certmgr spec def.
+          authority = mkOption {
+            type = attrs;
+            description = "certmgr spec authority object.";
+          };
+
+          certificate = mkOption {
+            type = nullOr attrs;
+            description = "certmgr spec certificate object.";
+          };
+
+          private_key = mkOption {
+            type = nullOr attrs;
+            description = "certmgr spec private_key object.";
+          };
+
+          request = mkOption {
+            type = nullOr attrs;
+            description = "certmgr spec request object.";
+          };
+        };
+    }) path);
+      description = ''
+        Certificate specs as described by:
+        <link xlink:href="https://github.com/cloudflare/certmgr#certificate-specs" />
+        These will be added to the Nix store, so they will be world readable.
+      '';
+    };
+
+    svcManager = mkOption {
+      default = "systemd";
+      type = types.enum [ "circus" "command" "dummy" "openrc" "systemd" "sysv" ];
+      description = ''
+        This specifies the service manager to use for restarting or reloading services.
+        See: <link xlink:href="https://github.com/cloudflare/certmgr#certmgryaml" />.
+        For how to use the "command" service manager in particular,
+        see: <link xlink:href="https://github.com/cloudflare/certmgr#command-svcmgr-and-how-to-use-it" />.
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = cfg.specs != {};
+        message = "Certmgr specs cannot be empty.";
+      }
+      {
+        assertion = !any (hasAttrByPath [ "authority" "auth_key" ]) (attrValues cfg.specs);
+        message = ''
+          Inline services.certmgr.specs are added to the Nix store rendering them world readable.
+          Specify paths as specs, if you want to use include auth_key - or use the auth_key_file option."
+        '';
+      }
+    ];
+
+    systemd.services.certmgr = {
+      description = "certmgr";
+      path = mkIf (cfg.svcManager == "command") [ pkgs.bash ];
+      after = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      inherit preStart;
+
+      serviceConfig = {
+        Restart = "always";
+        RestartSec = "10s";
+        ExecStart = "${pkgs.certmgr}/bin/certmgr -f ${certmgrYaml}";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/security/cfssl.nix b/nixos/modules/services/security/cfssl.nix
new file mode 100644
index 000000000000..ee6d5d91fe15
--- /dev/null
+++ b/nixos/modules/services/security/cfssl.nix
@@ -0,0 +1,209 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.cfssl;
+in {
+  options.services.cfssl = {
+    enable = mkEnableOption "the CFSSL CA api-server";
+
+    dataDir = mkOption {
+      default = "/var/lib/cfssl";
+      type = types.path;
+      description = "Cfssl work directory.";
+    };
+
+    address = mkOption {
+      default = "127.0.0.1";
+      type = types.str;
+      description = "Address to bind.";
+    };
+
+    port = mkOption {
+      default = 8888;
+      type = types.ints.u16;
+      description = "Port to bind.";
+    };
+
+    ca = mkOption {
+      defaultText = "\${cfg.dataDir}/ca.pem";
+      type = types.str;
+      description = "CA used to sign the new certificate -- accepts '[file:]fname' or 'env:varname'.";
+    };
+
+    caKey = mkOption {
+      defaultText = "file:\${cfg.dataDir}/ca-key.pem";
+      type = types.str;
+      description = "CA private key -- accepts '[file:]fname' or 'env:varname'.";
+    };
+
+    caBundle = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = "Path to root certificate store.";
+    };
+
+    intBundle = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = "Path to intermediate certificate store.";
+    };
+
+    intDir = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = "Intermediates directory.";
+    };
+
+    metadata = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = ''
+        Metadata file for root certificate presence.
+        The content of the file is a json dictionary (k,v): each key k is
+        a SHA-1 digest of a root certificate while value v is a list of key
+        store filenames.
+      '';
+    };
+
+    remote = mkOption {
+      default = null;
+      type = types.nullOr types.str;
+      description = "Remote CFSSL server.";
+    };
+
+    configFile = mkOption {
+      default = null;
+      type = types.nullOr types.str;
+      description = "Path to configuration file. Do not put this in nix-store as it might contain secrets.";
+    };
+
+    responder = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = "Certificate for OCSP responder.";
+    };
+
+    responderKey = mkOption {
+      default = null;
+      type = types.nullOr types.str;
+      description = "Private key for OCSP responder certificate. Do not put this in nix-store.";
+    };
+
+    tlsKey = mkOption {
+      default = null;
+      type = types.nullOr types.str;
+      description = "Other endpoint's CA private key. Do not put this in nix-store.";
+    };
+
+    tlsCert = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = "Other endpoint's CA to set up TLS protocol.";
+    };
+
+    mutualTlsCa = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = "Mutual TLS - require clients be signed by this CA.";
+    };
+
+    mutualTlsCn = mkOption {
+      default = null;
+      type = types.nullOr types.str;
+      description = "Mutual TLS - regex for whitelist of allowed client CNs.";
+    };
+
+    tlsRemoteCa = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = "CAs to trust for remote TLS requests.";
+    };
+
+    mutualTlsClientCert = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = "Mutual TLS - client certificate to call remote instance requiring client certs.";
+    };
+
+    mutualTlsClientKey = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = "Mutual TLS - client key to call remote instance requiring client certs. Do not put this in nix-store.";
+    };
+
+    dbConfig = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = "Certificate db configuration file. Path must be writeable.";
+    };
+
+    logLevel = mkOption {
+      default = 1;
+      type = types.enum [ 0 1 2 3 4 5 ];
+      description = "Log level (0 = DEBUG, 5 = FATAL).";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraGroups.cfssl = {
+      gid = config.ids.gids.cfssl;
+    };
+
+    users.extraUsers.cfssl = {
+      description = "cfssl user";
+      createHome = true;
+      home = cfg.dataDir;
+      group = "cfssl";
+      uid = config.ids.uids.cfssl;
+    };
+
+    systemd.services.cfssl = {
+      description = "CFSSL CA API server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        WorkingDirectory = cfg.dataDir;
+        StateDirectory = cfg.dataDir;
+        StateDirectoryMode = 700;
+        Restart = "always";
+        User = "cfssl";
+
+        ExecStart = with cfg; let
+          opt = n: v: optionalString (v != null) ''-${n}="${v}"'';
+        in
+          lib.concatStringsSep " \\\n" [
+            "${pkgs.cfssl}/bin/cfssl serve"
+            (opt "address" address)
+            (opt "port" (toString port))
+            (opt "ca" ca)
+            (opt "ca-key" caKey)
+            (opt "ca-bundle" caBundle)
+            (opt "int-bundle" intBundle)
+            (opt "int-dir" intDir)
+            (opt "metadata" metadata)
+            (opt "remote" remote)
+            (opt "config" configFile)
+            (opt "responder" responder)
+            (opt "responder-key" responderKey)
+            (opt "tls-key" tlsKey)
+            (opt "tls-cert" tlsCert)
+            (opt "mutual-tls-ca" mutualTlsCa)
+            (opt "mutual-tls-cn" mutualTlsCn)
+            (opt "mutual-tls-client-key" mutualTlsClientKey)
+            (opt "mutual-tls-client-cert" mutualTlsClientCert)
+            (opt "tls-remote-ca" tlsRemoteCa)
+            (opt "db-config" dbConfig)
+            (opt "loglevel" (toString logLevel))
+          ];
+      };
+    };
+
+    services.cfssl = {
+      ca = mkDefault "${cfg.dataDir}/ca.pem";
+      caKey = mkDefault "${cfg.dataDir}/ca-key.pem";
+    };
+  };
+}
diff --git a/nixos/modules/services/security/vault.nix b/nixos/modules/services/security/vault.nix
index 47c70cf0687b..0b28bc894458 100644
--- a/nixos/modules/services/security/vault.nix
+++ b/nixos/modules/services/security/vault.nix
@@ -1,6 +1,7 @@
 { config, lib, pkgs, ... }:
 
 with lib;
+
 let
   cfg = config.services.vault;
 
@@ -24,15 +25,22 @@ let
           ${cfg.telemetryConfig}
         }
       ''}
+    ${cfg.extraConfig}
   '';
 in
+
 {
   options = {
-
     services.vault = {
-
       enable = mkEnableOption "Vault daemon";
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.vault;
+        defaultText = "pkgs.vault";
+        description = "This option specifies the vault package to use.";
+      };
+
       address = mkOption {
         type = types.str;
         default = "127.0.0.1:8200";
@@ -58,7 +66,7 @@ in
         default = ''
           tls_min_version = "tls12"
         '';
-        description = "extra configuration";
+        description = "Extra text appended to the listener section.";
       };
 
       storageBackend = mkOption {
@@ -84,6 +92,12 @@ in
         default = "";
         description = "Telemetry configuration";
       };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Extra text appended to <filename>vault.hcl</filename>.";
+      };
     };
   };
 
@@ -122,7 +136,7 @@ in
         User = "vault";
         Group = "vault";
         PermissionsStartOnly = true;
-        ExecStart = "${pkgs.vault}/bin/vault server -config ${configFile}";
+        ExecStart = "${cfg.package}/bin/vault server -config ${configFile}";
         PrivateDevices = true;
         PrivateTmp = true;
         ProtectSystem = "full";
diff --git a/nixos/modules/services/system/cloud-init.nix b/nixos/modules/services/system/cloud-init.nix
index d513e44dcfba..1a700828ce77 100644
--- a/nixos/modules/services/system/cloud-init.nix
+++ b/nixos/modules/services/system/cloud-init.nix
@@ -104,8 +104,9 @@ in
     systemd.services.cloud-init =
       { description = "Initial cloud-init job (metadata service crawler)";
         wantedBy = [ "multi-user.target" ];
-        wants = [ "local-fs.target" "cloud-init-local.service" "sshd.service" "sshd-keygen.service" ];
-        after = [ "local-fs.target" "network.target" "cloud-init-local.service" ];
+        wants = [ "local-fs.target" "network-online.target" "cloud-init-local.service"
+                  "sshd.service" "sshd-keygen.service" ];
+        after = [ "local-fs.target" "network-online.target" "cloud-init-local.service" ];
         before = [ "sshd.service" "sshd-keygen.service" ];
         requires = [ "network.target "];
         path = path;
@@ -121,8 +122,8 @@ in
     systemd.services.cloud-config =
       { description = "Apply the settings specified in cloud-config";
         wantedBy = [ "multi-user.target" ];
-        wants = [ "network.target" ];
-        after = [ "network.target" "syslog.target" "cloud-config.target" ];
+        wants = [ "network-online.target" ];
+        after = [ "network-online.target" "syslog.target" "cloud-config.target" ];
 
         path = path;
         serviceConfig =
@@ -137,8 +138,8 @@ in
     systemd.services.cloud-final =
       { description = "Execute cloud user/final scripts";
         wantedBy = [ "multi-user.target" ];
-        wants = [ "network.target" ];
-        after = [ "network.target" "syslog.target" "cloud-config.service" "rc-local.service" ];
+        wants = [ "network-online.target" ];
+        after = [ "network-online.target" "syslog.target" "cloud-config.service" "rc-local.service" ];
         requires = [ "cloud-config.target" ];
         path = path;
         serviceConfig =
diff --git a/nixos/modules/services/system/localtime.nix b/nixos/modules/services/system/localtime.nix
index b9355bbb9441..c7e897c96448 100644
--- a/nixos/modules/services/system/localtime.nix
+++ b/nixos/modules/services/system/localtime.nix
@@ -22,14 +22,8 @@ in {
   config = mkIf cfg.enable {
     services.geoclue2.enable = true;
 
-    security.polkit.extraConfig = ''
-     polkit.addRule(function(action, subject) {
-       if (action.id == "org.freedesktop.timedate1.set-timezone"
-           && subject.user == "localtimed") {
-         return polkit.Result.YES;
-       }
-     });
-    '';
+    # so polkit will pick up the rules
+    environment.systemPackages = [ pkgs.localtime ];
 
     users.users = [{
       name = "localtimed";
diff --git a/nixos/modules/services/web-apps/youtrack.nix b/nixos/modules/services/web-apps/youtrack.nix
index 8c675c642005..6ad38028a641 100644
--- a/nixos/modules/services/web-apps/youtrack.nix
+++ b/nixos/modules/services/web-apps/youtrack.nix
@@ -118,14 +118,14 @@ in
 
     systemd.services.youtrack = {
       environment.HOME = cfg.statePath;
-      environment.YOUTRACK_JVM_OPTS = "-Xmx${cfg.maxMemory} -XX:MaxMetaspaceSize=${cfg.maxMetaspaceSize} ${cfg.jvmOpts} ${extraAttr}";
+      environment.YOUTRACK_JVM_OPTS = "${extraAttr}";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
         Type = "simple";
         User = "youtrack";
         Group = "youtrack";
-        ExecStart = ''${cfg.package}/bin/youtrack ${cfg.address}:${toString cfg.port}'';
+        ExecStart = ''${cfg.package}/bin/youtrack --J-Xmx${cfg.maxMemory} --J-XX:MaxMetaspaceSize=${cfg.maxMetaspaceSize} ${cfg.jvmOpts} ${cfg.address}:${toString cfg.port}'';
       };
     };
 
diff --git a/nixos/modules/services/web-servers/hydron.nix b/nixos/modules/services/web-servers/hydron.nix
index 49a18f5e7b28..ed63230bc784 100644
--- a/nixos/modules/services/web-servers/hydron.nix
+++ b/nixos/modules/services/web-servers/hydron.nix
@@ -1,6 +1,8 @@
 { config, lib, pkgs, ... }:
 
-let cfg = config.services.hydron;
+let
+  cfg = config.services.hydron;
+  postgres = config.services.postgresql;
 in with lib; {
   options.services.hydron = {
     enable = mkEnableOption "hydron";
@@ -14,10 +16,10 @@ in with lib; {
 
     interval = mkOption {
       type = types.str;
-      default = "hourly";
+      default = "weekly";
       example = "06:00";
       description = ''
-        How often we run hydron import and possibly fetch tags. Runs by default every hour.
+        How often we run hydron import and possibly fetch tags. Runs by default every week.
 
         The format is described in
         <citerefentry><refentrytitle>systemd.time</refentrytitle>
@@ -25,6 +27,38 @@ in with lib; {
       '';
     };
 
+    password = mkOption {
+      type = types.str;
+      default = "hydron";
+      example = "dumbpass";
+      description = "Password for the hydron database.";
+    };
+
+    passwordFile = mkOption {
+      type = types.path;
+      default = "/run/keys/hydron-password-file";
+      example = "/home/okina/hydron/keys/pass";
+      description = "Password file for the hydron database.";
+    };
+
+    postgresArgs = mkOption {
+      type = types.str;
+      description = "Postgresql connection arguments.";
+      example = ''
+        {
+          "driver": "postgres",
+          "connection": "user=hydron password=dumbpass dbname=hydron sslmode=disable"
+        }
+      '';
+    };
+
+    postgresArgsFile = mkOption {
+      type = types.path;
+      default = "/run/keys/hydron-postgres-args";
+      example = "/home/okina/hydron/keys/postgres";
+      description = "Postgresql connection arguments file.";
+    };
+
     listenAddress = mkOption {
       type = types.nullOr types.str;
       default = null;
@@ -47,16 +81,36 @@ in with lib; {
   };
 
   config = mkIf cfg.enable {
+    security.sudo.enable = cfg.enable;
+    services.postgresql.enable = cfg.enable;
+    services.hydron.passwordFile = mkDefault (pkgs.writeText "hydron-password-file" cfg.password);
+    services.hydron.postgresArgsFile = mkDefault (pkgs.writeText "hydron-postgres-args" cfg.postgresArgs);
+    services.hydron.postgresArgs = mkDefault ''
+      {
+        "driver": "postgres",
+        "connection": "user=hydron password=${cfg.password} dbname=hydron sslmode=disable"
+      }
+    '';
+
     systemd.services.hydron = {
       description = "hydron";
-      after = [ "network.target" ];
+      after = [ "network.target" "postgresql.service" ];
       wantedBy = [ "multi-user.target" ];
 
       preStart = ''
-        # Ensure folder exists and permissions are correct
-        mkdir -p ${escapeShellArg cfg.dataDir}/images
+        # Ensure folder exists or create it and permissions are correct
+        mkdir -p ${escapeShellArg cfg.dataDir}/{.hydron,images}
+        ln -sf ${escapeShellArg cfg.postgresArgsFile} ${escapeShellArg cfg.dataDir}/.hydron/db_conf.json
         chmod 750 ${escapeShellArg cfg.dataDir}
         chown -R hydron:hydron ${escapeShellArg cfg.dataDir}
+
+        # Ensure the database is correct or create it
+        ${pkgs.sudo}/bin/sudo -u ${postgres.superUser} ${postgres.package}/bin/createuser \
+          -SDR hydron || true
+        ${pkgs.sudo}/bin/sudo -u ${postgres.superUser} ${postgres.package}/bin/createdb \
+          -T template0 -E UTF8 -O hydron hydron || true
+        ${pkgs.sudo}/bin/sudo -u hydron ${postgres.package}/bin/psql \
+          -c "ALTER ROLE hydron WITH PASSWORD '$(cat ${escapeShellArg cfg.passwordFile})';" || true
       '';
 
       serviceConfig = {
@@ -83,9 +137,13 @@ in with lib; {
 
     systemd.timers.hydron-fetch = {
       description = "Automatically import paths into hydron and possibly fetch tags";
-      after = [ "network.target" ];
+      after = [ "network.target" "hydron.service" ];
       wantedBy = [ "timers.target" ];
-      timerConfig.OnCalendar = cfg.interval;
+      
+      timerConfig = {
+        Persistent = true;
+        OnCalendar = cfg.interval;
+      };
     };
 
     users = {
@@ -101,5 +159,9 @@ in with lib; {
     };
   };
 
+  imports = [
+    (mkRenamedOptionModule [ "services" "hydron" "baseDir" ] [ "services" "hydron" "dataDir" ])
+  ];
+
   meta.maintainers = with maintainers; [ chiiruno ];
 }
diff --git a/nixos/modules/services/web-servers/meguca.nix b/nixos/modules/services/web-servers/meguca.nix
index ed7325ff0790..11aebcb91d88 100644
--- a/nixos/modules/services/web-servers/meguca.nix
+++ b/nixos/modules/services/web-servers/meguca.nix
@@ -1,65 +1,71 @@
 { config, lib, pkgs, ... }:
 
-with lib;
 let
   cfg = config.services.meguca;
   postgres = config.services.postgresql;
-in
-{
+in with lib; {
   options.services.meguca = {
     enable = mkEnableOption "meguca";
 
-    baseDir = mkOption {
+    dataDir = mkOption {
       type = types.path;
-      default = "/run/meguca";
+      default = "/var/lib/meguca";
+      example = "/home/okina/meguca";
       description = "Location where meguca stores it's database and links.";
     };
 
     password = mkOption {
       type = types.str;
       default = "meguca";
+      example = "dumbpass";
       description = "Password for the meguca database.";
     };
 
     passwordFile = mkOption {
       type = types.path;
       default = "/run/keys/meguca-password-file";
+      example = "/home/okina/meguca/keys/pass";
       description = "Password file for the meguca database.";
     };
 
     reverseProxy = mkOption {
       type = types.nullOr types.str;
       default = null;
+      example = "192.168.1.5";
       description = "Reverse proxy IP.";
     };
 
     sslCertificate = mkOption {
       type = types.nullOr types.str;
       default = null;
+      example = "/home/okina/meguca/ssl.cert";
       description = "Path to the SSL certificate.";
     };
 
     listenAddress = mkOption {
       type = types.nullOr types.str;
       default = null;
+      example = "127.0.0.1:8000";
       description = "Listen on a specific IP address and port.";
     };
 
     cacheSize = mkOption {
       type = types.nullOr types.int;
       default = null;
+      example = 256;
       description = "Cache size in MB.";
     };
 
     postgresArgs = mkOption {
       type = types.str;
-      default = "user=meguca password=" + cfg.password + " dbname=meguca sslmode=disable";
+      example = "user=meguca password=dumbpass dbname=meguca sslmode=disable";
       description = "Postgresql connection arguments.";
     };
 
     postgresArgsFile = mkOption {
       type = types.path;
       default = "/run/keys/meguca-postgres-args";
+      example = "/home/okina/meguca/keys/postgres";
       description = "Postgresql connection arguments file.";
     };
 
@@ -83,18 +89,11 @@ in
   };
 
   config = mkIf cfg.enable {
-    security.sudo.enable = cfg.enable == true;
-    services.postgresql.enable = cfg.enable == true;
-
-    services.meguca.passwordFile = mkDefault (toString (pkgs.writeTextFile {
-      name = "meguca-password-file";
-      text = cfg.password;
-    }));
-
-    services.meguca.postgresArgsFile = mkDefault (toString (pkgs.writeTextFile {
-      name = "meguca-postgres-args";
-      text = cfg.postgresArgs;
-    }));
+    security.sudo.enable = cfg.enable;
+    services.postgresql.enable = cfg.enable;
+    services.meguca.passwordFile = mkDefault (pkgs.writeText "meguca-password-file" cfg.password);
+    services.meguca.postgresArgsFile = mkDefault (pkgs.writeText "meguca-postgres-args" cfg.postgresArgs);
+    services.meguca.postgresArgs = mkDefault "user=meguca password=${cfg.password} dbname=meguca sslmode=disable";
 
     systemd.services.meguca = {
       description = "meguca";
@@ -102,10 +101,11 @@ in
       wantedBy = [ "multi-user.target" ];
 
       preStart = ''
-        # Ensure folder exists and links are correct or create them
-        mkdir -p ${cfg.baseDir}
-        chmod 750 ${cfg.baseDir}
-        ln -sf ${pkgs.meguca}/share/meguca/www ${cfg.baseDir}
+        # Ensure folder exists or create it and links and permissions are correct
+        mkdir -p ${escapeShellArg cfg.dataDir}
+        ln -sf ${pkgs.meguca}/share/meguca/www ${escapeShellArg cfg.dataDir}
+        chmod 750 ${escapeShellArg cfg.dataDir}
+        chown -R meguca:meguca ${escapeShellArg cfg.dataDir}
 
         # Ensure the database is correct or create it
         ${pkgs.sudo}/bin/sudo -u ${postgres.superUser} ${postgres.package}/bin/createuser \
@@ -113,47 +113,46 @@ in
         ${pkgs.sudo}/bin/sudo -u ${postgres.superUser} ${postgres.package}/bin/createdb \
           -T template0 -E UTF8 -O meguca meguca || true
         ${pkgs.sudo}/bin/sudo -u meguca ${postgres.package}/bin/psql \
-          -c "ALTER ROLE meguca WITH PASSWORD '$(cat ${cfg.passwordFile})';" || true
+          -c "ALTER ROLE meguca WITH PASSWORD '$(cat ${escapeShellArg cfg.passwordFile})';" || true
       '';
 
     script = ''
-      cd ${cfg.baseDir}
-
-      ${pkgs.meguca}/bin/meguca -d "$(cat ${cfg.postgresArgsFile})"\
-        ${optionalString (cfg.reverseProxy != null) " -R ${cfg.reverseProxy}"}\
-        ${optionalString (cfg.sslCertificate != null) " -S ${cfg.sslCertificate}"}\
-        ${optionalString (cfg.listenAddress != null) " -a ${cfg.listenAddress}"}\
-        ${optionalString (cfg.cacheSize != null) " -c ${toString cfg.cacheSize}"}\
-        ${optionalString (cfg.compressTraffic) " -g"}\
-        ${optionalString (cfg.assumeReverseProxy) " -r"}\
-        ${optionalString (cfg.httpsOnly) " -s"} start
-    '';
+      cd ${escapeShellArg cfg.dataDir}
+
+      ${pkgs.meguca}/bin/meguca -d "$(cat ${escapeShellArg cfg.postgresArgsFile})"''
+      + optionalString (cfg.reverseProxy != null) " -R ${cfg.reverseProxy}"
+      + optionalString (cfg.sslCertificate != null) " -S ${cfg.sslCertificate}"
+      + optionalString (cfg.listenAddress != null) " -a ${cfg.listenAddress}"
+      + optionalString (cfg.cacheSize != null) " -c ${toString cfg.cacheSize}"
+      + optionalString (cfg.compressTraffic) " -g"
+      + optionalString (cfg.assumeReverseProxy) " -r"
+      + optionalString (cfg.httpsOnly) " -s" + " start";
 
       serviceConfig = {
         PermissionsStartOnly = true;
         Type = "forking";
         User = "meguca";
         Group = "meguca";
-        RuntimeDirectory = "meguca";
         ExecStop = "${pkgs.meguca}/bin/meguca stop";
       };
     };
 
     users = {
+      groups.meguca.gid = config.ids.gids.meguca;
+
       users.meguca = {
         description = "meguca server service user";
-        home = cfg.baseDir;
+        home = cfg.dataDir;
         createHome = true;
         group = "meguca";
         uid = config.ids.uids.meguca;
       };
-
-      groups.meguca = {
-        gid = config.ids.gids.meguca;
-        members = [ "meguca" ];
-      };
     };
   };
 
+  imports = [
+    (mkRenamedOptionModule [ "services" "meguca" "baseDir" ] [ "services" "meguca" "dataDir" ])
+  ];
+
   meta.maintainers = with maintainers; [ chiiruno ];
 }
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index f435e85f6b83..cce35aa28ba8 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -96,13 +96,13 @@ in
           else if any (w: w.name == defaultDM) cfg.session.list then
             defaultDM
           else
-            throw ''
-              Default desktop manager (${defaultDM}) not found.
-              Probably you want to change
-                services.xserver.desktopManager.default = "${defaultDM}";
-              to one of
+            builtins.trace ''
+              Default desktop manager (${defaultDM}) not found at evaluation time.
+              These are the known valid session names:
                 ${concatMapStringsSep "\n  " (w: "services.xserver.desktopManager.default = \"${w.name}\";") cfg.session.list}
-            '';
+              It's also possible the default can be found in one of these packages:
+                ${concatMapStringsSep "\n  " (p: p.name) config.services.xserver.displayManager.extraSessionFilePackages}
+            '' defaultDM;
       };
 
     };
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index 9fb8f44b2421..c339d24b098a 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -57,8 +57,12 @@ in {
       sessionPath = mkOption {
         default = [];
         example = literalExample "[ pkgs.gnome3.gpaste ]";
-        description = "Additional list of packages to be added to the session search path.
-                       Useful for gnome shell extensions or gsettings-conditionated autostart.";
+        description = ''
+          Additional list of packages to be added to the session search path.
+          Useful for GNOME Shell extensions or GSettings-conditional autostart.
+
+          Note that this should be a last resort; patching the package is preferred (see GPaste).
+        '';
         apply = list: list ++ [ pkgs.gnome3.gnome-shell pkgs.gnome3.gnome-shell-extensions ];
       };
 
@@ -93,6 +97,8 @@ in {
     services.udisks2.enable = true;
     services.accounts-daemon.enable = true;
     services.geoclue2.enable = mkDefault true;
+    # GNOME should have its own geoclue agent
+    services.geoclue2.enableDemoAgent = false;
     services.dleyna-renderer.enable = mkDefault true;
     services.dleyna-server.enable = mkDefault true;
     services.gnome3.at-spi2-core.enable = true;
@@ -126,18 +132,10 @@ in {
 
     fonts.fonts = [ pkgs.dejavu_fonts pkgs.cantarell-fonts ];
 
-    services.xserver.desktopManager.session = singleton
-      { name = "gnome3";
-        bgSupport = true;
-        start = ''
-          # Set GTK_DATA_PREFIX so that GTK+ can find the themes
-          export GTK_DATA_PREFIX=${config.system.path}
-
-          # find theme engines
-          export GTK_PATH=${config.system.path}/lib/gtk-3.0:${config.system.path}/lib/gtk-2.0
-
-          export XDG_MENU_PREFIX=gnome-
+    services.xserver.displayManager.extraSessionFilePackages = [ pkgs.gnome3.gnome-session ];
 
+    services.xserver.displayManager.sessionCommands = ''
+      if test "$XDG_CURRENT_DESKTOP" = "GNOME"; then
           ${concatMapStrings (p: ''
             if [ -d "${p}/share/gsettings-schemas/${p.name}" ]; then
               export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${p}/share/gsettings-schemas/${p.name}
@@ -148,34 +146,28 @@ in {
               export LD_LIBRARY_PATH=$LD_LIBRARY_PATH''${LD_LIBRARY_PATH:+:}${p}/lib
             fi
           '') cfg.sessionPath}
+      fi
+    '';
 
-          # Override default mimeapps
-          export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${mimeAppsList}/share
-
-          # Override gsettings-desktop-schema
-          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/
-
-          # Find the mouse
-          export XCURSOR_PATH=~/.icons:${config.system.path}/share/icons
+    environment.variables.GNOME_SESSION_DEBUG = optionalString cfg.debug "1";
 
-          # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
-          ${pkgs.xdg-user-dirs}/bin/xdg-user-dirs-update
+    # Override default mimeapps
+    environment.variables.XDG_DATA_DIRS = [ "${mimeAppsList}/share" ];
 
-          ${pkgs.gnome3.gnome-session}/bin/gnome-session ${optionalString cfg.debug "--debug"} &
-          waitPID=$!
-        '';
-      };
+    # Override GSettings schemas
+    environment.variables.NIX_GSETTINGS_OVERRIDES_DIR = "${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas";
 
-    services.xserver.updateDbusEnvironment = true;
+    # Let nautilus find extensions
+    # TODO: Create nautilus-with-extensions package
+    environment.variables.NAUTILUS_EXTENSION_DIR = "${config.system.path}/lib/nautilus/extensions-3.0";
 
     environment.variables.GIO_EXTRA_MODULES = [ "${lib.getLib pkgs.gnome3.dconf}/lib/gio/modules"
                                                 "${pkgs.gnome3.glib-networking.out}/lib/gio/modules"
                                                 "${pkgs.gnome3.gvfs}/lib/gio/modules" ];
     environment.systemPackages = pkgs.gnome3.corePackages ++ cfg.sessionPath
-      ++ (removePackagesByName pkgs.gnome3.optionalPackages config.environment.gnome3.excludePackages);
+      ++ (removePackagesByName pkgs.gnome3.optionalPackages config.environment.gnome3.excludePackages) ++ [
+      pkgs.xdg-user-dirs # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
+    ];
 
     # Use the correct gnome3 packageSet
     networking.networkmanager.basePackages =
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index 17733aa7e4f6..83d1957a646a 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -224,7 +224,7 @@ in
       # Update the start menu for each user that has `isNormalUser` set.
       system.activationScripts.plasmaSetup = stringAfter [ "users" "groups" ]
         (concatStringsSep "\n"
-          (mapAttrsToList (name: value: "${pkgs.su}/bin/su ${name} -c kbuildsycoca5")
+          (mapAttrsToList (name: value: "${pkgs.su}/bin/su ${name} -c ${pkgs.libsForQt5.kservice}/bin/kbuildsycoca5")
             (filterAttrs (n: v: v.isNormalUser) config.users.users)));
     })
   ];
diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix
index 43ed21c95fee..66886f23737d 100644
--- a/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixos/modules/services/x11/display-managers/default.nix
@@ -27,55 +27,26 @@ let
     Xft.hintstyle: hintslight
   '';
 
-  # file provided by services.xserver.displayManager.session.script
-  xsession = wm: dm: pkgs.writeScript "xsession"
+  # file provided by services.xserver.displayManager.session.wrapper
+  xsessionWrapper = pkgs.writeScript "xsession-wrapper"
     ''
       #! ${pkgs.bash}/bin/bash
 
-      # Expected parameters:
-      #   $1 = <desktop-manager>+<window-manager>
-
-      # Actual parameters (FIXME):
-      # SDDM is calling this script like the following:
-      #   $1 = /nix/store/xxx-xsession (= $0)
-      #   $2 = <desktop-manager>+<window-manager>
-      # SLiM is using the following parameter:
-      #   $1 = /nix/store/xxx-xsession <desktop-manager>+<window-manager>
-      # LightDM keeps the double quotes:
-      #   $1 = /nix/store/xxx-xsession "<desktop-manager>+<window-manager>"
-      # The fake/auto display manager doesn't use any parameters and GDM is
-      # broken.
-      # If you want to "debug" this script don't print the parameters to stdout
-      # or stderr because this script will be executed multiple times and the
-      # output won't be visible in the log when the script is executed for the
-      # first time (e.g. append them to a file instead)!
-
-      # All of the above cases are handled by the following hack (FIXME).
-      # Since this line is *very important* for *all display managers* it is
-      # very important to test changes to the following line with all display
-      # managers:
-      if [ "''${1:0:1}" = "/" ]; then eval exec "$1" "$2" ; fi
-
-      # Now it should be safe to assume that the script was called with the
-      # expected parameters.
+      # Shared environment setup for graphical sessions.
 
       . /etc/profile
       cd "$HOME"
 
-      # The first argument of this script is the session type.
-      sessionType="$1"
-      if [ "$sessionType" = default ]; then sessionType=""; fi
-
       ${optionalString cfg.startDbusSession ''
         if test -z "$DBUS_SESSION_BUS_ADDRESS"; then
-          exec ${pkgs.dbus.dbus-launch} --exit-with-session "$0" "$sessionType"
+          exec ${pkgs.dbus.dbus-launch} --exit-with-session "$0" "$@"
         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"
+          exec ${config.systemd.package}/bin/systemd-cat -t xsession "$0" "$@"
         fi
       ''}
 
@@ -85,12 +56,10 @@ let
 
       # Start PulseAudio if enabled.
       ${optionalString (config.hardware.pulseaudio.enable) ''
-        ${optionalString (!config.hardware.pulseaudio.systemWide)
-          "${config.hardware.pulseaudio.package.out}/bin/pulseaudio --start"
-        }
-
         # Publish access credentials in the root window.
-        ${config.hardware.pulseaudio.package.out}/bin/pactl load-module module-x11-publish "display=$DISPLAY"
+        if ${config.hardware.pulseaudio.package.out}/bin/pulseaudio --dump-modules | grep module-x11-publish &> /dev/null; then
+          ${config.hardware.pulseaudio.package.out}/bin/pactl load-module module-x11-publish "display=$DISPLAY"
+        fi
       ''}
 
       # Tell systemd about our $DISPLAY and $XAUTHORITY.
@@ -101,6 +70,7 @@ let
       ${config.systemd.package}/bin/systemctl --user import-environment DISPLAY XAUTHORITY DBUS_SESSION_BUS_ADDRESS
 
       # Load X defaults.
+      # FIXME: Check XDG_SESSION_TYPE against x11
       ${xorg.xrdb}/bin/xrdb -merge ${xresourcesXft}
       if test -e ~/.Xresources; then
           ${xorg.xrdb}/bin/xrdb -merge ~/.Xresources
@@ -132,11 +102,32 @@ let
       # Allow the user to setup a custom session type.
       if test -x ~/.xsession; then
           exec ~/.xsession
+      fi
+
+      if test "$1"; then
+          # Run the supplied session command. Remove any double quotes with eval.
+          eval exec "$@"
       else
-          if test "$sessionType" = "custom"; then
-              sessionType="" # fall-thru if there is no ~/.xsession
-          fi
+          # Fall back to the default window/desktopManager
+          exec ${cfg.displayManager.session.script}
       fi
+    '';
+
+  # file provided by services.xserver.displayManager.session.script
+  xsession = wm: dm: pkgs.writeScript "xsession"
+    ''
+      #! ${pkgs.bash}/bin/bash
+
+      # Legacy session script used to construct .desktop files from
+      # `services.xserver.displayManager.session` entries. Called from
+      # `sessionWrapper`.
+
+      # Expected parameters:
+      #   $1 = <desktop-manager>+<window-manager>
+
+      # The first argument of this script is the session type.
+      sessionType="$1"
+      if [ "$sessionType" = default ]; then sessionType=""; fi
 
       # The session type is "<desktop-manager>+<window-manager>", so
       # extract those (see:
@@ -186,19 +177,22 @@ let
       allowSubstitutes = false;
     }
     ''
-      mkdir -p "$out"
+      mkdir -p "$out/share/xsessions"
       ${concatMapStrings (n: ''
-        cat - > "$out/${n}.desktop" << EODESKTOP
+        cat - > "$out/share/xsessions/${n}.desktop" << EODESKTOP
         [Desktop Entry]
         Version=1.0
         Type=XSession
         TryExec=${cfg.displayManager.session.script}
         Exec=${cfg.displayManager.session.script} "${n}"
-        X-GDM-BypassXsession=true
         Name=${n}
         Comment=
         EODESKTOP
       '') names}
+
+      ${concatMapStrings (pkg: ''
+        ${xorg.lndir}/bin/lndir ${pkg}/share/xsessions $out/share/xsessions
+      '') cfg.displayManager.extraSessionFilePackages}
     '';
 
 in
@@ -245,6 +239,14 @@ in
         '';
       };
 
+      extraSessionFilePackages = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        description = ''
+          A list of packages containing xsession files to be passed to the display manager.
+        '';
+      };
+
       session = mkOption {
         default = [];
         example = literalExample
@@ -280,6 +282,7 @@ in
               (filter (w: d.name != "none" || w.name != "none") wm));
           desktops = mkDesktops names;
           script = xsession wm dm;
+          wrapper = xsessionWrapper;
         };
       };
 
diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix
index 8b08c01ea0db..78dc39f7f62f 100644
--- a/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixos/modules/services/x11/display-managers/gdm.nix
@@ -109,7 +109,7 @@ in
         environment = {
           GDM_X_SERVER_EXTRA_ARGS = toString
             (filter (arg: arg != "-terminate") cfg.xserverArgs);
-          GDM_SESSIONS_DIR = "${cfg.session.desktops}";
+          GDM_SESSIONS_DIR = "${cfg.session.desktops}/share/xsessions";
           # Find the mouse
           XCURSOR_PATH = "~/.icons:${pkgs.gnome3.adwaita-icon-theme}/share/icons";
         };
@@ -173,6 +173,8 @@ in
       ${optionalString cfg.gdm.debug "Enable=true"}
     '';
 
+    environment.etc."gdm/Xsession".source = config.services.xserver.displayManager.session.wrapper;
+
     # GDM LFS PAM modules, adapted somehow to NixOS
     security.pam.services = {
       gdm-launch-environment.text = ''
diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
index cfa38f175dd3..013956c05466 100644
--- a/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
@@ -23,7 +23,7 @@ let
       makeWrapper ${pkgs.lightdm_gtk_greeter}/sbin/lightdm-gtk-greeter \
         $out/greeter \
         --prefix PATH : "${pkgs.glibc.bin}/bin" \
-        --set GDK_PIXBUF_MODULE_FILE "${pkgs.gdk_pixbuf.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" \
+        --set GDK_PIXBUF_MODULE_FILE "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" \
         --set GTK_PATH "${theme}:${pkgs.gtk3.out}" \
         --set GTK_EXE_PREFIX "${theme}" \
         --set GTK_DATA_PREFIX "${theme}" \
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index 54d4520a0c8b..dc82f7086c82 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -15,7 +15,7 @@ let
 
   inherit (pkgs) lightdm writeScript writeText;
 
-  # lightdm runs with clearenv(), but we need a few things in the enviornment for X to startup
+  # lightdm runs with clearenv(), but we need a few things in the environment for X to startup
   xserverWrapper = writeScript "xserver-wrapper"
     ''
       #! ${pkgs.bash}/bin/bash
@@ -45,11 +45,11 @@ let
         greeter-user = ${config.users.users.lightdm.name}
         greeters-directory = ${cfg.greeter.package}
       ''}
-      sessions-directory = ${dmcfg.session.desktops}
+      sessions-directory = ${dmcfg.session.desktops}/share/xsessions
 
       [Seat:*]
       xserver-command = ${xserverWrapper}
-      session-wrapper = ${dmcfg.session.script}
+      session-wrapper = ${dmcfg.session.wrapper}
       ${optionalString cfg.greeter.enable ''
         greeter-session = ${cfg.greeter.name}
       ''}
@@ -176,21 +176,13 @@ in
           LightDM auto-login requires services.xserver.displayManager.lightdm.autoLogin.user to be set
         '';
       }
-      { assertion = cfg.autoLogin.enable -> elem defaultSessionName dmcfg.session.names;
+      { assertion = cfg.autoLogin.enable -> dmDefault != "none" || wmDefault != "none";
         message = ''
           LightDM auto-login requires that services.xserver.desktopManager.default and
           services.xserver.windowMananger.default are set to valid values. The current
           default session: ${defaultSessionName} is not valid.
         '';
       }
-      { assertion = hasDefaultUserSession -> elem defaultSessionName dmcfg.session.names;
-        message = ''
-          services.xserver.desktopManager.default and
-          services.xserver.windowMananger.default are not set to valid
-          values. The current default session: ${defaultSessionName}
-          is not valid.
-        '';
-      }
       { assertion = !cfg.greeter.enable -> (cfg.autoLogin.enable && cfg.autoLogin.timeout == 0);
         message = ''
           LightDM can only run without greeter if automatic login is enabled and the timeout for it
@@ -217,9 +209,12 @@ in
     services.dbus.enable = true;
     services.dbus.packages = [ lightdm ];
 
-    # lightdm uses the accounts daemon to rember language/window-manager per user
+    # lightdm uses the accounts daemon to remember language/window-manager per user
     services.accounts-daemon.enable = true;
 
+    # Enable the accounts daemon to find lightdm's dbus interface
+    environment.systemPackages = [ lightdm ];
+
     security.pam.services.lightdm = {
       allowNullPassword = true;
       startSession = true;
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix
index 426b899586f5..2b03ed81b5ed 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/x11/display-managers/sddm.nix
@@ -49,8 +49,8 @@ let
     MinimumVT=${toString (if xcfg.tty != null then xcfg.tty else 7)}
     ServerPath=${xserverWrapper}
     XephyrPath=${pkgs.xorg.xorgserver.out}/bin/Xephyr
-    SessionCommand=${dmcfg.session.script}
-    SessionDir=${dmcfg.session.desktops}
+    SessionCommand=${dmcfg.session.wrapper}
+    SessionDir=${dmcfg.session.desktops}/share/xsessions
     XauthPath=${pkgs.xorg.xauth}/bin/xauth
     DisplayCommand=${Xsetup}
     DisplayStopCommand=${Xstop}
@@ -265,6 +265,7 @@ in
     };
 
     environment.etc."sddm.conf".source = cfgFile;
+    environment.pathsToLink = [ "/share/sddm/themes" ];
 
     users.groups.sddm.gid = config.ids.gids.sddm;
 
diff --git a/nixos/modules/services/x11/display-managers/slim.nix b/nixos/modules/services/x11/display-managers/slim.nix
index f645a5c2f078..51ce5f4e2437 100644
--- a/nixos/modules/services/x11/display-managers/slim.nix
+++ b/nixos/modules/services/x11/display-managers/slim.nix
@@ -13,8 +13,8 @@ let
       xauth_path ${dmcfg.xauthBin}
       default_xserver ${dmcfg.xserverBin}
       xserver_arguments ${toString dmcfg.xserverArgs}
-      sessiondir ${dmcfg.session.desktops}
-      login_cmd exec ${pkgs.runtimeShell} ${dmcfg.session.script} "%session"
+      sessiondir ${dmcfg.session.desktops}/share/xsessions
+      login_cmd exec ${pkgs.runtimeShell} ${dmcfg.session.wrapper} "%session"
       halt_cmd ${config.systemd.package}/sbin/shutdown -h now
       reboot_cmd ${config.systemd.package}/sbin/shutdown -r now
       logfile /dev/stderr
diff --git a/nixos/modules/services/x11/redshift.nix b/nixos/modules/services/x11/redshift.nix
index 30d853841ea4..b7dd7debcb63 100644
--- a/nixos/modules/services/x11/redshift.nix
+++ b/nixos/modules/services/x11/redshift.nix
@@ -116,6 +116,9 @@ in {
       }
     ];
 
+    # needed so that .desktop files are installed, which geoclue cares about
+    environment.systemPackages = [ cfg.package ];
+
     services.geoclue2.enable = mkIf (cfg.provider == "geoclue2") true;
 
     systemd.user.services.redshift = 
diff --git a/nixos/modules/services/x11/window-managers/metacity.nix b/nixos/modules/services/x11/window-managers/metacity.nix
index 436eccbaf0c2..5175fd7f3b1f 100644
--- a/nixos/modules/services/x11/window-managers/metacity.nix
+++ b/nixos/modules/services/x11/window-managers/metacity.nix
@@ -5,9 +5,7 @@ with lib;
 let
 
   cfg = config.services.xserver.windowManager.metacity;
-  xorg = config.services.xserver.package;
-  gnome = pkgs.gnome;
-
+  inherit (pkgs) gnome3;
 in
 
 {
@@ -20,16 +18,12 @@ in
     services.xserver.windowManager.session = singleton
       { name = "metacity";
         start = ''
-          env LD_LIBRARY_PATH=${lib.makeLibraryPath [ xorg.libX11 xorg.libXext ]}:/usr/lib/
-          # !!! Hack: load the schemas for Metacity.
-          GCONF_CONFIG_SOURCE=xml::~/.gconf ${gnome.GConf.out}/bin/gconftool-2 \
-            --makefile-install-rule ${gnome.metacity}/etc/gconf/schemas/*.schemas # */
-          ${gnome.metacity}/bin/metacity &
+          ${gnome3.metacity}/bin/metacity &
           waitPID=$!
         '';
       };
 
-    environment.systemPackages = [ gnome.metacity ];
+    environment.systemPackages = [ gnome3.metacity ];
 
   };
 
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index da29e08b3691..9d410a8b9ca4 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -46,7 +46,7 @@ let
 
         ln -s ${kernelPath} $out/kernel
         ln -s ${config.system.modulesTree} $out/kernel-modules
-        ${optionalString (pkgs.stdenv.platform.kernelDTB or false) ''
+        ${optionalString (pkgs.stdenv.hostPlatform.platform.kernelDTB or false) ''
           ln -s ${config.boot.kernelPackages.kernel}/dtbs $out/dtbs
         ''}
 
@@ -74,7 +74,7 @@ let
       echo -n "$configurationName" > $out/configuration-name
       echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version
       echo -n "$nixosLabel" > $out/nixos-version
-      echo -n "$system" > $out/system
+      echo -n "${pkgs.stdenv.hostPlatform.system}" > $out/system
 
       mkdir $out/fine-tune
       childCount=0
@@ -175,7 +175,7 @@ in
 
     system.boot.loader.kernelFile = mkOption {
       internal = true;
-      default = pkgs.stdenv.platform.kernelTarget;
+      default = pkgs.stdenv.hostPlatform.platform.kernelTarget;
       type = types.str;
       description = ''
         Name of the kernel file to be passed to the bootloader.
diff --git a/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
index f8a00784034a..2d27611946e2 100644
--- a/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
+++ b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
@@ -13,7 +13,7 @@ let
   };
 
   # Temporary check, for nixos to cope both with nixpkgs stdenv-updates and trunk
-  platform = pkgs.stdenv.platform;
+  inherit (pkgs.stdenv.hostPlatform) platform;
 
 in
 
diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
index f974d07da9e5..9bec24c53f5b 100644
--- a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
+++ b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
@@ -15,7 +15,7 @@ let
     inherit configTxt;
   };
 
-  platform = pkgs.stdenv.platform;
+  inherit (pkgs.stdenv.hostPlatform) platform;
 
   builderUboot = import ./builder_uboot.nix { inherit config; inherit pkgs; inherit configTxt; };
 
diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
index 3333569c36be..6016a85ea061 100644
--- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
+++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
@@ -42,7 +42,8 @@ def write_loader_conf(profile, generation):
         else:
             f.write("default nixos-generation-%d\n" % (generation))
         if not @editor@:
-            f.write("editor 0");
+            f.write("editor 0\n");
+        f.write("console-mode @consoleMode@\n");
     os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf")
 
 def profile_path(profile, generation, name):
diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
index a5a88a99be8f..feed863efd66 100644
--- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
+++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
@@ -22,6 +22,8 @@ let
 
     editor = if cfg.editor then "True" else "False";
 
+    inherit (cfg) consoleMode;
+
     inherit (efi) efiSysMountPoint canTouchEfiVariables;
   };
 in {
@@ -52,6 +54,38 @@ in {
         compatibility.
       '';
     };
+
+    consoleMode = mkOption {
+      default = "keep";
+
+      type = types.enum [ "0" "1" "2" "auto" "max" "keep" ];
+
+      description = ''
+        The resolution of the console. The following values are valid:
+        </para>
+        <para>
+        <itemizedlist>
+          <listitem><para>
+            <literal>"0"</literal>: Standard UEFI 80x25 mode
+          </para></listitem>
+          <listitem><para>
+            <literal>"1"</literal>: 80x50 mode, not supported by all devices
+          </para></listitem>
+          <listitem><para>
+            <literal>"2"</literal>: The first non-standard mode provided by the device firmware, if any
+          </para></listitem>
+          <listitem><para>
+            <literal>"auto"</literal>: Pick a suitable mode automatically using heuristics
+          </para></listitem>
+          <listitem><para>
+            <literal>"max"</literal>: Pick the highest-numbered available mode
+          </para></listitem>
+          <listitem><para>
+            <literal>"keep"</literal>: Keep the mode selected by firmware (the default)
+          </para></listitem>
+        </itemizedlist>
+      '';
+    };
   };
 
   config = mkIf cfg.enable {
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 7ebfdb134d7d..27c1f891f485 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -5,61 +5,171 @@ with lib;
 let
   luks = config.boot.initrd.luks;
 
-  openCommand = name': { name, device, header, keyFile, keyFileSize, allowDiscards, yubikey, fallbackToPassword, ... }: assert name' == name; ''
+  commonFunctions = ''
+    die() {
+      echo "$@" >&2
+      exit 1
+    }
 
-    # Wait for a target (e.g. device, keyFile, header, ...) to appear.
     wait_target() {
         local name="$1"
         local target="$2"
+        local secs="''${3:-10}"
+        local desc="''${4:-$name $target to appear}"
 
         if [ ! -e $target ]; then
-            echo -n "Waiting 10 seconds for $name $target to appear"
+            echo -n "Waiting $secs seconds for $desc..."
             local success=false;
-            for try in $(seq 10); do
+            for try in $(seq $secs); do
                 echo -n "."
                 sleep 1
-                if [ -e $target ]; then success=true break; fi
+                if [ -e $target ]; then
+                    success=true
+                    break
+                fi
             done
-            if [ $success = true ]; then
+            if [ $success == true ]; then
                 echo " - success";
+                return 0
             else
                 echo " - failure";
+                return 1
             fi
         fi
+        return 0
     }
 
+    wait_yubikey() {
+      local secs="''${1:-10}"
+
+      ykinfo -v 1>/dev/null 2>&1
+      if [ $? != 0 ]; then
+          echo -n "Waiting $secs seconds for Yubikey to appear..."
+          local success=false
+          for try in $(seq $secs); do
+              echo -n .
+              sleep 1
+              ykinfo -v 1>/dev/null 2>&1
+              if [ $? == 0 ]; then
+                  success=true
+                  break
+              fi
+          done
+          if [ $success == true ]; then
+              echo " - success";
+              return 0
+          else
+              echo " - failure";
+              return 1
+          fi
+      fi
+      return 0
+    }
+  '';
+
+  preCommands = ''
+    # A place to store crypto things
+
+    # A ramfs is used here to ensure that the file used to update
+    # the key slot with cryptsetup will never get swapped out.
+    # Warning: Do NOT replace with tmpfs!
+    mkdir -p /crypt-ramfs
+    mount -t ramfs none /crypt-ramfs
+
+    # For Yubikey salt storage
+    mkdir -p /crypt-storage
+
+    # Disable all input echo for the whole stage. We could use read -s
+    # instead but that would ocasionally leak characters between read
+    # invocations.
+    stty -echo
+  '';
+
+  postCommands = ''
+    stty echo
+    umount /crypt-storage 2>/dev/null
+    umount /crypt-ramfs 2>/dev/null
+  '';
+
+  openCommand = name': { name, device, header, keyFile, keyFileSize, keyFileOffset, allowDiscards, yubikey, fallbackToPassword, ... }: assert name' == name;
+  let
+    csopen   = "cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} ${optionalString (header != null) "--header=${header}"}";
+    cschange = "cryptsetup luksChangeKey ${device} ${optionalString (header != null) "--header=${header}"}";
+  in ''
     # Wait for luksRoot (and optionally keyFile and/or header) to appear, e.g.
     # if on a USB drive.
-    wait_target "device" ${device}
-
-    ${optionalString (keyFile != null) ''
-      wait_target "key file" ${keyFile}
-    ''}
+    wait_target "device" ${device} || die "${device} is unavailable"
 
     ${optionalString (header != null) ''
-      wait_target "header" ${header}
+      wait_target "header" ${header} || die "${header} is unavailable"
     ''}
 
+    do_open_passphrase() {
+        local passphrase
+
+        while true; do
+            echo -n "Passphrase for ${device}: "
+            passphrase=
+            while true; do
+                if [ -e /crypt-ramfs/passphrase ]; then
+                    echo "reused"
+                    passphrase=$(cat /crypt-ramfs/passphrase)
+                    break
+                else
+                    # ask cryptsetup-askpass
+                    echo -n "${device}" > /crypt-ramfs/device
+
+                    # and try reading it from /dev/console with a timeout
+                    IFS= read -t 1 -r passphrase
+                    if [ -n "$passphrase" ]; then
+                       ${if luks.reusePassphrases then ''
+                         # remember it for the next device
+                         echo -n "$passphrase" > /crypt-ramfs/passphrase
+                       '' else ''
+                         # Don't save it to ramfs. We are very paranoid
+                       ''}
+                       echo
+                       break
+                    fi
+                fi
+            done
+            echo -n "Verifiying passphrase for ${device}..."
+            echo -n "$passphrase" | ${csopen} --key-file=-
+            if [ $? == 0 ]; then
+                echo " - success"
+                ${if luks.reusePassphrases then ''
+                  # we don't rm here because we might reuse it for the next device
+                '' else ''
+                  rm -f /crypt-ramfs/passphrase
+                ''}
+                break
+            else
+                echo " - failure"
+                # ask for a different one
+                rm -f /crypt-ramfs/passphrase
+            fi
+        done
+    }
+
+    # LUKS
     open_normally() {
-        echo luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
-          ${optionalString (header != null) "--header=${header}"} \
-          > /.luksopen_args
-        ${optionalString (keyFile != null) ''
-        ${optionalString fallbackToPassword "if [ -e ${keyFile} ]; then"}
-            echo " --key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}" \
-              >> /.luksopen_args
-        ${optionalString fallbackToPassword ''
+        ${if (keyFile != null) then ''
+        if wait_target "key file" ${keyFile}; then
+            ${csopen} --key-file=${keyFile} \
+              ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"} \
+              ${optionalString (keyFileOffset != null) "--keyfile-offset=${toString keyFileOffset}"}
         else
-            echo "keyfile ${keyFile} not found -- fallback to interactive unlocking"
+            ${if fallbackToPassword then "echo" else "die"} "${keyFile} is unavailable"
+            echo " - failing back to interactive password prompt"
+            do_open_passphrase
         fi
+        '' else ''
+        do_open_passphrase
         ''}
-        ''}
-        cryptsetup-askpass
-        rm /.luksopen_args
     }
 
-    ${optionalString (luks.yubikeySupport && (yubikey != null)) ''
-
+    ${if luks.yubikeySupport && (yubikey != null) then ''
+    # Yubikey
     rbtohex() {
         ( od -An -vtx1 | tr -d ' \n' )
     }
@@ -68,8 +178,7 @@ let
         ( tr '[:lower:]' '[:upper:]' | sed -e 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf )
     }
 
-    open_yubikey() {
-
+    do_open_yubikey() {
         # Make all of these local to this function
         # to prevent their values being leaked
         local salt
@@ -85,19 +194,18 @@ let
         local new_response
         local new_k_luks
 
-        mkdir -p ${yubikey.storage.mountPoint}
-        mount -t ${yubikey.storage.fsType} ${toString yubikey.storage.device} ${yubikey.storage.mountPoint}
+        mount -t ${yubikey.storage.fsType} ${yubikey.storage.device} /crypt-storage || \
+          die "Failed to mount Yubikey salt storage device"
 
-        salt="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path} | sed -n 1p | tr -d '\n')"
-        iterations="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path} | sed -n 2p | tr -d '\n')"
+        salt="$(cat /crypt-storage${yubikey.storage.path} | sed -n 1p | tr -d '\n')"
+        iterations="$(cat /crypt-storage${yubikey.storage.path} | sed -n 2p | tr -d '\n')"
         challenge="$(echo -n $salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
         response="$(ykchalresp -${toString yubikey.slot} -x $challenge 2>/dev/null)"
 
         for try in $(seq 3); do
-
             ${optionalString yubikey.twoFactor ''
             echo -n "Enter two-factor passphrase: "
-            read -s k_user
+            read -r k_user
             echo
             ''}
 
@@ -107,9 +215,9 @@ let
                 k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
             fi
 
-            echo -n "$k_luks" | hextorb | cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} --key-file=-
+            echo -n "$k_luks" | hextorb | ${csopen} --key-file=-
 
-            if [ $? == "0" ]; then
+            if [ $? == 0 ]; then
                 opened=true
                 break
             else
@@ -118,11 +226,7 @@ let
             fi
         done
 
-        if [ "$opened" == false ]; then
-            umount ${yubikey.storage.mountPoint}
-            echo "Maximum authentication errors reached"
-            exit 1
-        fi
+        [ "$opened" == false ] && die "Maximum authentication errors reached"
 
         echo -n "Gathering entropy for new salt (please enter random keys to generate entropy if this blocks for long)..."
         for i in $(seq ${toString yubikey.saltLength}); do
@@ -147,67 +251,50 @@ let
             new_k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
         fi
 
-        mkdir -p ${yubikey.ramfsMountPoint}
-        # A ramfs is used here to ensure that the file used to update
-        # the key slot with cryptsetup will never get swapped out.
-        # Warning: Do NOT replace with tmpfs!
-        mount -t ramfs none ${yubikey.ramfsMountPoint}
+        echo -n "$new_k_luks" | hextorb > /crypt-ramfs/new_key
+        echo -n "$k_luks" | hextorb | ${cschange} --key-file=- /crypt-ramfs/new_key
 
-        echo -n "$new_k_luks" | hextorb > ${yubikey.ramfsMountPoint}/new_key
-        echo -n "$k_luks" | hextorb | cryptsetup luksChangeKey ${device} --key-file=- ${yubikey.ramfsMountPoint}/new_key
-
-        if [ $? == "0" ]; then
-            echo -ne "$new_salt\n$new_iterations" > ${yubikey.storage.mountPoint}${yubikey.storage.path}
+        if [ $? == 0 ]; then
+            echo -ne "$new_salt\n$new_iterations" > /crypt-storage${yubikey.storage.path}
         else
             echo "Warning: Could not update LUKS key, current challenge persists!"
         fi
 
-        rm -f ${yubikey.ramfsMountPoint}/new_key
-        umount ${yubikey.ramfsMountPoint}
-        rm -rf ${yubikey.ramfsMountPoint}
+        rm -f /crypt-ramfs/new_key
+        umount /crypt-storage
+    }
 
-        umount ${yubikey.storage.mountPoint}
+    open_yubikey() {
+        if wait_yubikey ${toString yubikey.gracePeriod}; then
+            do_open_yubikey
+        else
+            echo "No yubikey found, falling back to non-yubikey open procedure"
+            open_normally
+        fi
     }
 
-    ${optionalString (yubikey.gracePeriod > 0) ''
-    echo -n "Waiting ${toString yubikey.gracePeriod} seconds as grace..."
-    for i in $(seq ${toString yubikey.gracePeriod}); do
-        sleep 1
-        echo -n .
-    done
-    echo "ok"
+    open_yubikey
+    '' else ''
+    open_normally
     ''}
+  '';
 
-    yubikey_missing=true
-    ykinfo -v 1>/dev/null 2>&1
-    if [ $? != "0" ]; then
-        echo -n "waiting 10 seconds for yubikey to appear..."
-        for try in $(seq 10); do
-            sleep 1
-            ykinfo -v 1>/dev/null 2>&1
-            if [ $? == "0" ]; then
-                yubikey_missing=false
-                break
-            fi
-            echo -n .
-        done
-        echo "ok"
-    else
-        yubikey_missing=false
-    fi
-
-    if [ "$yubikey_missing" == true ]; then
-        echo "no yubikey found, falling back to non-yubikey open procedure"
-        open_normally
-    else
-        open_yubikey
-    fi
-    ''}
+  askPass = pkgs.writeScriptBin "cryptsetup-askpass" ''
+    #!/bin/sh
 
-    # open luksRoot and scan for logical volumes
-    ${optionalString ((!luks.yubikeySupport) || (yubikey == null)) ''
-    open_normally
-    ''}
+    ${commonFunctions}
+
+    while true; do
+        wait_target "luks" /crypt-ramfs/device 10 "LUKS to request a passphrase" || die "Passphrase is not requested now"
+        device=$(cat /crypt-ramfs/device)
+
+        echo -n "Passphrase for $device: "
+        IFS= read -rs passphrase
+        echo
+
+        rm /crypt-ramfs/device
+        echo -n "$passphrase" > /crypt-ramfs/passphrase
+    done
   '';
 
   preLVM = filterAttrs (n: v: v.preLVM) luks.devices;
@@ -255,6 +342,22 @@ in
       '';
     };
 
+    boot.initrd.luks.reusePassphrases = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        When opening a new LUKS device try reusing last successful
+        passphrase.
+
+        Useful for mounting a number of devices that use the same
+        passphrase without retyping it several times.
+
+        Such setup can be useful if you use <command>cryptsetup
+        luksSuspend</command>. Different LUKS devices will still have
+        different master keys even when using the same passphrase.
+      '';
+    };
+
     boot.initrd.luks.devices = mkOption {
       default = { };
       example = { "luksroot".device = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08"; };
@@ -316,6 +419,19 @@ in
             '';
           };
 
+          keyFileOffset = mkOption {
+            default = null;
+            example = 4096;
+            type = types.nullOr types.int;
+            description = ''
+              The offset of the key file. Use this in combination with
+              <literal>keyFileSize</literal> to use part of a file as key file
+              (often the case if a raw device or partition is used as a key file).
+              If not specified, the key begins at the first byte of
+              <literal>keyFile</literal>.
+            '';
+          };
+
           # FIXME: get rid of this option.
           preLVM = mkOption {
             default = true;
@@ -383,15 +499,9 @@ in
                 };
 
                 gracePeriod = mkOption {
-                  default = 2;
+                  default = 10;
                   type = types.int;
-                  description = "Time in seconds to wait before attempting to find the Yubikey.";
-                };
-
-                ramfsMountPoint = mkOption {
-                  default = "/crypt-ramfs";
-                  type = types.str;
-                  description = "Path where the ramfs used to update the LUKS key will be mounted during early boot.";
+                  description = "Time in seconds to wait for the Yubikey.";
                 };
 
                 /* TODO: Add to the documentation of the current module:
@@ -414,12 +524,6 @@ in
                     description = "The filesystem of the unencrypted device.";
                   };
 
-                  mountPoint = mkOption {
-                    default = "/crypt-storage";
-                    type = types.str;
-                    description = "Path where the unencrypted device will be mounted during early boot.";
-                  };
-
                   path = mkOption {
                     default = "/crypt-storage/default";
                     type = types.str;
@@ -432,8 +536,8 @@ in
               };
             });
           };
-
-        }; }));
+        };
+      }));
     };
 
     boot.initrd.luks.yubikeySupport = mkOption {
@@ -463,18 +567,8 @@ in
     # copy the cryptsetup binary and it's dependencies
     boot.initrd.extraUtilsCommands = ''
       copy_bin_and_libs ${pkgs.cryptsetup}/bin/cryptsetup
-
-      cat > $out/bin/cryptsetup-askpass <<EOF
-      #!$out/bin/sh -e
-      if [ -e /.luksopen_args ]; then
-        cryptsetup \$(cat /.luksopen_args)
-        killall -q cryptsetup
-      else
-        echo "Passphrase is not requested now"
-        exit 1
-      fi
-      EOF
-      chmod +x $out/bin/cryptsetup-askpass
+      copy_bin_and_libs ${askPass}/bin/cryptsetup-askpass
+      sed -i s,/bin/sh,$out/bin/sh, $out/bin/cryptsetup-askpass
 
       ${optionalString luks.yubikeySupport ''
         copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykchalresp
@@ -506,8 +600,9 @@ in
       ''}
     '';
 
-    boot.initrd.preLVMCommands = concatStrings (mapAttrsToList openCommand preLVM);
-    boot.initrd.postDeviceCommands = concatStrings (mapAttrsToList openCommand postLVM);
+    boot.initrd.preFailCommands = postCommands;
+    boot.initrd.preLVMCommands = commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand preLVM) + postCommands;
+    boot.initrd.postDeviceCommands = commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand postLVM) + postCommands;
 
     environment.systemPackages = [ pkgs.cryptsetup ];
   };
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index ce770d067608..c11aaeaeb6dc 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -11,17 +11,29 @@ let
   checkLink = checkUnitConfig "Link" [
     (assertOnlyFields [
       "Description" "Alias" "MACAddressPolicy" "MACAddress" "NamePolicy" "Name"
-      "MTUBytes" "BitsPerSecond" "Duplex" "WakeOnLan"
+      "MTUBytes" "BitsPerSecond" "Duplex" "AutoNegotiation" "WakeOnLan" "Port"
+      "TCPSegmentationOffload" "TCP6SegmentationOffload" "GenericSegmentationOffload"
+      "GenericReceiveOffload" "LargeReceiveOffload" "RxChannels" "TxChannels"
+      "OtherChannels" "CombinedChannels"
     ])
-    (assertValueOneOf "MACAddressPolicy" ["persistent" "random"])
+    (assertValueOneOf "MACAddressPolicy" ["persistent" "random" "none"])
     (assertMacAddress "MACAddress")
-    (assertValueOneOf "NamePolicy" [
-      "kernel" "database" "onboard" "slot" "path" "mac"
-    ])
     (assertByteFormat "MTUBytes")
     (assertByteFormat "BitsPerSecond")
     (assertValueOneOf "Duplex" ["half" "full"])
-    (assertValueOneOf "WakeOnLan" ["phy" "magic" "off"])
+    (assertValueOneOf "AutoNegotiation" boolValues)
+    (assertValueOneOf "WakeOnLan" ["phy" "unicast" "multicast" "broadcast" "arp" "magic" "secureon" "off"])
+    (assertValueOneOf "Port" ["tp" "aui" "bnc" "mii" "fibre"])
+    (assertValueOneOf "TCPSegmentationOffload" boolValues)
+    (assertValueOneOf "TCP6SegmentationOffload" boolValues)
+    (assertValueOneOf "GenericSegmentationOffload" boolValues)
+    (assertValueOneOf "UDPSegmentationOffload" boolValues)
+    (assertValueOneOf "GenericReceiveOffload" boolValues)
+    (assertValueOneOf "LargeReceiveOffload" boolValues)
+    (assertRange "RxChannels" 1 4294967295)
+    (assertRange "TxChannels" 1 4294967295)
+    (assertRange "OtherChannels" 1 4294967295)
+    (assertRange "CombinedChannels" 1 4294967295)
   ];
 
   checkNetdev = checkUnitConfig "Netdev" [
@@ -31,16 +43,21 @@ let
     (assertHasField "Name")
     (assertHasField "Kind")
     (assertValueOneOf "Kind" [
-      "bridge" "bond" "vlan" "macvlan" "vxlan" "ipip"
-      "gre" "sit" "vti" "veth" "tun" "tap" "dummy"
+      "bond" "bridge" "dummy" "gre" "gretap" "ip6gre" "ip6tnl" "ip6gretap" "ipip"
+      "ipvlan" "macvlan" "macvtap" "sit" "tap" "tun" "veth" "vlan" "vti" "vti6"
+      "vxlan" "geneve" "vrf" "vcan" "vxcan" "wireguard" "netdevsim"
     ])
     (assertByteFormat "MTUBytes")
     (assertMacAddress "MACAddress")
   ];
 
   checkVlan = checkUnitConfig "VLAN" [
-    (assertOnlyFields ["Id"])
+    (assertOnlyFields ["Id" "GVRP" "MVRP" "LooseBinding" "ReorderHeader"])
     (assertRange "Id" 0 4094)
+    (assertValueOneOf "GVRP" boolValues)
+    (assertValueOneOf "MVRP" boolValues)
+    (assertValueOneOf "LooseBinding" boolValues)
+    (assertValueOneOf "ReorderHeader" boolValues)
   ];
 
   checkMacvlan = checkUnitConfig "MACVLAN" [
@@ -49,15 +66,41 @@ let
   ];
 
   checkVxlan = checkUnitConfig "VXLAN" [
-    (assertOnlyFields ["Id" "Group" "TOS" "TTL" "MacLearning"])
+    (assertOnlyFields [
+      "Id" "Remote" "Local" "TOS" "TTL" "MacLearning" "FDBAgeingSec"
+      "MaximumFDBEntries" "ReduceARPProxy" "L2MissNotification"
+      "L3MissNotification" "RouteShortCircuit" "UDPChecksum"
+      "UDP6ZeroChecksumTx" "UDP6ZeroChecksumRx" "RemoteChecksumTx"
+      "RemoteChecksumRx" "GroupPolicyExtension" "DestinationPort" "PortRange"
+      "FlowLabel"
+    ])
     (assertRange "TTL" 0 255)
     (assertValueOneOf "MacLearning" boolValues)
+    (assertValueOneOf "ReduceARPProxy" boolValues)
+    (assertValueOneOf "L2MissNotification" boolValues)
+    (assertValueOneOf "L3MissNotification" boolValues)
+    (assertValueOneOf "RouteShortCircuit" boolValues)
+    (assertValueOneOf "UDPChecksum" boolValues)
+    (assertValueOneOf "UDP6ZeroChecksumTx" boolValues)
+    (assertValueOneOf "UDP6ZeroChecksumRx" boolValues)
+    (assertValueOneOf "RemoteChecksumTx" boolValues)
+    (assertValueOneOf "RemoteChecksumRx" boolValues)
+    (assertValueOneOf "GroupPolicyExtension" boolValues)
+    (assertRange "FlowLabel" 0 1048575)
   ];
 
   checkTunnel = checkUnitConfig "Tunnel" [
-    (assertOnlyFields ["Local" "Remote" "TOS" "TTL" "DiscoverPathMTU"])
+    (assertOnlyFields [
+      "Local" "Remote" "TOS" "TTL" "DiscoverPathMTU" "IPv6FlowLabel" "CopyDSCP"
+      "EncapsulationLimit" "Key" "InputKey" "OutputKey" "Mode" "Independent"
+      "AllowLocalRemote"
+    ])
     (assertRange "TTL" 0 255)
     (assertValueOneOf "DiscoverPathMTU" boolValues)
+    (assertValueOneOf "CopyDSCP" boolValues)
+    (assertValueOneOf "Mode" ["ip6ip6" "ipip6" "any"])
+    (assertValueOneOf "Independent" boolValues)
+    (assertValueOneOf "AllowLocalRemote" boolValues)
   ];
 
   checkPeer = checkUnitConfig "Peer" [
@@ -66,10 +109,11 @@ let
   ];
 
   tunTapChecks = [
-    (assertOnlyFields ["OneQueue" "MultiQueue" "PacketInfo" "User" "Group"])
+    (assertOnlyFields ["OneQueue" "MultiQueue" "PacketInfo" "VNetHeader" "User" "Group"])
     (assertValueOneOf "OneQueue" boolValues)
     (assertValueOneOf "MultiQueue" boolValues)
     (assertValueOneOf "PacketInfo" boolValues)
+    (assertValueOneOf "VNetHeader" boolValues)
   ];
 
   checkTun = checkUnitConfig "Tun" tunTapChecks;
@@ -79,67 +123,121 @@ let
   checkBond = checkUnitConfig "Bond" [
     (assertOnlyFields [
       "Mode" "TransmitHashPolicy" "LACPTransmitRate" "MIIMonitorSec"
-      "UpDelaySec" "DownDelaySec" "GratuitousARP"
+      "UpDelaySec" "DownDelaySec" "LearnPacketIntervalSec" "AdSelect"
+      "FailOverMACPolicy" "ARPValidate" "ARPIntervalSec" "ARPIPTargets"
+      "ARPAllTargets" "PrimaryReselectPolicy" "ResendIGMP" "PacketsPerSlave"
+      "GratuitousARP" "AllSlavesActive" "MinLinks"
     ])
     (assertValueOneOf "Mode" [
       "balance-rr" "active-backup" "balance-xor"
       "broadcast" "802.3ad" "balance-tlb" "balance-alb"
     ])
     (assertValueOneOf "TransmitHashPolicy" [
-      "layer2" "layer3+4" "layer2+3" "encap2+3" "802.3ad" "encap3+4"
+      "layer2" "layer3+4" "layer2+3" "encap2+3" "encap3+4"
     ])
     (assertValueOneOf "LACPTransmitRate" ["slow" "fast"])
+    (assertValueOneOf "AdSelect" ["stable" "bandwidth" "count"])
+    (assertValueOneOf "FailOverMACPolicy" ["none" "active" "follow"])
+    (assertValueOneOf "ARPValidate" ["none" "active" "backup" "all"])
+    (assertValueOneOf "ARPAllTargets" ["any" "all"])
+    (assertValueOneOf "PrimaryReselectPolicy" ["always" "better" "failure"])
+    (assertRange "ResendIGMP" 0 255)
+    (assertRange "PacketsPerSlave" 0 65535)
+    (assertRange "GratuitousARP" 0 255)
+    (assertValueOneOf "AllSlavesActive" boolValues)
   ];
 
   checkNetwork = checkUnitConfig "Network" [
     (assertOnlyFields [
-      "Description" "DHCP" "DHCPServer" "IPForward" "IPMasquerade" "IPv4LL" "IPv4LLRoute"
-      "LLMNR" "MulticastDNS" "Domains" "Bridge" "Bond" "IPv6PrivacyExtensions"
+      "Description" "DHCP" "DHCPServer" "LinkLocalAddressing" "IPv4LLRoute"
+      "IPv6Token" "LLMNR" "MulticastDNS" "DNSOverTLS" "DNSSEC"
+      "DNSSECNegativeTrustAnchors" "LLDP" "EmitLLDP" "BindCarrier" "Address"
+      "Gateway" "DNS" "Domains" "NTP" "IPForward" "IPMasquerade"
+      "IPv6PrivacyExtensions" "IPv6AcceptRA" "IPv6DuplicateAddressDetection"
+      "IPv6HopLimit" "IPv4ProxyARP" "IPv6ProxyNDP" "IPv6ProxyNDPAddress"
+      "IPv6PrefixDelegation" "IPv6MTUBytes" "Bridge" "Bond" "VRF" "VLAN"
+      "IPVLAN" "MACVLAN" "VXLAN" "Tunnel" "ActiveSlave" "PrimarySlave"
+      "ConfigureWithoutCarrier"
     ])
-    (assertValueOneOf "DHCP" ["both" "none" "v4" "v6"])
+    # Note: For DHCP the values both, none, v4, v6 are deprecated
+    (assertValueOneOf "DHCP" ["yes" "no" "ipv4" "ipv6" "both" "none" "v4" "v6"])
     (assertValueOneOf "DHCPServer" boolValues)
+    (assertValueOneOf "LinkLocalAddressing" ["yes" "no" "ipv4" "ipv6"])
+    (assertValueOneOf "IPv4LLRoute" boolValues)
+    (assertValueOneOf "LLMNR" ["yes" "resolve" "no"])
+    (assertValueOneOf "MulticastDNS" ["yes" "resolve" "no"])
+    (assertValueOneOf "DNSOverTLS" ["opportunistic" "no"])
+    (assertValueOneOf "DNSSEC" ["yes" "allow-downgrade" "no"])
+    (assertValueOneOf "LLDP" ["yes" "routers-only" "no"])
+    (assertValueOneOf "EmitLLDP" ["yes" "no" "nearest-bridge" "non-tpmr-bridge" "customer-bridge"])
     (assertValueOneOf "IPForward" ["yes" "no" "ipv4" "ipv6"])
     (assertValueOneOf "IPMasquerade" boolValues)
-    (assertValueOneOf "IPv4LL" boolValues)
-    (assertValueOneOf "IPv4LLRoute" boolValues)
-    (assertValueOneOf "LLMNR" boolValues)
-    (assertValueOneOf "MulticastDNS" boolValues)
     (assertValueOneOf "IPv6PrivacyExtensions" ["yes" "no" "prefer-public" "kernel"])
+    (assertValueOneOf "IPv6AcceptRA" boolValues)
+    (assertValueOneOf "IPv4ProxyARP" boolValues)
+    (assertValueOneOf "IPv6ProxyNDP" boolValues)
+    (assertValueOneOf "IPv6PrefixDelegation" boolValues)
+    (assertValueOneOf "ActiveSlave" boolValues)
+    (assertValueOneOf "PrimarySlave" boolValues)
+    (assertValueOneOf "ConfigureWithoutCarrier" boolValues)
   ];
 
   checkAddress = checkUnitConfig "Address" [
-    (assertOnlyFields ["Address" "Peer" "Broadcast" "Label"])
+    (assertOnlyFields [
+      "Address" "Peer" "Broadcast" "Label" "PreferredLifetime" "Scope"
+      "HomeAddress" "DuplicateAddressDetection" "ManageTemporaryAddress"
+      "PrefixRoute" "AutoJoin"
+    ])
     (assertHasField "Address")
+    (assertValueOneOf "PreferredLifetime" ["forever" "infinity" "0" 0])
+    (assertValueOneOf "HomeAddress" boolValues)
+    (assertValueOneOf "DuplicateAddressDetection" boolValues)
+    (assertValueOneOf "ManageTemporaryAddress" boolValues)
+    (assertValueOneOf "PrefixRoute" boolValues)
+    (assertValueOneOf "AutoJoin" boolValues)
   ];
 
   checkRoute = checkUnitConfig "Route" [
-    (assertOnlyFields ["Gateway" "Destination" "Metric"])
+    (assertOnlyFields [
+      "Gateway" "GatewayOnlink" "Destination" "Source" "Metric"
+      "IPv6Preference" "Scope" "PreferredSource" "Table" "Protocol" "Type"
+      "InitialCongestionWindow" "InitialAdvertisedReceiveWindow" "QuickAck"
+      "MTUBytes"
+    ])
     (assertHasField "Gateway")
   ];
 
   checkDhcp = checkUnitConfig "DHCP" [
     (assertOnlyFields [
-      "UseDNS" "UseMTU" "SendHostname" "UseHostname" "UseDomains" "UseRoutes"
-      "CriticalConnections" "VendorClassIdentifier" "RequestBroadcast"
-      "RouteMetric"
+      "UseDNS" "UseNTP" "UseMTU" "Anonymize" "SendHostname" "UseHostname"
+      "Hostname" "UseDomains" "UseRoutes" "UseTimezone" "CriticalConnection"
+      "ClientIdentifier" "VendorClassIdentifier" "UserClass" "DUIDType"
+      "DUIDRawData" "IAID" "RequestBroadcast" "RouteMetric" "RouteTable"
+      "ListenPort" "RapidCommit"
     ])
     (assertValueOneOf "UseDNS" boolValues)
+    (assertValueOneOf "UseNTP" boolValues)
     (assertValueOneOf "UseMTU" boolValues)
+    (assertValueOneOf "Anonymize" boolValues)
     (assertValueOneOf "SendHostname" boolValues)
     (assertValueOneOf "UseHostname" boolValues)
-    (assertValueOneOf "UseDomains" boolValues)
+    (assertValueOneOf "UseDomains" ["yes" "no" "route"])
     (assertValueOneOf "UseRoutes" boolValues)
-    (assertValueOneOf "CriticalConnections" boolValues)
+    (assertValueOneOf "UseTimezone" boolValues)
+    (assertValueOneOf "CriticalConnection" boolValues)
     (assertValueOneOf "RequestBroadcast" boolValues)
+    (assertRange "RouteTable" 0 4294967295)
+    (assertValueOneOf "RapidCommit" boolValues)
   ];
 
   checkDhcpServer = checkUnitConfig "DHCPServer" [
     (assertOnlyFields [
       "PoolOffset" "PoolSize" "DefaultLeaseTimeSec" "MaxLeaseTimeSec"
-      "EmitDNS" "DNS" "EmitNTP" "NTP" "EmitTimezone" "Timezone"
+      "EmitDNS" "DNS" "EmitNTP" "NTP" "EmitRouter" "EmitTimezone" "Timezone"
     ])
     (assertValueOneOf "EmitDNS" boolValues)
     (assertValueOneOf "EmitNTP" boolValues)
+    (assertValueOneOf "EmitRouter" boolValues)
     (assertValueOneOf "EmitTimezone" boolValues)
   ];
 
@@ -461,6 +559,36 @@ let
       '';
     };
 
+    bridge = mkOption {
+      default = [ ];
+      type = types.listOf types.str;
+      description = ''
+        A list of bridge interfaces to be added to the network section of the
+        unit.  See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
+    bond = mkOption {
+      default = [ ];
+      type = types.listOf types.str;
+      description = ''
+        A list of bond interfaces to be added to the network section of the
+        unit.  See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
+    vrf = mkOption {
+      default = [ ];
+      type = types.listOf types.str;
+      description = ''
+        A list of vrf interfaces to be added to the network section of the
+        unit.  See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
     vlan = mkOption {
       default = [ ];
       type = types.listOf types.str;
@@ -619,6 +747,9 @@ let
           ${concatStringsSep "\n" (map (s: "Gateway=${s}") def.gateway)}
           ${concatStringsSep "\n" (map (s: "DNS=${s}") def.dns)}
           ${concatStringsSep "\n" (map (s: "NTP=${s}") def.ntp)}
+          ${concatStringsSep "\n" (map (s: "Bridge=${s}") def.bridge)}
+          ${concatStringsSep "\n" (map (s: "Bond=${s}") def.bond)}
+          ${concatStringsSep "\n" (map (s: "VRF=${s}") def.vrf)}
           ${concatStringsSep "\n" (map (s: "VLAN=${s}") def.vlan)}
           ${concatStringsSep "\n" (map (s: "MACVLAN=${s}") def.macvlan)}
           ${concatStringsSep "\n" (map (s: "VXLAN=${s}") def.vxlan)}
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 71b806a0b4e1..f58b68cb3353 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -179,7 +179,7 @@ let
         fi
       done
 
-      if [ -z "${toString pkgs.stdenv.isCross}" ]; then
+      if [ -z "${toString (pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform)}" ]; then
       # Make sure that the patchelf'ed binaries still work.
       echo "testing patched programs..."
       $out/bin/ash -c 'echo hello world' | grep "hello world"
@@ -248,6 +248,14 @@ let
 
     isExecutable = true;
 
+    postInstall = ''
+      echo checking syntax
+      # check both with bash
+      ${pkgs.bash}/bin/sh -n $target
+      # and with ash shell, just in case
+      ${extraUtils}/bin/ash -n $target
+    '';
+
     inherit udevRules extraUtils modulesClosure;
 
     inherit (config.boot) resumeDevice;
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 951da797fe5c..12e029ae57f8 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -65,6 +65,7 @@ let
       "systemd-user-sessions.service"
       "dbus-org.freedesktop.machine1.service"
       "user@.service"
+      "user-runtime-dir@.service"
 
       # Journal.
       "systemd-journald.socket"
@@ -189,9 +190,8 @@ let
     ];
 
   makeJobScript = name: text:
-    let mkScriptName =  s: (replaceChars [ "\\" ] [ "-" ] (shellEscape s) );
-        x = pkgs.writeTextFile { name = "unit-script"; executable = true; destination = "/bin/${mkScriptName name}"; inherit text; };
-    in "${x}/bin/${mkScriptName name}";
+    let mkScriptName =  s: "unit-script-" + (replaceChars [ "\\" "@" ] [ "-" "_" ] (shellEscape s) );
+    in  pkgs.writeTextFile { name = mkScriptName name; executable = true; inherit text; };
 
   unitConfig = { config, ... }: {
     config = {
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 7120856387ef..2b3b09d725c7 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -23,12 +23,8 @@ let
 
   kernel = config.boot.kernelPackages;
 
-  packages = if config.boot.zfs.enableLegacyCrypto then {
-    spl = kernel.splLegacyCrypto;
-    zfs = kernel.zfsLegacyCrypto;
-    zfsUser = pkgs.zfsLegacyCrypto;
-  } else if config.boot.zfs.enableUnstable then {
-    spl = kernel.splUnstable;
+  packages = if config.boot.zfs.enableUnstable then {
+    spl = null;
     zfs = kernel.zfsUnstable;
     zfsUser = pkgs.zfsUnstable;
   } else {
@@ -117,27 +113,6 @@ in
           '';
       };
 
-      enableLegacyCrypto = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Enabling this option will allow you to continue to use the old format for
-          encrypted datasets. With the inclusion of stability patches the format of
-          encrypted datasets has changed. They can still be accessed and mounted but
-          in read-only mode mounted. It is highly recommended to convert them to
-          the new format.
-
-          This option is only for convenience to people that cannot convert their
-          datasets to the new format yet and it will be removed in due time.
-
-          For migration strategies from old format to this new one, check the Wiki:
-          https://nixos.wiki/wiki/NixOS_on_ZFS#Encrypted_Dataset_Format_Change
-
-          See https://github.com/zfsonlinux/zfs/pull/6864 for more details about
-          the stability patches.
-          '';
-      };
-
       extraPools = mkOption {
         type = types.listOf types.str;
         default = [];
@@ -350,12 +325,12 @@ in
       virtualisation.lxd.zfsSupport = true;
 
       boot = {
-        kernelModules = [ "spl" "zfs" ] ;
-        extraModulePackages = with packages; [ spl zfs ];
+        kernelModules = [ "zfs" ] ++ optional (!cfgZfs.enableUnstable) "spl";
+        extraModulePackages = with packages; [ zfs ] ++ optional (!cfgZfs.enableUnstable) spl;
       };
 
       boot.initrd = mkIf inInitrd {
-        kernelModules = [ "spl" "zfs" ];
+        kernelModules = [ "zfs" ] ++ optional (!cfgZfs.enableUnstable) "spl";
         extraUtilsCommands =
           ''
             copy_bin_and_libs ${packages.zfsUser}/sbin/zfs
diff --git a/nixos/modules/tasks/trackpoint.nix b/nixos/modules/tasks/trackpoint.nix
index 3575a291b2b4..b154cf9f5f08 100644
--- a/nixos/modules/tasks/trackpoint.nix
+++ b/nixos/modules/tasks/trackpoint.nix
@@ -55,6 +55,15 @@ with lib;
         '';
       };
 
+      device = mkOption {
+        default = "TPPS/2 IBM TrackPoint";
+        type = types.str;
+        description = ''
+          The device name of the trackpoint. You can check with xinput.
+          Some newer devices (example x1c6) use "TPPS/2 Elan TrackPoint".
+        '';
+      };
+
     };
 
   };
@@ -68,12 +77,12 @@ with lib;
     (mkIf cfg.enable {
       services.udev.extraRules =
       ''
-        ACTION=="add|change", SUBSYSTEM=="input", ATTR{name}=="TPPS/2 IBM TrackPoint", ATTR{device/speed}="${toString cfg.speed}", ATTR{device/sensitivity}="${toString cfg.sensitivity}"
+        ACTION=="add|change", SUBSYSTEM=="input", ATTR{name}=="${cfg.device}", ATTR{device/speed}="${toString cfg.speed}", ATTR{device/sensitivity}="${toString cfg.sensitivity}"
       '';
 
       system.activationScripts.trackpoint =
         ''
-          ${config.systemd.package}/bin/udevadm trigger --attr-match=name="TPPS/2 IBM TrackPoint"
+          ${config.systemd.package}/bin/udevadm trigger --attr-match=name="${cfg.device}"
         '';
     })
 
@@ -81,7 +90,7 @@ with lib;
       services.xserver.inputClassSections =
         [''
         Identifier "Trackpoint Wheel Emulation"
-          MatchProduct "${if cfg.fakeButtons then "PS/2 Generic Mouse" else "ETPS/2 Elantech TrackPoint|Elantech PS/2 TrackPoint|TPPS/2 IBM TrackPoint|DualPoint Stick|Synaptics Inc. Composite TouchPad / TrackPoint|ThinkPad USB Keyboard with TrackPoint|USB Trackpoint pointing device|Composite TouchPad / TrackPoint"}"
+          MatchProduct "${if cfg.fakeButtons then "PS/2 Generic Mouse" else "ETPS/2 Elantech TrackPoint|Elantech PS/2 TrackPoint|TPPS/2 IBM TrackPoint|DualPoint Stick|Synaptics Inc. Composite TouchPad / TrackPoint|ThinkPad USB Keyboard with TrackPoint|USB Trackpoint pointing device|Composite TouchPad / TrackPoint|${cfg.device}"}"
           MatchDevicePath "/dev/input/event*"
           Option "EmulateWheel" "true"
           Option "EmulateWheelButton" "2"
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index 3d002bc22329..3e38662f5b0f 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -17,6 +17,10 @@ let
     ${optionalString cfg.qemuOvmf ''
       nvram = ["/run/libvirt/nix-ovmf/OVMF_CODE.fd:/run/libvirt/nix-ovmf/OVMF_VARS.fd"]
     ''}
+    ${optionalString (!cfg.qemuRunAsRoot) ''
+      user = "qemu-libvirtd"
+      group = "qemu-libvirtd"
+    ''}
     ${cfg.qemuVerbatimConfig}
   '';
 
@@ -56,6 +60,18 @@ in {
       '';
     };
 
+    virtualisation.libvirtd.qemuRunAsRoot = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        If true,  libvirtd runs qemu as root.
+        If false, libvirtd runs qemu as unprivileged user qemu-libvirtd.
+        Changing this option to false may cause file permission issues
+        for existing guests. To fix these, manually change ownership
+        of affected files in /var/lib/libvirt/qemu to qemu-libvirtd.
+      '';
+    };
+
     virtualisation.libvirtd.qemuVerbatimConfig = mkOption {
       type = types.lines;
       default = ''
@@ -110,6 +126,14 @@ in {
 
     users.groups.libvirtd.gid = config.ids.gids.libvirtd;
 
+    # libvirtd runs qemu as this user and group by default
+    users.extraGroups.qemu-libvirtd.gid = config.ids.gids.qemu-libvirtd;
+    users.extraUsers.qemu-libvirtd = {
+      uid = config.ids.uids.qemu-libvirtd;
+      isNormalUser = false;
+      group = "qemu-libvirtd";
+    };
+
     systemd.packages = [ pkgs.libvirt ];
 
     systemd.services.libvirtd = {
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index 0abf7b11703c..4e9c87222d0a 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -156,9 +156,6 @@ let
             --partition-guid=2:970C694F-AFD0-4B99-B750-CDB7A329AB6F \
             --hybrid 2 \
             --recompute-chs /dev/vda
-          . /sys/class/block/vda2/uevent
-          mknod /dev/vda2 b $MAJOR $MINOR
-          . /sys/class/block/vda/uevent
           ${pkgs.dosfstools}/bin/mkfs.fat -F16 /dev/vda2
           export MTOOLS_SKIP_CHECK=1
           ${pkgs.mtools}/bin/mlabel -i /dev/vda2 ::boot
diff --git a/nixos/modules/virtualisation/virtualbox-host.nix b/nixos/modules/virtualisation/virtualbox-host.nix
index 8adf3aa919d8..60779579402c 100644
--- a/nixos/modules/virtualisation/virtualbox-host.nix
+++ b/nixos/modules/virtualisation/virtualbox-host.nix
@@ -5,8 +5,9 @@ with lib;
 let
   cfg = config.virtualisation.virtualbox.host;
 
-  virtualbox = pkgs.virtualbox.override {
-    inherit (cfg) enableExtensionPack enableHardening headless;
+  virtualbox = cfg.package.override {
+    inherit (cfg) enableHardening headless;
+    extensionPack = if cfg.enableExtensionPack then pkgs.virtualboxExtpack else null;
   };
 
   kernelModules = config.boot.kernelPackages.virtualbox.override {
@@ -28,7 +29,25 @@ in
       '';
     };
 
-    enableExtensionPack = mkEnableOption "VirtualBox extension pack";
+    enableExtensionPack = mkEnableOption "VirtualBox extension pack" // {
+      description = ''
+        Whether to install the Oracle Extension Pack for VirtualBox.
+
+        <important><para>
+          You must set <literal>nixpkgs.config.allowUnfree = true</literal> in
+          order to use this.  This requires you accept the VirtualBox PUEL.
+        </para></important>
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.virtualbox;
+      defaultText = "pkgs.virtualbox";
+      description = ''
+        Which VirtualBox package to use.
+      '';
+    };
 
     addNetworkInterface = mkOption {
       type = types.bool;