about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/services/web-apps
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/services/web-apps')
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/akkoma.nix143
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/alps.nix16
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/anuko-time-tracker.nix56
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/atlassian/confluence.nix34
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/atlassian/crowd.nix28
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/atlassian/jira.nix34
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/bookstack.nix52
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/calibre-web.nix26
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/castopod.md25
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/castopod.nix318
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/changedetection-io.nix24
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/chatgpt-retrieval-plugin.nix14
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/cloudlog.nix56
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/code-server.nix42
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/coder.nix51
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/convos.nix8
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/crabfit.nix171
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/davis.md32
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/davis.nix554
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/dex.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/discourse.nix88
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/documize.nix22
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix34
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/dolibarr.nix28
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/engelsystem.nix8
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/ethercalc.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/firefly-iii.nix367
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/fluidd.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/freshrss.nix40
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/galene.nix26
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/gerrit.nix18
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/gotify-server.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/gotosocial.nix12
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/grocy.nix18
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/guacamole-client.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/guacamole-server.nix12
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/healthchecks.nix31
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix30
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/hledger-web.nix61
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/honk.nix16
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix32
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/icingaweb2/module-monitoring.nix38
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/invidious.nix32
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix34
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/isso.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/jirafeau.nix20
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix39
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/kasmweb/default.nix30
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/kavita.nix18
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/keycloak.nix46
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/lanraragi.nix10
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/lemmy.nix30
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/limesurvey.nix46
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mainsail.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mastodon.nix115
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/matomo.nix10
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mattermost.nix36
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mealie.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mediawiki.nix62
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/meme-bingo-web.nix10
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/microbin.nix8
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/miniflux.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mobilizon.nix29
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/monica.nix52
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/moodle.nix26
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/movim.nix709
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/netbox.nix26
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud-notify_push.nix10
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.md28
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.nix127
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nexus.nix14
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nifi.nix26
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/node-red.nix20
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/ocis.md113
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/ocis.nix201
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/onlyoffice.nix22
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/openvscode-server.nix34
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/openwebrx.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/outline.nix140
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/peering-manager.nix22
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/peertube.nix52
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/pgpkeyserver-lite.nix8
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/photoprism.nix16
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/phylactery.nix8
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/pict-rs.nix12
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/pixelfed.nix37
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/plantuml-server.nix19
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/plausible.nix44
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/powerdns-admin.nix10
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/pretalx.nix48
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/prosody-filer.nix4
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/rss-bridge.nix75
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/selfoss.nix20
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/sftpgo.nix56
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/shiori.nix8
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/silverbullet.nix123
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/snipe-it.nix58
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/sogo.nix14
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/suwayomi-server.nix34
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/trilium.nix20
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/tt-rss.nix84
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/vikunja.nix22
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/whitebophir.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/wiki-js.nix24
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/windmill.nix18
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/wordpress.nix40
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/writefreely.nix50
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/youtrack.nix24
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/zabbix.nix30
109 files changed, 4230 insertions, 1496 deletions
diff --git a/nixpkgs/nixos/modules/services/web-apps/akkoma.nix b/nixpkgs/nixos/modules/services/web-apps/akkoma.nix
index 4cd9e2664378..7c9bf6c46516 100644
--- a/nixpkgs/nixos/modules/services/web-apps/akkoma.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/akkoma.nix
@@ -50,19 +50,19 @@ let
     options = {
       package = mkOption {
         type = types.package;
-        description = mdDoc "Akkoma frontend package.";
+        description = "Akkoma frontend package.";
         example = literalExpression "pkgs.akkoma-frontends.akkoma-fe";
       };
 
       name = mkOption {
         type = types.nonEmptyStr;
-        description = mdDoc "Akkoma frontend name.";
+        description = "Akkoma frontend name.";
         example = "akkoma-fe";
       };
 
       ref = mkOption {
         type = types.nonEmptyStr;
-        description = mdDoc "Akkoma frontend reference.";
+        description = "Akkoma frontend reference.";
         example = "stable";
       };
     };
@@ -350,27 +350,27 @@ let
 in {
   options = {
     services.akkoma = {
-      enable = mkEnableOption (mdDoc "Akkoma");
+      enable = mkEnableOption "Akkoma";
 
       package = mkPackageOption pkgs "akkoma" { };
 
       user = mkOption {
         type = types.nonEmptyStr;
         default = "akkoma";
-        description = mdDoc "User account under which Akkoma runs.";
+        description = "User account under which Akkoma runs.";
       };
 
       group = mkOption {
         type = types.nonEmptyStr;
         default = "akkoma";
-        description = mdDoc "Group account under which Akkoma runs.";
+        description = "Group account under which Akkoma runs.";
       };
 
       initDb = {
         enable = mkOption {
           type = types.bool;
           default = true;
-          description = mdDoc ''
+          description = ''
             Whether to automatically initialise the database on startup. This will create a
             database role and database if they do not already exist, and (re)set the role password
             and the ownership of the database.
@@ -403,7 +403,7 @@ in {
           type = types.nonEmptyStr;
           default = config.services.postgresql.superUser;
           defaultText = literalExpression "config.services.postgresql.superUser";
-          description = mdDoc ''
+          description = ''
             Name of the database user to initialise the database with.
 
             This user is required to have the `CREATEROLE` and `CREATEDB` capabilities.
@@ -413,7 +413,7 @@ in {
         password = mkOption {
           type = types.nullOr secret;
           default = null;
-          description = mdDoc ''
+          description = ''
             Password of the database user to initialise the database with.
 
             If set to `null`, no password will be used.
@@ -426,7 +426,7 @@ in {
       initSecrets = mkOption {
         type = types.bool;
         default = true;
-        description = mdDoc ''
+        description = ''
           Whether to initialise non‐existent secrets with random values.
 
           If enabled, appropriate secrets for the following options will be created automatically
@@ -444,7 +444,7 @@ in {
       installWrapper = mkOption {
         type = types.bool;
         default = true;
-        description = mdDoc ''
+        description = ''
           Whether to install a wrapper around `pleroma_ctl` to simplify administration of the
           Akkoma instance.
         '';
@@ -455,7 +455,7 @@ in {
         default = with pkgs; [ exiftool ffmpeg_5-headless graphicsmagick-imagemagick-compat ];
         defaultText = literalExpression "with pkgs; [ exiftool graphicsmagick-imagemagick-compat ffmpeg_5-headless ]";
         example = literalExpression "with pkgs; [ exiftool imagemagick ffmpeg_5-full ]";
-        description = mdDoc ''
+        description = ''
           List of extra packages to include in the executable search path of the service unit.
           These are needed by various configurable components such as:
 
@@ -467,7 +467,7 @@ in {
       };
 
       frontends = mkOption {
-        description = mdDoc "Akkoma frontends.";
+        description = "Akkoma frontends.";
         type = with types; attrsOf (submodule frontend);
         default = {
           primary = {
@@ -499,7 +499,7 @@ in {
 
       extraStatic = mkOption {
         type = with types; nullOr (attrsOf package);
-        description = mdDoc ''
+        description = ''
           Attribute set of extra packages to add to the static files directory.
 
           Do not add frontends here. These should be configured through
@@ -537,7 +537,7 @@ in {
         address = mkOption {
           type = ipAddress;
           default = "127.0.0.1";
-          description = mdDoc ''
+          description = ''
             Listen address for Erlang distribution protocol and Port Mapper Daemon (epmd).
           '';
         };
@@ -545,33 +545,33 @@ in {
         epmdPort = mkOption {
           type = types.port;
           default = 4369;
-          description = mdDoc "TCP port to bind Erlang Port Mapper Daemon to.";
+          description = "TCP port to bind Erlang Port Mapper Daemon to.";
         };
 
         extraFlags = mkOption {
           type = with types; listOf str;
           default = [ ];
-          description = mdDoc "Extra flags to pass to Erlang";
+          description = "Extra flags to pass to Erlang";
           example = [ "+sbwt" "none" "+sbwtdcpu" "none" "+sbwtdio" "none" ];
         };
 
         portMin = mkOption {
           type = types.port;
           default = 49152;
-          description = mdDoc "Lower bound for Erlang distribution protocol TCP port.";
+          description = "Lower bound for Erlang distribution protocol TCP port.";
         };
 
         portMax = mkOption {
           type = types.port;
           default = 65535;
-          description = mdDoc "Upper bound for Erlang distribution protocol TCP port.";
+          description = "Upper bound for Erlang distribution protocol TCP port.";
         };
 
         cookie = mkOption {
           type = types.nullOr secret;
           default = null;
           example = { _secret = "/var/lib/secrets/akkoma/releaseCookie"; };
-          description = mdDoc ''
+          description = ''
             Erlang release cookie.
 
             If set to `null`, a temporary random cookie will be generated.
@@ -580,7 +580,7 @@ in {
       };
 
       config = mkOption {
-        description = mdDoc ''
+        description = ''
           Configuration for Akkoma. The attributes are serialised to Elixir DSL.
 
           Refer to <https://docs.akkoma.dev/stable/configuration/cheatsheet/> for
@@ -597,17 +597,17 @@ in {
               ":instance" = {
                 name = mkOption {
                   type = types.nonEmptyStr;
-                  description = mdDoc "Instance name.";
+                  description = "Instance name.";
                 };
 
                 email = mkOption {
                   type = types.nonEmptyStr;
-                  description = mdDoc "Instance administrator email.";
+                  description = "Instance administrator email.";
                 };
 
                 description = mkOption {
                   type = types.nonEmptyStr;
-                  description = mdDoc "Instance description.";
+                  description = "Instance description.";
                 };
 
                 static_dir = mkOption {
@@ -619,7 +619,7 @@ in {
                     - [{option}`services.akkoma.frontends`](#opt-services.akkoma.frontends)
                     - [{option}`services.akkoma.extraStatic`](#opt-services.akkoma.extraStatic)
                   '';
-                  description = mdDoc ''
+                  description = ''
                     Directory of static files.
 
                     This directory can be built using a derivation, or it can be managed as mutable
@@ -630,7 +630,7 @@ in {
                 upload_dir = mkOption {
                   type = absolutePath;
                   default = "/var/lib/akkoma/uploads";
-                  description = mdDoc ''
+                  description = ''
                     Directory where Akkoma will put uploaded files.
                   '';
                 };
@@ -652,7 +652,7 @@ in {
                     database = "akkoma";
                   }
                 '';
-                description = mdDoc ''
+                description = ''
                   Database configuration.
 
                   Refer to
@@ -667,19 +667,19 @@ in {
                     type = types.nonEmptyStr;
                     default = config.networking.fqdn;
                     defaultText = literalExpression "config.networking.fqdn";
-                    description = mdDoc "Domain name of the instance.";
+                    description = "Domain name of the instance.";
                   };
 
                   scheme = mkOption {
                     type = types.nonEmptyStr;
                     default = "https";
-                    description = mdDoc "URL scheme.";
+                    description = "URL scheme.";
                   };
 
                   port = mkOption {
                     type = types.port;
                     default = 443;
-                    description = mdDoc "External port number.";
+                    description = "External port number.";
                   };
                 };
 
@@ -688,7 +688,7 @@ in {
                     type = types.either absolutePath ipAddress;
                     default = "/run/akkoma/socket";
                     example = "::1";
-                    description = mdDoc ''
+                    description = ''
                       Listener IP address or Unix socket path.
 
                       The value is automatically converted to Elixir’s internal address
@@ -704,7 +704,7 @@ in {
                         then 0
                         else 4000;
                     '';
-                    description = mdDoc ''
+                    description = ''
                       Listener port number.
 
                       Must be 0 if using a Unix socket.
@@ -715,7 +715,7 @@ in {
                 secret_key_base = mkOption {
                   type = secret;
                   default = { _secret = "/var/lib/secrets/akkoma/key-base"; };
-                  description = mdDoc ''
+                  description = ''
                     Secret key used as a base to generate further secrets for encrypting and
                     signing data.
 
@@ -733,7 +733,7 @@ in {
                   signing_salt = mkOption {
                     type = secret;
                     default = { _secret = "/var/lib/secrets/akkoma/liveview-salt"; };
-                    description = mdDoc ''
+                    description = ''
                       LiveView signing salt.
 
                       The attribute `_secret` should point to a file containing the secret.
@@ -750,7 +750,7 @@ in {
                 signing_salt = mkOption {
                   type = secret;
                   default = { _secret = "/var/lib/secrets/akkoma/signing-salt"; };
-                  description = mdDoc ''
+                  description = ''
                     Signing salt.
 
                     The attribute `_secret` should point to a file containing the secret.
@@ -764,6 +764,26 @@ in {
                 };
               };
 
+              "Pleroma.Upload" = let
+                httpConf = cfg.config.":pleroma"."Pleroma.Web.Endpoint".url;
+              in {
+                base_url = mkOption {
+                    type = types.nonEmptyStr;
+                    default = if lib.versionOlder config.system.stateVersion "24.05"
+                              then "${httpConf.scheme}://${httpConf.host}:${builtins.toString httpConf.port}/media/"
+                              else null;
+                    defaultText = literalExpression ''
+                      if lib.versionOlder config.system.stateVersion "24.05"
+                      then "$\{httpConf.scheme}://$\{httpConf.host}:$\{builtins.toString httpConf.port}/media/"
+                      else null;
+                    '';
+                    description = ''
+                      Base path which uploads will be stored at.
+                      Whilst this can just be set to a subdirectory of the main domain, it is now recommended to use a different subdomain.
+                    '';
+                };
+              };
+
               ":frontends" = mkOption {
                 type = elixirValue;
                 default = mapAttrs
@@ -774,18 +794,48 @@ in {
                     (pkgs.formats.elixirConf { }).lib.mkMap { name = val.name; ref = val.ref; })
                     config.services.akkoma.frontends;
                 '';
-                description = mdDoc ''
+                description = ''
                   Frontend configuration.
 
                   Users should rely on the default value and prefer to configure frontends through
                   [{option}`config.services.akkoma.frontends`](#opt-services.akkoma.frontends).
                 '';
               };
+
+
+              ":media_proxy" = let
+                httpConf = cfg.config.":pleroma"."Pleroma.Web.Endpoint".url;
+              in {
+                enabled = mkOption {
+                    type = types.bool;
+                    default = false;
+                    defaultText = literalExpression "false";
+                    description = ''
+                      Whether to enable proxying of remote media through the instance's proxy.
+                    '';
+                };
+                base_url = mkOption {
+                    type = types.nullOr types.nonEmptyStr;
+                    default = if lib.versionOlder config.system.stateVersion "24.05"
+                              then "${httpConf.scheme}://${httpConf.host}:${builtins.toString httpConf.port}"
+                              else null;
+                    defaultText = literalExpression ''
+                      if lib.versionOlder config.system.stateVersion "24.05"
+                      then "$\{httpConf.scheme}://$\{httpConf.host}:$\{builtins.toString httpConf.port}"
+                      else null;
+                    '';
+                    description = ''
+                      Base path for the media proxy.
+                      Whilst this can just be set to a subdirectory of the main domain, it is now recommended to use a different subdomain.
+                    '';
+                };
+              };
+
             };
 
             ":web_push_encryption" = mkOption {
               default = { };
-              description = mdDoc ''
+              description = ''
                 Web Push Notifications configuration.
 
                 The necessary key pair can be generated as follows:
@@ -804,19 +854,19 @@ in {
                       defaultText = literalExpression ''
                         "mailto:''${config.services.akkoma.config.":pleroma".":instance".email}"
                       '';
-                      description = mdDoc "mailto URI for administrative contact.";
+                      description = "mailto URI for administrative contact.";
                     };
 
                     public_key = mkOption {
                       type = with types; either nonEmptyStr secret;
                       default = { _secret = "/var/lib/secrets/akkoma/vapid-public"; };
-                      description = mdDoc "base64-encoded public ECDH key.";
+                      description = "base64-encoded public ECDH key.";
                     };
 
                     private_key = mkOption {
                       type = secret;
                       default = { _secret = "/var/lib/secrets/akkoma/vapid-private"; };
-                      description = mdDoc ''
+                      description = ''
                         base64-encoded private ECDH key.
 
                         The attribute `_secret` should point to a file containing the secret.
@@ -831,7 +881,7 @@ in {
               ":default_signer" = mkOption {
                 type = secret;
                 default = { _secret = "/var/lib/secrets/akkoma/jwt-signer"; };
-                description = mdDoc ''
+                description = ''
                   JWT signing secret.
 
                   The attribute `_secret` should point to a file containing the secret.
@@ -866,7 +916,7 @@ in {
                   apply = format.lib.mkAtom;
                   default = ":info";
                   example = ":warning";
-                  description = mdDoc ''
+                  description = ''
                     Log level.
 
                     Refer to
@@ -894,7 +944,7 @@ in {
         type = with types; nullOr (submodule
           (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }));
         default = null;
-        description = mdDoc ''
+        description = ''
           Extra configuration for the nginx virtual host of Akkoma.
 
           If set to `null`, no virtual host will be added to the nginx configuration.
@@ -904,7 +954,10 @@ in {
   };
 
   config = mkIf cfg.enable {
-    warnings = optionals (with config.security; (!sudo.enable) && (!sudo-rs.enable)) [''
+    assertions = optionals (cfg.config.":pleroma".":media_proxy".enabled && cfg.config.":pleroma".":media_proxy".base_url == null) [''
+      `services.akkoma.config.":pleroma".":media_proxy".base_url` must be set when the media proxy is enabled.
+    ''];
+    warnings = optionals (with config.security; cfg.installWrapper && (!sudo.enable) && (!sudo-rs.enable)) [''
       The pleroma_ctl wrapper enabled by the installWrapper option relies on
       sudo, which appears to have been disabled through security.sudo.enable.
     ''];
@@ -1083,6 +1136,6 @@ in {
     };
   };
 
-  meta.maintainers = with maintainers; [ mvs ];
+  meta.maintainers = with maintainers; [ mvs tcmal ];
   meta.doc = ./akkoma.md;
 }
diff --git a/nixpkgs/nixos/modules/services/web-apps/alps.nix b/nixpkgs/nixos/modules/services/web-apps/alps.nix
index 81c6b8ad30b5..e72b85eb3569 100644
--- a/nixpkgs/nixos/modules/services/web-apps/alps.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/alps.nix
@@ -6,12 +6,12 @@ let
   cfg = config.services.alps;
 in {
   options.services.alps = {
-    enable = mkEnableOption (lib.mdDoc "alps");
+    enable = mkEnableOption "alps";
 
     port = mkOption {
       type = types.port;
       default = 1323;
-      description = lib.mdDoc ''
+      description = ''
         TCP port the service should listen on.
       '';
     };
@@ -19,7 +19,7 @@ in {
     bindIP = mkOption {
       default = "[::]";
       type = types.str;
-      description = lib.mdDoc ''
+      description = ''
         The IP the service should listen on.
       '';
     };
@@ -27,7 +27,7 @@ in {
     theme = mkOption {
       type = types.enum [ "alps" "sourcehut" ];
       default = "sourcehut";
-      description = lib.mdDoc ''
+      description = ''
         The frontend's theme to use.
       '';
     };
@@ -36,7 +36,7 @@ in {
       port = mkOption {
         type = types.port;
         default = 993;
-        description = lib.mdDoc ''
+        description = ''
           The IMAPS server port.
         '';
       };
@@ -45,7 +45,7 @@ in {
         type = types.str;
         default = "[::1]";
         example = "mail.example.org";
-        description = lib.mdDoc ''
+        description = ''
           The IMAPS server address.
         '';
       };
@@ -55,7 +55,7 @@ in {
       port = mkOption {
         type = types.port;
         default = 465;
-        description = lib.mdDoc ''
+        description = ''
           The SMTPS server port.
         '';
       };
@@ -65,7 +65,7 @@ in {
         default = cfg.imaps.host;
         defaultText = "services.alps.imaps.host";
         example = "mail.example.org";
-        description = lib.mdDoc ''
+        description = ''
           The SMTPS server address.
         '';
       };
diff --git a/nixpkgs/nixos/modules/services/web-apps/anuko-time-tracker.nix b/nixpkgs/nixos/modules/services/web-apps/anuko-time-tracker.nix
index 3b326390fa43..75f3d66b2f99 100644
--- a/nixpkgs/nixos/modules/services/web-apps/anuko-time-tracker.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/anuko-time-tracker.nix
@@ -56,7 +56,7 @@ let
 in
 {
   options.services.anuko-time-tracker = {
-    enable = lib.mkEnableOption (lib.mdDoc "Anuko Time Tracker");
+    enable = lib.mkEnableOption "Anuko Time Tracker";
 
     package = lib.mkPackageOption pkgs "anuko-time-tracker" {};
 
@@ -64,30 +64,30 @@ in
       createLocally = lib.mkOption {
         type = lib.types.bool;
         default = true;
-        description = lib.mdDoc "Create the database and database user locally.";
+        description = "Create the database and database user locally.";
       };
 
       host = lib.mkOption {
         type = lib.types.str;
-        description = lib.mdDoc "Database host.";
+        description = "Database host.";
         default = "localhost";
       };
 
       name = lib.mkOption {
         type = lib.types.str;
-        description = lib.mdDoc "Database name.";
+        description = "Database name.";
         default = "anuko_time_tracker";
       };
 
       user = lib.mkOption {
         type = lib.types.str;
-        description = lib.mdDoc "Database username.";
+        description = "Database username.";
         default = "anuko_time_tracker";
       };
 
       passwordFile = lib.mkOption {
         type = lib.types.nullOr lib.types.str;
-        description = lib.mdDoc "Database user password file.";
+        description = "Database user password file.";
         default = null;
       };
     };
@@ -102,7 +102,7 @@ in
         "pm.max_spare_servers" = 4;
         "pm.max_requests" = 500;
       };
-      description = lib.mdDoc ''
+      description = ''
         Options for Anuko Time Tracker's PHP-FPM pool.
       '';
     };
@@ -115,7 +115,7 @@ in
         else config.networking.hostName;
       defaultText = lib.literalExpression "config.networking.fqdn";
       example = "anuko.example.com";
-      description = lib.mdDoc ''
+      description = ''
         The hostname to serve Anuko Time Tracker on.
       '';
     };
@@ -137,7 +137,7 @@ in
           enableACME = true;
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         With this option, you can customize the Nginx virtualHost settings.
       '';
     };
@@ -145,21 +145,21 @@ in
     dataDir = lib.mkOption {
       type = lib.types.str;
       default = "/var/lib/anuko-time-tracker";
-      description = lib.mdDoc "Default data folder for Anuko Time Tracker.";
+      description = "Default data folder for Anuko Time Tracker.";
       example = "/mnt/anuko-time-tracker";
     };
 
     user = lib.mkOption {
       type = lib.types.str;
       default = "anuko_time_tracker";
-      description = lib.mdDoc "User under which Anuko Time Tracker runs.";
+      description = "User under which Anuko Time Tracker runs.";
     };
 
     settings = {
       multiorgMode = lib.mkOption {
         type = lib.types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Defines whether users see the Register option in the menu of Time Tracker that allows them
           to self-register and create new organizations (top groups).
         '';
@@ -168,13 +168,13 @@ in
       emailRequired = lib.mkOption {
         type = lib.types.bool;
         default = false;
-        description = lib.mdDoc "Defines whether an email is required for new registrations.";
+        description = "Defines whether an email is required for new registrations.";
       };
 
       weekendStartDay = lib.mkOption {
         type = lib.types.int;
         default = 6;
-        description = lib.mdDoc ''
+        description = ''
           This option defines which days are highlighted with weekend color.
           6 means Saturday. For Saudi Arabia, etc. set it to 4 for Thursday and Friday to be
           weekend days.
@@ -183,58 +183,58 @@ in
 
       forumLink = lib.mkOption {
         type = lib.types.str;
-        description = lib.mdDoc "Forum link from the main menu.";
+        description = "Forum link from the main menu.";
         default = "https://www.anuko.com/forum/viewforum.php?f=4";
       };
 
       helpLink = lib.mkOption {
         type = lib.types.str;
-        description = lib.mdDoc "Help link from the main menu.";
+        description = "Help link from the main menu.";
         default = "https://www.anuko.com/time-tracker/user-guide/index.htm";
       };
 
       email = {
         sender = lib.mkOption {
           type = lib.types.str;
-          description = lib.mdDoc "Default sender for mail.";
+          description = "Default sender for mail.";
           default = "Anuko Time Tracker <bounces@example.com>";
         };
 
         mode = lib.mkOption {
           type = lib.types.str;
-          description = lib.mdDoc "Mail sending mode. Can be 'mail' or 'smtp'.";
+          description = "Mail sending mode. Can be 'mail' or 'smtp'.";
           default = "smtp";
         };
 
         smtpHost = lib.mkOption {
           type = lib.types.str;
-          description = lib.mdDoc "MTA hostname.";
+          description = "MTA hostname.";
           default = "localhost";
         };
 
         smtpPort = lib.mkOption {
           type = lib.types.int;
-          description = lib.mdDoc "MTA port.";
+          description = "MTA port.";
           default = 25;
         };
 
         smtpUser = lib.mkOption {
           type = lib.types.str;
-          description = lib.mdDoc "MTA authentication username.";
+          description = "MTA authentication username.";
           default = "";
         };
 
         smtpAuth = lib.mkOption {
           type = lib.types.bool;
           default = false;
-          description = lib.mdDoc "MTA requires authentication.";
+          description = "MTA requires authentication.";
         };
 
         smtpPasswordFile = lib.mkOption {
           type = lib.types.nullOr lib.types.path;
           default = null;
           example = "/var/lib/anuko-time-tracker/secrets/smtp-password";
-          description = lib.mdDoc ''
+          description = ''
             Path to file containing the MTA authentication password.
           '';
         };
@@ -242,13 +242,13 @@ in
         smtpDebug = lib.mkOption {
           type = lib.types.bool;
           default = false;
-          description = lib.mdDoc "Debug mail sending.";
+          description = "Debug mail sending.";
         };
       };
 
       defaultLanguage = lib.mkOption {
         type = lib.types.str;
-        description = lib.mdDoc ''
+        description = ''
           Defines Anuko Time Tracker default language. It is used on Time Tracker login page.
           After login, a language set for user group is used.
           Empty string means the language is defined by user browser.
@@ -259,7 +259,7 @@ in
 
       defaultCurrency = lib.mkOption {
         type = lib.types.str;
-        description = lib.mdDoc ''
+        description = ''
           Defines a default currency symbol for new groups.
           Use €, £, a more specific dollar like US$, CAD, etc.
         '';
@@ -270,7 +270,7 @@ in
       exportDecimalDuration = lib.mkOption {
         type = lib.types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Defines whether time duration values are decimal in CSV and XML data
           exports (1.25 vs 1:15).
         '';
@@ -279,7 +279,7 @@ in
       reportFooter = lib.mkOption {
         type = lib.types.bool;
         default = true;
-        description = lib.mdDoc "Defines whether to use a footer on reports.";
+        description = "Defines whether to use a footer on reports.";
       };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/atlassian/confluence.nix b/nixpkgs/nixos/modules/services/web-apps/atlassian/confluence.nix
index aa13659fcc30..683a1c7603ef 100644
--- a/nixpkgs/nixos/modules/services/web-apps/atlassian/confluence.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/atlassian/confluence.nix
@@ -29,101 +29,101 @@ in
 {
   options = {
     services.confluence = {
-      enable = mkEnableOption (lib.mdDoc "Atlassian Confluence service");
+      enable = mkEnableOption "Atlassian Confluence service";
 
       user = mkOption {
         type = types.str;
         default = "confluence";
-        description = lib.mdDoc "User which runs confluence.";
+        description = "User which runs confluence.";
       };
 
       group = mkOption {
         type = types.str;
         default = "confluence";
-        description = lib.mdDoc "Group which runs confluence.";
+        description = "Group which runs confluence.";
       };
 
       home = mkOption {
         type = types.str;
         default = "/var/lib/confluence";
-        description = lib.mdDoc "Home directory of the confluence instance.";
+        description = "Home directory of the confluence instance.";
       };
 
       listenAddress = mkOption {
         type = types.str;
         default = "127.0.0.1";
-        description = lib.mdDoc "Address to listen on.";
+        description = "Address to listen on.";
       };
 
       listenPort = mkOption {
         type = types.port;
         default = 8090;
-        description = lib.mdDoc "Port to listen on.";
+        description = "Port to listen on.";
       };
 
       catalinaOptions = mkOption {
         type = types.listOf types.str;
         default = [];
         example = [ "-Xms1024m" "-Xmx2048m" "-Dconfluence.disable.peopledirectory.all=true" ];
-        description = lib.mdDoc "Java options to pass to catalina/tomcat.";
+        description = "Java options to pass to catalina/tomcat.";
       };
 
       proxy = {
-        enable = mkEnableOption (lib.mdDoc "proxy support");
+        enable = mkEnableOption "proxy support";
 
         name = mkOption {
           type = types.str;
           example = "confluence.example.com";
-          description = lib.mdDoc "Virtual hostname at the proxy";
+          description = "Virtual hostname at the proxy";
         };
 
         port = mkOption {
           type = types.port;
           default = 443;
           example = 80;
-          description = lib.mdDoc "Port used at the proxy";
+          description = "Port used at the proxy";
         };
 
         scheme = mkOption {
           type = types.str;
           default = "https";
           example = "http";
-          description = lib.mdDoc "Protocol used at the proxy.";
+          description = "Protocol used at the proxy.";
         };
       };
 
       sso = {
-        enable = mkEnableOption (lib.mdDoc "SSO with Atlassian Crowd");
+        enable = mkEnableOption "SSO with Atlassian Crowd";
 
         crowd = mkOption {
           type = types.str;
           example = "http://localhost:8095/crowd";
-          description = lib.mdDoc "Crowd Base URL without trailing slash";
+          description = "Crowd Base URL without trailing slash";
         };
 
         applicationName = mkOption {
           type = types.str;
           example = "jira";
-          description = lib.mdDoc "Exact name of this Confluence instance in Crowd";
+          description = "Exact name of this Confluence instance in Crowd";
         };
 
         applicationPassword = mkOption {
           type = types.nullOr types.str;
           default = null;
-          description = lib.mdDoc "Application password of this Confluence instance in Crowd";
+          description = "Application password of this Confluence instance in Crowd";
         };
 
         applicationPasswordFile = mkOption {
           type = types.nullOr types.str;
           default = null;
-          description = lib.mdDoc "Path to the application password for Crowd of Confluence.";
+          description = "Path to the application password for Crowd of Confluence.";
         };
 
         validationInterval = mkOption {
           type = types.int;
           default = 2;
           example = 0;
-          description = lib.mdDoc ''
+          description = ''
             Set to 0, if you want authentication checks to occur on each
             request. Otherwise set to the number of minutes between request
             to validate if the user is logged in or out of the Crowd SSO
diff --git a/nixpkgs/nixos/modules/services/web-apps/atlassian/crowd.nix b/nixpkgs/nixos/modules/services/web-apps/atlassian/crowd.nix
index eed1a127fe4f..527fa1743df2 100644
--- a/nixpkgs/nixos/modules/services/web-apps/atlassian/crowd.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/atlassian/crowd.nix
@@ -34,84 +34,84 @@ in
 {
   options = {
     services.crowd = {
-      enable = mkEnableOption (lib.mdDoc "Atlassian Crowd service");
+      enable = mkEnableOption "Atlassian Crowd service";
 
       user = mkOption {
         type = types.str;
         default = "crowd";
-        description = lib.mdDoc "User which runs Crowd.";
+        description = "User which runs Crowd.";
       };
 
       group = mkOption {
         type = types.str;
         default = "crowd";
-        description = lib.mdDoc "Group which runs Crowd.";
+        description = "Group which runs Crowd.";
       };
 
       home = mkOption {
         type = types.str;
         default = "/var/lib/crowd";
-        description = lib.mdDoc "Home directory of the Crowd instance.";
+        description = "Home directory of the Crowd instance.";
       };
 
       listenAddress = mkOption {
         type = types.str;
         default = "127.0.0.1";
-        description = lib.mdDoc "Address to listen on.";
+        description = "Address to listen on.";
       };
 
       listenPort = mkOption {
         type = types.port;
         default = 8092;
-        description = lib.mdDoc "Port to listen on.";
+        description = "Port to listen on.";
       };
 
       openidPassword = mkOption {
         type = types.str;
         default = "WILL_NEVER_BE_SET";
-        description = lib.mdDoc "Application password for OpenID server.";
+        description = "Application password for OpenID server.";
       };
 
       openidPasswordFile = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = lib.mdDoc "Path to the file containing the application password for OpenID server.";
+        description = "Path to the file containing the application password for OpenID server.";
       };
 
       catalinaOptions = mkOption {
         type = types.listOf types.str;
         default = [];
         example = [ "-Xms1024m" "-Xmx2048m" ];
-        description = lib.mdDoc "Java options to pass to catalina/tomcat.";
+        description = "Java options to pass to catalina/tomcat.";
       };
 
       proxy = {
-        enable = mkEnableOption (lib.mdDoc "reverse proxy support");
+        enable = mkEnableOption "reverse proxy support";
 
         name = mkOption {
           type = types.str;
           example = "crowd.example.com";
-          description = lib.mdDoc "Virtual hostname at the proxy";
+          description = "Virtual hostname at the proxy";
         };
 
         port = mkOption {
           type = types.port;
           default = 443;
           example = 80;
-          description = lib.mdDoc "Port used at the proxy";
+          description = "Port used at the proxy";
         };
 
         scheme = mkOption {
           type = types.str;
           default = "https";
           example = "http";
-          description = lib.mdDoc "Protocol used at the proxy.";
+          description = "Protocol used at the proxy.";
         };
 
         secure = mkOption {
           type = types.bool;
           default = true;
-          description = lib.mdDoc "Whether the connections to the proxy should be considered secure.";
+          description = "Whether the connections to the proxy should be considered secure.";
         };
       };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/atlassian/jira.nix b/nixpkgs/nixos/modules/services/web-apps/atlassian/jira.nix
index a9f337810a0f..40c5d95cae3a 100644
--- a/nixpkgs/nixos/modules/services/web-apps/atlassian/jira.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/atlassian/jira.nix
@@ -29,100 +29,100 @@ in
 {
   options = {
     services.jira = {
-      enable = mkEnableOption (lib.mdDoc "Atlassian JIRA service");
+      enable = mkEnableOption "Atlassian JIRA service";
 
       user = mkOption {
         type = types.str;
         default = "jira";
-        description = lib.mdDoc "User which runs JIRA.";
+        description = "User which runs JIRA.";
       };
 
       group = mkOption {
         type = types.str;
         default = "jira";
-        description = lib.mdDoc "Group which runs JIRA.";
+        description = "Group which runs JIRA.";
       };
 
       home = mkOption {
         type = types.str;
         default = "/var/lib/jira";
-        description = lib.mdDoc "Home directory of the JIRA instance.";
+        description = "Home directory of the JIRA instance.";
       };
 
       listenAddress = mkOption {
         type = types.str;
         default = "127.0.0.1";
-        description = lib.mdDoc "Address to listen on.";
+        description = "Address to listen on.";
       };
 
       listenPort = mkOption {
         type = types.port;
         default = 8091;
-        description = lib.mdDoc "Port to listen on.";
+        description = "Port to listen on.";
       };
 
       catalinaOptions = mkOption {
         type = types.listOf types.str;
         default = [];
         example = [ "-Xms1024m" "-Xmx2048m" ];
-        description = lib.mdDoc "Java options to pass to catalina/tomcat.";
+        description = "Java options to pass to catalina/tomcat.";
       };
 
       proxy = {
-        enable = mkEnableOption (lib.mdDoc "reverse proxy support");
+        enable = mkEnableOption "reverse proxy support";
 
         name = mkOption {
           type = types.str;
           example = "jira.example.com";
-          description = lib.mdDoc "Virtual hostname at the proxy";
+          description = "Virtual hostname at the proxy";
         };
 
         port = mkOption {
           type = types.port;
           default = 443;
           example = 80;
-          description = lib.mdDoc "Port used at the proxy";
+          description = "Port used at the proxy";
         };
 
         scheme = mkOption {
           type = types.str;
           default = "https";
           example = "http";
-          description = lib.mdDoc "Protocol used at the proxy.";
+          description = "Protocol used at the proxy.";
         };
 
         secure = mkOption {
           type = types.bool;
           default = true;
-          description = lib.mdDoc "Whether the connections to the proxy should be considered secure.";
+          description = "Whether the connections to the proxy should be considered secure.";
         };
       };
 
       sso = {
-        enable = mkEnableOption (lib.mdDoc "SSO with Atlassian Crowd");
+        enable = mkEnableOption "SSO with Atlassian Crowd";
 
         crowd = mkOption {
           type = types.str;
           example = "http://localhost:8095/crowd";
-          description = lib.mdDoc "Crowd Base URL without trailing slash";
+          description = "Crowd Base URL without trailing slash";
         };
 
         applicationName = mkOption {
           type = types.str;
           example = "jira";
-          description = lib.mdDoc "Exact name of this JIRA instance in Crowd";
+          description = "Exact name of this JIRA instance in Crowd";
         };
 
         applicationPasswordFile = mkOption {
           type = types.str;
-          description = lib.mdDoc "Path to the file containing the application password of this JIRA instance in Crowd";
+          description = "Path to the file containing the application password of this JIRA instance in Crowd";
         };
 
         validationInterval = mkOption {
           type = types.int;
           default = 2;
           example = 0;
-          description = lib.mdDoc ''
+          description = ''
             Set to 0, if you want authentication checks to occur on each
             request. Otherwise set to the number of minutes between request
             to validate if the user is logged in or out of the Crowd SSO
diff --git a/nixpkgs/nixos/modules/services/web-apps/bookstack.nix b/nixpkgs/nixos/modules/services/web-apps/bookstack.nix
index 4999eceb2b60..21948fd310d6 100644
--- a/nixpkgs/nixos/modules/services/web-apps/bookstack.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/bookstack.nix
@@ -34,22 +34,22 @@ in {
 
   options.services.bookstack = {
 
-    enable = mkEnableOption (lib.mdDoc "BookStack");
+    enable = mkEnableOption "BookStack";
 
     user = mkOption {
       default = "bookstack";
-      description = lib.mdDoc "User bookstack runs as.";
+      description = "User bookstack runs as.";
       type = types.str;
     };
 
     group = mkOption {
       default = "bookstack";
-      description = lib.mdDoc "Group bookstack runs as.";
+      description = "Group bookstack runs as.";
       type = types.str;
     };
 
     appKeyFile = mkOption {
-      description = lib.mdDoc ''
+      description = ''
         A file containing the Laravel APP_KEY - a 32 character long,
         base64 encoded key used for encryption where needed. Can be
         generated with `head -c 32 /dev/urandom | base64`.
@@ -63,13 +63,13 @@ in {
       default = config.networking.fqdnOrHostName;
       defaultText = lib.literalExpression "config.networking.fqdnOrHostName";
       example = "bookstack.example.com";
-      description = lib.mdDoc ''
+      description = ''
         The hostname to serve BookStack on.
       '';
     };
 
     appURL = mkOption {
-      description = lib.mdDoc ''
+      description = ''
         The root URL that you want to host BookStack on. All URLs in BookStack will be generated using this value.
         If you change this in the future you may need to run a command to update stored URLs in the database. Command example: `php artisan bookstack:update-url https://old.example.com https://new.example.com`
       '';
@@ -80,7 +80,7 @@ in {
     };
 
     dataDir = mkOption {
-      description = lib.mdDoc "BookStack data directory";
+      description = "BookStack data directory";
       default = "/var/lib/bookstack";
       type = types.path;
     };
@@ -89,29 +89,29 @@ in {
       host = mkOption {
         type = types.str;
         default = "localhost";
-        description = lib.mdDoc "Database host address.";
+        description = "Database host address.";
       };
       port = mkOption {
         type = types.port;
         default = 3306;
-        description = lib.mdDoc "Database host port.";
+        description = "Database host port.";
       };
       name = mkOption {
         type = types.str;
         default = "bookstack";
-        description = lib.mdDoc "Database name.";
+        description = "Database name.";
       };
       user = mkOption {
         type = types.str;
         default = user;
         defaultText = literalExpression "user";
-        description = lib.mdDoc "Database username.";
+        description = "Database username.";
       };
       passwordFile = mkOption {
         type = with types; nullOr path;
         default = null;
         example = "/run/keys/bookstack-dbpassword";
-        description = lib.mdDoc ''
+        description = ''
           A file containing the password corresponding to
           {option}`database.user`.
         '';
@@ -119,7 +119,7 @@ in {
       createLocally = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc "Create the database and database user locally.";
+        description = "Create the database and database user locally.";
       };
     };
 
@@ -127,39 +127,39 @@ in {
       driver = mkOption {
         type = types.enum [ "smtp" "sendmail" ];
         default = "smtp";
-        description = lib.mdDoc "Mail driver to use.";
+        description = "Mail driver to use.";
       };
       host = mkOption {
         type = types.str;
         default = "localhost";
-        description = lib.mdDoc "Mail host address.";
+        description = "Mail host address.";
       };
       port = mkOption {
         type = types.port;
         default = 1025;
-        description = lib.mdDoc "Mail host port.";
+        description = "Mail host port.";
       };
       fromName = mkOption {
         type = types.str;
         default = "BookStack";
-        description = lib.mdDoc "Mail \"from\" name.";
+        description = "Mail \"from\" name.";
       };
       from = mkOption {
         type = types.str;
         default = "mail@bookstackapp.com";
-        description = lib.mdDoc "Mail \"from\" email.";
+        description = "Mail \"from\" email.";
       };
       user = mkOption {
         type = with types; nullOr str;
         default = null;
         example = "bookstack";
-        description = lib.mdDoc "Mail username.";
+        description = "Mail username.";
       };
       passwordFile = mkOption {
         type = with types; nullOr path;
         default = null;
         example = "/run/keys/bookstack-mailpassword";
-        description = lib.mdDoc ''
+        description = ''
           A file containing the password corresponding to
           {option}`mail.user`.
         '';
@@ -167,7 +167,7 @@ in {
       encryption = mkOption {
         type = with types; nullOr (enum [ "tls" ]);
         default = null;
-        description = lib.mdDoc "SMTP encryption mechanism to use.";
+        description = "SMTP encryption mechanism to use.";
       };
     };
 
@@ -175,7 +175,7 @@ in {
       type = types.str;
       default = "18M";
       example = "1G";
-      description = lib.mdDoc "The maximum size for uploads (e.g. images).";
+      description = "The maximum size for uploads (e.g. images).";
     };
 
     poolConfig = mkOption {
@@ -188,7 +188,7 @@ in {
         "pm.max_spare_servers" = 4;
         "pm.max_requests" = 500;
       };
-      description = lib.mdDoc ''
+      description = ''
         Options for the bookstack PHP pool. See the documentation on `php-fpm.conf`
         for details on configuration directives.
       '';
@@ -210,7 +210,7 @@ in {
           enableACME = true;
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         With this option, you can customize the nginx virtualHost settings.
       '';
     };
@@ -231,7 +231,7 @@ in {
                 options = {
                   _secret = mkOption {
                     type = nullOr str;
-                    description = lib.mdDoc ''
+                    description = ''
                       The path to a file containing the value the
                       option should be set to in the final
                       configuration file.
@@ -253,7 +253,7 @@ in {
           OIDC_ISSUER_DISCOVER = true;
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         BookStack configuration options to set in the
         {file}`.env` file.
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/calibre-web.nix b/nixpkgs/nixos/modules/services/web-apps/calibre-web.nix
index 80567db10c97..0ca9ed2fbcf3 100644
--- a/nixpkgs/nixos/modules/services/web-apps/calibre-web.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/calibre-web.nix
@@ -8,7 +8,7 @@ in
 {
   options = {
     services.calibre-web = {
-      enable = mkEnableOption (lib.mdDoc "Calibre-Web");
+      enable = mkEnableOption "Calibre-Web";
 
       package = lib.mkPackageOption pkgs "calibre-web" { };
 
@@ -16,7 +16,7 @@ in
         ip = mkOption {
           type = types.str;
           default = "::1";
-          description = lib.mdDoc ''
+          description = ''
             IP address that Calibre-Web should listen on.
           '';
         };
@@ -24,7 +24,7 @@ in
         port = mkOption {
           type = types.port;
           default = 8083;
-          description = lib.mdDoc ''
+          description = ''
             Listen port for Calibre-Web.
           '';
         };
@@ -33,7 +33,7 @@ in
       dataDir = mkOption {
         type = types.str;
         default = "calibre-web";
-        description = lib.mdDoc ''
+        description = ''
           The directory below {file}`/var/lib` where Calibre-Web stores its data.
         '';
       };
@@ -41,19 +41,19 @@ in
       user = mkOption {
         type = types.str;
         default = "calibre-web";
-        description = lib.mdDoc "User account under which Calibre-Web runs.";
+        description = "User account under which Calibre-Web runs.";
       };
 
       group = mkOption {
         type = types.str;
         default = "calibre-web";
-        description = lib.mdDoc "Group account under which Calibre-Web runs.";
+        description = "Group account under which Calibre-Web runs.";
       };
 
       openFirewall = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           Open ports in the firewall for the server.
         '';
       };
@@ -62,7 +62,7 @@ in
         calibreLibrary = mkOption {
           type = types.nullOr types.path;
           default = null;
-          description = lib.mdDoc ''
+          description = ''
             Path to Calibre library.
           '';
         };
@@ -70,17 +70,17 @@ in
         enableBookConversion = mkOption {
           type = types.bool;
           default = false;
-          description = lib.mdDoc ''
+          description = ''
             Configure path to the Calibre's ebook-convert in the DB.
           '';
         };
 
-        enableKepubify = mkEnableOption (lib.mdDoc "kebup conversion support");
+        enableKepubify = mkEnableOption "kebup conversion support";
 
         enableBookUploading = mkOption {
           type = types.bool;
           default = false;
-          description = lib.mdDoc ''
+          description = ''
             Allow books to be uploaded via Calibre-Web UI.
           '';
         };
@@ -89,7 +89,7 @@ in
           enable = mkOption {
             type = types.bool;
             default = false;
-            description = lib.mdDoc ''
+            description = ''
               Enable authorization using auth proxy.
             '';
           };
@@ -97,7 +97,7 @@ in
           header = mkOption {
             type = types.str;
             default = "";
-            description = lib.mdDoc ''
+            description = ''
               Auth proxy header name.
             '';
           };
diff --git a/nixpkgs/nixos/modules/services/web-apps/castopod.md b/nixpkgs/nixos/modules/services/web-apps/castopod.md
new file mode 100644
index 000000000000..5ecd807686fd
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/castopod.md
@@ -0,0 +1,25 @@
+# Castopod {#module-services-castopod}
+
+Castopod is an open-source hosting platform made for podcasters who want to engage and interact with their audience.
+
+## Quickstart {#module-services-castopod-quickstart}
+
+Configure ACME (https://nixos.org/manual/nixos/unstable/#module-security-acme).
+Use the following configuration to start a public instance of Castopod on `castopod.example.com` domain:
+
+```nix
+{
+  networking.firewall.allowedTCPPorts = [ 80 443 ];
+  services.castopod = {
+    enable = true;
+    database.createLocally = true;
+    nginx.virtualHost = {
+      serverName = "castopod.example.com";
+      enableACME = true;
+      forceSSL = true;
+    };
+  };
+}
+```
+
+Go to `https://castopod.example.com/cp-install` to create superadmin account after applying the above configuration.
diff --git a/nixpkgs/nixos/modules/services/web-apps/castopod.nix b/nixpkgs/nixos/modules/services/web-apps/castopod.nix
new file mode 100644
index 000000000000..d3750c3dd393
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/castopod.nix
@@ -0,0 +1,318 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.services.castopod;
+  fpm = config.services.phpfpm.pools.castopod;
+
+  user = "castopod";
+
+  # https://docs.castopod.org/getting-started/install.html#requirements
+  phpPackage = pkgs.php.withExtensions ({ enabled, all }: with all; [
+    intl
+    curl
+    mbstring
+    gd
+    exif
+    mysqlnd
+  ] ++ enabled);
+in
+{
+  meta.doc = ./castopod.md;
+  meta.maintainers = with lib.maintainers; [ alexoundos ];
+
+  options.services = {
+    castopod = {
+      enable = lib.mkEnableOption "Castopod, a hosting platform for podcasters";
+      package = lib.mkOption {
+        type = lib.types.package;
+        default = pkgs.castopod;
+        defaultText = lib.literalMD "pkgs.castopod";
+        description = "Which Castopod package to use.";
+      };
+      dataDir = lib.mkOption {
+        type = lib.types.path;
+        default = "/var/lib/castopod";
+        description = ''
+          The path where castopod stores all data. This path must be in sync
+          with the castopod package (where it is hardcoded during the build in
+          accordance with its own `dataDir` argument).
+        '';
+      };
+      database = {
+        createLocally = lib.mkOption {
+          type = lib.types.bool;
+          default = true;
+          description = ''
+            Create the database and database user locally.
+          '';
+        };
+        hostname = lib.mkOption {
+          type = lib.types.str;
+          default = "localhost";
+          description = "Database hostname.";
+        };
+        name = lib.mkOption {
+          type = lib.types.str;
+          default = "castopod";
+          description = "Database name.";
+        };
+        user = lib.mkOption {
+          type = lib.types.str;
+          default = user;
+          description = "Database user.";
+        };
+        passwordFile = lib.mkOption {
+          type = lib.types.nullOr lib.types.path;
+          default = null;
+          example = "/run/keys/castopod-dbpassword";
+          description = ''
+            A file containing the password corresponding to
+            [](#opt-services.castopod.database.user).
+
+            This file is loaded using systemd LoadCredentials.
+          '';
+        };
+      };
+      settings = lib.mkOption {
+        type = with lib.types; attrsOf (oneOf [ str int bool ]);
+        default = { };
+        example = {
+          "email.protocol" = "smtp";
+          "email.SMTPHost" = "localhost";
+          "email.SMTPUser" = "myuser";
+          "email.fromEmail" = "castopod@example.com";
+        };
+        description = ''
+          Environment variables used for Castopod.
+          See [](https://code.castopod.org/adaures/castopod/-/blob/main/.env.example)
+          for available environment variables.
+        '';
+      };
+      environmentFile = lib.mkOption {
+        type = lib.types.nullOr lib.types.path;
+        default = null;
+        example = "/run/keys/castopod-env";
+        description = ''
+          Environment file to inject e.g. secrets into the configuration.
+          See [](https://code.castopod.org/adaures/castopod/-/blob/main/.env.example)
+          for available environment variables.
+
+          This file is loaded using systemd LoadCredentials.
+        '';
+      };
+      configureNginx = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = "Configure nginx as a reverse proxy for CastoPod.";
+      };
+      localDomain = lib.mkOption {
+        type = lib.types.str;
+        example = "castopod.example.org";
+        description = "The domain serving your CastoPod instance.";
+      };
+      poolSettings = lib.mkOption {
+        type = with lib.types; attrsOf (oneOf [ str int bool ]);
+        default = {
+          "pm" = "dynamic";
+          "pm.max_children" = "32";
+          "pm.start_servers" = "2";
+          "pm.min_spare_servers" = "2";
+          "pm.max_spare_servers" = "4";
+          "pm.max_requests" = "500";
+        };
+        description = ''
+          Options for Castopod's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives.
+        '';
+      };
+      maxUploadSize = lib.mkOption {
+        type = lib.types.str;
+        default = "512M";
+        description = ''
+          Maximum supported size for a file upload in. Maximum HTTP body
+          size is set to this value for nginx and PHP (because castopod doesn't
+          support chunked uploads yet:
+          https://code.castopod.org/adaures/castopod/-/issues/330).
+
+          Note, that practical upload size limit is smaller. For example, with
+          512 MiB setting - around 500 MiB is possible.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    services.castopod.settings =
+      let
+        sslEnabled = with config.services.nginx.virtualHosts.${cfg.localDomain}; addSSL || forceSSL || onlySSL || enableACME || useACMEHost != null;
+        baseURL = "http${lib.optionalString sslEnabled "s"}://${cfg.localDomain}";
+      in
+      lib.mapAttrs (_: lib.mkDefault) {
+        "app.forceGlobalSecureRequests" = sslEnabled;
+        "app.baseURL" = baseURL;
+
+        "media.baseURL" = baseURL;
+        "media.root" = "media";
+        "media.storage" = cfg.dataDir;
+
+        "admin.gateway" = "admin";
+        "auth.gateway" = "auth";
+
+        "database.default.hostname" = cfg.database.hostname;
+        "database.default.database" = cfg.database.name;
+        "database.default.username" = cfg.database.user;
+        "database.default.DBPrefix" = "cp_";
+
+        "cache.handler" = "file";
+      };
+
+    services.phpfpm.pools.castopod = {
+      inherit user;
+      group = config.services.nginx.group;
+      inherit phpPackage;
+      phpOptions = ''
+        # https://code.castopod.org/adaures/castopod/-/blob/develop/docker/production/common/uploads.template.ini
+        file_uploads = On
+        memory_limit = 512M
+        upload_max_filesize = ${cfg.maxUploadSize}
+        post_max_size = ${cfg.maxUploadSize}
+        max_execution_time = 300
+        max_input_time = 300
+      '';
+      settings = {
+        "listen.owner" = config.services.nginx.user;
+        "listen.group" = config.services.nginx.group;
+      } // cfg.poolSettings;
+    };
+
+    systemd.services.castopod-setup = {
+      after = lib.optional config.services.mysql.enable "mysql.service";
+      requires = lib.optional config.services.mysql.enable "mysql.service";
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.openssl phpPackage ];
+      script =
+        let
+          envFile = "${cfg.dataDir}/.env";
+          media = "${cfg.settings."media.storage"}/${cfg.settings."media.root"}";
+        in
+        ''
+          mkdir -p ${cfg.dataDir}/writable/{cache,logs,session,temp,uploads}
+
+          if [ ! -d ${lib.escapeShellArg media} ]; then
+            cp --no-preserve=mode,ownership -r ${cfg.package}/share/castopod/public/media ${lib.escapeShellArg media}
+          fi
+
+          if [ ! -f ${cfg.dataDir}/salt ]; then
+            openssl rand -base64 33 > ${cfg.dataDir}/salt
+          fi
+
+          cat <<'EOF' > ${envFile}
+          ${lib.generators.toKeyValue { } cfg.settings}
+          EOF
+
+          echo "analytics.salt=$(cat ${cfg.dataDir}/salt)" >> ${envFile}
+
+          ${if (cfg.database.passwordFile != null) then ''
+            echo "database.default.password=$(cat "$CREDENTIALS_DIRECTORY/dbpasswordfile)" >> ${envFile}
+          '' else ''
+            echo "database.default.password=" >> ${envFile}
+          ''}
+
+          ${lib.optionalString (cfg.environmentFile != null) ''
+            cat "$CREDENTIALS_DIRECTORY/envfile" >> ${envFile}
+          ''}
+
+          php ${cfg.package}/share/castopod/spark castopod:database-update
+        '';
+      serviceConfig = {
+        StateDirectory = "castopod";
+        LoadCredential = lib.optional (cfg.environmentFile != null)
+          "envfile:${cfg.environmentFile}"
+        ++ (lib.optional (cfg.database.passwordFile != null)
+          "dbpasswordfile:${cfg.database.passwordFile}");
+        WorkingDirectory = "${cfg.package}/share/castopod";
+        Type = "oneshot";
+        RemainAfterExit = true;
+        User = user;
+        Group = config.services.nginx.group;
+        ReadWritePaths = cfg.dataDir;
+      };
+    };
+
+    systemd.services.castopod-scheduled = {
+      after = [ "castopod-setup.service" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ phpPackage ];
+      script = ''
+        php ${cfg.package}/share/castopod/spark tasks:run
+      '';
+      serviceConfig = {
+        StateDirectory = "castopod";
+        WorkingDirectory = "${cfg.package}/share/castopod";
+        Type = "oneshot";
+        User = user;
+        Group = config.services.nginx.group;
+        ReadWritePaths = cfg.dataDir;
+        LogLevelMax = "notice"; # otherwise periodic tasks flood the journal
+      };
+    };
+
+    systemd.timers.castopod-scheduled = {
+      wantedBy = [ "timers.target" ];
+      timerConfig = {
+        OnCalendar = "*-*-* *:*:00";
+        Unit = "castopod-scheduled.service";
+      };
+    };
+
+    services.mysql = lib.mkIf cfg.database.createLocally {
+      enable = true;
+      package = lib.mkDefault pkgs.mariadb;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [{
+        name = cfg.database.user;
+        ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
+      }];
+    };
+
+    services.nginx = lib.mkIf cfg.configureNginx {
+      enable = true;
+      virtualHosts."${cfg.localDomain}" = {
+        root = lib.mkForce "${cfg.package}/share/castopod/public";
+
+        extraConfig = ''
+          try_files $uri $uri/ /index.php?$args;
+          index index.php index.html;
+          client_max_body_size ${cfg.maxUploadSize};
+        '';
+
+        locations."^~ /${cfg.settings."media.root"}/" = {
+          root = cfg.settings."media.storage";
+          extraConfig = ''
+            add_header Access-Control-Allow-Origin "*";
+            expires max;
+            access_log off;
+          '';
+        };
+
+        locations."~ \.php$" = {
+          fastcgiParams = {
+            SERVER_NAME = "$host";
+          };
+          extraConfig = ''
+            fastcgi_intercept_errors on;
+            fastcgi_index index.php;
+            fastcgi_pass unix:${fpm.socket};
+            try_files $uri =404;
+            fastcgi_read_timeout 3600;
+            fastcgi_send_timeout 3600;
+          '';
+        };
+      };
+    };
+
+    users.users.${user} = lib.mapAttrs (_: lib.mkDefault) {
+      description = "Castopod user";
+      isSystemUser = true;
+      group = config.services.nginx.group;
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/changedetection-io.nix b/nixpkgs/nixos/modules/services/web-apps/changedetection-io.nix
index bbf4c2aed186..f0d72b1e4d69 100644
--- a/nixpkgs/nixos/modules/services/web-apps/changedetection-io.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/changedetection-io.nix
@@ -7,12 +7,12 @@ let
 in
 {
   options.services.changedetection-io = {
-    enable = mkEnableOption (lib.mdDoc "changedetection-io");
+    enable = mkEnableOption "changedetection-io";
 
     user = mkOption {
       default = "changedetection-io";
       type = types.str;
-      description = lib.mdDoc ''
+      description = ''
         User account under which changedetection-io runs.
       '';
     };
@@ -20,7 +20,7 @@ in
     group = mkOption {
       default = "changedetection-io";
       type = types.str;
-      description = lib.mdDoc ''
+      description = ''
         Group account under which changedetection-io runs.
       '';
     };
@@ -28,19 +28,19 @@ in
     listenAddress = mkOption {
       type = types.str;
       default = "localhost";
-      description = lib.mdDoc "Address the server will listen on.";
+      description = "Address the server will listen on.";
     };
 
     port = mkOption {
       type = types.port;
       default = 5000;
-      description = lib.mdDoc "Port the server will listen on.";
+      description = "Port the server will listen on.";
     };
 
     datastorePath = mkOption {
       type = types.str;
       default = "/var/lib/changedetection-io";
-      description = lib.mdDoc ''
+      description = ''
         The directory used to store all data for changedetection-io.
       '';
     };
@@ -49,7 +49,7 @@ in
       type = types.nullOr types.str;
       default = null;
       example = "https://changedetection-io.example";
-      description = lib.mdDoc ''
+      description = ''
         The base url used in notifications and `{base_url}` token.
       '';
     };
@@ -57,7 +57,7 @@ in
     behindProxy = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Enable this option when changedetection-io runs behind a reverse proxy, so that it trusts X-* headers.
         It is recommend to run changedetection-io behind a TLS reverse proxy.
       '';
@@ -67,7 +67,7 @@ in
       type = types.nullOr types.path;
       default = null;
       example = "/run/secrets/changedetection-io.env";
-      description = lib.mdDoc ''
+      description = ''
         Securely pass environment variabels to changedetection-io.
 
         This can be used to set for example a frontend password reproducible via `SALTED_PASS`
@@ -81,7 +81,7 @@ in
     webDriverSupport = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Enable support for fetching web pages using WebDriver and Chromium.
         This starts a headless chromium controlled by puppeteer in an oci container.
 
@@ -95,7 +95,7 @@ in
     playwrightSupport = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Enable support for fetching web pages using playwright and Chromium.
         This starts a headless Chromium controlled by puppeteer in an oci container.
 
@@ -109,7 +109,7 @@ in
     chromePort = mkOption {
       type = types.port;
       default = 4444;
-      description = lib.mdDoc ''
+      description = ''
         A free port on which webDriverSupport or playwrightSupport listen on localhost.
       '';
     };
diff --git a/nixpkgs/nixos/modules/services/web-apps/chatgpt-retrieval-plugin.nix b/nixpkgs/nixos/modules/services/web-apps/chatgpt-retrieval-plugin.nix
index f29d095bc10b..c1ab7ec40949 100644
--- a/nixpkgs/nixos/modules/services/web-apps/chatgpt-retrieval-plugin.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/chatgpt-retrieval-plugin.nix
@@ -7,24 +7,24 @@ let
 in
 {
   options.services.chatgpt-retrieval-plugin = {
-    enable = mkEnableOption (lib.mdDoc "chatgpt-retrieval-plugin service");
+    enable = mkEnableOption "chatgpt-retrieval-plugin service";
 
     port = mkOption {
       type = types.port;
       default = 8080;
-      description = lib.mdDoc "Port the chatgpt-retrieval-plugin service listens on.";
+      description = "Port the chatgpt-retrieval-plugin service listens on.";
     };
 
     host = mkOption {
       type = types.str;
       default = "127.0.0.1";
       example = "0.0.0.0";
-      description = lib.mdDoc "The hostname or IP address for chatgpt-retrieval-plugin to bind to.";
+      description = "The hostname or IP address for chatgpt-retrieval-plugin to bind to.";
     };
 
     bearerTokenPath = mkOption {
       type = types.path;
-      description = lib.mdDoc ''
+      description = ''
         Path to the secret bearer token used for the http api authentication.
       '';
       default = "";
@@ -33,7 +33,7 @@ in
 
     openaiApiKeyPath = mkOption {
       type = types.path;
-      description = lib.mdDoc ''
+      description = ''
         Path to the secret openai api key used for embeddings.
       '';
       default = "";
@@ -43,12 +43,12 @@ in
     datastore = mkOption {
       type = types.enum [ "pinecone" "weaviate" "zilliz" "milvus" "qdrant" "redis" ];
       default = "qdrant";
-      description = lib.mdDoc "This specifies the vector database provider you want to use to store and query embeddings.";
+      description = "This specifies the vector database provider you want to use to store and query embeddings.";
     };
 
     qdrantCollection = mkOption {
       type = types.str;
-      description = lib.mdDoc ''
+      description = ''
         name of the qdrant collection used to store documents.
       '';
       default = "document_chunks";
diff --git a/nixpkgs/nixos/modules/services/web-apps/cloudlog.nix b/nixpkgs/nixos/modules/services/web-apps/cloudlog.nix
index 5519d6967a12..6550d112d537 100644
--- a/nixpkgs/nixos/modules/services/web-apps/cloudlog.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/cloudlog.nix
@@ -69,46 +69,46 @@ let
 in
 {
   options.services.cloudlog = with types; {
-    enable = mkEnableOption (mdDoc "Cloudlog");
+    enable = mkEnableOption "Cloudlog";
     dataDir = mkOption {
       type = str;
       default = "/var/lib/cloudlog";
-      description = mdDoc "Cloudlog data directory.";
+      description = "Cloudlog data directory.";
     };
     baseUrl = mkOption {
       type = str;
       default = "http://localhost";
-      description = mdDoc "Cloudlog base URL";
+      description = "Cloudlog base URL";
     };
     user = mkOption {
       type = str;
       default = "cloudlog";
-      description = mdDoc "User account under which Cloudlog runs.";
+      description = "User account under which Cloudlog runs.";
     };
     database = {
       createLocally = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc "Create the database and database user locally.";
+        description = "Create the database and database user locally.";
       };
       host = mkOption {
         type = str;
-        description = mdDoc "MySQL database host";
+        description = "MySQL database host";
         default = "localhost";
       };
       name = mkOption {
         type = str;
-        description = mdDoc "MySQL database name.";
+        description = "MySQL database name.";
         default = "cloudlog";
       };
       user = mkOption {
         type = str;
-        description = mdDoc "MySQL user name.";
+        description = "MySQL user name.";
         default = "cloudlog";
       };
       passwordFile = mkOption {
         type = nullOr str;
-        description = mdDoc "MySQL user password file.";
+        description = "MySQL user password file.";
         default = null;
       };
     };
@@ -122,20 +122,20 @@ in
         "pm.max_spare_servers" = 4;
         "pm.max_requests" = 500;
       };
-      description = mdDoc ''
+      description = ''
         Options for Cloudlog's PHP-FPM pool.
       '';
     };
     virtualHost = mkOption {
       type = nullOr str;
       default = "localhost";
-      description = mdDoc ''
+      description = ''
         Name of the nginx virtualhost to use and setup. If null, do not setup
          any virtualhost.
       '';
     };
     extraConfig = mkOption {
-      description = mdDoc ''
+      description = ''
        Any additional text to be appended to the config.php
        configuration file. This is a PHP script. For configuration
        settings, see <https://github.com/magicbug/Cloudlog/wiki/Cloudlog.php-Configuration-File>.
@@ -150,7 +150,7 @@ in
       enable = mkOption {
         type = bool;
         default = true;
-        description = mdDoc ''
+        description = ''
           Whether to periodically upload logs to LoTW. If enabled, a systemd
           timer will run the log upload task as specified by the interval
            option.
@@ -159,7 +159,7 @@ in
       interval = mkOption {
         type = str;
         default = "daily";
-        description = mdDoc ''
+        description = ''
           Specification (in the format described by systemd.time(7)) of the
           time at which the LoTW upload will occur.
         '';
@@ -169,7 +169,7 @@ in
       enable = mkOption {
         type = bool;
         default = true;
-        description = mdDoc ''
+        description = ''
           Whether to periodically upload logs to Clublog. If enabled, a systemd
           timer will run the log upload task as specified by the interval option.
         '';
@@ -177,7 +177,7 @@ in
       interval = mkOption {
         type = str;
         default = "daily";
-        description = mdDoc ''
+        description = ''
           Specification (in the format described by systemd.time(7)) of the time
           at which the Clublog upload will occur.
         '';
@@ -187,7 +187,7 @@ in
       enable = mkOption {
         type = bool;
         default = true;
-        description = mdDoc ''
+        description = ''
           Whether to periodically update the list of LoTW users. If enabled, a
           systemd timer will run the update task as specified by the interval
           option.
@@ -196,7 +196,7 @@ in
       interval = mkOption {
         type = str;
         default = "weekly";
-        description = mdDoc ''
+        description = ''
           Specification (in the format described by systemd.time(7)) of the
           time at which the LoTW user update will occur.
         '';
@@ -206,7 +206,7 @@ in
       enable = mkOption {
         type = bool;
         default = true;
-        description = mdDoc ''
+        description = ''
           Whether to periodically update the DOK resource file. If enabled, a
           systemd timer will run the update task as specified by the interval option.
         '';
@@ -214,7 +214,7 @@ in
       interval = mkOption {
         type = str;
         default = "monthly";
-        description = mdDoc ''
+        description = ''
           Specification (in the format described by systemd.time(7)) of the
           time at which the DOK update will occur.
         '';
@@ -224,7 +224,7 @@ in
       enable = mkOption {
         type = bool;
         default = true;
-        description = mdDoc ''
+        description = ''
           Whether to periodically update the Clublog SCP database. If enabled,
           a systemd timer will run the update task as specified by the interval
           option.
@@ -233,7 +233,7 @@ in
       interval = mkOption {
         type = str;
         default = "monthly";
-        description = mdDoc ''
+        description = ''
           Specification (in the format described by systemd.time(7)) of the time
           at which the Clublog SCP update will occur.
         '';
@@ -243,7 +243,7 @@ in
       enable = mkOption {
         type = bool;
         default = true;
-        description = mdDoc ''
+        description = ''
           Whether to periodically update the WWFF database. If enabled, a
           systemd timer will run the update task as specified by the interval
           option.
@@ -252,7 +252,7 @@ in
       interval = mkOption {
         type = str;
         default = "monthly";
-        description = mdDoc ''
+        description = ''
           Specification (in the format described by systemd.time(7)) of the time
           at which the WWFF update will occur.
         '';
@@ -262,7 +262,7 @@ in
       enable = mkOption {
         type = bool;
         default = true;
-        description = mdDoc ''
+        description = ''
           Whether to periodically upload logs to QRZ. If enabled, a systemd
           timer will run the update task as specified by the interval option.
         '';
@@ -270,7 +270,7 @@ in
       interval = mkOption {
         type = str;
         default = "daily";
-        description = mdDoc ''
+        description = ''
           Specification (in the format described by systemd.time(7)) of the
           time at which the QRZ upload will occur.
         '';
@@ -280,7 +280,7 @@ in
       enable = mkOption {
         type = bool;
         default = true;
-        description = mdDoc ''
+        description = ''
           Whether to periodically update the SOTA database. If enabled, a
           systemd timer will run the update task as specified by the interval option.
         '';
@@ -288,7 +288,7 @@ in
       interval = mkOption {
         type = str;
         default = "monthly";
-        description = mdDoc ''
+        description = ''
           Specification (in the format described by systemd.time(7)) of the time
           at which the SOTA update will occur.
         '';
diff --git a/nixpkgs/nixos/modules/services/web-apps/code-server.nix b/nixpkgs/nixos/modules/services/web-apps/code-server.nix
index d087deb7848d..abb5be50d353 100644
--- a/nixpkgs/nixos/modules/services/web-apps/code-server.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/code-server.nix
@@ -7,7 +7,7 @@ let
 in {
   options = {
     services.code-server = {
-      enable = lib.mkEnableOption (lib.mdDoc "code-server");
+      enable = lib.mkEnableOption "code-server";
 
       package = lib.mkPackageOptionMD pkgs "code-server" {
         example = ''
@@ -23,7 +23,7 @@ in {
 
       extraPackages = lib.mkOption {
         default = [ ];
-        description = lib.mdDoc ''
+        description = ''
           Additional packages to add to the code-server {env}`PATH`.
         '';
         example = lib.literalExpression "[ pkgs.go ]";
@@ -32,7 +32,7 @@ in {
 
       extraEnvironment = lib.mkOption {
         type = lib.types.attrsOf lib.types.str;
-        description = lib.mdDoc ''
+        description = ''
           Additional environment variables to pass to code-server.
         '';
         default = { };
@@ -41,7 +41,7 @@ in {
 
       extraArguments = lib.mkOption {
         default = [ ];
-        description = lib.mdDoc ''
+        description = ''
           Additional arguments to pass to code-server.
         '';
         example = lib.literalExpression ''[ "--log=info" ]'';
@@ -50,7 +50,7 @@ in {
 
       host = lib.mkOption {
         default = "localhost";
-        description = lib.mdDoc ''
+        description = ''
           The host name or IP address the server should listen to.
         '';
         type = lib.types.str;
@@ -58,7 +58,7 @@ in {
 
       port = lib.mkOption {
         default = 4444;
-        description = lib.mdDoc ''
+        description = ''
           The port the server should listen to.
         '';
         type = lib.types.port;
@@ -66,7 +66,7 @@ in {
 
       auth = lib.mkOption {
         default = "password";
-        description = lib.mdDoc ''
+        description = ''
           The type of authentication to use.
         '';
         type = lib.types.enum [ "none" "password" ];
@@ -74,7 +74,7 @@ in {
 
       hashedPassword = lib.mkOption {
         default = "";
-        description = lib.mdDoc ''
+        description = ''
           Create the password with: `echo -n 'thisismypassword' | npx argon2-cli -e`.
         '';
         type = lib.types.str;
@@ -83,7 +83,7 @@ in {
       user = lib.mkOption {
         default = defaultUser;
         example = "yourUser";
-        description = lib.mdDoc ''
+        description = ''
           The user to run code-server as.
           By default, a user named `${defaultUser}` will be created.
         '';
@@ -93,7 +93,7 @@ in {
       group = lib.mkOption {
         default = defaultGroup;
         example = "yourGroup";
-        description = lib.mdDoc ''
+        description = ''
           The group to run code-server under.
           By default, a group named `${defaultGroup}` will be created.
         '';
@@ -102,7 +102,7 @@ in {
 
       extraGroups = lib.mkOption {
         default = [ ];
-        description = lib.mdDoc ''
+        description = ''
           An array of additional groups for the `${defaultUser}` user.
         '';
         example = [ "docker" ];
@@ -112,7 +112,7 @@ in {
       socket = lib.mkOption {
         default = null;
         example = "/run/code-server/socket";
-        description = lib.mdDoc ''
+        description = ''
           Path to a socket (bind-addr will be ignored).
         '';
         type = lib.types.nullOr lib.types.str;
@@ -120,7 +120,7 @@ in {
 
       socketMode = lib.mkOption {
         default = null;
-        description = lib.mdDoc ''
+        description = ''
            File mode of the socket.
         '';
         type = lib.types.nullOr lib.types.str;
@@ -128,7 +128,7 @@ in {
 
       userDataDir = lib.mkOption {
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           Path to the user data directory.
         '';
         type = lib.types.nullOr lib.types.str;
@@ -136,7 +136,7 @@ in {
 
       extensionsDir = lib.mkOption {
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           Path to the extensions directory.
         '';
         type = lib.types.nullOr lib.types.str;
@@ -145,7 +145,7 @@ in {
       proxyDomain = lib.mkOption {
         default = null;
         example = "code-server.lan";
-        description = lib.mdDoc ''
+        description = ''
           Domain used for proxying ports.
         '';
         type = lib.types.nullOr lib.types.str;
@@ -154,7 +154,7 @@ in {
       disableTelemetry = lib.mkOption {
         default = false;
         example = true;
-        description = lib.mdDoc ''
+        description = ''
           Disable telemetry.
         '';
         type = lib.types.bool;
@@ -163,7 +163,7 @@ in {
       disableUpdateCheck = lib.mkOption {
         default = false;
         example = true;
-        description = lib.mdDoc ''
+        description = ''
           Disable update check.
           Without this flag, code-server checks every 6 hours against the latest github release and
           then notifies you once every week that a new release is available.
@@ -174,7 +174,7 @@ in {
       disableFileDownloads = lib.mkOption {
         default = false;
         example = true;
-        description = lib.mdDoc ''
+        description = ''
           Disable file downloads from Code.
         '';
         type = lib.types.bool;
@@ -183,7 +183,7 @@ in {
       disableWorkspaceTrust = lib.mkOption {
         default = false;
         example = true;
-        description = lib.mdDoc ''
+        description = ''
           Disable Workspace Trust feature.
         '';
         type = lib.types.bool;
@@ -192,7 +192,7 @@ in {
       disableGettingStartedOverride = lib.mkOption {
         default = false;
         example = true;
-        description = lib.mdDoc ''
+        description = ''
           Disable the coder/coder override in the Help: Getting Started page.
         '';
         type = lib.types.bool;
diff --git a/nixpkgs/nixos/modules/services/web-apps/coder.nix b/nixpkgs/nixos/modules/services/web-apps/coder.nix
index 0f5cb2c3c689..5450adbe118d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/coder.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/coder.nix
@@ -8,12 +8,12 @@ let
 in {
   options = {
     services.coder = {
-      enable = mkEnableOption (lib.mdDoc "Coder service");
+      enable = mkEnableOption "Coder service";
 
       user = mkOption {
         type = types.str;
         default = "coder";
-        description = lib.mdDoc ''
+        description = ''
           User under which the coder service runs.
 
           ::: {.note}
@@ -26,7 +26,7 @@ in {
       group = mkOption {
         type = types.str;
         default = "coder";
-        description = lib.mdDoc ''
+        description = ''
           Group under which the coder service runs.
 
           ::: {.note}
@@ -40,7 +40,7 @@ in {
 
       homeDir = mkOption {
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           Home directory for coder user.
         '';
         default = "/var/lib/coder";
@@ -48,7 +48,7 @@ in {
 
       listenAddress = mkOption {
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           Listen address.
         '';
         default = "127.0.0.1:3000";
@@ -56,7 +56,7 @@ in {
 
       accessUrl = mkOption {
         type = types.nullOr types.str;
-        description = lib.mdDoc ''
+        description = ''
           Access URL should be a external IP address or domain with DNS records pointing to Coder.
         '';
         default = null;
@@ -65,18 +65,35 @@ in {
 
       wildcardAccessUrl = mkOption {
         type = types.nullOr types.str;
-        description = lib.mdDoc ''
+        description = ''
           If you are providing TLS certificates directly to the Coder server, you must use a single certificate for the root and wildcard domains.
         '';
         default = null;
         example = "*.coder.example.com";
       };
 
+      environment = {
+        extra = mkOption {
+          type = types.attrs;
+          description = "Extra environment variables to pass run Coder's server with. See Coder documentation.";
+          default = {};
+          example = {
+            CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS = true;
+            CODER_OAUTH2_GITHUB_ALLOWED_ORGS = "your-org";
+          };
+        };
+        file = mkOption {
+          type = types.nullOr types.path;
+          description = "Systemd environment file to add to Coder.";
+          default = null;
+        };
+      };
+
       database = {
         createLocally = mkOption {
           type = types.bool;
           default = true;
-          description = lib.mdDoc ''
+          description = ''
             Create the database and database user locally.
           '';
         };
@@ -84,7 +101,7 @@ in {
         host = mkOption {
           type = types.str;
           default = "/run/postgresql";
-          description = lib.mdDoc ''
+          description = ''
             Hostname hosting the database.
           '';
         };
@@ -92,7 +109,7 @@ in {
         database = mkOption {
           type = types.str;
           default = "coder";
-          description = lib.mdDoc ''
+          description = ''
             Name of database.
           '';
         };
@@ -100,7 +117,7 @@ in {
         username = mkOption {
           type = types.str;
           default = "coder";
-          description = lib.mdDoc ''
+          description = ''
             Username for accessing the database.
           '';
         };
@@ -108,7 +125,7 @@ in {
         password = mkOption {
           type = types.nullOr types.str;
           default = null;
-          description = lib.mdDoc ''
+          description = ''
             Password for accessing the database.
           '';
         };
@@ -116,7 +133,7 @@ in {
         sslmode = mkOption {
           type = types.nullOr types.str;
           default = "disable";
-          description = lib.mdDoc ''
+          description = ''
             Password for accessing the database.
           '';
         };
@@ -124,7 +141,7 @@ in {
 
       tlsCert = mkOption {
         type = types.nullOr types.path;
-        description = lib.mdDoc ''
+        description = ''
           The path to the TLS certificate.
         '';
         default = null;
@@ -132,7 +149,7 @@ in {
 
       tlsKey = mkOption {
         type = types.nullOr types.path;
-        description = lib.mdDoc ''
+        description = ''
           The path to the TLS key.
         '';
         default = null;
@@ -152,7 +169,7 @@ in {
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
 
-      environment = {
+      environment = cfg.environment.extra // {
         CODER_ACCESS_URL = cfg.accessUrl;
         CODER_WILDCARD_ACCESS_URL = cfg.wildcardAccessUrl;
         CODER_PG_CONNECTION_URL = "user=${cfg.database.username} ${optionalString (cfg.database.password != null) "password=${cfg.database.password}"} database=${cfg.database.database} host=${cfg.database.host} ${optionalString (cfg.database.sslmode != null) "sslmode=${cfg.database.sslmode}"}";
@@ -177,6 +194,7 @@ in {
         ExecStart = "${cfg.package}/bin/coder server";
         User = cfg.user;
         Group = cfg.group;
+        EnvironmentFile = lib.mkIf (cfg.environment.file != null) cfg.environment.file;
       };
     };
 
@@ -205,4 +223,5 @@ in {
       };
     };
   };
+  meta.maintainers = pkgs.coder.meta.maintainers;
 }
diff --git a/nixpkgs/nixos/modules/services/web-apps/convos.nix b/nixpkgs/nixos/modules/services/web-apps/convos.nix
index cd9f9d885d69..da5f7cbf724f 100644
--- a/nixpkgs/nixos/modules/services/web-apps/convos.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/convos.nix
@@ -7,23 +7,23 @@ let
 in
 {
   options.services.convos = {
-    enable = mkEnableOption (lib.mdDoc "Convos");
+    enable = mkEnableOption "Convos";
     listenPort = mkOption {
       type = types.port;
       default = 3000;
       example = 8080;
-      description = lib.mdDoc "Port the web interface should listen on";
+      description = "Port the web interface should listen on";
     };
     listenAddress = mkOption {
       type = types.str;
       default = "*";
       example = "127.0.0.1";
-      description = lib.mdDoc "Address or host the web interface should listen on";
+      description = "Address or host the web interface should listen on";
     };
     reverseProxy = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Enables reverse proxy support. This will allow Convos to automatically
         pick up the `X-Forwarded-For` and
         `X-Request-Base` HTTP headers set in your reverse proxy
diff --git a/nixpkgs/nixos/modules/services/web-apps/crabfit.nix b/nixpkgs/nixos/modules/services/web-apps/crabfit.nix
new file mode 100644
index 000000000000..d58027a6965d
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/crabfit.nix
@@ -0,0 +1,171 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  inherit (lib)
+    literalExpression
+    mkEnableOption
+    mkIf
+    mkOption
+    mkPackageOption
+    ;
+
+  inherit (lib.types)
+    attrsOf
+    package
+    port
+    str
+    ;
+
+  cfg = config.services.crabfit;
+in
+
+{
+  options.services.crabfit = {
+    enable = mkEnableOption "Crab Fit, a meeting scheduler based on peoples' availability";
+
+    frontend = {
+      package = mkPackageOption pkgs "crabfit-frontend" { };
+
+      finalDrv = mkOption {
+        readOnly = true;
+        type = package;
+        default = cfg.frontend.package.override {
+          api_url = "https://${cfg.api.host}";
+          frontend_url = cfg.frontend.host;
+        };
+
+        defaultText = literalExpression ''
+          cfg.package.override {
+            api_url = "https://''${cfg.api.host}";
+            frontend_url = cfg.frontend.host;
+          };
+        '';
+
+        description = ''
+          The patched frontend, using the correct urls for the API and frontend.
+        '';
+      };
+
+      environment = mkOption {
+        type = attrsOf str;
+        default = { };
+        description = ''
+          Environment variables for the crabfit frontend.
+        '';
+      };
+
+      host = mkOption {
+        type = str;
+        description = ''
+          The hostname of the frontend.
+        '';
+      };
+
+      port = mkOption {
+        type = port;
+        default = 3001;
+        description = ''
+          The internal listening port of the frontend.
+        '';
+      };
+    };
+
+    api = {
+      package = mkPackageOption pkgs "crabfit-api" { };
+
+      environment = mkOption {
+        type = attrsOf str;
+        default = { };
+        description = ''
+          Environment variables for the crabfit API.
+        '';
+      };
+
+      host = mkOption {
+        type = str;
+        description = ''
+          The hostname of the API.
+        '';
+      };
+
+      port = mkOption {
+        type = port;
+        default = 3000;
+        description = ''
+          The internal listening port of the API.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services = {
+      crabfit-api = {
+        description = "The API for Crab Fit.";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "postgresql.service" ];
+
+        serviceConfig = {
+          # TODO: harden
+          ExecStart = lib.getExe cfg.api.package;
+          User = "crabfit";
+        };
+
+        environment = {
+          API_LISTEN = "127.0.0.1:${builtins.toString cfg.api.port}";
+          DATABASE_URL = "postgres:///crabfit?host=/run/postgresql";
+          FRONTEND_URL = "https://${cfg.frontend.host}";
+        } // cfg.api.environment;
+      };
+
+      crabfit-frontend = {
+        description = "The frontend for Crab Fit.";
+
+        wantedBy = [ "multi-user.target" ];
+
+        serviceConfig = {
+          # TODO: harden
+          CacheDirectory = "crabfit";
+          DynamicUser = true;
+          ExecStart = "${lib.getExe pkgs.nodejs} standalone/server.js";
+          WorkingDirectory = cfg.frontend.finalDrv;
+        };
+
+        environment = {
+          NEXT_PUBLIC_API_URL = "https://${cfg.api.host}";
+          PORT = builtins.toString cfg.frontend.port;
+        } // cfg.frontend.environment;
+      };
+    };
+
+    users = {
+      groups.crabfit = { };
+
+      users.crabfit = {
+        group = "crabfit";
+        isSystemUser = true;
+      };
+    };
+
+    services = {
+      postgresql = {
+        enable = true;
+
+        ensureDatabases = [ "crabfit" ];
+
+        ensureUsers = [
+          {
+            name = "crabfit";
+            ensureDBOwnership = true;
+          }
+        ];
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/davis.md b/nixpkgs/nixos/modules/services/web-apps/davis.md
new file mode 100644
index 000000000000..9775d8221b5b
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/davis.md
@@ -0,0 +1,32 @@
+# Davis {#module-services-davis}
+
+[Davis](https://github.com/tchapi/davis/) is a caldav and carrddav server. It
+has a simple, fully translatable admin interface for sabre/dav based on Symfony
+5 and Bootstrap 5, initially inspired by BaĂŻkal.
+
+## Basic Usage {#module-services-davis-basic-usage}
+
+At first, an application secret is needed, this can be generated with:
+```ShellSession
+$ cat /dev/urandom | tr -dc a-zA-Z0-9 | fold -w 48 | head -n 1
+```
+
+After that, `davis` can be deployed like this:
+```
+{
+  services.davis = {
+    enable = true;
+    hostname = "davis.example.com";
+    mail = {
+      dsn = "smtp://username@example.com:25";
+      inviteFromAddress = "davis@example.com";
+    };
+    adminLogin = "admin";
+    adminPasswordFile = "/run/secrets/davis-admin-password";
+    appSecretFile = "/run/secrets/davis-app-secret";
+    nginx = {};
+  };
+}
+```
+
+This deploys Davis using a sqlite database running out of `/var/lib/davis`.
diff --git a/nixpkgs/nixos/modules/services/web-apps/davis.nix b/nixpkgs/nixos/modules/services/web-apps/davis.nix
new file mode 100644
index 000000000000..d9b28020dc2d
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/davis.nix
@@ -0,0 +1,554 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  cfg = config.services.davis;
+  db = cfg.database;
+  mail = cfg.mail;
+
+  mysqlLocal = db.createLocally && db.driver == "mysql";
+  pgsqlLocal = db.createLocally && db.driver == "postgresql";
+
+  user = cfg.user;
+  group = cfg.group;
+
+  isSecret = v: lib.isAttrs v && v ? _secret && (lib.isString v._secret || builtins.isPath v._secret);
+  davisEnvVars = lib.generators.toKeyValue {
+    mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" {
+      mkValueString =
+        v:
+        if builtins.isInt v then
+          toString v
+        else if lib.isString v then
+          "\"${v}\""
+        else if true == v then
+          "true"
+        else if false == v then
+          "false"
+        else if null == v then
+          ""
+        else if isSecret v then
+          if (lib.isString v._secret) then
+            builtins.hashString "sha256" v._secret
+          else
+            builtins.hashString "sha256" (builtins.readFile v._secret)
+        else
+          throw "unsupported type ${builtins.typeOf v}: ${(lib.generators.toPretty { }) v}";
+    };
+  };
+  secretPaths = lib.mapAttrsToList (_: v: v._secret) (lib.filterAttrs (_: isSecret) cfg.config);
+  mkSecretReplacement = file: ''
+    replace-secret ${
+      lib.escapeShellArgs [
+        (
+          if (lib.isString file) then
+            builtins.hashString "sha256" file
+          else
+            builtins.hashString "sha256" (builtins.readFile file)
+        )
+        file
+        "${cfg.dataDir}/.env.local"
+      ]
+    }
+  '';
+  secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
+  filteredConfig = lib.converge (lib.filterAttrsRecursive (
+    _: v:
+    !lib.elem v [
+      { }
+      null
+    ]
+  )) cfg.config;
+  davisEnv = pkgs.writeText "davis.env" (davisEnvVars filteredConfig);
+in
+{
+  options.services.davis = {
+    enable = lib.mkEnableOption "Davis is a caldav and carddav server";
+
+    user = lib.mkOption {
+      default = "davis";
+      description = "User davis runs as.";
+      type = lib.types.str;
+    };
+
+    group = lib.mkOption {
+      default = "davis";
+      description = "Group davis runs as.";
+      type = lib.types.str;
+    };
+
+    package = lib.mkPackageOption pkgs "davis" { };
+
+    dataDir = lib.mkOption {
+      type = lib.types.path;
+      default = "/var/lib/davis";
+      description = ''
+        Davis data directory.
+      '';
+    };
+
+    hostname = lib.mkOption {
+      type = lib.types.str;
+      example = "davis.yourdomain.org";
+      description = ''
+        Domain of the host to serve davis under. You may want to change it if you
+        run Davis on a different URL than davis.yourdomain.
+      '';
+    };
+
+    config = lib.mkOption {
+      type = lib.types.attrsOf (
+        lib.types.nullOr (
+          lib.types.either
+            (lib.types.oneOf [
+              lib.types.bool
+              lib.types.int
+              lib.types.port
+              lib.types.path
+              lib.types.str
+            ])
+            (
+              lib.types.submodule {
+                options = {
+                  _secret = lib.mkOption {
+                    type = lib.types.nullOr (
+                      lib.types.oneOf [
+                        lib.types.str
+                        lib.types.path
+                      ]
+                    );
+                    description = ''
+                      The path to a file containing the value the
+                      option should be set to in the final
+                      configuration file.
+                    '';
+                  };
+                };
+              }
+            )
+        )
+      );
+      default = { };
+
+      example = '''';
+      description = '''';
+    };
+
+    adminLogin = lib.mkOption {
+      type = lib.types.str;
+      default = "root";
+      description = ''
+        Username for the admin account.
+      '';
+    };
+    adminPasswordFile = lib.mkOption {
+      type = lib.types.path;
+      description = ''
+        The full path to a file that contains the admin's password. Must be
+        readable by the user.
+      '';
+      example = "/run/secrets/davis-admin-pass";
+    };
+
+    appSecretFile = lib.mkOption {
+      type = lib.types.path;
+      description = ''
+        A file containing the Symfony APP_SECRET - Its value should be a series
+        of characters, numbers and symbols chosen randomly and the recommended
+        length is around 32 characters. Can be generated with <code>cat
+        /dev/urandom | tr -dc a-zA-Z0-9 | fold -w 48 | head -n 1</code>.
+      '';
+      example = "/run/secrets/davis-appsecret";
+    };
+
+    database = {
+      driver = lib.mkOption {
+        type = lib.types.enum [
+          "sqlite"
+          "postgresql"
+          "mysql"
+        ];
+        default = "sqlite";
+        description = "Database type, required in all circumstances.";
+      };
+      urlFile = lib.mkOption {
+        type = lib.types.nullOr lib.types.path;
+        default = null;
+        example = "/run/secrets/davis-db-url";
+        description = ''
+          A file containing the database connection url. If set then it
+          overrides all other database settings (except driver). This is
+          mandatory if you want to use an external database, that is when
+          `services.davis.database.createLocally` is `false`.
+        '';
+      };
+      name = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
+        default = "davis";
+        description = "Database name, only used when the databse is created locally.";
+      };
+      createLocally = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = "Create the database and database user locally.";
+      };
+    };
+
+    mail = {
+      dsn = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
+        default = null;
+        description = "Mail DSN for sending emails. Mutually exclusive with `services.davis.mail.dsnFile`.";
+        example = "smtp://username:password@example.com:25";
+      };
+      dsnFile = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
+        default = null;
+        example = "/run/secrets/davis-mail-dsn";
+        description = "A file containing the mail DSN for sending emails.  Mutually exclusive with `servies.davis.mail.dsn`.";
+      };
+      inviteFromAddress = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
+        default = null;
+        description = "Email address to send invitations from.";
+        example = "no-reply@dav.example.com";
+      };
+    };
+
+    nginx = lib.mkOption {
+      type = lib.types.submodule (
+        lib.recursiveUpdate (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) { }
+      );
+      default = null;
+      example = ''
+        {
+          serverAliases = [
+            "dav.''${config.networking.domain}"
+          ];
+          # To enable encryption and let let's encrypt take care of certificate
+          forceSSL = true;
+          enableACME = true;
+        }
+      '';
+      description = ''
+        With this option, you can customize the nginx virtualHost settings.
+      '';
+    };
+
+    poolConfig = lib.mkOption {
+      type = lib.types.attrsOf (
+        lib.types.oneOf [
+          lib.types.str
+          lib.types.int
+          lib.types.bool
+        ]
+      );
+      default = {
+        "pm" = "dynamic";
+        "pm.max_children" = 32;
+        "pm.start_servers" = 2;
+        "pm.min_spare_servers" = 2;
+        "pm.max_spare_servers" = 4;
+        "pm.max_requests" = 500;
+      };
+      description = ''
+        Options for the davis PHP pool. See the documentation on <literal>php-fpm.conf</literal>
+        for details on configuration directives.
+      '';
+    };
+  };
+
+  config =
+    let
+      defaultServiceConfig = {
+        ReadWritePaths = "${cfg.dataDir}";
+        User = user;
+        UMask = 77;
+        DeviceAllow = "";
+        LockPersonality = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "~@resources"
+          "~@privileged"
+        ];
+        WorkingDirectory = "${cfg.package}/";
+      };
+    in
+    lib.mkIf cfg.enable {
+      assertions = [
+        {
+          assertion = db.createLocally -> db.urlFile == null;
+          message = "services.davis.database.urlFile must be unset if services.davis.database.createLocally is set true.";
+        }
+        {
+          assertion = db.createLocally || db.urlFile != null;
+          message = "One of services.davis.database.urlFile or services.davis.database.createLocally must be set.";
+        }
+        {
+          assertion = (mail.dsn != null) != (mail.dsnFile != null);
+          message = "One of (and only one of) services.davis.mail.dsn or services.davis.mail.dsnFile must be set.";
+        }
+      ];
+      services.davis.config =
+        {
+          APP_ENV = "prod";
+          APP_CACHE_DIR = "${cfg.dataDir}/var/cache";
+          # note: we do not need the log dir (we log to stdout/journald), by davis/symfony will try to create it, and the default value is one in the nix-store
+          #       so we set it to a path under dataDir to avoid something like: Unable to create the "logs" directory (/nix/store/5cfskz0ybbx37s1161gjn5klwb5si1zg-davis-4.4.1/var/log).
+          APP_LOG_DIR = "${cfg.dataDir}/var/log";
+          LOG_FILE_PATH = "/dev/stdout";
+          DATABASE_DRIVER = db.driver;
+          INVITE_FROM_ADDRESS = mail.inviteFromAddress;
+          APP_SECRET._secret = cfg.appSecretFile;
+          ADMIN_LOGIN = cfg.adminLogin;
+          ADMIN_PASSWORD._secret = cfg.adminPasswordFile;
+          APP_TIMEZONE = config.time.timeZone;
+          WEBDAV_ENABLED = false;
+          CALDAV_ENABLED = true;
+          CARDDAV_ENABLED = true;
+        }
+        // (if mail.dsn != null then { MAILER_DSN = mail.dsn; } else { MAILER_DSN._secret = mail.dsnFile; })
+        // (
+          if db.createLocally then
+            {
+              DATABASE_URL =
+                if db.driver == "sqlite" then
+                  "sqlite:///${cfg.dataDir}/davis.db" # note: sqlite needs 4 slashes for an absolute path
+                else if
+                  pgsqlLocal
+                # note: davis expects a non-standard postgres uri (due to the underlying doctrine library)
+                # specifically the dummy hostname which is overriden by the host query parameter
+                then
+                  "postgres://${user}@localhost/${db.name}?host=/run/postgresql"
+                else if mysqlLocal then
+                  "mysql://${user}@localhost/${db.name}?socket=/run/mysqld/mysqld.sock"
+                else
+                  null;
+            }
+          else
+            { DATABASE_URL._secret = db.urlFile; }
+        );
+
+      users = {
+        users = lib.mkIf (user == "davis") {
+          davis = {
+            description = "Davis service user";
+            group = cfg.group;
+            isSystemUser = true;
+            home = cfg.dataDir;
+          };
+        };
+        groups = lib.mkIf (group == "davis") { davis = { }; };
+      };
+
+      systemd.tmpfiles.rules = [
+        "d ${cfg.dataDir}                            0710 ${user} ${group} - -"
+        "d ${cfg.dataDir}/var                        0700 ${user} ${group} - -"
+        "d ${cfg.dataDir}/var/log                    0700 ${user} ${group} - -"
+        "d ${cfg.dataDir}/var/cache                  0700 ${user} ${group} - -"
+      ];
+
+      services.phpfpm.pools.davis = {
+        inherit user group;
+        phpOptions = ''
+          log_errors = on
+        '';
+        phpEnv = {
+          ENV_DIR = "${cfg.dataDir}";
+          APP_CACHE_DIR = "${cfg.dataDir}/var/cache";
+          APP_LOG_DIR = "${cfg.dataDir}/var/log";
+        };
+        settings =
+          {
+            "listen.mode" = "0660";
+            "pm" = "dynamic";
+            "pm.max_children" = 256;
+            "pm.start_servers" = 10;
+            "pm.min_spare_servers" = 5;
+            "pm.max_spare_servers" = 20;
+          }
+          // (
+            if cfg.nginx != null then
+              {
+                "listen.owner" = config.services.nginx.user;
+                "listen.group" = config.services.nginx.group;
+              }
+            else
+              { }
+          )
+          // cfg.poolConfig;
+      };
+
+      # Reading the user-provided secret files requires root access
+      systemd.services.davis-env-setup = {
+        description = "Setup davis environment";
+        before = [
+          "phpfpm-davis.service"
+          "davis-db-migrate.service"
+        ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+        };
+        path = [ pkgs.replace-secret ];
+        restartTriggers = [
+          cfg.package
+          davisEnv
+        ];
+        script = ''
+          # error handling
+          set -euo pipefail
+          # create .env file with the upstream values
+          install -T -m 0600 -o ${user} ${cfg.package}/env-upstream "${cfg.dataDir}/.env"
+          # create .env.local file with the user-provided values
+          install -T -m 0600 -o ${user} ${davisEnv} "${cfg.dataDir}/.env.local"
+          ${secretReplacements}
+        '';
+      };
+
+      systemd.services.davis-db-migrate = {
+        description = "Migrate davis database";
+        before = [ "phpfpm-davis.service" ];
+        after =
+          lib.optional mysqlLocal "mysql.service"
+          ++ lib.optional pgsqlLocal "postgresql.service"
+          ++ [ "davis-env-setup.service" ];
+        requires =
+          lib.optional mysqlLocal "mysql.service"
+          ++ lib.optional pgsqlLocal "postgresql.service"
+          ++ [ "davis-env-setup.service" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = defaultServiceConfig // {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          Environment = [
+            "ENV_DIR=${cfg.dataDir}"
+            "APP_CACHE_DIR=${cfg.dataDir}/var/cache"
+            "APP_LOG_DIR=${cfg.dataDir}/var/log"
+          ];
+          EnvironmentFile = "${cfg.dataDir}/.env.local";
+        };
+        restartTriggers = [
+          cfg.package
+          davisEnv
+        ];
+        script = ''
+          set -euo pipefail
+          ${cfg.package}/bin/console cache:clear --no-debug
+          ${cfg.package}/bin/console cache:warmup --no-debug
+          ${cfg.package}/bin/console doctrine:migrations:migrate
+        '';
+      };
+
+      systemd.services.phpfpm-davis.after = [
+        "davis-env-setup.service"
+        "davis-db-migrate.service"
+      ];
+      systemd.services.phpfpm-davis.requires = [
+        "davis-env-setup.service"
+        "davis-db-migrate.service"
+      ] ++ lib.optional mysqlLocal "mysql.service" ++ lib.optional pgsqlLocal "postgresql.service";
+      systemd.services.phpfpm-davis.serviceConfig.ReadWritePaths = [ cfg.dataDir ];
+
+      services.nginx = lib.mkIf (cfg.nginx != null) {
+        enable = lib.mkDefault true;
+        virtualHosts = {
+          "${cfg.hostname}" = lib.mkMerge [
+            cfg.nginx
+            {
+              root = lib.mkForce "${cfg.package}/public";
+              extraConfig = ''
+                charset utf-8;
+                index index.php;
+              '';
+              locations = {
+                "/" = {
+                  extraConfig = ''
+                    try_files $uri $uri/ /index.php$is_args$args;
+                  '';
+                };
+                "~* ^/.well-known/(caldav|carddav)$" = {
+                  extraConfig = ''
+                    return 302 $http_x_forwarded_proto://$host/dav/;
+                  '';
+                };
+                "~ ^(.+\.php)(.*)$" = {
+                  extraConfig = ''
+                    try_files                $fastcgi_script_name =404;
+                    include                  ${config.services.nginx.package}/conf/fastcgi_params;
+                    include                  ${config.services.nginx.package}/conf/fastcgi.conf;
+                    fastcgi_pass             unix:${config.services.phpfpm.pools.davis.socket};
+                    fastcgi_param            SCRIPT_FILENAME  $document_root$fastcgi_script_name;
+                    fastcgi_param            PATH_INFO        $fastcgi_path_info;
+                    fastcgi_split_path_info  ^(.+\.php)(.*)$;
+                    fastcgi_param            X-Forwarded-Proto $http_x_forwarded_proto;
+                    fastcgi_param            X-Forwarded-Port $http_x_forwarded_port;
+                  '';
+                };
+                "~ /(\\.ht)" = {
+                  extraConfig = ''
+                    deny all;
+                    return 404;
+                  '';
+                };
+              };
+            }
+          ];
+        };
+      };
+
+      services.mysql = lib.mkIf mysqlLocal {
+        enable = true;
+        package = lib.mkDefault pkgs.mariadb;
+        ensureDatabases = [ db.name ];
+        ensureUsers = [
+          {
+            name = user;
+            ensurePermissions = {
+              "${db.name}.*" = "ALL PRIVILEGES";
+            };
+          }
+        ];
+      };
+
+      services.postgresql = lib.mkIf pgsqlLocal {
+        enable = true;
+        ensureDatabases = [ db.name ];
+        ensureUsers = [
+          {
+            name = user;
+            ensureDBOwnership = true;
+          }
+        ];
+      };
+    };
+
+  meta = {
+    doc = ./davis.md;
+    maintainers = pkgs.davis.meta.maintainers;
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/dex.nix b/nixpkgs/nixos/modules/services/web-apps/dex.nix
index 0c4a71c6dfe4..7fbbd8a0c284 100644
--- a/nixpkgs/nixos/modules/services/web-apps/dex.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/dex.nix
@@ -19,12 +19,12 @@ let
 in
 {
   options.services.dex = {
-    enable = mkEnableOption (lib.mdDoc "the OpenID Connect and OAuth2 identity provider");
+    enable = mkEnableOption "the OpenID Connect and OAuth2 identity provider";
 
     environmentFile = mkOption {
       type = types.nullOr types.path;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         Environment file (see `systemd.exec(5)`
         "EnvironmentFile=" section for the syntax) to define variables for dex.
         This option can be used to safely include secret keys into the dex configuration.
@@ -56,7 +56,7 @@ in
           ];
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         The available options can be found in
         [the example configuration](https://github.com/dexidp/dex/blob/v${pkgs.dex-oidc.version}/config.yaml.dist).
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/discourse.nix b/nixpkgs/nixos/modules/services/web-apps/discourse.nix
index da1dba7d940b..849a03be8bc8 100644
--- a/nixpkgs/nixos/modules/services/web-apps/discourse.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/discourse.nix
@@ -26,7 +26,7 @@ in
 {
   options = {
     services.discourse = {
-      enable = lib.mkEnableOption (lib.mdDoc "Discourse, an open source discussion platform");
+      enable = lib.mkEnableOption "Discourse, an open source discussion platform";
 
       package = lib.mkOption {
         type = lib.types.package;
@@ -35,7 +35,7 @@ in
           plugins = lib.unique (p.enabledPlugins ++ cfg.plugins);
         };
         defaultText = lib.literalExpression "pkgs.discourse";
-        description = lib.mdDoc ''
+        description = ''
           The discourse package to use.
         '';
       };
@@ -45,7 +45,7 @@ in
         default = config.networking.fqdnOrHostName;
         defaultText = lib.literalExpression "config.networking.fqdnOrHostName";
         example = "discourse.example.com";
-        description = lib.mdDoc ''
+        description = ''
           The hostname to serve Discourse on.
         '';
       };
@@ -54,7 +54,7 @@ in
         type = with lib.types; nullOr path;
         default = null;
         example = "/run/keys/secret_key_base";
-        description = lib.mdDoc ''
+        description = ''
           The path to a file containing the
           `secret_key_base` secret.
 
@@ -78,7 +78,7 @@ in
         type = with lib.types; nullOr path;
         default = null;
         example = "/run/keys/ssl.cert";
-        description = lib.mdDoc ''
+        description = ''
           The path to the server SSL certificate. Set this to enable
           SSL.
         '';
@@ -88,7 +88,7 @@ in
         type = with lib.types; nullOr path;
         default = null;
         example = "/run/keys/ssl.key";
-        description = lib.mdDoc ''
+        description = ''
           The path to the server SSL certificate key. Set this to
           enable SSL.
         '';
@@ -101,7 +101,7 @@ in
           `true`, unless {option}`services.discourse.sslCertificate`
           and {option}`services.discourse.sslCertificateKey` are set.
         '';
-        description = lib.mdDoc ''
+        description = ''
           Whether an ACME certificate should be used to secure
           connections to the server.
         '';
@@ -118,7 +118,7 @@ in
             max_reqs_per_ip_mode = "warn+block";
           };
         '';
-        description = lib.mdDoc ''
+        description = ''
           Additional settings to put in the
           {file}`discourse.conf` file.
 
@@ -147,7 +147,7 @@ in
             };
           };
         '';
-        description = lib.mdDoc ''
+        description = ''
           Discourse site settings. These are the settings that can be
           changed from the UI. This only defines their default values:
           they can still be overridden from the UI.
@@ -175,7 +175,7 @@ in
         skipCreate = lib.mkOption {
           type = lib.types.bool;
           default = false;
-          description = lib.mdDoc ''
+          description = ''
             Do not create the admin account, instead rely on other
             existing admin accounts.
           '';
@@ -184,7 +184,7 @@ in
         email = lib.mkOption {
           type = lib.types.str;
           example = "admin@example.com";
-          description = lib.mdDoc ''
+          description = ''
             The admin user email address.
           '';
         };
@@ -192,21 +192,21 @@ in
         username = lib.mkOption {
           type = lib.types.str;
           example = "admin";
-          description = lib.mdDoc ''
+          description = ''
             The admin user username.
           '';
         };
 
         fullName = lib.mkOption {
           type = lib.types.str;
-          description = lib.mdDoc ''
+          description = ''
             The admin user's full name.
           '';
         };
 
         passwordFile = lib.mkOption {
           type = lib.types.path;
-          description = lib.mdDoc ''
+          description = ''
             A path to a file containing the admin user's password.
 
             This should be a string, not a nix path, since nix paths are
@@ -218,7 +218,7 @@ in
       nginx.enable = lib.mkOption {
         type = lib.types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether an `nginx` virtual host should be
           set up to serve Discourse. Only disable if you're planning
           to use a different web server, which is not recommended.
@@ -229,7 +229,7 @@ in
         pool = lib.mkOption {
           type = lib.types.int;
           default = 8;
-          description = lib.mdDoc ''
+          description = ''
             Database connection pool size.
           '';
         };
@@ -237,7 +237,7 @@ in
         host = lib.mkOption {
           type = with lib.types; nullOr str;
           default = null;
-          description = lib.mdDoc ''
+          description = ''
             Discourse database hostname. `null` means
             “prefer local unix socket connection”.
           '';
@@ -246,7 +246,7 @@ in
         passwordFile = lib.mkOption {
           type = with lib.types; nullOr path;
           default = null;
-          description = lib.mdDoc ''
+          description = ''
             File containing the Discourse database user password.
 
             This should be a string, not a nix path, since nix paths are
@@ -257,7 +257,7 @@ in
         createLocally = lib.mkOption {
           type = lib.types.bool;
           default = true;
-          description = lib.mdDoc ''
+          description = ''
             Whether a database should be automatically created on the
             local host. Set this to `false` if you plan
             on provisioning a local database yourself. This has no effect
@@ -268,7 +268,7 @@ in
         name = lib.mkOption {
           type = lib.types.str;
           default = "discourse";
-          description = lib.mdDoc ''
+          description = ''
             Discourse database name.
           '';
         };
@@ -276,7 +276,7 @@ in
         username = lib.mkOption {
           type = lib.types.str;
           default = "discourse";
-          description = lib.mdDoc ''
+          description = ''
             Discourse database user.
           '';
         };
@@ -284,7 +284,7 @@ in
         ignorePostgresqlVersion = lib.mkOption {
           type = lib.types.bool;
           default = false;
-          description = lib.mdDoc ''
+          description = ''
             Whether to allow other versions of PostgreSQL than the
             recommended one. Only effective when
             {option}`services.discourse.database.createLocally`
@@ -297,7 +297,7 @@ in
         host = lib.mkOption {
           type = lib.types.str;
           default = "localhost";
-          description = lib.mdDoc ''
+          description = ''
             Redis server hostname.
           '';
         };
@@ -305,7 +305,7 @@ in
         passwordFile = lib.mkOption {
           type = with lib.types; nullOr path;
           default = null;
-          description = lib.mdDoc ''
+          description = ''
             File containing the Redis password.
 
             This should be a string, not a nix path, since nix paths are
@@ -316,7 +316,7 @@ in
         dbNumber = lib.mkOption {
           type = lib.types.int;
           default = 0;
-          description = lib.mdDoc ''
+          description = ''
             Redis database number.
           '';
         };
@@ -325,7 +325,7 @@ in
           type = lib.types.bool;
           default = cfg.redis.host != "localhost";
           defaultText = lib.literalExpression ''config.${opt.redis.host} != "localhost"'';
-          description = lib.mdDoc ''
+          description = ''
             Connect to Redis with SSL.
           '';
         };
@@ -338,7 +338,7 @@ in
           defaultText = lib.literalExpression ''
             "''${if config.services.discourse.mail.incoming.enable then "notifications" else "noreply"}@''${config.services.discourse.hostname}"
           '';
-          description = lib.mdDoc ''
+          description = ''
             The `from:` email address used when
             sending all essential system emails. The domain specified
             here must have SPF, DKIM and reverse PTR records set
@@ -349,7 +349,7 @@ in
         contactEmailAddress = lib.mkOption {
           type = lib.types.str;
           default = "";
-          description = lib.mdDoc ''
+          description = ''
             Email address of key contact responsible for this
             site. Used for critical notifications, as well as on the
             `/about` contact form for urgent matters.
@@ -360,7 +360,7 @@ in
           serverAddress = lib.mkOption {
             type = lib.types.str;
             default = "localhost";
-            description = lib.mdDoc ''
+            description = ''
               The address of the SMTP server Discourse should use to
               send email.
             '';
@@ -369,7 +369,7 @@ in
           port = lib.mkOption {
             type = lib.types.port;
             default = 25;
-            description = lib.mdDoc ''
+            description = ''
               The port of the SMTP server Discourse should use to
               send email.
             '';
@@ -378,7 +378,7 @@ in
           username = lib.mkOption {
             type = with lib.types; nullOr str;
             default = null;
-            description = lib.mdDoc ''
+            description = ''
               The username of the SMTP server.
             '';
           };
@@ -386,7 +386,7 @@ in
           passwordFile = lib.mkOption {
             type = lib.types.nullOr lib.types.path;
             default = null;
-            description = lib.mdDoc ''
+            description = ''
               A file containing the password of the SMTP server account.
 
               This should be a string, not a nix path, since nix paths
@@ -398,7 +398,7 @@ in
             type = lib.types.str;
             default = cfg.hostname;
             defaultText = lib.literalExpression "config.${opt.hostname}";
-            description = lib.mdDoc ''
+            description = ''
               HELO domain to use for outgoing mail.
             '';
           };
@@ -406,7 +406,7 @@ in
           authentication = lib.mkOption {
             type = with lib.types; nullOr (enum ["plain" "login" "cram_md5"]);
             default = null;
-            description = lib.mdDoc ''
+            description = ''
               Authentication type to use, see https://api.rubyonrails.org/classes/ActionMailer/Base.html
             '';
           };
@@ -414,7 +414,7 @@ in
           enableStartTLSAuto = lib.mkOption {
             type = lib.types.bool;
             default = true;
-            description = lib.mdDoc ''
+            description = ''
               Whether to try to use StartTLS.
             '';
           };
@@ -422,7 +422,7 @@ in
           opensslVerifyMode = lib.mkOption {
             type = lib.types.str;
             default = "peer";
-            description = lib.mdDoc ''
+            description = ''
               How OpenSSL checks the certificate, see https://api.rubyonrails.org/classes/ActionMailer/Base.html
             '';
           };
@@ -430,7 +430,7 @@ in
           forceTLS = lib.mkOption {
             type = lib.types.bool;
             default = false;
-            description = lib.mdDoc ''
+            description = ''
               Force implicit TLS as per RFC 8314 3.3.
             '';
           };
@@ -440,7 +440,7 @@ in
           enable = lib.mkOption {
             type = lib.types.bool;
             default = false;
-            description = lib.mdDoc ''
+            description = ''
               Whether to set up Postfix to receive incoming mail.
             '';
           };
@@ -449,7 +449,7 @@ in
             type = lib.types.str;
             default = "%{reply_key}@${cfg.hostname}";
             defaultText = lib.literalExpression ''"%{reply_key}@''${config.services.discourse.hostname}"'';
-            description = lib.mdDoc ''
+            description = ''
               Template for reply by email incoming email address, for
               example: %{reply_key}@reply.example.com or
               replies+%{reply_key}@example.com
@@ -460,7 +460,7 @@ in
             type = lib.types.package;
             default = pkgs.discourse-mail-receiver;
             defaultText = lib.literalExpression "pkgs.discourse-mail-receiver";
-            description = lib.mdDoc ''
+            description = ''
               The discourse-mail-receiver package to use.
             '';
           };
@@ -468,7 +468,7 @@ in
           apiKeyFile = lib.mkOption {
             type = lib.types.nullOr lib.types.path;
             default = null;
-            description = lib.mdDoc ''
+            description = ''
               A file containing the Discourse API key used to add
               posts and messages from mail. If left at its default
               value `null`, one will be automatically
@@ -490,7 +490,7 @@ in
             discourse-github
           ];
         '';
-        description = lib.mdDoc ''
+        description = ''
           Plugins to install as part of Discourse, expressed as a list of derivations.
         '';
       };
@@ -498,7 +498,7 @@ in
       sidekiqProcesses = lib.mkOption {
         type = lib.types.int;
         default = 1;
-        description = lib.mdDoc ''
+        description = ''
           How many Sidekiq processes should be spawned.
         '';
       };
@@ -506,7 +506,7 @@ in
       unicornTimeout = lib.mkOption {
         type = lib.types.int;
         default = 30;
-        description = lib.mdDoc ''
+        description = ''
           Time in seconds before a request to Unicorn times out.
 
           This can be raised if the system Discourse is running on is
diff --git a/nixpkgs/nixos/modules/services/web-apps/documize.nix b/nixpkgs/nixos/modules/services/web-apps/documize.nix
index 6f88b3f3c6d2..ab6d08f05992 100644
--- a/nixpkgs/nixos/modules/services/web-apps/documize.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/documize.nix
@@ -12,12 +12,12 @@ let
 
 in {
   options.services.documize = {
-    enable = mkEnableOption (lib.mdDoc "Documize Wiki");
+    enable = mkEnableOption "Documize Wiki";
 
     stateDirectoryName = mkOption {
       type = types.str;
       default = "documize";
-      description = lib.mdDoc ''
+      description = ''
         The name of the directory below {file}`/var/lib/private`
         where documize runs in and stores, for example, backups.
       '';
@@ -29,7 +29,7 @@ in {
       type = types.nullOr types.str;
       default = null;
       example = "3edIYV6c8B28b19fh";
-      description = lib.mdDoc ''
+      description = ''
         The salt string used to encode JWT tokens, if not set a random value will be generated.
       '';
     };
@@ -37,7 +37,7 @@ in {
     cert = mkOption {
       type = types.nullOr types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         The {file}`cert.pem` file used for https.
       '';
     };
@@ -45,7 +45,7 @@ in {
     key = mkOption {
       type = types.nullOr types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         The {file}`key.pem` file used for https.
       '';
     };
@@ -53,7 +53,7 @@ in {
     port = mkOption {
       type = types.port;
       default = 5001;
-      description = lib.mdDoc ''
+      description = ''
         The http/https port number.
       '';
     };
@@ -61,7 +61,7 @@ in {
     forcesslport = mkOption {
       type = types.nullOr types.port;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         Redirect given http port number to TLS.
       '';
     };
@@ -69,7 +69,7 @@ in {
     offline = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Set `true` for offline mode.
       '';
       apply = v: if true == v then 1 else 0;
@@ -78,14 +78,14 @@ in {
     dbtype = mkOption {
       type = types.enum [ "mysql" "percona" "mariadb" "postgresql" "sqlserver" ];
       default = "postgresql";
-      description = lib.mdDoc ''
+      description = ''
         Specify the database provider: `mysql`, `percona`, `mariadb`, `postgresql`, `sqlserver`
       '';
     };
 
     db = mkOption {
       type = types.str;
-      description = lib.mdDoc ''
+      description = ''
         Database specific connection string for example:
         - MySQL/Percona/MariaDB:
           `user:password@tcp(host:3306)/documize`
@@ -102,7 +102,7 @@ in {
     location = mkOption {
       type = types.nullOr types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         reserved
       '';
     };
diff --git a/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix b/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix
index 256ab3229ea6..c5ea809c8d59 100644
--- a/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix
@@ -91,13 +91,13 @@ let
 
       page = mkOption {
         type = types.str;
-        description = lib.mdDoc "Page or namespace to restrict";
+        description = "Page or namespace to restrict";
         example = "start";
       };
 
       actor = mkOption {
         type = types.str;
-        description = lib.mdDoc "User or group to restrict";
+        description = "User or group to restrict";
         example = "@external";
       };
 
@@ -113,7 +113,7 @@ let
       in mkOption {
         type = types.enum ((attrValues available) ++ (attrNames available));
         apply = x: if isInt x then x else available.${x};
-        description = lib.mdDoc ''
+        description = ''
           Permission level to restrict the actor(s) to.
           See <https://www.dokuwiki.org/acl#background_info> for explanation
         '';
@@ -126,14 +126,14 @@ let
     {
 
       options = {
-        enable = mkEnableOption (lib.mdDoc "DokuWiki web application");
+        enable = mkEnableOption "DokuWiki web application";
 
         package = mkPackageOption pkgs "dokuwiki" { };
 
         stateDir = mkOption {
           type = types.path;
           default = "/var/lib/dokuwiki/${name}/data";
-          description = lib.mdDoc "Location of the DokuWiki state directory.";
+          description = "Location of the DokuWiki state directory.";
         };
 
         acl = mkOption {
@@ -153,7 +153,7 @@ let
               }
             ]
           '';
-          description = lib.mdDoc ''
+          description = ''
             Access Control Lists: see <https://www.dokuwiki.org/acl>
             Mutually exclusive with services.dokuwiki.aclFile
             Set this to a value other than null to take precedence over aclFile option.
@@ -166,7 +166,7 @@ let
         aclFile = mkOption {
           type = with types; nullOr str;
           default = if (config.mergedConfig.useacl && config.acl == null) then "/var/lib/dokuwiki/${name}/acl.auth.php" else null;
-          description = lib.mdDoc ''
+          description = ''
             Location of the dokuwiki acl rules. Mutually exclusive with services.dokuwiki.acl
             Mutually exclusive with services.dokuwiki.acl which is preferred.
             Consult documentation <https://www.dokuwiki.org/acl> for further instructions.
@@ -183,7 +183,7 @@ let
             authmysql = false;
             authpgsql = false;
           };
-          description = lib.mdDoc ''
+          description = ''
             List of the dokuwiki (un)loaded plugins.
           '';
         };
@@ -191,7 +191,7 @@ let
         usersFile = mkOption {
           type = with types; nullOr str;
           default = if config.mergedConfig.useacl then "/var/lib/dokuwiki/${name}/users.auth.php" else null;
-          description = lib.mdDoc ''
+          description = ''
             Location of the dokuwiki users file. List of users. Format:
 
                 login:passwordhash:Real Name:email:groups,comma,separated
@@ -208,7 +208,7 @@ let
         plugins = mkOption {
           type = types.listOf types.path;
           default = [];
-          description = lib.mdDoc ''
+          description = ''
                 List of path(s) to respective plugin(s) which are copied from the 'plugin' directory.
 
                 ::: {.note}
@@ -235,7 +235,7 @@ let
         templates = mkOption {
           type = types.listOf types.path;
           default = [];
-          description = lib.mdDoc ''
+          description = ''
                 List of path(s) to respective template(s) which are copied from the 'tpl' directory.
 
                 ::: {.note}
@@ -270,7 +270,7 @@ let
             "pm.max_spare_servers" = 4;
             "pm.max_requests" = 500;
           };
-          description = lib.mdDoc ''
+          description = ''
             Options for the DokuWiki PHP pool. See the documentation on `php-fpm.conf`
             for details on configuration directives.
           '';
@@ -284,7 +284,7 @@ let
         phpOptions = mkOption {
           type = types.attrsOf types.str;
           default = {};
-          description = lib.mdDoc ''
+          description = ''
             Options for PHP's php.ini file for this dokuwiki site.
           '';
           example = literalExpression ''
@@ -304,7 +304,7 @@ let
             useacl = true;
             superuser = "admin";
           };
-          description = lib.mdDoc ''
+          description = ''
             Structural DokuWiki configuration.
             Refer to <https://www.dokuwiki.org/config>
             for details and supported values.
@@ -333,7 +333,7 @@ let
               useacl = true;
             }
           '';
-          description = lib.mdDoc ''
+          description = ''
             Read only representation of the final configuration.
           '';
         };
@@ -348,13 +348,13 @@ in
       sites = mkOption {
         type = types.attrsOf (types.submodule siteOpts);
         default = {};
-        description = lib.mdDoc "Specification of one or more DokuWiki sites to serve";
+        description = "Specification of one or more DokuWiki sites to serve";
       };
 
       webserver = mkOption {
         type = types.enum [ "nginx" "caddy" ];
         default = "nginx";
-        description = lib.mdDoc ''
+        description = ''
           Whether to use nginx or caddy for virtual host management.
 
           Further nginx configuration can be done by adapting `services.nginx.virtualHosts.<name>`.
diff --git a/nixpkgs/nixos/modules/services/web-apps/dolibarr.nix b/nixpkgs/nixos/modules/services/web-apps/dolibarr.nix
index 193be47ab9b2..3f9f853e3b25 100644
--- a/nixpkgs/nixos/modules/services/web-apps/dolibarr.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/dolibarr.nix
@@ -48,14 +48,14 @@ in
 {
   # interface
   options.services.dolibarr = {
-    enable = mkEnableOption (lib.mdDoc "dolibarr");
+    enable = mkEnableOption "dolibarr";
 
     package = mkPackageOption pkgs "dolibarr" { };
 
     domain = mkOption {
       type = types.str;
       default = "localhost";
-      description = lib.mdDoc ''
+      description = ''
         Domain name of your server.
       '';
     };
@@ -63,7 +63,7 @@ in
     user = mkOption {
       type = types.str;
       default = "dolibarr";
-      description = lib.mdDoc ''
+      description = ''
         User account under which dolibarr runs.
 
         ::: {.note}
@@ -77,7 +77,7 @@ in
     group = mkOption {
       type = types.str;
       default = "dolibarr";
-      description = lib.mdDoc ''
+      description = ''
         Group account under which dolibarr runs.
 
         ::: {.note}
@@ -91,7 +91,7 @@ in
     stateDir = mkOption {
       type = types.str;
       default = "/var/lib/dolibarr";
-      description = lib.mdDoc ''
+      description = ''
         State and configuration directory dolibarr will use.
       '';
     };
@@ -100,40 +100,40 @@ in
       host = mkOption {
         type = types.str;
         default = "localhost";
-        description = lib.mdDoc "Database host address.";
+        description = "Database host address.";
       };
       port = mkOption {
         type = types.port;
         default = 3306;
-        description = lib.mdDoc "Database host port.";
+        description = "Database host port.";
       };
       name = mkOption {
         type = types.str;
         default = "dolibarr";
-        description = lib.mdDoc "Database name.";
+        description = "Database name.";
       };
       user = mkOption {
         type = types.str;
         default = "dolibarr";
-        description = lib.mdDoc "Database username.";
+        description = "Database username.";
       };
       passwordFile = mkOption {
         type = with types; nullOr path;
         default = null;
         example = "/run/keys/dolibarr-dbpassword";
-        description = lib.mdDoc "Database password file.";
+        description = "Database password file.";
       };
       createLocally = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc "Create the database and database user locally.";
+        description = "Create the database and database user locally.";
       };
     };
 
     settings = mkOption {
       type = with types; (attrsOf (oneOf [ bool int str ]));
       default = { };
-      description = lib.mdDoc "Dolibarr settings, see <https://github.com/Dolibarr/dolibarr/blob/develop/htdocs/conf/conf.php.example> for details.";
+      description = "Dolibarr settings, see <https://github.com/Dolibarr/dolibarr/blob/develop/htdocs/conf/conf.php.example> for details.";
     };
 
     nginx = mkOption {
@@ -157,7 +157,7 @@ in
           enableACME = false;
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
           With this option, you can customize an nginx virtual host which already has sensible defaults for Dolibarr.
           Set to {} if you do not need any customization to the virtual host.
           If enabled, then by default, the {option}`serverName` is
@@ -177,7 +177,7 @@ in
         "pm.max_spare_servers" = 4;
         "pm.max_requests" = 500;
       };
-      description = lib.mdDoc ''
+      description = ''
         Options for the Dolibarr PHP pool. See the documentation on [`php-fpm.conf`](https://www.php.net/manual/en/install.fpm.configuration.php)
         for details on configuration directives.
       '';
diff --git a/nixpkgs/nixos/modules/services/web-apps/engelsystem.nix b/nixpkgs/nixos/modules/services/web-apps/engelsystem.nix
index ae7b2b9e7d0c..fe815f0a9742 100644
--- a/nixpkgs/nixos/modules/services/web-apps/engelsystem.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/engelsystem.nix
@@ -9,7 +9,7 @@ in {
       enable = mkOption {
         default = false;
         example = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether to enable engelsystem, an online tool for coordinating volunteers
           and shifts on large events.
         '';
@@ -19,7 +19,7 @@ in {
       domain = mkOption {
         type = types.str;
         example = "engelsystem.example.com";
-        description = lib.mdDoc "Domain to serve on.";
+        description = "Domain to serve on.";
       };
 
       package = mkPackageOption pkgs "engelsystem" { };
@@ -27,7 +27,7 @@ in {
       createDatabase = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether to create a local database automatically.
           This will override every database setting in {option}`services.engelsystem.config`.
         '';
@@ -65,7 +65,7 @@ in {
         min_password_length = 6;
         default_locale = "de_DE";
       };
-      description = lib.mdDoc ''
+      description = ''
         Options to be added to config.php, as a nix attribute set. Options containing secret data
         should be set to an attribute set containing the attribute _secret - a string pointing to a
         file containing the value the option should be set to. See the example to get a better
diff --git a/nixpkgs/nixos/modules/services/web-apps/ethercalc.nix b/nixpkgs/nixos/modules/services/web-apps/ethercalc.nix
index a38e89ec0de9..fd6c6e05a872 100644
--- a/nixpkgs/nixos/modules/services/web-apps/ethercalc.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/ethercalc.nix
@@ -10,7 +10,7 @@ in {
       enable = mkOption {
         default = false;
         type = types.bool;
-        description = lib.mdDoc ''
+        description = ''
           ethercalc, an online collaborative spreadsheet server.
 
           Persistent state will be maintained under
@@ -29,13 +29,13 @@ in {
       host = mkOption {
         type = types.str;
         default = "0.0.0.0";
-        description = lib.mdDoc "Address to listen on (use 0.0.0.0 to allow access from any address).";
+        description = "Address to listen on (use 0.0.0.0 to allow access from any address).";
       };
 
       port = mkOption {
         type = types.port;
         default = 8000;
-        description = lib.mdDoc "Port to bind to.";
+        description = "Port to bind to.";
       };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/firefly-iii.nix b/nixpkgs/nixos/modules/services/web-apps/firefly-iii.nix
new file mode 100644
index 000000000000..b0024ce09c38
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/firefly-iii.nix
@@ -0,0 +1,367 @@
+{ pkgs, config, lib, ... }:
+
+let
+  inherit (lib) optionalString mkDefault mkIf mkOption mkEnableOption literalExpression;
+  inherit (lib.types) nullOr attrsOf oneOf str int bool path package enum submodule;
+  inherit (lib.strings) concatMapStringsSep removePrefix toShellVars removeSuffix hasSuffix;
+  inherit (lib.attrsets) attrValues genAttrs filterAttrs mapAttrs' nameValuePair;
+  inherit (builtins) isInt isString toString typeOf;
+
+  cfg = config.services.firefly-iii;
+
+  user = cfg.user;
+  group = cfg.group;
+
+  defaultUser = "firefly-iii";
+  defaultGroup = "firefly-iii";
+
+  artisan = "${cfg.package}/artisan";
+
+  env-file-values = mapAttrs' (n: v: nameValuePair (removeSuffix "_FILE" n) v)
+    (filterAttrs (n: v: hasSuffix "_FILE" n) cfg.settings);
+  env-nonfile-values = filterAttrs (n: v: ! hasSuffix "_FILE" n) cfg.settings;
+
+  envfile = pkgs.writeText "firefly-iii-env" ''
+    ${toShellVars env-file-values}
+    ${toShellVars env-nonfile-values}
+  '';
+
+  fileenv-func = ''
+    cp --no-preserve=mode ${envfile} /tmp/firefly-iii-env
+    ${concatMapStringsSep "\n"
+      (n: "${pkgs.replace-secret}/bin/replace-secret ${n} ${n} /tmp/firefly-iii-env")
+      (attrValues env-file-values)}
+    set -a
+    . /tmp/firefly-iii-env
+    set +a
+  '';
+
+  firefly-iii-maintenance = pkgs.writeShellScript "firefly-iii-maintenance.sh" ''
+    ${fileenv-func}
+
+    ${optionalString (cfg.settings.DB_CONNECTION == "sqlite")
+      "touch ${cfg.dataDir}/storage/database/database.sqlite"}
+    ${artisan} migrate --seed --no-interaction --force
+    ${artisan} firefly-iii:decrypt-all
+    ${artisan} firefly-iii:upgrade-database
+    ${artisan} firefly-iii:correct-database
+    ${artisan} firefly-iii:report-integrity
+    ${artisan} firefly-iii:laravel-passport-keys
+    ${artisan} cache:clear
+
+    mv /tmp/firefly-iii-env /run/phpfpm/firefly-iii-env
+  '';
+
+  commonServiceConfig = {
+    Type = "oneshot";
+    User = user;
+    Group = group;
+    StateDirectory = "${removePrefix "/var/lib/" cfg.dataDir}";
+    WorkingDirectory = cfg.package;
+    PrivateTmp = true;
+    PrivateDevices = true;
+    CapabilityBoundingSet = "";
+    AmbientCapabilities = "";
+    ProtectSystem = "strict";
+    ProtectKernelTunables = true;
+    ProtectKernelModules = true;
+    ProtectControlGroups = true;
+    ProtectClock = true;
+    ProtectHostname = true;
+    ProtectHome = "tmpfs";
+    ProtectKernelLogs = true;
+    ProtectProc = "invisible";
+    ProcSubset = "pid";
+    PrivateNetwork = false;
+    RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX";
+    SystemCallArchitectures = "native";
+    SystemCallFilter = [
+      "@system-service @resources"
+      "~@obsolete @privileged"
+    ];
+    RestrictSUIDSGID = true;
+    RemoveIPC = true;
+    NoNewPrivileges = true;
+    RestrictRealtime = true;
+    RestrictNamespaces = true;
+    LockPersonality = true;
+    PrivateUsers = true;
+  };
+
+in {
+
+  options.services.firefly-iii = {
+
+    enable = mkEnableOption "Firefly III: A free and open source personal finance manager";
+
+    user = mkOption {
+      type = str;
+      default = defaultUser;
+      description = "User account under which firefly-iii runs.";
+    };
+
+    group = mkOption {
+      type = str;
+      default = if cfg.enableNginx then "nginx" else defaultGroup;
+      defaultText = "If `services.firefly-iii.enableNginx` is true then `nginx` else ${defaultGroup}";
+      description = ''
+        Group under which firefly-iii runs. It is best to set this to the group
+        of whatever webserver is being used as the frontend.
+      '';
+    };
+
+    dataDir = mkOption {
+      type = path;
+      default = "/var/lib/firefly-iii";
+      description = ''
+        The place where firefly-iii stores its state.
+      '';
+    };
+
+    package = mkOption {
+      type = package;
+      default = pkgs.firefly-iii;
+      defaultText = literalExpression "pkgs.firefly-iii";
+      description = ''
+        The firefly-iii package served by php-fpm and the webserver of choice.
+        This option can be used to point the webserver to the correct root. It
+        may also be used to set the package to a different version, say a
+        development version.
+      '';
+      apply = firefly-iii : firefly-iii.override (prev: {
+        dataDir = cfg.dataDir;
+      });
+    };
+
+    enableNginx = mkOption {
+      type = bool;
+      default = false;
+      description = ''
+        Whether to enable nginx or not. If enabled, an nginx virtual host will
+        be created for access to firefly-iii. If not enabled, then you may use
+        `''${config.services.firefly-iii.package}` as your document root in
+        whichever webserver you wish to setup.
+      '';
+    };
+
+    virtualHost = mkOption {
+      type = str;
+      description = ''
+        The hostname at which you wish firefly-iii to be served. If you have
+        enabled nginx using `services.firefly-iii.enableNginx` then this will
+        be used.
+      '';
+    };
+
+    poolConfig = mkOption {
+      type = attrsOf (oneOf [ str int bool ]);
+      default = {
+        "pm" = "dynamic";
+        "pm.max_children" = 32;
+        "pm.start_servers" = 2;
+        "pm.min_spare_servers" = 2;
+        "pm.max_spare_servers" = 4;
+        "pm.max_requests" = 500;
+      };
+      description = ''
+        Options for the Firefly III PHP pool. See the documentation on <literal>php-fpm.conf</literal>
+        for details on configuration directives.
+      '';
+    };
+
+    settings = mkOption {
+      description = ''
+        Options for firefly-iii configuration. Refer to
+        <https://github.com/firefly-iii/firefly-iii/blob/main/.env.example> for
+        details on supported values. All <option>_FILE values supported by
+        upstream are supported here.
+
+        APP_URL will be set by `services.firefly-iii.virtualHost`, do not
+        redefine it here.
+      '';
+      example = literalExpression ''
+        {
+          APP_ENV = "production";
+          APP_KEY_FILE = "/var/secrets/firefly-iii-app-key.txt";
+          SITE_OWNER = "mail@example.com";
+          DB_CONNECTION = "mysql";
+          DB_HOST = "db";
+          DB_PORT = 3306;
+          DB_DATABASE = "firefly";
+          DB_USERNAME = "firefly";
+          DB_PASSWORD_FILE = "/var/secrets/firefly-iii-mysql-password.txt;
+        }
+      '';
+      default = {};
+      type = submodule {
+        freeformType = attrsOf (oneOf [str int bool]);
+        options = {
+          DB_CONNECTION = mkOption {
+            type = enum [ "sqlite" "pgsql" "mysql" ];
+            default = "sqlite";
+            example = "pgsql";
+            description = ''
+              The type of database you wish to use. Can be one of "sqlite",
+              "mysql" or "pgsql".
+            '';
+          };
+          APP_ENV = mkOption {
+            type = enum [ "local" "production" "testing" ];
+            default = "local";
+            example = "production";
+            description = ''
+              The app environment. It is recommended to keep this at "local".
+              Possible values are "local", "production" and "testing"
+            '';
+          };
+          DB_PORT = mkOption {
+            type = nullOr int;
+            default = if cfg.settings.DB_CONNECTION == "sqlite" then null
+                      else if cfg.settings.DB_CONNECTION == "mysql" then 3306
+                      else 5432;
+            defaultText = ''
+              `null` if DB_CONNECTION is "sqlite", `3306` if "mysql", `5432` if "pgsql"
+            '';
+            description = ''
+              The port your database is listening at. sqlite does not require
+              this value to be filled.
+            '';
+          };
+          APP_KEY_FILE = mkOption {
+            type = path;
+            description = ''
+              The path to your appkey. The file should contain a 32 character
+              random app key. This may be set using `echo "base64:$(head -c 32
+              /dev/urandom | base64)" > /path/to/key-file`.
+            '';
+          };
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    services.firefly-iii = {
+      settings = {
+        APP_URL = cfg.virtualHost;
+      };
+    };
+
+    services.phpfpm.pools.firefly-iii = {
+      inherit user group;
+      phpPackage = cfg.package.phpPackage;
+      phpOptions = ''
+        log_errors = on
+      '';
+      settings = {
+        "listen.mode" = "0660";
+        "listen.owner" = user;
+        "listen.group" = group;
+        "clear_env" = "no";
+      } // cfg.poolConfig;
+    };
+
+    systemd.services.phpfpm-firefly-iii.serviceConfig = {
+      EnvironmentFile = "/run/phpfpm/firefly-iii-env";
+      ExecStartPost = "${pkgs.coreutils}/bin/rm /run/phpfpm/firefly-iii-env";
+    };
+
+    systemd.services.firefly-iii-setup = {
+      requiredBy = [ "phpfpm-firefly-iii.service" ];
+      before = [ "phpfpm-firefly-iii.service" ];
+      serviceConfig = {
+        ExecStart = firefly-iii-maintenance;
+        RuntimeDirectory = "phpfpm";
+        RuntimeDirectoryPreserve = true;
+      } // commonServiceConfig;
+      unitConfig.JoinsNamespaceOf = "phpfpm-firefly-iii.service";
+    };
+
+    systemd.services.firefly-iii-cron = {
+      description = "Daily Firefly III cron job";
+      script = ''
+        ${fileenv-func}
+        ${artisan} firefly-iii:cron
+      '';
+      serviceConfig = commonServiceConfig;
+    };
+
+    systemd.timers.firefly-iii-cron = {
+      description = "Trigger Firefly Cron";
+      timerConfig = {
+        OnCalendar = "Daily";
+        RandomizedDelaySec = "1800s";
+        Persistent = true;
+      };
+      wantedBy = [ "timers.target" ];
+    };
+
+    services.nginx = mkIf cfg.enableNginx {
+      enable = true;
+      recommendedTlsSettings = mkDefault true;
+      recommendedOptimisation = mkDefault true;
+      recommendedGzipSettings = mkDefault true;
+      virtualHosts.${cfg.virtualHost} = {
+        root = "${cfg.package}/public";
+        locations = {
+          "/" = {
+            tryFiles = "$uri $uri/ /index.php?$query_string";
+            index = "index.php";
+            extraConfig = ''
+              sendfile off;
+            '';
+          };
+          "~ \.php$" = {
+            extraConfig = ''
+              include ${config.services.nginx.package}/conf/fastcgi_params ;
+              fastcgi_param SCRIPT_FILENAME $request_filename;
+              fastcgi_param modHeadersAvailable true; #Avoid sending the security headers twice
+              fastcgi_pass unix:${config.services.phpfpm.pools.firefly-iii.socket};
+            '';
+          };
+        };
+      };
+    };
+
+    systemd.tmpfiles.settings."10-firefly-iii" = genAttrs [
+      "${cfg.dataDir}/storage"
+      "${cfg.dataDir}/storage/app"
+      "${cfg.dataDir}/storage/database"
+      "${cfg.dataDir}/storage/export"
+      "${cfg.dataDir}/storage/framework"
+      "${cfg.dataDir}/storage/framework/cache"
+      "${cfg.dataDir}/storage/framework/sessions"
+      "${cfg.dataDir}/storage/framework/views"
+      "${cfg.dataDir}/storage/logs"
+      "${cfg.dataDir}/storage/upload"
+      "${cfg.dataDir}/cache"
+    ] (n: {
+      d = {
+        group = group;
+        mode = "0700";
+        user = user;
+      };
+    }) // {
+      "${cfg.dataDir}".d = {
+        group = group;
+        mode = "0710";
+        user = user;
+      };
+    };
+
+    users = {
+      users = mkIf (user == defaultUser) {
+        ${defaultUser} = {
+          description = "Firefly-iii service user";
+          inherit group;
+          isSystemUser = true;
+          home = cfg.dataDir;
+        };
+      };
+      groups = mkIf (group == defaultGroup) {
+        ${defaultGroup} = {};
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/fluidd.nix b/nixpkgs/nixos/modules/services/web-apps/fluidd.nix
index 1d9b56f5ccf2..f30127dd17ad 100644
--- a/nixpkgs/nixos/modules/services/web-apps/fluidd.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/fluidd.nix
@@ -6,14 +6,14 @@ let
 in
 {
   options.services.fluidd = {
-    enable = mkEnableOption (lib.mdDoc "Fluidd, a Klipper web interface for managing your 3d printer");
+    enable = mkEnableOption "Fluidd, a Klipper web interface for managing your 3d printer";
 
     package = mkPackageOption pkgs "fluidd" { };
 
     hostName = mkOption {
       type = types.str;
       default = "localhost";
-      description = lib.mdDoc "Hostname to serve fluidd on";
+      description = "Hostname to serve fluidd on";
     };
 
     nginx = mkOption {
@@ -25,7 +25,7 @@ in
           serverAliases = [ "fluidd.''${config.networking.domain}" ];
         }
       '';
-      description = lib.mdDoc "Extra configuration for the nginx virtual host of fluidd.";
+      description = "Extra configuration for the nginx virtual host of fluidd.";
     };
   };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/freshrss.nix b/nixpkgs/nixos/modules/services/web-apps/freshrss.nix
index edec9d547a30..77c5ecb24617 100644
--- a/nixpkgs/nixos/modules/services/web-apps/freshrss.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/freshrss.nix
@@ -10,34 +10,34 @@ in
   meta.maintainers = with maintainers; [ etu stunkymonkey mattchrist ];
 
   options.services.freshrss = {
-    enable = mkEnableOption (mdDoc "FreshRSS feed reader");
+    enable = mkEnableOption "FreshRSS feed reader";
 
     package = mkPackageOption pkgs "freshrss" { };
 
     defaultUser = mkOption {
       type = types.str;
       default = "admin";
-      description = mdDoc "Default username for FreshRSS.";
+      description = "Default username for FreshRSS.";
       example = "eva";
     };
 
     passwordFile = mkOption {
       type = types.nullOr types.path;
       default = null;
-      description = mdDoc "Password for the defaultUser for FreshRSS.";
+      description = "Password for the defaultUser for FreshRSS.";
       example = "/run/secrets/freshrss";
     };
 
     baseUrl = mkOption {
       type = types.str;
-      description = mdDoc "Default URL for FreshRSS.";
+      description = "Default URL for FreshRSS.";
       example = "https://freshrss.example.com";
     };
 
     language = mkOption {
       type = types.str;
       default = "en";
-      description = mdDoc "Default language for FreshRSS.";
+      description = "Default language for FreshRSS.";
       example = "de";
     };
 
@@ -45,46 +45,46 @@ in
       type = mkOption {
         type = types.enum [ "sqlite" "pgsql" "mysql" ];
         default = "sqlite";
-        description = mdDoc "Database type.";
+        description = "Database type.";
         example = "pgsql";
       };
 
       host = mkOption {
         type = types.nullOr types.str;
         default = "localhost";
-        description = mdDoc "Database host for FreshRSS.";
+        description = "Database host for FreshRSS.";
       };
 
       port = mkOption {
         type = types.nullOr types.port;
         default = null;
-        description = mdDoc "Database port for FreshRSS.";
+        description = "Database port for FreshRSS.";
         example = 3306;
       };
 
       user = mkOption {
         type = types.nullOr types.str;
         default = "freshrss";
-        description = mdDoc "Database user for FreshRSS.";
+        description = "Database user for FreshRSS.";
       };
 
       passFile = mkOption {
         type = types.nullOr types.path;
         default = null;
-        description = mdDoc "Database password file for FreshRSS.";
+        description = "Database password file for FreshRSS.";
         example = "/run/secrets/freshrss";
       };
 
       name = mkOption {
         type = types.nullOr types.str;
         default = "freshrss";
-        description = mdDoc "Database name for FreshRSS.";
+        description = "Database name for FreshRSS.";
       };
 
       tableprefix = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = mdDoc "Database table prefix for FreshRSS.";
+        description = "Database table prefix for FreshRSS.";
         example = "freshrss";
       };
     };
@@ -92,14 +92,14 @@ in
     dataDir = mkOption {
       type = types.str;
       default = "/var/lib/freshrss";
-      description = mdDoc "Default data folder for FreshRSS.";
+      description = "Default data folder for FreshRSS.";
       example = "/mnt/freshrss";
     };
 
     virtualHost = mkOption {
       type = types.nullOr types.str;
       default = "freshrss";
-      description = mdDoc ''
+      description = ''
         Name of the nginx virtualhost to use and setup. If null, do not setup any virtualhost.
       '';
     };
@@ -107,7 +107,7 @@ in
     pool = mkOption {
       type = types.str;
       default = poolName;
-      description = mdDoc ''
+      description = ''
         Name of the phpfpm pool to use and setup. If not specified, a pool will be created
         with default values.
       '';
@@ -116,13 +116,13 @@ in
     user = mkOption {
       type = types.str;
       default = "freshrss";
-      description = lib.mdDoc "User under which FreshRSS runs.";
+      description = "User under which FreshRSS runs.";
     };
 
     authType = mkOption {
       type = types.enum [ "form" "http_auth" "none" ];
       default = "form";
-      description = mdDoc "Authentication type for FreshRSS.";
+      description = "Authentication type for FreshRSS.";
     };
   };
 
@@ -268,11 +268,11 @@ in
 
           script =
             let
-              userScriptArgs = ''--user ${cfg.defaultUser} --password "$(cat ${cfg.passwordFile})"'';
-              updateUserScript = optionalString (cfg.authType == "form") ''
+              userScriptArgs = ''--user ${cfg.defaultUser} ${optionalString (cfg.authType == "form") ''--password "$(cat ${cfg.passwordFile})"''}'';
+              updateUserScript = optionalString (cfg.authType == "form" || cfg.authType == "none") ''
                 ./cli/update-user.php ${userScriptArgs}
               '';
-              createUserScript = optionalString (cfg.authType == "form") ''
+              createUserScript = optionalString (cfg.authType == "form" || cfg.authType == "none") ''
                 ./cli/create-user.php ${userScriptArgs}
               '';
             in
diff --git a/nixpkgs/nixos/modules/services/web-apps/galene.nix b/nixpkgs/nixos/modules/services/web-apps/galene.nix
index 28d4069ec385..32854e757ac3 100644
--- a/nixpkgs/nixos/modules/services/web-apps/galene.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/galene.nix
@@ -12,12 +12,12 @@ in
 {
   options = {
     services.galene = {
-      enable = mkEnableOption (lib.mdDoc "Galene Service");
+      enable = mkEnableOption "Galene Service";
 
       stateDir = mkOption {
         default = defaultstateDir;
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           The directory where Galene stores its internal state. If left as the default
           value this directory will automatically be created before the Galene server
           starts, otherwise the sysadmin is responsible for ensuring the directory
@@ -28,19 +28,19 @@ in
       user = mkOption {
         type = types.str;
         default = "galene";
-        description = lib.mdDoc "User account under which galene runs.";
+        description = "User account under which galene runs.";
       };
 
       group = mkOption {
         type = types.str;
         default = "galene";
-        description = lib.mdDoc "Group under which galene runs.";
+        description = "Group under which galene runs.";
       };
 
       insecure = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           Whether Galene should listen in http or in https. If left as the default
           value (false), Galene needs to be fed a private key and a certificate.
         '';
@@ -50,7 +50,7 @@ in
         type = types.nullOr types.str;
         default = null;
         example = "/path/to/your/cert.pem";
-        description = lib.mdDoc ''
+        description = ''
           Path to the server's certificate. The file is copied at runtime to
           Galene's data directory where it needs to reside.
         '';
@@ -60,7 +60,7 @@ in
         type = types.nullOr types.str;
         default = null;
         example = "/path/to/your/key.pem";
-        description = lib.mdDoc ''
+        description = ''
           Path to the server's private key. The file is copied at runtime to
           Galene's data directory where it needs to reside.
         '';
@@ -69,13 +69,13 @@ in
       httpAddress = mkOption {
         type = types.str;
         default = "";
-        description = lib.mdDoc "HTTP listen address for galene.";
+        description = "HTTP listen address for galene.";
       };
 
       httpPort = mkOption {
         type = types.port;
         default = 8443;
-        description = lib.mdDoc "HTTP listen port.";
+        description = "HTTP listen port.";
       };
 
       staticDir = mkOption {
@@ -83,7 +83,7 @@ in
         default = "${cfg.package.static}/static";
         defaultText = literalExpression ''"''${package.static}/static"'';
         example = "/var/lib/galene/static";
-        description = lib.mdDoc "Web server directory.";
+        description = "Web server directory.";
       };
 
       recordingsDir = mkOption {
@@ -91,7 +91,7 @@ in
         default = defaultrecordingsDir;
         defaultText = literalExpression ''"''${config.${opt.stateDir}}/recordings"'';
         example = "/var/lib/galene/recordings";
-        description = lib.mdDoc "Recordings directory.";
+        description = "Recordings directory.";
       };
 
       dataDir = mkOption {
@@ -99,7 +99,7 @@ in
         default = defaultdataDir;
         defaultText = literalExpression ''"''${config.${opt.stateDir}}/data"'';
         example = "/var/lib/galene/data";
-        description = lib.mdDoc "Data directory.";
+        description = "Data directory.";
       };
 
       groupsDir = mkOption {
@@ -107,7 +107,7 @@ in
         default = defaultgroupsDir;
         defaultText = literalExpression ''"''${config.${opt.stateDir}}/groups"'';
         example = "/var/lib/galene/groups";
-        description = lib.mdDoc "Web server directory.";
+        description = "Web server directory.";
       };
 
       package = mkPackageOption pkgs "galene" { };
diff --git a/nixpkgs/nixos/modules/services/web-apps/gerrit.nix b/nixpkgs/nixos/modules/services/web-apps/gerrit.nix
index 5c62a7ebbd93..573c9d0d7dbb 100644
--- a/nixpkgs/nixos/modules/services/web-apps/gerrit.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/gerrit.nix
@@ -59,7 +59,7 @@ in
 {
   options = {
     services.gerrit = {
-      enable = mkEnableOption (lib.mdDoc "Gerrit service");
+      enable = mkEnableOption "Gerrit service";
 
       package = mkPackageOption pkgs "gerrit" { };
 
@@ -71,13 +71,13 @@ in
           "-Dflogger.backend_factory=com.google.common.flogger.backend.log4j.Log4jBackendFactory#getInstance"
           "-Dflogger.logging_context=com.google.gerrit.server.logging.LoggingContext#getInstance"
         ];
-        description = lib.mdDoc "A list of JVM options to start gerrit with.";
+        description = "A list of JVM options to start gerrit with.";
       };
 
       jvmHeapLimit = mkOption {
         type = types.str;
         default = "1024m";
-        description = lib.mdDoc ''
+        description = ''
           How much memory to allocate to the JVM heap
         '';
       };
@@ -85,7 +85,7 @@ in
       listenAddress = mkOption {
         type = types.str;
         default = "[::]:8080";
-        description = lib.mdDoc ''
+        description = ''
           `hostname:port` to listen for HTTP traffic.
 
           This is bound using the systemd socket activation.
@@ -95,7 +95,7 @@ in
       settings = mkOption {
         type = gitIniType;
         default = {};
-        description = lib.mdDoc ''
+        description = ''
           Gerrit configuration. This will be generated to the
           `etc/gerrit.config` file.
         '';
@@ -104,7 +104,7 @@ in
       replicationSettings = mkOption {
         type = gitIniType;
         default = {};
-        description = lib.mdDoc ''
+        description = ''
           Replication configuration. This will be generated to the
           `etc/replication.config` file.
         '';
@@ -113,7 +113,7 @@ in
       plugins = mkOption {
         type = types.listOf types.package;
         default = [];
-        description = lib.mdDoc ''
+        description = ''
           List of plugins to add to Gerrit. Each derivation is a jar file
           itself where the name of the derivation is the name of plugin.
         '';
@@ -122,7 +122,7 @@ in
       builtinPlugins = mkOption {
         type = types.listOf (types.enum cfg.package.passthru.plugins);
         default = [];
-        description = lib.mdDoc ''
+        description = ''
           List of builtins plugins to install. Those are shipped in the
           `gerrit.war` file.
         '';
@@ -130,7 +130,7 @@ in
 
       serverId = mkOption {
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           Set a UUID that uniquely identifies the server.
 
           This can be generated with
diff --git a/nixpkgs/nixos/modules/services/web-apps/gotify-server.nix b/nixpkgs/nixos/modules/services/web-apps/gotify-server.nix
index 8db3a8ef3e81..b700fd14ee52 100644
--- a/nixpkgs/nixos/modules/services/web-apps/gotify-server.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/gotify-server.nix
@@ -7,11 +7,11 @@ let
 in {
   options = {
     services.gotify = {
-      enable = mkEnableOption (lib.mdDoc "Gotify webserver");
+      enable = mkEnableOption "Gotify webserver";
 
       port = mkOption {
         type = types.port;
-        description = lib.mdDoc ''
+        description = ''
           Port the server listens to.
         '';
       };
@@ -19,7 +19,7 @@ in {
       stateDirectoryName = mkOption {
         type = types.str;
         default = "gotify-server";
-        description = lib.mdDoc ''
+        description = ''
           The name of the directory below {file}`/var/lib` where
           gotify stores its runtime data.
         '';
diff --git a/nixpkgs/nixos/modules/services/web-apps/gotosocial.nix b/nixpkgs/nixos/modules/services/web-apps/gotosocial.nix
index 657509c11005..aee1edf66a6a 100644
--- a/nixpkgs/nixos/modules/services/web-apps/gotosocial.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/gotosocial.nix
@@ -27,17 +27,17 @@ let
 in
 {
   meta.doc = ./gotosocial.md;
-  meta.maintainers = with lib.maintainers; [ misuzu blakesmith ];
+  meta.maintainers = with lib.maintainers; [ blakesmith ];
 
   options.services.gotosocial = {
-    enable = lib.mkEnableOption (lib.mdDoc "ActivityPub social network server");
+    enable = lib.mkEnableOption "ActivityPub social network server";
 
     package = lib.mkPackageOption pkgs "gotosocial" { };
 
     openFirewall = lib.mkOption {
       type = lib.types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Open the configured port in the firewall.
         Using a reverse proxy instead is highly recommended.
       '';
@@ -46,7 +46,7 @@ in
     setupPostgresqlDB = lib.mkOption {
       type = lib.types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Whether to setup a local postgres database and populate the
         `db-type` fields in `services.gotosocial.settings`.
       '';
@@ -59,7 +59,7 @@ in
         application-name = "My GoToSocial";
         host = "gotosocial.example.com";
       };
-      description = lib.mdDoc ''
+      description = ''
         Contents of the GoToSocial YAML config.
 
         Please refer to the
@@ -73,7 +73,7 @@ in
 
     environmentFile = lib.mkOption {
       type = lib.types.nullOr lib.types.path;
-      description = lib.mdDoc ''
+      description = ''
         File path containing environment variables for configuring the GoToSocial service
         in the format of an EnvironmentFile as described by systemd.exec(5).
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/grocy.nix b/nixpkgs/nixos/modules/services/web-apps/grocy.nix
index 858fd74279d0..eb4feb191aa5 100644
--- a/nixpkgs/nixos/modules/services/web-apps/grocy.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/grocy.nix
@@ -6,13 +6,13 @@ let
   cfg = config.services.grocy;
 in {
   options.services.grocy = {
-    enable = mkEnableOption (lib.mdDoc "grocy");
+    enable = mkEnableOption "grocy";
 
     package = mkPackageOption pkgs "grocy" { };
 
     hostName = mkOption {
       type = types.str;
-      description = lib.mdDoc ''
+      description = ''
         FQDN for the grocy instance.
       '';
     };
@@ -20,7 +20,7 @@ in {
     nginx.enableSSL = mkOption {
       type = types.bool;
       default = true;
-      description = lib.mdDoc ''
+      description = ''
         Whether or not to enable SSL (with ACME and let's encrypt)
         for the grocy vhost.
       '';
@@ -41,7 +41,7 @@ in {
         "pm.max_requests" = "500";
       };
 
-      description = lib.mdDoc ''
+      description = ''
         Options for grocy's PHPFPM pool.
       '';
     };
@@ -49,7 +49,7 @@ in {
     dataDir = mkOption {
       type = types.str;
       default = "/var/lib/grocy";
-      description = lib.mdDoc ''
+      description = ''
         Home directory of the `grocy` user which contains
         the application's state.
       '';
@@ -60,7 +60,7 @@ in {
         type = types.str;
         default = "USD";
         example = "EUR";
-        description = lib.mdDoc ''
+        description = ''
           ISO 4217 code for the currency to display.
         '';
       };
@@ -68,7 +68,7 @@ in {
       culture = mkOption {
         type = types.enum [ "de" "en" "da" "en_GB" "es" "fr" "hu" "it" "nl" "no" "pl" "pt_BR" "ru" "sk_SK" "sv_SE" "tr" ];
         default = "en";
-        description = lib.mdDoc ''
+        description = ''
           Display language of the frontend.
         '';
       };
@@ -77,14 +77,14 @@ in {
         showWeekNumber = mkOption {
           default = true;
           type = types.bool;
-          description = lib.mdDoc ''
+          description = ''
             Show the number of the weeks in the calendar views.
           '';
         };
         firstDayOfWeek = mkOption {
           default = null;
           type = types.nullOr (types.enum (range 0 6));
-          description = lib.mdDoc ''
+          description = ''
             Which day of the week (0=Sunday, 1=Monday etc.) should be the
             first day.
           '';
diff --git a/nixpkgs/nixos/modules/services/web-apps/guacamole-client.nix b/nixpkgs/nixos/modules/services/web-apps/guacamole-client.nix
index 04d867c0a943..98a6cac34f3d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/guacamole-client.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/guacamole-client.nix
@@ -10,7 +10,7 @@ in
 {
   options = {
     services.guacamole-client = {
-      enable = lib.mkEnableOption (lib.mdDoc "Apache Guacamole Client (Tomcat)");
+      enable = lib.mkEnableOption "Apache Guacamole Client (Tomcat)";
       package = lib.mkPackageOption pkgs "guacamole-client" { };
 
       settings = lib.mkOption {
@@ -21,7 +21,7 @@ in
           guacd-hostname = "localhost";
           guacd-port = 4822;
         };
-        description = lib.mdDoc ''
+        description = ''
           Configuration written to `guacamole.properties`.
 
           ::: {.note}
@@ -36,7 +36,7 @@ in
       enableWebserver = lib.mkOption {
         type = lib.types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Enable the Guacamole web application in a Tomcat webserver.
         '';
       };
diff --git a/nixpkgs/nixos/modules/services/web-apps/guacamole-server.nix b/nixpkgs/nixos/modules/services/web-apps/guacamole-server.nix
index 71e80d8aad32..6f6d12e9939f 100644
--- a/nixpkgs/nixos/modules/services/web-apps/guacamole-server.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/guacamole-server.nix
@@ -9,7 +9,7 @@ in
 {
   options = {
     services.guacamole-server = {
-      enable = lib.mkEnableOption (lib.mdDoc "Apache Guacamole Server (guacd)");
+      enable = lib.mkEnableOption "Apache Guacamole Server (guacd)";
       package = lib.mkPackageOption pkgs "guacamole-server" { };
 
       extraEnvironment = lib.mkOption {
@@ -20,12 +20,12 @@ in
             ENVIRONMENT = "production";
           }
         '';
-        description = lib.mdDoc "Environment variables to pass to guacd.";
+        description = "Environment variables to pass to guacd.";
       };
 
       host = lib.mkOption {
         default = "127.0.0.1";
-        description = lib.mdDoc ''
+        description = ''
           The host name or IP address the server should listen to.
         '';
         type = lib.types.str;
@@ -33,7 +33,7 @@ in
 
       port = lib.mkOption {
         default = 4822;
-        description = lib.mdDoc ''
+        description = ''
           The port the guacd server should listen to.
         '';
         type = lib.types.port;
@@ -43,7 +43,7 @@ in
         type = lib.types.nullOr lib.types.path;
         default = null;
         example = "/path/to/logback.xml";
-        description = lib.mdDoc ''
+        description = ''
           Configuration file that correspond to `logback.xml`.
         '';
       };
@@ -52,7 +52,7 @@ in
         type = lib.types.nullOr lib.types.path;
         default = null;
         example = "/path/to/user-mapping.xml";
-        description = lib.mdDoc ''
+        description = ''
           Configuration file that correspond to `user-mapping.xml`.
         '';
       };
diff --git a/nixpkgs/nixos/modules/services/web-apps/healthchecks.nix b/nixpkgs/nixos/modules/services/web-apps/healthchecks.nix
index 1d439f162313..5562b37e502c 100644
--- a/nixpkgs/nixos/modules/services/web-apps/healthchecks.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/healthchecks.nix
@@ -26,8 +26,8 @@ let
 in
 {
   options.services.healthchecks = {
-    enable = mkEnableOption (lib.mdDoc "healthchecks") // {
-      description = lib.mdDoc ''
+    enable = mkEnableOption "healthchecks" // {
+      description = ''
         Enable healthchecks.
         It is expected to be run behind a HTTP reverse proxy.
       '';
@@ -38,7 +38,7 @@ in
     user = mkOption {
       default = defaultUser;
       type = types.str;
-      description = lib.mdDoc ''
+      description = ''
         User account under which healthchecks runs.
 
         ::: {.note}
@@ -52,7 +52,7 @@ in
     group = mkOption {
       default = defaultUser;
       type = types.str;
-      description = lib.mdDoc ''
+      description = ''
         Group account under which healthchecks runs.
 
         ::: {.note}
@@ -66,19 +66,19 @@ in
     listenAddress = mkOption {
       type = types.str;
       default = "localhost";
-      description = lib.mdDoc "Address the server will listen on.";
+      description = "Address the server will listen on.";
     };
 
     port = mkOption {
       type = types.port;
       default = 8000;
-      description = lib.mdDoc "Port the server will listen on.";
+      description = "Port the server will listen on.";
     };
 
     dataDir = mkOption {
       type = types.str;
       default = "/var/lib/healthchecks";
-      description = lib.mdDoc ''
+      description = ''
         The directory used to store all data for healthchecks.
 
         ::: {.note}
@@ -90,7 +90,7 @@ in
     };
 
     settings = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         Environment variables which are read by healthchecks `(local)_settings.py`.
 
         Settings which are explicitly covered in options below, are type-checked and/or transformed
@@ -116,26 +116,26 @@ in
           ALLOWED_HOSTS = lib.mkOption {
             type = types.listOf types.str;
             default = [ "*" ];
-            description = lib.mdDoc "The host/domain names that this site can serve.";
+            description = "The host/domain names that this site can serve.";
             apply = lib.concatStringsSep ",";
           };
 
           SECRET_KEY_FILE = mkOption {
             type = types.path;
-            description = lib.mdDoc "Path to a file containing the secret key.";
+            description = "Path to a file containing the secret key.";
           };
 
           DEBUG = mkOption {
             type = types.bool;
             default = false;
-            description = lib.mdDoc "Enable debug mode.";
+            description = "Enable debug mode.";
             apply = boolToPython;
           };
 
           REGISTRATION_OPEN = mkOption {
             type = types.bool;
             default = false;
-            description = lib.mdDoc ''
+            description = ''
               A boolean that controls whether site visitors can create new accounts.
               Set it to false if you are setting up a private Healthchecks instance,
               but it needs to be publicly accessible (so, for example, your cloud
@@ -149,7 +149,7 @@ in
           DB = mkOption {
             type = types.enum [ "sqlite" "postgres" "mysql" ];
             default = "sqlite";
-            description = lib.mdDoc "Database engine to use.";
+            description = "Database engine to use.";
           };
 
           DB_NAME = mkOption {
@@ -163,7 +163,7 @@ in
               then "''${config.${opt.dataDir}}/healthchecks.sqlite"
               else "hc"
             '';
-            description = lib.mdDoc "Database name.";
+            description = "Database name.";
           };
         };
       });
@@ -213,8 +213,7 @@ in
           preStart = ''
             ${pkg}/opt/healthchecks/manage.py collectstatic --no-input
             ${pkg}/opt/healthchecks/manage.py remove_stale_contenttypes --no-input
-            ${pkg}/opt/healthchecks/manage.py compress
-          '';
+          '' + lib.optionalString (cfg.settings.DEBUG != "True") "${pkg}/opt/healthchecks/manage.py compress";
 
           serviceConfig = commonConfig // {
             Restart = "always";
diff --git a/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix b/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix
index 8b17c6cbc3be..919d870b3a2c 100644
--- a/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix
@@ -1,7 +1,7 @@
 { config, lib, pkgs, ... }:
 
 let
-  inherit (lib) mkOption types mdDoc literalExpression;
+  inherit (lib) mkOption types literalExpression;
 
   cfg = config.services.hedgedoc;
 
@@ -34,7 +34,7 @@ in
 
   options.services.hedgedoc = {
     package = lib.mkPackageOption pkgs "hedgedoc" { };
-    enable = lib.mkEnableOption (mdDoc "the HedgeDoc Markdown Editor");
+    enable = lib.mkEnableOption "the HedgeDoc Markdown Editor";
 
     settings = mkOption {
       type = types.submodule {
@@ -44,7 +44,7 @@ in
             type = with types; nullOr str;
             default = null;
             example = "hedgedoc.org";
-            description = mdDoc ''
+            description = ''
               Domain to use for website.
 
               This is useful if you are trying to run hedgedoc behind
@@ -55,7 +55,7 @@ in
             type = with types; nullOr str;
             default = null;
             example = "hedgedoc";
-            description = mdDoc ''
+            description = ''
               URL path for the website.
 
               This is useful if you are hosting hedgedoc on a path like
@@ -65,7 +65,7 @@ in
           host = mkOption {
             type = with types; nullOr str;
             default = "localhost";
-            description = mdDoc ''
+            description = ''
               Address to listen on.
             '';
           };
@@ -73,7 +73,7 @@ in
             type = types.port;
             default = 3000;
             example = 80;
-            description = mdDoc ''
+            description = ''
               Port to listen on.
             '';
           };
@@ -81,7 +81,7 @@ in
             type = with types; nullOr path;
             default = null;
             example = "/run/hedgedoc/hedgedoc.sock";
-            description = mdDoc ''
+            description = ''
               Path to UNIX domain socket to listen on
 
               ::: {.note}
@@ -93,7 +93,7 @@ in
             type = types.bool;
             default = false;
             example = true;
-            description = mdDoc ''
+            description = ''
               Use `https://` for all links.
 
               This is useful if you are trying to run hedgedoc behind
@@ -111,7 +111,7 @@ in
               with config.services.hedgedoc.settings; [ host ] ++ lib.optionals (domain != null) [ domain ]
             '';
             example = [ "localhost" "hedgedoc.org" ];
-            description = mdDoc ''
+            description = ''
               List of domains to whitelist.
             '';
           };
@@ -137,7 +137,7 @@ in
                 dialect = "postgresql";
               };
             '';
-            description = mdDoc ''
+            description = ''
               Specify the configuration for sequelize.
               HedgeDoc supports `mysql`, `postgres`, `sqlite` and `mssql`.
               See <https://sequelize.readthedocs.io/en/v3/>
@@ -151,7 +151,7 @@ in
           useSSL = mkOption {
             type = types.bool;
             default = false;
-            description = mdDoc ''
+            description = ''
               Enable to use SSL server.
 
               ::: {.note}
@@ -170,7 +170,7 @@ in
             type = types.path;
             default = "/var/lib/${name}/uploads";
             defaultText = "/var/lib/hedgedoc/uploads";
-            description = mdDoc ''
+            description = ''
               Directory for storing uploaded images.
             '';
           };
@@ -180,7 +180,7 @@ in
             type = types.bool;
             default = false;
             example = true;
-            description = mdDoc ''
+            description = ''
               Whether to enable [Libravatar](https://wiki.libravatar.org/) as
               profile picture source on your instance.
 
@@ -191,7 +191,7 @@ in
         };
       };
 
-      description = mdDoc ''
+      description = ''
         HedgeDoc configuration, see
         <https://docs.hedgedoc.org/configuration/>
         for documentation.
@@ -202,7 +202,7 @@ in
       type = with types; nullOr path;
       default = null;
       example = "/var/lib/hedgedoc/hedgedoc.env";
-      description = mdDoc ''
+      description = ''
         Environment file as defined in {manpage}`systemd.exec(5)`.
 
         Secrets may be passed to the service without adding them to the world-readable
diff --git a/nixpkgs/nixos/modules/services/web-apps/hledger-web.nix b/nixpkgs/nixos/modules/services/web-apps/hledger-web.nix
index be8ecc645e59..32d9df4e8458 100644
--- a/nixpkgs/nixos/modules/services/web-apps/hledger-web.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/hledger-web.nix
@@ -5,14 +5,14 @@ let
 in {
   options.services.hledger-web = {
 
-    enable = mkEnableOption (lib.mdDoc "hledger-web service");
+    enable = mkEnableOption "hledger-web service";
 
-    serveApi = mkEnableOption (lib.mdDoc "serving only the JSON web API, without the web UI");
+    serveApi = mkEnableOption "serving only the JSON web API, without the web UI";
 
     host = mkOption {
       type = types.str;
       default = "127.0.0.1";
-      description = lib.mdDoc ''
+      description = ''
         Address to listen on.
       '';
     };
@@ -21,39 +21,28 @@ in {
       type = types.port;
       default = 5000;
       example = 80;
-      description = lib.mdDoc ''
+      description = ''
         Port to listen on.
       '';
     };
 
-    capabilities = {
-      view = mkOption {
-        type = types.bool;
-        default = true;
-        description = lib.mdDoc ''
-          Enable the view capability.
-        '';
-      };
-      add = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Enable the add capability.
-        '';
-      };
-      manage = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Enable the manage capability.
-        '';
-      };
+    allow = mkOption {
+      type = types.enum [ "view" "add" "edit" "sandstorm" ];
+      default = "view";
+      description = ''
+        User's access level for changing data.
+
+        * view: view only permission.
+        * add: view and add permissions.
+        * edit: view, add, and edit permissions.
+        * sandstorm: permissions from the `X-Sandstorm-Permissions` request header.
+      '';
     };
 
     stateDir = mkOption {
       type = types.path;
       default = "/var/lib/hledger-web";
-      description = lib.mdDoc ''
+      description = ''
         Path the service has access to. If left as the default value this
         directory will automatically be created before the hledger-web server
         starts, otherwise the sysadmin is responsible for ensuring the
@@ -64,7 +53,7 @@ in {
     journalFiles = mkOption {
       type = types.listOf types.str;
       default = [ ".hledger.journal" ];
-      description = lib.mdDoc ''
+      description = ''
         Paths to journal files relative to {option}`services.hledger-web.stateDir`.
       '';
     };
@@ -73,7 +62,7 @@ in {
       type = with types; nullOr str;
       default = null;
       example = "https://example.org";
-      description = lib.mdDoc ''
+      description = ''
         Base URL, when sharing over a network.
       '';
     };
@@ -82,13 +71,18 @@ in {
       type = types.listOf types.str;
       default = [];
       example = [ "--forecast" ];
-      description = lib.mdDoc ''
+      description = ''
         Extra command line arguments to pass to hledger-web.
       '';
     };
 
   };
 
+  imports = [
+    (mkRemovedOptionModule [ "services" "hledger-web" "capabilities" ]
+      "This option has been replaced by new option `services.hledger-web.allow`.")
+  ];
+
   config = mkIf cfg.enable {
 
     users.users.hledger = {
@@ -102,16 +96,11 @@ in {
     users.groups.hledger = {};
 
     systemd.services.hledger-web = let
-      capabilityString = with cfg.capabilities; concatStringsSep "," (
-        (optional view "view")
-        ++ (optional add "add")
-        ++ (optional manage "manage")
-      );
       serverArgs = with cfg; escapeShellArgs ([
         "--serve"
         "--host=${host}"
         "--port=${toString port}"
-        "--capabilities=${capabilityString}"
+        "--allow=${allow}"
         (optionalString (cfg.baseUrl != null) "--base-url=${cfg.baseUrl}")
         (optionalString (cfg.serveApi) "--serve-api")
       ] ++ (map (f: "--file=${stateDir}/${f}") cfg.journalFiles)
diff --git a/nixpkgs/nixos/modules/services/web-apps/honk.nix b/nixpkgs/nixos/modules/services/web-apps/honk.nix
index eb270a661ecb..e6a446192122 100644
--- a/nixpkgs/nixos/modules/services/web-apps/honk.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/honk.nix
@@ -21,12 +21,12 @@ in
 {
   options = {
     services.honk = {
-      enable = lib.mkEnableOption (lib.mdDoc "the Honk server");
+      enable = lib.mkEnableOption "the Honk server";
       package = lib.mkPackageOption pkgs "honk" { };
 
       host = lib.mkOption {
         default = "127.0.0.1";
-        description = lib.mdDoc ''
+        description = ''
           The host name or IP address the server should listen to.
         '';
         type = lib.types.str;
@@ -34,21 +34,21 @@ in
 
       port = lib.mkOption {
         default = 8080;
-        description = lib.mdDoc ''
+        description = ''
           The port the server should listen to.
         '';
         type = lib.types.port;
       };
 
       username = lib.mkOption {
-        description = lib.mdDoc ''
+        description = ''
           The admin account username.
         '';
         type = lib.types.str;
       };
 
       passwordFile = lib.mkOption {
-        description = lib.mdDoc ''
+        description = ''
           Password for admin account.
           NOTE: Should be string not a store path, to prevent the password from being world readable
         '';
@@ -56,7 +56,7 @@ in
       };
 
       servername = lib.mkOption {
-        description = lib.mdDoc ''
+        description = ''
           The server name.
         '';
         type = lib.types.str;
@@ -64,7 +64,7 @@ in
 
       extraJS = lib.mkOption {
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           An extra JavaScript file to be loaded by the client.
         '';
         type = lib.types.nullOr lib.types.path;
@@ -72,7 +72,7 @@ in
 
       extraCSS = lib.mkOption {
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           An extra CSS file to be loaded by the client.
         '';
         type = lib.types.nullOr lib.types.path;
diff --git a/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix b/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix
index 67d235ab4475..b9761061aaae 100644
--- a/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix
@@ -12,12 +12,12 @@ in {
   meta.maintainers = with maintainers; [ das_j ];
 
   options.services.icingaweb2 = with types; {
-    enable = mkEnableOption (lib.mdDoc "the icingaweb2 web interface");
+    enable = mkEnableOption "the icingaweb2 web interface";
 
     pool = mkOption {
       type = str;
       default = poolName;
-      description = lib.mdDoc ''
+      description = ''
          Name of existing PHP-FPM pool that is used to run Icingaweb2.
          If not specified, a pool will automatically created with default values.
       '';
@@ -26,7 +26,7 @@ in {
     libraryPaths = mkOption {
       type = attrsOf package;
       default = { };
-      description = lib.mdDoc ''
+      description = ''
         Libraries to add to the Icingaweb2 library path.
         The name of the attribute is the name of the library, the value
         is the package to add.
@@ -36,7 +36,7 @@ in {
     virtualHost = mkOption {
       type = nullOr str;
       default = "icingaweb2";
-      description = lib.mdDoc ''
+      description = ''
         Name of the nginx virtualhost to use and setup. If null, no virtualhost is set up.
       '';
     };
@@ -45,15 +45,15 @@ in {
       type = str;
       default = "UTC";
       example = "Europe/Berlin";
-      description = lib.mdDoc "PHP-compliant timezone specification";
+      description = "PHP-compliant timezone specification";
     };
 
     modules = {
-      doc.enable = mkEnableOption (lib.mdDoc "the icingaweb2 doc module");
-      migrate.enable = mkEnableOption (lib.mdDoc "the icingaweb2 migrate module");
-      setup.enable = mkEnableOption (lib.mdDoc "the icingaweb2 setup module");
-      test.enable = mkEnableOption (lib.mdDoc "the icingaweb2 test module");
-      translation.enable = mkEnableOption (lib.mdDoc "the icingaweb2 translation module");
+      doc.enable = mkEnableOption "the icingaweb2 doc module";
+      migrate.enable = mkEnableOption "the icingaweb2 migrate module";
+      setup.enable = mkEnableOption "the icingaweb2 setup module";
+      test.enable = mkEnableOption "the icingaweb2 test module";
+      translation.enable = mkEnableOption "the icingaweb2 translation module";
     };
 
     modulePackages = mkOption {
@@ -64,7 +64,7 @@ in {
           "snow" = icingaweb2Modules.theme-snow;
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         Name-package attrset of Icingaweb 2 modules packages to enable.
 
         If you enable modules manually (e.g. via the web ui), they will not be touched.
@@ -84,7 +84,7 @@ in {
           level = "CRITICAL";
         };
       };
-      description = lib.mdDoc ''
+      description = ''
         config.ini contents.
         Will automatically be converted to a .ini file.
         If you don't set global.module_path, the module will take care of it.
@@ -108,7 +108,7 @@ in {
           dbname = "icingaweb2";
         };
       };
-      description = lib.mdDoc ''
+      description = ''
         resources.ini contents.
         Will automatically be converted to a .ini file.
 
@@ -127,7 +127,7 @@ in {
           resource = "icingaweb_db";
         };
       };
-      description = lib.mdDoc ''
+      description = ''
         authentication.ini contents.
         Will automatically be converted to a .ini file.
 
@@ -145,7 +145,7 @@ in {
           resource = "icingaweb_db";
         };
       };
-      description = lib.mdDoc ''
+      description = ''
         groups.ini contents.
         Will automatically be converted to a .ini file.
 
@@ -163,7 +163,7 @@ in {
           permissions = "*";
         };
       };
-      description = lib.mdDoc ''
+      description = ''
         roles.ini contents.
         Will automatically be converted to a .ini file.
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/icingaweb2/module-monitoring.nix b/nixpkgs/nixos/modules/services/web-apps/icingaweb2/module-monitoring.nix
index 9a848870e9da..e9c1d4ffe5ea 100644
--- a/nixpkgs/nixos/modules/services/web-apps/icingaweb2/module-monitoring.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/icingaweb2/module-monitoring.nix
@@ -34,50 +34,50 @@ in {
     enable = mkOption {
       type = bool;
       default = true;
-      description = lib.mdDoc "Whether to enable the icingaweb2 monitoring module.";
+      description = "Whether to enable the icingaweb2 monitoring module.";
     };
 
     generalConfig = {
       mutable = mkOption {
         type = bool;
         default = false;
-        description = lib.mdDoc "Make config.ini of the monitoring module mutable (e.g. via the web interface).";
+        description = "Make config.ini of the monitoring module mutable (e.g. via the web interface).";
       };
 
       protectedVars = mkOption {
         type = listOf str;
         default = [ "*pw*" "*pass*" "community" ];
-        description = lib.mdDoc "List of string patterns for custom variables which should be excluded from user’s view.";
+        description = "List of string patterns for custom variables which should be excluded from user’s view.";
       };
     };
 
     mutableBackends = mkOption {
       type = bool;
       default = false;
-      description = lib.mdDoc "Make backends.ini of the monitoring module mutable (e.g. via the web interface).";
+      description = "Make backends.ini of the monitoring module mutable (e.g. via the web interface).";
     };
 
     backends = mkOption {
       default = { icinga = { resource = "icinga_ido"; }; };
-      description = lib.mdDoc "Monitoring backends to define";
+      description = "Monitoring backends to define";
       type = attrsOf (submodule ({ name, ... }: {
         options = {
           name = mkOption {
             visible = false;
             default = name;
             type = str;
-            description = lib.mdDoc "Name of this backend";
+            description = "Name of this backend";
           };
 
           resource = mkOption {
             type = str;
-            description = lib.mdDoc "Name of the IDO resource";
+            description = "Name of the IDO resource";
           };
 
           disabled = mkOption {
             type = bool;
             default = false;
-            description = lib.mdDoc "Disable this backend";
+            description = "Disable this backend";
           };
         };
       }));
@@ -86,62 +86,62 @@ in {
     mutableTransports = mkOption {
       type = bool;
       default = true;
-      description = lib.mdDoc "Make commandtransports.ini of the monitoring module mutable (e.g. via the web interface).";
+      description = "Make commandtransports.ini of the monitoring module mutable (e.g. via the web interface).";
     };
 
     transports = mkOption {
       default = {};
-      description = lib.mdDoc "Command transports to define";
+      description = "Command transports to define";
       type = attrsOf (submodule ({ name, ... }: {
         options = {
           name = mkOption {
             visible = false;
             default = name;
             type = str;
-            description = lib.mdDoc "Name of this transport";
+            description = "Name of this transport";
           };
 
           type = mkOption {
             type = enum [ "api" "local" "remote" ];
             default = "api";
-            description = lib.mdDoc "Type of  this transport";
+            description = "Type of  this transport";
           };
 
           instance = mkOption {
             type = nullOr str;
             default = null;
-            description = lib.mdDoc "Assign a icinga instance to this transport";
+            description = "Assign a icinga instance to this transport";
           };
 
           path = mkOption {
             type = str;
-            description = lib.mdDoc "Path to the socket for local or remote transports";
+            description = "Path to the socket for local or remote transports";
           };
 
           host = mkOption {
             type = str;
-            description = lib.mdDoc "Host for the api or remote transport";
+            description = "Host for the api or remote transport";
           };
 
           port = mkOption {
             type = nullOr str;
             default = null;
-            description = lib.mdDoc "Port to connect to for the api or remote transport";
+            description = "Port to connect to for the api or remote transport";
           };
 
           username = mkOption {
             type = str;
-            description = lib.mdDoc "Username for the api or remote transport";
+            description = "Username for the api or remote transport";
           };
 
           password = mkOption {
             type = str;
-            description = lib.mdDoc "Password for the api transport";
+            description = "Password for the api transport";
           };
 
           resource = mkOption {
             type = str;
-            description = lib.mdDoc "SSH identity resource for the remote transport";
+            description = "SSH identity resource for the remote transport";
           };
         };
       }));
diff --git a/nixpkgs/nixos/modules/services/web-apps/invidious.nix b/nixpkgs/nixos/modules/services/web-apps/invidious.nix
index 359aaabfe673..f0e860383a62 100644
--- a/nixpkgs/nixos/modules/services/web-apps/invidious.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/invidious.nix
@@ -237,14 +237,14 @@ let
 in
 {
   options.services.invidious = {
-    enable = lib.mkEnableOption (lib.mdDoc "Invidious");
+    enable = lib.mkEnableOption "Invidious";
 
     package = lib.mkPackageOption pkgs "invidious" { };
 
     settings = lib.mkOption {
       type = settingsFormat.type;
       default = { };
-      description = lib.mdDoc ''
+      description = ''
         The settings Invidious should use.
 
         See [config.example.yml](https://github.com/iv-org/invidious/blob/master/config/config.example.yml) for a list of all possible options.
@@ -254,7 +254,7 @@ in
     hmacKeyFile = lib.mkOption {
       type = types.nullOr types.path;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         A path to a file containing the `hmac_key`. If `null`, a key will be generated automatically on first
         start.
 
@@ -266,7 +266,7 @@ in
     extraSettingsFile = lib.mkOption {
       type = types.nullOr types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         A file including Invidious settings.
 
         It gets merged with the settings specified in {option}`services.invidious.settings`
@@ -277,7 +277,7 @@ in
     serviceScale = lib.mkOption {
       type = types.int;
       default = 1;
-      description = lib.mdDoc ''
+      description = ''
         How many invidious instances to run.
 
         See https://docs.invidious.io/improve-public-instance/#2-multiple-invidious-processes for more details
@@ -294,7 +294,7 @@ in
     domain = lib.mkOption {
       type = types.nullOr types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         The FQDN Invidious is reachable on.
 
         This is used to configure nginx and for building absolute URLs.
@@ -306,7 +306,7 @@ in
       # default from https://github.com/iv-org/invidious/blob/master/config/config.example.yml
       default = if cfg.nginx.enable then "127.0.0.1" else "0.0.0.0";
       defaultText = lib.literalExpression ''if config.services.invidious.nginx.enable then "127.0.0.1" else "0.0.0.0"'';
-      description = lib.mdDoc ''
+      description = ''
         The IP address Invidious should bind to.
       '';
     };
@@ -315,7 +315,7 @@ in
       type = types.port;
       # Default from https://docs.invidious.io/Configuration.md
       default = 3000;
-      description = lib.mdDoc ''
+      description = ''
         The port Invidious should listen on.
 
         To allow access from outside,
@@ -328,7 +328,7 @@ in
       createLocally = lib.mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether to create a local database with PostgreSQL.
         '';
       };
@@ -336,7 +336,7 @@ in
       host = lib.mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           The database host Invidious should use.
 
           If `null`, the local unix socket is used. Otherwise
@@ -346,9 +346,9 @@ in
 
       port = lib.mkOption {
         type = types.port;
-        default = options.services.postgresql.port.default;
-        defaultText = lib.literalExpression "options.services.postgresql.port.default";
-        description = lib.mdDoc ''
+        default = config.services.postgresql.settings.port;
+        defaultText = lib.literalExpression "config.services.postgresql.settings.port";
+        description = ''
           The port of the database Invidious should use.
 
           Defaults to the the default postgresql port.
@@ -359,7 +359,7 @@ in
         type = types.nullOr types.str;
         apply = lib.mapNullable toString;
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           Path to file containing the database password.
         '';
       };
@@ -368,7 +368,7 @@ in
     nginx.enable = lib.mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Whether to configure nginx as a reverse proxy for Invidious.
 
         It serves it under the domain specified in {option}`services.invidious.settings.domain` with enabled TLS and ACME.
@@ -381,7 +381,7 @@ in
       enable = lib.mkOption {
         type = lib.types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           Whether to enable http3-ytproxy for faster loading of images and video playback.
 
           If {option}`services.invidious.nginx.enable` is used, nginx will be configured automatically. If not, you
diff --git a/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix b/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix
index 618bd848ebcb..4d0e25958e35 100644
--- a/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix
@@ -80,12 +80,12 @@ let
     {
       options = {
 
-        enable = mkEnableOption (lib.mdDoc "InvoicePlane web application");
+        enable = mkEnableOption "InvoicePlane web application";
 
         stateDir = mkOption {
           type = types.path;
           default = "/var/lib/invoiceplane/${name}";
-          description = lib.mdDoc ''
+          description = ''
             This directory is used for uploads of attachments and cache.
             The directory passed here is automatically created and permissions
             adjusted as required.
@@ -96,32 +96,32 @@ let
           host = mkOption {
             type = types.str;
             default = "localhost";
-            description = lib.mdDoc "Database host address.";
+            description = "Database host address.";
           };
 
           port = mkOption {
             type = types.port;
             default = 3306;
-            description = lib.mdDoc "Database host port.";
+            description = "Database host port.";
           };
 
           name = mkOption {
             type = types.str;
             default = "invoiceplane";
-            description = lib.mdDoc "Database name.";
+            description = "Database name.";
           };
 
           user = mkOption {
             type = types.str;
             default = "invoiceplane";
-            description = lib.mdDoc "Database user.";
+            description = "Database user.";
           };
 
           passwordFile = mkOption {
             type = types.nullOr types.path;
             default = null;
             example = "/run/keys/invoiceplane-dbpassword";
-            description = lib.mdDoc ''
+            description = ''
               A file containing the password corresponding to
               {option}`database.user`.
             '';
@@ -130,14 +130,14 @@ let
           createLocally = mkOption {
             type = types.bool;
             default = true;
-            description = lib.mdDoc "Create the database and database user locally.";
+            description = "Create the database and database user locally.";
           };
         };
 
         invoiceTemplates = mkOption {
           type = types.listOf types.path;
           default = [];
-          description = lib.mdDoc ''
+          description = ''
             List of path(s) to respective template(s) which are copied from the 'invoice_templates/pdf' directory.
 
             ::: {.note}
@@ -176,7 +176,7 @@ let
             "pm.max_spare_servers" = 4;
             "pm.max_requests" = 500;
           };
-          description = lib.mdDoc ''
+          description = ''
             Options for the InvoicePlane PHP pool. See the documentation on `php-fpm.conf`
             for details on configuration directives.
           '';
@@ -190,7 +190,7 @@ let
             DISABLE_SETUP=true
             IP_URL=https://invoice.example.com
           '';
-          description = lib.mdDoc ''
+          description = ''
             InvoicePlane configuration. Refer to
             <https://github.com/InvoicePlane/InvoicePlane/blob/master/ipconfig.php.example>
             for details on supported values.
@@ -204,7 +204,7 @@ let
         settings = mkOption {
           type = types.attrsOf types.anything;
           default = {};
-          description = lib.mdDoc ''
+          description = ''
             Structural InvoicePlane configuration. Refer to
             <https://github.com/InvoicePlane/InvoicePlane/blob/master/ipconfig.php.example>
             for details and supported values.
@@ -222,7 +222,7 @@ let
           enable = mkOption {
             type = types.bool;
             default = false;
-            description = lib.mdDoc ''
+            description = ''
               Enable cron service which periodically runs Invoiceplane tasks.
               Requires key taken from the administration page. Refer to
               <https://wiki.invoiceplane.com/en/1.0/modules/recurring-invoices>
@@ -231,7 +231,7 @@ let
           };
           key = mkOption {
             type = types.str;
-            description = lib.mdDoc "Cron key taken from the administration page.";
+            description = "Cron key taken from the administration page.";
           };
         };
 
@@ -248,20 +248,20 @@ in
         options.sites = mkOption {
           type = types.attrsOf (types.submodule siteOpts);
           default = {};
-          description = lib.mdDoc "Specification of one or more WordPress sites to serve";
+          description = "Specification of one or more WordPress sites to serve";
         };
 
         options.webserver = mkOption {
           type = types.enum [ "caddy" "nginx" ];
           default = "caddy";
           example = "nginx";
-          description = lib.mdDoc ''
+          description = ''
             Which webserver to use for virtual host management.
           '';
         };
       };
       default = {};
-      description = lib.mdDoc "InvoicePlane configuration.";
+      description = "InvoicePlane configuration.";
     };
 
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/isso.nix b/nixpkgs/nixos/modules/services/web-apps/isso.nix
index 6cb2d9ec785e..4e7785d1eb3e 100644
--- a/nixpkgs/nixos/modules/services/web-apps/isso.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/isso.nix
@@ -11,16 +11,16 @@ in {
 
   options = {
     services.isso = {
-      enable = mkEnableOption (lib.mdDoc ''
+      enable = mkEnableOption ''
         isso, a commenting server similar to Disqus.
 
         Note: The application's author suppose to run isso behind a reverse proxy.
         The embedded solution offered by NixOS is also only suitable for small installations
         below 20 requests per second
-      '');
+      '';
 
       settings = mkOption {
-        description = lib.mdDoc ''
+        description = ''
           Configuration for `isso`.
 
           See [Isso Server Configuration](https://posativ.org/isso/docs/configuration/server/)
diff --git a/nixpkgs/nixos/modules/services/web-apps/jirafeau.nix b/nixpkgs/nixos/modules/services/web-apps/jirafeau.nix
index 5f754d824a28..12a228f41d3d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/jirafeau.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/jirafeau.nix
@@ -25,7 +25,7 @@ in
     adminPasswordSha256 = mkOption {
       type = types.str;
       default = "";
-      description = lib.mdDoc ''
+      description = ''
         SHA-256 of the desired administration password. Leave blank/unset for no password.
       '';
     };
@@ -33,10 +33,10 @@ in
     dataDir = mkOption {
       type = types.path;
       default = "/var/lib/jirafeau/data/";
-      description = lib.mdDoc "Location of Jirafeau storage directory.";
+      description = "Location of Jirafeau storage directory.";
     };
 
-    enable = mkEnableOption (lib.mdDoc "Jirafeau file upload application");
+    enable = mkEnableOption "Jirafeau file upload application";
 
     extraConfig = mkOption {
       type = types.lines;
@@ -48,8 +48,7 @@ in
       description =  let
         documentationLink =
           "https://gitlab.com/mojo42/Jirafeau/-/blob/${cfg.package.version}/lib/config.original.php";
-      in
-        lib.mdDoc ''
+      in ''
           Jirefeau configuration. Refer to <${documentationLink}> for supported
           values.
         '';
@@ -58,13 +57,13 @@ in
     hostName = mkOption {
       type = types.str;
       default = "localhost";
-      description = lib.mdDoc "URL of instance. Must have trailing slash.";
+      description = "URL of instance. Must have trailing slash.";
     };
 
     maxUploadSizeMegabytes = mkOption {
       type = types.int;
       default = 0;
-      description = lib.mdDoc "Maximum upload size of accepted files.";
+      description = "Maximum upload size of accepted files.";
     };
 
     maxUploadTimeout = mkOption {
@@ -72,8 +71,7 @@ in
       default = "30m";
       description = let
         nginxCoreDocumentation = "http://nginx.org/en/docs/http/ngx_http_core_module.html";
-      in
-        lib.mdDoc ''
+      in ''
           Timeout for reading client request bodies and headers. Refer to
           <${nginxCoreDocumentation}#client_body_timeout> and
           <${nginxCoreDocumentation}#client_header_timeout> for accepted values.
@@ -89,7 +87,7 @@ in
           serverAliases = [ "wiki.''${config.networking.domain}" ];
         }
       '';
-      description = lib.mdDoc "Extra configuration for the nginx virtual host of Jirafeau.";
+      description = "Extra configuration for the nginx virtual host of Jirafeau.";
     };
 
     package = mkPackageOption pkgs "jirafeau" { };
@@ -104,7 +102,7 @@ in
         "pm.max_spare_servers" = 4;
         "pm.max_requests" = 500;
       };
-      description = lib.mdDoc ''
+      description = ''
         Options for Jirafeau PHP pool. See documentation on `php-fpm.conf` for
         details on configuration directives.
       '';
diff --git a/nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix b/nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix
index c4505534d635..76753b89ec9e 100644
--- a/nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix
@@ -47,12 +47,12 @@ let
 in
 {
   options.services.jitsi-meet = with types; {
-    enable = mkEnableOption (lib.mdDoc "Jitsi Meet - Secure, Simple and Scalable Video Conferences");
+    enable = mkEnableOption "Jitsi Meet - Secure, Simple and Scalable Video Conferences";
 
     hostName = mkOption {
       type = str;
       example = "meet.example.org";
-      description = lib.mdDoc ''
+      description = ''
         FQDN of the Jitsi Meet instance.
       '';
     };
@@ -66,7 +66,7 @@ in
           defaultLang = "fi";
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         Client-side web application settings that override the defaults in {file}`config.js`.
 
         See <https://github.com/jitsi/jitsi-meet/blob/master/config.js> for default
@@ -77,7 +77,7 @@ in
     extraConfig = mkOption {
       type = lines;
       default = "";
-      description = lib.mdDoc ''
+      description = ''
         Text to append to {file}`config.js` web application config file.
 
         Can be used to insert JavaScript logic to determine user's region in cascading bridges setup.
@@ -93,7 +93,7 @@ in
           SHOW_WATERMARK_FOR_GUESTS = false;
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         Client-side web-app interface settings that override the defaults in {file}`interface_config.js`.
 
         See <https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js> for
@@ -105,7 +105,7 @@ in
       enable = mkOption {
         type = bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Jitsi Videobridge instance and configure it to connect to Prosody.
 
           Additional configuration is possible with {option}`services.jitsi-videobridge`
@@ -116,7 +116,7 @@ in
         type = nullOr str;
         default = null;
         example = "/run/keys/videobridge";
-        description = lib.mdDoc ''
+        description = ''
           File containing password to the Prosody account for videobridge.
 
           If `null`, a file with password will be generated automatically. Setting
@@ -128,7 +128,7 @@ in
     jicofo.enable = mkOption {
       type = bool;
       default = true;
-      description = lib.mdDoc ''
+      description = ''
         Whether to enable JiCoFo instance and configure it to connect to Prosody.
 
         Additional configuration is possible with {option}`services.jicofo`.
@@ -138,7 +138,7 @@ in
     jibri.enable = mkOption {
       type = bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Whether to enable a Jibri instance and configure it to connect to Prosody.
 
         Additional configuration is possible with {option}`services.jibri`, and
@@ -159,7 +159,7 @@ in
     nginx.enable = mkOption {
       type = bool;
       default = true;
-      description = lib.mdDoc ''
+      description = ''
         Whether to enable nginx virtual host that will serve the javascript application and act as
         a proxy for the XMPP server. Further nginx configuration can be done by adapting
         {option}`services.nginx.virtualHosts.<hostName>`.
@@ -170,25 +170,32 @@ in
       '';
     };
 
-    caddy.enable = mkEnableOption (lib.mdDoc "Whether to enable caddy reverse proxy to expose jitsi-meet");
+    caddy.enable = mkEnableOption "Whether to enable caddy reverse proxy to expose jitsi-meet";
 
     prosody.enable = mkOption {
       type = bool;
       default = true;
-      description = lib.mdDoc ''
+      description = ''
         Whether to configure Prosody to relay XMPP messages between Jitsi Meet components. Turn this
         off if you want to configure it manually.
       '';
     };
 
-    excalidraw.enable = mkEnableOption (lib.mdDoc "Excalidraw collaboration backend for Jitsi");
+    excalidraw.enable = mkEnableOption "Excalidraw collaboration backend for Jitsi";
     excalidraw.port = mkOption {
       type = types.port;
       default = 3002;
-      description = lib.mdDoc ''The port which the Excalidraw backend for Jitsi should listen to.'';
+      description = ''The port which the Excalidraw backend for Jitsi should listen to.'';
     };
 
-    secureDomain.enable = mkEnableOption (lib.mdDoc "Authenticated room creation");
+    secureDomain = {
+      enable = mkEnableOption "Authenticated room creation";
+      authentication = mkOption {
+        type = types.str;
+        default = "internal_hashed";
+        description = ''The authentication type to be used by jitsi'';
+      };
+    };
   };
 
   config = mkIf cfg.enable {
@@ -309,7 +316,7 @@ in
         enabled = true;
         domain = cfg.hostName;
         extraConfig = ''
-          authentication = ${if cfg.secureDomain.enable then "\"internal_hashed\"" else "\"jitsi-anonymous\""}
+          authentication = ${if cfg.secureDomain.enable then "\"${cfg.secureDomain.authentication}\"" else "\"jitsi-anonymous\""}
           c2s_require_encryption = false
           admins = { "focus@auth.${cfg.hostName}" }
           smacks_max_unacked_stanzas = 5
diff --git a/nixpkgs/nixos/modules/services/web-apps/kasmweb/default.nix b/nixpkgs/nixos/modules/services/web-apps/kasmweb/default.nix
index 0d78025ecf0f..9f1da78f3c76 100644
--- a/nixpkgs/nixos/modules/services/web-apps/kasmweb/default.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/kasmweb/default.nix
@@ -5,12 +5,12 @@ let
 in
 {
   options.services.kasmweb = {
-    enable = lib.mkEnableOption (lib.mdDoc "kasmweb");
+    enable = lib.mkEnableOption "kasmweb";
 
     networkSubnet = lib.mkOption {
       default = "172.20.0.0/16";
       type = lib.types.str;
-      description = lib.mdDoc ''
+      description = ''
         The network subnet to use for the containers.
       '';
     };
@@ -19,14 +19,14 @@ in
       user = lib.mkOption {
         default = "kasmweb";
         type = lib.types.str;
-        description = lib.mdDoc ''
+        description = ''
           Username to use for the postgres database.
         '';
       };
       password = lib.mkOption {
         default = "kasmweb";
         type = lib.types.str;
-        description = lib.mdDoc ''
+        description = ''
           password to use for the postgres database.
         '';
       };
@@ -35,7 +35,7 @@ in
     redisPassword = lib.mkOption {
       default = "kasmweb";
       type = lib.types.str;
-      description = lib.mdDoc ''
+      description = ''
         password to use for the redis cache.
       '';
     };
@@ -43,7 +43,7 @@ in
     defaultAdminPassword = lib.mkOption {
       default = "kasmweb";
       type = lib.types.str;
-      description = lib.mdDoc ''
+      description = ''
         default admin password to use.
       '';
     };
@@ -51,7 +51,7 @@ in
     defaultUserPassword = lib.mkOption {
       default = "kasmweb";
       type = lib.types.str;
-      description = lib.mdDoc ''
+      description = ''
         default user password to use.
       '';
     };
@@ -59,7 +59,7 @@ in
     defaultManagerToken = lib.mkOption {
       default = "kasmweb";
       type = lib.types.str;
-      description = lib.mdDoc ''
+      description = ''
         default manager token to use.
       '';
     };
@@ -67,7 +67,7 @@ in
     defaultGuacToken = lib.mkOption {
       default = "kasmweb";
       type = lib.types.str;
-      description = lib.mdDoc ''
+      description = ''
         default guac token to use.
       '';
     };
@@ -75,7 +75,7 @@ in
     defaultRegistrationToken = lib.mkOption {
       default = "kasmweb";
       type = lib.types.str;
-      description = lib.mdDoc ''
+      description = ''
         default registration token to use.
       '';
     };
@@ -83,7 +83,7 @@ in
     datastorePath = lib.mkOption {
       type = lib.types.str;
       default = "/var/lib/kasmweb";
-      description = lib.mdDoc ''
+      description = ''
         The directory used to store all data for kasmweb.
       '';
     };
@@ -91,7 +91,7 @@ in
     listenAddress = lib.mkOption {
       type = lib.types.str;
       default = "0.0.0.0";
-      description = lib.mdDoc ''
+      description = ''
         The address on which kasmweb should listen.
       '';
     };
@@ -99,7 +99,7 @@ in
     listenPort = lib.mkOption {
       type = lib.types.int;
       default = 443;
-      description = lib.mdDoc ''
+      description = ''
         The port on which kasmweb should listen.
       '';
     };
@@ -107,7 +107,7 @@ in
     sslCertificate = lib.mkOption {
       type = lib.types.nullOr lib.types.path;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         The SSL certificate to be used for kasmweb.
       '';
     };
@@ -115,7 +115,7 @@ in
     sslCertificateKey = lib.mkOption {
       type = lib.types.nullOr lib.types.path;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         The SSL certificate's key to be used for kasmweb. Make sure to specify
         this as a string and not a literal path, so that it is not accidentally
         included in your nixstore.
diff --git a/nixpkgs/nixos/modules/services/web-apps/kavita.nix b/nixpkgs/nixos/modules/services/web-apps/kavita.nix
index c90697bcfa8b..fe22d28af5d0 100644
--- a/nixpkgs/nixos/modules/services/web-apps/kavita.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/kavita.nix
@@ -15,12 +15,12 @@ in
   ];
 
   options.services.kavita = {
-    enable = lib.mkEnableOption (lib.mdDoc "Kavita reading server");
+    enable = lib.mkEnableOption "Kavita reading server";
 
     user = lib.mkOption {
       type = lib.types.str;
       default = "kavita";
-      description = lib.mdDoc "User account under which Kavita runs.";
+      description = "User account under which Kavita runs.";
     };
 
     package = lib.mkPackageOption pkgs "kavita" { };
@@ -28,20 +28,20 @@ in
     dataDir = lib.mkOption {
       default = "/var/lib/kavita";
       type = lib.types.str;
-      description = lib.mdDoc "The directory where Kavita stores its state.";
+      description = "The directory where Kavita stores its state.";
     };
 
     tokenKeyFile = lib.mkOption {
       type = lib.types.path;
-      description = lib.mdDoc ''
-        A file containing the TokenKey, a secret with at 128+ bits.
-        It can be generated with `head -c 32 /dev/urandom | base64`.
+      description = ''
+        A file containing the TokenKey, a secret with at 512+ bits.
+        It can be generated with `head -c 64 /dev/urandom | base64 --wrap=0`.
       '';
     };
 
     settings = lib.mkOption {
       default = { };
-      description = lib.mdDoc ''
+      description = ''
         Kavita configuration options, as configured in {file}`appsettings.json`.
       '';
       type = lib.types.submodule {
@@ -51,13 +51,13 @@ in
           Port = lib.mkOption {
             default = 5000;
             type = lib.types.port;
-            description = lib.mdDoc "Port to bind to.";
+            description = "Port to bind to.";
           };
 
           IpAddresses = lib.mkOption {
             default = "0.0.0.0,::";
             type = lib.types.commas;
-            description = lib.mdDoc ''
+            description = ''
               IP Addresses to bind to. The default is to bind to all IPv4 and IPv6 addresses.
             '';
           };
diff --git a/nixpkgs/nixos/modules/services/web-apps/keycloak.nix b/nixpkgs/nixos/modules/services/web-apps/keycloak.nix
index 6d2948913b19..cf1282b3d4cf 100644
--- a/nixpkgs/nixos/modules/services/web-apps/keycloak.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/keycloak.nix
@@ -99,7 +99,7 @@ in
         type = bool;
         default = false;
         example = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether to enable the Keycloak identity and access management
           server.
         '';
@@ -110,7 +110,7 @@ in
         default = null;
         example = "/run/keys/ssl_cert";
         apply = assertStringPath "sslCertificate";
-        description = lib.mdDoc ''
+        description = ''
           The path to a PEM formatted certificate to use for TLS/SSL
           connections.
         '';
@@ -121,7 +121,7 @@ in
         default = null;
         example = "/run/keys/ssl_key";
         apply = assertStringPath "sslCertificateKey";
-        description = lib.mdDoc ''
+        description = ''
           The path to a PEM formatted private key to use for TLS/SSL
           connections.
         '';
@@ -130,7 +130,7 @@ in
       plugins = lib.mkOption {
         type = lib.types.listOf lib.types.path;
         default = [ ];
-        description = lib.mdDoc ''
+        description = ''
           Keycloak plugin jar, ear files or derivations containing
           them. Packaged plugins are available through
           `pkgs.keycloak.plugins`.
@@ -142,7 +142,7 @@ in
           type = enum [ "mysql" "mariadb" "postgresql" ];
           default = "postgresql";
           example = "mariadb";
-          description = lib.mdDoc ''
+          description = ''
             The type of database Keycloak should connect to.
           '';
         };
@@ -150,7 +150,7 @@ in
         host = mkOption {
           type = str;
           default = "localhost";
-          description = lib.mdDoc ''
+          description = ''
             Hostname of the database to connect to.
           '';
         };
@@ -167,7 +167,7 @@ in
             type = port;
             default = dbPorts.${cfg.database.type};
             defaultText = literalMD "default port of selected database";
-            description = lib.mdDoc ''
+            description = ''
               Port of the database to connect to.
             '';
           };
@@ -176,7 +176,7 @@ in
           type = bool;
           default = cfg.database.host != "localhost";
           defaultText = literalExpression ''config.${opt.database.host} != "localhost"'';
-          description = lib.mdDoc ''
+          description = ''
             Whether the database connection should be secured by SSL /
             TLS.
           '';
@@ -185,7 +185,7 @@ in
         caCert = mkOption {
           type = nullOr path;
           default = null;
-          description = lib.mdDoc ''
+          description = ''
             The SSL / TLS CA certificate that verifies the identity of the
             database server.
 
@@ -200,7 +200,7 @@ in
         createLocally = mkOption {
           type = bool;
           default = true;
-          description = lib.mdDoc ''
+          description = ''
             Whether a database should be automatically created on the
             local host. Set this to false if you plan on provisioning a
             local database yourself. This has no effect if
@@ -211,7 +211,7 @@ in
         name = mkOption {
           type = str;
           default = "keycloak";
-          description = lib.mdDoc ''
+          description = ''
             Database name to use when connecting to an external or
             manually provisioned database; has no effect when a local
             database is automatically provisioned.
@@ -225,7 +225,7 @@ in
         username = mkOption {
           type = str;
           default = "keycloak";
-          description = lib.mdDoc ''
+          description = ''
             Username to use when connecting to an external or manually
             provisioned database; has no effect when a local database is
             automatically provisioned.
@@ -240,7 +240,7 @@ in
           type = path;
           example = "/run/keys/db_password";
           apply = assertStringPath "passwordFile";
-          description = lib.mdDoc ''
+          description = ''
             The path to a file containing the database password.
           '';
         };
@@ -251,7 +251,7 @@ in
       initialAdminPassword = mkOption {
         type = str;
         default = "changeme";
-        description = lib.mdDoc ''
+        description = ''
           Initial password set for the `admin`
           user. The password is not stored safely and should be changed
           immediately in the admin panel.
@@ -261,7 +261,7 @@ in
       themes = mkOption {
         type = attrsOf package;
         default = { };
-        description = lib.mdDoc ''
+        description = ''
           Additional theme packages for Keycloak. Each theme is linked into
           subdirectory with a corresponding attribute name.
 
@@ -281,7 +281,7 @@ in
               type = str;
               default = "0.0.0.0";
               example = "127.0.0.1";
-              description = lib.mdDoc ''
+              description = ''
                 On which address Keycloak should accept new connections.
               '';
             };
@@ -290,7 +290,7 @@ in
               type = port;
               default = 80;
               example = 8080;
-              description = lib.mdDoc ''
+              description = ''
                 On which port Keycloak should listen for new HTTP connections.
               '';
             };
@@ -299,7 +299,7 @@ in
               type = port;
               default = 443;
               example = 8443;
-              description = lib.mdDoc ''
+              description = ''
                 On which port Keycloak should listen for new HTTPS connections.
               '';
             };
@@ -309,7 +309,7 @@ in
               default = "/";
               example = "/auth";
               apply = x: if !(hasPrefix "/") x then "/" + x else x;
-              description = lib.mdDoc ''
+              description = ''
                 The path relative to `/` for serving
                 resources.
 
@@ -331,7 +331,7 @@ in
               type = nullOr str;
               default = null;
               example = "keycloak.example.com";
-              description = lib.mdDoc ''
+              description = ''
                 The hostname part of the public URL used as base for
                 all frontend requests.
 
@@ -344,7 +344,7 @@ in
               type = bool;
               default = false;
               example = true;
-              description = lib.mdDoc ''
+              description = ''
                 Whether Keycloak should force all requests to go
                 through the frontend URL. By default, Keycloak allows
                 backend requests to instead use its local hostname or
@@ -360,7 +360,7 @@ in
               type = enum [ "edge" "reencrypt" "passthrough" "none" ];
               default = "none";
               example = "edge";
-              description = lib.mdDoc ''
+              description = ''
                 The proxy address forwarding mode if the server is
                 behind a reverse proxy.
 
@@ -389,7 +389,7 @@ in
           }
         '';
 
-        description = lib.mdDoc ''
+        description = ''
           Configuration options corresponding to parameters set in
           {file}`conf/keycloak.conf`.
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/lanraragi.nix b/nixpkgs/nixos/modules/services/web-apps/lanraragi.nix
index 6703da005ab0..7b7fb01918bb 100644
--- a/nixpkgs/nixos/modules/services/web-apps/lanraragi.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/lanraragi.nix
@@ -8,20 +8,20 @@ in
 
   options.services = {
     lanraragi = {
-      enable = lib.mkEnableOption (lib.mdDoc "LANraragi");
+      enable = lib.mkEnableOption "LANraragi";
       package = lib.mkPackageOption pkgs "lanraragi" { };
 
       port = lib.mkOption {
         type = lib.types.port;
         default = 3000;
-        description = lib.mdDoc "Port for LANraragi's web interface.";
+        description = "Port for LANraragi's web interface.";
       };
 
       passwordFile = lib.mkOption {
         type = lib.types.nullOr lib.types.path;
         default = null;
         example = "/run/keys/lanraragi-password";
-        description = lib.mdDoc ''
+        description = ''
           A file containing the password for LANraragi's admin interface.
         '';
       };
@@ -30,13 +30,13 @@ in
         port = lib.mkOption {
           type = lib.types.port;
           default = 6379;
-          description = lib.mdDoc "Port for LANraragi's Redis server.";
+          description = "Port for LANraragi's Redis server.";
         };
         passwordFile = lib.mkOption {
           type = lib.types.nullOr lib.types.path;
           default = null;
           example = "/run/keys/redis-lanraragi-password";
-          description = lib.mdDoc ''
+          description = ''
             A file containing the password for LANraragi's Redis server.
           '';
         };
diff --git a/nixpkgs/nixos/modules/services/web-apps/lemmy.nix b/nixpkgs/nixos/modules/services/web-apps/lemmy.nix
index 968dcac93fab..3185f9a4263c 100644
--- a/nixpkgs/nixos/modules/services/web-apps/lemmy.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/lemmy.nix
@@ -14,7 +14,7 @@ in
 
   options.services.lemmy = {
 
-    enable = mkEnableOption (lib.mdDoc "lemmy a federated alternative to reddit in rust");
+    enable = mkEnableOption "lemmy a federated alternative to reddit in rust";
 
     server = {
       package = mkPackageOption pkgs "lemmy-server" {};
@@ -26,50 +26,50 @@ in
       port = mkOption {
         type = types.port;
         default = 1234;
-        description = lib.mdDoc "Port where lemmy-ui should listen for incoming requests.";
+        description = "Port where lemmy-ui should listen for incoming requests.";
       };
     };
 
-    caddy.enable = mkEnableOption (lib.mdDoc "exposing lemmy with the caddy reverse proxy");
-    nginx.enable = mkEnableOption (lib.mdDoc "exposing lemmy with the nginx reverse proxy");
+    caddy.enable = mkEnableOption "exposing lemmy with the caddy reverse proxy";
+    nginx.enable = mkEnableOption "exposing lemmy with the nginx reverse proxy";
 
     database = {
-      createLocally = mkEnableOption (lib.mdDoc "creation of database on the instance");
+      createLocally = mkEnableOption "creation of database on the instance";
 
       uri = mkOption {
         type = with types; nullOr str;
         default = null;
-        description = lib.mdDoc "The connection URI to use. Takes priority over the configuration file if set.";
+        description = "The connection URI to use. Takes priority over the configuration file if set.";
       };
 
       uriFile = mkOption {
         type = with types; nullOr path;
         default = null;
-        description = lib.mdDoc "File which contains the database uri.";
+        description = "File which contains the database uri.";
       };
     };
 
     pictrsApiKeyFile = mkOption {
       type = with types; nullOr path;
       default = null;
-      description = lib.mdDoc "File which contains the value of `pictrs.api_key`.";
+      description = "File which contains the value of `pictrs.api_key`.";
     };
 
     smtpPasswordFile = mkOption {
       type = with types; nullOr path;
       default = null;
-      description = lib.mdDoc "File which contains the value of `email.smtp_password`.";
+      description = "File which contains the value of `email.smtp_password`.";
     };
 
     adminPasswordFile = mkOption {
       type = with types; nullOr path;
       default = null;
-      description = lib.mdDoc "File which contains the value of `setup.admin_password`.";
+      description = "File which contains the value of `setup.admin_password`.";
     };
 
     settings = mkOption {
       default = { };
-      description = lib.mdDoc "Lemmy configuration";
+      description = "Lemmy configuration";
 
       type = types.submodule {
         freeformType = settingsFormat.type;
@@ -77,25 +77,25 @@ in
         options.hostname = mkOption {
           type = types.str;
           default = null;
-          description = lib.mdDoc "The domain name of your instance (eg 'lemmy.ml').";
+          description = "The domain name of your instance (eg 'lemmy.ml').";
         };
 
         options.port = mkOption {
           type = types.port;
           default = 8536;
-          description = lib.mdDoc "Port where lemmy should listen for incoming requests.";
+          description = "Port where lemmy should listen for incoming requests.";
         };
 
         options.captcha = {
           enabled = mkOption {
             type = types.bool;
             default = true;
-            description = lib.mdDoc "Enable Captcha.";
+            description = "Enable Captcha.";
           };
           difficulty = mkOption {
             type = types.enum [ "easy" "medium" "hard" ];
             default = "medium";
-            description = lib.mdDoc "The difficultly of the captcha to solve.";
+            description = "The difficultly of the captcha to solve.";
           };
         };
       };
diff --git a/nixpkgs/nixos/modules/services/web-apps/limesurvey.nix b/nixpkgs/nixos/modules/services/web-apps/limesurvey.nix
index 920e6928ef5c..cdd60f572b99 100644
--- a/nixpkgs/nixos/modules/services/web-apps/limesurvey.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/limesurvey.nix
@@ -2,7 +2,7 @@
 
 let
 
-  inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption;
+  inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption mkPackageOption;
   inherit (lib) literalExpression mapAttrs optional optionalString types;
 
   cfg = config.services.limesurvey;
@@ -12,8 +12,6 @@ let
   group = config.services.httpd.group;
   stateDir = "/var/lib/limesurvey";
 
-  pkg = pkgs.limesurvey;
-
   configType = with types; oneOf [ (attrsOf configType) str int bool ] // {
     description = "limesurvey config type (str, int, bool or attribute set thereof)";
   };
@@ -32,12 +30,14 @@ in
   # interface
 
   options.services.limesurvey = {
-    enable = mkEnableOption (lib.mdDoc "Limesurvey web application");
+    enable = mkEnableOption "Limesurvey web application";
+
+    package = mkPackageOption pkgs "limesurvey" { };
 
     encryptionKey = mkOption {
       type = types.str;
       default = "E17687FC77CEE247F0E22BB3ECF27FDE8BEC310A892347EC13013ABA11AA7EB5";
-      description = lib.mdDoc ''
+      description = ''
         This is a 32-byte key used to encrypt variables in the database.
         You _must_ change this from the default value.
       '';
@@ -46,7 +46,7 @@ in
     encryptionNonce = mkOption {
       type = types.str;
       default = "1ACC8555619929DB91310BE848025A427B0F364A884FFA77";
-      description = lib.mdDoc ''
+      description = ''
         This is a 24-byte nonce used to encrypt variables in the database.
         You _must_ change this from the default value.
       '';
@@ -57,45 +57,45 @@ in
         type = types.enum [ "mysql" "pgsql" "odbc" "mssql" ];
         example = "pgsql";
         default = "mysql";
-        description = lib.mdDoc "Database engine to use.";
+        description = "Database engine to use.";
       };
 
       dbEngine = mkOption {
         type = types.enum [ "MyISAM" "InnoDB" ];
         default = "InnoDB";
-        description = lib.mdDoc "Database storage engine to use.";
+        description = "Database storage engine to use.";
       };
 
       host = mkOption {
         type = types.str;
         default = "localhost";
-        description = lib.mdDoc "Database host address.";
+        description = "Database host address.";
       };
 
       port = mkOption {
         type = types.port;
         default = if cfg.database.type == "pgsql" then 5442 else 3306;
         defaultText = literalExpression "3306";
-        description = lib.mdDoc "Database host port.";
+        description = "Database host port.";
       };
 
       name = mkOption {
         type = types.str;
         default = "limesurvey";
-        description = lib.mdDoc "Database name.";
+        description = "Database name.";
       };
 
       user = mkOption {
         type = types.str;
         default = "limesurvey";
-        description = lib.mdDoc "Database user.";
+        description = "Database user.";
       };
 
       passwordFile = mkOption {
         type = types.nullOr types.path;
         default = null;
         example = "/run/keys/limesurvey-dbpassword";
-        description = lib.mdDoc ''
+        description = ''
           A file containing the password corresponding to
           {option}`database.user`.
         '';
@@ -109,14 +109,14 @@ in
           else null
         ;
         defaultText = literalExpression "/run/mysqld/mysqld.sock";
-        description = lib.mdDoc "Path to the unix socket file to use for authentication.";
+        description = "Path to the unix socket file to use for authentication.";
       };
 
       createLocally = mkOption {
         type = types.bool;
         default = cfg.database.type == "mysql";
         defaultText = literalExpression "true";
-        description = lib.mdDoc ''
+        description = ''
           Create the database and database user locally.
           This currently only applies if database type "mysql" is selected.
         '';
@@ -133,7 +133,7 @@ in
           enableACME = true;
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         Apache configuration can be done by adapting `services.httpd.virtualHosts.<name>`.
         See [](#opt-services.httpd.virtualHosts) for further information.
       '';
@@ -149,7 +149,7 @@ in
         "pm.max_spare_servers" = 4;
         "pm.max_requests" = 500;
       };
-      description = lib.mdDoc ''
+      description = ''
         Options for the LimeSurvey PHP pool. See the documentation on `php-fpm.conf`
         for details on configuration directives.
       '';
@@ -158,7 +158,7 @@ in
     config = mkOption {
       type = configType;
       default = {};
-      description = lib.mdDoc ''
+      description = ''
         LimeSurvey configuration. Refer to
         <https://manual.limesurvey.org/Optional_settings>
         for details on supported values.
@@ -240,7 +240,7 @@ in
       adminAddr = mkDefault cfg.virtualHost.adminAddr;
       extraModules = [ "proxy_fcgi" ];
       virtualHosts.${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost {
-        documentRoot = mkForce "${pkg}/share/limesurvey";
+        documentRoot = mkForce "${cfg.package}/share/limesurvey";
         extraConfig = ''
           Alias "/tmp" "${stateDir}/tmp"
           <Directory "${stateDir}">
@@ -256,7 +256,7 @@ in
             Options -Indexes
           </Directory>
 
-          <Directory "${pkg}/share/limesurvey">
+          <Directory "${cfg.package}/share/limesurvey">
             <FilesMatch "\.php$">
               <If "-f %{REQUEST_FILENAME}">
                 SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
@@ -277,7 +277,7 @@ in
       "d ${stateDir}/tmp/assets 0750 ${user} ${group} - -"
       "d ${stateDir}/tmp/runtime 0750 ${user} ${group} - -"
       "d ${stateDir}/tmp/upload 0750 ${user} ${group} - -"
-      "C ${stateDir}/upload 0750 ${user} ${group} - ${pkg}/share/limesurvey/upload"
+      "C ${stateDir}/upload 0750 ${user} ${group} - ${cfg.package}/share/limesurvey/upload"
     ];
 
     systemd.services.limesurvey-init = {
@@ -288,8 +288,8 @@ in
       environment.LIMESURVEY_CONFIG = limesurveyConfig;
       script = ''
         # update or install the database as required
-        ${pkgs.php81}/bin/php ${pkg}/share/limesurvey/application/commands/console.php updatedb || \
-        ${pkgs.php81}/bin/php ${pkg}/share/limesurvey/application/commands/console.php install admin password admin admin@example.com verbose
+        ${pkgs.php81}/bin/php ${cfg.package}/share/limesurvey/application/commands/console.php updatedb || \
+        ${pkgs.php81}/bin/php ${cfg.package}/share/limesurvey/application/commands/console.php install admin password admin admin@example.com verbose
       '';
       serviceConfig = {
         User = user;
diff --git a/nixpkgs/nixos/modules/services/web-apps/mainsail.nix b/nixpkgs/nixos/modules/services/web-apps/mainsail.nix
index 95de2c5640b4..cfe4c5250b55 100644
--- a/nixpkgs/nixos/modules/services/web-apps/mainsail.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/mainsail.nix
@@ -6,14 +6,14 @@ let
 in
 {
   options.services.mainsail = {
-    enable = mkEnableOption (lib.mdDoc "a modern and responsive user interface for Klipper");
+    enable = mkEnableOption "a modern and responsive user interface for Klipper";
 
     package = mkPackageOption pkgs "mainsail" { };
 
     hostName = mkOption {
       type = types.str;
       default = "localhost";
-      description = lib.mdDoc "Hostname to serve mainsail on";
+      description = "Hostname to serve mainsail on";
     };
 
     nginx = mkOption {
@@ -25,7 +25,7 @@ in
           serverAliases = [ "mainsail.''${config.networking.domain}" ];
         }
       '';
-      description = lib.mdDoc "Extra configuration for the nginx virtual host of mainsail.";
+      description = "Extra configuration for the nginx virtual host of mainsail.";
     };
   };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix
index 7fc710c6fcec..570f2770fb29 100644
--- a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix
@@ -187,10 +187,10 @@ in {
 
   options = {
     services.mastodon = {
-      enable = lib.mkEnableOption (lib.mdDoc "Mastodon, a federated social network server");
+      enable = lib.mkEnableOption "Mastodon, a federated social network server";
 
       configureNginx = lib.mkOption {
-        description = lib.mdDoc ''
+        description = ''
           Configure nginx as a reverse proxy for mastodon.
           Note that this makes some assumptions on your setup, and sets settings that will
           affect other virtualHosts running on your nginx instance, if any.
@@ -213,7 +213,7 @@ in {
       };
 
       user = lib.mkOption {
-        description = lib.mdDoc ''
+        description = ''
           User under which mastodon runs. If it is set to "mastodon",
           that user will be created, otherwise it should be set to the
           name of a user created elsewhere.
@@ -226,7 +226,7 @@ in {
       };
 
       group = lib.mkOption {
-        description = lib.mdDoc ''
+        description = ''
           Group under which mastodon runs.
         '';
         type = lib.types.str;
@@ -234,7 +234,7 @@ in {
       };
 
       streamingProcesses = lib.mkOption {
-        description = lib.mdDoc ''
+        description = ''
           Number of processes used by the mastodon-streaming service.
           Please define this explicitly, recommended is the amount of your CPU cores minus one.
         '';
@@ -243,44 +243,44 @@ in {
       };
 
       webPort = lib.mkOption {
-        description = lib.mdDoc "TCP port used by the mastodon-web service.";
+        description = "TCP port used by the mastodon-web service.";
         type = lib.types.port;
         default = 55001;
       };
       webProcesses = lib.mkOption {
-        description = lib.mdDoc "Processes used by the mastodon-web service.";
+        description = "Processes used by the mastodon-web service.";
         type = lib.types.int;
         default = 2;
       };
       webThreads = lib.mkOption {
-        description = lib.mdDoc "Threads per process used by the mastodon-web service.";
+        description = "Threads per process used by the mastodon-web service.";
         type = lib.types.int;
         default = 5;
       };
 
       sidekiqPort = lib.mkOption {
-        description = lib.mdDoc "TCP port used by the mastodon-sidekiq service.";
+        description = "TCP port used by the mastodon-sidekiq service.";
         type = lib.types.port;
         default = 55002;
       };
 
       sidekiqThreads = lib.mkOption {
-        description = lib.mdDoc "Worker threads used by the mastodon-sidekiq-all service. If `sidekiqProcesses` is configured and any processes specify null `threads`, this value is used.";
+        description = "Worker threads used by the mastodon-sidekiq-all service. If `sidekiqProcesses` is configured and any processes specify null `threads`, this value is used.";
         type = lib.types.int;
         default = 25;
       };
 
       sidekiqProcesses = lib.mkOption {
-        description = lib.mdDoc "How many Sidekiq processes should be used to handle background jobs, and which job classes they handle. *Read the [upstream documentation](https://docs.joinmastodon.org/admin/scaling/#sidekiq) before configuring this!*";
+        description = "How many Sidekiq processes should be used to handle background jobs, and which job classes they handle. *Read the [upstream documentation](https://docs.joinmastodon.org/admin/scaling/#sidekiq) before configuring this!*";
         type = with lib.types; attrsOf (submodule {
           options = {
             jobClasses = lib.mkOption {
               type = listOf (enum [ "default" "push" "pull" "mailers" "scheduler" "ingress" ]);
-              description = lib.mdDoc "If not empty, which job classes should be executed by this process. *Only one process should handle the 'scheduler' class. If left empty, this process will handle the 'scheduler' class.*";
+              description = "If not empty, which job classes should be executed by this process. *Only one process should handle the 'scheduler' class. If left empty, this process will handle the 'scheduler' class.*";
             };
             threads = lib.mkOption {
               type = nullOr int;
-              description = lib.mdDoc "Number of threads this process should use for executing jobs. If null, the configured `sidekiqThreads` are used.";
+              description = "Number of threads this process should use for executing jobs. If null, the configured `sidekiqThreads` are used.";
             };
           };
         });
@@ -311,7 +311,7 @@ in {
       };
 
       vapidPublicKeyFile = lib.mkOption {
-        description = lib.mdDoc ''
+        description = ''
           Path to file containing the public key used for Web Push
           Voluntary Application Server Identification.  A new keypair can
           be generated by running:
@@ -326,13 +326,13 @@ in {
       };
 
       localDomain = lib.mkOption {
-        description = lib.mdDoc "The domain serving your Mastodon instance.";
+        description = "The domain serving your Mastodon instance.";
         example = "social.example.org";
         type = lib.types.str;
       };
 
       secretKeyBaseFile = lib.mkOption {
-        description = lib.mdDoc ''
+        description = ''
           Path to file containing the secret key base.
           A new secret key base can be generated by running:
 
@@ -345,7 +345,7 @@ in {
       };
 
       otpSecretFile = lib.mkOption {
-        description = lib.mdDoc ''
+        description = ''
           Path to file containing the OTP secret.
           A new OTP secret can be generated by running:
 
@@ -358,7 +358,7 @@ in {
       };
 
       vapidPrivateKeyFile = lib.mkOption {
-        description = lib.mdDoc ''
+        description = ''
           Path to file containing the private key used for Web Push
           Voluntary Application Server Identification.  A new keypair can
           be generated by running:
@@ -373,7 +373,7 @@ in {
       };
 
       trustedProxy = lib.mkOption {
-        description = lib.mdDoc ''
+        description = ''
           You need to set it to the IP from which your reverse proxy sends requests to Mastodon's web process,
           otherwise Mastodon will record the reverse proxy's own IP as the IP of all requests, which would be
           bad because IP addresses are used for important rate limits and security functions.
@@ -383,7 +383,7 @@ in {
       };
 
       enableUnixSocket = lib.mkOption {
-        description = lib.mdDoc ''
+        description = ''
           Instead of binding to an IP address like 127.0.0.1, you may bind to a Unix socket. This variable
           is process-specific, e.g. you need different values for every process, and it works for both web (Puma)
           processes and streaming API (Node.js) processes.
@@ -394,32 +394,32 @@ in {
 
       redis = {
         createLocally = lib.mkOption {
-          description = lib.mdDoc "Configure local Redis server for Mastodon.";
+          description = "Configure local Redis server for Mastodon.";
           type = lib.types.bool;
           default = true;
         };
 
         host = lib.mkOption {
-          description = lib.mdDoc "Redis host.";
+          description = "Redis host.";
           type = lib.types.str;
           default = "127.0.0.1";
         };
 
         port = lib.mkOption {
-          description = lib.mdDoc "Redis port.";
+          description = "Redis port.";
           type = lib.types.port;
           default = 31637;
         };
 
         passwordFile = lib.mkOption {
-          description = lib.mdDoc "A file containing the password for Redis database.";
+          description = "A file containing the password for Redis database.";
           type = lib.types.nullOr lib.types.path;
           default = null;
           example = "/run/keys/mastodon-redis-password";
         };
 
         enableUnixSocket = lib.mkOption {
-          description = lib.mdDoc "Use Unix socket";
+          description = "Use Unix socket";
           type = lib.types.bool;
           default = true;
         };
@@ -427,7 +427,7 @@ in {
 
       database = {
         createLocally = lib.mkOption {
-          description = lib.mdDoc "Configure local PostgreSQL database server for Mastodon.";
+          description = "Configure local PostgreSQL database server for Mastodon.";
           type = lib.types.bool;
           default = true;
         };
@@ -436,7 +436,7 @@ in {
           type = lib.types.str;
           default = "/run/postgresql";
           example = "192.168.23.42";
-          description = lib.mdDoc "Database host address or unix socket.";
+          description = "Database host address or unix socket.";
         };
 
         port = lib.mkOption {
@@ -447,26 +447,26 @@ in {
             then null
             else 5432
           '';
-          description = lib.mdDoc "Database host port.";
+          description = "Database host port.";
         };
 
         name = lib.mkOption {
           type = lib.types.str;
           default = "mastodon";
-          description = lib.mdDoc "Database name.";
+          description = "Database name.";
         };
 
         user = lib.mkOption {
           type = lib.types.str;
           default = "mastodon";
-          description = lib.mdDoc "Database user.";
+          description = "Database user.";
         };
 
         passwordFile = lib.mkOption {
           type = lib.types.nullOr lib.types.path;
           default = null;
           example = "/var/lib/mastodon/secrets/db-password";
-          description = lib.mdDoc ''
+          description = ''
             A file containing the password corresponding to
             {option}`database.user`.
           '';
@@ -475,31 +475,31 @@ in {
 
       smtp = {
         createLocally = lib.mkOption {
-          description = lib.mdDoc "Configure local Postfix SMTP server for Mastodon.";
+          description = "Configure local Postfix SMTP server for Mastodon.";
           type = lib.types.bool;
           default = true;
         };
 
         authenticate = lib.mkOption {
-          description = lib.mdDoc "Authenticate with the SMTP server using username and password.";
+          description = "Authenticate with the SMTP server using username and password.";
           type = lib.types.bool;
           default = false;
         };
 
         host = lib.mkOption {
-          description = lib.mdDoc "SMTP host used when sending emails to users.";
+          description = "SMTP host used when sending emails to users.";
           type = lib.types.str;
           default = "127.0.0.1";
         };
 
         port = lib.mkOption {
-          description = lib.mdDoc "SMTP port used when sending emails to users.";
+          description = "SMTP port used when sending emails to users.";
           type = lib.types.port;
           default = 25;
         };
 
         fromAddress = lib.mkOption {
-          description = lib.mdDoc ''"From" address used when sending Emails to users.'';
+          description = ''"From" address used when sending Emails to users.'';
           type = lib.types.str;
         };
 
@@ -507,14 +507,14 @@ in {
           type = lib.types.nullOr lib.types.str;
           default = null;
           example = "mastodon@example.com";
-          description = lib.mdDoc "SMTP login name.";
+          description = "SMTP login name.";
         };
 
         passwordFile = lib.mkOption {
           type = lib.types.nullOr lib.types.path;
           default = null;
           example = "/var/lib/mastodon/secrets/smtp-password";
-          description = lib.mdDoc ''
+          description = ''
             Path to file containing the SMTP password.
           '';
         };
@@ -522,7 +522,7 @@ in {
 
       elasticsearch = {
         host = lib.mkOption {
-          description = lib.mdDoc ''
+          description = ''
             Elasticsearch host.
             If it is not null, Elasticsearch full text search will be enabled.
           '';
@@ -531,13 +531,13 @@ in {
         };
 
         port = lib.mkOption {
-          description = lib.mdDoc "Elasticsearch port.";
+          description = "Elasticsearch port.";
           type = lib.types.port;
           default = 9200;
         };
 
         preset = lib.mkOption {
-          description = lib.mdDoc ''
+          description = ''
             It controls the ElasticSearch indices configuration (number of shards and replica).
           '';
           type = lib.types.enum [ "single_node_cluster" "small_cluster" "large_cluster" ];
@@ -546,14 +546,14 @@ in {
         };
 
         user = lib.mkOption {
-          description = lib.mdDoc "Used for optionally authenticating with Elasticsearch.";
+          description = "Used for optionally authenticating with Elasticsearch.";
           type = lib.types.nullOr lib.types.str;
           default = null;
           example = "elasticsearch-mastodon";
         };
 
         passwordFile = lib.mkOption {
-          description = lib.mdDoc ''
+          description = ''
             Path to file containing password for optionally authenticating with Elasticsearch.
           '';
           type = lib.types.nullOr lib.types.path;
@@ -566,13 +566,13 @@ in {
         type = lib.types.package;
         default = pkgs.mastodon;
         defaultText = lib.literalExpression "pkgs.mastodon";
-        description = lib.mdDoc "Mastodon package to use.";
+        description = "Mastodon package to use.";
       };
 
       extraConfig = lib.mkOption {
         type = lib.types.attrs;
         default = {};
-        description = lib.mdDoc ''
+        description = ''
           Extra environment variables to pass to all mastodon services.
         '';
       };
@@ -580,7 +580,7 @@ in {
       extraEnvFiles = lib.mkOption {
         type = with lib.types; listOf path;
         default = [];
-        description = lib.mdDoc ''
+        description = ''
           Extra environment files to pass to all mastodon services. Useful for passing down environmental secrets.
         '';
         example = [ "/etc/mastodon/s3config.env" ];
@@ -589,7 +589,7 @@ in {
       automaticMigrations = lib.mkOption {
         type = lib.types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Do automatic database migrations.
         '';
       };
@@ -599,7 +599,7 @@ in {
           type = lib.types.bool;
           default = true;
           example = false;
-          description = lib.mdDoc ''
+          description = ''
             Automatically remove remote media attachments and preview cards older than the configured amount of days.
 
             Recommended in https://docs.joinmastodon.org/admin/setup/.
@@ -610,7 +610,7 @@ in {
           type = lib.types.str;
           default = "daily";
           example = "hourly";
-          description = lib.mdDoc ''
+          description = ''
             How often to remove remote media.
 
             The format is described in {manpage}`systemd.time(7)`.
@@ -621,7 +621,7 @@ in {
           type = lib.types.int;
           default = 30;
           example = 14;
-          description = lib.mdDoc ''
+          description = ''
             How old remote media needs to be in order to be removed.
           '';
         };
@@ -742,11 +742,16 @@ in {
         umask 077
         export PGPASSWORD="$(cat '${cfg.database.passwordFile}')"
       '' + ''
-        if [ `psql -c \
-                "select count(*) from pg_class c \
-                join pg_namespace s on s.oid = c.relnamespace \
-                where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \
-                and s.nspname not like 'pg_temp%';" | sed -n 3p` -eq 0 ]; then
+        result="$(psql -t --csv -c \
+            "select count(*) from pg_class c \
+            join pg_namespace s on s.oid = c.relnamespace \
+            where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \
+            and s.nspname not like 'pg_temp%';")" || error_code=$?
+        if [ "''${error_code:-0}" -ne 0 ]; then
+          echo "Failure checking if database is seeded. psql gave exit code $error_code"
+          exit "$error_code"
+        fi
+        if [ "$result" -eq 0 ]; then
           echo "Seeding database"
           SAFETY_ASSURED=1 rails db:schema:load
           rails db:seed
diff --git a/nixpkgs/nixos/modules/services/web-apps/matomo.nix b/nixpkgs/nixos/modules/services/web-apps/matomo.nix
index fef5dc82de04..722745dbdb5d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/matomo.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/matomo.nix
@@ -30,7 +30,7 @@ in {
       enable = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           Enable Matomo web analytics with php-fpm backend.
           Either the nginx option or the webServerUser option is mandatory.
         '';
@@ -42,7 +42,7 @@ in {
         type = types.nullOr types.str;
         default = null;
         example = "lighttpd";
-        description = lib.mdDoc ''
+        description = ''
           Name of the web server user that forwards requests to {option}`services.phpfpm.pools.<name>.socket` the fastcgi socket for Matomo if the nginx
           option is not used. Either this option or the nginx option is mandatory.
           If you want to use another webserver than nginx, you need to set this to that server's user
@@ -53,7 +53,7 @@ in {
       periodicArchiveProcessing = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Enable periodic archive processing, which generates aggregated reports from the visits.
 
           This means that you can safely disable browser triggers for Matomo archiving,
@@ -71,7 +71,7 @@ in {
           "${user}.''${config.${options.networking.fqdnOrHostName}}"
         '';
         example = "matomo.yourdomain.org";
-        description = lib.mdDoc ''
+        description = ''
           URL of the host, without https prefix. You may want to change it if you
           run Matomo on a different URL than matomo.yourdomain.
         '';
@@ -99,7 +99,7 @@ in {
             enableACME = false;
           }
         '';
-        description = lib.mdDoc ''
+        description = ''
             With this option, you can customize an nginx virtualHost which already has sensible defaults for Matomo.
             Either this option or the webServerUser option is mandatory.
             Set this to {} to just enable the virtualHost if you don't need any customization.
diff --git a/nixpkgs/nixos/modules/services/web-apps/mattermost.nix b/nixpkgs/nixos/modules/services/web-apps/mattermost.nix
index 3d03c96d1c19..fee0ec2d641d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/mattermost.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/mattermost.nix
@@ -100,20 +100,20 @@ in
 {
   options = {
     services.mattermost = {
-      enable = mkEnableOption (lib.mdDoc "Mattermost chat server");
+      enable = mkEnableOption "Mattermost chat server";
 
       package = mkPackageOption pkgs "mattermost" { };
 
       statePath = mkOption {
         type = types.str;
         default = "/var/lib/mattermost";
-        description = lib.mdDoc "Mattermost working directory";
+        description = "Mattermost working directory";
       };
 
       siteUrl = mkOption {
         type = types.str;
         example = "https://chat.example.com";
-        description = lib.mdDoc ''
+        description = ''
           URL this Mattermost instance is reachable under, without trailing slash.
         '';
       };
@@ -121,14 +121,14 @@ in
       siteName = mkOption {
         type = types.str;
         default = "Mattermost";
-        description = lib.mdDoc "Name of this Mattermost site.";
+        description = "Name of this Mattermost site.";
       };
 
       listenAddress = mkOption {
         type = types.str;
         default = ":8065";
         example = "[::1]:8065";
-        description = lib.mdDoc ''
+        description = ''
           Address and port this Mattermost instance listens to.
         '';
       };
@@ -136,7 +136,7 @@ in
       mutableConfig = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           Whether the Mattermost config.json is writeable by Mattermost.
 
           Most of the settings can be edited in the system console of
@@ -153,7 +153,7 @@ in
       preferNixConfig = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           If both mutableConfig and this option are set, the Nix configuration
           will take precedence over any settings configured in the server
           console.
@@ -163,7 +163,7 @@ in
       extraConfig = mkOption {
         type = types.attrs;
         default = { };
-        description = lib.mdDoc ''
+        description = ''
           Additional configuration options as Nix attribute set in config.json schema.
         '';
       };
@@ -172,7 +172,7 @@ in
         type = types.listOf (types.oneOf [types.path types.package]);
         default = [];
         example = "[ ./com.github.moussetc.mattermost.plugin.giphy-2.0.0.tar.gz ]";
-        description = lib.mdDoc ''
+        description = ''
           Plugins to add to the configuration. Overrides any installed if non-null.
           This is a list of paths to .tar.gz files or derivations evaluating to
           .tar.gz files.
@@ -181,7 +181,7 @@ in
       environmentFile = mkOption {
         type = types.nullOr types.path;
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           Environment file (see {manpage}`systemd.exec(5)`
           "EnvironmentFile=" section for the syntax) which sets config options
           for mattermost (see [the mattermost documentation](https://docs.mattermost.com/configure/configuration-settings.html#environment-variables)).
@@ -198,7 +198,7 @@ in
       localDatabaseCreate = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Create a local PostgreSQL database for Mattermost automatically.
         '';
       };
@@ -206,7 +206,7 @@ in
       localDatabaseName = mkOption {
         type = types.str;
         default = "mattermost";
-        description = lib.mdDoc ''
+        description = ''
           Local Mattermost database name.
         '';
       };
@@ -214,7 +214,7 @@ in
       localDatabaseUser = mkOption {
         type = types.str;
         default = "mattermost";
-        description = lib.mdDoc ''
+        description = ''
           Local Mattermost database username.
         '';
       };
@@ -222,7 +222,7 @@ in
       localDatabasePassword = mkOption {
         type = types.str;
         default = "mmpgsecret";
-        description = lib.mdDoc ''
+        description = ''
           Password for local Mattermost database user.
         '';
       };
@@ -230,7 +230,7 @@ in
       user = mkOption {
         type = types.str;
         default = "mattermost";
-        description = lib.mdDoc ''
+        description = ''
           User which runs the Mattermost service.
         '';
       };
@@ -238,19 +238,19 @@ in
       group = mkOption {
         type = types.str;
         default = "mattermost";
-        description = lib.mdDoc ''
+        description = ''
           Group which runs the Mattermost service.
         '';
       };
 
       matterircd = {
-        enable = mkEnableOption (lib.mdDoc "Mattermost IRC bridge");
+        enable = mkEnableOption "Mattermost IRC bridge";
         package = mkPackageOption pkgs "matterircd" { };
         parameters = mkOption {
           type = types.listOf types.str;
           default = [ ];
           example = [ "-mmserver chat.example.com" "-bind [::]:6667" ];
-          description = lib.mdDoc ''
+          description = ''
             Set commandline parameters to pass to matterircd. See
             https://github.com/42wim/matterircd#usage for more information.
           '';
diff --git a/nixpkgs/nixos/modules/services/web-apps/mealie.nix b/nixpkgs/nixos/modules/services/web-apps/mealie.nix
index 8bb7542c6b56..8f68828e7a0b 100644
--- a/nixpkgs/nixos/modules/services/web-apps/mealie.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/mealie.nix
@@ -24,7 +24,7 @@ in
     settings = lib.mkOption {
       type = with lib.types; attrsOf anything;
       default = {};
-      description = lib.mdDoc ''
+      description = ''
         Configuration of the Mealie service.
 
         See [the mealie documentation](https://nightly.mealie.io/documentation/getting-started/installation/backend-config/) for available options and default values.
diff --git a/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix b/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix
index 5549b6ae1eaa..b11626ec2dc3 100644
--- a/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix
@@ -18,6 +18,9 @@ let
   cacheDir = "/var/cache/mediawiki";
   stateDir = "/var/lib/mediawiki";
 
+  # https://www.mediawiki.org/wiki/Compatibility
+  php = pkgs.php81;
+
   pkg = pkgs.stdenv.mkDerivation rec {
     pname = "mediawiki-full";
     inherit (src) version;
@@ -46,7 +49,7 @@ let
   } ''
     mkdir -p $out/bin
     for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do
-      makeWrapper ${pkgs.php}/bin/php $out/bin/mediawiki-$(basename $i .php) \
+      makeWrapper ${php}/bin/php $out/bin/mediawiki-$(basename $i .php) \
         --set MEDIAWIKI_CONFIG ${mediawikiConfig} \
         --add-flags ${pkg}/share/mediawiki/maintenance/$i
     done
@@ -192,7 +195,7 @@ in
   options = {
     services.mediawiki = {
 
-      enable = mkEnableOption (lib.mdDoc "MediaWiki");
+      enable = mkEnableOption "MediaWiki";
 
       package = mkPackageOption pkgs "mediawiki" { };
 
@@ -201,7 +204,7 @@ in
         readOnly = true;
         default = pkg;
         defaultText = literalExpression "pkg";
-        description = lib.mdDoc ''
+        description = ''
           The final package used by the module. This is the package that will have extensions and skins installed.
         '';
       };
@@ -210,7 +213,7 @@ in
         type = types.str;
         default = "MediaWiki";
         example = "Foobar Wiki";
-        description = lib.mdDoc "Name of the wiki.";
+        description = "Name of the wiki.";
       };
 
       url = mkOption {
@@ -229,13 +232,13 @@ in
           if "mediawiki uses ssl" then "{"https" else "http"}://''${cfg.hostName}" else "http://localhost";
         '';
         example = "https://wiki.example.org";
-        description = lib.mdDoc "URL of the wiki.";
+        description = "URL of the wiki.";
       };
 
       uploadsDir = mkOption {
         type = types.nullOr types.path;
         default = "${stateDir}/uploads";
-        description = lib.mdDoc ''
+        description = ''
           This directory is used for uploads of pictures. The directory passed here is automatically
           created and permissions adjusted as required.
         '';
@@ -243,7 +246,9 @@ in
 
       passwordFile = mkOption {
         type = types.path;
-        description = lib.mdDoc "A file containing the initial password for the admin user.";
+        description = ''
+          A file containing the initial password for the administrator account "admin".
+        '';
         example = "/run/keys/mediawiki-password";
       };
 
@@ -262,13 +267,13 @@ in
             else
               config.services.httpd.adminAddr else "root@localhost"
         '';
-        description = lib.mdDoc "Contact address for password reset.";
+        description = "Contact address for password reset.";
       };
 
       skins = mkOption {
         default = {};
         type = types.attrsOf types.path;
-        description = lib.mdDoc ''
+        description = ''
           Attribute set of paths whose content is copied to the {file}`skins`
           subdirectory of the MediaWiki installation in addition to the default skins.
         '';
@@ -277,7 +282,7 @@ in
       extensions = mkOption {
         default = {};
         type = types.attrsOf (types.nullOr types.path);
-        description = lib.mdDoc ''
+        description = ''
           Attribute set of paths whose content is copied to the {file}`extensions`
           subdirectory of the MediaWiki installation and enabled in configuration.
 
@@ -297,46 +302,46 @@ in
       webserver = mkOption {
         type = types.enum [ "apache" "none" "nginx" ];
         default = "apache";
-        description = lib.mdDoc "Webserver to use.";
+        description = "Webserver to use.";
       };
 
       database = {
         type = mkOption {
           type = types.enum [ "mysql" "postgres" "mssql" "oracle" ];
           default = "mysql";
-          description = lib.mdDoc "Database engine to use. MySQL/MariaDB is the database of choice by MediaWiki developers.";
+          description = "Database engine to use. MySQL/MariaDB is the database of choice by MediaWiki developers.";
         };
 
         host = mkOption {
           type = types.str;
           default = "localhost";
-          description = lib.mdDoc "Database host address.";
+          description = "Database host address.";
         };
 
         port = mkOption {
           type = types.port;
           default = if cfg.database.type == "mysql" then 3306 else 5432;
           defaultText = literalExpression "3306";
-          description = lib.mdDoc "Database host port.";
+          description = "Database host port.";
         };
 
         name = mkOption {
           type = types.str;
           default = "mediawiki";
-          description = lib.mdDoc "Database name.";
+          description = "Database name.";
         };
 
         user = mkOption {
           type = types.str;
           default = "mediawiki";
-          description = lib.mdDoc "Database user.";
+          description = "Database user.";
         };
 
         passwordFile = mkOption {
           type = types.nullOr types.path;
           default = null;
           example = "/run/keys/mediawiki-dbpassword";
-          description = lib.mdDoc ''
+          description = ''
             A file containing the password corresponding to
             {option}`database.user`.
           '';
@@ -345,7 +350,7 @@ in
         tablePrefix = mkOption {
           type = types.nullOr types.str;
           default = null;
-          description = lib.mdDoc ''
+          description = ''
             If you only have access to a single database and wish to install more than
             one version of MediaWiki, or have other applications that also use the
             database, you can give the table names a unique prefix to stop any naming
@@ -363,14 +368,14 @@ in
             else
               null;
           defaultText = literalExpression "/run/mysqld/mysqld.sock";
-          description = lib.mdDoc "Path to the unix socket file to use for authentication.";
+          description = "Path to the unix socket file to use for authentication.";
         };
 
         createLocally = mkOption {
           type = types.bool;
           default = cfg.database.type == "mysql" || cfg.database.type == "postgres";
           defaultText = literalExpression "true";
-          description = lib.mdDoc ''
+          description = ''
             Create the database and database user locally.
             This currently only applies if database type "mysql" is selected.
           '';
@@ -381,7 +386,7 @@ in
         type = types.str;
         example = literalExpression ''wiki.example.com'';
         default = "localhost";
-        description = lib.mdDoc ''
+        description = ''
           The hostname to use for the nginx virtual host.
           This is used to generate the nginx configuration.
         '';
@@ -397,7 +402,7 @@ in
             enableACME = true;
           }
         '';
-        description = lib.mdDoc ''
+        description = ''
           Apache configuration can be done by adapting {option}`services.httpd.virtualHosts`.
           See [](#opt-services.httpd.virtualHosts) for further information.
         '';
@@ -413,7 +418,7 @@ in
           "pm.max_spare_servers" = 4;
           "pm.max_requests" = 500;
         };
-        description = lib.mdDoc ''
+        description = ''
           Options for the MediaWiki PHP pool. See the documentation on `php-fpm.conf`
           for details on configuration directives.
         '';
@@ -421,7 +426,7 @@ in
 
       extraConfig = mkOption {
         type = types.lines;
-        description = lib.mdDoc ''
+        description = ''
           Any additional text to be appended to MediaWiki's
           LocalSettings.php configuration file. For configuration
           settings, see <https://www.mediawiki.org/wiki/Manual:Configuration_settings>.
@@ -485,8 +490,7 @@ in
     services.phpfpm.pools.mediawiki = {
       inherit user group;
       phpEnv.MEDIAWIKI_CONFIG = "${mediawikiConfig}";
-      # https://www.mediawiki.org/wiki/Compatibility
-      phpPackage = pkgs.php81;
+      phpPackage = php;
       settings = (if (cfg.webserver == "apache") then {
         "listen.owner" = config.services.httpd.user;
         "listen.group" = config.services.httpd.group;
@@ -598,8 +602,8 @@ in
         fi
 
         echo "exit( wfGetDB( DB_MASTER )->tableExists( 'user' ) ? 1 : 0 );" | \
-        ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/eval.php --conf ${mediawikiConfig} && \
-        ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/install.php \
+        ${php}/bin/php ${pkg}/share/mediawiki/maintenance/eval.php --conf ${mediawikiConfig} && \
+        ${php}/bin/php ${pkg}/share/mediawiki/maintenance/install.php \
           --confpath /tmp \
           --scriptpath / \
           --dbserver ${lib.escapeShellArg dbAddr} \
@@ -613,7 +617,7 @@ in
           ${lib.escapeShellArg cfg.name} \
           admin
 
-        ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/update.php --conf ${mediawikiConfig} --quick
+        ${php}/bin/php ${pkg}/share/mediawiki/maintenance/update.php --conf ${mediawikiConfig} --quick
       '';
 
       serviceConfig = {
diff --git a/nixpkgs/nixos/modules/services/web-apps/meme-bingo-web.nix b/nixpkgs/nixos/modules/services/web-apps/meme-bingo-web.nix
index fe68bbecca57..ab123ba8ef9c 100644
--- a/nixpkgs/nixos/modules/services/web-apps/meme-bingo-web.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/meme-bingo-web.nix
@@ -1,22 +1,22 @@
 { config, lib, pkgs, ... }:
 
 let
-  inherit (lib) mkEnableOption mkPackageOption mkIf mkOption mdDoc types literalExpression;
+  inherit (lib) mkEnableOption mkPackageOption mkIf mkOption types literalExpression;
 
   cfg = config.services.meme-bingo-web;
 in {
   options = {
     services.meme-bingo-web = {
-      enable = mkEnableOption (mdDoc ''
+      enable = mkEnableOption ''
         a web app for the meme bingo, rendered entirely on the web server and made interactive with forms.
 
         Note: The application's author suppose to run meme-bingo-web behind a reverse proxy for SSL and HTTP/3
-      '');
+      '';
 
       package = mkPackageOption pkgs "meme-bingo-web" { };
 
       baseUrl = mkOption {
-        description = mdDoc ''
+        description = ''
           URL to be used for the HTML <base> element on all HTML routes.
         '';
         type = types.str;
@@ -24,7 +24,7 @@ in {
         example = "https://bingo.example.com/";
       };
       port = mkOption {
-        description = mdDoc ''
+        description = ''
           Port to be used for the web server.
         '';
         type = types.port;
diff --git a/nixpkgs/nixos/modules/services/web-apps/microbin.nix b/nixpkgs/nixos/modules/services/web-apps/microbin.nix
index 233bfac6e699..0ebe644a2595 100644
--- a/nixpkgs/nixos/modules/services/web-apps/microbin.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/microbin.nix
@@ -5,7 +5,7 @@ let
 in
 {
   options.services.microbin = {
-    enable = lib.mkEnableOption (lib.mdDoc "MicroBin is a super tiny, feature rich, configurable paste bin web application");
+    enable = lib.mkEnableOption "MicroBin is a super tiny, feature rich, configurable paste bin web application";
 
     package = lib.mkPackageOption pkgs "microbin" { };
 
@@ -16,7 +16,7 @@ in
         MICROBIN_PORT = 8080;
         MICROBIN_HIDE_LOGO = false;
       };
-      description = lib.mdDoc ''
+      description = ''
         Additional configuration for MicroBin, see
         <https://microbin.eu/docs/installation-and-configuration/configuration/>
         for supported values.
@@ -28,14 +28,14 @@ in
     dataDir = lib.mkOption {
       type = lib.types.str;
       default = "/var/lib/microbin";
-      description = lib.mdDoc "Default data folder for MicroBin.";
+      description = "Default data folder for MicroBin.";
     };
 
     passwordFile = lib.mkOption {
       type = lib.types.nullOr lib.types.path;
       default = null;
       example = "/run/secrets/microbin.env";
-      description = lib.mdDoc ''
+      description = ''
         Path to file containing environment variables.
         Useful for passing down secrets.
         Variables that can be considered secrets are:
diff --git a/nixpkgs/nixos/modules/services/web-apps/miniflux.nix b/nixpkgs/nixos/modules/services/web-apps/miniflux.nix
index 16b6fb0d655d..d65d6db3cdaa 100644
--- a/nixpkgs/nixos/modules/services/web-apps/miniflux.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/miniflux.nix
@@ -16,7 +16,7 @@ in
 {
   options = {
     services.miniflux = {
-      enable = mkEnableOption (lib.mdDoc "miniflux");
+      enable = mkEnableOption "miniflux";
 
       package = mkPackageOption pkgs "miniflux" { };
 
@@ -38,7 +38,7 @@ in
             LISTEN_ADDR = "localhost:8080";
           }
         '';
-        description = lib.mdDoc ''
+        description = ''
           Configuration for Miniflux, refer to
           <https://miniflux.app/docs/configuration.html>
           for documentation on the supported values.
@@ -50,7 +50,7 @@ in
 
       adminCredentialsFile = mkOption {
         type = types.path;
-        description = lib.mdDoc ''
+        description = ''
           File containing the ADMIN_USERNAME and
           ADMIN_PASSWORD (length >= 6) in the format of
           an EnvironmentFile=, as described by systemd.exec(5).
diff --git a/nixpkgs/nixos/modules/services/web-apps/mobilizon.nix b/nixpkgs/nixos/modules/services/web-apps/mobilizon.nix
index bdb08f613149..b7fad7f3066e 100644
--- a/nixpkgs/nixos/modules/services/web-apps/mobilizon.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/mobilizon.nix
@@ -59,13 +59,12 @@ in
 {
   options = {
     services.mobilizon = {
-      enable = mkEnableOption
-        (lib.mdDoc "Mobilizon federated organization and mobilization platform");
+      enable = mkEnableOption "Mobilizon federated organization and mobilization platform";
 
       nginx.enable = lib.mkOption {
         type = lib.types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether an Nginx virtual host should be
           set up to serve Mobilizon.
         '';
@@ -90,7 +89,7 @@ in
                     defaultText = lib.literalMD ''
                       ''${settings.":mobilizon".":instance".hostname}
                     '';
-                    description = lib.mdDoc ''
+                    description = ''
                       Your instance's hostname for generating URLs throughout the app
                     '';
                   };
@@ -99,14 +98,14 @@ in
                     port = mkOption {
                       type = elixirTypes.port;
                       default = 4000;
-                      description = lib.mdDoc ''
+                      description = ''
                         The port to run the server
                       '';
                     };
                     ip = mkOption {
                       type = elixirTypes.tuple;
                       default = settingsFormat.lib.mkTuple [ 0 0 0 0 0 0 0 1 ];
-                      description = lib.mdDoc ''
+                      description = ''
                         The IP address to listen on. Defaults to [::1] notated as a byte tuple.
                       '';
                     };
@@ -115,7 +114,7 @@ in
                   has_reverse_proxy = mkOption {
                     type = elixirTypes.bool;
                     default = true;
-                    description = lib.mdDoc ''
+                    description = ''
                       Whether you use a reverse proxy
                     '';
                   };
@@ -124,14 +123,14 @@ in
                 ":instance" = {
                   name = mkOption {
                     type = elixirTypes.str;
-                    description = lib.mdDoc ''
+                    description = ''
                       The fallback instance name if not configured into the admin UI
                     '';
                   };
 
                   hostname = mkOption {
                     type = elixirTypes.str;
-                    description = lib.mdDoc ''
+                    description = ''
                       Your instance's hostname
                     '';
                   };
@@ -141,7 +140,7 @@ in
                     defaultText = literalExpression ''
                       noreply@''${settings.":mobilizon".":instance".hostname}
                     '';
-                    description = lib.mdDoc ''
+                    description = ''
                       The email for the From: header in emails
                     '';
                   };
@@ -151,7 +150,7 @@ in
                     defaultText = literalExpression ''
                       ''${email_from}
                     '';
-                    description = lib.mdDoc ''
+                    description = ''
                       The email for the Reply-To: header in emails
                     '';
                   };
@@ -161,7 +160,7 @@ in
                   socket_dir = mkOption {
                     type = types.nullOr elixirTypes.str;
                     default = postgresqlSocketDir;
-                    description = lib.mdDoc ''
+                    description = ''
                       Path to the postgres socket directory.
 
                       Set this to null if you want to connect to a remote database.
@@ -178,7 +177,7 @@ in
                   username = mkOption {
                     type = types.nullOr elixirTypes.str;
                     default = user;
-                    description = lib.mdDoc ''
+                    description = ''
                       User used to connect to the database
                     '';
                   };
@@ -186,7 +185,7 @@ in
                   database = mkOption {
                     type = types.nullOr elixirTypes.str;
                     default = "mobilizon_prod";
-                    description = lib.mdDoc ''
+                    description = ''
                       Name of the database
                     '';
                   };
@@ -196,7 +195,7 @@ in
           };
         default = { };
 
-        description = lib.mdDoc ''
+        description = ''
           Mobilizon Elixir documentation, see
           <https://docs.joinmobilizon.org/administration/configure/reference/>
           for supported values.
diff --git a/nixpkgs/nixos/modules/services/web-apps/monica.nix b/nixpkgs/nixos/modules/services/web-apps/monica.nix
index 2bff42f7ffa4..6774e2c9bb46 100644
--- a/nixpkgs/nixos/modules/services/web-apps/monica.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/monica.nix
@@ -32,22 +32,22 @@ with lib; let
   tlsEnabled = cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME;
 in {
   options.services.monica = {
-    enable = mkEnableOption (lib.mdDoc "monica");
+    enable = mkEnableOption "monica";
 
     user = mkOption {
       default = "monica";
-      description = lib.mdDoc "User monica runs as.";
+      description = "User monica runs as.";
       type = types.str;
     };
 
     group = mkOption {
       default = "monica";
-      description = lib.mdDoc "Group monica runs as.";
+      description = "Group monica runs as.";
       type = types.str;
     };
 
     appKeyFile = mkOption {
-      description = lib.mdDoc ''
+      description = ''
         A file containing the Laravel APP_KEY - a 32 character long,
         base64 encoded key used for encryption where needed. Can be
         generated with <code>head -c 32 /dev/urandom | base64</code>.
@@ -64,13 +64,13 @@ in {
         else config.networking.hostName;
       defaultText = lib.literalExpression "config.networking.fqdn";
       example = "monica.example.com";
-      description = lib.mdDoc ''
+      description = ''
         The hostname to serve monica on.
       '';
     };
 
     appURL = mkOption {
-      description = lib.mdDoc ''
+      description = ''
         The root URL that you want to host monica on. All URLs in monica will be generated using this value.
         If you change this in the future you may need to run a command to update stored URLs in the database.
         Command example: <code>php artisan monica:update-url https://old.example.com https://new.example.com</code>
@@ -82,7 +82,7 @@ in {
     };
 
     dataDir = mkOption {
-      description = lib.mdDoc "monica data directory";
+      description = "monica data directory";
       default = "/var/lib/monica";
       type = types.path;
     };
@@ -91,29 +91,29 @@ in {
       host = mkOption {
         type = types.str;
         default = "localhost";
-        description = lib.mdDoc "Database host address.";
+        description = "Database host address.";
       };
       port = mkOption {
         type = types.port;
         default = 3306;
-        description = lib.mdDoc "Database host port.";
+        description = "Database host port.";
       };
       name = mkOption {
         type = types.str;
         default = "monica";
-        description = lib.mdDoc "Database name.";
+        description = "Database name.";
       };
       user = mkOption {
         type = types.str;
         default = user;
         defaultText = lib.literalExpression "user";
-        description = lib.mdDoc "Database username.";
+        description = "Database username.";
       };
       passwordFile = mkOption {
         type = with types; nullOr path;
         default = null;
         example = "/run/keys/monica-dbpassword";
-        description = lib.mdDoc ''
+        description = ''
           A file containing the password corresponding to
           <option>database.user</option>.
         '';
@@ -121,7 +121,7 @@ in {
       createLocally = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc "Create the database and database user locally.";
+        description = "Create the database and database user locally.";
       };
     };
 
@@ -129,39 +129,39 @@ in {
       driver = mkOption {
         type = types.enum ["smtp" "sendmail"];
         default = "smtp";
-        description = lib.mdDoc "Mail driver to use.";
+        description = "Mail driver to use.";
       };
       host = mkOption {
         type = types.str;
         default = "localhost";
-        description = lib.mdDoc "Mail host address.";
+        description = "Mail host address.";
       };
       port = mkOption {
         type = types.port;
         default = 1025;
-        description = lib.mdDoc "Mail host port.";
+        description = "Mail host port.";
       };
       fromName = mkOption {
         type = types.str;
         default = "monica";
-        description = lib.mdDoc "Mail \"from\" name.";
+        description = "Mail \"from\" name.";
       };
       from = mkOption {
         type = types.str;
         default = "mail@monica.com";
-        description = lib.mdDoc "Mail \"from\" email.";
+        description = "Mail \"from\" email.";
       };
       user = mkOption {
         type = with types; nullOr str;
         default = null;
         example = "monica";
-        description = lib.mdDoc "Mail username.";
+        description = "Mail username.";
       };
       passwordFile = mkOption {
         type = with types; nullOr path;
         default = null;
         example = "/run/keys/monica-mailpassword";
-        description = lib.mdDoc ''
+        description = ''
           A file containing the password corresponding to
           <option>mail.user</option>.
         '';
@@ -169,7 +169,7 @@ in {
       encryption = mkOption {
         type = with types; nullOr (enum ["tls"]);
         default = null;
-        description = lib.mdDoc "SMTP encryption mechanism to use.";
+        description = "SMTP encryption mechanism to use.";
       };
     };
 
@@ -177,7 +177,7 @@ in {
       type = types.str;
       default = "18M";
       example = "1G";
-      description = lib.mdDoc "The maximum size for uploads (e.g. images).";
+      description = "The maximum size for uploads (e.g. images).";
     };
 
     poolConfig = mkOption {
@@ -190,7 +190,7 @@ in {
         "pm.max_spare_servers" = 4;
         "pm.max_requests" = 500;
       };
-      description = lib.mdDoc ''
+      description = ''
         Options for the monica PHP pool. See the documentation on <literal>php-fpm.conf</literal>
         for details on configuration directives.
       '';
@@ -212,7 +212,7 @@ in {
           enableACME = true;
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         With this option, you can customize the nginx virtualHost settings.
       '';
     };
@@ -233,7 +233,7 @@ in {
               options = {
                 _secret = mkOption {
                   type = nullOr str;
-                  description = lib.mdDoc ''
+                  description = ''
                     The path to a file containing the value the
                     option should be set to in the final
                     configuration file.
@@ -255,7 +255,7 @@ in {
           OIDC_ISSUER_DISCOVER = true;
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         monica configuration options to set in the
         <filename>.env</filename> file.
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/moodle.nix b/nixpkgs/nixos/modules/services/web-apps/moodle.nix
index 496a0e32436f..7e2d59d3c3e7 100644
--- a/nixpkgs/nixos/modules/services/web-apps/moodle.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/moodle.nix
@@ -64,14 +64,14 @@ in
 {
   # interface
   options.services.moodle = {
-    enable = mkEnableOption (lib.mdDoc "Moodle web application");
+    enable = mkEnableOption "Moodle web application";
 
     package = mkPackageOption pkgs "moodle" { };
 
     initialPassword = mkOption {
       type = types.str;
       example = "correcthorsebatterystaple";
-      description = lib.mdDoc ''
+      description = ''
         Specifies the initial password for the admin, i.e. the password assigned if the user does not already exist.
         The password specified here is world-readable in the Nix store, so it should be changed promptly.
       '';
@@ -81,18 +81,18 @@ in
       type = mkOption {
         type = types.enum [ "mysql" "pgsql" ];
         default = "mysql";
-        description = lib.mdDoc "Database engine to use.";
+        description = "Database engine to use.";
       };
 
       host = mkOption {
         type = types.str;
         default = "localhost";
-        description = lib.mdDoc "Database host address.";
+        description = "Database host address.";
       };
 
       port = mkOption {
         type = types.port;
-        description = lib.mdDoc "Database host port.";
+        description = "Database host port.";
         default = {
           mysql = 3306;
           pgsql = 5432;
@@ -103,20 +103,20 @@ in
       name = mkOption {
         type = types.str;
         default = "moodle";
-        description = lib.mdDoc "Database name.";
+        description = "Database name.";
       };
 
       user = mkOption {
         type = types.str;
         default = "moodle";
-        description = lib.mdDoc "Database user.";
+        description = "Database user.";
       };
 
       passwordFile = mkOption {
         type = types.nullOr types.path;
         default = null;
         example = "/run/keys/moodle-dbpassword";
-        description = lib.mdDoc ''
+        description = ''
           A file containing the password corresponding to
           {option}`database.user`.
         '';
@@ -129,13 +129,13 @@ in
           else if pgsqlLocal then "/run/postgresql"
           else null;
         defaultText = literalExpression "/run/mysqld/mysqld.sock";
-        description = lib.mdDoc "Path to the unix socket file to use for authentication.";
+        description = "Path to the unix socket file to use for authentication.";
       };
 
       createLocally = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc "Create the database and database user locally.";
+        description = "Create the database and database user locally.";
       };
     };
 
@@ -149,7 +149,7 @@ in
           enableACME = true;
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         Apache configuration can be done by adapting {option}`services.httpd.virtualHosts`.
         See [](#opt-services.httpd.virtualHosts) for further information.
       '';
@@ -165,7 +165,7 @@ in
         "pm.max_spare_servers" = 4;
         "pm.max_requests" = 500;
       };
-      description = lib.mdDoc ''
+      description = ''
         Options for the Moodle PHP pool. See the documentation on `php-fpm.conf`
         for details on configuration directives.
       '';
@@ -174,7 +174,7 @@ in
     extraConfig = mkOption {
       type = types.lines;
       default = "";
-      description = lib.mdDoc ''
+      description = ''
         Any additional text to be appended to the config.php
         configuration file. This is a PHP script. For configuration
         details, see <https://docs.moodle.org/37/en/Configuration_file>.
diff --git a/nixpkgs/nixos/modules/services/web-apps/movim.nix b/nixpkgs/nixos/modules/services/web-apps/movim.nix
new file mode 100644
index 000000000000..29bed0e067fa
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/movim.nix
@@ -0,0 +1,709 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib)
+    filterAttrsRecursive
+    generators
+    literalExpression
+    mkDefault
+    mkIf
+    mkOption
+    mkEnableOption
+    mkPackageOption
+    mkMerge
+    pipe
+    types
+    ;
+
+  cfg = config.services.movim;
+
+  defaultPHPCfg = {
+    "output_buffering" = 0;
+    "error_reporting" = "E_ALL & ~E_DEPRECATED & ~E_STRICT";
+    "opcache.enable_cli" = 1;
+    "opcache.interned_strings_buffer" = 8;
+    "opcache.max_accelerated_files" = 6144;
+    "opcache.memory_consumption" = 128;
+    "opcache.revalidate_freq" = 2;
+    "opcache.fast_shutdown" = 1;
+  };
+
+  phpCfg = generators.toKeyValue
+    { mkKeyValue = generators.mkKeyValueDefault { } " = "; }
+    (defaultPHPCfg // cfg.phpCfg);
+
+  podConfigFlags =
+    let
+      bevalue = a: lib.escapeShellArg (generators.mkValueStringDefault { } a);
+    in
+    lib.concatStringsSep " "
+      (lib.attrsets.foldlAttrs
+        (acc: k: v: acc ++ lib.optional (v != null) "--${k}=${bevalue v}")
+        [ ]
+        cfg.podConfig);
+
+  package =
+    let
+      p = cfg.package.override
+        ({
+          inherit phpCfg;
+          withPgsql = cfg.database.type == "pgsql";
+          withMysql = cfg.database.type == "mysql";
+          inherit (cfg) minifyStaticFiles;
+        } // lib.optionalAttrs (lib.isAttrs cfg.minifyStaticFiles) (with cfg.minifyStaticFiles; {
+          esbuild = esbuild.package;
+          lightningcss = lightningcss.package;
+          scour = scour.package;
+        }));
+    in
+    p.overrideAttrs (finalAttrs: prevAttrs:
+      let
+        appDir = "$out/share/php/${finalAttrs.pname}";
+
+        stateDirectories = ''
+          # Symlinking in our state directories
+          rm -rf $out/.env $out/cache ${appDir}/public/cache
+          ln -s ${cfg.dataDir}/.env ${appDir}/.env
+          ln -s ${cfg.dataDir}/public/cache ${appDir}/public/cache
+          ln -s ${cfg.logDir} ${appDir}/log
+          ln -s ${cfg.runtimeDir}/cache ${appDir}/cache
+        '';
+
+        exposeComposer = ''
+          # Expose PHP Composer for scripts
+          mkdir -p $out/bin
+          echo "#!${lib.getExe pkgs.dash}" > $out/bin/movim-composer
+          echo "${finalAttrs.php.packages.composer}/bin/composer --working-dir="${appDir}" \"\$@\"" >> $out/bin/movim-composer
+          chmod +x $out/bin/movim-composer
+        '';
+
+        podConfigInputDisableReplace = lib.optionalString (podConfigFlags != "")
+          (lib.concatStringsSep "\n"
+            (lib.attrsets.foldlAttrs
+              (acc: k: v:
+                acc ++ lib.optional (v != null)
+                  # Disable all Admin panel options that were set in the
+                  # `cfg.podConfig` to prevent confusing situtions where the
+                  # values are rewritten on server reboot
+                  ''
+                    substituteInPlace ${appDir}/app/widgets/AdminMain/adminmain.tpl \
+                      --replace-warn 'name="${k}"' 'name="${k}" disabled'
+                  '')
+              [ ]
+              cfg.podConfig));
+
+        precompressStaticFilesJobs =
+          let
+            inherit (cfg.precompressStaticFiles) brotli gzip;
+
+            findTextFileNames = lib.concatStringsSep " -o "
+              (builtins.map (n: ''-iname "*.${n}"'')
+                [ "css" "ini" "js" "json" "manifest" "mjs" "svg" "webmanifest" ]);
+          in
+          lib.concatStringsSep "\n" [
+            (lib.optionalString brotli.enable ''
+              echo -n "Precompressing static files with Brotli …"
+              find ${appDir}/public -type f ${findTextFileNames} -print0 \
+                | xargs -0 -n 1 -P $NIX_BUILD_CORES ${pkgs.writeShellScript "movim_precompress_broti" ''
+                    file="$1"
+                    ${lib.getExe brotli.package} --keep --quality=${builtins.toString brotli.compressionLevel} --output=$file.br $file
+                  ''}
+              echo " done."
+            '')
+            (lib.optionalString gzip.enable ''
+              echo -n "Precompressing static files with Gzip …"
+              find ${appDir}/public -type f ${findTextFileNames} -print0 \
+                | xargs -0 -n 1 -P $NIX_BUILD_CORES ${pkgs.writeShellScript "movim_precompress_broti" ''
+                    file="$1"
+                    ${lib.getExe gzip.package} -c -${builtins.toString gzip.compressionLevel} $file > $file.gz
+                  ''}
+              echo " done."
+            '')
+          ];
+      in
+      {
+        postInstall = lib.concatStringsSep "\n\n" [
+          prevAttrs.postInstall
+          stateDirectories
+          exposeComposer
+          podConfigInputDisableReplace
+          precompressStaticFilesJobs
+        ];
+      });
+
+  configFile = pipe cfg.settings [
+    (filterAttrsRecursive (_: v: v != null))
+    (generators.toKeyValue { })
+    (pkgs.writeText "movim-env")
+  ];
+
+  pool = "movim";
+  fpm = config.services.phpfpm.pools.${pool};
+  phpExecutionUnit = "phpfpm-${pool}";
+
+  dbService = {
+    "postgresql" = "postgresql.service";
+    "mysql" = "mysql.service";
+  }.${cfg.database.type};
+in
+{
+  options.services = {
+    movim = {
+      enable = mkEnableOption "a Movim instance";
+      package = mkPackageOption pkgs "movim" { };
+      phpPackage = mkPackageOption pkgs "php" { };
+
+      phpCfg = mkOption {
+        type = with types; attrsOf (oneOf [ int str bool ]);
+        defaultText = literalExpression (generators.toPretty { } defaultPHPCfg);
+        default = { };
+        description = "Extra PHP INI options such as `memory_limit`, `max_execution_time`, etc.";
+      };
+
+      user = mkOption {
+        type = types.nonEmptyStr;
+        default = "movim";
+        description = "User running Movim service";
+      };
+
+      group = mkOption {
+        type = types.nonEmptyStr;
+        default = "movim";
+        description = "Group running Movim service";
+      };
+
+      dataDir = mkOption {
+        type = types.nonEmptyStr;
+        default = "/var/lib/movim";
+        description = "State directory of the `movim` user which holds the application’s state & data.";
+      };
+
+      logDir = mkOption {
+        type = types.nonEmptyStr;
+        default = "/var/log/movim";
+        description = "Log directory of the `movim` user which holds the application’s logs.";
+      };
+
+      runtimeDir = mkOption {
+        type = types.nonEmptyStr;
+        default = "/run/movim";
+        description = "Runtime directory of the `movim` user which holds the application’s caches & temporary files.";
+      };
+
+      domain = mkOption {
+        type = types.nonEmptyStr;
+        description = "Fully-qualified domain name (FQDN) for the Movim instance.";
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 8080;
+        description = "Movim daemon port.";
+      };
+
+      debug = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Debugging logs.";
+      };
+
+      verbose = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Verbose logs.";
+      };
+
+      minifyStaticFiles = mkOption {
+        type = with types; either bool (submodule {
+          options = {
+            script = mkOption {
+              type = types.submodule {
+                options = {
+                  enable = mkEnableOption "Script minification";
+                  package = mkPackageOption pkgs "esbuild" { };
+                  target = mkOption {
+                    type = with types; nullOr nonEmptyStr;
+                    default = null;
+                  };
+                };
+              };
+            };
+            style = mkOption {
+              type = types.submodule {
+                options = {
+                  enable = mkEnableOption "Script minification";
+                  package = mkPackageOption pkgs "lightningcss" { };
+                  target = mkOption {
+                    type = with types; nullOr nonEmptyStr;
+                    default = null;
+                  };
+                };
+              };
+            };
+            svg = mkOption {
+              type = types.submodule {
+                options = {
+                  enable = mkEnableOption "SVG minification";
+                  package = mkPackageOption pkgs "scour" { };
+                };
+              };
+            };
+          };
+        });
+        default = true;
+        description = "Do minification on public static files";
+      };
+
+      precompressStaticFiles = mkOption {
+        type = with types; submodule {
+          options = {
+            brotli = {
+              enable = mkEnableOption "Brotli precompression";
+              package = mkPackageOption pkgs "brotli" { };
+              compressionLevel = mkOption {
+                type = types.ints.between 0 11;
+                default = 11;
+                description = "Brotli compression level";
+              };
+            };
+            gzip = {
+              enable = mkEnableOption "Gzip precompression";
+              package = mkPackageOption pkgs "gzip" { };
+              compressionLevel = mkOption {
+                type = types.ints.between 1 9;
+                default = 9;
+                description = "Gzip compression level";
+              };
+            };
+          };
+        };
+        default = {
+          brotli.enable = true;
+          gzip.enable = false;
+        };
+        description = "Aggressively precompress static files";
+      };
+
+      podConfig = mkOption {
+        type = types.submodule {
+          options = {
+            info = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "Content of the info box on the login page";
+            };
+
+            description = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "General description of the instance";
+            };
+
+            timezone = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "The server timezone";
+            };
+
+            restrictsuggestions = mkOption {
+              type = with types; nullOr bool;
+              default = null;
+              description = "Only suggest chatrooms, Communities and other contents that are available on the user XMPP server and related services";
+            };
+
+            chatonly = mkOption {
+              type = with types; nullOr bool;
+              default = null;
+              description = "Disable all the social feature (Communities, Blog…) and keep only the chat ones";
+            };
+
+            disableregistration = mkOption {
+              type = with types; nullOr bool;
+              default = null;
+              description = "Remove the XMPP registration flow and buttons from the interface";
+            };
+
+            loglevel = mkOption {
+              type = with types; nullOr (ints.between 0 3);
+              default = null;
+              description = "The server loglevel";
+            };
+
+            locale = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "The server main locale";
+            };
+
+            xmppdomain = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "The default XMPP server domain";
+            };
+
+            xmppdescription = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "The default XMPP server description";
+            };
+
+            xmppwhitelist = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "The allowlisted XMPP servers";
+            };
+          };
+        };
+        default = { };
+        description = ''
+          Pod configuration (values from `php daemon.php config --help`).
+          Note that these values will now be disabled in the admin panel.
+        '';
+      };
+
+      settings = mkOption {
+        type = with types; attrsOf (nullOr (oneOf [ int str bool ]));
+        default = { };
+        description = ".env settings for Movim. Secrets should use `secretFile` option instead. `null`s will be culled.";
+      };
+
+      secretFile = mkOption {
+        type = with types; nullOr path;
+        default = null;
+        description = "The secret file to be sourced for the .env settings.";
+      };
+
+      database = {
+        type = mkOption {
+          type = types.enum [ "mysql" "postgresql" ];
+          example = "mysql";
+          default = "postgresql";
+          description = "Database engine to use.";
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "movim";
+          description = "Database name.";
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "movim";
+          description = "Database username.";
+        };
+
+        createLocally = mkOption {
+          type = types.bool;
+          default = true;
+          description = "local database using UNIX socket authentication";
+        };
+      };
+
+      nginx = mkOption {
+        type = with types; nullOr (submodule
+          (import ../web-servers/nginx/vhost-options.nix {
+            inherit config lib;
+          }));
+        default = null;
+        example = lib.literalExpression /* nginx */ ''
+          {
+            serverAliases = [
+              "pics.''${config.networking.domain}"
+            ];
+            enableACME = true;
+            forceHttps = true;
+          }
+        '';
+        description = ''
+          With this option, you can customize an nginx virtual host which already has sensible defaults for Movim.
+          Set to `{ }` if you do not need any customization to the virtual host.
+          If enabled, then by default, the {option}`serverName` is `''${domain}`,
+          If this is set to null (the default), no nginx virtualHost will be configured.
+        '';
+      };
+
+      poolConfig = mkOption {
+        type = with types; attrsOf (oneOf [ int str bool ]);
+        default = { };
+        description = "Options for Movim’s PHP-FPM pool.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    users = {
+      users = {
+        movim = mkIf (cfg.user == "movim") {
+          isSystemUser = true;
+          group = cfg.group;
+        };
+        "${config.services.nginx.user}".extraGroups = [ cfg.group ];
+      };
+      groups = {
+        ${cfg.group} = { };
+      };
+    };
+
+    services = {
+      movim = {
+        settings = mkMerge [
+          {
+            DAEMON_URL = "//${cfg.domain}";
+            DAEMON_PORT = cfg.port;
+            DAEMON_INTERFACE = "127.0.0.1";
+            DAEMON_DEBUG = cfg.debug;
+            DAEMON_VERBOSE = cfg.verbose;
+          }
+          (mkIf cfg.database.createLocally {
+            DB_DRIVER = {
+              "postgresql" = "pgsql";
+              "mysql" = "mysql";
+            }.${cfg.database.type};
+            DB_HOST = "localhost";
+            DB_PORT = config.services.${cfg.database.type}.settings.port;
+            DB_DATABASE = cfg.database.name;
+            DB_USERNAME = cfg.database.user;
+            DB_PASSWORD = "";
+          })
+        ];
+
+        poolConfig = lib.mapAttrs' (n: v: lib.nameValuePair n (lib.mkDefault v)) {
+          "pm" = "dynamic";
+          "php_admin_value[error_log]" = "stderr";
+          "php_admin_flag[log_errors]" = true;
+          "catch_workers_output" = true;
+          "pm.max_children" = 32;
+          "pm.start_servers" = 2;
+          "pm.min_spare_servers" = 2;
+          "pm.max_spare_servers" = 8;
+          "pm.max_requests" = 500;
+        };
+      };
+
+      nginx = mkIf (cfg.nginx != null) {
+        enable = true;
+        recommendedOptimisation = true;
+        recommendedGzipSettings = true;
+        recommendedBrotliSettings = true;
+        recommendedProxySettings = true;
+        # TODO: recommended cache options already in Nginx⁇
+        appendHttpConfig = /* nginx */ ''
+          fastcgi_cache_path /tmp/nginx_cache levels=1:2 keys_zone=nginx_cache:100m inactive=60m;
+          fastcgi_cache_key "$scheme$request_method$host$request_uri";
+        '';
+        virtualHosts."${cfg.domain}" = mkMerge [
+          cfg.nginx
+          {
+            root = lib.mkForce "${package}/share/php/movim/public";
+            locations = {
+              "/favicon.ico" = {
+                priority = 100;
+                extraConfig = /* nginx */ ''
+                  access_log off;
+                  log_not_found off;
+                '';
+              };
+              "/robots.txt" = {
+                priority = 100;
+                extraConfig = /* nginx */ ''
+                  access_log off;
+                  log_not_found off;
+                '';
+              };
+              "~ /\\.(?!well-known).*" = {
+                priority = 210;
+                extraConfig = /* nginx */ ''
+                  deny all;
+                '';
+              };
+              # Ask nginx to cache every URL starting with "/picture"
+              "/picture" = {
+                priority = 400;
+                tryFiles = "$uri $uri/ /index.php$is_args$args";
+                extraConfig = /* nginx */ ''
+                  set $no_cache 0; # Enable cache only there
+                '';
+              };
+              "/" = {
+                priority = 490;
+                tryFiles = "$uri $uri/ /index.php$is_args$args";
+                extraConfig = /* nginx */ ''
+                  # https://github.com/movim/movim/issues/314
+                  add_header Content-Security-Policy "default-src 'self'; img-src 'self' aesgcm: https:; media-src 'self' aesgcm: https:; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline';";
+                  set $no_cache 1;
+                '';
+              };
+              "~ \\.php$" = {
+                priority = 500;
+                tryFiles = "$uri =404";
+                extraConfig = /* nginx */ ''
+                  include ${config.services.nginx.package}/conf/fastcgi.conf;
+                  add_header X-Cache $upstream_cache_status;
+                  fastcgi_ignore_headers "Cache-Control" "Expires" "Set-Cookie";
+                  fastcgi_cache nginx_cache;
+                  fastcgi_cache_valid any 7d;
+                  fastcgi_cache_bypass $no_cache;
+                  fastcgi_no_cache $no_cache;
+                  fastcgi_split_path_info ^(.+\.php)(/.+)$;
+                  fastcgi_index index.php;
+                  fastcgi_pass unix:${fpm.socket};
+                '';
+              };
+              "/ws/" = {
+                priority = 900;
+                proxyPass = "http://${cfg.settings.DAEMON_INTERFACE}:${builtins.toString cfg.port}/";
+                proxyWebsockets = true;
+                recommendedProxySettings = true;
+                extraConfig = /* nginx */ ''
+                  proxy_set_header X-Forwarded-Proto $scheme;
+                  proxy_redirect off;
+                '';
+              };
+            };
+            extraConfig = /* ngnix */ ''
+              index index.php;
+            '';
+          }
+        ];
+      };
+
+      mysql = mkIf (cfg.database.createLocally && cfg.database.type == "mysql") {
+        enable = mkDefault true;
+        package = mkDefault pkgs.mariadb;
+        ensureDatabases = [ cfg.database.name ];
+        ensureUsers = [{
+          name = cfg.user;
+          ensureDBOwnership = true;
+        }];
+      };
+
+      postgresql = mkIf (cfg.database.createLocally && cfg.database.type == "postgresql") {
+        enable = mkDefault true;
+        ensureDatabases = [ cfg.database.name ];
+        ensureUsers = [{
+          name = cfg.user;
+          ensureDBOwnership = true;
+        }];
+        authentication = ''
+          host ${cfg.database.name} ${cfg.database.user} localhost trust
+        '';
+      };
+
+      phpfpm.pools.${pool} =
+        let
+          socketOwner =
+            if (cfg.nginx != null)
+            then config.services.nginx.user
+            else cfg.user;
+        in
+        {
+          phpPackage = package.php;
+          user = cfg.user;
+          group = cfg.group;
+
+          phpOptions = ''
+            error_log = 'stderr'
+            log_errors = on
+          '';
+
+          settings = {
+            "listen.owner" = socketOwner;
+            "listen.group" = cfg.group;
+            "listen.mode" = "0660";
+            "catch_workers_output" = true;
+          } // cfg.poolConfig;
+        };
+    };
+
+    systemd = {
+      services.movim-data-setup = {
+        description = "Movim setup: .env file, databases init, cache reload";
+        wantedBy = [ "multi-user.target" ];
+        requiredBy = [ "${phpExecutionUnit}.service" ];
+        before = [ "${phpExecutionUnit}.service" ];
+        after = lib.optional cfg.database.createLocally dbService;
+        requires = lib.optional cfg.database.createLocally dbService;
+
+        serviceConfig = {
+          Type = "oneshot";
+          User = cfg.user;
+          Group = cfg.group;
+          UMask = "077";
+        } // lib.optionalAttrs (cfg.secretFile != null) {
+          LoadCredential = "env-secrets:${cfg.secretFile}";
+        };
+
+        script = ''
+          # Env vars
+          rm -f ${cfg.dataDir}/.env
+          cp --no-preserve=all ${configFile} ${cfg.dataDir}/.env
+          echo -e '\n' >> ${cfg.dataDir}/.env
+          if [[ -f "$CREDENTIALS_DIRECTORY/env-secrets"  ]]; then
+            cat "$CREDENTIALS_DIRECTORY/env-secrets" >> ${cfg.dataDir}/.env
+            echo -e '\n' >> ${cfg.dataDir}/.env
+          fi
+
+          # Caches, logs
+          mkdir -p ${cfg.dataDir}/public/cache ${cfg.logDir} ${cfg.runtimeDir}/cache
+          chmod -R ug+rw ${cfg.dataDir}/public/cache
+          chmod -R ug+rw ${cfg.logDir}
+          chmod -R ug+rwx ${cfg.runtimeDir}/cache
+
+          # Migrations
+          MOVIM_VERSION="${package.version}"
+          if [[ ! -f "${cfg.dataDir}/.migration-version" ]] || [[ "$MOVIM_VERSION" != "$(<${cfg.dataDir}/.migration-version)" ]]; then
+            ${package}/bin/movim-composer movim:migrate && echo $MOVIM_VERSION > ${cfg.dataDir}/.migration-version
+          fi
+        ''
+        + lib.optionalString (podConfigFlags != "") (
+          let
+            flags = lib.concatStringsSep " "
+              ([ "--no-interaction" ]
+                ++ lib.optional cfg.debug "-vvv"
+                ++ lib.optional (!cfg.debug && cfg.verbose) "-v");
+          in
+          ''
+            ${lib.getExe package} config ${podConfigFlags}
+          ''
+        );
+      };
+
+      services.movim = {
+        description = "Movim daemon";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "movim-data-setup.service" ];
+        requires = [ "movim-data-setup.service" ]
+          ++ lib.optional cfg.database.createLocally dbService;
+        environment = {
+          PUBLIC_URL = "//${cfg.domain}";
+          WS_PORT = builtins.toString cfg.port;
+        };
+
+        serviceConfig = {
+          User = cfg.user;
+          Group = cfg.group;
+          WorkingDirectory = "${package}/share/php/movim";
+          ExecStart = "${lib.getExe package} start";
+        };
+      };
+
+      services.${phpExecutionUnit} = {
+        after = [ "movim-data-setup.service" ];
+        requires = [ "movim-data-setup.service" ]
+          ++ lib.optional cfg.database.createLocally dbService;
+      };
+
+      tmpfiles.settings."10-movim" = with cfg; {
+        "${dataDir}".d = { inherit user group; mode = "0710"; };
+        "${dataDir}/public".d = { inherit user group; mode = "0750"; };
+        "${dataDir}/public/cache".d = { inherit user group; mode = "0750"; };
+        "${runtimeDir}".d = { inherit user group; mode = "0700"; };
+        "${runtimeDir}/cache".d = { inherit user group; mode = "0700"; };
+        "${logDir}".d = { inherit user group; mode = "0700"; };
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/netbox.nix b/nixpkgs/nixos/modules/services/web-apps/netbox.nix
index d034f3234a2b..7bcbde2a018e 100644
--- a/nixpkgs/nixos/modules/services/web-apps/netbox.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/netbox.nix
@@ -32,7 +32,7 @@ in {
     enable = lib.mkOption {
       type = lib.types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Enable Netbox.
 
         This module requires a reverse proxy that serves `/static` separately.
@@ -41,7 +41,7 @@ in {
     };
 
     settings = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         Configuration options to set in `configuration.py`.
         See the [documentation](https://docs.netbox.dev/en/stable/configuration/) for more possible options.
       '';
@@ -55,7 +55,7 @@ in {
           ALLOWED_HOSTS = lib.mkOption {
             type = with lib.types; listOf str;
             default = ["*"];
-            description = lib.mdDoc ''
+            description = ''
               A list of valid fully-qualified domain names (FQDNs) and/or IP
               addresses that can be used to reach the NetBox service.
             '';
@@ -67,7 +67,7 @@ in {
     listenAddress = lib.mkOption {
       type = lib.types.str;
       default = "[::1]";
-      description = lib.mdDoc ''
+      description = ''
         Address the server will listen on.
       '';
     };
@@ -91,7 +91,7 @@ in {
         then pkgs.netbox_3_5
         else pkgs.netbox_3_3;
       '';
-      description = lib.mdDoc ''
+      description = ''
         NetBox package to use.
       '';
     };
@@ -99,7 +99,7 @@ in {
     port = lib.mkOption {
       type = lib.types.port;
       default = 8001;
-      description = lib.mdDoc ''
+      description = ''
         Port the server will listen on.
       '';
     };
@@ -110,7 +110,7 @@ in {
       defaultText = lib.literalExpression ''
         python3Packages: with python3Packages; [];
       '';
-      description = lib.mdDoc ''
+      description = ''
         List of plugin packages to install.
       '';
     };
@@ -118,14 +118,14 @@ in {
     dataDir = lib.mkOption {
       type = lib.types.str;
       default = "/var/lib/netbox";
-      description = lib.mdDoc ''
+      description = ''
         Storage path of netbox.
       '';
     };
 
     secretKeyFile = lib.mkOption {
       type = lib.types.path;
-      description = lib.mdDoc ''
+      description = ''
         Path to a file containing the secret key.
       '';
     };
@@ -133,7 +133,7 @@ in {
     extraConfig = lib.mkOption {
       type = lib.types.lines;
       default = "";
-      description = lib.mdDoc ''
+      description = ''
         Additional lines of configuration appended to the `configuration.py`.
         See the [documentation](https://docs.netbox.dev/en/stable/configuration/) for more possible options.
       '';
@@ -142,7 +142,7 @@ in {
     enableLdap = lib.mkOption {
       type = lib.types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Enable LDAP-Authentication for Netbox.
 
         This requires a configuration file being pass through `ldapConfigPath`.
@@ -152,7 +152,7 @@ in {
     ldapConfigPath = lib.mkOption {
       type = lib.types.path;
       default = "";
-      description = lib.mdDoc ''
+      description = ''
         Path to the Configuration-File for LDAP-Authentication, will be loaded as `ldap_config.py`.
         See the [documentation](https://netbox.readthedocs.io/en/stable/installation/6-ldap/#configuration) for possible options.
       '';
@@ -185,7 +185,7 @@ in {
     keycloakClientSecret = lib.mkOption {
       type = with lib.types; nullOr path;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         File that contains the keycloak client secret.
       '';
     };
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud-notify_push.nix b/nixpkgs/nixos/modules/services/web-apps/nextcloud-notify_push.nix
index 7b90e0bbaa9b..d6d17158a559 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud-notify_push.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud-notify_push.nix
@@ -6,31 +6,31 @@ let
 in
 {
   options.services.nextcloud.notify_push = {
-    enable = lib.mkEnableOption (lib.mdDoc "Notify push");
+    enable = lib.mkEnableOption "Notify push";
 
     package = lib.mkOption {
       type = lib.types.package;
       default = pkgs.nextcloud-notify_push;
       defaultText = lib.literalMD "pkgs.nextcloud-notify_push";
-      description = lib.mdDoc "Which package to use for notify_push";
+      description = "Which package to use for notify_push";
     };
 
     socketPath = lib.mkOption {
       type = lib.types.str;
       default = "/run/nextcloud-notify_push/sock";
-      description = lib.mdDoc "Socket path to use for notify_push";
+      description = "Socket path to use for notify_push";
     };
 
     logLevel = lib.mkOption {
       type = lib.types.enum [ "error" "warn" "info" "debug" "trace" ];
       default = "error";
-      description = lib.mdDoc "Log level";
+      description = "Log level";
     };
 
     bendDomainToLocalhost = lib.mkOption {
       type = lib.types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Whether to add an entry to `/etc/hosts` for the configured nextcloud domain to point to `localhost` and add `localhost `to nextcloud's `trusted_proxies` config option.
 
         This is useful when nextcloud's domain is not a static IP address and when the reverse proxy cannot be bypassed because the backend connection is done via unix socket.
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.md b/nixpkgs/nixos/modules/services/web-apps/nextcloud.md
index 06a8712b0b8a..ec860d307b38 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.md
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.md
@@ -5,7 +5,7 @@ self-hostable cloud platform. The server setup can be automated using
 [services.nextcloud](#opt-services.nextcloud.enable). A
 desktop client is packaged at `pkgs.nextcloud-client`.
 
-The current default by NixOS is `nextcloud28` which is also the latest
+The current default by NixOS is `nextcloud29` which is also the latest
 major version available.
 
 ## Basic usage {#module-services-nextcloud-basic-usage}
@@ -184,6 +184,32 @@ Alternatively, extra apps can also be declared with the [](#opt-services.nextclo
 When using this setting, apps can no longer be managed statefully because this can lead to Nextcloud updating apps
 that are managed by Nix. If you want automatic updates it is recommended that you use web interface to install apps.
 
+## Known warnings {#module-services-nextcloud-known-warnings}
+
+### Failed to get an iterator for log entries: Logreader application only supports "file" log_type {#module-services-nextcloud-warning-logreader}
+
+This is because
+
+* our module writes logs into the journal (`journalctl -t Nextcloud`)
+* the Logreader application that allows reading logs in the admin panel is enabled
+  by default and requires logs written to a file.
+
+The logreader application doesn't work, as it was the case before. The only change is that
+it complains loudly now. So nothing actionable here by default. Alternatively you can
+
+* disable the logreader application to shut up the "error".
+
+  We can't really do that by default since whether apps are enabled/disabled is part
+  of the application's state and tracked inside the database.
+
+* set [](#opt-services.nextcloud.settings.log_type) to "file" to be able to view logs
+  from the admin panel.
+
+### Your web server is not properly set up to resolve `.well-known` URLs, failed on: `/.well-known/caldav` {#module-services-nextcloud-warning-wellknown-caldav}
+
+This warning appearing seems to be an upstream issue and is being sorted out
+in [nextcloud/server#45033](https://github.com/nextcloud/server/issues/45033).
+
 ## Maintainer information {#module-services-nextcloud-maintainer-info}
 
 As stated in the previous paragraph, we must provide a clean upgrade-path for Nextcloud
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
index 7f998207c434..21f76938f20c 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
@@ -223,22 +223,22 @@ in {
   ];
 
   options.services.nextcloud = {
-    enable = mkEnableOption (lib.mdDoc "nextcloud");
+    enable = mkEnableOption "nextcloud";
 
     hostName = mkOption {
       type = types.str;
-      description = lib.mdDoc "FQDN for the nextcloud instance.";
+      description = "FQDN for the nextcloud instance.";
     };
     home = mkOption {
       type = types.str;
       default = "/var/lib/nextcloud";
-      description = lib.mdDoc "Storage path of nextcloud.";
+      description = "Storage path of nextcloud.";
     };
     datadir = mkOption {
       type = types.str;
       default = config.services.nextcloud.home;
       defaultText = literalExpression "config.services.nextcloud.home";
-      description = lib.mdDoc ''
+      description = ''
         Nextcloud's data storage path.  Will be [](#opt-services.nextcloud.home) by default.
         This folder will be populated with a config.php file and a data folder which contains the state of the instance (excluding the database).";
       '';
@@ -247,7 +247,7 @@ in {
     extraApps = mkOption {
       type = types.attrsOf types.package;
       default = { };
-      description = lib.mdDoc ''
+      description = ''
         Extra apps to install. Should be an attrSet of appid to packages generated by fetchNextcloudApp.
         The appid must be identical to the "id" value in the apps appinfo/info.xml.
         Using this will disable the appstore to prevent Nextcloud from updating these apps (see [](#opt-services.nextcloud.appstoreEnable)).
@@ -267,7 +267,7 @@ in {
     extraAppsEnable = mkOption {
       type = types.bool;
       default = true;
-      description = lib.mdDoc ''
+      description = ''
         Automatically enable the apps in [](#opt-services.nextcloud.extraApps) every time Nextcloud starts.
         If set to false, apps need to be enabled in the Nextcloud web user interface or with `nextcloud-occ app:enable`.
       '';
@@ -276,7 +276,7 @@ in {
       type = types.nullOr types.bool;
       default = null;
       example = true;
-      description = lib.mdDoc ''
+      description = ''
         Allow the installation and updating of apps from the Nextcloud appstore.
         Enabled by default unless there are packages in [](#opt-services.nextcloud.extraApps).
         Set this to true to force enable the store even if [](#opt-services.nextcloud.extraApps) is used.
@@ -286,11 +286,11 @@ in {
     https = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc "Use HTTPS for generated links.";
+      description = "Use HTTPS for generated links.";
     };
     package = mkOption {
       type = types.package;
-      description = lib.mdDoc "Which package to use for the Nextcloud instance.";
+      description = "Which package to use for the Nextcloud instance.";
       relatedPackages = [ "nextcloud26" "nextcloud27" "nextcloud28" ];
     };
     phpPackage = mkPackageOption pkgs "php" {
@@ -300,7 +300,7 @@ in {
     maxUploadSize = mkOption {
       default = "512M";
       type = types.str;
-      description = lib.mdDoc ''
+      description = ''
         The upload limit for files. This changes the relevant options
         in php.ini and nginx if enabled.
       '';
@@ -309,7 +309,7 @@ in {
     webfinger = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Enable this option if you plan on using the webfinger plugin.
         The appropriate nginx rewrite rules will be added to your configuration.
       '';
@@ -319,7 +319,7 @@ in {
       type = with types; functionTo (listOf package);
       default = all: [];
       defaultText = literalExpression "all: []";
-      description = lib.mdDoc ''
+      description = ''
         Additional PHP extensions to use for Nextcloud.
         By default, only extensions necessary for a vanilla Nextcloud installation are enabled,
         but you may choose from the list of available extensions and add further ones.
@@ -333,7 +333,7 @@ in {
     phpOptions = mkOption {
       type = with types; attrsOf (oneOf [ str int ]);
       defaultText = literalExpression (generators.toPretty { } defaultPHPSettings);
-      description = lib.mdDoc ''
+      description = ''
         Options for PHP's php.ini file for nextcloud.
 
         Please note that this option is _additive_ on purpose while the
@@ -372,7 +372,7 @@ in {
         "pm.max_spare_servers" = "4";
         "pm.max_requests" = "500";
       };
-      description = lib.mdDoc ''
+      description = ''
         Options for nextcloud's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives.
       '';
     };
@@ -380,7 +380,7 @@ in {
     poolConfig = mkOption {
       type = types.nullOr types.lines;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         Options for Nextcloud's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives.
       '';
     };
@@ -388,7 +388,7 @@ in {
     fastcgiTimeout = mkOption {
       type = types.int;
       default = 120;
-      description = lib.mdDoc ''
+      description = ''
         FastCGI timeout for database connection in seconds.
       '';
     };
@@ -398,7 +398,7 @@ in {
       createLocally = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           Whether to create the database and database user locally.
         '';
       };
@@ -409,22 +409,22 @@ in {
       dbtype = mkOption {
         type = types.enum [ "sqlite" "pgsql" "mysql" ];
         default = "sqlite";
-        description = lib.mdDoc "Database type.";
+        description = "Database type.";
       };
       dbname = mkOption {
         type = types.nullOr types.str;
         default = "nextcloud";
-        description = lib.mdDoc "Database name.";
+        description = "Database name.";
       };
       dbuser = mkOption {
         type = types.nullOr types.str;
         default = "nextcloud";
-        description = lib.mdDoc "Database user.";
+        description = "Database user.";
       };
       dbpassFile = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           The full path to a file that contains the database password.
         '';
       };
@@ -436,7 +436,7 @@ in {
           else "localhost";
         defaultText = "localhost";
         example = "localhost:5000";
-        description = lib.mdDoc ''
+        description = ''
           Database host (+port) or socket path.
           If [](#opt-services.nextcloud.database.createLocally) is true and
           [](#opt-services.nextcloud.config.dbtype) is either `pgsql` or `mysql`,
@@ -446,12 +446,12 @@ in {
       dbtableprefix = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = lib.mdDoc "Table prefix in Nextcloud's database.";
+        description = "Table prefix in Nextcloud's database.";
       };
       adminuser = mkOption {
         type = types.str;
         default = "root";
-        description = lib.mdDoc ''
+        description = ''
           Username for the admin account. The username is only set during the
           initial setup of Nextcloud! Since the username also acts as unique
           ID internally, it cannot be changed later!
@@ -459,7 +459,7 @@ in {
       };
       adminpassFile = mkOption {
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           The full path to a file that contains the admin's password. Must be
           readable by user `nextcloud`. The password is set only in the initial
           setup of Nextcloud by the systemd service `nextcloud-setup.service`.
@@ -467,7 +467,7 @@ in {
       };
       objectstore = {
         s3 = {
-          enable = mkEnableOption (lib.mdDoc ''
+          enable = mkEnableOption ''
             S3 object storage as primary storage.
 
             This mounts a bucket on an Amazon S3 object storage or compatible
@@ -475,31 +475,31 @@ in {
 
             Further details about this feature can be found in the
             [upstream documentation](https://docs.nextcloud.com/server/22/admin_manual/configuration_files/primary_storage.html).
-          '');
+          '';
           bucket = mkOption {
             type = types.str;
             example = "nextcloud";
-            description = lib.mdDoc ''
+            description = ''
               The name of the S3 bucket.
             '';
           };
           autocreate = mkOption {
             type = types.bool;
-            description = lib.mdDoc ''
+            description = ''
               Create the objectstore if it does not exist.
             '';
           };
           key = mkOption {
             type = types.str;
             example = "EJ39ITYZEUH5BGWDRUFY";
-            description = lib.mdDoc ''
+            description = ''
               The access key for the S3 bucket.
             '';
           };
           secretFile = mkOption {
             type = types.str;
             example = "/var/nextcloud-objectstore-s3-secret";
-            description = lib.mdDoc ''
+            description = ''
               The full path to a file that contains the access secret. Must be
               readable by user `nextcloud`.
             '';
@@ -508,21 +508,21 @@ in {
             type = types.nullOr types.str;
             default = null;
             example = "example.com";
-            description = lib.mdDoc ''
+            description = ''
               Required for some non-Amazon implementations.
             '';
           };
           port = mkOption {
             type = types.nullOr types.port;
             default = null;
-            description = lib.mdDoc ''
+            description = ''
               Required for some non-Amazon implementations.
             '';
           };
           useSsl = mkOption {
             type = types.bool;
             default = true;
-            description = lib.mdDoc ''
+            description = ''
               Use SSL for objectstore access.
             '';
           };
@@ -530,14 +530,14 @@ in {
             type = types.nullOr types.str;
             default = null;
             example = "REGION";
-            description = lib.mdDoc ''
+            description = ''
               Required for some non-Amazon implementations.
             '';
           };
           usePathStyle = mkOption {
             type = types.bool;
             default = false;
-            description = lib.mdDoc ''
+            description = ''
               Required for some non-Amazon S3 implementations.
 
               Ordinarily, requests will be made with
@@ -550,7 +550,7 @@ in {
             type = types.nullOr types.path;
             default = null;
             example = "/var/nextcloud-objectstore-s3-sse-c-key";
-            description = lib.mdDoc ''
+            description = ''
               If provided this is the full path to a file that contains the key
               to enable [server-side encryption with customer-provided keys][1]
               (SSE-C).
@@ -571,13 +571,13 @@ in {
       };
     };
 
-    enableImagemagick = mkEnableOption (lib.mdDoc ''
+    enableImagemagick = mkEnableOption ''
         the ImageMagick module for PHP.
         This is used by the theming app and for generating previews of certain images (e.g. SVG and HEIF).
         You may want to disable it for increased security. In that case, previews will still be available
         for some images (e.g. JPEG and PNG).
         See <https://github.com/nextcloud/server/issues/13099>.
-    '') // {
+    '' // {
       default = true;
     };
 
@@ -585,7 +585,7 @@ in {
       type = lib.types.bool;
       default = config.services.nextcloud.notify_push.enable;
       defaultText = literalExpression "config.services.nextcloud.notify_push.enable";
-      description = lib.mdDoc ''
+      description = ''
         Whether to configure Nextcloud to use the recommended Redis settings for small instances.
 
         ::: {.note}
@@ -598,14 +598,14 @@ in {
       apcu = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether to load the APCu module into PHP.
         '';
       };
       redis = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           Whether to load the Redis module into PHP.
           You still need to enable Redis in your config.php.
           See https://docs.nextcloud.com/server/14/admin_manual/configuration_server/caching_configuration.html
@@ -614,7 +614,7 @@ in {
       memcached = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           Whether to load the Memcached module into PHP.
           You still need to enable Memcached in your config.php.
           See https://docs.nextcloud.com/server/14/admin_manual/configuration_server/caching_configuration.html
@@ -625,7 +625,7 @@ in {
       enable = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           Run a regular auto-update of all apps installed from the Nextcloud app store.
         '';
       };
@@ -633,7 +633,7 @@ in {
         type = with types; either str (listOf str);
         default = "05:00:00";
         example = "Sun 14:00:00";
-        description = lib.mdDoc ''
+        description = ''
           When to run the update. See `systemd.services.<name>.startAt`.
         '';
       };
@@ -643,7 +643,7 @@ in {
       default = occ;
       defaultText = literalMD "generated script";
       internal = true;
-      description = lib.mdDoc ''
+      description = ''
         The nextcloud-occ program preconfigured to target this Nextcloud instance.
       '';
     };
@@ -656,7 +656,7 @@ in {
           loglevel = mkOption {
             type = types.ints.between 0 4;
             default = 2;
-            description = lib.mdDoc ''
+            description = ''
               Log level value between 0 (DEBUG) and 4 (FATAL).
 
               - 0 (debug): Log all activity.
@@ -673,7 +673,7 @@ in {
           log_type = mkOption {
             type = types.enum [ "errorlog" "file" "syslog" "systemd" ];
             default = "syslog";
-            description = lib.mdDoc ''
+            description = ''
               Logging backend to use.
               systemd requires the php-systemd package to be added to services.nextcloud.phpExtraExtensions.
               See the [nextcloud documentation](https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/logging_configuration.html) for details.
@@ -682,7 +682,7 @@ in {
           skeletondirectory = mkOption {
             default = "";
             type = types.str;
-            description = lib.mdDoc ''
+            description = ''
               The directory where the skeleton files are located. These files will be
               copied to the data directory of new users. Leave empty to not copy any
               skeleton files.
@@ -691,7 +691,7 @@ in {
           trusted_domains = mkOption {
             type = types.listOf types.str;
             default = [];
-            description = lib.mdDoc ''
+            description = ''
               Trusted domains, from which the nextcloud installation will be
               accessible. You don't need to add
               `services.nextcloud.hostname` here.
@@ -700,7 +700,7 @@ in {
           trusted_proxies = mkOption {
             type = types.listOf types.str;
             default = [];
-            description = lib.mdDoc ''
+            description = ''
               Trusted proxies, to provide if the nextcloud installation is being
               proxied to secure against e.g. spoofing.
             '';
@@ -709,7 +709,7 @@ in {
             type = types.enum [ "" "http" "https" ];
             default = "";
             example = "https";
-            description = lib.mdDoc ''
+            description = ''
               Force Nextcloud to always use HTTP or HTTPS i.e. for link generation.
               Nextcloud uses the currently used protocol by default, but when
               behind a reverse-proxy, it may use `http` for everything although
@@ -720,7 +720,7 @@ in {
             default = "";
             type = types.str;
             example = "DE";
-            description = lib.mdDoc ''
+            description = ''
               An [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html)
               country code which replaces automatic phone-number detection
               without a country code.
@@ -729,8 +729,8 @@ in {
               the `+49` prefix can be omitted for phone numbers.
             '';
           };
-          "profile.enabled" = mkEnableOption (lib.mdDoc "global profiles") // {
-            description = lib.mdDoc ''
+          "profile.enabled" = mkEnableOption "global profiles" // {
+            description = ''
               Makes user-profiles globally available under `nextcloud.tld/u/user.name`.
               Even though it's enabled by default in Nextcloud, it must be explicitly enabled
               here because it has the side-effect that personal information is even accessible to
@@ -751,7 +751,7 @@ in {
         };
       };
       default = {};
-      description = lib.mdDoc ''
+      description = ''
         Extra options which should be appended to Nextcloud's config.php file.
       '';
       example = literalExpression '' {
@@ -768,7 +768,7 @@ in {
     secretFile = mkOption {
       type = types.nullOr types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         Secret options which will be appended to Nextcloud's config.php file (written as JSON, in the same
         form as the [](#opt-services.nextcloud.settings) option), for example
         `{"redis":{"password":"secret"}}`.
@@ -779,12 +779,12 @@ in {
       recommendedHttpHeaders = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc "Enable additional recommended HTTP response headers";
+        description = "Enable additional recommended HTTP response headers";
       };
       hstsMaxAge = mkOption {
         type = types.ints.positive;
         default = 15552000;
-        description = lib.mdDoc ''
+        description = ''
           Value for the `max-age` directive of the HTTP
           `Strict-Transport-Security` header.
 
@@ -819,7 +819,8 @@ in {
         ++ (optional (versionOlder cfg.package.version "25") (upgradeWarning 24 "22.11"))
         ++ (optional (versionOlder cfg.package.version "26") (upgradeWarning 25 "23.05"))
         ++ (optional (versionOlder cfg.package.version "27") (upgradeWarning 26 "23.11"))
-        ++ (optional (versionOlder cfg.package.version "28") (upgradeWarning 27 "24.05"));
+        ++ (optional (versionOlder cfg.package.version "28") (upgradeWarning 27 "24.05"))
+        ++ (optional (versionOlder cfg.package.version "29") (upgradeWarning 28 "24.11"));
 
       services.nextcloud.package = with pkgs;
         mkDefault (
@@ -832,10 +833,12 @@ in {
           else if versionOlder stateVersion "23.05" then nextcloud25
           else if versionOlder stateVersion "23.11" then nextcloud26
           else if versionOlder stateVersion "24.05" then nextcloud27
-          else nextcloud28
+          else nextcloud29
         );
 
-      services.nextcloud.phpPackage = pkgs.php82;
+      services.nextcloud.phpPackage =
+        if versionOlder cfg.package.version "29" then pkgs.php82
+        else pkgs.php83;
 
       services.nextcloud.phpOptions = mkMerge [
         (mapAttrs (const mkOptionDefault) defaultPHPSettings)
diff --git a/nixpkgs/nixos/modules/services/web-apps/nexus.nix b/nixpkgs/nixos/modules/services/web-apps/nexus.nix
index c67562d38992..fdf42ace6b0e 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nexus.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/nexus.nix
@@ -10,7 +10,7 @@ in
 {
   options = {
     services.nexus = {
-      enable = mkEnableOption (lib.mdDoc "Sonatype Nexus3 OSS service");
+      enable = mkEnableOption "Sonatype Nexus3 OSS service";
 
       package = lib.mkPackageOption pkgs "nexus" { };
 
@@ -19,31 +19,31 @@ in
       user = mkOption {
         type = types.str;
         default = "nexus";
-        description = lib.mdDoc "User which runs Nexus3.";
+        description = "User which runs Nexus3.";
       };
 
       group = mkOption {
         type = types.str;
         default = "nexus";
-        description = lib.mdDoc "Group which runs Nexus3.";
+        description = "Group which runs Nexus3.";
       };
 
       home = mkOption {
         type = types.str;
         default = "/var/lib/sonatype-work";
-        description = lib.mdDoc "Home directory of the Nexus3 instance.";
+        description = "Home directory of the Nexus3 instance.";
       };
 
       listenAddress = mkOption {
         type = types.str;
         default = "127.0.0.1";
-        description = lib.mdDoc "Address to listen on.";
+        description = "Address to listen on.";
       };
 
       listenPort = mkOption {
         type = types.int;
         default = 8081;
-        description = lib.mdDoc "Port to listen on.";
+        description = "Port to listen on.";
       };
 
       jvmOpts = mkOption {
@@ -89,7 +89,7 @@ in
           '''
         '';
 
-        description = lib.mdDoc ''
+        description = ''
           Options for the JVM written to `nexus.jvmopts`.
           Please refer to the docs (https://help.sonatype.com/repomanager3/installation/configuring-the-runtime-environment)
           for further information.
diff --git a/nixpkgs/nixos/modules/services/web-apps/nifi.nix b/nixpkgs/nixos/modules/services/web-apps/nifi.nix
index c0fc443f0df7..48de6b1495ab 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nifi.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/nifi.nix
@@ -25,31 +25,31 @@ let
 in {
   options = {
     services.nifi = {
-      enable = lib.mkEnableOption (lib.mdDoc "Apache NiFi");
+      enable = lib.mkEnableOption "Apache NiFi";
 
       package = lib.mkOption {
         type = lib.types.package;
         default = pkgs.nifi;
         defaultText = lib.literalExpression "pkgs.nifi";
-        description = lib.mdDoc "Apache NiFi package to use.";
+        description = "Apache NiFi package to use.";
       };
 
       user = lib.mkOption {
         type = lib.types.str;
         default = "nifi";
-        description = lib.mdDoc "User account where Apache NiFi runs.";
+        description = "User account where Apache NiFi runs.";
       };
 
       group = lib.mkOption {
         type = lib.types.str;
         default = "nifi";
-        description = lib.mdDoc "Group account where Apache NiFi runs.";
+        description = "Group account where Apache NiFi runs.";
       };
 
       enableHTTPS = lib.mkOption {
         type = lib.types.bool;
         default = true;
-        description = lib.mdDoc "Enable HTTPS protocol. Don`t use in production.";
+        description = "Enable HTTPS protocol. Don`t use in production.";
       };
 
       listenHost = lib.mkOption {
@@ -60,7 +60,7 @@ in {
           then "0.0.0.0"
           else "127.0.0.1"
         '';
-        description = lib.mdDoc "Bind to an ip for Apache NiFi web-ui.";
+        description = "Bind to an ip for Apache NiFi web-ui.";
       };
 
       listenPort = lib.mkOption {
@@ -71,7 +71,7 @@ in {
           then "8443"
           else "8000"
         '';
-        description = lib.mdDoc "Bind to a port for Apache NiFi web-ui.";
+        description = "Bind to a port for Apache NiFi web-ui.";
       };
 
       proxyHost = lib.mkOption {
@@ -82,7 +82,7 @@ in {
           then "0.0.0.0"
           else null
         '';
-        description = lib.mdDoc "Allow requests from a specific host.";
+        description = "Allow requests from a specific host.";
       };
 
       proxyPort = lib.mkOption {
@@ -93,34 +93,34 @@ in {
           then "8443"
           else null
         '';
-        description = lib.mdDoc "Allow requests from a specific port.";
+        description = "Allow requests from a specific port.";
       };
 
       initUser = lib.mkOption {
         type = lib.types.nullOr lib.types.str;
         default = null;
-        description = lib.mdDoc "Initial user account for Apache NiFi. Username must be at least 4 characters.";
+        description = "Initial user account for Apache NiFi. Username must be at least 4 characters.";
       };
 
       initPasswordFile = lib.mkOption {
         type = lib.types.nullOr lib.types.path;
         default = null;
         example = "/run/keys/nifi/password-nifi";
-        description = lib.mdDoc "nitial password for Apache NiFi. Password must be at least 12 characters.";
+        description = "nitial password for Apache NiFi. Password must be at least 12 characters.";
       };
 
       initJavaHeapSize = lib.mkOption {
         type = lib.types.nullOr lib.types.int;
         default = null;
         example = 1024;
-        description = lib.mdDoc "Set the initial heap size for the JVM in MB.";
+        description = "Set the initial heap size for the JVM in MB.";
       };
 
       maxJavaHeapSize = lib.mkOption {
         type = lib.types.nullOr lib.types.int;
         default = null;
         example = 2048;
-        description = lib.mdDoc "Set the initial heap size for the JVM in MB.";
+        description = "Set the initial heap size for the JVM in MB.";
       };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/node-red.nix b/nixpkgs/nixos/modules/services/web-apps/node-red.nix
index 82f89783d778..7c8a2a6687b9 100644
--- a/nixpkgs/nixos/modules/services/web-apps/node-red.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/node-red.nix
@@ -17,14 +17,14 @@ let
 in
 {
   options.services.node-red = {
-    enable = mkEnableOption (lib.mdDoc "the Node-RED service");
+    enable = mkEnableOption "the Node-RED service";
 
     package = mkPackageOption pkgs [ "nodePackages" "node-red" ] { };
 
     openFirewall = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Open ports in the firewall for the server.
       '';
     };
@@ -32,7 +32,7 @@ in
     withNpmAndGcc = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Give Node-RED access to NPM and GCC at runtime, so 'Nodes' can be
         downloaded and managed imperatively via the 'Palette Manager'.
       '';
@@ -42,7 +42,7 @@ in
       type = types.path;
       default = "${cfg.package}/lib/node_modules/node-red/settings.js";
       defaultText = literalExpression ''"''${package}/lib/node_modules/node-red/settings.js"'';
-      description = lib.mdDoc ''
+      description = ''
         Path to the JavaScript configuration file.
         See <https://github.com/node-red/node-red/blob/master/packages/node_modules/node-red/settings.js>
         for a configuration example.
@@ -52,13 +52,13 @@ in
     port = mkOption {
       type = types.port;
       default = 1880;
-      description = lib.mdDoc "Listening port.";
+      description = "Listening port.";
     };
 
     user = mkOption {
       type = types.str;
       default = defaultUser;
-      description = lib.mdDoc ''
+      description = ''
         User under which Node-RED runs.If left as the default value this user
         will automatically be created on system activation, otherwise the
         sysadmin is responsible for ensuring the user exists.
@@ -68,7 +68,7 @@ in
     group = mkOption {
       type = types.str;
       default = defaultUser;
-      description = lib.mdDoc ''
+      description = ''
         Group under which Node-RED runs.If left as the default value this group
         will automatically be created on system activation, otherwise the
         sysadmin is responsible for ensuring the group exists.
@@ -78,7 +78,7 @@ in
     userDir = mkOption {
       type = types.path;
       default = "/var/lib/node-red";
-      description = lib.mdDoc ''
+      description = ''
         The directory to store all user data, such as flow and credential files and all library data. If left
         as the default value this directory will automatically be created before the node-red service starts,
         otherwise the sysadmin is responsible for ensuring the directory exists with appropriate ownership
@@ -89,13 +89,13 @@ in
     safe = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc "Whether to launch Node-RED in --safe mode.";
+      description = "Whether to launch Node-RED in --safe mode.";
     };
 
     define = mkOption {
       type = types.attrs;
       default = {};
-      description = lib.mdDoc "List of settings.js overrides to pass via -D to Node-RED.";
+      description = "List of settings.js overrides to pass via -D to Node-RED.";
       example = literalExpression ''
         {
           "logging.console.level" = "trace";
diff --git a/nixpkgs/nixos/modules/services/web-apps/ocis.md b/nixpkgs/nixos/modules/services/web-apps/ocis.md
new file mode 100644
index 000000000000..9156e927ed2d
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/ocis.md
@@ -0,0 +1,113 @@
+# ownCloud Infinite Scale {#module-services-ocis}
+
+[ownCloud Infinite Scale](https://owncloud.dev/ocis/) (oCIS) is an open-source,
+modern file-sync and sharing platform. It is a ground-up rewrite of the well-known PHP based ownCloud server.
+
+The server setup can be automated using
+[services.ocis](#opt-services.ocis.enable). The desktop client is packaged at
+`pkgs.owncloud-client`.
+
+## Basic usage {#module-services-ocis-basic-usage}
+
+oCIS is a golang application and does not require an HTTP server (such as nginx)
+in front of it, though you may optionally use one if you will.
+
+oCIS is configured using a combination of yaml and environment variables. It is
+recommended to familiarize yourself with upstream's available configuration
+options and deployment instructions:
+
+* [Getting Started](https://owncloud.dev/ocis/getting-started/)
+* [Configuration](https://owncloud.dev/ocis/config/)
+* [Basic Setup](https://owncloud.dev/ocis/deployment/basic-remote-setup/)
+
+A very basic configuration may look like this:
+```
+{ pkgs, ... }:
+{
+  services.ocis = {
+    enable = true;
+    configDir = "/etc/ocis/config";
+  };
+}
+```
+
+This will start the oCIS server and make it available at `https://localhost:9200`
+
+However to make this configuration work you will need generate a configuration.
+You can do this with:
+
+```console
+$ nix-shell -p ocis-bin
+$ mkdir scratch/
+$ cd scratch/
+$ ocis init --config-path . --admin-password "changeme"
+```
+
+You may need to pass `--insecure true` or provide the `OCIS_INSECURE = true;` to
+[`services.ocis.environment`][mod-envFile], if TLS certificates are generated
+and managed externally (e.g. if you are using oCIS behind reverse proxy).
+
+If you want to manage the config file in your nix configuration, then it is
+encouraged to use a secrets manager like sops-nix or agenix.
+
+Be careful not to write files containing secrets to the globally readable nix
+store.
+
+Please note that current NixOS module for oCIS is configured to run in `fullstack`
+mode, which starts all the services for owncloud on single instance. This will
+start multiple ocis services and listen on multiple other ports.
+
+Current known services and their ports are as below:
+
+| Service            | Group   |  Port |
+|--------------------|---------|-------|
+| gateway            | api     |  9142 |
+| sharing            | api     |  9150 |
+| app-registry       | api     |  9242 |
+| ocdav              | web     | 45023 |
+| auth-machine       | api     |  9166 |
+| storage-system     | api     |  9215 |
+| webdav             | web     |  9115 |
+| webfinger          | web     | 46871 |
+| storage-system     | web     |  9216 |
+| web                | web     |  9100 |
+| eventhistory       | api     | 33177 |
+| ocs                | web     |  9110 |
+| storage-publiclink | api     |  9178 |
+| settings           | web     |  9190 |
+| ocm                | api     |  9282 |
+| settings           | api     |  9191 |
+| ocm                | web     |  9280 |
+| app-provider       | api     |  9164 |
+| storage-users      | api     |  9157 |
+| auth-service       | api     |  9199 |
+| thumbnails         | web     |  9186 |
+| thumbnails         | api     |  9185 |
+| storage-shares     | api     |  9154 |
+| sse                | sse     | 46833 |
+| userlog            | userlog | 45363 |
+| search             | api     |  9220 |
+| proxy              | web     |  9200 |
+| idp                | web     |  9130 |
+| frontend           | web     |  9140 |
+| groups             | api     |  9160 |
+| graph              | graph   |  9120 |
+| users              | api     |  9144 |
+| auth-basic         | api     |  9146 |
+
+## Configuration via environment variables
+
+You can also eschew the config file entirely and pass everything to oCIS via
+environment variables. For this make use of
+[`services.ocis.environment`][mod-env] for non-sensitive
+values, and
+[`services.ocis.environmentFile`][mod-envFile] for
+sensitive values.
+
+Configuration in (`services.ocis.environment`)[mod-env] overrides those from
+[`services.ocis.environmentFile`][mod-envFile] and will have highest
+precedence
+
+
+[mod-env]: #opt-services.ocis.environment
+[mod-envFile]: #opt-services.ocis.environmentFile
diff --git a/nixpkgs/nixos/modules/services/web-apps/ocis.nix b/nixpkgs/nixos/modules/services/web-apps/ocis.nix
new file mode 100644
index 000000000000..0266eb6ad29c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/ocis.nix
@@ -0,0 +1,201 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  inherit (lib) types;
+  cfg = config.services.ocis;
+  defaultUser = "ocis";
+  defaultGroup = defaultUser;
+in
+{
+  options = {
+    services.ocis = {
+      enable = lib.mkEnableOption "ownCloud Infinite Scale";
+
+      package = lib.mkPackageOption pkgs "ocis-bin" { };
+
+      configDir = lib.mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/var/lib/ocis/config";
+        description = ''
+          Path to directory containing oCIS config file.
+
+          Example config can be generated by `ocis init --config-path fileName --admin-password "adminPass"`.
+          Add `--insecure true` if SSL certificates are generated and managed externally (e.g. using oCIS behind reverse proxy).
+
+          Note: This directory must contain at least a `ocis.yaml`. Ensure
+          [user](#opt-services.ocis.user) has read/write access to it. In some
+          circumstances you may need to add additional oCIS configuration files (e.g.,
+          `proxy.yaml`) to this directory.
+        '';
+      };
+
+      environmentFile = lib.mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/run/keys/ocis.env";
+        description = ''
+          An environment file as defined in {manpage}`systemd.exec(5)`.
+
+          Configuration provided in this file will override those from [configDir](#opt-services.ocis.configDir)/ocis.yaml.
+        '';
+      };
+
+      user = lib.mkOption {
+        type = types.str;
+        default = defaultUser;
+        example = "yourUser";
+        description = ''
+          The user to run oCIS as.
+          By default, a user named `${defaultUser}` will be created whose home
+          directory is [stateDir](#opt-services.ocis.stateDir).
+        '';
+      };
+
+      group = lib.mkOption {
+        type = types.str;
+        default = defaultGroup;
+        example = "yourGroup";
+        description = ''
+          The group to run oCIS under.
+          By default, a group named `${defaultGroup}` will be created.
+        '';
+      };
+
+      address = lib.mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = "Web interface address.";
+      };
+
+      port = lib.mkOption {
+        type = types.port;
+        default = 9200;
+        description = "Web interface port.";
+      };
+
+      url = lib.mkOption {
+        type = types.str;
+        default = "https://localhost:9200";
+        example = "https://some-hostname-or-ip:9200";
+        description = "Web interface address.";
+      };
+
+      stateDir = lib.mkOption {
+        default = "/var/lib/ocis";
+        type = types.str;
+        description = "ownCloud data directory.";
+      };
+
+      environment = lib.mkOption {
+        type = types.attrsOf types.str;
+        default = { };
+        description = ''
+          Extra config options.
+
+          See [the documentation](https://doc.owncloud.com/ocis/next/deployment/services/services.html) for available options.
+          See [notes for environment variables](https://doc.owncloud.com/ocis/next/deployment/services/env-var-note.html) for more information.
+
+          Note that all the attributes here will be copied to /nix/store/ and will be world readable. Options like *_PASSWORD or *_SECRET should be part of     [environmentFile](#opt-services.ocis.environmentFile) instead, and are only provided here for illustrative purpose.
+
+          Configuration here will override those from [environmentFile](#opt-services.ocis.environmentFile) and will have highest precedence, at the cost of security. Do NOT put security sensitive stuff here.
+        '';
+        example = {
+          OCIS_INSECURE = "false";
+          OCIS_LOG_LEVEL = "error";
+          OCIS_JWT_SECRET = "super_secret";
+          OCIS_TRANSFER_SECRET = "foo";
+          OCIS_MACHINE_AUTH_API_KEY = "foo";
+          OCIS_SYSTEM_USER_ID = "123";
+          OCIS_MOUNT_ID = "123";
+          OCIS_STORAGE_USERS_MOUNT_ID = "123";
+          GATEWAY_STORAGE_USERS_MOUNT_ID = "123";
+          CS3_ALLOW_INSECURE = "true";
+          OCIS_INSECURE_BACKENDS = "true";
+          TLS_INSECURE = "true";
+          TLS_SKIP_VERIFY_CLIENT_CERT = "true";
+          WEBDAV_ALLOW_INSECURE = "true";
+          IDP_TLS = "false";
+          GRAPH_APPLICATION_ID = "1234";
+          IDM_IDPSVC_PASSWORD = "password";
+          IDM_REVASVC_PASSWORD = "password";
+          IDM_SVC_PASSWORD = "password";
+          IDP_ISS = "https://localhost:9200";
+          OCIS_LDAP_BIND_PASSWORD = "password";
+          OCIS_SERVICE_ACCOUNT_ID = "foo";
+          OCIS_SERVICE_ACCOUNT_SECRET = "foo";
+          OCIS_SYSTEM_USER_API_KEY = "foo";
+          STORAGE_USERS_MOUNT_ID = "123";
+        };
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    users.users.${defaultUser} = lib.mkIf (cfg.user == defaultUser) {
+      group = cfg.group;
+      home = cfg.stateDir;
+      isSystemUser = true;
+      createHome = true;
+      description = "ownCloud Infinite Scale daemon user";
+    };
+
+    users.groups = lib.mkIf (cfg.group == defaultGroup) { ${defaultGroup} = { }; };
+
+    systemd = {
+      services.ocis = {
+        description = "ownCloud Infinite Scale Stack";
+        wantedBy = [ "multi-user.target" ];
+        environment = {
+          PROXY_HTTP_ADDR = "${cfg.address}:${toString cfg.port}";
+          OCIS_URL = cfg.url;
+          OCIS_CONFIG_DIR = if (cfg.configDir == null) then "${cfg.stateDir}/config" else cfg.configDir;
+          OCIS_BASE_DATA_PATH = cfg.stateDir;
+        } // cfg.environment;
+        serviceConfig = {
+          Type = "simple";
+          ExecStart = "${lib.getExe cfg.package} server";
+          WorkingDirectory = cfg.stateDir;
+          User = cfg.user;
+          Group = cfg.group;
+          Restart = "always";
+          EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile;
+          ReadWritePaths = [ cfg.stateDir ];
+          ReadOnlyPaths = [ cfg.configDir ];
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
+          PrivateTmp = true;
+          PrivateDevices = true;
+          ProtectSystem = "strict";
+          ProtectHome = true;
+          ProtectControlGroups = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectKernelLogs = true;
+          RestrictAddressFamilies = [
+            "AF_UNIX"
+            "AF_INET"
+            "AF_INET6"
+            "AF_NETLINK"
+          ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          LockPersonality = true;
+          SystemCallArchitectures = "native";
+        };
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [
+    bhankas
+    danth
+    ramblurr
+  ];
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/onlyoffice.nix b/nixpkgs/nixos/modules/services/web-apps/onlyoffice.nix
index 343ca80c9fc2..545ca68ccaac 100644
--- a/nixpkgs/nixos/modules/services/web-apps/onlyoffice.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/onlyoffice.nix
@@ -7,20 +7,20 @@ let
 in
 {
   options.services.onlyoffice = {
-    enable = mkEnableOption (lib.mdDoc "OnlyOffice DocumentServer");
+    enable = mkEnableOption "OnlyOffice DocumentServer";
 
-    enableExampleServer = mkEnableOption (lib.mdDoc "OnlyOffice example server");
+    enableExampleServer = mkEnableOption "OnlyOffice example server";
 
     hostname = mkOption {
       type = types.str;
       default = "localhost";
-      description = lib.mdDoc "FQDN for the onlyoffice instance.";
+      description = "FQDN for the onlyoffice instance.";
     };
 
     jwtSecretFile = mkOption {
       type = types.nullOr types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         Path to a file that contains the secret to sign web requests using JSON Web Tokens.
         If left at the default value null signing is disabled.
       '';
@@ -31,31 +31,31 @@ in
     port = mkOption {
       type = types.port;
       default = 8000;
-      description = lib.mdDoc "Port the OnlyOffice DocumentServer should listens on.";
+      description = "Port the OnlyOffice DocumentServer should listens on.";
     };
 
     examplePort = mkOption {
       type = types.port;
       default = null;
-      description = lib.mdDoc "Port the OnlyOffice Example server should listens on.";
+      description = "Port the OnlyOffice Example server should listens on.";
     };
 
     postgresHost = mkOption {
       type = types.str;
       default = "/run/postgresql";
-      description = lib.mdDoc "The Postgresql hostname or socket path OnlyOffice should connect to.";
+      description = "The Postgresql hostname or socket path OnlyOffice should connect to.";
     };
 
     postgresName = mkOption {
       type = types.str;
       default = "onlyoffice";
-      description = lib.mdDoc "The name of database OnlyOffice should user.";
+      description = "The name of database OnlyOffice should user.";
     };
 
     postgresPasswordFile = mkOption {
       type = types.nullOr types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         Path to a file that contains the password OnlyOffice should use to connect to Postgresql.
         Unused when using socket authentication.
       '';
@@ -64,7 +64,7 @@ in
     postgresUser = mkOption {
       type = types.str;
       default = "onlyoffice";
-      description = lib.mdDoc ''
+      description = ''
         The username OnlyOffice should use to connect to Postgresql.
         Unused when using socket authentication.
       '';
@@ -73,7 +73,7 @@ in
     rabbitmqUrl = mkOption {
       type = types.str;
       default = "amqp://guest:guest@localhost:5672";
-      description = lib.mdDoc "The Rabbitmq in amqp URI style OnlyOffice should connect to.";
+      description = "The Rabbitmq in amqp URI style OnlyOffice should connect to.";
     };
   };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/openvscode-server.nix b/nixpkgs/nixos/modules/services/web-apps/openvscode-server.nix
index 81b9d1f3b4c8..b3c22cd43b78 100644
--- a/nixpkgs/nixos/modules/services/web-apps/openvscode-server.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/openvscode-server.nix
@@ -8,13 +8,13 @@ in
 {
   options = {
     services.openvscode-server = {
-      enable = lib.mkEnableOption (lib.mdDoc "openvscode-server");
+      enable = lib.mkEnableOption "openvscode-server";
 
       package = lib.mkPackageOption pkgs "openvscode-server" { };
 
       extraPackages = lib.mkOption {
         default = [ ];
-        description = lib.mdDoc ''
+        description = ''
           Additional packages to add to the openvscode-server {env}`PATH`.
         '';
         example = lib.literalExpression "[ pkgs.go ]";
@@ -23,7 +23,7 @@ in
 
       extraEnvironment = lib.mkOption {
         type = lib.types.attrsOf lib.types.str;
-        description = lib.mdDoc ''
+        description = ''
           Additional environment variables to pass to openvscode-server.
         '';
         default = { };
@@ -32,7 +32,7 @@ in
 
       extraArguments = lib.mkOption {
         default = [ ];
-        description = lib.mdDoc ''
+        description = ''
           Additional arguments to pass to openvscode-server.
         '';
         example = lib.literalExpression ''[ "--log=info" ]'';
@@ -41,7 +41,7 @@ in
 
       host = lib.mkOption {
         default = "localhost";
-        description = lib.mdDoc ''
+        description = ''
           The host name or IP address the server should listen to.
         '';
         type = lib.types.str;
@@ -49,7 +49,7 @@ in
 
       port = lib.mkOption {
         default = 3000;
-        description = lib.mdDoc ''
+        description = ''
           The port the server should listen to. If 0 is passed a random free port is picked. If a range in the format num-num is passed, a free port from the range (end inclusive) is selected.
         '';
         type = lib.types.port;
@@ -58,7 +58,7 @@ in
       user = lib.mkOption {
         default = defaultUser;
         example = "yourUser";
-        description = lib.mdDoc ''
+        description = ''
           The user to run openvscode-server as.
           By default, a user named `${defaultUser}` will be created.
         '';
@@ -68,7 +68,7 @@ in
       group = lib.mkOption {
         default = defaultGroup;
         example = "yourGroup";
-        description = lib.mdDoc ''
+        description = ''
           The group to run openvscode-server under.
           By default, a group named `${defaultGroup}` will be created.
         '';
@@ -77,7 +77,7 @@ in
 
       extraGroups = lib.mkOption {
         default = [ ];
-        description = lib.mdDoc ''
+        description = ''
           An array of additional groups for the `${defaultUser}` user.
         '';
         example = [ "docker" ];
@@ -86,7 +86,7 @@ in
 
       withoutConnectionToken = lib.mkOption {
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           Run without a connection token. Only use this if the connection is secured by other means.
         '';
         example = true;
@@ -96,7 +96,7 @@ in
       socketPath = lib.mkOption {
         default = null;
         example = "/run/openvscode/socket";
-        description = lib.mdDoc ''
+        description = ''
           The path to a socket file for the server to listen to.
         '';
         type = lib.types.nullOr lib.types.str;
@@ -104,7 +104,7 @@ in
 
       userDataDir = lib.mkOption {
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.
         '';
         type = lib.types.nullOr lib.types.str;
@@ -112,7 +112,7 @@ in
 
       serverDataDir = lib.mkOption {
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           Specifies the directory that server data is kept in.
         '';
         type = lib.types.nullOr lib.types.str;
@@ -120,7 +120,7 @@ in
 
       extensionsDir = lib.mkOption {
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           Set the root path for extensions.
         '';
         type = lib.types.nullOr lib.types.str;
@@ -129,7 +129,7 @@ in
       telemetryLevel = lib.mkOption {
         default = null;
         example = "crash";
-        description = lib.mdDoc ''
+        description = ''
           Sets the initial telemetry level. Valid levels are: 'off', 'crash', 'error' and 'all'.
         '';
         type = lib.types.nullOr (lib.types.enum [ "off" "crash" "error" "all" ]);
@@ -138,7 +138,7 @@ in
       connectionToken = lib.mkOption {
         default = null;
         example = "secret-token";
-        description = lib.mdDoc ''
+        description = ''
           A secret that must be included with all requests.
         '';
         type = lib.types.nullOr lib.types.str;
@@ -146,7 +146,7 @@ in
 
       connectionTokenFile = lib.mkOption {
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           Path to a file that contains the connection token.
         '';
         type = lib.types.nullOr lib.types.str;
diff --git a/nixpkgs/nixos/modules/services/web-apps/openwebrx.nix b/nixpkgs/nixos/modules/services/web-apps/openwebrx.nix
index ddc2d66e723c..614eb963b4a3 100644
--- a/nixpkgs/nixos/modules/services/web-apps/openwebrx.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/openwebrx.nix
@@ -4,7 +4,7 @@ let
 in
 {
   options.services.openwebrx = with lib; {
-    enable = mkEnableOption (lib.mdDoc "OpenWebRX Web interface for Software-Defined Radios on http://localhost:8073");
+    enable = mkEnableOption "OpenWebRX Web interface for Software-Defined Radios on http://localhost:8073";
 
     package = mkPackageOption pkgs "openwebrx" { };
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/outline.nix b/nixpkgs/nixos/modules/services/web-apps/outline.nix
index 702755dfa2ab..4c1de579ecc5 100644
--- a/nixpkgs/nixos/modules/services/web-apps/outline.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/outline.nix
@@ -16,7 +16,7 @@ in
   #   https://github.com/outline/outline/blob/v0.67.0/shared/types.ts
   # The order is kept the same here to make updating easier.
   options.services.outline = {
-    enable = lib.mkEnableOption (lib.mdDoc "outline");
+    enable = lib.mkEnableOption "outline";
 
     package = lib.mkOption {
       default = pkgs.outline;
@@ -33,13 +33,13 @@ in
           ${"''"};
         })
       '';
-      description = lib.mdDoc "Outline package to use.";
+      description = "Outline package to use.";
     };
 
     user = lib.mkOption {
       type = lib.types.str;
       default = defaultUser;
-      description = lib.mdDoc ''
+      description = ''
         User under which the service should run. If this is the default value,
         the user will be created, with the specified group as the primary
         group.
@@ -49,7 +49,7 @@ in
     group = lib.mkOption {
       type = lib.types.str;
       default = defaultUser;
-      description = lib.mdDoc ''
+      description = ''
         Group under which the service should run. If this is the default value,
         the group will be created.
       '';
@@ -62,7 +62,7 @@ in
     secretKeyFile = lib.mkOption {
       type = lib.types.str;
       default = "/var/lib/outline/secret_key";
-      description = lib.mdDoc ''
+      description = ''
         File path that contains the application secret key. It must be 32
         bytes long and hex-encoded. If the file does not exist, a new key will
         be generated and saved here.
@@ -72,7 +72,7 @@ in
     utilsSecretFile = lib.mkOption {
       type = lib.types.str;
       default = "/var/lib/outline/utils_secret";
-      description = lib.mdDoc ''
+      description = ''
         File path that contains the utility secret key. If the file does not
         exist, a new key will be generated and saved here.
       '';
@@ -81,7 +81,7 @@ in
     databaseUrl = lib.mkOption {
       type = lib.types.str;
       default = "local";
-      description = lib.mdDoc ''
+      description = ''
         URI to use for the main PostgreSQL database. If this needs to include
         credentials that shouldn't be world-readable in the Nix store, set an
         environment file on the systemd service and override the
@@ -93,7 +93,7 @@ in
     redisUrl = lib.mkOption {
       type = lib.types.str;
       default = "local";
-      description = lib.mdDoc ''
+      description = ''
         Connection to a redis server. If this needs to include credentials
         that shouldn't be world-readable in the Nix store, set an environment
         file on the systemd service and override the
@@ -105,17 +105,17 @@ in
     publicUrl = lib.mkOption {
       type = lib.types.str;
       default = "http://localhost:3000";
-      description = lib.mdDoc "The fully qualified, publicly accessible URL";
+      description = "The fully qualified, publicly accessible URL";
     };
 
     port = lib.mkOption {
       type = lib.types.port;
       default = 3000;
-      description = lib.mdDoc "Listening port.";
+      description = "Listening port.";
     };
 
     storage = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         To support uploading of images for avatars and document attachments an
         s3-compatible storage can be provided. AWS S3 is recommended for
         redundancy however if you want to keep all file storage local an
@@ -139,12 +139,12 @@ in
         options = {
           storageType = lib.mkOption {
             type = lib.types.enum [ "local" "s3" ];
-            description = lib.mdDoc "File storage type, it can be local or s3.";
+            description = "File storage type, it can be local or s3.";
             default = "s3";
           };
           localRootDir = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc ''
+            description = ''
               If `storageType` is `local`, this sets the parent directory
               under which all attachments/images go.
             '';
@@ -152,42 +152,42 @@ in
           };
           accessKey = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "S3 access key.";
+            description = "S3 access key.";
           };
           secretKeyFile = lib.mkOption {
             type = lib.types.path;
-            description = lib.mdDoc "File path that contains the S3 secret key.";
+            description = "File path that contains the S3 secret key.";
           };
           region = lib.mkOption {
             type = lib.types.str;
             default = "xx-xxxx-x";
-            description = lib.mdDoc "AWS S3 region name.";
+            description = "AWS S3 region name.";
           };
           uploadBucketUrl = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc ''
+            description = ''
               URL endpoint of an S3-compatible API where uploads should be
               stored.
             '';
           };
           uploadBucketName = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "Name of the bucket where uploads should be stored.";
+            description = "Name of the bucket where uploads should be stored.";
           };
           uploadMaxSize = lib.mkOption {
             type = lib.types.int;
             default = 26214400;
-            description = lib.mdDoc "Maxmium file size for uploads.";
+            description = "Maxmium file size for uploads.";
           };
           forcePathStyle = lib.mkOption {
             type = lib.types.bool;
             default = true;
-            description = lib.mdDoc "Force S3 path style.";
+            description = "Force S3 path style.";
           };
           acl = lib.mkOption {
             type = lib.types.str;
             default = "private";
-            description = lib.mdDoc "ACL setting.";
+            description = "ACL setting.";
           };
         };
       };
@@ -198,7 +198,7 @@ in
     #
 
     slackAuthentication = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         To configure Slack auth, you'll need to create an Application at
         https://api.slack.com/apps
 
@@ -210,18 +210,18 @@ in
         options = {
           clientId = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "Authentication key.";
+            description = "Authentication key.";
           };
           secretFile = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "File path containing the authentication secret.";
+            description = "File path containing the authentication secret.";
           };
         };
       });
     };
 
     googleAuthentication = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         To configure Google auth, you'll need to create an OAuth Client ID at
         https://console.cloud.google.com/apis/credentials
 
@@ -233,18 +233,18 @@ in
         options = {
           clientId = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "Authentication client identifier.";
+            description = "Authentication client identifier.";
           };
           clientSecretFile = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "File path containing the authentication secret.";
+            description = "File path containing the authentication secret.";
           };
         };
       });
     };
 
     azureAuthentication = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         To configure Microsoft/Azure auth, you'll need to create an OAuth
         Client. See
         [the guide](https://wiki.generaloutline.com/share/dfa77e56-d4d2-4b51-8ff8-84ea6608faa4)
@@ -255,22 +255,22 @@ in
         options = {
           clientId = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "Authentication client identifier.";
+            description = "Authentication client identifier.";
           };
           clientSecretFile = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "File path containing the authentication secret.";
+            description = "File path containing the authentication secret.";
           };
           resourceAppId = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "Authentication application resource ID.";
+            description = "Authentication application resource ID.";
           };
         };
       });
     };
 
     oidcAuthentication = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         To configure generic OIDC auth, you'll need some kind of identity
         provider. See the documentation for whichever IdP you use to fill out
         all the fields. The redirect URL is
@@ -281,27 +281,27 @@ in
         options = {
           clientId = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "Authentication client identifier.";
+            description = "Authentication client identifier.";
           };
           clientSecretFile = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "File path containing the authentication secret.";
+            description = "File path containing the authentication secret.";
           };
           authUrl = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "OIDC authentication URL endpoint.";
+            description = "OIDC authentication URL endpoint.";
           };
           tokenUrl = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "OIDC token URL endpoint.";
+            description = "OIDC token URL endpoint.";
           };
           userinfoUrl = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "OIDC userinfo URL endpoint.";
+            description = "OIDC userinfo URL endpoint.";
           };
           usernameClaim = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc ''
+            description = ''
               Specify which claims to derive user information from. Supports any
               valid JSON path with the JWT payload
             '';
@@ -309,12 +309,12 @@ in
           };
           displayName = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "Display name for OIDC authentication.";
+            description = "Display name for OIDC authentication.";
             default = "OpenID";
           };
           scopes = lib.mkOption {
             type = lib.types.listOf lib.types.str;
-            description = lib.mdDoc "OpenID authentication scopes.";
+            description = "OpenID authentication scopes.";
             default = [ "openid" "profile" "email" ];
           };
         };
@@ -328,7 +328,7 @@ in
     sslKeyFile = lib.mkOption {
       type = lib.types.nullOr lib.types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         File path that contains the Base64-encoded private key for HTTPS
         termination. This is only required if you do not use an external reverse
         proxy. See
@@ -338,7 +338,7 @@ in
     sslCertFile = lib.mkOption {
       type = lib.types.nullOr lib.types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         File path that contains the Base64-encoded certificate for HTTPS
         termination. This is only required if you do not use an external reverse
         proxy. See
@@ -349,7 +349,7 @@ in
     cdnUrl = lib.mkOption {
       type = lib.types.str;
       default = "";
-      description = lib.mdDoc ''
+      description = ''
         If using a Cloudfront/Cloudflare distribution or similar it can be set
         using this option. This will cause paths to JavaScript files,
         stylesheets and images to be updated to the hostname defined here. In
@@ -360,7 +360,7 @@ in
     forceHttps = lib.mkOption {
       type = lib.types.bool;
       default = true;
-      description = lib.mdDoc ''
+      description = ''
         Auto-redirect to HTTPS in production. The default is
         `true` but you may set this to `false`
         if you can be sure that SSL is terminated at an external loadbalancer.
@@ -370,7 +370,7 @@ in
     enableUpdateCheck = lib.mkOption {
       type = lib.types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Have the installation check for updates by sending anonymized statistics
         to the maintainers.
       '';
@@ -379,7 +379,7 @@ in
     concurrency = lib.mkOption {
       type = lib.types.int;
       default = 1;
-      description = lib.mdDoc ''
+      description = ''
         How many processes should be spawned. For a rough estimate, divide your
         server's available memory by 512.
       '';
@@ -388,7 +388,7 @@ in
     maximumImportSize = lib.mkOption {
       type = lib.types.int;
       default = 5120000;
-      description = lib.mdDoc ''
+      description = ''
         The maximum size of document imports. Overriding this could be required
         if you have especially large Word documents with embedded imagery.
       '';
@@ -397,11 +397,11 @@ in
     debugOutput = lib.mkOption {
       type = lib.types.nullOr (lib.types.enum [ "http" ]);
       default = null;
-      description = lib.mdDoc "Set this to `http` log HTTP requests.";
+      description = "Set this to `http` log HTTP requests.";
     };
 
     slackIntegration = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         For a complete Slack integration with search and posting to channels
         this configuration is also needed. See here for details:
         https://wiki.generaloutline.com/share/be25efd1-b3ef-4450-b8e5-c4a4fc11e02a
@@ -411,16 +411,16 @@ in
         options = {
           verificationTokenFile = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "File path containing the verification token.";
+            description = "File path containing the verification token.";
           };
           appId = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "Application ID.";
+            description = "Application ID.";
           };
           messageActions = lib.mkOption {
             type = lib.types.bool;
             default = true;
-            description = lib.mdDoc "Whether to enable message actions.";
+            description = "Whether to enable message actions.";
           };
         };
       });
@@ -429,7 +429,7 @@ in
     googleAnalyticsId = lib.mkOption {
       type = lib.types.nullOr lib.types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         Optionally enable Google Analytics to track page views in the knowledge
         base.
       '';
@@ -438,7 +438,7 @@ in
     sentryDsn = lib.mkOption {
       type = lib.types.nullOr lib.types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         Optionally enable [Sentry](https://sentry.io/) to
         track errors and performance.
       '';
@@ -447,7 +447,7 @@ in
     sentryTunnel = lib.mkOption {
       type = lib.types.nullOr lib.types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         Optionally add a
         [Sentry proxy tunnel](https://docs.sentry.io/platforms/javascript/troubleshooting/#using-the-tunnel-option)
         for bypassing ad blockers in the UI.
@@ -457,14 +457,14 @@ in
     logo = lib.mkOption {
       type = lib.types.nullOr lib.types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         Custom logo displayed on the authentication screen. This will be scaled
         to a height of 60px.
       '';
     };
 
     smtp = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         To support sending outgoing transactional emails such as
         "document updated" or "you've been invited" you'll need to provide
         authentication for an SMTP server.
@@ -474,39 +474,39 @@ in
         options = {
           host = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "Host name or IP address of the SMTP server.";
+            description = "Host name or IP address of the SMTP server.";
           };
           port = lib.mkOption {
             type = lib.types.port;
-            description = lib.mdDoc "TCP port of the SMTP server.";
+            description = "TCP port of the SMTP server.";
           };
           username = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "Username to authenticate with.";
+            description = "Username to authenticate with.";
           };
           passwordFile = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc ''
+            description = ''
               File path containing the password to authenticate with.
             '';
           };
           fromEmail = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "Sender email in outgoing mail.";
+            description = "Sender email in outgoing mail.";
           };
           replyEmail = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "Reply address in outgoing mail.";
+            description = "Reply address in outgoing mail.";
           };
           tlsCiphers = lib.mkOption {
             type = lib.types.str;
             default = "";
-            description = lib.mdDoc "Override SMTP cipher configuration.";
+            description = "Override SMTP cipher configuration.";
           };
           secure = lib.mkOption {
             type = lib.types.bool;
             default = true;
-            description = lib.mdDoc "Use a secure SMTP connection.";
+            description = "Use a secure SMTP connection.";
           };
         };
       });
@@ -535,7 +535,7 @@ in
          "zh_TW"
       ];
       default = "en_US";
-      description = lib.mdDoc ''
+      description = ''
         The default interface language. See
         [translate.getoutline.com](https://translate.getoutline.com/)
         for a list of available language codes and their rough percentage
@@ -543,16 +543,16 @@ in
       '';
     };
 
-    rateLimiter.enable = lib.mkEnableOption (lib.mdDoc "rate limiter for the application web server");
+    rateLimiter.enable = lib.mkEnableOption "rate limiter for the application web server";
     rateLimiter.requests = lib.mkOption {
       type = lib.types.int;
       default = 5000;
-      description = lib.mdDoc "Maximum number of requests in a throttling window.";
+      description = "Maximum number of requests in a throttling window.";
     };
     rateLimiter.durationWindow = lib.mkOption {
       type = lib.types.int;
       default = 60;
-      description = lib.mdDoc "Length of a throttling window.";
+      description = "Length of a throttling window.";
     };
   };
 
@@ -783,6 +783,8 @@ in
         # This working directory is required to find stuff like the set of
         # onboarding files:
         WorkingDirectory = "${cfg.package}/share/outline";
+        # In case this directory is not in /var/lib/outline, it needs to be made writable explicitly
+        ReadWritePaths = lib.mkIf (cfg.storage.storageType == "local") [ cfg.storage.localRootDir ];
       };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/peering-manager.nix b/nixpkgs/nixos/modules/services/web-apps/peering-manager.nix
index 0382ce717473..c85cb76e5ea1 100644
--- a/nixpkgs/nixos/modules/services/web-apps/peering-manager.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/peering-manager.nix
@@ -31,7 +31,7 @@ in {
     enable = mkOption {
       type = types.bool;
       default = false;
-      description = mdDoc ''
+      description = ''
         Enable Peering Manager.
 
         This module requires a reverse proxy that serves `/static` separately.
@@ -50,7 +50,7 @@ in {
     listenAddress = mkOption {
       type = types.str;
       default = "[::1]";
-      description = mdDoc ''
+      description = ''
         Address the server will listen on.
       '';
     };
@@ -58,7 +58,7 @@ in {
     port = mkOption {
       type = types.port;
       default = 8001;
-      description = mdDoc ''
+      description = ''
         Port the server will listen on.
       '';
     };
@@ -69,14 +69,14 @@ in {
       defaultText = literalExpression ''
         python3Packages: with python3Packages; [];
       '';
-      description = mdDoc ''
+      description = ''
         List of plugin packages to install.
       '';
     };
 
     secretKeyFile = mkOption {
       type = types.path;
-      description = mdDoc ''
+      description = ''
         Path to a file containing the secret key.
       '';
     };
@@ -84,13 +84,13 @@ in {
     peeringdbApiKeyFile = mkOption {
       type = with types; nullOr path;
       default = null;
-      description = mdDoc ''
+      description = ''
         Path to a file containing the PeeringDB API key.
       '';
     };
 
     settings = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         Configuration options to set in `configuration.py`.
         See the [documentation](https://peering-manager.readthedocs.io/en/stable/configuration/optional-settings/) for more possible options.
       '';
@@ -104,7 +104,7 @@ in {
           ALLOWED_HOSTS = lib.mkOption {
             type = with lib.types; listOf str;
             default = ["*"];
-            description = lib.mdDoc ''
+            description = ''
               A list of valid fully-qualified domain names (FQDNs) and/or IP
               addresses that can be used to reach the peering manager service.
             '';
@@ -116,7 +116,7 @@ in {
     extraConfig = mkOption {
       type = types.lines;
       default = "";
-      description = mdDoc ''
+      description = ''
         Additional lines of configuration appended to the `configuration.py`.
         See the [documentation](https://peering-manager.readthedocs.io/en/stable/configuration/optional-settings/) for more possible options.
       '';
@@ -125,7 +125,7 @@ in {
     enableLdap = mkOption {
       type = types.bool;
       default = false;
-      description = mdDoc ''
+      description = ''
         Enable LDAP-Authentication for Peering Manager.
 
         This requires a configuration file being pass through `ldapConfigPath`.
@@ -134,7 +134,7 @@ in {
 
     ldapConfigPath = mkOption {
       type = types.path;
-      description = mdDoc ''
+      description = ''
         Path to the Configuration-File for LDAP-Authentication, will be loaded as `ldap_config.py`.
         See the [documentation](https://peering-manager.readthedocs.io/en/stable/setup/6-ldap/#configuration) for possible options.
       '';
diff --git a/nixpkgs/nixos/modules/services/web-apps/peertube.nix b/nixpkgs/nixos/modules/services/web-apps/peertube.nix
index 76f869913592..e3f15f4f438c 100644
--- a/nixpkgs/nixos/modules/services/web-apps/peertube.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/peertube.nix
@@ -75,56 +75,56 @@ let
 
 in {
   options.services.peertube = {
-    enable = lib.mkEnableOption (lib.mdDoc "Peertube");
+    enable = lib.mkEnableOption "Peertube";
 
     user = lib.mkOption {
       type = lib.types.str;
       default = "peertube";
-      description = lib.mdDoc "User account under which Peertube runs.";
+      description = "User account under which Peertube runs.";
     };
 
     group = lib.mkOption {
       type = lib.types.str;
       default = "peertube";
-      description = lib.mdDoc "Group under which Peertube runs.";
+      description = "Group under which Peertube runs.";
     };
 
     localDomain = lib.mkOption {
       type = lib.types.str;
       example = "peertube.example.com";
-      description = lib.mdDoc "The domain serving your PeerTube instance.";
+      description = "The domain serving your PeerTube instance.";
     };
 
     listenHttp = lib.mkOption {
       type = lib.types.port;
       default = 9000;
-      description = lib.mdDoc "The port that the local PeerTube web server will listen on.";
+      description = "The port that the local PeerTube web server will listen on.";
     };
 
     listenWeb = lib.mkOption {
       type = lib.types.port;
       default = 9000;
-      description = lib.mdDoc "The public-facing port that PeerTube will be accessible at (likely 80 or 443 if running behind a reverse proxy). Clients will try to access PeerTube at this port.";
+      description = "The public-facing port that PeerTube will be accessible at (likely 80 or 443 if running behind a reverse proxy). Clients will try to access PeerTube at this port.";
     };
 
     enableWebHttps = lib.mkOption {
       type = lib.types.bool;
       default = false;
-      description = lib.mdDoc "Whether clients will access your PeerTube instance with HTTPS. Does NOT configure the PeerTube webserver itself to listen for incoming HTTPS connections.";
+      description = "Whether clients will access your PeerTube instance with HTTPS. Does NOT configure the PeerTube webserver itself to listen for incoming HTTPS connections.";
     };
 
     dataDirs = lib.mkOption {
       type = lib.types.listOf lib.types.path;
       default = [ ];
       example = [ "/opt/peertube/storage" "/var/cache/peertube" ];
-      description = lib.mdDoc "Allow access to custom data locations.";
+      description = "Allow access to custom data locations.";
     };
 
     serviceEnvironmentFile = lib.mkOption {
       type = lib.types.nullOr lib.types.path;
       default = null;
       example = "/run/keys/peertube/password-init-root";
-      description = lib.mdDoc ''
+      description = ''
         Set environment variables for the service. Mainly useful for setting the initial root password.
         For example write to file:
         PT_INITIAL_ROOT_PASSWORD=changeme
@@ -148,13 +148,13 @@ in {
           };
         }
       '';
-      description = lib.mdDoc "Configuration for peertube.";
+      description = "Configuration for peertube.";
     };
 
     configureNginx = lib.mkOption {
       type = lib.types.bool;
       default = false;
-      description = lib.mdDoc "Configure nginx as a reverse proxy for peertube.";
+      description = "Configure nginx as a reverse proxy for peertube.";
     };
 
     secrets = {
@@ -162,7 +162,7 @@ in {
         type = lib.types.nullOr lib.types.path;
         default = null;
         example = "/run/secrets/peertube";
-        description = lib.mdDoc ''
+        description = ''
           Secrets to run PeerTube.
           Generate one using `openssl rand -hex 32`
         '';
@@ -173,7 +173,7 @@ in {
       createLocally = lib.mkOption {
         type = lib.types.bool;
         default = false;
-        description = lib.mdDoc "Configure local PostgreSQL database server for PeerTube.";
+        description = "Configure local PostgreSQL database server for PeerTube.";
       };
 
       host = lib.mkOption {
@@ -185,32 +185,32 @@ in {
           else null
         '';
         example = "192.168.15.47";
-        description = lib.mdDoc "Database host address or unix socket.";
+        description = "Database host address or unix socket.";
       };
 
       port = lib.mkOption {
         type = lib.types.port;
         default = 5432;
-        description = lib.mdDoc "Database host port.";
+        description = "Database host port.";
       };
 
       name = lib.mkOption {
         type = lib.types.str;
         default = "peertube";
-        description = lib.mdDoc "Database name.";
+        description = "Database name.";
       };
 
       user = lib.mkOption {
         type = lib.types.str;
         default = "peertube";
-        description = lib.mdDoc "Database user.";
+        description = "Database user.";
       };
 
       passwordFile = lib.mkOption {
         type = lib.types.nullOr lib.types.path;
         default = null;
         example = "/run/keys/peertube/password-postgresql";
-        description = lib.mdDoc "Password for PostgreSQL database.";
+        description = "Password for PostgreSQL database.";
       };
     };
 
@@ -218,7 +218,7 @@ in {
       createLocally = lib.mkOption {
         type = lib.types.bool;
         default = false;
-        description = lib.mdDoc "Configure local Redis server for PeerTube.";
+        description = "Configure local Redis server for PeerTube.";
       };
 
       host = lib.mkOption {
@@ -229,7 +229,7 @@ in {
           then "127.0.0.1"
           else null
         '';
-        description = lib.mdDoc "Redis host.";
+        description = "Redis host.";
       };
 
       port = lib.mkOption {
@@ -240,21 +240,21 @@ in {
           then null
           else 6379
         '';
-        description = lib.mdDoc "Redis port.";
+        description = "Redis port.";
       };
 
       passwordFile = lib.mkOption {
         type = lib.types.nullOr lib.types.path;
         default = null;
         example = "/run/keys/peertube/password-redis-db";
-        description = lib.mdDoc "Password for redis database.";
+        description = "Password for redis database.";
       };
 
       enableUnixSocket = lib.mkOption {
         type = lib.types.bool;
         default = cfg.redis.createLocally;
         defaultText = lib.literalExpression "config.${opt.redis.createLocally}";
-        description = lib.mdDoc "Use Unix socket.";
+        description = "Use Unix socket.";
       };
     };
 
@@ -262,14 +262,14 @@ in {
       createLocally = lib.mkOption {
         type = lib.types.bool;
         default = false;
-        description = lib.mdDoc "Configure local Postfix SMTP server for PeerTube.";
+        description = "Configure local Postfix SMTP server for PeerTube.";
       };
 
       passwordFile = lib.mkOption {
         type = lib.types.nullOr lib.types.path;
         default = null;
         example = "/run/keys/peertube/password-smtp";
-        description = lib.mdDoc "Password for smtp server.";
+        description = "Password for smtp server.";
       };
     };
 
@@ -277,7 +277,7 @@ in {
       type = lib.types.package;
       default = pkgs.peertube;
       defaultText = lib.literalExpression "pkgs.peertube";
-      description = lib.mdDoc "PeerTube package to use.";
+      description = "PeerTube package to use.";
     };
   };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/pgpkeyserver-lite.nix b/nixpkgs/nixos/modules/services/web-apps/pgpkeyserver-lite.nix
index 7a5ab579c408..f1e5f022c379 100644
--- a/nixpkgs/nixos/modules/services/web-apps/pgpkeyserver-lite.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/pgpkeyserver-lite.nix
@@ -18,13 +18,13 @@ in
 
     services.pgpkeyserver-lite = {
 
-      enable = mkEnableOption (lib.mdDoc "pgpkeyserver-lite on a nginx vHost proxying to a gpg keyserver");
+      enable = mkEnableOption "pgpkeyserver-lite on a nginx vHost proxying to a gpg keyserver";
 
       package = mkPackageOption pkgs "pgpkeyserver-lite" { };
 
       hostname = mkOption {
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           Which hostname to set the vHost to that is proxying to sks.
         '';
       };
@@ -33,7 +33,7 @@ in
         default = builtins.head sksCfg.hkpAddress;
         defaultText = literalExpression "head config.${sksOpt.hkpAddress}";
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           Which IP address the sks-keyserver is listening on.
         '';
       };
@@ -42,7 +42,7 @@ in
         default = sksCfg.hkpPort;
         defaultText = literalExpression "config.${sksOpt.hkpPort}";
         type = types.int;
-        description = lib.mdDoc ''
+        description = ''
           Which port the sks-keyserver is listening on.
         '';
       };
diff --git a/nixpkgs/nixos/modules/services/web-apps/photoprism.nix b/nixpkgs/nixos/modules/services/web-apps/photoprism.nix
index 39eb7c65c635..ec4126b420cd 100644
--- a/nixpkgs/nixos/modules/services/web-apps/photoprism.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/photoprism.nix
@@ -26,12 +26,12 @@ in
 
   options.services.photoprism = {
 
-    enable = lib.mkEnableOption (lib.mdDoc "Photoprism web server");
+    enable = lib.mkEnableOption "Photoprism web server";
 
     passwordFile = lib.mkOption {
       type = lib.types.nullOr lib.types.path;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         Admin password file.
       '';
     };
@@ -39,7 +39,7 @@ in
     address = lib.mkOption {
       type = lib.types.str;
       default = "localhost";
-      description = lib.mdDoc ''
+      description = ''
         Web interface address.
       '';
     };
@@ -47,7 +47,7 @@ in
     port = lib.mkOption {
       type = lib.types.port;
       default = 2342;
-      description = lib.mdDoc ''
+      description = ''
         Web interface port.
       '';
     };
@@ -56,7 +56,7 @@ in
       type = lib.types.path;
       default = null;
       example = "/data/photos";
-      description = lib.mdDoc ''
+      description = ''
         Storage path of your original media files (photos and videos).
       '';
     };
@@ -64,7 +64,7 @@ in
     importPath = lib.mkOption {
       type = lib.types.str;
       default = "import";
-      description = lib.mdDoc ''
+      description = ''
         Relative or absolute to the `originalsPath` from where the files should be imported.
       '';
     };
@@ -72,7 +72,7 @@ in
     storagePath = lib.mkOption {
       type = lib.types.path;
       default = "/var/lib/photoprism";
-      description = lib.mdDoc ''
+      description = ''
         Location for sidecar, cache, and database files.
       '';
     };
@@ -82,7 +82,7 @@ in
     settings = lib.mkOption {
       type = lib.types.attrsOf lib.types.str;
       default = { };
-      description = lib.mdDoc ''
+      description = ''
         See [the getting-started guide](https://docs.photoprism.app/getting-started/config-options/) for available options.
       '';
       example = {
diff --git a/nixpkgs/nixos/modules/services/web-apps/phylactery.nix b/nixpkgs/nixos/modules/services/web-apps/phylactery.nix
index 488373d0e426..02a3a1765d90 100644
--- a/nixpkgs/nixos/modules/services/web-apps/phylactery.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/phylactery.nix
@@ -4,22 +4,22 @@ with lib;
 let cfg = config.services.phylactery;
 in {
   options.services.phylactery = {
-    enable = mkEnableOption (lib.mdDoc "Phylactery server");
+    enable = mkEnableOption "Phylactery server";
 
     host = mkOption {
       type = types.str;
       default = "localhost";
-      description = lib.mdDoc "Listen host for Phylactery";
+      description = "Listen host for Phylactery";
     };
 
     port = mkOption {
       type = types.port;
-      description = lib.mdDoc "Listen port for Phylactery";
+      description = "Listen port for Phylactery";
     };
 
     library = mkOption {
       type = types.path;
-      description = lib.mdDoc "Path to CBZ library";
+      description = "Path to CBZ library";
     };
 
     package = mkPackageOption pkgs "phylactery" { };
diff --git a/nixpkgs/nixos/modules/services/web-apps/pict-rs.nix b/nixpkgs/nixos/modules/services/web-apps/pict-rs.nix
index 983342c37732..07f84578a59b 100644
--- a/nixpkgs/nixos/modules/services/web-apps/pict-rs.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/pict-rs.nix
@@ -12,14 +12,14 @@ in
   meta.doc = ./pict-rs.md;
 
   options.services.pict-rs = {
-    enable = lib.mkEnableOption (lib.mdDoc "pict-rs server");
+    enable = lib.mkEnableOption "pict-rs server";
 
     package = lib.mkPackageOption pkgs "pict-rs" { };
 
     dataDir = mkOption {
       type = types.path;
       default = "/var/lib/pict-rs";
-      description = lib.mdDoc ''
+      description = ''
         The directory where to store the uploaded images & database.
       '';
     };
@@ -27,7 +27,7 @@ in
     repoPath = mkOption {
       type = types.nullOr (types.path);
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         The directory where to store the database.
         This option takes precedence over dataDir.
       '';
@@ -36,7 +36,7 @@ in
     storePath = mkOption {
       type = types.nullOr (types.path);
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         The directory where to store the uploaded images.
         This option takes precedence over dataDir.
       '';
@@ -45,7 +45,7 @@ in
     address = mkOption {
       type = types.str;
       default = "127.0.0.1";
-      description = lib.mdDoc ''
+      description = ''
         The IPv4 address to deploy the service to.
       '';
     };
@@ -53,7 +53,7 @@ in
     port = mkOption {
       type = types.port;
       default = 8080;
-      description = lib.mdDoc ''
+      description = ''
         The port which to bind the service to.
       '';
     };
diff --git a/nixpkgs/nixos/modules/services/web-apps/pixelfed.nix b/nixpkgs/nixos/modules/services/web-apps/pixelfed.nix
index 2add98264447..cd0e8f62b65c 100644
--- a/nixpkgs/nixos/modules/services/web-apps/pixelfed.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/pixelfed.nix
@@ -38,14 +38,14 @@ let
 in {
   options.services = {
     pixelfed = {
-      enable = mkEnableOption (lib.mdDoc "a Pixelfed instance");
+      enable = mkEnableOption "a Pixelfed instance";
       package = mkPackageOption pkgs "pixelfed" { };
       phpPackage = mkPackageOption pkgs "php81" { };
 
       user = mkOption {
         type = types.str;
         default = "pixelfed";
-        description = lib.mdDoc ''
+        description = ''
           User account under which pixelfed runs.
 
           ::: {.note}
@@ -59,7 +59,7 @@ in {
       group = mkOption {
         type = types.str;
         default = "pixelfed";
-        description = lib.mdDoc ''
+        description = ''
           Group account under which pixelfed runs.
 
           ::: {.note}
@@ -72,14 +72,14 @@ in {
 
       domain = mkOption {
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           FQDN for the Pixelfed instance.
         '';
       };
 
       secretFile = mkOption {
         type = types.path;
-        description = lib.mdDoc ''
+        description = ''
           A secret file to be sourced for the .env settings.
           Place `APP_KEY` and other settings that should not end up in the Nix store here.
         '';
@@ -87,7 +87,7 @@ in {
 
       settings = mkOption {
         type = with types; (attrsOf (oneOf [ bool int str ]));
-        description = lib.mdDoc ''
+        description = ''
           .env settings for Pixelfed.
           Secrets should use `secretFile` option instead.
         '';
@@ -108,7 +108,7 @@ in {
             forceHttps = true;
           }
         '';
-        description = lib.mdDoc ''
+        description = ''
           With this option, you can customize an nginx virtual host which already has sensible defaults for Dolibarr.
           Set to {} if you do not need any customization to the virtual host.
           If enabled, then by default, the {option}`serverName` is
@@ -117,19 +117,16 @@ in {
         '';
       };
 
-      redis.createLocally = mkEnableOption
-        (lib.mdDoc "a local Redis database using UNIX socket authentication")
+      redis.createLocally = mkEnableOption "a local Redis database using UNIX socket authentication"
         // {
           default = true;
         };
 
       database = {
-        createLocally = mkEnableOption
-          (lib.mdDoc "a local database using UNIX socket authentication") // {
+        createLocally = mkEnableOption "a local database using UNIX socket authentication" // {
             default = true;
           };
-        automaticMigrations = mkEnableOption
-          (lib.mdDoc "automatic migrations for database schema and data") // {
+        automaticMigrations = mkEnableOption "automatic migrations for database schema and data" // {
             default = true;
           };
 
@@ -137,7 +134,7 @@ in {
           type = types.enum [ "mysql" "pgsql" ];
           example = "pgsql";
           default = "mysql";
-          description = lib.mdDoc ''
+          description = ''
             Database engine to use.
             Note that PGSQL is not well supported: https://github.com/pixelfed/pixelfed/issues/2727
           '';
@@ -146,14 +143,14 @@ in {
         name = mkOption {
           type = types.str;
           default = "pixelfed";
-          description = lib.mdDoc "Database name.";
+          description = "Database name.";
         };
       };
 
       maxUploadSize = mkOption {
         type = types.str;
         default = "8M";
-        description = lib.mdDoc ''
+        description = ''
           Max upload size with units.
         '';
       };
@@ -162,7 +159,7 @@ in {
         type = with types; attrsOf (oneOf [ int str bool ]);
         default = { };
 
-        description = lib.mdDoc ''
+        description = ''
           Options for Pixelfed's PHP-FPM pool.
         '';
       };
@@ -170,7 +167,7 @@ in {
       dataDir = mkOption {
         type = types.str;
         default = "/var/lib/pixelfed";
-        description = lib.mdDoc ''
+        description = ''
           State directory of the `pixelfed` user which holds
           the application's state and data.
         '';
@@ -179,7 +176,7 @@ in {
       runtimeDir = mkOption {
         type = types.str;
         default = "/run/pixelfed";
-        description = lib.mdDoc ''
+        description = ''
           Ruutime directory of the `pixelfed` user which holds
           the application's caches and temporary files.
         '';
@@ -188,7 +185,7 @@ in {
       schedulerInterval = mkOption {
         type = types.str;
         default = "1d";
-        description = lib.mdDoc "How often the Pixelfed cron task should run";
+        description = "How often the Pixelfed cron task should run";
       };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/plantuml-server.nix b/nixpkgs/nixos/modules/services/web-apps/plantuml-server.nix
index b7bdf997d955..91a5be124d08 100644
--- a/nixpkgs/nixos/modules/services/web-apps/plantuml-server.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/plantuml-server.nix
@@ -3,7 +3,6 @@
 let
   inherit (lib)
     literalExpression
-    mdDoc
     mkEnableOption
     mkIf
     mkOption
@@ -23,7 +22,7 @@ in
 
   options = {
     services.plantuml-server = {
-      enable = mkEnableOption (mdDoc "PlantUML server");
+      enable = mkEnableOption "PlantUML server";
 
       package = mkPackageOption pkgs "plantuml-server" { };
 
@@ -45,37 +44,37 @@ in
       user = mkOption {
         type = types.str;
         default = "plantuml";
-        description = mdDoc "User which runs PlantUML server.";
+        description = "User which runs PlantUML server.";
       };
 
       group = mkOption {
         type = types.str;
         default = "plantuml";
-        description = mdDoc "Group which runs PlantUML server.";
+        description = "Group which runs PlantUML server.";
       };
 
       home = mkOption {
         type = types.path;
         default = "/var/lib/plantuml";
-        description = mdDoc "Home directory of the PlantUML server instance.";
+        description = "Home directory of the PlantUML server instance.";
       };
 
       listenHost = mkOption {
         type = types.str;
         default = "127.0.0.1";
-        description = mdDoc "Host to listen on.";
+        description = "Host to listen on.";
       };
 
       listenPort = mkOption {
         type = types.int;
         default = 8080;
-        description = mdDoc "Port to listen on.";
+        description = "Port to listen on.";
       };
 
       plantumlLimitSize = mkOption {
         type = types.int;
         default = 4096;
-        description = mdDoc "Limits image width and height.";
+        description = "Limits image width and height.";
       };
 
       graphvizPackage = mkPackageOption pkgs "graphviz" { };
@@ -83,13 +82,13 @@ in
       plantumlStats = mkOption {
         type = types.bool;
         default = false;
-        description = mdDoc "Set it to on to enable statistics report (https://plantuml.com/statistics-report).";
+        description = "Set it to on to enable statistics report (https://plantuml.com/statistics-report).";
       };
 
       httpAuthorization = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = mdDoc "When calling the proxy endpoint, the value of HTTP_AUTHORIZATION will be used to set the HTTP Authorization header.";
+        description = "When calling the proxy endpoint, the value of HTTP_AUTHORIZATION will be used to set the HTTP Authorization header.";
       };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/plausible.nix b/nixpkgs/nixos/modules/services/web-apps/plausible.nix
index a6bb81e0b73f..8e49e591f75c 100644
--- a/nixpkgs/nixos/modules/services/web-apps/plausible.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/plausible.nix
@@ -7,7 +7,7 @@ let
 
 in {
   options.services.plausible = {
-    enable = mkEnableOption (lib.mdDoc "plausible");
+    enable = mkEnableOption "plausible";
 
     package = mkPackageOption pkgs "plausible" { };
 
@@ -15,7 +15,7 @@ in {
       name = mkOption {
         default = "admin";
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           Name of the admin user that plausible will created on initial startup.
         '';
       };
@@ -23,45 +23,45 @@ in {
       email = mkOption {
         type = types.str;
         example = "admin@localhost";
-        description = lib.mdDoc ''
+        description = ''
           Email-address of the admin-user.
         '';
       };
 
       passwordFile = mkOption {
         type = types.either types.str types.path;
-        description = lib.mdDoc ''
+        description = ''
           Path to the file which contains the password of the admin user.
         '';
       };
 
-      activate = mkEnableOption (lib.mdDoc "activating the freshly created admin-user");
+      activate = mkEnableOption "activating the freshly created admin-user";
     };
 
     database = {
       clickhouse = {
-        setup = mkEnableOption (lib.mdDoc "creating a clickhouse instance") // { default = true; };
+        setup = mkEnableOption "creating a clickhouse instance" // { default = true; };
         url = mkOption {
           default = "http://localhost:8123/default";
           type = types.str;
-          description = lib.mdDoc ''
+          description = ''
             The URL to be used to connect to `clickhouse`.
           '';
         };
       };
       postgres = {
-        setup = mkEnableOption (lib.mdDoc "creating a postgresql instance") // { default = true; };
+        setup = mkEnableOption "creating a postgresql instance" // { default = true; };
         dbname = mkOption {
           default = "plausible";
           type = types.str;
-          description = lib.mdDoc ''
+          description = ''
             Name of the database to use.
           '';
         };
         socket = mkOption {
           default = "/run/postgresql";
           type = types.str;
-          description = lib.mdDoc ''
+          description = ''
             Path to the UNIX domain-socket to communicate with `postgres`.
           '';
         };
@@ -72,13 +72,13 @@ in {
       disableRegistration = mkOption {
         default = true;
         type = types.enum [true false "invite_only"];
-        description = lib.mdDoc ''
+        description = ''
           Whether to prohibit creating an account in plausible's UI or allow on `invite_only`.
         '';
       };
       secretKeybaseFile = mkOption {
         type = types.either types.path types.str;
-        description = lib.mdDoc ''
+        description = ''
           Path to the secret used by the `phoenix`-framework. Instructions
           how to generate one are documented in the
           [
@@ -88,20 +88,20 @@ in {
       listenAddress = mkOption {
         default = "127.0.0.1";
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           The IP address on which the server is listening.
         '';
       };
       port = mkOption {
         default = 8000;
         type = types.port;
-        description = lib.mdDoc ''
+        description = ''
           Port where the service should be available.
         '';
       };
       baseUrl = mkOption {
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           Public URL where plausible is available.
 
           Note that `/path` components are currently ignored:
@@ -116,7 +116,7 @@ in {
       email = mkOption {
         default = "hello@plausible.local";
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           The email id to use for as *from* address of all communications
           from Plausible.
         '';
@@ -125,36 +125,36 @@ in {
         hostAddr = mkOption {
           default = "localhost";
           type = types.str;
-          description = lib.mdDoc ''
+          description = ''
             The host address of your smtp server.
           '';
         };
         hostPort = mkOption {
           default = 25;
           type = types.port;
-          description = lib.mdDoc ''
+          description = ''
             The port of your smtp server.
           '';
         };
         user = mkOption {
           default = null;
           type = types.nullOr types.str;
-          description = lib.mdDoc ''
+          description = ''
             The username/email in case SMTP auth is enabled.
           '';
         };
         passwordFile = mkOption {
           default = null;
           type = with types; nullOr (either str path);
-          description = lib.mdDoc ''
+          description = ''
             The path to the file with the password in case SMTP auth is enabled.
           '';
         };
-        enableSSL = mkEnableOption (lib.mdDoc "SSL when connecting to the SMTP server");
+        enableSSL = mkEnableOption "SSL when connecting to the SMTP server";
         retries = mkOption {
           type = types.ints.unsigned;
           default = 2;
-          description = lib.mdDoc ''
+          description = ''
             Number of retries to make until mailer gives up.
           '';
         };
diff --git a/nixpkgs/nixos/modules/services/web-apps/powerdns-admin.nix b/nixpkgs/nixos/modules/services/web-apps/powerdns-admin.nix
index 7b6fb06e3565..d64c468a9cb5 100644
--- a/nixpkgs/nixos/modules/services/web-apps/powerdns-admin.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/powerdns-admin.nix
@@ -19,7 +19,7 @@ let
 in
 {
   options.services.powerdns-admin = {
-    enable = mkEnableOption (lib.mdDoc "the PowerDNS web interface");
+    enable = mkEnableOption "the PowerDNS web interface";
 
     extraArgs = mkOption {
       type = types.listOf types.str;
@@ -27,7 +27,7 @@ in
       example = literalExpression ''
         [ "-b" "127.0.0.1:8000" ]
       '';
-      description = lib.mdDoc ''
+      description = ''
         Extra arguments passed to powerdns-admin.
       '';
     };
@@ -40,7 +40,7 @@ in
         PORT = 8000
         SQLALCHEMY_DATABASE_URI = 'postgresql://powerdnsadmin@/powerdnsadmin?host=/run/postgresql'
       '';
-      description = lib.mdDoc ''
+      description = ''
         Configuration python file.
         See [the example configuration](https://github.com/ngoduykhanh/PowerDNS-Admin/blob/v${pkgs.powerdns-admin.version}/configs/development.py)
         for options.
@@ -50,7 +50,7 @@ in
     secretKeyFile = mkOption {
       type = types.nullOr types.path;
       example = "/etc/powerdns-admin/secret";
-      description = lib.mdDoc ''
+      description = ''
         The secret used to create cookies.
         This needs to be set, otherwise the default is used and everyone can forge valid login cookies.
         Set this to null to ignore this setting and configure it through another way.
@@ -60,7 +60,7 @@ in
     saltFile = mkOption {
       type = types.nullOr types.path;
       example = "/etc/powerdns-admin/salt";
-      description = lib.mdDoc ''
+      description = ''
         The salt used for serialization.
         This should be set, otherwise the default is used.
         Set this to null to ignore this setting and configure it through another way.
diff --git a/nixpkgs/nixos/modules/services/web-apps/pretalx.nix b/nixpkgs/nixos/modules/services/web-apps/pretalx.nix
index ff6218112d2f..b062a8b7eeea 100644
--- a/nixpkgs/nixos/modules/services/web-apps/pretalx.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/pretalx.nix
@@ -28,7 +28,7 @@ in
   };
 
   options.services.pretalx = {
-    enable = lib.mkEnableOption (lib.mdDoc "pretalx");
+    enable = lib.mkEnableOption "pretalx";
 
     package = lib.mkPackageOptionMD pkgs "pretalx" {};
 
@@ -56,7 +56,7 @@ in
         "--max-requests-jitter=50"
         "--log-level=info"
       ];
-      description = lib.mdDoc ''
+      description = ''
         Extra arguments to pass to gunicorn.
         See <https://docs.pretalx.org/administrator/installation.html#step-6-starting-pretalx-as-a-service> for details.
       '';
@@ -68,7 +68,7 @@ in
         type = lib.types.bool;
         default = true;
         example = false;
-        description = lib.mdDoc ''
+        description = ''
           Whether to set up celery as an asynchronous task runner.
         '';
       };
@@ -76,7 +76,7 @@ in
       extraArgs = lib.mkOption {
         type = with lib.types; listOf str;
         default = [ ];
-        description = lib.mdDoc ''
+        description = ''
           Extra arguments to pass to celery.
 
           See <https://docs.celeryq.dev/en/stable/reference/cli.html#celery-worker> for more info.
@@ -90,7 +90,7 @@ in
         type = lib.types.bool;
         default = true;
         example = false;
-        description = lib.mdDoc ''
+        description = ''
           Whether to set up an nginx virtual host.
         '';
       };
@@ -98,7 +98,7 @@ in
       domain = lib.mkOption {
         type = lib.types.str;
         example = "talks.example.com";
-        description = lib.mdDoc ''
+        description = ''
           The domain name under which to set up the virtual host.
         '';
       };
@@ -108,7 +108,7 @@ in
       type = lib.types.bool;
       default = true;
       example = false;
-      description = lib.mdDoc ''
+      description = ''
         Whether to automatically set up the database on the local DBMS instance.
 
         Currently only supported for PostgreSQL. Not required for sqlite.
@@ -125,7 +125,7 @@ in
                 "postgresql"
               ];
               default = "postgresql";
-              description = lib.mdDoc ''
+              description = ''
                 Database backend to use.
 
                 Currently only PostgreSQL gets tested, and as such we don't support any other DBMS.
@@ -143,7 +143,7 @@ in
                 else if config.services.pretalx.settings.database.backend == "mysql" then "/run/mysqld/mysqld.sock"
                 else null
               '';
-              description = lib.mdDoc ''
+              description = ''
                 Database host or socket path.
               '';
             };
@@ -151,7 +151,7 @@ in
             name = lib.mkOption {
               type = lib.types.str;
               default = "pretalx";
-              description = lib.mdDoc ''
+              description = ''
                 Database name.
               '';
             };
@@ -159,7 +159,7 @@ in
             user = lib.mkOption {
               type = lib.types.str;
               default = "pretalx";
-              description = lib.mdDoc ''
+              description = ''
                 Database username.
               '';
             };
@@ -169,14 +169,14 @@ in
             data = lib.mkOption {
               type = lib.types.path;
               default = "/var/lib/pretalx";
-              description = lib.mdDoc ''
+              description = ''
                 Base path for all other storage paths.
               '';
             };
             logs = lib.mkOption {
               type = lib.types.path;
               default = "/var/log/pretalx";
-              description = lib.mdDoc ''
+              description = ''
                 Path to the log directory, that pretalx logs message to.
               '';
             };
@@ -185,7 +185,7 @@ in
               default = "${cfg.package.static}/";
               defaultText = lib.literalExpression "\${config.services.pretalx.package}.static}/";
               readOnly = true;
-              description = lib.mdDoc ''
+              description = ''
                 Path to the directory that contains static files.
               '';
             };
@@ -198,7 +198,7 @@ in
               defaultText = lib.literalExpression ''
                 optionalString config.services.pretalx.celery.enable "redis+socket://''${config.services.redis.servers.pretalx.unixSocket}?virtual_host=1"
               '';
-              description = lib.mdDoc ''
+              description = ''
                 URI to the celery backend used for the asynchronous job queue.
               '';
             };
@@ -209,7 +209,7 @@ in
               defaultText = lib.literalExpression ''
                 optionalString config.services.pretalx.celery.enable "redis+socket://''${config.services.redis.servers.pretalx.unixSocket}?virtual_host=2"
               '';
-              description = lib.mdDoc ''
+              description = ''
                 URI to the celery broker used for the asynchronous job queue.
               '';
             };
@@ -222,7 +222,7 @@ in
               defaultText = lib.literalExpression ''
                 "unix://''${config.services.redis.servers.pretalx.unixSocket}?db=0"
               '';
-              description = lib.mdDoc ''
+              description = ''
                 URI to the redis server, used to speed up locking, caching and session storage.
               '';
             };
@@ -231,7 +231,7 @@ in
               type = lib.types.bool;
               default = true;
               example = false;
-              description = lib.mdDoc ''
+              description = ''
                 Whether to use redis as the session storage.
               '';
             };
@@ -243,7 +243,7 @@ in
               default = "https://${cfg.nginx.domain}";
               defaultText = lib.literalExpression "https://\${config.services.pretalx.nginx.domain}";
               example = "https://talks.example.com";
-              description = lib.mdDoc ''
+              description = ''
                 The base URI below which your pretalx instance will be reachable.
               '';
             };
@@ -251,7 +251,7 @@ in
         };
       };
       default = { };
-      description = lib.mdDoc ''
+      description = ''
         pretalx configuration as a Nix attribute set. All settings can also be passed
         from the environment.
 
@@ -286,16 +286,16 @@ in
         virtualHosts.${cfg.nginx.domain} = {
           # https://docs.pretalx.org/administrator/installation.html#step-7-ssl
           extraConfig = ''
-            more_set_headers Referrer-Policy same-origin;
-            more_set_headers X-Content-Type-Options nosniff;
+            more_set_headers "Referrer-Policy: same-origin";
+            more_set_headers "X-Content-Type-Options: nosniff";
           '';
           locations = {
             "/".proxyPass = "http://pretalx";
             "/media/" = {
-              alias = "${cfg.settings.filesystem.data}/data/media/";
+              alias = "${cfg.settings.filesystem.data}/media/";
               extraConfig = ''
                 access_log off;
-                more_set_headers Content-Disposition 'attachment; filename="$1"';
+                more_set_headers 'Content-Disposition: attachment; filename="$1"';
                 expires 7d;
               '';
             };
diff --git a/nixpkgs/nixos/modules/services/web-apps/prosody-filer.nix b/nixpkgs/nixos/modules/services/web-apps/prosody-filer.nix
index 84953546d8e0..91880cab2976 100644
--- a/nixpkgs/nixos/modules/services/web-apps/prosody-filer.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/prosody-filer.nix
@@ -11,10 +11,10 @@ in {
 
   options = {
     services.prosody-filer = {
-      enable = mkEnableOption (lib.mdDoc "Prosody Filer XMPP upload file server");
+      enable = mkEnableOption "Prosody Filer XMPP upload file server";
 
       settings = mkOption {
-        description = lib.mdDoc ''
+        description = ''
           Configuration for Prosody Filer.
           Refer to <https://github.com/ThomasLeister/prosody-filer#configure-prosody-filer> for details on supported values.
         '';
diff --git a/nixpkgs/nixos/modules/services/web-apps/rss-bridge.nix b/nixpkgs/nixos/modules/services/web-apps/rss-bridge.nix
index 1a710f4a6a67..b03c7f7e8069 100644
--- a/nixpkgs/nixos/modules/services/web-apps/rss-bridge.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/rss-bridge.nix
@@ -5,18 +5,31 @@ let
 
   poolName = "rss-bridge";
 
-  whitelist = pkgs.writeText "rss-bridge_whitelist.txt"
-    (concatStringsSep "\n" cfg.whitelist);
+  configAttr = lib.recursiveUpdate { FileCache.path = "${cfg.dataDir}/cache/"; } cfg.config;
+  cfgHalf = lib.mapAttrsRecursive (path: value: let
+    envName = lib.toUpper ("RSSBRIDGE_" + lib.concatStringsSep "_" path);
+    envValue = if lib.isList value then
+      lib.concatStringsSep "," value
+    else if lib.isBool value then
+      lib.boolToString value
+    else
+      toString value;
+  in "fastcgi_param \"${envName}\" \"${envValue}\";") configAttr;
+  cfgEnv = lib.concatStringsSep "\n" (lib.collect lib.isString cfgHalf);
 in
 {
+  imports = [
+    (mkRenamedOptionModule [ "services" "rss-bridge" "whitelist" ] [ "services" "rss-bridge" "config" "system" "enabled_bridges" ])
+  ];
+
   options = {
     services.rss-bridge = {
-      enable = mkEnableOption (lib.mdDoc "rss-bridge");
+      enable = mkEnableOption "rss-bridge";
 
       user = mkOption {
         type = types.str;
         default = "nginx";
-        description = lib.mdDoc ''
+        description = ''
           User account under which both the service and the web-application run.
         '';
       };
@@ -24,7 +37,7 @@ in
       group = mkOption {
         type = types.str;
         default = "nginx";
-        description = lib.mdDoc ''
+        description = ''
           Group under which the web-application run.
         '';
       };
@@ -32,7 +45,7 @@ in
       pool = mkOption {
         type = types.str;
         default = poolName;
-        description = lib.mdDoc ''
+        description = ''
           Name of existing phpfpm pool that is used to run web-application.
           If not specified a pool will be created automatically with
           default values.
@@ -42,7 +55,7 @@ in
       dataDir = mkOption {
         type = types.str;
         default = "/var/lib/rss-bridge";
-        description = lib.mdDoc ''
+        description = ''
           Location in which cache directory will be created.
           You can put `config.ini.php` in here.
         '';
@@ -51,25 +64,31 @@ in
       virtualHost = mkOption {
         type = types.nullOr types.str;
         default = "rss-bridge";
-        description = lib.mdDoc ''
+        description = ''
           Name of the nginx virtualhost to use and setup. If null, do not setup any virtualhost.
         '';
       };
 
-      whitelist = mkOption {
-        type = types.listOf types.str;
-        default = [];
+      config = mkOption {
+        type = with types; attrsOf (attrsOf (oneOf [ bool int str (listOf str) ]));
+        default = {};
+        defaultText = options.literalExpression "FileCache.path = \"\${config.services.rss-bridge.dataDir}/cache/\"";
         example = options.literalExpression ''
-          [
-            "Facebook"
-            "Instagram"
-            "Twitter"
-          ]
+          {
+            system.enabled_bridges = [ "*" ];
+            error = {
+              output = "http";
+              report_limit = 5;
+            };
+            FileCache = {
+              enable_purge = true;
+            };
+          }
         '';
-        description = lib.mdDoc ''
-          List of bridges to be whitelisted.
-          If the list is empty, rss-bridge will use whitelist.default.txt.
-          Use `[ "*" ]` to whitelist all.
+        description = ''
+          Attribute set of arbitrary config options.
+          Please consult the documentation at the [wiki](https://rss-bridge.github.io/rss-bridge/For_Hosts/Custom_Configuration.html)
+          and [sample config](https://github.com/RSS-Bridge/rss-bridge/blob/master/config.default.ini.php) to see a list of available options.
         '';
       };
     };
@@ -93,11 +112,16 @@ in
         };
       };
     };
-    systemd.tmpfiles.rules = [
-      "d '${cfg.dataDir}/cache' 0750 ${cfg.user} ${cfg.group} - -"
-      (mkIf (cfg.whitelist != []) "L+ ${cfg.dataDir}/whitelist.txt - - - - ${whitelist}")
-      "z '${cfg.dataDir}/config.ini.php' 0750 ${cfg.user} ${cfg.group} - -"
-    ];
+    systemd.tmpfiles.settings.rss-bridge = let
+      perm = {
+        mode = "0750";
+        user = cfg.user;
+        group = cfg.group;
+      };
+    in {
+      "${configAttr.FileCache.path}".d = perm;
+      "${cfg.dataDir}/config.ini.php".z = perm;
+    };
 
     services.nginx = mkIf (cfg.virtualHost != null) {
       enable = true;
@@ -116,6 +140,7 @@ in
               fastcgi_pass unix:${config.services.phpfpm.pools.${cfg.pool}.socket};
               fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
               fastcgi_param RSSBRIDGE_DATA ${cfg.dataDir};
+              ${cfgEnv}
             '';
           };
         };
diff --git a/nixpkgs/nixos/modules/services/web-apps/selfoss.nix b/nixpkgs/nixos/modules/services/web-apps/selfoss.nix
index 8debd4904e88..899976ac696c 100644
--- a/nixpkgs/nixos/modules/services/web-apps/selfoss.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/selfoss.nix
@@ -30,12 +30,12 @@ in
   {
     options = {
       services.selfoss = {
-        enable = mkEnableOption (lib.mdDoc "selfoss");
+        enable = mkEnableOption "selfoss";
 
         user = mkOption {
           type = types.str;
           default = "nginx";
-          description = lib.mdDoc ''
+          description = ''
             User account under which both the service and the web-application run.
           '';
         };
@@ -43,7 +43,7 @@ in
         pool = mkOption {
           type = types.str;
           default = "${poolName}";
-          description = lib.mdDoc ''
+          description = ''
             Name of existing phpfpm pool that is used to run web-application.
             If not specified a pool will be created automatically with
             default values.
@@ -54,7 +54,7 @@ in
         type = mkOption {
           type = types.enum ["pgsql" "mysql" "sqlite"];
           default = "sqlite";
-          description = lib.mdDoc ''
+          description = ''
             Database to store feeds. Supported are sqlite, pgsql and mysql.
           '';
         };
@@ -62,7 +62,7 @@ in
         host = mkOption {
           type = types.str;
           default = "localhost";
-          description = lib.mdDoc ''
+          description = ''
             Host of the database (has no effect if type is "sqlite").
           '';
         };
@@ -70,7 +70,7 @@ in
         name = mkOption {
           type = types.str;
           default = "tt_rss";
-          description = lib.mdDoc ''
+          description = ''
             Name of the existing database (has no effect if type is "sqlite").
           '';
         };
@@ -78,7 +78,7 @@ in
         user = mkOption {
           type = types.str;
           default = "tt_rss";
-          description = lib.mdDoc ''
+          description = ''
             The database user. The user must exist and has access to
             the specified database (has no effect if type is "sqlite").
           '';
@@ -87,7 +87,7 @@ in
         password = mkOption {
           type = types.nullOr types.str;
           default = null;
-          description = lib.mdDoc ''
+          description = ''
             The database user's password (has no effect if type is "sqlite").
           '';
         };
@@ -95,7 +95,7 @@ in
         port = mkOption {
           type = types.nullOr types.int;
           default = null;
-          description = lib.mdDoc ''
+          description = ''
             The database's port. If not set, the default ports will be
             provided (5432 and 3306 for pgsql and mysql respectively)
             (has no effect if type is "sqlite").
@@ -105,7 +105,7 @@ in
       extraConfig = mkOption {
         type = types.lines;
         default = "";
-        description = lib.mdDoc ''
+        description = ''
           Extra configuration added to config.ini
         '';
       };
diff --git a/nixpkgs/nixos/modules/services/web-apps/sftpgo.nix b/nixpkgs/nixos/modules/services/web-apps/sftpgo.nix
index 1b5111e5a81c..3ad6d0436afc 100644
--- a/nixpkgs/nixos/modules/services/web-apps/sftpgo.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/sftpgo.nix
@@ -20,7 +20,7 @@ in
     enable = mkOption {
       type = types.bool;
       default = false;
-      description = mdDoc "sftpgo";
+      description = "sftpgo";
     };
 
     package = mkPackageOption pkgs "sftpgo" { };
@@ -28,7 +28,7 @@ in
     extraArgs = mkOption {
       type = with types; listOf str;
       default = [];
-      description = mdDoc ''
+      description = ''
         Additional command line arguments to pass to the sftpgo daemon.
       '';
       example = [ "--log-level" "info" ];
@@ -37,7 +37,7 @@ in
     dataDir = mkOption {
       type = types.str;
       default = "/var/lib/sftpgo";
-      description = mdDoc ''
+      description = ''
         The directory where SFTPGo stores its data files.
       '';
     };
@@ -45,7 +45,7 @@ in
     user = mkOption {
       type = types.str;
       default = defaultUser;
-      description = mdDoc ''
+      description = ''
         User account name under which SFTPGo runs.
       '';
     };
@@ -53,7 +53,7 @@ in
     group = mkOption {
       type = types.str;
       default = defaultUser;
-      description = mdDoc ''
+      description = ''
         Group name under which SFTPGo runs.
       '';
     };
@@ -61,7 +61,7 @@ in
     loadDataFile = mkOption {
       default = null;
       type = with types; nullOr path;
-      description = mdDoc ''
+      description = ''
         Path to a json file containing users and folders to load (or update) on startup.
         Check the [documentation](https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md)
         for the `--loaddata-from` command line argument for more info.
@@ -70,7 +70,7 @@ in
 
     settings = mkOption {
       default = {};
-      description = mdDoc ''
+      description = ''
         The primary sftpgo configuration. See the
         [configuration reference](https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md)
         for possible values.
@@ -80,7 +80,7 @@ in
         options = {
           httpd.bindings = mkOption {
             default = [];
-            description = mdDoc ''
+            description = ''
               Configure listen addresses and ports for httpd.
             '';
             type = types.listOf (types.submodule {
@@ -89,7 +89,7 @@ in
                 address = mkOption {
                   type = types.str;
                   default = "127.0.0.1";
-                  description = mdDoc ''
+                  description = ''
                     Network listen address. Leave blank to listen on all available network interfaces.
                     On *NIX you can specify an absolute path to listen on a Unix-domain socket.
                   '';
@@ -98,7 +98,7 @@ in
                 port = mkOption {
                   type = types.port;
                   default = 8080;
-                  description = mdDoc ''
+                  description = ''
                     The port for serving HTTP(S) requests.
 
                     Setting the port to `0` disables listening on this interface binding.
@@ -108,7 +108,7 @@ in
                 enable_web_admin = mkOption {
                   type = types.bool;
                   default = true;
-                  description = mdDoc ''
+                  description = ''
                     Enable the built-in web admin for this interface binding.
                   '';
                 };
@@ -116,7 +116,7 @@ in
                 enable_web_client = mkOption {
                   type = types.bool;
                   default = true;
-                  description = mdDoc ''
+                  description = ''
                     Enable the built-in web client for this interface binding.
                   '';
                 };
@@ -126,7 +126,7 @@ in
 
           ftpd.bindings = mkOption {
             default = [];
-            description = mdDoc ''
+            description = ''
               Configure listen addresses and ports for ftpd.
             '';
             type = types.listOf (types.submodule {
@@ -135,7 +135,7 @@ in
                 address = mkOption {
                   type = types.str;
                   default = "127.0.0.1";
-                  description = mdDoc ''
+                  description = ''
                     Network listen address. Leave blank to listen on all available network interfaces.
                     On *NIX you can specify an absolute path to listen on a Unix-domain socket.
                   '';
@@ -144,7 +144,7 @@ in
                 port = mkOption {
                   type = types.port;
                   default = 0;
-                  description = mdDoc ''
+                  description = ''
                     The port for serving FTP requests.
 
                     Setting the port to `0` disables listening on this interface binding.
@@ -156,7 +156,7 @@ in
 
           sftpd.bindings = mkOption {
             default = [];
-            description = mdDoc ''
+            description = ''
               Configure listen addresses and ports for sftpd.
             '';
             type = types.listOf (types.submodule {
@@ -165,7 +165,7 @@ in
                 address = mkOption {
                   type = types.str;
                   default = "127.0.0.1";
-                  description = mdDoc ''
+                  description = ''
                     Network listen address. Leave blank to listen on all available network interfaces.
                     On *NIX you can specify an absolute path to listen on a Unix-domain socket.
                   '';
@@ -174,7 +174,7 @@ in
                 port = mkOption {
                   type = types.port;
                   default = 0;
-                  description = mdDoc ''
+                  description = ''
                     The port for serving SFTP requests.
 
                     Setting the port to `0` disables listening on this interface binding.
@@ -186,7 +186,7 @@ in
 
           webdavd.bindings = mkOption {
             default = [];
-            description = mdDoc ''
+            description = ''
               Configure listen addresses and ports for webdavd.
             '';
             type = types.listOf (types.submodule {
@@ -195,7 +195,7 @@ in
                 address = mkOption {
                   type = types.str;
                   default = "127.0.0.1";
-                  description = mdDoc ''
+                  description = ''
                     Network listen address. Leave blank to listen on all available network interfaces.
                     On *NIX you can specify an absolute path to listen on a Unix-domain socket.
                   '';
@@ -204,7 +204,7 @@ in
                 port = mkOption {
                   type = types.port;
                   default = 0;
-                  description = mdDoc ''
+                  description = ''
                     The port for serving WebDAV requests.
 
                     Setting the port to `0` disables listening on this interface binding.
@@ -216,7 +216,7 @@ in
 
           smtp = mkOption {
             default = {};
-            description = mdDoc ''
+            description = ''
               SMTP configuration section.
             '';
             type = types.submodule {
@@ -225,7 +225,7 @@ in
                 host = mkOption {
                   type = types.str;
                   default = "";
-                  description = mdDoc ''
+                  description = ''
                     Location of SMTP email server. Leave empty to disable email sending capabilities.
                   '';
                 };
@@ -233,13 +233,13 @@ in
                 port = mkOption {
                   type = types.port;
                   default = 465;
-                  description = mdDoc "Port of the SMTP Server.";
+                  description = "Port of the SMTP Server.";
                 };
 
                 encryption = mkOption {
                   type = types.enum [ 0 1 2 ];
                   default = 1;
-                  description = mdDoc ''
+                  description = ''
                     Encryption scheme:
                     - `0`: No encryption
                     - `1`: TLS
@@ -250,7 +250,7 @@ in
                 auth_type = mkOption {
                   type = types.enum [ 0 1 2 ];
                   default = 0;
-                  description = mdDoc ''
+                  description = ''
                     - `0`: Plain
                     - `1`: Login
                     - `2`: CRAM-MD5
@@ -260,13 +260,13 @@ in
                 user = mkOption {
                   type = types.str;
                   default = "sftpgo";
-                  description = mdDoc "SMTP username.";
+                  description = "SMTP username.";
                 };
 
                 from = mkOption {
                   type = types.str;
                   default = "SFTPGo <sftpgo@example.com>";
-                  description = mdDoc ''
+                  description = ''
                     From address.
                   '';
                 };
diff --git a/nixpkgs/nixos/modules/services/web-apps/shiori.nix b/nixpkgs/nixos/modules/services/web-apps/shiori.nix
index f9026e04d155..022bb5e43881 100644
--- a/nixpkgs/nixos/modules/services/web-apps/shiori.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/shiori.nix
@@ -6,14 +6,14 @@ let
 in {
   options = {
     services.shiori = {
-      enable = mkEnableOption (lib.mdDoc "Shiori simple bookmarks manager");
+      enable = mkEnableOption "Shiori simple bookmarks manager";
 
       package = mkPackageOption pkgs "shiori" { };
 
       address = mkOption {
         type = types.str;
         default = "";
-        description = lib.mdDoc ''
+        description = ''
           The IP address on which Shiori will listen.
           If empty, listens on all interfaces.
         '';
@@ -22,14 +22,14 @@ in {
       port = mkOption {
         type = types.port;
         default = 8080;
-        description = lib.mdDoc "The port of the Shiori web application";
+        description = "The port of the Shiori web application";
       };
 
       webRoot = mkOption {
         type = types.str;
         default = "/";
         example = "/shiori";
-        description = lib.mdDoc "The root of the Shiori web application";
+        description = "The root of the Shiori web application";
       };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/silverbullet.nix b/nixpkgs/nixos/modules/services/web-apps/silverbullet.nix
new file mode 100644
index 000000000000..c316d074cbaa
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/silverbullet.nix
@@ -0,0 +1,123 @@
+{ config
+, pkgs
+, lib
+, ...
+}:
+let
+  cfg = config.services.silverbullet;
+  defaultUser = "silverbullet";
+  defaultGroup = defaultUser;
+  defaultSpaceDir = "/var/lib/silverbullet";
+in
+{
+  options = {
+    services.silverbullet = {
+      enable = lib.mkEnableOption "Silverbullet, an open-source, self-hosted, offline-capable Personal Knowledge Management (PKM) web application.";
+
+      package = lib.mkPackageOptionMD pkgs "silverbullet" { };
+
+      openFirewall = lib.mkOption {
+        type = lib.types.bool;
+        default = false;
+        description = "Open port in the firewall.";
+      };
+
+      listenPort = lib.mkOption {
+        type = lib.types.int;
+        default = 3000;
+        description = "Port to listen on.";
+      };
+
+      listenAddress = lib.mkOption {
+        type = lib.types.str;
+        default = "127.0.0.1";
+        description = "Address or hostname to listen on. Defaults to 127.0.0.1.";
+      };
+
+      spaceDir = lib.mkOption {
+        type = lib.types.path;
+        default = defaultSpaceDir;
+        example = "/home/yourUser/silverbullet";
+        description = ''
+          Folder to store Silverbullet's space/workspace.
+          By default it is located at `${defaultSpaceDir}`.
+        '';
+      };
+
+      user = lib.mkOption {
+        type = lib.types.str;
+        default = defaultUser;
+        example = "yourUser";
+        description = ''
+          The user to run Silverbullet as.
+          By default, a user named `${defaultUser}` will be created whose space
+          directory is [spaceDir](#opt-services.silverbullet.spaceDir).
+        '';
+      };
+
+      group = lib.mkOption {
+        type = lib.types.str;
+        default = defaultGroup;
+        example = "yourGroup";
+        description = ''
+          The group to run Silverbullet under.
+          By default, a group named `${defaultGroup}` will be created.
+        '';
+      };
+
+      envFile = lib.mkOption {
+        type = lib.types.nullOr lib.types.path;
+        default = null;
+        example = "/etc/silverbullet.env";
+        description = ''
+          File containing extra environment variables. For example:
+
+          ```
+          SB_USER=user:password
+          SB_AUTH_TOKEN=abcdefg12345
+          ```
+        '';
+      };
+
+      extraArgs = lib.mkOption {
+        type = lib.types.listOf lib.types.str;
+        default = [ ];
+        example = [ "--db /path/to/silverbullet.db" ];
+        description = "Extra arguments passed to silverbullet.";
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.silverbullet = {
+      description = "Silverbullet service";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = lib.mkIf (!lib.hasPrefix "/var/lib/" cfg.spaceDir) "mkdir -p '${cfg.spaceDir}'";
+      serviceConfig = {
+        Type = "simple";
+        User = "${cfg.user}";
+        Group = "${cfg.group}";
+        EnvironmentFile = lib.mkIf (cfg.envFile != null) "${cfg.envFile}";
+        StateDirectory = lib.mkIf (lib.hasPrefix "/var/lib/" cfg.spaceDir) (lib.last (lib.splitString "/" cfg.spaceDir));
+        ExecStart = "${lib.getExe cfg.package} --port ${toString cfg.listenPort} --hostname '${cfg.listenAddress}' '${cfg.spaceDir}' " + lib.concatStringsSep " " cfg.extraArgs;
+        Restart = "on-failure";
+      };
+    };
+
+    networking.firewall = lib.mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.listenPort ];
+    };
+
+    users.users.${defaultUser} = lib.mkIf (cfg.user == defaultUser) {
+      isSystemUser = true;
+      group = cfg.group;
+      description = "Silverbullet daemon user";
+    };
+
+    users.groups.${defaultGroup} = lib.mkIf (cfg.group == defaultGroup) { };
+  };
+
+  meta.maintainers = with lib.maintainers; [ aorith ];
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix b/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
index 4fbf2bad750b..272dd23d7271 100644
--- a/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
@@ -34,22 +34,22 @@ let
 in {
   options.services.snipe-it = {
 
-    enable = mkEnableOption (lib.mdDoc "snipe-it, a free open source IT asset/license management system");
+    enable = mkEnableOption "snipe-it, a free open source IT asset/license management system";
 
     user = mkOption {
       default = "snipeit";
-      description = lib.mdDoc "User snipe-it runs as.";
+      description = "User snipe-it runs as.";
       type = types.str;
     };
 
     group = mkOption {
       default = "snipeit";
-      description = lib.mdDoc "Group snipe-it runs as.";
+      description = "Group snipe-it runs as.";
       type = types.str;
     };
 
     appKeyFile = mkOption {
-      description = lib.mdDoc ''
+      description = ''
         A file containing the Laravel APP_KEY - a 32 character long,
         base64 encoded key used for encryption where needed. Can be
         generated with `head -c 32 /dev/urandom | base64`.
@@ -63,13 +63,13 @@ in {
       default = config.networking.fqdnOrHostName;
       defaultText = lib.literalExpression "config.networking.fqdnOrHostName";
       example = "snipe-it.example.com";
-      description = lib.mdDoc ''
+      description = ''
         The hostname to serve Snipe-IT on.
       '';
     };
 
     appURL = mkOption {
-      description = lib.mdDoc ''
+      description = ''
         The root URL that you want to host Snipe-IT on. All URLs in Snipe-IT will be generated using this value.
         If you change this in the future you may need to run a command to update stored URLs in the database.
         Command example: `snipe-it snipe-it:update-url https://old.example.com https://new.example.com`
@@ -83,7 +83,7 @@ in {
     };
 
     dataDir = mkOption {
-      description = lib.mdDoc "snipe-it data directory";
+      description = "snipe-it data directory";
       default = "/var/lib/snipe-it";
       type = types.path;
     };
@@ -92,29 +92,29 @@ in {
       host = mkOption {
         type = types.str;
         default = "localhost";
-        description = lib.mdDoc "Database host address.";
+        description = "Database host address.";
       };
       port = mkOption {
         type = types.port;
         default = 3306;
-        description = lib.mdDoc "Database host port.";
+        description = "Database host port.";
       };
       name = mkOption {
         type = types.str;
         default = "snipeit";
-        description = lib.mdDoc "Database name.";
+        description = "Database name.";
       };
       user = mkOption {
         type = types.str;
         default = user;
         defaultText = literalExpression "user";
-        description = lib.mdDoc "Database username.";
+        description = "Database username.";
       };
       passwordFile = mkOption {
         type = with types; nullOr path;
         default = null;
         example = "/run/keys/snipe-it/dbpassword";
-        description = lib.mdDoc ''
+        description = ''
           A file containing the password corresponding to
           {option}`database.user`.
         '';
@@ -122,7 +122,7 @@ in {
       createLocally = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc "Create the database and database user locally.";
+        description = "Create the database and database user locally.";
       };
     };
 
@@ -130,34 +130,34 @@ in {
       driver = mkOption {
         type = types.enum [ "smtp" "sendmail" ];
         default = "smtp";
-        description = lib.mdDoc "Mail driver to use.";
+        description = "Mail driver to use.";
       };
       host = mkOption {
         type = types.str;
         default = "localhost";
-        description = lib.mdDoc "Mail host address.";
+        description = "Mail host address.";
       };
       port = mkOption {
         type = types.port;
         default = 1025;
-        description = lib.mdDoc "Mail host port.";
+        description = "Mail host port.";
       };
       encryption = mkOption {
         type = with types; nullOr (enum [ "tls" "ssl" ]);
         default = null;
-        description = lib.mdDoc "SMTP encryption mechanism to use.";
+        description = "SMTP encryption mechanism to use.";
       };
       user = mkOption {
         type = with types; nullOr str;
         default = null;
         example = "snipeit";
-        description = lib.mdDoc "Mail username.";
+        description = "Mail username.";
       };
       passwordFile = mkOption {
         type = with types; nullOr path;
         default = null;
         example = "/run/keys/snipe-it/mailpassword";
-        description = lib.mdDoc ''
+        description = ''
           A file containing the password corresponding to
           {option}`mail.user`.
         '';
@@ -165,30 +165,30 @@ in {
       backupNotificationAddress = mkOption {
         type = types.str;
         default = "backup@example.com";
-        description = lib.mdDoc "Email Address to send Backup Notifications to.";
+        description = "Email Address to send Backup Notifications to.";
       };
       from = {
         name = mkOption {
           type = types.str;
           default = "Snipe-IT Asset Management";
-          description = lib.mdDoc "Mail \"from\" name.";
+          description = "Mail \"from\" name.";
         };
         address = mkOption {
           type = types.str;
           default = "mail@example.com";
-          description = lib.mdDoc "Mail \"from\" address.";
+          description = "Mail \"from\" address.";
         };
       };
       replyTo = {
         name = mkOption {
           type = types.str;
           default = "Snipe-IT Asset Management";
-          description = lib.mdDoc "Mail \"reply-to\" name.";
+          description = "Mail \"reply-to\" name.";
         };
         address = mkOption {
           type = types.str;
           default = "mail@example.com";
-          description = lib.mdDoc "Mail \"reply-to\" address.";
+          description = "Mail \"reply-to\" address.";
         };
       };
     };
@@ -197,7 +197,7 @@ in {
       type = types.str;
       default = "18M";
       example = "1G";
-      description = lib.mdDoc "The maximum size for uploads (e.g. images).";
+      description = "The maximum size for uploads (e.g. images).";
     };
 
     poolConfig = mkOption {
@@ -210,7 +210,7 @@ in {
         "pm.max_spare_servers" = 4;
         "pm.max_requests" = 500;
       };
-      description = lib.mdDoc ''
+      description = ''
         Options for the snipe-it PHP pool. See the documentation on `php-fpm.conf`
         for details on configuration directives.
       '';
@@ -232,7 +232,7 @@ in {
           enableACME = true;
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         With this option, you can customize the nginx virtualHost settings.
       '';
     };
@@ -253,7 +253,7 @@ in {
                 options = {
                   _secret = mkOption {
                     type = nullOr (oneOf [ str path ]);
-                    description = lib.mdDoc ''
+                    description = ''
                       The path to a file containing the value the
                       option should be set to in the final
                       configuration file.
@@ -275,7 +275,7 @@ in {
           OIDC_ISSUER_DISCOVER = true;
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         Snipe-IT configuration options to set in the
         {file}`.env` file.
         Refer to <https://snipe-it.readme.io/docs/configuration>
diff --git a/nixpkgs/nixos/modules/services/web-apps/sogo.nix b/nixpkgs/nixos/modules/services/web-apps/sogo.nix
index 9427eff35d14..78b577f18f28 100644
--- a/nixpkgs/nixos/modules/services/web-apps/sogo.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/sogo.nix
@@ -18,34 +18,34 @@
 
 in {
   options.services.sogo = with types; {
-    enable = mkEnableOption (lib.mdDoc "SOGo groupware");
+    enable = mkEnableOption "SOGo groupware";
 
     vhostName = mkOption {
-      description = lib.mdDoc "Name of the nginx vhost";
+      description = "Name of the nginx vhost";
       type = str;
       default = "sogo";
     };
 
     timezone = mkOption {
-      description = lib.mdDoc "Timezone of your SOGo instance";
+      description = "Timezone of your SOGo instance";
       type = str;
       example = "America/Montreal";
     };
 
     language = mkOption {
-      description = lib.mdDoc "Language of SOGo";
+      description = "Language of SOGo";
       type = str;
       default = "English";
     };
 
     ealarmsCredFile = mkOption {
-      description = lib.mdDoc "Optional path to a credentials file for email alarms";
+      description = "Optional path to a credentials file for email alarms";
       type = nullOr str;
       default = null;
     };
 
     configReplaces = mkOption {
-      description = lib.mdDoc ''
+      description = ''
         Replacement-filepath mapping for sogo.conf.
         Every key is replaced with the contents of the file specified as value.
 
@@ -60,7 +60,7 @@ in {
     };
 
     extraConfig = mkOption {
-      description = lib.mdDoc "Extra sogo.conf configuration lines";
+      description = "Extra sogo.conf configuration lines";
       type = lines;
       default = "";
     };
diff --git a/nixpkgs/nixos/modules/services/web-apps/suwayomi-server.nix b/nixpkgs/nixos/modules/services/web-apps/suwayomi-server.nix
index 99c6ea2a36e6..5b61852a534d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/suwayomi-server.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/suwayomi-server.nix
@@ -2,14 +2,14 @@
 
 let
   cfg = config.services.suwayomi-server;
-  inherit (lib) mkOption mdDoc mkEnableOption mkIf types;
+  inherit (lib) mkOption mkEnableOption mkIf types;
 
   format = pkgs.formats.hocon { };
 in
 {
   options = {
     services.suwayomi-server = {
-      enable = mkEnableOption (mdDoc "Suwayomi, a free and open source manga reader server that runs extensions built for Tachiyomi.");
+      enable = mkEnableOption "Suwayomi, a free and open source manga reader server that runs extensions built for Tachiyomi.";
 
       package = lib.mkPackageOptionMD pkgs "suwayomi-server" { };
 
@@ -17,7 +17,7 @@ in
         type = types.path;
         default = "/var/lib/suwayomi-server";
         example = "/var/data/mangas";
-        description = mdDoc ''
+        description = ''
           The path to the data directory in which Suwayomi-Server will download scans.
         '';
       };
@@ -26,7 +26,7 @@ in
         type = types.str;
         default = "suwayomi";
         example = "root";
-        description = mdDoc ''
+        description = ''
           User account under which Suwayomi-Server runs.
         '';
       };
@@ -35,7 +35,7 @@ in
         type = types.str;
         default = "suwayomi";
         example = "medias";
-        description = mdDoc ''
+        description = ''
           Group under which Suwayomi-Server runs.
         '';
       };
@@ -43,7 +43,7 @@ in
       openFirewall = mkOption {
         type = types.bool;
         default = false;
-        description = mdDoc ''
+        description = ''
           Whether to open the firewall for the port in {option}`services.suwayomi-server.settings.server.port`.
         '';
       };
@@ -57,7 +57,7 @@ in
                 type = types.str;
                 default = "0.0.0.0";
                 example = "127.0.0.1";
-                description = mdDoc ''
+                description = ''
                   The ip that Suwayomi will bind to.
                 '';
               };
@@ -66,20 +66,20 @@ in
                 type = types.port;
                 default = 8080;
                 example = 4567;
-                description = mdDoc ''
+                description = ''
                   The port that Suwayomi will listen to.
                 '';
               };
 
-              basicAuthEnabled = mkEnableOption (mdDoc ''
+              basicAuthEnabled = mkEnableOption ''
                 Add basic access authentication to Suwayomi-Server.
                 Enabling this option is useful when hosting on a public network/the Internet
-              '');
+              '';
 
               basicAuthUsername = mkOption {
                 type = types.nullOr types.str;
                 default = null;
-                description = mdDoc ''
+                description = ''
                   The username value that you have to provide when authenticating.
                 '';
               };
@@ -89,7 +89,7 @@ in
                 type = types.nullOr types.path;
                 default = null;
                 example = "/var/secrets/suwayomi-server-password";
-                description = mdDoc ''
+                description = ''
                   The password file containing the value that you have to provide when authenticating.
                 '';
               };
@@ -97,7 +97,7 @@ in
               downloadAsCbz = mkOption {
                 type = types.bool;
                 default = false;
-                description = mdDoc ''
+                description = ''
                   Download chapters as `.cbz` files.
                 '';
               };
@@ -108,7 +108,7 @@ in
                 example = [
                   "https://raw.githubusercontent.com/MY_ACCOUNT/MY_REPO/repo/index.min.json"
                 ];
-                description = mdDoc ''
+                description = ''
                   URL of repositories from which the extensions can be installed.
                 '';
               };
@@ -118,7 +118,7 @@ in
                 default = cfg.dataDir;
                 defaultText = lib.literalExpression "suwayomi-server.dataDir";
                 example = "/var/data/local_mangas";
-                description = mdDoc ''
+                description = ''
                   Path to the local source folder.
                 '';
               };
@@ -126,14 +126,14 @@ in
               systemTrayEnabled = mkOption {
                 type = types.bool;
                 default = false;
-                description = mdDoc ''
+                description = ''
                   Whether to enable a system tray icon, if possible.
                 '';
               };
             };
           };
         };
-        description = mdDoc ''
+        description = ''
           Configuration to write to {file}`server.conf`.
           See <https://github.com/Suwayomi/Suwayomi-Server/wiki/Configuring-Suwayomi-Server> for more information.
         '';
diff --git a/nixpkgs/nixos/modules/services/web-apps/trilium.nix b/nixpkgs/nixos/modules/services/web-apps/trilium.nix
index a91d64f620b6..42b0a16827c3 100644
--- a/nixpkgs/nixos/modules/services/web-apps/trilium.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/trilium.nix
@@ -24,12 +24,12 @@ in
 {
 
   options.services.trilium-server = with lib; {
-    enable = mkEnableOption (lib.mdDoc "trilium-server");
+    enable = mkEnableOption "trilium-server";
 
     dataDir = mkOption {
       type = types.str;
       default = "/var/lib/trilium";
-      description = lib.mdDoc ''
+      description = ''
         The directory storing the notes database and the configuration.
       '';
     };
@@ -37,7 +37,7 @@ in
     instanceName = mkOption {
       type = types.str;
       default = "Trilium";
-      description = lib.mdDoc ''
+      description = ''
         Instance name used to distinguish between different instances
       '';
     };
@@ -45,7 +45,7 @@ in
     noBackup = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Disable periodic database backups.
       '';
     };
@@ -53,7 +53,7 @@ in
     noAuthentication = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         If set to true, no password is required to access the web frontend.
       '';
     };
@@ -61,7 +61,7 @@ in
     host = mkOption {
       type = types.str;
       default = "127.0.0.1";
-      description = lib.mdDoc ''
+      description = ''
         The host address to bind to (defaults to localhost).
       '';
     };
@@ -69,14 +69,14 @@ in
     port = mkOption {
       type = types.port;
       default = 8080;
-      description = lib.mdDoc ''
+      description = ''
         The port number to bind to.
       '';
     };
 
     nginx = mkOption {
       default = {};
-      description = lib.mdDoc ''
+      description = ''
         Configuration for nginx reverse proxy.
       '';
 
@@ -85,14 +85,14 @@ in
           enable = mkOption {
             type = types.bool;
             default = false;
-            description = lib.mdDoc ''
+            description = ''
               Configure the nginx reverse proxy settings.
             '';
           };
 
           hostName = mkOption {
             type = types.str;
-            description = lib.mdDoc ''
+            description = ''
               The hostname use to setup the virtualhost configuration
             '';
           };
diff --git a/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix b/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix
index 84342165c9c0..9826febb3c66 100644
--- a/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix
@@ -123,12 +123,12 @@ let
 
     services.tt-rss = {
 
-      enable = mkEnableOption (lib.mdDoc "tt-rss");
+      enable = mkEnableOption "tt-rss";
 
       root = mkOption {
         type = types.path;
         default = "/var/lib/tt-rss";
-        description = lib.mdDoc ''
+        description = ''
           Root of the application.
         '';
       };
@@ -136,7 +136,7 @@ let
       user = mkOption {
         type = types.str;
         default = "tt_rss";
-        description = lib.mdDoc ''
+        description = ''
           User account under which both the update daemon and the web-application run.
         '';
       };
@@ -144,7 +144,7 @@ let
       pool = mkOption {
         type = types.str;
         default = "${poolName}";
-        description = lib.mdDoc ''
+        description = ''
           Name of existing phpfpm pool that is used to run web-application.
           If not specified a pool will be created automatically with
           default values.
@@ -154,7 +154,7 @@ let
       virtualHost = mkOption {
         type = types.nullOr types.str;
         default = "tt-rss";
-        description = lib.mdDoc ''
+        description = ''
           Name of the nginx virtualhost to use and setup. If null, do not setup any virtualhost.
         '';
       };
@@ -163,7 +163,7 @@ let
         type = mkOption {
           type = types.enum ["pgsql" "mysql"];
           default = "pgsql";
-          description = lib.mdDoc ''
+          description = ''
             Database to store feeds. Supported are pgsql and mysql.
           '';
         };
@@ -171,7 +171,7 @@ let
         host = mkOption {
           type = types.nullOr types.str;
           default = null;
-          description = lib.mdDoc ''
+          description = ''
             Host of the database. Leave null to use Unix domain socket.
           '';
         };
@@ -179,7 +179,7 @@ let
         name = mkOption {
           type = types.str;
           default = "tt_rss";
-          description = lib.mdDoc ''
+          description = ''
             Name of the existing database.
           '';
         };
@@ -187,7 +187,7 @@ let
         user = mkOption {
           type = types.str;
           default = "tt_rss";
-          description = lib.mdDoc ''
+          description = ''
             The database user. The user must exist and has access to
             the specified database.
           '';
@@ -196,7 +196,7 @@ let
         password = mkOption {
           type = types.nullOr types.str;
           default = null;
-          description = lib.mdDoc ''
+          description = ''
             The database user's password.
           '';
         };
@@ -204,7 +204,7 @@ let
         passwordFile = mkOption {
           type = types.nullOr types.str;
           default = null;
-          description = lib.mdDoc ''
+          description = ''
             The database user's password.
           '';
         };
@@ -212,7 +212,7 @@ let
         port = mkOption {
           type = types.nullOr types.port;
           default = null;
-          description = lib.mdDoc ''
+          description = ''
             The database's port. If not set, the default ports will be provided (5432
             and 3306 for pgsql and mysql respectively).
           '';
@@ -221,7 +221,7 @@ let
         createLocally = mkOption {
           type = types.bool;
           default = true;
-          description = lib.mdDoc "Create the database and database user locally.";
+          description = "Create the database and database user locally.";
         };
       };
 
@@ -229,7 +229,7 @@ let
         autoCreate = mkOption {
           type = types.bool;
           default = true;
-          description = lib.mdDoc ''
+          description = ''
             Allow authentication modules to auto-create users in tt-rss internal
             database when authenticated successfully.
           '';
@@ -238,7 +238,7 @@ let
         autoLogin = mkOption {
           type = types.bool;
           default = true;
-          description = lib.mdDoc ''
+          description = ''
             Automatically login user on remote or other kind of externally supplied
             authentication, otherwise redirect to login form as normal.
             If set to true, users won't be able to set application language
@@ -251,7 +251,7 @@ let
         hub = mkOption {
           type = types.str;
           default = "";
-          description = lib.mdDoc ''
+          description = ''
             URL to a PubSubHubbub-compatible hub server. If defined, "Published
             articles" generated feed would automatically become PUSH-enabled.
           '';
@@ -260,7 +260,7 @@ let
         enable = mkOption {
           type = types.bool;
           default = false;
-          description = lib.mdDoc ''
+          description = ''
             Enable client PubSubHubbub support in tt-rss. When disabled, tt-rss
             won't try to subscribe to PUSH feed updates.
           '';
@@ -271,7 +271,7 @@ let
         server = mkOption {
           type = types.str;
           default = "localhost:9312";
-          description = lib.mdDoc ''
+          description = ''
             Hostname:port combination for the Sphinx server.
           '';
         };
@@ -279,7 +279,7 @@ let
         index = mkOption {
           type = types.listOf types.str;
           default = ["ttrss" "delta"];
-          description = lib.mdDoc ''
+          description = ''
             Index names in Sphinx configuration. Example configuration
             files are available on tt-rss wiki.
           '';
@@ -290,7 +290,7 @@ let
         enable = mkOption {
           type = types.bool;
           default = false;
-          description = lib.mdDoc ''
+          description = ''
             Allow users to register themselves. Please be aware that allowing
             random people to access your tt-rss installation is a security risk
             and potentially might lead to data loss or server exploit. Disabled
@@ -301,7 +301,7 @@ let
         notifyAddress = mkOption {
           type = types.str;
           default = "";
-          description = lib.mdDoc ''
+          description = ''
             Email address to send new user notifications to.
           '';
         };
@@ -309,7 +309,7 @@ let
         maxUsers = mkOption {
           type = types.int;
           default = 0;
-          description = lib.mdDoc ''
+          description = ''
             Maximum amount of users which will be allowed to register on this
             system. 0 - no limit.
           '';
@@ -321,7 +321,7 @@ let
           type = types.str;
           default = "";
           example = "localhost:25";
-          description = lib.mdDoc ''
+          description = ''
             Hostname:port combination to send outgoing mail. Blank - use system
             MTA.
           '';
@@ -330,7 +330,7 @@ let
         login = mkOption {
           type = types.str;
           default = "";
-          description = lib.mdDoc ''
+          description = ''
             SMTP authentication login used when sending outgoing mail.
           '';
         };
@@ -338,7 +338,7 @@ let
         password = mkOption {
           type = types.str;
           default = "";
-          description = lib.mdDoc ''
+          description = ''
             SMTP authentication password used when sending outgoing mail.
           '';
         };
@@ -346,7 +346,7 @@ let
         security = mkOption {
           type = types.enum ["" "ssl" "tls"];
           default = "";
-          description = lib.mdDoc ''
+          description = ''
             Used to select a secure SMTP connection. Allowed values: ssl, tls,
             or empty.
           '';
@@ -355,7 +355,7 @@ let
         fromName = mkOption {
           type = types.str;
           default = "Tiny Tiny RSS";
-          description = lib.mdDoc ''
+          description = ''
             Name for sending outgoing mail. This applies to password reset
             notifications, digest emails and any other mail.
           '';
@@ -364,7 +364,7 @@ let
         fromAddress = mkOption {
           type = types.str;
           default = "";
-          description = lib.mdDoc ''
+          description = ''
             Address for sending outgoing mail. This applies to password reset
             notifications, digest emails and any other mail.
           '';
@@ -373,7 +373,7 @@ let
         digestSubject = mkOption {
           type = types.str;
           default = "[tt-rss] New headlines for last 24 hours";
-          description = lib.mdDoc ''
+          description = ''
             Subject line for email digests.
           '';
         };
@@ -382,7 +382,7 @@ let
       sessionCookieLifetime = mkOption {
         type = types.int;
         default = 86400;
-        description = lib.mdDoc ''
+        description = ''
           Default lifetime of a session (e.g. login) cookie. In seconds,
           0 means cookie will be deleted when browser closes.
         '';
@@ -390,7 +390,7 @@ let
 
       selfUrlPath = mkOption {
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           Full URL of your tt-rss installation. This should be set to the
           location of tt-rss directory, e.g. http://example.org/tt-rss/
           You need to set this option correctly otherwise several features
@@ -402,7 +402,7 @@ let
       feedCryptKey = mkOption {
         type = types.str;
         default = "";
-        description = lib.mdDoc ''
+        description = ''
           Key used for encryption of passwords for password-protected feeds
           in the database. A string of 24 random characters. If left blank, encryption
           is not used. Requires mcrypt functions.
@@ -415,7 +415,7 @@ let
         type = types.bool;
         default = false;
 
-        description = lib.mdDoc ''
+        description = ''
           Operate in single user mode, disables all functionality related to
           multiple users and authentication. Enabling this assumes you have
           your tt-rss directory protected by other means (e.g. http auth).
@@ -425,7 +425,7 @@ let
       simpleUpdateMode = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           Enables fallback update mode where tt-rss tries to update feeds in
           background while tt-rss is open in your browser.
           If you don't have a lot of feeds and don't want to or can't run
@@ -439,7 +439,7 @@ let
       forceArticlePurge = mkOption {
         type = types.int;
         default = 0;
-        description = lib.mdDoc ''
+        description = ''
           When this option is not 0, users ability to control feed purging
           intervals is disabled and all articles (which are not starred)
           older than this amount of days are purged.
@@ -449,7 +449,7 @@ let
       enableGZipOutput = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Selectively gzip output to improve wire performance. This requires
           PHP Zlib extension on the server.
           Enabling this can break tt-rss in several httpd/php configurations,
@@ -462,7 +462,7 @@ let
         type = lib.types.package;
         default = pkgs.php;
         defaultText = "pkgs.php";
-        description = lib.mdDoc ''
+        description = ''
           php package to use for php fpm and update daemon.
         '';
       };
@@ -470,7 +470,7 @@ let
       plugins = mkOption {
         type = types.listOf types.str;
         default = ["auth_internal" "note"];
-        description = lib.mdDoc ''
+        description = ''
           List of plugins to load automatically for all users.
           System plugins have to be specified here. Please enable at least one
           authentication plugin here (auth_*).
@@ -484,7 +484,7 @@ let
       pluginPackages = mkOption {
         type = types.listOf types.package;
         default = [];
-        description = lib.mdDoc ''
+        description = ''
           List of plugins to install. The list elements are expected to
           be derivations. All elements in this derivation are automatically
           copied to the `plugins.local` directory.
@@ -494,7 +494,7 @@ let
       themePackages = mkOption {
         type = types.listOf types.package;
         default = [];
-        description = lib.mdDoc ''
+        description = ''
           List of themes to install. The list elements are expected to
           be derivations. All elements in this derivation are automatically
           copied to the `themes.local` directory.
@@ -504,7 +504,7 @@ let
       logDestination = mkOption {
         type = types.enum ["" "sql" "syslog"];
         default = "sql";
-        description = lib.mdDoc ''
+        description = ''
           Log destination to use. Possible values: sql (uses internal logging
           you can read in Preferences -> System), syslog - logs to system log.
           Setting this to blank uses PHP logging (usually to http server
@@ -515,7 +515,7 @@ let
       extraConfig = mkOption {
         type = types.lines;
         default = "";
-        description = lib.mdDoc ''
+        description = ''
           Additional lines to append to `config.php`.
         '';
       };
diff --git a/nixpkgs/nixos/modules/services/web-apps/vikunja.nix b/nixpkgs/nixos/modules/services/web-apps/vikunja.nix
index efa9c676d9a5..9727eaccc1d0 100644
--- a/nixpkgs/nixos/modules/services/web-apps/vikunja.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/vikunja.nix
@@ -14,36 +14,36 @@ in {
   ];
 
   options.services.vikunja = with lib; {
-    enable = mkEnableOption (lib.mdDoc "vikunja service");
+    enable = mkEnableOption "vikunja service";
     package = mkPackageOption pkgs "vikunja" { };
     environmentFiles = mkOption {
       type = types.listOf types.path;
       default = [ ];
-      description = lib.mdDoc ''
+      description = ''
         List of environment files set in the vikunja systemd service.
         For example passwords should be set in one of these files.
       '';
     };
     frontendScheme = mkOption {
       type = types.enum [ "http" "https" ];
-      description = lib.mdDoc ''
+      description = ''
         Whether the site is available via http or https.
       '';
     };
     frontendHostname = mkOption {
       type = types.str;
-      description = lib.mdDoc "The Hostname under which the frontend is running.";
+      description = "The Hostname under which the frontend is running.";
     };
     port = mkOption {
       type = types.port;
       default = 3456;
-      description = lib.mdDoc "The TCP port exposed by the API.";
+      description = "The TCP port exposed by the API.";
     };
 
     settings = mkOption {
       type = format.type;
       default = {};
-      description = lib.mdDoc ''
+      description = ''
         Vikunja configuration. Refer to
         <https://vikunja.io/docs/config-options/>
         for details on supported values.
@@ -54,27 +54,27 @@ in {
         type = types.enum [ "sqlite" "mysql" "postgres" ];
         example = "postgres";
         default = "sqlite";
-        description = lib.mdDoc "Database engine to use.";
+        description = "Database engine to use.";
       };
       host = mkOption {
         type = types.str;
         default = "localhost";
-        description = lib.mdDoc "Database host address. Can also be a socket.";
+        description = "Database host address. Can also be a socket.";
       };
       user = mkOption {
         type = types.str;
         default = "vikunja";
-        description = lib.mdDoc "Database user.";
+        description = "Database user.";
       };
       database = mkOption {
         type = types.str;
         default = "vikunja";
-        description = lib.mdDoc "Database name.";
+        description = "Database name.";
       };
       path = mkOption {
         type = types.str;
         default = "/var/lib/vikunja/vikunja.db";
-        description = lib.mdDoc "Path to the sqlite3 database file.";
+        description = "Path to the sqlite3 database file.";
       };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/whitebophir.nix b/nixpkgs/nixos/modules/services/web-apps/whitebophir.nix
index dabcf38b2dbd..332a8d9d4ec6 100644
--- a/nixpkgs/nixos/modules/services/web-apps/whitebophir.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/whitebophir.nix
@@ -7,20 +7,20 @@ let
 in {
   options = {
     services.whitebophir = {
-      enable = mkEnableOption (lib.mdDoc "whitebophir, an online collaborative whiteboard server (persistent state will be maintained under {file}`/var/lib/whitebophir`)");
+      enable = mkEnableOption "whitebophir, an online collaborative whiteboard server (persistent state will be maintained under {file}`/var/lib/whitebophir`)";
 
       package = mkPackageOption pkgs "whitebophir" { };
 
       listenAddress = mkOption {
         type = types.str;
         default = "0.0.0.0";
-        description = lib.mdDoc "Address to listen on (use 0.0.0.0 to allow access from any address).";
+        description = "Address to listen on (use 0.0.0.0 to allow access from any address).";
       };
 
       port = mkOption {
         type = types.port;
         default = 5001;
-        description = lib.mdDoc "Port to bind to.";
+        description = "Port to bind to.";
       };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/wiki-js.nix b/nixpkgs/nixos/modules/services/web-apps/wiki-js.nix
index 631740f51ce3..dedc4c584628 100644
--- a/nixpkgs/nixos/modules/services/web-apps/wiki-js.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/wiki-js.nix
@@ -10,13 +10,13 @@ let
   configFile = format.generate "wiki-js.yml" cfg.settings;
 in {
   options.services.wiki-js = {
-    enable = mkEnableOption (lib.mdDoc "wiki-js");
+    enable = mkEnableOption "wiki-js";
 
     environmentFile = mkOption {
       type = types.nullOr types.path;
       default = null;
       example = "/root/wiki-js.env";
-      description = lib.mdDoc ''
+      description = ''
         Environment file to inject e.g. secrets into the configuration.
       '';
     };
@@ -24,7 +24,7 @@ in {
     stateDirectoryName = mkOption {
       default = "wiki-js";
       type = types.str;
-      description = lib.mdDoc ''
+      description = ''
         Name of the directory in {file}`/var/lib`.
       '';
     };
@@ -37,7 +37,7 @@ in {
           port = mkOption {
             type = types.port;
             default = 3000;
-            description = lib.mdDoc ''
+            description = ''
               TCP port the process should listen to.
             '';
           };
@@ -45,7 +45,7 @@ in {
           bindIP = mkOption {
             default = "0.0.0.0";
             type = types.str;
-            description = lib.mdDoc ''
+            description = ''
               IPs the service should listen to.
             '';
           };
@@ -54,7 +54,7 @@ in {
             type = mkOption {
               default = "postgres";
               type = types.enum [ "postgres" "mysql" "mariadb" "mssql" ];
-              description = lib.mdDoc ''
+              description = ''
                 Database driver to use for persistence. Please note that `sqlite`
                 is currently not supported as the build process for it is currently not implemented
                 in `pkgs.wiki-js` and it's not recommended by upstream for
@@ -64,14 +64,14 @@ in {
             host = mkOption {
               type = types.str;
               example = "/run/postgresql";
-              description = lib.mdDoc ''
+              description = ''
                 Hostname or socket-path to connect to.
               '';
             };
             db = mkOption {
               default = "wiki";
               type = types.str;
-              description = lib.mdDoc ''
+              description = ''
                 Name of the database to use.
               '';
             };
@@ -80,20 +80,20 @@ in {
           logLevel = mkOption {
             default = "info";
             type = types.enum [ "error" "warn" "info" "verbose" "debug" "silly" ];
-            description = lib.mdDoc ''
+            description = ''
               Define how much detail is supposed to be logged at runtime.
             '';
           };
 
-          offline = mkEnableOption (lib.mdDoc "offline mode") // {
-            description = lib.mdDoc ''
+          offline = mkEnableOption "offline mode" // {
+            description = ''
               Disable latest file updates and enable
               [sideloading](https://docs.requarks.io/install/sideload).
             '';
           };
         };
       };
-      description = lib.mdDoc ''
+      description = ''
         Settings to configure `wiki-js`. This directly
         corresponds to [the upstream configuration options](https://docs.requarks.io/install/config).
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/windmill.nix b/nixpkgs/nixos/modules/services/web-apps/windmill.nix
index 8e940dabdc1f..f5ec7f70e877 100644
--- a/nixpkgs/nixos/modules/services/web-apps/windmill.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/windmill.nix
@@ -5,18 +5,18 @@ let
 in
 {
   options.services.windmill = {
-    enable = lib.mkEnableOption (lib.mdDoc "windmill service");
+    enable = lib.mkEnableOption "windmill service";
 
     serverPort = lib.mkOption {
       type = lib.types.port;
       default = 8001;
-      description = lib.mdDoc "Port the windmill server listens on.";
+      description = "Port the windmill server listens on.";
     };
 
     lspPort = lib.mkOption {
       type = lib.types.port;
       default = 3001;
-      description = lib.mdDoc "Port the windmill lsp listens on.";
+      description = "Port the windmill lsp listens on.";
     };
 
     database = {
@@ -24,19 +24,19 @@ in
         type = lib.types.str;
         # the simplest database setup is to have the database named like the user.
         default = "windmill";
-        description = lib.mdDoc "Database name.";
+        description = "Database name.";
       };
 
       user = lib.mkOption {
         type = lib.types.str;
         # the simplest database setup is to have the database user like the name.
         default = "windmill";
-        description = lib.mdDoc "Database user.";
+        description = "Database user.";
       };
 
       urlPath = lib.mkOption {
         type = lib.types.path;
-        description = lib.mdDoc ''
+        description = ''
           Path to the file containing the database url windmill should connect to. This is not deducted from database user and name as it might contain a secret
         '';
         example = "config.age.secrets.DATABASE_URL_FILE.path";
@@ -44,13 +44,13 @@ in
       createLocally = lib.mkOption {
         type = lib.types.bool;
         default = true;
-        description = lib.mdDoc "Whether to create a local database automatically.";
+        description = "Whether to create a local database automatically.";
       };
     };
 
     baseUrl = lib.mkOption {
       type = lib.types.str;
-      description = lib.mdDoc ''
+      description = ''
         The base url that windmill will be served on.
       '';
       example = "https://windmill.example.com";
@@ -59,7 +59,7 @@ in
     logLevel = lib.mkOption {
       type = lib.types.enum [ "error" "warn" "info" "debug" "trace" ];
       default = "info";
-      description = lib.mdDoc "Log level";
+      description = "Log level";
     };
   };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/wordpress.nix b/nixpkgs/nixos/modules/services/web-apps/wordpress.nix
index 2f7306309d69..0d49f2d92998 100644
--- a/nixpkgs/nixos/modules/services/web-apps/wordpress.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/wordpress.nix
@@ -109,7 +109,7 @@ let
         uploadsDir = mkOption {
           type = types.path;
           default = "/var/lib/wordpress/${name}/uploads";
-          description = lib.mdDoc ''
+          description = ''
             This directory is used for uploads of pictures. The directory passed here is automatically
             created and permissions adjusted as required.
           '';
@@ -118,7 +118,7 @@ let
         fontsDir = mkOption {
           type = types.path;
           default = "/var/lib/wordpress/${name}/fonts";
-          description = lib.mdDoc ''
+          description = ''
             This directory is used to download fonts from a remote location, e.g.
             to host google fonts locally.
           '';
@@ -131,7 +131,7 @@ let
               listToAttrs (map (p: nameValuePair (p.name or (throw "${p} does not have a name")) p) l))
             (attrsOf path);
           default = {};
-          description = lib.mdDoc ''
+          description = ''
             Path(s) to respective plugin(s) which are copied from the 'plugins' directory.
 
             ::: {.note}
@@ -153,7 +153,7 @@ let
             (attrsOf path);
           default = { inherit (pkgs.wordpressPackages.themes) twentytwentythree; };
           defaultText = literalExpression "{ inherit (pkgs.wordpressPackages.themes) twentytwentythree; }";
-          description = lib.mdDoc ''
+          description = ''
             Path(s) to respective theme(s) which are copied from the 'theme' directory.
 
             ::: {.note}
@@ -170,7 +170,7 @@ let
         languages = mkOption {
           type = types.listOf types.path;
           default = [];
-          description = lib.mdDoc ''
+          description = ''
             List of path(s) to respective language(s) which are copied from the 'languages' directory.
           '';
           example = literalExpression ''
@@ -197,32 +197,32 @@ let
           host = mkOption {
             type = types.str;
             default = "localhost";
-            description = lib.mdDoc "Database host address.";
+            description = "Database host address.";
           };
 
           port = mkOption {
             type = types.port;
             default = 3306;
-            description = lib.mdDoc "Database host port.";
+            description = "Database host port.";
           };
 
           name = mkOption {
             type = types.str;
             default = "wordpress";
-            description = lib.mdDoc "Database name.";
+            description = "Database name.";
           };
 
           user = mkOption {
             type = types.str;
             default = "wordpress";
-            description = lib.mdDoc "Database user.";
+            description = "Database user.";
           };
 
           passwordFile = mkOption {
             type = types.nullOr types.path;
             default = null;
             example = "/run/keys/wordpress-dbpassword";
-            description = lib.mdDoc ''
+            description = ''
               A file containing the password corresponding to
               {option}`database.user`.
             '';
@@ -231,7 +231,7 @@ let
           tablePrefix = mkOption {
             type = types.str;
             default = "wp_";
-            description = lib.mdDoc ''
+            description = ''
               The $table_prefix is the value placed in the front of your database tables.
               Change the value if you want to use something other than wp_ for your database
               prefix. Typically this is changed if you are installing multiple WordPress blogs
@@ -245,13 +245,13 @@ let
             type = types.nullOr types.path;
             default = null;
             defaultText = literalExpression "/run/mysqld/mysqld.sock";
-            description = lib.mdDoc "Path to the unix socket file to use for authentication.";
+            description = "Path to the unix socket file to use for authentication.";
           };
 
           createLocally = mkOption {
             type = types.bool;
             default = true;
-            description = lib.mdDoc "Create the database and database user locally.";
+            description = "Create the database and database user locally.";
           };
         };
 
@@ -264,7 +264,7 @@ let
               enableACME = true;
             }
           '';
-          description = lib.mdDoc ''
+          description = ''
             Apache configuration can be done by adapting {option}`services.httpd.virtualHosts`.
           '';
         };
@@ -279,7 +279,7 @@ let
             "pm.max_spare_servers" = 4;
             "pm.max_requests" = 500;
           };
-          description = lib.mdDoc ''
+          description = ''
             Options for the WordPress PHP pool. See the documentation on `php-fpm.conf`
             for details on configuration directives.
           '';
@@ -288,7 +288,7 @@ let
         settings = mkOption {
           type = types.attrsOf types.anything;
           default = {};
-          description = lib.mdDoc ''
+          description = ''
             Structural Wordpress configuration.
             Refer to <https://developer.wordpress.org/apis/wp-config-php>
             for details and supported values.
@@ -316,7 +316,7 @@ let
               AUTOMATIC_UPDATER_DISABLED = true;
             }
           '';
-          description = lib.mdDoc ''
+          description = ''
             Read only representation of the final configuration.
           '';
         };
@@ -324,7 +324,7 @@ let
         extraConfig = mkOption {
           type = types.lines;
           default = "";
-          description = lib.mdDoc ''
+          description = ''
             Any additional text to be appended to the wp-config.php
             configuration file. This is a PHP script. For configuration
             settings, see <https://codex.wordpress.org/Editing_wp-config.php>.
@@ -351,13 +351,13 @@ in
       sites = mkOption {
         type = types.attrsOf (types.submodule siteOpts);
         default = {};
-        description = lib.mdDoc "Specification of one or more WordPress sites to serve";
+        description = "Specification of one or more WordPress sites to serve";
       };
 
       webserver = mkOption {
         type = types.enum [ "httpd" "nginx" "caddy" ];
         default = "httpd";
-        description = lib.mdDoc ''
+        description = ''
           Whether to use apache2 or nginx for virtual host management.
 
           Further nginx configuration can be done by adapting `services.nginx.virtualHosts.<name>`.
diff --git a/nixpkgs/nixos/modules/services/web-apps/writefreely.nix b/nixpkgs/nixos/modules/services/web-apps/writefreely.nix
index 2e9a34897909..4bb5d8a579fd 100644
--- a/nixpkgs/nixos/modules/services/web-apps/writefreely.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/writefreely.nix
@@ -132,43 +132,43 @@ let
 in {
   options.services.writefreely = {
     enable =
-      lib.mkEnableOption (lib.mdDoc "Writefreely, build a digital writing community");
+      lib.mkEnableOption "Writefreely, build a digital writing community";
 
     package = lib.mkOption {
       type = lib.types.package;
       default = pkgs.writefreely;
       defaultText = lib.literalExpression "pkgs.writefreely";
-      description = lib.mdDoc "Writefreely package to use.";
+      description = "Writefreely package to use.";
     };
 
     stateDir = mkOption {
       type = types.path;
       default = "/var/lib/writefreely";
-      description = lib.mdDoc "The state directory where keys and data are stored.";
+      description = "The state directory where keys and data are stored.";
     };
 
     user = mkOption {
       type = types.str;
       default = "writefreely";
-      description = lib.mdDoc "User under which Writefreely is ran.";
+      description = "User under which Writefreely is ran.";
     };
 
     group = mkOption {
       type = types.str;
       default = "writefreely";
-      description = lib.mdDoc "Group under which Writefreely is ran.";
+      description = "Group under which Writefreely is ran.";
     };
 
     host = mkOption {
       type = types.str;
       default = "";
-      description = lib.mdDoc "The public host name to serve.";
+      description = "The public host name to serve.";
       example = "example.com";
     };
 
     settings = mkOption {
       default = { };
-      description = lib.mdDoc ''
+      description = ''
         Writefreely configuration ({file}`config.ini`). Refer to
         <https://writefreely.org/docs/latest/admin/config>
         for details.
@@ -182,7 +182,7 @@ in {
             theme = mkOption {
               type = types.str;
               default = "write";
-              description = lib.mdDoc "The theme to apply.";
+              description = "The theme to apply.";
             };
           };
 
@@ -191,7 +191,7 @@ in {
               type = types.port;
               default = if cfg.nginx.enable then 18080 else 80;
               defaultText = "80";
-              description = lib.mdDoc "The port WriteFreely should listen on.";
+              description = "The port WriteFreely should listen on.";
             };
           };
         };
@@ -202,58 +202,56 @@ in {
       type = mkOption {
         type = types.enum [ "sqlite3" "mysql" ];
         default = "sqlite3";
-        description = lib.mdDoc "The database provider to use.";
+        description = "The database provider to use.";
       };
 
       name = mkOption {
         type = types.str;
         default = "writefreely";
-        description = lib.mdDoc "The name of the database to store data in.";
+        description = "The name of the database to store data in.";
       };
 
       user = mkOption {
         type = types.nullOr types.str;
         default = if cfg.database.type == "mysql" then "writefreely" else null;
         defaultText = "writefreely";
-        description = lib.mdDoc "The database user to connect as.";
+        description = "The database user to connect as.";
       };
 
       passwordFile = mkOption {
         type = types.nullOr types.path;
         default = null;
-        description = lib.mdDoc "The file to load the database password from.";
+        description = "The file to load the database password from.";
       };
 
       host = mkOption {
         type = types.str;
         default = "localhost";
-        description = lib.mdDoc "The database host to connect to.";
+        description = "The database host to connect to.";
       };
 
       port = mkOption {
         type = types.port;
         default = 3306;
-        description = lib.mdDoc "The port used when connecting to the database host.";
+        description = "The port used when connecting to the database host.";
       };
 
       tls = mkOption {
         type = types.bool;
         default = false;
-        description =
-          lib.mdDoc "Whether or not TLS should be used for the database connection.";
+        description = "Whether or not TLS should be used for the database connection.";
       };
 
       migrate = mkOption {
         type = types.bool;
         default = true;
-        description =
-          lib.mdDoc "Whether or not to automatically run migrations on startup.";
+        description = "Whether or not to automatically run migrations on startup.";
       };
 
       createLocally = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           When {option}`services.writefreely.database.type` is set to
           `"mysql"`, this option will enable the MySQL service locally.
         '';
@@ -263,13 +261,13 @@ in {
     admin = {
       name = mkOption {
         type = types.nullOr types.str;
-        description = lib.mdDoc "The name of the first admin user.";
+        description = "The name of the first admin user.";
         default = null;
       };
 
       initialPasswordFile = mkOption {
         type = types.path;
-        description = lib.mdDoc ''
+        description = ''
           Path to a file containing the initial password for the admin user.
           If not provided, the default password will be set to `nixos`.
         '';
@@ -282,14 +280,13 @@ in {
       enable = mkOption {
         type = types.bool;
         default = false;
-        description =
-          lib.mdDoc "Whether or not to enable and configure nginx as a proxy for WriteFreely.";
+        description = "Whether or not to enable and configure nginx as a proxy for WriteFreely.";
       };
 
       forceSSL = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc "Whether or not to force the use of SSL.";
+        description = "Whether or not to force the use of SSL.";
       };
     };
 
@@ -297,8 +294,7 @@ in {
       enable = mkOption {
         type = types.bool;
         default = false;
-        description =
-          lib.mdDoc "Whether or not to automatically fetch and configure SSL certs.";
+        description = "Whether or not to automatically fetch and configure SSL certs.";
       };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/youtrack.nix b/nixpkgs/nixos/modules/services/web-apps/youtrack.nix
index 08e180b520f0..ff48a978b734 100644
--- a/nixpkgs/nixos/modules/services/web-apps/youtrack.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/youtrack.nix
@@ -12,10 +12,10 @@ in
   ];
 
   options.services.youtrack = {
-    enable = lib.mkEnableOption (lib.mdDoc "YouTrack service");
+    enable = lib.mkEnableOption "YouTrack service";
 
     address = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         The interface youtrack will listen on.
       '';
       default = "127.0.0.1";
@@ -24,7 +24,7 @@ in
 
     extraParams = lib.mkOption {
       default = {};
-      description = lib.mdDoc ''
+      description = ''
         Extra parameters to pass to youtrack.
         Use to configure YouTrack 2022.x, deprecated with YouTrack 2023.x. Use `services.youtrack.generalParameters`.
         https://www.jetbrains.com/help/youtrack/standalone/YouTrack-Java-Start-Parameters.html
@@ -40,7 +40,7 @@ in
     };
 
     package = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         Package to use.
       '';
       type = lib.types.package;
@@ -50,7 +50,7 @@ in
 
 
     statePath = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         Path were the YouTrack state is stored.
         To this path the base version (e.g. 2023_1) of the used package will be appended.
       '';
@@ -59,7 +59,7 @@ in
     };
 
     virtualHost = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         Name of the nginx virtual host to use and setup.
         If null, do not setup anything.
       '';
@@ -68,7 +68,7 @@ in
     };
 
     jvmOpts = lib.mkOption {
-      description = lib.mdDoc ''
+      description = ''
         Extra options to pass to the JVM.
         Only has a use with YouTrack 2022.x, deprecated with YouTrack 2023.x. Use `serivces.youtrack.generalParameters`.
         See https://www.jetbrains.com/help/youtrack/standalone/Configure-JVM-Options.html
@@ -83,12 +83,12 @@ in
     autoUpgrade = lib.mkOption {
       type = lib.types.bool;
       default = true;
-      description = lib.mdDoc "Whether YouTrack should auto upgrade it without showing the upgrade dialog.";
+      description = "Whether YouTrack should auto upgrade it without showing the upgrade dialog.";
     };
 
     generalParameters = lib.mkOption {
       type = with lib.types; listOf str;
-      description = lib.mdDoc ''
+      description = ''
         General configuration parameters and other JVM options.
         Only has an effect for YouTrack 2023.x.
         See https://www.jetbrains.com/help/youtrack/server/2023.3/youtrack-java-start-parameters.html#general-parameters
@@ -110,16 +110,16 @@ in
           listen-address = lib.mkOption {
             type = lib.types.str;
             default = "0.0.0.0";
-            description = lib.mdDoc "The interface YouTrack will listen on.";
+            description = "The interface YouTrack will listen on.";
           };
           listen-port = lib.mkOption {
             type = lib.types.port;
             default = 8080;
-            description = lib.mdDoc "The port YouTrack will listen on.";
+            description = "The port YouTrack will listen on.";
           };
         };
       };
-      description = lib.mdDoc ''
+      description = ''
         Environmental configuration parameters, set imperatively. The values doesn't get removed, when removed in Nix.
         Only has an effect for YouTrack 2023.x.
         See https://www.jetbrains.com/help/youtrack/server/2023.3/youtrack-java-start-parameters.html#environmental-parameters
diff --git a/nixpkgs/nixos/modules/services/web-apps/zabbix.nix b/nixpkgs/nixos/modules/services/web-apps/zabbix.nix
index 4f6d7e4e6c1c..2455e676e583 100644
--- a/nixpkgs/nixos/modules/services/web-apps/zabbix.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/zabbix.nix
@@ -40,20 +40,20 @@ in
 
   options.services = {
     zabbixWeb = {
-      enable = mkEnableOption (lib.mdDoc "the Zabbix web interface");
+      enable = mkEnableOption "the Zabbix web interface";
 
       package = mkPackageOption pkgs [ "zabbix" "web" ] { };
 
       server = {
         port = mkOption {
           type = types.port;
-          description = lib.mdDoc "The port of the Zabbix server to connect to.";
+          description = "The port of the Zabbix server to connect to.";
           default = 10051;
         };
 
         address = mkOption {
           type = types.str;
-          description = lib.mdDoc "The IP address or hostname of the Zabbix server to connect to.";
+          description = "The IP address or hostname of the Zabbix server to connect to.";
           default = "localhost";
         };
       };
@@ -63,46 +63,46 @@ in
           type = types.enum [ "mysql" "pgsql" "oracle" ];
           example = "mysql";
           default = "pgsql";
-          description = lib.mdDoc "Database engine to use.";
+          description = "Database engine to use.";
         };
 
         host = mkOption {
           type = types.str;
           default = "";
-          description = lib.mdDoc "Database host address.";
+          description = "Database host address.";
         };
 
         port = mkOption {
           type = types.port;
           default =
             if cfg.database.type == "mysql" then config.services.mysql.port
-            else if cfg.database.type == "pgsql" then config.services.postgresql.port
+            else if cfg.database.type == "pgsql" then config.services.postgresql.settings.port
             else 1521;
           defaultText = literalExpression ''
             if config.${opt.database.type} == "mysql" then config.${options.services.mysql.port}
-            else if config.${opt.database.type} == "pgsql" then config.${options.services.postgresql.port}
+            else if config.${opt.database.type} == "pgsql" then config.services.postgresql.settings.port
             else 1521
           '';
-          description = lib.mdDoc "Database host port.";
+          description = "Database host port.";
         };
 
         name = mkOption {
           type = types.str;
           default = "zabbix";
-          description = lib.mdDoc "Database name.";
+          description = "Database name.";
         };
 
         user = mkOption {
           type = types.str;
           default = "zabbix";
-          description = lib.mdDoc "Database user.";
+          description = "Database user.";
         };
 
         passwordFile = mkOption {
           type = types.nullOr types.path;
           default = null;
           example = "/run/keys/zabbix-dbpassword";
-          description = lib.mdDoc ''
+          description = ''
             A file containing the password corresponding to
             {option}`database.user`.
           '';
@@ -112,7 +112,7 @@ in
           type = types.nullOr types.path;
           default = null;
           example = "/run/postgresql";
-          description = lib.mdDoc "Path to the unix socket file to use for authentication.";
+          description = "Path to the unix socket file to use for authentication.";
         };
       };
 
@@ -126,7 +126,7 @@ in
             enableACME = true;
           }
         '';
-        description = lib.mdDoc ''
+        description = ''
           Apache configuration can be done by adapting `services.httpd.virtualHosts.<name>`.
           See [](#opt-services.httpd.virtualHosts) for further information.
         '';
@@ -142,7 +142,7 @@ in
           "pm.max_spare_servers" = 4;
           "pm.max_requests" = 500;
         };
-        description = lib.mdDoc ''
+        description = ''
           Options for the Zabbix PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives.
         '';
       };
@@ -150,7 +150,7 @@ in
       extraConfig = mkOption {
         type = types.lines;
         default = "";
-        description = lib.mdDoc ''
+        description = ''
           Additional configuration to be copied verbatim into {file}`zabbix.conf.php`.
         '';
       };