about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/release-notes/rl-2103.xml47
-rw-r--r--nixos/modules/hardware/i2c.nix43
-rw-r--r--nixos/modules/hardware/network/ath-user-regd.nix31
-rw-r--r--nixos/modules/hardware/sensor/hddtemp.nix81
-rw-r--r--nixos/modules/hardware/video/nvidia.nix51
-rw-r--r--nixos/modules/hardware/video/switcheroo-control.nix18
-rw-r--r--nixos/modules/module-list.nix11
-rw-r--r--nixos/modules/programs/cdemu.nix3
-rw-r--r--nixos/modules/programs/venus.nix173
-rw-r--r--nixos/modules/rename.nix2
-rw-r--r--nixos/modules/security/acme.nix6
-rw-r--r--nixos/modules/services/audio/snapserver.nix28
-rw-r--r--nixos/modules/services/backup/mysql-backup.nix4
-rw-r--r--nixos/modules/services/backup/postgresql-backup.nix3
-rw-r--r--nixos/modules/services/backup/restic.nix4
-rw-r--r--nixos/modules/services/cluster/hadoop/default.nix4
-rw-r--r--nixos/modules/services/cluster/k3s/default.nix1
-rw-r--r--nixos/modules/services/cluster/kubernetes/kubelet.nix12
-rw-r--r--nixos/modules/services/continuous-integration/buildbot/master.nix4
-rw-r--r--nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix55
-rw-r--r--nixos/modules/services/development/hoogle.nix1
-rw-r--r--nixos/modules/services/hardware/auto-cpufreq.nix18
-rw-r--r--nixos/modules/services/hardware/power-profiles-daemon.nix53
-rw-r--r--nixos/modules/services/hardware/thinkfan.nix260
-rw-r--r--nixos/modules/services/mail/postfix.nix2
-rw-r--r--nixos/modules/services/misc/etebase-server.nix205
-rw-r--r--nixos/modules/services/misc/pykms.nix13
-rw-r--r--nixos/modules/services/misc/rippled.nix1
-rw-r--r--nixos/modules/services/misc/svnserve.nix1
-rw-r--r--nixos/modules/services/misc/synergy.nix5
-rw-r--r--nixos/modules/services/misc/weechat.nix1
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.nix3
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nginx.nix7
-rw-r--r--nixos/modules/services/network-filesystems/netatalk.nix3
-rw-r--r--nixos/modules/services/network-filesystems/openafs/server.nix1
-rw-r--r--nixos/modules/services/network-filesystems/xtreemfs.nix15
-rw-r--r--nixos/modules/services/network-filesystems/yandex-disk.nix2
-rw-r--r--nixos/modules/services/networking/bee-clef.nix107
-rw-r--r--nixos/modules/services/networking/bee.nix149
-rw-r--r--nixos/modules/services/networking/bind.nix36
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy2.nix8
-rw-r--r--nixos/modules/services/networking/epmd.nix2
-rw-r--r--nixos/modules/services/networking/flashpolicyd.nix86
-rw-r--r--nixos/modules/services/networking/ircd-hybrid/default.nix8
-rw-r--r--nixos/modules/services/networking/mailpile.nix4
-rw-r--r--nixos/modules/services/networking/prayer.nix3
-rw-r--r--nixos/modules/services/networking/quassel.nix4
-rw-r--r--nixos/modules/services/networking/radvd.nix1
-rw-r--r--nixos/modules/services/networking/resilio.nix1
-rw-r--r--nixos/modules/services/networking/sabnzbd.nix3
-rw-r--r--nixos/modules/services/networking/shairport-sync.nix2
-rw-r--r--nixos/modules/services/networking/ssh/lshd.nix6
-rw-r--r--nixos/modules/services/security/oauth2_proxy.nix1
-rw-r--r--nixos/modules/services/security/oauth2_proxy_nginx.nix2
-rw-r--r--nixos/modules/services/web-apps/galene.nix178
-rw-r--r--nixos/modules/services/web-apps/mediawiki.nix1
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix25
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/vhost-options.nix2
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix3
-rw-r--r--nixos/modules/services/web-servers/nginx/location-options.nix2
-rw-r--r--nixos/modules/services/web-servers/unit/default.nix2
-rw-r--r--nixos/modules/services/x11/clight.nix30
-rw-r--r--nixos/modules/services/x11/desktop-managers/cinnamon.nix1
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix1
-rw-r--r--nixos/modules/services/x11/desktop-managers/pantheon.nix1
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix1
-rw-r--r--nixos/modules/services/x11/window-managers/exwm.nix1
-rw-r--r--nixos/modules/services/x11/xserver.nix3
-rw-r--r--nixos/modules/system/boot/kernel.nix18
-rw-r--r--nixos/modules/tasks/filesystems.nix9
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix84
-rw-r--r--nixos/modules/virtualisation/cri-o.nix5
-rw-r--r--nixos/modules/virtualisation/docker.nix1
-rw-r--r--nixos/modules/virtualisation/lxd.nix24
-rw-r--r--nixos/modules/virtualisation/nixos-containers.nix55
-rw-r--r--nixos/modules/virtualisation/xen-dom0.nix5
-rw-r--r--nixos/tests/agda.nix11
-rw-r--r--nixos/tests/all-tests.nix3
-rw-r--r--nixos/tests/containers-custom-pkgs.nix50
-rw-r--r--nixos/tests/installer.nix4
-rw-r--r--nixos/tests/kernel-latest-ath-user-regd.nix17
-rw-r--r--nixos/tests/power-profiles-daemon.nix45
-rw-r--r--nixos/tests/scala.nix33
-rw-r--r--nixos/tests/snapcast.nix19
-rw-r--r--nixos/tests/vscodium.nix62
85 files changed, 1670 insertions, 621 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2103.xml b/nixos/doc/manual/release-notes/rl-2103.xml
index 24a0281310c6b..b76d09302904c 100644
--- a/nixos/doc/manual/release-notes/rl-2103.xml
+++ b/nixos/doc/manual/release-notes/rl-2103.xml
@@ -466,6 +466,34 @@ self: super:
      ALSA OSS emulation (<varname>sound.enableOSSEmulation</varname>) is now disabled by default.
     </para>
    </listitem>
+   <listitem>
+    <para>
+      Thinkfan as been updated to <literal>1.2.x</literal>, which comes with a
+      new YAML based configuration format. For this reason, several NixOS options
+      of the thinkfan module have been changed to non-backward compatible types.
+      In addition, a new <xref linkend="opt-services.thinkfan.settings"/> option has
+      been added.
+    </para>
+    <para>
+      Please read the <link xlink:href="https://github.com/vmatare/thinkfan#readme">
+      thinkfan documentation</link> before updating.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Adobe Flash Player support has been dropped from the tree. In particular,
+     the following packages no longer support it:
+     <itemizedlist>
+      <listitem><simpara><package>chromium</package></simpara></listitem>
+      <listitem><simpara><package>firefox</package></simpara></listitem>
+      <listitem><simpara><package>qt48</package></simpara></listitem>
+      <listitem><simpara><package>qt5.qtwebkit</package></simpara></listitem>
+     </itemizedlist>
+     Additionally, packages <package>flashplayer</package> and
+     <package>hal-flash</package> were removed along with the
+     <varname>services.flashpolicyd</varname> module.
+    </para>
+   </listitem>
   </itemizedlist>
  </section>
 
@@ -479,6 +507,14 @@ self: super:
   <itemizedlist>
    <listitem>
     <para>
+     <literal>stdenv.lib</literal> has been deprecated and will break
+     eval in 21.11.  Please use <literal>pkgs.lib</literal> instead.
+     See <link xlink:href="https://github.com/NixOS/nixpkgs/issues/108938">#108938</link>
+     for details.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
      The Mailman NixOS module (<literal>services.mailman</literal>) has a new
      option <xref linkend="opt-services.mailman.enablePostfix" />, defaulting
      to true, that controls integration with Postfix.
@@ -653,6 +689,17 @@ self: super:
      The <varname>platform</varname> grouping of these things never meant anything, and was just a historial/implementation artifact that was overdue removal.
     </para>
    </listitem>
+   <listitem>
+    <para>
+     <varname>services.restic</varname> now uses a dedicated cache directory for every backup defined in <varname>services.restic.backups</varname>. The old global cache directory, <literal>/root/.cache/restic</literal>, is now unused and can be removed to free up disk space.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     <literal>isync</literal>: The <literal>isync</literal> compatibility wrapper was removed and the Master/Slave
+     terminology has been deprecated and should be replaced with Far/Near in the configuration file.
+    </para>
+   </listitem>
   </itemizedlist>
  </section>
 </section>
diff --git a/nixos/modules/hardware/i2c.nix b/nixos/modules/hardware/i2c.nix
new file mode 100644
index 0000000000000..ff14b4b1c891d
--- /dev/null
+++ b/nixos/modules/hardware/i2c.nix
@@ -0,0 +1,43 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.i2c;
+in
+
+{
+  options.hardware.i2c = {
+    enable = mkEnableOption ''
+      i2c devices support. By default access is granted to users in the "i2c"
+      group (will be created if non-existent) and any user with a seat, meaning
+      logged on the computer locally.
+    '';
+
+    group = mkOption {
+      type = types.str;
+      default = "i2c";
+      description = ''
+        Grant access to i2c devices (/dev/i2c-*) to users in this group.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    boot.kernelModules = [ "i2c-dev" ];
+
+    users.groups = mkIf (cfg.group == "i2c") {
+      i2c = { };
+    };
+
+    services.udev.extraRules = ''
+      # allow group ${cfg.group} and users with a seat use of i2c devices
+      ACTION=="add", KERNEL=="i2c-[0-9]*", TAG+="uaccess", GROUP="${cfg.group}", MODE="660"
+    '';
+
+  };
+
+  meta.maintainers = [ maintainers.rnhmjoj ];
+
+}
diff --git a/nixos/modules/hardware/network/ath-user-regd.nix b/nixos/modules/hardware/network/ath-user-regd.nix
new file mode 100644
index 0000000000000..b5ade5ed50105
--- /dev/null
+++ b/nixos/modules/hardware/network/ath-user-regd.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  kernelVersion = config.boot.kernelPackages.kernel.version;
+  linuxKernelMinVersion = "5.8";
+  kernelPatch = pkgs.kernelPatches.ath_regd_optional // {
+    extraConfig = ''
+      ATH_USER_REGD y
+    '';
+  };
+in
+{
+  options.networking.wireless.athUserRegulatoryDomain = mkOption {
+    default = false;
+    type = types.bool;
+    description = ''
+      If enabled, sets the ATH_USER_REGD kernel config switch to true to
+      disable the enforcement of EEPROM regulatory restrictions for ath
+      drivers. Requires at least Linux ${linuxKernelMinVersion}.
+    '';
+  };
+
+  config = mkIf config.networking.wireless.athUserRegulatoryDomain {
+    assertions = singleton {
+      assertion = lessThan 0 (builtins.compareVersions kernelVersion linuxKernelMinVersion);
+      message = "ATH_USER_REGD patch for kernels older than ${linuxKernelMinVersion} not ported yet!";
+    };
+    boot.kernelPatches = [ kernelPatch ];
+  };
+}
diff --git a/nixos/modules/hardware/sensor/hddtemp.nix b/nixos/modules/hardware/sensor/hddtemp.nix
new file mode 100644
index 0000000000000..df3f75e229a2f
--- /dev/null
+++ b/nixos/modules/hardware/sensor/hddtemp.nix
@@ -0,0 +1,81 @@
+{ config, lib, pkgs, ... }:
+let
+  inherit (lib) mkIf mkOption types;
+
+  cfg = config.hardware.sensor.hddtemp;
+
+  wrapper = pkgs.writeShellScript "hddtemp-wrapper" ''
+    set -eEuo pipefail
+
+    file=/var/lib/hddtemp/hddtemp.db
+
+    drives=(${toString (map (e: ''$(realpath ${lib.escapeShellArg e}) '') cfg.drives)})
+
+    cp ${pkgs.hddtemp}/share/hddtemp/hddtemp.db $file
+    ${lib.concatMapStringsSep "\n" (e: "echo ${lib.escapeShellArg e} >> $file") cfg.dbEntries}
+
+    exec ${pkgs.hddtemp}/bin/hddtemp ${lib.escapeShellArgs cfg.extraArgs} \
+      --daemon \
+      --unit=${cfg.unit} \
+      --file=$file \
+      ''${drives[@]}
+  '';
+
+in
+{
+  meta.maintainers = with lib.maintainers; [ peterhoeg ];
+
+  ###### interface
+
+  options = {
+    hardware.sensor.hddtemp = {
+      enable = mkOption {
+        description = ''
+          Enable this option to support HDD/SSD temperature sensors.
+        '';
+        type = types.bool;
+        default = false;
+      };
+
+      drives = mkOption {
+        description = "List of drives to monitor. If you pass /dev/disk/by-path/* entries the symlinks will be resolved as hddtemp doesn't like names with colons.";
+        type = types.listOf types.str;
+      };
+
+      unit = mkOption {
+        description = "Celcius or Fahrenheit";
+        type = types.enum [ "C" "F" ];
+        default = "C";
+      };
+
+      dbEntries = mkOption {
+        description = "Additional DB entries";
+        type = types.listOf types.str;
+        default = [ ];
+      };
+
+      extraArgs = mkOption {
+        description = "Additional arguments passed to the daemon.";
+        type = types.listOf types.str;
+        default = [ ];
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.hddtemp = {
+      description = "HDD/SSD temperature";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = wrapper;
+        StateDirectory = "hddtemp";
+        PrivateTmp = true;
+        ProtectHome = "tmpfs";
+        ProtectSystem = "strict";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index 72eb9fcfaa602..97accc7b99a03 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -5,36 +5,17 @@
 with lib;
 
 let
-
-  drivers = config.services.xserver.videoDrivers;
-
-  # FIXME: should introduce an option like
-  # ‘hardware.video.nvidia.package’ for overriding the default NVIDIA
-  # driver.
-  nvidiaForKernel = kernelPackages:
-    if elem "nvidia" drivers then
-        kernelPackages.nvidia_x11
-    else if elem "nvidiaBeta" drivers then
-        kernelPackages.nvidia_x11_beta
-    else if elem "nvidiaVulkanBeta" drivers then
-        kernelPackages.nvidia_x11_vulkan_beta
-    else if elem "nvidiaLegacy304" drivers then
-      kernelPackages.nvidia_x11_legacy304
-    else if elem "nvidiaLegacy340" drivers then
-      kernelPackages.nvidia_x11_legacy340
-    else if elem "nvidiaLegacy390" drivers then
-      kernelPackages.nvidia_x11_legacy390
-    else null;
-
-  nvidia_x11 = nvidiaForKernel config.boot.kernelPackages;
-  nvidia_libs32 =
-    if versionOlder nvidia_x11.version "391" then
-      ((nvidiaForKernel pkgs.pkgsi686Linux.linuxPackages).override { libsOnly = true; kernel = null; }).out
-    else
-      (nvidiaForKernel config.boot.kernelPackages).lib32;
+  nvidia_x11 = let
+    drivers = config.services.xserver.videoDrivers;
+    isDeprecated = str: (hasPrefix "nvidia" str) && (str != "nvidia");
+    hasDeprecated = drivers: any isDeprecated drivers;
+  in if (hasDeprecated drivers) then
+    throw ''
+      Selecting an nvidia driver has been modified for NixOS 19.03. The version is now set using `hardware.nvidia.package`.
+    ''
+  else if (elem "nvidia" drivers) then cfg.package else null;
 
   enabled = nvidia_x11 != null;
-
   cfg = config.hardware.nvidia;
 
   pCfg = cfg.prime;
@@ -170,6 +151,16 @@ in
         GPUs stay awake even during headless mode.
       '';
     };
+
+    hardware.nvidia.package = lib.mkOption {
+      type = lib.types.package;
+      default = config.boot.kernelPackages.nvidiaPackages.stable;
+      defaultText = "config.boot.kernelPackages.nvidiaPackages.stable";
+      description = ''
+        The NVIDIA X11 derivation to use.
+      '';
+      example = "config.boot.kernelPackages.nvidiaPackages.legacy340";
+    };
   };
 
   config = let
@@ -271,9 +262,9 @@ in
     };
 
     hardware.opengl.package = mkIf (!offloadCfg.enable) nvidia_x11.out;
-    hardware.opengl.package32 = mkIf (!offloadCfg.enable) nvidia_libs32;
+    hardware.opengl.package32 = mkIf (!offloadCfg.enable) nvidia_x11.lib32;
     hardware.opengl.extraPackages = optional offloadCfg.enable nvidia_x11.out;
-    hardware.opengl.extraPackages32 = optional offloadCfg.enable nvidia_libs32;
+    hardware.opengl.extraPackages32 = optional offloadCfg.enable nvidia_x11.lib32;
 
     environment.systemPackages = [ nvidia_x11.bin nvidia_x11.settings ]
       ++ optionals nvidiaPersistencedEnabled [ nvidia_x11.persistenced ];
diff --git a/nixos/modules/hardware/video/switcheroo-control.nix b/nixos/modules/hardware/video/switcheroo-control.nix
new file mode 100644
index 0000000000000..199adb2ad8f52
--- /dev/null
+++ b/nixos/modules/hardware/video/switcheroo-control.nix
@@ -0,0 +1,18 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  pkg = [ pkgs.switcheroo-control ];
+  cfg = config.services.switcherooControl;
+in {
+  options.services.switcherooControl = {
+    enable = mkEnableOption "switcheroo-control, a D-Bus service to check the availability of dual-GPU";
+  };
+
+  config = mkIf cfg.enable {
+    services.dbus.packages = pkg;
+    environment.systemPackages = pkg;
+    systemd.packages = pkg;
+    systemd.targets.multi-user.wants = [ "switcheroo-control.service" ];
+  };
+}
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 7586ae41bbb0b..977218686601b 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -46,12 +46,15 @@
   ./hardware/cpu/intel-microcode.nix
   ./hardware/digitalbitbox.nix
   ./hardware/device-tree.nix
+  ./hardware/i2c.nix
+  ./hardware/sensor/hddtemp.nix
   ./hardware/sensor/iio.nix
   ./hardware/keyboard/zsa.nix
   ./hardware/ksm.nix
   ./hardware/ledger.nix
   ./hardware/logitech.nix
   ./hardware/mcelog.nix
+  ./hardware/network/ath-user-regd.nix
   ./hardware/network/b43.nix
   ./hardware/network/intel-2200bg.nix
   ./hardware/nitrokey.nix
@@ -175,7 +178,6 @@
   ./programs/tsm-client.nix
   ./programs/udevil.nix
   ./programs/usbtop.nix
-  ./programs/venus.nix
   ./programs/vim.nix
   ./programs/wavemon.nix
   ./programs/waybar.nix
@@ -357,6 +359,7 @@
   ./services/games/terraria.nix
   ./services/hardware/acpid.nix
   ./services/hardware/actkbd.nix
+  ./services/hardware/auto-cpufreq.nix
   ./services/hardware/bluetooth.nix
   ./services/hardware/bolt.nix
   ./services/hardware/brltty.nix
@@ -371,6 +374,7 @@
   ./services/hardware/nvidia-optimus.nix
   ./services/hardware/pcscd.nix
   ./services/hardware/pommed.nix
+  ./services/hardware/power-profiles-daemon.nix
   ./services/hardware/ratbagd.nix
   ./services/hardware/sane.nix
   ./services/hardware/sane_extra_backends/brscan4.nix
@@ -454,6 +458,7 @@
   ./services/misc/domoticz.nix
   ./services/misc/errbot.nix
   ./services/misc/etcd.nix
+  ./services/misc/etebase-server.nix
   ./services/misc/ethminer.nix
   ./services/misc/exhibitor.nix
   ./services/misc/felix.nix
@@ -608,6 +613,8 @@
   ./services/networking/atftpd.nix
   ./services/networking/avahi-daemon.nix
   ./services/networking/babeld.nix
+  ./services/networking/bee.nix
+  ./services/networking/bee-clef.nix
   ./services/networking/biboumi.nix
   ./services/networking/bind.nix
   ./services/networking/bitcoind.nix
@@ -644,7 +651,6 @@
   ./services/networking/fireqos.nix
   ./services/networking/firewall.nix
   ./services/networking/flannel.nix
-  ./services/networking/flashpolicyd.nix
   ./services/networking/freenet.nix
   ./services/networking/freeradius.nix
   ./services/networking/gale.nix
@@ -872,6 +878,7 @@
   ./services/web-apps/documize.nix
   ./services/web-apps/dokuwiki.nix
   ./services/web-apps/engelsystem.nix
+  ./services/web-apps/galene.nix
   ./services/web-apps/gerrit.nix
   ./services/web-apps/gotify-server.nix
   ./services/web-apps/grocy.nix
diff --git a/nixos/modules/programs/cdemu.nix b/nixos/modules/programs/cdemu.nix
index a59cd93cadfc0..142e293424057 100644
--- a/nixos/modules/programs/cdemu.nix
+++ b/nixos/modules/programs/cdemu.nix
@@ -16,18 +16,21 @@ in {
         '';
       };
       group = mkOption {
+        type = types.str;
         default = "cdrom";
         description = ''
           Group that users must be in to use <command>cdemu</command>.
         '';
       };
       gui = mkOption {
+        type = types.bool;
         default = true;
         description = ''
           Whether to install the <command>cdemu</command> GUI (gCDEmu).
         '';
       };
       image-analyzer = mkOption {
+        type = types.bool;
         default = true;
         description = ''
           Whether to install the image analyzer.
diff --git a/nixos/modules/programs/venus.nix b/nixos/modules/programs/venus.nix
deleted file mode 100644
index 58faf38777d06..0000000000000
--- a/nixos/modules/programs/venus.nix
+++ /dev/null
@@ -1,173 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-let
-  cfg = config.services.venus;
-
-  configFile = pkgs.writeText "venus.ini"
-    ''
-      [Planet]
-      name = ${cfg.name}
-      link = ${cfg.link}
-      owner_name = ${cfg.ownerName}
-      owner_email = ${cfg.ownerEmail}
-      output_theme = ${cfg.cacheDirectory}/theme
-      output_dir = ${cfg.outputDirectory}
-      cache_directory = ${cfg.cacheDirectory}
-      items_per_page = ${toString cfg.itemsPerPage}
-      ${(concatStringsSep "\n\n"
-            (map ({ name, feedUrl, homepageUrl }:
-            ''
-              [${feedUrl}]
-              name = ${name}
-              link = ${homepageUrl}
-            '') cfg.feeds))}
-    '';
-
-in
-{
-
-  options = {
-    services.venus = {
-      enable = mkOption {
-        default = false;
-        type = types.bool;
-        description = ''
-          Planet Venus is an awesome ‘river of news’ feed reader. It downloads
-          news feeds published by web sites and aggregates their content
-          together into a single combined feed, latest news first.
-        '';
-      };
-
-      dates = mkOption {
-        default = "*:0/15";
-        type = types.str;
-        description = ''
-          Specification (in the format described by
-          <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>7</manvolnum></citerefentry>) of the time at
-          which the Venus will collect feeds.
-        '';
-      };
-
-      user = mkOption {
-        default = "root";
-        type = types.str;
-        description = ''
-          User for running venus script.
-        '';
-      };
-
-      group = mkOption {
-        default = "root";
-        type = types.str;
-        description = ''
-          Group for running venus script.
-        '';
-      };
-
-      name = mkOption {
-        default = "NixOS Planet";
-        type = types.str;
-        description = ''
-          Your planet's name.
-        '';
-      };
-
-      link = mkOption {
-        default = "https://planet.nixos.org";
-        type = types.str;
-        description = ''
-          Link to the main page.
-        '';
-      };
-
-      ownerName = mkOption {
-        default = "Rok Garbas";
-        type = types.str;
-        description = ''
-          Your name.
-        '';
-      };
-
-      ownerEmail = mkOption {
-        default = "some@example.com";
-        type = types.str;
-        description = ''
-          Your e-mail address.
-        '';
-      };
-
-      outputTheme = mkOption {
-        default = "${pkgs.venus}/themes/classic_fancy";
-        type = types.path;
-        description = ''
-          Directory containing a config.ini file which is merged with this one.
-          This is typically used to specify templating and bill of material
-          information.
-        '';
-      };
-
-      outputDirectory = mkOption {
-        type = types.path;
-        description = ''
-          Directory to place output files.
-        '';
-      };
-
-      cacheDirectory = mkOption {
-        default = "/var/cache/venus";
-        type = types.path;
-        description = ''
-            Where cached feeds are stored.
-        '';
-      };
-
-      itemsPerPage = mkOption {
-        default = 15;
-        type = types.int;
-        description = ''
-          How many items to put on each page.
-        '';
-      };
-
-      feeds = mkOption {
-        default = [];
-        example = [
-          {
-            name = "Rok Garbas";
-            feedUrl= "http://url/to/rss/feed.xml";
-            homepageUrl = "http://garbas.si";
-          }
-        ];
-        description = ''
-          List of feeds.
-        '';
-      };
-
-    };
-  };
-
-  config = mkIf cfg.enable {
-
-    system.activationScripts.venus =
-      ''
-        mkdir -p ${cfg.outputDirectory}
-        chown ${cfg.user}:${cfg.group} ${cfg.outputDirectory} -R
-        rm -rf ${cfg.cacheDirectory}/theme
-        mkdir -p ${cfg.cacheDirectory}/theme
-        cp -R ${cfg.outputTheme}/* ${cfg.cacheDirectory}/theme
-        chown ${cfg.user}:${cfg.group} ${cfg.cacheDirectory} -R
-      '';
-
-    systemd.services.venus =
-      { description = "Planet Venus Feed Reader";
-        path  = [ pkgs.venus ];
-        script = "exec venus-planet ${configFile}";
-        serviceConfig.User = "${cfg.user}";
-        serviceConfig.Group = "${cfg.group}";
-        startAt = cfg.dates;
-      };
-
-  };
-}
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index c6f705bb2d6c4..2d07e421efe45 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -70,6 +70,8 @@ with lib;
     '')
 
     (mkRemovedOptionModule [ "services" "seeks" ] "")
+    (mkRemovedOptionModule [ "services" "venus" ] "The corresponding package was removed from nixpkgs.")
+    (mkRemovedOptionModule [ "services" "flashpolicyd" ] "The flashpolicyd module has been removed. Adobe Flash Player is deprecated.")
 
     # Do NOT add any option renames here, see top of the file
   ];
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index 6b62e5043cafa..c33a92580d4cd 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -346,7 +346,7 @@ let
       webroot = mkOption {
         type = types.nullOr types.str;
         default = null;
-        example = "/var/lib/acme/acme-challenges";
+        example = "/var/lib/acme/acme-challenge";
         description = ''
           Where the webroot of the HTTP vhost is located.
           <filename>.well-known/acme-challenge/</filename> directory
@@ -579,12 +579,12 @@ in {
         example = literalExample ''
           {
             "example.com" = {
-              webroot = "/var/www/challenges/";
+              webroot = "/var/lib/acme/acme-challenge/";
               email = "foo@example.com";
               extraDomainNames = [ "www.example.com" "foo.example.com" ];
             };
             "bar.example.com" = {
-              webroot = "/var/www/challenges/";
+              webroot = "/var/lib/acme/acme-challenge/";
               email = "bar@example.com";
             };
           }
diff --git a/nixos/modules/services/audio/snapserver.nix b/nixos/modules/services/audio/snapserver.nix
index f614f0ba3e10c..a261b87607805 100644
--- a/nixos/modules/services/audio/snapserver.nix
+++ b/nixos/modules/services/audio/snapserver.nix
@@ -48,8 +48,8 @@ let
     ++ [ "--stream.port ${toString cfg.port}" ]
     ++ optionalNull cfg.sampleFormat "--stream.sampleformat ${cfg.sampleFormat}"
     ++ optionalNull cfg.codec "--stream.codec ${cfg.codec}"
-    ++ optionalNull cfg.streamBuffer "--stream.stream_buffer ${cfg.streamBuffer}"
-    ++ optionalNull cfg.buffer "--stream.buffer ${cfg.buffer}"
+    ++ optionalNull cfg.streamBuffer "--stream.stream_buffer ${toString cfg.streamBuffer}"
+    ++ optionalNull cfg.buffer "--stream.buffer ${toString cfg.buffer}"
     ++ optional cfg.sendToMuted "--stream.send_to_muted"
     # tcp json rpc
     ++ [ "--tcp.enabled ${toString cfg.tcp.enable}" ]
@@ -198,13 +198,14 @@ in {
         type = with types; attrsOf (submodule {
           options = {
             location = mkOption {
-              type = types.path;
+              type = types.oneOf [ types.path types.str ];
               description = ''
-                The location of the pipe.
+                The location of the pipe, file, Librespot/Airplay/process binary, or a TCP address.
+                Use an empty string for alsa.
               '';
             };
             type = mkOption {
-              type = types.enum [ "pipe" "file" "process" "spotify" "airplay" ];
+              type = types.enum [ "pipe" "librespot" "airplay" "file" "process" "tcp" "alsa" "spotify" ];
               default = "pipe";
               description = ''
                 The type of input stream.
@@ -219,13 +220,21 @@ in {
               example = literalExample ''
                 # for type == "pipe":
                 {
-                  mode = "listen";
+                  mode = "create";
                 };
                 # for type == "process":
                 {
                   params = "--param1 --param2";
                   logStderr = "true";
                 };
+                # for type == "tcp":
+                {
+                  mode = "client";
+                }
+                # for type == "alsa":
+                {
+                  device = "hw:0,0";
+                }
               '';
             };
             inherit sampleFormat;
@@ -255,6 +264,11 @@ in {
 
   config = mkIf cfg.enable {
 
+    # https://github.com/badaix/snapcast/blob/98ac8b2fb7305084376607b59173ce4097c620d8/server/streamreader/stream_manager.cpp#L85
+    warnings = filter (w: w != "") (mapAttrsToList (k: v: if v.type == "spotify" then ''
+      services.snapserver.streams.${k}.type = "spotify" is deprecated, use services.snapserver.streams.${k}.type = "librespot" instead.
+    '' else "") cfg.streams);
+
     systemd.services.snapserver = {
       after = [ "network.target" ];
       description = "Snapserver";
@@ -272,7 +286,7 @@ in {
         ProtectKernelTunables = true;
         ProtectControlGroups = true;
         ProtectKernelModules = true;
-        RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX";
+        RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX AF_NETLINK";
         RestrictNamespaces = true;
         RuntimeDirectory = name;
         StateDirectory = name;
diff --git a/nixos/modules/services/backup/mysql-backup.nix b/nixos/modules/services/backup/mysql-backup.nix
index 31d606b141a8d..506ded5e9e8c4 100644
--- a/nixos/modules/services/backup/mysql-backup.nix
+++ b/nixos/modules/services/backup/mysql-backup.nix
@@ -48,6 +48,7 @@ in
       };
 
       user = mkOption {
+        type = types.str;
         default = defaultUser;
         description = ''
           User to be used to perform backup.
@@ -56,12 +57,14 @@ in
 
       databases = mkOption {
         default = [];
+        type = types.listOf types.str;
         description = ''
           List of database names to dump.
         '';
       };
 
       location = mkOption {
+        type = types.path;
         default = "/var/backup/mysql";
         description = ''
           Location to put the gzipped MySQL database dumps.
@@ -70,6 +73,7 @@ in
 
       singleTransaction = mkOption {
         default = false;
+        type = types.bool;
         description = ''
           Whether to create database dump in a single transaction
         '';
diff --git a/nixos/modules/services/backup/postgresql-backup.nix b/nixos/modules/services/backup/postgresql-backup.nix
index 428861a7598a1..f4bd3aa447e5e 100644
--- a/nixos/modules/services/backup/postgresql-backup.nix
+++ b/nixos/modules/services/backup/postgresql-backup.nix
@@ -48,6 +48,7 @@ in {
 
       startAt = mkOption {
         default = "*-*-* 01:15:00";
+        type = types.str;
         description = ''
           This option defines (see <literal>systemd.time</literal> for format) when the
           databases should be dumped.
@@ -70,6 +71,7 @@ in {
 
       databases = mkOption {
         default = [];
+        type = types.listOf types.str;
         description = ''
           List of database names to dump.
         '';
@@ -77,6 +79,7 @@ in {
 
       location = mkOption {
         default = "/var/backup/postgresql";
+        type = types.path;
         description = ''
           Location to put the gzipped PostgreSQL database dumps.
         '';
diff --git a/nixos/modules/services/backup/restic.nix b/nixos/modules/services/backup/restic.nix
index d869835bf07e6..573f0efa9da41 100644
--- a/nixos/modules/services/backup/restic.nix
+++ b/nixos/modules/services/backup/restic.nix
@@ -243,9 +243,11 @@ in
           restartIfChanged = false;
           serviceConfig = {
             Type = "oneshot";
-            ExecStart = [ "${resticCmd} backup ${concatStringsSep " " backup.extraBackupArgs} ${backupPaths}" ] ++ pruneCmd;
+            ExecStart = [ "${resticCmd} backup --cache-dir=%C/restic-backups-${name} ${concatStringsSep " " backup.extraBackupArgs} ${backupPaths}" ] ++ pruneCmd;
             User = backup.user;
             RuntimeDirectory = "restic-backups-${name}";
+            CacheDirectory = "restic-backups-${name}";
+            CacheDirectoryMode = "0700";
           } // optionalAttrs (backup.s3CredentialsFile != null) {
             EnvironmentFile = backup.s3CredentialsFile;
           };
diff --git a/nixos/modules/services/cluster/hadoop/default.nix b/nixos/modules/services/cluster/hadoop/default.nix
index 171d4aced651e..41ac46e538e35 100644
--- a/nixos/modules/services/cluster/hadoop/default.nix
+++ b/nixos/modules/services/cluster/hadoop/default.nix
@@ -7,6 +7,7 @@ with lib;
   options.services.hadoop = {
     coreSite = mkOption {
       default = {};
+      type = types.attrsOf types.anything;
       example = literalExample ''
         {
           "fs.defaultFS" = "hdfs://localhost";
@@ -17,6 +18,7 @@ with lib;
 
     hdfsSite = mkOption {
       default = {};
+      type = types.attrsOf types.anything;
       example = literalExample ''
         {
           "dfs.nameservices" = "namenode1";
@@ -27,6 +29,7 @@ with lib;
 
     mapredSite = mkOption {
       default = {};
+      type = types.attrsOf types.anything;
       example = literalExample ''
         {
           "mapreduce.map.cpu.vcores" = "1";
@@ -37,6 +40,7 @@ with lib;
 
     yarnSite = mkOption {
       default = {};
+      type = types.attrsOf types.anything;
       example = literalExample ''
         {
           "yarn.resourcemanager.ha.id" = "resourcemanager1";
diff --git a/nixos/modules/services/cluster/k3s/default.nix b/nixos/modules/services/cluster/k3s/default.nix
index f0317fdbd160f..e62fbc94415ca 100644
--- a/nixos/modules/services/cluster/k3s/default.nix
+++ b/nixos/modules/services/cluster/k3s/default.nix
@@ -47,6 +47,7 @@ in
 
     extraFlags = mkOption {
       description = "Extra flags to pass to the k3s command.";
+      type = types.str;
       default = "";
       example = "--no-deploy traefik --cluster-cidr 10.24.0.0/16";
     };
diff --git a/nixos/modules/services/cluster/kubernetes/kubelet.nix b/nixos/modules/services/cluster/kubernetes/kubelet.nix
index 2b6e45ba1b905..479027f1b2708 100644
--- a/nixos/modules/services/cluster/kubernetes/kubelet.nix
+++ b/nixos/modules/services/cluster/kubernetes/kubelet.nix
@@ -241,7 +241,17 @@ in
         description = "Kubernetes Kubelet Service";
         wantedBy = [ "kubernetes.target" ];
         after = [ "network.target" "docker.service" "kube-apiserver.service" ];
-        path = with pkgs; [ gitMinimal openssh docker util-linux iproute ethtool thin-provisioning-tools iptables socat ] ++ top.path;
+        path = with pkgs; [
+          gitMinimal
+          openssh
+          docker
+          util-linux
+          iproute
+          ethtool
+          thin-provisioning-tools
+          iptables
+          socat
+        ] ++ lib.optional config.boot.zfs.enabled config.boot.zfs.package ++ top.path;
         preStart = ''
           ${concatMapStrings (img: ''
             echo "Seeding docker image: ${img}"
diff --git a/nixos/modules/services/continuous-integration/buildbot/master.nix b/nixos/modules/services/continuous-integration/buildbot/master.nix
index d30d94c53cc33..a49f5f8100dc9 100644
--- a/nixos/modules/services/continuous-integration/buildbot/master.nix
+++ b/nixos/modules/services/continuous-integration/buildbot/master.nix
@@ -223,7 +223,7 @@ in {
       };
 
       pythonPackages = mkOption {
-        type = types.listOf types.package;
+        type = types.functionTo (types.listOf types.package);
         default = pythonPackages: with pythonPackages; [ ];
         defaultText = "pythonPackages: with pythonPackages; [ ]";
         description = "Packages to add the to the PYTHONPATH of the buildbot process.";
@@ -283,5 +283,5 @@ in {
     '')
   ];
 
-  meta.maintainers = with lib.maintainers; [ nand0p mic92 ];
+  meta.maintainers = with lib.maintainers; [ nand0p mic92 lopsided98 ];
 }
diff --git a/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix b/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix
index 4aed493c0fb0a..24884655c660f 100644
--- a/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix
+++ b/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix
@@ -9,7 +9,15 @@ Platform-specific code is in the respective default.nix files.
 { config, lib, options, pkgs, ... }:
 
 let
-  inherit (lib) mkOption mkIf types filterAttrs literalExample mkRenamedOptionModule;
+  inherit (lib)
+    filterAttrs
+    literalExample
+    mkIf
+    mkOption
+    mkRemovedOptionModule
+    mkRenamedOptionModule
+    types
+    ;
 
   cfg =
     config.services.hercules-ci-agent;
@@ -77,10 +85,11 @@ let
     };
   };
 
+  # TODO (2022) remove
   checkNix =
     if !cfg.checkNix
     then ""
-    else if lib.versionAtLeast config.nix.package.version "2.4.0"
+    else if lib.versionAtLeast config.nix.package.version "2.3.10"
     then ""
     else pkgs.stdenv.mkDerivation {
       name = "hercules-ci-check-system-nix-src";
@@ -88,23 +97,12 @@ let
       configurePhase = ":";
       buildPhase = ''
         echo "Checking in-memory pathInfoCache expiry"
-        if ! grep 'struct PathInfoCacheValue' src/libstore/store-api.hh >/dev/null; then
+        if ! grep 'PathInfoCacheValue' src/libstore/store-api.hh >/dev/null; then
           cat 1>&2 <<EOF
 
           You are deploying Hercules CI Agent on a system with an incompatible
-          nix-daemon. Please
-           - either upgrade Nix to version 2.4.0 (when released),
-           - or set option services.hercules-ci-agent.patchNix = true;
-           - or set option nix.package to a build of Nix 2.3 with this patch applied:
-               https://github.com/NixOS/nix/pull/3405
-
-          The patch is required for Nix-daemon clients that expect a change in binary
-          cache contents while running, like the agent's evaluator. Without it, import
-          from derivation will fail if your cluster has more than one machine.
-          We are conservative with changes to the overall system, which is why we
-          keep changes to a minimum and why we ask for confirmation in the form of
-          services.hercules-ci-agent.patchNix = true before applying.
-
+          nix-daemon. Please make sure nix.package is set to a Nix version of at
+          least 2.3.10 or a master version more recent than Mar 12, 2020.
         EOF
           exit 1
         fi
@@ -112,26 +110,13 @@ let
       installPhase = "touch $out";
     };
 
-  patchedNix = lib.mkIf (!lib.versionAtLeast pkgs.nix.version "2.4.0") (
-    if lib.versionAtLeast pkgs.nix.version "2.4pre"
-    then lib.warn "Hercules CI Agent module will not patch 2.4 pre-release. Make sure it includes (equivalently) PR #3043, commit d048577909 or is no older than 2020-03-13." pkgs.nix
-    else pkgs.nix.overrideAttrs (
-      o: {
-        patches = (o.patches or []) ++ [ backportNix3398 ];
-      }
-    )
-  );
-
-  backportNix3398 = pkgs.fetchurl {
-    url = "https://raw.githubusercontent.com/hercules-ci/hercules-ci-agent/hercules-ci-agent-0.7.3/for-upstream/issue-3398-path-info-cache-ttls-backport-2.3.patch";
-    sha256 = "0jfckqjir9il2il7904yc1qyadw366y7xqzg81sp9sl3f1pw70ib";
-  };
 in
 {
   imports = [
     (mkRenamedOptionModule ["services" "hercules-ci-agent" "extraOptions"] ["services" "hercules-ci-agent" "settings"])
     (mkRenamedOptionModule ["services" "hercules-ci-agent" "baseDirectory"] ["services" "hercules-ci-agent" "settings" "baseDirectory"])
     (mkRenamedOptionModule ["services" "hercules-ci-agent" "concurrentTasks"] ["services" "hercules-ci-agent" "settings" "concurrentTasks"])
+    (mkRemovedOptionModule ["services" "hercules-ci-agent" "patchNix"] "Nix versions packaged in this version of Nixpkgs don't need a patched nix-daemon to work correctly in Hercules CI Agent clusters.")
   ];
 
   options.services.hercules-ci-agent = {
@@ -147,15 +132,6 @@ in
         Support is available at <link xlink:href="mailto:help@hercules-ci.com">help@hercules-ci.com</link>.
       '';
     };
-    patchNix = mkOption {
-      type = types.bool;
-      default = false;
-      description = ''
-        Fix Nix 2.3 cache path metadata caching behavior. Has the effect of <literal>nix.package = patch pkgs.nix;</literal>
-
-        This option will be removed when Hercules CI Agent moves to Nix 2.4 (upcoming Nix release).
-      '';
-    };
     checkNix = mkOption {
       type = types.bool;
       default = true;
@@ -206,7 +182,6 @@ in
       # even shortly after the previous lookup. This *also* applies to the daemon.
       narinfo-cache-negative-ttl = 0
     '';
-    nix.package = mkIf cfg.patchNix patchedNix;
     services.hercules-ci-agent.tomlFile =
       format.generate "hercules-ci-agent.toml" cfg.settings;
   };
diff --git a/nixos/modules/services/development/hoogle.nix b/nixos/modules/services/development/hoogle.nix
index bd55483f46d80..6d6c88b9b2aa9 100644
--- a/nixos/modules/services/development/hoogle.nix
+++ b/nixos/modules/services/development/hoogle.nix
@@ -41,7 +41,6 @@ in {
     haskellPackages = mkOption {
       description = "Which haskell package set to use.";
       default = pkgs.haskellPackages;
-      type = types.package;
       defaultText = "pkgs.haskellPackages";
     };
 
diff --git a/nixos/modules/services/hardware/auto-cpufreq.nix b/nixos/modules/services/hardware/auto-cpufreq.nix
new file mode 100644
index 0000000000000..72c4eccaff72e
--- /dev/null
+++ b/nixos/modules/services/hardware/auto-cpufreq.nix
@@ -0,0 +1,18 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.auto-cpufreq;
+in {
+  options = {
+    services.auto-cpufreq = {
+      enable = mkEnableOption "auto-cpufreq daemon";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.auto-cpufreq ];
+
+    systemd.packages = [ pkgs.auto-cpufreq ];
+    systemd.services.auto-cpufreq.path = with pkgs; [ bash coreutils ];
+  };
+}
diff --git a/nixos/modules/services/hardware/power-profiles-daemon.nix b/nixos/modules/services/hardware/power-profiles-daemon.nix
new file mode 100644
index 0000000000000..70b7a72b8bae0
--- /dev/null
+++ b/nixos/modules/services/hardware/power-profiles-daemon.nix
@@ -0,0 +1,53 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.power-profiles-daemon;
+  package = pkgs.power-profiles-daemon;
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.power-profiles-daemon = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable power-profiles-daemon, a DBus daemon that allows
+          changing system behavior based upon user-selected power profiles.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = !config.services.tlp.enable;
+        message = ''
+          You have set services.power-profiles-daemon.enable = true;
+          which conflicts with services.tlp.enable = true;
+        '';
+      }
+    ];
+
+    services.dbus.packages = [ package ];
+
+    services.udev.packages = [ package ];
+
+    systemd.packages = [ package ];
+
+  };
+
+}
diff --git a/nixos/modules/services/hardware/thinkfan.nix b/nixos/modules/services/hardware/thinkfan.nix
index 3bda61ed1a938..7a5a7e1c41ce2 100644
--- a/nixos/modules/services/hardware/thinkfan.nix
+++ b/nixos/modules/services/hardware/thinkfan.nix
@@ -5,49 +5,95 @@ with lib;
 let
 
   cfg = config.services.thinkfan;
-  configFile = pkgs.writeText "thinkfan.conf" ''
-    # ATTENTION: There is only very basic sanity checking on the configuration.
-    # That means you can set your temperature limits as insane as you like. You
-    # can do anything stupid, e.g. turn off your fan when your CPU reaches 70°C.
-    #
-    # That's why this program is called THINKfan: You gotta think for yourself.
-    #
-    ######################################################################
-    #
-    # IBM/Lenovo Thinkpads (thinkpad_acpi, /proc/acpi/ibm)
-    # ====================================================
-    #
-    # IMPORTANT:
-    #
-    # To keep your HD from overheating, you have to specify a correction value for
-    # the sensor that has the HD's temperature. You need to do this because
-    # thinkfan uses only the highest temperature it can find in the system, and
-    # that'll most likely never be your HD, as most HDs are already out of spec
-    # when they reach 55 °C.
-    # Correction values are applied from left to right in the same order as the
-    # temperatures are read from the file.
-    #
-    # For example:
-    # tp_thermal /proc/acpi/ibm/thermal (0, 0, 10)
-    # will add a fixed value of 10 °C the 3rd value read from that file. Check out
-    # http://www.thinkwiki.org/wiki/Thermal_Sensors to find out how much you may
-    # want to add to certain temperatures.
-
-    ${cfg.fan}
-    ${cfg.sensors}
-
-    #  Syntax:
-    #  (LEVEL, LOW, HIGH)
-    #  LEVEL is the fan level to use (0-7 with thinkpad_acpi)
-    #  LOW is the temperature at which to step down to the previous level
-    #  HIGH is the temperature at which to step up to the next level
-    #  All numbers are integers.
-    #
-
-    ${cfg.levels}
-  '';
+  settingsFormat = pkgs.formats.yaml { };
+  configFile = settingsFormat.generate "thinkfan.yaml" cfg.settings;
+  thinkfan = pkgs.thinkfan.override { inherit (cfg) smartSupport; };
+
+  # fan-speed and temperature levels
+  levelType = with types;
+    let
+      tuple = ts: mkOptionType {
+        name = "tuple";
+        merge = mergeOneOption;
+        check = xs: all id (zipListsWith (t: x: t.check x) ts xs);
+        description = "tuple of" + concatMapStrings (t: " (${t.description})") ts;
+      };
+      level = ints.unsigned;
+      special = enum [ "level auto" "level full-speed" "level disengage" ];
+    in
+      tuple [ (either level special) level level ];
+
+  # sensor or fan config
+  sensorType = name: types.submodule {
+    freeformType = types.attrsOf settingsFormat.type;
+    options = {
+      type = mkOption {
+        type = types.enum [ "hwmon" "atasmart" "tpacpi" "nvml" ];
+        description = ''
+          The ${name} type, can be
+          <literal>hwmon</literal> for standard ${name}s,
 
-  thinkfan = pkgs.thinkfan.override { smartSupport = cfg.smartSupport; };
+          <literal>atasmart</literal> to read the temperature via
+          S.M.A.R.T (requires smartSupport to be enabled),
+
+          <literal>tpacpi</literal> for the legacy thinkpac_acpi driver, or
+
+          <literal>nvml</literal> for the (proprietary) nVidia driver.
+        '';
+      };
+      query = mkOption {
+        type = types.str;
+        description = ''
+          The query string used to match one or more ${name}s: can be
+          a fullpath to the temperature file (single ${name}) or a fullpath
+          to a driver directory (multiple ${name}s).
+
+          <note><para>
+            When multiple ${name}s match, the query can be restricted using the
+            <option>name</option> or <option>indices</option> options.
+          </para></note>
+        '';
+      };
+      indices = mkOption {
+        type = with types; nullOr (listOf ints.unsigned);
+        default = null;
+        description = ''
+          A list of ${name}s to pick in case multiple ${name}s match the query.
+
+          <note><para>Indices start from 0.</para></note>
+        '';
+      };
+    } // optionalAttrs (name == "sensor") {
+      correction = mkOption {
+        type = with types; nullOr (listOf int);
+        default = null;
+        description = ''
+          A list of values to be added to the temperature of each sensor,
+          can be used to equalize small discrepancies in temperature ratings.
+        '';
+      };
+    };
+  };
+
+  # removes NixOS special and unused attributes
+  sensorToConf = { type, query, ... }@args:
+    (filterAttrs (k: v: v != null && !(elem k ["type" "query"])) args)
+    // { "${type}" = query; };
+
+  syntaxNote = name: ''
+    <note><para>
+      This section slightly departs from the thinkfan.conf syntax.
+      The type and path must be specified like this:
+      <literal>
+        type = "tpacpi";
+        query = "/proc/acpi/ibm/${name}";
+      </literal>
+      instead of a single declaration like:
+      <literal>
+        - tpacpi: /proc/acpi/ibm/${name}
+      </literal>
+    </para></note>
+  '';
 
 in {
 
@@ -59,76 +105,93 @@ in {
         type = types.bool;
         default = false;
         description = ''
-          Whether to enable thinkfan, fan controller for IBM/Lenovo ThinkPads.
+          Whether to enable thinkfan, a fan control program.
+
+          <note><para>
+            This module targets IBM/Lenovo thinkpads by default, for
+            other hardware you will have configure it more carefully.
+          </para></note>
         '';
+        relatedPackages = [ "thinkfan" ];
       };
 
       smartSupport = mkOption {
         type = types.bool;
         default = false;
         description = ''
-          Whether to build thinkfan with SMART support to read temperatures
+          Whether to build thinkfan with S.M.A.R.T. support to read temperatures
           directly from hard disks.
         '';
       };
 
       sensors = mkOption {
-        type = types.lines;
-        default = ''
-          tp_thermal /proc/acpi/ibm/thermal (0,0,10)
-        '';
-        description =''
-          thinkfan can read temperatures from three possible sources:
-
-            /proc/acpi/ibm/thermal
-              Which is provided by the thinkpad_acpi kernel
-              module (keyword tp_thermal)
-
-            /sys/class/hwmon/*/temp*_input
-              Which may be provided by any hwmon drivers (keyword
-              hwmon)
-
-            S.M.A.R.T. (requires smartSupport to be enabled)
-              Which reads the temperature directly from the hard
-              disk using libatasmart (keyword atasmart)
-
-          Multiple sensors may be added, in which case they will be
-          numbered in their order of appearance.
-        '';
+        type = types.listOf (sensorType "sensor");
+        default = [
+          { type = "tpacpi";
+            query = "/proc/acpi/ibm/thermal";
+          }
+        ];
+        description = ''
+          List of temperature sensors thinkfan will monitor.
+        '' + syntaxNote "thermal";
       };
 
-      fan = mkOption {
-        type = types.str;
-        default = "tp_fan /proc/acpi/ibm/fan";
-        description =''
-          Specifies the fan we want to use.
-          On anything other than a Thinkpad you'll probably
-          use some PWM control file in /sys/class/hwmon.
-          A sysfs fan would be specified like this:
-            pwm_fan /sys/class/hwmon/hwmon2/device/pwm1
-        '';
+      fans = mkOption {
+        type = types.listOf (sensorType "fan");
+        default = [
+          { type = "tpacpi";
+            query = "/proc/acpi/ibm/fan";
+          }
+        ];
+        description = ''
+          List of fans thinkfan will control.
+        '' + syntaxNote "fan";
       };
 
       levels = mkOption {
-        type = types.lines;
-        default = ''
-          (0,     0,      55)
-          (1,     48,     60)
-          (2,     50,     61)
-          (3,     52,     63)
-          (6,     56,     65)
-          (7,     60,     85)
-          (127,   80,     32767)
-        '';
+        type = types.listOf levelType;
+        default = [
+          [0  0   55]
+          [1  48  60]
+          [2  50  61]
+          [3  52  63]
+          [6  56  65]
+          [7  60  85]
+          ["level auto" 80 32767]
+        ];
         description = ''
-          (LEVEL, LOW, HIGH)
-          LEVEL is the fan level to use (0-7 with thinkpad_acpi).
+          [LEVEL LOW HIGH]
+
+          LEVEL is the fan level to use: it can be an integer (0-7 with thinkpad_acpi),
+          "level auto" (to keep the default firmware behavior), "level full-speed" or
+          "level disengage" (to run the fan as fast as possible).
           LOW is the temperature at which to step down to the previous level.
           HIGH is the temperature at which to step up to the next level.
           All numbers are integers.
         '';
       };
 
+      extraArgs = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "-b" "0" ];
+        description = ''
+          A list of extra command line arguments to pass to thinkfan.
+          Check the thinkfan(1) manpage for available arguments.
+        '';
+      };
+
+      settings = mkOption {
+        type = types.attrsOf settingsFormat.type;
+        default = { };
+        description = ''
+          Thinkfan settings. Use this option to configure thinkfan
+          settings not exposed in a NixOS option or to bypass one.
+          Before changing this, read the <literal>thinkfan.conf(5)</literal>
+          manpage and take a look at the example config file at
+          <link xlink:href="https://github.com/vmatare/thinkfan/blob/master/examples/thinkfan.yaml"/>
+        '';
+      };
 
     };
 
@@ -138,12 +201,21 @@ in {
 
     environment.systemPackages = [ thinkfan ];
 
-    systemd.services.thinkfan = {
-      description = "Thinkfan";
-      after = [ "basic.target" ];
-      wantedBy = [ "multi-user.target" ];
-      path = [ thinkfan ];
-      serviceConfig.ExecStart = "${thinkfan}/bin/thinkfan -n -c ${configFile}";
+    services.thinkfan.settings = mapAttrs (k: v: mkDefault v) {
+      sensors = map sensorToConf cfg.sensors;
+      fans    = map sensorToConf cfg.fans;
+      levels  = cfg.levels;
+    };
+
+    systemd.packages = [ thinkfan ];
+
+    systemd.services = {
+      thinkfan.environment.THINKFAN_ARGS = escapeShellArgs ([ "-c" configFile ] ++ cfg.extraArgs);
+
+      # must be added manually, see issue #81138
+      thinkfan.wantedBy = [ "multi-user.target" ];
+      thinkfan-wakeup.wantedBy = [ "sleep.target" ];
+      thinkfan-sleep.wantedBy = [ "sleep.target" ];
     };
 
     boot.extraModprobeConfig = "options thinkpad_acpi experimental=1 fan_control=1";
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
index 1dcdcab8d481c..63c0961b7568b 100644
--- a/nixos/modules/services/mail/postfix.nix
+++ b/nixos/modules/services/mail/postfix.nix
@@ -560,6 +560,7 @@ in
 
       transport = mkOption {
         default = "";
+        type = types.lines;
         description = "
           Entries for the transport map, cf. man-page transport(8).
         ";
@@ -573,6 +574,7 @@ in
 
       dnsBlacklistOverrides = mkOption {
         default = "";
+        type = types.lines;
         description = "contents of check_client_access for overriding dnsBlacklists";
       };
 
diff --git a/nixos/modules/services/misc/etebase-server.nix b/nixos/modules/services/misc/etebase-server.nix
new file mode 100644
index 0000000000000..d9d12698d79dc
--- /dev/null
+++ b/nixos/modules/services/misc/etebase-server.nix
@@ -0,0 +1,205 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.etebase-server;
+
+  pythonEnv = pkgs.python3.withPackages (ps: with ps;
+    [ etebase-server daphne ]);
+
+  dbConfig = {
+    sqlite3 = ''
+      engine = django.db.backends.sqlite3
+      name = ${cfg.dataDir}/db.sqlite3
+    '';
+  };
+
+  defaultConfigIni = toString (pkgs.writeText "etebase-server.ini" ''
+    [global]
+    debug = false
+    secret_file = ${if cfg.secretFile != null then cfg.secretFile else ""}
+    media_root = ${cfg.dataDir}/media
+
+    [allowed_hosts]
+    allowed_host1 = ${cfg.host}
+
+    [database]
+    ${dbConfig."${cfg.database.type}"}
+  '');
+
+  configIni = if cfg.customIni != null then cfg.customIni else defaultConfigIni;
+
+  defaultUser = "etebase-server";
+in
+{
+  options = {
+    services.etebase-server = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        example = true;
+        description = ''
+          Whether to enable the Etebase server.
+
+          Once enabled you need to create an admin user using the
+          shell command <literal>etebase-server createsuperuser</literal>.
+          Then you can login and create accounts on your-etebase-server.com/admin
+        '';
+      };
+
+      secretFile = mkOption {
+        default = null;
+        type = with types; nullOr str;
+        description = ''
+          The path to a file containing the secret
+          used as django's SECRET_KEY.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.str;
+        default = "/var/lib/etebase-server";
+        description = "Directory to store the Etebase server data.";
+      };
+
+      port = mkOption {
+        type = with types; nullOr port;
+        default = 8001;
+        description = "Port to listen on.";
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to open ports in the firewall for the server.
+        '';
+      };
+
+      host = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        example = "localhost";
+        description = ''
+          Host to listen on.
+        '';
+      };
+
+      unixSocket = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = "The path to the socket to bind to.";
+        example = "/run/etebase-server/etebase-server.sock";
+      };
+
+      database = {
+        type = mkOption {
+          type = types.enum [ "sqlite3" ];
+          default = "sqlite3";
+          description = ''
+            Database engine to use.
+            Currently only sqlite3 is supported.
+            Other options can be configured using <literal>extraConfig</literal>.
+          '';
+        };
+      };
+
+      customIni = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Custom etebase-server.ini.
+
+          See <literal>etebase-src/etebase-server.ini.example</literal> for available options.
+
+          Setting this option overrides the default config which is generated from the options
+          <literal>secretFile</literal>, <literal>host</literal> and <literal>database</literal>.
+        '';
+        example = literalExample ''
+          [global]
+          debug = false
+          secret_file = /path/to/secret
+          media_root = /path/to/media
+
+          [allowed_hosts]
+          allowed_host1 = example.com
+
+          [database]
+          engine = django.db.backends.sqlite3
+          name = db.sqlite3
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = defaultUser;
+        description = "User under which Etebase server runs.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = with pkgs; [
+      (runCommand "etebase-server" {
+        buildInputs = [ makeWrapper ];
+      } ''
+        makeWrapper ${pythonEnv}/bin/etebase-server \
+          $out/bin/etebase-server \
+          --run "cd ${cfg.dataDir}" \
+          --prefix ETEBASE_EASY_CONFIG_PATH : "${configIni}"
+      '')
+    ];
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
+    ];
+
+    systemd.services.etebase-server = {
+      description = "An Etebase (EteSync 2.0) server";
+      after = [ "network.target" "systemd-tmpfiles-setup.service" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User = cfg.user;
+        Restart = "always";
+        WorkingDirectory = cfg.dataDir;
+      };
+      environment = {
+        PYTHONPATH="${pythonEnv}/${pkgs.python3.sitePackages}";
+        ETEBASE_EASY_CONFIG_PATH="${configIni}";
+      };
+      preStart = ''
+        # Auto-migrate on first run or if the package has changed
+        versionFile="${cfg.dataDir}/src-version"
+        if [[ $(cat "$versionFile" 2>/dev/null) != ${pkgs.etebase-server} ]]; then
+          ${pythonEnv}/bin/etebase-server migrate
+          echo ${pkgs.etebase-server} > "$versionFile"
+        fi
+      '';
+      script =
+        let
+          networking = if cfg.unixSocket != null
+          then "-u ${cfg.unixSocket}"
+          else "-b 0.0.0.0 -p ${toString cfg.port}";
+        in ''
+          cd "${pythonEnv}/lib/etebase-server";
+          ${pythonEnv}/bin/daphne ${networking} \
+            etebase_server.asgi:application
+        '';
+    };
+
+    users = optionalAttrs (cfg.user == defaultUser) {
+      users.${defaultUser} = {
+        group = defaultUser;
+        home = cfg.dataDir;
+      };
+
+      groups.${defaultUser} = {};
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.port ];
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/pykms.nix b/nixos/modules/services/misc/pykms.nix
index d6aeae48ccb62..2f752bcc7ed6b 100644
--- a/nixos/modules/services/misc/pykms.nix
+++ b/nixos/modules/services/misc/pykms.nix
@@ -1,12 +1,12 @@
 { config, lib, pkgs, ... }:
 
 with lib;
-
 let
   cfg = config.services.pykms;
   libDir = "/var/lib/pykms";
 
-in {
+in
+{
   meta.maintainers = with lib.maintainers; [ peterhoeg ];
 
   imports = [
@@ -46,14 +46,14 @@ in {
       };
 
       logLevel = mkOption {
-        type = types.enum [ "CRITICAL" "ERROR" "WARNING" "INFO" "DEBUG" "MINI" ];
+        type = types.enum [ "CRITICAL" "ERROR" "WARNING" "INFO" "DEBUG" "MININFO" ];
         default = "INFO";
         description = "How much to log";
       };
 
       extraArgs = mkOption {
         type = types.listOf types.str;
-        default = [];
+        default = [ ];
         description = "Additional arguments";
       };
     };
@@ -74,8 +74,9 @@ in {
         ExecStartPre = "${getBin pykms}/libexec/create_pykms_db.sh ${libDir}/clients.db";
         ExecStart = lib.concatStringsSep " " ([
           "${getBin pykms}/bin/server"
-          "--logfile STDOUT"
-          "--loglevel ${cfg.logLevel}"
+          "--logfile=STDOUT"
+          "--loglevel=${cfg.logLevel}"
+          "--sqlite=${libDir}/clients.db"
         ] ++ cfg.extraArgs ++ [
           cfg.listenAddress
           (toString cfg.port)
diff --git a/nixos/modules/services/misc/rippled.nix b/nixos/modules/services/misc/rippled.nix
index ef34e3a779f01..2fce3b9dc94c7 100644
--- a/nixos/modules/services/misc/rippled.nix
+++ b/nixos/modules/services/misc/rippled.nix
@@ -389,6 +389,7 @@ in
 
       extraConfig = mkOption {
         default = "";
+        type = types.lines;
         description = ''
           Extra lines to be added verbatim to the rippled.cfg configuration file.
         '';
diff --git a/nixos/modules/services/misc/svnserve.nix b/nixos/modules/services/misc/svnserve.nix
index f70e3ca7fef0a..5fa262ca3b945 100644
--- a/nixos/modules/services/misc/svnserve.nix
+++ b/nixos/modules/services/misc/svnserve.nix
@@ -24,6 +24,7 @@ in
       };
 
       svnBaseDir = mkOption {
+        type = types.str;
         default = "/repos";
         description = "Base directory from which Subversion repositories are accessed.";
       };
diff --git a/nixos/modules/services/misc/synergy.nix b/nixos/modules/services/misc/synergy.nix
index 5b7cf3ac46c3c..7990a9f6f4cec 100644
--- a/nixos/modules/services/misc/synergy.nix
+++ b/nixos/modules/services/misc/synergy.nix
@@ -23,12 +23,14 @@ in
 
         screenName = mkOption {
           default = "";
+          type = types.str;
           description = ''
             Use the given name instead of the hostname to identify
             ourselves to the server.
           '';
         };
         serverAddress = mkOption {
+          type = types.str;
           description = ''
             The server address is of the form: [hostname][:port].  The
             hostname must be the address or hostname of the server.  The
@@ -46,10 +48,12 @@ in
         enable = mkEnableOption "the Synergy server (send keyboard and mouse events)";
 
         configFile = mkOption {
+          type = types.path;
           default = "/etc/synergy-server.conf";
           description = "The Synergy server configuration file.";
         };
         screenName = mkOption {
+          type = types.str;
           default = "";
           description = ''
             Use the given name instead of the hostname to identify
@@ -57,6 +61,7 @@ in
           '';
         };
         address = mkOption {
+          type = types.str;
           default = "";
           description = "Address on which to listen for clients.";
         };
diff --git a/nixos/modules/services/misc/weechat.nix b/nixos/modules/services/misc/weechat.nix
index c6ff540ea12f4..b71250f62e0f3 100644
--- a/nixos/modules/services/misc/weechat.nix
+++ b/nixos/modules/services/misc/weechat.nix
@@ -20,6 +20,7 @@ in
       type = types.str;
     };
     binary = mkOption {
+      type = types.path;
       description = "Binary to execute (by default \${weechat}/bin/weechat).";
       example = literalExample ''
         ''${pkgs.weechat}/bin/weechat-headless
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
index 1fd85c66f843d..64de15f4a2faf 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -238,9 +238,6 @@ in
     services.prometheus.exporters.minio.minioAccessSecret = mkDefault config.services.minio.secretKey;
   })] ++ [(mkIf config.services.prometheus.exporters.rtl_433.enable {
     hardware.rtl-sdr.enable = mkDefault true;
-  })] ++ [(mkIf config.services.nginx.enable {
-    systemd.services.prometheus-nginx-exporter.after = [ "nginx.service" ];
-    systemd.services.prometheus-nginx-exporter.requires = [ "nginx.service" ];
   })] ++ [(mkIf config.services.postfix.enable {
     services.prometheus.exporters.postfix.group = mkDefault config.services.postfix.setgidGroup;
   })] ++ (mapAttrsToList (name: conf:
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
index 56cddfc55b719..5ee8c346be1dc 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
@@ -42,7 +42,7 @@ in
       '';
     };
   };
-  serviceOpts = {
+  serviceOpts = mkMerge ([{
     serviceConfig = {
       ExecStart = ''
         ${pkgs.prometheus-nginx-exporter}/bin/nginx-prometheus-exporter \
@@ -54,7 +54,10 @@ in
           ${concatStringsSep " \\\n  " cfg.extraFlags}
       '';
     };
-  };
+  }] ++ [(mkIf config.services.nginx.enable {
+    after = [ "nginx.service" ];
+    requires = [ "nginx.service" ];
+  })]);
   imports = [
     (mkRenamedOptionModule [ "telemetryEndpoint" ] [ "telemetryPath" ])
     (mkRemovedOptionModule [ "insecure" ] ''
diff --git a/nixos/modules/services/network-filesystems/netatalk.nix b/nixos/modules/services/network-filesystems/netatalk.nix
index ca9d32311f5f3..33e851210bc6f 100644
--- a/nixos/modules/services/network-filesystems/netatalk.nix
+++ b/nixos/modules/services/network-filesystems/netatalk.nix
@@ -46,6 +46,7 @@ in
       enable = mkEnableOption "the Netatalk AFP fileserver";
 
       port = mkOption {
+        type = types.port;
         default = 548;
         description = "TCP port to be used for AFP.";
       };
@@ -68,6 +69,7 @@ in
         };
 
         path = mkOption {
+          type = types.str;
           default = "";
           example = "afp-data";
           description = "Share not the whole user home but this subdirectory path.";
@@ -75,6 +77,7 @@ in
 
         basedirRegex = mkOption {
           example = "/home";
+          type = types.str;
           description = "Regex which matches the parent directory of the user homes.";
         };
 
diff --git a/nixos/modules/services/network-filesystems/openafs/server.nix b/nixos/modules/services/network-filesystems/openafs/server.nix
index d782f78216563..4fce650b01336 100644
--- a/nixos/modules/services/network-filesystems/openafs/server.nix
+++ b/nixos/modules/services/network-filesystems/openafs/server.nix
@@ -61,6 +61,7 @@ in {
       };
 
       advertisedAddresses = mkOption {
+        type = types.listOf types.str;
         default = [];
         description = "List of IP addresses this server is advertised under. See NetInfo(5)";
       };
diff --git a/nixos/modules/services/network-filesystems/xtreemfs.nix b/nixos/modules/services/network-filesystems/xtreemfs.nix
index 27a9fe847c581..6cc8a05ee00b0 100644
--- a/nixos/modules/services/network-filesystems/xtreemfs.nix
+++ b/nixos/modules/services/network-filesystems/xtreemfs.nix
@@ -92,6 +92,7 @@ in
       enable = mkEnableOption "XtreemFS";
 
       homeDir = mkOption {
+        type = types.path;
         default = "/var/lib/xtreemfs";
         description = ''
           XtreemFS home dir for the xtreemfs user.
@@ -109,6 +110,7 @@ in
 
         uuid = mkOption {
           example = "eacb6bab-f444-4ebf-a06a-3f72d7465e40";
+          type = types.str;
           description = ''
             Must be set to a unique identifier, preferably a UUID according to
             RFC 4122. UUIDs can be generated with `uuidgen` command, found in
@@ -117,11 +119,13 @@ in
         };
         port = mkOption {
           default = 32638;
+          type = types.port;
           description = ''
             The port to listen on for incoming connections (TCP).
           '';
         };
         address = mkOption {
+          type = types.str;
           example = "127.0.0.1";
           default = "";
           description = ''
@@ -131,12 +135,14 @@ in
         };
         httpPort = mkOption {
           default = 30638;
+          type = types.port;
           description = ''
             Specifies the listen port for the HTTP service that returns the
             status page.
           '';
         };
         syncMode = mkOption {
+          type = types.enum [ "ASYNC" "SYNC_WRITE_METADATA" "SYNC_WRITE" "FDATASYNC" "ASYNC" ];
           default = "FSYNC";
           example = "FDATASYNC";
           description = ''
@@ -229,6 +235,7 @@ in
 
         uuid = mkOption {
           example = "eacb6bab-f444-4ebf-a06a-3f72d7465e41";
+          type = types.str;
           description = ''
             Must be set to a unique identifier, preferably a UUID according to
             RFC 4122. UUIDs can be generated with `uuidgen` command, found in
@@ -237,12 +244,14 @@ in
         };
         port = mkOption {
           default = 32636;
+          type = types.port;
           description = ''
             The port to listen on for incoming connections (TCP).
           '';
         };
         address = mkOption {
           example = "127.0.0.1";
+          type = types.str;
           default = "";
           description = ''
             If specified, it defines the interface to listen on. If not
@@ -251,6 +260,7 @@ in
         };
         httpPort = mkOption {
           default = 30636;
+          type = types.port;
           description = ''
             Specifies the listen port for the HTTP service that returns the
             status page.
@@ -258,6 +268,7 @@ in
         };
         syncMode = mkOption {
           default = "FSYNC";
+          type = types.enum [ "ASYNC" "SYNC_WRITE_METADATA" "SYNC_WRITE" "FDATASYNC" "ASYNC" ];
           example = "FDATASYNC";
           description = ''
             The sync mode influences how operations are committed to the disk
@@ -367,6 +378,7 @@ in
 
         uuid = mkOption {
           example = "eacb6bab-f444-4ebf-a06a-3f72d7465e42";
+          type = types.str;
           description = ''
             Must be set to a unique identifier, preferably a UUID according to
             RFC 4122. UUIDs can be generated with `uuidgen` command, found in
@@ -375,12 +387,14 @@ in
         };
         port = mkOption {
           default = 32640;
+          type = types.port;
           description = ''
             The port to listen on for incoming connections (TCP and UDP).
           '';
         };
         address = mkOption {
           example = "127.0.0.1";
+          type = types.str;
           default = "";
           description = ''
             If specified, it defines the interface to listen on. If not
@@ -389,6 +403,7 @@ in
         };
         httpPort = mkOption {
           default = 30640;
+          type = types.port;
           description = ''
             Specifies the listen port for the HTTP service that returns the
             status page.
diff --git a/nixos/modules/services/network-filesystems/yandex-disk.nix b/nixos/modules/services/network-filesystems/yandex-disk.nix
index cc73f13bf77ac..a5b1f9d4ab630 100644
--- a/nixos/modules/services/network-filesystems/yandex-disk.nix
+++ b/nixos/modules/services/network-filesystems/yandex-disk.nix
@@ -46,12 +46,14 @@ in
 
       user = mkOption {
         default = null;
+        type = types.nullOr types.str;
         description = ''
           The user the yandex-disk daemon should run as.
         '';
       };
 
       directory = mkOption {
+        type = types.path;
         default = "/home/Yandex.Disk";
         description = "The directory to use for Yandex.Disk storage";
       };
diff --git a/nixos/modules/services/networking/bee-clef.nix b/nixos/modules/services/networking/bee-clef.nix
new file mode 100644
index 0000000000000..719714b289827
--- /dev/null
+++ b/nixos/modules/services/networking/bee-clef.nix
@@ -0,0 +1,107 @@
+{ config, lib, pkgs, ... }:
+
+# NOTE for now nothing is installed into /etc/bee-clef/. the config files are used as read-only from the nix store.
+
+with lib;
+let
+  cfg = config.services.bee-clef;
+in {
+  meta = {
+    maintainers = with maintainers; [ attila-lendvai ];
+  };
+
+  ### interface
+
+  options = {
+    services.bee-clef = {
+      enable = mkEnableOption "clef external signer instance for Ethereum Swarm Bee";
+
+      dataDir = mkOption {
+        type = types.nullOr types.str;
+        default = "/var/lib/bee-clef";
+        description = ''
+          Data dir for bee-clef. Beware that some helper scripts may not work when changed!
+          The service itself should work fine, though.
+        '';
+      };
+
+      passwordFile = mkOption {
+        type = types.nullOr types.str;
+        default = "/var/lib/bee-clef/password";
+        description = "Password file for bee-clef.";
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "bee-clef";
+        description = ''
+          User the bee-clef daemon should execute under.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "bee-clef";
+        description = ''
+          Group the bee-clef daemon should execute under.
+        '';
+      };
+    };
+  };
+
+  ### implementation
+
+  config = mkIf cfg.enable {
+    # if we ever want to have rules.js under /etc/bee-clef/
+    # environment.etc."bee-clef/rules.js".source = ${pkgs.bee-clef}/rules.js
+
+    systemd.packages = [ pkgs.bee-clef ]; # include the upstream bee-clef.service file
+
+    systemd.tmpfiles.rules = [
+        "d '${cfg.dataDir}/'         0750 ${cfg.user} ${cfg.group}"
+        "d '${cfg.dataDir}/keystore' 0700 ${cfg.user} ${cfg.group}"
+      ];
+
+    systemd.services.bee-clef = {
+      path = [
+        # these are needed for the ensure-clef-account script
+        pkgs.coreutils
+        pkgs.gnused
+        pkgs.gawk
+      ];
+
+      wantedBy = [ "bee.service" "multi-user.target" ];
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStartPre = ''${pkgs.bee-clef}/share/bee-clef/ensure-clef-account "${cfg.dataDir}" "${pkgs.bee-clef}/share/bee-clef/"'';
+        ExecStart = [
+          "" # this hides/overrides what's in the original entry
+          "${pkgs.bee-clef}/share/bee-clef/bee-clef-service start"
+        ];
+        ExecStop = [
+          "" # this hides/overrides what's in the original entry
+          "${pkgs.bee-clef}/share/bee-clef/bee-clef-service stop"
+        ];
+        Environment = [
+          "CONFIGDIR=${cfg.dataDir}"
+          "PASSWORD_FILE=${cfg.passwordFile}"
+        ];
+      };
+    };
+
+    users.users = optionalAttrs (cfg.user == "bee-clef") {
+      bee-clef = {
+        group = cfg.group;
+        home = cfg.dataDir;
+        isSystemUser = true;
+        description = "Daemon user for the bee-clef service";
+      };
+    };
+
+    users.groups = optionalAttrs (cfg.group == "bee-clef") {
+      bee-clef = {};
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/bee.nix b/nixos/modules/services/networking/bee.nix
new file mode 100644
index 0000000000000..8a77ce23ab4d6
--- /dev/null
+++ b/nixos/modules/services/networking/bee.nix
@@ -0,0 +1,149 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.bee;
+  format = pkgs.formats.yaml {};
+  configFile = format.generate "bee.yaml" cfg.settings;
+in {
+  meta = {
+    # doc = ./bee.xml;
+    maintainers = with maintainers; [ attila-lendvai ];
+  };
+
+  ### interface
+
+  options = {
+    services.bee = {
+      enable = mkEnableOption "Ethereum Swarm Bee";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.bee;
+        defaultText = "pkgs.bee";
+        example = "pkgs.bee-unstable";
+        description = "The package providing the bee binary for the service.";
+      };
+
+      settings = mkOption {
+        type = format.type;
+        description = ''
+          Ethereum Swarm Bee configuration. Refer to
+          <link xlink:href="https://gateway.ethswarm.org/bzz/docs.swarm.eth/docs/installation/configuration/"/>
+          for details on supported values.
+        '';
+      };
+
+      daemonNiceLevel = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Daemon process priority for bee.
+          0 is the default Unix process priority, 19 is the lowest.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "bee";
+        description = ''
+          User the bee binary should execute under.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "bee";
+        description = ''
+          Group the bee binary should execute under.
+        '';
+      };
+    };
+  };
+
+  ### implementation
+
+  config = mkIf cfg.enable {
+    assertions = [
+      { assertion = (hasAttr "password" cfg.settings) != true;
+        message = ''
+          `services.bee.settings.password` is insecure. Use `services.bee.settings.password-file` or `systemd.services.bee.serviceConfig.EnvironmentFile` instead.
+        '';
+      }
+      { assertion = (hasAttr "swap-endpoint" cfg.settings) || (cfg.settings.swap-enable or true == false);
+        message = ''
+          In a swap-enabled network a working Ethereum blockchain node is required. You must specify one using `services.bee.settings.swap-endpoint`, or disable `services.bee.settings.swap-enable` = false.
+        '';
+      }
+    ];
+
+    warnings = optional (! config.services.bee-clef.enable) "The bee service requires an external signer. Consider setting `config.services.bee-clef.enable` = true";
+
+    services.bee.settings = {
+      data-dir             = lib.mkDefault "/var/lib/bee";
+      password-file        = lib.mkDefault "/var/lib/bee/password";
+      clef-signer-enable   = lib.mkDefault true;
+      clef-signer-endpoint = lib.mkDefault "/var/lib/bee-clef/clef.ipc";
+      swap-endpoint        = lib.mkDefault "https://rpc.slock.it/goerli";
+    };
+
+    systemd.packages = [ cfg.package ]; # include the upstream bee.service file
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.settings.data-dir}' 0750 ${cfg.user} ${cfg.group}"
+    ];
+
+    systemd.services.bee = {
+      requires = optional config.services.bee-clef.enable
+        "bee-clef.service";
+
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Nice = cfg.daemonNiceLevel;
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStart = [
+          "" # this hides/overrides what's in the original entry
+          "${cfg.package}/bin/bee --config=${configFile} start"
+        ];
+      };
+
+      preStart = with cfg.settings; ''
+        if ! test -f ${password-file}; then
+          < /dev/urandom tr -dc _A-Z-a-z-0-9 2> /dev/null | head -c32 > ${password-file}
+          chmod 0600 ${password-file}
+          echo "Initialized ${password-file} from /dev/urandom"
+        fi
+        if [ ! -f ${data-dir}/keys/libp2p.key ]; then
+          ${cfg.package}/bin/bee init --config=${configFile} >/dev/null
+          echo "
+Logs:   journalctl -f -u bee.service
+
+Bee has SWAP enabled by default and it needs ethereum endpoint to operate.
+It is recommended to use external signer with bee.
+Check documentation for more info:
+- SWAP https://docs.ethswarm.org/docs/installation/manual#swap-bandwidth-incentives
+- External signer https://docs.ethswarm.org/docs/installation/bee-clef
+
+After you finish configuration run 'sudo bee-get-addr'."
+        fi
+      '';
+    };
+
+    users.users = optionalAttrs (cfg.user == "bee") {
+      bee = {
+        group = cfg.group;
+        home = cfg.settings.data-dir;
+        isSystemUser = true;
+        description = "Daemon user for Ethereum Swarm Bee";
+        extraGroups = optional config.services.bee-clef.enable
+          config.services.bee-clef.group;
+      };
+    };
+
+    users.groups = optionalAttrs (cfg.group == "bee") {
+      bee = {};
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/bind.nix b/nixos/modules/services/networking/bind.nix
index faad886357590..e507e8ce9eebc 100644
--- a/nixos/modules/services/networking/bind.nix
+++ b/nixos/modules/services/networking/bind.nix
@@ -8,6 +8,35 @@ let
 
   bindUser = "named";
 
+  bindZoneOptions = {
+    name = mkOption {
+      type = types.str;
+      description = "Name of the zone.";
+    };
+    master = mkOption {
+      description = "Master=false means slave server";
+      type = types.bool;
+    };
+    file = mkOption {
+      type = types.either types.str types.path;
+      description = "Zone file resource records contain columns of data, separated by whitespace, that define the record.";
+    };
+    masters = mkOption {
+      type = types.listOf types.str;
+      description = "List of servers for inclusion in stub and secondary zones.";
+    };
+    slaves = mkOption {
+      type = types.listOf types.str;
+      description = "Addresses who may request zone transfers.";
+      default = [];
+    };
+    extraConfig = mkOption {
+      type = types.str;
+      description = "Extra zone config to be appended at the end of the zone section.";
+      default = "";
+    };
+  };
+
   confFile = pkgs.writeText "named.conf"
     ''
       include "/etc/bind/rndc.key";
@@ -72,6 +101,7 @@ in
 
       cacheNetworks = mkOption {
         default = ["127.0.0.0/24"];
+        type = types.listOf types.str;
         description = "
           What networks are allowed to use us as a resolver.  Note
           that this is for recursive queries -- all networks are
@@ -83,6 +113,7 @@ in
 
       blockedNetworks = mkOption {
         default = [];
+        type = types.listOf types.str;
         description = "
           What networks are just blocked.
         ";
@@ -90,6 +121,7 @@ in
 
       ipv4Only = mkOption {
         default = false;
+        type = types.bool;
         description = "
           Only use ipv4, even if the host supports ipv6.
         ";
@@ -97,6 +129,7 @@ in
 
       forwarders = mkOption {
         default = config.networking.nameservers;
+        type = types.listOf types.str;
         description = "
           List of servers we should forward requests to.
         ";
@@ -120,10 +153,9 @@ in
 
       zones = mkOption {
         default = [];
+        type = types.listOf (types.submodule [ { options = bindZoneOptions; } ]);
         description = "
           List of zones we claim authority over.
-            master=false means slave server; slaves means addresses
-           who may request zone transfer.
         ";
         example = [{
           name = "example.com";
diff --git a/nixos/modules/services/networking/dnscrypt-proxy2.nix b/nixos/modules/services/networking/dnscrypt-proxy2.nix
index ff8a2ab307746..afc2a6d1c757c 100644
--- a/nixos/modules/services/networking/dnscrypt-proxy2.nix
+++ b/nixos/modules/services/networking/dnscrypt-proxy2.nix
@@ -87,6 +87,7 @@ in
         NoNewPrivileges = true;
         NonBlocking = true;
         PrivateDevices = true;
+        ProtectClock = true;
         ProtectControlGroups = true;
         ProtectHome = true;
         ProtectHostname = true;
@@ -107,8 +108,13 @@ in
         SystemCallFilter = [
           "@system-service"
           "@chown"
+          "~@aio"
+          "~@keyring"
+          "~@memlock"
           "~@resources"
-          "@privileged"
+          "~@setuid"
+          "~@sync"
+          "~@timer"
         ];
       };
     };
diff --git a/nixos/modules/services/networking/epmd.nix b/nixos/modules/services/networking/epmd.nix
index 692b75e4f0865..f7cdc0fe79c04 100644
--- a/nixos/modules/services/networking/epmd.nix
+++ b/nixos/modules/services/networking/epmd.nix
@@ -53,4 +53,6 @@ in
       };
     };
   };
+
+  meta.maintainers = teams.beam.members;
 }
diff --git a/nixos/modules/services/networking/flashpolicyd.nix b/nixos/modules/services/networking/flashpolicyd.nix
deleted file mode 100644
index d3ac78430ca34..0000000000000
--- a/nixos/modules/services/networking/flashpolicyd.nix
+++ /dev/null
@@ -1,86 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  cfg = config.services.flashpolicyd;
-
-  flashpolicyd = pkgs.stdenv.mkDerivation {
-    name = "flashpolicyd-0.6";
-
-    src = pkgs.fetchurl {
-      name = "flashpolicyd_v0.6.zip";
-      url = "https://download.adobe.com/pub/adobe/devnet/flashplayer/articles/socket_policy_files/flashpolicyd_v0.6.zip";
-      sha256 = "16zk237233npwfq1m4ksy4g5lzy1z9fp95w7pz0cdlpmv0fv9sm3";
-    };
-
-    buildInputs = [ pkgs.unzip pkgs.perl ];
-
-    installPhase = "mkdir $out; cp -pr * $out/; chmod +x $out/*/*.pl";
-  };
-
-  flashpolicydWrapper = pkgs.writeScriptBin "flashpolicyd"
-    ''
-      #! ${pkgs.runtimeShell}
-      exec ${flashpolicyd}/Perl_xinetd/in.flashpolicyd.pl \
-        --file=${pkgs.writeText "flashpolixy.xml" cfg.policy} \
-        2> /dev/null
-    '';
-
-in
-
-{
-
-  ###### interface
-
-  options = {
-
-    services.flashpolicyd = {
-
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description =
-          ''
-            Whether to enable the Flash Policy server.  This is
-            necessary if you want Flash applications to make
-            connections to your server.
-          '';
-      };
-
-      policy = mkOption {
-        type = types.lines;
-        default =
-          ''
-            <?xml version="1.0"?>
-            <!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">
-            <cross-domain-policy>
-              <site-control permitted-cross-domain-policies="master-only"/>
-              <allow-access-from domain="*" to-ports="*" />
-            </cross-domain-policy>
-          '';
-        description = "The policy to be served.  The default is to allow connections from any domain to any port.";
-      };
-
-    };
-
-  };
-
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-
-    services.xinetd.enable = true;
-
-    services.xinetd.services = singleton
-      { name = "flashpolicy";
-        port = 843;
-        unlisted = true;
-        server = "${flashpolicydWrapper}/bin/flashpolicyd";
-      };
-
-  };
-
-}
diff --git a/nixos/modules/services/networking/ircd-hybrid/default.nix b/nixos/modules/services/networking/ircd-hybrid/default.nix
index 91d0bf437d693..0781159b6ee73 100644
--- a/nixos/modules/services/networking/ircd-hybrid/default.nix
+++ b/nixos/modules/services/networking/ircd-hybrid/default.nix
@@ -40,6 +40,7 @@ in
 
       serverName = mkOption {
         default = "hades.arpa";
+        type = types.str;
         description = "
           IRCD server name.
         ";
@@ -47,6 +48,7 @@ in
 
       sid = mkOption {
         default = "0NL";
+        type = types.str;
         description = "
           IRCD server unique ID in a net of servers.
         ";
@@ -54,6 +56,7 @@ in
 
       description = mkOption {
         default = "Hybrid-7 IRC server.";
+        type = types.str;
         description = "
           IRCD server description.
         ";
@@ -62,6 +65,7 @@ in
       rsaKey = mkOption {
         default = null;
         example = literalExample "/root/certificates/irc.key";
+        type = types.nullOr types.path;
         description = "
           IRCD server RSA key.
         ";
@@ -70,6 +74,7 @@ in
       certificate = mkOption {
         default = null;
         example = literalExample "/root/certificates/irc.pem";
+        type = types.nullOr types.path;
         description = "
           IRCD server SSL certificate. There are some limitations - read manual.
         ";
@@ -77,6 +82,7 @@ in
 
       adminEmail = mkOption {
         default = "<bit-bucket@example.com>";
+        type = types.str;
         example = "<name@domain.tld>";
         description = "
           IRCD server administrator e-mail.
@@ -86,6 +92,7 @@ in
       extraIPs = mkOption {
         default = [];
         example = ["127.0.0.1"];
+        type = types.listOf types.str;
         description = "
           Extra IP's to bind.
         ";
@@ -93,6 +100,7 @@ in
 
       extraPort = mkOption {
         default = "7117";
+        type = types.str;
         description = "
           Extra port to avoid filtering.
         ";
diff --git a/nixos/modules/services/networking/mailpile.nix b/nixos/modules/services/networking/mailpile.nix
index b79ee11d17db2..4673a2580b602 100644
--- a/nixos/modules/services/networking/mailpile.nix
+++ b/nixos/modules/services/networking/mailpile.nix
@@ -21,11 +21,13 @@ in
       enable = mkEnableOption "Mailpile the mail client";
 
       hostname = mkOption {
+        type = types.str;
         default = "localhost";
         description = "Listen to this hostname or ip.";
       };
       port = mkOption {
-        default = "33411";
+        type = types.port;
+        default = 33411;
         description = "Listen on this port.";
       };
     };
diff --git a/nixos/modules/services/networking/prayer.nix b/nixos/modules/services/networking/prayer.nix
index f04dac01d9b8e..ae9258b27122f 100644
--- a/nixos/modules/services/networking/prayer.nix
+++ b/nixos/modules/services/networking/prayer.nix
@@ -44,7 +44,8 @@ in
       enable = mkEnableOption "the prayer webmail http server";
 
       port = mkOption {
-        default = "2080";
+        default = 2080;
+        type = types.port;
         description = ''
           Port the prayer http server is listening to.
         '';
diff --git a/nixos/modules/services/networking/quassel.nix b/nixos/modules/services/networking/quassel.nix
index 2958fb9a8b334..bfbd3b46ab4d9 100644
--- a/nixos/modules/services/networking/quassel.nix
+++ b/nixos/modules/services/networking/quassel.nix
@@ -45,6 +45,7 @@ in
       };
 
       interfaces = mkOption {
+        type = types.listOf types.str;
         default = [ "127.0.0.1" ];
         description = ''
           The interfaces the Quassel daemon will be listening to.  If `[ 127.0.0.1 ]',
@@ -54,6 +55,7 @@ in
       };
 
       portNumber = mkOption {
+        type = types.port;
         default = 4242;
         description = ''
           The port number the Quassel daemon will be listening to.
@@ -62,6 +64,7 @@ in
 
       dataDir = mkOption {
         default = "/home/${user}/.config/quassel-irc.org";
+        type = types.str;
         description = ''
           The directory holding configuration files, the SQlite database and the SSL Cert.
         '';
@@ -69,6 +72,7 @@ in
 
       user = mkOption {
         default = null;
+        type = types.nullOr types.str;
         description = ''
           The existing user the Quassel daemon should run as. If left empty, a default "quassel" user will be created.
         '';
diff --git a/nixos/modules/services/networking/radvd.nix b/nixos/modules/services/networking/radvd.nix
index f4b00c9b356ef..53fac4b7b72dc 100644
--- a/nixos/modules/services/networking/radvd.nix
+++ b/nixos/modules/services/networking/radvd.nix
@@ -33,6 +33,7 @@ in
     };
 
     services.radvd.config = mkOption {
+      type = types.lines;
       example =
         ''
           interface eth0 {
diff --git a/nixos/modules/services/networking/resilio.nix b/nixos/modules/services/networking/resilio.nix
index 6193d7340fc4a..4701b0e8143d2 100644
--- a/nixos/modules/services/networking/resilio.nix
+++ b/nixos/modules/services/networking/resilio.nix
@@ -183,6 +183,7 @@ in
 
       sharedFolders = mkOption {
         default = [];
+        type = types.listOf (types.attrsOf types.anything);
         example =
           [ { secret         = "AHMYFPCQAHBM7LQPFXQ7WV6Y42IGUXJ5Y";
               directory      = "/home/user/sync_test";
diff --git a/nixos/modules/services/networking/sabnzbd.nix b/nixos/modules/services/networking/sabnzbd.nix
index ff5aef7d1cb47..43566dfd25c5f 100644
--- a/nixos/modules/services/networking/sabnzbd.nix
+++ b/nixos/modules/services/networking/sabnzbd.nix
@@ -18,16 +18,19 @@ in
       enable = mkEnableOption "the sabnzbd server";
 
       configFile = mkOption {
+        type = types.path;
         default = "/var/lib/sabnzbd/sabnzbd.ini";
         description = "Path to config file.";
       };
 
       user = mkOption {
         default = "sabnzbd";
+        type = types.str;
         description = "User to run the service as";
       };
 
       group = mkOption {
+        type = types.str;
         default = "sabnzbd";
         description = "Group to run the service as";
       };
diff --git a/nixos/modules/services/networking/shairport-sync.nix b/nixos/modules/services/networking/shairport-sync.nix
index b4b86a2d55bee..ac526c0e9f6f4 100644
--- a/nixos/modules/services/networking/shairport-sync.nix
+++ b/nixos/modules/services/networking/shairport-sync.nix
@@ -28,6 +28,7 @@ in
       };
 
       arguments = mkOption {
+        type = types.str;
         default = "-v -o pa";
         description = ''
           Arguments to pass to the daemon. Defaults to a local pulseaudio
@@ -36,6 +37,7 @@ in
       };
 
       user = mkOption {
+        type = types.str;
         default = "shairport";
         description = ''
           User account name under which to run shairport-sync. The account
diff --git a/nixos/modules/services/networking/ssh/lshd.nix b/nixos/modules/services/networking/ssh/lshd.nix
index e46d62bf1e82f..862ff7df05407 100644
--- a/nixos/modules/services/networking/ssh/lshd.nix
+++ b/nixos/modules/services/networking/ssh/lshd.nix
@@ -29,6 +29,7 @@ in
 
       portNumber = mkOption {
         default = 22;
+        type = types.port;
         description = ''
           The port on which to listen for connections.
         '';
@@ -36,6 +37,7 @@ in
 
       interfaces = mkOption {
         default = [];
+        type = types.listOf types.str;
         description = ''
           List of network interfaces where listening for connections.
           When providing the empty list, `[]', lshd listens on all
@@ -46,6 +48,7 @@ in
 
       hostKey = mkOption {
         default = "/etc/lsh/host-key";
+        type = types.str;
         description = ''
           Path to the server's private key.  Note that this key must
           have been created, e.g., using "lsh-keygen --server |
@@ -79,6 +82,7 @@ in
 
       loginShell = mkOption {
         default = null;
+        type = types.nullOr types.str;
         description = ''
           If non-null, override the default login shell with the
           specified value.
@@ -88,6 +92,7 @@ in
 
       srpKeyExchange = mkOption {
         default = false;
+        type = types.bool;
         description = ''
           Whether to enable SRP key exchange and user authentication.
         '';
@@ -106,6 +111,7 @@ in
       };
 
       subsystems = mkOption {
+        type = types.listOf types.path;
         description = ''
           List of subsystem-path pairs, where the head of the pair
           denotes the subsystem name, and the tail denotes the path to
diff --git a/nixos/modules/services/security/oauth2_proxy.nix b/nixos/modules/services/security/oauth2_proxy.nix
index 486f3ab05386d..77c579279abe5 100644
--- a/nixos/modules/services/security/oauth2_proxy.nix
+++ b/nixos/modules/services/security/oauth2_proxy.nix
@@ -538,6 +538,7 @@ in
 
     extraConfig = mkOption {
       default = {};
+      type = types.attrsOf types.anything;
       description = ''
         Extra config to pass to oauth2-proxy.
       '';
diff --git a/nixos/modules/services/security/oauth2_proxy_nginx.nix b/nixos/modules/services/security/oauth2_proxy_nginx.nix
index be6734f439f3d..553638ad49658 100644
--- a/nixos/modules/services/security/oauth2_proxy_nginx.nix
+++ b/nixos/modules/services/security/oauth2_proxy_nginx.nix
@@ -31,7 +31,7 @@ in
         proxyPass = cfg.proxy;
         extraConfig = ''
           proxy_set_header X-Scheme                $scheme;
-          proxy_set_header X-Auth-Request-Redirect $request_uri;
+          proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
         '';
       };
       locations."/oauth2/auth" = {
diff --git a/nixos/modules/services/web-apps/galene.nix b/nixos/modules/services/web-apps/galene.nix
new file mode 100644
index 0000000000000..769490e915ac8
--- /dev/null
+++ b/nixos/modules/services/web-apps/galene.nix
@@ -0,0 +1,178 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.galene;
+  defaultstateDir = "/var/lib/galene";
+  defaultrecordingsDir = "${cfg.stateDir}/recordings";
+  defaultgroupsDir = "${cfg.stateDir}/groups";
+  defaultdataDir = "${cfg.stateDir}/data";
+in
+{
+  options = {
+    services.galene = {
+      enable = mkEnableOption "Galene Service.";
+
+      stateDir = mkOption {
+        default = defaultstateDir;
+        type = types.str;
+        description = ''
+          The directory where Galene stores its internal state. If left as the default
+          value this directory will automatically be created before the Galene server
+          starts, otherwise the sysadmin is responsible for ensuring the directory
+          exists with appropriate ownership and permissions.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "galene";
+        description = "User account under which galene runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "galene";
+        description = "Group under which galene runs.";
+      };
+
+      insecure = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether Galene should listen in http or in https. If left as the default
+          value (false), Galene needs to be fed a private key and a certificate.
+        '';
+      };
+
+      certFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/path/to/your/cert.pem";
+        description = ''
+          Path to the server's certificate. The file is copied at runtime to
+          Galene's data directory where it needs to reside.
+        '';
+      };
+
+      keyFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/path/to/your/key.pem";
+        description = ''
+          Path to the server's private key. The file is copied at runtime to
+          Galene's data directory where it needs to reside.
+        '';
+      };
+
+      httpAddress = mkOption {
+        type = types.str;
+        default = "";
+        description = "HTTP listen address for galene.";
+      };
+
+      httpPort = mkOption {
+        type = types.port;
+        default = 8443;
+        description = "HTTP listen port.";
+      };
+
+      staticDir = mkOption {
+        type = types.str;
+        default = "${cfg.package.static}/static";
+        example = "/var/lib/galene/static";
+        description = "Web server directory.";
+      };
+
+      recordingsDir = mkOption {
+        type = types.str;
+        default = defaultrecordingsDir;
+        example = "/var/lib/galene/recordings";
+        description = "Recordings directory.";
+      };
+
+      dataDir = mkOption {
+        type = types.str;
+        default = defaultdataDir;
+        example = "/var/lib/galene/data";
+        description = "Data directory.";
+      };
+
+      groupsDir = mkOption {
+        type = types.str;
+        default = defaultgroupsDir;
+        example = "/var/lib/galene/groups";
+        description = "Web server directory.";
+      };
+
+      package = mkOption {
+        default = pkgs.galene;
+        defaultText = "pkgs.galene";
+        type = types.package;
+        description = ''
+          Package for running Galene.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = cfg.insecure || (cfg.certFile != null && cfg.keyFile != null);
+        message = ''
+          Galene needs both certFile and keyFile defined for encryption, or
+          the insecure flag.
+        '';
+      }
+    ];
+
+    systemd.services.galene = {
+      description = "galene";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        install -m 700 -o '${cfg.user}' -g '${cfg.group}' ${cfg.certFile} ${cfg.dataDir}/cert.pem
+        install -m 700 -o '${cfg.user}' -g '${cfg.group}' ${cfg.keyFile} ${cfg.dataDir}/key.pem
+      '';
+
+      serviceConfig = mkMerge [
+        {
+          Type = "simple";
+          User = cfg.user;
+          Group = cfg.group;
+          WorkingDirectory = cfg.stateDir;
+          ExecStart = ''${cfg.package}/bin/galene \
+          ${optionalString (cfg.insecure) "-insecure"} \
+          -data ${cfg.dataDir} \
+          -groups ${cfg.groupsDir} \
+          -recordings ${cfg.recordingsDir} \
+          -static ${cfg.staticDir}'';
+          Restart = "always";
+          # Upstream Requirements
+          LimitNOFILE = 65536;
+          StateDirectory = [ ] ++
+            optional (cfg.stateDir == defaultstateDir) "galene" ++
+            optional (cfg.dataDir == defaultdataDir) "galene/data" ++
+            optional (cfg.groupsDir == defaultgroupsDir) "galene/groups" ++
+            optional (cfg.recordingsDir == defaultrecordingsDir) "galene/recordings";
+        }
+      ];
+    };
+
+    users.users = mkIf (cfg.user == "galene")
+      {
+        galene = {
+          description = "galene Service";
+          group = cfg.group;
+          isSystemUser = true;
+        };
+      };
+
+    users.groups = mkIf (cfg.group == "galene") {
+      galene = { };
+    };
+  };
+  meta.maintainers = with lib.maintainers; [ rgrunbla ];
+}
diff --git a/nixos/modules/services/web-apps/mediawiki.nix b/nixos/modules/services/web-apps/mediawiki.nix
index 0a5b6047bb58a..1db1652022a34 100644
--- a/nixos/modules/services/web-apps/mediawiki.nix
+++ b/nixos/modules/services/web-apps/mediawiki.nix
@@ -180,6 +180,7 @@ in
       };
 
       name = mkOption {
+        type = types.str;
         default = "MediaWiki";
         example = "Foobar Wiki";
         description = "Name of the wiki.";
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index 1b643bd3260a2..c5c14050b5c2f 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -244,7 +244,8 @@ in {
         type = types.nullOr types.str;
         default = null;
         description = ''
-          The full path to a file that contains the admin's password.
+          The full path to a file that contains the admin's password. Must be
+          readable by user <literal>nextcloud</literal>.
         '';
       };
 
@@ -482,6 +483,28 @@ in {
           path = [ occ ];
           script = ''
             chmod og+x ${cfg.home}
+
+            ${optionalString (c.dbpassFile != null) ''
+              if [ ! -r "${c.dbpassFile}" ]; then
+                echo "dbpassFile ${c.dbpassFile} is not readable by nextcloud:nextcloud! Aborting..."
+                exit 1
+              fi
+              if [ -z "$(<${c.dbpassFile})" ]; then
+                echo "dbpassFile ${c.dbpassFile} is empty!"
+                exit 1
+              fi
+            ''}
+            ${optionalString (c.adminpassFile != null) ''
+              if [ ! -r "${c.adminpassFile}" ]; then
+                echo "adminpassFile ${c.adminpassFile} is not readable by nextcloud:nextcloud! Aborting..."
+                exit 1
+              fi
+              if [ -z "$(<${c.adminpassFile})" ]; then
+                echo "adminpassFile ${c.adminpassFile} is empty!"
+                exit 1
+              fi
+            ''}
+
             ln -sf ${cfg.package}/apps ${cfg.home}/
 
             # create nextcloud directories.
diff --git a/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix b/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix
index 173c0f8561c0f..394f9a305546c 100644
--- a/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix
@@ -112,7 +112,7 @@ in
 
     acmeRoot = mkOption {
       type = types.str;
-      default = "/var/lib/acme/acme-challenges";
+      default = "/var/lib/acme/acme-challenge";
       description = "Directory for the acme challenge which is PUBLIC, don't put certs or keys in here";
     };
 
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index d6f463be9e811..fa8614e8ec174 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -79,6 +79,8 @@ let
       include ${pkgs.mailcap}/etc/nginx/mime.types;
       include ${cfg.package}/conf/fastcgi.conf;
       include ${cfg.package}/conf/uwsgi_params;
+
+      default_type application/octet-stream;
   '';
 
   configFile = pkgs.writers.writeNginxConfig "nginx.conf" ''
@@ -404,6 +406,7 @@ in
 
       logError = mkOption {
         default = "stderr";
+        type = types.str;
         description = "
           Configures logging.
           The first parameter defines a file that will store the log. The
diff --git a/nixos/modules/services/web-servers/nginx/location-options.nix b/nixos/modules/services/web-servers/nginx/location-options.nix
index 5a7f5188b6cfe..d8c976f202fd1 100644
--- a/nixos/modules/services/web-servers/nginx/location-options.nix
+++ b/nixos/modules/services/web-servers/nginx/location-options.nix
@@ -52,7 +52,7 @@ with lib;
       default = false;
       example = true;
       description = ''
-        Whether to supporty proxying websocket connections with HTTP/1.1.
+        Whether to support proxying websocket connections with HTTP/1.1.
       '';
     };
 
diff --git a/nixos/modules/services/web-servers/unit/default.nix b/nixos/modules/services/web-servers/unit/default.nix
index 894271d1e55e4..2a264bf2e9a6f 100644
--- a/nixos/modules/services/web-servers/unit/default.nix
+++ b/nixos/modules/services/web-servers/unit/default.nix
@@ -28,10 +28,12 @@ in {
         description = "Group account under which unit runs.";
       };
       stateDir = mkOption {
+        type = types.path;
         default = "/var/spool/unit";
         description = "Unit data directory.";
       };
       logDir = mkOption {
+        type = types.path;
         default = "/var/log/unit";
         description = "Unit log directory.";
       };
diff --git a/nixos/modules/services/x11/clight.nix b/nixos/modules/services/x11/clight.nix
index 4daf6d8d9db7e..873f425fb8be4 100644
--- a/nixos/modules/services/x11/clight.nix
+++ b/nixos/modules/services/x11/clight.nix
@@ -11,14 +11,21 @@ let
     else if isBool v      then boolToString v
     else if isString v    then ''"${escape [''"''] v}"''
     else if isList v      then "[ " + concatMapStringsSep ", " toConf v + " ]"
+    else if isAttrs v     then "\n{\n" + convertAttrs v + "\n}"
     else abort "clight.toConf: unexpected type (v = ${v})";
 
-  clightConf = pkgs.writeText "clight.conf"
-    (concatStringsSep "\n" (mapAttrsToList
-      (name: value: "${toString name} = ${toConf value};")
-      (filterAttrs
-        (_: value: value != null)
-        cfg.settings)));
+  getSep = v:
+    if isAttrs v then ":"
+    else "=";
+
+  convertAttrs = attrs: concatStringsSep "\n" (mapAttrsToList
+    (name: value: "${toString name} ${getSep value} ${toConf value};")
+    attrs);
+
+  clightConf = pkgs.writeText "clight.conf" (convertAttrs
+    (filterAttrs
+      (_: value: value != null)
+      cfg.settings));
 in {
   options.services.clight = {
     enable = mkOption {
@@ -49,9 +56,10 @@ in {
     };
 
     settings = let
-      validConfigTypes = with types; either int (either str (either bool float));
+      validConfigTypes = with types; oneOf [ int str bool float ];
+      collectionTypes = with types; oneOf [ validConfigTypes (listOf validConfigTypes) ];
     in mkOption {
-      type = with types; attrsOf (nullOr (either validConfigTypes (listOf validConfigTypes)));
+      type = with types; attrsOf (nullOr (either collectionTypes (attrsOf collectionTypes)));
       default = {};
       example = { captures = 20; gamma_long_transition = true; ac_capture_timeouts = [ 120 300 60 ]; };
       description = ''
@@ -69,10 +77,10 @@ in {
     services.upower.enable = true;
 
     services.clight.settings = {
-      gamma_temp = with cfg.temperature; mkDefault [ day night ];
+      gamma.temp = with cfg.temperature; mkDefault [ day night ];
     } // (optionalAttrs (config.location.provider == "manual") {
-      latitude = mkDefault config.location.latitude;
-      longitude = mkDefault config.location.longitude;
+      daytime.latitude = mkDefault config.location.latitude;
+      daytime.longitude = mkDefault config.location.longitude;
     });
 
     services.geoclue2.appConfig.clightc = {
diff --git a/nixos/modules/services/x11/desktop-managers/cinnamon.nix b/nixos/modules/services/x11/desktop-managers/cinnamon.nix
index a404143a03d4b..14dcf009a7d13 100644
--- a/nixos/modules/services/x11/desktop-managers/cinnamon.nix
+++ b/nixos/modules/services/x11/desktop-managers/cinnamon.nix
@@ -25,6 +25,7 @@ in
 
       sessionPath = mkOption {
         default = [];
+        type = types.listOf types.package;
         example = literalExample "[ pkgs.gnome3.gpaste ]";
         description = ''
           Additional list of packages to be added to the session search path.
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index a36a47d376b6e..671301246a8c7 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -118,6 +118,7 @@ in
 
       sessionPath = mkOption {
         default = [];
+        type = types.listOf types.package;
         example = literalExample "[ pkgs.gnome3.gpaste ]";
         description = ''
           Additional list of packages to be added to the session search path.
diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixos/modules/services/x11/desktop-managers/pantheon.nix
index cf02a71248b17..195da75e74437 100644
--- a/nixos/modules/services/x11/desktop-managers/pantheon.nix
+++ b/nixos/modules/services/x11/desktop-managers/pantheon.nix
@@ -42,6 +42,7 @@ in
 
       sessionPath = mkOption {
         default = [];
+        type = types.listOf types.package;
         example = literalExample "[ pkgs.gnome3.gpaste ]";
         description = ''
           Additional list of packages to be added to the session search path.
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 de932e6e840ae..9c1dc1d1c12d9 100644
--- a/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
@@ -88,6 +88,7 @@ in
       cursorTheme = {
 
         package = mkOption {
+          type = types.package;
           default = pkgs.gnome3.adwaita-icon-theme;
           defaultText = "pkgs.gnome3.adwaita-icon-theme";
           description = ''
diff --git a/nixos/modules/services/x11/window-managers/exwm.nix b/nixos/modules/services/x11/window-managers/exwm.nix
index 0743f35c1d316..4b707d3984969 100644
--- a/nixos/modules/services/x11/window-managers/exwm.nix
+++ b/nixos/modules/services/x11/window-managers/exwm.nix
@@ -21,6 +21,7 @@ in
       enable = mkEnableOption "exwm";
       loadScript = mkOption {
         default = "(require 'exwm)";
+        type = types.lines;
         example = literalExample ''
           (require 'exwm)
           (exwm-enable)
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index eb8c4c17e987e..8858559d8f27d 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -441,6 +441,7 @@ in
 
       serverFlagsSection = mkOption {
         default = "";
+        type = types.lines;
         example =
           ''
           Option "BlankTime" "0"
@@ -649,7 +650,7 @@ in
         xorg.xprop
         xorg.xauth
         pkgs.xterm
-        pkgs.xdg_utils
+        pkgs.xdg-utils
         xorg.xf86inputevdev.out # get evdev.4 man page
       ]
       ++ optional (elem "virtualbox" cfg.videoDrivers) xorg.xrefresh;
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index ed7226331d70e..363d8e47a0ff1 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -156,6 +156,16 @@ in
       description = "List of modules that are always loaded by the initrd.";
     };
 
+    boot.initrd.includeDefaultModules = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        This option, if set, adds a collection of default kernel modules
+        to <option>boot.initrd.availableKernelModules</option> and
+        <option>boot.initrd.kernelModules</option>.
+      '';
+    };
+
     system.modulesTree = mkOption {
       type = types.listOf types.path;
       internal = true;
@@ -195,7 +205,8 @@ in
   config = mkMerge
     [ (mkIf config.boot.initrd.enable {
         boot.initrd.availableKernelModules =
-          [ # Note: most of these (especially the SATA/PATA modules)
+          optionals config.boot.initrd.includeDefaultModules ([
+            # Note: most of these (especially the SATA/PATA modules)
             # shouldn't be included by default since nixos-generate-config
             # detects them, but I'm keeping them for now for backwards
             # compatibility.
@@ -235,10 +246,11 @@ in
 
             # x86 RTC needed by the stage 2 init script.
             "rtc_cmos"
-          ];
+          ]);
 
         boot.initrd.kernelModules =
-          [ # For LVM.
+          optionals config.boot.initrd.includeDefaultModules [
+            # For LVM.
             "dm_mod"
           ];
       })
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index a055072f9c967..a9b5b134d889d 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -7,8 +7,9 @@ let
 
   addCheckDesc = desc: elemType: check: types.addCheck elemType check
     // { description = "${elemType.description} (with check: ${desc})"; };
-  nonEmptyStr = addCheckDesc "non-empty" types.str
-    (x: x != "" && ! (all (c: c == " " || c == "\t") (stringToCharacters x)));
+
+  isNonEmpty = s: (builtins.match "[ \t\n]*" s) == null;
+  nonEmptyStr = addCheckDesc "non-empty" types.str isNonEmpty;
 
   fileSystems' = toposort fsBefore (attrValues config.fileSystems);
 
@@ -28,10 +29,10 @@ let
   coreFileSystemOpts = { name, config, ... }: {
 
     options = {
-
       mountPoint = mkOption {
         example = "/mnt/usb";
-        type = nonEmptyStr;
+        type = addCheckDesc "non-empty without trailing slash" types.str
+          (s: isNonEmpty s && (builtins.match ".+/" s) == null);
         description = "Location of the mounted the file system.";
       };
 
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 16ba0b746789b..b750820bfa509 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -17,20 +17,8 @@ let
   inInitrd = any (fs: fs == "zfs") config.boot.initrd.supportedFilesystems;
   inSystem = any (fs: fs == "zfs") config.boot.supportedFilesystems;
 
-  enableZfs = inInitrd || inSystem;
-
-  kernel = config.boot.kernelPackages;
-
-  packages = if config.boot.zfs.enableUnstable then {
-    zfs = kernel.zfsUnstable;
-    zfsUser = pkgs.zfsUnstable;
-  } else {
-    zfs = kernel.zfs;
-    zfsUser = pkgs.zfs;
-  };
-
   autosnapPkg = pkgs.zfstools.override {
-    zfs = packages.zfsUser;
+    zfs = cfgZfs.package;
   };
 
   zfsAutoSnap = "${autosnapPkg}/bin/zfs-auto-snapshot";
@@ -111,6 +99,20 @@ in
 
   options = {
     boot.zfs = {
+      package = mkOption {
+        readOnly = true;
+        type = types.package;
+        default = if config.boot.zfs.enableUnstable then pkgs.zfsUnstable else pkgs.zfs;
+        description = "Configured ZFS userland tools package.";
+      };
+
+      enabled = mkOption {
+        readOnly = true;
+        type = types.bool;
+        default = inInitrd || inSystem;
+        description = "True if ZFS filesystem support is enabled";
+      };
+
       enableUnstable = mkOption {
         type = types.bool;
         default = false;
@@ -354,7 +356,7 @@ in
   ###### implementation
 
   config = mkMerge [
-    (mkIf enableZfs {
+    (mkIf cfgZfs.enabled {
       assertions = [
         {
           assertion = config.networking.hostId != null;
@@ -366,20 +368,24 @@ in
         }
       ];
 
-      virtualisation.lxd.zfsSupport = true;
-
       boot = {
         kernelModules = [ "zfs" ];
-        extraModulePackages = with packages; [ zfs ];
+
+        extraModulePackages = [
+          (if config.boot.zfs.enableUnstable then
+            config.boot.kernelPackages.zfsUnstable
+           else
+            config.boot.kernelPackages.zfs)
+        ];
       };
 
       boot.initrd = mkIf inInitrd {
         kernelModules = [ "zfs" ] ++ optional (!cfgZfs.enableUnstable) "spl";
         extraUtilsCommands =
           ''
-            copy_bin_and_libs ${packages.zfsUser}/sbin/zfs
-            copy_bin_and_libs ${packages.zfsUser}/sbin/zdb
-            copy_bin_and_libs ${packages.zfsUser}/sbin/zpool
+            copy_bin_and_libs ${cfgZfs.package}/sbin/zfs
+            copy_bin_and_libs ${cfgZfs.package}/sbin/zdb
+            copy_bin_and_libs ${cfgZfs.package}/sbin/zpool
           '';
         extraUtilsCommandsTest = mkIf inInitrd
           ''
@@ -433,7 +439,7 @@ in
       services.zfs.zed.settings = {
         ZED_EMAIL_PROG = mkDefault "${pkgs.mailutils}/bin/mail";
         PATH = lib.makeBinPath [
-          packages.zfsUser
+          cfgZfs.package
           pkgs.coreutils
           pkgs.curl
           pkgs.gawk
@@ -461,18 +467,18 @@ in
             "vdev_clear-led.sh"
           ]
         )
-        (file: { source = "${packages.zfsUser}/etc/${file}"; })
+        (file: { source = "${cfgZfs.package}/etc/${file}"; })
       // {
         "zfs/zed.d/zed.rc".text = zedConf;
-        "zfs/zpool.d".source = "${packages.zfsUser}/etc/zfs/zpool.d/";
+        "zfs/zpool.d".source = "${cfgZfs.package}/etc/zfs/zpool.d/";
       };
 
-      system.fsPackages = [ packages.zfsUser ]; # XXX: needed? zfs doesn't have (need) a fsck
-      environment.systemPackages = [ packages.zfsUser ]
+      system.fsPackages = [ cfgZfs.package ]; # XXX: needed? zfs doesn't have (need) a fsck
+      environment.systemPackages = [ cfgZfs.package ]
         ++ optional cfgSnapshots.enable autosnapPkg; # so the user can run the command to see flags
 
-      services.udev.packages = [ packages.zfsUser ]; # to hook zvol naming, etc.
-      systemd.packages = [ packages.zfsUser ];
+      services.udev.packages = [ cfgZfs.package ]; # to hook zvol naming, etc.
+      systemd.packages = [ cfgZfs.package ];
 
       systemd.services = let
         getPoolFilesystems = pool:
@@ -506,8 +512,8 @@ in
             environment.ZFS_FORCE = optionalString cfgZfs.forceImportAll "-f";
             script = (importLib {
               # See comments at importLib definition.
-              zpoolCmd="${packages.zfsUser}/sbin/zpool";
-              awkCmd="${pkgs.gawk}/bin/awk";
+              zpoolCmd = "${cfgZfs.package}/sbin/zpool";
+              awkCmd = "${pkgs.gawk}/bin/awk";
               inherit cfgZfs;
             }) + ''
               poolImported "${pool}" && exit
@@ -522,7 +528,7 @@ in
                 ${optionalString (if isBool cfgZfs.requestEncryptionCredentials
                                   then cfgZfs.requestEncryptionCredentials
                                   else cfgZfs.requestEncryptionCredentials != []) ''
-                  ${packages.zfsUser}/sbin/zfs list -rHo name,keylocation ${pool} | while IFS=$'\t' read ds kl; do
+                  ${cfgZfs.package}/sbin/zfs list -rHo name,keylocation ${pool} | while IFS=$'\t' read ds kl; do
                     (${optionalString (!isBool cfgZfs.requestEncryptionCredentials) ''
                          if ! echo '${concatStringsSep "\n" cfgZfs.requestEncryptionCredentials}' | grep -qFx "$ds"; then
                            continue
@@ -532,10 +538,10 @@ in
                       none )
                         ;;
                       prompt )
-                        ${config.systemd.package}/bin/systemd-ask-password "Enter key for $ds:" | ${packages.zfsUser}/sbin/zfs load-key "$ds"
+                        ${config.systemd.package}/bin/systemd-ask-password "Enter key for $ds:" | ${cfgZfs.package}/sbin/zfs load-key "$ds"
                         ;;
                       * )
-                        ${packages.zfsUser}/sbin/zfs load-key "$ds"
+                        ${cfgZfs.package}/sbin/zfs load-key "$ds"
                         ;;
                     esac) < /dev/null # To protect while read ds kl in case anything reads stdin
                   done
@@ -561,7 +567,7 @@ in
               RemainAfterExit = true;
             };
             script = ''
-              ${packages.zfsUser}/sbin/zfs set nixos:shutdown-time="$(date)" "${pool}"
+              ${cfgZfs.package}/sbin/zfs set nixos:shutdown-time="$(date)" "${pool}"
             '';
           };
         createZfsService = serv:
@@ -587,7 +593,7 @@ in
       systemd.targets.zfs.wantedBy = [ "multi-user.target" ];
     })
 
-    (mkIf (enableZfs && cfgSnapshots.enable) {
+    (mkIf (cfgZfs.enabled && cfgSnapshots.enable) {
       systemd.services = let
                            descr = name: if name == "frequent" then "15 mins"
                                     else if name == "hourly" then "hour"
@@ -625,7 +631,7 @@ in
                             }) snapshotNames);
     })
 
-    (mkIf (enableZfs && cfgScrub.enable) {
+    (mkIf (cfgZfs.enabled && cfgScrub.enable) {
       systemd.services.zfs-scrub = {
         description = "ZFS pools scrubbing";
         after = [ "zfs-import.target" ];
@@ -633,11 +639,11 @@ in
           Type = "oneshot";
         };
         script = ''
-          ${packages.zfsUser}/bin/zpool scrub ${
+          ${cfgZfs.package}/bin/zpool scrub ${
             if cfgScrub.pools != [] then
               (concatStringsSep " " cfgScrub.pools)
             else
-              "$(${packages.zfsUser}/bin/zpool list -H -o name)"
+              "$(${cfgZfs.package}/bin/zpool list -H -o name)"
             }
         '';
       };
@@ -652,11 +658,11 @@ in
       };
     })
 
-    (mkIf (enableZfs && cfgTrim.enable) {
+    (mkIf (cfgZfs.enabled && cfgTrim.enable) {
       systemd.services.zpool-trim = {
         description = "ZFS pools trim";
         after = [ "zfs-import.target" ];
-        path = [ packages.zfsUser ];
+        path = [ cfgZfs.package ];
         startAt = cfgTrim.interval;
         # By default we ignore errors returned by the trim command, in case:
         # - HDDs are mixed with SSDs
diff --git a/nixos/modules/virtualisation/cri-o.nix b/nixos/modules/virtualisation/cri-o.nix
index aa416e7990a8b..8d352e36ef99a 100644
--- a/nixos/modules/virtualisation/cri-o.nix
+++ b/nixos/modules/virtualisation/cri-o.nix
@@ -103,7 +103,10 @@ in
       cgroup_manager = "systemd"
       log_level = "${cfg.logLevel}"
       pinns_path = "${cfg.package}/bin/pinns"
-      hooks_dir = []
+      hooks_dir = [
+      ${lib.optionalString config.virtualisation.containers.ociSeccompBpfHook.enable
+        ''"${config.boot.kernelPackages.oci-seccomp-bpf-hook}",''}
+      ]
 
       ${optionalString (cfg.runtime != null) ''
       default_runtime = "${cfg.runtime}"
diff --git a/nixos/modules/virtualisation/docker.nix b/nixos/modules/virtualisation/docker.nix
index 689f664b676d5..b1415bf021dd9 100644
--- a/nixos/modules/virtualisation/docker.nix
+++ b/nixos/modules/virtualisation/docker.nix
@@ -157,6 +157,7 @@ in
 
       systemd.services.docker = {
         wantedBy = optional cfg.enableOnBoot "multi-user.target";
+        requires = [ "docker.socket" ];
         environment = proxy_env;
         serviceConfig = {
           Type = "notify";
diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix
index 103e689abae85..4b2adf4cc699b 100644
--- a/nixos/modules/virtualisation/lxd.nix
+++ b/nixos/modules/virtualisation/lxd.nix
@@ -5,13 +5,12 @@
 with lib;
 
 let
-
   cfg = config.virtualisation.lxd;
-  zfsCfg = config.boot.zfs;
-
-in
+in {
+  imports = [
+    (mkRemovedOptionModule [ "virtualisation" "lxd" "zfsPackage" ] "Override zfs in an overlay instead to override it globally")
+  ];
 
-{
   ###### interface
 
   options = {
@@ -51,18 +50,10 @@ in
         '';
       };
 
-      zfsPackage = mkOption {
-        type = types.package;
-        default = with pkgs; if zfsCfg.enableUnstable then zfsUnstable else zfs;
-        defaultText = "pkgs.zfs";
-        description = ''
-          The ZFS package to use with LXD.
-        '';
-      };
-
       zfsSupport = mkOption {
         type = types.bool;
-        default = false;
+        default = config.boot.zfs.enabled;
+        defaultText = "config.boot.zfs.enabled";
         description = ''
           Enables lxd to use zfs as a storage for containers.
 
@@ -87,7 +78,6 @@ in
   };
 
   ###### implementation
-
   config = mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
 
@@ -110,7 +100,7 @@ in
       wantedBy = [ "multi-user.target" ];
       after = [ "systemd-udev-settle.service" ];
 
-      path = lib.optional cfg.zfsSupport cfg.zfsPackage;
+      path = lib.optional config.boot.zfs.enabled config.boot.zfs.package;
 
       preStart = ''
         mkdir -m 0755 -p /var/lib/lxc/rootfs
diff --git a/nixos/modules/virtualisation/nixos-containers.nix b/nixos/modules/virtualisation/nixos-containers.nix
index 7bec1b1ff26e8..f06977f88fc16 100644
--- a/nixos/modules/virtualisation/nixos-containers.nix
+++ b/nixos/modules/virtualisation/nixos-containers.nix
@@ -463,21 +463,15 @@ in
         { config, options, name, ... }:
         {
           options = {
-
             config = mkOption {
               description = ''
                 A specification of the desired configuration of this
                 container, as a NixOS module.
               '';
-              type = let
-                confPkgs = if config.pkgs == null then pkgs else config.pkgs;
-              in lib.mkOptionType {
+              type = lib.mkOptionType {
                 name = "Toplevel NixOS config";
-                merge = loc: defs: (import (confPkgs.path + "/nixos/lib/eval-config.nix") {
+                merge = loc: defs: (import "${toString config.nixpkgs}/nixos/lib/eval-config.nix" {
                   inherit system;
-                  pkgs = confPkgs;
-                  baseModules = import (confPkgs.path + "/nixos/modules/module-list.nix");
-                  inherit (confPkgs) lib;
                   modules =
                     let
                       extraConfig = {
@@ -526,12 +520,18 @@ in
               '';
             };
 
-            pkgs = mkOption {
-              type = types.nullOr types.attrs;
-              default = null;
-              example = literalExample "pkgs";
+            nixpkgs = mkOption {
+              type = types.path;
+              default = pkgs.path;
+              defaultText = "pkgs.path";
               description = ''
-                Customise which nixpkgs to use for this container.
+                A path to the nixpkgs that provide the modules, pkgs and lib for evaluating the container.
+
+                To only change the <literal>pkgs</literal> argument used inside the container modules,
+                set the <literal>nixpkgs.*</literal> options in the container <option>config</option>.
+                Setting <literal>config.nixpkgs.pkgs = pkgs</literal> speeds up the container evaluation
+                by reusing the system pkgs, but the <literal>nixpkgs.config</literal> option in the
+                container config is ignored in this case.
               '';
             };
 
@@ -672,14 +672,31 @@ in
               '';
             };
 
+            # Removed option. See `checkAssertion` below for the accompanying error message.
+            pkgs = mkOption { visible = false; };
           } // networkOptions;
 
-          config = mkMerge
-            [
-              (mkIf options.config.isDefined {
-                path = config.config.system.build.toplevel;
-              })
-            ];
+          config = let
+            # Throw an error when removed option `pkgs` is used.
+            # Because this is a submodule we cannot use `mkRemovedOptionModule` or option `assertions`.
+            optionPath = "containers.${name}.pkgs";
+            files = showFiles options.pkgs.files;
+            checkAssertion = if options.pkgs.isDefined then throw ''
+              The option definition `${optionPath}' in ${files} no longer has any effect; please remove it.
+
+              Alternatively, you can use the following options:
+              - containers.${name}.nixpkgs
+                This sets the nixpkgs (and thereby the modules, pkgs and lib) that
+                are used for evaluating the container.
+
+              - containers.${name}.config.nixpkgs.pkgs
+                This only sets the `pkgs` argument used inside the container modules.
+            ''
+            else null;
+          in {
+            path = builtins.seq checkAssertion
+              mkIf options.config.isDefined config.config.system.build.toplevel;
+          };
         }));
 
       default = {};
diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix
index 5ad647769bbd9..5b57ca860da29 100644
--- a/nixos/modules/virtualisation/xen-dom0.nix
+++ b/nixos/modules/virtualisation/xen-dom0.nix
@@ -57,7 +57,8 @@ in
 
     virtualisation.xen.bootParams =
       mkOption {
-        default = "";
+        default = [];
+        type = types.listOf types.str;
         description =
           ''
             Parameters passed to the Xen hypervisor at boot time.
@@ -68,6 +69,7 @@ in
       mkOption {
         default = 0;
         example = 512;
+        type = types.addCheck types.int (n: n >= 0);
         description =
           ''
             Amount of memory (in MiB) allocated to Domain 0 on boot.
@@ -78,6 +80,7 @@ in
     virtualisation.xen.bridge = {
         name = mkOption {
           default = "xenbr0";
+          type = types.str;
           description = ''
               Name of bridge the Xen domUs connect to.
             '';
diff --git a/nixos/tests/agda.nix b/nixos/tests/agda.nix
index bbdeb7395aa7e..3773907cff557 100644
--- a/nixos/tests/agda.nix
+++ b/nixos/tests/agda.nix
@@ -23,6 +23,13 @@ in
   };
 
   testScript = ''
+    assert (
+        "${pkgs.agdaPackages.lib.interfaceFile "Everything.agda"}" == "Everything.agdai"
+    ), "wrong interface file for Everything.agda"
+    assert (
+        "${pkgs.agdaPackages.lib.interfaceFile "tmp/Everything.agda.md"}" == "tmp/Everything.agdai"
+    ), "wrong interface file for tmp/Everything.agda.md"
+
     # Minimal script that typechecks
     machine.succeed("touch TestEmpty.agda")
     machine.succeed("agda TestEmpty.agda")
@@ -36,6 +43,10 @@ in
         "cp ${hello-world} HelloWorld.agda"
     )
     machine.succeed("agda -l standard-library -i . -c HelloWorld.agda")
+    # Check execution
+    assert "Hello World!" in machine.succeed(
+        "./HelloWorld"
+    ), "HelloWorld does not run properly"
   '';
 }
 )
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index d267ddeb4cf4e..444580bc0bed6 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -189,6 +189,7 @@ in
   kernel-latest = handleTest ./kernel-latest.nix {};
   kernel-lts = handleTest ./kernel-lts.nix {};
   kernel-testing = handleTest ./kernel-testing.nix {};
+  kernel-latest-ath-user-regd = handleTest ./kernel-latest-ath-user-regd.nix {};
   keycloak = discoverTests (import ./keycloak.nix);
   keymap = handleTest ./keymap.nix {};
   knot = handleTest ./knot.nix {};
@@ -344,7 +345,6 @@ in
   sanoid = handleTest ./sanoid.nix {};
   sbt = handleTest ./sbt.nix {};
   sbt-extras = handleTest ./sbt-extras.nix {};
-  scala = handleTest ./scala.nix {};
   sddm = handleTest ./sddm.nix {};
   searx = handleTest ./searx.nix {};
   service-runner = handleTest ./service-runner.nix {};
@@ -413,6 +413,7 @@ in
   vector = handleTest ./vector.nix {};
   victoriametrics = handleTest ./victoriametrics.nix {};
   virtualbox = handleTestOn ["x86_64-linux"] ./virtualbox.nix {};
+  vscodium = handleTest ./vscodium.nix {};
   wasabibackend = handleTest ./wasabibackend.nix {};
   wireguard = handleTest ./wireguard {};
   wordpress = handleTest ./wordpress.nix {};
diff --git a/nixos/tests/containers-custom-pkgs.nix b/nixos/tests/containers-custom-pkgs.nix
index 397a4a905e6d9..1412c32bfb5f1 100644
--- a/nixos/tests/containers-custom-pkgs.nix
+++ b/nixos/tests/containers-custom-pkgs.nix
@@ -1,42 +1,34 @@
-# Test for NixOS' container support.
-
 import ./make-test-python.nix ({ pkgs, lib, ...} : let
 
-  customPkgs = pkgs // {
-    hello = pkgs.hello.overrideAttrs(old: {
-      name = "custom-hello";
+  customPkgs = pkgs.appendOverlays [ (self: super: {
+    hello = super.hello.overrideAttrs (old: {
+       name = "custom-hello";
     });
-  };
+  }) ];
 
 in {
-  name = "containers-hosts";
+  name = "containers-custom-pkgs";
   meta = with lib.maintainers; {
-    maintainers = [ adisbladis ];
+    maintainers = [ adisbladis earvstedt ];
   };
 
-  machine =
-    { ... }:
-    {
-      virtualisation.memorySize = 256;
-      virtualisation.vlans = [];
+  machine = { config, ... }: {
+    assertions = let
+      helloName = (builtins.head config.containers.test.config.system.extraDependencies).name;
+    in [ {
+      assertion = helloName == "custom-hello";
+      message = "Unexpected value: ${helloName}";
+    } ];
 
-      containers.simple = {
-        autoStart = true;
-        pkgs = customPkgs;
-        config = {pkgs, config, ... }: {
-          environment.systemPackages = [
-            pkgs.hello
-          ];
-        };
+    containers.test = {
+      autoStart = true;
+      config = { pkgs, config, ... }: {
+        nixpkgs.pkgs = customPkgs;
+        system.extraDependencies = [ pkgs.hello ];
       };
-
     };
+  };
 
-  testScript = ''
-    start_all()
-    machine.wait_for_unit("default.target")
-    machine.succeed(
-        "test $(nixos-container run simple -- readlink -f /run/current-system/sw/bin/hello) = ${customPkgs.hello}/bin/hello"
-    )
-  '';
+  # This test only consists of evaluating the test machine
+  testScript = "";
 })
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 5fa4704d02b6a..789add331b793 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -76,8 +76,8 @@ let
       def assemble_qemu_flags():
           flags = "-cpu max"
           ${if system == "x86_64-linux"
-            then ''flags += " -m 768"''
-            else ''flags += " -m 512 -enable-kvm -machine virt,gic-version=host"''
+            then ''flags += " -m 1024"''
+            else ''flags += " -m 768 -enable-kvm -machine virt,gic-version=host"''
           }
           return flags
 
diff --git a/nixos/tests/kernel-latest-ath-user-regd.nix b/nixos/tests/kernel-latest-ath-user-regd.nix
new file mode 100644
index 0000000000000..11a3959e692e9
--- /dev/null
+++ b/nixos/tests/kernel-latest-ath-user-regd.nix
@@ -0,0 +1,17 @@
+import ./make-test-python.nix ({ pkgs, ...} : {
+  name = "kernel-latest-ath-user-regd";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ veehaitch ];
+  };
+
+  machine = { pkgs, ... }:
+    {
+      boot.kernelPackages = pkgs.linuxPackages_latest;
+      networking.wireless.athUserRegulatoryDomain = true;
+    };
+
+  testScript =
+    ''
+      assert "CONFIG_ATH_USER_REGD=y" in machine.succeed("zcat /proc/config.gz")
+    '';
+})
diff --git a/nixos/tests/power-profiles-daemon.nix b/nixos/tests/power-profiles-daemon.nix
new file mode 100644
index 0000000000000..e073677bee9d7
--- /dev/null
+++ b/nixos/tests/power-profiles-daemon.nix
@@ -0,0 +1,45 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+
+{
+  name = "power-profiles-daemon";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ mvnetbiz ];
+  };
+  machine = { pkgs, ... }: {
+    services.power-profiles-daemon.enable = true;
+    environment.systemPackages = [ pkgs.glib ];
+  };
+
+  testScript = ''
+    def get_profile():
+        return machine.succeed(
+            """gdbus call --system --dest net.hadess.PowerProfiles --object-path /net/hadess/PowerProfiles \
+    --method org.freedesktop.DBus.Properties.Get 'net.hadess.PowerProfiles' 'ActiveProfile'
+    """
+        )
+
+
+    def set_profile(profile):
+        return machine.succeed(
+            """gdbus call --system --dest net.hadess.PowerProfiles --object-path /net/hadess/PowerProfiles \
+    --method org.freedesktop.DBus.Properties.Set 'net.hadess.PowerProfiles' 'ActiveProfile' "<'{profile}'>"
+    """.format(
+                profile=profile
+            )
+        )
+
+
+    machine.wait_for_unit("multi-user.target")
+
+    set_profile("power-saver")
+    profile = get_profile()
+    if not "power-saver" in profile:
+        raise Exception("Unable to set power-saver profile")
+
+
+    set_profile("balanced")
+    profile = get_profile()
+    if not "balanced" in profile:
+        raise Exception("Unable to set balanced profile")
+  '';
+})
diff --git a/nixos/tests/scala.nix b/nixos/tests/scala.nix
deleted file mode 100644
index 4fc3f8aa7b0ad..0000000000000
--- a/nixos/tests/scala.nix
+++ /dev/null
@@ -1,33 +0,0 @@
-{ system ? builtins.currentSystem,
-  config ? {},
-  pkgs ? import ../.. { inherit system config; }
-}:
-
-with pkgs.lib;
-
-let
-  common = name: package: (import ./make-test-python.nix ({
-    inherit name;
-    meta = with pkgs.lib.maintainers; {
-      maintainers = [ nequissimus ];
-    };
-
-    nodes = {
-      scala = { ... }: {
-        environment.systemPackages = [ package ];
-      };
-    };
-
-    testScript = ''
-      start_all()
-
-      scala.succeed("scalac -version 2>&1 | grep '^Scala compiler version ${package.version}'")
-    '';
-  }) { inherit system; });
-
-in with pkgs; {
-  scala_2_10  = common "scala_2_10"  scala_2_10;
-  scala_2_11  = common "scala_2_11"  scala_2_11;
-  scala_2_12  = common "scala_2_12"  scala_2_12;
-  scala_2_13  = common "scala_2_13"  scala_2_13;
-}
diff --git a/nixos/tests/snapcast.nix b/nixos/tests/snapcast.nix
index a69b7afe99da7..2fef636251401 100644
--- a/nixos/tests/snapcast.nix
+++ b/nixos/tests/snapcast.nix
@@ -4,6 +4,8 @@ let
   port = 10004;
   tcpPort = 10005;
   httpPort = 10080;
+  tcpStreamPort = 10006;
+  bufferSize = 742;
 in {
   name = "snapcast";
   meta = with pkgs.lib.maintainers; {
@@ -17,18 +19,27 @@ in {
         port = port;
         tcp.port = tcpPort;
         http.port = httpPort;
+        buffer = bufferSize;
         streams = {
           mpd = {
             type = "pipe";
             location = "/run/snapserver/mpd";
+            query.mode = "create";
           };
           bluetooth = {
             type = "pipe";
             location = "/run/snapserver/bluetooth";
           };
+          tcp = {
+            type = "tcp";
+            location = "127.0.0.1:${toString tcpStreamPort}";
+          };
         };
       };
     };
+    client = {
+      environment.systemPackages = [ pkgs.snapcast ];
+    };
   };
 
   testScript = ''
@@ -42,6 +53,7 @@ in {
     server.wait_until_succeeds("ss -ntl | grep -q ${toString port}")
     server.wait_until_succeeds("ss -ntl | grep -q ${toString tcpPort}")
     server.wait_until_succeeds("ss -ntl | grep -q ${toString httpPort}")
+    server.wait_until_succeeds("ss -ntl | grep -q ${toString tcpStreamPort}")
 
     with subtest("check that pipes are created"):
         server.succeed("test -p /run/snapserver/mpd")
@@ -54,5 +66,12 @@ in {
         server.succeed(
             "curl --fail http://localhost:${toString httpPort}/jsonrpc -d '{json.dumps(get_rpc_version)}'"
         )
+
+    with subtest("test a connection"):
+        client.execute("systemd-run snapclient -h server -p ${toString port}")
+        server.wait_until_succeeds(
+            "journalctl -o cat -u snapserver.service | grep -q 'Hello from'"
+        )
+        client.wait_until_succeeds("journalctl -o cat -u run-\* | grep -q ${toString bufferSize}")
   '';
 })
diff --git a/nixos/tests/vscodium.nix b/nixos/tests/vscodium.nix
new file mode 100644
index 0000000000000..398896540ee80
--- /dev/null
+++ b/nixos/tests/vscodium.nix
@@ -0,0 +1,62 @@
+import ./make-test-python.nix ({ pkgs, ...} :
+
+{
+  name = "vscodium";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ turion ];
+  };
+
+  machine = { ... }:
+
+  {
+    imports = [
+      ./common/user-account.nix
+      ./common/x11.nix
+    ];
+
+    virtualisation.memorySize = 2047;
+    services.xserver.enable = true;
+    test-support.displayManager.auto.user = "alice";
+    environment.systemPackages = with pkgs; [
+      vscodium
+    ];
+  };
+
+  enableOCR = true;
+
+  testScript = { nodes, ... }: ''
+    # Start up X
+    start_all()
+    machine.wait_for_x()
+
+    # Create a file that we'll open
+    machine.succeed("su - alice -c 'echo \"   Hello World\" > foo.txt'")
+
+    # It's one line long
+    assert "1 foo.txt" in machine.succeed(
+        "su - alice -c 'wc foo.txt -l'"
+    ), "File has wrong length"
+
+    # Start VSCodium with that file
+    machine.succeed("su - alice -c 'codium foo.txt' &")
+
+    # Wait for the window to appear
+    machine.wait_for_text("VSCodium")
+
+    # Add a line
+    machine.send_key("ret")
+
+    # Save file
+    machine.send_key("ctrl-s")
+
+    # Wait until the file has been saved
+    machine.sleep(1)
+
+    # Now the file is 2 lines long
+    assert "2 foo.txt" in machine.succeed(
+        "su - alice -c 'wc foo.txt -l'"
+    ), "File has wrong length"
+
+    machine.screenshot("VSCodium")
+  '';
+})