about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/services
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/services')
-rw-r--r--nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix4
-rw-r--r--nixpkgs/nixos/modules/services/backup/borgmatic.nix2
-rw-r--r--nixpkgs/nixos/modules/services/backup/restic.nix40
-rw-r--r--nixpkgs/nixos/modules/services/blockchain/ethereum/erigon.nix4
-rw-r--r--nixpkgs/nixos/modules/services/logging/syslog-ng.nix2
-rw-r--r--nixpkgs/nixos/modules/services/mail/mlmmj.nix17
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/certspotter.md74
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/certspotter.nix143
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/goss.md44
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/goss.nix86
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/ups.nix8
-rw-r--r--nixpkgs/nixos/modules/services/networking/gvpe.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix36
-rw-r--r--nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/spiped.nix5
-rw-r--r--nixpkgs/nixos/modules/services/networking/ssh/sshd.nix86
-rw-r--r--nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix30
-rw-r--r--nixpkgs/nixos/modules/services/networking/tailscale.nix8
-rw-r--r--nixpkgs/nixos/modules/services/networking/unifi.nix4
-rw-r--r--nixpkgs/nixos/modules/services/system/nix-daemon.nix5
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.md42
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.nix125
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mattermost.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/plausible.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/shiori.nix9
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/snipe-it.nix16
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/garage.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/nginx/default.nix1
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/stargazer.nix8
29 files changed, 700 insertions, 117 deletions
diff --git a/nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix b/nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix
index 1fb67ecfe506..205e05f2ed17 100644
--- a/nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix
+++ b/nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix
@@ -37,6 +37,9 @@ in
             enable = mkEnableOption (mdDoc "Wyoming faster-whisper server");
 
             model = mkOption {
+              # Intersection between available and referenced models here:
+              # https://github.com/rhasspy/models/releases/tag/v1.0
+              # https://github.com/rhasspy/rhasspy3/blob/wyoming-v1/programs/asr/faster-whisper/server/wyoming_faster_whisper/download.py#L17-L27
               type = enum [
                 "tiny"
                 "tiny-int8"
@@ -44,7 +47,6 @@ in
                 "base-int8"
                 "small"
                 "small-int8"
-                "medium"
                 "medium-int8"
               ];
               default = "tiny-int8";
diff --git a/nixpkgs/nixos/modules/services/backup/borgmatic.nix b/nixpkgs/nixos/modules/services/backup/borgmatic.nix
index d3ba7628e85d..b27dd2817120 100644
--- a/nixpkgs/nixos/modules/services/backup/borgmatic.nix
+++ b/nixpkgs/nixos/modules/services/backup/borgmatic.nix
@@ -81,7 +81,7 @@ in
   config = mkIf cfg.enable {
 
     warnings = []
-      ++ optional (cfg.settings != null && cfg.settings.location != null)
+      ++ optional (cfg.settings != null && cfg.settings ? location)
         "`services.borgmatic.settings.location` is deprecated, please move your options out of sections to the global scope"
       ++ optional (catAttrs "location" (attrValues cfg.configurations) != [])
         "`services.borgmatic.configurations.<name>.location` is deprecated, please move your options out of sections to the global scope"
diff --git a/nixpkgs/nixos/modules/services/backup/restic.nix b/nixpkgs/nixos/modules/services/backup/restic.nix
index 78220e99c3d1..49a55d056014 100644
--- a/nixpkgs/nixos/modules/services/backup/restic.nix
+++ b/nixpkgs/nixos/modules/services/backup/restic.nix
@@ -23,25 +23,13 @@ in
 
         environmentFile = mkOption {
           type = with types; nullOr str;
-          # added on 2021-08-28, s3CredentialsFile should
-          # be removed in the future (+ remember the warning)
-          default = config.s3CredentialsFile;
+          default = null;
           description = lib.mdDoc ''
             file containing the credentials to access the repository, in the
             format of an EnvironmentFile as described by systemd.exec(5)
           '';
         };
 
-        s3CredentialsFile = mkOption {
-          type = with types; nullOr str;
-          default = null;
-          description = lib.mdDoc ''
-            file containing the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
-            for an S3-hosted repository, in the format of an EnvironmentFile
-            as described by systemd.exec(5)
-          '';
-        };
-
         rcloneOptions = mkOption {
           type = with types; nullOr (attrsOf (oneOf [ str bool ]));
           default = null;
@@ -113,12 +101,15 @@ in
         };
 
         paths = mkOption {
+          # This is nullable for legacy reasons only. We should consider making it a pure listOf
+          # after some time has passed since this comment was added.
           type = types.nullOr (types.listOf types.str);
-          default = null;
+          default = [ ];
           description = lib.mdDoc ''
-            Which paths to backup.  If null or an empty array, no
-            backup command will be run.  This can be used to create a
-            prune-only job.
+            Which paths to backup, in addition to ones specified via
+            `dynamicFilesFrom`.  If null or an empty array and
+            `dynamicFilesFrom` is also null, no backup command will be run.
+             This can be used to create a prune-only job.
           '';
           example = [
             "/var/lib/postgresql"
@@ -231,7 +222,7 @@ in
           description = lib.mdDoc ''
             A script that produces a list of files to back up.  The
             results of this command are given to the '--files-from'
-            option.
+            option. The result is merged with paths specified via `paths`.
           '';
           example = "find /home/matt/git -type d -name .git";
         };
@@ -297,7 +288,6 @@ in
   };
 
   config = {
-    warnings = mapAttrsToList (n: v: "services.restic.backups.${n}.s3CredentialsFile is deprecated, please use services.restic.backups.${n}.environmentFile instead.") (filterAttrs (n: v: v.s3CredentialsFile != null) config.services.restic.backups);
     assertions = mapAttrsToList (n: v: {
       assertion = (v.repository == null) != (v.repositoryFile == null);
       message = "services.restic.backups.${n}: exactly one of repository or repositoryFile should be set";
@@ -310,10 +300,7 @@ in
             resticCmd = "${backup.package}/bin/restic${extraOptions}";
             excludeFlags = optional (backup.exclude != []) "--exclude-file=${pkgs.writeText "exclude-patterns" (concatStringsSep "\n" backup.exclude)}";
             filesFromTmpFile = "/run/restic-backups-${name}/includes";
-            backupPaths =
-              if (backup.dynamicFilesFrom == null)
-              then optionalString (backup.paths != null) (concatStringsSep " " backup.paths)
-              else "--files-from ${filesFromTmpFile}";
+            doBackup = (backup.dynamicFilesFrom != null) || (backup.paths != null && backup.paths != []);
             pruneCmd = optionals (builtins.length backup.pruneOpts > 0) [
               (resticCmd + " forget --prune " + (concatStringsSep " " backup.pruneOpts))
               (resticCmd + " check " + (concatStringsSep " " backup.checkOpts))
@@ -348,7 +335,7 @@ in
             after = [ "network-online.target" ];
             serviceConfig = {
               Type = "oneshot";
-              ExecStart = (optionals (backupPaths != "") [ "${resticCmd} backup ${concatStringsSep " " (backup.extraBackupArgs ++ excludeFlags)} ${backupPaths}" ])
+              ExecStart = (optionals doBackup [ "${resticCmd} backup ${concatStringsSep " " (backup.extraBackupArgs ++ excludeFlags)} --files-from=${filesFromTmpFile}" ])
                 ++ pruneCmd;
               User = backup.user;
               RuntimeDirectory = "restic-backups-${name}";
@@ -366,8 +353,11 @@ in
               ${optionalString (backup.initialize) ''
                 ${resticCmd} snapshots || ${resticCmd} init
               ''}
+              ${optionalString (backup.paths != null && backup.paths != []) ''
+                cat ${pkgs.writeText "staticPaths" (concatStringsSep "\n" backup.paths)} >> ${filesFromTmpFile}
+              ''}
               ${optionalString (backup.dynamicFilesFrom != null) ''
-                ${pkgs.writeScript "dynamicFilesFromScript" backup.dynamicFilesFrom} > ${filesFromTmpFile}
+                ${pkgs.writeScript "dynamicFilesFromScript" backup.dynamicFilesFrom} >> ${filesFromTmpFile}
               ''}
             '';
           } // optionalAttrs (backup.dynamicFilesFrom != null || backup.backupCleanupCommand != null) {
diff --git a/nixpkgs/nixos/modules/services/blockchain/ethereum/erigon.nix b/nixpkgs/nixos/modules/services/blockchain/ethereum/erigon.nix
index 8ebe0fcaff54..945a373d1274 100644
--- a/nixpkgs/nixos/modules/services/blockchain/ethereum/erigon.nix
+++ b/nixpkgs/nixos/modules/services/blockchain/ethereum/erigon.nix
@@ -13,6 +13,8 @@ in {
     services.erigon = {
       enable = mkEnableOption (lib.mdDoc "Ethereum implementation on the efficiency frontier");
 
+      package = mkPackageOptionMD pkgs "erigon" { };
+
       extraArgs = mkOption {
         type = types.listOf types.str;
         description = lib.mdDoc "Additional arguments passed to Erigon";
@@ -92,7 +94,7 @@ in {
 
       serviceConfig = {
         LoadCredential = "ERIGON_JWT:${cfg.secretJwtPath}";
-        ExecStart = "${pkgs.erigon}/bin/erigon --config ${configFile} --authrpc.jwtsecret=%d/ERIGON_JWT ${lib.escapeShellArgs cfg.extraArgs}";
+        ExecStart = "${cfg.package}/bin/erigon --config ${configFile} --authrpc.jwtsecret=%d/ERIGON_JWT ${lib.escapeShellArgs cfg.extraArgs}";
         DynamicUser = true;
         Restart = "on-failure";
         StateDirectory = "erigon";
diff --git a/nixpkgs/nixos/modules/services/logging/syslog-ng.nix b/nixpkgs/nixos/modules/services/logging/syslog-ng.nix
index d22acbeaa70c..48d556b9459e 100644
--- a/nixpkgs/nixos/modules/services/logging/syslog-ng.nix
+++ b/nixpkgs/nixos/modules/services/logging/syslog-ng.nix
@@ -67,7 +67,7 @@ in {
       configHeader = mkOption {
         type = types.lines;
         default = ''
-          @version: 3.6
+          @version: 4.4
           @include "scl.conf"
         '';
         description = lib.mdDoc ''
diff --git a/nixpkgs/nixos/modules/services/mail/mlmmj.nix b/nixpkgs/nixos/modules/services/mail/mlmmj.nix
index 642f8b20fe35..3f07fabcf177 100644
--- a/nixpkgs/nixos/modules/services/mail/mlmmj.nix
+++ b/nixpkgs/nixos/modules/services/mail/mlmmj.nix
@@ -143,13 +143,11 @@ in
 
     environment.systemPackages = [ pkgs.mlmmj ];
 
-    system.activationScripts.mlmmj = ''
-          ${pkgs.coreutils}/bin/mkdir -p ${stateDir} ${spoolDir}/${cfg.listDomain}
-          ${pkgs.coreutils}/bin/chown -R ${cfg.user}:${cfg.group} ${spoolDir}
-          ${concatMapLines (createList cfg.listDomain) cfg.mailLists}
-          ${pkgs.postfix}/bin/postmap /etc/postfix/virtual
-          ${pkgs.postfix}/bin/postmap /etc/postfix/transport
-      '';
+    systemd.tmpfiles.rules = [
+      ''d "${stateDir}" -''
+      ''d "${spoolDir}/${cfg.listDomain}" -''
+      ''Z "${spoolDir}" - "${cfg.user}" "${cfg.group}" -''
+    ];
 
     systemd.services.mlmmj-maintd = {
       description = "mlmmj maintenance daemon";
@@ -158,6 +156,11 @@ in
         Group = cfg.group;
         ExecStart = "${pkgs.mlmmj}/bin/mlmmj-maintd -F -d ${spoolDir}/${cfg.listDomain}";
       };
+      preStart = ''
+        ${concatMapLines (createList cfg.listDomain) cfg.mailLists}
+        ${pkgs.postfix}/bin/postmap /etc/postfix/virtual
+        ${pkgs.postfix}/bin/postmap /etc/postfix/transport
+      '';
     };
 
     systemd.timers.mlmmj-maintd = {
diff --git a/nixpkgs/nixos/modules/services/monitoring/certspotter.md b/nixpkgs/nixos/modules/services/monitoring/certspotter.md
new file mode 100644
index 000000000000..9bf6e1d946a0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/certspotter.md
@@ -0,0 +1,74 @@
+# Cert Spotter {#module-services-certspotter}
+
+Cert Spotter is a tool for monitoring [Certificate Transparency](https://en.wikipedia.org/wiki/Certificate_Transparency)
+logs.
+
+## Service Configuration {#modules-services-certspotter-service-configuration}
+
+A basic config that notifies you of all certificate changes for your
+domain would look as follows:
+
+```nix
+services.certspotter = {
+  enable = true;
+  # replace example.org with your domain name
+  watchlist = [ ".example.org" ];
+  emailRecipients = [ "webmaster@example.org" ];
+};
+
+# Configure an SMTP client
+programs.msmtp.enable = true;
+# Or you can use any other module that provides sendmail, like
+# services.nullmailer, services.opensmtpd, services.postfix
+```
+
+In this case, the leading dot in `".example.org"` means that Cert
+Spotter should monitor not only `example.org`, but also all of its
+subdomains.
+
+## Operation {#modules-services-certspotter-operation}
+
+**By default, NixOS configures Cert Spotter to skip all certificates
+issued before its first launch**, because checking the entire
+Certificate Transparency logs requires downloading tens of terabytes of
+data. If you want to check the *entire* logs for previously issued
+certificates, you have to set `services.certspotter.startAtEnd` to
+`false` and remove all previously saved log state in
+`/var/lib/certspotter/logs`. The downloaded logs aren't saved, so if you
+add a new domain to the watchlist and want Cert Spotter to go through
+the logs again, you will have to remove `/var/lib/certspotter/logs`
+again.
+
+After catching up with the logs, Cert Spotter will start monitoring live
+logs. As of October 2023, it uses around **20 Mbps** of traffic on
+average.
+
+## Hooks {#modules-services-certspotter-hooks}
+
+Cert Spotter supports running custom hooks instead of (or in addition
+to) sending emails. Hooks are shell scripts that will be passed certain
+environment variables.
+
+To see hook documentation, see Cert Spotter's man pages:
+
+```ShellSession
+nix-shell -p certspotter --run 'man 8 certspotter-script'
+```
+
+For example, you can remove `emailRecipients` and send email
+notifications manually using the following hook:
+
+```nix
+services.certspotter.hooks = [
+  (pkgs.writeShellScript "certspotter-hook" ''
+    function print_email() {
+      echo "Subject: [certspotter] $SUMMARY"
+      echo "Mime-Version: 1.0"
+      echo "Content-Type: text/plain; charset=US-ASCII"
+      echo
+      cat "$TEXT_FILENAME"
+    }
+    print_email | ${config.services.certspotter.sendmailPath} -i webmaster@example.org
+  '')
+];
+```
diff --git a/nixpkgs/nixos/modules/services/monitoring/certspotter.nix b/nixpkgs/nixos/modules/services/monitoring/certspotter.nix
new file mode 100644
index 000000000000..aafa29daa872
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/certspotter.nix
@@ -0,0 +1,143 @@
+{ config
+, lib
+, pkgs
+, ... }:
+
+let
+  cfg = config.services.certspotter;
+
+  configDir = pkgs.linkFarm "certspotter-config" (
+    lib.toList {
+      name = "watchlist";
+      path = pkgs.writeText "certspotter-watchlist" (builtins.concatStringsSep "\n" cfg.watchlist);
+    }
+    ++ lib.optional (cfg.emailRecipients != [ ]) {
+      name = "email_recipients";
+      path = pkgs.writeText "certspotter-email_recipients" (builtins.concatStringsSep "\n" cfg.emailRecipients);
+    }
+    # always generate hooks dir when no emails are provided to allow running cert spotter with no hooks/emails
+    ++ lib.optional (cfg.emailRecipients == [ ] || cfg.hooks != [ ]) {
+      name = "hooks.d";
+      path = pkgs.linkFarm "certspotter-hooks" (lib.imap1 (i: path: {
+        inherit path;
+        name = "hook${toString i}";
+      }) cfg.hooks);
+    });
+in
+{
+  options.services.certspotter = {
+    enable = lib.mkEnableOption "Cert Spotter, a Certificate Transparency log monitor";
+
+    package = lib.mkPackageOptionMD pkgs "certspotter" { };
+
+    startAtEnd = lib.mkOption {
+      type = lib.types.bool;
+      description = ''
+        Whether to skip certificates issued before the first launch of Cert Spotter.
+        Setting this to `false` will cause Cert Spotter to download tens of terabytes of data.
+      '';
+      default = true;
+    };
+
+    sendmailPath = lib.mkOption {
+      type = with lib.types; nullOr path;
+      description = ''
+        Path to the `sendmail` binary. By default, the local sendmail wrapper is used
+        (see {option}`services.mail.sendmailSetuidWrapper`}).
+      '';
+      example = lib.literalExpression ''"''${pkgs.system-sendmail}/bin/sendmail"'';
+    };
+
+    watchlist = lib.mkOption {
+      type = with lib.types; listOf str;
+      description = "Domain names to watch. To monitor a domain with all subdomains, prefix its name with `.` (e.g. `.example.org`).";
+      default = [ ];
+      example = [ ".example.org" "another.example.com" ];
+    };
+
+    emailRecipients = lib.mkOption {
+      type = with lib.types; listOf str;
+      description = "A list of email addresses to send certificate updates to.";
+      default = [ ];
+    };
+
+    hooks = lib.mkOption {
+      type = with lib.types; listOf path;
+      description = ''
+        Scripts to run upon the detection of a new certificate. See `man 8 certspotter-script` or
+        [the GitHub page](https://github.com/SSLMate/certspotter/blob/${pkgs.certspotter.src.rev or "master"}/man/certspotter-script.md)
+        for more info.
+      '';
+      default = [ ];
+      example = lib.literalExpression ''
+        [
+          (pkgs.writeShellScript "certspotter-hook" '''
+            echo "Event summary: $SUMMARY."
+          ''')
+        ]
+      '';
+    };
+
+    extraFlags = lib.mkOption {
+      type = with lib.types; listOf str;
+      description = "Extra command-line arguments to pass to Cert Spotter";
+      example = [ "-no_save" ];
+      default = [ ];
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = (cfg.emailRecipients != [ ]) -> (cfg.sendmailPath != null);
+        message = ''
+          You must configure the sendmail setuid wrapper (services.mail.sendmailSetuidWrapper)
+          or services.certspotter.sendmailPath
+        '';
+      }
+    ];
+
+    services.certspotter.sendmailPath = let
+      inherit (config.security) wrapperDir;
+      inherit (config.services.mail) sendmailSetuidWrapper;
+    in lib.mkMerge [
+      (lib.mkIf (sendmailSetuidWrapper != null) (lib.mkOptionDefault "${wrapperDir}/${sendmailSetuidWrapper.program}"))
+      (lib.mkIf (sendmailSetuidWrapper == null) (lib.mkOptionDefault null))
+    ];
+
+    users.users.certspotter = {
+      description = "Cert Spotter user";
+      group = "certspotter";
+      home = "/var/lib/certspotter";
+      isSystemUser = true;
+    };
+    users.groups.certspotter = { };
+
+    systemd.services.certspotter = {
+      description = "Cert Spotter - Certificate Transparency Monitor";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      environment.CERTSPOTTER_CONFIG_DIR = configDir;
+      environment.SENDMAIL_PATH = if cfg.sendmailPath != null then cfg.sendmailPath else "/run/current-system/sw/bin/false";
+      script = ''
+        export CERTSPOTTER_STATE_DIR="$STATE_DIRECTORY"
+        cd "$CERTSPOTTER_STATE_DIR"
+        ${lib.optionalString cfg.startAtEnd ''
+          if [[ ! -d logs ]]; then
+            # Don't download certificates issued before the first launch
+            exec ${cfg.package}/bin/certspotter -start_at_end ${lib.escapeShellArgs cfg.extraFlags}
+          fi
+        ''}
+        exec ${cfg.package}/bin/certspotter ${lib.escapeShellArgs cfg.extraFlags}
+      '';
+      serviceConfig = {
+        User = "certspotter";
+        Group = "certspotter";
+        StateDirectory = "certspotter";
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ chayleaf ];
+  meta.doc = ./certspotter.md;
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/goss.md b/nixpkgs/nixos/modules/services/monitoring/goss.md
new file mode 100644
index 000000000000..1e636aa3bdf3
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/goss.md
@@ -0,0 +1,44 @@
+# Goss {#module-services-goss}
+
+[goss](https://goss.rocks/) is a YAML based serverspec alternative tool
+for validating a server's configuration.
+
+## Basic Usage {#module-services-goss-basic-usage}
+
+A minimal configuration looks like this:
+
+```
+{
+  services.goss = {
+    enable = true;
+
+    environment = {
+      GOSS_FMT = "json";
+      GOSS_LOGLEVEL = "TRACE";
+    };
+
+    settings = {
+      addr."tcp://localhost:8080" = {
+        reachable = true;
+        local-address = "127.0.0.1";
+      };
+      command."check-goss-version" = {
+        exec = "${lib.getExe pkgs.goss} --version";
+        exit-status = 0;
+      };
+      dns.localhost.resolvable = true;
+      file."/nix" = {
+        filetype = "directory";
+        exists = true;
+      };
+      group.root.exists = true;
+      kernel-param."kernel.ostype".value = "Linux";
+      service.goss = {
+        enabled = true;
+        running = true;
+      };
+      user.root.exists = true;
+    };
+  };
+}
+```
diff --git a/nixpkgs/nixos/modules/services/monitoring/goss.nix b/nixpkgs/nixos/modules/services/monitoring/goss.nix
new file mode 100644
index 000000000000..64a8dad0703e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/goss.nix
@@ -0,0 +1,86 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.goss;
+
+  settingsFormat = pkgs.formats.yaml { };
+  configFile = settingsFormat.generate "goss.yaml" cfg.settings;
+
+in {
+  meta = {
+    doc = ./goss.md;
+    maintainers = [ lib.maintainers.anthonyroussel ];
+  };
+
+  options = {
+    services.goss = {
+      enable = lib.mkEnableOption (lib.mdDoc "Goss daemon");
+
+      package = lib.mkPackageOptionMD pkgs "goss" { };
+
+      environment = lib.mkOption {
+        type = lib.types.attrsOf lib.types.str;
+        default = { };
+        example = {
+          GOSS_FMT = "json";
+          GOSS_LOGLEVEL = "FATAL";
+          GOSS_LISTEN = ":8080";
+        };
+        description = lib.mdDoc ''
+          Environment variables to set for the goss service.
+
+          See <https://github.com/goss-org/goss/blob/master/docs/manual.md>
+        '';
+      };
+
+      settings = lib.mkOption {
+        type = lib.types.submodule { freeformType = settingsFormat.type; };
+        default = { };
+        example = {
+          addr."tcp://localhost:8080" = {
+            reachable = true;
+            local-address = "127.0.0.1";
+          };
+          service.goss = {
+            enabled = true;
+            running = true;
+          };
+        };
+        description = lib.mdDoc ''
+          The global options in `config` file in yaml format.
+
+          Refer to <https://github.com/goss-org/goss/blob/master/docs/goss-json-schema.yaml> for schema.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.services.goss = {
+      description = "Goss - Quick and Easy server validation";
+      unitConfig.Documentation = "https://github.com/goss-org/goss/blob/master/docs/manual.md";
+
+      after = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network-online.target" ];
+
+      environment = {
+        GOSS_FILE = configFile;
+      } // cfg.environment;
+
+      reloadTriggers = [ configFile ];
+
+      serviceConfig = {
+        DynamicUser = true;
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        ExecStart = "${cfg.package}/bin/goss serve";
+        Group = "goss";
+        Restart = "on-failure";
+        RestartSec = 5;
+        User = "goss";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/ups.nix b/nixpkgs/nixos/modules/services/monitoring/ups.nix
index bb11b6a1c1d0..efef2d777acd 100644
--- a/nixpkgs/nixos/modules/services/monitoring/ups.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/ups.nix
@@ -239,11 +239,9 @@ in
 
     power.ups.schedulerRules = mkDefault "${pkgs.nut}/etc/upssched.conf.sample";
 
-    system.activationScripts.upsSetup = stringAfter [ "users" "groups" ]
-      ''
-        # Used to store pid files of drivers.
-        mkdir -p /var/state/ups
-      '';
+    systemd.tmpfiles.rules = [
+      "d /var/state/ups -"
+    ];
 
 
 /*
diff --git a/nixpkgs/nixos/modules/services/networking/gvpe.nix b/nixpkgs/nixos/modules/services/networking/gvpe.nix
index 2279ceee2f58..558f499022c8 100644
--- a/nixpkgs/nixos/modules/services/networking/gvpe.nix
+++ b/nixpkgs/nixos/modules/services/networking/gvpe.nix
@@ -29,7 +29,7 @@ let
 
       export PATH=$PATH:${pkgs.iproute2}/sbin
 
-      ip link set $IFNAME up
+      ip link set dev $IFNAME up
       ip address add ${cfg.ipAddress} dev $IFNAME
       ip route add ${cfg.subnet} dev $IFNAME
 
diff --git a/nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix b/nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix
index 9c71a988f29c..6c30f89b7968 100644
--- a/nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix
+++ b/nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix
@@ -52,25 +52,27 @@ in
     '';
     environment.etc."iscsi/initiatorname.iscsi".text = "InitiatorName=${cfg.name}";
 
-    system.activationScripts.iscsid = let
-      extraCfgDumper = optionalString (cfg.extraConfigFile != null) ''
-        if [ -f "${cfg.extraConfigFile}" ]; then
-          printf "\n# The following is from ${cfg.extraConfigFile}:\n"
-          cat "${cfg.extraConfigFile}"
-        else
-          echo "Warning: services.openiscsi.extraConfigFile ${cfg.extraConfigFile} does not exist!" >&2
-        fi
-      '';
-    in ''
-      (
-        cat ${config.environment.etc."iscsi/iscsid.conf.fragment".source}
-        ${extraCfgDumper}
-      ) > /etc/iscsi/iscsid.conf
-    '';
-
     systemd.packages = [ cfg.package ];
 
-    systemd.services."iscsid".wantedBy = [ "multi-user.target" ];
+    systemd.services."iscsid" = {
+      wantedBy = [ "multi-user.target" ];
+      preStart =
+        let
+          extraCfgDumper = optionalString (cfg.extraConfigFile != null) ''
+            if [ -f "${cfg.extraConfigFile}" ]; then
+              printf "\n# The following is from ${cfg.extraConfigFile}:\n"
+              cat "${cfg.extraConfigFile}"
+            else
+              echo "Warning: services.openiscsi.extraConfigFile ${cfg.extraConfigFile} does not exist!" >&2
+            fi
+          '';
+        in ''
+          (
+            cat ${config.environment.etc."iscsi/iscsid.conf.fragment".source}
+            ${extraCfgDumper}
+          ) > /etc/iscsi/iscsid.conf
+        '';
+    };
     systemd.sockets."iscsid".wantedBy = [ "sockets.target" ];
 
     systemd.services."iscsi" = mkIf cfg.enableAutoLoginOut {
diff --git a/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
index 82e68bf92af1..99ffbf56ccb0 100644
--- a/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
+++ b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
@@ -76,5 +76,5 @@ with lib;
     };
   };
 
-  meta.maintainers = with maintainers; [ patricksjackson ymarkus ];
+  meta.maintainers = with maintainers; [ arcuru ymarkus ];
 }
diff --git a/nixpkgs/nixos/modules/services/networking/spiped.nix b/nixpkgs/nixos/modules/services/networking/spiped.nix
index 3e01ace54ad1..547317dbcbe2 100644
--- a/nixpkgs/nixos/modules/services/networking/spiped.nix
+++ b/nixpkgs/nixos/modules/services/networking/spiped.nix
@@ -197,8 +197,9 @@ in
       script = "exec ${pkgs.spiped}/bin/spiped -F `cat /etc/spiped/$1.spec`";
     };
 
-    system.activationScripts.spiped = optionalString (cfg.config != {})
-      "mkdir -p /var/lib/spiped";
+    systemd.tmpfiles.rules = lib.mkIf (cfg.config != { }) [
+      "d /var/lib/spiped -"
+    ];
 
     # Setup spiped config files
     environment.etc = mapAttrs' (name: cfg: nameValuePair "spiped/${name}.spec"
diff --git a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
index 1946e9e79e98..14bc59089adf 100644
--- a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
@@ -12,22 +12,44 @@ let
     then cfgc.package
     else pkgs.buildPackages.openssh;
 
-  # reports boolean as yes / no
-  mkValueStringSshd = with lib; v:
-        if isInt           v then toString v
-        else if isString   v then v
-        else if true  ==   v then "yes"
-        else if false ==   v then "no"
-        else if isList     v then concatStringsSep "," v
-        else throw "unsupported type ${builtins.typeOf v}: ${(lib.generators.toPretty {}) v}";
-
   # dont use the "=" operator
-  settingsFormat = (pkgs.formats.keyValue {
-      mkKeyValue = lib.generators.mkKeyValueDefault {
-      mkValueString = mkValueStringSshd;
-    } " ";});
+  settingsFormat =
+    let
+      # reports boolean as yes / no
+      mkValueString = with lib; v:
+            if isInt           v then toString v
+            else if isString   v then v
+            else if true  ==   v then "yes"
+            else if false ==   v then "no"
+            else throw "unsupported type ${builtins.typeOf v}: ${(lib.generators.toPretty {}) v}";
+
+      base = pkgs.formats.keyValue {
+        mkKeyValue = lib.generators.mkKeyValueDefault { inherit mkValueString; } " ";
+      };
+      # OpenSSH is very inconsistent with options that can take multiple values.
+      # For some of them, they can simply appear multiple times and are appended, for others the
+      # values must be separated by whitespace or even commas.
+      # Consult either sshd_config(5) or, as last resort, the OpehSSH source for parsing
+      # the options at servconf.c:process_server_config_line_depth() to determine the right "mode"
+      # for each. But fortunaly this fact is documented for most of them in the manpage.
+      commaSeparated = [ "Ciphers" "KexAlgorithms" "Macs" ];
+      spaceSeparated = [ "AuthorizedKeysFile" "AllowGroups" "AllowUsers" "DenyGroups" "DenyUsers" ];
+    in {
+      inherit (base) type;
+      generate = name: value:
+        let transformedValue = mapAttrs (key: val:
+          if isList val then
+            if elem key commaSeparated then concatStringsSep "," val
+            else if elem key spaceSeparated then concatStringsSep " " val
+            else throw "list value for unknown key ${key}: ${(lib.generators.toPretty {}) val}"
+          else
+            val
+          ) value;
+        in
+          base.generate name transformedValue;
+    };
 
-  configFile = settingsFormat.generate "sshd.conf-settings" cfg.settings;
+  configFile = settingsFormat.generate "sshd.conf-settings" (filterAttrs (n: v: v != null) cfg.settings);
   sshconf = pkgs.runCommand "sshd.conf-final" { } ''
     cat ${configFile} - >$out <<EOL
     ${cfg.extraConfig}
@@ -431,6 +453,42 @@ in
                 <https://infosec.mozilla.org/guidelines/openssh#modern-openssh-67>
               '';
             };
+            AllowUsers = mkOption {
+              type = with types; nullOr (listOf str);
+              default = null;
+              description = lib.mdDoc ''
+                If specified, login is allowed only for the listed users.
+                See {manpage}`sshd_config(5)` for details.
+              '';
+            };
+            DenyUsers = mkOption {
+              type = with types; nullOr (listOf str);
+              default = null;
+              description = lib.mdDoc ''
+                If specified, login is denied for all listed users. Takes
+                precedence over [](#opt-services.openssh.settings.AllowUsers).
+                See {manpage}`sshd_config(5)` for details.
+              '';
+            };
+            AllowGroups = mkOption {
+              type = with types; nullOr (listOf str);
+              default = null;
+              description = lib.mdDoc ''
+                If specified, login is allowed only for users part of the
+                listed groups.
+                See {manpage}`sshd_config(5)` for details.
+              '';
+            };
+            DenyGroups = mkOption {
+              type = with types; nullOr (listOf str);
+              default = null;
+              description = lib.mdDoc ''
+                If specified, login is denied for all users part of the listed
+                groups. Takes precedence over
+                [](#opt-services.openssh.settings.AllowGroups). See
+                {manpage}`sshd_config(5)` for details.
+              '';
+            };
           };
         });
       };
diff --git a/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix
index c51e8ad9f5fc..bfea89969728 100644
--- a/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix
+++ b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix
@@ -43,21 +43,21 @@ in  {
 
     # The swanctl command complains when the following directories don't exist:
     # See: https://wiki.strongswan.org/projects/strongswan/wiki/Swanctldirectory
-    system.activationScripts.strongswan-swanctl-etc = stringAfter ["etc"] ''
-      mkdir -p '/etc/swanctl/x509'     # Trusted X.509 end entity certificates
-      mkdir -p '/etc/swanctl/x509ca'   # Trusted X.509 Certificate Authority certificates
-      mkdir -p '/etc/swanctl/x509ocsp'
-      mkdir -p '/etc/swanctl/x509aa'   # Trusted X.509 Attribute Authority certificates
-      mkdir -p '/etc/swanctl/x509ac'   # Attribute Certificates
-      mkdir -p '/etc/swanctl/x509crl'  # Certificate Revocation Lists
-      mkdir -p '/etc/swanctl/pubkey'   # Raw public keys
-      mkdir -p '/etc/swanctl/private'  # Private keys in any format
-      mkdir -p '/etc/swanctl/rsa'      # PKCS#1 encoded RSA private keys
-      mkdir -p '/etc/swanctl/ecdsa'    # Plain ECDSA private keys
-      mkdir -p '/etc/swanctl/bliss'
-      mkdir -p '/etc/swanctl/pkcs8'    # PKCS#8 encoded private keys of any type
-      mkdir -p '/etc/swanctl/pkcs12'   # PKCS#12 containers
-    '';
+    systemd.tmpfiles.rules = [
+      "d /etc/swanctl/x509 -"     # Trusted X.509 end entity certificates
+      "d /etc/swanctl/x509ca -"   # Trusted X.509 Certificate Authority certificates
+      "d /etc/swanctl/x509ocsp -"
+      "d /etc/swanctl/x509aa -"   # Trusted X.509 Attribute Authority certificates
+      "d /etc/swanctl/x509ac -"   # Attribute Certificates
+      "d /etc/swanctl/x509crl -"  # Certificate Revocation Lists
+      "d /etc/swanctl/pubkey -"   # Raw public keys
+      "d /etc/swanctl/private -"  # Private keys in any format
+      "d /etc/swanctl/rsa -"      # PKCS#1 encoded RSA private keys
+      "d /etc/swanctl/ecdsa -"    # Plain ECDSA private keys
+      "d /etc/swanctl/bliss -"
+      "d /etc/swanctl/pkcs8 -"    # PKCS#8 encoded private keys of any type
+      "d /etc/swanctl/pkcs12 -"   # PKCS#12 containers
+    ];
 
     systemd.services.strongswan-swanctl = {
       description = "strongSwan IPsec IKEv1/IKEv2 daemon using swanctl";
diff --git a/nixpkgs/nixos/modules/services/networking/tailscale.nix b/nixpkgs/nixos/modules/services/networking/tailscale.nix
index 8b35cc8d6669..a5d171e0baab 100644
--- a/nixpkgs/nixos/modules/services/networking/tailscale.nix
+++ b/nixpkgs/nixos/modules/services/networking/tailscale.nix
@@ -31,6 +31,12 @@ in {
 
     package = lib.mkPackageOptionMD pkgs "tailscale" {};
 
+    openFirewall = mkOption {
+      default = false;
+      type = types.bool;
+      description = lib.mdDoc "Whether to open the firewall for the specified port.";
+    };
+
     useRoutingFeatures = mkOption {
       type = types.enum [ "none" "client" "server" "both" ];
       default = "none";
@@ -113,6 +119,8 @@ in {
       "net.ipv6.conf.all.forwarding" = mkOverride 97 true;
     };
 
+    networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ cfg.port ];
+
     networking.firewall.checkReversePath = mkIf (cfg.useRoutingFeatures == "client" || cfg.useRoutingFeatures == "both") "loose";
 
     networking.dhcpcd.denyInterfaces = [ cfg.interfaceName ];
diff --git a/nixpkgs/nixos/modules/services/networking/unifi.nix b/nixpkgs/nixos/modules/services/networking/unifi.nix
index 37a739f41d48..6b6837109806 100644
--- a/nixpkgs/nixos/modules/services/networking/unifi.nix
+++ b/nixpkgs/nixos/modules/services/networking/unifi.nix
@@ -6,9 +6,9 @@ let
   cmd = ''
     @${cfg.jrePackage}/bin/java java \
         ${optionalString (lib.versionAtLeast (lib.getVersion cfg.jrePackage) "16")
-        "--add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED "
+        ("--add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED "
         + "--add-opens java.base/sun.security.util=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED "
-        + "--add-opens java.rmi/sun.rmi.transport=ALL-UNNAMED"} \
+        + "--add-opens java.rmi/sun.rmi.transport=ALL-UNNAMED")} \
         ${optionalString (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m"} \
         ${optionalString (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m"} \
         -jar ${stateDir}/lib/ace.jar
diff --git a/nixpkgs/nixos/modules/services/system/nix-daemon.nix b/nixpkgs/nixos/modules/services/system/nix-daemon.nix
index c9df20196dbd..ce255cd8d0a4 100644
--- a/nixpkgs/nixos/modules/services/system/nix-daemon.nix
+++ b/nixpkgs/nixos/modules/services/system/nix-daemon.nix
@@ -249,11 +249,6 @@ in
 
     services.xserver.displayManager.hiddenUsers = attrNames nixbldUsers;
 
-    system.activationScripts.nix = stringAfter [ "etc" "users" ]
-      ''
-        install -m 0755 -d /nix/var/nix/{gcroots,profiles}/per-user
-      '';
-
     # Legacy configuration conversion.
     nix.settings = mkMerge [
       (mkIf (isNixAtLeast "2.3pre") { sandbox-fallback = false; })
diff --git a/nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.md b/nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.md
new file mode 100644
index 000000000000..236953bd4ff7
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.md
@@ -0,0 +1,42 @@
+# c2FmZQ {#module-services-c2fmzq}
+
+c2FmZQ is an application that can securely encrypt, store, and share files,
+including but not limited to pictures and videos.
+
+The service `c2fmzq-server` can be enabled by setting
+```
+{
+  services.c2fmzq-server.enable = true;
+}
+```
+This will spin up an instance of the server which is API-compatible with
+[Stingle Photos](https://stingle.org) and an experimental Progressive Web App
+(PWA) to interact with the storage via the browser.
+
+In principle the server can be exposed directly on a public interface and there
+are command line options to manage HTTPS certificates directly, but the module
+is designed to be served behind a reverse proxy or only accessed via localhost.
+
+```
+{
+  services.c2fmzq-server = {
+    enable = true;
+    bindIP = "127.0.0.1"; # default
+    port = 8080; # default
+  };
+
+  services.nginx = {
+    enable = true;
+    recommendedProxySettings = true;
+    virtualHosts."example.com" = {
+      enableACME = true;
+      forceSSL = true;
+      locations."/" = {
+        proxyPass = "http://127.0.0.1:8080";
+      };
+    };
+  };
+}
+```
+
+For more information, see <https://github.com/c2FmZQ/c2FmZQ/>.
diff --git a/nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.nix b/nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.nix
new file mode 100644
index 000000000000..2749c2a5a87a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.nix
@@ -0,0 +1,125 @@
+{ lib, pkgs, config, ... }:
+
+let
+  inherit (lib) mkEnableOption mkPackageOption mkOption types;
+
+  cfg = config.services.c2fmzq-server;
+
+  argsFormat = {
+    type = with lib.types; nullOr (oneOf [ bool int str ]);
+    generate = lib.cli.toGNUCommandLineShell { };
+  };
+in {
+  options.services.c2fmzq-server = {
+    enable = mkEnableOption "c2fmzq-server";
+
+    bindIP = mkOption {
+      type = types.str;
+      default = "127.0.0.1";
+      description = "The local address to use.";
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 8080;
+      description = "The local port to use.";
+    };
+
+    passphraseFile = mkOption {
+      type = types.str;
+      example = "/run/secrets/c2fmzq/pwfile";
+      description = "Path to file containing the database passphrase";
+    };
+
+    package = mkPackageOption pkgs "c2fmzq" { };
+
+    settings = mkOption {
+      type = types.submodule {
+        freeformType = argsFormat.type;
+
+        options = {
+          address = mkOption {
+            internal = true;
+            type = types.str;
+            default = "${cfg.bindIP}:${toString cfg.port}";
+          };
+
+          database = mkOption {
+            type = types.str;
+            default = "%S/c2fmzq-server/data";
+            description = "Path of the database";
+          };
+
+          verbose = mkOption {
+            type = types.ints.between 1 3;
+            default = 2;
+            description = "The level of logging verbosity: 1:Error 2:Info 3:Debug";
+          };
+        };
+      };
+      description = ''
+        Configuration for c2FmZQ-server passed as CLI arguments.
+        Run {command}`c2FmZQ-server help` for supported values.
+      '';
+      example = {
+        verbose = 3;
+        allow-new-accounts = true;
+        auto-approve-new-accounts = true;
+        encrypt-metadata = true;
+        enable-webapp = true;
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.c2fmzq-server = {
+      description = "c2FmZQ-server";
+      documentation = [ "https://github.com/c2FmZQ/c2FmZQ/blob/main/README.md" ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" "network-online.target" ];
+
+      serviceConfig = {
+        ExecStart = "${lib.getExe cfg.package} ${argsFormat.generate cfg.settings}";
+        AmbientCapabilities = "";
+        CapabilityBoundingSet = "";
+        DynamicUser = true;
+        Environment = "C2FMZQ_PASSPHRASE_FILE=%d/passphrase-file";
+        IPAccounting = true;
+        IPAddressAllow = cfg.bindIP;
+        IPAddressDeny = "any";
+        LoadCredential = "passphrase-file:${cfg.passphraseFile}";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateIPC = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SocketBindAllow = cfg.port;
+        SocketBindDeny = "any";
+        StateDirectory = "c2fmzq-server";
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [ "@system-service" "~@privileged @obsolete" ];
+      };
+    };
+  };
+
+  meta = {
+    doc = ./c2fmzq-server.md;
+    maintainers = with lib.maintainers; [ hmenke ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/mattermost.nix b/nixpkgs/nixos/modules/services/web-apps/mattermost.nix
index 66e5f1695a15..24f3b3331845 100644
--- a/nixpkgs/nixos/modules/services/web-apps/mattermost.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/mattermost.nix
@@ -287,9 +287,9 @@ in
 
       # The systemd service will fail to execute the preStart hook
       # if the WorkingDirectory does not exist
-      system.activationScripts.mattermost = ''
-        mkdir -p "${cfg.statePath}"
-      '';
+      systemd.tmpfiles.rules = [
+        ''d "${cfg.statePath}" -''
+      ];
 
       systemd.services.mattermost = {
         description = "Mattermost chat service";
diff --git a/nixpkgs/nixos/modules/services/web-apps/plausible.nix b/nixpkgs/nixos/modules/services/web-apps/plausible.nix
index e5deb6cf511f..576b54a7edf2 100644
--- a/nixpkgs/nixos/modules/services/web-apps/plausible.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/plausible.nix
@@ -78,9 +78,9 @@ in {
     server = {
       disableRegistration = mkOption {
         default = true;
-        type = types.bool;
+        type = types.enum [true false "invite_only"];
         description = lib.mdDoc ''
-          Whether to prohibit creating an account in plausible's UI.
+          Whether to prohibit creating an account in plausible's UI or allow on `invite_only`.
         '';
       };
       secretKeybaseFile = mkOption {
@@ -209,7 +209,7 @@ in {
             # Configuration options from
             # https://plausible.io/docs/self-hosting-configuration
             PORT = toString cfg.server.port;
-            DISABLE_REGISTRATION = boolToString cfg.server.disableRegistration;
+            DISABLE_REGISTRATION = if isBool cfg.server.disableRegistration then boolToString cfg.server.disableRegistration else cfg.server.disableRegistration;
 
             RELEASE_TMP = "/var/lib/plausible/tmp";
             # Home is needed to connect to the node with iex
diff --git a/nixpkgs/nixos/modules/services/web-apps/shiori.nix b/nixpkgs/nixos/modules/services/web-apps/shiori.nix
index f0505e052e1c..71b5ad4d4c06 100644
--- a/nixpkgs/nixos/modules/services/web-apps/shiori.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/shiori.nix
@@ -29,6 +29,13 @@ in {
         default = 8080;
         description = lib.mdDoc "The port of the Shiori web application";
       };
+
+      webRoot = mkOption {
+        type = types.str;
+        default = "/";
+        example = "/shiori";
+        description = lib.mdDoc "The root of the Shiori web application";
+      };
     };
   };
 
@@ -40,7 +47,7 @@ in {
       environment.SHIORI_DIR = "/var/lib/shiori";
 
       serviceConfig = {
-        ExecStart = "${package}/bin/shiori serve --address '${address}' --port '${toString port}'";
+        ExecStart = "${package}/bin/shiori serve --address '${address}' --port '${toString port}' --webroot '${webRoot}'";
 
         DynamicUser = true;
         StateDirectory = "shiori";
diff --git a/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix b/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
index 9cba5cb4fa9e..4fbf2bad750b 100644
--- a/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
@@ -18,15 +18,19 @@ let
   inherit (snipe-it.passthru) phpPackage;
 
   # shell script for local administration
-  artisan = pkgs.writeScriptBin "snipe-it" ''
+  artisan = (pkgs.writeScriptBin "snipe-it" ''
     #! ${pkgs.runtimeShell}
-    cd ${snipe-it}
+    cd "${snipe-it}/share/php/snipe-it"
     sudo=exec
     if [[ "$USER" != ${user} ]]; then
       sudo='exec /run/wrappers/bin/sudo -u ${user}'
     fi
     $sudo ${phpPackage}/bin/php artisan $*
-  '';
+  '').overrideAttrs (old: {
+    meta = old.meta // {
+      mainProgram = "snipe-it";
+    };
+  });
 in {
   options.services.snipe-it = {
 
@@ -357,7 +361,7 @@ in {
     services.nginx = {
       enable = mkDefault true;
       virtualHosts."${cfg.hostName}" = mkMerge [ cfg.nginx {
-        root = mkForce "${snipe-it}/public";
+        root = mkForce "${snipe-it}/share/php/snipe-it/public";
         extraConfig = optionalString (cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME) "fastcgi_param HTTPS on;";
         locations = {
           "/" = {
@@ -394,7 +398,7 @@ in {
         RuntimeDirectory = "snipe-it/cache";
         RuntimeDirectoryMode = "0700";
       };
-      path = [ pkgs.replace-secret ];
+      path = [ pkgs.replace-secret artisan ];
       script =
         let
           isSecret  = v: isAttrs v && v ? _secret && (isString v._secret || builtins.isPath v._secret);
@@ -451,7 +455,7 @@ in {
           rm "${cfg.dataDir}"/bootstrap/cache/*.php || true
 
           # migrate db
-          ${phpPackage}/bin/php artisan migrate --force
+          ${lib.getExe artisan} migrate --force
 
           # A placeholder file for invalid barcodes
           invalid_barcode_location="${cfg.dataDir}/public/uploads/barcodes/invalid_barcode.gif"
diff --git a/nixpkgs/nixos/modules/services/web-servers/garage.nix b/nixpkgs/nixos/modules/services/web-servers/garage.nix
index 731d5315f23a..47b4c6ab416e 100644
--- a/nixpkgs/nixos/modules/services/web-servers/garage.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/garage.nix
@@ -86,7 +86,7 @@ in
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/garage server";
 
-        StateDirectory = mkIf (hasPrefix "/var/lib/garage" cfg.settings.data_dir && hasPrefix "/var/lib/garage" cfg.settings.metadata_dir) "garage";
+        StateDirectory = mkIf (hasPrefix "/var/lib/garage" cfg.settings.data_dir || hasPrefix "/var/lib/garage" cfg.settings.metadata_dir) "garage";
         DynamicUser = lib.mkDefault true;
         ProtectHome = true;
         NoNewPrivileges = true;
diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
index 9eebd18855c7..f2e8585a9365 100644
--- a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
@@ -35,6 +35,7 @@ let
   compressMimeTypes = [
     "application/atom+xml"
     "application/geo+json"
+    "application/javascript" # Deprecated by IETF RFC 9239, but still widely used
     "application/json"
     "application/ld+json"
     "application/manifest+json"
diff --git a/nixpkgs/nixos/modules/services/web-servers/stargazer.nix b/nixpkgs/nixos/modules/services/web-servers/stargazer.nix
index f0c3cf8787eb..18f57363137c 100644
--- a/nixpkgs/nixos/modules/services/web-servers/stargazer.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/stargazer.nix
@@ -204,11 +204,9 @@ in
     };
 
     # Create default cert store
-    system.activationScripts.makeStargazerCertDir =
-      lib.optionalAttrs (cfg.store == /var/lib/gemini/certs) ''
-        mkdir -p /var/lib/gemini/certs
-        chown -R ${cfg.user}:${cfg.group} /var/lib/gemini/certs
-      '';
+    systemd.tmpfiles.rules = lib.mkIf (cfg.store == /var/lib/gemini/certs) [
+      ''d /var/lib/gemini/certs - "${cfg.user}" "${cfg.group}" -''
+    ];
 
     users.users = lib.optionalAttrs (cfg.user == "stargazer") {
       stargazer = {