about summary refs log tree commit diff
path: root/nixos/modules/services/web-apps
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/web-apps')
-rw-r--r--nixos/modules/services/web-apps/akkoma.nix2
-rw-r--r--nixos/modules/services/web-apps/akkoma.xml398
-rw-r--r--nixos/modules/services/web-apps/discourse.nix2
-rw-r--r--nixos/modules/services/web-apps/discourse.xml331
-rw-r--r--nixos/modules/services/web-apps/grocy.nix2
-rw-r--r--nixos/modules/services/web-apps/grocy.xml84
-rw-r--r--nixos/modules/services/web-apps/jitsi-meet.nix2
-rw-r--r--nixos/modules/services/web-apps/jitsi-meet.xml55
-rw-r--r--nixos/modules/services/web-apps/keycloak.nix2
-rw-r--r--nixos/modules/services/web-apps/keycloak.xml177
-rw-r--r--nixos/modules/services/web-apps/lemmy.nix2
-rw-r--r--nixos/modules/services/web-apps/lemmy.xml53
-rw-r--r--nixos/modules/services/web-apps/matomo.nix2
-rw-r--r--nixos/modules/services/web-apps/matomo.xml107
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix2
-rw-r--r--nixos/modules/services/web-apps/nextcloud.xml333
-rw-r--r--nixos/modules/services/web-apps/pict-rs.nix2
-rw-r--r--nixos/modules/services/web-apps/pict-rs.xml185
-rw-r--r--nixos/modules/services/web-apps/plausible.nix2
-rw-r--r--nixos/modules/services/web-apps/plausible.xml45
20 files changed, 10 insertions, 1778 deletions
diff --git a/nixos/modules/services/web-apps/akkoma.nix b/nixos/modules/services/web-apps/akkoma.nix
index 47ba53e42221a..fc482ff32debc 100644
--- a/nixos/modules/services/web-apps/akkoma.nix
+++ b/nixos/modules/services/web-apps/akkoma.nix
@@ -1082,5 +1082,5 @@ in {
   };
 
   meta.maintainers = with maintainers; [ mvs ];
-  meta.doc = ./akkoma.xml;
+  meta.doc = ./akkoma.md;
 }
diff --git a/nixos/modules/services/web-apps/akkoma.xml b/nixos/modules/services/web-apps/akkoma.xml
deleted file mode 100644
index 49cbcc911e1d1..0000000000000
--- a/nixos/modules/services/web-apps/akkoma.xml
+++ /dev/null
@@ -1,398 +0,0 @@
-<!-- Do not edit this file directly, edit its companion .md instead
-     and regenerate this file using nixos/doc/manual/md-to-db.sh -->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-akkoma">
-  <title>Akkoma</title>
-  <para>
-    <link xlink:href="https://akkoma.dev/">Akkoma</link> is a
-    lightweight ActivityPub microblogging server forked from Pleroma.
-  </para>
-  <section xml:id="modules-services-akkoma-service-configuration">
-    <title>Service configuration</title>
-    <para>
-      The Elixir configuration file required by Akkoma is generated
-      automatically from
-      <link xlink:href="options.html#opt-services.akkoma.config"><option>services.akkoma.config</option></link>.
-      Secrets must be included from external files outside of the Nix
-      store by setting the configuration option to an attribute set
-      containing the attribute <option>_secret</option> – a string
-      pointing to the file containing the actual value of the option.
-    </para>
-    <para>
-      For the mandatory configuration settings these secrets will be
-      generated automatically if the referenced file does not exist
-      during startup, unless disabled through
-      <link xlink:href="options.html#opt-services.akkoma.initSecrets"><option>services.akkoma.initSecrets</option></link>.
-    </para>
-    <para>
-      The following configuration binds Akkoma to the Unix socket
-      <literal>/run/akkoma/socket</literal>, expecting to be run behind
-      a HTTP proxy on <literal>fediverse.example.com</literal>.
-    </para>
-    <programlisting language="nix">
-services.akkoma.enable = true;
-services.akkoma.config = {
-  &quot;:pleroma&quot; = {
-    &quot;:instance&quot; = {
-      name = &quot;My Akkoma instance&quot;;
-      description = &quot;More detailed description&quot;;
-      email = &quot;admin@example.com&quot;;
-      registration_open = false;
-    };
-
-    &quot;Pleroma.Web.Endpoint&quot; = {
-      url.host = &quot;fediverse.example.com&quot;;
-    };
-  };
-};
-</programlisting>
-    <para>
-      Please refer to the
-      <link xlink:href="https://docs.akkoma.dev/stable/configuration/cheatsheet/">configuration
-      cheat sheet</link> for additional configuration options.
-    </para>
-  </section>
-  <section xml:id="modules-services-akkoma-user-management">
-    <title>User management</title>
-    <para>
-      After the Akkoma service is running, the administration utility
-      can be used to
-      <link xlink:href="https://docs.akkoma.dev/stable/administration/CLI_tasks/user/">manage
-      users</link>. In particular an administrative user can be created
-      with
-    </para>
-    <programlisting>
-$ pleroma_ctl user new &lt;nickname&gt; &lt;email&gt; --admin --moderator --password &lt;password&gt;
-</programlisting>
-  </section>
-  <section xml:id="modules-services-akkoma-proxy-configuration">
-    <title>Proxy configuration</title>
-    <para>
-      Although it is possible to expose Akkoma directly, it is common
-      practice to operate it behind an HTTP reverse proxy such as nginx.
-    </para>
-    <programlisting language="nix">
-services.akkoma.nginx = {
-  enableACME = true;
-  forceSSL = true;
-};
-
-services.nginx = {
-  enable = true;
-
-  clientMaxBodySize = &quot;16m&quot;;
-  recommendedTlsSettings = true;
-  recommendedOptimisation = true;
-  recommendedGzipSettings = true;
-};
-</programlisting>
-    <para>
-      Please refer to <xref linkend="module-security-acme" /> for
-      details on how to provision an SSL/TLS certificate.
-    </para>
-    <section xml:id="modules-services-akkoma-media-proxy">
-      <title>Media proxy</title>
-      <para>
-        Without the media proxy function, Akkoma does not store any
-        remote media like pictures or video locally, and clients have to
-        fetch them directly from the source server.
-      </para>
-      <programlisting language="nix">
-# Enable nginx slice module distributed with Tengine
-services.nginx.package = pkgs.tengine;
-
-# Enable media proxy
-services.akkoma.config.&quot;:pleroma&quot;.&quot;:media_proxy&quot; = {
-  enabled = true;
-  proxy_opts.redirect_on_failure = true;
-};
-
-# Adjust the persistent cache size as needed:
-#  Assuming an average object size of 128 KiB, around 1 MiB
-#  of memory is required for the key zone per GiB of cache.
-# Ensure that the cache directory exists and is writable by nginx.
-services.nginx.commonHttpConfig = ''
-  proxy_cache_path /var/cache/nginx/cache/akkoma-media-cache
-    levels= keys_zone=akkoma_media_cache:16m max_size=16g
-    inactive=1y use_temp_path=off;
-'';
-
-services.akkoma.nginx = {
-  locations.&quot;/proxy&quot; = {
-    proxyPass = &quot;http://unix:/run/akkoma/socket&quot;;
-
-    extraConfig = ''
-      proxy_cache akkoma_media_cache;
-
-      # Cache objects in slices of 1 MiB
-      slice 1m;
-      proxy_cache_key $host$uri$is_args$args$slice_range;
-      proxy_set_header Range $slice_range;
-
-      # Decouple proxy and upstream responses
-      proxy_buffering on;
-      proxy_cache_lock on;
-      proxy_ignore_client_abort on;
-
-      # Default cache times for various responses
-      proxy_cache_valid 200 1y;
-      proxy_cache_valid 206 301 304 1h;
-
-      # Allow serving of stale items
-      proxy_cache_use_stale error timeout invalid_header updating;
-    '';
-  };
-};
-</programlisting>
-      <section xml:id="modules-services-akkoma-prefetch-remote-media">
-        <title>Prefetch remote media</title>
-        <para>
-          The following example enables the
-          <literal>MediaProxyWarmingPolicy</literal> MRF policy which
-          automatically fetches all media associated with a post through
-          the media proxy, as soon as the post is received by the
-          instance.
-        </para>
-        <programlisting language="nix">
-services.akkoma.config.&quot;:pleroma&quot;.&quot;:mrf&quot;.policies =
-  map (pkgs.formats.elixirConf { }).lib.mkRaw [
-    &quot;Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy&quot;
-];
-</programlisting>
-      </section>
-      <section xml:id="modules-services-akkoma-media-previews">
-        <title>Media previews</title>
-        <para>
-          Akkoma can generate previews for media.
-        </para>
-        <programlisting language="nix">
-services.akkoma.config.&quot;:pleroma&quot;.&quot;:media_preview_proxy&quot; = {
-  enabled = true;
-  thumbnail_max_width = 1920;
-  thumbnail_max_height = 1080;
-};
-</programlisting>
-      </section>
-    </section>
-  </section>
-  <section xml:id="modules-services-akkoma-frontend-management">
-    <title>Frontend management</title>
-    <para>
-      Akkoma will be deployed with the <literal>pleroma-fe</literal> and
-      <literal>admin-fe</literal> frontends by default. These can be
-      modified by setting
-      <link xlink:href="options.html#opt-services.akkoma.frontends"><option>services.akkoma.frontends</option></link>.
-    </para>
-    <para>
-      The following example overrides the primary frontend’s default
-      configuration using a custom derivation.
-    </para>
-    <programlisting language="nix">
-services.akkoma.frontends.primary.package = pkgs.runCommand &quot;pleroma-fe&quot; {
-  config = builtins.toJSON {
-    expertLevel = 1;
-    collapseMessageWithSubject = false;
-    stopGifs = false;
-    replyVisibility = &quot;following&quot;;
-    webPushHideIfCW = true;
-    hideScopeNotice = true;
-    renderMisskeyMarkdown = false;
-    hideSiteFavicon = true;
-    postContentType = &quot;text/markdown&quot;;
-    showNavShortcuts = false;
-  };
-  nativeBuildInputs = with pkgs; [ jq xorg.lndir ];
-  passAsFile = [ &quot;config&quot; ];
-} ''
-  mkdir $out
-  lndir ${pkgs.akkoma-frontends.pleroma-fe} $out
-
-  rm $out/static/config.json
-  jq -s add ${pkgs.akkoma-frontends.pleroma-fe}/static/config.json ${config} \
-    &gt;$out/static/config.json
-'';
-</programlisting>
-  </section>
-  <section xml:id="modules-services-akkoma-federation-policies">
-    <title>Federation policies</title>
-    <para>
-      Akkoma comes with a number of modules to police federation with
-      other ActivityPub instances. The most valuable for typical users
-      is the
-      <link xlink:href="https://docs.akkoma.dev/stable/configuration/cheatsheet/#mrf_simple"><literal>:mrf_simple</literal></link>
-      module which allows limiting federation based on instance
-      hostnames.
-    </para>
-    <para>
-      This configuration snippet provides an example on how these can be
-      used. Choosing an adequate federation policy is not trivial and
-      entails finding a balance between connectivity to the rest of the
-      fediverse and providing a pleasant experience to the users of an
-      instance.
-    </para>
-    <programlisting language="nix">
-services.akkoma.config.&quot;:pleroma&quot; = with (pkgs.formats.elixirConf { }).lib; {
-  &quot;:mrf&quot;.policies = map mkRaw [
-    &quot;Pleroma.Web.ActivityPub.MRF.SimplePolicy&quot;
-  ];
-
-  &quot;:mrf_simple&quot; = {
-    # Tag all media as sensitive
-    media_nsfw = mkMap {
-      &quot;nsfw.weird.kinky&quot; = &quot;Untagged NSFW content&quot;;
-    };
-
-    # Reject all activities except deletes
-    reject = mkMap {
-      &quot;kiwifarms.cc&quot; = &quot;Persistent harassment of users, no moderation&quot;;
-    };
-
-    # Force posts to be visible by followers only
-    followers_only = mkMap {
-      &quot;beta.birdsite.live&quot; = &quot;Avoid polluting timelines with Twitter posts&quot;;
-    };
-  };
-};
-</programlisting>
-  </section>
-  <section xml:id="modules-services-akkoma-upload-filters">
-    <title>Upload filters</title>
-    <para>
-      This example strips GPS and location metadata from uploads,
-      deduplicates them and anonymises the the file name.
-    </para>
-    <programlisting language="nix">
-services.akkoma.config.&quot;:pleroma&quot;.&quot;Pleroma.Upload&quot;.filters =
-  map (pkgs.formats.elixirConf { }).lib.mkRaw [
-    &quot;Pleroma.Upload.Filter.Exiftool&quot;
-    &quot;Pleroma.Upload.Filter.Dedupe&quot;
-    &quot;Pleroma.Upload.Filter.AnonymizeFilename&quot;
-  ];
-</programlisting>
-  </section>
-  <section xml:id="modules-services-akkoma-migration-pleroma">
-    <title>Migration from Pleroma</title>
-    <para>
-      Pleroma instances can be migrated to Akkoma either by copying the
-      database and upload data or by pointing Akkoma to the existing
-      data. The necessary database migrations are run automatically
-      during startup of the service.
-    </para>
-    <para>
-      The configuration has to be copy‐edited manually.
-    </para>
-    <para>
-      Depending on the size of the database, the initial migration may
-      take a long time and exceed the startup timeout of the system
-      manager. To work around this issue one may adjust the startup
-      timeout
-      <option>systemd.services.akkoma.serviceConfig.TimeoutStartSec</option>
-      or simply run the migrations manually:
-    </para>
-    <programlisting>
-pleroma_ctl migrate
-</programlisting>
-    <section xml:id="modules-services-akkoma-migration-pleroma-copy">
-      <title>Copying data</title>
-      <para>
-        Copying the Pleroma data instead of re‐using it in place may
-        permit easier reversion to Pleroma, but allows the two data sets
-        to diverge.
-      </para>
-      <para>
-        First disable Pleroma and then copy its database and upload
-        data:
-      </para>
-      <programlisting>
-# Create a copy of the database
-nix-shell -p postgresql --run 'createdb -T pleroma akkoma'
-
-# Copy upload data
-mkdir /var/lib/akkoma
-cp -R --reflink=auto /var/lib/pleroma/uploads /var/lib/akkoma/
-</programlisting>
-      <para>
-        After the data has been copied, enable the Akkoma service and
-        verify that the migration has been successful. If no longer
-        required, the original data may then be deleted:
-      </para>
-      <programlisting>
-# Delete original database
-nix-shell -p postgresql --run 'dropdb pleroma'
-
-# Delete original Pleroma state
-rm -r /var/lib/pleroma
-</programlisting>
-    </section>
-    <section xml:id="modules-services-akkoma-migration-pleroma-reuse">
-      <title>Re‐using data</title>
-      <para>
-        To re‐use the Pleroma data in place, disable Pleroma and enable
-        Akkoma, pointing it to the Pleroma database and upload
-        directory.
-      </para>
-      <programlisting language="nix">
-# Adjust these settings according to the database name and upload directory path used by Pleroma
-services.akkoma.config.&quot;:pleroma&quot;.&quot;Pleroma.Repo&quot;.database = &quot;pleroma&quot;;
-services.akkoma.config.&quot;:pleroma&quot;.&quot;:instance&quot;.upload_dir = &quot;/var/lib/pleroma/uploads&quot;;
-</programlisting>
-      <para>
-        Please keep in mind that after the Akkoma service has been
-        started, any migrations applied by Akkoma have to be rolled back
-        before the database can be used again with Pleroma. This can be
-        achieved through <literal>pleroma_ctl ecto.rollback</literal>.
-        Refer to the
-        <link xlink:href="https://hexdocs.pm/ecto_sql/Mix.Tasks.Ecto.Rollback.html">Ecto
-        SQL documentation</link> for details.
-      </para>
-    </section>
-  </section>
-  <section xml:id="modules-services-akkoma-advanced-deployment">
-    <title>Advanced deployment options</title>
-    <section xml:id="modules-services-akkoma-confinement">
-      <title>Confinement</title>
-      <para>
-        The Akkoma systemd service may be confined to a chroot with
-      </para>
-      <programlisting language="nix">
-services.systemd.akkoma.confinement.enable = true;
-</programlisting>
-      <para>
-        Confinement of services is not generally supported in NixOS and
-        therefore disabled by default. Depending on the Akkoma
-        configuration, the default confinement settings may be
-        insufficient and lead to subtle errors at run time, requiring
-        adjustment:
-      </para>
-      <para>
-        Use
-        <link xlink:href="options.html#opt-systemd.services._name_.confinement.packages"><option>services.systemd.akkoma.confinement.packages</option></link>
-        to make packages available in the chroot.
-      </para>
-      <para>
-        <option>services.systemd.akkoma.serviceConfig.BindPaths</option>
-        and
-        <option>services.systemd.akkoma.serviceConfig.BindReadOnlyPaths</option>
-        permit access to outside paths through bind mounts. Refer to
-        <link xlink:href="https://www.freedesktop.org/software/systemd/man/systemd.exec.html#BindPaths="><citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry></link>
-        for details.
-      </para>
-    </section>
-    <section xml:id="modules-services-akkoma-distributed-deployment">
-      <title>Distributed deployment</title>
-      <para>
-        Being an Elixir application, Akkoma can be deployed in a
-        distributed fashion.
-      </para>
-      <para>
-        This requires setting
-        <link xlink:href="options.html#opt-services.akkoma.dist.address"><option>services.akkoma.dist.address</option></link>
-        and
-        <link xlink:href="options.html#opt-services.akkoma.dist.cookie"><option>services.akkoma.dist.cookie</option></link>.
-        The specifics depend strongly on the deployment environment. For
-        more information please check the relevant
-        <link xlink:href="https://www.erlang.org/doc/reference_manual/distributed.html">Erlang
-        documentation</link>.
-      </para>
-    </section>
-  </section>
-</chapter>
diff --git a/nixos/modules/services/web-apps/discourse.nix b/nixos/modules/services/web-apps/discourse.nix
index b8104ade4676d..5565a4f45d1e6 100644
--- a/nixos/modules/services/web-apps/discourse.nix
+++ b/nixos/modules/services/web-apps/discourse.nix
@@ -1080,6 +1080,6 @@ in
     ];
   };
 
-  meta.doc = ./discourse.xml;
+  meta.doc = ./discourse.md;
   meta.maintainers = [ lib.maintainers.talyz ];
 }
diff --git a/nixos/modules/services/web-apps/discourse.xml b/nixos/modules/services/web-apps/discourse.xml
deleted file mode 100644
index a5e8b3656b7de..0000000000000
--- a/nixos/modules/services/web-apps/discourse.xml
+++ /dev/null
@@ -1,331 +0,0 @@
-<!-- Do not edit this file directly, edit its companion .md instead
-     and regenerate this file using nixos/doc/manual/md-to-db.sh -->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-discourse">
-  <title>Discourse</title>
-  <para>
-    <link xlink:href="https://www.discourse.org/">Discourse</link> is a
-    modern and open source discussion platform.
-  </para>
-  <section xml:id="module-services-discourse-basic-usage">
-    <title>Basic usage</title>
-    <para>
-      A minimal configuration using Let’s Encrypt for TLS certificates
-      looks like this:
-    </para>
-    <programlisting>
-services.discourse = {
-  enable = true;
-  hostname = &quot;discourse.example.com&quot;;
-  admin = {
-    email = &quot;admin@example.com&quot;;
-    username = &quot;admin&quot;;
-    fullName = &quot;Administrator&quot;;
-    passwordFile = &quot;/path/to/password_file&quot;;
-  };
-  secretKeyBaseFile = &quot;/path/to/secret_key_base_file&quot;;
-};
-security.acme.email = &quot;me@example.com&quot;;
-security.acme.acceptTerms = true;
-</programlisting>
-    <para>
-      Provided a proper DNS setup, you’ll be able to connect to the
-      instance at <literal>discourse.example.com</literal> and log in
-      using the credentials provided in
-      <literal>services.discourse.admin</literal>.
-    </para>
-  </section>
-  <section xml:id="module-services-discourse-tls">
-    <title>Using a regular TLS certificate</title>
-    <para>
-      To set up TLS using a regular certificate and key on file, use the
-      <xref linkend="opt-services.discourse.sslCertificate" /> and
-      <xref linkend="opt-services.discourse.sslCertificateKey" />
-      options:
-    </para>
-    <programlisting>
-services.discourse = {
-  enable = true;
-  hostname = &quot;discourse.example.com&quot;;
-  sslCertificate = &quot;/path/to/ssl_certificate&quot;;
-  sslCertificateKey = &quot;/path/to/ssl_certificate_key&quot;;
-  admin = {
-    email = &quot;admin@example.com&quot;;
-    username = &quot;admin&quot;;
-    fullName = &quot;Administrator&quot;;
-    passwordFile = &quot;/path/to/password_file&quot;;
-  };
-  secretKeyBaseFile = &quot;/path/to/secret_key_base_file&quot;;
-};
-</programlisting>
-  </section>
-  <section xml:id="module-services-discourse-database">
-    <title>Database access</title>
-    <para>
-      Discourse uses PostgreSQL to store most of its data. A database
-      will automatically be enabled and a database and role created
-      unless <xref linkend="opt-services.discourse.database.host" /> is
-      changed from its default of <literal>null</literal> or
-      <xref linkend="opt-services.discourse.database.createLocally" />
-      is set to <literal>false</literal>.
-    </para>
-    <para>
-      External database access can also be configured by setting
-      <xref linkend="opt-services.discourse.database.host" />,
-      <xref linkend="opt-services.discourse.database.username" /> and
-      <xref linkend="opt-services.discourse.database.passwordFile" /> as
-      appropriate. Note that you need to manually create a database
-      called <literal>discourse</literal> (or the name you chose in
-      <xref linkend="opt-services.discourse.database.name" />) and allow
-      the configured database user full access to it.
-    </para>
-  </section>
-  <section xml:id="module-services-discourse-mail">
-    <title>Email</title>
-    <para>
-      In addition to the basic setup, you’ll want to configure an SMTP
-      server Discourse can use to send user registration and password
-      reset emails, among others. You can also optionally let Discourse
-      receive email, which enables people to reply to threads and
-      conversations via email.
-    </para>
-    <para>
-      A basic setup which assumes you want to use your configured
-      <link linkend="opt-services.discourse.hostname">hostname</link> as
-      email domain can be done like this:
-    </para>
-    <programlisting>
-services.discourse = {
-  enable = true;
-  hostname = &quot;discourse.example.com&quot;;
-  sslCertificate = &quot;/path/to/ssl_certificate&quot;;
-  sslCertificateKey = &quot;/path/to/ssl_certificate_key&quot;;
-  admin = {
-    email = &quot;admin@example.com&quot;;
-    username = &quot;admin&quot;;
-    fullName = &quot;Administrator&quot;;
-    passwordFile = &quot;/path/to/password_file&quot;;
-  };
-  mail.outgoing = {
-    serverAddress = &quot;smtp.emailprovider.com&quot;;
-    port = 587;
-    username = &quot;user@emailprovider.com&quot;;
-    passwordFile = &quot;/path/to/smtp_password_file&quot;;
-  };
-  mail.incoming.enable = true;
-  secretKeyBaseFile = &quot;/path/to/secret_key_base_file&quot;;
-};
-</programlisting>
-    <para>
-      This assumes you have set up an MX record for the address you’ve
-      set in
-      <link linkend="opt-services.discourse.hostname">hostname</link>
-      and requires proper SPF, DKIM and DMARC configuration to be done
-      for the domain you’re sending from, in order for email to be
-      reliably delivered.
-    </para>
-    <para>
-      If you want to use a different domain for your outgoing email (for
-      example <literal>example.com</literal> instead of
-      <literal>discourse.example.com</literal>) you should set
-      <xref linkend="opt-services.discourse.mail.notificationEmailAddress" />
-      and
-      <xref linkend="opt-services.discourse.mail.contactEmailAddress" />
-      manually.
-    </para>
-    <note>
-      <para>
-        Setup of TLS for incoming email is currently only configured
-        automatically when a regular TLS certificate is used, i.e. when
-        <xref linkend="opt-services.discourse.sslCertificate" /> and
-        <xref linkend="opt-services.discourse.sslCertificateKey" /> are
-        set.
-      </para>
-    </note>
-  </section>
-  <section xml:id="module-services-discourse-settings">
-    <title>Additional settings</title>
-    <para>
-      Additional site settings and backend settings, for which no
-      explicit NixOS options are provided, can be set in
-      <xref linkend="opt-services.discourse.siteSettings" /> and
-      <xref linkend="opt-services.discourse.backendSettings" />
-      respectively.
-    </para>
-    <section xml:id="module-services-discourse-site-settings">
-      <title>Site settings</title>
-      <para>
-        <quote>Site settings</quote> are the settings that can be
-        changed through the Discourse UI. Their
-        <emphasis>default</emphasis> values can be set using
-        <xref linkend="opt-services.discourse.siteSettings" />.
-      </para>
-      <para>
-        Settings are expressed as a Nix attribute set which matches the
-        structure of the configuration in
-        <link xlink:href="https://github.com/discourse/discourse/blob/master/config/site_settings.yml">config/site_settings.yml</link>.
-        To find a setting’s path, you only need to care about the first
-        two levels; i.e. its category (e.g. <literal>login</literal>)
-        and name (e.g. <literal>invite_only</literal>).
-      </para>
-      <para>
-        Settings containing secret data should be set to an attribute
-        set containing the attribute <literal>_secret</literal> - a
-        string pointing to a file containing the value the option should
-        be set to. See the example.
-      </para>
-    </section>
-    <section xml:id="module-services-discourse-backend-settings">
-      <title>Backend settings</title>
-      <para>
-        Settings are expressed as a Nix attribute set which matches the
-        structure of the configuration in
-        <link xlink:href="https://github.com/discourse/discourse/blob/stable/config/discourse_defaults.conf">config/discourse.conf</link>.
-        Empty parameters can be defined by setting them to
-        <literal>null</literal>.
-      </para>
-    </section>
-    <section xml:id="module-services-discourse-settings-example">
-      <title>Example</title>
-      <para>
-        The following example sets the title and description of the
-        Discourse instance and enables GitHub login in the site
-        settings, and changes a few request limits in the backend
-        settings:
-      </para>
-      <programlisting>
-services.discourse = {
-  enable = true;
-  hostname = &quot;discourse.example.com&quot;;
-  sslCertificate = &quot;/path/to/ssl_certificate&quot;;
-  sslCertificateKey = &quot;/path/to/ssl_certificate_key&quot;;
-  admin = {
-    email = &quot;admin@example.com&quot;;
-    username = &quot;admin&quot;;
-    fullName = &quot;Administrator&quot;;
-    passwordFile = &quot;/path/to/password_file&quot;;
-  };
-  mail.outgoing = {
-    serverAddress = &quot;smtp.emailprovider.com&quot;;
-    port = 587;
-    username = &quot;user@emailprovider.com&quot;;
-    passwordFile = &quot;/path/to/smtp_password_file&quot;;
-  };
-  mail.incoming.enable = true;
-  siteSettings = {
-    required = {
-      title = &quot;My Cats&quot;;
-      site_description = &quot;Discuss My Cats (and be nice plz)&quot;;
-    };
-    login = {
-      enable_github_logins = true;
-      github_client_id = &quot;a2f6dfe838cb3206ce20&quot;;
-      github_client_secret._secret = /run/keys/discourse_github_client_secret;
-    };
-  };
-  backendSettings = {
-    max_reqs_per_ip_per_minute = 300;
-    max_reqs_per_ip_per_10_seconds = 60;
-    max_asset_reqs_per_ip_per_10_seconds = 250;
-    max_reqs_per_ip_mode = &quot;warn+block&quot;;
-  };
-  secretKeyBaseFile = &quot;/path/to/secret_key_base_file&quot;;
-};
-</programlisting>
-      <para>
-        In the resulting site settings file, the
-        <literal>login.github_client_secret</literal> key will be set to
-        the contents of the
-        <filename>/run/keys/discourse_github_client_secret</filename>
-        file.
-      </para>
-    </section>
-  </section>
-  <section xml:id="module-services-discourse-plugins">
-    <title>Plugins</title>
-    <para>
-      You can install Discourse plugins using the
-      <xref linkend="opt-services.discourse.plugins" /> option.
-      Pre-packaged plugins are provided in
-      <literal>&lt;your_discourse_package_here&gt;.plugins</literal>. If
-      you want the full suite of plugins provided through
-      <literal>nixpkgs</literal>, you can also set the
-      <xref linkend="opt-services.discourse.package" /> option to
-      <literal>pkgs.discourseAllPlugins</literal>.
-    </para>
-    <para>
-      Plugins can be built with the
-      <literal>&lt;your_discourse_package_here&gt;.mkDiscoursePlugin</literal>
-      function. Normally, it should suffice to provide a
-      <literal>name</literal> and <literal>src</literal> attribute. If
-      the plugin has Ruby dependencies, however, they need to be
-      packaged in accordance with the
-      <link xlink:href="https://nixos.org/manual/nixpkgs/stable/#developing-with-ruby">Developing
-      with Ruby</link> section of the Nixpkgs manual and the appropriate
-      gem options set in <literal>bundlerEnvArgs</literal> (normally
-      <literal>gemdir</literal> is sufficient). A plugin’s Ruby
-      dependencies are listed in its <filename>plugin.rb</filename> file
-      as function calls to <literal>gem</literal>. To construct the
-      corresponding <filename>Gemfile</filename> manually, run
-      <command>bundle init</command>, then add the
-      <literal>gem</literal> lines to it verbatim.
-    </para>
-    <para>
-      Much of the packaging can be done automatically by the
-      <filename>nixpkgs/pkgs/servers/web-apps/discourse/update.py</filename>
-      script - just add the plugin to the <literal>plugins</literal>
-      list in the <literal>update_plugins</literal> function and run the
-      script:
-    </para>
-    <programlisting language="bash">
-./update.py update-plugins
-</programlisting>
-    <para>
-      Some plugins provide
-      <link linkend="module-services-discourse-site-settings">site
-      settings</link>. Their defaults can be configured using
-      <xref linkend="opt-services.discourse.siteSettings" />, just like
-      regular site settings. To find the names of these settings, look
-      in the <literal>config/settings.yml</literal> file of the plugin
-      repo.
-    </para>
-    <para>
-      For example, to add the
-      <link xlink:href="https://github.com/discourse/discourse-spoiler-alert">discourse-spoiler-alert</link>
-      and
-      <link xlink:href="https://github.com/discourse/discourse-solved">discourse-solved</link>
-      plugins, and disable <literal>discourse-spoiler-alert</literal> by
-      default:
-    </para>
-    <programlisting>
-services.discourse = {
-  enable = true;
-  hostname = &quot;discourse.example.com&quot;;
-  sslCertificate = &quot;/path/to/ssl_certificate&quot;;
-  sslCertificateKey = &quot;/path/to/ssl_certificate_key&quot;;
-  admin = {
-    email = &quot;admin@example.com&quot;;
-    username = &quot;admin&quot;;
-    fullName = &quot;Administrator&quot;;
-    passwordFile = &quot;/path/to/password_file&quot;;
-  };
-  mail.outgoing = {
-    serverAddress = &quot;smtp.emailprovider.com&quot;;
-    port = 587;
-    username = &quot;user@emailprovider.com&quot;;
-    passwordFile = &quot;/path/to/smtp_password_file&quot;;
-  };
-  mail.incoming.enable = true;
-  plugins = with config.services.discourse.package.plugins; [
-    discourse-spoiler-alert
-    discourse-solved
-  ];
-  siteSettings = {
-    plugins = {
-      spoiler_enabled = false;
-    };
-  };
-  secretKeyBaseFile = &quot;/path/to/secret_key_base_file&quot;;
-};
-</programlisting>
-  </section>
-</chapter>
diff --git a/nixos/modules/services/web-apps/grocy.nix b/nixos/modules/services/web-apps/grocy.nix
index 6efc2ccfd3020..3bcda3caedace 100644
--- a/nixos/modules/services/web-apps/grocy.nix
+++ b/nixos/modules/services/web-apps/grocy.nix
@@ -167,6 +167,6 @@ in {
 
   meta = {
     maintainers = with maintainers; [ ma27 ];
-    doc = ./grocy.xml;
+    doc = ./grocy.md;
   };
 }
diff --git a/nixos/modules/services/web-apps/grocy.xml b/nixos/modules/services/web-apps/grocy.xml
deleted file mode 100644
index 08de25b4ce2b8..0000000000000
--- a/nixos/modules/services/web-apps/grocy.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<!-- Do not edit this file directly, edit its companion .md instead
-     and regenerate this file using nixos/doc/manual/md-to-db.sh -->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-grocy">
-  <title>Grocy</title>
-  <para>
-    <link xlink:href="https://grocy.info/">Grocy</link> is a web-based
-    self-hosted groceries &amp; household management solution for your
-    home.
-  </para>
-  <section xml:id="module-services-grocy-basic-usage">
-    <title>Basic usage</title>
-    <para>
-      A very basic configuration may look like this:
-    </para>
-    <programlisting>
-{ pkgs, ... }:
-{
-  services.grocy = {
-    enable = true;
-    hostName = &quot;grocy.tld&quot;;
-  };
-}
-</programlisting>
-    <para>
-      This configures a simple vhost using
-      <link linkend="opt-services.nginx.enable">nginx</link> which
-      listens to <literal>grocy.tld</literal> with fully configured
-      ACME/LE (this can be disabled by setting
-      <link linkend="opt-services.grocy.nginx.enableSSL">services.grocy.nginx.enableSSL</link>
-      to <literal>false</literal>). After the initial setup the
-      credentials <literal>admin:admin</literal> can be used to login.
-    </para>
-    <para>
-      The application’s state is persisted at
-      <literal>/var/lib/grocy/grocy.db</literal> in a
-      <literal>sqlite3</literal> database. The migration is applied when
-      requesting the <literal>/</literal>-route of the application.
-    </para>
-  </section>
-  <section xml:id="module-services-grocy-settings">
-    <title>Settings</title>
-    <para>
-      The configuration for <literal>grocy</literal> is located at
-      <literal>/etc/grocy/config.php</literal>. By default, the
-      following settings can be defined in the NixOS-configuration:
-    </para>
-    <programlisting>
-{ pkgs, ... }:
-{
-  services.grocy.settings = {
-    # The default currency in the system for invoices etc.
-    # Please note that exchange rates aren't taken into account, this
-    # is just the setting for what's shown in the frontend.
-    currency = &quot;EUR&quot;;
-
-    # The display language (and locale configuration) for grocy.
-    culture = &quot;de&quot;;
-
-    calendar = {
-      # Whether or not to show the week-numbers
-      # in the calendar.
-      showWeekNumber = true;
-
-      # Index of the first day to be shown in the calendar (0=Sunday, 1=Monday,
-      # 2=Tuesday and so on).
-      firstDayOfWeek = 2;
-    };
-  };
-}
-</programlisting>
-    <para>
-      If you want to alter the configuration file on your own, you can
-      do this manually with an expression like this:
-    </para>
-    <programlisting>
-{ lib, ... }:
-{
-  environment.etc.&quot;grocy/config.php&quot;.text = lib.mkAfter ''
-    // Arbitrary PHP code in grocy's configuration file
-  '';
-}
-</programlisting>
-  </section>
-</chapter>
diff --git a/nixos/modules/services/web-apps/jitsi-meet.nix b/nixos/modules/services/web-apps/jitsi-meet.nix
index 5b0934b2fb76f..28be3a3702eb6 100644
--- a/nixos/modules/services/web-apps/jitsi-meet.nix
+++ b/nixos/modules/services/web-apps/jitsi-meet.nix
@@ -451,6 +451,6 @@ in
     };
   };
 
-  meta.doc = ./jitsi-meet.xml;
+  meta.doc = ./jitsi-meet.md;
   meta.maintainers = lib.teams.jitsi.members;
 }
diff --git a/nixos/modules/services/web-apps/jitsi-meet.xml b/nixos/modules/services/web-apps/jitsi-meet.xml
deleted file mode 100644
index 4d2d8aa55e19f..0000000000000
--- a/nixos/modules/services/web-apps/jitsi-meet.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<!-- Do not edit this file directly, edit its companion .md instead
-     and regenerate this file using nixos/doc/manual/md-to-db.sh -->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-jitsi-meet">
-  <title>Jitsi Meet</title>
-  <para>
-    With Jitsi Meet on NixOS you can quickly configure a complete,
-    private, self-hosted video conferencing solution.
-  </para>
-  <section xml:id="module-services-jitsi-basic-usage">
-    <title>Basic usage</title>
-    <para>
-      A minimal configuration using Let’s Encrypt for TLS certificates
-      looks like this:
-    </para>
-    <programlisting>
-{
-  services.jitsi-meet = {
-    enable = true;
-    hostName = &quot;jitsi.example.com&quot;;
-  };
-  services.jitsi-videobridge.openFirewall = true;
-  networking.firewall.allowedTCPPorts = [ 80 443 ];
-  security.acme.email = &quot;me@example.com&quot;;
-  security.acme.acceptTerms = true;
-}
-</programlisting>
-  </section>
-  <section xml:id="module-services-jitsi-configuration">
-    <title>Configuration</title>
-    <para>
-      Here is the minimal configuration with additional configurations:
-    </para>
-    <programlisting>
-{
-  services.jitsi-meet = {
-    enable = true;
-    hostName = &quot;jitsi.example.com&quot;;
-    config = {
-      enableWelcomePage = false;
-      prejoinPageEnabled = true;
-      defaultLang = &quot;fi&quot;;
-    };
-    interfaceConfig = {
-      SHOW_JITSI_WATERMARK = false;
-      SHOW_WATERMARK_FOR_GUESTS = false;
-    };
-  };
-  services.jitsi-videobridge.openFirewall = true;
-  networking.firewall.allowedTCPPorts = [ 80 443 ];
-  security.acme.email = &quot;me@example.com&quot;;
-  security.acme.acceptTerms = true;
-}
-</programlisting>
-  </section>
-</chapter>
diff --git a/nixos/modules/services/web-apps/keycloak.nix b/nixos/modules/services/web-apps/keycloak.nix
index d52190a28648e..a7e4fab8ea287 100644
--- a/nixos/modules/services/web-apps/keycloak.nix
+++ b/nixos/modules/services/web-apps/keycloak.nix
@@ -674,6 +674,6 @@ in
           mkIf createLocalMySQL (mkDefault dbPkg);
       };
 
-  meta.doc = ./keycloak.xml;
+  meta.doc = ./keycloak.md;
   meta.maintainers = [ maintainers.talyz ];
 }
diff --git a/nixos/modules/services/web-apps/keycloak.xml b/nixos/modules/services/web-apps/keycloak.xml
deleted file mode 100644
index 148782d30f39e..0000000000000
--- a/nixos/modules/services/web-apps/keycloak.xml
+++ /dev/null
@@ -1,177 +0,0 @@
-<!-- Do not edit this file directly, edit its companion .md instead
-     and regenerate this file using nixos/doc/manual/md-to-db.sh -->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-keycloak">
-  <title>Keycloak</title>
-  <para>
-    <link xlink:href="https://www.keycloak.org/">Keycloak</link> is an
-    open source identity and access management server with support for
-    <link xlink:href="https://openid.net/connect/">OpenID
-    Connect</link>, <link xlink:href="https://oauth.net/2/">OAUTH
-    2.0</link> and
-    <link xlink:href="https://en.wikipedia.org/wiki/SAML_2.0">SAML
-    2.0</link>.
-  </para>
-  <section xml:id="module-services-keycloak-admin">
-    <title>Administration</title>
-    <para>
-      An administrative user with the username <literal>admin</literal>
-      is automatically created in the <literal>master</literal> realm.
-      Its initial password can be configured by setting
-      <xref linkend="opt-services.keycloak.initialAdminPassword" /> and
-      defaults to <literal>changeme</literal>. The password is not
-      stored safely and should be changed immediately in the admin
-      panel.
-    </para>
-    <para>
-      Refer to the
-      <link xlink:href="https://www.keycloak.org/docs/latest/server_admin/index.html">Keycloak
-      Server Administration Guide</link> for information on how to
-      administer your Keycloak instance.
-    </para>
-  </section>
-  <section xml:id="module-services-keycloak-database">
-    <title>Database access</title>
-    <para>
-      Keycloak can be used with either PostgreSQL, MariaDB or MySQL.
-      Which one is used can be configured in
-      <xref linkend="opt-services.keycloak.database.type" />. The
-      selected database will automatically be enabled and a database and
-      role created unless
-      <xref linkend="opt-services.keycloak.database.host" /> is changed
-      from its default of <literal>localhost</literal> or
-      <xref linkend="opt-services.keycloak.database.createLocally" /> is
-      set to <literal>false</literal>.
-    </para>
-    <para>
-      External database access can also be configured by setting
-      <xref linkend="opt-services.keycloak.database.host" />,
-      <xref linkend="opt-services.keycloak.database.name" />,
-      <xref linkend="opt-services.keycloak.database.username" />,
-      <xref linkend="opt-services.keycloak.database.useSSL" /> and
-      <xref linkend="opt-services.keycloak.database.caCert" /> as
-      appropriate. Note that you need to manually create the database
-      and allow the configured database user full access to it.
-    </para>
-    <para>
-      <xref linkend="opt-services.keycloak.database.passwordFile" />
-      must be set to the path to a file containing the password used to
-      log in to the database. If
-      <xref linkend="opt-services.keycloak.database.host" /> and
-      <xref linkend="opt-services.keycloak.database.createLocally" />
-      are kept at their defaults, the database role
-      <literal>keycloak</literal> with that password is provisioned on
-      the local database instance.
-    </para>
-    <warning>
-      <para>
-        The path should be provided as a string, not a Nix path, since
-        Nix paths are copied into the world readable Nix store.
-      </para>
-    </warning>
-  </section>
-  <section xml:id="module-services-keycloak-hostname">
-    <title>Hostname</title>
-    <para>
-      The hostname is used to build the public URL used as base for all
-      frontend requests and must be configured through
-      <xref linkend="opt-services.keycloak.settings.hostname" />.
-    </para>
-    <note>
-      <para>
-        If you’re migrating an old Wildfly based Keycloak instance and
-        want to keep compatibility with your current clients, you’ll
-        likely want to set
-        <xref linkend="opt-services.keycloak.settings.http-relative-path" />
-        to <literal>/auth</literal>. See the option description for more
-        details.
-      </para>
-    </note>
-    <para>
-      <xref linkend="opt-services.keycloak.settings.hostname-strict-backchannel" />
-      determines 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 IP address and may
-      also advertise it to clients through its OpenID Connect Discovery
-      endpoint.
-    </para>
-    <para>
-      For more information on hostname configuration, see the
-      <link xlink:href="https://www.keycloak.org/server/hostname">Hostname
-      section of the Keycloak Server Installation and Configuration
-      Guide</link>.
-    </para>
-  </section>
-  <section xml:id="module-services-keycloak-tls">
-    <title>Setting up TLS/SSL</title>
-    <para>
-      By default, Keycloak won’t accept unsecured HTTP connections
-      originating from outside its local network.
-    </para>
-    <para>
-      HTTPS support requires a TLS/SSL certificate and a private key,
-      both
-      <link xlink:href="https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail">PEM
-      formatted</link>. Their paths should be set through
-      <xref linkend="opt-services.keycloak.sslCertificate" /> and
-      <xref linkend="opt-services.keycloak.sslCertificateKey" />.
-    </para>
-    <warning>
-      <para>
-        The paths should be provided as a strings, not a Nix paths,
-        since Nix paths are copied into the world readable Nix store.
-      </para>
-    </warning>
-  </section>
-  <section xml:id="module-services-keycloak-themes">
-    <title>Themes</title>
-    <para>
-      You can package custom themes and make them visible to Keycloak
-      through <xref linkend="opt-services.keycloak.themes" />. See the
-      <link xlink:href="https://www.keycloak.org/docs/latest/server_development/#_themes">Themes
-      section of the Keycloak Server Development Guide</link> and the
-      description of the aforementioned NixOS option for more
-      information.
-    </para>
-  </section>
-  <section xml:id="module-services-keycloak-settings">
-    <title>Configuration file settings</title>
-    <para>
-      Keycloak server configuration parameters can be set in
-      <xref linkend="opt-services.keycloak.settings" />. These
-      correspond directly to options in
-      <filename>conf/keycloak.conf</filename>. Some of the most
-      important parameters are documented as suboptions, the rest can be
-      found in the
-      <link xlink:href="https://www.keycloak.org/server/all-config">All
-      configuration section of the Keycloak Server Installation and
-      Configuration Guide</link>.
-    </para>
-    <para>
-      Options containing secret data should be set to an attribute set
-      containing the attribute <literal>_secret</literal> - a string
-      pointing to a file containing the value the option should be set
-      to. See the description of
-      <xref linkend="opt-services.keycloak.settings" /> for an example.
-    </para>
-  </section>
-  <section xml:id="module-services-keycloak-example-config">
-    <title>Example configuration</title>
-    <para>
-      A basic configuration with some custom settings could look like
-      this:
-    </para>
-    <programlisting>
-services.keycloak = {
-  enable = true;
-  settings = {
-    hostname = &quot;keycloak.example.com&quot;;
-    hostname-strict-backchannel = true;
-  };
-  initialAdminPassword = &quot;e6Wcm0RrtegMEHl&quot;;  # change on first login
-  sslCertificate = &quot;/run/keys/ssl_cert&quot;;
-  sslCertificateKey = &quot;/run/keys/ssl_key&quot;;
-  database.passwordFile = &quot;/run/keys/db_password&quot;;
-};
-</programlisting>
-  </section>
-</chapter>
diff --git a/nixos/modules/services/web-apps/lemmy.nix b/nixos/modules/services/web-apps/lemmy.nix
index f2eb6e726b903..af0fb38121a34 100644
--- a/nixos/modules/services/web-apps/lemmy.nix
+++ b/nixos/modules/services/web-apps/lemmy.nix
@@ -6,7 +6,7 @@ let
 in
 {
   meta.maintainers = with maintainers; [ happysalada ];
-  meta.doc = ./lemmy.xml;
+  meta.doc = ./lemmy.md;
 
   imports = [
     (mkRemovedOptionModule [ "services" "lemmy" "jwtSecretPath" ] "As of v0.13.0, Lemmy auto-generates the JWT secret.")
diff --git a/nixos/modules/services/web-apps/lemmy.xml b/nixos/modules/services/web-apps/lemmy.xml
deleted file mode 100644
index 114e11f3488ad..0000000000000
--- a/nixos/modules/services/web-apps/lemmy.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<!-- Do not edit this file directly, edit its companion .md instead
-     and regenerate this file using nixos/doc/manual/md-to-db.sh -->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-lemmy">
-  <title>Lemmy</title>
-  <para>
-    Lemmy is a federated alternative to reddit in rust.
-  </para>
-  <section xml:id="module-services-lemmy-quickstart">
-    <title>Quickstart</title>
-    <para>
-      the minimum to start lemmy is
-    </para>
-    <programlisting language="nix">
-services.lemmy = {
-  enable = true;
-  settings = {
-    hostname = &quot;lemmy.union.rocks&quot;;
-    database.createLocally = true;
-  };
-  caddy.enable = true;
-}
-</programlisting>
-    <para>
-      this will start the backend on port 8536 and the frontend on port
-      1234. It will expose your instance with a caddy reverse proxy to
-      the hostname you’ve provided. Postgres will be initialized on that
-      same instance automatically.
-    </para>
-  </section>
-  <section xml:id="module-services-lemmy-usage">
-    <title>Usage</title>
-    <para>
-      On first connection you will be asked to define an admin user.
-    </para>
-  </section>
-  <section xml:id="module-services-lemmy-missing">
-    <title>Missing</title>
-    <itemizedlist spacing="compact">
-      <listitem>
-        <para>
-          Exposing with nginx is not implemented yet.
-        </para>
-      </listitem>
-      <listitem>
-        <para>
-          This has been tested using a local database with a unix socket
-          connection. Using different database settings will likely
-          require modifications
-        </para>
-      </listitem>
-    </itemizedlist>
-  </section>
-</chapter>
diff --git a/nixos/modules/services/web-apps/matomo.nix b/nixos/modules/services/web-apps/matomo.nix
index 9845106599527..eadf8b62b9779 100644
--- a/nixos/modules/services/web-apps/matomo.nix
+++ b/nixos/modules/services/web-apps/matomo.nix
@@ -325,7 +325,7 @@ in {
   };
 
   meta = {
-    doc = ./matomo.xml;
+    doc = ./matomo.md;
     maintainers = with lib.maintainers; [ florianjacob ];
   };
 }
diff --git a/nixos/modules/services/web-apps/matomo.xml b/nixos/modules/services/web-apps/matomo.xml
deleted file mode 100644
index 30994cc9f1dad..0000000000000
--- a/nixos/modules/services/web-apps/matomo.xml
+++ /dev/null
@@ -1,107 +0,0 @@
-<!-- Do not edit this file directly, edit its companion .md instead
-     and regenerate this file using nixos/doc/manual/md-to-db.sh -->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-matomo">
-  <title>Matomo</title>
-  <para>
-    Matomo is a real-time web analytics application. This module
-    configures php-fpm as backend for Matomo, optionally configuring an
-    nginx vhost as well.
-  </para>
-  <para>
-    An automatic setup is not suported by Matomo, so you need to
-    configure Matomo itself in the browser-based Matomo setup.
-  </para>
-  <section xml:id="module-services-matomo-database-setup">
-    <title>Database Setup</title>
-    <para>
-      You also need to configure a MariaDB or MySQL database and -user
-      for Matomo yourself, and enter those credentials in your browser.
-      You can use passwordless database authentication via the
-      UNIX_SOCKET authentication plugin with the following SQL commands:
-    </para>
-    <programlisting>
-# For MariaDB
-INSTALL PLUGIN unix_socket SONAME 'auth_socket';
-CREATE DATABASE matomo;
-CREATE USER 'matomo'@'localhost' IDENTIFIED WITH unix_socket;
-GRANT ALL PRIVILEGES ON matomo.* TO 'matomo'@'localhost';
-
-# For MySQL
-INSTALL PLUGIN auth_socket SONAME 'auth_socket.so';
-CREATE DATABASE matomo;
-CREATE USER 'matomo'@'localhost' IDENTIFIED WITH auth_socket;
-GRANT ALL PRIVILEGES ON matomo.* TO 'matomo'@'localhost';
-</programlisting>
-    <para>
-      Then fill in <literal>matomo</literal> as database user and
-      database name, and leave the password field blank. This
-      authentication works by allowing only the
-      <literal>matomo</literal> unix user to authenticate as the
-      <literal>matomo</literal> database user (without needing a
-      password), but no other users. For more information on
-      passwordless login, see
-      <link xlink:href="https://mariadb.com/kb/en/mariadb/unix_socket-authentication-plugin/">https://mariadb.com/kb/en/mariadb/unix_socket-authentication-plugin/</link>.
-    </para>
-    <para>
-      Of course, you can use password based authentication as well, e.g.
-      when the database is not on the same host.
-    </para>
-  </section>
-  <section xml:id="module-services-matomo-archive-processing">
-    <title>Archive Processing</title>
-    <para>
-      This module comes with the systemd service
-      <literal>matomo-archive-processing.service</literal> and a timer
-      that automatically triggers archive processing every hour. This
-      means that you can safely
-      <link xlink:href="https://matomo.org/docs/setup-auto-archiving/#disable-browser-triggers-for-matomo-archiving-and-limit-matomo-reports-to-updating-every-hour">disable
-      browser triggers for Matomo archiving</link> at
-      <literal>Administration &gt; System &gt; General Settings</literal>.
-    </para>
-    <para>
-      With automatic archive processing, you can now also enable to
-      <link xlink:href="https://matomo.org/docs/privacy/#step-2-delete-old-visitors-logs">delete
-      old visitor logs</link> at
-      <literal>Administration &gt; System &gt; Privacy</literal>, but
-      make sure that you run
-      <literal>systemctl start matomo-archive-processing.service</literal>
-      at least once without errors if you have already collected data
-      before, so that the reports get archived before the source data
-      gets deleted.
-    </para>
-  </section>
-  <section xml:id="module-services-matomo-backups">
-    <title>Backup</title>
-    <para>
-      You only need to take backups of your MySQL database and the
-      <filename>/var/lib/matomo/config/config.ini.php</filename> file.
-      Use a user in the <literal>matomo</literal> group or root to
-      access the file. For more information, see
-      <link xlink:href="https://matomo.org/faq/how-to-install/faq_138/">https://matomo.org/faq/how-to-install/faq_138/</link>.
-    </para>
-  </section>
-  <section xml:id="module-services-matomo-issues">
-    <title>Issues</title>
-    <itemizedlist spacing="compact">
-      <listitem>
-        <para>
-          Matomo will warn you that the JavaScript tracker is not
-          writable. This is because it’s located in the read-only nix
-          store. You can safely ignore this, unless you need a plugin
-          that needs JavaScript tracker access.
-        </para>
-      </listitem>
-    </itemizedlist>
-  </section>
-  <section xml:id="module-services-matomo-other-web-servers">
-    <title>Using other Web Servers than nginx</title>
-    <para>
-      You can use other web servers by forwarding calls for
-      <filename>index.php</filename> and <filename>piwik.php</filename>
-      to the
-      <link linkend="opt-services.phpfpm.pools._name_.socket"><literal>services.phpfpm.pools.&lt;name&gt;.socket</literal></link>
-      fastcgi unix socket. You can use the nginx configuration in the
-      module code as a reference to what else should be configured.
-    </para>
-  </section>
-</chapter>
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index 90801e9968175..50c2d68c77e48 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -1146,5 +1146,5 @@ in {
     }
   ]);
 
-  meta.doc = ./nextcloud.xml;
+  meta.doc = ./nextcloud.md;
 }
diff --git a/nixos/modules/services/web-apps/nextcloud.xml b/nixos/modules/services/web-apps/nextcloud.xml
deleted file mode 100644
index a5ac05723ef47..0000000000000
--- a/nixos/modules/services/web-apps/nextcloud.xml
+++ /dev/null
@@ -1,333 +0,0 @@
-<!-- Do not edit this file directly, edit its companion .md instead
-     and regenerate this file using nixos/doc/manual/md-to-db.sh -->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-nextcloud">
-  <title>Nextcloud</title>
-  <para>
-    <link xlink:href="https://nextcloud.com/">Nextcloud</link> is an
-    open-source, self-hostable cloud platform. The server setup can be
-    automated using
-    <link linkend="opt-services.nextcloud.enable">services.nextcloud</link>.
-    A desktop client is packaged at
-    <literal>pkgs.nextcloud-client</literal>.
-  </para>
-  <para>
-    The current default by NixOS is <literal>nextcloud25</literal> which
-    is also the latest major version available.
-  </para>
-  <section xml:id="module-services-nextcloud-basic-usage">
-    <title>Basic usage</title>
-    <para>
-      Nextcloud is a PHP-based application which requires an HTTP server
-      (<link linkend="opt-services.nextcloud.enable"><literal>services.nextcloud</literal></link>
-      optionally supports
-      <link linkend="opt-services.nginx.enable"><literal>services.nginx</literal></link>)
-      and a database (it’s recommended to use
-      <link linkend="opt-services.postgresql.enable"><literal>services.postgresql</literal></link>).
-    </para>
-    <para>
-      A very basic configuration may look like this:
-    </para>
-    <programlisting>
-{ pkgs, ... }:
-{
-  services.nextcloud = {
-    enable = true;
-    hostName = &quot;nextcloud.tld&quot;;
-    config = {
-      dbtype = &quot;pgsql&quot;;
-      dbuser = &quot;nextcloud&quot;;
-      dbhost = &quot;/run/postgresql&quot;; # nextcloud will add /.s.PGSQL.5432 by itself
-      dbname = &quot;nextcloud&quot;;
-      adminpassFile = &quot;/path/to/admin-pass-file&quot;;
-      adminuser = &quot;root&quot;;
-    };
-  };
-
-  services.postgresql = {
-    enable = true;
-    ensureDatabases = [ &quot;nextcloud&quot; ];
-    ensureUsers = [
-     { name = &quot;nextcloud&quot;;
-       ensurePermissions.&quot;DATABASE nextcloud&quot; = &quot;ALL PRIVILEGES&quot;;
-     }
-    ];
-  };
-
-  # ensure that postgres is running *before* running the setup
-  systemd.services.&quot;nextcloud-setup&quot; = {
-    requires = [&quot;postgresql.service&quot;];
-    after = [&quot;postgresql.service&quot;];
-  };
-
-  networking.firewall.allowedTCPPorts = [ 80 443 ];
-}
-</programlisting>
-    <para>
-      The <literal>hostName</literal> option is used internally to
-      configure an HTTP server using
-      <link xlink:href="https://php-fpm.org/"><literal>PHP-FPM</literal></link>
-      and <literal>nginx</literal>. The <literal>config</literal>
-      attribute set is used by the imperative installer and all values
-      are written to an additional file to ensure that changes can be
-      applied by changing the module’s options.
-    </para>
-    <para>
-      In case the application serves multiple domains (those are checked
-      with
-      <link xlink:href="http://php.net/manual/en/reserved.variables.server.php"><literal>$_SERVER['HTTP_HOST']</literal></link>)
-      it’s needed to add them to
-      <link linkend="opt-services.nextcloud.config.extraTrustedDomains"><literal>services.nextcloud.config.extraTrustedDomains</literal></link>.
-    </para>
-    <para>
-      Auto updates for Nextcloud apps can be enabled using
-      <link linkend="opt-services.nextcloud.autoUpdateApps.enable"><literal>services.nextcloud.autoUpdateApps</literal></link>.
-    </para>
-  </section>
-  <section xml:id="module-services-nextcloud-pitfalls-during-upgrade">
-    <title>Common problems</title>
-    <itemizedlist>
-      <listitem>
-        <para>
-          <emphasis role="strong">General notes.</emphasis>
-          Unfortunately Nextcloud appears to be very stateful when it
-          comes to managing its own configuration. The config file lives
-          in the home directory of the <literal>nextcloud</literal> user
-          (by default
-          <literal>/var/lib/nextcloud/config/config.php</literal>) and
-          is also used to track several states of the application (e.g.,
-          whether installed or not).
-        </para>
-        <para>
-          All configuration parameters are also stored in
-          <filename>/var/lib/nextcloud/config/override.config.php</filename>
-          which is generated by the module and linked from the store to
-          ensure that all values from <filename>config.php</filename>
-          can be modified by the module. However
-          <filename>config.php</filename> manages the application’s
-          state and shouldn’t be touched manually because of that.
-        </para>
-        <warning>
-          <para>
-            Don’t delete <filename>config.php</filename>! This file
-            tracks the application’s state and a deletion can cause
-            unwanted side-effects!
-          </para>
-        </warning>
-        <warning>
-          <para>
-            Don’t rerun
-            <literal>nextcloud-occ maintenance:install</literal>! This
-            command tries to install the application and can cause
-            unwanted side-effects!
-          </para>
-        </warning>
-      </listitem>
-      <listitem>
-        <para>
-          <emphasis role="strong">Multiple version upgrades.</emphasis>
-          Nextcloud doesn’t allow to move more than one major-version
-          forward. E.g., if you’re on <literal>v16</literal>, you cannot
-          upgrade to <literal>v18</literal>, you need to upgrade to
-          <literal>v17</literal> first. This is ensured automatically as
-          long as the
-          <link linkend="opt-system.stateVersion">stateVersion</link> is
-          declared properly. In that case the oldest version available
-          (one major behind the one from the previous NixOS release)
-          will be selected by default and the module will generate a
-          warning that reminds the user to upgrade to latest Nextcloud
-          <emphasis>after</emphasis> that deploy.
-        </para>
-      </listitem>
-      <listitem>
-        <para>
-          <emphasis role="strong"><literal>Error: Command &quot;upgrade&quot; is not defined.</literal></emphasis>
-          This error usually occurs if the initial installation
-          (<command>nextcloud-occ maintenance:install</command>) has
-          failed. After that, the application is not installed, but the
-          upgrade is attempted to be executed. Further context can be
-          found in
-          <link xlink:href="https://github.com/NixOS/nixpkgs/issues/111175">NixOS/nixpkgs#111175</link>.
-        </para>
-        <para>
-          First of all, it makes sense to find out what went wrong by
-          looking at the logs of the installation via
-          <command>journalctl -u nextcloud-setup</command> and try to
-          fix the underlying issue.
-        </para>
-        <itemizedlist>
-          <listitem>
-            <para>
-              If this occurs on an <emphasis>existing</emphasis> setup,
-              this is most likely because the maintenance mode is
-              active. It can be deactivated by running
-              <command>nextcloud-occ maintenance:mode --off</command>.
-              It’s advisable though to check the logs first on why the
-              maintenance mode was activated.
-            </para>
-          </listitem>
-          <listitem>
-            <warning>
-              <para>
-                Only perform the following measures on <emphasis>freshly
-                installed instances!</emphasis>
-              </para>
-            </warning>
-            <para>
-              A re-run of the installer can be forced by
-              <emphasis>deleting</emphasis>
-              <filename>/var/lib/nextcloud/config/config.php</filename>.
-              This is the only time advisable because the fresh install
-              doesn’t have any state that can be lost. In case that
-              doesn’t help, an entire re-creation can be forced via
-              <command>rm -rf ~nextcloud/</command>.
-            </para>
-          </listitem>
-        </itemizedlist>
-      </listitem>
-      <listitem>
-        <para>
-          <emphasis role="strong">Server-side encryption.</emphasis>
-          Nextcloud supports
-          <link xlink:href="https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html">server-side
-          encryption (SSE)</link>. This is not an end-to-end encryption,
-          but can be used to encrypt files that will be persisted to
-          external storage such as S3. Please note that this won’t work
-          anymore when using OpenSSL 3 for PHP’s openssl extension
-          because this is implemented using the legacy cipher RC4. If
-          <xref linkend="opt-system.stateVersion" /> is
-          <emphasis>above</emphasis> <literal>22.05</literal>, this is
-          disabled by default. To turn it on again and for further
-          information please refer to
-          <xref linkend="opt-services.nextcloud.enableBrokenCiphersForSSE" />.
-        </para>
-      </listitem>
-    </itemizedlist>
-  </section>
-  <section xml:id="module-services-nextcloud-httpd">
-    <title>Using an alternative webserver as reverse-proxy (e.g.
-    <literal>httpd</literal>)</title>
-    <para>
-      By default, <literal>nginx</literal> is used as reverse-proxy for
-      <literal>nextcloud</literal>. However, it’s possible to use e.g.
-      <literal>httpd</literal> by explicitly disabling
-      <literal>nginx</literal> using
-      <xref linkend="opt-services.nginx.enable" /> and fixing the
-      settings <literal>listen.owner</literal> &amp;
-      <literal>listen.group</literal> in the
-      <link linkend="opt-services.phpfpm.pools">corresponding
-      <literal>phpfpm</literal> pool</link>.
-    </para>
-    <para>
-      An exemplary configuration may look like this:
-    </para>
-    <programlisting>
-{ config, lib, pkgs, ... }: {
-  services.nginx.enable = false;
-  services.nextcloud = {
-    enable = true;
-    hostName = &quot;localhost&quot;;
-
-    /* further, required options */
-  };
-  services.phpfpm.pools.nextcloud.settings = {
-    &quot;listen.owner&quot; = config.services.httpd.user;
-    &quot;listen.group&quot; = config.services.httpd.group;
-  };
-  services.httpd = {
-    enable = true;
-    adminAddr = &quot;webmaster@localhost&quot;;
-    extraModules = [ &quot;proxy_fcgi&quot; ];
-    virtualHosts.&quot;localhost&quot; = {
-      documentRoot = config.services.nextcloud.package;
-      extraConfig = ''
-        &lt;Directory &quot;${config.services.nextcloud.package}&quot;&gt;
-          &lt;FilesMatch &quot;\.php$&quot;&gt;
-            &lt;If &quot;-f %{REQUEST_FILENAME}&quot;&gt;
-              SetHandler &quot;proxy:unix:${config.services.phpfpm.pools.nextcloud.socket}|fcgi://localhost/&quot;
-            &lt;/If&gt;
-          &lt;/FilesMatch&gt;
-          &lt;IfModule mod_rewrite.c&gt;
-            RewriteEngine On
-            RewriteBase /
-            RewriteRule ^index\.php$ - [L]
-            RewriteCond %{REQUEST_FILENAME} !-f
-            RewriteCond %{REQUEST_FILENAME} !-d
-            RewriteRule . /index.php [L]
-          &lt;/IfModule&gt;
-          DirectoryIndex index.php
-          Require all granted
-          Options +FollowSymLinks
-        &lt;/Directory&gt;
-      '';
-    };
-  };
-}
-</programlisting>
-  </section>
-  <section xml:id="installing-apps-php-extensions-nextcloud">
-    <title>Installing Apps and PHP extensions</title>
-    <para>
-      Nextcloud apps are installed statefully through the web interface.
-      Some apps may require extra PHP extensions to be installed. This
-      can be configured with the
-      <xref linkend="opt-services.nextcloud.phpExtraExtensions" />
-      setting.
-    </para>
-    <para>
-      Alternatively, extra apps can also be declared with the
-      <xref linkend="opt-services.nextcloud.extraApps" /> setting. 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.
-    </para>
-  </section>
-  <section xml:id="module-services-nextcloud-maintainer-info">
-    <title>Maintainer information</title>
-    <para>
-      As stated in the previous paragraph, we must provide a clean
-      upgrade-path for Nextcloud since it cannot move more than one
-      major version forward on a single upgrade. This chapter adds some
-      notes how Nextcloud updates should be rolled out in the future.
-    </para>
-    <para>
-      While minor and patch-level updates are no problem and can be done
-      directly in the package-expression (and should be backported to
-      supported stable branches after that), major-releases should be
-      added in a new attribute (e.g. Nextcloud
-      <literal>v19.0.0</literal> should be available in
-      <literal>nixpkgs</literal> as
-      <literal>pkgs.nextcloud19</literal>). To provide simple upgrade
-      paths it’s generally useful to backport those as well to stable
-      branches. As long as the package-default isn’t altered, this won’t
-      break existing setups. After that, the versioning-warning in the
-      <literal>nextcloud</literal>-module should be updated to make sure
-      that the
-      <link linkend="opt-services.nextcloud.package">package</link>-option
-      selects the latest version on fresh setups.
-    </para>
-    <para>
-      If major-releases will be abandoned by upstream, we should check
-      first if those are needed in NixOS for a safe upgrade-path before
-      removing those. In that case we should keep those packages, but
-      mark them as insecure in an expression like this (in
-      <literal>&lt;nixpkgs/pkgs/servers/nextcloud/default.nix&gt;</literal>):
-    </para>
-    <programlisting>
-/* ... */
-{
-  nextcloud17 = generic {
-    version = &quot;17.0.x&quot;;
-    sha256 = &quot;0000000000000000000000000000000000000000000000000000&quot;;
-    eol = true;
-  };
-}
-</programlisting>
-    <para>
-      Ideally we should make sure that it’s possible to jump two NixOS
-      versions forward: i.e. the warnings and the logic in the module
-      should guard a user to upgrade from a Nextcloud on e.g. 19.09 to a
-      Nextcloud on 20.09.
-    </para>
-  </section>
-</chapter>
diff --git a/nixos/modules/services/web-apps/pict-rs.nix b/nixos/modules/services/web-apps/pict-rs.nix
index ad07507ca37db..0f13b2ae6db13 100644
--- a/nixos/modules/services/web-apps/pict-rs.nix
+++ b/nixos/modules/services/web-apps/pict-rs.nix
@@ -5,7 +5,7 @@ let
 in
 {
   meta.maintainers = with maintainers; [ happysalada ];
-  meta.doc = ./pict-rs.xml;
+  meta.doc = ./pict-rs.md;
 
   options.services.pict-rs = {
     enable = mkEnableOption (lib.mdDoc "pict-rs server");
diff --git a/nixos/modules/services/web-apps/pict-rs.xml b/nixos/modules/services/web-apps/pict-rs.xml
deleted file mode 100644
index 3f5900c55f151..0000000000000
--- a/nixos/modules/services/web-apps/pict-rs.xml
+++ /dev/null
@@ -1,185 +0,0 @@
-<!-- Do not edit this file directly, edit its companion .md instead
-     and regenerate this file using nixos/doc/manual/md-to-db.sh -->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-pict-rs">
-  <title>Pict-rs</title>
-  <para>
-    pict-rs is a a simple image hosting service.
-  </para>
-  <section xml:id="module-services-pict-rs-quickstart">
-    <title>Quickstart</title>
-    <para>
-      the minimum to start pict-rs is
-    </para>
-    <programlisting language="nix">
-services.pict-rs.enable = true;
-</programlisting>
-    <para>
-      this will start the http server on port 8080 by default.
-    </para>
-  </section>
-  <section xml:id="module-services-pict-rs-usage">
-    <title>Usage</title>
-    <para>
-      pict-rs offers the following endpoints:
-    </para>
-    <itemizedlist>
-      <listitem>
-        <para>
-          <literal>POST /image</literal> for uploading an image.
-          Uploaded content must be valid multipart/form-data with an
-          image array located within the <literal>images[]</literal> key
-        </para>
-        <para>
-          This endpoint returns the following JSON structure on success
-          with a 201 Created status
-        </para>
-        <programlisting language="json">
-{
-    &quot;files&quot;: [
-        {
-            &quot;delete_token&quot;: &quot;JFvFhqJA98&quot;,
-            &quot;file&quot;: &quot;lkWZDRvugm.jpg&quot;
-        },
-        {
-            &quot;delete_token&quot;: &quot;kAYy9nk2WK&quot;,
-            &quot;file&quot;: &quot;8qFS0QooAn.jpg&quot;
-        },
-        {
-            &quot;delete_token&quot;: &quot;OxRpM3sf0Y&quot;,
-            &quot;file&quot;: &quot;1hJaYfGE01.jpg&quot;
-        }
-    ],
-    &quot;msg&quot;: &quot;ok&quot;
-}
-</programlisting>
-      </listitem>
-      <listitem>
-        <para>
-          <literal>GET /image/download?url=...</literal> Download an
-          image from a remote server, returning the same JSON payload as
-          the <literal>POST</literal> endpoint
-        </para>
-      </listitem>
-      <listitem>
-        <para>
-          <literal>GET /image/original/{file}</literal> for getting a
-          full-resolution image. <literal>file</literal> here is the
-          <literal>file</literal> key from the <literal>/image</literal>
-          endpoint’s JSON
-        </para>
-      </listitem>
-      <listitem>
-        <para>
-          <literal>GET /image/details/original/{file}</literal> for
-          getting the details of a full-resolution image. The returned
-          JSON is structured like so:
-        </para>
-        <programlisting language="json">
-{
-    &quot;width&quot;: 800,
-    &quot;height&quot;: 537,
-    &quot;content_type&quot;: &quot;image/webp&quot;,
-    &quot;created_at&quot;: [
-        2020,
-        345,
-        67376,
-        394363487
-    ]
-}
-</programlisting>
-      </listitem>
-      <listitem>
-        <para>
-          <literal>GET /image/process.{ext}?src={file}&amp;...</literal>
-          get a file with transformations applied. existing
-          transformations include
-        </para>
-        <itemizedlist spacing="compact">
-          <listitem>
-            <para>
-              <literal>identity=true</literal>: apply no changes
-            </para>
-          </listitem>
-          <listitem>
-            <para>
-              <literal>blur={float}</literal>: apply a gaussian blur to
-              the file
-            </para>
-          </listitem>
-          <listitem>
-            <para>
-              <literal>thumbnail={int}</literal>: produce a thumbnail of
-              the image fitting inside an <literal>{int}</literal> by
-              <literal>{int}</literal> square using raw pixel sampling
-            </para>
-          </listitem>
-          <listitem>
-            <para>
-              <literal>resize={int}</literal>: produce a thumbnail of
-              the image fitting inside an <literal>{int}</literal> by
-              <literal>{int}</literal> square using a Lanczos2 filter.
-              This is slower than sampling but looks a bit better in
-              some cases
-            </para>
-          </listitem>
-          <listitem>
-            <para>
-              <literal>crop={int-w}x{int-h}</literal>: produce a cropped
-              version of the image with an <literal>{int-w}</literal> by
-              <literal>{int-h}</literal> aspect ratio. The resulting
-              crop will be centered on the image. Either the width or
-              height of the image will remain full-size, depending on
-              the image’s aspect ratio and the requested aspect ratio.
-              For example, a 1600x900 image cropped with a 1x1 aspect
-              ratio will become 900x900. A 1600x1100 image cropped with
-              a 16x9 aspect ratio will become 1600x900.
-            </para>
-          </listitem>
-        </itemizedlist>
-        <para>
-          Supported <literal>ext</literal> file extensions include
-          <literal>png</literal>, <literal>jpg</literal>, and
-          <literal>webp</literal>
-        </para>
-        <para>
-          An example of usage could be
-        </para>
-        <programlisting>
-GET /image/process.jpg?src=asdf.png&amp;thumbnail=256&amp;blur=3.0
-</programlisting>
-        <para>
-          which would create a 256x256px JPEG thumbnail and blur it
-        </para>
-      </listitem>
-      <listitem>
-        <para>
-          <literal>GET /image/details/process.{ext}?src={file}&amp;...</literal>
-          for getting the details of a processed image. The returned
-          JSON is the same format as listed for the full-resolution
-          details endpoint.
-        </para>
-      </listitem>
-      <listitem>
-        <para>
-          <literal>DELETE /image/delete/{delete_token}/{file}</literal>
-          or <literal>GET /image/delete/{delete_token}/{file}</literal>
-          to delete a file, where <literal>delete_token</literal> and
-          <literal>file</literal> are from the <literal>/image</literal>
-          endpoint’s JSON
-        </para>
-      </listitem>
-    </itemizedlist>
-  </section>
-  <section xml:id="module-services-pict-rs-missing">
-    <title>Missing</title>
-    <itemizedlist spacing="compact">
-      <listitem>
-        <para>
-          Configuring the secure-api-key is not included yet. The
-          envisioned basic use case is consumption on localhost by other
-          services without exposing the service to the internet.
-        </para>
-      </listitem>
-    </itemizedlist>
-  </section>
-</chapter>
diff --git a/nixos/modules/services/web-apps/plausible.nix b/nixos/modules/services/web-apps/plausible.nix
index e5dc1b1036017..f64254d62524e 100644
--- a/nixos/modules/services/web-apps/plausible.nix
+++ b/nixos/modules/services/web-apps/plausible.nix
@@ -292,5 +292,5 @@ in {
   };
 
   meta.maintainers = with maintainers; [ ma27 ];
-  meta.doc = ./plausible.xml;
+  meta.doc = ./plausible.md;
 }
diff --git a/nixos/modules/services/web-apps/plausible.xml b/nixos/modules/services/web-apps/plausible.xml
deleted file mode 100644
index 39ff004ffd95f..0000000000000
--- a/nixos/modules/services/web-apps/plausible.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<!-- Do not edit this file directly, edit its companion .md instead
-     and regenerate this file using nixos/doc/manual/md-to-db.sh -->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-plausible">
-  <title>Plausible</title>
-  <para>
-    <link xlink:href="https://plausible.io/">Plausible</link> is a
-    privacy-friendly alternative to Google analytics.
-  </para>
-  <section xml:id="module-services-plausible-basic-usage">
-    <title>Basic Usage</title>
-    <para>
-      At first, a secret key is needed to be generated. This can be done
-      with e.g.
-    </para>
-    <programlisting>
-$ openssl rand -base64 64
-</programlisting>
-    <para>
-      After that, <literal>plausible</literal> can be deployed like
-      this:
-    </para>
-    <programlisting>
-{
-  services.plausible = {
-    enable = true;
-    adminUser = {
-      # activate is used to skip the email verification of the admin-user that's
-      # automatically created by plausible. This is only supported if
-      # postgresql is configured by the module. This is done by default, but
-      # can be turned off with services.plausible.database.postgres.setup.
-      activate = true;
-      email = &quot;admin@localhost&quot;;
-      passwordFile = &quot;/run/secrets/plausible-admin-pwd&quot;;
-    };
-    server = {
-      baseUrl = &quot;http://analytics.example.org&quot;;
-      # secretKeybaseFile is a path to the file which contains the secret generated
-      # with openssl as described above.
-      secretKeybaseFile = &quot;/run/secrets/plausible-secret-key-base&quot;;
-    };
-  };
-}
-</programlisting>
-  </section>
-</chapter>