Keycloak LDAP Integration: Step-by-Step Tutorial
Last updated: March 2026
LDAP directories remain the backbone of user management in many organizations. Whether you are running OpenLDAP, 389 Directory Server, FreeIPA, or another LDAP-compliant directory, Keycloak’s User Federation feature lets you connect directly and authenticate users against your existing directory without migrating user data.
This tutorial covers the full integration process with generic LDAP directories (as opposed to Active Directory, which has its own specific configuration covered in our AD integration guide). We will start with an OpenLDAP test environment using Docker, then walk through Keycloak federation configuration, user and group mapping, sync modes, and custom attribute mapping.
Setting Up an OpenLDAP Test Environment
Before configuring Keycloak, let us set up a local OpenLDAP instance with test data. This gives you a safe environment to experiment with federation settings before connecting to your production directory.
Docker Compose Setup
Create a docker-compose.yml with both OpenLDAP and Keycloak:
services:
openldap:
image: osixia/openldap:1.5.0
container_name: openldap
environment:
LDAP_ORGANISATION: "Example Corp"
LDAP_DOMAIN: "example.com"
LDAP_ADMIN_PASSWORD: "adminpassword"
LDAP_CONFIG_PASSWORD: "configpassword"
LDAP_READONLY_USER: "true"
LDAP_READONLY_USER_USERNAME: "readonly"
LDAP_READONLY_USER_PASSWORD: "readonlypassword"
ports:
- "389:389"
- "636:636"
volumes:
- ./ldap/bootstrap:/container/service/slapd/assets/config/bootstrap/ldif/custom
command: "--copy-service"
networks:
- auth-network
phpldapadmin:
image: osixia/phpLDAPadmin:0.9.0
container_name: phpldapadmin
environment:
PHPLDAPADMIN_LDAP_HOSTS: openldap
ports:
- "6443:443"
depends_on:
- openldap
networks:
- auth-network
keycloak:
image: quay.io/keycloak/keycloak:26.1.0
container_name: keycloak
command: start-dev
environment:
KC_BOOTSTRAP_ADMIN_USERNAME: admin
KC_BOOTSTRAP_ADMIN_PASSWORD: admin
KC_HEALTH_ENABLED: "true"
ports:
- "8080:8080"
depends_on:
- openldap
networks:
- auth-network
networks:
auth-network:
driver: bridge
For more Keycloak Docker Compose options, use our Docker Compose Generator.
Bootstrap LDAP Data
Create the directory structure with test users and groups. Save this as ldap/bootstrap/01-structure.ldif:
# Create organizational units
dn: ou=People,dc=example,dc=com
objectClass: organizationalUnit
ou: People
dn: ou=Groups,dc=example,dc=com
objectClass: organizationalUnit
ou: Groups
# Create users
dn: uid=jdoe,ou=People,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: jdoe
cn: Jane Doe
sn: Doe
givenName: Jane
mail: [email protected]
userPassword: {SSHA}password123
uidNumber: 1001
gidNumber: 1001
homeDirectory: /home/jdoe
loginShell: /bin/bash
employeeType: engineer
departmentNumber: Engineering
dn: uid=bsmith,ou=People,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: bsmith
cn: Bob Smith
sn: Smith
givenName: Bob
mail: [email protected]
userPassword: {SSHA}password456
uidNumber: 1002
gidNumber: 1002
homeDirectory: /home/bsmith
loginShell: /bin/bash
employeeType: manager
departmentNumber: Product
dn: uid=alee,ou=People,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: alee
cn: Alice Lee
sn: Lee
givenName: Alice
mail: [email protected]
userPassword: {SSHA}password789
uidNumber: 1003
gidNumber: 1003
homeDirectory: /home/alee
loginShell: /bin/bash
employeeType: engineer
departmentNumber: Engineering
# Create groups
dn: cn=developers,ou=Groups,dc=example,dc=com
objectClass: groupOfNames
cn: developers
member: uid=jdoe,ou=People,dc=example,dc=com
member: uid=alee,ou=People,dc=example,dc=com
dn: cn=managers,ou=Groups,dc=example,dc=com
objectClass: groupOfNames
cn: managers
member: uid=bsmith,ou=People,dc=example,dc=com
dn: cn=all-staff,ou=Groups,dc=example,dc=com
objectClass: groupOfNames
cn: all-staff
member: uid=jdoe,ou=People,dc=example,dc=com
member: uid=bsmith,ou=People,dc=example,dc=com
member: uid=alee,ou=People,dc=example,dc=com
Start the environment:
docker compose up -d
Verify LDAP is working:
# Search for all users
ldapsearch -x -H ldap://localhost:389
-D "cn=admin,dc=example,dc=com"
-w adminpassword
-b "ou=People,dc=example,dc=com"
"(objectClass=inetOrgPerson)" uid cn mail
# Search for all groups
ldapsearch -x -H ldap://localhost:389
-D "cn=admin,dc=example,dc=com"
-w adminpassword
-b "ou=Groups,dc=example,dc=com"
"(objectClass=groupOfNames)" cn member
Configuring Keycloak LDAP Federation
Step 1: Create a Realm
Log in to the Keycloak admin console at http://localhost:8080/admin/ and create a new realm (or use an existing one).
Step 2: Add LDAP Federation
- Navigate to User Federation
- Click Add LDAP providers
Step 3: Connection Settings
| Setting | Value | Notes |
|---|---|---|
| Console Display Name | OpenLDAP |
Name shown in admin console |
| Vendor | Other |
Select “Other” for generic LDAP |
| Connection URL | ldap://openldap:389 |
Use the Docker service name |
| Bind Type | simple |
Standard LDAP bind |
| Bind DN | cn=readonly,dc=example,dc=com |
Read-only service account |
| Bind Credential | readonlypassword |
Service account password |
Click Test connection to verify connectivity, then Test authentication to verify credentials.
Step 4: LDAP Search Settings
| Setting | Value | Notes |
|---|---|---|
| Edit Mode | READ_ONLY |
Do not write to LDAP |
| Users DN | ou=People,dc=example,dc=com |
Base DN for user searches |
| Username LDAP attribute | uid |
Attribute used as Keycloak username |
| RDN LDAP attribute | uid |
Relative DN attribute |
| UUID LDAP attribute | entryUUID |
Unique identifier (entryUUID for OpenLDAP) |
| User Object Classes | inetOrgPerson |
Object class filter for users |
| Search Scope | Subtree |
Search nested OUs |
Step 5: Sync Settings
| Setting | Value | Notes |
|---|---|---|
| Import Users | On |
Cache LDAP users in Keycloak |
| Periodic Full Sync | On |
|
| Full Sync Period | 86400 |
Every 24 hours |
| Periodic Changed Users Sync | On |
|
| Changed Users Sync Period | 300 |
Every 5 minutes |
Save the configuration, then click Sync all users to trigger an initial synchronization.
Verifying the Sync
After syncing, navigate to Users in the admin console. You should see the LDAP users (jdoe, bsmith, alee) listed alongside any local Keycloak users. Each federated user shows the LDAP federation as its source.
Test authentication by logging in with an LDAP user:
# Get a token using LDAP credentials
curl -X POST http://localhost:8080/realms/my-realm/protocol/openid-connect/token
-d "client_id=admin-cli"
-d "username=jdoe"
-d "password=password123"
-d "grant_type=password"
Decode the resulting token with our JWT Token Analyzer to inspect the claims.
User Attribute Mapping
Default Mappers
Keycloak creates basic attribute mappers automatically. Review them under User Federation > Your LDAP > Mappers:
| Mapper | LDAP Attribute | Keycloak Attribute |
|---|---|---|
| username | uid |
username |
| first name | givenName |
firstName |
| last name | sn |
lastName |
mail |
email |
Adding Custom Mappers
Map additional LDAP attributes to Keycloak user attributes:
- Go to Mappers tab
- Click Add mapper
- Select user-attribute-ldap-mapper
Example: Department attribute
| Setting | Value |
|---|---|
| Name | department |
| Mapper Type | user-attribute-ldap-mapper |
| User Model Attribute | department |
| LDAP Attribute | departmentNumber |
| Read Only | On |
| Always Read Value From LDAP | On |
Example: Employee type attribute
| Setting | Value |
|---|---|
| Name | employeeType |
| Mapper Type | user-attribute-ldap-mapper |
| User Model Attribute | employeeType |
| LDAP Attribute | employeeType |
| Read Only | On |
| Always Read Value From LDAP | On |
Including Custom Attributes in Tokens
After mapping LDAP attributes to Keycloak user attributes, add protocol mappers to include them in tokens:
- Navigate to Client Scopes > create a new scope (e.g.,
ldap-attributes) - Go to Mappers tab > Add mapper > By configuration > User Attribute
| Setting | Value |
|---|---|
| Name | department |
| User Attribute | department |
| Token Claim Name | department |
| Claim JSON Type | String |
| Add to ID token | On |
| Add to access token | On |
For a detailed guide, see using custom user attributes in Keycloak OIDC tokens.
Group Synchronization
Configure Group Mapper
Add a group mapper to sync LDAP groups to Keycloak:
- Go to Mappers tab
- Click Add mapper
- Select group-ldap-mapper
| Setting | Value | Notes |
|---|---|---|
| Name | LDAP Groups |
|
| LDAP Groups DN | ou=Groups,dc=example,dc=com |
Base DN for groups |
| Group Name LDAP Attribute | cn |
Group name attribute |
| Group Object Classes | groupOfNames |
Object class for groups |
| Membership LDAP Attribute | member |
How members are listed |
| Membership Attribute Type | DN |
Members identified by DN |
| Membership User LDAP Attribute | uid |
User attribute for matching |
| Mode | READ_ONLY |
|
| User Groups Retrieve Strategy | LOAD_GROUPS_BY_MEMBER_ATTRIBUTE |
|
| Drop Non-Existing Groups During Sync | Off |
After saving, click Sync LDAP Groups To Keycloak. The groups (developers, managers, all-staff) should appear under Groups in the admin console with the correct members.
Mapping Groups to Roles
Map LDAP groups to Keycloak roles for application-level access control:
- Navigate to Groups > select a group (e.g.,
developers) - Go to Role Mappings
- Assign realm roles or client roles
Your application can then check token claims for role membership. This bridges your existing LDAP group structure to Keycloak’s RBAC system.
Sync Modes: Full vs Changed
Keycloak supports two synchronization strategies:
Full Sync
- Queries all users matching the LDAP filter
- Compares with Keycloak’s local cache
- Adds new users, updates changed users, optionally removes deleted users
- Resource intensive for large directories
- Run daily or less frequently
Changed Users Sync
- Queries only users modified since the last sync (using
modifyTimestamp) - Faster and lighter than full sync
- Does not detect deleted users (users removed from LDAP stay in Keycloak)
- Run every 5-15 minutes
Recommended Configuration
Full sync: every 24 hours (86400 seconds)
Changed users sync: every 5 minutes (300 seconds)
For large directories (100,000+ users), consider:
- Running full sync during off-peak hours
- Increasing changed users sync frequency
- Using LDAP filters to limit the scope of synchronized users
Read-Only vs Writable Mode
READ_ONLY Mode
Users authenticate against LDAP, but Keycloak cannot modify LDAP data. Password changes, profile updates, and user creation through Keycloak are blocked for federated users.
Best for:
- Production environments where LDAP is the authoritative source
- Compliance scenarios where changes must go through LDAP management tools
- Reducing the attack surface (Keycloak cannot accidentally modify directory data)
WRITABLE Mode
Keycloak can push changes back to LDAP. Profile updates, password changes, and (optionally) user registration are written to the LDAP directory.
Best for:
- Self-service password reset through Keycloak
- User profile management through Keycloak’s account console
- Environments where Keycloak is the primary user management interface
UNSYNCED Mode
Keycloak imports users from LDAP but stores changes locally. LDAP users get a local copy in Keycloak’s database, and changes are applied to the local copy only.
Best for:
- Testing and development
- Temporary overrides for specific users
- Migration scenarios where you are moving users from LDAP to Keycloak
Custom LDAP Filters
Filter by Group Membership
Only federate users who belong to a specific LDAP group:
(memberOf=cn=keycloak-users,ou=Groups,dc=example,dc=com)
Note: The memberOf overlay must be enabled in OpenLDAP for this filter to work. It is available by default in 389 Directory Server and Active Directory.
Filter by Object Class
Only federate users with a specific object class:
(&(objectClass=inetOrgPerson)(objectClass=posixAccount))
Filter by Attribute
Only federate users in a specific department:
(&(objectClass=inetOrgPerson)(departmentNumber=Engineering))
Exclude Service Accounts
(&(objectClass=inetOrgPerson)(!(uid=svc-*)))
Set the filter in the Custom User LDAP Filter field of the federation configuration.
Hardening and Security
Use LDAPS
Always use LDAPS (port 636) or StartTLS in production to encrypt LDAP traffic:
Connection URL: ldaps://ldap.example.com:636
If your LDAP server uses a self-signed certificate or internal CA:
# Extract the LDAP server certificate
openssl s_client -connect ldap.example.com:636
-showcerts </dev/null 2>/dev/null |
openssl x509 -outform PEM > ldap-ca.pem
# Import into Keycloak's truststore
keytool -importcert -trustcacerts
-file ldap-ca.pem
-alias "ldap-ca"
-keystore /opt/keycloak/conf/truststore.jks
-storepass changeit -noprompt
Connection Pool Settings
For high-traffic environments, tune the LDAP connection pool:
| Setting | Value | Notes |
|---|---|---|
| Connection Pooling | On |
Reuse LDAP connections |
| Connection Pool Max Size | 20 |
Maximum connections |
| Connection Timeout | 30000 |
30 seconds |
| Read Timeout | 30000 |
30 seconds |
Service Account Best Practices
- Use a dedicated service account with the minimum required permissions
- Grant read-only access to the user and group OUs
- Do not use the LDAP admin account for federation
- Set a non-expiring password or automate password rotation
- Monitor the service account for unauthorized use through your directory’s audit logs
Troubleshooting
Users Not Appearing After Sync
# Verify the LDAP query returns users
ldapsearch -x -H ldap://localhost:389
-D "cn=readonly,dc=example,dc=com"
-w readonlypassword
-b "ou=People,dc=example,dc=com"
"(objectClass=inetOrgPerson)" uid cn
# Check Keycloak logs for sync errors
docker logs keycloak 2>&1 | grep -i "ldap|sync|federation"
Common causes:
- Wrong Users DN
- Custom filter excluding users
- UUID attribute mismatch (
entryUUIDfor OpenLDAP vsobjectGUIDfor AD vsnsUniqueIdfor 389 DS)
Authentication Failures
# Test bind directly
ldapwhoami -x -H ldap://localhost:389
-D "uid=jdoe,ou=People,dc=example,dc=com"
-w password123
Check for:
- Password encoding issues (SSHA vs plain text)
- Account lockout policies in LDAP
- Case sensitivity in username matching
Slow Sync Performance
For directories with many users:
- Add LDAP indexes on queried attributes (
uid,mail,modifyTimestamp) - Use narrower
Users DNto limit search scope - Increase
Batch Sizefor sync operations - Schedule full syncs during off-peak hours
For additional troubleshooting, see our guides on connection refused errors and realm not found issues.
Testing with SCIM
If your directory supports SCIM 2.0, you can also use Keycloak’s SCIM provisioning for user lifecycle management. Test your SCIM endpoints with our SCIM Endpoint Tester.
Further Reading
- Keycloak LDAP and Active Directory documentation
- Keycloak Active Directory Integration
- Using Custom User Attributes in Keycloak OIDC Tokens
- Setting Up SSO with Microsoft in Skycloak
- Identity Providers in Keycloak
- Keycloak Docker Compose Generator
LDAP federation gives Keycloak access to your existing user directory, but managing the Keycloak infrastructure itself adds operational overhead. Skycloak handles Keycloak hosting, upgrades, and monitoring so your team can focus on integration, not infrastructure. See pricing to learn more.
Ready to simplify your authentication?
Deploy production-ready Keycloak in minutes. Unlimited users, flat pricing, no SSO tax.