
Managing user authentication across multiple Linux servers without a centralized directory is a recipe for administrative chaos. When you need one source of truth for user accounts, groups, and access policies, OpenLDAP is the go-to open-source solution that sysadmins have trusted for decades. This guide walks you through a complete OpenLDAP on AlmaLinux 10 setup, from installing build dependencies to enabling TLS encryption and verifying the installation. By the end, you will have a production-ready directory server running OpenLDAP 2.6.13 on AlmaLinux 10.
One important thing to know upfront: AlmaLinux 10, like its upstream RHEL 10, no longer ships the openldap-servers RPM package in its repositories. That means the standard dnf install openldap-servers command you may have used on older RHEL systems will not work here. Instead, you need to compile OpenLDAP directly from source. This guide covers that entire process in detail so you know exactly what you are doing and why at every step.
What Is OpenLDAP and Why Does It Matter for AlmaLinux 10?
OpenLDAP is an open-source implementation of the Lightweight Directory Access Protocol (LDAP). It stores users, groups, and organizational data in a hierarchical directory tree, similar in concept to Microsoft Active Directory but built natively for Linux and Unix environments.
Here are the most common real-world use cases for running OpenLDAP on a Linux server:
- Centralized user authentication across multiple Linux servers without managing separate
/etc/passwdentries on each machine - Single Sign-On (SSO) foundation for web applications, VPNs, and mail servers
- POSIX account management for granting SSH login access based on group membership
- Integration with SSSD for seamless Linux client authentication
AlmaLinux 10 uses the RHEL 10 codebase, which dropped the openldap-servers package. This means you must build OpenLDAP from source. The current stable release is OpenLDAP 2.6.13, which uses the MDB (Memory-Mapped Database) backend. MDB replaces the old BDB and HDB backends, offering better performance and reliability.
Prerequisites
Before you start this Linux server tutorial, make sure the following are in place:
- A server running AlmaLinux 10 with at least 2 GB of RAM and 20 GB of disk space
- Root or sudo access to the server
- A fully qualified domain name (FQDN) configured on the server (set it with
hostnamectl set-hostname ldap.example.com) - Ports 389 (LDAP) and 636 (LDAPS) available and not blocked
- Active internet access to download the OpenLDAP source tarball
- Basic familiarity with the Linux terminal and text editors such as
vimornano
Confirm your OS version before starting:
cat /etc/almalinux-release
Expected output:
AlmaLinux release 10.0 (Purple Lion)
Step 1: Update AlmaLinux 10
Always start with a full system update. This prevents dependency conflicts and ensures all security patches are in place before you build new software.
sudo dnf update -y
If the update includes a new kernel or core library changes, reboot before continuing:
sudo reboot
After the reboot, verify your OS version one more time to confirm you are working on the right machine.
Step 2: Install Required Build Dependencies
Because the OpenLDAP server daemon (slapd) is not available as a pre-built package on AlmaLinux 10, you need a full compiler toolchain and several development libraries to build it from source.
First, install the Development Tools group, which includes gcc, make, autoconf, and related utilities:
sudo dnf groupinstall "Development Tools" -y
Next, install the specific development libraries that OpenLDAP needs at compile time:
sudo dnf install wget vim cyrus-sasl-devel libtool-ltdl-devel \
openssl-devel make libtool autoconf tar gcc perl perl-devel groff -y
Here is what the key packages do:
cyrus-sasl-devel: Enables SASL authentication support in OpenLDAPopenssl-devel: Required for TLS/SSL encryption at compile timelibtool-ltdl-devel: Needed for dynamic module loading supportgroff: Required for man page generation during the build
Verify the compiler installed correctly:
gcc --version
You should see output similar to gcc (GCC) 14.x.x.
Step 3: Create the LDAP System User Account
Running slapd as a dedicated non-privileged system user is a security best practice. If the service is ever compromised, the attacker is limited to that user’s permissions rather than root access.
Create the system user with a fixed UID of 55, no home directory, and no login shell:
sudo useradd -r -M -d /var/lib/openldap -u 55 -s /usr/sbin/nologin ldap
Verify the user was created successfully:
id ldap
Expected output:
uid=55(ldap) gid=55(ldap) groups=55(ldap)
The -M flag skips home directory creation. The data directory /var/lib/openldap will be set up manually in a later step with proper ownership.
Step 4: Download and Extract the OpenLDAP Source Code
Download the latest stable OpenLDAP tarball directly from the official release page. Using a version variable makes the commands reusable when newer versions are released.
VER=2.6.13
wget https://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-${VER}.tgz
Extract the archive and move it to /opt to keep your source builds organized and separate from system files:
tar xzf openldap-${VER}.tgz
sudo mv openldap-${VER} /opt/openldap-${VER}
Confirm the directory is in place:
ls /opt/openldap-${VER}
You should see the source tree directories including servers/, clients/, libraries/, and the configure script.
Step 5: Compile and Install OpenLDAP from Source
Navigate into the source directory and run the configure script. The flags you pass here determine which features get compiled into the binary.
cd /opt/openldap-${VER}
Run the configure script with TLS, SASL, and module support enabled:
sudo ./configure --prefix=/usr --sysconfdir=/etc \
--enable-debug --with-tls=openssl --with-cyrus-sasl \
--enable-dynamic --enable-crypt --enable-spasswd \
--enable-slapd --enable-modules --enable-rlookups
Here is what the important flags mean:
--with-tls=openssl: Uses OpenSSL for encryption instead of GnuTLS--with-cyrus-sasl: Enables SASL authentication mechanisms--enable-modules: Allows loading modules likeback_mdb.laat runtime--enable-slapd: Explicitly includes the server daemon in the build
A successful configure run ends with: “Please run make depend to build dependencies”. Now build and install:
sudo make depend
sudo make
sudo make install
The make step takes a few minutes depending on your server’s CPU speed. After it completes, verify the installation:
/usr/libexec/slapd -VV
Expected output:
@(#) $OpenLDAP: slapd 2.6.13 (Mar 21 2026)
Confirm the configuration files are in place:
ls /etc/openldap/
You should see certs/, schema/, ldap.conf, slapd.conf, and slapd.ldif.
Step 6: Configure OpenLDAP with cn=config Backend
This is the most technical section of the guide. cn=config is OpenLDAP’s dynamic configuration backend, stored under /etc/openldap/slapd.d/. Unlike the older static slapd.conf approach, cn=config lets you apply configuration changes via standard LDAP operations while the server is running, without a restart.
Start by creating the required directories and setting ownership:
sudo mkdir -p /var/lib/openldap /etc/openldap/slapd.d
sudo chown -R ldap:ldap /var/lib/openldap
sudo chown root:ldap /etc/openldap/slapd.conf
sudo chmod 640 /etc/openldap/slapd.conf
Create the Sudo Schema LDIF
The sudo schema allows you to manage sudoers rules through LDAP rather than editing /etc/sudoers on every server. First, check that your system’s sudo binary supports LDAP:
sudo -V | grep -i ldap
Locate the schema file that ships with the sudo package:
rpm -ql sudo | grep schema.OpenLDAP
Copy it to the OpenLDAP schema directory:
sudo cp /usr/share/doc/sudo/schema.OpenLDAP /etc/openldap/schema/sudo.schema
Now create the LDIF version of the sudo schema at /etc/openldap/schema/sudo.ldif:
sudo vi /etc/openldap/schema/sudo.ldif
Add the following content:
dn: cn=sudo,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: sudo
olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.1 NAME 'sudoUser'
DESC 'User(s) who may run sudo' EQUALITY caseExactIA5Match
SUBSTR caseExactIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.2 NAME 'sudoHost'
DESC 'Host(s) who may run sudo' EQUALITY caseExactIA5Match
SUBSTR caseExactIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.3 NAME 'sudoCommand'
DESC 'Command(s) to be executed by sudo' EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.5 NAME 'sudoOption'
DESC 'Options(s) followed by sudo' EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.6 NAME 'sudoRunAsUser'
DESC 'User(s) impersonated by sudo' EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.7 NAME 'sudoRunAsGroup'
DESC 'Group(s) impersonated by sudo' EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcObjectClasses: ( 1.3.6.1.4.1.15953.9.2.1 NAME 'sudoRole' SUP top
STRUCTURAL DESC 'Sudoer Entries' MUST ( cn )
MAY ( sudoUser $ sudoHost $ sudoCommand $ sudoRunAsUser $
sudoRunAsGroup $ sudoOption $ description ) )
Configure the slapd.ldif Database File
Back up the default slapd.ldif before replacing it:
sudo mv /etc/openldap/slapd.ldif /etc/openldap/slapd.ldif.bak
Create a fresh slapd.ldif with the global config, MDB backend module, required schemas, and database access controls:
sudo vi /etc/openldap/slapd.ldif
Add the following:
dn: cn=config
objectClass: olcGlobal
cn: config
olcArgsFile: /var/lib/openldap/slapd.args
olcPidFile: /var/lib/openldap/slapd.pid
dn: cn=schema,cn=config
objectClass: olcSchemaConfig
cn: schema
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModulepath: /usr/libexec/openldap
olcModuleload: back_mdb.la
include: file:///etc/openldap/schema/core.ldif
include: file:///etc/openldap/schema/cosine.ldif
include: file:///etc/openldap/schema/nis.ldif
include: file:///etc/openldap/schema/inetorgperson.ldif
include: file:///etc/openldap/schema/sudo.ldif
dn: olcDatabase=frontend,cn=config
objectClass: olcDatabaseConfig
objectClass: olcFrontendConfig
olcDatabase: frontend
olcAccess: to dn.base="cn=Subschema" by * read
olcAccess: to *
by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage
by * none
dn: olcDatabase=config,cn=config
objectClass: olcDatabaseConfig
olcDatabase: config
olcRootDN: cn=config
olcAccess: to *
by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage
by * none
Run a dry run first to catch any typos or missing schema references before applying:
sudo slapadd -n 0 -F /etc/openldap/slapd.d -l /etc/openldap/slapd.ldif -u
If the dry run completes cleanly with Closing DB... and no errors, apply the configuration:
sudo slapadd -n 0 -F /etc/openldap/slapd.d -l /etc/openldap/slapd.ldif
Set ownership on the configuration directory:
sudo chown -R ldap:ldap /etc/openldap/slapd.d
Step 7: Create the systemd Service File and Start slapd
Since you installed OpenLDAP from source, there is no systemd unit file included. You need to create one manually at /etc/systemd/system/slapd.service.
sudo vi /etc/systemd/system/slapd.service
Add the following service definition:
[Unit]
Description=OpenLDAP Server Daemon
After=syslog.target network-online.target
Documentation=man:slapd
Documentation=man:slapd-mdb
[Service]
Type=forking
PIDFile=/var/lib/openldap/slapd.pid
Environment="SLAPD_URLS=ldap:/// ldapi:/// ldaps:///"
Environment="SLAPD_OPTIONS=-F /etc/openldap/slapd.d"
ExecStart=/usr/libexec/slapd -u ldap -g ldap -h ${SLAPD_URLS} $SLAPD_OPTIONS
[Install]
WantedBy=multi-user.target
Reload systemd and enable the service to start at boot:
sudo systemctl daemon-reload
sudo systemctl enable --now slapd
Verify slapd is running:
systemctl status slapd
You should see Active: active (running) in the output. If you see failed, check the journal immediately with sudo journalctl -u slapd -xe to review the error.
Step 8: Configure the Root DN and MDB Database Backend
With slapd running, you can now configure the MDB database backend that will hold your actual directory data. Start by generating a hashed password for the root DN (your LDAP administrator account):
sudo slappasswd
You will be prompted twice. Copy the output hash that starts with {SSHA}.
Create the root DN LDIF file:
vi rootdn.ldif
Add the following content. Replace dc=example,dc=com with your actual domain and paste your {SSHA} hash at olcRootPW:
dn: olcDatabase=mdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: mdb
olcDbMaxSize: 42949672960
olcDbDirectory: /var/lib/openldap
olcSuffix: dc=example,dc=com
olcRootDN: cn=admin,dc=example,dc=com
olcRootPW: {SSHA}YourHashedPasswordHere
olcDbIndex: uid pres,eq
olcDbIndex: cn,sn pres,eq,approx,sub
olcDbIndex: mail pres,eq,sub
olcDbIndex: objectClass pres,eq
olcDbIndex: loginShell pres,eq
olcDbIndex: sudoUser,sudoHost pres,eq
olcAccess: to attrs=userPassword,shadowLastChange,shadowExpire
by self write
by anonymous auth
by dn.subtree="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage
by dn.subtree="ou=system,dc=example,dc=com" read
by * none
olcAccess: to dn.subtree="ou=system,dc=example,dc=com"
by dn.subtree="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage
by * none
olcAccess: to dn.subtree="dc=example,dc=com"
by dn.subtree="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage
by users read
by * none
Apply the configuration to the running slapd instance:
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f rootdn.ldif
Expected output:
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
adding new entry "olcDatabase=mdb,cn=config"
Step 9: Add the Base DN and Organizational Units
Now define the top of your directory tree with a base domain object and two organizational units (OUs): one for user accounts and one for groups.
vi basedn.ldif
Add the following, replacing the domain components with your own:
dn: dc=example,dc=com
objectClass: dcObject
objectClass: organization
objectClass: top
o: Example Organization
dc: example
dn: ou=groups,dc=example,dc=com
objectClass: organizationalUnit
objectClass: top
ou: groups
dn: ou=people,dc=example,dc=com
objectClass: organizationalUnit
objectClass: top
ou: people
Import the base DN:
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f basedn.ldif
You should see three adding new entry lines, one for each object.
Step 10: Create LDAP Users and a Bind DN Service Account
With the directory structure in place, add your first user. A standard LDAP user entry needs the inetOrgPerson, posixAccount, and shadowAccount objectClasses to support both directory lookups and Linux login.
vi users.ldif
dn: uid=jdoe,ou=people,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: jdoe
cn: John
sn: Doe
loginShell: /bin/bash
uidNumber: 10000
gidNumber: 10000
homeDirectory: /home/jdoe
shadowMax: 60
shadowMin: 1
shadowWarning: 7
shadowInactive: 7
shadowLastChange: 0
dn: cn=jdoe,ou=groups,dc=example,dc=com
objectClass: posixGroup
cn: jdoe
gidNumber: 10000
memberUid: jdoe
Import the user:
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f users.ldif
Set the user’s password:
sudo ldappasswd -H ldapi:/// -Y EXTERNAL -S "uid=jdoe,ou=people,dc=example,dc=com"
Create a Read-Only Bind DN Service Account
A bind DN is a service account that LDAP clients use to authenticate against the directory for read-only lookups, such as resolving user IDs and group memberships. Using a dedicated bind DN keeps your admin credentials out of every client’s configuration file.
Generate a separate password hash for the bind DN:
sudo slappasswd
Create the bind DN LDIF:
vi bindDNuser.ldif
dn: ou=system,dc=example,dc=com
objectClass: organizationalUnit
objectClass: top
ou: system
dn: cn=readonly,ou=system,dc=example,dc=com
objectClass: organizationalRole
objectClass: simpleSecurityObject
cn: readonly
userPassword: {SSHA}YourBindDNPasswordHash
description: Bind DN user for LDAP client operations
Apply the bind DN:
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f bindDNuser.ldif
Step 11: Enable TLS/SSL Encryption for OpenLDAP
Without TLS, credentials travel between clients and the LDAP server in plain text. Port 389 without encryption is acceptable on a private loopback interface only. For any network traffic, TLS is required.
There are two approaches:
- LDAPS (port 636): Full TLS from the moment a connection opens
- StartTLS: Upgrades a plain port 389 connection to TLS before sending credentials
For production, use a CA-signed certificate from Let’s Encrypt or your internal PKI. This guide uses a self-signed certificate.
Generate the certificate and private key:
sudo openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 \
-keyout /etc/pki/tls/ldapserver.key \
-out /etc/pki/tls/ldapserver.crt
Set proper ownership so the ldap user can read both files:
sudo chown ldap:ldap /etc/pki/tls/{ldapserver.crt,ldapserver.key}
Create the TLS configuration LDIF:
sudo vi add-tls.ldif
dn: cn=config
changetype: modify
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/pki/tls/ldapserver.crt
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/pki/tls/ldapserver.key
-
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/pki/tls/ldapserver.crt
Apply it:
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f add-tls.ldif
Update the LDAP client config to trust the certificate:
sudo vi /etc/openldap/ldap.conf
Change the TLS_CACERT line to:
TLS_CACERT /etc/pki/tls/ldapserver.crt
Restart slapd and verify both ports are open:
sudo systemctl restart slapd
ss -tlnp | grep slapd
Expected output shows listeners on both port 389 and port 636.
Step 12: Open Firewall Ports for LDAP and LDAPS
AlmaLinux 10 uses firewalld as its default firewall manager. Open both LDAP and LDAPS services permanently:
sudo firewall-cmd --add-service={ldap,ldaps} --permanent
sudo firewall-cmd --reload
Verify both services are active:
sudo firewall-cmd --list-services
Expected output includes ldap ldaps alongside any existing services like ssh and cockpit.
| Port | Protocol | Purpose |
|---|---|---|
| 389 | TCP | LDAP (plain or StartTLS) |
| 636 | TCP | LDAPS (TLS from connection start) |
Step 13: Test the OpenLDAP Installation with ldapsearch
Run three progressive tests to confirm everything works end to end.
Test 1: Local EXTERNAL SASL query (verifies slapd responds and base DN is visible):
sudo ldapsearch -H ldapi:/// -Y EXTERNAL -b "dc=example,dc=com" -LLL
You should see output listing dc=example,dc=com, the two OUs, and the jdoe user entry.
Test 2: Bind DN authentication over plain LDAP (verifies client-style authentication works):
ldapsearch -x -H ldap://127.0.0.1 \
-D "cn=readonly,ou=system,dc=example,dc=com" \
-W -b "ou=people,dc=example,dc=com"
Enter the bind DN password when prompted. A successful response returns the user entries.
Test 3: LDAPS encrypted connection (verifies TLS on port 636 is operational):
ldapsearch -x -H ldaps://127.0.0.1 \
-D "cn=readonly,ou=system,dc=example,dc=com" \
-W -b "ou=people,dc=example,dc=com"
You can also confirm individual user authentication with:
ldapwhoami -x -D "uid=jdoe,ou=people,dc=example,dc=com" -W
Troubleshooting Common OpenLDAP Issues on AlmaLinux 10
Even with a clean install, a few issues come up regularly across the community. Here are the most common ones with direct solutions.
Problem: ldap_add: Insufficient access (50) when applying rootdn.ldif
- Cause: The
olcAccessrules inslapd.ldifare too restrictive or the config database ACL was not applied correctly. - Fix: Verify the current ACLs on the config database:
sudo ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config" "(olcDatabase=config)" olcAccess
Ensure the EXTERNAL SASL DN has manage access. If not, re-apply slapd.ldif with the correct ACL block.
Problem: include file:///etc/openldap/schema/ppolicy.ldif failed
- Cause: The ppolicy schema was referenced in
slapd.ldifbut the file does not exist. - Fix: Remove the ppolicy
includeline fromslapd.ldifif you do not need password policy features. If you do need it, create theppolicy.ldiffrom the schema source in/etc/openldap/schema/ppolicy.schemabefore runningslapadd.
Problem: index attribute "sudoUser" undefined
- Cause: The
rootdn.ldifreferencessudoUserinolcDbIndexbut the sudo schema was not loaded intocn=configyet. - Fix: Confirm
sudo.ldifis included inslapd.ldifbefore theolcDatabase=mdbentry. Delete/etc/openldap/slapd.d, re-runslapaddwith the correctedslapd.ldif, then re-applyrootdn.ldif.
Problem: chown: invalid user: 'ldap:ldap'
- Cause: The
ldapsystem user was not created before runningchowncommands. - Fix: Run the
useraddcommand from Step 3 first, then retry thechowncommands.
Problem: slapd starts but LDAPS on port 636 refuses connections
- Fix: Verify TLS is configured correctly using:
openssl s_client -connect localhost:636 -showcerts
If you see a certificate error, recheck the file paths and ownership in add-tls.ldif. Also confirm the SLAPD_URLS environment variable in the systemd service file includes ldaps:///.
Congratulations! You have successfully installed OpenLDAP. Thanks for using this tutorial for installing OpenLDAP on your AlmaLinux OS 10 system. For additional or useful information, we recommend you check the official OpenLDAP website.