Keycloak LDAP Integration: Step-by-Step Tutorial

Guilliano Molaire Guilliano Molaire Updated May 27, 2026 9 min read

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

  1. Navigate to User Federation
  2. 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
email mail email

Adding Custom Mappers

Map additional LDAP attributes to Keycloak user attributes:

  1. Go to Mappers tab
  2. Click Add mapper
  3. 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:

  1. Navigate to Client Scopes > create a new scope (e.g., ldap-attributes)
  2. 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:

  1. Go to Mappers tab
  2. Click Add mapper
  3. 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:

  1. Navigate to Groups > select a group (e.g., developers)
  2. Go to Role Mappings
  3. 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

  1. Use a dedicated service account with the minimum required permissions
  2. Grant read-only access to the user and group OUs
  3. Do not use the LDAP admin account for federation
  4. Set a non-expiring password or automate password rotation
  5. 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 (entryUUID for OpenLDAP vs objectGUID for AD vs nsUniqueId for 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 DN to limit search scope
  • Increase Batch Size for 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


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.

Guilliano Molaire
Written by Guilliano Molaire Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Ready to simplify your authentication?

Deploy production-ready Keycloak in minutes. Unlimited users, flat pricing, no SSO tax.

© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman