Skip to main content

Your Admin Dashboard Doesn't Need Keycloak: Authelia and Pocket ID as Forward-Auth, Not Full IdP

8 min readDora NodaDora Noda
Share
On this page

Every self-hosted-SSO comparison on the internet starts from the same assumption: you need an identity provider, so pick one — Keycloak, Authentik, Zitadel — and stand it up. That assumption is right if you're issuing logins to tenants. It's the wrong frame entirely for a narrower, more common problem: putting a login wall in front of the operator's own internal tooling — a Cluster API fleet's admin dashboard, a Grafana instance, an internal status page — where the audience is a handful of people on the platform team, not an open signup flow. Standing up Keycloak for that is like buying a forklift to move a filing cabinet.

The tool that actually fits is a forward-auth gateway, not a full IdP, and it's a materially smaller thing to run. Below is a working config for both of the lightweight options that fit this job — Authelia and Pocket ID paired with OAuth2-Proxy — the real footprint numbers behind "lightweight," and the specific point where this setup stops being enough and a real IdP becomes the right call.

Forward-auth in one paragraph

A full IdP like Keycloak issues tokens, manages realms, and expects applications to speak OIDC or SAML to it directly. Forward-auth flips that: the reverse proxy in front of your app (Traefik, Caddy, NGINX) makes a sub-request to a tiny auth service on every incoming request. That service answers with a 200 (plus identity headers) or a 401. A 200 lets the request through to the backend unmodified; a 401 redirects the browser to a login page. The app behind the proxy never has to know an auth flow exists — it just sees a request with Remote-User or X-Auth-Request-User already set in the headers. No client libraries, no OIDC integration in the app itself, because the app was often never built to have one — that's exactly the situation for an internal Grafana dashboard, a Traefik dashboard, or a homegrown fleet-status page.

Two tools fill that gateway role at a fraction of a full IdP's weight: Authelia, which bundles the forward-auth check and the login portal into one binary, and Pocket ID, a passkey-only OIDC provider that needs OAuth2-Proxy in front of it to do the forward-auth part itself. Here's both, wired up for real.

Build 1: Authelia in front of Traefik

Authelia ships as a single ~20MB Go binary, configured entirely in files (no admin UI to click through), and answers forward-auth checks against its /api/authz/forward-auth endpoint. The Traefik side is one middleware block:

yaml
# traefik dynamic config
http:
  middlewares:
    authelia:
      forwardAuth:
        address: "http://authelia:9091/api/authz/forward-auth"
        trustForwardHeader: true
        maxResponseBodySize: 8192
        authResponseHeaders:
          - "Remote-User"
          - "Remote-Groups"
          - "Remote-Email"
          - "Remote-Name"
 
  routers:
    fleet-dashboard:
      rule: "Host(`fleet.internal.example.com`)"
      service: fleet-dashboard-svc
      middlewares: ["authelia"]

Authelia itself needs an access-control policy telling it which routes require which factor:

yaml
# authelia configuration.yml (excerpt)
access_control:
  default_policy: deny
  rules:
    - domain: "fleet.internal.example.com"
      policy: two_factor
      subject: "group:platform-team"

That's the whole integration. Every request to fleet.internal.example.com bounces through Authelia's forward-auth endpoint; unauthenticated requests get redirected to Authelia's own login portal, which handles password + TOTP/WebAuthn out of the box. No separate database is required by default — Authelia's session store and user backend can run against flat files or SQLite for a deployment this size.

Build 2: Pocket ID + OAuth2-Proxy in front of Caddy

Pocket ID takes a different shape: it's a pure OIDC provider, not a forward-auth gateway. It's also deliberately narrower than Authelia in one specific way — passkeys are the only authentication method, no passwords at all, which is a reasonable constraint for a five-person platform team who already carry a YubiKey or use a password manager's passkey support. Because Pocket ID only speaks OIDC, it needs OAuth2-Proxy sitting in front of the protected app to do the actual forward-auth interception:

yaml
# oauth2-proxy config.yaml
provider: oidc
oidc_issuer_url: "https://id.internal.example.com"
client_id: "fleet-dashboard"
client_secret: "REPLACE_WITH_POCKET_ID_CLIENT_SECRET"
cookie_secret: "REPLACE_WITH_32_BYTE_BASE64"
cookie_secure: true
email_domains:
  - "*"
set_xauthrequest: true
pass_user_headers: true
reverse_proxy: true

And the Caddy side, using the forward_auth directive:

caddyfile
fleet.internal.example.com {
  handle {
    forward_auth oauth2-proxy:4180 {
      uri /oauth2/auth
      copy_headers X-Auth-Request-User X-Auth-Request-Email
      @error status 401
      handle_response @error {
        redir * /oauth2/sign_in?rd={scheme}://{host}{uri}
      }
    }
    reverse_proxy fleet-dashboard:8080
  }
 
  handle /oauth2/* {
    reverse_proxy oauth2-proxy:4180
  }
}

Pocket ID itself is a single container backed by SQLite by default — no Postgres, no Redis, no JVM. First-run setup takes under a minute and the whole stack (Pocket ID + OAuth2-Proxy) still lands well under what a full IdP needs at idle.

The gateway becomes a single point of failure — plan for that, not around it

Putting a login wall in front of the fleet dashboard also means that wall is now on the critical path to fixing anything if the fleet is unhealthy. Two operational details matter more than the auth protocol choice:

  • Session persistence across restarts. Authelia's default file-based session backend loses every logged-in session on a container restart, forcing a re-login (with MFA) at the worst possible moment — mid-incident, when the admin dashboard is the thing you need open. Point Authelia at Redis for session storage in anything beyond a single-operator homelab, and do the same for OAuth2-Proxy's session_store if running Pocket ID. Both support it as a config flag, not a rewrite.
  • A break-glass path that doesn't depend on the gateway being healthy. If Authelia or OAuth2-Proxy itself is down — a bad config push, a database connection issue — and the fleet dashboard is unreachable behind it, that's the exact moment an operator needs direct access most. A documented fallback (SSH tunnel straight to the dashboard's internal port, bypassing the proxy) costs nothing to set up ahead of time and is the difference between a five-minute fix and being locked out of your own tooling during an incident.

Neither of these needs a heavier tool — Keycloak has the identical failure mode if its own database connection drops. They're just details that "add a login page" glosses over, and a forward-auth setup this cheap to run is worth doing right rather than fast.

The footprint numbers that justify skipping Keycloak

"Lightweight" is a claim worth pricing out rather than taking on faith:

AutheliaPocket ID + OAuth2-ProxyAuthentikKeycloak
Idle RAM~30MB~40–60MB combined200–400MB+~1GB
Required datastorenone (file/SQLite)SQLite (bundled)PostgreSQL + RedisPostgreSQL (or embedded)
Auth methodspassword, TOTP, WebAuthn, pushpasskey onlypassword, WebAuthn, social, SAML upstreamspassword, WebAuthn, social, SAML, LDAP
Protocol surfaceforward-auth native; OIDC provider role still in open betaOIDC provider (needs a forward-auth companion)full OIDC + SAML IdP, multi-applicationfull OIDC + SAML IdP, multi-realm
Admin surfaceYAML config files, no click-through UIminimal web UIfull web UI, RBAC, flows editorfull web UI, realms, federation
Fitsprotecting the operator's own tooling behind one proxysame, if passkey-only auth is acceptabletenant-facing apps needing a real IdP without Keycloak's weightmulti-realm, LDAP federation, enterprise SSO requirements

The gap isn't marginal. A Cluster API management node running Authelia or Pocket ID spends single-digit percentage points of a small VM's RAM on auth; the same node running Keycloak dedicates roughly a third of a 4GB box to an identity provider serving, in this use case, fewer than a dozen logins a day. That's the entire argument for picking the smaller tool — not that Keycloak is bad, but that its weight is priced for a job (tenant-facing, multi-realm identity) this use case doesn't have.

Where forward-auth stops being enough

The honest limit is real, and it isn't about scale — a two-person team and a two-hundred-person team both fit comfortably behind Authelia if the audience is "our own staff." It's about who's logging in and what they need to do once they're in:

  • Self-service signup. Forward-auth gateways assume an operator provisions accounts by hand or via config file. The moment strangers need to create their own accounts, that's IdP territory.
  • Per-tenant OIDC clients or SAML. If tenants — not the platform team — need to log into their own views (a per-tenant dashboard, a billing portal), each one is a separate OIDC/SAML relying party with its own claims and scopes. Authelia and Pocket ID aren't built to manage a directory of external client registrations at that scale; Keycloak and Authentik are.
  • Delegated administration. A platform team managing its own dozen accounts in a YAML file is fine. A support team that needs to reset a tenant's password without platform-engineer involvement needs an admin UI with role-scoped delegation — the full-IdP feature set exists for exactly this.
  • Federation and LDAP. Once there's an existing corporate directory to bind against, or a requirement to federate with an upstream enterprise IdP, that's squarely Keycloak/Authentik's job, not a forward-auth gateway's.

For a platform like this — Cluster API underneath, a Render-compatible deploy API, an admin dashboard that only the operating team touches — the two problems stay genuinely separate for a long time: protecting your own internal surfaces is a forward-auth job today, and it stays one until the day tenants themselves need to authenticate against the platform directly rather than through their own app's login. That's the day to open a Keycloak or Authentik ticket, not before.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex