Tune Large LDAP Groups and memberOf Performance in 389 Directory Server

Tune large LDAP groups in 389 Directory Server with safe membership indexes, scoped memberOf processing, fixup tasks, and repeatable benchmarks.

Published

Updated

Read time 19 min read

Reviewed byDeepak Prasad

Diagram showing large LDAP group membership, memberOf processing, indexes, and replication in 389 Directory Server

Static groups with thousands of member or uniqueMember values can make reads, searches, and incremental updates expensive. The MemberOf plug-in adds reverse membership on user entries whenever static group membership changes, and nested groups force additional graph traversal that scales with group size and nesting depth.

This guide covers the complete large static-group performance workflow: inventorying heavy groups, generating reproducible test data, configuring safe membership indexes, benchmarking reads and modifications, tuning MemberOf scope and nested handling, running fixup tasks safely, and monitoring plug-in and replication overhead.

Before you start:

Tested on: Rocky Linux 10.2; 389 Directory Server 3.2.0.


Understand why large LDAP groups become expensive

The following are example benchmark tiers for this guide, not product limits or official size classifications:

text
Small group:        100 members
Medium group:       5,000 members
Large group:        50,000 members
Very large group:   100,000 or more members
Operation Large-group cost
Read complete group Transfers and decodes every membership value
Search for a member DN Depends on a suitable equality index
Add one member Updates the group entry and related indexes
Remove one member Searches and updates the multi-valued attribute
Replace all members Rewrites a very large value set
MemberOf processing Updates reverse membership on affected entries
Referential Integrity Searches for and updates references after delete or rename
Replication Transfers membership modifications to other replicas
Export and import Processes the complete multi-valued entry
Fixup task Recalculates membership across many entries and groups

The bottleneck may be any combination of:

text
Group entry size
Membership indexes
Enabled plug-ins
Nested membership
Replication
Client result handling

Do not assume every large-group problem is a database cache issue. Measure reads, indexed searches, incremental modifications, and plug-in-generated writes separately.


Choose the appropriate group design

Design Membership storage Best use
groupOfNames member DNs Common static LDAP groups
groupOfUniqueNames uniqueMember DNs Static DN membership with optional uniqueness component
POSIX group memberUid names Unix account integration
Dynamic group LDAP URL and filter Membership calculated from entry attributes
Auto Membership Static members generated from rules Applications requiring stored membership
Roles Role attributes Directory Server policy and access workflows

The primary trade-off:

text
Static group:
Stores an explicit member list that can be retrieved directly,
although transferring a very large list remains expensive.

Dynamic group:
No huge stored member list
but membership must be calculated from a filter.

Do not replace every static group with a dynamic group. Confirm the application supports filter-based membership and does not require an explicit member or uniqueMember list on the group entry.

NOTE
Dynamic groups do not produce stored reverse memberOf values through the MemberOf plug-in. Applications that depend on reading memberOf from user entries may therefore require static groups or application-side evaluation of the dynamic group filter.

New entries in this section are added with the ldapadd command.

Inventory largest groups and generate test data

Export static groups with LDIF wrapping disabled so long DNs are not split across continuation lines:

bash
ldapsearch -LLL -o ldif-wrap=no -Y EXTERNAL -H ldapi://%2Frun%2Fslapd-ldap1.socket -b "ou=Groups,dc=example,dc=com" -s sub "(|(objectClass=groupOfNames)(objectClass=groupOfUniqueNames))" dn member uniqueMember > /tmp/groups-membership.ldif

Count membership values per group entry:

bash
awk 'BEGIN{RS="";FS="\n"}{dn="";members=0;attribute="";for(i=1;i<=NF;i++){if($i~/^dn: /){dn=substr($i,5)}else if($i~/^member: /){members++;attribute="member"}else if($i~/^uniqueMember: /){members++;attribute="uniqueMember"}}if(members>0)printf "%10d\t%-12s\t%s\n",members,attribute,dn}' /tmp/groups-membership.ldif | sort -nr
output
512	member      	cn=all-employees,ou=Groups,dc=example,dc=com
         2	member      	cn=full-time-employees,ou=Groups,dc=example,dc=com
         2	member      	cn=engineering-admins,ou=Groups,dc=example,dc=com
         1	uniqueMember	cn=ops-unique,ou=Groups,dc=example,dc=com
         1	member      	cn=developers,ou=Groups,dc=example,dc=com

The first column is the current membership-value count. -o ldif-wrap=no prevents wrapped LDIF lines from corrupting the count. This export does not show modification frequency—derive that from access-log analysis if you need it.

Build a worksheet from the sorted output:

Group DN Attribute Members
cn=all-employees,ou=Groups,dc=example,dc=com member 512
cn=full-time-employees,ou=Groups,dc=example,dc=com member 2
cn=ops-unique,ou=Groups,dc=example,dc=com uniqueMember 1

Also note groups nested inside other groups, users belonging to many groups, groups modified or retrieved frequently, groups maintained by Auto Membership, and groups processed by Referential Integrity. Do not expose a complete membership export outside a protected test environment.

Generate reproducible large-group datasets

Use a dedicated test suffix or disposable backend. Confirm that ou=Groups and ou=People already exist before loading generated entries. Confirm generator syntax on your installed release:

Offline instance work in this section uses dsctl commands.

bash
dsctl ldap1 ldifgen groups --help

Build several dataset shapes so you can separate many-small-group behavior from few-very-large-group behavior:

text
1,000 groups × 10 members
500 groups × 100 members
100 groups × 1,000 members
10 groups × 10,000 members
1 group × 100,000 members

Generate a medium lab file with groupOfNames and member:

bash
dsctl ldap1 ldifgen groups perf_group__ --number 100 --suffix "dc=example,dc=com" --parent "ou=Groups,dc=example,dc=com" --num-members 1000 --create-members --member-parent "ou=People,dc=example,dc=com" --member-attr member --ldif-file /tmp/large-groups.ldif
output
Successfully created LDIF file: /tmp/large-groups.ldif

For groupOfUniqueNames, pass --member-attr uniquemember. Load with online ldapadd when you need plug-in and index behavior, or with dsctl ldap1 ldif2db for offline bulk-load testing on a disposable backend. Keep those results separate—online LDAP operations exercise the normal write path, including MemberOf processing and index maintenance, while offline import bypasses normal LDAP operation handling. Benchmark Referential Integrity separately with controlled delete, rename, and move operations.

WARNING
A single group containing tens of thousands of DN values can exceed nsslapd-maxbersize during online ldapadd. Check the generated LDIF entry size before testing. Prefer an offline import on a disposable backend when the entry cannot fit safely within the configured LDAP request limit.

See Benchmark performance with ldclt for full generator and load-path detail.


Configure safe membership indexes

List indexes on the backend that holds your groups:

Online configuration in this section uses dsconf commands.

bash
dsconf ldap1 backend index list userroot
output
...
dn: cn=member,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config
...
dn: cn=memberOf,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config
...
dn: cn=uniquemember,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config
...

Inspect the membership attributes your application uses:

bash
dsconf ldap1 backend index get --attr member userroot
output
dn: cn=member,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config
cn: member
nsIndexType: eq
nsIndexType: pres

The lab already has member, memberOf, and uniquemember index entries. Use two different paths depending on what you find.

When the index entry does not exist:

bash
dsconf ldap1 backend index add --attr member --index-type eq --reindex userroot

When the index entry exists but lacks equality indexing:

bash
dsconf ldap1 backend index set --attr member --add-type eq --reindex userroot

Apply the same distinction to uniquemember and memberOf when applications filter on uniqueMember or memberOf.

WARNING
Online reindexing can temporarily produce incomplete or inconsistent search results while regeneration is in progress. Schedule it during a maintenance window, or use an offline reindex workflow where strict consistency is required.

Verify application filters in the access log before adding indexes. General index theory and reindex troubleshooting live in Configure LDAP indexes.

Do not create substring indexes on membership attributes

WARNING
Do not configure member: sub or uniqueMember: sub on large static groups. Directory Server must evaluate substring index keys for the complete multi-valued attribute when a member is added or removed. On groups with thousands of values, routine membership changes become significantly more expensive. Red Hat explicitly warns against substring indexes on member and uniqueMember.

Use equality searches instead:

text
(member=USER_DN)
(uniqueMember=USER_DN)

When an application submits (member=*uid=user1*), correct the client filter rather than adding a substring index to support an inefficient partial-DN search.

If a substring index was added previously, remove it and rebuild:

bash
dsconf ldap1 backend index set --attr member --del-type sub --reindex userroot
bash
dsconf ldap1 backend index set --attr uniquemember --del-type sub --reindex userroot

After removal, repeat your group modification test, confirm search behavior, and monitor the access log for newly unindexed queries.


Benchmark group reads and modifications

Measure complete-group reads, indexed membership searches, reverse memberOf reads, and incremental modifications separately. The lab group cn=all-employees,ou=Groups,dc=example,dc=com contains 512 member values—a modest lab group useful for small-to-medium timing comparisons before you scale to the 5,000-member medium tier in generated datasets.

Retrieve the complete group

bash
time ldapsearch -LLL -o ldif-wrap=no -Y EXTERNAL -H ldapi://%2Frun%2Fslapd-ldap1.socket -b "cn=all-employees,ou=Groups,dc=example,dc=com" -s base member
output
member: uid=user497,ou=people,dc=example,dc=com
member: uid=user498,ou=people,dc=example,dc=com
member: uid=user499,ou=people,dc=example,dc=com
member: uid=user500,ou=people,dc=example,dc=com

real	0m0.308s

Measure response size as well as latency:

bash
ldapsearch -LLL -o ldif-wrap=no -Y EXTERNAL -H ldapi://%2Frun%2Fslapd-ldap1.socket -b "cn=all-employees,ou=Groups,dc=example,dc=com" -s base member | tee /tmp/all-employees.ldif | wc -c
output
24864
bash
grep -c '^member:' /tmp/all-employees.ldif
output
512

Record membership count, returned bytes, client time, server etime from the access log or logconv.pl, and client memory use.

Search for groups containing one user

bash
time ldapsearch -LLL -Y EXTERNAL -H ldapi://%2Frun%2Fslapd-ldap1.socket -b "ou=Groups,dc=example,dc=com" "(member=uid=user1,ou=people,dc=example,dc=com)" dn
output
dn: cn=developers,ou=Groups,dc=example,dc=com
dn: cn=all-employees,ou=Groups,dc=example,dc=com
dn: cn=full-time-employees,ou=Groups,dc=example,dc=com

real	0m0.017s

These two timings measure different operations: one transfers hundreds of values from a single entry; the other returns a few group DNs. An exact-DN equality search avoids transferring the complete membership list and allows the equality index to narrow candidate groups. Treat its timing separately from a base-object read that returns every member value.

For a true index comparison, run the same (member=USER_DN) search with the equality index present, after removing it on a disposable backend, and after restoring and rebuilding it—then compare access-log notes, etime, and entries examined.

Read reverse membership from the user

bash
ldapsearch -LLL -Y EXTERNAL -H ldapi://%2Frun%2Fslapd-ldap1.socket -b "uid=user1,ou=people,dc=example,dc=com" -s base memberOf
output
dn: uid=user1,ou=people,dc=example,dc=com
memberOf: cn=developers,ou=Groups,dc=example,dc=com
memberOf: cn=engineering,ou=Groups,dc=example,dc=com
memberOf: cn=all-employees,ou=Groups,dc=example,dc=com
memberOf: cn=full-time-employees,ou=Groups,dc=example,dc=com

Compare the cost of retrieving a very large group entry versus reading one user's memberOf values. Request only attributes the application needs—a search that requests * can pull the complete membership list when the client only needs the group name.

Benchmark incremental modifications

Add one membership value with a single-value LDAP modification rather than replacing the entire member attribute. Compare a small-group control against the 512-member cn=all-employees group so modification cost is not measured only on a one-member entry.

Confirm each test user exists and is not already a member of the target group. Otherwise LDAP can return Type or value exists (20) instead of measuring a successful addition:

bash
ldapsearch -LLL -Y EXTERNAL -H ldapi://%2Frun%2Fslapd-ldap1.socket -b "cn=developers,ou=Groups,dc=example,dc=com" -s base "(member=uid=user50001,ou=people,dc=example,dc=com)" dn

An empty result means the user is not yet a member. Use a group without overlapping Auto Membership rules when those rules would immediately recreate membership.

Small-group control (cn=developers, one member)

Create uid=user50001 with ldapadd when the entry does not already exist.

bash
time ldapmodify -Y EXTERNAL -H ldapi://%2Frun%2Fslapd-ldap1.socket <<'EOF'
dn: cn=developers,ou=Groups,dc=example,dc=com
changetype: modify
add: member
member: uid=user50001,ou=people,dc=example,dc=com
EOF
output
modifying entry "cn=developers,ou=Groups,dc=example,dc=com"

real	0m0.028s
bash
time ldapmodify -Y EXTERNAL -H ldapi://%2Frun%2Fslapd-ldap1.socket <<'EOF'
dn: cn=developers,ou=Groups,dc=example,dc=com
changetype: modify
delete: member
member: uid=user50001,ou=people,dc=example,dc=com
EOF
output
modifying entry "cn=developers,ou=Groups,dc=example,dc=com"

real	0m0.027s

Large-group test (cn=all-employees, 512 members)

Pick a user who is not already a member. In this lab, uid=user501 is not in cn=all-employees:

bash
ldapsearch -LLL -Y EXTERNAL -H ldapi://%2Frun%2Fslapd-ldap1.socket -b "cn=all-employees,ou=Groups,dc=example,dc=com" -s base "(member=uid=user501,ou=people,dc=example,dc=com)" dn

Add one membership value:

bash
time ldapmodify -Y EXTERNAL -H ldapi://%2Frun%2Fslapd-ldap1.socket <<'EOF'
dn: cn=all-employees,ou=Groups,dc=example,dc=com
changetype: modify
add: member
member: uid=user501,ou=people,dc=example,dc=com
EOF
output
modifying entry "cn=all-employees,ou=Groups,dc=example,dc=com"

real	0m0.289s

Remove the value afterward:

bash
time ldapmodify -Y EXTERNAL -H ldapi://%2Frun%2Fslapd-ldap1.socket <<'EOF'
dn: cn=all-employees,ou=Groups,dc=example,dc=com
changetype: modify
delete: member
member: uid=user501,ou=people,dc=example,dc=com
EOF
output
modifying entry "cn=all-employees,ou=Groups,dc=example,dc=com"

real	0m0.277s

In this lab, the 512-member group modifications took about ten times longer than the one-member control. MemberOf and index maintenance were enabled during both runs; that configuration was associated with the higher latency on the large group, although a single timing pair does not isolate each component.

Capture client latency plus access-log wtime, optime, and etime through logconv.pl as described in Benchmark performance with ldclt. Compare incremental add and delete against a client that replaces the full member attribute. Run modifications with production-required plug-ins enabled.


Tune MemberOf and membership fixup tasks

The MemberOf plug-in updates reverse membership on user entries whenever static group membership changes. Review the active configuration:

bash
dsconf ldap1 plugin memberof show
output
memberofattr: memberOf
memberofentryscope: dc=example,dc=com
memberofentryscopeexcludesubtree: ou=Private,dc=example,dc=com
memberofgroupattr: member
memberofgroupattr: uniqueMember
memberofskipnested: off

Apply related MemberOf configuration changes together, restart the instance once, and verify the resulting configuration. Run a targeted fixup when a change affects previously calculated memberOf values.

Limit entry scope and exclude archive branches:

bash
dsconf ldap1 plugin memberof set --scope "dc=example,dc=com"
bash
dsconf ldap1 plugin memberof set --exclude "ou=Archive,dc=example,dc=com"

Monitor only membership attributes in use:

bash
dsconf ldap1 plugin memberof set --groupattr member uniqueMember

On 389 DS 3.2, optionally limit processing to selected group object classes or filters with --specific-group-oc, --specific-group-filter, and --exclude-specific-group-filter. Do not configure conflicting include and exclude filters. Run a memberOf fixup after changing group-specific filtering so existing memberOf values match the new rules.

bash
dsctl ldap1 restart
bash
dsconf ldap1 plugin memberof show

Complete attribute reference and shared-configuration detail live in Configure the memberOf plug-in.

Validate nested-group schema before benchmarking

When a group is itself a member of another group, add extensibleObject to the nested child group entry so it can store the operational memberOf value from the parent. In the example below, cn=developers is nested inside cn=engineering, so cn=developers must permit memberOf:

ldif
dn: cn=developers,ou=Groups,dc=example,dc=com
objectClass: top
objectClass: groupOfNames
objectClass: extensibleObject
member: uid=user1,ou=People,dc=example,dc=com
memberOf: cn=engineering,ou=Groups,dc=example,dc=com

Without an object class that permits memberOf, updates can fail with LDAP result code 65, Object Class Violation. Red Hat confirms that groups used in nested membership require extensibleObject.

Confirm the live entry before benchmarking indirect membership:

bash
ldapsearch -LLL \
  -Y EXTERNAL \
  -H ldapi://%2Frun%2Fslapd-ldap1.socket \
  -b "cn=developers,ou=Groups,dc=example,dc=com" \
  -s base \
  objectClass member memberOf

Disable nested expansion when indirect membership is unnecessary

When applications require only direct membership:

bash
dsconf ldap1 plugin memberof set --skipnested on

Without skip-nested processing, Directory Server examines parent-group relationships to maintain direct and indirect memberOf values. For users already belonging to many groups, those repeated searches can consume substantial CPU.

Requirement --skipnested
Applications require indirect group membership off
Only direct membership is required on
No groups contain other groups on is normally appropriate

Restart, verify with dsconf ldap1 plugin memberof show, run fixup on a pilot subtree, and verify representative users and nested groups. Do not enable skip-nested processing only for speed when applications depend on indirect membership.

Avoid unnecessary cross-backend searches

Enable all-backend processing only when users and groups genuinely reside in different databases:

bash
dsconf ldap1 plugin memberof set --allbackends on

Cross-backend processing broadens the searches required during each membership change. Keep frequently modified groups and their member users in the same backend where the directory design permits.

Run MemberOf fixup safely

Run fixup after enabling MemberOf on existing data, importing group or user LDIF, changing nested behavior, changing scope, or changing group-specific filters. Start with a narrow subtree and an explicit filter when user entries use inetOrgPerson:

bash
dsconf ldap1 plugin memberof fixup -f "(objectClass=inetOrgPerson)" "ou=People,dc=example,dc=com"
output
Adding fixup task entry...
Successfully added task entry "cn=memberOf_fixup_2026-07-18T06:04:09.334444,cn=memberOf task,cn=tasks,cn=config". This task is running in the background. To track its progress you can use the "fixup-status" command.

Monitor progress:

bash
dsconf ldap1 plugin memberof fixup-status --watch

Or inspect the task log:

bash
dsconf ldap1 plugin memberof fixup-status --show-log
output
Found 1 fix-up task
 - Base DN:       ou=people,dc=example,dc=com
 - Filter:        (objectclass=inetorgperson)
 - Status:        Memberof task finished (processed 511 entries in 1 seconds)
 - Exit Code:     0

When -f is omitted, MemberOf fixup selects entries containing inetUser, inetAdmin, or nsMemberOf. Add an explicit filter when your users contain only another suitable object class such as inetOrgPerson. That default selection is documented by Red Hat and implemented by the current dsconf command.

Before a large fixup, record CPU and memory, check replication backlog, stop competing import or fixup tasks, pilot on a small subtree, schedule production work during reduced traffic, and verify a sample of users afterward.

The task is created and executed on the selected server. Whether its resulting memberOf changes replicate depends on your fractional-replication model. When memberOf is excluded from replication and every server calculates it locally, run the repair on each server that needs recalculation. When suppliers calculate memberOf and replicate it to read-only consumers, run the task on the appropriate calculating supplier and verify consumer convergence.

Control Auto Membership and Referential Integrity load

Large Auto Membership fixup tasks can update many static groups and trigger MemberOf processing. Run a targeted task asynchronously:

bash
dsconf ldap1 plugin automember fixup "ou=People,dc=example,dc=com" -f "(objectClass=inetOrgPerson)" -s sub
output
Attempting to add task entry... This will fail if Automembership plug-in is not enabled.
Successfully added task entry "cn=automember_rebuild_2026-07-18T05:51:23.649897,cn=automember rebuild membership,cn=tasks,cn=config". This task is running in the background. To track its progress you can use the "fixup-status" command.

Without --wait, dsconf submits the background task and returns; --timeout is not used for that submission response.

Check status immediately after submission:

bash
dsconf ldap1 plugin automember fixup-status
output
Found 1 fix-up task
 - Base DN:       ou=people,dc=example,dc=com
 - Filter:        (objectClass=inetOrgPerson)
 - Status:        Automember rebuild task finished. Processed (0) entries in 0 seconds

Processed (0) means no entries matched the submitted base DN, filter, and configured Auto Membership definitions. If matches were expected, inspect the definition scope, definition filter, target groups, and the fixup filter you passed. Without matching definitions, the task can finish successfully while performing no membership updates.

Choose either asynchronous submission or synchronous waiting; do not run both for the same test. To block until completion instead of polling fixup-status, add --wait and set a client-side limit:

bash
dsconf ldap1 plugin automember fixup \
  "ou=People,dc=example,dc=com" \
  -f "(objectClass=inetOrgPerson)" \
  -s sub --wait \
  --timeout 300

--timeout limits how long the client waits for task completion when --wait is also set. It is not a server CPU or task-runtime limit, and a timed-out client does not necessarily mean the server-side task stopped.

Cancel when server load becomes unsafe:

bash
dsconf ldap1 plugin automember abort-fixup

Use Auto Membership dry-run tasks, limit base DN and filter, and avoid running multiple membership fixups simultaneously. Rule configuration lives in Auto Membership.

Referential Integrity searches configured DN-valued attributes after entry deletes, renames, and moves. For large groups, keep member and uniqueMember equality-indexed, limit container scope to branches that store references, and measure delete latency when one user belongs to hundreds of groups. Complete Referential Integrity configuration is in Referential Integrity.


Tune client queries and replication behavior

Prefer efficient query patterns:

Goal Prefer
Find groups for one user with MemberOf enabled Base read of memberOf on the user entry
Find groups without MemberOf (member=USER_DN) with equality index
Group metadata only dn, cn, description — not *
Partial DN wildcard Fix the client — not a substring index

Avoid repeatedly reading the same huge group when application caching and your authorization model permit bounded staleness. A group entry with tens of thousands of values can also stress LDAP client libraries, application memory, JSON conversion, and proxy response limits—measure client and server behavior together.

In a replicated topology, test membership modifications through every writable supplier and keep one consistent MemberOf model:

text
Local calculation on every server:
  Enable MemberOf on suppliers and consumers
  Exclude memberOf from replication

Supplier calculation with consumer replication:
  Enable MemberOf on writable suppliers
  Exclude memberOf between writable suppliers
  Replicate memberOf to read-only consumers
  Disable MemberOf on those consumers

Avoid changing tens of thousands of group values through different suppliers simultaneously.


Monitor large-group operations

Review access and error logs under /var/log/dirsrv/slapd-ldap1/. Correlate operations by conn and op, and capture wtime, optime, etime, notes, result code, and entries returned. Use logconv.pl on rotated access logs as described in Benchmark performance with ldclt.

Check Directory Server counters:

bash
dsconf ldap1 monitor server
output
version: 389-Directory/3.2.0 B2026.177.0000
threads: 18
currentconnections: 1
opsinitiated: 4658
opscompleted: 4657
entriessent: 3328

Track complete group reads, equality membership searches, incremental adds and deletes, MemberOf-generated writes, fixup tasks, and replication updates separately.

Before-and-after test matrix

Populate this worksheet during testing on your own dataset:

Test Small group Large group Tuned result
Read complete group
Search (member=USER_DN)
Add one member
Delete one member
Replace all members
Read user memberOf
Nested membership change
MemberOf fixup
Delete referenced user
Replication convergence

Change one tuning lever per comparison run—for example baseline, remove a substring membership index and retest, or enable --skipnested and retest. Do not change indexes, plug-in scope, cache sizes, and replication settings in a single comparison.

After testing, remove generated users and groups, delete temporary LDIF exports, and restore any plug-in settings you changed (memberofskipnested, scope, all-backends, or specific-group filters) followed by a targeted fixup.


Troubleshoot large-group performance

Symptom Likely cause Fix
Adding one member is unexpectedly slow Substring index on member or uniqueMember, nested MemberOf, many parent groups, Referential Integrity, replication backlog Remove substring indexes; narrow MemberOf scope; measure plug-in and replication cost per change
Group search is slow despite equality index Wrong filter, incomplete reindex, ID-list scan limits, broad search base Match client filter to indexed equality form; check access-log notes=A or notes=U
Reading the group consumes excessive memory Client requests full membership list Request fewer attributes; read memberOf on the user when MemberOf is enabled
MemberOf updates use high CPU Nested expansion, broad scope, all-backends search, overly broad group filters Tune memberofskipnested, scope, exclude subtrees, and group-specific filters
Fixup task affects normal traffic Wide base DN, competing import or fixup tasks Narrow base and filter; schedule during reduced load
Auto Membership fixup overloads the server Complex rules on large groups with MemberOf enabled Check fixup-status; run abort-fixup; pilot with dry-run tasks
Deleted user causes long delays Referential Integrity scope or missing equality indexes Limit scope; index maintained DN attributes
Membership differs across replicas Inconsistent plug-in placement or fractional replication Align MemberOf model; check backlog and supplier used for updates
Application sees stale nested membership memberofskipnested=on while indirect membership is required Disable skip-nested; run fixup; retest authorization
User memberOf missing after group change MemberOf disabled, wrong group attribute, scope mismatch, fixup not run Confirm plug-in state and scopes; run targeted fixup
Type or value exists (20) during benchmark add Member already present or Auto Membership recreated membership Verify with a base-scope filter before add; choose a test group without overlapping rules

What's next

After you complete this guide, continue with:

Summary

  1. Inventory the largest static groups with an LDIF-aware membership count.
  2. Build reproducible test datasets with dsctl ldifgen groups on a disposable backend.
  3. Configure equality indexes for member, uniqueMember, and memberOf where filters require them.
  4. Never add substring indexes to membership attributes on large groups.
  5. Measure complete-group reads, indexed searches, and incremental modifications separately.
  6. Narrow MemberOf scope, group attributes, and group-specific filters; enable skip-nested only when indirect membership is unnecessary.
  7. Run MemberOf and Auto Membership fixups on pilot subtrees during controlled activity.
  8. Account for Referential Integrity, replication, and client result handling—not only local database latency.

References


Frequently Asked Questions

1. Should I add a substring index on member for large groups?

No. Red Hat and upstream guidance warn against substring indexes on member and uniqueMember because each membership change can force Directory Server to re-evaluate substring keys for every value in a large multi-valued attribute. Use equality indexes and exact DN filters such as (member=USER_DN) instead.

2. When should I enable memberofskipnested?

Enable skip-nested processing only when applications do not require indirect membership through nested groups. It avoids parent-group searches that can consume substantial CPU for users who already belong to many groups. Run a memberOf fixup after changing the setting and verify nested authorization behavior before production.

3. What is the safest way to run a large memberOf fixup?

Start with a narrow base DN and LDAP filter, confirm CPU and replication backlog on a pilot subtree, schedule production fixup during reduced traffic, and avoid running MemberOf and Auto Membership fixups simultaneously.

4. Is there a universal maximum LDAP group size?

No. Performance depends on member count, DN length, update frequency, nesting depth, indexes, enabled plug-ins, replication model, and client result handling. Benchmark your application filters and modification patterns instead of targeting a fixed member count.

5. How do I find which groups have the most members?

Export group entries with ldapsearch -o ldif-wrap=no, count member or uniqueMember values per DN with an LDIF-aware parser, and sort by count. Modification frequency requires access-log analysis and cannot be derived from the export alone.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …