Migrating From 389-ds Version 1.3 to 1.4

The default LDAP schema used in 389-ds versions up until 1.3 had been RFC 2307. As a reluctant—and only once every few years—LDAP admin I was mostly ignorant of this fact until a recent upgrade made me pay closer attention. As of version 1.4 it seems the 389-ds project has switched to the RFC 2307bis schema¹ in addition to some other attribute changes. This schema is based on RFC 2307, but has a few incompatible changes. Either schema will work², though after some consideration I decided that sticking with the old schema would add a bit of technical debt to my system. In general I prefer to stay as close to upstream as possible to lessen the maintenance burden on myself.

This posed a dilemma which delayed my migration for a few months, as I didn’t have time to wrap my head around the schema differences and there didn’t seem to be a tool to help with this migration. After several discussions with the developers in #389 on IRC (Libera Chat currently) I finally understood the context of their decision and the scope of changes required. Since LDIF is a text-based format I reasoned that it should be easy enough to write a script to do the migration process myself.

TL;DR I wrote an awk script to do the migration. You can find it here: migrate-ldif-389-ds-1.4.awk

Migrating an LDIF With Awk

At first I thought the easiest way to do this would be using sed, so I compared dumps from my 389-ds 1.3.x instance and a fresh 1.4.x instance and set about writing a slew of search and replace patterns. After a few minutes I realized there were going to be some corner cases to handle that involved multi-line records, context-aware modifications, etc, and that awk would be a much more appropriate tool for this job. I begrudgingly brushed up on my awk and got to work.

A common principle of tasks like this is: know your data. I was not concerned with theoretical corner cases, error handling, or with covering the RFC 2307 and 2307bis schemas in their entirety—only the bare minimum to get my directory server up and running. Having said that, here is a summarized list of changes I had to make:


  • Replace objectClass: person with objectClass: nsPerson
  • Replace objectClass: organizationalPerson with objectClass: nsAccount
  • Replace objectClass: inetOrgPerson with objectClass: nsOrgPerson
  • Remove givenName
  • Remove sn
  • Add displayName
  • Remove facsimileTelephoneNumber


  • Replace objectClass: groupofuniquenames with objectClass: groupOfNames
  • Replace objectClass: posixgroup with objectClass: posixGroup
  • Add objectClass: nsMemberOf to each group
  • Convert memberUid: name for each member to member: uid=name,ou=People,... with their DNs (his is the major difference coming from RFC 2307bis)

Your data might require changes to other attributes to be changed or have different corner cases to handle.

Using the Script

To use the script you must have an LDIF file from your old 389-ds server. The best way to do that is using db2ldif with your LDAP instance temporarily shut down:

# systemctl stop dirsrv@mjanja.service
# db2ldif -Z mjanja -n userRoot -a userRoot.ldif
# systemctl start dirsrv@mjanja.service
# rsync -av userRoot.ldif newserver:

Then, on the new server, run it through the script and import it into your instance:

# awk -f migrate-ldif-389-ds-1.4.awk \
         userRoot.ldif > /var/lib/dirsrv/slapd-mjanja/ldif/userRoot-migrated.ldif
# chown dirsrv:dirsrv /var/lib/dirsrv/slapd-mjanja/ldif/userRoot-migrated.ldif
# dsconf mjanja backend import userRoot userRoot-migrated.ldif

Note 1: the path to the LDIF file is relative to the instance’s data directory, which is why we copy it to /var/lib/dirsrv/mjanja/ldif.
Note 2: I have hard-coded my base DN. You must edit the script to replace it with yours!

If you’re lucky you will see all your users and groups when you query with ldapsearch:

# ldapsearch -x | tail -n 2
# numResponses: 398
# numEntries: 397

Congratulations! Now you can go on to configure SSSD and the rest of your stack. I am not planning to think about this issue again until I upgrade to CentOS Stream 9 with 389-ds 2.0.x in June, 2024. 😉


¹ For reference, it seems that CentOS 7 / RHEL 7 with 389-ds 1.3.x corresponds to Red Hat Directory Server 10, and CentOS 8 / RHEL 8 with 389-ds 1.4.x corresponds to Red Had Directory Server 11. Red Hat Directory Server 12 uses 389-ds 2.0.x.
² This depends on your definition of “work”. If you plan on using command line tools like dsidm to manage users and groups you will need to migrate your directory server’s data to the 1.4.x format. Otherwise, the 389-ds 1.4.x works absolutely fine with data in 389-ds 1.3.x format.