An identity provider is the dependency every other service shares and none of them report on. When Keycloak or authentik goes down, your wiki, your Grafana, your Git forge, and your internal tools all stay up, return 200 to a monitor, and refuse to let anyone in. Our guide to monitoring OAuth and OpenID providers covers the protocol-level checks that apply to any IdP, hosted or not: discovery document, JWKS, callback. This one is about what is different when you run the IdP yourself: health endpoints that live on ports you must not expose, background workers that fail silently, outposts that sit between users and apps, and databases that only you are backing up.
Both products get the same four layers: an external monitor on something that proves the server is serving, an assertion that catches a half-broken state, a heartbeat for the parts that cannot be seen from outside, and the certificate. Keycloak first.
Keycloak
Where the health endpoints are, and why you probably cannot use them
Keycloak has four health endpoints, /health, /health/live, /health/ready, and /health/started, and they come with two conditions. First, they only exist if the server was built with --health-enabled=true. Second, since Keycloak 25 they are served by default on the management interface, port 9000, not on the port your users reach. The documented probe is curl --head -fsS http://localhost:9000/health/ready. You can move them back onto the main port by setting http-management-health-enabled=false, and the documentation's advice if you do is to block /health at your reverse proxy. Either way, the message is the same: these endpoints are for Kubernetes probes and internal tooling, and they should not be reachable from the internet.
Two consequences for external monitoring. You will usually be checking something else, and the thing that checks the database directly (/health/ready) stays inside the network, where you reach it with a heartbeat rather than an HTTP monitor.
What to monitor from outside
Three unauthenticated URLs on the public port tell you most of what you need:
- The realm endpoint.
GET https://sso.example.com/realms/<realm>returns a small JSON document with"realm":"<realm>", the realm's public key, and the token and account service URLs. A 200 means Keycloak is up and that realm exists; a 404 means someone deleted or renamed it. This is the primary monitor. On the free plan, expected status 200 is the whole configuration; on Pro, add the assertion contains"realm":"<realm>"so a proxy serving a 200 error page cannot pass. - The discovery document.
/realms/<realm>/.well-known/openid-configurationis what every client library fetches to find the endpoints. Pro assertion: contains"issuer":"https://sso.example.com/realms/<realm>", with your exact hostname. This catches the classic self-hosted mistake whereKC_HOSTNAME(or a proxy header) is wrong and the issuer comes back as an internal name, which makes every client reject every token even though the server is "up". - The JWKS.
/realms/<realm>/protocol/openid-connect/certsreturns{"keys":[...]}. Applications validate token signatures against it, often on every request; if it is unreachable, sessions start dying as caches expire. Pro assertion: contains"keys", or the regex"kid":"to require at least one key.
If you want to prove the login page itself renders, point a monitor at the authorization endpoint with a real client: /realms/<realm>/protocol/openid-connect/auth?client_id=<public-client>&response_type=code&redirect_uri=<a registered URI>&scope=openid. Keycloak returns the login form with a 200; with a missing or unregistered parameter it returns a 400 error page instead, so a wrong redirect URI fails the monitor, which is the behavior you want. Use a Pro keyword from the form (the default theme's login form has the id kc-form-login; a custom theme will differ). Do not go further than rendering the form: scripted logins create sessions and events on every check.
The admin console (/admin/master/console/) is a single-page app that returns 200 even when the backend is sick, and it is the one path you should restrict at the proxy anyway. If you do restrict it, add an inverse monitor expecting the proxy's 403, as described in the custom-headers guide, so you are told if the restriction ever disappears.
The cache problem, and the readiness heartbeat
Keycloak caches realms, clients, and keys in Infinispan. After the database goes away, /realms/<realm>, the discovery document, and the JWKS can keep answering from cache for some time, while anything that writes, which is every login and every token refresh, fails. Your external monitors stay green while your users are locked out. /health/ready is the endpoint that checks the database, and it is on the management port.
So bring it to CronAlert the other way round. On any machine in the same network (or in a sidecar), run a cron job that probes readiness and pings a heartbeat monitor only on success:
# every minute; the Keycloak container image has no curl, so run this from the host or another container
* * * * * curl --head -fsS --max-time 5 http://keycloak:9000/health/ready >/dev/null && curl -fsS --max-time 10 https://cronalert.com/api/heartbeat/YOUR-TOKEN Heartbeat interval 1 minute, grace 3 to 5 minutes. A database outage now produces an alert within a few minutes even while the cached public endpoints look fine. If https-client-auth is set to required, the management interface inherits it; set https-management-client-auth to request or none, as the docs suggest, so the probe does not need a client certificate. Heartbeat monitors are a Pro feature; sizing the grace period explains the numbers.
Clustering and the load balancer
With several Keycloak nodes behind a load balancer, the external monitors see the balancer's view, which is right for "can users log in", and wrong for "is node 2 healthy". The readiness heartbeat above is per node (one cron line per node, one heartbeat each), which tells you a node dropped out of a cluster that is still serving. Sticky sessions matter less than they used to, but if you use them, a monitor that lands on one dead node and fails while users on other nodes are fine is a real possibility; multi-region checks with a majority threshold help here.
Authentik
Health endpoints you can actually reach
authentik is easier from outside. The server answers GET /-/health/live/ with 200 as long as the process is running, and GET /-/health/ready/ with 200 when it can establish a PostgreSQL connection (older versions also required Redis; check your version's docs). Both are on the main port, both are unauthenticated, and the official Docker Compose and Helm deployments use them as probes. Create two HTTP monitors:
https://auth.example.com/-/health/ready/, expected 200, timeout 10 seconds. This is the primary monitor: a 503 or timeout means the database is gone or the server is not accepting connections, which is a login outage.https://auth.example.com/-/health/live/, expected 200. Separately useful because live green with ready red is a database problem, while both red is the server, the proxy, or the host.
Add the discovery document for any OIDC provider you care about, /application/o/<provider-slug>/.well-known/openid-configuration, with the same issuer assertion used for Keycloak above; a wrong AUTHENTIK_HOST or external host setting shows up there first. And unless you have reason not to, restrict /-/health/ at your proxy to monitoring and probe sources only; it leaks nothing sensitive, but there is no need to advertise it.
The worker
authentik's worker runs everything that is not a request: outpost synchronisation, LDAP and SCIM sources, token and session cleanup, email, blueprints. When it dies, nothing visible breaks immediately; logins keep working from the server process, and the damage shows up hours later as expired certificates that did not renew, a directory sync that never ran, or an outpost whose config is stale. The worker has no public endpoint. The documented check is ak healthcheck inside the worker container (it also serves /-/health/ready/ on its own listen port, internally).
# every 5 minutes on the Docker host
*/5 * * * * docker exec authentik-worker-1 ak healthcheck && curl -fsS --max-time 10 https://cronalert.com/api/heartbeat/YOUR-TOKEN Heartbeat interval 5 minutes, grace 10. On Kubernetes, a CronJob that runs the same command against the worker service, or simply a check that the worker Deployment has ready replicas, feeds the same heartbeat; Kubernetes CronJob monitoring has the template.
Outposts
Outposts are the proxy, LDAP, RADIUS, and RAC components that sit in front of applications or speak other protocols. Each exposes /outpost.goauthentik.io/ping on its metrics port 9300, which is internal. From outside, the useful test is the thing the outpost is for:
- Proxy outposts. A monitor on the protected application,
https://grafana.example.com/say, gets redirected to authentik's login flow. CronAlert's HTTP checks follow redirects, so the monitor lands on the flow page with a 200; on Pro, assert a keyword from that page (the word authentik appears in the default flow interface's title) so you know the redirect went to authentik and not to an error page. This single monitor proves the outpost, its connection to the server, and the server itself. A redirect loop or a 502 from the outpost fails it. Remember the trap described in reverse proxy monitoring: without the keyword, any page that eventually returns 200 passes. - LDAP outposts. Use a TCP monitor on the LDAP port (389 or 636 as you have configured it) if it is exposed to the monitoring network; otherwise an
ldapsearchin a cron job with a heartbeat, which also verifies a bind. - Embedded outpost. Runs inside the server process, so the server's
/-/health/ready/covers it.
Both: certificates and backups
Every HTTP monitor on every plan watches the certificate of the hostname it checks, so the realm or readiness monitor already alerts before sso.example.com expires. If clients also use a separate admin or account hostname, monitor it too; a dead certificate on an IdP is a total outage with a browser warning as its only error message. See SSL certificate monitoring.
The database behind either product is the thing you cannot recreate: every user, credential, client secret, and signing key is in it. A nightly pg_dump (Keycloak also supports MariaDB, MySQL, and Oracle; authentik is PostgreSQL only) with a heartbeat ping at the end, interval 1 day, grace a few hours, is the minimum; monitoring database backups covers the restore test. For authentik, blueprints and the media directory (custom icons, flow backgrounds) belong in the same job.
Reading an alert
| Alert | Likely cause | First thing to check |
|---|---|---|
| Keycloak realm endpoint timeout or 502 | Proxy, host, or Keycloak process | Container or pod status; proxy logs |
| Realm endpoint 404 | Realm renamed or deleted, or wrong http-relative-path after an upgrade | Admin console realm list; KC_HTTP_RELATIVE_PATH |
| Discovery issuer assertion fails | KC_HOSTNAME or proxy headers wrong after a change | The issuer value in the body |
| Public endpoints green, readiness heartbeat overdue | Database unreachable; endpoints served from cache | Database; connection pool errors in Keycloak logs |
authentik live green, ready 503 | PostgreSQL (or Redis on older versions) unreachable | Database container; AUTHENTIK_POSTGRESQL__* |
| authentik worker heartbeat overdue | Worker container stopped or crash-looping | docker logs on the worker; memory |
| Protected-app monitor fails, authentik itself green | Proxy outpost down, lost its token, or redirect loop | Outpost logs; Outposts page health in the admin UI |
| Certificate alert | Renewal stopped | Proxy's ACME logs; renewal monitoring |
Frequently asked questions
Should I just expose Keycloak's management port to CronAlert?
No. There are no fixed probe IP ranges to allowlist, and the management interface also serves metrics. Keep it internal and use the heartbeat.
How often should the IdP be checked?
The interval is set by plan: every 3 minutes on Free, every minute on Pro. Discovery and JWKS requests are cheap and cached by Keycloak and authentik alike; a one-minute check is far below what a single busy application generates.
Can one monitor cover both the IdP and an app behind it?
The proxy-outpost monitor above does, for authentik. For Keycloak-protected apps using an OIDC library, the app typically shows its own login button rather than redirecting, so monitor the IdP endpoints directly and the app separately; monitoring internal tools covers the app side.
What about the authentik admin interface?
/if/admin/ requires a session and redirects to the login flow, so a monitor on it tests the same thing as the flow page. Prefer the readiness endpoint and, if you restrict the admin path at the proxy, an inverse monitor expecting the refusal.
Is any of this on a paid plan?
The status-only monitors on the realm, discovery, JWKS, and authentik health endpoints, plus certificate monitoring, are free. Keyword assertions, heartbeats (readiness, worker, backups), and TCP monitors for LDAP are Pro at $5/month.
Get started
Two or three public URLs with the right assertions, a heartbeat for what the network hides, and the certificate, and your identity provider stops being the outage nobody's monitor reports. Create a free CronAlert account and add the realm or readiness monitor first. Related reading: monitoring OAuth and OpenID providers, monitoring Docker self-hosted services, Kubernetes uptime monitoring, monitoring self-hosted GitLab, and monitoring multi-tenant SaaS.