BlogSecuritySelf-Host Authentik or Keycloak: Open-Source Auth0 Alternatives for SSO (2026)

Self-Host Authentik or Keycloak: Open-Source Auth0 Alternatives for SSO (2026)

Adrian Silaghi
Adrian Silaghi
April 20, 2026
14 min read
7 views
#authentik #keycloak #sso #auth0-alternative #self-hosted #identity #oidc #saml
Self-Host Authentik or Keycloak: Open-Source Auth0 Alternatives for SSO (2026)

Identity and access management (IAM) is the plumbing of modern software. Every product with a login button, every internal tool, every SaaS dashboard, every customer portal — they all need a way to authenticate users, authorize actions, federate identities, and often enforce SSO policies for enterprise customers.

For years, the default answer has been Auth0, Okta, or more recently Clerk. Integrate a few SDKs, flip some toggles, and you have production-grade login flows in an afternoon. The problem shows up on the invoice: these vendors charge per monthly active user (MAU), and the pricing curve gets ugly fast. A free tier of 7,500 MAU sounds generous — until your product grows and the monthly bill jumps from $0 to $2,000 to $20,000 in the span of a few quarters.

There are two mature, open-source alternatives that solve exactly this problem: Authentik and Keycloak. Both are battle-tested, both support every protocol enterprise buyers care about (SAML, OIDC, LDAP, SCIM), both run comfortably on a single VPS, and both cost roughly the same whether you have 100 users or 100,000.

This guide walks you through choosing between them, deploying either on a GDPR-compliant European VPS, wiring up your apps via reverse proxy forward auth, and keeping the whole thing backed up and maintained. By the end, you will have a production-ready identity provider that costs less than €40/month all-in.

Why self-host identity at all?

There are four reasons teams move off Auth0, Okta, or Clerk — usually in combination:

  1. Cost predictability. Per-MAU pricing means your identity bill grows linearly (or worse) with your user base. A free open-source IdP running on a €37/month stack serves 100 users or 100,000 for the same price.
  2. Data residency and GDPR. With a self-hosted IdP on a German VPS, you know exactly where your users' password hashes, email addresses, and audit logs live. No data processing agreements with US vendors, no Schrems II worries.
  3. Protocol completeness. Authentik and Keycloak both speak SAML, OIDC, OAuth2, LDAP, and SCIM out of the box. Auth0 charges extra for SAML on most plans. Clerk does not support SAML at all on its Pro tier.
  4. Vendor lock-in risk. Your user store is the most critical asset in your stack. If your IdP vendor changes pricing, gets acquired, deprecates a feature, or suffers a breach, migrating hundreds of thousands of users with their passwords, MFA enrollments, and session state is a nightmare. Owning it means you can never be held hostage.

The trade-off is operational ownership: you have to run it, upgrade it, back it up, and monitor it. On a managed VPS with automated snapshots, that overhead is much smaller than people assume — about the same as running a WordPress site.

The cost math: Auth0 vs Clerk vs self-hosted

Pricing changes over time, but the shape of the curve does not. Here is a snapshot of 2026 pricing for the three commercial options and the self-hosted alternative, scaled by MAU count. Numbers assume the production-grade tier where SAML SSO is included (Auth0 "B2B Essentials", Clerk Pro add-on).

Monthly Active Users Auth0 B2B Clerk Pro Okta Customer Identity Self-Hosted (DanubeData)
1,000 MAU $175/mo $25/mo + $20 (SAML) = $45 ~$170/mo €37.47/mo (~$40)
10,000 MAU $800/mo $25 + $200 + SAML = ~$245 ~$1,500/mo €37.47/mo (~$40)
100,000 MAU ~$4,800/mo (custom) ~$2,000+/mo Enterprise quote only €37-€80/mo (upsized)
500,000 MAU $20,000+/mo $10,000+/mo $30,000+/mo €80-€150/mo

At 10,000 MAU the self-hosted stack is already paying back your engineering time in weeks. At 100,000 MAU the gap is >100x. Self-hosting is not the right answer for every team, but once you cross about 5,000 real active users, the economics flip hard.

Authentik vs Keycloak: which one should you pick?

Both projects are mature, widely deployed, open-source, and check every protocol box. The honest answer is that for most greenfield deployments in 2026, Authentik is the easier and faster choice, but Keycloak is still the safer bet if you need Red Hat support or integrate with the broader Red Hat ecosystem.

Dimension Authentik Keycloak
Language Python (Django + Go proxy) Java (Quarkus since v17)
Maintainer Authentik Security Inc. (commercial backing, open-core) Red Hat / CNCF (since 2023)
Admin UI Modern, React-based, fast Functional but dated
Memory footprint ~1.5-2 GB (all services) ~2-4 GB (JVM overhead)
Cold-start time ~15 seconds 30-60 seconds (JVM)
Required dependencies PostgreSQL + Redis PostgreSQL (Infinispan embedded)
OIDC / OAuth2 Yes Yes
SAML 2.0 Yes (IdP and SP) Yes (IdP and SP)
LDAP (as provider/source) Both directions Both directions
SCIM provisioning Yes (outbound) Via community extensions
Forward auth (reverse proxy protection) Built-in (outpost concept) Via external oauth2-proxy
MFA / WebAuthn / TOTP Yes Yes
Flow customization Visual flow builder Authentication flows (less visual)
Enterprise support Authentik Enterprise (paid) Red Hat build of Keycloak (paid)
Community size Growing fast, ~15k GitHub stars Very large, ~22k stars, older ecosystem

Rule of thumb:

  • Greenfield project, small team, want a clean admin UI and the easiest path to protecting internal apps behind SSO? Pick Authentik.
  • Already on Red Hat / OpenShift, or you need paid vendor support with a Red Hat SLA? Pick Keycloak.
  • Migrating from Auth0 and want the least-surprising behavior with standard flows? Either works, but Authentik's UI is friendlier for non-Java teams.

The rest of this guide covers both. We start with Authentik since it is the typical pick in 2026, then show the Keycloak equivalent.

Protocols you need to understand

Before deploying, a quick refresher on what each protocol is good for — this shapes how you configure your IdP.

Protocol Best for Notes
OIDC / OAuth2 Modern web and mobile apps, APIs JSON/JWT based, easy to integrate, default for new apps
SAML 2.0 Enterprise SaaS, legacy apps, B2B SSO XML-based, ugly to debug, but required by enterprise buyers
LDAP Legacy apps, VPNs, network appliances Authentik/Keycloak can act as an LDAP server for old clients
SCIM User lifecycle automation (provision / deprovision) Critical for enterprise: push user changes to downstream SaaS
Forward auth Protecting apps that have no auth (dashboards, Grafana, etc.) Nginx/Traefik asks IdP "is this request authenticated?" before proxying

Sizing your deployment

Both IdPs are surprisingly lean when run correctly. The heavy work (password hashing, session lookups, JWT signing) is CPU-light at small-to-medium scale. The bottleneck tends to be Postgres under high concurrent login load, not the IdP itself.

Deployment size Concurrent sessions Recommended stack Est. monthly cost
Personal / homelab < 50 DanubeData VPS Standard (2 vCPU / 4 GB) — all-in-one Docker Compose €8.99
Small team / startup (production) 50-2,000 DanubeData DD Small (4 vCPU / 8 GB) + managed Postgres + managed Redis €37.47
Medium SaaS 2,000-10,000 DD Medium (8 vCPU / 16 GB) + managed Postgres Standard + Redis €65-€80
Large / HA 10,000+ 2x DD Medium behind a load balancer + Postgres with replica + Redis €150-€250

The "small team" row is the sweet spot for most production deployments: DanubeData's DD Small (€12.49/mo) gives you 4 vCPU and 8 GB RAM — plenty to run Authentik server + worker with room to spare. Managed Postgres (€19.99/mo) and managed Redis (€4.99/mo) handle the state layer so you do not have to operate those databases yourself. Total: €37.47/month.

Deploying Authentik with Docker Compose

Authentik ships an official Docker Compose file that works out of the box. We will tweak it slightly to use the DanubeData managed Postgres and Redis instances — which you probably want for production — rather than co-located containers.

Step 1: Provision the infrastructure

  1. Create a VPS — pick DD Small (€12.49/mo), Ubuntu 24.04 LTS.
  2. Create a managed PostgreSQL instance — smallest plan, €19.99/mo. Note the hostname, port, username, password.
  3. Create a managed Redis instance — €4.99/mo. Note the endpoint.
  4. Point a DNS A record (e.g. auth.yourdomain.com) at your VPS IP.

Step 2: Initial server setup

# SSH in
ssh root@YOUR_SERVER_IP

# Update and install Docker
apt update && apt upgrade -y
curl -fsSL https://get.docker.com | sh
apt install -y docker-compose-plugin ufw

# Firewall
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

# Create the deployment directory
mkdir -p /opt/authentik
cd /opt/authentik

Step 3: Generate secrets

# Authentik requires two secrets: SECRET_KEY and PG_PASS
openssl rand -base64 60 | tr -d '
'  # use as AUTHENTIK_SECRET_KEY
openssl rand -base64 36 | tr -d '
'  # save somewhere safe (if using bundled Postgres)

Step 4: Create the .env file

cat > .env << 'EOF'
# Core secret (change this!)
AUTHENTIK_SECRET_KEY=REPLACE_WITH_GENERATED_SECRET

# Point Authentik at the DanubeData managed Postgres
AUTHENTIK_POSTGRESQL__HOST=your-pg-host.databases.danubedata.ro
AUTHENTIK_POSTGRESQL__PORT=5432
AUTHENTIK_POSTGRESQL__USER=authentik
AUTHENTIK_POSTGRESQL__NAME=authentik
AUTHENTIK_POSTGRESQL__PASSWORD=YOUR_MANAGED_PG_PASSWORD
AUTHENTIK_POSTGRESQL__USE_SSL=true

# Point Authentik at the DanubeData managed Redis
AUTHENTIK_REDIS__HOST=your-redis-host.caches.danubedata.ro
AUTHENTIK_REDIS__PORT=6379
AUTHENTIK_REDIS__PASSWORD=YOUR_MANAGED_REDIS_PASSWORD
AUTHENTIK_REDIS__TLS=true

# Email (optional but recommended for password resets)
AUTHENTIK_EMAIL__HOST=smtp.yourdomain.com
AUTHENTIK_EMAIL__PORT=587
AUTHENTIK_EMAIL__USERNAME=noreply@yourdomain.com
AUTHENTIK_EMAIL__PASSWORD=YOUR_SMTP_PASSWORD
AUTHENTIK_EMAIL__USE_TLS=true
AUTHENTIK_EMAIL__FROM=auth@yourdomain.com

# Error reporting opt-out (privacy)
AUTHENTIK_ERROR_REPORTING__ENABLED=false

# Image version (pin this!)
AUTHENTIK_IMAGE=ghcr.io/goauthentik/server
AUTHENTIK_TAG=2026.4
EOF

chmod 600 .env

Step 5: Docker Compose file

cat > docker-compose.yml << 'EOF'
services:
  server:
    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.4}
    restart: unless-stopped
    command: server
    environment:
      AUTHENTIK_REDIS__HOST: ${AUTHENTIK_REDIS__HOST}
      AUTHENTIK_REDIS__PORT: ${AUTHENTIK_REDIS__PORT}
      AUTHENTIK_REDIS__PASSWORD: ${AUTHENTIK_REDIS__PASSWORD}
      AUTHENTIK_POSTGRESQL__HOST: ${AUTHENTIK_POSTGRESQL__HOST}
      AUTHENTIK_POSTGRESQL__USER: ${AUTHENTIK_POSTGRESQL__USER}
      AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRESQL__NAME}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_POSTGRESQL__PASSWORD}
    env_file:
      - .env
    volumes:
      - ./media:/media
      - ./custom-templates:/templates
    ports:
      - "9000:9000"
      - "9443:9443"

  worker:
    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.4}
    restart: unless-stopped
    command: worker
    environment:
      AUTHENTIK_REDIS__HOST: ${AUTHENTIK_REDIS__HOST}
      AUTHENTIK_REDIS__PORT: ${AUTHENTIK_REDIS__PORT}
      AUTHENTIK_REDIS__PASSWORD: ${AUTHENTIK_REDIS__PASSWORD}
      AUTHENTIK_POSTGRESQL__HOST: ${AUTHENTIK_POSTGRESQL__HOST}
      AUTHENTIK_POSTGRESQL__USER: ${AUTHENTIK_POSTGRESQL__USER}
      AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRESQL__NAME}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_POSTGRESQL__PASSWORD}
    user: root
    env_file:
      - .env
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./media:/media
      - ./certs:/certs
      - ./custom-templates:/templates
EOF

Step 6: Start it up

docker compose up -d
docker compose logs -f server
# Watch for "Starting authentik server" and "GET /api/v3/root/config/"

On first boot, Authentik runs migrations against your managed Postgres and seeds default flows. This takes 30-60 seconds. Once the server is healthy, visit http://YOUR_SERVER_IP:9000/if/flow/initial-setup/ to create your first admin account (akadmin).

Step 7: Put it behind HTTPS with Caddy

cat > Caddyfile << 'EOF'
auth.yourdomain.com {
    reverse_proxy server:9000 {
        # Needed so Authentik knows the original scheme/host
        header_up X-Forwarded-Proto {scheme}
        header_up X-Forwarded-Host {host}
    }

    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
        Referrer-Policy strict-origin-when-cross-origin
    }

    encode gzip
}
EOF

# Add Caddy as a service in docker-compose.yml
# (reverse proxy to the "server" service on port 9000)

Append a Caddy service to your docker-compose.yml:

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy-data:/data
      - caddy-config:/config
    depends_on:
      - server

volumes:
  caddy-data:
  caddy-config:

Restart with docker compose up -d. Caddy will auto-issue a Let's Encrypt cert for auth.yourdomain.com. You should now be able to log in at https://auth.yourdomain.com.

Keycloak on Docker Compose — the alternative recipe

If you prefer Keycloak, the compose setup is almost identical. Keycloak does not need Redis (it uses embedded Infinispan for session cache), but does want a dedicated Postgres database.

cat > docker-compose.yml << 'EOF'
services:
  keycloak:
    image: quay.io/keycloak/keycloak:26.1
    restart: unless-stopped
    command: start --optimized
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://your-pg-host.databases.danubedata.ro:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: ${KC_DB_PASSWORD}
      KC_HOSTNAME: auth.yourdomain.com
      KC_HOSTNAME_STRICT: "true"
      KC_PROXY_HEADERS: xforwarded
      KC_HTTP_ENABLED: "true"
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD}
    ports:
      - "8080:8080"
    depends_on: []

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy-data:/data
      - caddy-config:/config

volumes:
  caddy-data:
  caddy-config:
EOF

The Caddyfile is the same, pointing at keycloak:8080 instead of server:9000. First boot takes longer because Keycloak has to import its default realm and build the Quarkus image optimizations.

Once up, visit https://auth.yourdomain.com/admin and log in with the admin credentials from the env vars. Create a new realm for your organization — do not use the default "master" realm for applications; it is meant only for administering Keycloak itself.

Connecting your first application: OIDC

The most common integration pattern is OIDC. Authentik and Keycloak both expose standard OIDC discovery endpoints, so any OIDC-compliant client works.

In Authentik

  1. Go to Applications → Providers → Create → OAuth2/OpenID Provider.
  2. Pick a name (e.g. "Grafana"), set an authorization flow (default is fine), and note the generated client_id and client_secret.
  3. Set the redirect URI to whatever your app uses (e.g. https://grafana.yourdomain.com/login/generic_oauth).
  4. Go to Applications → Applications → Create. Bind it to the provider.
  5. The OIDC discovery URL will be https://auth.yourdomain.com/application/o/<slug>/.well-known/openid-configuration.

In Keycloak

  1. Inside your realm, go to Clients → Create client.
  2. Pick client type OpenID Connect, give it an ID, enable "Client authentication" for confidential clients.
  3. On the next screen, set the valid redirect URI.
  4. Once created, grab the client secret from the "Credentials" tab.
  5. The OIDC discovery URL is https://auth.yourdomain.com/realms/<realm>/.well-known/openid-configuration.

Most apps (Grafana, Gitea, Nextcloud, Outline, Jellyfin, Vaultwarden, Proxmox 8+, etc.) have an "OIDC" or "Generic OAuth" section where you paste the discovery URL, client ID, and secret. Five minutes of clicking and the app redirects to your IdP for login.

Social login: Google and GitHub

You can federate external IdPs into your own so users can "sign in with Google" but still end up as users in your Authentik/Keycloak — useful when your product accepts both internal and external customers.

In Authentik, go to Directory → Federation & Social login → Create → OAuth Source. Pick "Google" or "GitHub" from the dropdown, paste a Google/GitHub OAuth client ID and secret, and Authentik handles the rest.

In Keycloak, inside a realm go to Identity providers → Add provider → Google (or GitHub). Same flow: paste OAuth credentials, hit save.

After this, your login page shows "Sign in with Google" buttons and users can bind an external identity to a new or existing local user.

SAML for enterprise customers

When an enterprise buyer asks for SAML SSO, it usually means their IT department wants to connect their IdP (often Okta, Azure AD / Entra, or Google Workspace) to your product. Your IdP acts as a service provider (SP) and their IdP acts as an identity provider.

In Authentik: Directory → Federation → Create → SAML Source. You configure the metadata URL the customer provides, hit save, and now their users can log into your app as federated identities.

If instead your IdP is the SAML IdP for a third-party app (e.g. BambooHR, Atlassian, Salesforce), you create a SAML Provider in Authentik (or a SAML client in Keycloak), download the metadata XML, and hand it to the customer to upload on the SaaS side.

A common mistake is mixing up SP-initiated and IdP-initiated flows. Most B2B SaaS expects SP-initiated (user clicks "Login with SSO" from the app), but some enterprise portals still launch from an IdP dashboard. Both Authentik and Keycloak support both modes; just check "IdP-initiated flow" in the provider settings if needed.

Protecting an app with no built-in auth (forward auth)

Some tools (Prometheus, Kibana, a raw internal dashboard) have no authentication at all. You do not want to expose them publicly — but you also do not want to write custom auth for every one. The solution is forward auth: your reverse proxy calls the IdP for each request and only proxies to the upstream if the user is authenticated.

Authentik ships an "outpost" proxy that handles this natively. In the Authentik UI, create a Proxy Provider pointing at your upstream service, attach it to an Application, and Authentik spawns a tiny Go proxy that Nginx/Traefik can query. A minimal nginx config looks like this:

location /outpost.goauthentik.io {
    proxy_pass       http://authentik-outpost:9000/outpost.goauthentik.io;
    proxy_set_header Host $host;
}

location / {
    auth_request     /outpost.goauthentik.io/auth/nginx;
    error_page       401 = @goauthentik_proxy_signin;

    auth_request_set $authentik_username $upstream_http_x_authentik_username;
    proxy_set_header X-Authentik-Username $authentik_username;

    proxy_pass http://your-upstream-app:3000;
}

location @goauthentik_proxy_signin {
    internal;
    return 302 /outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri;
}

With Keycloak, the equivalent is deploying oauth2-proxy as a sidecar and pointing it at your Keycloak OIDC client — a few extra moving parts but the end result is identical.

MFA and WebAuthn / passkeys

Both IdPs support TOTP (Google Authenticator, Authy), push/duo, and WebAuthn (passkeys, YubiKeys). In Authentik, MFA is wired through authentication flows: you add a "MFA validation" stage to the default "authentication-flow" and define whether it is required, optional, or conditional on group membership.

In Keycloak, you edit the realm's Authentication → Flows, duplicate the browser flow, and add an "OTP Form" or "WebAuthn Authenticator" step. Bind the flow to the realm, and new logins will prompt for MFA enrollment.

Passkeys are the best UX for 2026 — no phone needed, no TOTP secret to back up, and they are phishing-resistant by design. Both IdPs have first-class WebAuthn support.

SCIM: the secret weapon for enterprise deals

SCIM (System for Cross-domain Identity Management) lets your IdP push user changes to downstream apps. When a user is deactivated in your IdP, SCIM automatically deprovisions them from connected SaaS — no more forgotten accounts floating around when someone leaves the company.

Authentik supports outbound SCIM natively: go to Applications → Providers → Create → SCIM Provider, paste the target URL and bearer token from the downstream app (e.g. Slack, GitHub Enterprise), and Authentik will sync user lifecycle events.

Keycloak has community extensions for SCIM but not first-party support — if SCIM is mission-critical, Authentik is the cleaner choice.

Backup strategy

Your IdP's Postgres database is the most important thing in the entire stack. Losing it means losing every user password hash, every MFA enrollment, every SAML certificate, every application configuration. Back it up.

Since we are using DanubeData managed Postgres, automated daily snapshots happen on the database instance itself — no setup required on the IdP side. That covers the database. For the IdP configuration (Docker Compose file, Caddyfile, media uploads, custom templates), a simple tarball-to-S3 cron job is enough:

#!/bin/bash
# /opt/authentik/backup.sh
set -e
DATE=$(date +%Y-%m-%d_%H-%M-%S)
BACKUP_DIR=/opt/authentik/backups
mkdir -p $BACKUP_DIR

tar -czf "$BACKUP_DIR/authentik-config_$DATE.tar.gz" 
    /opt/authentik/.env 
    /opt/authentik/docker-compose.yml 
    /opt/authentik/Caddyfile 
    /opt/authentik/media 
    /opt/authentik/custom-templates

# Upload to DanubeData S3
rclone copy "$BACKUP_DIR/authentik-config_$DATE.tar.gz" "dd-s3:auth-backups/"

# Prune local
find $BACKUP_DIR -name "*.tar.gz" -mtime +14 -delete

Schedule it in cron:

(crontab -l 2>/dev/null; echo "0 3 * * * /opt/authentik/backup.sh >> /var/log/auth-backup.log 2>&1") | crontab -

Test restore by spinning up a staging VPS, pointing a test Postgres instance at a snapshot, and running the compose stack against it. Practice this once when things are calm; you do not want your first restore to happen during an outage.

Upgrade and maintenance cadence

Authentik ships a new release every 1-2 months. Keycloak is on a similar cadence. Both projects publish release notes with any breaking changes (there are rarely any that affect plain OIDC/SAML setups — most breakage comes from custom templates or unusual flow configurations).

A safe monthly routine:

  1. Snapshot the managed Postgres (via DanubeData dashboard — 5 seconds).
  2. Pin the new version in .env (e.g. AUTHENTIK_TAG=2026.5).
  3. docker compose pull then docker compose up -d.
  4. Tail logs for 2 minutes, verify login still works.

If something breaks, roll back the tag, docker compose up -d, and restore the database from snapshot if migrations ran. The total blast radius of a bad upgrade is ~10 minutes of login downtime.

Production checklist

  • HTTPS with auto-renewing cert (Caddy handles this).
  • Strong, random AUTHENTIK_SECRET_KEY (or Keycloak KC_ADMIN_PASSWORD) stored only in .env with chmod 600.
  • Firewall restricts ingress to 80/443 only. IdP ports (9000/8080) should not be exposed.
  • Managed Postgres with SSL enforced; automated daily snapshots enabled on DanubeData.
  • Managed Redis with TLS + password auth.
  • Admin account has MFA enabled (TOTP or passkey).
  • Second admin account as recovery, stored offline.
  • SMTP configured so users can reset their own passwords.
  • Monitoring: uptime check on https://auth.yourdomain.com/-/health/live/ (Authentik) or /health/ready (Keycloak).
  • Structured logs shipped to a log aggregator (Loki, Elastic, Datadog) — auth events are a security goldmine.
  • Backup script tested by doing one real restore to a staging box.

FAQ

Should I pick Authentik or Keycloak in 2026?

For most greenfield deployments, Authentik. The admin UI is years ahead, the memory footprint is smaller, and features like SCIM and embedded forward-auth outposts ship out of the box. Keycloak is the safer pick if you are already in a Red Hat shop, need paid vendor support with a commercial SLA, or integrate tightly with other Red Hat / Quarkus services. Both are excellent.

How hard is it to migrate from Auth0 or Clerk?

Plan for a few weekends, not a few hours. The database migration is usually the hardest part: both Auth0 and Clerk let you export users but passwords are hashed with bcrypt/scrypt, and your new IdP has to accept those hashes on first login. Authentik supports importing bcrypt hashes via API. Clerk users can be migrated using Clerk's export endpoint and Authentik's bulk user API. The other step is rewriting OIDC/SAML configuration in every downstream app — budget one hour per app.

SAML vs OIDC — which should my app support?

Both, if you sell to enterprise. OIDC is the default for modern apps; SAML is what enterprise buyers' security teams demand. Adding SAML support is a one-time integration that unlocks a huge tier of deals. Fortunately, both Authentik and Keycloak make this trivial on the IdP side — you just configure a SAML provider alongside your existing OIDC clients.

Can I really run this on €37/month?

Yes, for the "small team" tier (up to several thousand concurrent sessions). DanubeData's DD Small VPS (€12.49) handles the Authentik server and worker comfortably. Managed Postgres (€19.99) and managed Redis (€4.99) handle the state layer with automatic backups. Total: €37.47/month, all-in. Once you cross ~10,000 concurrent sessions, upgrade to DD Medium (€24.99). You will not cross that line with a few thousand active users.

What about high availability?

Both Authentik and Keycloak support HA. The Authentik server and worker containers are stateless (state lives in Postgres and Redis), so running two VPS instances behind a load balancer with a shared managed database gives you N+1 redundancy. Keycloak has its own clustering mode over Infinispan. For most startups the failure mode of "IdP down = login broken" is acceptable for 10-15 minutes per year, so single-node is fine until you have a real SLO on auth.

How do I handle password hashing in a migration?

Both Authentik and Keycloak support pluggable password hashers. On import, you tell the IdP the hash algorithm (e.g. bcrypt$$10$$...) and it validates against that on first login, then rehashes to its preferred algorithm. This is the same "passwordless migration" approach Auth0 itself uses. Zero user-visible reset required.

Are there compliance certifications for self-hosted IdPs?

The IdPs themselves (Authentik, Keycloak) are not "certified" because they are software you run — compliance is a property of the operator, not the software. What matters for GDPR, SOC 2, or ISO 27001 auditors is that your infrastructure is in a compliant data center (DanubeData's Falkenstein facility is ISO 27001 certified), backups are encrypted, access is logged, and MFA is enforced. You get all of that on a DanubeData stack out of the box.

What if I outgrow the single-VPS setup?

Scale vertically first (DD Medium, then DD Large), then horizontally. Horizontal scaling on DanubeData means deploying two identical IdP VPSes, pointing both at the same managed Postgres + Redis (they handle the concurrency), and putting an HAProxy or Caddy load balancer in front with session stickiness disabled (all state is in Redis, so any node can serve any request). This gets you to hundreds of thousands of MAU without architectural changes.

Get started

Owning your identity stack is one of those things that sounds scary until you do it once. After that, it is just another service in your Docker Compose file — one that happens to save you thousands of dollars a month at any real scale.

Everything you need is a VPS, a Postgres instance, and a Redis instance. On DanubeData that is:

  • DD Small VPS — €12.49/mo — 4 vCPU, 8 GB RAM, NVMe, 20 TB traffic
  • Managed PostgreSQL — €19.99/mo — SSL, daily snapshots, replica-ready
  • Managed Redis — €4.99/mo — TLS, password auth, high availability option

All in a GDPR-compliant German data center, all with automated backups, all for a total of €37.47/month — plus DanubeData's €50 signup credit covers your first month or two entirely.

👉 Create your Authentik stack on DanubeData

Questions about migrating from Auth0 or picking between Authentik and Keycloak? Get in touch — we run this stack ourselves and are happy to compare notes.

Share this article

Ready to Get Started?

Deploy your infrastructure in minutes with DanubeData's managed services.