Configure LDAP Search Limits in 389 Directory Server

Configure 389 Directory Server size, time, look-through, paged, range, and ID-list limits with global and per-user overrides.

Published

Updated

Read time 12 min read

Reviewed byDeepak Prasad

389 Directory Server LDAP search size time look-through and ID-list limits with global and per-user overrides

A single broad LDAP search can walk thousands of candidate entries and return more data than an application can handle. Directory Server enforces size, time, look-through, and ID-list limits so one client cannot exhaust the instance.

This guide covers search resource limits in 389 Directory Server:

  • Global instance limits in cn=config
  • Global LDBM database plug-in limits for look-through and ID-list behaviour
  • Per-user operational overrides for application bind accounts
  • Paged and range search limits
  • Access-log indicators for limit and index problems

Before you start:

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

The lab instance is ldap1 on ldap1.example.com with LDAP port 389. The suffix dc=example,dc=com is served by backend userroot.


Understand search resource limits

Directory Server applies limits at several layers. Instance-wide settings in cn=config cap ordinary searches for most bind identities. Look-through, ID-list, paged look-through, paged ID-list, and range look-through limits live on the global LDBM database plug-in entry at cn=config,cn=ldbm database,cn=plugins,cn=config. The command dsconf ldap1 backend config get reads that global database configuration; it does not read an individual backend such as userroot. Per-entry operational attributes on a bind DN can tighten or relax limits for one application account.

text
LDAP client bind
        |
        v
Per-entry operational limits (nsSizeLimit, nsTimeLimit, ...)
        |
        v
Global instance limits (nsslapd-sizelimit, nsslapd-timelimit, ...)
        |
        v
Global LDBM plug-in limits (nsslapd-lookthroughlimit, nsslapd-idlistscanlimit, ...)
        |
        v
Search executes or stops with LDAP result 4 (size limit) or 3 (time limit)
Limit Global attribute User-level attribute What it controls
Size nsslapd-sizelimit nsSizeLimit Maximum entries returned to the client
Time nsslapd-timelimit nsTimeLimit Maximum seconds spent processing one search
Look-through nsslapd-lookthroughlimit nsLookThroughLimit Maximum candidate entries examined
ID-list scan nsslapd-idlistscanlimit nsIDListScanLimit Maximum entry IDs loaded from one index key before the search is treated as unindexed
Fine-grained ID-list nsIndexIDListScanLimit Override ID-list behaviour for a specific index entry in a specific backend
Paged size nsslapd-pagedsizelimit nsPagedSizeLimit Maximum entries returned by a search using the simple paged-results control
Paged look-through nsslapd-pagedlookthroughlimit nsPagedLookThroughLimit Look-through cap for paged searches
Paged ID-list scan nsslapd-pagedidlistscanlimit nsPagedIDListScanLimit ID-list cap for paged searches
Range look-through nsslapd-rangelookthroughlimit Not available Look-through cap for range searches
text
Size limit:
How many entries can be returned

Look-through limit:
How many entries may be examined to find those results

ID-list scan limit:
How many candidate IDs may be loaded from an index

A search can return only one entry but still examine thousands of candidates.

Clarifications that prevent the most common mistakes:

  • Directory Manager bypasses global size and time limits. Testing nsslapd-sizelimit with Directory Manager will not show the same behaviour application accounts see.
  • Per-user limits override global defaults for that bind DN. When uid=reporting-app carries nsSizeLimit: 2, that value wins over nsslapd-sizelimit for searches bound as that user.
  • -1 means unlimited only where the attribute schema documents that value. Use it sparingly on trusted accounts.
  • Paged limit 0 inherits the ordinary limit. When nsslapd-pagedsizelimit is 0, paged searches use nsslapd-sizelimit. The same fallback design applies to nsslapd-pagedlookthroughlimit and nsslapd-pagedidlistscanlimit. Use -1 only when the paged limit must be explicitly unlimited.
  • Client page size is separate from nsslapd-pagedsizelimit. The client requests a batch size through the paged-results control, for example ldapsearch -E pr=50/noprompt. nsslapd-pagedsizelimit caps the total entries returned by the complete paged search operation.
  • notes=U is not only a missing-index signal. It also appears when an index key matches more entry IDs than nsslapd-idlistscanlimit allows. See Configure LDAP indexes for index creation.

Check current search limits

Read the active instance configuration before changing anything. Recording the baseline makes rollback straightforward.

bash
dsconf ldap1 config get nsslapd-sizelimit nsslapd-timelimit nsslapd-pagedsizelimit
output
nsslapd-sizelimit: 2000
nsslapd-timelimit: 3600
nsslapd-pagedsizelimit: 0

The lab allows up to 2000 entries per ordinary search and 3600 seconds of processing time. nsslapd-pagedsizelimit: 0 means paged searches inherit nsslapd-sizelimit.

Read the global LDBM database plug-in limits:

bash
dsconf ldap1 backend config get | grep -E 'lookthrough|idlist|paged|rangelookthrough'
output
nsslapd-lookthroughlimit: 5000
nsslapd-pagedlookthroughlimit: 0
nsslapd-idlistscanlimit: 4000
nsslapd-pagedidlistscanlimit: 0
nsslapd-rangelookthroughlimit: 5000

On the tested upstream 389 Directory Server 3.2.0 build, dsconf reported nsslapd-idlistscanlimit: 4000. Do not assume that value on other builds. Current RHDS 13 documentation marks the global setting as deprecated and documents different defaults. Inspect the active value on the installed release.

Paged look-through and paged ID-list values of 0 inherit nsslapd-lookthroughlimit and nsslapd-idlistscanlimit respectively.

Create the reporting-app service account when it does not exist yet. Save reporting-app.ldif:

ldif
dn: uid=reporting-app,ou=ServiceAccounts,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Reporting Application
sn: Application
uid: reporting-app
userPassword: ReportingAppPass!
bash
ldapadd -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f reporting-app.ldif

Grant read access under ou=People before limit tests. Save reporting-app-aci.ldif:

ldif
dn: ou=people,dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr="*")(version 3.0; acl "reporting-app read people"; allow (read,search,compare) userdn="ldap:///uid=reporting-app,ou=ServiceAccounts,dc=example,dc=com";)
bash
ldapmodify -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f reporting-app-aci.ldif

Verify the ACI was accepted, then confirm reporting-app can search under ou=people. See ACI examples when the bind succeeds but searches return no entries.


Configure search size, time, and look-through limits

Size limit

Lower the global size limit temporarily so the lab can demonstrate result code 4:

bash
dsconf ldap1 config replace nsslapd-sizelimit=3
output
Successfully replaced value(s) for 'nsslapd-sizelimit': '3'

No restart is required for nsslapd-sizelimit.

Test with Directory Manager first to confirm the bypass:

Directory queries in this section use the ldapsearch command.

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -b "ou=people,dc=example,dc=com" -s sub "(objectClass=inetOrgPerson)" dn 2>&1 | tail -3

Directory Manager still returns every matching entry even when nsslapd-sizelimit=3. Use an application bind DN when you validate limit changes.

Bind as reporting-app:

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "uid=reporting-app,ou=ServiceAccounts,dc=example,dc=com" -W -b "ou=people,dc=example,dc=com" -s sub "(objectClass=inetOrgPerson)" dn
output
result: 4 Size limit exceeded
# numEntries: 3

The server returned three entries and stopped with LDAP result code 4.

Restore the global size limit:

bash
dsconf ldap1 config replace nsslapd-sizelimit=2000

Time limit

Set a global time limit in seconds:

bash
dsconf ldap1 config replace nsslapd-timelimit=60
output
Successfully replaced value(s) for 'nsslapd-timelimit': '60'

A tiny lab suffix often finishes every search before the limit is reached, so this build may not produce err=3 during testing. On a sufficiently large suffix, bind as a non-Directory-Manager account, run an intentionally expensive search, and look for Time limit exceeded (3) in the client output and access log.

Restore the lab default when testing finishes:

bash
dsconf ldap1 config replace nsslapd-timelimit=3600

Look-through limit

Raise the global look-through limit only when representative filters genuinely need to examine more candidates:

bash
dsconf ldap1 backend config set --lookthroughlimit=10000
output
Successfully updated database configuration
bash
dsconf ldap1 backend config get | grep nsslapd-lookthroughlimit
output
nsslapd-lookthroughlimit: 10000

Look-through limits apply while the server evaluates a filter, not only when entries are returned. A filter that returns 250 entries can still examine 8,000 candidates along the way.

Restore the original value after the demonstration:

bash
dsconf ldap1 backend config set --lookthroughlimit=5000

Do not increase the look-through limit merely to hide an indexing problem.


Configure paged and range search limits

Simple paged results let a client walk a large result set in smaller chunks using the LDAP paged-results control.

Setting What it controls
nsslapd-pagedsizelimit / nsPagedSizeLimit Maximum entries returned by a complete paged search operation
-E pr=50/noprompt on ldapsearch Client-requested page batch size for one response

Check the current paged size limit:

bash
dsconf ldap1 config get nsslapd-pagedsizelimit
output
nsslapd-pagedsizelimit: 0

When the value is 0, paged searches inherit nsslapd-sizelimit. Set a positive value such as 500 with dsconf ldap1 config replace nsslapd-pagedsizelimit=500 when a complete paged search must not return more than 500 entries total. The client's requested page size in the paged-results control remains a separate value.

Paged look-through and paged ID-list limits are global LDBM plug-in settings:

bash
dsconf ldap1 backend config get | grep -E 'pagedlookthrough|pagedidlist'
output
nsslapd-pagedlookthroughlimit: 0
nsslapd-pagedidlistscanlimit: 0

Values of 0 inherit nsslapd-lookthroughlimit and nsslapd-idlistscanlimit. Use -1 on the paged attribute when the paged search must be explicitly unlimited.

Range searches use nsslapd-rangelookthroughlimit. The lab default is 5000. Directory Manager is not exempt from range look-through limits.

Test the simple paged-results control

This command proves the client and server support paging. It does not by itself prove enforcement of nsslapd-pagedsizelimit; that requires a suffix large enough to exceed the configured total paged cap.

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "uid=reporting-app,ou=ServiceAccounts,dc=example,dc=com" -W -b "ou=people,dc=example,dc=com" -s sub "(objectClass=inetOrgPerson)" uid -E pr=50/noprompt 2>&1 | tail -6

When paging works, the access log records notes=P on the RESULT lines. Test total paged-search enforcement separately on a populated suffix with nsslapd-pagedsizelimit or nsPagedSizeLimit set below the result count.


Configure limits for one user or application

Service accounts often need tighter caps than the global defaults. Store overrides on the user entry as operational attributes. The example below demonstrates common controls; it is not the complete supported set. Official documentation also lists nsIDListScanLimit, nsPagedLookThroughLimit, and nsPagedIDListScanLimit. Range look-through has no per-user counterpart.

Save reporting-app-limits.ldif:

ldif
dn: uid=reporting-app,ou=ServiceAccounts,dc=example,dc=com
changetype: modify
replace: nsSizeLimit
nsSizeLimit: 2
-
replace: nsTimeLimit
nsTimeLimit: 60
-
replace: nsLookThroughLimit
nsLookThroughLimit: 5000
-
replace: nsPagedSizeLimit
nsPagedSizeLimit: 100
bash
ldapmodify -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f reporting-app-limits.ldif

Read the effective per-user limits with +:

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -b "uid=reporting-app,ou=ServiceAccounts,dc=example,dc=com" -s base "(objectClass=*)" "+" nsSizeLimit nsTimeLimit nsLookThroughLimit nsPagedSizeLimit
output
nsSizeLimit: 2
nsTimeLimit: 60
nsLookThroughLimit: 5000
nsPagedSizeLimit: 100

With nsslapd-sizelimit at 2000, the per-user nsSizeLimit: 2 still wins for this bind DN:

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "uid=reporting-app,ou=ServiceAccounts,dc=example,dc=com" -W -b "ou=people,dc=example,dc=com" -s sub "(objectClass=inetOrgPerson)" dn
output
result: 4 Size limit exceeded
# numEntries: 2

Protect operational limit attributes with ACIs so users cannot raise their own caps.


Tune ID-list scan limits

When one index key points at a very large candidate set, Directory Server can treat the search as partially unindexed even though the attribute is indexed.

text
Indexed value matches 50,000 entry IDs
        |
        v
ID-list scan limit is 4,000
        |
        v
Server stops loading that index list
        |
        v
Search is processed as an unindexed search

Check the active global value:

bash
dsconf ldap1 backend config get | grep nsslapd-idlistscanlimit
output
nsslapd-idlistscanlimit: 4000

On the tested upstream 389 Directory Server 3.2.0 build, dsconf reported 4000. Current RHDS 13 documentation states that changes to the global ID-list setting affect subsequent searches without a restart. Because the attribute is deprecated and its defaults vary by release, inspect rather than change it unless performance testing demonstrates a specific need. Prefer selective nsIndexIDListScanLimit rules, narrower filters, and better indexes over broadly raising the global setting.

Fine-grained ID-list rules

Fine-grained rules modify a specific index entry in a specific backend. The objectClass equality index under userroot is a common target for (&(objectClass=inetOrgPerson)(...)) filters.

IMPORTANT
Do not add a limit=0 rule merely because objectClass matches many entries. Confirm through access logs and representative filters that another selective indexed term can efficiently drive the search.

Save objectclass-idlist-limit.ldif:

ldif
dn: cn=objectclass,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config
changetype: modify
add: nsIndexIDListScanLimit
nsIndexIDListScanLimit: limit=0 type=eq flags=AND values=inetOrgPerson
bash
ldapmodify -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f objectclass-idlist-limit.ldif

The syntax is limit=NNN [type=eq[,sub,...]] [flags=AND[,XXX,...]] [values=val[,val,...]]. Setting limit=0 for objectClass=inetOrgPerson in an AND filter deliberately avoids building an ID list for that value.

Value Meaning
-1 Unlimited ID list (where supported on the fine-grained rule)
0 Do not use the ID list for the matching condition
Positive number Maximum IDs loaded

Remove the rule when the lab demonstration finishes. Save remove-objectclass-idlist-limit.ldif:

ldif
dn: cn=objectclass,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config
changetype: modify
delete: nsIndexIDListScanLimit
nsIndexIDListScanLimit: limit=0 type=eq flags=AND values=inetOrgPerson
bash
ldapmodify -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f remove-objectclass-idlist-limit.ldif

Diagnose search-limit problems in access logs

text
/var/log/dirsrv/slapd-ldap1/access
bash
grep -E 'notes=.*(A|U|P)|err=4|err=3' /var/log/dirsrv/slapd-ldap1/access | tail -5
output
[17/Jul/2026:23:47:01 +0530] conn=79 op=1 RESULT err=0 tag=101 nentries=1 etime=0.014392270 notes=U details="Partially Unindexed Filter"
[17/Jul/2026:23:47:19 +0530] conn=85 op=1 RESULT err=4 tag=101 nentries=3 etime=0.004090297
[17/Jul/2026:23:47:47 +0530] conn=3 op=7 RESULT err=0 tag=101 nentries=50 etime=0.022746790 notes=P details="Paged Search"
Field Meaning
notes=A Every candidate filter attribute was unindexed; a full scan was required
notes=U At least one term was unindexed, or an index key exceeded nsslapd-idlistscanlimit
notes=P Simple paged-results control was used
err=4 Size limit exceeded
err=3 Time limit exceeded
High etime The operation took longer to complete on the server

notes=U does not always mean a missing index file. When every indexed attribute is present but notes=U persists, check whether the ID-list scan limit was exceeded before adding more indexes.

Correlate SRCH, BIND, and RESULT lines by conn and op numbers.


Test and troubleshoot

Test Identity Setting Expected signal
Global size cap reporting-app nsslapd-sizelimit=3 err=4 with nentries equal to the cap
Per-user size cap reporting-app nsSizeLimit: 2 err=4 after two entries
Directory Manager bypass Directory Manager nsslapd-sizelimit=3 More than three entries returned
Paged control reporting-app -E pr=50/noprompt notes=P in access log
ID-list threshold Application bind nsslapd-idlistscanlimit notes=U on broad indexed filters
Symptom Likely cause Fix
Size limit never triggers Tests use Directory Manager Repeat with the application bind DN
Application hits size limit unexpectedly Per-user nsSizeLimit is lower than global Read operational attributes with ldapsearch ... "+"
Paged search stops below client page size Total paged cap hit Check nsslapd-pagedsizelimit or nsPagedSizeLimit; remember 0 inherits the ordinary limit
Time limit never triggers in a small lab Suffix too small Test on a populated suffix with a non-Directory-Manager bind

Security and operational recommendations:

  • Test limit changes with the real application bind DN, not Directory Manager.
  • Avoid -1 unlimited size or time limits except on tightly controlled administrative accounts.
  • Keep per-service-account limit attributes in change control alongside ACIs.
  • Use simple paged results for large directory browsers instead of raising global size limits alone.
  • Treat notes=U as a limit or index design problem before blindly raising nsslapd-idlistscanlimit.
  • Change one setting at a time and retest with access logs.

What's next

After you complete this guide, continue with:

Summary

  1. Instance attributes in cn=config cap ordinary searches; global LDBM plug-in attributes control look-through and ID-list behaviour.
  2. Directory Manager bypasses global size and time limits; validate changes with application bind accounts.
  3. Per-entry operational attributes override global defaults for that bind DN.
  4. Paged limit 0 inherits the corresponding ordinary limit; -1 means explicitly unlimited where the schema allows it.
  5. nsslapd-idlistscanlimit behaviour varies by release; prefer fine-grained nsIndexIDListScanLimit rules and better filters over broad global changes.
  6. notes=U signals unindexed work or an ID-list cap, not only a missing index file.

For worker threads, listeners, and connection descriptors, see Configure LDAP threads and connections.


References


Frequently Asked Questions

1. Does Directory Manager bypass global search size and time limits?

Yes. Directory Manager receives unlimited search size and time resources by default, with the exception of range searches. Lowering nsslapd-sizelimit does not cap Directory Manager the way it caps ordinary application binds.

2. Does notes=U always mean a missing index?

No. notes=U appears when at least one search term is unindexed or when a matching index result exceeds nsslapd-idlistscanlimit. A fully indexed filter can still log notes=U when the ID-list scan limit is exceeded.

3. What does -1 mean for search limit attributes?

-1 means unlimited only for attributes whose documented valid range supports it, such as size, time, look-through, paged, and range limits. Do not assume -1 is accepted by every global ID-list attribute; check the schema reference for the installed release.

4. What does 0 mean for paged search limit attributes?

For nsslapd-pagedsizelimit, nsslapd-pagedlookthroughlimit, and nsslapd-pagedidlistscanlimit, 0 means inherit the corresponding ordinary search limit. For example, nsslapd-pagedsizelimit: 0 makes a paged search use nsslapd-sizelimit. Use -1 only when the paged limit must be explicitly unlimited.

5. Which search limit takes precedence when per-user attributes are set?

Per-entry operational attributes such as nsSizeLimit on the binding user override global nsslapd-sizelimit for that bind DN. Global LDBM database plug-in limits still apply unless the user entry also defines the matching nsLookThroughLimit, nsIDListScanLimit, or related operational override.
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 …