Keycloak with OpenLDAP Directory on Ubuntu 24.04 on Azure User Guide
Overview
This image pairs the two halves of the canonical enterprise identity stack on one VM. OpenLDAP (slapd) is the authoritative user and group directory, and Keycloak is the modern identity provider in front of it, adding single sign on, OpenID Connect, OAuth 2.0 and SAML to accounts that live in LDAP. The pairing is not just co installed: on first boot the image wires a Keycloak LDAP user federation provider against the local directory in a dedicated realm named cloudimg, runs a full user sync, and proves that a directory stored user can obtain a token through Keycloak before the machine is handed to you.
What is included:
- Keycloak 26.7.1 (Quarkus distribution) from the official GitHub release tarball at
/opt/keycloak, OpenJDK 21 - OpenLDAP 2.6 (slapd) from the Ubuntu 24.04 repository, mdb backend, base DN
dc=example,dc=com - Keycloak realm
cloudimgwith an LDAP user federation provider (WRITABLE edit mode, registrations sync back to LDAP) pointing atldap://127.0.0.1:389, users fromou=people,dc=example,dc=com - A seeded directory:
ou=people,ou=groupsand a demo user (uid=demo) that is synced into Keycloak and can sign in through it - LDAP on TCP 389 (with StartTLS), LDAPS on TCP 636 (per VM self signed certificate), Keycloak HTTP on TCP 8080
- The directory database on a dedicated 20 GB data disk at
/var/lib/ldap, independently resizable - Unique per VM passwords for the Keycloak admin (
cloudimg), the directory admin (cn=admin,dc=example,dc=com) and the demo user, generated at first boot, no default logins - Verbatim licence texts in the image under
/usr/share/cloudimg/licenses/(Keycloak Apache 2.0, OpenLDAP Public License) - 24/7 cloudimg support
Prerequisites
Before deploying, ensure you have:
- An active Azure subscription
- An SSH key pair for the VM login user
- A virtual network and subnet (or let the portal create one)
Recommended VM size: Standard_B2s (2 vCPU, 4 GB RAM) suits evaluation and small deployments. For production SSO with larger user populations, move Keycloak to a PostgreSQL backend and a D series size.
Network Ports
| Protocol | Port | Description |
|---|---|---|
| TCP | 22 | SSH access |
| TCP | 8080 | Keycloak HTTP (admin console, OIDC/SAML endpoints) |
| TCP | 389 | LDAP (bind, search, StartTLS) |
| TCP | 636 | LDAPS (LDAP over TLS) |
An LDAP endpoint reachable from the internet is a serious exposure: restrict 389 and 636 to your own VNet CIDR, and keep 8080 behind a TLS reverse proxy or restrict it to your management network for production.
Step 1: Deploy the Virtual Machine
Option A - Azure Portal: search Azure Marketplace for "Keycloak with OpenLDAP Directory cloudimg", select the offer, choose Standard_B2s, your SSH key and network, and create the VM. Open inbound ports 22 and 8080 (and 389/636 only to your VNet).
Option B - Azure CLI:
az vm create \
--resource-group my-rg \
--name keycloak-ldap-vm \
--image cloudimg:keycloak-openldap:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_rsa.pub
Step 2: Connect via SSH
ssh azureuser@<vm-ip>
First boot takes a couple of minutes: the image rotates every credential, regenerates the LDAP TLS certificate, bootstraps the Keycloak admin, wires the LDAP federation and verifies a federated login before writing the credentials file.
Step 3: Read the Per VM Credentials
sudo cat /root/keycloak-openldap-credentials.txt
The file (mode 0600, root only) holds the three per VM secrets generated on first boot: KEYCLOAK_ADMIN_PASSWORD (console user cloudimg), OPENLDAP_ADMIN_PASSWORD (bind DN cn=admin,dc=example,dc=com) and DEMO_USER_PASSWORD (the LDAP demo user that signs in through Keycloak).
Step 4: Service Status and Versions
sudo systemctl status slapd --no-pager | head -5
sudo systemctl status keycloak --no-pager | head -5
sudo /opt/keycloak/bin/kc.sh --version 2>&1 | head -1
dpkg-query -W -f='slapd ${Version}\n' slapd
Expected output:
● slapd.service - LSB: OpenLDAP standalone server (Lightweight Directory Access Protocol)
Active: active (running)
● keycloak.service - Keycloak 26 Identity & Access Management (cloudimg, OpenLDAP-federated)
Active: active (running)
Keycloak 26.7.1
slapd 2.6.10+dfsg-0ubuntu0.24.04.1

Step 5: Query the Directory
Bind as the directory administrator and list the seeded entries:
LDAP_PW=$(sudo grep -m1 '^OPENLDAP_ADMIN_PASSWORD=' /root/keycloak-openldap-credentials.txt | cut -d= -f2-)
ldapwhoami -x -H ldap://127.0.0.1 -D 'cn=admin,dc=example,dc=com' -w "$LDAP_PW"
ldapsearch -x -LLL -H ldap://127.0.0.1 -D 'cn=admin,dc=example,dc=com' -w "$LDAP_PW" \
-b 'dc=example,dc=com' '(objectclass=*)' dn
Expected output:
dn:cn=admin,dc=example,dc=com
dn: dc=example,dc=com
dn: ou=people,dc=example,dc=com
dn: ou=groups,dc=example,dc=com
dn: uid=demo,ou=people,dc=example,dc=com

Step 6: Verify LDAPS
The image serves LDAP over TLS on port 636 with a certificate regenerated for this VM at first boot:
LDAP_PW=$(sudo grep -m1 '^OPENLDAP_ADMIN_PASSWORD=' /root/keycloak-openldap-credentials.txt | cut -d= -f2-)
LDAPTLS_REQCERT=never ldapwhoami -x -H ldaps://127.0.0.1:636 -D 'cn=admin,dc=example,dc=com' -w "$LDAP_PW"
Expected output:
dn:cn=admin,dc=example,dc=com
For production, replace the self signed pair at /etc/ldap/certs/ldap.crt and /etc/ldap/certs/ldap.key with a CA signed certificate and restart slapd.
Step 7: The Federation - LDAP Users Sign In Through Keycloak
Realm cloudimg federates its users from the local directory. Confirm the realm is live and prove the end to end path by obtaining an OpenID Connect token for the LDAP stored demo user:
curl -sf http://127.0.0.1:8080/realms/cloudimg/.well-known/openid-configuration | head -c 200; echo
DEMO_PW=$(sudo grep -m1 '^DEMO_USER_PASSWORD=' /root/keycloak-openldap-credentials.txt | cut -d= -f2-)
curl -sf -X POST http://127.0.0.1:8080/realms/cloudimg/protocol/openid-connect/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d "grant_type=password&client_id=admin-cli&username=demo&password=${DEMO_PW}" \
| grep -o '"token_type":"Bearer"'
Expected output:
{"issuer":"http://127.0.0.1:8080/realms/cloudimg","authorization_endpoint":"http://127.0.0.1:8080/realms/cloudimg/protocol/openid-connect/auth", ...
"token_type":"Bearer"
The password you used is stored only in OpenLDAP - Keycloak validated it by binding to the directory. That is the bundle working as designed: the directory stays the single source of truth and Keycloak issues the tokens.

Step 8: Admin Console Login
Browse to http://<vm-ip>:8080/ - Keycloak 26 takes you straight to the admin console sign in form (there is no separate welcome page). Sign in as cloudimg with KEYCLOAK_ADMIN_PASSWORD from Step 3.

After signing in you land on the master realm console. Keycloak flags the first boot admin as a temporary account - creating your own permanent administrator and deleting cloudimg is a recommended hardening step.

Step 9: The Pre Wired LDAP Federation Provider
Open Manage realms and switch to the cloudimg realm, then open User federation. The openldap provider is already configured and enabled against the local directory: connection ldap://127.0.0.1:389, users DN ou=people,dc=example,dc=com, bind DN cn=admin,dc=example,dc=com, WRITABLE edit mode with registration sync, so users created in Keycloak are written back to LDAP.

Step 10: Directory Users Inside Keycloak
Open Users in realm cloudimg. Because the realm has a federated provider the list starts empty - search for * (or a username) as the console suggests. The demo user is returned with its name and email attributes mapped from LDAP: it lives in OpenLDAP and was imported by the first boot full sync. Any user you add under ou=people appears here after a sync, and any user you create here is written into the directory.

Server Components
| Component | Version | Path |
|---|---|---|
| Keycloak | 26.7.1 | /opt/keycloak/ |
| OpenJDK | 21 | /usr/lib/jvm/ |
| OpenLDAP slapd | 2.6.10 | /usr/sbin/slapd |
| ldap-utils | 2.6.10 | /usr/bin/ldapsearch etc. |
| Keycloak systemd unit | - | /etc/systemd/system/keycloak.service |
| Keycloak runtime env | - | /etc/keycloak/keycloak.env |
| Keycloak H2 database | - | /opt/keycloak/data/h2/ |
| LDAP database (mdb) | - | /var/lib/ldap/ (dedicated data disk) |
| LDAP TLS certificate | - | /etc/ldap/certs/ldap.crt + ldap.key |
| First boot script | - | /usr/local/sbin/keycloak-openldap-firstboot.sh |
| Credentials | - | /root/keycloak-openldap-credentials.txt (0600 root:root) |
| Licences | - | /usr/share/cloudimg/licenses/ |
Filesystem Layout
| Mount Point | Size | Description |
|---|---|---|
| / | 30 GB | Root filesystem (Keycloak, OS, Keycloak H2 database) |
| /boot/efi | 100 MB | UEFI boot partition (Gen2 Hyper V) |
| /var/lib/ldap | 20 GB | Dedicated data disk for the OpenLDAP mdb database |
| /mnt | varies | Azure temporary resource disk |
The directory database is separate from the OS disk: you can resize it independently and it is preserved on every VM created from this image.
Managing the Services
sudo systemctl restart slapd
sudo systemctl restart keycloak
sudo journalctl -u keycloak --no-pager | tail -20
sudo journalctl -u slapd --no-pager | tail -20
Keycloak takes 30 to 60 seconds to serve requests after a restart.
Production Hardening
- Restrict the NSG: 389/636 to your VNet CIDR only; 8080 behind a TLS reverse proxy (or enable Keycloak HTTPS) and restrict to trusted networks
- Keycloak database: move from H2 to PostgreSQL (
KC_DB=postgres,KC_DB_URL=...in/etc/keycloak/keycloak.env, switch the unit fromstart-devtostart --hostname=<your-fqdn>) for production populations - LDAP TLS: install a CA signed certificate at
/etc/ldap/certs/and restart slapd; then set the federation provider's connection toldaps://127.0.0.1:636in the admin console for encrypted local binds - Directory ACLs: the default policy refuses anonymous writes and protects
userPassword; reviewolcAccessbefore granting applications broader access - Create service accounts: bind applications with dedicated read only DNs under
ou=people, never withcn=admin - Patching: unattended upgrades is enabled; still plan monthly
apt-get update && apt-get upgrade && reboot
Troubleshooting
Keycloak is not reachable on 8080
sudo systemctl status keycloak --no-pager | head -5
sudo journalctl -u keycloak --no-pager | tail -30
If the unit reports a failed condition on first boot, the first boot service has not finished yet - check sudo journalctl -u keycloak-openldap-firstboot --no-pager | tail -30.
A directory user cannot sign in through Keycloak
sudo journalctl -u keycloak --no-pager | grep -i ldap | tail -10
Verify the user exists in LDAP (Step 5), then open User federation in realm cloudimg and use Sync all users. Keycloak validates passwords by binding to LDAP, so the user's userPassword must be set in the directory.
slapd will not start
sudo journalctl -u slapd --no-pager | tail -20
df -h /var/lib/ldap
Confirm the data disk is mounted at /var/lib/ldap (it is provisioned automatically from the image).
Licensing
Keycloak is Apache License 2.0; OpenLDAP is distributed under the OpenLDAP Public License (BSD style, OSI approved). Both verbatim licence texts ship in the image under /usr/share/cloudimg/licenses/. cloudimg provides commercial support separately.
Support
- Email: support@cloudimg.co.uk
- Response Time: 24/7 with guaranteed response SLA
- Website: https://www.cloudimg.co.uk