Why self-host your OIDC provider
Every self-hosted service that manages its own user base introduces a dependency you have to maintain: passwords to reset, accounts to revoke, sessions to invalidate. Multiplied across five or ten services, that becomes a real operational burden. An identity provider (IdP) centralises this work: a single source of identity, revocation that applies everywhere, a single MFA configuration point.
The question is not whether you need an IdP, but which one — and on what foundation you are willing to operate it.
What you get with a self-hosted OIDC IdP
- Centralised identity — a single account for Gitea, Nextcloud, Grafana and all OIDC-compatible services.
- Global revocation — disabling an account in Pocket ID cuts access to all connected applications at the next token renewal.
- Passkey authentication — Touch ID, Face ID or a FIDO2 key as the sole factor, with no password to transmit.
- No cloud dependency — your identities stay under your jurisdiction, with no per-user quota or per-action pricing.
- Minimal footprint — Pocket ID consumes less than 50 MB of RAM at idle, versus 300–600 MB for Authentik and 1–2 GB for Keycloak.
- Embedded SQLite — no external database engine to provision or back up separately.
- Back-channel logout (v2.18.0) — when a user is disabled or revokes a client, Pocket ID notifies registered applications and invalidates their server-side sessions.
- Single container — the entire service fits in a ~30 MB Docker image, with no Redis dependency and no separate worker.
Concrete prerequisites
Pocket ID is designed for small stacks. Its requirements are intentionally low:
- Docker 24+ and Docker Compose v2 — required by the official image.
- 512 MB RAM minimum on the VPS — Pocket ID runs under 50 MB at idle; the rest serves the OS and the reverse proxy.
- A domain and a TLS certificate — OIDC requires HTTPS without exception; without a publicly reachable domain (or an internal network with TLS), the installation cannot function.
- No external database — SQLite is embedded. If you prefer PostgreSQL for simpler backups, switching is done by changing a single environment variable.
Pocket ID does not handle SAML, LDAP, or directory federation. If your stack includes applications that only accept SAML, or if you need to federate an Active Directory, use Authentik or Keycloak instead. For a stack of modern web services that support OIDC, Pocket ID covers the need without the overhead.
Installing Pocket ID
Prepare the VPS and domain
Order a VPS running Ubuntu 22.04 or Debian 12 with at least 512 MB of RAM. Create a DNS A record pointing
auth.your-domain.comto your VPS IP address. This subdomain is the public URL of your Pocket ID instance — it must be reachable before the first configuration, as OIDC redirects depend on it.Install Docker and the Compose plugin:
curl -fsSL https://get.docker.com | shCreate the working directory
On the server, create a dedicated directory:
mkdir -p /opt/pocket-id && cd /opt/pocket-idWrite the docker-compose.yml file
Create
/opt/pocket-id/docker-compose.yml:services: pocket-id: image: ghcr.io/pocket-id/pocket-id:v2.18.0 container_name: pocket-id restart: unless-stopped ports: - "1411:1411" volumes: - ./data:/app/data environment: APP_URL: "https://auth.your-domain.com" TRUST_PROXY: "true"APP_URLmust exactly match the public URL of your instance, including thehttps://scheme.TRUST_PROXY: "true"tells Pocket ID to trust theX-Forwarded-*headers forwarded by the reverse proxy.Configure the nginx reverse proxy
Create the nginx vhost for
auth.your-domain.com. Pocket ID listens on HTTP on port 1411; the reverse proxy handles TLS.server { listen 443 ssl http2; server_name auth.your-domain.com; ssl_certificate /etc/letsencrypt/live/auth.your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/auth.your-domain.com/privkey.pem; proxy_buffer_size 256k; proxy_buffers 4 512k; proxy_busy_buffers_size 512k; location / { proxy_pass http://127.0.0.1:1411; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }Obtain the certificate with Certbot before reloading nginx:
certbot certonly --nginx -d auth.your-domain.com nginx -t && systemctl reload nginxStart the container
From
/opt/pocket-id:docker compose up -d docker compose logs -f pocket-idThe log line
Starting Pocket ID on :1411confirms the server is ready. Visithttps://auth.your-domain.comto complete the initial setup.Create the admin account and register a passkey
On the first visit, Pocket ID prompts you to create the administrator account. Enter your email, then immediately register a passkey: go to Settings → Passkeys → Add and follow your browser's prompt (Touch ID, Windows Hello, or a FIDO2 key). Once the passkey is registered, it is the only factor required for subsequent logins.
Register your first OIDC application
In the administration interface, open OIDC Clients → New client. Fill in:
- Name: a readable identifier for the application (e.g.Gitea)
- Callback URL: the redirect URL declared in the client application (e.g.https://git.your-domain.com/user/oauth2/pocket-id/callback)Pocket ID v2.18.0 automatically creates a
client_secretat creation time — retrieve it immediately, as it will not be displayed in plain text again.Note the
Client IDand the discovery URLhttps://auth.your-domain.com/.well-known/openid-configuration: these are the two values to enter in the client application.Test the flow from the client application
In Gitea, go to Administration → Authentication Sources → Add → OAuth2. Select
OpenID Connectas the provider, fill in theClient ID,Client Secretand the discovery URL. Trigger a test login: the browser should be redirected toauth.your-domain.com, prompt for the registered passkey, and then redirect back to Gitea with the user logged in.
Post-installation configuration
After the initial setup, a few settings improve the robustness of the instance.
Hardening the instance
Restrict registrations: by default, Pocket ID allows any visitor to create an account. In production, disable public registration under Settings → General → Allow registration and invite users individually.
Back up the data volume: all persistence (SQLite database, OIDC signing keys) lives in ./data. A daily rsync to an external storage is sufficient for a small instance. If you migrate to PostgreSQL, add DB_PROVIDER=postgresql and DB_CONNECTION_STRING=... to your Compose file.
Pin the image version: the example above explicitly uses v2.18.0. Do not leave latest in production — an automatic update can introduce a schema migration that your backup has not yet covered.
Limit network exposure: if Pocket ID only needs to be accessible from the VPS internal network, bind port 1411 to the loopback interface and let nginx communicate with the container via the internal Docker network.
Troubleshooting
The most common errors when setting up Pocket ID follow well-identified patterns.
Common errors and solutions
- 502 Bad Gateway on all pages — most common cause: nginx buffers are too small for Pocket ID's OIDC response headers. Add
proxy_buffer_size 256k,proxy_buffers 4 512kandproxy_busy_buffers_size 512kin yourlocation /block and reload nginx. redirect_uri_mismatchduring login — the Callback URL declared in Pocket ID does not exactly match the one sent by the client application. Check for a trailing slash,httpvshttpsscheme, and the port number. The match must be exact, character for character.invalid_clientat code exchange — theclient_secretsent by the application does not match. In v2.18.0, Pocket ID creates a secret automatically at client creation; if you did not save it, regenerate one from the client record in the administration panel.- Passkey rejected:
NotAllowedError— your browser cannot access WebAuthn APIs without HTTPS. Verify thatAPP_URLis set tohttps://...and that the TLS certificate is valid (not self-signed, not expired); Certbot must have issued a certificate for the correct domain. - Sessions are not invalidated after revocation — back-channel logout requires the client application to declare a
Backchannel Logout URLin its OIDC record. Without this URL, Pocket ID cannot notify the application; existing sessions will expire at their natural lifetime. Verify the field is filled in and reachable from the Pocket ID container.
Pocket ID, Authentik, Keycloak, Authelia: which to choose
Scroll the table
| Criterion | Pocket ID 2.18 | Authelia 4.39 | Authentik 2026.8 | Keycloak 26 |
|---|---|---|---|---|
| IdP protocols | OIDC/OAuth2 | OIDC/OAuth2 (beta) | OIDC, SAML 2.0, LDAP, SCIM | OIDC, SAML 2.0, LDAP, Kerberos |
| Authentication | Passkeys only (FIDO2) | Password + MFA, passkeys | Password + MFA, passkeys | Password + MFA, OTP |
| RAM at idle | < 50 MB | ~30 MB | 200–400 MB | 1–2 GB |
| Database | Embedded SQLite (or PostgreSQL) | YAML file (or external backend) | PostgreSQL required | PostgreSQL recommended |
| Number of containers | 1 | 1 | 3 (server, worker, PostgreSQL) | 2 (Keycloak + database) |
| User management | Built-in directory, email invitation | YAML file or external provider | Built-in directory, LDAP, social proxy | Built-in directory, LDAP/AD federation |
| Best suited for | Lightweight stack, passkeys, simple IdP | Forward-auth layer in front of a proxy | Full SSO, SAML, visual flows | Enterprise SSO, multi-tenant, compliance |
Conclusion
Pocket ID v2.18.0 answers a specific need: centralising authentication for a self-hosted service stack without the operational cost of Keycloak or Authentik. One container, SQLite, passkeys, and under 50 MB of RAM at idle. Version 2.18.0 adds back-channel logout to invalidate application sessions as soon as an account is revoked, and restores automatic client secret creation to simplify application registration.
If your stack includes applications that only accept SAML, or if you need to federate an Active Directory, Authentik or Keycloak are better suited. For everything else — modern web services on a 1 or 2 GB VPS — Pocket ID leaves the RAM for other things.
See also our guides on Authentik and Keycloak to choose with full information.