Configure 389 Directory Server Indexes for Faster LDAP Searches

Configure 389 Directory Server indexes with dsconf backend index, detect notes=A and notes=U unindexed searches, and reindex online or offline for faster LDAP filters.

Published

Updated

Read time 13 min read

Reviewed byDeepak Prasad

389 Directory Server LDAP database indexes accelerating equality and substring searches through dsconf backend index configuration

Slow LDAP searches usually mean a filter is running without a suitable index. Directory Server maintains separate index data for each indexed attribute on each database backend, and one attribute can carry several index types. Indexes accelerate matching searches but consume disk space and add work to add, modify, and delete operations.

This guide covers standard LDAP indexing in 389 Directory Server:

  • Presence, equality, approximate, substring, and matching-rule indexes
  • Choosing index types from real application filters
  • Creating and modifying per-backend indexes
  • Online and offline reindexing
  • Default indexes for future databases
  • Detecting unindexed and partially indexed searches
  • Substring key tuning and safe index removal

Before you start:

IMPORTANT
This guide covers standard database indexes for LDAP search filters. It does not configure Virtual List View indexes, search size and ID-list limits, LMDB cache tuning, large-group performance, or repeatable performance benchmarking.

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 primary backend is userroot for suffix dc=example,dc=com.


How LDAP indexes work in 389 Directory Server

text
LDAP search filter
        |
        v
Directory Server checks available index keys
        |
        +--> Suitable index exists
        |        |
        |        v
        |   Load matching entry IDs
        |
        +--> No usable index
                 |
                 v
          Scan database entries

Indexes are configured per database backend:

text
Suffix: dc=example,dc=com
Backend: userroot

An index created in userroot does not configure the same attribute in another backend.

One attribute index entry can list several index types:

text
cn:
- pres
- eq
- sub

LDAP index types

Index type Identifier Example filter Use
Presence pres (mail=*) Find entries that contain an attribute
Equality eq (uid=user1) Match one normalized value
Approximate approx (cn~=Jon Smith) Phonetic or approximate matching
Substring sub (cn=*smith*) Match part of a value
Matching-rule index OID-based Extensible or language search Specialized ordering or collation

Equality indexes

Use equality indexes for frequent exact-value searches:

text
(uid=user1)
([email protected])
(employeeNumber=10025)
(member=uid=user1,ou=People,dc=example,dc=com)

Presence indexes

Use presence indexes when applications search for entries that contain an attribute:

text
(mail=*)
(employeeNumber=*)

Do not create a presence index only because an attribute exists on most entries. If nearly the whole database matches, the index may provide little benefit.

Approximate indexes

Use approximate indexes only when clients submit approximate filters such as (cn~=John Smith). Approximate matching uses a phonetic algorithm intended for US-ASCII English values. It is not a general multilingual or numeric search index.

Substring indexes

Use substring indexes for filters such as (cn=John*), (cn=*Smith), or (telephoneNumber=*555*). Substring indexes require substantially more keys and write work than equality indexes. They do not apply to binary attributes.

IMPORTANT
Do not define a substring index for membership attributes such as member or uniqueMember. When members are added or removed on a large group, substring maintenance can evaluate far more values than the single change. Use equality indexes for exact DN membership filters such as (member=uid=user1,ou=People,dc=example,dc=com).

Plan indexes from filters and find unindexed searches

Do not begin by indexing every attribute. Collect frequently used filters from application configuration, access logs, monitoring tools, SSSD or identity-client searches, administrative scripts, and group or role lookups.

Search filter Attribute Required index
(uid=user1) uid eq
(mail=*) mail pres
(telephoneNumber=*555*) telephoneNumber sub
(cn~=John Smith) cn approx
(member=USER_DN) member eq

For compound filters such as (&(objectClass=inetOrgPerson)(employeeNumber=10025)), review index coverage for each filter component. The goal is not zero notes=U events at any cost. Index frequent and expensive queries without creating unnecessary write and storage overhead.

Review the access log on the server host:

text
/var/log/dirsrv/slapd-ldap1/access

Search for index-related notes:

bash
grep -E 'notes=.*(A|U)' /var/log/dirsrv/slapd-ldap1/access
output
[17/Jul/2026:23:23:19 +0530] conn=18 op=1 RESULT err=0 tag=101 nentries=1 etime=0.002870207 notes=U details="Partially Unindexed Filter"

Correlate SRCH and RESULT records by connection ID and operation ID. Record the search base, scope, filter, bind DN, entries returned, etime, and notes field.

Indicator Meaning
notes=A All candidate filter attributes were unindexed and a full scan was required
notes=U At least part of the search was unindexed, or an index exceeded the ID-list scan limit
High etime The operation took longer to complete on the server

notes=U does not always mean an index file is missing. A search can also be treated as partially unindexed when a matching index key produces more entry IDs than the configured ID-list scan limit allows. Keep ID-list limit tuning in search limits.

Before adding an index, run the target filter several times and record client time, server etime, returned entry count, and whether notes=A or notes=U appears. Repeat after reindexing instead of trusting one cached result. The roomNumber example in the next section shows a full before-and-after run.


Check existing database indexes

List indexes configured for userroot:

bash
dsconf ldap1 backend index list userroot

Sample output:

output
dn: cn=cn,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config
cn: cn
nsIndexType: pres
nsIndexType: eq
nsIndexType: sub

Inspect one attribute:

bash
dsconf ldap1 backend index get userroot --attr roomNumber

Alternatively, read the backend index configuration directly:

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -b "cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config" -s one cn nsIndexType nsMatchingRule nsSystemIndex

Identify existing index types, system indexes, custom indexes, attributes configured but not rebuilt, and indexes no longer used by applications. Do not remove system or default operational indexes only because they do not appear in an application search log.


Create and modify indexes

Use roomNumber as the primary custom-index example because it is not part of the default index set and supports straightforward equality, presence, and substring tests.

Confirm roomNumber has no index entry yet:

bash
dsconf ldap1 backend index get userroot --attr roomNumber

On a fresh backend index get returns no nsIndexType lines for the attribute. If a previous test left configuration behind, remove it with dsconf ldap1 backend index delete --attr roomNumber userroot before continuing.

Assign a test value on the standard lab entry. Save add-roomNumber.ldif:

ldif
dn: uid=user1,ou=People,dc=example,dc=com
changetype: modify
add: roomNumber
roomNumber: 401
bash
ldapmodify -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f add-roomNumber.ldif

The modify completes with no output when the attribute is added successfully.

Run the target filter before creating any index:

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -b "dc=example,dc=com" "(roomNumber=401)" dn roomNumber
output
dn: uid=user1,ou=People,dc=example,dc=com
roomNumber: 401

Correlate the matching SRCH and RESULT lines in /var/log/dirsrv/slapd-ldap1/access by connection ID and operation ID:

output
conn=93 op=1 SRCH base="dc=example,dc=com" scope=2 filter="(roomNumber=401)" attrs="distinguishedName roomNumber"
conn=93 op=1 RESULT err=0 tag=101 nentries=1 etime=0.035569932 notes=U details="Partially Unindexed Filter"

Directory Server logged notes=U because roomNumber had no equality index yet. A filter where every candidate attribute is unindexed can also appear as notes=A.

Create an equality index and rebuild existing data immediately:

bash
dsconf ldap1 backend index add --attr roomNumber --index-type eq --reindex userroot
output
Index task index_attrs_2026-07-17T23:38:24.821495 completed successfully
Successfully added index

Creating the configuration without rebuilding does not populate index keys for entries that already existed.

Verify the new index:

bash
dsconf ldap1 backend index get userroot --attr roomNumber
output
nsIndexType: eq

Rerun the same LDAP search:

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -b "dc=example,dc=com" "(roomNumber=401)" dn roomNumber
output
dn: uid=user1,ou=People,dc=example,dc=com
roomNumber: 401

The LDAP result is unchanged. The correlated access-log RESULT line no longer carries an unindexed note:

output
conn=100 op=1 SRCH base="dc=example,dc=com" scope=2 filter="(roomNumber=401)" attrs="distinguishedName"
conn=100 op=1 RESULT err=0 tag=101 nentries=1 etime=0.000376217

Server etime dropped from 0.035569932 to 0.000376217 in this small lab. On a tiny suffix the client may not see a meaningful timing difference, but the access log confirms the filter is now fully indexed.

Add a presence index type to the same attribute:

bash
dsconf ldap1 backend index set --attr roomNumber --add-type pres userroot

Rebuild the attribute:

bash
dsconf ldap1 backend index reindex --attr roomNumber --wait userroot
output
Index task index_attrs_2026-07-17T23:40:28.976951 completed successfully
Successfully reindexed database

Remove one index type when it is no longer needed:

bash
dsconf ldap1 backend index set --attr roomNumber --del-type pres userroot

Reindex again after changing index types. The same workflow applies to common production attributes such as uid, mail, employeeNumber, and member in userroot or any other backend where applications search. Attributes such as cn and telephoneNumber are already present in the default index set on a normal userroot backend, so use index set --add-type rather than index add when extending them.


Tune 389 Directory Server substring indexes

Continue with roomNumber, which already has an equality index from the previous section. Add substring indexing when wildcard searches on that attribute are frequent:

bash
dsconf ldap1 backend index set --attr roomNumber --add-type sub --reindex userroot
output
Index task index_attrs_2026-07-17T23:39:06.803455 completed successfully
Index successfully updated

The 389 DS 3.2.0 CLI uses index add for a new entry and index set --add-type for an existing one. This section extends roomNumber; it does not run index add on telephoneNumber.

telephoneNumber and cn are part of the standard default index set. A normal userroot backend already has index entries for those attributes, so index add would fail with a duplicate entry. Inspect an existing attribute first:

bash
dsconf ldap1 backend index get userroot --attr telephoneNumber

Use index set --add-type only when the required type is absent.

Test distinct substring patterns on roomNumber:

text
(roomNumber=40*)
(roomNumber=*01*)
(roomNumber=*401)

By default, the minimum substring search-key length is three characters. Filters with shorter fragments might not use the substring index as expected.

Change the substring search-key length

Save substring-key-length.ldif:

ldif
dn: cn=roomNumber,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config
changetype: modify
add: objectClass
objectClass: extensibleObject
-
add: nsSubStrBegin
nsSubStrBegin: 2
-
add: nsSubStrMiddle
nsSubStrMiddle: 2
-
add: nsSubStrEnd
nsSubStrEnd: 2
bash
ldapmodify -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f substring-key-length.ldif

The modify completes with no output when the substring key settings are accepted.

Rebuild the attribute:

bash
dsconf ldap1 backend index reindex --attr roomNumber --wait userroot
Setting Pattern affected
nsSubStrBegin abc*
nsSubStrMiddle *abc*
nsSubStrEnd *abc

Shorter search keys create more index keys and increase index size, memory pressure, and update cost.


Approximate and matching-rule indexes

Create an approximate index only when applications issue approximate filters. The cn attribute already has an index entry on a normal userroot backend:

bash
dsconf ldap1 backend index get userroot --attr cn
output
nsIndexType: pres
nsIndexType: eq
nsIndexType: sub

Add approx to the existing entry rather than running index add:

bash
dsconf ldap1 backend index set --attr cn --add-type approx --reindex userroot

--add-type and --reindex are supported on index set in 389 DS 3.2.0.

output
Index task index_attrs_2026-07-17T23:39:12.291897 completed successfully
Index successfully updated

Test:

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -b "dc=example,dc=com" "(cn~=Jon Smith)" dn cn

Approximate matching uses a phonetic algorithm intended for US-ASCII English values. Create the index only when clients actually submit ~= filters.

For international or extensible matching, confirm the attribute syntax, obtain the matching-rule OID, and add it to the index configuration. The 3.2.0 CLI exposes --matching-rule on index add and --add-mr on index set.

For a new index entry:

bash
dsconf ldap1 backend index add --attr ATTRIBUTE --index-type eq --matching-rule MATCHING_RULE_OID --reindex userroot

For an existing index entry:

bash
dsconf ldap1 backend index set --attr ATTRIBUTE --add-mr MATCHING_RULE_OID --reindex userroot

Reindex the attribute and test the exact extensible filter the client generates. Keep the complete explanation of LDAP filter syntax and extensible matching in your search-filter documentation.


Reindex online and offline

Online reindex while Directory Server is running

Reindex one attribute:

bash
dsconf ldap1 backend index reindex --attr roomNumber --wait userroot

Monitor /var/log/dirsrv/slapd-ldap1/errors while the task runs. Online reindexing keeps the server available, but searches involving the rebuilding attribute can be incomplete or inconsistent until the task finishes. Schedule large online rebuilds during reduced activity when temporary inconsistency is acceptable.

Offline rebuild with dsctl db2index

Stop the instance:

Offline instance work in this section uses dsctl commands.

bash
dsctl ldap1 stop

Rebuild selected attributes:

bash
dsctl ldap1 db2index userroot --attr roomNumber employeeNumber

Rebuild all configured indexes:

bash
dsctl ldap1 db2index userroot

Start the instance:

bash
dsctl ldap1 start

Use offline rebuilding when search consistency must be preserved, many indexes need rebuilding, an index may be damaged, or a maintenance window is available. Back up the database before major index repairs.


Default indexes for new databases

Default indexes are stored under:

text
cn=default indexes,cn=config,cn=ldbm database,cn=plugins,cn=config

List them:

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -b "cn=default indexes,cn=config,cn=ldbm database,cn=plugins,cn=config" -s one cn nsIndexType nsSystemIndex

Add a default index for future backends. Save default-roomNumber-index.ldif:

ldif
dn: cn=roomNumber,cn=default indexes,cn=config,cn=ldbm database,cn=plugins,cn=config
objectClass: top
objectClass: nsIndex
cn: roomNumber
nsSystemIndex: false
nsIndexType: eq
nsIndexType: sub
bash
ldapadd -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -f default-roomNumber-index.ldif
output
adding new entry "cn=roomNumber,cn=default indexes,cn=config,cn=ldbm database,cn=plugins,cn=config"

Verify the new default index entry:

bash
ldapsearch -x -H ldap://ldap1.example.com:389 -D "cn=Directory Manager" -W -b "cn=default indexes,cn=config,cn=ldbm database,cn=plugins,cn=config" -s one "(cn=roomNumber)" cn nsIndexType nsSystemIndex
output
dn: cn=roomNumber,cn=default indexes,cn=config,cn=ldbm database,cn=plugins,cn=config
cn: roomNumber
nsIndexType: eq
nsIndexType: sub
nsSystemIndex: false

Red Hat documents that default indexes are maintained through LDIF and copied only into newly created databases. Default index changes do not update backends that already exist. Configure and reindex existing backends separately. Use default indexes only for attributes that should consistently be indexed in every future backend.


Remove indexes, monitor overhead, and troubleshoot

Before removing an index:

  1. Search access logs for use of the attribute.
  2. Identify all relevant filter types.
  3. Record the existing configuration.
  4. Test in a nonproduction environment.
  5. Remove one index type rather than the whole attribute where possible.
  6. Rebuild the remaining index.
  7. Monitor for notes=A, notes=U, and increased etime.

Remove one index type:

bash
dsconf ldap1 backend index set --attr description --del-type sub userroot

Remove the complete attribute index:

bash
dsconf ldap1 backend index delete --attr description userroot

Do not remove system indexes or frequently used default indexes solely to save disk space.

More indexes generally mean faster matching searches, larger database files, slower writes, and longer imports and rebuilds. Pay particular attention to substring indexes, attributes with many values, frequently modified attributes, large group membership attributes, and indexes that match nearly every entry.

Symptom Likely cause Fix
Search returns inconsistent results after a change Online reindex still running Check errors log; complete offline db2index if consistency is required
Equality is fast but wildcard search is slow Only eq exists for the attribute Add sub only after confirming the wildcard search is frequent and necessary
Two-character substring search stays unindexed Default three-character key length Tune nsSubStrBegin, nsSubStrMiddle, nsSubStrEnd and reindex
Adds and modifies became slower Recent sub, approx, or broad pres indexes Remove unused types; avoid substring indexes on member or uniqueMember
Index exists in one suffix but not another Indexes are per backend Configure and rebuild in every backend applications search
Existing entries missing but new entries match Configuration without completed rebuild Reindex the attribute and verify task completion

What's next

After you complete this guide, continue with:

Summary

  1. Collect real search filters and access-log evidence.
  2. Map each filter to the required index type.
  3. Create the index in the correct backend.
  4. Reindex existing data.
  5. Retest the exact application query.
  6. Monitor search speed, disk usage, and write latency.
  7. Tune substring keys only for proven short-fragment searches.
  8. Remove unused indexes cautiously.
  9. Keep VLV and ID-list-limit tuning in their dedicated articles.

Directory Server supports presence, equality, approximate, substring, and OID-based matching-rule indexes. Approximate indexes target English phonetic searches. Substring indexes are more expensive to maintain and are inappropriate for member and uniqueMember. Online reindex keeps the server available but can return inconsistent results until the task completes; offline dsctl db2index is safer when consistency matters.


References


Frequently Asked Questions

1. Does creating an index configuration immediately speed up searches for existing entries?

No. The index entry must be populated for existing data. Use --reindex on add, dsconf backend index reindex, or dsctl db2index after offline stop. Newly added entries are indexed on write once configuration exists.

2. What is the difference between notes=A and notes=U in the access log?

notes=A means every candidate filter attribute was unindexed and the server scanned entries. notes=U means at least part of the search was unindexed or an index result exceeded the ID-list scan limit. notes=U does not always mean a missing index file.

3. Should I index member with substring type for group searches?

No. Red Hat advises against substring indexes on membership attributes such as member and uniqueMember. Use equality indexes for exact DN membership filters. Substring maintenance on large groups is expensive when members change.

4. When should I use offline db2index instead of online reindex?

Use offline rebuilding when search consistency matters, many indexes need rebuilding, an index may be damaged, or the database is large. Online reindex keeps the server available but searches against the rebuilding attribute can be inconsistent until the task finishes.

5. Do default indexes update existing databases automatically?

No. Default index entries under cn=default indexes apply when a new database is created. Existing backends such as userroot must be configured and reindexed separately.
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 …