Deploy Pocket ID — Passkey-Only OIDC Provider
Self-host Pocket ID — passwordless passkey SSO for your apps
Pocket ID
Just deployed
/app/data
Deploy and Host Pocket ID on Railway
Pocket ID is a simple, self-hosted OIDC provider built entirely around passkeys — no passwords, no TOTP codes, no external dependencies. Add single sign-on to your self-hosted services and sign in everywhere with a WebAuthn passkey or a hardware key like a YubiKey. It's the lightweight, passwordless alternative to heavier identity providers like Keycloak and Authentik, designed for people who want one modern login across their apps without running a complex platform. This template deploys it as a single container with the WebAuthn-critical settings handled.
What This Template Deploys
| Service | Purpose |
|---|---|
| Pocket ID | The passkey-based OIDC provider, admin UI, and API on port 1411 |
A single, lightweight Go container backed by SQLite — no external database to manage. Everything (the database, uploads, and GeoLite2 IP data) lives under one /app/data volume, so the whole identity provider is one service with one volume.
About Hosting
Pocket ID is genuinely simple to run, but passkeys make one setting unusually strict — get it right and there's almost nothing else to configure.
APP_URL must exactly match your browser-facing URL — passkeys depend on it. Pocket ID puts APP_URL in three places: the OIDC issuer, the cookie domain, and — critically — the WebAuthn relying-party ID. Passkeys are cryptographically bound to that relying-party domain, so if APP_URL doesn't exactly match the URL users visit, passkeys won't register or verify at all, and because there are no passwords, there's no fallback. Set it to your exact Railway public domain before you run the first-time setup, and don't change it afterward.
TRUST_PROXY=true is required on Railway, and HTTPS is mandatory. WebAuthn only works in a secure (HTTPS) context, which Railway provides automatically. But Pocket ID runs behind Railway's proxy, so TRUST_PROXY=true must be set or it misreads the request protocol and origin, and passkey checks fail. This template sets it.
Claim your admin passkey immediately — the setup page is first-come. On first deploy, visit /setup and register your admin passkey right away. Whoever reaches /setup first becomes the administrator, so do this the moment the service is live, before the URL is exposed to anyone else.
ENCRYPTION_KEY protects sensitive data — pin it. Generate it with openssl rand -base64 32 (at least 16 bytes), set it once, and keep it stable. Combined with the /app/data volume, that keeps your identity provider intact across redeploys.
Typical cost: ~$5/month on Railway for the single lightweight container. Pocket ID is open source and free, and OpenID Connect Certified.
How It Compares
| Pocket ID (passkey-only) | Authentik / Keycloak | Auth0 / Okta | Password + TOTP apps | |
|---|---|---|---|---|
| Authentication | Passkeys only (WebAuthn) | Many methods | Many methods | Password + 2FA |
| Complexity | Very low | High | Managed | Varies |
| Passwordless | Fully | Optional | Optional | No |
| Dependencies | SQLite only | Postgres (+Redis) | Vendor | Varies |
| Best for | Homelab / simple SSO | Enterprise IdP | Managed SSO | Legacy auth |
| Self-hostable | Yes | Yes | No | Varies |
Authentik and Keycloak are powerful but heavy for a handful of services. Auth0 and Okta are managed and per-user priced. Pocket ID's niche is deliberate simplicity: a single-container, passkey-only OIDC provider that gives your self-hosted apps modern passwordless SSO without the operational weight — ideal for homelabs and small teams that just want one secure login.
Deploy in Under 5 Minutes
- Click Deploy on Railway — Pocket ID builds automatically (~2 minutes)
- Set
APP_URLto your exact Railway public domain (this is critical for passkeys) - Confirm
TRUST_PROXY=true,ENCRYPTION_KEYis set, and the/app/datavolume is mounted - Immediately visit
/setupand register your admin passkey before anyone else can - In the admin UI, add OIDC clients for your services and point each app at Pocket ID's discovery URL
Each connected app gets an OIDC client with callback URLs; your users then sign in with a passkey.
Common Use Cases
- Unified homelab SSO — one passkey login across Grafana, Immich, Nextcloud, Gitea, Proxmox, and other OIDC apps
- Passwordless authentication — eliminate passwords entirely using WebAuthn passkeys and hardware keys
- YubiKey / hardware-key login — sign in to all your services with a physical security key
- Simple SSO without the weight — get single sign-on without running Keycloak or a multi-service identity platform
- Protect apps with no native auth — pair with a proxy like OAuth2-Proxy to gate services that lack OIDC support
Configuration
| Variable | Required | Description |
|---|---|---|
APP_URL | Required | Your exact Railway public domain — used for OIDC issuer, cookies, and the WebAuthn relying party |
ENCRYPTION_KEY | Required | Encrypts sensitive data — openssl rand -base64 32 (≥16 bytes), keep stable |
TRUST_PROXY | Required | true — required behind Railway's HTTPS proxy for correct origin handling |
MAXMIND_LICENSE_KEY | Optional | For GeoLite2 IP data used in login location display |
PUID / PGID | Optional | User/group IDs for the data volume if running non-root |
APP_URLmust match exactly, or passkeys break. It's baked into the WebAuthn relying-party ID, so a mismatch means passkeys won't register or verify — with no password fallback. Set it to your real Railway domain before first setup and keep it fixed.
Set
TRUST_PROXY=trueand claim/setupfirst. WebAuthn needs the correct HTTPS origin, which requires trusting Railway's proxy. And whoever visits/setupfirst becomes admin — register your admin passkey the moment the service is live.
Dependencies for Pocket ID Hosting
- Railway account — Hobby plan (~$5/month) runs the single container
- A persistent Railway volume mounted at
/app/data(included) - A passkey-capable device or hardware key (YubiKey, phone, laptop biometrics)
- OIDC-compatible apps to connect (most self-hosted services support OIDC)
Deployment Dependencies
- Pocket ID GitHub Repository
- Pocket ID Documentation
- Environment Variables Reference
- Railway Volumes Documentation
Implementation Details
The template runs ghcr.io/pocket-id/pocket-id on port 1411 with a persistent volume at /app/data, which holds the SQLite database, uploads, and the GeoLite2 IP database — no external database is required. The app is a single Go binary, and Railway provides the automatic HTTPS that WebAuthn mandates.
APP_URL is the linchpin: Pocket ID writes it into the OIDC issuer identifier, the cookie domain, and the WebAuthn relying-party ID. Because passkeys are bound to the relying-party domain, APP_URL must exactly match the URL users load, or registration and verification fail with no password fallback. TRUST_PROXY=true is set so Pocket ID correctly derives the external HTTPS origin from Railway's proxy headers, and ENCRYPTION_KEY encrypts sensitive stored data and must remain stable across redeploys.
Administration is passkey-secured from the start: the first visitor to /setup registers the admin passkey and claims the instance, after which OIDC clients are added through the admin UI — each with a launch URL and callback URLs — and connected services authenticate against Pocket ID's OIDC discovery endpoint.
Frequently Asked Questions
Why must APP_URL be exact? It's used as the WebAuthn relying-party ID, and passkeys are cryptographically bound to that domain. If APP_URL doesn't match the URL users visit, passkeys won't register or verify — and since there are no passwords, there's no way in. Set it correctly before first setup.
What if I lose access to my passkey? Register more than one passkey (e.g. a hardware key plus a device) so you have a backup. Since Pocket ID is passwordless, keeping a second passkey enrolled is the recovery path.
Do I need a database? No external one. Pocket ID uses SQLite stored on the /app/data volume, so it's a single container with a single volume — part of what makes it so simple.
Why TRUST_PROXY=true? Railway terminates HTTPS with a proxy in front of Pocket ID. WebAuthn needs the correct secure origin, so Pocket ID must trust the proxy's forwarded headers, or passkey checks fail.
How do I connect my apps? In the admin UI, add an OIDC client for each app with its callback URLs, then point the app at Pocket ID's OIDC discovery URL. Most self-hosted services (Grafana, Immich, Nextcloud, and more) support OIDC.
Can I protect apps that don't support OIDC? Yes, by pairing Pocket ID with an auth proxy like OAuth2-Proxy, which authenticates against Pocket ID and gates services that have no native OIDC support.
Is it really passwordless? Completely. Pocket ID supports only passkeys — WebAuthn/FIDO2 — so there are no passwords to store, phish, or leak. That's the entire point.
Why Deploy Pocket ID on Railway?
Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.
By deploying Pocket ID on Railway you get modern passwordless SSO with the tricky parts solved — APP_URL and TRUST_PROXY set for WebAuthn, the /app/data volume for persistence, and automatic HTTPS. One passkey login across all your self-hosted services, on infrastructure you own.
Template Content
Pocket ID
ghcr.io/pocket-id/pocket-id
