Switching from Authentik to Kanidm

March 29, 2026

Kanidm dashboard

Like many homelabbers, I wanted to solve the problem of having so many accounts for so many services that I selfhost. I went down the Single Sign-On rabbit hole, looking at solutions such as Keycloak and Authelia, and finally settling on Authentik. Authentik’s docs were great and it was a popular choice with lots of support. I painstakingly configured login flows with 2FA and password recovery, customized the branding, and sent it off to production. There it remained, chugging along, for a few years. Eventually, high resource usage (2GB of memory!) and getting burned on breaking changes left me wanting for a simpler identity manager.

Enter: Kanidm (kar-nee-dee-em)

I had my eye on Kanidm for a year before making the switch. After all, ripping out a perfectly good OAuth provider for another is a daunting task. But after reading through their superb docs (including a lengthy page on domain names) and opinionated design, I was convinced Kanidm was for me. I wanted authentication that protects my services from the start, without having to finagle flows. I remember when I first started configuring Authentik, I accidentally created a flow that allowed a user to change the password of any other user. What a nightmare!

With Kanidm, all I have to worry about is creating users, groups, and OAuth resources, which is all done via the Kanidm CLI client. No more poison needle flow management. It’s secure by default with up-to-date standards (PKCE, ES256) while providing further per-resource configuration for legacy clients. Although I only needed an OAuth provider, Kanidm supports a whole lot more out of the box like passkeys, LDAP, RADIUS, and SSH key distribution, to name a few.

There have been some thorns along the way. While Authentik natively supports securing static pages using a forward auth middleware, Kanidm does not. This issue was discussed extensively and I settled on traefik-oidc-auth which I discuss further below. Another missing feature is forgotten password self service. There is user self service if you’re already authenticated, but resetting any lost credentials while unauthenticated requires administrator intervention. Though, this feature may be released in the future.

Kanidm’s Traefik documentation is superb and I would highly suggest following that if you want to deploy it in your homelab!

Migration

Deploying Authentik so long ago meant that it had built up a bit of crust over time. There were quite a few services using Authentik that had fallen by the wayside, so before I even started migrating I had to figure out what I was migrating. The final migration list was 18 services, with 10 services tossed.

Some services, like Immich, were very easy to migrate. Simply unlink OAuth and relink with the new provider. Others involved delving into the SQLite database to replace any OAuth provider identifiers. If the service had no user data, like Komodo, I purged the existing user and recreated one with Kanidm.

For permission management, I have one group per service (e.g. immich_group) and two main groups (users and admins). Depending on the nature of the service, I will add either users or admins to the immich_group. admins inherit groups from users so it’s not necessary to add both. For finer permissions, you could also add a user to only the service group. I’m no expert on this, so there may be a better way to go about things.

Tip

To easily onboard new services and users to Kanidm, I wrote a cheat sheet in my personal docs.

Service management

kanidm system oauth2 create immich "Immich" https://immich.domain.tld
kanidm group create immich_group
kanidm system oauth2 add-redirect-url immich  https://immich.domain.tld/some/callback # Callbacks may differ
kanidm system oauth2 update-scope-map immich immich_group openid email profile
kanidm group add-members immich_group users|admins # Pick one group
kanidm system oauth2 show-basic-secret immich
# Use short usernames instead of wordy usernames (e.g. user vs user@auth.domain.tld)
kanidm system oauth2 prefer-short-username immich
# If PKCE is not supported
kanidm system oauth2 warning-insecure-client-disable-pkce immich
# Set dashboard image
curl -O https://cdn.jsdelivr.net/gh/selfhst/icons/svg/immich.svg && kanidm system oauth2 set-image immich immich.svg svg && rm immich.svg
# Review the client definition
kanidm system oauth2 get immich
# Service-side configuration
ISSUER_URL=https://auth.domain.tld/oauth2/openid/immich
CONFIGURATION_URL=https://auth.domain.tld/oauth2/openid/immich/.well-known/openid-configuration
CLIENT_ID=immich

Person management

kanidm person create padme "Padmé"
kanidm person update padme --mail "padme@mail.com"
kanidm group add-members users|immich_group padme # Pick one group
kanidm person credential create-reset-token padme

traefik-oidc-auth

At first, I was overwhelmed with how exactly I would replace Authentik’s outpost, and I feared it wouldn’t work with services like Navidrome and its forward auth. However, all worrying was for naught as the setup was relatively straight forward.

First, load the traefik-oidc-auth plugin into Traefik. Then, create a middleware for each service that needs authentication. In the example, I have one for Navidrome and one for administrative pages. Although it’s not recommended, I pool all admin pages to one middleware because the configuration is a bit unwieldy.

Warning

You should not share a single OAuth definition with multiple applications. This is so disclosure of one token doesn’t impact other services.

Other configuration of note include setting UnauthorizedBehavior: "Challenge" because Gatus, my endpoint monitor, would otherwise report 401 Unauthorized on Navidrome. For Subsonic clients (/rest) and public shares (/share) to work, add their paths to BypassAuthenticationRule. Also, consider adding a custom CookieNamePrefix to obscure your infrastructure. Finally, set Navidrome’s environment variable, ND_EXTAUTH_TRUSTEDSOURCES, to Traefik’s IP or subnet.

static config

experimental:
  plugins:
    traefik-oidc-auth:
      moduleName: github.com/sevensolutions/traefik-oidc-auth
      version: "v0.18.0"

dynamic config

    navidrome-auth:
      plugin:
        traefik-oidc-auth:
          Provider:
            Url: "https://auth.domain.tld/oauth2/openid/navidrome-auth"
            ClientId: "navidrome-auth"
            ClientSecret: '{{ env "TRAEFIK_OIDC_NAVIDROME_AUTH_CLIENT_SECRET" }}'
            UsePkce: true
          Scopes: ["openid", "profile", "email"]
          Headers:
            - Name: "Remote-User"
              Value: "{{`{{ .claims.preferred_username }}`}}"
          BypassAuthenticationRule: "PathPrefix(`/rest`)" # Bypass Subsonic clients
          UnauthorizedBehavior: "Challenge" # Returns 200 OK, default returns 401 Unauthorized
          CookieNamePrefix: "OidcAuth" # Default is TraefikOidcAuth

    admin-auth:
      plugin:
        traefik-oidc-auth:
          Provider:
            Url: "https://auth.domain.tld/oauth2/openid/admin-auth"
            ClientId: "admin-auth"
            ClientSecret: '{{ env "TRAEFIK_OIDC_ADMIN_AUTH_CLIENT_SECRET" }}'
            UsePkce: true
          Scopes: ["openid", "profile", "email"]
          CookieNamePrefix: "OidcAuth" # Default is TraefikOidcAuth

Branding

Authentik supports various branding settings such as title, logo, favicon, and flow background image. These are easily customized in Kanidm except for favicon and background image. After some trial and error, I found that custom assets can be mounted like so, and referenced in override.css with the path /pkg/assets/background.jpg.

Kanidm container

    volumes:
      - path/to/override.css:/hpkg/override.css
      - path/to/assets:/hpkg/assets

Afterword

There are so many authentication solutions out there that it’s tough to pick the right one for your needs. In fact, there are over 20 listed on selfh.st! For me, a good authentication provider is simple to configure and difficult to make vulnerable. I’ve been really happy with Kanidm and hope this post has helped you consider it for your own infrastructure.

homelabdockerauthentikkanidmtraefik