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.md284
-rw-r--r--nixos/modules/services/web-apps/discourse.md218
-rw-r--r--nixos/modules/services/web-apps/gotosocial.md52
-rw-r--r--nixos/modules/services/web-apps/keycloak.md22
-rw-r--r--nixos/modules/services/web-apps/lemmy.md14
-rw-r--r--nixos/modules/services/web-apps/pict-rs.md4
-rw-r--r--nixos/modules/services/web-apps/suwayomi-server.md2
7 files changed, 318 insertions, 278 deletions
diff --git a/nixos/modules/services/web-apps/akkoma.md b/nixos/modules/services/web-apps/akkoma.md
index 83dd1a8b35f23..13b074b228a41 100644
--- a/nixos/modules/services/web-apps/akkoma.md
+++ b/nixos/modules/services/web-apps/akkoma.md
@@ -19,21 +19,23 @@ be run behind a HTTP proxy on `fediverse.example.com`.
 
 
 ```nix
-services.akkoma.enable = true;
-services.akkoma.config = {
-  ":pleroma" = {
-    ":instance" = {
-      name = "My Akkoma instance";
-      description = "More detailed description";
-      email = "admin@example.com";
-      registration_open = false;
-    };
-
-    "Pleroma.Web.Endpoint" = {
-      url.host = "fediverse.example.com";
+{
+  services.akkoma.enable = true;
+  services.akkoma.config = {
+    ":pleroma" = {
+      ":instance" = {
+        name = "My Akkoma instance";
+        description = "More detailed description";
+        email = "admin@example.com";
+        registration_open = false;
+      };
+
+      "Pleroma.Web.Endpoint" = {
+        url.host = "fediverse.example.com";
+      };
     };
   };
-};
+}
 ```
 
 Please refer to the [configuration cheat sheet](https://docs.akkoma.dev/stable/configuration/cheatsheet/)
@@ -55,19 +57,21 @@ Although it is possible to expose Akkoma directly, it is common practice to oper
 HTTP reverse proxy such as nginx.
 
 ```nix
-services.akkoma.nginx = {
-  enableACME = true;
-  forceSSL = true;
-};
-
-services.nginx = {
-  enable = true;
-
-  clientMaxBodySize = "16m";
-  recommendedTlsSettings = true;
-  recommendedOptimisation = true;
-  recommendedGzipSettings = true;
-};
+{
+  services.akkoma.nginx = {
+    enableACME = true;
+    forceSSL = true;
+  };
+
+  services.nginx = {
+    enable = true;
+
+    clientMaxBodySize = "16m";
+    recommendedTlsSettings = true;
+    recommendedOptimisation = true;
+    recommendedGzipSettings = true;
+  };
+}
 ```
 
 Please refer to [](#module-security-acme) for details on how to provision an SSL/TLS certificate.
@@ -78,51 +82,53 @@ Without the media proxy function, Akkoma does not store any remote media like pi
 locally, and clients have to fetch them directly from the source server.
 
 ```nix
-# Enable nginx slice module distributed with Tengine
-services.nginx.package = pkgs.tengine;
-
-# Enable media proxy
-services.akkoma.config.":pleroma".":media_proxy" = {
-  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."/proxy" = {
-    proxyPass = "http://unix:/run/akkoma/socket";
-
-    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;
-    '';
+{
+  # Enable nginx slice module distributed with Tengine
+  services.nginx.package = pkgs.tengine;
+
+  # Enable media proxy
+  services.akkoma.config.":pleroma".":media_proxy" = {
+    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."/proxy" = {
+      proxyPass = "http://unix:/run/akkoma/socket";
+
+      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;
+      '';
+    };
+  };
+}
 ```
 
 #### Prefetch remote media {#modules-services-akkoma-prefetch-remote-media}
@@ -132,10 +138,12 @@ fetches all media associated with a post through the media proxy, as soon as the
 received by the instance.
 
 ```nix
-services.akkoma.config.":pleroma".":mrf".policies =
-  map (pkgs.formats.elixirConf { }).lib.mkRaw [
-    "Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy"
-];
+{
+  services.akkoma.config.":pleroma".":mrf".policies =
+    map (pkgs.formats.elixirConf { }).lib.mkRaw [
+      "Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy"
+  ];
+}
 ```
 
 #### Media previews {#modules-services-akkoma-media-previews}
@@ -143,11 +151,13 @@ services.akkoma.config.":pleroma".":mrf".policies =
 Akkoma can generate previews for media.
 
 ```nix
-services.akkoma.config.":pleroma".":media_preview_proxy" = {
-  enabled = true;
-  thumbnail_max_width = 1920;
-  thumbnail_max_height = 1080;
-};
+{
+  services.akkoma.config.":pleroma".":media_preview_proxy" = {
+    enabled = true;
+    thumbnail_max_width = 1920;
+    thumbnail_max_height = 1080;
+  };
+}
 ```
 
 ## Frontend management {#modules-services-akkoma-frontend-management}
@@ -160,29 +170,31 @@ The following example overrides the primary frontend’s default configuration u
 derivation.
 
 ```nix
-services.akkoma.frontends.primary.package = pkgs.runCommand "akkoma-fe" {
-  config = builtins.toJSON {
-    expertLevel = 1;
-    collapseMessageWithSubject = false;
-    stopGifs = false;
-    replyVisibility = "following";
-    webPushHideIfCW = true;
-    hideScopeNotice = true;
-    renderMisskeyMarkdown = false;
-    hideSiteFavicon = true;
-    postContentType = "text/markdown";
-    showNavShortcuts = false;
-  };
-  nativeBuildInputs = with pkgs; [ jq xorg.lndir ];
-  passAsFile = [ "config" ];
-} ''
-  mkdir $out
-  lndir ${pkgs.akkoma-frontends.akkoma-fe} $out
-
-  rm $out/static/config.json
-  jq -s add ${pkgs.akkoma-frontends.akkoma-fe}/static/config.json ${config} \
-    >$out/static/config.json
-'';
+{
+  services.akkoma.frontends.primary.package = pkgs.runCommand "akkoma-fe" {
+    config = builtins.toJSON {
+      expertLevel = 1;
+      collapseMessageWithSubject = false;
+      stopGifs = false;
+      replyVisibility = "following";
+      webPushHideIfCW = true;
+      hideScopeNotice = true;
+      renderMisskeyMarkdown = false;
+      hideSiteFavicon = true;
+      postContentType = "text/markdown";
+      showNavShortcuts = false;
+    };
+    nativeBuildInputs = with pkgs; [ jq xorg.lndir ];
+    passAsFile = [ "config" ];
+  } ''
+    mkdir $out
+    lndir ${pkgs.akkoma-frontends.akkoma-fe} $out
+
+    rm $out/static/config.json
+    jq -s add ${pkgs.akkoma-frontends.akkoma-fe}/static/config.json ${config} \
+      >$out/static/config.json
+  '';
+}
 ```
 
 ## Federation policies {#modules-services-akkoma-federation-policies}
@@ -198,28 +210,30 @@ of the fediverse and providing a pleasant experience to the users of an instance
 
 
 ```nix
-services.akkoma.config.":pleroma" = with (pkgs.formats.elixirConf { }).lib; {
-  ":mrf".policies = map mkRaw [
-    "Pleroma.Web.ActivityPub.MRF.SimplePolicy"
-  ];
-
-  ":mrf_simple" = {
-    # Tag all media as sensitive
-    media_nsfw = mkMap {
-      "nsfw.weird.kinky" = "Untagged NSFW content";
-    };
-
-    # Reject all activities except deletes
-    reject = mkMap {
-      "kiwifarms.cc" = "Persistent harassment of users, no moderation";
-    };
-
-    # Force posts to be visible by followers only
-    followers_only = mkMap {
-      "beta.birdsite.live" = "Avoid polluting timelines with Twitter posts";
+{
+  services.akkoma.config.":pleroma" = with (pkgs.formats.elixirConf { }).lib; {
+    ":mrf".policies = map mkRaw [
+      "Pleroma.Web.ActivityPub.MRF.SimplePolicy"
+    ];
+
+    ":mrf_simple" = {
+      # Tag all media as sensitive
+      media_nsfw = mkMap {
+        "nsfw.weird.kinky" = "Untagged NSFW content";
+      };
+
+      # Reject all activities except deletes
+      reject = mkMap {
+        "kiwifarms.cc" = "Persistent harassment of users, no moderation";
+      };
+
+      # Force posts to be visible by followers only
+      followers_only = mkMap {
+        "beta.birdsite.live" = "Avoid polluting timelines with Twitter posts";
+      };
     };
   };
-};
+}
 ```
 
 ## Upload filters {#modules-services-akkoma-upload-filters}
@@ -228,12 +242,14 @@ This example strips GPS and location metadata from uploads, deduplicates them an
 the file name.
 
 ```nix
-services.akkoma.config.":pleroma"."Pleroma.Upload".filters =
-  map (pkgs.formats.elixirConf { }).lib.mkRaw [
-    "Pleroma.Upload.Filter.Exiftool"
-    "Pleroma.Upload.Filter.Dedupe"
-    "Pleroma.Upload.Filter.AnonymizeFilename"
-  ];
+{
+  services.akkoma.config.":pleroma"."Pleroma.Upload".filters =
+    map (pkgs.formats.elixirConf { }).lib.mkRaw [
+      "Pleroma.Upload.Filter.Exiftool"
+      "Pleroma.Upload.Filter.Dedupe"
+      "Pleroma.Upload.Filter.AnonymizeFilename"
+    ];
+}
 ```
 
 ## Migration from Pleroma {#modules-services-akkoma-migration-pleroma}
@@ -286,9 +302,11 @@ To re‐use the Pleroma data in place, disable Pleroma and enable Akkoma, pointi
 Pleroma database and upload directory.
 
 ```nix
-# Adjust these settings according to the database name and upload directory path used by Pleroma
-services.akkoma.config.":pleroma"."Pleroma.Repo".database = "pleroma";
-services.akkoma.config.":pleroma".":instance".upload_dir = "/var/lib/pleroma/uploads";
+{
+  # Adjust these settings according to the database name and upload directory path used by Pleroma
+  services.akkoma.config.":pleroma"."Pleroma.Repo".database = "pleroma";
+  services.akkoma.config.":pleroma".":instance".upload_dir = "/var/lib/pleroma/uploads";
+}
 ```
 
 Please keep in mind that after the Akkoma service has been started, any migrations applied by
@@ -304,7 +322,9 @@ details.
 The Akkoma systemd service may be confined to a chroot with
 
 ```nix
-services.systemd.akkoma.confinement.enable = true;
+{
+  services.systemd.akkoma.confinement.enable = true;
+}
 ```
 
 Confinement of services is not generally supported in NixOS and therefore disabled by default.
diff --git a/nixos/modules/services/web-apps/discourse.md b/nixos/modules/services/web-apps/discourse.md
index 799a01c6ff5b3..d4b9c93c4ead0 100644
--- a/nixos/modules/services/web-apps/discourse.md
+++ b/nixos/modules/services/web-apps/discourse.md
@@ -7,19 +7,21 @@ modern and open source discussion platform.
 
 A minimal configuration using Let's Encrypt for TLS certificates looks like this:
 ```nix
-services.discourse = {
-  enable = true;
-  hostname = "discourse.example.com";
-  admin = {
-    email = "admin@example.com";
-    username = "admin";
-    fullName = "Administrator";
-    passwordFile = "/path/to/password_file";
+{
+  services.discourse = {
+    enable = true;
+    hostname = "discourse.example.com";
+    admin = {
+      email = "admin@example.com";
+      username = "admin";
+      fullName = "Administrator";
+      passwordFile = "/path/to/password_file";
+    };
+    secretKeyBaseFile = "/path/to/secret_key_base_file";
   };
-  secretKeyBaseFile = "/path/to/secret_key_base_file";
-};
-security.acme.email = "me@example.com";
-security.acme.acceptTerms = true;
+  security.acme.email = "me@example.com";
+  security.acme.acceptTerms = true;
+}
 ```
 
 Provided a proper DNS setup, you'll be able to connect to the
@@ -35,19 +37,21 @@ and [](#opt-services.discourse.sslCertificateKey)
 options:
 
 ```nix
-services.discourse = {
-  enable = true;
-  hostname = "discourse.example.com";
-  sslCertificate = "/path/to/ssl_certificate";
-  sslCertificateKey = "/path/to/ssl_certificate_key";
-  admin = {
-    email = "admin@example.com";
-    username = "admin";
-    fullName = "Administrator";
-    passwordFile = "/path/to/password_file";
+{
+  services.discourse = {
+    enable = true;
+    hostname = "discourse.example.com";
+    sslCertificate = "/path/to/ssl_certificate";
+    sslCertificateKey = "/path/to/ssl_certificate_key";
+    admin = {
+      email = "admin@example.com";
+      username = "admin";
+      fullName = "Administrator";
+      passwordFile = "/path/to/password_file";
+    };
+    secretKeyBaseFile = "/path/to/secret_key_base_file";
   };
-  secretKeyBaseFile = "/path/to/secret_key_base_file";
-};
+}
 ```
 
 ## Database access {#module-services-discourse-database}
@@ -81,26 +85,28 @@ A basic setup which assumes you want to use your configured
 email domain can be done like this:
 
 ```nix
-services.discourse = {
-  enable = true;
-  hostname = "discourse.example.com";
-  sslCertificate = "/path/to/ssl_certificate";
-  sslCertificateKey = "/path/to/ssl_certificate_key";
-  admin = {
-    email = "admin@example.com";
-    username = "admin";
-    fullName = "Administrator";
-    passwordFile = "/path/to/password_file";
-  };
-  mail.outgoing = {
-    serverAddress = "smtp.emailprovider.com";
-    port = 587;
-    username = "user@emailprovider.com";
-    passwordFile = "/path/to/smtp_password_file";
+{
+  services.discourse = {
+    enable = true;
+    hostname = "discourse.example.com";
+    sslCertificate = "/path/to/ssl_certificate";
+    sslCertificateKey = "/path/to/ssl_certificate_key";
+    admin = {
+      email = "admin@example.com";
+      username = "admin";
+      fullName = "Administrator";
+      passwordFile = "/path/to/password_file";
+    };
+    mail.outgoing = {
+      serverAddress = "smtp.emailprovider.com";
+      port = 587;
+      username = "user@emailprovider.com";
+      passwordFile = "/path/to/smtp_password_file";
+    };
+    mail.incoming.enable = true;
+    secretKeyBaseFile = "/path/to/secret_key_base_file";
   };
-  mail.incoming.enable = true;
-  secretKeyBaseFile = "/path/to/secret_key_base_file";
-};
+}
 ```
 
 This assumes you have set up an MX record for the address you've
@@ -163,43 +169,45 @@ Discourse instance and enables
 GitHub login in the site settings,
 and changes a few request limits in the backend settings:
 ```nix
-services.discourse = {
-  enable = true;
-  hostname = "discourse.example.com";
-  sslCertificate = "/path/to/ssl_certificate";
-  sslCertificateKey = "/path/to/ssl_certificate_key";
-  admin = {
-    email = "admin@example.com";
-    username = "admin";
-    fullName = "Administrator";
-    passwordFile = "/path/to/password_file";
-  };
-  mail.outgoing = {
-    serverAddress = "smtp.emailprovider.com";
-    port = 587;
-    username = "user@emailprovider.com";
-    passwordFile = "/path/to/smtp_password_file";
-  };
-  mail.incoming.enable = true;
-  siteSettings = {
-    required = {
-      title = "My Cats";
-      site_description = "Discuss My Cats (and be nice plz)";
+{
+  services.discourse = {
+    enable = true;
+    hostname = "discourse.example.com";
+    sslCertificate = "/path/to/ssl_certificate";
+    sslCertificateKey = "/path/to/ssl_certificate_key";
+    admin = {
+      email = "admin@example.com";
+      username = "admin";
+      fullName = "Administrator";
+      passwordFile = "/path/to/password_file";
     };
-    login = {
-      enable_github_logins = true;
-      github_client_id = "a2f6dfe838cb3206ce20";
-      github_client_secret._secret = /run/keys/discourse_github_client_secret;
+    mail.outgoing = {
+      serverAddress = "smtp.emailprovider.com";
+      port = 587;
+      username = "user@emailprovider.com";
+      passwordFile = "/path/to/smtp_password_file";
     };
+    mail.incoming.enable = true;
+    siteSettings = {
+      required = {
+        title = "My Cats";
+        site_description = "Discuss My Cats (and be nice plz)";
+      };
+      login = {
+        enable_github_logins = true;
+        github_client_id = "a2f6dfe838cb3206ce20";
+        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 = "warn+block";
+    };
+    secretKeyBaseFile = "/path/to/secret_key_base_file";
   };
-  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 = "warn+block";
-  };
-  secretKeyBaseFile = "/path/to/secret_key_base_file";
-};
+}
 ```
 
 In the resulting site settings file, the
@@ -254,33 +262,35 @@ plugins, and disable `discourse-spoiler-alert`
 by default:
 
 ```nix
-services.discourse = {
-  enable = true;
-  hostname = "discourse.example.com";
-  sslCertificate = "/path/to/ssl_certificate";
-  sslCertificateKey = "/path/to/ssl_certificate_key";
-  admin = {
-    email = "admin@example.com";
-    username = "admin";
-    fullName = "Administrator";
-    passwordFile = "/path/to/password_file";
-  };
-  mail.outgoing = {
-    serverAddress = "smtp.emailprovider.com";
-    port = 587;
-    username = "user@emailprovider.com";
-    passwordFile = "/path/to/smtp_password_file";
-  };
-  mail.incoming.enable = true;
-  plugins = with config.services.discourse.package.plugins; [
-    discourse-spoiler-alert
-    discourse-solved
-  ];
-  siteSettings = {
-    plugins = {
-      spoiler_enabled = false;
+{
+  services.discourse = {
+    enable = true;
+    hostname = "discourse.example.com";
+    sslCertificate = "/path/to/ssl_certificate";
+    sslCertificateKey = "/path/to/ssl_certificate_key";
+    admin = {
+      email = "admin@example.com";
+      username = "admin";
+      fullName = "Administrator";
+      passwordFile = "/path/to/password_file";
+    };
+    mail.outgoing = {
+      serverAddress = "smtp.emailprovider.com";
+      port = 587;
+      username = "user@emailprovider.com";
+      passwordFile = "/path/to/smtp_password_file";
+    };
+    mail.incoming.enable = true;
+    plugins = with config.services.discourse.package.plugins; [
+      discourse-spoiler-alert
+      discourse-solved
+    ];
+    siteSettings = {
+      plugins = {
+        spoiler_enabled = false;
+      };
     };
+    secretKeyBaseFile = "/path/to/secret_key_base_file";
   };
-  secretKeyBaseFile = "/path/to/secret_key_base_file";
-};
+}
 ```
diff --git a/nixos/modules/services/web-apps/gotosocial.md b/nixos/modules/services/web-apps/gotosocial.md
index a290d7d1893a1..b3540f0d5811f 100644
--- a/nixos/modules/services/web-apps/gotosocial.md
+++ b/nixos/modules/services/web-apps/gotosocial.md
@@ -8,17 +8,19 @@ The following configuration sets up the PostgreSQL as database backend and binds
 GoToSocial to `127.0.0.1:8080`, expecting to be run behind a HTTP proxy on `gotosocial.example.com`.
 
 ```nix
-services.gotosocial = {
-  enable = true;
-  setupPostgresqlDB = true;
-  settings = {
-    application-name = "My GoToSocial";
-    host = "gotosocial.example.com";
-    protocol = "https";
-    bind-address = "127.0.0.1";
-    port = 8080;
+{
+  services.gotosocial = {
+    enable = true;
+    setupPostgresqlDB = true;
+    settings = {
+      application-name = "My GoToSocial";
+      host = "gotosocial.example.com";
+      protocol = "https";
+      bind-address = "127.0.0.1";
+      port = 8080;
+    };
   };
-};
+}
 ```
 
 Please refer to the [GoToSocial Documentation](https://docs.gotosocial.org/en/latest/configuration/general/)
@@ -30,24 +32,26 @@ Although it is possible to expose GoToSocial directly, it is common practice to
 HTTP reverse proxy such as nginx.
 
 ```nix
-networking.firewall.allowedTCPPorts = [ 80 443 ];
-services.nginx = {
-  enable = true;
-  clientMaxBodySize = "40M";
-  virtualHosts = with config.services.gotosocial.settings; {
-    "${host}" = {
-      enableACME = true;
-      forceSSL = true;
-      locations = {
-        "/" = {
-          recommendedProxySettings = true;
-          proxyWebsockets = true;
-          proxyPass = "http://${bind-address}:${toString port}";
+{
+  networking.firewall.allowedTCPPorts = [ 80 443 ];
+  services.nginx = {
+    enable = true;
+    clientMaxBodySize = "40M";
+    virtualHosts = with config.services.gotosocial.settings; {
+      "${host}" = {
+        enableACME = true;
+        forceSSL = true;
+        locations = {
+          "/" = {
+            recommendedProxySettings = true;
+            proxyWebsockets = true;
+            proxyPass = "http://${bind-address}:${toString port}";
+          };
         };
       };
     };
   };
-};
+}
 ```
 
 Please refer to [](#module-security-acme) for details on how to provision an SSL/TLS certificate.
diff --git a/nixos/modules/services/web-apps/keycloak.md b/nixos/modules/services/web-apps/keycloak.md
index 2ab6e96e5e6ed..020bee4003489 100644
--- a/nixos/modules/services/web-apps/keycloak.md
+++ b/nixos/modules/services/web-apps/keycloak.md
@@ -127,15 +127,17 @@ should be set to. See the description of
 
 A basic configuration with some custom settings could look like this:
 ```nix
-services.keycloak = {
-  enable = true;
-  settings = {
-    hostname = "keycloak.example.com";
-    hostname-strict-backchannel = true;
+{
+  services.keycloak = {
+    enable = true;
+    settings = {
+      hostname = "keycloak.example.com";
+      hostname-strict-backchannel = true;
+    };
+    initialAdminPassword = "e6Wcm0RrtegMEHl";  # change on first login
+    sslCertificate = "/run/keys/ssl_cert";
+    sslCertificateKey = "/run/keys/ssl_key";
+    database.passwordFile = "/run/keys/db_password";
   };
-  initialAdminPassword = "e6Wcm0RrtegMEHl";  # change on first login
-  sslCertificate = "/run/keys/ssl_cert";
-  sslCertificateKey = "/run/keys/ssl_key";
-  database.passwordFile = "/run/keys/db_password";
-};
+}
 ```
diff --git a/nixos/modules/services/web-apps/lemmy.md b/nixos/modules/services/web-apps/lemmy.md
index faafe096d1382..0ed23607d00b9 100644
--- a/nixos/modules/services/web-apps/lemmy.md
+++ b/nixos/modules/services/web-apps/lemmy.md
@@ -7,13 +7,15 @@ Lemmy is a federated alternative to reddit in rust.
 the minimum to start lemmy is
 
 ```nix
-services.lemmy = {
-  enable = true;
-  settings = {
-    hostname = "lemmy.union.rocks";
-    database.createLocally = true;
+{
+  services.lemmy = {
+    enable = true;
+    settings = {
+      hostname = "lemmy.union.rocks";
+      database.createLocally = true;
+    };
+    caddy.enable = true;
   };
-  caddy.enable = true;
 }
 ```
 
diff --git a/nixos/modules/services/web-apps/pict-rs.md b/nixos/modules/services/web-apps/pict-rs.md
index 2fa6bb3aebced..56c51e0d72594 100644
--- a/nixos/modules/services/web-apps/pict-rs.md
+++ b/nixos/modules/services/web-apps/pict-rs.md
@@ -7,7 +7,9 @@ pict-rs is a  a simple image hosting service.
 the minimum to start pict-rs is
 
 ```nix
-services.pict-rs.enable = true;
+{
+  services.pict-rs.enable = true;
+}
 ```
 
 this will start the http server on port 8080 by default.
diff --git a/nixos/modules/services/web-apps/suwayomi-server.md b/nixos/modules/services/web-apps/suwayomi-server.md
index 18e7a631443f4..2185556a87212 100644
--- a/nixos/modules/services/web-apps/suwayomi-server.md
+++ b/nixos/modules/services/web-apps/suwayomi-server.md
@@ -100,7 +100,7 @@ Not all the configuration options are available directly in this module, but you
       server = {
         port = 4567;
         autoDownloadNewChapters = false;
-        maxSourcesInParallel" = 6;
+        maxSourcesInParallel = 6;
         extensionRepos = [
           "https://raw.githubusercontent.com/MY_ACCOUNT/MY_REPO/repo/index.min.json"
         ];