Pocket ID is an OpenID Connect provider where the only way to sign in is a passkey; there is no password, anywhere. It replaces Google or Microsoft sign-in for the self-hosted apps that already speak OIDC.
Our verdict on Pocket ID#
Pocket ID is alive and moving: v2.14.0 landed in August 2026 after roughly monthly minors through the year, and it passed the OpenID Foundation's conformance suite in 2026. Read its exclusions as the product rather than as gaps. There is no password login, no SAML, no LDAP server (only inbound LDAP sync), no forward auth and no proxy outpost. That last one is the decision that matters: Pocket ID signs users into apps that already speak OIDC and does precisely nothing for apps that do not, so anything without an OIDC client still needs oauth2-proxy or similar in front of it. The passkey-only stance costs you two concrete things. WebAuthn credentials are bound to your domain, so moving from id.home.lan to id.example.com kills every passkey your users registered. And WebAuthn requires a secure context, so a plain HTTP deployment on a LAN address cannot register a credential at all.
Who Pocket ID is for#
Choose it if
- You have five to fifteen apps that all speak OIDC (Immich, Grafana, Forgejo, Nextcloud, Paperless) and want one login across them.
- Your users already have synced passkeys through iCloud Keychain, Bitwarden or 1Password, so a new device costs nothing to enroll.
- Anyone who tried authentik, watched it take a gigabyte, and only wanted the OIDC issuer part.
- Small groups where an admin handing out a one-time link per new person is a reasonable onboarding process.
Look elsewhere if
- Some of your users will not or cannot use passkeys. There is no password fallback and that is deliberate.
- You need to protect apps with no OIDC support. There is no forward auth, no proxy provider and no header injection here.
- You need SAML, or a directory that other systems can bind against over LDAP. Pocket ID consumes LDAP, it does not serve it.
What running it actually looks like#
One container, one volume, one port. Pull ghcr.io/pocket-id/pocket-id:v2, mount a data directory, set APP_URL to the exact external HTTPS URL, and generate ENCRYPTION_KEY with openssl rand -base64 32. It listens on 1411. TRUST_PROXY defaults to false, so set it to your reverse proxy's IP or CIDR before you look at the audit log and find every login coming from the proxy. TLS is not optional: WebAuthn needs a secure context, so Caddy or nginx with a real certificate has to be in front before the first passkey can be registered. First run is /setup, a one-shot page that creates the admin account and then closes itself. Upgrades are a tag bump with migrations at start; v2.11.0 specifically fixed PostgreSQL migration bugs, so read the notes if you are on Postgres.
Resource profile#
- Memory
- 30 MB idle, around 60 MB in ordinary use. One Go binary serving a static SvelteKit bundle, with SQLite in-process. Estimated from typical installs; the project publishes no figure, and PostgreSQL instead of SQLite changes almost nothing.
- CPU and acceleration
- Negligible. WebAuthn assertion verification is a couple of elliptic curve operations per login, and there is no password hashing to pay for at all.
- Storage growth
- The database is kilobytes per user: credentials, clients and sessions. `data/uploads` holds the logo and background you uploaded. Nothing here grows on its own.
- Operational load
- 1 of 5, Set and forget. One container, one volume, SQLite by default, migrations that run at start, backup that is a file copy of a stopped instance. The only recurring cost is that releases land roughly monthly, so you read notes more often than you would like.
Figures describe a small single-household install and are the working assumptions behind our stack planner. Your numbers will differ with library size, user count and hardware. See methodology.
Data and backup surface#
The data/ directory plus the ENCRYPTION_KEY, kept apart from each other. data/pocket-id.db is the SQLite database holding users, OIDC clients and their secrets, registered passkey credentials and sessions; data/uploads holds the logo and background images. The token signing keys are stored encrypted in the database under ENCRYPTION_KEY, so a database backup without that key is not usefully restorable: put the key in your password manager, not only in the .env file sitting next to the volume. Stop the container or use the sqlite3 .backup command rather than copying the file while it is open, and remove any stale -wal file before restoring. On PostgreSQL it is pg_dump, the uploads directory, and the key.
Derived backup shape
SQLite backup. sqlite3 <db> ".backup out.db" while running, or stop the container and copy. A live cp of a WAL-mode SQLite file can restore corrupt.
Traps and surprises#
Passkeys are welded to your domain
WebAuthn credentials are scoped to a relying party ID derived from the host in APP_URL. Change that hostname and every registered passkey stops working, for everyone, at once. Recovery is an admin-issued one-time access link per user, followed by re-enrollment. Decide on the final domain before you invite anyone, and do not prototype on pocket-id.lan intending to move to a real domain later.
No HTTPS, no passkeys
The WebAuthn API only runs in a secure context. Point a browser at http://192.168.1.20:1411 and the login page renders fine, then registration fails with a browser error that never mentions TLS. There is no configuration flag that relaxes this because it is enforced by the browser, not by Pocket ID. Put a real certificate in front before your first login attempt.
The one-time access link is your only way back in
There is no password to fall back on. If the only admin loses every enrolled passkey (phone replaced, browser profile wiped, no synced provider), you are generating a one-time link from the host or editing the database directly. Create a second admin account with a passkey on different hardware, or a hardware security key kept in a drawer, on the day you install it.
UI_CONFIG_DISABLED makes the settings page read-only
Set UI_CONFIG_DISABLED=true and Pocket ID takes APP_NAME, SESSION_DURATION, ALLOW_USER_SIGNUPS and the rest from environment variables, and the settings UI stops accepting changes. It is the right choice for a declaratively managed instance, and it is confusing six months later when someone tries to change the app name in the UI and the save button does nothing.
TRUST_PROXY is off by default
Out of the box Pocket ID does not trust X-Forwarded-For, so behind a reverse proxy every request appears to come from the proxy's address. Rate limiting then counts all your users as one client and the audit log records a single IP for everything. Set TRUST_PROXY to the proxy address or CIDR (CIDR support arrived in v2.11.0), or use TRUSTED_PLATFORM if you are behind Cloudflare.
Specifications#
- Category
- Passwords, SSO and identity
- Licence
- BSD-2-Clause (Permissive)
- Written in
- Go / SvelteKit
- First release
- 2024
- Maturity
- Young
- Datastore
- SQLite (default) or PostgreSQL
- Default ports
1411- Ships as
- docker, native binary, helm chart, nixos module
- arm64 builds
- Yes
- Identity
- Native OIDC. It is the OIDC issuer, OpenID Certified, and it can sync users and groups inward from an LDAP directory. There is no SAML, no upstream social login and no password login.
- Replaces
- Okta, Auth0, Google Sign-In, Microsoft Entra ID
- Project site
- pocket-id.org
- Source
- github.com/pocket-id/pocket-id
- Documentation
- pocket-id.org/docs/introduction
Alternatives to Pocket ID#
Everything else in passwords, sso and identity, closest in operational weight first.
| Tool | Ops load | Idle RAM | Licence |
|---|---|---|---|
| LLDAP | 1, Set and forget | 15 MB | GPL-3.0 |
| Vaultwarden | 2, Light | 40 MB | AGPL-3.0 |
| Authelia | 3, Moderate | 35 MB | Apache-2.0 |
| Keycloak | 4, Heavy | 750 MB | Apache-2.0 |
| authentik | 4, Heavy | 1000 MB | MIT (core); authentik/enterprise/ under a separate Authentik Security license |
Where Pocket ID comes up elsewhere#
Head to head
Research
Sources#
Facts on this page were checked against the project's own material. Where a number is our own estimate rather than a published figure, the text says so.
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.