Pocket ID: Passkey-Only Single Sign-On for Your Homelab with Docker Compose

Pocket ID is a small, OpenID Connect certified identity provider that only uses passkeys. Here is how to run v2.19 with Docker Compose, connect your first app and lock down who can sign in.

Share
Pocket ID sign-in page asking the user to authenticate with a passkey
Screenshot: pocket-id/pocket-id repository (BSD-2-Clause)

Every self-hosted app comes with its own login page, its own user list and its own password. After the tenth service that gets old, and it is not great for security either. The classic fix is single sign-on (SSO) with an identity provider, but tools like Keycloak or Authentik can feel like running a second job.

Pocket ID takes a refreshingly simple approach: it is a small OpenID Connect (OIDC) provider that only supports passkeys. No passwords to leak, no TOTP codes to type. You sign in with Face ID, Windows Hello, your phone or a YubiKey, and every connected app trusts that login. In this guide we set up the current release v2.19.1 (22 October 2026) with Docker Compose.

What is Pocket ID?

Pocket ID describes itself as an easy-to-use, OpenID Connect Certified and OAuth 2.0 provider that lets users sign in to your applications with passkeys. It is written in Go with a Svelte frontend, released under the BSD-2-Clause license, and close to 10,000 stars on GitHub.

Pocket ID sign-in page for an app, asking the user to authenticate with a passkey
The Pocket ID sign-in page: one button, one passkey, no password field. Source: pocket-id/pocket-id repository (BSD-2-Clause)

The project is explicit about its goal: Keycloak and ORY Hydra exist, but they are often too complex for simple use cases. Pocket ID is a single program with a built-in SQLite database, so one container is all it needs.

Key features

  • Passkey-only login (WebAuthn), including hardware keys such as YubiKeys
  • Certified OIDC provider, so it works with any app that supports "Login with OpenID Connect"
  • User groups and per-app allowed groups to control who may sign in where
  • App dashboard ("My Apps") so users see the services they can open
  • Audit log of sign-ins with IP address and location
  • LDAP sync and SCIM provisioning for larger setups
  • Community guides for close to a hundred apps, including Immich, Jellyfin, Nextcloud, Proxmox, Paperless-ngx, Vaultwarden and Grafana

What's new in recent releases

Pocket ID ships often. A few highlights from the last months:

  • v2.12.0 (July 2026) included security fixes and a QR code sign-in option. If you are on an older version, update.
  • v2.14.0 (August) added multiple client secrets per OIDC client and RFC 8414 authorization server metadata.
  • v2.17.0 (October) added OIDC back-channel logout and a redesigned client details page.
  • v2.18.0 (October) lets you search and pick client icons from the selfh.st icon library.
  • v2.19.0 (October) added configurable login notifications, an option to force email verification, searchable "My Apps" and the WebAuthn-recommended passkey algorithms. v2.19.1 updated Go and the base images.

Before you start: HTTPS is required

Browsers only allow passkeys on HTTPS pages (or on localhost while testing). Passkeys are also bound to the domain, so pick the final address up front, for example id.example.com, and put a reverse proxy with a valid certificate in front of Pocket ID. Changing the domain later means your users have to register new passkeys.

Installing Pocket ID with Docker Compose

1. Get the files

mkdir -p /opt/pocket-id && cd /opt/pocket-id
curl -O https://raw.githubusercontent.com/pocket-id/pocket-id/v2/docker-compose.yml
curl -o .env https://raw.githubusercontent.com/pocket-id/pocket-id/v2/.env.example

2. Set the URL and encryption key

Pocket ID needs its public address and an encryption key. The example file recommends a file-based key:

openssl rand -base64 32 > encryption_key
chmod 600 encryption_key
# .env
APP_URL=https://id.example.com
TRUST_PROXY=false
PUID=1000
PGID=1000

The Compose file below mounts the key file into the container and sets ENCRYPTION_KEY_FILE to that path. Make sure the user from PUID/PGID can read it (chown 1000:1000 encryption_key). Alternatively you can put ENCRYPTION_KEY= with the generated value directly into .env. Back up this key together with the data folder; without it, encrypted data cannot be read.

3. The Compose file with Traefik labels

services:
  pocket-id:
    image: ghcr.io/pocket-id/pocket-id:v2.19.1
    container_name: pocket-id
    restart: unless-stopped
    env_file: .env
    environment:
      ENCRYPTION_KEY_FILE: /run/secrets/encryption_key
    volumes:
      - ./data:/app/data
      - ./encryption_key:/run/secrets/encryption_key:ro
    networks:
      - traefik
    labels:
      - traefik.enable=true
      - traefik.http.routers.pocket-id.rule=Host(`id.example.com`)
      - traefik.http.routers.pocket-id.entrypoints=websecure
      - traefik.http.routers.pocket-id.tls.certresolver=letsencrypt
      - traefik.http.services.pocket-id.loadbalancer.server.port=1411
    healthcheck:
      test: ["CMD", "/app/pocket-id", "healthcheck"]
      interval: 1m30s
      timeout: 5s
      retries: 2
      start_period: 10s

networks:
  traefik:
    external: true

The upstream file uses the floating v2 tag; pinning a release makes updates a conscious decision. Images are published as ghcr.io/pocket-id/pocket-id and pocketid/pocket-id on Docker Hub.

Not using Traefik? Publish port 1411 and point Caddy or Nginx at it. The docs note that Nginx needs larger proxy buffers for Pocket ID, otherwise some pages fail with 502 Bad Gateway.

docker compose up -d

4. Show Pocket ID the real client IPs

Behind a proxy, every request seems to come from the proxy. Since Pocket ID logs sign-in IPs and rate-limits per IP, tell it which proxy to trust. TRUST_PROXY accepts true/false or a list of networks, for example the Docker network shared with Traefik:

TRUST_PROXY=172.18.0.0/16

Use your actual Docker network range (docker network inspect traefik). Behind Cloudflare, use TRUSTED_PLATFORM=CF-Connecting-IP instead.

5. Create the admin account

Open https://id.example.com/setup, enter username, email and name, and register your first passkey. The setup page only works until the first account exists. If the browser refuses to create a passkey, the page is not on HTTPS or APP_URL does not match the address bar.

Connecting your first app

Every app that signs in with Pocket ID is an OIDC client:

  1. Find the app's callback URL (sometimes called redirect URI) in its OIDC docs.
  2. In Pocket ID go to Administration > OIDC Clients > Add OIDC Client, enter a name and the callback URL. Apps with a backend are confidential clients; mobile, desktop and single-page apps are public clients and use PKCE.
  3. Copy the client ID and client secret. The secret is only shown once.
  4. In the app, enter the issuer https://id.example.com (discovery URL https://id.example.com/.well-known/openid-configuration), the client ID and the secret, with the scopes openid profile email (plus groups if the app maps roles from groups).

A nice real-world example is Gotify 3, which added OIDC login this year and names Pocket ID in its docs. In Pocket ID, add the callbacks https://gotify.example.com/auth/oidc/callback and gotify://oidc/callback and enable PKCE. Gotify then only needs a few variables:

GOTIFY_OIDC_ENABLED=true
GOTIFY_OIDC_ISSUER=https://id.example.com
GOTIFY_OIDC_CLIENTID=<client id>
GOTIFY_OIDC_CLIENTSECRET=<client secret>
GOTIFY_OIDC_REDIRECTURL=https://gotify.example.com/auth/oidc/callback

The Pocket ID docs also have client guides for several tools we covered here, such as Beszel, BookStack, Healthchecks, Karakeep, PatchMon, Termix and Wallos.

Decide who may sign in

Each OIDC client has an Access setting: either all users or only members of selected user groups. Create groups like admins, family and media, and allow only the right groups per app. Users outside those groups get "You are not allowed to access this service", and Pocket ID re-checks the groups on every token refresh, so removing someone from a group also removes their access.

What about apps without OIDC?

Pocket ID only speaks OIDC. For apps without SSO support, put an authentication proxy in front of them that acts as the OIDC client, for example Tinyauth, OAuth2 Proxy, caddy-security or Traefik forward auth. The Pocket ID docs have examples for each.

Tips from practice

  • Register two passkeys per admin (for example phone and a YubiKey). If you lose your only passkey, follow the account recovery page in the docs.
  • Back up data/ and the encryption key. That is your whole identity provider.
  • Don't put the SQLite database on a network share. Recent versions even warn about that in the admin UI.
  • Air-gapped? VERSION_CHECK_DISABLED=true, ANALYTICS_DISABLED=true and ICON_LIBRARY_URL=disabled turn off the outgoing requests.
  • Keep it updated and watch it with Uptime Kuma. If your identity provider is down, nobody can log in anywhere.

Pocket ID vs Authentik, Authelia and Keycloak

Pocket ID Authentik Authelia Keycloak
Focus Simple OIDC provider Full identity platform Forward-auth portal + OIDC Enterprise IAM
Login Passkeys only Passwords, MFA, passkeys, flows Passwords + 2FA (TOTP, WebAuthn) Everything
Footprint One small container, SQLite Server, worker, PostgreSQL Small, file or DB config Java app + database
Learning curve Low High Medium High

If you want passwordless SSO for a handful of apps and don't need complex flows, Pocket ID is the easiest option by far. If you need password logins, LDAP as a source of truth or detailed policies, look at Authentik or Keycloak.

Conclusion

Pocket ID turns SSO from a weekend project into a 15-minute job. Passkeys make the login both easier and safer than passwords, and the OIDC certification means most self-hosted apps just work. Start with one or two apps, add your family as users, and enjoy never typing a homelab password again.

Sources