Skip to content
Go back

Tinyauth vs Pocket ID vs Authelia

By KingPin 13 min read
Tinyauth vs Pocket ID vs Authelia
Contents

You do not need Keycloak to lock five apps

Somewhere on Reddit right now, someone is deploying Keycloak to put a login page in front of Portainer, Uptime Kuma, and a Grafana dashboard nobody else will ever see. That is like hiring a forklift to move a couch. Technically it works, but your neighbors will have questions, and you will spend your Saturday reading about realms instead of watching the game.

You have three lighter options, and they are not interchangeable despite what the “top 10 self-hosted SSO tools” listicles imply. Tinyauth is a forward-auth shim that also ships its own OIDC provider: it sits in front of your reverse proxy, shows a login form, and gets out of the way. Pocket ID is a passkey-only OIDC provider: it does not sit in front of anything, your apps redirect to it. Authelia does both jobs too, but with a central policy file that picks a different second factor per rule, and it is the heaviest of the three by a wide margin.

Here is the verdict up front. If you are protecting five home lab services that do not natively support OIDC login (Portainer without the OIDC plugin, Uptime Kuma, a raw Grafana behind Traefik), run Tinyauth. It is one container, one environment variable block, and a login wall in under ten minutes. Tinyauth can be the OIDC source for the ones that do speak it (Immich, Gitea, Vaultwarden with the right build). Swap in Pocket ID when you want those logins to be passkey-only, with no password to phish in the first place. Reach for Authelia only when you want one policy file deciding which routes demand a second factor and which do not, rather than per-app labels. Nobody on a five-service home lab needs Keycloak or Authentik yet. Bookmark that thought for the last section.

Here is the same verdict as a table, for when you are skimming this at 2 AM trying to remember which one issues tokens:

TinyauthPocket IDAuthelia
Version checkedv5.2.0v2.14.0v4.39.28
LicenseAGPL-3.0BSD-2-ClauseApache-2.0
Core jobForward-auth, plus its own OIDC providerOIDC provider onlyForward-auth and OIDC
OIDC directionClient and provider (OpenID Certified Basic OP since v5.1.0)Provider (issues its own tokens)Provider (issues its own tokens, open beta)
PasskeysNot supportedRequired, no password fallbackSupported as one 2FA method among several
Second factorTOTP for local accountsThe passkey itself is the factorTOTP, WebAuthn, Duo Push, chosen per access rule
User storeEnv var, mounted users.yml, or LDAP bindBuilt-in SQLite, optional LDAP syncYAML file or LDAP
Extra containers to run itZeroZeroZero on SQLite, add Postgres or MySQL only past one instance
Config before first login3 env vars, 1 middleware label1 compose file, 1 env file, 1 setup wizardconfiguration.yml plus users_database.yml, roughly 6 sections

Read that last row twice. Tinyauth and Pocket ID both get you to a working login screen with one file. Authelia asks for six sections of YAML before it will let anyone in, and that is the price of the central policy file.

Tinyauth: a login wall, nothing else pretends

Tinyauth (tinyauthapp/tinyauth, currently v5.2.0, AGPL-3.0 licensed) is a forward-auth proxy middleware. It sits between your reverse proxy and the app it protects, checks a cookie, and either lets the request through or shows a login page. That is the entire feature set, and that is the point.

Tinyauth works both directions on OIDC. As a client, it lets a user log in with GitHub, Google, or a generic upstream issuer, the same way a lot of SaaS dashboards offer “Sign in with Google.” As a provider, it has been OpenID Certified for Basic OP since v5.1.0 (certified 2026-06-25), so pointing Immich or Gitea at Tinyauth as their OIDC login source does work. Run oidc create to mint a client ID and secret, and Tinyauth publishes discovery at /.well-known/openid-configuration like any other OP.

A minimal Traefik setup looks like this:

compose.yml
services:
traefik:
image: traefik:v3.3
command: --api.insecure=true --providers.docker
restart: unless-stopped
ports:
- 80:80
volumes:
- /var/run/docker.sock:/var/run/docker.sock
whoami:
image: traefik/whoami:latest
restart: unless-stopped
labels:
traefik.enable: true
traefik.http.routers.whoami.rule: Host(`whoami.example.com`)
traefik.http.routers.whoami.middlewares: tinyauth
tinyauth:
image: ghcr.io/tinyauthapp/tinyauth:v5
restart: unless-stopped
environment:
- TINYAUTH_APPURL=https://tinyauth.example.com
- TINYAUTH_AUTH_USERS=user:$$2a$$10$$UdLYoJ5lgPsC0RKqYH/jMua7zIn0g9kPqWmhYayJYLaZQ/FTmH2/u
- TINYAUTH_AUTH_TRUSTEDPROXIES=172.17.0.0/16
labels:
traefik.enable: true
traefik.http.routers.tinyauth.rule: Host(`tinyauth.example.com`)
traefik.http.middlewares.tinyauth.forwardauth.address: http://tinyauth:3000/api/auth/traefik

Three environment variables and one middleware label. TINYAUTH_AUTH_USERS takes a bcrypt hash, not a plaintext password, so generate it with htpasswd before you paste it in. No database, no config file, no separate volume beyond the container itself. Every app that should sit behind the wall gets one label pointing at the tinyauth middleware.

Tinyauth documents forward-auth for Traefik as the primary path, with community guides for nginx (via nginx Proxy Manager) and Caddy. It supports TOTP as a second factor for local accounts, on top of the GitHub/Google/generic-OIDC client login. The user store is a flat environment variable, a mounted users.yml, or an LDAP bind: TINYAUTH_LDAP_ADDRESS, TINYAUTH_LDAP_BINDDN, TINYAUTH_LDAP_BASEDN and TINYAUTH_LDAP_SEARCHFILTER are all documented config keys, so a directory server later does not force a tool swap.

Per-app access control is label-driven and already there. “These three users reach the NAS UI, those two reach the media server” is two labels, tinyauth.apps.nas.users.allow and tinyauth.apps.media.users.allow, not two deployments. The same namespace covers .users.block, .oauth.groups, .ldap.groups and .ip.allow/.ip.block.

What Tinyauth lacks is a place to see all of that at once. Every rule lives as a Docker label next to the app it guards, so auditing who can reach what means grepping your compose files. There is also no per-rule choice of second factor: TOTP is on for an account or it is not. Those two gaps, not some missing protocol, are the real reason to move up.

Pocket ID: the passkey-only OIDC provider

Pocket ID (pocket-id/pocket-id, currently v2.14.0, BSD-2-Clause licensed) sits at the opposite end of the lightweight tier. It is a real OIDC provider: other applications redirect to it, Pocket ID authenticates the user, and it hands back a signed token the way Google or Microsoft would for a corporate app. It does not do forward-auth. There is no middleware mode, no auth_request endpoint, no header injection for apps that only understand a proxy sitting in front of them. If an app cannot speak OIDC, Pocket ID cannot protect it. Check your app’s login docs for “OIDC” or “OpenID Connect” before you pick Pocket ID as your only answer for a given service.

The other defining choice: Pocket ID requires passwordless login. There is no password fallback field anywhere in the login flow, by design, because the project’s stated goal is “easier and more secure than signing in with a password.” You register a passkey (a phone, a YubiKey, Windows Hello, a password manager that does WebAuthn) and that is how every login works from then on. If you lose every device that can produce your passkey and never printed a recovery code, you are locked out of every app behind Pocket ID at the same time. Print the recovery codes. Do it now, not after the second beer.

The Docker setup is a single container with an embedded SQLite database, no separate database service to run:

compose.yml
services:
pocket-id:
image: pocketid/pocket-id:v2
restart: unless-stopped
env_file: .env
ports:
- "1411:1411"
volumes:
- ./data:/app/data
healthcheck:
test: ["CMD", "/app/pocket-id", "healthcheck"]
interval: 90s
timeout: 5s
retries: 2
start_period: 10s
.env
APP_URL=https://pocket-id.example.com
TRUST_PROXY=true
ENCRYPTION_KEY_FILE=/app/data/encryption.key

Generate the encryption key once with openssl rand -base64 32, mount the data directory, hit /setup in a browser, and you have an admin account with a registered passkey. That is the entire quickstart: one compose file, one env file, one setup wizard. No config YAML to hand-edit, no users database to maintain by hand.

Pocket ID’s user store is its own SQLite database, with optional LDAP sync that pulls users and groups in from an existing directory. That covers a five-service home lab and then some. It outgrows its lane on the forward-auth side instead: the moment an app in your stack has no OIDC login option, Pocket ID cannot help you, and you add a different tool next to it rather than upgrading this one.

Authelia: the one that does both jobs

Authelia (authelia/authelia, currently v4.39.28, Apache-2.0 licensed) is the heavyweight of this specific comparison, though it is still a fraction of Keycloak’s footprint. It runs as forward-auth middleware in front of Traefik, nginx, Caddy, HAProxy, Envoy, SWAG, or Skipper, with official integration guides for every one of those. It can also act as an OpenID Connect 1.0 provider, issuing real tokens the way Pocket ID does. That second capability is documented as an open beta in Authelia’s own docs as of September 2026, not a stable, load-bearing feature you should point your only externally-exposed app at without a fallback plan.

Setup cost is higher than the other two, by a clear margin. You need a configuration.yml for sessions, storage, notifications, and access control policy, plus a users_database.yml for local accounts (or LDAP connection details instead). A trimmed single-container example:

compose.yml
services:
authelia:
image: authelia/authelia:4.39
restart: unless-stopped
volumes:
- ./config:/config
environment:
- TZ=UTC
labels:
traefik.enable: true
traefik.http.routers.authelia.rule: Host(`auth.example.com`)
traefik.http.middlewares.authelia.forwardauth.address: http://authelia:9091/api/authz/forward-auth?authelia_url=https://auth.example.com/
config/configuration.yml
identity_validation:
reset_password:
jwt_secret: a_very_long_random_string
authentication_backend:
file:
path: /config/users_database.yml
session:
secret: another_long_random_string
cookies:
- domain: example.com
authelia_url: https://auth.example.com
default_redirection_url: https://example.com
storage:
encryption_key: a_third_long_random_string
local:
path: /config/db.sqlite3
notifier:
filesystem:
filename: /config/notification.txt
access_control:
default_policy: deny
rules:
- domain: "*.example.com"
policy: one_factor

That is roughly six required sections before the first login works, against Tinyauth’s three environment variables. Authelia is honest about that trade: the storage backend runs fine on SQLite for a home lab, PostgreSQL or MySQL are the documented production recommendation once you have more than one Authelia instance or care about write concurrency. That storage backend is separate from the authentication backend, which is limited to a flat YAML file or LDAP (including Active Directory, FreeIPA, GLAuth, and LLDAP). There is no plain-SQL user store option the way there is for session storage.

2FA options are the deepest of the three tools here: TOTP, WebAuthn/passkeys, and Duo Push, selectable per access-control rule (one_factor vs two_factor). That per-rule granularity, not just per-user, is the feature Tinyauth and Pocket ID both lack, and it is usually the actual reason people reach for Authelia over Tinyauth even when they do not need OIDC yet: “the NAS admin panel needs 2FA, the read-only dashboard does not” is an access-control rule, not a separate deployment.

The LDAP upgrade path is real but it is no longer a reason to pick Authelia over the other two. Swap the authentication_backend block from file to an ldap connection block once you have a directory worth pointing at, and every app that already trusts Authelia keeps working without a label change. Tinyauth gets there with TINYAUTH_LDAP_* env vars and Pocket ID with its LDAP sync, so all three land in the same place. Pick on policy depth instead.

When you outgrow all three

Tinyauth tops out the moment you want your access rules in one auditable place instead of scattered across Docker labels, or you need different routes to demand different second factors. Pocket ID tops out the moment an app in your stack has no OIDC login option, or you need it to also gate apps that only understand forward-auth. Authelia tops out when you need self-service user registration, SCIM provisioning into a dozen apps, a polished admin UI for non-technical household members, or an OIDC provider that is not sitting in an open beta. That is the point where Authentik or Keycloak earns its extra weight, and we already covered that specific matchup, so this is not the place to relitigate it.

For a five-service home lab in 2026, start smaller than you think. Tinyauth if nothing needs OIDC. Pocket ID next to it the day something does. Authelia only if you want one policy file deciding which routes demand a second factor. None of the three needs a database server you have to babysit, and none of them needs its own realm, tenant, or brand config just to say “yes, this cookie is valid, let them through.”

Common Questions

Do I need OIDC or is forward-auth enough?

Forward-auth is enough if every app you are protecting sits behind a reverse proxy and has no built-in login system worth trusting, which covers most dashboards, admin panels, and internal tools. You need real OIDC only when an app (Immich, Gitea, Nextcloud) already has an OIDC login option and you want one identity across all of them instead of separate proxy walls.

Does Pocket ID support password login as a fallback?

No. Pocket ID only supports passwordless authentication through passkeys and WebAuthn, with no password field anywhere in the login flow. Generate and store your account recovery codes during setup, because losing every registered passkey device locks you out of every app behind Pocket ID at once.

Is Authelia’s OIDC provider safe to rely on for a home lab?

Authelia’s OIDC provider works for a home lab, but Authelia’s own documentation still labels OpenID Connect 1.0 provider support as an open beta as of September 2026. Treat it as usable for low-stakes internal apps, keep a non-OIDC login path for anything you cannot afford to lock yourself out of, and expect config changes across future releases.

What happens to my apps if the auth container goes down?

All three fail closed for anything using forward-auth: Traefik or nginx cannot reach the auth endpoint, so protected routes return an error instead of letting traffic through. Apps using Pocket ID or Authelia purely for OIDC login will refuse new logins but usually keep existing sessions alive until the token expires, depending on each app’s own session handling.

Do I need LDAP for a five-service home lab?

No. LDAP earns its complexity once you have a dozen or more users or apps that already expect a directory (like Proxmox or pfSense). For five services and a household of two or three people, a flat users file (Tinyauth, Authelia) or a built-in SQLite user table (Pocket ID) is less to maintain and just as secure at that scale. All three can bind to LDAP later, so the choice is not permanent.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Next Post
Pterodactyl vs Pelican vs Crafty

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts