Local OpenLDAP Setup
About
Setting up a local LDAP server is essential for learning, testing integrations, or developing features dependent on centralized directory services. This setup mimics real-world LDAP deployments and allows experimentation with authentication, user federation (e.g., in Keycloak), and automation.
For simplicity and repeatability, we use Docker to spin up:
An OpenLDAP server using the
osixia/openldapimageA GUI client (
phpLDAPadmin) to manage the directory visually
Prerequisites
Docker and Docker Compose installed
Basic knowledge of LDAP terminology (DN, OU, CN, etc.)
Familiarity with command-line operations
Technology Stack
OpenLDAP
Lightweight directory server implementation
phpLDAPadmin
Web UI for LDAP directory management
Docker
Containerized setup for isolated testing
Folder Structure
Create a folder named local-ldap-setup:
mkdir local-ldap-setup && cd local-ldap-setupCreate the following files:
.
├── docker-compose.yml
└── ldap-bootstrap/seed-data.ldifdocker-compose.yml
docker-compose.ymlThe docker-compose.yml file is a configuration file used by Docker Compose to define and run multi-container Docker applications — in this case, an LDAP server and a GUI admin interface.
What it does in this setup
Defines two services:
openldap: Starts the OpenLDAP directory serverphpldapadmin: Starts a web-based GUI to interact with the LDAP directory
Sets environment variables:
LDAP_DOMAIN,LDAP_ORGANISATION, andLDAP_ADMIN_PASSWORDdefine the base configuration for the OpenLDAP server.
Exposes ports:
Port
389: for standard LDAP communicationPort
636: for LDAPS (secure LDAP, optional)Port
6443: to access phpLDAPadmin via a browser over HTTPS
Mounts seed data:
Maps the local file
seed-data.ldifinto the container so it can be auto-imported at startup to bootstrap the directory with users, groups, and OUs.
This file enables you to spin up a complete LDAP environment with a single command:
docker-compose up -dseed-data.ldif
seed-data.ldifThe seed-data.ldif file serves as a bootstrap initializer for your LDAP directory. It defines a realistic, structured set of directory entries that simulate how an enterprise LDAP setup might look. This includes:
Organizational Units (OUs) for different categories of entries
Users across departments
Groups for access control
Service accounts for system integrations (e.g., Keycloak)
When this file is mounted in the Docker Compose setup, the OpenLDAP container reads and loads its contents at startup — allowing your directory to come pre-populated with meaningful data.
1. Organizational Units (OUs)
Organizational Units help categorize and separate types of directory entries. This keeps the directory clean and enables easier access control, delegation, and search filters.
dn: ou=People,dc=corp,dc=acme,dc=com
objectClass: organizationalUnit
ou: Peopleou=People: Stores human user accounts like employees.ou=Groups: Stores role-based groups (engineering, HR, etc.).ou=Departments: Placeholder for defining department-based entries (can be linked viadepartmentNumber).ou=ServiceAccounts: Stores machine or integration accounts used by applications.
These OUs mirror common real-world enterprise directory layouts.
2. User Entries
Each user is an inetOrgPerson object — a standard LDAP object class for representing people. It includes attributes like cn, sn, uid, mail, and userPassword.
Example:
dn: uid=alice.jones,ou=People,dc=corp,dc=acme,dc=com
objectClass: inetOrgPerson
cn: Alice Jones
sn: Jones
uid: alice.jones
mail: [email protected]
userPassword: Password@123
departmentNumber: ENG
title: Senior Software EngineerUID: Unique identifier within the directory.
departmentNumber: Common attribute used for department-level filtering.
title: Useful for role-based access, UI display, or hierarchy mapping.
This structure makes it easy to:
Filter users by department
Assign access based on job title
Identify users with consistent naming conventions
3. Group Entries
Groups are created using the groupOfNames object class, which references user DNs via the member attribute.
dn: cn=engineering,ou=Groups,dc=corp,dc=abc,dc=com
objectClass: groupOfNames
cn: engineering
member: uid=alice.jones,ou=People,dc=corp,dc=abc,dc=comcn=engineering: Group namemember: References full DN of user(s)
These groups support:
Role-based access control (RBAC) in external apps
LDAP queries for access policies (
memberOfattributes, filters)
You can associate users with multiple groups for layered access.
4. Service Account
Service accounts are special user entries used by applications (e.g., Keycloak) to connect to the LDAP server.
dn: uid=svc.keycloak,ou=ServiceAccounts,dc=corp,dc=abc,dc=com
objectClass: inetOrgPerson
cn: Keycloak Service Account
sn: Service
uid: svc.keycloak
mail: [email protected]
userPassword: Keycloak@2024Located under
ou=ServiceAccountsFollows naming convention
svc.<application>Can be granted read-only or scoped privileges
These accounts enable:
Application integration without using human credentials
Logging and auditing by account identity
Password rotation automation via DevOps pipelines
Note: OpenLDAP in this Docker image automatically applies LDIF files found in the bootstrap path during first-time container initialization.
Docker Compose File
docker-compose.yml
version: '3.8'
services:
openldap:
image: osixia/openldap:1.5.0
container_name: openldap
command: --copy-service
environment:
LDAP_ORGANISATION: "Abc Solutions Pvt Ltd"
LDAP_DOMAIN: "corp.abc.com"
LDAP_ADMIN_PASSWORD: "StrongAdminPass123"
ports:
- "389:389"
- "636:636"
volumes:
- ./ldap-bootstrap/seed-data.ldif:/container/service/slapd/assets/config/bootstrap/ldif/50-bootstrap.ldif
phpldapadmin:
image: osixia/phpldapadmin:0.9.0
container_name: phpldapadmin
environment:
PHPLDAPADMIN_LDAP_HOSTS: openldap
ports:
- "6443:443"
depends_on:
- openldapThis will start OpenLDAP on port 389 and phpLDAPadmin UI on https://localhost:6443.
corp.abc.comis used as a subdomain-style domain which mimics real enterprise usage.Organization name
Abc Solutions Pvt Ltdreflects a real company format.Admin password is set to a strong placeholder (
StrongAdminPass123). You should secure this in.envor a secrets manager in a real setup.

Initial Directory Seed (seed-data.ldif)
seed-data.ldif)# Organizational Units
dn: ou=People,dc=corp,dc=abc,dc=com
objectClass: organizationalUnit
ou: People
dn: ou=Groups,dc=corp,dc=abc,dc=com
objectClass: organizationalUnit
ou: Groups
dn: ou=Departments,dc=corp,dc=abc,dc=com
objectClass: organizationalUnit
ou: Departments
dn: ou=ServiceAccounts,dc=corp,dc=abc,dc=com
objectClass: organizationalUnit
ou: ServiceAccounts
# Sample user in Engineering
dn: uid=alice.jones,ou=People,dc=corp,dc=abc,dc=com
objectClass: inetOrgPerson
cn: Alice Jones
sn: Jones
uid: alice.jones
mail: [email protected]
userPassword: Password@123
departmentNumber: ENG
title: Senior Software Engineer
# Sample user in HR
dn: uid=bob.smith,ou=People,dc=corp,dc=abc,dc=com
objectClass: inetOrgPerson
cn: Bob Smith
sn: Smith
uid: bob.smith
mail: [email protected]
userPassword: Password@123
departmentNumber: HR
title: HR Manager
# Sample group for engineers
dn: cn=engineering,ou=Groups,dc=corp,dc=abc,dc=com
objectClass: groupOfNames
cn: engineering
member: uid=alice.jones,ou=People,dc=corp,dc=abc,dc=com
# Sample group for HR
dn: cn=hr-team,ou=Groups,dc=corp,dc=abc,dc=com
objectClass: groupOfNames
cn: hr-team
member: uid=bob.smith,ou=People,dc=corp,dc=abc,dc=com
# Service account
dn: uid=svc.keycloak,ou=ServiceAccounts,dc=corp,dc=abc,dc=com
objectClass: inetOrgPerson
cn: Keycloak Service Account
sn: Service
uid: svc.keycloak
mail: [email protected]
userPassword: Keycloak@2024
Start the Containers
docker-compose up -dCheck logs:
docker logs -f openldapAccess phpLDAPadmin UI
Open your browser and visit:
https://localhost:6443Login DN:
cn=admin,dc=example,dc=com
Password:
admin
Once logged in:
Browse entries
Add more users/groups via GUI
Edit attributes
Verifying LDAP Server Using ldapsearch
Install LDAP utilities if not already:
sudo apt install ldap-utils # Debian/UbuntuExample command:
ldapsearch -x -H ldap://localhost -D "cn=admin,dc=example,dc=com" -w admin -b "dc=example,dc=com"Search for user:
ldapsearch -x -b "dc=example,dc=com" "(uid=john.doe)"Based on local LDAP setup
Keycloak LDAP Configuration
Field
Value
Edit Mode
READ_ONLY or IMPORT (choose based on use case)
Vendor
Other
Connection URL
ldap://localhost:389
Bind DN
cn=admin,dc=corp,dc=abc,dc=com
Bind Credential
StrongAdminPass123 (or whatever you used in docker-compose)
Users DN
ou=People,dc=corp,dc=abc,dc=com
Username LDAP attribute
uid
RDN LDAP attribute
uid
UUID LDAP attribute
entryUUID
User Object Classes
inetOrgPerson
Search Scope
One Level or Subtree (Subtree recommended)
Pagination
ON
Sync Registrations
OFF (or ON if you want new Keycloak users written to LDAP)
Allow Kerberos Authentication
OFF (unless you’re integrating with AD or SASL/Kerberos)
Batch Size For LDAP Queries
1000 (default)
Keycloak Group Mapper Configuration
If we're syncing groups from LDAP as well (e.g., cn=engineering, cn=hr-team):
Field
Value
Mapper Type
Group LDAP Mapper
Name
LDAP Groups Mapper
LDAP Groups DN
ou=Groups,dc=corp,dc=abc,dc=com
Group Name LDAP Attribute
cn
Group Object Classes
groupOfNames
Membership LDAP Attribute
member
Membership Attribute Type
DN
User Groups Retrieve Strategy
LOAD_GROUPS_BY_MEMBER_ATTRIBUTE
Mapped Group Attributes
Leave blank or include additional group info like description
Mode
READ_ONLY
Keycloak Test LDAP Sync
Once configured:
Navigate to User Federation →
ldap→ Synchronize all usersCheck the Users tab →
alice.jonesandbob.smithshould appear.Navigate to Groups (if group mapper is configured) →
engineering,hr-teamshould be available.
phpLDAPadmin Login Details
Field
Value
Login URL
https://localhost:6443
Login DN (username)
cn=admin,dc=corp,dc=abc,dc=com
Password
StrongAdminPass123 (as defined in LDAP_ADMIN_PASSWORD)
We may get a browser warning because the container uses a self-signed TLS certificate. We can safely proceed for local use.



Verify seeded data for People, Groups, Departments, and ServiceAccounts

Last updated