about summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/hardware/brightnessctl.nix31
-rw-r--r--nixos/modules/installer/cd-dvd/channel.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix3
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix8
-rw-r--r--nixos/modules/installer/tools/nixos-build-vms/build-vms.nix2
-rw-r--r--nixos/modules/misc/version.nix6
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/modules/programs/fish_completion-generator.patch12
-rw-r--r--nixos/modules/programs/zsh/zsh.nix3
-rw-r--r--nixos/modules/rename.nix6
-rw-r--r--nixos/modules/services/audio/alsa.nix6
-rw-r--r--nixos/modules/services/databases/postgresql.nix35
-rw-r--r--nixos/modules/services/databases/redis.nix47
-rw-r--r--nixos/modules/services/networking/i2pd.nix22
-rw-r--r--nixos/modules/services/networking/knot.nix40
-rw-r--r--nixos/modules/services/networking/pppd.nix4
-rw-r--r--nixos/modules/services/networking/shorewall.nix8
-rw-r--r--nixos/modules/services/networking/shorewall6.nix8
-rw-r--r--nixos/modules/services/web-apps/jirafeau.nix169
-rw-r--r--nixos/modules/services/web-servers/caddy.nix18
-rw-r--r--nixos/modules/services/x11/desktop-managers/mate.nix1
-rw-r--r--nixos/modules/services/x11/unclutter.nix2
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix6
-rw-r--r--nixos/modules/tasks/encrypted-devices.nix2
-rw-r--r--nixos/modules/virtualisation/docker-containers.nix29
25 files changed, 342 insertions, 132 deletions
diff --git a/nixos/modules/hardware/brightnessctl.nix b/nixos/modules/hardware/brightnessctl.nix
deleted file mode 100644
index 2d54398d10df..000000000000
--- a/nixos/modules/hardware/brightnessctl.nix
+++ /dev/null
@@ -1,31 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-let
-  cfg = config.hardware.brightnessctl;
-in
-{
-
-  options = {
-
-    hardware.brightnessctl = {
-
-      enable = mkOption {
-        default = false;
-        type = types.bool;
-        description = ''
-          Enable brightnessctl in userspace.
-          This will allow brightness control from users in the video group.
-        '';
-
-      };
-    };
-  };
-
-
-  config = mkIf cfg.enable {
-    services.udev.packages = with pkgs; [ brightnessctl ];
-    environment.systemPackages = with pkgs; [ brightnessctl ];
-  };
-
-}
diff --git a/nixos/modules/installer/cd-dvd/channel.nix b/nixos/modules/installer/cd-dvd/channel.nix
index ab5e7c0645f3..92164d65e533 100644
--- a/nixos/modules/installer/cd-dvd/channel.nix
+++ b/nixos/modules/installer/cd-dvd/channel.nix
@@ -21,7 +21,9 @@ let
       if [ ! -e $out/nixos/nixpkgs ]; then
         ln -s . $out/nixos/nixpkgs
       fi
-      echo -n ${config.system.nixos.revision} > $out/nixos/.git-revision
+      ${optionalString (config.system.nixos.revision != null) ''
+        echo -n ${config.system.nixos.revision} > $out/nixos/.git-revision
+      ''}
       echo -n ${config.system.nixos.versionSuffix} > $out/nixos/.version-suffix
       echo ${config.system.nixos.versionSuffix} | sed -e s/pre// > $out/nixos/svn-revision
     '';
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix
index e0b558dcb0d8..fa19daf13280 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix
@@ -44,6 +44,9 @@ with lib;
     pkgs.bvi # binary editor
     pkgs.joe
 
+    # Include some version control tools.
+    pkgs.git
+
     # Firefox for reading the manual.
     pkgs.firefox
 
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index c2f2578733ba..72b5850a4d92 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,6 +1,6 @@
 {
-  x86_64-linux = "/nix/store/0q5qnh10m2sfrriszc1ysmggw659q6qm-nix-2.3.2";
-  i686-linux = "/nix/store/i7ad7r5d8a5b3l22hg4a1im2qq05y6vd-nix-2.3.2";
-  aarch64-linux = "/nix/store/bv06pavfw0dbqzr8w3l7s71nx27gnxa0-nix-2.3.2";
-  x86_64-darwin = "/nix/store/x6mnl1nij7y4v5ihlplr4k937ayr403r-nix-2.3.2";
+  x86_64-linux = "/nix/store/68mycwwczrciryylq2a66jwfhxp09zsg-nix-2.3.3-debug";
+  i686-linux = "/nix/store/5axys7hsggb4282dsbps5k5p0v59yv13-nix-2.3.3";
+  aarch64-linux = "/nix/store/k80nwvi19hxwbz3c9cxgp24f1jjxwmcc-nix-2.3.3";
+  x86_64-darwin = "/nix/store/lrnvapsqmf0ja6zfyx4cpxr7ahdr7f9b-nix-2.3.3";
 }
diff --git a/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix b/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix
index c1028a0ad7e9..90f0702f7173 100644
--- a/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix
+++ b/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix
@@ -5,7 +5,7 @@
 
 let nodes = import networkExpr; in
 
-with import ../../../../lib/testing.nix {
+with import ../../../../lib/testing-python.nix {
   inherit system;
   pkgs = import ../../../../.. { inherit system config; };
 };
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index c394ff592f49..9557def622d8 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -4,10 +4,6 @@ with lib;
 
 let
   cfg = config.system.nixos;
-
-  gitRepo      = "${toString pkgs.path}/.git";
-  gitRepoValid = lib.pathIsGitRepo gitRepo;
-  gitCommitId  = lib.substring 0 7 (commitIdFromGitRepo gitRepo);
 in
 
 {
@@ -98,8 +94,6 @@ in
       # These defaults are set here rather than up there so that
       # changing them would not rebuild the manual
       version = mkDefault (cfg.release + cfg.versionSuffix);
-      revision      = mkIf gitRepoValid (mkDefault            gitCommitId);
-      versionSuffix = mkIf gitRepoValid (mkDefault (".git." + gitCommitId));
     };
 
     # Generate /etc/os-release.  See
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 6b032f64bdb1..6734929b9d4e 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -41,7 +41,6 @@
   ./hardware/acpilight.nix
   ./hardware/all-firmware.nix
   ./hardware/bladeRF.nix
-  ./hardware/brightnessctl.nix
   ./hardware/brillo.nix
   ./hardware/ckb-next.nix
   ./hardware/cpu/amd-microcode.nix
@@ -821,6 +820,7 @@
   ./services/web-apps/icingaweb2/icingaweb2.nix
   ./services/web-apps/icingaweb2/module-monitoring.nix
   ./services/web-apps/ihatemoney
+  ./services/web-apps/jirafeau.nix
   ./services/web-apps/limesurvey.nix
   ./services/web-apps/mattermost.nix
   ./services/web-apps/mediawiki.nix
diff --git a/nixos/modules/programs/fish_completion-generator.patch b/nixos/modules/programs/fish_completion-generator.patch
index a8c797d185a6..997f38c5066d 100644
--- a/nixos/modules/programs/fish_completion-generator.patch
+++ b/nixos/modules/programs/fish_completion-generator.patch
@@ -1,11 +1,13 @@
 --- a/create_manpage_completions.py
 +++ b/create_manpage_completions.py
-@@ -776,8 +776,6 @@ def parse_manpage_at_path(manpage_path, output_directory):
-
+@@ -844,10 +844,6 @@ def parse_manpage_at_path(manpage_path, output_directory):
+ 
              built_command_output.insert(0, "# " + CMDNAME)
-
+ 
 -            # Output the magic word Autogenerated so we can tell if we can overwrite this
--            built_command_output.insert(1, "# Autogenerated from man page " + manpage_path)
+-            built_command_output.insert(
+-                1, "# Autogenerated from man page " + manpage_path
+-            )
              # built_command_output.insert(2, "# using " + parser.__class__.__name__) # XXX MISATTRIBUTES THE CULPABILE PARSER! Was really using Type2 but reporting TypeDeroffManParser
-
+ 
              for line in built_command_output:
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index 4fbdba47b1df..930cc1987a33 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -162,9 +162,8 @@ in
         # This file is read for all shells.
 
         # Only execute this file once per shell.
-        # But don't clobber the environment of interactive non-login children!
         if [ -n "$__ETC_ZSHENV_SOURCED" ]; then return; fi
-        export __ETC_ZSHENV_SOURCED=1
+        __ETC_ZSHENV_SOURCED=1
 
         if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]; then
             . ${config.system.build.setEnvironment}
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 3b1b1b8bb55c..2cc6c46e3581 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -42,6 +42,12 @@ with lib;
       instead, or any other display manager in NixOS as they all support auto-login.
     '')
     (mkRemovedOptionModule [ "services" "dnscrypt-proxy" ] "Use services.dnscrypt-proxy2 instead")
+    (mkRemovedOptionModule ["hardware" "brightnessctl" ] ''
+      The brightnessctl module was removed because newer versions of
+      brightnessctl don't require the udev rules anymore (they can use the
+      systemd-logind API). Instead of using the module you can now
+      simply add the brightnessctl package to environment.systemPackages.
+    '')
 
     # Do NOT add any option renames here, see top of the file
   ];
diff --git a/nixos/modules/services/audio/alsa.nix b/nixos/modules/services/audio/alsa.nix
index 990398e65463..3fe76a165401 100644
--- a/nixos/modules/services/audio/alsa.nix
+++ b/nixos/modules/services/audio/alsa.nix
@@ -91,11 +91,7 @@ in
     environment.systemPackages = [ alsaUtils ];
 
     environment.etc = mkIf (!pulseaudioEnabled && config.sound.extraConfig != "")
-      [
-        { source = pkgs.writeText "asound.conf" config.sound.extraConfig;
-          target = "asound.conf";
-        }
-      ];
+      { "asound.conf".text = config.sound.extraConfig; };
 
     # ALSA provides a udev rule for restoring volume settings.
     services.udev.packages = [ alsaUtils ];
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index c8fdd89d0d8f..0b79a996dc78 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -20,7 +20,9 @@ let
       listen_addresses = '${if cfg.enableTCPIP then "*" else "localhost"}'
       port = ${toString cfg.port}
       ${cfg.extraConfig}
-    '';
+    ''; 
+
+  groupAccessAvailable = versionAtLeast postgresql.version "11.0";
 
 in
 
@@ -88,6 +90,16 @@ in
         '';
       };
 
+      initdbArgs = mkOption {
+        type = with types; listOf str;
+        default = [];
+        example = [ "--data-checksums" "--allow-group-access" ];
+        description = ''
+          Additional arguments passed to <literal>initdb</literal> during data dir
+          initialisation.
+        '';
+      };
+
       initialScript = mkOption {
         type = types.nullOr types.path;
         default = null;
@@ -220,7 +232,7 @@ in
 
   ###### implementation
 
-  config = mkIf config.services.postgresql.enable {
+  config = mkIf cfg.enable {
 
     services.postgresql.package =
       # Note: when changing the default, make it conditional on
@@ -232,13 +244,14 @@ in
             else throw "postgresql_9_4 was removed, please upgrade your postgresql version.");
 
     services.postgresql.dataDir =
-      mkDefault (if versionAtLeast config.system.stateVersion "17.09" then "/var/lib/postgresql/${config.services.postgresql.package.psqlSchema}"
-                 else "/var/db/postgresql");
+      mkDefault (if versionAtLeast config.system.stateVersion "17.09"
+                  then "/var/lib/postgresql/${cfg.package.psqlSchema}"
+                  else "/var/db/postgresql");
 
     services.postgresql.authentication = mkAfter
       ''
         # Generated file; do not edit!
-        local all all              ident
+        local all all              peer
         host  all all 127.0.0.1/32 md5
         host  all all ::1/128      md5
       '';
@@ -284,7 +297,7 @@ in
           ''
             # Initialise the database.
             if ! test -e ${cfg.dataDir}/PG_VERSION; then
-              initdb -U ${cfg.superUser}
+              initdb -U ${cfg.superUser} ${concatStringsSep " " cfg.initdbArgs}
               # See postStart!
               touch "${cfg.dataDir}/.first_startup"
             fi
@@ -293,8 +306,12 @@ in
               ln -sfn "${pkgs.writeText "recovery.conf" cfg.recoveryConfig}" \
                 "${cfg.dataDir}/recovery.conf"
             ''}
+            ${optionalString (!groupAccessAvailable) ''
+              # postgresql pre 11.0 doesn't start if state directory mode is group accessible
+              chmod 0700 "${cfg.dataDir}"
+            ''}
 
-             exec postgres
+            exec postgres
           '';
 
         serviceConfig =
@@ -303,7 +320,7 @@ in
             Group = "postgres";
             PermissionsStartOnly = true;
             RuntimeDirectory = "postgresql";
-            Type = if lib.versionAtLeast cfg.package.version "9.6"
+            Type = if versionAtLeast cfg.package.version "9.6"
                    then "notify"
                    else "simple";
 
@@ -352,5 +369,5 @@ in
   };
 
   meta.doc = ./postgresql.xml;
-  meta.maintainers = with lib.maintainers; [ thoughtpolice ];
+  meta.maintainers = with lib.maintainers; [ thoughtpolice danbst ];
 }
diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix
index 70895fa53e45..5c817422aae5 100644
--- a/nixos/modules/services/databases/redis.nix
+++ b/nixos/modules/services/databases/redis.nix
@@ -150,10 +150,20 @@ in
       requirePass = mkOption {
         type = with types; nullOr str;
         default = null;
-        description = "Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)";
+        description = ''
+          Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE).
+          Use requirePassFile to store it outside of the nix store in a dedicated file.
+        '';
         example = "letmein!";
       };
 
+      requirePassFile = mkOption {
+        type = with types; nullOr path;
+        default = null;
+        description = "File with password for the database.";
+        example = "/run/keys/redis-password";
+      };
+
       appendOnly = mkOption {
         type = types.bool;
         default = false;
@@ -192,6 +202,10 @@ in
   ###### implementation
 
   config = mkIf config.services.redis.enable {
+    assertions = [{
+      assertion = cfg.requirePass != null -> cfg.requirePassFile == null;
+      message = "You can only set one services.redis.requirePass or services.redis.requirePassFile";
+    }];
     boot.kernel.sysctl = (mkMerge [
       { "vm.nr_hugepages" = "0"; }
       ( mkIf cfg.vmOverCommit { "vm.overcommit_memory" = "1"; } )
@@ -208,21 +222,26 @@ in
 
     environment.systemPackages = [ cfg.package ];
 
-    systemd.services.redis =
-      { description = "Redis Server";
+    systemd.services.redis = {
+      description = "Redis Server";
 
-        wantedBy = [ "multi-user.target" ];
-        after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
 
-        serviceConfig = {
-          ExecStart = "${cfg.package}/bin/redis-server ${redisConfig}";
-          RuntimeDirectory = "redis";
-          StateDirectory = "redis";
-          Type = "notify";
-          User = "redis";
-        };
-      };
+      preStart = ''
+        install -m 600 ${redisConfig} /run/redis/redis.conf
+      '' + optionalString (cfg.requirePassFile != null) ''
+        password=$(cat ${escapeShellArg cfg.requirePassFile})
+        echo "requirePass $password" >> /run/redis/redis.conf
+      '';
 
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/redis-server /run/redis/redis.conf";
+        RuntimeDirectory = "redis";
+        StateDirectory = "redis";
+        Type = "notify";
+        User = "redis";
+      };
+    };
   };
-
 }
diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix
index 326d34f6ca92..93a21fd4c97e 100644
--- a/nixos/modules/services/networking/i2pd.nix
+++ b/nixos/modules/services/networking/i2pd.nix
@@ -158,10 +158,10 @@ let
       (sec "addressbook")
       (strOpt "defaulturl" cfg.addressbook.defaulturl)
     ] ++ (optionalEmptyList "subscriptions" cfg.addressbook.subscriptions)
-      ++ (flip mapAttrs
-      (collect (name: proto: proto ? port && proto ? address && proto ? name) cfg.proto)
+      ++ (flip map
+      (collect (proto: proto ? port && proto ? address) cfg.proto)
       (proto: let protoOpts = [
-        (sec name)
+        (sec proto.name)
         (boolOpt "enabled" proto.enable)
         (strOpt "address" proto.address)
         (intOpt "port" proto.port)
@@ -181,10 +181,10 @@ let
 
   tunnelConf = let opts = [
     notice
-    (flip mapAttrs
-      (collect (name: tun: tun ? port && tun ? destination) cfg.outTunnels)
+    (flip map
+      (collect (tun: tun ? port && tun ? destination) cfg.outTunnels)
       (tun: let outTunOpts = [
-        (sec name)
+        (sec tun.name)
         "type = client"
         (intOpt "port" tun.port)
         (strOpt "destination" tun.destination)
@@ -204,10 +204,10 @@ let
         ++ (if tun ? crypto.tagsToSend then
             optionalNullInt "crypto.tagstosend" tun.crypto.tagsToSend else []);
         in concatStringsSep "\n" outTunOpts))
-    (flip mapAttrs
-      (collect (name: tun: tun ? port && tun ? address) cfg.inTunnels)
+    (flip map
+      (collect (tun: tun ? port && tun ? address) cfg.inTunnels)
       (tun: let inTunOpts = [
-        (sec name)
+        (sec tun.name)
         "type = server"
         (intOpt "port" tun.port)
         (strOpt "host" tun.address)
@@ -606,7 +606,7 @@ in
 
       outTunnels = mkOption {
         default = {};
-        type = with types; loaOf (submodule (
+        type = with types; attrsOf (submodule (
           { name, ... }: {
             options = {
               destinationPort = mkOption {
@@ -627,7 +627,7 @@ in
 
       inTunnels = mkOption {
         default = {};
-        type = with types; loaOf (submodule (
+        type = with types; attrsOf (submodule (
           { name, ... }: {
             options = {
               inPort = mkOption {
diff --git a/nixos/modules/services/networking/knot.nix b/nixos/modules/services/networking/knot.nix
index 47364ecb8464..12ff89fe8492 100644
--- a/nixos/modules/services/networking/knot.nix
+++ b/nixos/modules/services/networking/knot.nix
@@ -5,14 +5,16 @@ with lib;
 let
   cfg = config.services.knot;
 
-  configFile = pkgs.writeText "knot.conf" cfg.extraConfig;
-  socketFile = "/run/knot/knot.sock";
+  configFile = pkgs.writeTextFile {
+    name = "knot.conf";
+    text = (concatMapStringsSep "\n" (file: "include: ${file}") cfg.keyFiles) + "\n" +
+           cfg.extraConfig;
+    checkPhase = lib.optionalString (cfg.keyFiles == []) ''
+      ${cfg.package}/bin/knotc --config=$out conf-check
+    '';
+  };
 
-  knotConfCheck = file: pkgs.runCommand "knot-config-checked"
-    { buildInputs = [ cfg.package ]; } ''
-    ln -s ${configFile} $out
-    knotc --config=${configFile} conf-check
-  '';
+  socketFile = "/run/knot/knot.sock";
 
   knot-cli-wrappers = pkgs.stdenv.mkDerivation {
     name = "knot-cli-wrappers";
@@ -45,6 +47,19 @@ in {
         '';
       };
 
+      keyFiles = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = ''
+          A list of files containing additional configuration
+          to be included using the include directive. This option
+          allows to include configuration like TSIG keys without
+          exposing them to the nix store readable to any process.
+          Note that using this option will also disable configuration
+          checks at build time.
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
@@ -65,6 +80,13 @@ in {
   };
 
   config = mkIf config.services.knot.enable {
+    users.users.knot = {
+      isSystemUser = true;
+      group = "knot";
+      description = "Knot daemon user";
+    };
+
+    users.groups.knot.gid = null;
     systemd.services.knot = {
       unitConfig.Documentation = "man:knotd(8) man:knot.conf(5) man:knotc(8) https://www.knot-dns.cz/docs/${cfg.package.version}/html/";
       description = cfg.package.meta.description;
@@ -74,12 +96,12 @@ in {
 
       serviceConfig = {
         Type = "notify";
-        ExecStart = "${cfg.package}/bin/knotd --config=${knotConfCheck configFile} --socket=${socketFile} ${concatStringsSep " " cfg.extraArgs}";
+        ExecStart = "${cfg.package}/bin/knotd --config=${configFile} --socket=${socketFile} ${concatStringsSep " " cfg.extraArgs}";
         ExecReload = "${knot-cli-wrappers}/bin/knotc reload";
         CapabilityBoundingSet = "CAP_NET_BIND_SERVICE CAP_SETPCAP";
         AmbientCapabilities = "CAP_NET_BIND_SERVICE CAP_SETPCAP";
         NoNewPrivileges = true;
-        DynamicUser = "yes";
+        User = "knot";
         RuntimeDirectory = "knot";
         StateDirectory = "knot";
         StateDirectoryMode = "0700";
diff --git a/nixos/modules/services/networking/pppd.nix b/nixos/modules/services/networking/pppd.nix
index b31bfa642358..c1cbdb461765 100644
--- a/nixos/modules/services/networking/pppd.nix
+++ b/nixos/modules/services/networking/pppd.nix
@@ -130,7 +130,7 @@ in
     systemdConfigs = listToAttrs (map mkSystemd enabledConfigs);
 
   in mkIf cfg.enable {
-    environment.etc = mkMerge etcFiles;
-    systemd.services = mkMerge systemdConfigs;
+    environment.etc = etcFiles;
+    systemd.services = systemdConfigs;
   };
 }
diff --git a/nixos/modules/services/networking/shorewall.nix b/nixos/modules/services/networking/shorewall.nix
index 0f94d414fcf7..c59a53669158 100644
--- a/nixos/modules/services/networking/shorewall.nix
+++ b/nixos/modules/services/networking/shorewall.nix
@@ -33,7 +33,6 @@ in {
           The attribute name defines the name of the config,
           and the attribute value defines the content of the config.
         '';
-        apply = lib.mapAttrs (name: text: pkgs.writeText "${name}" text);
       };
     };
   };
@@ -63,12 +62,7 @@ in {
       '';
     };
     environment = {
-      etc = lib.mapAttrsToList
-              (name: file:
-                { source = file;
-                  target = "shorewall/${name}";
-                })
-              cfg.configs;
+      etc = lib.mapAttrs' (name: conf: lib.nameValuePair "shorewall/${name}" {text=conf;}) cfg.configs;
       systemPackages = [ cfg.package ];
     };
   };
diff --git a/nixos/modules/services/networking/shorewall6.nix b/nixos/modules/services/networking/shorewall6.nix
index 9c22a037c0b4..374e407cc7a1 100644
--- a/nixos/modules/services/networking/shorewall6.nix
+++ b/nixos/modules/services/networking/shorewall6.nix
@@ -33,7 +33,6 @@ in {
           The attribute name defines the name of the config,
           and the attribute value defines the content of the config.
         '';
-        apply = lib.mapAttrs (name: text: pkgs.writeText "${name}" text);
       };
     };
   };
@@ -63,12 +62,7 @@ in {
       '';
     };
     environment = {
-      etc = lib.mapAttrsToList
-              (name: file:
-                { source = file;
-                  target = "shorewall6/${name}";
-                })
-              cfg.configs;
+      etc = lib.mapAttrs' (name: conf: lib.nameValuePair "shorewall6/${name}" {text=conf;}) cfg.configs;
       systemPackages = [ cfg.package ];
     };
   };
diff --git a/nixos/modules/services/web-apps/jirafeau.nix b/nixos/modules/services/web-apps/jirafeau.nix
new file mode 100644
index 000000000000..4f181257ef7c
--- /dev/null
+++ b/nixos/modules/services/web-apps/jirafeau.nix
@@ -0,0 +1,169 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.jirafeau;
+
+  group = config.services.nginx.group;
+  user = config.services.nginx.user;
+
+  withTrailingSlash = str: if hasSuffix "/" str then str else "${str}/";
+
+  localConfig = pkgs.writeText "config.local.php" ''
+    <?php
+      $cfg['admin_password'] = '${cfg.adminPasswordSha256}';
+      $cfg['web_root'] = 'http://${withTrailingSlash cfg.hostName}';
+      $cfg['var_root'] = '${withTrailingSlash cfg.dataDir}';
+      $cfg['maximal_upload_size'] = ${builtins.toString cfg.maxUploadSizeMegabytes};
+      $cfg['installation_done'] = true;
+
+      ${cfg.extraConfig}
+  '';
+in
+{
+  options.services.jirafeau = {
+    adminPasswordSha256 = mkOption {
+      type = types.str;
+      default = "";
+      description = ''
+        SHA-256 of the desired administration password. Leave blank/unset for no password.
+      '';
+    };
+
+    dataDir = mkOption {
+      type = types.path;
+      default = "/var/lib/jirafeau/data/";
+      description = "Location of Jirafeau storage directory.";
+    };
+
+    enable = mkEnableOption "Jirafeau file upload application.";
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      example = ''
+        $cfg['style'] = 'courgette';
+        $cfg['organisation'] = 'ACME';
+      '';
+      description = let
+        documentationLink =
+          "https://gitlab.com/mojo42/Jirafeau/-/blob/${cfg.package.version}/lib/config.original.php";
+      in
+        ''
+          Jirefeau configuration. Refer to <link xlink:href="${documentationLink}"/> for supported
+          values.
+        '';
+    };
+
+    hostName = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = "URL of instance. Must have trailing slash.";
+    };
+
+    maxUploadSizeMegabytes = mkOption {
+      type = types.int;
+      default = 0;
+      description = "Maximum upload size of accepted files.";
+    };
+
+    maxUploadTimeout = mkOption {
+      type = types.str;
+      default = "30m";
+      description = let
+        nginxCoreDocumentation = "http://nginx.org/en/docs/http/ngx_http_core_module.html";
+      in
+        ''
+          Timeout for reading client request bodies and headers. Refer to
+          <link xlink:href="${nginxCoreDocumentation}#client_body_timeout"/> and
+          <link xlink:href="${nginxCoreDocumentation}#client_header_timeout"/> for accepted values.
+        '';
+    };
+
+    nginxConfig = mkOption {
+      type = types.submodule
+        (import ../web-servers/nginx/vhost-options.nix { inherit config lib; });
+      default = {};
+      example = {
+        serverAliases = [ "wiki.\${config.networking.domain}" ];
+      };
+      description = "Extra configuration for the nginx virtual host of Jirafeau.";
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.jirafeau;
+      defaultText = "pkgs.jirafeau";
+      description = "Jirafeau package to use";
+      example = "pkgs.jirafeau";
+    };
+
+    poolConfig = mkOption {
+      type = with types; attrsOf (oneOf [ str int bool ]);
+      default = {
+        "pm" = "dynamic";
+        "pm.max_children" = 32;
+        "pm.start_servers" = 2;
+        "pm.min_spare_servers" = 2;
+        "pm.max_spare_servers" = 4;
+        "pm.max_requests" = 500;
+      };
+      description = ''
+        Options for Jirafeau PHP pool. See documentation on <literal>php-fpm.conf</literal> for
+        details on configuration directives.
+      '';
+    };
+  };
+
+
+  config = mkIf cfg.enable {
+    services = {
+      nginx = {
+        enable = true;
+        virtualHosts."${cfg.hostName}" = mkMerge [
+          cfg.nginxConfig
+          {
+            extraConfig = let
+              clientMaxBodySize =
+                if cfg.maxUploadSizeMegabytes == 0 then "0" else "${cfg.maxUploadSizeMegabytes}m";
+            in
+              ''
+                index index.php;
+                client_max_body_size ${clientMaxBodySize};
+                client_body_timeout ${cfg.maxUploadTimeout};
+                client_header_timeout ${cfg.maxUploadTimeout};
+              '';
+            locations = {
+              "~ \\.php$".extraConfig = ''
+                include ${pkgs.nginx}/conf/fastcgi_params;
+                fastcgi_split_path_info ^(.+\.php)(/.+)$;
+                fastcgi_index index.php;
+                fastcgi_pass unix:${config.services.phpfpm.pools.jirafeau.socket};
+                fastcgi_param PATH_INFO $fastcgi_path_info;
+                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+              '';
+            };
+            root = mkForce "${cfg.package}";
+          }
+        ];
+      };
+
+      phpfpm.pools.jirafeau = {
+        inherit group user;
+        phpEnv."JIRAFEAU_CONFIG" = "${localConfig}";
+        settings = {
+          "listen.mode" = "0660";
+          "listen.owner" = user;
+          "listen.group" = group;
+        } // cfg.poolConfig;
+      };
+    };
+
+    systemd.tmpfiles.rules = [
+      "d ${cfg.dataDir} 0750 ${user} ${group} - -"
+      "d ${cfg.dataDir}/files/ 0750 ${user} ${group} - -"
+      "d ${cfg.dataDir}/links/ 0750 ${user} ${group} - -"
+      "d ${cfg.dataDir}/async/ 0750 ${user} ${group} - -"
+    ];
+  };
+}
diff --git a/nixos/modules/services/web-servers/caddy.nix b/nixos/modules/services/web-servers/caddy.nix
index 132c50735d96..0e6e10a5f47d 100644
--- a/nixos/modules/services/web-servers/caddy.nix
+++ b/nixos/modules/services/web-servers/caddy.nix
@@ -64,32 +64,38 @@ in {
   config = mkIf cfg.enable {
     systemd.services.caddy = {
       description = "Caddy web server";
+      # upstream unit: https://github.com/caddyserver/caddy/blob/master/dist/init/linux-systemd/caddy.service
       after = [ "network-online.target" ];
+      wants = [ "network-online.target" ]; # systemd-networkd-wait-online.service
       wantedBy = [ "multi-user.target" ];
       environment = mkIf (versionAtLeast config.system.stateVersion "17.09")
         { CADDYPATH = cfg.dataDir; };
       serviceConfig = {
         ExecStart = ''
-          ${cfg.package}/bin/caddy -root=/var/tmp -conf=${configFile} \
+          ${cfg.package}/bin/caddy -log stdout -log-timestamps=false \
+            -root=/var/tmp -conf=${configFile} \
             -ca=${cfg.ca} -email=${cfg.email} ${optionalString cfg.agree "-agree"}
         '';
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        ExecReload = "${pkgs.coreutils}/bin/kill -USR1 $MAINPID";
         Type = "simple";
         User = "caddy";
         Group = "caddy";
-        Restart = "on-failure";
-        StartLimitInterval = 86400;
-        StartLimitBurst = 5;
+        Restart = "on-abnormal";
+        StartLimitIntervalSec = 14400;
+        StartLimitBurst = 10;
         AmbientCapabilities = "cap_net_bind_service";
         CapabilityBoundingSet = "cap_net_bind_service";
         NoNewPrivileges = true;
-        LimitNPROC = 64;
+        LimitNPROC = 512;
         LimitNOFILE = 1048576;
         PrivateTmp = true;
         PrivateDevices = true;
         ProtectHome = true;
         ProtectSystem = "full";
         ReadWriteDirectories = cfg.dataDir;
+        KillMode = "mixed";
+        KillSignal = "SIGQUIT";
+        TimeoutStopSec = "5s";
       };
     };
 
diff --git a/nixos/modules/services/x11/desktop-managers/mate.nix b/nixos/modules/services/x11/desktop-managers/mate.nix
index 4a6f2ca727d9..910a246d776c 100644
--- a/nixos/modules/services/x11/desktop-managers/mate.nix
+++ b/nixos/modules/services/x11/desktop-managers/mate.nix
@@ -86,6 +86,7 @@ in
         pkgs.shared-mime-info
         pkgs.xdg-user-dirs # Update user dirs as described in https://freedesktop.org/wiki/Software/xdg-user-dirs/
         pkgs.mate.mate-settings-daemon
+        pkgs.yelp # for 'Contents' in 'Help' menus
       ];
 
     programs.dconf.enable = true;
diff --git a/nixos/modules/services/x11/unclutter.nix b/nixos/modules/services/x11/unclutter.nix
index c0868604a688..56e30c79d1f1 100644
--- a/nixos/modules/services/x11/unclutter.nix
+++ b/nixos/modules/services/x11/unclutter.nix
@@ -61,7 +61,7 @@ in {
       serviceConfig.ExecStart = ''
         ${cfg.package}/bin/unclutter \
           -idle ${toString cfg.timeout} \
-          -jitter ${toString (cfg.threeshold - 1)} \
+          -jitter ${toString (cfg.threshold - 1)} \
           ${optionalString cfg.keystroke "-keystroke"} \
           ${concatMapStrings (x: " -"+x) cfg.extraOptions} \
           -not ${concatStringsSep " " cfg.excluded} \
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index 26c1197bf975..b97ef88a7ca0 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -224,7 +224,11 @@ in
 
       extraConfig = mkOption {
         default = "";
-        example = "serial; terminal_output.serial";
+        example = ''
+          serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
+          terminal_input --append serial
+          terminal_output --append serial
+        '';
         type = types.lines;
         description = ''
           Additional GRUB commands inserted in the configuration file
diff --git a/nixos/modules/tasks/encrypted-devices.nix b/nixos/modules/tasks/encrypted-devices.nix
index 2c9231f55236..bc0933f16fec 100644
--- a/nixos/modules/tasks/encrypted-devices.nix
+++ b/nixos/modules/tasks/encrypted-devices.nix
@@ -65,7 +65,7 @@ in
     boot.initrd = {
       luks = {
         devices =
-          map (dev: { name = dev.encrypted.label; device = dev.encrypted.blkDev; } ) keylessEncDevs;
+          builtins.listToAttrs (map (dev: { name = dev.encrypted.label; value = { device = dev.encrypted.blkDev; }; }) keylessEncDevs);
         forceLuksSupportInInitrd = true;
       };
       postMountCommands =
diff --git a/nixos/modules/virtualisation/docker-containers.nix b/nixos/modules/virtualisation/docker-containers.nix
index 216ba2c733fc..5ab990a3d7cc 100644
--- a/nixos/modules/virtualisation/docker-containers.nix
+++ b/nixos/modules/virtualisation/docker-containers.nix
@@ -192,16 +192,34 @@ let
             ["--network=host"]
           '';
         };
+
+        autoStart = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            When enabled, the container is automatically started on boot.
+            If this option is set to false, the container has to be started on-demand via its service.
+          '';
+        };
       };
     };
 
   mkService = name: container: let
     mkAfter = map (x: "docker-${x}.service") container.dependsOn;
   in rec {
-    wantedBy = [ "multi-user.target" ];
+    wantedBy = [] ++ optional (container.autoStart) "multi-user.target";
     after = [ "docker.service" "docker.socket" ] ++ mkAfter;
     requires = after;
-
+    path = [ pkgs.docker ];
+
+    preStart = ''
+      docker rm -f ${name} || true
+      ${optionalString (container.imageFile != null) ''
+        docker load -i ${container.imageFile}
+        ''}
+      '';
+    postStop = "docker rm -f ${name} || true";
+        
     serviceConfig = {
       ExecStart = concatStringsSep " \\\n  " ([
         "${pkgs.docker}/bin/docker run"
@@ -220,12 +238,7 @@ let
         ++ map escapeShellArg container.cmd
       );
 
-      ExecStartPre =
-        ["-${pkgs.docker}/bin/docker rm -f ${name}"] ++
-        (optional (container.imageFile != null) "${pkgs.docker}/bin/docker load -i ${container.imageFile}");
-
-      ExecStop = ''${pkgs.bash}/bin/sh -c "[ $SERVICE_RESULT = success ] || ${pkgs.docker}/bin/docker stop ${name}"'';
-      ExecStopPost = "-${pkgs.docker}/bin/docker rm -f ${name}";
+      ExecStop = ''${pkgs.bash}/bin/sh -c "[ $SERVICE_RESULT = success ] || docker stop ${name}"'';
 
       ### There is no generalized way of supporting `reload` for docker
       ### containers. Some containers may respond well to SIGHUP sent to their