summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
authorJohn Ericson <John.Ericson@Obsidian.Systems>2018-03-19 21:47:56 -0400
committerJohn Ericson <John.Ericson@Obsidian.Systems>2018-03-19 21:47:56 -0400
commitb9a720c524908aaef0788697edf7eb2d8a75e53b (patch)
tree85e8c7a7c13bca63582a8defd209688d9e803135 /nixos/modules
parent192f4144b282a7f04695fcb79d84e8278ee6af8c (diff)
parent5675f17b0ed8be07752dedb1a9c42a20142f07e9 (diff)
downloadnixlib-b9a720c524908aaef0788697edf7eb2d8a75e53b.tar
nixlib-b9a720c524908aaef0788697edf7eb2d8a75e53b.tar.gz
nixlib-b9a720c524908aaef0788697edf7eb2d8a75e53b.tar.bz2
nixlib-b9a720c524908aaef0788697edf7eb2d8a75e53b.tar.lz
nixlib-b9a720c524908aaef0788697edf7eb2d8a75e53b.tar.xz
nixlib-b9a720c524908aaef0788697edf7eb2d8a75e53b.tar.zst
nixlib-b9a720c524908aaef0788697edf7eb2d8a75e53b.zip
Merge remote-tracking branch 'upstream/master' into fix-cross-jobs
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/config/i18n.nix2
-rw-r--r--nixos/modules/config/no-x-libs.nix1
-rw-r--r--nixos/modules/config/zram.nix2
-rw-r--r--nixos/modules/hardware/video/amdgpu-pro.nix16
-rw-r--r--nixos/modules/hardware/video/nvidia.nix8
-rw-r--r--nixos/modules/installer/tools/nixos-rebuild.sh2
-rw-r--r--nixos/modules/installer/virtualbox-demo.nix2
-rw-r--r--nixos/modules/misc/nixpkgs.nix14
-rw-r--r--nixos/modules/misc/version.nix4
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/modules/profiles/demo.nix6
-rw-r--r--nixos/modules/programs/bash/bash.nix2
-rw-r--r--nixos/modules/programs/rootston.nix2
-rw-r--r--nixos/modules/programs/singularity.nix20
-rw-r--r--nixos/modules/programs/ssh.nix2
-rw-r--r--nixos/modules/programs/zsh/zsh.nix2
-rw-r--r--nixos/modules/security/audit.nix6
-rw-r--r--nixos/modules/security/sudo.nix6
-rw-r--r--nixos/modules/services/backup/borgbackup.nix580
-rw-r--r--nixos/modules/services/continuous-integration/buildkite-agent.nix2
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/default.nix7
-rw-r--r--nixos/modules/services/databases/4store-endpoint.nix2
-rw-r--r--nixos/modules/services/databases/4store.nix2
-rw-r--r--nixos/modules/services/editors/emacs.nix2
-rw-r--r--nixos/modules/services/hardware/udev.nix2
-rw-r--r--nixos/modules/services/misc/folding-at-home.nix2
-rw-r--r--nixos/modules/services/misc/geoip-updater.nix2
-rw-r--r--nixos/modules/services/misc/gitea.nix56
-rw-r--r--nixos/modules/services/misc/gitit.nix2
-rw-r--r--nixos/modules/services/misc/home-assistant.nix11
-rw-r--r--nixos/modules/services/misc/ihaskell.nix2
-rw-r--r--nixos/modules/services/misc/mesos-slave.nix2
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix2
-rw-r--r--nixos/modules/services/misc/nixos-manual.nix2
-rw-r--r--nixos/modules/services/misc/ssm-agent.nix2
-rw-r--r--nixos/modules/services/monitoring/apcupsd.nix2
-rw-r--r--nixos/modules/services/monitoring/grafana.nix14
-rw-r--r--nixos/modules/services/monitoring/smartd.nix2
-rw-r--r--nixos/modules/services/network-filesystems/xtreemfs.nix2
-rw-r--r--nixos/modules/services/network-filesystems/yandex-disk.nix4
-rw-r--r--nixos/modules/services/networking/amuled.nix2
-rw-r--r--nixos/modules/services/networking/firewall.nix2
-rw-r--r--nixos/modules/services/networking/flashpolicyd.nix2
-rw-r--r--nixos/modules/services/networking/nftables.nix2
-rw-r--r--nixos/modules/services/networking/nix-serve.nix2
-rw-r--r--nixos/modules/services/networking/rdnssd.nix2
-rw-r--r--nixos/modules/services/networking/resilio.nix7
-rw-r--r--nixos/modules/services/printing/cupsd.nix7
-rw-r--r--nixos/modules/services/security/torify.nix2
-rw-r--r--nixos/modules/services/security/torsocks.nix2
-rw-r--r--nixos/modules/services/torrent/transmission.nix2
-rw-r--r--nixos/modules/services/web-apps/tt-rss.nix2
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/owncloud.nix2
-rw-r--r--nixos/modules/services/web-servers/tomcat.nix380
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/slim.nix2
-rw-r--r--nixos/modules/services/x11/window-managers/default.nix1
-rw-r--r--nixos/modules/system/activation/activation-script.nix2
-rw-r--r--nixos/modules/system/boot/kexec.nix37
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix6
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl2
-rw-r--r--nixos/modules/system/boot/stage-1.nix54
-rw-r--r--nixos/modules/system/boot/stage-2.nix1
-rw-r--r--nixos/modules/system/boot/systemd.nix12
-rw-r--r--nixos/modules/tasks/kbd.nix2
-rw-r--r--nixos/modules/tasks/network-interfaces.nix14
-rw-r--r--nixos/modules/virtualisation/amazon-init.nix2
-rw-r--r--nixos/modules/virtualisation/azure-agent.nix2
-rw-r--r--nixos/modules/virtualisation/containers.nix4
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix14
-rw-r--r--nixos/modules/virtualisation/openvswitch.nix2
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix14
-rw-r--r--nixos/modules/virtualisation/xen-dom0.nix12
73 files changed, 1085 insertions, 319 deletions
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
index 46b22fc12854..6bf8c653e113 100644
--- a/nixos/modules/config/i18n.nix
+++ b/nixos/modules/config/i18n.nix
@@ -10,7 +10,7 @@ with lib;
     i18n = {
       glibcLocales = mkOption {
         type = types.path;
-        default = pkgs.glibcLocales.override {
+        default = pkgs.buildPackages.glibcLocales.override {
           allLocales = any (x: x == "all") config.i18n.supportedLocales;
           locales = config.i18n.supportedLocales;
         };
diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix
index d8980944adc0..b9d5b2b903e7 100644
--- a/nixos/modules/config/no-x-libs.nix
+++ b/nixos/modules/config/no-x-libs.nix
@@ -36,6 +36,7 @@ with lib;
       networkmanager-vpnc = pkgs.networkmanager-vpnc.override { withGnome = false; };
       networkmanager-iodine = pkgs.networkmanager-iodine.override { withGnome = false; };
       pinentry = pkgs.pinentry_ncurses;
+      gobjectIntrospection = pkgs.gobjectIntrospection.override { x11Support = false; };
     };
   };
 }
diff --git a/nixos/modules/config/zram.nix b/nixos/modules/config/zram.nix
index ad41ad4f3d7c..ae1b0a6c8e11 100644
--- a/nixos/modules/config/zram.nix
+++ b/nixos/modules/config/zram.nix
@@ -93,7 +93,7 @@ in
             serviceConfig = {
               Type = "oneshot";
               RemainAfterExit = true;
-              ExecStop = "${pkgs.stdenv.shell} -c 'echo 1 > /sys/class/block/${dev}/reset'";
+              ExecStop = "${pkgs.runtimeShell} -c 'echo 1 > /sys/class/block/${dev}/reset'";
             };
             script = ''
               set -u
diff --git a/nixos/modules/hardware/video/amdgpu-pro.nix b/nixos/modules/hardware/video/amdgpu-pro.nix
index 5cc96d8bd074..50af022b93c8 100644
--- a/nixos/modules/hardware/video/amdgpu-pro.nix
+++ b/nixos/modules/hardware/video/amdgpu-pro.nix
@@ -15,13 +15,19 @@ let
 
   opengl = config.hardware.opengl;
 
+  kernel = pkgs.linux_4_9.override {
+    extraConfig = ''
+      KALLSYMS_ALL y
+    '';
+  };
+
 in
 
 {
 
   config = mkIf enabled {
 
-    nixpkgs.config.xorg.abiCompat = "1.18";
+    nixpkgs.config.xorg.abiCompat = "1.19";
 
     services.xserver.drivers = singleton
       { name = "amdgpu"; modules = [ package ]; libPath = [ package ]; };
@@ -31,6 +37,9 @@ in
 
     boot.extraModulePackages = [ package ];
 
+    boot.kernelPackages =
+      pkgs.recurseIntoAttrs (pkgs.linuxPackagesFor kernel);
+
     boot.blacklistedKernelModules = [ "radeon" ];
 
     hardware.firmware = [ package ];
@@ -38,10 +47,15 @@ in
     system.activationScripts.setup-amdgpu-pro = ''
       mkdir -p /run/lib
       ln -sfn ${package}/lib ${package.libCompatDir}
+      ln -sfn ${package} /run/amdgpu-pro
     '' + optionalString opengl.driSupport32Bit ''
       ln -sfn ${package32}/lib ${package32.libCompatDir}
     '';
 
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "KALLSYMS_ALL")
+    ];
+
     environment.etc = {
       "amd/amdrc".source = package + "/etc/amd/amdrc";
       "amd/amdapfxx.blb".source = package + "/etc/amd/amdapfxx.blb";
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index 50c085dd7ee2..eafc9869315a 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -75,10 +75,10 @@ in
     # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
     services.udev.extraRules =
       ''
-        KERNEL=="nvidia", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidiactl c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 255'"
-        KERNEL=="nvidia_modeset", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidia-modeset c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 254'"
-        KERNEL=="card*", SUBSYSTEM=="drm", DRIVERS=="nvidia", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidia%n c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) %n'"
-        KERNEL=="nvidia_uvm", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidia-uvm c $(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0'"
+        KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 255'"
+        KERNEL=="nvidia_modeset", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-modeset c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 254'"
+        KERNEL=="card*", SUBSYSTEM=="drm", DRIVERS=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia%n c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) %n'"
+        KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm c $(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0'"
       '';
 
     boot.blacklistedKernelModules = [ "nouveau" "nvidiafb" ];
diff --git a/nixos/modules/installer/tools/nixos-rebuild.sh b/nixos/modules/installer/tools/nixos-rebuild.sh
index 9ede74a54cd7..2af73519bc52 100644
--- a/nixos/modules/installer/tools/nixos-rebuild.sh
+++ b/nixos/modules/installer/tools/nixos-rebuild.sh
@@ -382,6 +382,6 @@ fi
 if [ "$action" = build-vm ]; then
     cat >&2 <<EOF
 
-Done.  The virtual machine can be started by running $(echo $pathToConfig/bin/run-*-vm).
+Done.  The virtual machine can be started by running $(echo $pathToConfig/bin/run-*-vm)
 EOF
 fi
diff --git a/nixos/modules/installer/virtualbox-demo.nix b/nixos/modules/installer/virtualbox-demo.nix
index 5316cfce906b..13a0d7f4f6ee 100644
--- a/nixos/modules/installer/virtualbox-demo.nix
+++ b/nixos/modules/installer/virtualbox-demo.nix
@@ -19,4 +19,6 @@ with lib;
   # Add some more video drivers to give X11 a shot at working in
   # VMware and QEMU.
   services.xserver.videoDrivers = mkOverride 40 [ "virtualbox" "vmware" "cirrus" "vesa" "modesetting" ];
+
+  powerManagement.enable = false;
 }
diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix
index 11bd148d5dee..9217250eec29 100644
--- a/nixos/modules/misc/nixpkgs.nix
+++ b/nixos/modules/misc/nixpkgs.nix
@@ -61,7 +61,7 @@ in
             inherit (config.nixpkgs) config overlays system;
           }
         '';
-      default = import ../../.. { inherit (cfg) config overlays system; };
+      default = import ../../.. { inherit (cfg) config overlays system crossSystem; };
       type = pkgsType;
       example = literalExample ''import <nixpkgs> {}'';
       description = ''
@@ -130,6 +130,18 @@ in
       '';
     };
 
+    crossSystem = mkOption {
+      type = types.nullOr types.attrs;
+      default = null;
+      description = ''
+        The description of the system we're cross-compiling to, or null
+        if this isn't a cross-compile. See the description of the
+        crossSystem argument in the nixpkgs manual.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
+      '';
+    };
+
     system = mkOption {
       type = types.str;
       example = "i686-linux";
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index 6af584250a70..b8f0a223c910 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -85,8 +85,8 @@ in
       revision      = mkIf (pathIsDirectory gitRepo) (mkDefault            gitCommitId);
       versionSuffix = mkIf (pathIsDirectory gitRepo) (mkDefault (".git." + gitCommitId));
 
-      # Note: code names must only increase in alphabetical order.
-      codeName = "Impala";
+      # Note: the first letter is bumped on every release.  It's an animal.
+      codeName = "Jellyfish";
     };
 
     # Generate /etc/os-release.  See
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index e7f28c670bed..e0c0ec2711b8 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -104,6 +104,7 @@
   ./programs/shadow.nix
   ./programs/shell.nix
   ./programs/spacefm.nix
+  ./programs/singularity.nix
   ./programs/ssh.nix
   ./programs/ssmtp.nix
   ./programs/sysdig.nix
@@ -159,6 +160,7 @@
   ./services/audio/ympd.nix
   ./services/backup/almir.nix
   ./services/backup/bacula.nix
+  ./services/backup/borgbackup.nix
   ./services/backup/crashplan.nix
   ./services/backup/crashplan-small-business.nix
   ./services/backup/mysql-backup.nix
diff --git a/nixos/modules/profiles/demo.nix b/nixos/modules/profiles/demo.nix
index ef6fd77b5f8d..c3ee6e98371e 100644
--- a/nixos/modules/profiles/demo.nix
+++ b/nixos/modules/profiles/demo.nix
@@ -10,4 +10,10 @@
       password = "demo";
       uid = 1000;
     };
+
+  services.xserver.displayManager.sddm.autoLogin = {
+    enable = true;
+    relogin = true;
+    user = "demo";
+  };
 }
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
index 1a62f04972df..c0967316c0c7 100644
--- a/nixos/modules/programs/bash/bash.nix
+++ b/nixos/modules/programs/bash/bash.nix
@@ -126,7 +126,7 @@ in
     programs.bash = {
 
       shellInit = ''
-        . ${config.system.build.setEnvironment}
+        ${config.system.build.setEnvironment.text}
 
         ${cfge.shellInit}
       '';
diff --git a/nixos/modules/programs/rootston.nix b/nixos/modules/programs/rootston.nix
index 1946b1db657b..842d9e6cfb48 100644
--- a/nixos/modules/programs/rootston.nix
+++ b/nixos/modules/programs/rootston.nix
@@ -6,7 +6,7 @@ let
   cfg = config.programs.rootston;
 
   rootstonWrapped = pkgs.writeScriptBin "rootston" ''
-    #! ${pkgs.stdenv.shell}
+    #! ${pkgs.runtimeShell}
     if [[ "$#" -ge 1 ]]; then
       exec ${pkgs.rootston}/bin/rootston "$@"
     else
diff --git a/nixos/modules/programs/singularity.nix b/nixos/modules/programs/singularity.nix
new file mode 100644
index 000000000000..86153d933855
--- /dev/null
+++ b/nixos/modules/programs/singularity.nix
@@ -0,0 +1,20 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.programs.singularity;
+in {
+  options.programs.singularity = {
+    enable = mkEnableOption "Singularity";
+  };
+
+  config = mkIf cfg.enable {
+      environment.systemPackages = [ pkgs.singularity ];
+      systemd.tmpfiles.rules = [ "d /var/singularity/mnt/session 0770 root root -"
+                                 "d /var/singularity/mnt/final 0770 root root -"
+                                 "d /var/singularity/mnt/overlay 0770 root root -"
+                                 "d /var/singularity/mnt/container 0770 root root -"
+                                 "d /var/singularity/mnt/source 0770 root root -"];
+  };
+
+}
diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix
index 0935bf0cae71..36289080a82a 100644
--- a/nixos/modules/programs/ssh.nix
+++ b/nixos/modules/programs/ssh.nix
@@ -13,7 +13,7 @@ let
 
   askPasswordWrapper = pkgs.writeScript "ssh-askpass-wrapper"
     ''
-      #! ${pkgs.stdenv.shell} -e
+      #! ${pkgs.runtimeShell} -e
       export DISPLAY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^DISPLAY=\(.*\)/\1/; t; d')"
       exec ${askPassword}
     '';
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index 5102bfef0325..f689250dc61f 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -108,7 +108,7 @@ in
         if [ -n "$__ETC_ZSHENV_SOURCED" ]; then return; fi
         export __ETC_ZSHENV_SOURCED=1
 
-        . ${config.system.build.setEnvironment}
+        ${config.system.build.setEnvironment.text}
 
         ${cfge.shellInit}
 
diff --git a/nixos/modules/security/audit.nix b/nixos/modules/security/audit.nix
index 7ac21fd96507..2b22bdd9f0ae 100644
--- a/nixos/modules/security/audit.nix
+++ b/nixos/modules/security/audit.nix
@@ -13,7 +13,7 @@ let
   };
 
   disableScript = pkgs.writeScript "audit-disable" ''
-    #!${pkgs.stdenv.shell} -eu
+    #!${pkgs.runtimeShell} -eu
     # Explicitly disable everything, as otherwise journald might start it.
     auditctl -D
     auditctl -e 0 -a task,never
@@ -23,7 +23,7 @@ let
   # put in the store like this. At the same time, it doesn't feel like a huge deal and working
   # around that is a pain so I'm leaving it like this for now.
   startScript = pkgs.writeScript "audit-start" ''
-    #!${pkgs.stdenv.shell} -eu
+    #!${pkgs.runtimeShell} -eu
     # Clear out any rules we may start with
     auditctl -D
 
@@ -43,7 +43,7 @@ let
   '';
 
   stopScript = pkgs.writeScript "audit-stop" ''
-    #!${pkgs.stdenv.shell} -eu
+    #!${pkgs.runtimeShell} -eu
     # Clear the rules
     auditctl -D
 
diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix
index a57f14bb5ae1..24283e1d6165 100644
--- a/nixos/modules/security/sudo.nix
+++ b/nixos/modules/security/sudo.nix
@@ -47,8 +47,8 @@ in
       default = true;
       description =
         ''
-          Whether users of the <code>wheel</code> group can execute
-          commands as super user without entering a password.
+          Whether users of the <code>wheel</code> group must
+          provide a password to run commands as super user via <command>sudo</command>.
         '';
       };
 
@@ -215,7 +215,7 @@ in
           { src = pkgs.writeText "sudoers-in" cfg.configFile; }
           # Make sure that the sudoers file is syntactically valid.
           # (currently disabled - NIXOS-66)
-          "${pkgs.sudo}/sbin/visudo -f $src -c && cp $src $out";
+          "${pkgs.buildPackages.sudo}/sbin/visudo -f $src -c && cp $src $out";
         target = "sudoers";
         mode = "0440";
       };
diff --git a/nixos/modules/services/backup/borgbackup.nix b/nixos/modules/services/backup/borgbackup.nix
new file mode 100644
index 000000000000..1b730e0c2b76
--- /dev/null
+++ b/nixos/modules/services/backup/borgbackup.nix
@@ -0,0 +1,580 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  isLocalPath = x:
+    builtins.substring 0 1 x == "/"      # absolute path
+    || builtins.substring 0 1 x == "."   # relative path
+    || builtins.match "[.*:.*]" == null; # not machine:path
+ 
+  mkExcludeFile = cfg:
+    # Write each exclude pattern to a new line
+    pkgs.writeText "excludefile" (concatStringsSep "\n" cfg.exclude);
+
+  mkKeepArgs = cfg:
+    # If cfg.prune.keep e.g. has a yearly attribute,
+    # its content is passed on as --keep-yearly
+    concatStringsSep " "
+      (mapAttrsToList (x: y: "--keep-${x}=${toString y}") cfg.prune.keep);
+
+  mkBackupScript = cfg: ''
+    on_exit()
+    {
+      exitStatus=$?
+      # Reset the EXIT handler, or else we're called again on 'exit' below
+      trap - EXIT
+      ${cfg.postHook}
+      exit $exitStatus
+    }
+    trap 'on_exit' INT TERM QUIT EXIT
+
+    archiveName="${cfg.archiveBaseName}-$(date ${cfg.dateFormat})"
+    archiveSuffix="${optionalString cfg.appendFailedSuffix ".failed"}"
+    ${cfg.preHook}
+  '' + optionalString cfg.doInit ''
+    # Run borg init if the repo doesn't exist yet
+    if ! borg list > /dev/null; then
+      borg init \
+        --encryption ${cfg.encryption.mode} \
+        $extraInitArgs
+      ${cfg.postInit}
+    fi
+  '' + ''
+    borg create \
+      --compression ${cfg.compression} \
+      --exclude-from ${mkExcludeFile cfg} \
+      $extraCreateArgs \
+      "::$archiveName$archiveSuffix" \
+      ${escapeShellArgs cfg.paths}
+  '' + optionalString cfg.appendFailedSuffix ''
+    borg rename "::$archiveName$archiveSuffix" "$archiveName"
+  '' + ''
+    ${cfg.postCreate}
+  '' + optionalString (cfg.prune.keep != { }) ''
+    borg prune \
+      ${mkKeepArgs cfg} \
+      --prefix ${escapeShellArg cfg.prune.prefix} \
+      $extraPruneArgs
+    ${cfg.postPrune}
+  '';
+
+  mkPassEnv = cfg: with cfg.encryption;
+    if passCommand != null then
+      { BORG_PASSCOMMAND = passCommand; }
+    else if passphrase != null then
+      { BORG_PASSPHRASE = passphrase; }
+    else { };
+
+  mkBackupService = name: cfg: 
+    let
+      userHome = config.users.users.${cfg.user}.home;
+    in nameValuePair "borgbackup-job-${name}" {
+      description = "BorgBackup job ${name}";
+      path = with pkgs; [
+        borgbackup openssh
+      ];
+      script = mkBackupScript cfg;
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        # Only run when no other process is using CPU or disk
+        CPUSchedulingPolicy = "idle";
+        IOSchedulingClass = "idle";
+        ProtectSystem = "strict";
+        ReadWritePaths =
+          [ "${userHome}/.config/borg" "${userHome}/.cache/borg" ]
+          # Borg needs write access to repo if it is not remote
+          ++ optional (isLocalPath cfg.repo) cfg.repo;
+        PrivateTmp = true;
+      };
+      environment = {
+        BORG_REPO = cfg.repo;
+        inherit (cfg) extraInitArgs extraCreateArgs extraPruneArgs;
+      } // (mkPassEnv cfg) // cfg.environment;
+      inherit (cfg) startAt;
+    };
+
+  # Paths listed in ReadWritePaths must exist before service is started
+  mkActivationScript = name: cfg:
+    let
+      install = "install -o ${cfg.user} -g ${cfg.group}";
+    in
+      nameValuePair "borgbackup-job-${name}" (stringAfter [ "users" ] (''
+        # Eensure that the home directory already exists
+        # We can't assert createHome == true because that's not the case for root
+        cd "${config.users.users.${cfg.user}.home}"                                                                                                         
+        ${install} -d .config/borg
+        ${install} -d .cache/borg
+      '' + optionalString (isLocalPath cfg.repo) ''
+        ${install} -d ${escapeShellArg cfg.repo}
+      ''));
+
+  mkPassAssertion = name: cfg: {
+    assertion = with cfg.encryption;
+      mode != "none" -> passCommand != null || passphrase != null;
+    message =
+      "passCommand or passphrase has to be specified because"
+      + '' borgbackup.jobs.${name}.encryption != "none"'';
+  };
+
+  mkRepoService = name: cfg:
+    nameValuePair "borgbackup-repo-${name}" {
+      description = "Create BorgBackup repository ${name} directory";
+      script = ''
+        mkdir -p ${escapeShellArg cfg.path}
+        chown ${cfg.user}:${cfg.group} ${escapeShellArg cfg.path}
+      '';
+      serviceConfig = {
+        # The service's only task is to ensure that the specified path exists
+        Type = "oneshot";
+      };
+      wantedBy = [ "multi-user.target" ];
+    };
+
+  mkAuthorizedKey = cfg: appendOnly: key:
+    let
+      # Because of the following line, clients do not need to specify an absolute repo path
+      cdCommand = "cd ${escapeShellArg cfg.path}";
+      restrictedArg = "--restrict-to-${if cfg.allowSubRepos then "path" else "repository"} .";
+      appendOnlyArg = optionalString appendOnly "--append-only";
+      quotaArg = optionalString (cfg.quota != null) "--storage-quota ${cfg.quota}";
+      serveCommand = "borg serve ${restrictedArg} ${appendOnlyArg} ${quotaArg}";
+    in
+      ''command="${cdCommand} && ${serveCommand}",restrict ${key}'';
+
+  mkUsersConfig = name: cfg: {
+    users.${cfg.user} = {
+      openssh.authorizedKeys.keys =
+        (map (mkAuthorizedKey cfg false) cfg.authorizedKeys
+        ++ map (mkAuthorizedKey cfg true) cfg.authorizedKeysAppendOnly);
+      useDefaultShell = true;
+    };
+    groups.${cfg.group} = { };
+  };
+
+  mkKeysAssertion = name: cfg: {
+    assertion = cfg.authorizedKeys != [ ] || cfg.authorizedKeysAppendOnly != [ ];
+    message =
+      "borgbackup.repos.${name} does not make sense"
+      + " without at least one public key";
+  };
+
+in {
+  meta.maintainers = with maintainers; [ dotlambda ];
+
+  ###### interface
+
+  options.services.borgbackup.jobs = mkOption {
+    description = "Deduplicating backups using BorgBackup.";
+    default = { };
+    example = literalExample ''
+      {
+        rootBackup = {
+          paths = "/";
+          exclude = [ "/nix" ];
+          repo = "/path/to/local/repo";
+          encryption = {
+            mode = "repokey";
+            passphrase = "secret";
+          };
+          compression = "auto,lzma";
+          startAt = "weekly";
+        };
+      }
+    '';
+    type = types.attrsOf (types.submodule (let globalConfig = config; in
+      { name, config, ... }: {
+        options = {
+
+          paths = mkOption {
+            type = with types; either path (nonEmptyListOf path);
+            description = "Path(s) to back up.";
+            example = "/home/user";
+            apply = x: if isList x then x else [ x ];
+          };
+
+          repo = mkOption {
+            type = types.str;
+            description = "Remote or local repository to back up to.";
+            example = "user@machine:/path/to/repo";
+          };
+
+          archiveBaseName = mkOption {
+            type = types.strMatching "[^/{}]+";
+            default = "${globalConfig.networking.hostName}-${name}";
+            defaultText = "\${config.networking.hostName}-<name>";
+            description = ''
+              How to name the created archives. A timestamp, whose format is
+              determined by <option>dateFormat</option>, will be appended. The full
+              name can be modified at runtime (<literal>$archiveName</literal>).
+              Placeholders like <literal>{hostname}</literal> must not be used.
+            '';
+          };
+
+          dateFormat = mkOption {
+            type = types.str;
+            description = ''
+              Arguments passed to <command>date</command>
+              to create a timestamp suffix for the archive name.
+            '';
+            default = "+%Y-%m-%dT%H:%M:%S";
+            example = "-u +%s";
+          };
+
+          startAt = mkOption {
+            type = with types; either str (listOf str);
+            default = "daily";
+            description = ''
+              When or how often the backup should run.
+              Must be in the format described in
+              <citerefentry><refentrytitle>systemd.time</refentrytitle>
+              <manvolnum>7</manvolnum></citerefentry>.
+              If you do not want the backup to start
+              automatically, use <literal>[ ]</literal>.
+            '';
+          };
+
+          user = mkOption {
+            type = types.str;
+            description = ''
+              The user <command>borg</command> is run as.
+              User or group need read permission
+              for the specified <option>paths</option>.
+            '';
+            default = "root";
+          };
+
+          group = mkOption {
+            type = types.str;
+            description = ''
+              The group borg is run as. User or group needs read permission
+              for the specified <option>paths</option>.
+            '';
+            default = "root";
+          };
+
+          encryption.mode = mkOption {
+            type = types.enum [
+              "repokey" "keyfile"
+              "repokey-blake2" "keyfile-blake2"
+              "authenticated" "authenticated-blake2"
+              "none"
+            ];
+            description = ''
+              Encryption mode to use. Setting a mode
+              other than <literal>"none"</literal> requires
+              you to specify a <option>passCommand</option>
+              or a <option>passphrase</option>.
+            '';
+          };
+
+          encryption.passCommand = mkOption {
+            type = with types; nullOr str;
+            description = ''
+              A command which prints the passphrase to stdout.
+              Mutually exclusive with <option>passphrase</option>.
+            '';
+            default = null;
+            example = "cat /path/to/passphrase_file";
+          };
+
+          encryption.passphrase = mkOption {
+            type = with types; nullOr str;
+            description = ''
+              The passphrase the backups are encrypted with.
+              Mutually exclusive with <option>passCommand</option>.
+              If you do not want the passphrase to be stored in the
+              world-readable Nix store, use <option>passCommand</option>.
+            '';
+            default = null;
+          };
+
+          compression = mkOption {
+            # "auto" is optional,
+            # compression mode must be given,
+            # compression level is optional
+            type = types.strMatching "none|(auto,)?(lz4|zstd|zlib|lzma)(,[[:digit:]]{1,2})?";
+            description = ''
+              Compression method to use. Refer to
+              <command>borg help compression</command>
+              for all available options.
+            '';
+            default = "lz4";
+            example = "auto,lzma";
+          };
+
+          exclude = mkOption {
+            type = with types; listOf str;
+            description = ''
+              Exclude paths matching any of the given patterns. See
+              <command>borg help patterns</command> for pattern syntax.
+            '';
+            default = [ ];
+            example = [
+              "/home/*/.cache"
+              "/nix"
+            ];
+          };
+
+          doInit = mkOption {
+            type = types.bool;
+            description = ''
+              Run <command>borg init</command> if the
+              specified <option>repo</option> does not exist.
+              You should set this to <literal>false</literal>
+              if the repository is located on an external drive
+              that might not always be mounted.
+            '';
+            default = true;
+          };
+
+          appendFailedSuffix = mkOption {
+            type = types.bool;
+            description = ''
+              Append a <literal>.failed</literal> suffix
+              to the archive name, which is only removed if
+              <command>borg create</command> has a zero exit status.
+            '';
+            default = true;
+          };
+
+          prune.keep = mkOption {
+            # Specifying e.g. `prune.keep.yearly = -1`
+            # means there is no limit of yearly archives to keep
+            # The regex is for use with e.g. --keep-within 1y
+            type = with types; attrsOf (either int (strMatching "[[:digit:]]+[Hdwmy]"));
+            description = ''
+              Prune a repository by deleting all archives not matching any of the
+              specified retention options. See <command>borg help prune</command>
+              for the available options.
+            '';
+            default = { };
+            example = literalExample ''
+              {
+                within = "1d"; # Keep all archives from the last day
+                daily = 7;
+                weekly = 4;
+                monthly = -1;  # Keep at least one archive for each month
+              }
+            '';
+          };
+
+          prune.prefix = mkOption {
+            type = types.str;
+            description = ''
+              Only consider archive names starting with this prefix for pruning.
+              By default, only archives created by this job are considered.
+              Use <literal>""</literal> to consider all archives.
+            '';
+            default = config.archiveBaseName;
+            defaultText = "\${archiveBaseName}";
+          };
+
+          environment = mkOption {
+            type = with types; attrsOf str;
+            description = ''
+              Environment variables passed to the backup script.
+              You can for example specify which SSH key to use.
+            '';
+            default = { };
+            example = { BORG_RSH = "ssh -i /path/to/key"; };
+          };
+
+          preHook = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run before the backup.
+              This can for example be used to mount file systems.
+            '';
+            default = "";
+            example = ''
+              # To add excluded paths at runtime
+              extraCreateArgs="$extraCreateArgs --exclude /some/path"
+            '';
+          };
+
+          postInit = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run after <command>borg init</command>.
+            '';
+            default = "";
+          };
+
+          postCreate = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run after <command>borg create</command>. The name
+              of the created archive is stored in <literal>$archiveName</literal>.
+            '';
+            default = "";
+          };
+
+          postPrune = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run after <command>borg prune</command>.
+            '';
+            default = "";
+          };
+
+          postHook = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run just before exit. They are executed
+              even if a previous command exits with a non-zero exit code.
+              The latter is available as <literal>$exitStatus</literal>.
+            '';
+            default = "";
+          };
+
+          extraInitArgs = mkOption {
+            type = types.str;
+            description = ''
+              Additional arguments for <command>borg init</command>.
+              Can also be set at runtime using <literal>$extraInitArgs</literal>.
+            '';
+            default = "";
+            example = "--append-only";
+          };
+
+          extraCreateArgs = mkOption {
+            type = types.str;
+            description = ''
+              Additional arguments for <command>borg create</command>.
+              Can also be set at runtime using <literal>$extraCreateArgs</literal>.
+            '';
+            default = "";
+            example = "--stats --checkpoint-interval 600";
+          };
+
+          extraPruneArgs = mkOption {
+            type = types.str;
+            description = ''
+              Additional arguments for <command>borg prune</command>.
+              Can also be set at runtime using <literal>$extraPruneArgs</literal>.
+            '';
+            default = "";
+            example = "--save-space";
+          };
+
+        };
+      }
+    ));
+  };
+
+  options.services.borgbackup.repos = mkOption {
+    description = ''
+      Serve BorgBackup repositories to given public SSH keys,
+      restricting their access to the repository only.
+      Also, clients do not need to specify the absolute path when accessing the repository,
+      i.e. <literal>user@machine:.</literal> is enough. (Note colon and dot.)
+    '';
+    default = { };
+    type = types.attrsOf (types.submodule (
+      { name, config, ... }: {
+        options = {
+          
+          path = mkOption {
+            type = types.path;
+            description = ''
+              Where to store the backups. Note that the directory
+              is created automatically, with correct permissions.
+            '';
+            default = "/var/lib/borgbackup";
+          };
+
+          user = mkOption {
+            type = types.str;
+            description = ''
+              The user <command>borg serve</command> is run as.
+              User or group needs write permission
+              for the specified <option>path</option>.
+            '';
+            default = "borg";
+          };
+
+          group = mkOption {
+            type = types.str;
+            description = ''
+              The group <command>borg serve</command> is run as.
+              User or group needs write permission
+              for the specified <option>path</option>.
+            '';
+            default = "borg";
+          };
+
+          authorizedKeys = mkOption {
+            type = with types; listOf str;
+            description = ''
+              Public SSH keys that are given full write access to this repository.
+              You should use a different SSH key for each repository you write to, because
+              the specified keys are restricted to running <command>borg serve</command>
+              and can only access this single repository.
+            '';
+            default = [ ];
+          };
+
+          authorizedKeysAppendOnly = mkOption {
+            type = with types; listOf str;
+            description = ''
+              Public SSH keys that can only be used to append new data (archives) to the repository.
+              Note that archives can still be marked as deleted and are subsequently removed from disk
+              upon accessing the repo with full write access, e.g. when pruning.
+            '';
+            default = [ ];
+          };
+
+          allowSubRepos = mkOption {
+            type = types.bool;
+            description = ''
+              Allow clients to create repositories in subdirectories of the
+              specified <option>path</option>. These can be accessed using
+              <literal>user@machine:path/to/subrepo</literal>. Note that a
+              <option>quota</option> applies to repositories independently.
+              Therefore, if this is enabled, clients can create multiple
+              repositories and upload an arbitrary amount of data.
+            '';
+            default = false;
+          };
+
+          quota = mkOption {
+            # See the definition of parse_file_size() in src/borg/helpers/parseformat.py
+            type = with types; nullOr (strMatching "[[:digit:].]+[KMGTP]?");
+            description = ''
+              Storage quota for the repository. This quota is ensured for all
+              sub-repositories if <option>allowSubRepos</option> is enabled
+              but not for the overall storage space used.
+            '';
+            default = null;
+            example = "100G";
+          };
+
+        };
+      }
+    ));
+  };
+
+  ###### implementation
+
+  config = mkIf (with config.services.borgbackup; jobs != { } || repos != { })
+    (with config.services.borgbackup; {
+      assertions =
+        mapAttrsToList mkPassAssertion jobs
+        ++ mapAttrsToList mkKeysAssertion repos;
+
+      system.activationScripts = mapAttrs' mkActivationScript jobs;
+
+      systemd.services =
+        # A job named "foo" is mapped to systemd.services.borgbackup-job-foo
+        mapAttrs' mkBackupService jobs
+        # A repo named "foo" is mapped to systemd.services.borgbackup-repo-foo
+        // mapAttrs' mkRepoService repos;
+
+      users = mkMerge (mapAttrsToList mkUsersConfig repos);
+
+      environment.systemPackages = with pkgs; [ borgbackup ];
+    });
+}
diff --git a/nixos/modules/services/continuous-integration/buildkite-agent.nix b/nixos/modules/services/continuous-integration/buildkite-agent.nix
index 0a0c9f665d25..03af9a7859ec 100644
--- a/nixos/modules/services/continuous-integration/buildkite-agent.nix
+++ b/nixos/modules/services/continuous-integration/buildkite-agent.nix
@@ -18,7 +18,7 @@ let
   hooksDir = let
     mkHookEntry = name: value: ''
       cat > $out/${name} <<EOF
-      #! ${pkgs.stdenv.shell}
+      #! ${pkgs.runtimeShell}
       set -e
       ${value}
       EOF
diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix
index 54047a50caa6..c2f4e9c0c5a7 100644
--- a/nixos/modules/services/continuous-integration/jenkins/default.nix
+++ b/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -145,6 +145,11 @@ in {
   };
 
   config = mkIf cfg.enable {
+    # server references the dejavu fonts
+    environment.systemPackages = [
+      pkgs.dejavu_fonts
+    ];
+
     users.extraGroups = optional (cfg.group == "jenkins") {
       name = "jenkins";
       gid = config.ids.gids.jenkins;
@@ -200,10 +205,12 @@ in {
           ${replacePlugins}
         '';
 
+      # For reference: https://wiki.jenkins.io/display/JENKINS/JenkinsLinuxStartupScript
       script = ''
         ${pkgs.jdk}/bin/java ${concatStringsSep " " cfg.extraJavaOptions} -jar ${cfg.package}/webapps/jenkins.war --httpListenAddress=${cfg.listenAddress} \
                                                   --httpPort=${toString cfg.port} \
                                                   --prefix=${cfg.prefix} \
+                                                  -Djava.awt.headless=true \
                                                   ${concatStringsSep " " cfg.extraOptions}
       '';
 
diff --git a/nixos/modules/services/databases/4store-endpoint.nix b/nixos/modules/services/databases/4store-endpoint.nix
index 906cb320df98..d528355671f6 100644
--- a/nixos/modules/services/databases/4store-endpoint.nix
+++ b/nixos/modules/services/databases/4store-endpoint.nix
@@ -2,7 +2,7 @@
 let
   cfg = config.services.fourStoreEndpoint;
   endpointUser = "fourstorehttp";
-  run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${endpointUser} -c";
+  run = "${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${endpointUser} -c";
 in
 with lib;
 {
diff --git a/nixos/modules/services/databases/4store.nix b/nixos/modules/services/databases/4store.nix
index 62856822f906..abb62e1f2637 100644
--- a/nixos/modules/services/databases/4store.nix
+++ b/nixos/modules/services/databases/4store.nix
@@ -3,7 +3,7 @@ let
   cfg = config.services.fourStore;
   stateDir = "/var/lib/4store";
   fourStoreUser = "fourstore";
-  run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${fourStoreUser}";
+  run = "${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${fourStoreUser}";
 in
 with lib;
 {
diff --git a/nixos/modules/services/editors/emacs.nix b/nixos/modules/services/editors/emacs.nix
index 2c5a0c4849ef..bbc9bcf3dae1 100644
--- a/nixos/modules/services/editors/emacs.nix
+++ b/nixos/modules/services/editors/emacs.nix
@@ -7,7 +7,7 @@ let
   cfg = config.services.emacs;
 
   editorScript = pkgs.writeScriptBin "emacseditor" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
     if [ -z "$1" ]; then
       exec ${cfg.package}/bin/emacsclient --create-frame --alternate-editor ${cfg.package}/bin/emacs
     else
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
index 9f42f9e59ad5..7bfc3bb64872 100644
--- a/nixos/modules/services/hardware/udev.nix
+++ b/nixos/modules/services/hardware/udev.nix
@@ -146,7 +146,7 @@ let
 
       echo "Generating hwdb database..."
       # hwdb --update doesn't return error code even on errors!
-      res="$(${udev}/bin/udevadm hwdb --update --root=$(pwd) 2>&1)"
+      res="$(${pkgs.buildPackages.udev}/bin/udevadm hwdb --update --root=$(pwd) 2>&1)"
       echo "$res"
       [ -z "$(echo "$res" | egrep '^Error')" ]
       mv etc/udev/hwdb.bin $out
diff --git a/nixos/modules/services/misc/folding-at-home.nix b/nixos/modules/services/misc/folding-at-home.nix
index 053e7e95635f..164221cbab7f 100644
--- a/nixos/modules/services/misc/folding-at-home.nix
+++ b/nixos/modules/services/misc/folding-at-home.nix
@@ -57,7 +57,7 @@ in {
         chown ${fahUser} ${stateDir}
         cp -f ${pkgs.writeText "client.cfg" cfg.config} ${stateDir}/client.cfg
       '';
-      script = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${fahUser} -c 'cd ${stateDir}; ${pkgs.foldingathome}/bin/fah6'";
+      script = "${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${fahUser} -c 'cd ${stateDir}; ${pkgs.foldingathome}/bin/fah6'";
     };
 
     services.foldingAtHome.config = ''
diff --git a/nixos/modules/services/misc/geoip-updater.nix b/nixos/modules/services/misc/geoip-updater.nix
index 760fa66e80d6..e0b9df96f8e8 100644
--- a/nixos/modules/services/misc/geoip-updater.nix
+++ b/nixos/modules/services/misc/geoip-updater.nix
@@ -14,7 +14,7 @@ let
   # ExecStart= command with '@' doesn't work because we start a shell (new
   # process) that creates a new argv[0].)
   geoip-updater = pkgs.writeScriptBin "geoip-updater" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
     skipExisting=0
     debug()
     {
diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix
index f0b44b7bedeb..63e976ae566c 100644
--- a/nixos/modules/services/misc/gitea.nix
+++ b/nixos/modules/services/misc/gitea.nix
@@ -4,6 +4,8 @@ with lib;
 
 let
   cfg = config.services.gitea;
+  pg = config.services.postgresql;
+  usePostgresql = cfg.database.type == "postgres";
   configFile = pkgs.writeText "app.ini" ''
     APP_NAME = ${cfg.appName}
     RUN_USER = ${cfg.user}
@@ -16,6 +18,9 @@ let
     USER = ${cfg.database.user}
     PASSWD = #dbpass#
     PATH = ${cfg.database.path}
+    ${optionalString usePostgresql ''
+      SSL_MODE = disable
+    ''}
 
     [repository]
     ROOT = ${cfg.repositoryRoot}
@@ -35,6 +40,10 @@ let
     SECRET_KEY = #secretkey#
     INSTALL_LOCK = true
 
+    [log]
+    ROOT_PATH = ${cfg.log.rootPath}
+    LEVEL = ${cfg.log.level}
+
     ${cfg.extraConfig}
   '';
 in
@@ -60,6 +69,19 @@ in
         description = "gitea data directory.";
       };
 
+      log = {
+        rootPath = mkOption {
+          default = "${cfg.stateDir}/log";
+          type = types.str;
+          description = "Root path for log files.";
+        };
+        level = mkOption {
+          default = "Trace";
+          type = types.enum [ "Trace" "Debug" "Info" "Warn" "Error" "Critical" ];
+          description = "General log level.";
+        };
+      };
+
       user = mkOption {
         type = types.str;
         default = "gitea";
@@ -82,7 +104,7 @@ in
 
         port = mkOption {
           type = types.int;
-          default = 3306;
+          default = (if !usePostgresql then 3306 else pg.port);
           description = "Database host port.";
         };
 
@@ -123,6 +145,15 @@ in
           default = "${cfg.stateDir}/data/gitea.db";
           description = "Path to the sqlite3 database file.";
         };
+
+        createDatabase = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Whether to create a local postgresql database automatically.
+            This only applies if database type "postgres" is selected.
+          '';
+        };
       };
 
       appName = mkOption {
@@ -186,10 +217,11 @@ in
   };
 
   config = mkIf cfg.enable {
+    services.postgresql.enable = mkIf usePostgresql (mkDefault true);
 
     systemd.services.gitea = {
       description = "gitea";
-      after = [ "network.target" ];
+      after = [ "network.target" "postgresql.service" ];
       wantedBy = [ "multi-user.target" ];
       path = [ pkgs.gitea.bin ];
 
@@ -231,12 +263,31 @@ in
           mkdir -p ${cfg.stateDir}/conf
           cp -r ${pkgs.gitea.out}/locale ${cfg.stateDir}/conf/locale
         fi
+      '' + optionalString (usePostgresql && cfg.database.createDatabase) ''
+        if ! test -e "${cfg.stateDir}/db-created"; then
+          echo "CREATE ROLE ${cfg.database.user}
+                  WITH ENCRYPTED PASSWORD '$(head -n1 ${cfg.database.passwordFile})'
+                  NOCREATEDB NOCREATEROLE LOGIN"   |
+            ${pkgs.sudo}/bin/sudo -u ${pg.superUser} ${pg.package}/bin/psql
+          ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \
+            ${pg.package}/bin/createdb             \
+            --owner=${cfg.database.user}           \
+            --encoding=UTF8                        \
+            --lc-collate=C                         \
+            --lc-ctype=C                           \
+            --template=template0                   \
+            ${cfg.database.name}
+          touch "${cfg.stateDir}/db-created"
+        fi
+      '' + ''
+        chown ${cfg.user} -R ${cfg.stateDir}
       '';
 
       serviceConfig = {
         Type = "simple";
         User = cfg.user;
         WorkingDirectory = cfg.stateDir;
+        PermissionsStartOnly = true;
         ExecStart = "${pkgs.gitea.bin}/bin/gitea web";
         Restart = "always";
       };
@@ -253,6 +304,7 @@ in
         description = "Gitea Service";
         home = cfg.stateDir;
         createHome = true;
+        useDefaultShell = true;
       };
     };
 
diff --git a/nixos/modules/services/misc/gitit.nix b/nixos/modules/services/misc/gitit.nix
index 44880ebeda14..94a98e0335df 100644
--- a/nixos/modules/services/misc/gitit.nix
+++ b/nixos/modules/services/misc/gitit.nix
@@ -17,7 +17,7 @@ let
   gititSh = hsPkgs: extras: with pkgs; let
     env = gititWithPkgs hsPkgs extras;
   in writeScript "gitit" ''
-    #!${stdenv.shell}
+    #!${runtimeShell}
     cd $HOME
     export NIX_GHC="${env}/bin/ghc"
     export NIX_GHCPKG="${env}/bin/ghc-pkg"
diff --git a/nixos/modules/services/misc/home-assistant.nix b/nixos/modules/services/misc/home-assistant.nix
index cc60a143fa6c..ac37c11106ef 100644
--- a/nixos/modules/services/misc/home-assistant.nix
+++ b/nixos/modules/services/misc/home-assistant.nix
@@ -104,7 +104,6 @@ in {
   config = mkIf cfg.enable {
     systemd.services.home-assistant = {
       description = "Home Assistant";
-      wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
       preStart = lib.optionalString (cfg.config != null) ''
         rm -f ${cfg.configDir}/configuration.yaml
@@ -121,6 +120,16 @@ in {
         ReadWritePaths = "${cfg.configDir}";
         PrivateTmp = true;
       };
+      path = [
+        "/run/wrappers" # needed for ping
+      ];
+    };
+
+    systemd.targets.home-assistant = rec {
+      description = "Home Assistant";
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "home-assistant.service" ];
+      after = wants;
     };
 
     users.extraUsers.hass = {
diff --git a/nixos/modules/services/misc/ihaskell.nix b/nixos/modules/services/misc/ihaskell.nix
index e07a4a44613a..6da9cc8c47e6 100644
--- a/nixos/modules/services/misc/ihaskell.nix
+++ b/nixos/modules/services/misc/ihaskell.nix
@@ -55,7 +55,7 @@ in
       serviceConfig = {
         User = config.users.extraUsers.ihaskell.name;
         Group = config.users.extraGroups.ihaskell.name;
-        ExecStart = "${pkgs.stdenv.shell} -c \"cd $HOME;${ihaskell}/bin/ihaskell-notebook\"";
+        ExecStart = "${pkgs.runtimeShell} -c \"cd $HOME;${ihaskell}/bin/ihaskell-notebook\"";
       };
     };
   };
diff --git a/nixos/modules/services/misc/mesos-slave.nix b/nixos/modules/services/misc/mesos-slave.nix
index 47be10274d3b..12485141e219 100644
--- a/nixos/modules/services/misc/mesos-slave.nix
+++ b/nixos/modules/services/misc/mesos-slave.nix
@@ -188,7 +188,7 @@ in {
       description = "Mesos Slave";
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
-      path = [ pkgs.stdenv.shellPackage ];
+      path = [ pkgs.runtimeShellPackage ];
       serviceConfig = {
         ExecStart = ''
           ${pkgs.mesos}/bin/mesos-slave \
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index 72b70b28c80f..484079ed62df 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -30,7 +30,7 @@ let
       # /bin/sh in the sandbox as a bind-mount to bash. This means we
       # also need to include the entire closure of bash. Nix >= 2.0
       # provides a /bin/sh by default.
-      sh = pkgs.stdenv.shell;
+      sh = pkgs.runtimeShell;
       binshDeps = pkgs.writeReferencesToFile sh;
     in
       pkgs.runCommand "nix.conf" { extraOptions = cfg.extraOptions; } ''
diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix
index 5d0f2abd13a9..b8253956d54f 100644
--- a/nixos/modules/services/misc/nixos-manual.nix
+++ b/nixos/modules/services/misc/nixos-manual.nix
@@ -43,7 +43,7 @@ let
 
   helpScript = pkgs.writeScriptBin "nixos-help"
     ''
-      #! ${pkgs.stdenv.shell} -e
+      #! ${pkgs.runtimeShell} -e
       browser="$BROWSER"
       if [ -z "$browser" ]; then
         browser="$(type -P xdg-open || true)"
diff --git a/nixos/modules/services/misc/ssm-agent.nix b/nixos/modules/services/misc/ssm-agent.nix
index a57fbca86fb6..e951a4c7ffa8 100644
--- a/nixos/modules/services/misc/ssm-agent.nix
+++ b/nixos/modules/services/misc/ssm-agent.nix
@@ -8,7 +8,7 @@ let
   # in nixpkgs doesn't seem to work properly on NixOS, so let's just fake the two fields SSM
   # looks for. See https://github.com/aws/amazon-ssm-agent/issues/38 for upstream fix.
   fake-lsb-release = pkgs.writeScriptBin "lsb_release" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
 
     case "$1" in
       -i) echo "nixos";;
diff --git a/nixos/modules/services/monitoring/apcupsd.nix b/nixos/modules/services/monitoring/apcupsd.nix
index 9abd6e9ab641..839116de6265 100644
--- a/nixos/modules/services/monitoring/apcupsd.nix
+++ b/nixos/modules/services/monitoring/apcupsd.nix
@@ -38,7 +38,7 @@ let
   ];
 
   shellCmdsForEventScript = eventname: commands: ''
-    echo "#!${pkgs.stdenv.shell}" > "$out/${eventname}"
+    echo "#!${pkgs.runtimeShell}" > "$out/${eventname}"
     echo '${commands}' >> "$out/${eventname}"
     chmod a+x "$out/${eventname}"
   '';
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index 921be23f3681..a5b6dbab1577 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -25,6 +25,7 @@ let
     DATABASE_USER = cfg.database.user;
     DATABASE_PASSWORD = cfg.database.password;
     DATABASE_PATH = cfg.database.path;
+    DATABASE_CONN_MAX_LIFETIME = cfg.database.connMaxLifetime;
 
     SECURITY_ADMIN_USER = cfg.security.adminUser;
     SECURITY_ADMIN_PASSWORD = cfg.security.adminPassword;
@@ -143,6 +144,15 @@ in {
         default = "${cfg.dataDir}/data/grafana.db";
         type = types.path;
       };
+
+      connMaxLifetime = mkOption {
+        description = ''
+          Sets the maximum amount of time (in seconds) a connection may be reused.
+          For MySQL this setting should be shorter than the `wait_timeout' variable.
+        '';
+        default = 14400;
+        type = types.int;
+      };
     };
 
     security = {
@@ -241,7 +251,9 @@ in {
       description = "Grafana Service Daemon";
       wantedBy = ["multi-user.target"];
       after = ["networking.target"];
-      environment = mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions;
+      environment = {
+        QT_QPA_PLATFORM = "offscreen";
+      } // mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions;
       serviceConfig = {
         ExecStart = "${cfg.package.bin}/bin/grafana-server -homepath ${cfg.dataDir}";
         WorkingDirectory = cfg.dataDir;
diff --git a/nixos/modules/services/monitoring/smartd.nix b/nixos/modules/services/monitoring/smartd.nix
index b8d9e58a5a82..fecae4ca1b36 100644
--- a/nixos/modules/services/monitoring/smartd.nix
+++ b/nixos/modules/services/monitoring/smartd.nix
@@ -14,7 +14,7 @@ let
   nx = cfg.notifications.x11;
 
   smartdNotify = pkgs.writeScript "smartd-notify.sh" ''
-    #! ${pkgs.stdenv.shell}
+    #! ${pkgs.runtimeShell}
     ${optionalString nm.enable ''
       {
       ${pkgs.coreutils}/bin/cat << EOF
diff --git a/nixos/modules/services/network-filesystems/xtreemfs.nix b/nixos/modules/services/network-filesystems/xtreemfs.nix
index 0c6714563d8a..95d7641e8b53 100644
--- a/nixos/modules/services/network-filesystems/xtreemfs.nix
+++ b/nixos/modules/services/network-filesystems/xtreemfs.nix
@@ -11,7 +11,7 @@ let
   home = cfg.homeDir;
 
   startupScript = class: configPath: pkgs.writeScript "xtreemfs-osd.sh" ''
-    #! ${pkgs.stdenv.shell}
+    #! ${pkgs.runtimeShell}
     JAVA_HOME="${pkgs.jdk}"
     JAVADIR="${xtreemfs}/share/java"
     JAVA_CALL="$JAVA_HOME/bin/java -ea -cp $JAVADIR/XtreemFS.jar:$JAVADIR/BabuDB.jar:$JAVADIR/Flease.jar:$JAVADIR/protobuf-java-2.5.0.jar:$JAVADIR/Foundation.jar:$JAVADIR/jdmkrt.jar:$JAVADIR/jdmktk.jar:$JAVADIR/commons-codec-1.3.jar"
diff --git a/nixos/modules/services/network-filesystems/yandex-disk.nix b/nixos/modules/services/network-filesystems/yandex-disk.nix
index 4de206641331..44b0edf62018 100644
--- a/nixos/modules/services/network-filesystems/yandex-disk.nix
+++ b/nixos/modules/services/network-filesystems/yandex-disk.nix
@@ -99,10 +99,10 @@ in
             exit 1
         fi
 
-        ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${u} \
+        ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${u} \
           -c '${pkgs.yandex-disk}/bin/yandex-disk token -p ${cfg.password} ${cfg.username} ${dir}/token'
 
-        ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${u} \
+        ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${u} \
           -c '${pkgs.yandex-disk}/bin/yandex-disk start --no-daemon -a ${dir}/token -d ${cfg.directory} --exclude-dirs=${cfg.excludes}'
       '';
 
diff --git a/nixos/modules/services/networking/amuled.nix b/nixos/modules/services/networking/amuled.nix
index fc7d56a24fa7..9898f164c5cf 100644
--- a/nixos/modules/services/networking/amuled.nix
+++ b/nixos/modules/services/networking/amuled.nix
@@ -68,7 +68,7 @@ in
       '';
 
       script = ''
-        ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${user} \
+        ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${user} \
             -c 'HOME="${cfg.dataDir}" ${pkgs.amuleDaemon}/bin/amuled'
       '';
     };
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index bce48c8f65e5..20c0b0acf165 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -54,7 +54,7 @@ let
     '';
 
   writeShScript = name: text: let dir = pkgs.writeScriptBin name ''
-    #! ${pkgs.stdenv.shell} -e
+    #! ${pkgs.runtimeShell} -e
     ${text}
   ''; in "${dir}/bin/${name}";
 
diff --git a/nixos/modules/services/networking/flashpolicyd.nix b/nixos/modules/services/networking/flashpolicyd.nix
index 5ba85178179b..5b83ce131389 100644
--- a/nixos/modules/services/networking/flashpolicyd.nix
+++ b/nixos/modules/services/networking/flashpolicyd.nix
@@ -22,7 +22,7 @@ let
 
   flashpolicydWrapper = pkgs.writeScriptBin "flashpolicyd"
     ''
-      #! ${pkgs.stdenv.shell}
+      #! ${pkgs.runtimeShell}
       exec ${flashpolicyd}/Perl_xinetd/in.flashpolicyd.pl \
         --file=${pkgs.writeText "flashpolixy.xml" cfg.policy} \
         2> /dev/null
diff --git a/nixos/modules/services/networking/nftables.nix b/nixos/modules/services/networking/nftables.nix
index 56b942054140..ad7c013a5449 100644
--- a/nixos/modules/services/networking/nftables.nix
+++ b/nixos/modules/services/networking/nftables.nix
@@ -116,7 +116,7 @@ in
           include "${cfg.rulesetFile}"
         '';
         checkScript = pkgs.writeScript "nftables-check" ''
-          #! ${pkgs.stdenv.shell} -e
+          #! ${pkgs.runtimeShell} -e
           if $(${pkgs.kmod}/bin/lsmod | grep -q ip_tables); then
             echo "Unload ip_tables before using nftables!" 1>&2
             exit 1
diff --git a/nixos/modules/services/networking/nix-serve.nix b/nixos/modules/services/networking/nix-serve.nix
index 3e865e3b76a8..8499e7c0f7c4 100644
--- a/nixos/modules/services/networking/nix-serve.nix
+++ b/nixos/modules/services/networking/nix-serve.nix
@@ -55,6 +55,8 @@ in
       environment.NIX_SECRET_KEY_FILE = cfg.secretKeyFile;
 
       serviceConfig = {
+        Restart = "always";
+        RestartSec = "5s";
         ExecStart = "${pkgs.nix-serve}/bin/nix-serve " +
           "--listen ${cfg.bindAddress}:${toString cfg.port} ${cfg.extraParams}";
         User = "nix-serve";
diff --git a/nixos/modules/services/networking/rdnssd.nix b/nixos/modules/services/networking/rdnssd.nix
index 95833d31e99d..a102242eae71 100644
--- a/nixos/modules/services/networking/rdnssd.nix
+++ b/nixos/modules/services/networking/rdnssd.nix
@@ -6,7 +6,7 @@
 with lib;
 let
   mergeHook = pkgs.writeScript "rdnssd-merge-hook" ''
-    #! ${pkgs.stdenv.shell} -e
+    #! ${pkgs.runtimeShell} -e
     ${pkgs.openresolv}/bin/resolvconf -u
   '';
 in
diff --git a/nixos/modules/services/networking/resilio.nix b/nixos/modules/services/networking/resilio.nix
index d1c4101f80bd..2956a5ecbc04 100644
--- a/nixos/modules/services/networking/resilio.nix
+++ b/nixos/modules/services/networking/resilio.nix
@@ -50,12 +50,7 @@ in
         description = ''
           If enabled, start the Resilio Sync daemon. Once enabled, you can
           interact with the service through the Web UI, or configure it in your
-          NixOS configuration. Enabling the <literal>resilio</literal> service
-          also installs a systemd user unit which can be used to start
-          user-specific copies of the daemon. Once installed, you can use
-          <literal>systemctl --user start resilio</literal> as your user to start
-          the daemon using the configuration file located at
-          <literal>$HOME/.config/resilio-sync/config.json</literal>.
+          NixOS configuration.
         '';
       };
 
diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix
index 4c7f58d1d8bc..ecab8cfc7df9 100644
--- a/nixos/modules/services/printing/cupsd.nix
+++ b/nixos/modules/services/printing/cupsd.nix
@@ -124,7 +124,7 @@ in
 
       listenAddresses = mkOption {
         type = types.listOf types.str;
-        default = [ "127.0.0.1:631" ];
+        default = [ "localhost:631" ];
         example = [ "*:631" ];
         description = ''
           A list of addresses and ports on which to listen.
@@ -321,7 +321,10 @@ in
             ''}
           '';
 
-          serviceConfig.PrivateTmp = true;
+          serviceConfig = {
+            PrivateTmp = true;
+            RuntimeDirectory = [ "cups" ];
+          };
       };
 
     systemd.services.cups-browsed = mkIf avahiEnabled
diff --git a/nixos/modules/services/security/torify.nix b/nixos/modules/services/security/torify.nix
index a29cb3f33dae..08da726437ea 100644
--- a/nixos/modules/services/security/torify.nix
+++ b/nixos/modules/services/security/torify.nix
@@ -7,7 +7,7 @@ let
   torify = pkgs.writeTextFile {
     name = "tsocks";
     text = ''
-        #!${pkgs.stdenv.shell}
+        #!${pkgs.runtimeShell}
         TSOCKS_CONF_FILE=${pkgs.writeText "tsocks.conf" cfg.tsocks.config} LD_PRELOAD="${pkgs.tsocks}/lib/libtsocks.so $LD_PRELOAD" "$@"
     '';
     executable = true;
diff --git a/nixos/modules/services/security/torsocks.nix b/nixos/modules/services/security/torsocks.nix
index 1b5a05b21e77..c60c745443bc 100644
--- a/nixos/modules/services/security/torsocks.nix
+++ b/nixos/modules/services/security/torsocks.nix
@@ -23,7 +23,7 @@ let
   wrapTorsocks = name: server: pkgs.writeTextFile {
     name = name;
     text = ''
-        #!${pkgs.stdenv.shell}
+        #!${pkgs.runtimeShell}
         TORSOCKS_CONF_FILE=${pkgs.writeText "torsocks.conf" (configFile server)} ${pkgs.torsocks}/bin/torsocks "$@"
     '';
     executable = true;
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index dd6b585b7e23..4911a64c95d0 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -90,7 +90,7 @@ in
       # 1) Only the "transmission" user and group have access to torrents.
       # 2) Optionally update/force specific fields into the configuration file.
       serviceConfig.ExecStartPre = ''
-          ${pkgs.stdenv.shell} -c "mkdir -p ${homeDir} ${settingsDir} ${fullSettings.download-dir} ${fullSettings.incomplete-dir} && chmod 770 ${homeDir} ${settingsDir} ${fullSettings.download-dir} ${fullSettings.incomplete-dir} && rm -f ${settingsDir}/settings.json && cp -f ${settingsFile} ${settingsDir}/settings.json"
+          ${pkgs.runtimeShell} -c "mkdir -p ${homeDir} ${settingsDir} ${fullSettings.download-dir} ${fullSettings.incomplete-dir} && chmod 770 ${homeDir} ${settingsDir} ${fullSettings.download-dir} ${fullSettings.incomplete-dir} && rm -f ${settingsDir}/settings.json && cp -f ${settingsFile} ${settingsDir}/settings.json"
       '';
       serviceConfig.ExecStart = "${pkgs.transmission}/bin/transmission-daemon -f --port ${toString config.services.transmission.port}";
       serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix
index c784f4756d19..8f7a56189a07 100644
--- a/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixos/modules/services/web-apps/tt-rss.nix
@@ -505,7 +505,7 @@ let
                     ${cfg.database.name}''
 
               else if cfg.database.type == "mysql" then ''
-                  echo '${e}' | ${pkgs.mysql}/bin/mysql \
+                  echo '${e}' | ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${config.services.mysql.package}/bin/mysql \
                     -u ${cfg.database.user} \
                     ${optionalString (cfg.database.password != null) "-p${cfg.database.password}"} \
                     ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} -P ${toString dbPort}"} \
diff --git a/nixos/modules/services/web-servers/apache-httpd/owncloud.nix b/nixos/modules/services/web-servers/apache-httpd/owncloud.nix
index cfddab2f5047..82b8bf3e30db 100644
--- a/nixos/modules/services/web-servers/apache-httpd/owncloud.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/owncloud.nix
@@ -346,7 +346,7 @@ let
   postgresql = serverInfo.fullConfig.services.postgresql.package;
 
   setupDb = pkgs.writeScript "setup-owncloud-db" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
     PATH="${postgresql}/bin"
     createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true
     createdb "${config.dbName}" -O "${config.dbUser}" || true
diff --git a/nixos/modules/services/web-servers/tomcat.nix b/nixos/modules/services/web-servers/tomcat.nix
index 0b2e5c0b69d9..aa94e0e976c9 100644
--- a/nixos/modules/services/web-servers/tomcat.nix
+++ b/nixos/modules/services/web-servers/tomcat.nix
@@ -19,11 +19,7 @@ in
   options = {
 
     services.tomcat = {
-
-      enable = mkOption {
-        default = false;
-        description = "Whether to enable Apache Tomcat";
-      };
+      enable = mkEnableOption "Apache Tomcat";
 
       package = mkOption {
         type = types.package;
@@ -36,10 +32,30 @@ in
       };
 
       baseDir = mkOption {
+        type = lib.types.path;
         default = "/var/tomcat";
         description = "Location where Tomcat stores configuration files, webapplications and logfiles";
       };
 
+      logDirs = mkOption {
+        default = [];
+        type = types.listOf types.path;
+        description = "Directories to create in baseDir/logs/";
+      };
+
+      extraConfigFiles = mkOption {
+        default = [];
+        type = types.listOf types.path;
+        description = "Extra configuration files to pull into the tomcat conf directory";
+      };
+
+      extraEnvironment = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "ENVIRONMENT=production" ];
+        description = "Environment Variables to pass to the tomcat service";
+      };
+
       extraGroups = mkOption {
         default = [];
         example = [ "users" ];
@@ -47,31 +63,46 @@ in
       };
 
       user = mkOption {
+        type = types.str;
         default = "tomcat";
         description = "User account under which Apache Tomcat runs.";
       };
 
       group = mkOption {
+        type = types.str;
         default = "tomcat";
         description = "Group account under which Apache Tomcat runs.";
       };
 
       javaOpts = mkOption {
+        type = types.either (types.listOf types.str) types.str;
         default = "";
         description = "Parameters to pass to the Java Virtual Machine which spawns Apache Tomcat";
       };
 
       catalinaOpts = mkOption {
+        type = types.either (types.listOf types.str) types.str;
         default = "";
         description = "Parameters to pass to the Java Virtual Machine which spawns the Catalina servlet container";
       };
 
       sharedLibs = mkOption {
+        type = types.listOf types.str;
         default = [];
         description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications";
       };
 
+      serverXml = mkOption {
+        type = types.lines;
+        default = "";
+        description = "
+          Verbatim server.xml configuration.
+          This is mutually exclusive with the virtualHosts options.
+        ";
+      };
+
       commonLibs = mkOption {
+        type = types.listOf types.str;
         default = [];
         description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications and the servlet container";
       };
@@ -84,11 +115,21 @@ in
       };
 
       virtualHosts = mkOption {
+        type = types.listOf (types.submodule {
+          options = {
+            name = mkOption {
+              type = types.listOf types.str;
+              description = "name of the virtualhost";
+              default = [];
+            };
+          };
+        });
         default = [];
         description = "List consisting of a virtual host name and a list of web applications to deploy on each virtual host";
       };
 
       logPerVirtualHost = mkOption {
+        type = types.bool;
         default = false;
         description = "Whether to enable logging per virtual host.";
       };
@@ -104,11 +145,13 @@ in
 
         enable = mkOption {
           default = false;
+          type = types.bool;
           description = "Whether to enable an Apache Axis2 container";
         };
 
         services = mkOption {
           default = [];
+          type = types.listOf types.str;
           description = "List containing AAR files or directories with AAR files which are web services to be deployed on Axis2";
         };
 
@@ -140,130 +183,104 @@ in
       description = "Apache Tomcat server";
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
-      serviceConfig.Type = "oneshot";
-      serviceConfig.RemainAfterExit = true;
 
       preStart = ''
         # Create the base directory
-        mkdir -p ${cfg.baseDir}
+        mkdir -p \
+          ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work}
+        chown ${cfg.user}:${cfg.group} \
+          ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work}
 
         # Create a symlink to the bin directory of the tomcat component
         ln -sfn ${tomcat}/bin ${cfg.baseDir}/bin
 
-        # Create a conf/ directory
-        mkdir -p ${cfg.baseDir}/conf
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/conf
-
         # Symlink the config files in the conf/ directory (except for catalina.properties and server.xml)
-        for i in $(ls ${tomcat}/conf | grep -v catalina.properties | grep -v server.xml)
-        do
-            ln -sfn ${tomcat}/conf/$i ${cfg.baseDir}/conf/`basename $i`
+        for i in $(ls ${tomcat}/conf | grep -v catalina.properties | grep -v server.xml); do
+          ln -sfn ${tomcat}/conf/$i ${cfg.baseDir}/conf/`basename $i`
         done
 
-        # Create subdirectory for virtual hosts
-        mkdir -p ${cfg.baseDir}/virtualhosts
+        ${if cfg.extraConfigFiles != [] then ''
+          for i in ${toString cfg.extraConfigFiles}; do
+            ln -sfn $i ${cfg.baseDir}/conf/`basename $i`
+          done
+        '' else ""}
 
         # Create a modified catalina.properties file
         # Change all references from CATALINA_HOME to CATALINA_BASE and add support for shared libraries
         sed -e 's|''${catalina.home}|''${catalina.base}|g' \
-            -e 's|shared.loader=|shared.loader=''${catalina.base}/shared/lib/*.jar|' \
-            ${tomcat}/conf/catalina.properties > ${cfg.baseDir}/conf/catalina.properties
-
-        # Create a modified server.xml which also includes all virtual hosts
-        sed -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\  ${
-                     toString (map (virtualHost: ''<Host name=\"${virtualHost.name}\" appBase=\"virtualhosts/${virtualHost.name}/webapps\" unpackWARs=\"true\" autoDeploy=\"true\" xmlValidation=\"false\" xmlNamespaceAware=\"false\" >${if cfg.logPerVirtualHost then ''<Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs/${virtualHost.name}\"  prefix=\"${virtualHost.name}_access_log.\" pattern=\"combined\" resolveHosts=\"false\"/>'' else ""}</Host>'') cfg.virtualHosts)}" \
-            ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml
-
-        # Create a logs/ directory
-        mkdir -p ${cfg.baseDir}/logs
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs
-        ${if cfg.logPerVirtualHost then
-           toString (map (h: ''
-                                mkdir -p ${cfg.baseDir}/logs/${h.name}
-                                chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/${h.name}
-                             '') cfg.virtualHosts) else ''''}
-
-        # Create a temp/ directory
-        mkdir -p ${cfg.baseDir}/temp
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/temp
-
-        # Create a lib/ directory
-        mkdir -p ${cfg.baseDir}/lib
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/lib
-
-        # Create a shared/lib directory
-        mkdir -p ${cfg.baseDir}/shared/lib
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/shared/lib
-
-        # Create a webapps/ directory
-        mkdir -p ${cfg.baseDir}/webapps
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps
+          -e 's|shared.loader=|shared.loader=''${catalina.base}/shared/lib/*.jar|' \
+          ${tomcat}/conf/catalina.properties > ${cfg.baseDir}/conf/catalina.properties
+
+        ${if cfg.serverXml != "" then ''
+          cp -f ${pkgs.writeTextDir "server.xml" cfg.serverXml}/* ${cfg.baseDir}/conf/
+          '' else ''
+          # Create a modified server.xml which also includes all virtual hosts
+          sed -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\  ${toString (map (virtualHost: ''<Host name=\"${virtualHost.name}\" appBase=\"virtualhosts/${virtualHost.name}/webapps\" unpackWARs=\"true\" autoDeploy=\"true\" xmlValidation=\"false\" xmlNamespaceAware=\"false\" >${if cfg.logPerVirtualHost then ''<Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs/${virtualHost.name}\"  prefix=\"${virtualHost.name}_access_log.\" pattern=\"combined\" resolveHosts=\"false\"/>'' else ""}</Host>'') cfg.virtualHosts)}" \
+                ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml
+          ''
+        }
+        ${optionalString (cfg.logDirs != []) ''
+          for i in ${toString cfg.logDirs}; do
+            mkdir -p ${cfg.baseDir}/logs/$i
+            chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/$i
+          done
+        ''}
+        ${optionalString cfg.logPerVirtualHost (toString (map (h: ''
+          mkdir -p ${cfg.baseDir}/logs/${h.name}
+          chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/${h.name}
+        '') cfg.virtualHosts))}
 
         # Symlink all the given common libs files or paths into the lib/ directory
-        for i in ${tomcat} ${toString cfg.commonLibs}
-        do
-            if [ -f $i ]
-            then
-                # If the given web application is a file, symlink it into the common/lib/ directory
-                ln -sfn $i ${cfg.baseDir}/lib/`basename $i`
-            elif [ -d $i ]
-            then
-                # If the given web application is a directory, then iterate over the files
-                # in the special purpose directories and symlink them into the tomcat tree
-
-                for j in $i/lib/*
-                do
-                    ln -sfn $j ${cfg.baseDir}/lib/`basename $j`
-                done
-            fi
+        for i in ${tomcat} ${toString cfg.commonLibs}; do
+          if [ -f $i ]; then
+            # If the given web application is a file, symlink it into the common/lib/ directory
+            ln -sfn $i ${cfg.baseDir}/lib/`basename $i`
+          elif [ -d $i ]; then
+            # If the given web application is a directory, then iterate over the files
+            # in the special purpose directories and symlink them into the tomcat tree
+
+            for j in $i/lib/*; do
+              ln -sfn $j ${cfg.baseDir}/lib/`basename $j`
+            done
+          fi
         done
 
         # Symlink all the given shared libs files or paths into the shared/lib/ directory
-        for i in ${toString cfg.sharedLibs}
-        do
-            if [ -f $i ]
-            then
-                # If the given web application is a file, symlink it into the common/lib/ directory
-                ln -sfn $i ${cfg.baseDir}/shared/lib/`basename $i`
-            elif [ -d $i ]
-            then
-                # If the given web application is a directory, then iterate over the files
-                # in the special purpose directories and symlink them into the tomcat tree
-
-                for j in $i/shared/lib/*
-                do
-                    ln -sfn $j ${cfg.baseDir}/shared/lib/`basename $j`
-                done
-            fi
+        for i in ${toString cfg.sharedLibs}; do
+          if [ -f $i ]; then
+            # If the given web application is a file, symlink it into the common/lib/ directory
+            ln -sfn $i ${cfg.baseDir}/shared/lib/`basename $i`
+          elif [ -d $i ]; then
+            # If the given web application is a directory, then iterate over the files
+            # in the special purpose directories and symlink them into the tomcat tree
+
+            for j in $i/shared/lib/*; do
+              ln -sfn $j ${cfg.baseDir}/shared/lib/`basename $j`
+            done
+          fi
         done
 
         # Symlink all the given web applications files or paths into the webapps/ directory
-        for i in ${toString cfg.webapps}
-        do
-            if [ -f $i ]
-            then
-                # If the given web application is a file, symlink it into the webapps/ directory
-                ln -sfn $i ${cfg.baseDir}/webapps/`basename $i`
-            elif [ -d $i ]
-            then
-                # If the given web application is a directory, then iterate over the files
-                # in the special purpose directories and symlink them into the tomcat tree
-
-                for j in $i/webapps/*
-                do
-                    ln -sfn $j ${cfg.baseDir}/webapps/`basename $j`
-                done
+        for i in ${toString cfg.webapps}; do
+          if [ -f $i ]; then
+            # If the given web application is a file, symlink it into the webapps/ directory
+            ln -sfn $i ${cfg.baseDir}/webapps/`basename $i`
+          elif [ -d $i ]; then
+            # If the given web application is a directory, then iterate over the files
+            # in the special purpose directories and symlink them into the tomcat tree
+
+            for j in $i/webapps/*; do
+              ln -sfn $j ${cfg.baseDir}/webapps/`basename $j`
+            done
 
-                # Also symlink the configuration files if they are included
-                if [ -d $i/conf/Catalina ]
-                then
-                    for j in $i/conf/Catalina/*
-                    do
-                        mkdir -p ${cfg.baseDir}/conf/Catalina/localhost
-                        ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
-                    done
-                fi
+            # Also symlink the configuration files if they are included
+            if [ -d $i/conf/Catalina ]; then
+              for j in $i/conf/Catalina/*; do
+                mkdir -p ${cfg.baseDir}/conf/Catalina/localhost
+                ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
+              done
             fi
+          fi
         done
 
         ${toString (map (virtualHost: ''
@@ -275,94 +292,79 @@ in
 
           # Symlink all the given web applications files or paths into the webapps/ directory
           # of this virtual host
-          for i in "${if virtualHost ? webapps then toString virtualHost.webapps else ""}"
-          do
-              if [ -f $i ]
-              then
-                  # If the given web application is a file, symlink it into the webapps/ directory
-                  ln -sfn $i ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $i`
-              elif [ -d $i ]
-              then
-                  # If the given web application is a directory, then iterate over the files
-                  # in the special purpose directories and symlink them into the tomcat tree
-
-                  for j in $i/webapps/*
-                  do
-                      ln -sfn $j ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $j`
-                  done
-
-                  # Also symlink the configuration files if they are included
-                  if [ -d $i/conf/Catalina ]
-                  then
-                      for j in $i/conf/Catalina/*
-                      do
-                          mkdir -p ${cfg.baseDir}/conf/Catalina/${virtualHost.name}
-                          ln -sfn $j ${cfg.baseDir}/conf/Catalina/${virtualHost.name}/`basename $j`
-                      done
-                  fi
+          for i in "${if virtualHost ? webapps then toString virtualHost.webapps else ""}"; do
+            if [ -f $i ]; then
+              # If the given web application is a file, symlink it into the webapps/ directory
+              ln -sfn $i ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $i`
+            elif [ -d $i ]; then
+              # If the given web application is a directory, then iterate over the files
+              # in the special purpose directories and symlink them into the tomcat tree
+
+              for j in $i/webapps/*; do
+                ln -sfn $j ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $j`
+              done
+
+              # Also symlink the configuration files if they are included
+              if [ -d $i/conf/Catalina ]; then
+                for j in $i/conf/Catalina/*; do
+                  mkdir -p ${cfg.baseDir}/conf/Catalina/${virtualHost.name}
+                  ln -sfn $j ${cfg.baseDir}/conf/Catalina/${virtualHost.name}/`basename $j`
+                done
               fi
+            fi
           done
-
-          ''
-        ) cfg.virtualHosts) }
-
-        # Create a work/ directory
-        mkdir -p ${cfg.baseDir}/work
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/work
-
-        ${if cfg.axis2.enable then
-            ''
-            # Copy the Axis2 web application
-            cp -av ${pkgs.axis2}/webapps/axis2 ${cfg.baseDir}/webapps
-
-            # Turn off addressing, which causes many errors
-            sed -i -e 's%<module ref="addressing"/>%<!-- <module ref="addressing"/> -->%' ${cfg.baseDir}/webapps/axis2/WEB-INF/conf/axis2.xml
-
-            # Modify permissions on the Axis2 application
-            chown -R ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps/axis2
-
-            # Symlink all the given web service files or paths into the webapps/axis2/WEB-INF/services directory
-            for i in ${toString cfg.axis2.services}
-            do
-                if [ -f $i ]
-                then
-                    # If the given web service is a file, symlink it into the webapps/axis2/WEB-INF/services
-                    ln -sfn $i ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $i`
-                elif [ -d $i ]
-                then
-                    # If the given web application is a directory, then iterate over the files
-                    # in the special purpose directories and symlink them into the tomcat tree
-
-                    for j in $i/webapps/axis2/WEB-INF/services/*
-                    do
-                        ln -sfn $j ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $j`
-                    done
-
-                    # Also symlink the configuration files if they are included
-                    if [ -d $i/conf/Catalina ]
-                    then
-                        for j in $i/conf/Catalina/*
-                        do
-                            ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
-                        done
-                    fi
-                fi
-            done
-            ''
-        else ""}
-      '';
-
-      script = ''
-          ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c 'CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${cfg.jdk} JAVA_OPTS="${cfg.javaOpts}" CATALINA_OPTS="${cfg.catalinaOpts}" ${tomcat}/bin/startup.sh'
-      '';
-
-      preStop = ''
-        echo "Stopping tomcat..."
-        CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${cfg.jdk} ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c ${tomcat}/bin/shutdown.sh
+        '') cfg.virtualHosts)}
+
+        ${optionalString cfg.axis2.enable ''
+          # Copy the Axis2 web application
+          cp -av ${pkgs.axis2}/webapps/axis2 ${cfg.baseDir}/webapps
+
+          # Turn off addressing, which causes many errors
+          sed -i -e 's%<module ref="addressing"/>%<!-- <module ref="addressing"/> -->%' ${cfg.baseDir}/webapps/axis2/WEB-INF/conf/axis2.xml
+
+          # Modify permissions on the Axis2 application
+          chown -R ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps/axis2
+
+          # Symlink all the given web service files or paths into the webapps/axis2/WEB-INF/services directory
+          for i in ${toString cfg.axis2.services}; do
+            if [ -f $i ]; then
+              # If the given web service is a file, symlink it into the webapps/axis2/WEB-INF/services
+              ln -sfn $i ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $i`
+            elif [ -d $i ]; then
+              # If the given web application is a directory, then iterate over the files
+              # in the special purpose directories and symlink them into the tomcat tree
+
+              for j in $i/webapps/axis2/WEB-INF/services/*; do
+                ln -sfn $j ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $j`
+              done
+
+              # Also symlink the configuration files if they are included
+              if [ -d $i/conf/Catalina ]; then
+                for j in $i/conf/Catalina/*; do
+                  ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
+                done
+              fi
+            fi
+          done
+        ''}
       '';
 
+      serviceConfig = {
+        Type = "forking";
+        PermissionsStartOnly = true;
+        PIDFile="/run/tomcat/tomcat.pid";
+        RuntimeDirectory = "tomcat";
+        User = cfg.user;
+        Environment=[
+          "CATALINA_BASE=${cfg.baseDir}"
+          "CATALINA_PID=/run/tomcat/tomcat.pid"
+          "JAVA_HOME='${cfg.jdk}'"
+          "JAVA_OPTS='${builtins.toString cfg.javaOpts}'"
+          "CATALINA_OPTS='${builtins.toString cfg.catalinaOpts}'"
+        ] ++ cfg.extraEnvironment;
+        ExecStart = "${tomcat}/bin/startup.sh";
+        ExecStop = "${tomcat}/bin/shutdown.sh";
+      };
     };
-
   };
-
 }
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 489bffbee917..7dcc600d2664 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -128,7 +128,7 @@ in
         # Set GTK_DATA_PREFIX so that GTK+ can find the Xfce themes.
         export GTK_DATA_PREFIX=${config.system.path}
 
-        ${pkgs.stdenv.shell} ${pkgs.xfce.xinitrc} &
+        ${pkgs.runtimeShell} ${pkgs.xfce.xinitrc} &
         waitPID=$!
       '';
     }];
diff --git a/nixos/modules/services/x11/display-managers/slim.nix b/nixos/modules/services/x11/display-managers/slim.nix
index 0c4dd1973b53..f645a5c2f078 100644
--- a/nixos/modules/services/x11/display-managers/slim.nix
+++ b/nixos/modules/services/x11/display-managers/slim.nix
@@ -14,7 +14,7 @@ let
       default_xserver ${dmcfg.xserverBin}
       xserver_arguments ${toString dmcfg.xserverArgs}
       sessiondir ${dmcfg.session.desktops}
-      login_cmd exec ${pkgs.stdenv.shell} ${dmcfg.session.script} "%session"
+      login_cmd exec ${pkgs.runtimeShell} ${dmcfg.session.script} "%session"
       halt_cmd ${config.systemd.package}/sbin/shutdown -h now
       reboot_cmd ${config.systemd.package}/sbin/shutdown -r now
       logfile /dev/stderr
diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix
index 25ba95fccd75..bc420831ad83 100644
--- a/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixos/modules/services/x11/window-managers/default.nix
@@ -12,6 +12,7 @@ in
     ./afterstep.nix
     ./bspwm.nix
     ./dwm.nix
+    ./evilwm.nix
     ./exwm.nix
     ./fluxbox.nix
     ./fvwm.nix
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
index 8c9b35fe524f..c563614caaaf 100644
--- a/nixos/modules/system/activation/activation-script.nix
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -61,7 +61,7 @@ in
       apply = set: {
         script =
           ''
-            #! ${pkgs.stdenv.shell}
+            #! ${pkgs.runtimeShell}
 
             systemConfig=@out@
 
diff --git a/nixos/modules/system/boot/kexec.nix b/nixos/modules/system/boot/kexec.nix
index b7821f9509f1..14ebe66e6320 100644
--- a/nixos/modules/system/boot/kexec.nix
+++ b/nixos/modules/system/boot/kexec.nix
@@ -1,21 +1,22 @@
-{ config, pkgs, ... }:
+{ config, pkgs, lib, ... }:
 
 {
-  environment.systemPackages = [ pkgs.kexectools ];
+  config = lib.mkIf (pkgs.kexectools != null) {
+    environment.systemPackages = [ pkgs.kexectools ];
 
-  systemd.services."prepare-kexec" =
-    { description = "Preparation for kexec";
-      wantedBy = [ "kexec.target" ];
-      before = [ "systemd-kexec.service" ];
-      unitConfig.DefaultDependencies = false;
-      serviceConfig.Type = "oneshot";
-      path = [ pkgs.kexectools ];
-      script =
-        ''
-          p=$(readlink -f /nix/var/nix/profiles/system)
-          if ! [ -d $p ]; then exit 1; fi
-          exec kexec --load $p/kernel --initrd=$p/initrd --append="$(cat $p/kernel-params) init=$p/init"
-        '';
-    };
-
-}
\ No newline at end of file
+    systemd.services."prepare-kexec" =
+      { description = "Preparation for kexec";
+        wantedBy = [ "kexec.target" ];
+        before = [ "systemd-kexec.service" ];
+        unitConfig.DefaultDependencies = false;
+        serviceConfig.Type = "oneshot";
+        path = [ pkgs.kexectools ];
+        script =
+          ''
+            p=$(readlink -f /nix/var/nix/profiles/system)
+            if ! [ -d $p ]; then exit 1; fi
+            exec kexec --load $p/kernel --initrd=$p/initrd --append="$(cat $p/kernel-params) init=$p/init"
+          '';
+      };
+  };
+}
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index 0d83391de893..e2cff1c1bd94 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -40,7 +40,7 @@ let
     { splashImage = f cfg.splashImage;
       grub = f grub;
       grubTarget = f (grub.grubTarget or "");
-      shell = "${pkgs.stdenv.shell}";
+      shell = "${pkgs.runtimeShell}";
       fullName = (builtins.parseDrvName realGrub.name).name;
       fullVersion = (builtins.parseDrvName realGrub.name).version;
       grubEfi = f grubEfi;
@@ -536,9 +536,9 @@ in
             btrfsprogs = pkgs.btrfs-progs;
           };
         in pkgs.writeScript "install-grub.sh" (''
-        #!${pkgs.stdenv.shell}
+        #!${pkgs.runtimeShell}
         set -e
-        export PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX ListCompare ])}
+        export PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX XMLSAXBase ListCompare ])}
         ${optionalString cfg.enableCryptodisk "export GRUB_ENABLE_CRYPTODISK=y"}
       '' + flip concatMapStrings cfg.mirroredBoots (args: ''
         ${pkgs.perl}/bin/perl ${install-grub-pl} ${grubConfig args} $@
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index cc03e54ead63..8bd203106f55 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -182,7 +182,7 @@ sub GrubFs {
                 # Based on the type pull in the identifier from the system
                 my ($status, @devInfo) = runCommand("@utillinux@/bin/blkid -o export @{[$fs->device]}");
                 if ($status != 0) {
-                    die "Failed to get blkid info for @{[$fs->mount]} on @{[$fs->device]}";
+                    die "Failed to get blkid info (returned $status) for @{[$fs->mount]} on @{[$fs->device]}";
                 }
                 my @matches = join("", @devInfo) =~ m/@{[uc $fsIdentifier]}=([^\n]*)/;
                 if ($#matches != 0) {
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index df450be8c401..55bb6d3449c5 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -30,6 +30,50 @@ let
   # mounting `/`, like `/` on a loopback).
   fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems;
 
+  # A utility for enumerating the shared-library dependencies of a program
+  findLibs = pkgs.writeShellScriptBin "find-libs" ''
+    set -euo pipefail
+
+    declare -A seen
+    declare -a left
+
+    patchelf="${pkgs.buildPackages.patchelf}/bin/patchelf"
+
+    function add_needed {
+      rpath="$($patchelf --print-rpath $1)"
+      dir="$(dirname $1)"
+      for lib in $($patchelf --print-needed $1); do
+        left+=("$lib" "$rpath" "$dir")
+      done
+    }
+
+    add_needed $1
+
+    while [ ''${#left[@]} -ne 0 ]; do
+      next=''${left[0]}
+      rpath=''${left[1]}
+      ORIGIN=''${left[2]}
+      left=("''${left[@]:3}")
+      if [ -z ''${seen[$next]+x} ]; then
+        seen[$next]=1
+        IFS=: read -ra paths <<< $rpath
+        res=
+        for path in "''${paths[@]}"; do
+          path=$(eval "echo $path")
+          if [ -f "$path/$next" ]; then
+              res="$path/$next"
+              echo "$res"
+              add_needed "$res"
+              break
+          fi
+        done
+        if [ -z "$res" ]; then
+          echo "Couldn't satisfy dependency $next" >&2
+          exit 1
+        fi
+      fi
+    done
+  '';
 
   # Some additional utilities needed in stage 1, like mount, lvm, fsck
   # etc.  We don't want to bring in all of those packages, so we just
@@ -37,7 +81,7 @@ let
   # we just copy what we need from Glibc and use patchelf to make it
   # work.
   extraUtils = pkgs.runCommandCC "extra-utils"
-    { buildInputs = [pkgs.nukeReferences];
+    { nativeBuildInputs = [pkgs.buildPackages.nukeReferences];
       allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd
     }
     ''
@@ -103,9 +147,7 @@ let
       # Copy all of the needed libraries
       find $out/bin $out/lib -type f | while read BIN; do
         echo "Copying libs for executable $BIN"
-        LDD="$(ldd $BIN)" || continue
-        LIBS="$(echo "$LDD" | awk '{print $3}' | sed '/^$/d')"
-        for LIB in $LIBS; do
+        for LIB in $(${findLibs}/bin/find-libs $BIN); do
           TGT="$out/lib/$(basename $LIB)"
           if [ ! -f "$TGT" ]; then
             SRC="$(readlink -e $LIB)"
@@ -132,6 +174,7 @@ let
         fi
       done
 
+      if [ -z "${toString pkgs.stdenv.isCross}" ]; then
       # Make sure that the patchelf'ed binaries still work.
       echo "testing patched programs..."
       $out/bin/ash -c 'echo hello world' | grep "hello world"
@@ -144,6 +187,7 @@ let
       $out/bin/mdadm --version
 
       ${config.boot.initrd.extraUtilsCommandsTest}
+      fi
     ''; # */
 
 
@@ -245,7 +289,7 @@ let
             { src = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf"; }
             ''
               target=$out
-              ${pkgs.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out
+              ${pkgs.buildPackages.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out
             '';
           symlink = "/etc/modprobe.d/ubuntu.conf";
         }
diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix
index 8db6d2d2f734..78afbd8dbc12 100644
--- a/nixos/modules/system/boot/stage-2.nix
+++ b/nixos/modules/system/boot/stage-2.nix
@@ -10,6 +10,7 @@ let
   bootStage2 = pkgs.substituteAll {
     src = ./stage-2-init.sh;
     shellDebug = "${pkgs.bashInteractive}/bin/bash";
+    shell = "${pkgs.bash}/bin/bash";
     isExecutable = true;
     inherit (config.nix) readOnlyStore;
     inherit (config.networking) useHostResolvConf;
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index aff46ea861a2..92c9ee0c4691 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -241,37 +241,37 @@ let
         }
         (mkIf (config.preStart != "")
           { serviceConfig.ExecStartPre = makeJobScript "${name}-pre-start" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.preStart}
             '';
           })
         (mkIf (config.script != "")
           { serviceConfig.ExecStart = makeJobScript "${name}-start" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.script}
             '' + " " + config.scriptArgs;
           })
         (mkIf (config.postStart != "")
           { serviceConfig.ExecStartPost = makeJobScript "${name}-post-start" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.postStart}
             '';
           })
         (mkIf (config.reload != "")
           { serviceConfig.ExecReload = makeJobScript "${name}-reload" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.reload}
             '';
           })
         (mkIf (config.preStop != "")
           { serviceConfig.ExecStop = makeJobScript "${name}-pre-stop" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.preStop}
             '';
           })
         (mkIf (config.postStop != "")
           { serviceConfig.ExecStopPost = makeJobScript "${name}-post-stop" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.postStop}
             '';
           })
diff --git a/nixos/modules/tasks/kbd.nix b/nixos/modules/tasks/kbd.nix
index 7fb3cbc5c1bc..fbe42b8e8f04 100644
--- a/nixos/modules/tasks/kbd.nix
+++ b/nixos/modules/tasks/kbd.nix
@@ -13,7 +13,7 @@ let
   isUnicode = hasSuffix "UTF-8" (toUpper config.i18n.defaultLocale);
 
   optimizedKeymap = pkgs.runCommand "keymap" {
-    nativeBuildInputs = [ pkgs.kbd ];
+    nativeBuildInputs = [ pkgs.buildPackages.kbd ];
     LOADKEYS_KEYMAP_PATH = "${kbdEnv}/share/keymaps/**";
   } ''
     loadkeys -b ${optionalString isUnicode "-u"} "${config.i18n.consoleKeyMap}" > $out
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 5036b701bd86..a2d2eb1c3119 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -26,7 +26,7 @@ let
     executable = true;
     destination = "/bin/bridge-stp";
     text = ''
-      #!${pkgs.stdenv.shell} -e
+      #!${pkgs.runtimeShell} -e
       export PATH="${pkgs.mstpd}/bin"
 
       BRIDGES=(${concatStringsSep " " (attrNames rstpBridges)})
@@ -64,7 +64,7 @@ let
 
   # udev script that configures a physical wlan device and adds virtual interfaces
   wlanDeviceUdevScript = device: interfaceList: pkgs.writeScript "wlan-${device}-udev-script" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
 
     # Change the wireless phy device to a predictable name.
     if [ -e "/sys/class/net/${device}/phy80211/name" ]; then
@@ -142,7 +142,7 @@ let
         default = { };
         example = { mtu = "1492"; window = "524288"; };
         description = ''
-          Other route options. See the symbol <literal>OPTION</literal>
+          Other route options. See the symbol <literal>OPTIONS</literal>
           in the <literal>ip-route(8)</literal> manual page for the details.
         '';
       };
@@ -191,7 +191,7 @@ let
       preferTempAddress = mkOption {
         type = types.bool;
         default = cfg.enableIPv6;
-        defaultText = literalExample "config.networking.enableIpv6";
+        defaultText = literalExample "config.networking.enableIPv6";
         description = ''
           When using SLAAC prefer a temporary (IPv6) address over the EUI-64
           address for originating connections. This is used to reduce tracking.
@@ -489,7 +489,7 @@ in
     networking.interfaces = mkOption {
       default = {};
       example =
-        { eth0.ipv4 = [ {
+        { eth0.ipv4.addresses = [ {
             address = "131.211.84.78";
             prefixLength = 25;
           } ];
@@ -1158,7 +1158,7 @@ in
             # The script creates the required, new WLAN interfaces interfaces and configures the
             # existing, default interface.
             curInterfaceScript = device: current: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${device}.sh" ''
-              #!${pkgs.stdenv.shell}
+              #!${pkgs.runtimeShell}
               # Change the wireless phy device to a predictable name.
               ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/$INTERFACE/phy80211/name` set name ${device}
 
@@ -1177,7 +1177,7 @@ in
 
             # Udev script to execute for a new WLAN interface. The script configures the new WLAN interface.
             newInterfaceScript = device: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${new._iName}.sh" ''
-              #!${pkgs.stdenv.shell}
+              #!${pkgs.runtimeShell}
               # Configure the new interface
               ${pkgs.iw}/bin/iw dev ${new._iName} set type ${new.type}
               ${optionalString (new.type == "mesh" && new.meshID!=null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${new.meshID}"}
diff --git a/nixos/modules/virtualisation/amazon-init.nix b/nixos/modules/virtualisation/amazon-init.nix
index a7362423eb46..8032b2c6d7ca 100644
--- a/nixos/modules/virtualisation/amazon-init.nix
+++ b/nixos/modules/virtualisation/amazon-init.nix
@@ -2,7 +2,7 @@
 
 let
   script = ''
-    #!${pkgs.stdenv.shell} -eu
+    #!${pkgs.runtimeShell} -eu
 
     echo "attempting to fetch configuration from EC2 user data..."
 
diff --git a/nixos/modules/virtualisation/azure-agent.nix b/nixos/modules/virtualisation/azure-agent.nix
index 6817eb837a01..201d5f71ba34 100644
--- a/nixos/modules/virtualisation/azure-agent.nix
+++ b/nixos/modules/virtualisation/azure-agent.nix
@@ -47,7 +47,7 @@ let
   };
 
   provisionedHook = pkgs.writeScript "provisioned-hook" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
     ${config.systemd.package}/bin/systemctl start provisioned.target
   '';
 
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index 4038454b2d2f..e54a5fe7d40c 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -33,7 +33,7 @@ let
     in
       pkgs.writeScript "container-init"
       ''
-        #! ${pkgs.stdenv.shell} -e
+        #! ${pkgs.runtimeShell} -e
 
         # Initialise the container side of the veth pair.
         if [ "$PRIVATE_NETWORK" = 1 ]; then
@@ -223,7 +223,7 @@ let
   serviceDirectives = cfg: {
     ExecReload = pkgs.writeScript "reload-container"
       ''
-        #! ${pkgs.stdenv.shell} -e
+        #! ${pkgs.runtimeShell} -e
         ${pkgs.nixos-container}/bin/nixos-container run "$INSTANCE" -- \
           bash --login -c "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/bin/switch-to-configuration test"
       '';
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index a369b7ddbe1d..024db7f87c2e 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -119,18 +119,10 @@ in {
       after = [ "systemd-udev-settle.service" ]
               ++ optional vswitch.enable "vswitchd.service";
 
-      environment = {
-        LIBVIRTD_ARGS = ''--config "${configFile}" ${concatStringsSep " " cfg.extraOptions}'';
-      };
+      environment.LIBVIRTD_ARGS = ''--config "${configFile}" ${concatStringsSep " " cfg.extraOptions}'';
 
-      path = with pkgs; [
-          bridge-utils
-          dmidecode
-          dnsmasq
-          ebtables
-          cfg.qemuPackage # libvirtd requires qemu-img to manage disk images
-        ]
-        ++ optional vswitch.enable vswitch.package;
+      path = [ cfg.qemuPackage ] # libvirtd requires qemu-img to manage disk images
+             ++ optional vswitch.enable vswitch.package;
 
       preStart = ''
         mkdir -p /var/log/libvirt/qemu -m 755
diff --git a/nixos/modules/virtualisation/openvswitch.nix b/nixos/modules/virtualisation/openvswitch.nix
index 4218a3840fc1..38b138e06326 100644
--- a/nixos/modules/virtualisation/openvswitch.nix
+++ b/nixos/modules/virtualisation/openvswitch.nix
@@ -169,7 +169,7 @@ in {
         mkdir -p ${runDir}/ipsec/{etc/racoon,etc/init.d/,usr/sbin/}
         ln -fs ${pkgs.ipsecTools}/bin/setkey ${runDir}/ipsec/usr/sbin/setkey
         ln -fs ${pkgs.writeScript "racoon-restart" ''
-        #!${pkgs.stdenv.shell}
+        #!${pkgs.runtimeShell}
         /var/run/current-system/sw/bin/systemctl $1 racoon
         ''} ${runDir}/ipsec/etc/init.d/racoon
       '';
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index 13d0eb7de5c2..ee327ed805b2 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -30,7 +30,7 @@ let
   # Shell script to start the VM.
   startVM =
     ''
-      #! ${pkgs.stdenv.shell}
+      #! ${pkgs.runtimeShell}
 
       NIX_DISK_IMAGE=$(readlink -f ''${NIX_DISK_IMAGE:-${config.virtualisation.diskImage}})
 
@@ -319,8 +319,8 @@ in
       networkingOptions =
         mkOption {
           default = [
-            "-net nic,vlan=0,model=virtio"
-            "-net user,vlan=0\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
+            "-net nic,netdev=user.0,model=virtio"
+            "-netdev user,id=user.0\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
           ];
           type = types.listOf types.str;
           description = ''
@@ -434,9 +434,11 @@ in
 
     virtualisation.pathsInNixDB = [ config.system.build.toplevel ];
 
-    # FIXME: Figure out how to make this work on non-x86
-    virtualisation.qemu.options =
-      mkIf (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [ "-vga std" "-usbdevice tablet" ];
+    # FIXME: Consolidate this one day.
+    virtualisation.qemu.options = mkMerge [
+      (mkIf (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [ "-vga std" "-usb" "-device usb-tablet,bus=usb-bus.0" ])
+      (mkIf (pkgs.stdenv.isArm || pkgs.stdenv.isAarch64) [ "-device virtio-gpu-pci" "-device usb-ehci,id=usb0" "-device usb-kbd" "-device usb-tablet" ])
+    ];
 
     # Mount the host filesystem via 9P, and bind-mount the Nix store
     # of the host into our own filesystem.  We use mkVMOverride to
diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix
index afc5a42f8b4e..cf57868acef9 100644
--- a/nixos/modules/virtualisation/xen-dom0.nix
+++ b/nixos/modules/virtualisation/xen-dom0.nix
@@ -241,6 +241,12 @@ in
           '';
           target = "default/xendomains";
         }
+      ]
+      ++ lib.optionals (builtins.compareVersions cfg.package.version "4.10" >= 0) [
+        # in V 4.10 oxenstored requires /etc/xen/oxenstored.conf to start
+        { source = "${cfg.package}/etc/xen/oxenstored.conf";
+          target = "xen/oxenstored.conf";
+        }
       ];
 
     # Xen provides udev rules.
@@ -262,7 +268,7 @@ in
         mkdir -p /var/lib/xen # so we create them here unconditionally.
         grep -q control_d /proc/xen/capabilities
         '';
-      serviceConfig = if cfg.package.version < "4.8" then
+      serviceConfig = if (builtins.compareVersions cfg.package.version "4.8" < 0) then
         { ExecStart = ''
             ${cfg.stored}${optionalString cfg.trace " -T /var/log/xen/xenstored-trace.log"} --no-fork
             '';
@@ -275,7 +281,7 @@ in
           NotifyAccess    = "all";
         };
       postStart = ''
-        ${optionalString (cfg.package.version < "4.8") ''
+        ${optionalString (builtins.compareVersions cfg.package.version "4.8" < 0) ''
           time=0
           timeout=30
           # Wait for xenstored to actually come up, timing out after 30 seconds
@@ -320,7 +326,7 @@ in
       serviceConfig = {
         ExecStart = ''
           ${cfg.package}/bin/xenconsoled\
-            ${optionalString ((cfg.package.version >= "4.8")) " -i"}\
+            ${optionalString ((builtins.compareVersions cfg.package.version "4.8" >= 0)) " -i"}\
             ${optionalString cfg.trace " --log=all --log-dir=/var/log/xen"}
           '';
       };