diff --git a/docs/services.md b/docs/services.md
index eb82b7f..dc4b0a7 100644
--- a/docs/services.md
+++ b/docs/services.md
@@ -2,13 +2,12 @@
 
 | Service               | Backup | Reverse Proxy | SSO | LDAP  | Monitoring | Profiling |
 |-----------------------|--------|---------------|-----|-------|------------|-----------|
-| [Nextcloud Server][1] | P (1)  | Y             | N   | P (2) | Y          | P (3)     |
+| [Nextcloud Server][1] | P (1)  | Y             | N   | Y     | Y          | P (2)     |
 
 Legend: **N**: no but WIP; **P**: partial; **Y**: yes
 
 1. Does not backup the database yet.
-2. Works but requires manually setting up the integration.
-3. Works but the traces are not exported to Grafana yet.
+2. Works but the traces are not exported to Grafana yet.
 
 [1]: services-nextcloud.html
 
diff --git a/modules/services/nextcloud-server.nix b/modules/services/nextcloud-server.nix
index 3dc78d5..149b698 100644
--- a/modules/services/nextcloud-server.nix
+++ b/modules/services/nextcloud-server.nix
@@ -8,6 +8,8 @@ let
   # Make sure to bump both nextcloudPkg and nextcloudApps at the same time.
   nextcloudPkg = pkgs.nextcloud27;
   nextcloudApps = pkgs.nextcloud27Packages.apps;
+
+  occ = "${config.services.nextcloud.occ}/bin/nextcloud-occ";
 in
 {
   options.shb.nextcloud = {
@@ -186,6 +188,61 @@ in
               };
             };
           };
+
+          ldap = lib.mkOption {
+            description = ''
+              LDAP Integration App. [Manual](https://docs.nextcloud.com/server/latest/admin_manual/configuration_user/user_auth_ldap.html)
+
+              Enabling this app will create a new LDAP configuration or update one that exists with
+              the given host.
+            '';
+            default = {};
+            type = lib.types.nullOr (lib.types.submodule {
+              options = {
+                enable = lib.mkEnableOption "LDAP app.";
+
+                host = lib.mkOption {
+                  type = lib.types.nullOr lib.types.str;
+                  description = ''
+                    Host serving the LDAP server.
+                  '';
+                  default = "127.0.0.1";
+                };
+
+                port = lib.mkOption {
+                  type = lib.types.nullOr lib.types.port;
+                  description = ''
+                    Port of the service serving the LDAP server.
+                  '';
+                  default = 389;
+                };
+
+                dcdomain = lib.mkOption {
+                  type = lib.types.str;
+                  description = "dc domain for ldap.";
+                  example = "dc=mydomain,dc=com";
+                };
+
+                adminName = lib.mkOption {
+                  type = lib.types.str;
+                  description = "Admin user of the LDAP server.";
+                  default = "admin";
+                };
+
+                adminPasswordFile = lib.mkOption {
+                  type = lib.types.path;
+                  description = "File containing the admin password of the LDAP server.";
+                  default = "";
+                };
+
+                userGroup = lib.mkOption {
+                  type = lib.types.str;
+                  description = "Group users must belong to to be able to login to Nextcloud.";
+                  default = "nextcloud_user";
+                };
+              };
+            });
+          };
         };
       };
     };
@@ -453,8 +510,86 @@ in
           let
             debug = if cfg.debug or cfg.apps.previewgenerator.debug then "-vvv" else "";
           in
-            "${config.services.nextcloud.occ}/bin/nextcloud-occ ${debug} preview:pre-generate";
+            "${occ} ${debug} preview:pre-generate";
       };
     })
+
+    (lib.mkIf cfg.apps.ldap.enable {
+      systemd.services.nextcloud-setup.path = [ pkgs.jq ];
+      systemd.services.nextcloud-setup.script = ''
+        ${occ} app:install user_ldap || :
+        ${occ} app:enable  user_ldap
+
+        # The following code tries to match an existing config or creates a new one.
+        # The criteria for matching is the ldapHost value.
+
+        ALL_CONFIG="$(${occ} ldap:show-config --output=json --show-password)"
+
+        MATCHING_CONFIG_IDs="$(echo "$ALL_CONFIG" | jq '[to_entries[] | select(.value.ldapHost=="127.0.0.1") | .key]')"
+        if [[ $(echo "$MATCHING_CONFIG_IDs" | jq 'length') > 0 ]]; then
+          CONFIG_ID="$(echo "$MATCHING_CONFIG_IDs" | jq --raw-output '.[0]')"
+        else
+          CONFIG_ID="$(${occ} ldap:create-empty-config --only-print-prefix)"
+        fi
+
+        echo "Using configId $CONFIG_ID"
+
+        CONFIG="$(echo "$ALL_CONFIG" | jq ".$CONFIG_ID")"
+
+
+        # The following CLI commands follow
+        # https://github.com/lldap/lldap/blob/main/example_configs/nextcloud.md#nextcloud-config--the-cli-way
+
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapHost' \
+                  '${cfg.apps.ldap.host}'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapPort' \
+                  '${toString cfg.apps.ldap.port}'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapAgentName' \
+                  'uid=${cfg.apps.ldap.adminName},ou=people,${cfg.apps.ldap.dcdomain}'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapAgentPassword'  \
+                  "$(cat ${cfg.apps.ldap.adminPasswordFile})"
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapBase' \
+                  '${cfg.apps.ldap.dcdomain}'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapBaseGroups' \
+                  '${cfg.apps.ldap.dcdomain}'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapBaseUsers' \
+                  '${cfg.apps.ldap.dcdomain}'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapEmailAttribute' \
+                  'mail'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapGroupFilter' \
+                  '(&(|(objectclass=groupOfUniqueNames))(|(cn=${cfg.apps.ldap.userGroup})))'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapGroupFilterGroups' \
+                  '${cfg.apps.ldap.userGroup}'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapGroupFilterObjectclass' \
+                  'groupOfUniqueNames'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapGroupMemberAssocAttr' \
+                  'uniqueMember'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapLoginFilter' \
+                  '(&(&(objectclass=person)(memberOf=cn=${cfg.apps.ldap.userGroup},ou=groups,${cfg.apps.ldap.dcdomain}))(|(uid=%uid)(|(mail=%uid)(objectclass=%uid))))'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapLoginFilterAttributes' \
+                  'mail;objectclass'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapUserDisplayName' \
+                  'displayname'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapUserFilter' \
+                  '(&(objectclass=person)(memberOf=cn=${cfg.apps.ldap.userGroup},ou=groups,${cfg.apps.ldap.dcdomain}))'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapUserFilterMode' \
+                  '1'
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapUserFilterObjectclass' \
+                  'person'
+
+        ${occ} ldap:test-config -- "$CONFIG_ID"
+
+        # Only one active at the same time
+
+        for configid in $(echo "$ALL_CONFIG" | jq --raw-output "keys[]"); do
+          echo "Deactivating $configid"
+          ${occ} ldap:set-config "$configid" 'ldapConfigurationActive' \
+                    '0'
+        done
+
+        ${occ} ldap:set-config "$CONFIG_ID" 'ldapConfigurationActive' \
+                  '1'
+      '';
+    })
   ];
 }
diff --git a/modules/services/nextcloud-server/docs/default.md b/modules/services/nextcloud-server/docs/default.md
index b3674c1..451f5c8 100644
--- a/modules/services/nextcloud-server/docs/default.md
+++ b/modules/services/nextcloud-server/docs/default.md
@@ -6,12 +6,17 @@ This NixOS module is a service that sets up a [Nextcloud Server](https://nextclo
 
 ## Features {#services-nextcloud-server-features}
 
-- Declarative Apps Configuration - no need to configure through the UI.
+- Declarative [Apps](#services-nextcloud-server-options-shb.nextcloud.apps) Configuration - no need
+  to configure those with the UI.
+  - LDAP app: enables app and sets up integration with an existing LDAP server. The defaults are
+    suited to work with [LLDAP](https://github.com/lldap/lldap) which is provided as a [building
+    block](./block-ldap.html).
   - [Preview Generator](https://apps.nextcloud.com/apps/previewgenerator) app: enables app and sets
     up required cron job.
   - [Only Office](https://apps.nextcloud.com/apps/onlyoffice) app: enables app and sets up Only
     Office service.
-  - Any other app through the `shb.nextcloud.extraApps` option.
+  - Any other app through the
+    [shb.nextcloud.extraApps](#services-nextcloud-server-options-shb.nextcloud.extraApps) option.
 - [Demo](./demo-nextcloud-server.html)
   - Demo deploying a Nextcloud server with [Colmena](https://colmena.cli.rs/) and with proper
     secrets management with [sops-nix](https://github.com/Mic92/sops-nix).