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/openldap
imageA 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-setup
Create the following files:
.
├── docker-compose.yml
└── ldap-bootstrap/seed-data.ldif
docker-compose.yml
docker-compose.yml
The 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_PASSWORD
define 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.ldif
into 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 -d
seed-data.ldif
seed-data.ldif
The 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: People
ou=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 Engineer
UID: 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=com
cn=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 (
memberOf
attributes, 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@2024
Located under
ou=ServiceAccounts
Follows 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:
- openldap
This will start OpenLDAP on port 389 and phpLDAPadmin UI on https://localhost:6443.
corp.abc.com
is used as a subdomain-style domain which mimics real enterprise usage.Organization name
Abc Solutions Pvt Ltd
reflects a real company format.Admin password is set to a strong placeholder (
StrongAdminPass123
). You should secure this in.env
or 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 -d
Check logs:
docker logs -f openldap
Access phpLDAPadmin UI
Open your browser and visit:
https://localhost:6443
Login 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/Ubuntu
Example 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.jones
andbob.smith
should appear.Navigate to Groups (if group mapper is configured) →
engineering
,hr-team
should 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