OpenLDAP cn=config and MDB Explained with ldapmodify Examples

Understand OpenLDAP cn=config and MDB, discover numbered database DNs, query modules and overlays, and safely modify settings with ldapmodify.

Published

Updated

Read time 24 min read

Reviewed byDeepak Prasad

OpenLDAP cn=config tree and MDB database configuration with ldapmodify

OpenLDAP does not store its active server configuration in a single flat file you edit by hand. Modern RHEL-family packages use the dynamic cn=config tree, which you query and change through LDAP operations on the local server.

This guide walks through how to:

  • understand the cn=config directory structure and how it differs from your directory suffix
  • discover the correct numbered MDB database DN on the current host
  • query global, database, module, and overlay settings
  • use ldapmodify with add, replace, and delete
  • change logging, MDB size, indexes, and read-only mode
  • load a packaged module safely
  • back up, validate, and recover cn=config

The examples apply to OpenLDAP 2.6 on Red Hat Enterprise Linux, Rocky Linux, AlmaLinux, Oracle Linux, and CentOS Stream.

Tested on: Rocky Linux 10.2; OpenLDAP 2.6.10 from EPEL on ldap-server.example.com.

The lab server in this walkthrough uses:

Setting Value
Hostname ldap-server.example.com
Configuration database cn=config
Directory suffix dc=example,dc=com
Data backend MDB
Configuration access SASL EXTERNAL over ldapi:///
Generated configuration directory /etc/openldap/slapd.d/

Complete these lessons first:

IMPORTANT
Do not manually edit files under /etc/openldap/slapd.d/ while slapd is running. Use LDAP operations for online changes and the OpenLDAP slap* tools for offline validation or recovery.

Understand cn=config, MDB, and OLC

Three names appear together in almost every OpenLDAP operations conversation:

Term Meaning
cn=config Distinguished name at the root of the OpenLDAP configuration directory
slapd-config OpenLDAP dynamic runtime configuration system
OLC Online Configuration—the common name for cn=config
MDB OpenLDAP database backend based on LMDB
slapd.d Generated on-disk LDIF representation of cn=config

cn=config and your application directory are separate LDAP trees on the same slapd process:

text
OpenLDAP slapd
├── cn=config
│   ├── Global server settings
│   ├── Module definitions
│   ├── Schemas
│   ├── Backends
│   ├── Database definitions
│   └── Overlay definitions
└── dc=example,dc=com
    ├── Users
    ├── Groups
    └── Application directory entries

Keep these boundaries in mind:

  • cn=config stores server configuration.
  • dc=example,dc=com stores normal directory data.
  • Modifying cn=config changes how slapd operates.
  • Modifying the directory suffix changes users, groups, or application entries.
  • The database administrator DN for dc=example,dc=com does not automatically have permission to modify cn=config.
  • Root normally administers cn=config locally through SASL EXTERNAL and the LDAPI socket.
Operation Configuration tree Data tree
Query server log level cn=config No
Change MDB maximum size MDB entry below cn=config No
Add a user No dc=example,dc=com — see Manage OpenLDAP users and groups
Configure TLS cn=config No
Change a user's mail address No Directory suffix
Load a module Module entry below cn=config No

For DN, suffix, and schema terminology, see LDAP and OpenLDAP basics.


Explore the cn=config directory tree

Confirm that slapd is running before you query configuration:

bash
sudo systemctl is-active slapd

Sample output:

output
active

An inactive or failed result means ldapsearch and ldapmodify against ldapi:/// will not work until you start the service.

List every configuration DN. This search is read-only and a good first look at what your server actually loaded:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b cn=config \
  dn

The options in this command matter on RHEL-family systems:

Option Purpose
-Q Suppresses the SASL confirmation prompt
-LLL Produces concise LDIF output
-Y EXTERNAL Uses the local process identity for SASL authentication
-H ldapi:/// Connects through the local Unix-domain socket
-b cn=config Searches the configuration directory

Sample output from the lab server:

output
dn: cn=config

dn: cn=module{0},cn=config

dn: cn=schema,cn=config

dn: cn={0}core,cn=schema,cn=config

dn: cn={1}cosine,cn=schema,cn=config

dn: cn={2}nis,cn=schema,cn=config

dn: cn={3}inetorgperson,cn=schema,cn=config

dn: olcDatabase={-1}frontend,cn=config

dn: olcDatabase={0}config,cn=config

dn: olcDatabase={1}monitor,cn=config

dn: olcDatabase={2}mdb,cn=config

dn: olcOverlay={0}syncprov,olcDatabase={2}mdb,cn=config

Major entries you will touch during day-2 operations:

DN Role
cn=config Global slapd settings
cn=module{0},cn=config Dynamically loaded modules
cn=schema,cn=config Schema container
cn={N}name,cn=schema,cn=config Individual loaded schema
olcDatabase={-1}frontend,cn=config Defaults inherited by databases
olcDatabase={0}config,cn=config Configuration database itself
olcDatabase={N}monitor,cn=config Runtime monitoring database
olcDatabase={N}mdb,cn=config Normal MDB directory database
olcOverlay={N}name,... Overlay attached to a database

Understand numbered configuration DNs

Entries such as these are normal on a live server:

text
olcDatabase={2}mdb,cn=config
cn={1}cosine,cn=schema,cn=config
olcOverlay={0}syncprov,olcDatabase={2}mdb,cn=config

The value inside braces is an ordering prefix assigned when OpenLDAP created the entry. The backend type still appears after the prefix—mdb, cosine, syncprov, and so on.

Numbering can differ between installations. Installing a module, schema, database, or overlay can change the numbers used in later entries. {2}mdb from one tutorial is not authoritative on your host.

WARNING
Do not assume that the main directory database is always olcDatabase={2}mdb,cn=config. Query the suffix or olcMdbConfig object class and use the DN returned by the current server.
Incorrect assumption Likely result
Using {1}mdb from another server No such object (32)
Omitting mdb from the DN Invalid or nonexistent target
Targeting cn=config for an MDB attribute Object class or attribute error
Targeting the data suffix with ldapmodify -Y EXTERNAL Changes directory data, not server configuration

Discover the correct MDB database DN

List every MDB database configured on the server:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b cn=config \
  '(objectClass=olcMdbConfig)' \
  dn olcSuffix olcDbDirectory

Sample output:

output
dn: olcDatabase={2}mdb,cn=config
olcDbDirectory: /var/lib/ldap
olcSuffix: dc=example,dc=com

The olcSuffix line confirms which directory tree this database serves. The olcDbDirectory line shows where MDB files live on disk.

When more than one MDB database exists, filter by suffix instead of taking the first result. A single-database lab still benefits from this filter because it proves you are targeting the production suffix:

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config '(&(objectClass=olcMdbConfig)(olcSuffix=dc=example,dc=com))' dn olcSuffix olcDbDirectory

The result should show the same DN and suffix as the broader search above.

Store the discovered DN in a shell variable for the rest of the session:

bash
MDB_DN=$(
  sudo ldapsearch \
    -Q \
    -LLL \
    -Y EXTERNAL \
    -H ldapi:/// \
    -b cn=config \
    '(&(objectClass=olcMdbConfig)(olcSuffix=dc=example,dc=com))' \
    dn |
  awk '/^dn: / {sub(/^dn: /, ""); print; exit}'
)

The awk line strips the dn: prefix and prints only the first matching database DN.

Fail safely when no matching database exists. A script should stop before it writes an empty DN into an LDIF file:

bash
if [[ -z "$MDB_DN" ]]; then
  echo "No MDB database was found for dc=example,dc=com" >&2
  exit 1
fi

Print the DN you will use in later LDIF files:

bash
printf 'MDB database DN: %s\n' "$MDB_DN"

Sample output:

output
MDB database DN: olcDatabase={2}mdb,cn=config

Filtering by suffix is safer than assuming the first olcMdbConfig entry is your production directory.


Query global, database, module, and overlay settings

Set MDB_DN from the previous section before you run the database queries below.

Query global server settings

Global TLS and logging attributes live on cn=config itself:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b cn=config \
  -s base \
  olcLogLevel \
  olcIdleTimeout \
  olcReferral \
  olcTLSCACertificateFile \
  olcTLSCertificateFile

Sample output on the lab server after the TLS lesson:

output
dn: cn=config
olcTLSCACertificateFile: /etc/openldap/certs/example-ldap-ca.crt
olcTLSCertificateFile: /etc/openldap/certs/ldap-server.example.com.crt

Attributes that are not set yet simply do not appear in the result. To issue certificates and set olcTLS* paths on a new server, follow Configure OpenLDAP TLS on RHEL-based Linux.

Query generic database settings

Read suffix, root DN, and limits on the MDB entry:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b "$MDB_DN" \
  -s base \
  olcSuffix \
  olcRootDN \
  olcReadOnly \
  olcSizeLimit \
  olcTimeLimit

Sample output:

output
dn: olcDatabase={2}mdb,cn=config
olcSuffix: dc=example,dc=com
olcRootDN: cn=admin,dc=example,dc=com

olcRootDN is the bind DN you use for simple-bind administration of directory data—not for cn=config changes through LDAPI.

Query MDB-specific settings

MDB file location, map size, and indexes are database attributes:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b "$MDB_DN" \
  -s base \
  olcDbDirectory \
  olcDbMaxSize \
  olcDbMaxReaders \
  olcDbMaxEntrySize \
  olcDbIndex

Sample output:

output
dn: olcDatabase={2}mdb,cn=config
olcDbDirectory: /var/lib/ldap
olcDbIndex: objectClass eq,pres
olcDbIndex: ou,cn,mail,surname,givenname eq,pres,sub

olcDbMaxSize does not appear until you set it explicitly. Each olcDbIndex line is one index rule you can extend with add or change with delete/add.

List loaded modules

Module entries tell you which shared objects slapd has already loaded from olcModulePath:

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config '(objectClass=olcModuleList)' dn olcModulePath olcModuleLoad

Sample output:

output
dn: cn=module{0},cn=config
olcModulePath: /usr/lib64/openldap
olcModuleLoad: {0}syncprov.la

olcModulePath is the directory where slapd looks for filenames listed in olcModuleLoad.

List configured overlays

Overlays attach to a specific database DN. This search shows which overlays are active and where they sit in the tree:

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config '(objectClass=olcOverlayConfig)' dn olcOverlay

Sample output:

output
dn: olcOverlay={0}syncprov,olcDatabase={2}mdb,cn=config
olcOverlay: {0}syncprov

The overlay DN includes the parent database DN, which is why discovering $MDB_DN matters before you edit overlay settings elsewhere.

Component Meaning
Module Makes compiled functionality available to slapd
Overlay Attaches functionality to a specific database
Backend Defines a database implementation such as MDB
Database A configured instance of a backend
Schema Defines valid attributes and object classes

Loading a module does not automatically enable an overlay. The module only makes the functionality available; an overlay entry must then be added under the intended database. A syncprov overlay such as the one in the sample output belongs in provider-consumer replication; memberof and refint overlay walkthroughs are covered in Configure OpenLDAP memberOf and referential integrity overlays.


Back up cn=config before making changes

Create a root-only backup directory:

bash
sudo install \
  -d \
  -m 0700 \
  /root/openldap-config-backups

Export cn=config while slapd is stopped or running—slapcat -n 0 reads the configuration database. This exports only the configuration tree before a local configuration change. For recurring backup, retention, and disaster recovery across every MDB suffix, use OpenLDAP backup and restore. A planned server move that includes directory data uses the migration workflow in Migrate OpenLDAP to a new server.

bash
sudo slapcat \
  -n 0 \
  -l /root/openldap-config-backups/cn-config-before-change.ldif

The command exits silently on success.

Record a checksum so you can confirm the backup file later:

bash
sudo sha256sum \
  /root/openldap-config-backups/cn-config-before-change.ldif

Sample output:

output
c4969570bee390f69b89ecf7e84f15247f79cb422ddd7785602807ce0fbb361b  /root/openldap-config-backups/cn-config-before-change.ldif

Validate the live configuration before you change it:

bash
sudo slaptest -u

Sample output:

output
config file testing succeeded

A failure here means the current on-disk tree is already invalid—resolve that before you apply a new ldapmodify change.

Capture the target entry state when you need a precise rollback reference. The '*' '+' attribute list returns every user attribute plus operational attributes such as entryUUID:

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b "$MDB_DN" -s base '*' '+'

Save this output beside your change LDIF so you know the exact values to delete or restore if rollback is required.

Configuration backups can contain password hashes, replication credentials, TLS paths, bind credentials, internal structure, and ACL definitions. Keep them root-readable only.

Safe workflow for every production change:

text
1. Query the current value
2. Back up cn=config
3. Create a small LDIF containing one logical change
4. Review the target DN
5. Apply with ldapmodify
6. Query the value again
7. Check slapd logs
8. Run slaptest
9. Keep a rollback LDIF

Understand ldapmodify add, replace, and delete

LDIF modifications follow this structure:

ldif
dn: TARGET_DN
changetype: modify
OPERATION: attributeName
attributeName: new-or-existing-value

Compare the operations before you choose one:

Operation Behavior Common risk
add Adds a new attribute or another value Fails when the exact value already exists
replace Replaces every current value of that attribute Can unintentionally remove multi-valued settings
delete Deletes an attribute or one exact value Can remove more than intended when no value is supplied

A single modification record can chain operations when you separate them with -:

ldif
dn: TARGET_DN
changetype: modify
delete: attributeName
attributeName: old-value
-
add: attributeName
attributeName: new-value

Rules that prevent invalid format errors:

  • A single - on its own line separates operations within one modification record.
  • A blank line separates different LDAP entries.
  • Do not repeat the DN unless you are starting another record.
  • Attribute names after add, replace, or delete must match exactly.

Apply an LDIF through the local LDAPI socket. Root access through sudo maps to the SASL EXTERNAL identity that is allowed to modify cn=config:

bash
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f change.ldif

Add -v when you need the server to echo each modification on stderr during troubleshooting:

bash
sudo ldapmodify -Q -v -Y EXTERNAL -H ldapi:/// -f change.ldif

Change the OpenLDAP global log level

Read the current value first. An unset olcLogLevel produces an entry with no attribute lines:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b cn=config \
  -s base \
  olcLogLevel

Create set-loglevel.ldif with a single replace operation on the global cn=config entry:

ldif
dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: stats

Apply the LDIF online. No restart is required for olcLogLevel:

bash
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f set-loglevel.ldif

Sample output:

output
modifying entry "cn=config"

Verify the new value:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b cn=config \
  -s base \
  olcLogLevel

Sample output:

output
dn: cn=config
olcLogLevel: stats

The attribute should now appear in the result. Remove it with delete: olcLogLevel when you finish troubleshooting.

Useful temporary values during troubleshooting:

Value Use
stats General operation logging
stats stats2 More detailed operation and result logging
sync Syncrepl troubleshooting
acl ACL evaluation troubleshooting
config Configuration-processing troubleshooting
none Only messages that are always logged

Return to a quieter setting when you finish investigating. Very verbose logging fills journals quickly on busy servers.


Increase the MDB maximum size

olcDbMaxSize is the maximum MDB map size, not the current size of the database files on disk.

Query the current value:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b "$MDB_DN" \
  -s base \
  olcDbMaxSize

When the attribute is absent, OpenLDAP is using the backend default until you set one explicitly.

Convert a human-readable size to bytes:

bash
numfmt --from=iec 10G

Sample output:

output
10737418240

Create increase-mdb-size.ldif. Use add when the attribute is not present yet; use replace when it already exists:

bash
cat > increase-mdb-size.ldif <<EOF
dn: $MDB_DN
changetype: modify
add: olcDbMaxSize
olcDbMaxSize: 10737418240
EOF

Apply the LDIF against the discovered MDB database entry:

bash
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f increase-mdb-size.ldif

Sample output:

output
modifying entry "olcDatabase={2}mdb,cn=config"

Verify the persisted byte value matches what you intended:

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b "$MDB_DN" -s base olcDbMaxSize

Sample output:

output
dn: olcDatabase={2}mdb,cn=config
olcDbMaxSize: 10737418240

The byte value should match the output from numfmt.

Plan for expected directory growth, confirm the filesystem can accommodate the configured ceiling, and do not reduce the value below current database requirements. Back up cn=config before you change it.


Add and modify MDB indexes safely

Query existing indexes before you touch them:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b "$MDB_DN" \
  -s base \
  olcDbIndex

Sample output:

output
dn: olcDatabase={2}mdb,cn=config
olcDbIndex: objectClass eq,pres
olcDbIndex: ou,cn,mail,surname,givenname eq,pres,sub
Index Typical filter
eq (uid=jdoe)
sub (cn=*doe*)
pres (mail=*)
approx Approximate-match filter

Add indexes for search filters you actually run—not for every attribute in the schema. The OpenLDAP indexing and performance guide maps SSSD, sudo, and application filters to eq, pres, and sub index types.

Add a new index value

Create add-tel-index.ldif for an attribute that is not already indexed on its own:

bash
cat > add-tel-index.ldif <<EOF
dn: $MDB_DN
changetype: modify
add: olcDbIndex
olcDbIndex: telephoneNumber eq
EOF

Apply the index change online. OpenLDAP queues an internal indexing task after olcDbIndex changes:

bash
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f add-tel-index.ldif

Sample output:

output
modifying entry "olcDatabase={2}mdb,cn=config"

Confirm the new index line appears in the database entry:

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b "$MDB_DN" -s base olcDbIndex

Look for a new olcDbIndex line for the attribute you added.

If you try to add a duplicate index definition, OpenLDAP returns an implementation-specific error instead of silently duplicating the rule.

Modify an existing index value

Copy the exact existing value from ldapsearch before you delete it. To widen a single-attribute index, delete the old value and add the new one in one modification record:

bash
cat > modify-tel-index.ldif <<EOF
dn: $MDB_DN
changetype: modify
delete: olcDbIndex
olcDbIndex: telephoneNumber eq
-
add: olcDbIndex
olcDbIndex: telephoneNumber eq,sub
EOF

Apply the change:

bash
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f modify-tel-index.ldif

Sample output:

output
modifying entry "olcDatabase={2}mdb,cn=config"

The modifying entry line shows your discovered $MDB_DN value—not a number copied from another host.

Confirm the wider index replaced the old telephoneNumber rule:

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b "$MDB_DN" -s base olcDbIndex

Sample output:

output
olcDbIndex: telephoneNumber eq,sub

Do not use replace: olcDbIndex unless you intend to replace the complete index list. OpenLDAP can build changed indexes through an internal online task—monitor slapd logs until indexing finishes. If slapd stops before online index creation completes, finish the work offline with slapindex during a maintenance window.

Stop slapd before you run slapindex. The tool rebuilds indexes against the on-disk database while the server is down:

bash
sudo systemctl stop slapd

Rebuild indexes for the production suffix. Replace the suffix if your database serves a different naming context:

bash
sudo slapindex -F /etc/openldap/slapd.d -b "dc=example,dc=com"

The command exits silently on success when the directory is small.

slapindex writes to files under /var/lib/ldap. Restore ldap ownership so the daemon can open them after restart:

bash
sudo chown -R ldap:ldap /var/lib/ldap

Reapply SELinux file contexts after the index rebuild touched the data directory:

bash
sudo restorecon -Rv /var/lib/ldap

Start slapd again and confirm the service returns to active:

bash
sudo systemctl start slapd

Make the MDB database temporarily read-only

olcReadOnly is useful during migration freezes, controlled backups, maintenance, cutover preparation, or investigations where you must stop application writes without shutting down slapd.

Query the current value. When olcReadOnly is unset, the result contains only the entry DN:

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b "$MDB_DN" -s base olcReadOnly

Write the LDIF that sets the database to read-only mode:

bash
cat > enable-read-only.ldif <<EOF
dn: $MDB_DN
changetype: modify
replace: olcReadOnly
olcReadOnly: TRUE
EOF

Apply the change through the configuration path on LDAPI:

bash
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f enable-read-only.ldif

Confirm olcReadOnly now reads TRUE:

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b "$MDB_DN" -s base olcReadOnly

Sample output:

output
dn: olcDatabase={2}mdb,cn=config
olcReadOnly: TRUE

Test with an identity that can normally modify the directory. Use the data database administrator DN from olcRootDN, not the LDAPI EXTERNAL identity used for cn=config changes.

Create a harmless attribute change against a known user entry:

bash
cat > test-write.ldif <<EOF
dn: uid=jdoe,ou=people,dc=example,dc=com
changetype: modify
replace: description
description: read-only test
EOF

Run the test with a simple bind and StartTLS. Enter the olcRootDN password when prompted:

bash
ldapmodify -x -ZZ -H ldap://ldap-server.example.com -D "cn=admin,dc=example,dc=com" -W -f test-write.ldif

When olcReadOnly is active, the server rejects the otherwise-authorized update:

output
ldap_modify: Server is unwilling to perform (53)
	additional info: operation restricted

olcReadOnly: TRUE rejects ordinary database modifications with unwilling to perform. Syncrepl-originated changes can still update a replication consumer.

Re-enable writes when maintenance finishes. Set olcReadOnly back to FALSE with the same replace pattern:

bash
cat > disable-read-only.ldif <<EOF
dn: $MDB_DN
changetype: modify
replace: olcReadOnly
olcReadOnly: FALSE
EOF

Apply the disable LDIF the same way you enabled read-only mode:

bash
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f disable-read-only.ldif

olcReadOnly blocks normal client updates. Replication consumers may still accept updates delivered by Syncrepl—see provider-consumer replication for that behavior.


Load an OpenLDAP module safely

Inspect modules already loaded before you add another:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b cn=config \
  '(objectClass=olcModuleList)' \
  dn olcModulePath olcModuleLoad

List module files supplied by the installed RPM. Compare this list with olcModuleLoad before you add a filename that does not exist on the host:

bash
rpm -ql openldap-servers | grep -E '/openldap/.*\.(so|la)$'

On Rocky Linux 10.2 from EPEL, memberof.la is present under /usr/lib64/openldap/. Use the filename and path from your installed package—not from an Ubuntu or source-build tutorial.

When a module-list entry already exists

Discover its DN and store it the same way you stored $MDB_DN:

bash
MODULE_DN=$(
  sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config '(objectClass=olcModuleList)' dn |
  awk '/^dn: / {sub(/^dn: /, ""); print; exit}'
)

Create load-module.ldif with the verified module filename:

bash
cat > load-module.ldif <<EOF
dn: $MODULE_DN
changetype: modify
add: olcModuleLoad
olcModuleLoad: memberof.la
EOF

OpenLDAP accepts a simple module filename and searches for it under olcModulePath. You do not supply an ordering prefix in the LDIF.

Apply the change:

bash
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f load-module.ldif

When the module is already loaded, OpenLDAP refuses the duplicate value:

output
ldap_modify: Type or value exists (20)
	additional info: modify/add: olcModuleLoad: value #0 already exists
modifying entry "cn=module{0},cn=config"

On a clean server the load succeeds:

output
modifying entry "cn=module{0},cn=config"

Query the value OpenLDAP stored—do not assume the ordering prefix:

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b "$MODULE_DN" -s base olcModuleLoad

Sample output after loading memberof.la on the lab server:

output
dn: cn=module{0},cn=config
olcModuleLoad: {0}syncprov.la
olcModuleLoad: {1}memberof.la

The server assigned {1}memberof.la automatically. Loading the module does not configure a memberof overlay—that is a separate step.

When no module-list entry exists

On a minimal install, no olcModuleList entry may exist yet. Create one with ldapadd and point olcModulePath at the directory returned by rpm -ql:

ldif
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModulePath: /usr/lib64/openldap
olcModuleLoad: memberof.la

Check whether the functionality is built into your slapd binary, whether the module is already present, and whether the RPM actually ships the file before you load it.


Delete or roll back a configuration value

Use delete when you need to remove one value from a multi-valued attribute. The value in the LDIF must match the stored string exactly—including any ordering prefix the server added.

Remove one exact value. Copy the stored value from ldapsearch—for example {1}memberof.la after the server loads memberof.la:

ldif
dn: TARGET_DN
changetype: modify
delete: olcModuleLoad
olcModuleLoad: EXACT_STORED_VALUE

Apply rollback LDIF files with the same ldapmodify -Q -Y EXTERNAL -H ldapi:/// command you used for the original change.

Remove the complete attribute when you intend to drop every value at once:

ldif
dn: TARGET_DN
changetype: modify
delete: attributeName

Omitting the value deletes every value of that attribute. That is especially dangerous for multi-valued attributes such as olcDbIndex, olcAccess, olcModuleLoad, olcServerID, and olcSyncrepl. For ACL policy design and ordering, see OpenLDAP ACL Configuration with Practical Examples.

Keep a reversal LDIF beside every production change:

text
change-enable.ldif
change-rollback.ldif

After rollback, confirm the server still parses configuration and starts cleanly.

Check the on-disk slapd.d tree:

bash
sudo slaptest -u

Then read recent daemon messages for module, index, or ACL errors tied to the rollback:

bash
sudo journalctl -u slapd -n 50 --no-pager

slaptest -u checks syntax and structure. The journal shows whether slapd logged runtime errors after the rollback LDIF was applied.

Some loaded modules cannot be removed while slapd is running. Plan rollback with a known-good cn=config export when unload is rejected.


Validate OpenLDAP after each configuration change

Run these checks after every ldapmodify so a silent syntax success does not hide a broken runtime state.

slaptest -u validates the on-disk configuration tree without starting the service:

bash
sudo slaptest -u

Confirm slapd is still running after the change:

bash
sudo systemctl is-active slapd

Sample output:

output
active

If the service is not active, inspect journalctl -u slapd before you assume the configuration change succeeded.

Review recent log messages for indexing tasks, module load errors, or MDB warnings:

bash
sudo journalctl -u slapd --since "10 minutes ago" --no-pager

Run the same ldapsearch query you used before the change and confirm the attribute now shows the expected value.

When the setting affects directory behavior, run a focused functional test:

Setting changed Functional verification
olcLogLevel Confirm new journal messages
olcDbMaxSize Query the value and monitor MDB errors
olcDbIndex Run the indexed search and inspect logs
olcReadOnly Attempt a controlled write
Module load Query olcModuleLoad
Overlay Test the overlay behavior on the target database
Replication setting Compare replicated entries and CSNs

Successful ldapmodify output proves only that the configuration entry was accepted. It does not prove that the intended feature works end to end.


Recover a broken cn=config configuration

Use this section for local configuration recovery on one host. Full server migration belongs in Migrate OpenLDAP to a new server.

Preferred recovery order:

  1. Restore a known-good configuration backup.
  2. Export the broken configuration with slapcat, correct a copy, and reload it.
  3. Use slapmodify for a precise offline change.
  4. Use low-level back-LDIF recovery only as a last resort.

Stop the service before you touch the on-disk configuration tree:

bash
sudo systemctl stop slapd

Preserve the complete broken tree by moving it out of /etc/openldap/. A mv keeps the broken files intact for later inspection:

bash
BROKEN_DIR="/root/slapd.d-broken-$(date +%Y%m%d-%H%M%S)"
sudo mv /etc/openldap/slapd.d "$BROKEN_DIR"

Export the preserved copy to a single LDIF file you can edit safely:

bash
sudo slapcat -F "$BROKEN_DIR" -n 0 -l /root/broken-cn-config.ldif

The export succeeds even when slapd is stopped, as long as the preserved slapd.d tree is still readable.

Correct a copy of the LDIF—not the generated files under slapd.d. Save the edited file as /root/corrected-cn-config.ldif.

slapadd cannot load into a populated slapd.d directory. Create an empty tree owned by ldap:

bash
sudo install -d -o ldap -g ldap -m 0750 /etc/openldap/slapd.d

Import the corrected configuration export into database -n 0 (the cn=config tree):

bash
sudo slapadd -F /etc/openldap/slapd.d -n 0 -l /root/corrected-cn-config.ldif

slapadd recreates the LDIF files under slapd.d from your edited export.

Ensure the ldap user owns the rebuilt configuration tree:

bash
sudo chown -R ldap:ldap /etc/openldap/slapd.d

Restore SELinux contexts on the new configuration files:

bash
sudo restorecon -Rv /etc/openldap/slapd.d

Validate the rebuilt tree before you start the daemon:

bash
sudo slaptest -F /etc/openldap/slapd.d -u

Only start slapd after slaptest reports success:

bash
sudo systemctl start slapd

For a precise offline modification when the configuration still parses, use slapmodify instead of reloading the entire tree. slapd must be stopped, and -n 0 is required for the configuration database:

bash
sudo systemctl stop slapd

Apply one or more offline modifications from a reviewed LDIF file:

bash
sudo slapmodify -F /etc/openldap/slapd.d -n 0 -l /root/fix-cn-config.ldif

slapmodify edits the on-disk cn=config database in place—use it when a full slapadd reload is unnecessary.

Restore ldap ownership after the offline write:

bash
sudo chown -R ldap:ldap /etc/openldap/slapd.d

Reapply SELinux labels on the modified configuration files:

bash
sudo restorecon -Rv /etc/openldap/slapd.d

Confirm the tree still parses before you start the service:

bash
sudo slaptest -F /etc/openldap/slapd.d -u

Do not manually edit CRC-protected LDIF files inside slapd.d.


Troubleshoot cn=config, MDB, and ldapmodify

Error or symptom Likely cause Recommended check
No such object (32) Wrong numbered DN or wrong configuration level Rediscover the target with ldapsearch
Insufficient access (50) Command was not run through root SASL EXTERNAL access Use sudo, -Y EXTERNAL, and ldapi:///
Type or value exists (20) Attempted to add an existing value Query current values before using add
Object class violation (65) Attribute is invalid for the target entry Confirm global, module, backend, database, or overlay scope
Constraint violation (19) Invalid attribute value or unsupported setting Check the OpenLDAP 2.6 manual and package capabilities
Invalid format Broken LDIF syntax Check blank lines, hyphen separators, and continuation lines
MDB cannot grow olcDbMaxSize is too small Query and increase the MDB map size
Search remains slow after adding an index Index task is incomplete or filter does not use that index Review logs and the actual search filter
Module cannot be loaded Wrong path, wrong filename, missing RPM, or built-in feature Check rpm -ql, olcModulePath, and loaded modules
Module already loaded Duplicate olcModuleLoad value Do not add it again
slapd no longer starts Invalid persisted configuration Use slaptest, backup, slapcat, and slapmodify
Manual slapd.d edit is rejected Generated LDIF has integrity metadata Restore the file and use LDAP or offline slap tools
Changed value disappears after restart Modification targeted the wrong server or was not persisted Query local cn=config before and after restart

Useful diagnostics when a change fails or the numbered DN is unclear:

bash
sudo slaptest -u

slaptest confirms whether the on-disk configuration tree is syntactically valid.

bash
sudo journalctl -u slapd -n 100 --no-pager

Recent journal lines often show the exact attribute, module path, or index task that failed.

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config dn

Listing every configuration DN rediscovers the current numbered entries on this host.

bash
sudo slapcat -n 0 | grep -E '^(dn|olcDatabase|olcSuffix|olcDbDirectory|olcModuleLoad|olcOverlay):'

This one-liner summarizes databases, suffixes, module loads, and overlays from the live configuration export.


References


Summary

You can now navigate the cn=config directory, distinguish global, backend, database, module, schema, and overlay entries, and discover the numbered MDB DN on the current host. The walkthrough showed how to authenticate locally with SASL EXTERNAL, apply add, replace, and delete modifications, change olcLogLevel, increase olcDbMaxSize, add olcDbIndex values, toggle olcReadOnly, load a verified packaged module, back up configuration before changes, and recover a broken cn=config database with the slap* tools.


Frequently Asked Questions

1. What is cn=config in OpenLDAP?

cn=config is the distinguished name at the root of OpenLDAP dynamic configuration. It stores server settings such as logging, TLS paths, modules, schemas, database definitions, and overlays—not user or group entries.

2. What is the difference between cn=config and slapd.conf?

slapd.conf was the legacy flat configuration file. Modern OpenLDAP uses the slapd-config backend, exposed as the cn=config LDAP tree and mirrored under /etc/openldap/slapd.d/. Runtime changes should go through LDAP tools, not manual edits to generated LDIF files.

3. Why should I not edit files under /etc/openldap/slapd.d/?

Those LDIF files are generated by slapd and include integrity metadata. Manual edits while slapd is running can be rejected or overwritten. Use ldapmodify for online changes and slapcat, slapadd, or slapmodify for controlled offline work.

4. Why does my MDB database DN contain {2} or another number?

OpenLDAP assigns ordering prefixes in braces when it creates module, schema, database, and overlay entries. The number reflects creation order on that server and can differ between installations.

5. How do I find the correct olcDatabase DN?

Search cn=config for objectClass=olcMdbConfig and filter by olcSuffix when more than one MDB database exists. Use the full DN returned by your server—never copy a numbered DN from another host or tutorial.

6. What is the difference between ldapadd and ldapmodify?

ldapadd creates new entries. ldapmodify changes existing entries using LDIF changetype: modify records with add, replace, or delete operations.

7. What is the difference between add and replace in an LDIF modification?

add introduces a new attribute or another value to a multi-valued attribute. replace removes every current value of that attribute and stores only the values listed in the LDIF. Use add when extending multi-valued settings such as olcDbIndex.

8. Do OpenLDAP cn=config changes require a restart?

Most cn=config changes take effect immediately. Some settings still warrant a service check, functional test, or restart when documentation or your change type requires it—for example after certain TLS file replacements.

9. How do I increase the MDB database size?

Set olcDbMaxSize on the MDB database entry under cn=config to the desired maximum map size in bytes. Query the current value first, back up cn=config, apply ldapmodify, and confirm free filesystem capacity for future growth.

10. Why do I receive ldap_modify: No such object (32)?

The target DN is wrong or missing. Rediscover the database, module, or overlay DN with ldapsearch on cn=config instead of assuming numbered entries from another server.

11. How do I recover OpenLDAP when a bad cn=config change prevents startup?

Stop slapd, preserve the broken slapd.d directory, export with slapcat when possible, correct a copy of the LDIF, reload with slapadd or apply a precise offline change with slapmodify, then validate with slaptest before starting the service.

12. Does loading an OpenLDAP module automatically enable its overlay?

No. Loading a module only makes compiled functionality available to slapd. You still add an overlay entry under the intended database when you want that feature to affect directory behavior.
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 …