Kanidm on Ubuntu 24.04 LTS on Azure User Guide
Overview
This guide covers the deployment and verification of Kanidm 1.11.2 on Microsoft Azure using cloudimg's pre configured virtual machine image from the Azure Marketplace.
Kanidm is a complete identity management platform: a directory that holds your people, groups and service accounts, and an identity provider that lets applications authenticate against them. It is a single statically linked binary written in Rust, published under the Mozilla Public License 2.0.
Where most self hosted identity products are either a directory or a federation front end, Kanidm is both. One server gives you a web interface for administration and self service, a REST API, a read only LDAP compatibility endpoint for applications that only speak LDAP, an OAuth2 and OpenID Connect provider for applications that speak modern federation, RADIUS secrets for network authentication, and POSIX account attributes for Unix login.
There is no default password on this image, and none is generated for you.
That sentence is the most important one in this guide, so it is worth being precise about what it means. Kanidm ships two break glass accounts, admin and idm_admin, and neither has any credential at all. They cannot authenticate over HTTPS, LDAPS, OAuth2 or RADIUS. There is no vendor default to change, no first boot password written to a file for you to find, and therefore no window between your machine booting and you securing it during which anyone could sign in. The first credential on your server is one you mint yourself, from a root shell on the machine, with a single command.

What's included:
- Kanidm 1.11.2, released 11 September 2026, built from the upstream git tag
v1.11.2at commit547933867e. The build verifies that the tag resolves to that exact commit and that the licence file at that tag is the Mozilla Public License 2.0 before it compiles anything - The
kanidmcommand line client, from the same source tree and the same version as the server - A per machine TLS certificate and certificate authority, generated on your own virtual machine at first boot
- Ubuntu 24.04 LTS, fully patched at build time, with unattended security upgrades enabled
- A hardened
sshdconfiguration: no root login, no password authentication
Kanidm is written in Rust, and the upstream project publishes no prebuilt binary for this release, so this image is compiled from source at build time. The Rust toolchain is not in the image. The compiler, linker, cargo and the whole build tree live on a scratch volume that is destroyed before the image is captured, and the build then asserts twice, once against the filesystem and once against the package database, that no compiler, linker or cargo binary remains.
Exposed ports: 22, 443 and 636, and nothing else.

Port 636 carries the LDAP compatibility endpoint, and it is exposed deliberately, because a directory other machines cannot reach is not much of a directory. It is safe to expose by design rather than by configuration: Kanidm serves LDAP over TLS only and does not implement StartTLS at all, it rejects every write over LDAP, and a bind using a POSIX password is granted only anonymous level read. If you have no LDAP applications, close 636 in your network security group and nothing else changes.
There is no reverse proxy in this image. Kanidm terminates TLS itself on both ports, which is one fewer moving part and removes an entire class of misconfiguration around forwarded client addresses. Kanidm's trusted proxy settings are left at their default of trusting nothing, so no client supplied X-Forwarded-For header is believed by anything on this machine.
Before you deploy: choose your domain name
Kanidm is domain based. Its domain setting is used to build every account's security principal name, it is the basis of the LDAP base DN, and it is the WebAuthn relying party identifier. Upstream warns that changing it later breaks registered credentials, including WebAuthn tokens and issued OAuth2 tokens, and requires a kanidmd domain rename.
It is therefore much easier to set it before the first boot. Pass cloud init user data when you create the machine, and first boot will use your name instead of the default:
#cloud-config
write_files:
- path: /etc/kanidm/cloudimg-domain.conf
permissions: '0644'
content: |
KANIDM_DOMAIN=idm.yourcompany.example
If you skip this, the server comes up as idm.example.com, which is fine for evaluation and for anything reached by IP address, and you can change it later with the documented rename procedure.
Deploying the virtual machine
Create the machine from the Marketplace image. Standard_B2s is the recommended size; Kanidm is a single binary with a small memory footprint.
az vm create \
--resource-group my-identity-rg \
--name kanidm01 \
--image cloudimg:kanidm-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard \
--custom-data cloud-init-kanidm.yaml
az vm open-port --resource-group my-identity-rg --name kanidm01 --port 443 --priority 1001
az vm open-port --resource-group my-identity-rg --name kanidm01 --port 636 --priority 1002
First boot does four things and then starts the server: it writes your domain into the configuration, mints this machine's own certificate authority and TLS certificate, validates the resulting configuration and refuses to continue if it is rejected, and writes an access note to /root/kanidm-credentials.txt.
The Kanidm service is deliberately gated behind that first boot work. It will not start until the first boot script has finished successfully, so there is no window in which the server is listening with a configuration that has not been validated or with certificate material that does not exist.
Step 1: check the server is running
Connect over SSH and confirm the service is healthy. The status endpoint returns true when the server is ready to take requests.
sudo systemctl is-active kanidm-firstboot.service kanidmd.service
curl -sk https://127.0.0.1/status
echo
sudo kanidmd version
The access note records everything specific to this machine. It contains no password, because at this point no credential exists anywhere on the server.
sudo cat /root/kanidm-credentials.txt
Step 2: mint your first administrator credential
This is the command that turns a locked server into one you can administer. It must be run on the machine, as root or as the service account, and it prints a freshly generated random password exactly once.
Use idm_admin for day to day administration of people and groups. Recover admin only when you need to change server level configuration such as the domain name or the LDAP base DN. Keeping them separate is the point of having two accounts.
sudo -u kanidmd kanidmd recover-account idm_admin
INFO Running account recovery ...
INFO | new_password: "<a fresh 48 character password, shown once>"
Record that password in your password manager now. It is not stored in clear text anywhere on the machine, and running the command again simply replaces it with a new one.
For an automated deployment, capture it straight into a sign in so it is never written to a file at all. This is the exact sequence used to verify the image:
PW=$(sudo -u kanidmd kanidmd recover-account idm_admin 2>&1 | sed -n 's/.*new_password: "\(.*\)".*/\1/p')
sudo KANIDM_PASSWORD="$PW" kanidm login -D idm_admin
sudo kanidm self whoami -D idm_admin
The kanidm client on this machine is preconfigured with the server's address and its certificate authority, so no --url or --ca flags are needed here.

Note what the screenshot above shows on its last line: admin was never recovered, and it is still refused. Recovering one break glass account does not unlock the other.
Step 3: sign in to the web interface
Point a browser at https://<your-domain>/. If you have not yet pointed DNS at the machine, add a hosts file entry on your workstation first, because the certificate is issued for the domain name rather than for the IP address.
Sign in as idm_admin with the password you just recorded.

Your browser will warn about the certificate on the first visit. That is expected: the machine issued its own certificate at first boot, and upstream labels it plainly, with a subject of OU = Development and Evaluation - NOT FOR PRODUCTION. It exists so the server can start and so you can reach it immediately, not so you can leave it there. Replacing it is covered below and takes two file copies.
The web interface is where your people manage themselves: their own credentials, passkeys, and the applications they have access to. It also carries an administration panel for browsing persons and groups.
Administration in Kanidm 1.11 is done with the kanidm command line client, not the web interface. The admin panel lists and inspects persons and groups and lets you edit a group's membership, but creating accounts, setting POSIX attributes, issuing RADIUS secrets and registering OAuth2 clients are all command line operations. This is deliberate upstream design rather than a limitation of this image.
Step 4: create a person and a group
Create an account for a real person, put them in a group, and give both POSIX attributes so they are visible to Unix and LDAP clients.
sudo kanidm person create alice "Alice Baker" -D idm_admin
sudo kanidm person posix set alice --shell /bin/bash -D idm_admin
sudo kanidm group create platform-engineers -D idm_admin
sudo kanidm group add-members platform-engineers alice -D idm_admin
sudo kanidm group posix set platform-engineers -D idm_admin
sudo kanidm person get alice -D idm_admin
Both objects appear immediately in the administration panel.

Opening the person shows their security principal name, their UUID and every group they belong to.

The group's own page lists its members, and lets you add and remove them.

To let Alice set her own password and passkeys, generate a credential reset link and send it to her. The link is single use and time limited, so you never need to know or transport her password:
sudo kanidm person credential create-reset-token alice -D idm_admin
Step 5: use the LDAP directory
Applications that only speak LDAP bind to port 636 over TLS. The base DN is derived from your domain name, so idm.example.com becomes dc=idm,dc=example,dc=com; the access note records it for you.
Anonymous search resolves POSIX enabled people and groups, which is what most LDAP clients need in order to look up a user and their group membership:
D=$(sudo grep -m1 '^KANIDM_DOMAIN=' /root/kanidm-credentials.txt | cut -d= -f2-)
B=$(sudo grep -m1 '^KANIDM_LDAP_BASEDN=' /root/kanidm-credentials.txt | cut -d= -f2-)
sudo LDAPTLS_CACERT=/var/lib/kanidmd/ca.pem \
ldapsearch -LLL -o ldif-wrap=no -H "ldaps://$D:636" -b "$B" -x '(name=alice)' \
name spn gidnumber loginshell memberof
Read the base DN with cut -d= -f2- rather than awk -F= '{print $2}'. The value contains equals signs of its own, so taking only the second field silently truncates dc=idm,dc=example,dc=com to dc, and every search then fails with a constraint violation that looks like a server problem.
For an application that needs to read more than anonymous access allows, create a service account and issue it an API token. Binding with the reserved DN dn=token and the token as the password gives that service account's own read permissions:
D=$(sudo grep -m1 '^KANIDM_DOMAIN=' /root/kanidm-credentials.txt | cut -d= -f2-)
sudo kanidm service-account create svc-ldap "Directory reader" idm_admin -D idm_admin
TOK=$(sudo kanidm service-account api-token generate svc-ldap readers -D idm_admin | tail -1)
sudo LDAPTLS_CACERT=/var/lib/kanidmd/ca.pem \
ldapwhoami -H "ldaps://$D:636" -x -D 'dn=token' -w "$TOK"

A person can also bind as themselves using a POSIX password, which is what Unix clients do. Set one interactively; the command prompts twice and never accepts the password on the command line, so it cannot end up in your shell history:
sudo kanidm person posix set-password alice -D idm_admin
Two behaviours to expect. Writes over LDAP are always refused, by design, because LDAP cannot express Kanidm's data model; use the REST API or the kanidm client to make changes. And a failed bind soft locks the account for a short period, so a wrong password makes the next few attempts fail too even if they are correct. That is brute force protection working, not a fault.
If you have no LDAP applications at all, you can turn the POSIX password bind off entirely and leave only token binds:
sudo kanidm system domain set-ldap-allow-unix-password-bind false -D admin
Step 6: connect an application over OpenID Connect
For anything that speaks modern federation, register it as an OAuth2 client rather than pointing it at LDAP. Kanidm is the provider; no extra component is needed.
sudo kanidm system oauth2 create grafana "Grafana" https://grafana.example.com -D idm_admin
sudo kanidm system oauth2 update-scope-map grafana platform-engineers openid profile email -D idm_admin
sudo kanidm system oauth2 show-basic-secret grafana -D idm_admin
sudo kanidm system oauth2 list -D idm_admin
The discovery document your application needs is published at https://<your-domain>/oauth2/openid/grafana/.well-known/openid-configuration. Only members of the groups you grant a scope map to can sign in, so access control lives in the directory rather than in each application.
Step 7: RADIUS and Unix login
Kanidm holds the per account RADIUS secret and serves it to a FreeRADIUS instance running upstream's rlm_kanidm module. The RADIUS daemon itself is a separate deployment and is not installed on this image; this machine is the authority that issues and stores the secrets.
sudo kanidm person radius generate-secret alice -D idm_admin
sudo kanidm group create radius_access_allowed -D idm_admin
sudo kanidm group add-members radius_access_allowed alice -D idm_admin
For Unix login, the POSIX attributes you set in step 4 are consumed by kanidm-unixd, libnss-kanidm and libpam-kanidm installed on the client machines that you want people to log in to. Those components are deliberately not installed on this appliance: putting a third party NSS and PAM module into the authentication path of the machine that holds your only recovery command is a good way to lock yourself out of it.
Replacing the TLS certificate
The certificate generated at first boot is explicitly labelled by upstream as not for production. Replace it with one from your own certificate authority, or from a public one, as soon as the machine has a real DNS name.
Copy your full chain and private key over the two files Kanidm reads, keep the ownership and modes, and reload.
The ownership matters more than the mode. Kanidm checks its own TLS files at startup and warns that a key "may not be secure" whenever the service account owns it, at any mode, on the reasoning that an owner can always give itself write access again. So the key belongs to root and the service reads it through the kanidmd group: the server can use the key but cannot replace it. Kanidm re reads both files on SIGHUP and reloads them if they are valid, so this does not interrupt service:
sudo install -o root -g kanidmd -m 0644 fullchain.pem /var/lib/kanidmd/chain.pem
sudo install -o root -g kanidmd -m 0640 privkey.pem /var/lib/kanidmd/key.pem
sudo systemctl reload kanidmd
The same material serves both HTTPS and LDAPS, so your LDAP clients stop needing the private certificate authority at the same moment your browsers do.
Backups
Kanidm runs an online backup every night at 22:00 UTC and keeps seven copies, written to /var/lib/kanidmd/backups/. Copy that directory off the machine on your own schedule.
sudo ls -la /var/lib/kanidmd/backups/ 2>/dev/null || echo "no scheduled backup has run yet on this machine"
sudo grep -A3 '^\[online_backup\]' /etc/kanidm/server.toml
To take one immediately, or to restore, stop the server first; these are offline operations on the database file:
sudo systemctl stop kanidmd
sudo -u kanidmd kanidmd database backup /var/lib/kanidmd/backups/manual.json
sudo -u kanidmd kanidmd database restore /var/lib/kanidmd/backups/manual.json
sudo systemctl start kanidmd
Changing the domain name after first boot
If you have to change the domain after the fact, edit both settings, then run the rename. Expect to re register WebAuthn credentials and re issue OAuth2 tokens afterwards, and expect your LDAP base DN to change with it.
sudo sed -i 's/^domain = .*/domain = "idm.yourcompany.example"/' /etc/kanidm/server.toml
sudo sed -i 's|^origin = .*|origin = "https://idm.yourcompany.example"|' /etc/kanidm/server.toml
sudo systemctl stop kanidmd
sudo -u kanidmd kanidmd domain rename
sudo systemctl start kanidmd
Troubleshooting
The server will not start. The configuration is validated before the service is allowed to run, so start with the validator and the first boot log:
sudo -u kanidmd kanidmd configtest 2>&1 | tail -5
sudo systemctl status kanidm-firstboot.service --no-pager | head -12
Sign in is refused for admin or idm_admin. That is the expected state until you recover the account. An account that has never been recovered reports "denied":"invalid credential state" rather than rejecting a password, because there is no password to reject.
A browser cannot reach the server by IP address. The certificate and the configured origin are both bound to the domain name. Use the name, or change the domain to one that resolves to the machine.
An LDAP client reports "Can't contact LDAP server". Almost always TLS, and the message is the same whether the certificate is untrusted, the port is wrong or the scheme is ldap:// instead of ldaps://. Point the client at your certificate authority with LDAPTLS_CACERT=/var/lib/kanidmd/ca.pem and add -d 1 to see which it is.
sudo journalctl -u kanidmd --no-pager -n 20
Support
This image is maintained by cloudimg. Security updates are applied unattended, and the image is rebuilt and republished as new Kanidm releases appear.
For help with this image, contact cloudimg support. For questions about Kanidm itself, see the upstream documentation at kanidm.github.io/kanidm/stable/.
The Mozilla Public License 2.0 that covers Kanidm ships with the image at /usr/share/doc/kanidm/LICENSE.md.
head -1 /usr/share/doc/kanidm/LICENSE.md