Migrate 389 Directory Server from Berkeley DB to LMDB

Migrate 389 Directory Server from Berkeley DB to LMDB with backend detection, dsctl dblib bdb2mdb, disk planning, validation, replicated rolling conversion, manual LDIF fallback, and recovery.

Published

Updated

Read time 22 min read

Reviewed byDeepak Prasad

389 Directory Server Berkeley DB to LMDB migration workflow with dsctl dblib bdb2mdb export import and validation

Modern 389 Directory Server releases default to LMDB for new instances. Berkeley DB remains available only on distributions that ship the optional BDB backend package, and upstream is removing BDB support over time. Updating 389-ds-base through dnf or your package manager does not automatically convert an existing BDB-backed instance to LMDB.

A supported 389 Directory Server BDB to LMDB migration therefore requires a deliberate database-backend change. During dsctl dblib bdb2mdb, the instance is unavailable while backends export, nsslapd-backend-implement changes to mdb, and data imports into LMDB. The tool leaves old BDB files in place until you validate the result and run cleanup.

This lab uses three instances on ldap1.example.com:

Instance Role in this guide LDAP port
bdb-lab Standalone BDB conversion in Steps 4–5 and 7 20389
ldap1 LMDB supplier that stays online during Step 6 389
bdb-consumer BDB consumer replica converted in Step 6 21389

Steps 4–5 and 7 walk through bdb-lab with suffix dc=bdblab,dc=example,dc=com. Step 6 converts bdb-consumer, which replicates dc=example,dc=com from the ldap1 supplier while that supplier remains on LMDB. Replace instance names, paths, and ports with your deployment values.

Before you start:

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


Step 1: Check whether the instance uses BDB or LMDB

Before you schedule downtime, confirm the 389 Directory Server database backend on every instance you plan to convert.

List instances on the host:

Offline instance work in this section uses dsctl commands.

bash
dsctl -l
output
slapd-bdb-consumer
slapd-bdb-lab
slapd-ldap1
slapd-ldap2

Use the short instance name in later dsctl and dsconf commands. This lab converts bdb-lab in the standalone path and bdb-consumer in the replicated path.

When the instance is running, read the configured backend implementation:

Online configuration in this section uses dsconf commands.

bash
dsconf bdb-lab backend config get | grep nsslapd-backend-implement
output
nsslapd-backend-implement: bdb

A value of bdb means this guide applies. A value of mdb means the instance already uses LMDB and does not need dsctl dblib bdb2mdb.

When the instance is stopped, read the same attribute from dse.ldif:

bash
grep nsslapd-backend-implement /etc/dirsrv/slapd-bdb-lab/dse.ldif
output
nsslapd-backend-implement: bdb

The offline copy should match the value reported by dsconf when the instance was last running.

Confirm the installed package build and whether Berkeley DB libraries are available on your platform:

bash
rpm -q 389-ds-base 389-ds-base-bdb
output
389-ds-base-3.2.0-8.el10_2.x86_64
389-ds-base-bdb-3.2.0-8.el10_2.x86_64

When both packages are installed, the NEVRAs should match on the same release.

NOTE
Platform note: This lab uses Rocky Linux 10.2, whose CRB repository currently provides the optional 389-ds-base-bdb compatibility package. That does not make BDB a supported backend on Red Hat Enterprise Linux 10 or Red Hat Directory Server 13. Official RHEL 10 documentation states that the system libdb implementation is no longer available, BDB instances are no longer supported, and existing instances should be migrated to LMDB before or during the platform move. On supported Red Hat deployments, migrate BDB instances on the older supported platform before moving to RHEL 10 or RHDS 13, or follow Red Hat Support guidance. Do not tell RHEL 10 administrators to enable an unrelated community repository or install community compatibility RPMs.

On Rocky Linux 10 only, when you need the compatibility package for lab or legacy-instance testing, enable CRB and install the matching build with dnf command:

bash
dnf config-manager --set-enabled crb

The command exits silently when CRB is enabled.

bash
dnf install -y 389-ds-base-bdb

The install completes without errors when the CRB repository provides a matching 389-ds-base-bdb build.

Verify the installed NEVRAs match:

bash
rpm -q 389-ds-base 389-ds-base-bdb
output
389-ds-base-3.2.0-8.el10_2.x86_64
389-ds-base-bdb-3.2.0-8.el10_2.x86_64

Both packages should report the same release suffix before you create or migrate a BDB instance. Red Hat documents automated dsctl BDB-to-LMDB conversion for Directory Server 12.5 and later. Upstream 389 DS 3.x defaults new instances to LMDB. Read the release notes for your exact package stream before you plan production downtime.


Step 2: Choose the appropriate migration method

Environment Recommended method
Supported release with enough local disk space dsctl dblib bdb2mdb
LDIF files must use another filesystem dsctl dblib bdb2mdb --tmpdir PATH
LMDB map size must be set manually before import Manual export and import
Automated conversion fails Manual migration or replica rebuild
Replicated instance you can rebuild from a supplier Reinitialize or rebuild as LMDB
Release without dblib bdb2mdb support Upgrade to a supported build first, then migrate
FreeIPA or Red Hat IdM deployment Follow IdM-specific documentation

Use automated in-place conversion when the installed dsctl build supports dblib bdb2mdb and the host has room for exported LDIF files, the new LMDB map, and the original BDB files until cleanup. When /var/lib/dirsrv is too small, point --tmpdir at a filesystem with enough free space instead of switching immediately to a fully manual workflow.


Step 3: Prepare for the migration

Complete these checks before you remove the instance from client traffic:

  • Confirm dsctl INSTANCE dblib bdb2mdb exists on the installed build.
  • Install applicable errata for your Directory Server release.
  • Run dsctl INSTANCE healthcheck and resolve material errors.
  • Verify LDAP and LDAPS access for applications that depend on the instance.
  • Record every backend name and suffix with dsconf INSTANCE backend suffix list.
  • Estimate disk space under /var/lib/dirsrv/slapd-INSTANCE/.
  • Create a native backup while the instance still runs BDB.
  • Schedule downtime and stop application writes on standalone hosts.

Run a health check on the BDB instance:

bash
dsctl bdb-lab healthcheck
output
Healthcheck complete.
No issues found.

On a BDB instance, the health check may also report that BDB is deprecated. That warning is expected until you complete the migration.

Measure database size and free space on the instance path:

bash
du -sh /var/lib/dirsrv/slapd-bdb-lab/db
output
476K	/var/lib/dirsrv/slapd-bdb-lab/db

Confirm the filesystem that holds the instance still has enough headroom for export, import, and cleanup:

bash
df -h /var/lib/dirsrv/slapd-bdb-lab
output
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/rlm-root   14G   11G  2.3G  83% /

Calculate the initial LMDB map size by adding the sizes of all backend database directories and adding at least a 20% safety margin. When the BDB files, migration LDIF, and LMDB database share the same filesystem, plan free space of at least twice that calculated LMDB map size—not merely twice the current database-directory size. When you use --tmpdir, verify sufficient capacity on both the temporary filesystem and the filesystem containing the instance database.

For example:

bash
du -sch /var/lib/dirsrv/slapd-bdb-lab/db/*/
output
200K	/var/lib/dirsrv/slapd-bdb-lab/db/userroot
476K	total

The measured total was only 476 KiB in this lab. The 2.3 GiB free on / was therefore more than enough for this tiny test database, but it is not a general sizing rule for production directories.

IMPORTANT
A native BDB backup cannot be restored directly into an LMDB-backed instance. Keep the original BDB backup, package NEVRAs, and dse.ldif copy until LMDB validation completes.

Create and verify a native BDB backup before conversion:

bash
backup="/var/lib/dirsrv/slapd-bdb-lab/bak/bdb-lab-pre-mdb"

The variable holds the backup directory path used in the next commands.

Create the online backup while the instance still runs BDB:

bash
dsconf bdb-lab backup create "$backup"
output
The backup create task has finished successfully

Preserve the configuration snapshot and package list with the backup:

bash
cp -a /etc/dirsrv/slapd-bdb-lab/dse.ldif "$backup/dse.ldif.pre-migration"

The copy gives you the exact BDB-era configuration if you need to compare settings later.

Record the package versions installed at migration time:

bash
rpm -q 389-ds-base 389-ds-base-bdb | tee "$backup/package-versions.txt"

tee writes the NEVRAs into the backup directory and prints them to the terminal.

output
389-ds-base-3.2.0-8.el10_2.x86_64
389-ds-base-bdb-3.2.0-8.el10_2.x86_64

Create and verify a checksum manifest:

bash
(
  cd "$backup"
  find . -type f ! -name SHA256SUMS -print0 |
    sort -z |
    xargs -0 sha256sum > SHA256SUMS
  sha256sum -c SHA256SUMS
)

Every file in the backup directory should report OK.

output
./config_files/cert9.db: OK
./config_files/dse.ldif: OK
...

A BDB-era archive contains Berkeley DB files rather than LMDB data.mdb files; the checksum workflow is the same. The complete isolated-restore workflow lives in Back Up and Restore 389 Directory Server.

Record the entry count before conversion:

bash
ldapsearch -LLL -o ldif-wrap=no -Y EXTERNAL -H ldapi://%2Frun%2Fslapd-bdb-lab.socket -b "dc=bdblab,dc=example,dc=com" "(objectClass=*)" dn | grep -Ec '^dn::? '
output
13

Record this count so you can compare against the post-migration search later.


Step 4: Migrate BDB to LMDB with dsctl dblib bdb2mdb

This is the preferred method to migrate 389 Directory Server to LMDB on a standalone instance when disk space is sufficient.

Confirm the installed command exposes --tmpdir on your build:

bash
dsctl bdb-lab dblib bdb2mdb --help
output
usage: dsctl [-v] [-j] [instance] dblib bdb2mdb [-h] [--tmpdir TMPDIR]
...
  --tmpdir TMPDIR  ldif migration files directory path.

Remove the instance from client traffic, then run the conversion. bdb2mdb manages the instance stop and start internally: it stops the server, exports BDB backends, updates dse.ldif, imports into LMDB, and starts the instance again. You still need a maintenance window because the directory remains unavailable during export, conversion, and import.

Run the automated conversion:

bash
dsctl bdb-lab dblib bdb2mdb

Representative progress from the tested lab run:

output
INFO: Required space for LDIF files is about 24.0 KB
INFO: Required space for DBMAP files is about 240.0 KB
INFO: Required number of dbi is 128
INFO: Backends exportation 100%
INFO: Updating dse.ldif file
INFO: Backends importation 100%
INFO: Migration from Berkeley database to lmdb is done.

The command calculates an initial LMDB map size (nsslapd-mdb-max-size) and leaves the old BDB files in place until you run cleanup. Large production databases can take considerable time; do not interrupt the process only because the terminal appears idle between progress lines.

On affected RHEL 10-derived builds, a standalone conversion can display error 97 while checking for a nonexistent replication changelog. Do not ignore a failed conversion generally; ignore this specific message only when the remaining output confirms that export, configuration update, and LMDB import completed.

If dsctl healthcheck incorrectly reports that BDB is still active after dsconf backend config get reports mdb, rerun the check with the backend library set explicitly:

bash
NSSLAPD_DB_LIB=mdb dsctl bdb-lab healthcheck

That workaround addresses false DSBLE0005 or DSBLE0006 warnings on some RHEL 10-derived builds. Do not run dblib cleanup merely to silence a false health-check warning when validation otherwise passes.

When /var/lib/dirsrv does not have enough room for temporary LDIF files, create a migration directory owned by dirsrv on another filesystem:

bash
install -d -o dirsrv -g dirsrv -m 750 /mnt/389ds-migration

Run the conversion with --tmpdir pointing at that path:

bash
dsctl bdb-lab dblib bdb2mdb --tmpdir /mnt/389ds-migration

The conversion checks free space on the temporary path separately from the database directory.

Verify the instance is running after conversion:

bash
dsctl bdb-lab status
output
Instance "bdb-lab" is running

Do not run dsctl dblib cleanup yet.


Step 5: Verify the LMDB migration

Confirm the backend implementation changed:

bash
dsconf bdb-lab backend config get | grep nsslapd-backend-implement
output
nsslapd-backend-implement: mdb

Read the configured LMDB maximum map size:

bash
dsconf bdb-lab backend config get | grep nsslapd-mdb-max-size
output
nsslapd-mdb-max-size: 4194304

4194304 bytes is 4 MiB. The tested lab received that small map because the suffix is small. Production instances need headroom based on measured database size plus a safety margin. See Tune LMDB, Entry Cache and DN Cache in 389 Directory Server before you accept the auto-calculated value on a large directory.

List LMDB files in the database directory:

bash
ls -la /var/lib/dirsrv/slapd-bdb-lab/db/
output
-rw-------. 1 dirsrv dirsrv 118784 Jul 18 23:13 data.mdb
-rw-r--r--. 1 dirsrv dirsrv     72 Jul 18 23:13 INFO.mdb
-rw-------. 1 dirsrv dirsrv   8192 Jul 18 23:13 lock.mdb
drwx------. 2 dirsrv dirsrv   4096 Jul 18 23:13 userroot

data.mdb, INFO.mdb, and lock.mdb confirm LMDB is active. The remaining userroot/ directory holds old BDB files until cleanup.

Verify the entry count matches the pre-migration baseline:

bash
ldapsearch -LLL -o ldif-wrap=no -x -H ldap://127.0.0.1:20389 -D "cn=Directory Manager" -y /root/bdb-lab-dm.pw -b "dc=bdblab,dc=example,dc=com" "(objectClass=*)" dn | grep -Ec '^dn::? '
output
13

The post-migration count matches the pre-migration baseline of 13 entries.

Run the health check again. Before cleanup, the linter reports leftover BDB files:

bash
dsctl bdb-lab healthcheck
output
[1] DS Lint Error: DSBLE0004
...
Files for both MDB and BDB databases have been found.
...
Run 'dsctl <instance> dblib cleanup' to remove old database files...

That warning is expected until cleanup removes the obsolete BDB artifacts.

Validate LDAP, LDAPS, and indexed searches

Test authenticated LDAP:

bash
ldapwhoami -x -H ldap://127.0.0.1:20389 -D "cn=Directory Manager" -y /root/bdb-lab-dm.pw
output
dn: cn=directory manager

A successful bind confirms LDAP authentication still works on the converted backend.

Test LDAPS with the trust anchor your clients use. This lab instance listens on LDAPS port 20636 with the self-signed certificate created during dscreate:

bash
LDAPTLS_CACERT="/tmp/389ds-selfsigned-ca.pem" ldapwhoami -x -H ldaps://ldap1.example.com:20636 -D "cn=Directory Manager" -y /root/bdb-lab-dm.pw
output
dn: cn=directory manager

LDAPS succeeds when the client trusts the instance certificate or CA.

Verify an indexed application-style search:

bash
ldapsearch -LLL -x -H ldap://127.0.0.1:20389 -D "cn=Directory Manager" -y /root/bdb-lab-dm.pw -b "dc=bdblab,dc=example,dc=com" "(uid=demo_user)" dn uid
output
dn: uid=demo_user,ou=people,dc=bdblab,dc=example,dc=com
uid: demo_user

The indexed uid search returns the same entry as before migration.

Create the first LMDB-native backup before you discard BDB rollback files:

bash
dsconf bdb-lab backup create /var/lib/dirsrv/slapd-bdb-lab/bak/bdb-lab-first-lmdb
output
The backup create task has finished successfully

Checksum or restore-test that archive using the workflow in Back Up and Restore 389 Directory Server.

Review recent service logs:

bash
journalctl -u [email protected] --since "30 minutes ago" --no-pager
output
Jul 18 23:13:13 ldap1.example.com systemd[1]: Started [email protected] - 389 Directory Server bdb-lab..
Jul 18 23:13:13 ldap1.example.com ns-slapd[20901]: [18/Jul/2026:23:13:13.012451217 +0530] - INFO - slapd_daemon - slapd started.  Listening on All Interfaces port 20389 for LDAP requests

The journal should show a clean start after the LMDB import.

Only proceed to cleanup after LDAP, LDAPS, indexed searches, plug-ins, replication when enabled, and application checks all pass.


Step 6: Perform a rolling conversion in a replicated topology

A rolling Berkeley DB to LMDB migration proceeds one Directory Server instance at a time. In the usual one-instance-per-host topology, this is also a host-by-host migration. When a host runs multiple Directory Server instances, convert and validate each instance separately.

This lab keeps the ldap1 supplier on LMDB and converts the bdb-consumer replica on the same host. BDB and LMDB replicas can coexist briefly because replication transfers LDAP changes, not native database files. Do not convert every writable supplier simultaneously.

Confirm replication health before conversion

From the supplier that stays online, list agreements and read synchronization state:

bash
dsconf -D "cn=Directory Manager" -y /root/dm.pw ldap1 replication status --suffix "dc=example,dc=com"
output
{'agmt-name': ['ldap1-to-bdb-consumer'], 'replica': ['ldap1.example.com:21389'], 'replica-enabled': ['on'], 'replication-status': ['Not in Synchronization: supplier (Unknown) consumer (Unknown) State (green) Reason (error (0) replica acquired successfully: incremental update succeeded)'], 'replication-lag-time': ['unavailable'], 'last-update-status': ['Error (0) Replica acquired successfully: Incremental update succeeded']}

Confirm the supplier and the replica you plan to convert are synchronized. Use the hostname and LDAP port from your agreement output, and treat ds-replcheck as the authoritative check when replication-status looks ambiguous:

bash
ds-replcheck state -D "cn=Directory Manager" -y /root/dm.pw -m ldap://ldap1.example.com:389 -r ldap://ldap1.example.com:21389 -b "dc=example,dc=com"
output
Replication State: Supplier and Replica are in perfect synchronization

Do not start the conversion while agreements report unexplained lag or a disabled replica.

Convert one replica

Remove every client endpoint served by bdb-consumer from rotation. Create and verify a pre-migration backup on the replica:

bash
backup="/var/lib/dirsrv/slapd-bdb-consumer/bak/bdb-consumer-pre-mdb"
bash
dsconf bdb-consumer backup create "$backup"
output
The backup create task has finished successfully

Record the entry count on the BDB replica before conversion:

bash
ldapsearch -LLL -o ldif-wrap=no -x -H ldap://127.0.0.1:21389 -D "cn=Directory Manager" -y /root/bdb-consumer-dm.pw -b "dc=example,dc=com" "(objectClass=*)" dn | grep -Ec '^dn::? '
output
769

Run the automated conversion on the replica:

bash
dsctl bdb-consumer dblib bdb2mdb
output
INFO: Required space for LDIF files is about 5.7 MB
INFO: Required space for DBMAP files is about 9.0 MB
INFO: Required number of dbi is 128
INFO: Backends exportation 100%
INFO: Updating dse.ldif file
INFO: Backends importation 100%
INFO: Migration from Berkeley database to lmdb is done.

Confirm the backend changed and the instance restarted:

bash
dsconf bdb-consumer backend config get | grep nsslapd-backend-implement
output
nsslapd-backend-implement: mdb
bash
dsctl bdb-consumer status
output
Instance "bdb-consumer" is running

Verify the entry count matches the pre-migration baseline:

bash
ldapsearch -LLL -o ldif-wrap=no -x -H ldap://127.0.0.1:21389 -D "cn=Directory Manager" -y /root/bdb-consumer-dm.pw -b "dc=example,dc=com" "(objectClass=*)" dn | grep -Ec '^dn::? '
output
769

Review the service journal for a clean restart after import:

bash
journalctl -u [email protected] --since "30 minutes ago" --no-pager
output
Jul 18 23:13:39 ldap1.example.com systemd[1]: Started [email protected] - 389 Directory Server bdb-consumer..
Jul 18 23:13:39 ldap1.example.com ns-slapd[21019]: [18/Jul/2026:23:13:39.483545796 +0530] - INFO - slapd_daemon - slapd started.  Listening on All Interfaces port 21389 for LDAP requests

Verify replication before you convert the next replica

From the supplier, confirm the converted replica caught up:

bash
dsconf -D "cn=Directory Manager" -y /root/dm.pw ldap1 replication status --suffix "dc=example,dc=com"
output
{'agmt-name': ['ldap1-to-bdb-consumer'], 'replica': ['ldap1.example.com:21389'], 'replica-enabled': ['on'], 'replication-status': ['Not in Synchronization: supplier (Unknown) consumer (Unknown) State (green) Reason (error (0) replica acquired successfully: incremental update succeeded)'], 'replication-lag-time': ['unavailable'], 'last-update-status': ['Error (0) Replica acquired successfully: Incremental update succeeded']}
bash
ds-replcheck state -D "cn=Directory Manager" -y /root/dm.pw -m ldap://ldap1.example.com:389 -r ldap://ldap1.example.com:21389 -b "dc=example,dc=com"
output
Replication State: Supplier and Replica are in perfect synchronization

A successful ds-replcheck result and a successful last-update-status mean the converted replica can rejoin client traffic. Complete Step 7 on that instance before you convert the next replica.

Convert the remaining replicas

Repeat the same sequence on each BDB-backed replica:

  • Confirm surviving suppliers are healthy
  • Drain the instance from client traffic
  • Create and verify a pre-migration backup
  • Record the entry count
  • Run dsctl INSTANCE dblib bdb2mdb
  • Validate backend type, entry counts, LDAP, and replication state
  • Run cleanup after validation
  • Return the instance to client traffic before you convert the next replica

Running bdb2mdb on one instance does not convert other Directory Server instances on the same host. For a disposable consumer, reinitializing from a healthy supplier is often safer than in-place conversion when another current supplier is available.


Step 7: Clean up the old BDB data

Run cleanup only after the validation steps in Step 5 or Step 6 succeed.

Remove obsolete BDB database files:

bash
dsctl bdb-lab dblib cleanup
output
cleanup dbmapdir=/var/lib/dirsrv/slapd-bdb-lab/db dbhome=/dev/shm/slapd-bdb-lab dblib=mdb

dblib cleanup removes obsolete database files for the inactive backend implementation. Depending on the installed build, generated migration LDIF and changelog-export files may remain. In 389 DS 3.2.0, the source leaves __dblib-*.ldif files in place while removing the old BDB database directories.

Inspect the instance LDIF directory after cleanup:

bash
find /var/lib/dirsrv/slapd-bdb-lab/ldif -maxdepth 1 -type f \( -name '__dblib-*.ldif' -o -name '__dblib-*.cl5.dbtxt' \) -ls
output
554633     12 -rw-------   1 dirsrv   dirsrv       9350 Jul 18 23:13 /var/lib/dirsrv/slapd-bdb-lab/ldif/__dblib-userroot.ldif

When you used --tmpdir, inspect that directory only if it exists:

bash
if [ -d /mnt/389ds-migration ]; then
    find /mnt/389ds-migration -maxdepth 1 -type f -ls
fi

This lab did not use --tmpdir, so skip the command or expect no output when the directory does not exist.

Remove migration LDIF files only after validation and any required retention period. They can contain the complete directory dataset, including password hashes:

bash
find /var/lib/dirsrv/slapd-bdb-lab/ldif -maxdepth 1 -type f \( -name '__dblib-*.ldif' -o -name '__dblib-*.cl5.dbtxt' \) -delete

-delete produces no output when the files are removed successfully.

Confirm the health check is clean:

bash
dsctl bdb-lab healthcheck
output
Healthcheck complete.
No issues found.

List the database directory to confirm only LMDB files remain:

bash
ls -la /var/lib/dirsrv/slapd-bdb-lab/db/
output
-rw-------. 1 dirsrv dirsrv 118784 Jul 18 23:13 data.mdb
-rw-r--r--. 1 dirsrv dirsrv     72 Jul 18 23:13 INFO.mdb
-rw-------. 1 dirsrv dirsrv   8192 Jul 18 23:13 lock.mdb

Only LMDB database files remain under db/.


Step 8: Manual migration and recovery

Perform a manual BDB-to-LMDB migration

Use manual LDIF export and import when:

  • dsctl dblib bdb2mdb cannot complete
  • You must set nsslapd-mdb-max-size manually before import
  • You need to inspect or edit intermediate LDIF files

Use dsctl dblib bdb2mdb --tmpdir PATH when the issue is only separate LDIF storage. Reserve the manual sequence below for cases that require explicit control over map size or LDIF content.

IMPORTANT

Keep the instance removed from load balancers, DNS service records, application connection lists, monitoring writes, and administrative automation from the first offline export until all LMDB imports and validation checks finish.

The temporary starts in this procedure exist only so dsconf can modify the configuration. They do not return the instance to production.

The write freeze applies across all backends until every corresponding LDIF file has been imported.

This example has one data backend named userroot. The database implementation setting is global to the instance, so every configured backend must be exported before switching and imported afterward. List all configured suffixes and repeat the export and import commands for every backend before returning the instance to service:

bash
dsconf bdb-lab backend suffix list
output
dc=bdblab,dc=example,dc=com (userroot)

For multiple backends, use the same pattern with each backend name:

bash
dsctl bdb-lab db2ldif BACKEND_NAME /var/lib/dirsrv/slapd-bdb-lab/ldif/BACKEND_NAME-bdb.ldif

Replace BACKEND_NAME with each backend reported by dsconf backend suffix list.

Import the matching LDIF file for each backend after you switch to LMDB:

bash
dsctl bdb-lab ldif2db BACKEND_NAME /var/lib/dirsrv/slapd-bdb-lab/ldif/BACKEND_NAME-bdb.ldif

Standalone example for backend userroot on instance bdb-lab:

Stop and export the backend:

bash
dsctl bdb-lab stop
bash
dsctl bdb-lab db2ldif userroot /var/lib/dirsrv/slapd-bdb-lab/ldif/userroot-bdb.ldif

The export writes a complete LDIF snapshot for backend userroot.

Start the BDB instance temporarily so dsconf can update the configuration:

bash
dsctl bdb-lab start

Switch the database implementation:

bash
dsconf bdb-lab backend config set --db-lib mdb

The setting changes nsslapd-backend-implement from bdb to mdb.

Restart so the LMDB configuration becomes active:

bash
dsctl bdb-lab restart

Set the calculated map size:

bash
dsconf bdb-lab backend config set --mdb-max-size 3G

Replace 3G with the value you calculated for your database size.

Stop before the offline import:

bash
dsctl bdb-lab stop

Import the LDIF:

bash
dsctl bdb-lab ldif2db userroot /var/lib/dirsrv/slapd-bdb-lab/ldif/userroot-bdb.ldif

The import rebuilds the userroot backend as LMDB from the exported LDIF.

Start and validate:

bash
dsctl bdb-lab start
bash
dsctl bdb-lab healthcheck

Resolve any health-check errors before you return the instance to client traffic.

For a replicated supplier or hub, ordinary LDIF export and import alone is not enough when you must preserve replication state and changelog data. The upstream procedure uses replication-aware export and import plus separate changelog handling. Reinitializing from a healthy supplier is often safer than a hand-rolled manual conversion.

Recover from a failed migration

Problem Recommended action
Insufficient disk space Stop, free space, and recover through the supported path without deleting the only copy of BDB or LDIF data
Export fails Review backend and errors logs; do not switch nsslapd-backend-implement yet
Import fails Preserve generated LDIF and migration logs for support
Instance does not start Check dse.ldif, backend type, file permissions, and nsslapd-mdb-max-size
LMDB map is full Increase nsslapd-mdb-max-size using the supported dsconf command
Migration was interrupted Inspect the detected backend and migration files before rerunning
Validation fails before cleanup Retain BDB files and restore from the verified BDB-era backup
Recovery method Requires old BDB files?
Change dse.ldif back to bdb Yes
Run a fresh mdb2bdb conversion No, but write-capable BDB support and free disk space are required
Restore the original BDB native backup Requires a compatible BDB instance
Reinitialize from another supplier No native-backend compatibility requirement
WARNING
Changing nsslapd-backend-implement back to bdb is lossless only while the converted instance has accepted no new LDAP or replicated changes. Once LMDB has accepted writes, the retained BDB files are stale. Switching directly back would discard those newer changes. Keep the instance out of write traffic until you either accept the LMDB result or complete the rollback. When newer changes must be preserved, use a fresh supported conversion, LDIF transfer, or replica reinitialization instead of merely editing dse.ldif.

Before dblib cleanup, a failed conversion can sometimes be rolled back to the untouched BDB files when no new writes occurred after conversion began. Stop the instance, preserve the current configuration, and edit dse.ldif only while Directory Server is offline:

bash
dsctl bdb-lab stop

Copy the current configuration before you edit it:

bash
cp -a /etc/dirsrv/slapd-bdb-lab/dse.ldif /etc/dirsrv/slapd-bdb-lab/dse.ldif.before-bdb-rollback

Change nsslapd-backend-implement: mdb back to nsslapd-backend-implement: bdb in /etc/dirsrv/slapd-bdb-lab/dse.ldif, then restore SELinux context on the file:

bash
restorecon -v /etc/dirsrv/slapd-bdb-lab/dse.ldif

Start the instance on the original BDB files:

bash
dsctl bdb-lab start

Confirm the instance is healthy before you return it to service:

bash
dsctl bdb-lab healthcheck

Do not use this shortcut after cleanup, when BDB files are incomplete, or when the installed BDB package no longer matches the Directory Server build.

mdb2bdb exists only on builds that still ship Berkeley DB:

bash
dsctl bdb-lab dblib mdb2bdb --help
output
usage: dsctl [-v] [-j] [instance] dblib mdb2bdb [-h] [--tmpdir TMPDIR]

options:
  -v, --verbose    Display verbose operation tracing during command execution
  -j, --json       Return result in JSON object

If --help reports that mdb2bdb is unknown, the installed build no longer provides a BDB rollback path. The presence of the subcommand alone does not prove that the installed package provides a write-capable BDB backend on your platform.

Do not treat mdb2bdb as a permanent rollback strategy on releases that remove BDB entirely. It performs another full export-and-import conversion and can require substantial time and disk space. Plan recovery around verified backups and replica reinitialization instead. Run restore and reinitialization workflows from Restore a Replicated 389 Directory Server Safely when a converted replica cannot be returned to service.


What's next

After you complete this guide, continue with:

Summary

  1. Detect the backend with dsconf or dse.ldif before you schedule downtime.
  2. Choose automated bdb2mdb, replica rebuild, or manual LDIF migration.
  3. Verify disk space, create a verified native BDB backup, and record package versions.
  4. Run dsctl INSTANCE dblib bdb2mdb on one instance at a time.
  5. Validate LDAP, LDAPS, indexed searches, plug-ins, and replication.
  6. Convert replicas one at a time in replicated topologies; confirm ds-replcheck before returning traffic.
  7. Create the first LMDB-native backup, then run dsctl INSTANCE dblib cleanup and remove any leftover migration LDIF files.
  8. Use manual LDIF or replica reinitialization when automated conversion fails.

References


Frequently Asked Questions

1. Does dnf update 389-ds-base convert Berkeley DB to LMDB automatically?

No. A routine package update installs newer 389-ds-base RPMs but does not convert an existing BDB-backed instance to LMDB. You must run a supported database migration procedure such as dsctl dblib bdb2mdb or a manual LDIF export and import.

2. How do I check whether my instance uses BDB or LMDB?

On a running instance, run dsconf INSTANCE backend config get and read nsslapd-backend-implement. When the instance is stopped, grep the same attribute in /etc/dirsrv/slapd-INSTANCE/dse.ldif. The value bdb means migration is required; mdb means the instance already uses LMDB.

3. When can I run dsctl dblib cleanup?

Only after LDAP, LDAPS, entry counts, plug-ins, indexes, replication, and application checks all pass on the LMDB instance. Cleanup removes obsolete database files for the inactive backend implementation and closes the simple dse.ldif rollback path while old BDB files still exist.

4. Can BDB and LMDB replicas coexist during a rolling migration?

Yes, briefly. Replication carries LDAP changes, not native database files, so one replica can run LMDB while another still runs BDB. Migrate one Directory Server instance at a time; in a one-instance-per-host topology, this is also host by host. Verify synchronization before continuing.

5. Is mdb2bdb a safe production rollback?

mdb2bdb exists only when the installed build still provides write-capable BDB support; the presence of the subcommand alone does not prove that your platform still ships a usable BDB backend. It performs another full export-and-import conversion and can require substantial time and disk space. Running dblib cleanup removes the original BDB files and closes the simple dse.ldif rollback path. Changing dse.ldif back to bdb is lossless only while LMDB has accepted no new writes; once newer changes exist, use LDIF transfer, a fresh supported conversion, or replica reinitialization instead. Do not depend on BDB rollback on streams where BDB support is deprecated or removed.
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 …