about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/services/misc
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/services/misc')
-rw-r--r--nixpkgs/nixos/modules/services/misc/amazon-ssm-agent.nix8
-rw-r--r--nixpkgs/nixos/modules/services/misc/anki-sync-server.md2
-rw-r--r--nixpkgs/nixos/modules/services/misc/devpi-server.nix8
-rw-r--r--nixpkgs/nixos/modules/services/misc/forgejo.nix179
-rw-r--r--nixpkgs/nixos/modules/services/misc/gitea.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/gollum.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/graphical-desktop.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/invidious-router.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/jellyfin.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/mqtt2influxdb.nix5
-rw-r--r--nixpkgs/nixos/modules/services/misc/ollama.nix74
-rw-r--r--nixpkgs/nixos/modules/services/misc/open-webui.nix114
-rw-r--r--nixpkgs/nixos/modules/services/misc/paperless.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/pghero.nix142
-rw-r--r--nixpkgs/nixos/modules/services/misc/plex.nix29
-rw-r--r--nixpkgs/nixos/modules/services/misc/portunus.nix11
-rw-r--r--nixpkgs/nixos/modules/services/misc/private-gpt.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/renovate.nix153
-rw-r--r--nixpkgs/nixos/modules/services/misc/snapper.nix300
-rw-r--r--nixpkgs/nixos/modules/services/misc/sourcehut/service.nix9
-rw-r--r--nixpkgs/nixos/modules/services/misc/spice-autorandr.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/tandoor-recipes.nix4
22 files changed, 837 insertions, 219 deletions
diff --git a/nixpkgs/nixos/modules/services/misc/amazon-ssm-agent.nix b/nixpkgs/nixos/modules/services/misc/amazon-ssm-agent.nix
index 9ab4a7f96d08..0da10621d0a0 100644
--- a/nixpkgs/nixos/modules/services/misc/amazon-ssm-agent.nix
+++ b/nixpkgs/nixos/modules/services/misc/amazon-ssm-agent.nix
@@ -28,13 +28,7 @@ in {
 
   options.services.amazon-ssm-agent = {
     enable = mkEnableOption "Amazon SSM agent";
-
-    package = mkOption {
-      type = types.path;
-      description = "The Amazon SSM agent package to use";
-      default = pkgs.amazon-ssm-agent.override { overrideEtc = false; };
-      defaultText = literalExpression "pkgs.amazon-ssm-agent.override { overrideEtc = false; }";
-    };
+    package = mkPackageOption pkgs "amazon-ssm-agent" {};
   };
 
   config = mkIf cfg.enable {
diff --git a/nixpkgs/nixos/modules/services/misc/anki-sync-server.md b/nixpkgs/nixos/modules/services/misc/anki-sync-server.md
index f58d3d8ad0da..5482a4aa0e5f 100644
--- a/nixpkgs/nixos/modules/services/misc/anki-sync-server.md
+++ b/nixpkgs/nixos/modules/services/misc/anki-sync-server.md
@@ -52,7 +52,7 @@ following options:
 
 ```nix
 {
-  services.anki-sync-server.host = "0.0.0.0";
+  services.anki-sync-server.address = "0.0.0.0";
   services.anki-sync-server.openFirewall = true;
 }
 ```
diff --git a/nixpkgs/nixos/modules/services/misc/devpi-server.nix b/nixpkgs/nixos/modules/services/misc/devpi-server.nix
index 0234db4bc2c5..92c0c6206c8b 100644
--- a/nixpkgs/nixos/modules/services/misc/devpi-server.nix
+++ b/nixpkgs/nixos/modules/services/misc/devpi-server.nix
@@ -74,8 +74,9 @@ in
       # have 0600 permissions.
       preStart =
         ''
-          cp ${cfg.secretFile} ${runtimeDir}/${secretsFileName}
-          chmod 0600 ${runtimeDir}/*${secretsFileName}
+          ${optionalString (!isNull cfg.secretFile)
+            "install -Dm 0600 \${CREDENTIALS_DIRECTORY}/devpi-secret ${runtimeDir}/${secretsFileName}"
+          }
 
           if [ -f ${serverDir}/.nodeinfo ]; then
             # already initialized the package index, exit gracefully
@@ -85,6 +86,9 @@ in
         + strings.optionalString cfg.replica "--role=replica --master-url=${cfg.primaryUrl}";
 
       serviceConfig = {
+        LoadCredential = lib.mkIf (! isNull cfg.secretFile) [
+          "devpi-secret:${cfg.secretFile}"
+        ];
         Restart = "always";
         ExecStart =
           let
diff --git a/nixpkgs/nixos/modules/services/misc/forgejo.nix b/nixpkgs/nixos/modules/services/misc/forgejo.nix
index babed2d5acd4..9a102918f35e 100644
--- a/nixpkgs/nixos/modules/services/misc/forgejo.nix
+++ b/nixpkgs/nixos/modules/services/misc/forgejo.nix
@@ -12,6 +12,15 @@ let
   usePostgresql = cfg.database.type == "postgres";
   useSqlite = cfg.database.type == "sqlite3";
 
+  secrets = let
+    mkSecret = section: values: lib.mapAttrsToList (key: value: {
+      env = envEscape "FORGEJO__${section}__${key}__FILE";
+      path = value;
+    }) values;
+    # https://codeberg.org/forgejo/forgejo/src/tag/v7.0.2/contrib/environment-to-ini/environment-to-ini.go
+    envEscape = string: lib.replaceStrings [ "." "-" ] [ "_0X2E_" "_0X2D_" ] (lib.strings.toUpper string);
+  in lib.flatten (lib.mapAttrsToList mkSecret cfg.secrets);
+
   inherit (lib)
     literalExpression
     mkChangedOptionModule
@@ -34,6 +43,7 @@ in
     (mkRenamedOptionModule [ "services" "forgejo" "appName" ] [ "services" "forgejo" "settings" "DEFAULT" "APP_NAME" ])
     (mkRemovedOptionModule [ "services" "forgejo" "extraConfig" ] "services.forgejo.extraConfig has been removed. Please use the freeform services.forgejo.settings option instead")
     (mkRemovedOptionModule [ "services" "forgejo" "database" "password" ] "services.forgejo.database.password has been removed. Please use services.forgejo.database.passwordFile instead")
+    (mkRenamedOptionModule [ "services" "forgejo" "mailerPasswordFile" ] [ "services" "forgejo" "secrets" "mailer" "PASSWD" ])
 
     # copied from services.gitea; remove at some point
     (mkRenamedOptionModule [ "services" "forgejo" "cookieSecure" ] [ "services" "forgejo" "settings" "session" "COOKIE_SECURE" ])
@@ -224,13 +234,6 @@ in
         description = "Path to the git repositories.";
       };
 
-      mailerPasswordFile = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "/run/keys/forgejo-mailpw";
-        description = "Path to a file containing the SMTP password.";
-      };
-
       settings = mkOption {
         default = { };
         description = ''
@@ -347,6 +350,44 @@ in
           };
         };
       };
+
+      secrets = mkOption {
+        default = { };
+        description = ''
+          This is a small wrapper over systemd's `LoadCredential`.
+
+          It takes the same sections and keys as {option}`services.forgejo.settings`,
+          but the value of each key is a path instead of a string or bool.
+
+          The path is then loaded as credential, exported as environment variable
+          and then feed through
+          <https://codeberg.org/forgejo/forgejo/src/branch/forgejo/contrib/environment-to-ini/environment-to-ini.go>.
+
+          It does the required environment variable escaping for you.
+
+          ::: {.note}
+          Keys specified here take priority over the ones in {option}`services.forgejo.settings`!
+          :::
+        '';
+        example = literalExpression ''
+        {
+          metrics = {
+            TOKEN = "/run/keys/forgejo-metrics-token";
+          };
+          camo = {
+            HMAC_KEY = "/run/keys/forgejo-camo-hmac";
+          };
+          service = {
+            HCAPTCHA_SECRET = "/run/keys/forgejo-hcaptcha-secret";
+            HCAPTCHA_SITEKEY = "/run/keys/forgejo-hcaptcha-sitekey";
+          };
+        }
+        '';
+        type = types.submodule {
+          freeformType = with types; attrsOf (attrsOf path);
+          options = { };
+        };
+      };
     };
   };
 
@@ -381,7 +422,6 @@ in
           HOST = if cfg.database.socket != null then cfg.database.socket else cfg.database.host + ":" + toString cfg.database.port;
           NAME = cfg.database.name;
           USER = cfg.database.user;
-          PASSWD = "#dbpass#";
         })
         (mkIf useSqlite {
           PATH = cfg.database.path;
@@ -397,7 +437,6 @@ in
 
       server = mkIf cfg.lfs.enable {
         LFS_START_SERVER = true;
-        LFS_JWT_SECRET = "#lfsjwtsecret#";
       };
 
       session = {
@@ -405,21 +444,30 @@ in
       };
 
       security = {
-        SECRET_KEY = "#secretkey#";
-        INTERNAL_TOKEN = "#internaltoken#";
         INSTALL_LOCK = true;
       };
 
-      mailer = mkIf (cfg.mailerPasswordFile != null) {
-        PASSWD = "#mailerpass#";
+      lfs = mkIf cfg.lfs.enable {
+        PATH = cfg.lfs.contentDir;
+      };
+    };
+
+    services.forgejo.secrets = {
+      security = {
+        SECRET_KEY = "${cfg.customDir}/conf/secret_key";
+        INTERNAL_TOKEN = "${cfg.customDir}/conf/internal_token";
       };
 
       oauth2 = {
-        JWT_SECRET = "#oauth2jwtsecret#";
+        JWT_SECRET = "${cfg.customDir}/conf/oauth2_jwt_secret";
       };
 
-      lfs = mkIf cfg.lfs.enable {
-        PATH = cfg.lfs.contentDir;
+      database = mkIf (cfg.database.passwordFile != null) {
+        PASSWD = cfg.database.passwordFile;
+      };
+
+      server = mkIf cfg.lfs.enable {
+        LFS_JWT_SECRET = "${cfg.customDir}/conf/lfs_jwt_secret";
       };
     };
 
@@ -476,6 +524,37 @@ in
       "z '${cfg.lfs.contentDir}' 0750 ${cfg.user} ${cfg.group} - -"
     ];
 
+    systemd.services.forgejo-secrets = mkIf (!cfg.useWizard) {
+      description = "Forgejo secret bootstrap helper";
+      script = ''
+        if [ ! -s '${cfg.secrets.security.SECRET_KEY}' ]; then
+            ${exe} generate secret SECRET_KEY > '${cfg.secrets.security.SECRET_KEY}'
+        fi
+
+        if [ ! -s '${cfg.secrets.oauth2.JWT_SECRET}' ]; then
+            ${exe} generate secret JWT_SECRET > '${cfg.secrets.oauth2.JWT_SECRET}'
+        fi
+
+        ${optionalString cfg.lfs.enable ''
+        if [ ! -s '${cfg.secrets.server.LFS_JWT_SECRET}' ]; then
+            ${exe} generate secret LFS_JWT_SECRET > '${cfg.secrets.server.LFS_JWT_SECRET}'
+        fi
+        ''}
+
+        if [ ! -s '${cfg.secrets.security.INTERNAL_TOKEN}' ]; then
+            ${exe} generate secret INTERNAL_TOKEN > '${cfg.secrets.security.INTERNAL_TOKEN}'
+        fi
+      '';
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        User = cfg.user;
+        Group = cfg.group;
+        ReadWritePaths = [ cfg.customDir ];
+        UMask = "0077";
+      };
+    };
+
     systemd.services.forgejo = {
       description = "Forgejo (Beyond coding. We forge.)";
       after = [
@@ -484,11 +563,15 @@ in
         "postgresql.service"
       ] ++ optionals useMysql [
         "mysql.service"
+      ] ++ optionals (!cfg.useWizard) [
+        "forgejo-secrets.service"
       ];
       requires = optionals (cfg.database.createDatabase && usePostgresql) [
         "postgresql.service"
       ] ++ optionals (cfg.database.createDatabase && useMysql) [
         "mysql.service"
+      ] ++ optionals (!cfg.useWizard) [
+        "forgejo-secrets.service"
       ];
       wantedBy = [ "multi-user.target" ];
       path = [ cfg.package pkgs.git pkgs.gnupg ];
@@ -501,61 +584,15 @@ in
       # lfs_jwt_secret.
       # We have to consider this to stay compatible with older installations.
       preStart =
-        let
-          runConfig = "${cfg.customDir}/conf/app.ini";
-          secretKey = "${cfg.customDir}/conf/secret_key";
-          oauth2JwtSecret = "${cfg.customDir}/conf/oauth2_jwt_secret";
-          oldLfsJwtSecret = "${cfg.customDir}/conf/jwt_secret"; # old file for LFS_JWT_SECRET
-          lfsJwtSecret = "${cfg.customDir}/conf/lfs_jwt_secret"; # new file for LFS_JWT_SECRET
-          internalToken = "${cfg.customDir}/conf/internal_token";
-          replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret";
-        in
         ''
-          # copy custom configuration and generate random secrets if needed
-          ${lib.optionalString (!cfg.useWizard) ''
+          ${optionalString (!cfg.useWizard) ''
             function forgejo_setup {
-              cp -f '${format.generate "app.ini" cfg.settings}' '${runConfig}'
-
-              if [ ! -s '${secretKey}' ]; then
-                  ${exe} generate secret SECRET_KEY > '${secretKey}'
-              fi
-
-              # Migrate LFS_JWT_SECRET filename
-              if [[ -s '${oldLfsJwtSecret}' && ! -s '${lfsJwtSecret}' ]]; then
-                  mv '${oldLfsJwtSecret}' '${lfsJwtSecret}'
-              fi
-
-              if [ ! -s '${oauth2JwtSecret}' ]; then
-                  ${exe} generate secret JWT_SECRET > '${oauth2JwtSecret}'
-              fi
-
-              ${optionalString cfg.lfs.enable ''
-              if [ ! -s '${lfsJwtSecret}' ]; then
-                  ${exe} generate secret LFS_JWT_SECRET > '${lfsJwtSecret}'
-              fi
-              ''}
-
-              if [ ! -s '${internalToken}' ]; then
-                  ${exe} generate secret INTERNAL_TOKEN > '${internalToken}'
-              fi
-
-              chmod u+w '${runConfig}'
-              ${replaceSecretBin} '#secretkey#' '${secretKey}' '${runConfig}'
-              ${replaceSecretBin} '#oauth2jwtsecret#' '${oauth2JwtSecret}' '${runConfig}'
-              ${replaceSecretBin} '#internaltoken#' '${internalToken}' '${runConfig}'
-
-              ${optionalString cfg.lfs.enable ''
-                ${replaceSecretBin} '#lfsjwtsecret#' '${lfsJwtSecret}' '${runConfig}'
-              ''}
-
-              ${optionalString (cfg.database.passwordFile != null) ''
-                ${replaceSecretBin} '#dbpass#' '${cfg.database.passwordFile}' '${runConfig}'
-              ''}
-
-              ${optionalString (cfg.mailerPasswordFile != null) ''
-                ${replaceSecretBin} '#mailerpass#' '${cfg.mailerPasswordFile}' '${runConfig}'
-              ''}
-              chmod u-w '${runConfig}'
+              config='${cfg.customDir}/conf/app.ini'
+              cp -f '${format.generate "app.ini" cfg.settings}' "$config"
+
+              chmod u+w "$config"
+              ${lib.getExe' cfg.package "environment-to-ini"} --config "$config"
+              chmod u-w "$config"
             }
             (umask 027; forgejo_setup)
           ''}
@@ -616,6 +653,8 @@ in
         # System Call Filtering
         SystemCallArchitectures = "native";
         SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid" "setrlimit" ];
+        # cfg.secrets
+        LoadCredential = map (e: "${e.env}:${e.path}") secrets;
       };
 
       environment = {
@@ -625,7 +664,7 @@ in
         # is resolved.
         GITEA_WORK_DIR = cfg.stateDir;
         GITEA_CUSTOM = cfg.customDir;
-      };
+      } // lib.listToAttrs (map (e: lib.nameValuePair e.env "%d/${e.env}") secrets);
     };
 
     services.openssh.settings.AcceptEnv = mkIf (!cfg.settings.START_SSH_SERVER or false) "GIT_PROTOCOL";
diff --git a/nixpkgs/nixos/modules/services/misc/gitea.nix b/nixpkgs/nixos/modules/services/misc/gitea.nix
index a8526688b074..d43250c88268 100644
--- a/nixpkgs/nixos/modules/services/misc/gitea.nix
+++ b/nixpkgs/nixos/modules/services/misc/gitea.nix
@@ -722,5 +722,5 @@ in
       timerConfig.OnCalendar = cfg.dump.interval;
     };
   };
-  meta.maintainers = with lib.maintainers; [ srhb ma27 pyrox0 ];
+  meta.maintainers = with lib.maintainers; [ ma27 techknowlogick SuperSandro2000 ];
 }
diff --git a/nixpkgs/nixos/modules/services/misc/gollum.nix b/nixpkgs/nixos/modules/services/misc/gollum.nix
index 3966ef036bec..f320e78a9106 100644
--- a/nixpkgs/nixos/modules/services/misc/gollum.nix
+++ b/nixpkgs/nixos/modules/services/misc/gollum.nix
@@ -110,7 +110,7 @@ in
     users.groups."${cfg.group}" = { };
 
     systemd.tmpfiles.rules = [
-      "d '${cfg.stateDir}' - ${config.users.users.gollum.name} ${config.users.groups.gollum.name} - -"
+      "d '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -"
     ];
 
     systemd.services.gollum = {
diff --git a/nixpkgs/nixos/modules/services/misc/graphical-desktop.nix b/nixpkgs/nixos/modules/services/misc/graphical-desktop.nix
index a88c02e610bf..c8fe0d921c6a 100644
--- a/nixpkgs/nixos/modules/services/misc/graphical-desktop.nix
+++ b/nixpkgs/nixos/modules/services/misc/graphical-desktop.nix
@@ -38,7 +38,7 @@ in
 
     fonts.enableDefaultPackages = lib.mkDefault true;
 
-    hardware.opengl.enable = lib.mkDefault true;
+    hardware.graphics.enable = lib.mkDefault true;
 
     programs.gnupg.agent.pinentryPackage = lib.mkOverride 1100 pkgs.pinentry-gnome3;
 
diff --git a/nixpkgs/nixos/modules/services/misc/invidious-router.nix b/nixpkgs/nixos/modules/services/misc/invidious-router.nix
index 33da7e96b523..7a90c6ab9ddc 100644
--- a/nixpkgs/nixos/modules/services/misc/invidious-router.nix
+++ b/nixpkgs/nixos/modules/services/misc/invidious-router.nix
@@ -8,10 +8,10 @@
   settingsFormat = pkgs.formats.yaml {};
   configFile = settingsFormat.generate "config.yaml" cfg.settings;
 in {
-  meta.maintainers = [lib.maintainers.s1ls];
+  meta.maintainers = [lib.maintainers.sils];
 
   options.services.invidious-router = {
-    enable = lib.mkEnableOption "Enables the invidious-router service";
+    enable = lib.mkEnableOption "the invidious-router service";
     port = lib.mkOption {
       type = lib.types.port;
       default = 8050;
diff --git a/nixpkgs/nixos/modules/services/misc/jellyfin.nix b/nixpkgs/nixos/modules/services/misc/jellyfin.nix
index a1d3910bd93b..a00609087842 100644
--- a/nixpkgs/nixos/modules/services/misc/jellyfin.nix
+++ b/nixpkgs/nixos/modules/services/misc/jellyfin.nix
@@ -160,5 +160,5 @@ in
 
   };
 
-  meta.maintainers = with maintainers; [ minijackson nu-nu-ko ];
+  meta.maintainers = with maintainers; [ minijackson fsnkty ];
 }
diff --git a/nixpkgs/nixos/modules/services/misc/mqtt2influxdb.nix b/nixpkgs/nixos/modules/services/misc/mqtt2influxdb.nix
index a2d6a2b34a23..d07ce1e66ba3 100644
--- a/nixpkgs/nixos/modules/services/misc/mqtt2influxdb.nix
+++ b/nixpkgs/nixos/modules/services/misc/mqtt2influxdb.nix
@@ -124,7 +124,8 @@ let
 in {
   options = {
     services.mqtt2influxdb = {
-      enable = mkEnableOption "BigClown MQTT to InfluxDB bridge.";
+      enable = mkEnableOption "BigClown MQTT to InfluxDB bridge";
+      package = mkPackageOption pkgs ["python3Packages" "mqtt2influxdb"] {};
       environmentFiles = mkOption {
         type = types.listOf types.path;
         default = [];
@@ -245,7 +246,7 @@ in {
       '';
       serviceConfig = {
         EnvironmentFile = cfg.environmentFiles;
-        ExecStart = "${cfg.package}/bin/mqtt2influxdb -dc ${finalConfig}";
+        ExecStart = "${lib.getExe cfg.package} -dc ${finalConfig}";
         RuntimeDirectory = "mqtt2influxdb";
       };
     };
diff --git a/nixpkgs/nixos/modules/services/misc/ollama.nix b/nixpkgs/nixos/modules/services/misc/ollama.nix
index c0341984aa35..c460514783ef 100644
--- a/nixpkgs/nixos/modules/services/misc/ollama.nix
+++ b/nixpkgs/nixos/modules/services/misc/ollama.nix
@@ -1,6 +1,6 @@
 { config, lib, pkgs, ... }:
 let
-  inherit (lib) types;
+  inherit (lib) types mkBefore;
 
   cfg = config.services.ollama;
   ollamaPackage = cfg.package.override {
@@ -11,6 +11,11 @@ let
   };
 in
 {
+  imports = [
+    (lib.mkRemovedOptionModule [ "services" "ollama" "listenAddress" ]
+      "Use `services.ollama.host` and `services.ollama.port` instead.")
+  ];
+
   options = {
     services.ollama = {
       enable = lib.mkEnableOption "ollama server for local large language models";
@@ -64,12 +69,20 @@ in
           See also `services.ollama.sandbox`.
         '';
       };
-      listenAddress = lib.mkOption {
+      host = lib.mkOption {
         type = types.str;
-        default = "127.0.0.1:11434";
-        example = "0.0.0.0:11111";
+        default = "127.0.0.1";
+        example = "0.0.0.0";
         description = ''
-          The address which the ollama server HTTP interface binds and listens to.
+          The host address which the ollama server HTTP interface listens to.
+        '';
+      };
+      port = lib.mkOption {
+        type = types.port;
+        default = 11434;
+        example = 11111;
+        description = ''
+          Which port the ollama server listens to.
         '';
       };
       acceleration = lib.mkOption {
@@ -80,14 +93,30 @@ in
           What interface to use for hardware acceleration.
 
           - `null`: default behavior
-            if `nixpkgs.config.rocmSupport` is enabled, uses `"rocm"`
-            if `nixpkgs.config.cudaSupport` is enabled, uses `"cuda"`
-            otherwise defaults to `false`
+            - if `nixpkgs.config.rocmSupport` is enabled, uses `"rocm"`
+            - if `nixpkgs.config.cudaSupport` is enabled, uses `"cuda"`
+            - otherwise defaults to `false`
           - `false`: disable GPU, only use CPU
           - `"rocm"`: supported by most modern AMD GPUs
+            - may require overriding gpu type with `services.ollama.rocmOverrideGfx`
+              if rocm doesn't detect your AMD gpu
           - `"cuda"`: supported by most modern NVIDIA GPUs
         '';
       };
+      rocmOverrideGfx = lib.mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "10.3.0";
+        description = ''
+          Override what rocm will detect your gpu model as.
+          For example, make rocm treat your RX 5700 XT (or any other model)
+          as an RX 6900 XT using a value of `"10.3.0"` (gfx 1030).
+
+          This sets the value of `HSA_OVERRIDE_GFX_VERSION`. See [ollama's docs](
+          https://github.com/ollama/ollama/blob/main/docs/gpu.md#amd-radeon
+          ) for details.
+        '';
+      };
       environmentVariables = lib.mkOption {
         type = types.attrsOf types.str;
         default = { };
@@ -103,6 +132,22 @@ in
           Since `ollama run` is mostly a shell around the ollama server, this is usually sufficient.
         '';
       };
+      loadModels = lib.mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          The models to download as soon as the service starts.
+          Search for models of your choice from: https://ollama.com/library
+        '';
+      };
+      openFirewall = lib.mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to open the firewall for ollama.
+          This adds `services.ollama.port` to `networking.firewall.allowedTCPPorts`.
+        '';
+      };
     };
   };
 
@@ -114,7 +159,8 @@ in
       environment = cfg.environmentVariables // {
         HOME = cfg.home;
         OLLAMA_MODELS = cfg.models;
-        OLLAMA_HOST = cfg.listenAddress;
+        OLLAMA_HOST = "${cfg.host}:${toString cfg.port}";
+        HSA_OVERRIDE_GFX_VERSION = lib.mkIf (cfg.rocmOverrideGfx != null) cfg.rocmOverrideGfx;
       };
       serviceConfig = {
         ExecStart = "${lib.getExe ollamaPackage} serve";
@@ -123,8 +169,18 @@ in
         DynamicUser = cfg.sandbox;
         ReadWritePaths = cfg.writablePaths;
       };
+      postStart = mkBefore ''
+        set -x
+        export OLLAMA_HOST=${lib.escapeShellArg cfg.host}:${builtins.toString cfg.port}
+        for model in ${lib.escapeShellArgs cfg.loadModels}
+        do
+          ${lib.escapeShellArg (lib.getExe ollamaPackage)} pull "$model"
+        done
+      '';
     };
 
+    networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; };
+
     environment.systemPackages = [ ollamaPackage ];
   };
 
diff --git a/nixpkgs/nixos/modules/services/misc/open-webui.nix b/nixpkgs/nixos/modules/services/misc/open-webui.nix
new file mode 100644
index 000000000000..b4016d03f675
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/open-webui.nix
@@ -0,0 +1,114 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+
+  cfg = config.services.open-webui;
+in
+{
+  options = {
+    services.open-webui = {
+      enable = lib.mkEnableOption "Open-WebUI server";
+      package = lib.mkPackageOption pkgs "open-webui" { };
+
+      stateDir = lib.mkOption {
+        type = types.path;
+        default = "/var/lib/open-webui";
+        example = "/home/foo";
+        description = "State directory of Open-WebUI.";
+      };
+
+      host = lib.mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        example = "0.0.0.0";
+        description = ''
+          The host address which the Open-WebUI server HTTP interface listens to.
+        '';
+      };
+
+      port = lib.mkOption {
+        type = types.port;
+        default = 8080;
+        example = 11111;
+        description = ''
+          Which port the Open-WebUI server listens to.
+        '';
+      };
+
+      environment = lib.mkOption {
+        type = types.attrsOf types.str;
+        default = {
+          SCARF_NO_ANALYTICS = "True";
+          DO_NOT_TRACK = "True";
+          ANONYMIZED_TELEMETRY = "False";
+        };
+        example = ''
+          {
+            OLLAMA_API_BASE_URL = "http://127.0.0.1:11434";
+            # Disable authentication
+            WEBUI_AUTH = "False";
+          }
+        '';
+        description = "Extra environment variables for Open-WebUI";
+      };
+
+      openFirewall = lib.mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to open the firewall for Open-WebUI.
+          This adds `services.open-webui.port` to `networking.firewall.allowedTCPPorts`.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.open-webui = {
+      description = "User-friendly WebUI for LLMs";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      environment = {
+        STATIC_DIR = ".";
+        DATA_DIR = ".";
+        HF_HOME = ".";
+        SENTENCE_TRANSFORMERS_HOME = ".";
+      } // cfg.environment;
+
+      serviceConfig = {
+        ExecStart = "${lib.getExe cfg.package} serve --host ${cfg.host} --port ${toString cfg.port}";
+        WorkingDirectory = cfg.stateDir;
+        StateDirectory = "open-webui";
+        RuntimeDirectory = "open-webui";
+        RuntimeDirectoryMode = "0755";
+        PrivateTmp = true;
+        DynamicUser = true;
+        DevicePolicy = "closed";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = false; # onnxruntime/capi/onnxruntime_pybind11_state.so: cannot enable executable stack as shared object requires: Permission Denied
+        PrivateUsers = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectControlGroups = true;
+        ProcSubset = "all"; # Error in cpuinfo: failed to parse processor information from /proc/cpuinfo
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        SystemCallArchitectures = "native";
+        UMask = "0077";
+      };
+    };
+
+    networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; };
+  };
+
+  meta.maintainers = with lib.maintainers; [ shivaraj-bh ];
+}
diff --git a/nixpkgs/nixos/modules/services/misc/paperless.nix b/nixpkgs/nixos/modules/services/misc/paperless.nix
index e564fe3b8317..6d6a49c10bdd 100644
--- a/nixpkgs/nixos/modules/services/misc/paperless.nix
+++ b/nixpkgs/nixos/modules/services/misc/paperless.nix
@@ -225,7 +225,7 @@ in
       effectively never complete due to running into timeouts.
 
       This sets `OMP_NUM_THREADS` to `1` in order to mitigate the issue. See
-      https://github.com/NixOS/nixpkgs/issues/240591 for more information.
+      https://github.com/NixOS/nixpkgs/issues/240591 for more information
     '' // mkOption { default = true; };
   };
 
diff --git a/nixpkgs/nixos/modules/services/misc/pghero.nix b/nixpkgs/nixos/modules/services/misc/pghero.nix
new file mode 100644
index 000000000000..39515f10c8e1
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/pghero.nix
@@ -0,0 +1,142 @@
+{ config, pkgs, lib, utils, ... }:
+let
+  cfg = config.services.pghero;
+  settingsFormat = pkgs.formats.yaml { };
+  settingsFile = settingsFormat.generate "pghero.yaml" cfg.settings;
+in
+{
+  options.services.pghero = {
+    enable = lib.mkEnableOption "PgHero service";
+    package = lib.mkPackageOption pkgs "pghero" { };
+
+    listenAddress = lib.mkOption {
+      type = lib.types.str;
+      example = "[::1]:3000";
+      description = ''
+        `hostname:port` to listen for HTTP traffic.
+
+        This is bound using the systemd socket activation.
+      '';
+    };
+
+    extraArgs = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      default = [ ];
+      description = ''
+        Additional command-line arguments for the systemd service.
+
+        Refer to the [Puma web server documentation] for available arguments.
+
+        [Puma web server documentation]: https://puma.io/puma#configuration
+      '';
+    };
+
+    settings = lib.mkOption {
+      type = settingsFormat.type;
+      default = { };
+      example = {
+        databases = {
+          primary = {
+            url = "<%= ENV['PRIMARY_DATABASE_URL'] %>";
+          };
+        };
+      };
+      description = ''
+        PgHero configuration. Refer to the [PgHero documentation] for more
+        details.
+
+        [PgHero documentation]: https://github.com/ankane/pghero/blob/master/guides/Linux.md#multiple-databases
+      '';
+    };
+
+    environment = lib.mkOption {
+      type = lib.types.attrsOf lib.types.str;
+      default = { };
+      description = ''
+        Environment variables to set for the service. Secrets should be
+        specified using {option}`environmentFile`.
+      '';
+    };
+
+    environmentFiles = lib.mkOption {
+      type = lib.types.listOf lib.types.path;
+      default = [ ];
+      description = ''
+        File to load environment variables from. Loaded variables override
+        values set in {option}`environment`.
+      '';
+    };
+
+    extraGroups = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      default = [ ];
+      example = [ "tlskeys" ];
+      description = ''
+        Additional groups for the systemd service.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.sockets.pghero = {
+      unitConfig.Description = "PgHero HTTP socket";
+      wantedBy = [ "sockets.target" ];
+      listenStreams = [ cfg.listenAddress ];
+    };
+
+    systemd.services.pghero = {
+      description = "PgHero performance dashboard for PostgreSQL";
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "pghero.socket" ];
+      after = [ "pghero.socket" "network.target" ];
+
+      environment = {
+        RAILS_ENV = "production";
+        PGHERO_CONFIG_PATH = settingsFile;
+      } // cfg.environment;
+
+      serviceConfig = {
+        Type = "notify";
+        WatchdogSec = "10";
+
+        ExecStart = utils.escapeSystemdExecArgs ([
+          (lib.getExe cfg.package)
+          "--bind-to-activated-sockets"
+          "only"
+        ] ++ cfg.extraArgs);
+        Restart = "always";
+
+        WorkingDirectory = "${cfg.package}/share/pghero";
+
+        EnvironmentFile = cfg.environmentFiles;
+        SupplementaryGroups = cfg.extraGroups;
+
+        DynamicUser = true;
+        UMask = "0077";
+
+        ProtectHome = true;
+        ProtectProc = "invisible";
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectHostname = true;
+        ProtectControlGroups = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        PrivateUsers = true;
+        PrivateDevices = true;
+        RestrictRealtime = true;
+        RestrictNamespaces = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
+        DeviceAllow = [ "" ];
+        DevicePolicy = "closed";
+        CapabilityBoundingSet = [ "" ];
+        MemoryDenyWriteExecute = true;
+        LockPersonality = true;
+        SystemCallArchitectures = "native";
+        SystemCallErrorNumber = "EPERM";
+        SystemCallFilter = [ "@system-service" ];
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/plex.nix b/nixpkgs/nixos/modules/services/misc/plex.nix
index fcd8ebbac6ed..212abda5d1e0 100644
--- a/nixpkgs/nixos/modules/services/misc/plex.nix
+++ b/nixpkgs/nixos/modules/services/misc/plex.nix
@@ -93,6 +93,17 @@ in
         '';
       };
 
+      accelerationDevices = mkOption {
+        type = types.listOf types.str;
+        default = ["*"];
+        example = [ "/dev/dri/renderD128" ];
+        description = ''
+          A list of device paths to hardware acceleration devices that Plex should
+          have access to. This is useful when transcoding media files.
+          The special value `"*"` will allow all devices.
+        '';
+      };
+
       package = mkPackageOption pkgs "plex" {
         extraDescription = ''
           Plex subscribers may wish to use their own package here,
@@ -133,6 +144,24 @@ in
         KillSignal = "SIGQUIT";
         PIDFile = "${cfg.dataDir}/Plex Media Server/plexmediaserver.pid";
         Restart = "on-failure";
+
+        # Hardening
+        NoNewPrivileges = true;
+        PrivateTmp = true;
+        PrivateDevices = cfg.accelerationDevices == [];
+        DeviceAllow = mkIf (cfg.accelerationDevices != [] && !lib.elem "*" cfg.accelerationDevices) cfg.accelerationDevices;
+        ProtectSystem = true;
+        ProtectHome = true;
+        ProtectControlGroups = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK"];
+        # This could be made to work if the namespaces needed were known
+        # RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        MemoryDenyWriteExecute = true;
+        LockPersonality = true;
       };
 
       environment = {
diff --git a/nixpkgs/nixos/modules/services/misc/portunus.nix b/nixpkgs/nixos/modules/services/misc/portunus.nix
index bdb35da788e3..a9a069b0c055 100644
--- a/nixpkgs/nixos/modules/services/misc/portunus.nix
+++ b/nixpkgs/nixos/modules/services/misc/portunus.nix
@@ -70,7 +70,7 @@ in
 
         To activate dex, first a search user must be created in the Portunus web ui
         and then the password must to be set as the `DEX_SEARCH_USER_PASSWORD` environment variable
-        in the [](#opt-services.dex.environmentFile) setting.
+        in the [](#opt-services.dex.environmentFile) setting
       '';
 
       oidcClients = mkOption {
@@ -98,6 +98,10 @@ in
 
           The OIDC secret must be set as the `DEX_CLIENT_''${id}` environment variable
           in the [](#opt-services.dex.environmentFile) setting.
+
+          ::: {.note}
+          Make sure the id only contains characters that are allowed in an environment variable name, e.g. no -.
+          :::
         '';
       };
 
@@ -111,10 +115,7 @@ in
     ldap = {
       package = mkOption {
         type = types.package;
-        # needs openldap built with a libxcrypt that support crypt sha256 until users have had time to migrate to newer hashes
-        # Ref: <https://github.com/majewsky/portunus/issues/2>
-        # TODO: remove in NixOS 24.11 (cf. same note on pkgs/servers/portunus/default.nix)
-        default = pkgs.openldap.override { libxcrypt = pkgs.libxcrypt-legacy; };
+        default = pkgs.openldap;
         defaultText = lib.literalExpression "pkgs.openldap.override { libxcrypt = pkgs.libxcrypt-legacy; }";
         description = "The OpenLDAP package to use.";
       };
diff --git a/nixpkgs/nixos/modules/services/misc/private-gpt.nix b/nixpkgs/nixos/modules/services/misc/private-gpt.nix
index 9a3e5317cdb1..ad9b6f5ffa80 100644
--- a/nixpkgs/nixos/modules/services/misc/private-gpt.nix
+++ b/nixpkgs/nixos/modules/services/misc/private-gpt.nix
@@ -117,5 +117,5 @@ in
     };
   };
 
-  meta.maintainers = with lib.maintainers; [ drupol ];
+  meta.maintainers = with lib.maintainers; [ ];
 }
diff --git a/nixpkgs/nixos/modules/services/misc/renovate.nix b/nixpkgs/nixos/modules/services/misc/renovate.nix
new file mode 100644
index 000000000000..25a719c91cbd
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/renovate.nix
@@ -0,0 +1,153 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib)
+    mkEnableOption
+    mkPackageOption
+    mkOption
+    types
+    mkIf
+    ;
+  json = pkgs.formats.json { };
+  cfg = config.services.renovate;
+  generateValidatedConfig =
+    name: value:
+    pkgs.callPackage (
+      { runCommand, jq }:
+      runCommand name
+        {
+          nativeBuildInputs = [
+            jq
+            cfg.package
+          ];
+          value = builtins.toJSON value;
+          passAsFile = [ "value" ];
+          preferLocalBuild = true;
+        }
+        ''
+          jq . "$valuePath"> $out
+          renovate-config-validator $out
+        ''
+    ) { };
+  generateConfig = if cfg.validateSettings then generateValidatedConfig else json.generate;
+in
+{
+  meta.maintainers = with lib.maintainers; [ marie natsukium ];
+
+  options.services.renovate = {
+    enable = mkEnableOption "renovate";
+    package = mkPackageOption pkgs "renovate" { };
+    schedule = mkOption {
+      type = with types; nullOr str;
+      description = "How often to run renovate. See {manpage}`systemd.time(7)` for the format.";
+      example = "*:0/10";
+      default = null;
+    };
+    credentials = mkOption {
+      type = with types; attrsOf path;
+      description = ''
+        Allows configuring environment variable credentials for renovate, read from files.
+        This should always be used for passing confidential data to renovate.
+      '';
+      example = {
+        RENOVATE_TOKEN = "/etc/renovate/token";
+      };
+      default = { };
+    };
+    runtimePackages = mkOption {
+      type = with types; listOf package;
+      description = "Packages available to renovate.";
+      default = [ ];
+    };
+    validateSettings = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Weither to run renovate's config validator on the built configuration.";
+    };
+    settings = mkOption {
+      type = json.type;
+      default = { };
+      example = {
+        platform = "gitea";
+        endpoint = "https://git.example.com";
+        gitAuthor = "Renovate <renovate@example.com>";
+      };
+      description = ''
+        Renovate's global configuration.
+        If you want to pass secrets to renovate, please use {option}`services.renovate.credentials` for that.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.renovate.settings = {
+      cacheDir = "/var/cache/renovate";
+      baseDir = "/var/lib/renovate";
+    };
+
+    systemd.services.renovate = {
+      description = "Renovate dependency updater";
+      documentation = [ "https://docs.renovatebot.com/" ];
+      after = [ "network.target" ];
+      startAt = lib.optional (cfg.schedule != null) cfg.schedule;
+      path = [
+        config.systemd.package
+        pkgs.git
+      ] ++ cfg.runtimePackages;
+
+      serviceConfig = {
+        Type = "oneshot";
+        User = "renovate";
+        Group = "renovate";
+        DynamicUser = true;
+        LoadCredential = lib.mapAttrsToList (name: value: "SECRET-${name}:${value}") cfg.credentials;
+        RemainAfterExit = false;
+        Restart = "on-failure";
+        CacheDirectory = "renovate";
+        StateDirectory = "renovate";
+
+        # Hardening
+        CapabilityBoundingSet = [ "" ];
+        DeviceAllow = [ "" ];
+        LockPersonality = true;
+        PrivateDevices = true;
+        PrivateUsers = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        SystemCallArchitectures = "native";
+        UMask = "0077";
+      };
+
+      script = ''
+        ${lib.concatStringsSep "\n" (
+          builtins.map (name: "export ${name}=$(systemd-creds cat 'SECRET-${name}')") (
+            lib.attrNames cfg.credentials
+          )
+        )}
+        exec ${lib.escapeShellArg (lib.getExe cfg.package)}
+      '';
+
+      environment = {
+        RENOVATE_CONFIG_FILE = generateConfig "renovate-config.json" cfg.settings;
+        HOME = "/var/lib/renovate";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/snapper.nix b/nixpkgs/nixos/modules/services/misc/snapper.nix
index 33207ac2b5bd..1b16ef7958ad 100644
--- a/nixpkgs/nixos/modules/services/misc/snapper.nix
+++ b/nixpkgs/nixos/modules/services/misc/snapper.nix
@@ -1,16 +1,32 @@
-{ config, pkgs, lib, ... }:
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
 
 with lib;
 
 let
   cfg = config.services.snapper;
 
-  mkValue = v:
-    if isList v then "\"${concatMapStringsSep " " (escape [ "\\" " " ]) v}\""
-    else if v == true then "yes"
-    else if v == false then "no"
-    else if isString v then "\"${v}\""
-    else builtins.toJSON v;
+  mkValue =
+    v:
+    if isList v then
+      "\"${
+        concatMapStringsSep " " (escape [
+          "\\"
+          " "
+        ]) v
+      }\""
+    else if v == true then
+      "yes"
+    else if v == false then
+      "no"
+    else if isString v then
+      "\"${v}\""
+    else
+      builtins.toJSON v;
 
   mkKeyValue = k: v: "${k}=${mkValue v}";
 
@@ -43,7 +59,7 @@ let
 
     ALLOW_GROUPS = mkOption {
       type = types.listOf safeStr;
-      default = [];
+      default = [ ];
       description = ''
         List of groups allowed to operate with the config.
 
@@ -53,7 +69,7 @@ let
 
     ALLOW_USERS = mkOption {
       type = types.listOf safeStr;
-      default = [];
+      default = [ ];
       example = [ "alice" ];
       description = ''
         List of users allowed to operate with the config. "root" is always
@@ -78,6 +94,54 @@ let
         Defines whether hourly snapshots should be created.
       '';
     };
+
+    TIMELINE_LIMIT_HOURLY = mkOption {
+      type = types.str;
+      default = "10";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_DAILY = mkOption {
+      type = types.str;
+      default = "10";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_WEEKLY = mkOption {
+      type = types.str;
+      default = "0";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_MONTHLY = mkOption {
+      type = types.str;
+      default = "10";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_QUARTERLY = mkOption {
+      type = types.str;
+      default = "0";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_YEARLY = mkOption {
+      type = types.str;
+      default = "10";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
   };
 in
 
@@ -108,7 +172,7 @@ in
       type = types.bool;
       example = true;
       description = ''
-        Set the `persistentTimer` option for the
+        Set the `Persistent` option for the
         {manpage}`systemd.timer(5)`
         which triggers the snapshot immediately if the last trigger
         was missed (e.g. if the system was powered down).
@@ -152,112 +216,129 @@ in
         is valid here, even if NixOS doesn't document it.
       '';
 
-      type = types.attrsOf (types.submodule {
-        freeformType = types.attrsOf (types.oneOf [ (types.listOf safeStr) types.bool safeStr types.number ]);
-
-        options = configOptions;
-      });
+      type = types.attrsOf (
+        types.submodule {
+          freeformType = types.attrsOf (
+            types.oneOf [
+              (types.listOf safeStr)
+              types.bool
+              safeStr
+              types.number
+            ]
+          );
+
+          options = configOptions;
+        }
+      );
     };
   };
 
-  config = mkIf (cfg.configs != {}) (let
-    documentation = [ "man:snapper(8)" "man:snapper-configs(5)" ];
-  in {
-
-    environment = {
-
-      systemPackages = [ pkgs.snapper ];
-
-      # Note: snapper/config-templates/default is only needed for create-config
-      #       which is not the NixOS way to configure.
-      etc = {
-
-        "sysconfig/snapper".text = ''
-          SNAPPER_CONFIGS="${lib.concatStringsSep " " (builtins.attrNames cfg.configs)}"
-        '';
-
-      }
-      // (mapAttrs' (name: subvolume: nameValuePair "snapper/configs/${name}" ({
-        text = lib.generators.toKeyValue { inherit mkKeyValue; } (filterAttrs (k: v: v != defaultOf k) subvolume);
-      })) cfg.configs)
-      // (lib.optionalAttrs (cfg.filters != null) {
-        "snapper/filters/default.txt".text = cfg.filters;
-      });
-
-    };
+  config = mkIf (cfg.configs != { }) (
+    let
+      documentation = [
+        "man:snapper(8)"
+        "man:snapper-configs(5)"
+      ];
+    in
+    {
+      environment = {
+
+        systemPackages = [ pkgs.snapper ];
+
+        # Note: snapper/config-templates/default is only needed for create-config
+        #       which is not the NixOS way to configure.
+        etc =
+          {
+
+            "sysconfig/snapper".text = ''
+              SNAPPER_CONFIGS="${lib.concatStringsSep " " (builtins.attrNames cfg.configs)}"
+            '';
+          }
+          // (mapAttrs' (
+            name: subvolume:
+            nameValuePair "snapper/configs/${name}" ({
+              text = lib.generators.toKeyValue { inherit mkKeyValue; } (
+                filterAttrs (k: v: v != defaultOf k) subvolume
+              );
+            })
+          ) cfg.configs)
+          // (lib.optionalAttrs (cfg.filters != null) { "snapper/filters/default.txt".text = cfg.filters; });
+      };
 
-    services.dbus.packages = [ pkgs.snapper ];
-
-    systemd.services.snapperd = {
-      description = "DBus interface for snapper";
-      inherit documentation;
-      serviceConfig = {
-        Type = "dbus";
-        BusName = "org.opensuse.Snapper";
-        ExecStart = "${pkgs.snapper}/bin/snapperd";
-        CapabilityBoundingSet = "CAP_DAC_OVERRIDE CAP_FOWNER CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_SYS_ADMIN CAP_SYS_MODULE CAP_IPC_LOCK CAP_SYS_NICE";
-        LockPersonality = true;
-        NoNewPrivileges = false;
-        PrivateNetwork = true;
-        ProtectHostname = true;
-        RestrictAddressFamilies = "AF_UNIX";
-        RestrictRealtime = true;
+      services.dbus.packages = [ pkgs.snapper ];
+
+      systemd.services.snapperd = {
+        description = "DBus interface for snapper";
+        inherit documentation;
+        serviceConfig = {
+          Type = "dbus";
+          BusName = "org.opensuse.Snapper";
+          ExecStart = "${pkgs.snapper}/bin/snapperd";
+          CapabilityBoundingSet = "CAP_DAC_OVERRIDE CAP_FOWNER CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_SYS_ADMIN CAP_SYS_MODULE CAP_IPC_LOCK CAP_SYS_NICE";
+          LockPersonality = true;
+          NoNewPrivileges = false;
+          PrivateNetwork = true;
+          ProtectHostname = true;
+          RestrictAddressFamilies = "AF_UNIX";
+          RestrictRealtime = true;
+        };
       };
-    };
 
-    systemd.services.snapper-timeline = {
-      description = "Timeline of Snapper Snapshots";
-      inherit documentation;
-      requires = [ "local-fs.target" ];
-      serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --timeline";
-    };
+      systemd.services.snapper-timeline = {
+        description = "Timeline of Snapper Snapshots";
+        inherit documentation;
+        requires = [ "local-fs.target" ];
+        serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --timeline";
+      };
 
-    systemd.timers.snapper-timeline = {
-      wantedBy = [ "timers.target" ];
-      timerConfig = {
-        Persistent = cfg.persistentTimer;
-        OnCalendar = cfg.snapshotInterval;
+      systemd.timers.snapper-timeline = {
+        wantedBy = [ "timers.target" ];
+        timerConfig = {
+          Persistent = cfg.persistentTimer;
+          OnCalendar = cfg.snapshotInterval;
+        };
       };
-    };
 
-    systemd.services.snapper-cleanup = {
-      description = "Cleanup of Snapper Snapshots";
-      inherit documentation;
-      serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --cleanup";
-    };
+      systemd.services.snapper-cleanup = {
+        description = "Cleanup of Snapper Snapshots";
+        inherit documentation;
+        serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --cleanup";
+      };
 
-    systemd.timers.snapper-cleanup = {
-      description = "Cleanup of Snapper Snapshots";
-      inherit documentation;
-      wantedBy = [ "timers.target" ];
-      requires = [ "local-fs.target" ];
-      timerConfig.OnBootSec = "10m";
-      timerConfig.OnUnitActiveSec = cfg.cleanupInterval;
-    };
+      systemd.timers.snapper-cleanup = {
+        description = "Cleanup of Snapper Snapshots";
+        inherit documentation;
+        wantedBy = [ "timers.target" ];
+        requires = [ "local-fs.target" ];
+        timerConfig.OnBootSec = "10m";
+        timerConfig.OnUnitActiveSec = cfg.cleanupInterval;
+      };
 
-    systemd.services.snapper-boot = lib.optionalAttrs cfg.snapshotRootOnBoot {
-      description = "Take snapper snapshot of root on boot";
-      inherit documentation;
-      serviceConfig.ExecStart = "${pkgs.snapper}/bin/snapper --config root create --cleanup-algorithm number --description boot";
-      serviceConfig.Type = "oneshot";
-      requires = [ "local-fs.target" ];
-      wantedBy = [ "multi-user.target" ];
-      unitConfig.ConditionPathExists = "/etc/snapper/configs/root";
-    };
+      systemd.services.snapper-boot = lib.mkIf cfg.snapshotRootOnBoot {
+        description = "Take snapper snapshot of root on boot";
+        inherit documentation;
+        serviceConfig.ExecStart = "${pkgs.snapper}/bin/snapper --config root create --cleanup-algorithm number --description boot";
+        serviceConfig.Type = "oneshot";
+        requires = [ "local-fs.target" ];
+        wantedBy = [ "multi-user.target" ];
+        unitConfig.ConditionPathExists = "/etc/snapper/configs/root";
+      };
 
-    assertions =
-      concatMap
-        (name:
-          let
-            sub = cfg.configs.${name};
-          in
-          [ { assertion = !(sub ? extraConfig);
-              message = ''
-                The option definition `services.snapper.configs.${name}.extraConfig' no longer has any effect; please remove it.
-                The contents of this option should be migrated to attributes on `services.snapper.configs.${name}'.
-              '';
-            }
-          ] ++
+      assertions = concatMap (
+        name:
+        let
+          sub = cfg.configs.${name};
+        in
+        [
+          {
+            assertion = !(sub ? extraConfig);
+            message = ''
+              The option definition `services.snapper.configs.${name}.extraConfig' no longer has any effect; please remove it.
+              The contents of this option should be migrated to attributes on `services.snapper.configs.${name}'.
+            '';
+          }
+        ]
+        ++
           map
             (attr: {
               assertion = !(hasAttr attr sub);
@@ -265,8 +346,11 @@ in
                 The option definition `services.snapper.configs.${name}.${attr}' has been renamed to `services.snapper.configs.${name}.${toUpper attr}'.
               '';
             })
-            [ "fstype" "subvolume" ]
-        )
-        (attrNames cfg.configs);
-  });
+            [
+              "fstype"
+              "subvolume"
+            ]
+      ) (attrNames cfg.configs);
+    }
+  );
 }
diff --git a/nixpkgs/nixos/modules/services/misc/sourcehut/service.nix b/nixpkgs/nixos/modules/services/misc/sourcehut/service.nix
index ce5a0e78627c..3507a49ea13a 100644
--- a/nixpkgs/nixos/modules/services/misc/sourcehut/service.nix
+++ b/nixpkgs/nixos/modules/services/misc/sourcehut/service.nix
@@ -324,7 +324,8 @@ in
               };
               preStart =
                 let
-                  version = pkgs.sourcehut.${srvsrht}.version;
+                  package = pkgs.sourcehut.${srvsrht};
+                  version = package.version;
                   stateDir = "/var/lib/sourcehut/${srvsrht}";
                 in
                 mkBefore ''
@@ -336,14 +337,14 @@ in
                   if test ! -e ${stateDir}/db; then
                     # Setup the initial database.
                     # Note that it stamps the alembic head afterward
-                    ${cfg.python}/bin/${srvsrht}-initdb
+                    ${package}/bin/${srvsrht}-initdb
                     echo ${version} >${stateDir}/db
                   fi
 
                   ${optionalString cfg.settings.${iniKey}.migrate-on-upgrade ''
                     if [ "$(cat ${stateDir}/db)" != "${version}" ]; then
                       # Manage schema migrations using alembic
-                      ${cfg.python}/bin/${srvsrht}-migrate -a upgrade head
+                      ${package}/bin/${srvsrht}-migrate -a upgrade head
                       echo ${version} >${stateDir}/db
                     fi
                   ''}
@@ -389,7 +390,7 @@ in
               after = [ "network.target" "${srvsrht}.service" ];
               serviceConfig = {
                 Type = "oneshot";
-                ExecStart = "${cfg.python}/bin/${timerName}";
+                ExecStart = "${pkgs.sourcehut.${srvsrht}}/bin/${timerName}";
               };
             }
             (timer.service or { })
diff --git a/nixpkgs/nixos/modules/services/misc/spice-autorandr.nix b/nixpkgs/nixos/modules/services/misc/spice-autorandr.nix
index 0d58d2865717..92b8a15e93c5 100644
--- a/nixpkgs/nixos/modules/services/misc/spice-autorandr.nix
+++ b/nixpkgs/nixos/modules/services/misc/spice-autorandr.nix
@@ -6,7 +6,7 @@ in
 {
   options = {
     services.spice-autorandr = {
-      enable = lib.mkEnableOption "spice-autorandr service that will automatically resize display to match SPICE client window size.";
+      enable = lib.mkEnableOption "spice-autorandr service that will automatically resize display to match SPICE client window size";
       package = lib.mkPackageOption pkgs "spice-autorandr" { };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/misc/tandoor-recipes.nix b/nixpkgs/nixos/modules/services/misc/tandoor-recipes.nix
index a2210f3d7db5..1c903d280378 100644
--- a/nixpkgs/nixos/modules/services/misc/tandoor-recipes.nix
+++ b/nixpkgs/nixos/modules/services/misc/tandoor-recipes.nix
@@ -22,7 +22,7 @@ let
     ${lib.toShellVars env}
     eval "$(${config.systemd.package}/bin/systemctl show -pUID,GID,MainPID tandoor-recipes.service)"
     exec ${pkgs.util-linux}/bin/nsenter \
-      -t $MainPID -m -S $UID -G $GID \
+      -t $MainPID -m -S $UID -G $GID --wdns=${env.MEDIA_ROOT} \
       ${pkg}/bin/tandoor-recipes "$@"
   '';
 in
@@ -88,7 +88,7 @@ in
         Group = "tandoor_recipes";
         DynamicUser = true;
         StateDirectory = "tandoor-recipes";
-        WorkingDirectory = "/var/lib/tandoor-recipes";
+        WorkingDirectory = env.MEDIA_ROOT;
         RuntimeDirectory = "tandoor-recipes";
 
         BindReadOnlyPaths = [