about summary refs log tree commit diff
path: root/nixos/modules/services
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services')
-rw-r--r--nixos/modules/services/continuous-integration/buildbot/master.nix3
-rw-r--r--nixos/modules/services/continuous-integration/github-runner/options.nix1
-rw-r--r--nixos/modules/services/games/archisteamfarm.nix (renamed from nixos/modules/services/games/asf.nix)100
-rw-r--r--nixos/modules/services/mail/dovecot.nix5
-rw-r--r--nixos/modules/services/misc/moonraker.nix2
-rw-r--r--nixos/modules/services/misc/nix-gc.nix25
-rw-r--r--nixos/modules/services/misc/ollama.nix8
-rw-r--r--nixos/modules/services/misc/taskserver/helper-tool.py34
-rw-r--r--nixos/modules/services/networking/bird.nix9
-rw-r--r--nixos/modules/services/networking/keepalived/default.nix9
-rw-r--r--nixos/modules/services/security/clamav.nix2
-rw-r--r--nixos/modules/services/system/cloud-init.nix5
-rw-r--r--nixos/modules/services/system/dbus.nix1
-rw-r--r--nixos/modules/services/web-apps/netbox.nix19
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix211
-rw-r--r--nixos/modules/services/web-servers/ttyd.nix3
16 files changed, 239 insertions, 198 deletions
diff --git a/nixos/modules/services/continuous-integration/buildbot/master.nix b/nixos/modules/services/continuous-integration/buildbot/master.nix
index c86cb81e5df47..9f702b17937cf 100644
--- a/nixos/modules/services/continuous-integration/buildbot/master.nix
+++ b/nixos/modules/services/continuous-integration/buildbot/master.nix
@@ -267,8 +267,7 @@ in {
 
     systemd.services.buildbot-master = {
       description = "Buildbot Continuous Integration Server.";
-      after = [ "network-online.target" ];
-      wants = [ "network-online.target" ];
+      after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       path = cfg.packages ++ cfg.pythonPackages python.pkgs;
       environment.PYTHONPATH = "${python.withPackages (self: cfg.pythonPackages self ++ [ package ])}/${python.sitePackages}";
diff --git a/nixos/modules/services/continuous-integration/github-runner/options.nix b/nixos/modules/services/continuous-integration/github-runner/options.nix
index 2335826e8b665..b9b1ea05e9672 100644
--- a/nixos/modules/services/continuous-integration/github-runner/options.nix
+++ b/nixos/modules/services/continuous-integration/github-runner/options.nix
@@ -153,6 +153,7 @@ with lib;
     type = types.attrs;
     description = lib.mdDoc ''
       Modify the systemd service. Can be used to, e.g., adjust the sandboxing options.
+      See {manpage}`systemd.exec(5)` for more options.
     '';
     example = {
       ProtectHome = false;
diff --git a/nixos/modules/services/games/asf.nix b/nixos/modules/services/games/archisteamfarm.nix
index 27d174d6726ba..293e341bef385 100644
--- a/nixos/modules/services/games/asf.nix
+++ b/nixos/modules/services/games/archisteamfarm.nix
@@ -1,13 +1,11 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.services.archisteamfarm;
 
   format = pkgs.formats.json { };
 
-  asf-config = format.generate "ASF.json" (cfg.settings // {
+  configFile = format.generate "ASF.json" (cfg.settings // {
     # we disable it because ASF cannot update itself anyways
     # and nixos takes care of restarting the service
     # is in theory not needed as this is already the default for default builds
@@ -30,8 +28,8 @@ let
 in
 {
   options.services.archisteamfarm = {
-    enable = mkOption {
-      type = types.bool;
+    enable = lib.mkOption {
+      type = lib.types.bool;
       description = lib.mdDoc ''
         If enabled, starts the ArchisSteamFarm service.
         For configuring the SteamGuard token you will need to use the web-ui, which is enabled by default over on 127.0.0.1:1242.
@@ -40,14 +38,14 @@ in
       default = false;
     };
 
-    web-ui = mkOption {
-      type = types.submodule {
+    web-ui = lib.mkOption {
+      type = lib.types.submodule {
         options = {
-          enable = mkEnableOption "" // {
+          enable = lib.mkEnableOption "" // {
             description = lib.mdDoc "Whether to start the web-ui. This is the preferred way of configuring things such as the steam guard token.";
           };
 
-          package = mkPackageOption pkgs [ "ArchiSteamFarm" "ui" ] {
+          package = lib.mkPackageOption pkgs [ "ArchiSteamFarm" "ui" ] {
             extraDescription = ''
               ::: {.note}
               Contents must be in lib/dist
@@ -65,7 +63,7 @@ in
       description = lib.mdDoc "The Web-UI hosted on 127.0.0.1:1242.";
     };
 
-    package = mkPackageOption pkgs "ArchiSteamFarm" {
+    package = lib.mkPackageOption pkgs "ArchiSteamFarm" {
       extraDescription = ''
         ::: {.warning}
         Should always be the latest version, for security reasons,
@@ -74,15 +72,15 @@ in
       '';
     };
 
-    dataDir = mkOption {
-      type = types.path;
-      default = "/var/lib/asf";
+    dataDir = lib.mkOption {
+      type = lib.types.path;
+      default = "/var/lib/archisteamfarm";
       description = lib.mdDoc ''
         The ASF home directory used to store all data.
         If left as the default value this directory will automatically be created before the ASF server starts, otherwise the sysadmin is responsible for ensuring the directory exists with appropriate ownership and permissions.'';
     };
 
-    settings = mkOption {
+    settings = lib.mkOption {
       type = format.type;
       description = lib.mdDoc ''
         The ASF.json file, all the options are documented [here](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Configuration#global-config).
@@ -96,13 +94,13 @@ in
       default = { };
     };
 
-    ipcPasswordFile = mkOption {
-      type = types.nullOr types.path;
+    ipcPasswordFile = lib.mkOption {
+      type = with lib.types; nullOr path;
       default = null;
-      description = lib.mdDoc "Path to a file containing the password. The file must be readable by the `asf` user/group.";
+      description = lib.mdDoc "Path to a file containing the password. The file must be readable by the `archisteamfarm` user/group.";
     };
 
-    ipcSettings = mkOption {
+    ipcSettings = lib.mkOption {
       type = format.type;
       description = lib.mdDoc ''
         Settings to write to IPC.config.
@@ -120,25 +118,25 @@ in
       default = { };
     };
 
-    bots = mkOption {
-      type = types.attrsOf (types.submodule {
+    bots = lib.mkOption {
+      type = lib.types.attrsOf (lib.types.submodule {
         options = {
-          username = mkOption {
-            type = types.str;
+          username = lib.mkOption {
+            type = lib.types.str;
             description = lib.mdDoc "Name of the user to log in. Default is attribute name.";
             default = "";
           };
-          passwordFile = mkOption {
-            type = types.path;
-            description = lib.mdDoc "Path to a file containing the password. The file must be readable by the `asf` user/group.";
+          passwordFile = lib.mkOption {
+            type = lib.types.path;
+            description = lib.mdDoc "Path to a file containing the password. The file must be readable by the `archisteamfarm` user/group.";
           };
-          enabled = mkOption {
-            type = types.bool;
+          enabled = lib.mkOption {
+            type = lib.types.bool;
             default = true;
             description = lib.mdDoc "Whether to enable the bot on startup.";
           };
-          settings = mkOption {
-            type = types.attrs;
+          settings = lib.mkOption {
+            type = lib.types.attrs;
             description = lib.mdDoc ''
               Additional settings that are documented [here](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Configuration#bot-config).
             '';
@@ -152,7 +150,7 @@ in
       example = {
         exampleBot = {
           username = "alice";
-          passwordFile = "/var/lib/asf/secrets/password";
+          passwordFile = "/var/lib/archisteamfarm/secrets/password";
           settings = { SteamParentalCode = "1234"; };
         };
       };
@@ -160,32 +158,34 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
+    # TODO: drop with 24.11
+    services.archisteamfarm.dataDir = lib.mkIf (lib.versionAtLeast config.system.stateVersion "24.05") (lib.mkDefault "/var/lib/asf");
 
     users = {
-      users.asf = {
+      users.archisteamfarm = {
         home = cfg.dataDir;
         isSystemUser = true;
-        group = "asf";
+        group = "archisteamfarm";
         description = "Archis-Steam-Farm service user";
       };
-      groups.asf = { };
+      groups.archisteamfarm = { };
     };
 
     systemd.services = {
-      asf = {
+      archisteamfarm = {
         description = "Archis-Steam-Farm Service";
         after = [ "network.target" ];
         wantedBy = [ "multi-user.target" ];
 
-        serviceConfig = mkMerge [
-          (mkIf (cfg.dataDir == "/var/lib/asf") {
-            StateDirectory = "asf";
+        serviceConfig = lib.mkMerge [
+          (lib.mkIf (lib.hasPrefix "/var/lib/" cfg.dataDir) {
+            StateDirectory = lib.last (lib.splitString "/" cfg.dataDir);
             StateDirectoryMode = "700";
           })
           {
-            User = "asf";
-            Group = "asf";
+            User = "archisteamfarm";
+            Group = "archisteamfarm";
             WorkingDirectory = cfg.dataDir;
             Type = "simple";
             ExecStart = "${lib.getExe cfg.package} --no-restart --process-required --service --system-required --path ${cfg.dataDir}";
@@ -217,12 +217,10 @@ in
             RestrictNamespaces = true;
             RestrictRealtime = true;
             RestrictSUIDSGID = true;
-            SystemCallArchitectures = "native";
-            UMask = "0077";
-
-            # we luckily already have systemd v247+
             SecureBits = "noroot-locked";
+            SystemCallArchitectures = "native";
             SystemCallFilter = [ "@system-service" "~@privileged" ];
+            UMask = "0077";
           }
         ];
 
@@ -232,7 +230,7 @@ in
               mkdir -p $out
               # clean potential removed bots
               rm -rf $out/*.json
-              for i in ${strings.concatStringsSep " " (lists.map (x: "${getName x},${x}") (attrsets.mapAttrsToList mkBot cfg.bots))}; do IFS=",";
+              for i in ${lib.concatStringsSep " " (map (x: "${lib.getName x},${x}") (lib.mapAttrsToList mkBot cfg.bots))}; do IFS=",";
                 set -- $i
                 ln -fs $2 $out/$1
               done
@@ -242,22 +240,22 @@ in
           ''
             mkdir -p config
 
-            cp --no-preserve=mode ${asf-config} config/ASF.json
+            cp --no-preserve=mode ${configFile} config/ASF.json
 
-            ${optionalString (cfg.ipcPasswordFile != null) ''
+            ${lib.optionalString (cfg.ipcPasswordFile != null) ''
               ${replaceSecretBin} '#ipcPassword#' '${cfg.ipcPasswordFile}' config/ASF.json
             ''}
 
-            ${optionalString (cfg.ipcSettings != {}) ''
+            ${lib.optionalString (cfg.ipcSettings != {}) ''
               ln -fs ${ipc-config} config/IPC.config
             ''}
 
-            ${optionalString (cfg.ipcSettings != {}) ''
+            ${lib.optionalString (cfg.ipcSettings != {}) ''
               ln -fs ${createBotsScript}/* config/
             ''}
 
             rm -f www
-            ${optionalString cfg.web-ui.enable ''
+            ${lib.optionalString cfg.web-ui.enable ''
               ln -s ${cfg.web-ui.package}/ www
             ''}
           '';
@@ -267,6 +265,6 @@ in
 
   meta = {
     buildDocsInSandbox = false;
-    maintainers = with maintainers; [ lom SuperSandro2000 ];
+    maintainers = with lib.maintainers; [ lom SuperSandro2000 ];
   };
 }
diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix
index 79c8fec752521..25c7017a1d258 100644
--- a/nixos/modules/services/mail/dovecot.nix
+++ b/nixos/modules/services/mail/dovecot.nix
@@ -119,10 +119,9 @@ let
     ''
       plugin {
         sieve_plugins = ${concatStringsSep " " cfg.sieve.plugins}
+        sieve_extensions = ${concatStringsSep " " (map (el: "+${el}") cfg.sieve.extensions)}
+        sieve_global_extensions = ${concatStringsSep " " (map (el: "+${el}") cfg.sieve.globalExtensions)}
     ''
-    (optionalString (cfg.sieve.extensions != []) ''sieve_extensions = ${concatMapStringsSep " " (el: "+${el}") cfg.sieve.extensions}'')
-    (optionalString (cfg.sieve.globalExtensions != []) ''sieve_global_extensions = ${concatMapStringsSep " " (el: "+${el}") cfg.sieve.globalExtensions}'')
-
     (optionalString (cfg.imapsieve.mailbox != []) ''
       ${
         concatStringsSep "\n" (flatten (imap1 (
diff --git a/nixos/modules/services/misc/moonraker.nix b/nixos/modules/services/misc/moonraker.nix
index 750dca9d03736..4e419aafa990b 100644
--- a/nixos/modules/services/misc/moonraker.nix
+++ b/nixos/modules/services/misc/moonraker.nix
@@ -103,7 +103,7 @@ in {
 
   config = mkIf cfg.enable {
     warnings = []
-      ++ optional (cfg.settings ? update_manager)
+      ++ optional (cfg.settings.update_manager.enable_system_updates or false)
         ''Enabling update_manager is not supported on NixOS and will lead to non-removable warnings in some clients.''
       ++ optional (cfg.configDir != null)
         ''
diff --git a/nixos/modules/services/misc/nix-gc.nix b/nixos/modules/services/misc/nix-gc.nix
index 97596d28cd89b..de6bd76c7eb9d 100644
--- a/nixos/modules/services/misc/nix-gc.nix
+++ b/nixos/modules/services/misc/nix-gc.nix
@@ -1,7 +1,5 @@
 { config, lib, ... }:
 
-with lib;
-
 let
   cfg = config.nix.gc;
 in
@@ -14,14 +12,14 @@ in
 
     nix.gc = {
 
-      automatic = mkOption {
+      automatic = lib.mkOption {
         default = false;
-        type = types.bool;
+        type = lib.types.bool;
         description = lib.mdDoc "Automatically run the garbage collector at a specific time.";
       };
 
-      dates = mkOption {
-        type = types.str;
+      dates = lib.mkOption {
+        type = lib.types.singleLineStr;
         default = "03:15";
         example = "weekly";
         description = lib.mdDoc ''
@@ -33,9 +31,9 @@ in
         '';
       };
 
-      randomizedDelaySec = mkOption {
+      randomizedDelaySec = lib.mkOption {
         default = "0";
-        type = types.str;
+        type = lib.types.singleLineStr;
         example = "45min";
         description = lib.mdDoc ''
           Add a randomized delay before each garbage collection.
@@ -45,9 +43,9 @@ in
         '';
       };
 
-      persistent = mkOption {
+      persistent = lib.mkOption {
         default = true;
-        type = types.bool;
+        type = lib.types.bool;
         example = false;
         description = lib.mdDoc ''
           Takes a boolean argument. If true, the time when the service
@@ -61,10 +59,10 @@ in
         '';
       };
 
-      options = mkOption {
+      options = lib.mkOption {
         default = "";
         example = "--max-freed $((64 * 1024**3))";
-        type = types.str;
+        type = lib.types.singleLineStr;
         description = lib.mdDoc ''
           Options given to {file}`nix-collect-garbage` when the
           garbage collector is run automatically.
@@ -89,7 +87,8 @@ in
     systemd.services.nix-gc = lib.mkIf config.nix.enable {
       description = "Nix Garbage Collector";
       script = "exec ${config.nix.package.out}/bin/nix-collect-garbage ${cfg.options}";
-      startAt = optional cfg.automatic cfg.dates;
+      serviceConfig.Type = "oneshot";
+      startAt = lib.optional cfg.automatic cfg.dates;
     };
 
     systemd.timers.nix-gc = lib.mkIf cfg.automatic {
diff --git a/nixos/modules/services/misc/ollama.nix b/nixos/modules/services/misc/ollama.nix
index 9794bbbec464c..d9359d2b5cd44 100644
--- a/nixos/modules/services/misc/ollama.nix
+++ b/nixos/modules/services/misc/ollama.nix
@@ -9,6 +9,13 @@ in {
       enable = lib.mkEnableOption (
         lib.mdDoc "Server for local large language models"
       );
+      listenAddress = lib.mkOption {
+        type = lib.types.str;
+        default = "127.0.0.1:11434";
+        description = lib.mdDoc ''
+          Specifies the bind address on which the ollama server HTTP interface listens.
+        '';
+      };
       package = lib.mkPackageOption pkgs "ollama" { };
     };
   };
@@ -23,6 +30,7 @@ in {
         environment = {
           HOME = "%S/ollama";
           OLLAMA_MODELS = "%S/ollama/models";
+          OLLAMA_HOST = cfg.listenAddress;
         };
         serviceConfig = {
           ExecStart = "${lib.getExe cfg.package} serve";
diff --git a/nixos/modules/services/misc/taskserver/helper-tool.py b/nixos/modules/services/misc/taskserver/helper-tool.py
index fec05728b2b6b..b1eebb07686b2 100644
--- a/nixos/modules/services/misc/taskserver/helper-tool.py
+++ b/nixos/modules/services/misc/taskserver/helper-tool.py
@@ -61,6 +61,10 @@ def run_as_taskd_user():
     os.setuid(uid)
 
 
+def run_as_taskd_group():
+    gid = grp.getgrnam(TASKD_GROUP).gr_gid
+    os.setgid(gid)
+
 def taskd_cmd(cmd, *args, **kwargs):
     """
     Invoke taskd with the specified command with the privileges of the 'taskd'
@@ -90,7 +94,7 @@ def certtool_cmd(*args, **kwargs):
     """
     return subprocess.check_output(
         [CERTTOOL_COMMAND] + list(args),
-        preexec_fn=lambda: os.umask(0o077),
+        preexec_fn=run_as_taskd_group,
         stderr=subprocess.STDOUT,
         **kwargs
     )
@@ -156,17 +160,33 @@ def generate_key(org, user):
         sys.stderr.write(msg.format(user))
         return
 
-    basedir = os.path.join(TASKD_DATA_DIR, "keys", org, user)
-    if os.path.exists(basedir):
+    keysdir = os.path.join(TASKD_DATA_DIR, "keys" )
+    orgdir  = os.path.join(keysdir       , org    )
+    userdir = os.path.join(orgdir        , user   )
+    if os.path.exists(userdir):
         raise OSError("Keyfile directory for {} already exists.".format(user))
 
-    privkey = os.path.join(basedir, "private.key")
-    pubcert = os.path.join(basedir, "public.cert")
+    privkey = os.path.join(userdir, "private.key")
+    pubcert = os.path.join(userdir, "public.cert")
 
     try:
-        os.makedirs(basedir, mode=0o700)
+        # We change the permissions and the owner ship of the base directories
+        # so that cfg.group and cfg.user could read the directories' contents.
+        # See also: https://bugs.python.org/issue42367
+        for bd in [keysdir, orgdir, userdir]:
+            # Allow cfg.group, but not others to read the contents of this group
+            os.makedirs(bd, exist_ok=True)
+            # not using mode= argument to makedirs intentionally - forcing the
+            # permissions we want
+            os.chmod(bd, mode=0o750)
+            os.chown(
+                bd,
+                uid=pwd.getpwnam(TASKD_USER).pw_uid,
+                gid=grp.getgrnam(TASKD_GROUP).gr_gid,
+            )
 
         certtool_cmd("-p", "--bits", CERT_BITS, "--outfile", privkey)
+        os.chmod(privkey, 0o640)
 
         template_data = [
             "organization = {0}".format(org),
@@ -187,7 +207,7 @@ def generate_key(org, user):
                 "--outfile", pubcert
             )
     except:
-        rmtree(basedir)
+        rmtree(userdir)
         raise
 
 
diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix
index 9deeb7694d2ac..e25f5c7b03794 100644
--- a/nixos/modules/services/networking/bird.nix
+++ b/nixos/modules/services/networking/bird.nix
@@ -18,6 +18,13 @@ in
           <http://bird.network.cz/>
         '';
       };
+      autoReload = mkOption {
+        type = types.bool;
+        default = true;
+        description = lib.mdDoc ''
+          Whether bird2 should be automatically reloaded when the configuration changes.
+        '';
+      };
       checkConfig = mkOption {
         type = types.bool;
         default = true;
@@ -68,7 +75,7 @@ in
     systemd.services.bird2 = {
       description = "BIRD Internet Routing Daemon";
       wantedBy = [ "multi-user.target" ];
-      reloadTriggers = [ config.environment.etc."bird/bird2.conf".source ];
+      reloadTriggers = lib.optional cfg.autoReload config.environment.etc."bird/bird2.conf".source;
       serviceConfig = {
         Type = "forking";
         Restart = "on-failure";
diff --git a/nixos/modules/services/networking/keepalived/default.nix b/nixos/modules/services/networking/keepalived/default.nix
index 429a47c3962c6..599dfd52e271f 100644
--- a/nixos/modules/services/networking/keepalived/default.nix
+++ b/nixos/modules/services/networking/keepalived/default.nix
@@ -59,9 +59,11 @@ let
         ${optionalString i.vmacXmitBase "vmac_xmit_base"}
 
         ${optionalString (i.unicastSrcIp != null) "unicast_src_ip ${i.unicastSrcIp}"}
-        unicast_peer {
-          ${concatStringsSep "\n" i.unicastPeers}
-        }
+        ${optionalString (builtins.length i.unicastPeers > 0) ''
+          unicast_peer {
+            ${concatStringsSep "\n" i.unicastPeers}
+          }
+        ''}
 
         virtual_ipaddress {
           ${concatMapStringsSep "\n" virtualIpLine i.virtualIps}
@@ -138,6 +140,7 @@ let
 
 in
 {
+  meta.maintainers = [ lib.maintainers.raitobezarius ];
 
   options = {
     services.keepalived = {
diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix
index d3164373ec01f..4480c0cae60c9 100644
--- a/nixos/modules/services/security/clamav.nix
+++ b/nixos/modules/services/security/clamav.nix
@@ -196,6 +196,7 @@ in
     systemd.services.clamav-freshclam = mkIf cfg.updater.enable {
       description = "ClamAV virus database updater (freshclam)";
       restartTriggers = [ freshclamConfigFile ];
+      requires = [ "network-online.target" ];
       after = [ "network-online.target" ];
 
       serviceConfig = {
@@ -243,6 +244,7 @@ in
     systemd.services.clamav-fangfrisch = mkIf cfg.fangfrisch.enable {
       description = "ClamAV virus database updater (fangfrisch)";
       restartTriggers = [ fangfrischConfigFile ];
+      requires = [ "network-online.target" ];
       after = [ "network-online.target" "clamav-fangfrisch-init.service" ];
 
       serviceConfig = {
diff --git a/nixos/modules/services/system/cloud-init.nix b/nixos/modules/services/system/cloud-init.nix
index d782bb1a36668..00ae77be4271c 100644
--- a/nixos/modules/services/system/cloud-init.nix
+++ b/nixos/modules/services/system/cloud-init.nix
@@ -164,7 +164,10 @@ in
     systemd.services.cloud-init-local = {
       description = "Initial cloud-init job (pre-networking)";
       wantedBy = [ "multi-user.target" ];
-      before = [ "systemd-networkd.service" ];
+      # In certain environments (AWS for example), cloud-init-local will
+      # first configure an IP through DHCP, and later delete it.
+      # This can cause race conditions with anything else trying to set IP through DHCP.
+      before = [ "systemd-networkd.service" "dhcpcd.service" ];
       path = path;
       serviceConfig = {
         Type = "oneshot";
diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix
index b47ebc92f93a8..e8f8b48d0337f 100644
--- a/nixos/modules/services/system/dbus.nix
+++ b/nixos/modules/services/system/dbus.nix
@@ -95,6 +95,7 @@ in
         uid = config.ids.uids.messagebus;
         description = "D-Bus system message bus daemon user";
         home = homeDir;
+        homeMode = "0755";
         group = "messagebus";
       };
 
diff --git a/nixos/modules/services/web-apps/netbox.nix b/nixos/modules/services/web-apps/netbox.nix
index 72ec578146a76..d034f3234a2bd 100644
--- a/nixos/modules/services/web-apps/netbox.nix
+++ b/nixos/modules/services/web-apps/netbox.nix
@@ -75,13 +75,17 @@ in {
     package = lib.mkOption {
       type = lib.types.package;
       default =
-        if lib.versionAtLeast config.system.stateVersion "23.11"
+        if lib.versionAtLeast config.system.stateVersion "24.05"
+        then pkgs.netbox_3_7
+        else if lib.versionAtLeast config.system.stateVersion "23.11"
         then pkgs.netbox_3_6
         else if lib.versionAtLeast config.system.stateVersion "23.05"
         then pkgs.netbox_3_5
         else pkgs.netbox_3_3;
       defaultText = lib.literalExpression ''
-        if lib.versionAtLeast config.system.stateVersion "23.11"
+        if lib.versionAtLeast config.system.stateVersion "24.05"
+        then pkgs.netbox_3_7
+        else if lib.versionAtLeast config.system.stateVersion "23.11"
         then pkgs.netbox_3_6
         else if lib.versionAtLeast config.system.stateVersion "23.05"
         then pkgs.netbox_3_5
@@ -306,12 +310,13 @@ in {
           ${pkg}/bin/netbox trace_paths --no-input
           ${pkg}/bin/netbox collectstatic --no-input
           ${pkg}/bin/netbox remove_stale_contenttypes --no-input
-          # TODO: remove the condition when we remove netbox_3_3
-          ${lib.optionalString
-            (lib.versionAtLeast cfg.package.version "3.5.0")
-            "${pkg}/bin/netbox reindex --lazy"}
+          ${pkg}/bin/netbox reindex --lazy
           ${pkg}/bin/netbox clearsessions
-          ${pkg}/bin/netbox clearcache
+          ${lib.optionalString
+            # The clearcache command was removed in 3.7.0:
+            # https://github.com/netbox-community/netbox/issues/14458
+            (lib.versionOlder cfg.package.version "3.7.0")
+            "${pkg}/bin/netbox clearcache"}
 
           echo "${cfg.package.version}" > "$versionFile"
         '';
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index 38c51251aac1f..0b19265942c03 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -99,11 +99,101 @@ let
   mysqlLocal = cfg.database.createLocally && cfg.config.dbtype == "mysql";
   pgsqlLocal = cfg.database.createLocally && cfg.config.dbtype == "pgsql";
 
+  nextcloudGreaterOrEqualThan = versionAtLeast cfg.package.version;
+  nextcloudOlderThan = versionOlder cfg.package.version;
+
   # https://github.com/nextcloud/documentation/pull/11179
-  ocmProviderIsNotAStaticDirAnymore = versionAtLeast cfg.package.version "27.1.2"
-    || (versionOlder cfg.package.version "27.0.0"
-      && versionAtLeast cfg.package.version "26.0.8");
+  ocmProviderIsNotAStaticDirAnymore = nextcloudGreaterOrEqualThan "27.1.2"
+    || (nextcloudOlderThan "27.0.0" && nextcloudGreaterOrEqualThan "26.0.8");
+
+  overrideConfig = let
+    c = cfg.config;
+    requiresReadSecretFunction = c.dbpassFile != null || c.objectstore.s3.enable;
+    objectstoreConfig = let s3 = c.objectstore.s3; in optionalString s3.enable ''
+      'objectstore' => [
+        'class' => '\\OC\\Files\\ObjectStore\\S3',
+        'arguments' => [
+          'bucket' => '${s3.bucket}',
+          'autocreate' => ${boolToString s3.autocreate},
+          'key' => '${s3.key}',
+          'secret' => nix_read_secret('${s3.secretFile}'),
+          ${optionalString (s3.hostname != null) "'hostname' => '${s3.hostname}',"}
+          ${optionalString (s3.port != null) "'port' => ${toString s3.port},"}
+          'use_ssl' => ${boolToString s3.useSsl},
+          ${optionalString (s3.region != null) "'region' => '${s3.region}',"}
+          'use_path_style' => ${boolToString s3.usePathStyle},
+          ${optionalString (s3.sseCKeyFile != null) "'sse_c_key' => nix_read_secret('${s3.sseCKeyFile}'),"}
+        ],
+      ]
+    '';
+    showAppStoreSetting = cfg.appstoreEnable != null || cfg.extraApps != {};
+    renderedAppStoreSetting =
+      let
+        x = cfg.appstoreEnable;
+      in
+        if x == null then "false"
+        else boolToString x;
+    mkAppStoreConfig = name: { enabled, writable, ... }: optionalString enabled ''
+      [ 'path' => '${webroot}/${name}', 'url' => '/${name}', 'writable' => ${boolToString writable} ],
+    '';
+  in pkgs.writeText "nextcloud-config.php" ''
+    <?php
+    ${optionalString requiresReadSecretFunction ''
+      function nix_read_secret($file) {
+        if (!file_exists($file)) {
+          throw new \RuntimeException(sprintf(
+            "Cannot start Nextcloud, secret file %s set by NixOS doesn't seem to "
+            . "exist! Please make sure that the file exists and has appropriate "
+            . "permissions for user & group 'nextcloud'!",
+            $file
+          ));
+        }
+        return trim(file_get_contents($file));
+      }''}
+    function nix_decode_json_file($file, $error) {
+      if (!file_exists($file)) {
+        throw new \RuntimeException(sprintf($error, $file));
+      }
+      $decoded = json_decode(file_get_contents($file), true);
+
+      if (json_last_error() !== JSON_ERROR_NONE) {
+        throw new \RuntimeException(sprintf("Cannot decode %s, because: %s", $file, json_last_error_msg()));
+      }
 
+      return $decoded;
+    }
+    $CONFIG = [
+      'apps_paths' => [
+        ${concatStrings (mapAttrsToList mkAppStoreConfig appStores)}
+      ],
+      ${optionalString (showAppStoreSetting) "'appstoreenabled' => ${renderedAppStoreSetting},"}
+      ${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"}
+      ${optionalString (c.dbname != null) "'dbname' => '${c.dbname}',"}
+      ${optionalString (c.dbhost != null) "'dbhost' => '${c.dbhost}',"}
+      ${optionalString (c.dbuser != null) "'dbuser' => '${c.dbuser}',"}
+      ${optionalString (c.dbtableprefix != null) "'dbtableprefix' => '${toString c.dbtableprefix}',"}
+      ${optionalString (c.dbpassFile != null) ''
+          'dbpassword' => nix_read_secret(
+            "${c.dbpassFile}"
+          ),
+        ''
+      }
+      'dbtype' => '${c.dbtype}',
+      ${objectstoreConfig}
+    ];
+
+    $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file(
+      "${jsonFormat.generate "nextcloud-extraOptions.json" cfg.extraOptions}",
+      "impossible: this should never happen (decoding generated extraOptions file %s failed)"
+    ));
+
+    ${optionalString (cfg.secretFile != null) ''
+      $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file(
+        "${cfg.secretFile}",
+        "Cannot start Nextcloud, secrets file %s set by NixOS doesn't exist!"
+      ));
+    ''}
+  '';
 in {
 
   imports = [
@@ -787,107 +877,23 @@ in {
         timerConfig.Unit = "nextcloud-cron.service";
       };
 
-      systemd.tmpfiles.rules = ["d ${cfg.home} 0750 nextcloud nextcloud"];
+      systemd.tmpfiles.rules = map (dir: "d ${dir} 0750 nextcloud nextcloud - -") [
+        "${cfg.home}"
+        "${datadir}/config"
+        "${datadir}/data"
+        "${cfg.home}/store-apps"
+      ] ++ [
+        "L+ ${datadir}/config/override.config.php - - - - ${overrideConfig}"
+      ];
 
       systemd.services = {
         # When upgrading the Nextcloud package, Nextcloud can report errors such as
         # "The files of the app [all apps in /var/lib/nextcloud/apps] were not replaced correctly"
         # Restarting phpfpm on Nextcloud package update fixes these issues (but this is a workaround).
-        phpfpm-nextcloud.restartTriggers = [ webroot ];
+        phpfpm-nextcloud.restartTriggers = [ webroot overrideConfig ];
 
         nextcloud-setup = let
           c = cfg.config;
-          requiresReadSecretFunction = c.dbpassFile != null || c.objectstore.s3.enable;
-          objectstoreConfig = let s3 = c.objectstore.s3; in optionalString s3.enable ''
-            'objectstore' => [
-              'class' => '\\OC\\Files\\ObjectStore\\S3',
-              'arguments' => [
-                'bucket' => '${s3.bucket}',
-                'autocreate' => ${boolToString s3.autocreate},
-                'key' => '${s3.key}',
-                'secret' => nix_read_secret('${s3.secretFile}'),
-                ${optionalString (s3.hostname != null) "'hostname' => '${s3.hostname}',"}
-                ${optionalString (s3.port != null) "'port' => ${toString s3.port},"}
-                'use_ssl' => ${boolToString s3.useSsl},
-                ${optionalString (s3.region != null) "'region' => '${s3.region}',"}
-                'use_path_style' => ${boolToString s3.usePathStyle},
-                ${optionalString (s3.sseCKeyFile != null) "'sse_c_key' => nix_read_secret('${s3.sseCKeyFile}'),"}
-              ],
-            ]
-          '';
-
-          showAppStoreSetting = cfg.appstoreEnable != null || cfg.extraApps != {};
-          renderedAppStoreSetting =
-            let
-              x = cfg.appstoreEnable;
-            in
-              if x == null then "false"
-              else boolToString x;
-
-          nextcloudGreaterOrEqualThan = req: versionAtLeast cfg.package.version req;
-
-          mkAppStoreConfig = name: { enabled, writable, ... }: optionalString enabled ''
-            [ 'path' => '${webroot}/${name}', 'url' => '/${name}', 'writable' => ${boolToString writable} ],
-          '';
-
-          overrideConfig = pkgs.writeText "nextcloud-config.php" ''
-            <?php
-            ${optionalString requiresReadSecretFunction ''
-              function nix_read_secret($file) {
-                if (!file_exists($file)) {
-                  throw new \RuntimeException(sprintf(
-                    "Cannot start Nextcloud, secret file %s set by NixOS doesn't seem to "
-                    . "exist! Please make sure that the file exists and has appropriate "
-                    . "permissions for user & group 'nextcloud'!",
-                    $file
-                  ));
-                }
-                return trim(file_get_contents($file));
-              }''}
-            function nix_decode_json_file($file, $error) {
-              if (!file_exists($file)) {
-                throw new \RuntimeException(sprintf($error, $file));
-              }
-              $decoded = json_decode(file_get_contents($file), true);
-
-              if (json_last_error() !== JSON_ERROR_NONE) {
-                throw new \RuntimeException(sprintf("Cannot decode %s, because: %s", $file, json_last_error_msg()));
-              }
-
-              return $decoded;
-            }
-            $CONFIG = [
-              'apps_paths' => [
-                ${concatStrings (mapAttrsToList mkAppStoreConfig appStores)}
-              ],
-              ${optionalString (showAppStoreSetting) "'appstoreenabled' => ${renderedAppStoreSetting},"}
-              ${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"}
-              ${optionalString (c.dbname != null) "'dbname' => '${c.dbname}',"}
-              ${optionalString (c.dbhost != null) "'dbhost' => '${c.dbhost}',"}
-              ${optionalString (c.dbuser != null) "'dbuser' => '${c.dbuser}',"}
-              ${optionalString (c.dbtableprefix != null) "'dbtableprefix' => '${toString c.dbtableprefix}',"}
-              ${optionalString (c.dbpassFile != null) ''
-                  'dbpassword' => nix_read_secret(
-                    "${c.dbpassFile}"
-                  ),
-                ''
-              }
-              'dbtype' => '${c.dbtype}',
-              ${objectstoreConfig}
-            ];
-
-            $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file(
-              "${jsonFormat.generate "nextcloud-extraOptions.json" cfg.extraOptions}",
-              "impossible: this should never happen (decoding generated extraOptions file %s failed)"
-            ));
-
-            ${optionalString (cfg.secretFile != null) ''
-              $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file(
-                "${cfg.secretFile}",
-                "Cannot start Nextcloud, secrets file %s set by NixOS doesn't exist!"
-              ));
-            ''}
-          '';
           occInstallCmd = let
             mkExport = { arg, value }: "export ${arg}=${value}";
             dbpass = {
@@ -932,6 +938,7 @@ in {
           after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
           requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
           path = [ occ ];
+          restartTriggers = [ overrideConfig ];
           script = ''
             ${optionalString (c.dbpassFile != null) ''
               if [ ! -r "${c.dbpassFile}" ]; then
@@ -959,18 +966,6 @@ in {
               fi
             '') [ "nix-apps" "apps" ]}
 
-            # create nextcloud directories.
-            # if the directories exist already with wrong permissions, we fix that
-            for dir in ${datadir}/config ${datadir}/data ${cfg.home}/store-apps; do
-              if [ ! -e $dir ]; then
-                install -o nextcloud -g nextcloud -d $dir
-              elif [ $(stat -c "%G" $dir) != "nextcloud" ]; then
-                chgrp -R nextcloud $dir
-              fi
-            done
-
-            ln -sf ${overrideConfig} ${datadir}/config/override.config.php
-
             # Do not install if already installed
             if [[ ! -e ${datadir}/config/config.php ]]; then
               ${occInstallCmd}
diff --git a/nixos/modules/services/web-servers/ttyd.nix b/nixos/modules/services/web-servers/ttyd.nix
index 3b1d87ccb483e..e545869ca4320 100644
--- a/nixos/modules/services/web-servers/ttyd.nix
+++ b/nixos/modules/services/web-servers/ttyd.nix
@@ -180,10 +180,11 @@ in
         # Runs login which needs to be run as root
         # login: Cannot possibly work without effective root
         User = "root";
+        LoadCredential = lib.optionalString (cfg.passwordFile != null) "TTYD_PASSWORD_FILE:${cfg.passwordFile}";
       };
 
       script = if cfg.passwordFile != null then ''
-        PASSWORD=$(cat ${escapeShellArg cfg.passwordFile})
+        PASSWORD=$(cat "$CREDENTIALS_DIRECTORY/TTYD_PASSWORD_FILE")
         ${pkgs.ttyd}/bin/ttyd ${lib.escapeShellArgs args} \
           --credential ${escapeShellArg cfg.username}:"$PASSWORD" \
           ${pkgs.shadow}/bin/login