A simple OpenID Connect provider that signs users in with passkeys. Add single sign-on to your self-hosted apps without passwords. This repo is the Blueprint. The app runs from the official Pocket ID image.
This repository does not contain the Pocket ID source. The service pulls the official image from Docker Hub at a pinned version. To upgrade, change the tag in render.yaml and push.
| Piece | Role |
|---|---|
| Pocket ID | OIDC provider, admin UI, and passkey sign-in in one Go binary. |
pocket-id (Web Service) |
The app. |
pocket-id-db (Render Postgres) |
Users, passkeys, OIDC clients, and uploaded images. |
pocket-id-data (Persistent disk) |
Caches only. It makes Render stop the old instance before starting a new one, which Pocket ID requires. |
flowchart LR
user["Browser"] --> app["pocket-id"]
apps["Your apps (OIDC clients)"] --> app
app --> db[("pocket-id-db")]
app -.->|caches| disk[("pocket-id-data")]
hub["Docker Hub: pocketid/pocket-id:v2.19.2"] --> app
- Click Deploy to Render. The flow copies this repo into your GitHub account and applies
render.yaml. - Render creates the database and generates
ENCRYPTION_KEY. There is nothing to fill in. - Pocket ID creates its tables in Postgres and starts.
- Open
/setupand create the admin account with a passkey.
| Resource | Type | Plan | Notes |
|---|---|---|---|
pocket-id |
Web, image | Starter | Disk at /app/data, 1 GB. Health: /healthz. Single instance. |
pocket-id-db |
Postgres 16 | Basic 256 MB | Internal connections only. Also stores uploads (FILE_BACKEND=database). |
Default region: Oregon.
- Click Deploy to Render above.
- Wait until
pocket-idis Live. - Right away, open
https://<pocket-id>.onrender.com/setupand create the admin account. Until you do, anyone who opens that page can claim it. - In the admin UI, add an OIDC client for each app you want to sign in to.
curl -fsS "https://<pocket-id>.onrender.com/healthz"Important
Passkeys are bound to the domain in APP_URL. If you plan to use a custom domain, add it and update APP_URL before anyone registers a passkey. Passkeys made on the onrender.com domain stop working after the switch.
Pocket ID's export and import commands move all users, passkeys, OIDC clients, and settings. The import replaces the whole database, so skip /setup. Because the data lives in Postgres, you run the import from your old host, straight into pocket-id-db.
-
On the old instance, export:
docker exec pocket-id /app/pocket-id export --path - > pocket-id-export.zip
The Render instance must run the same Pocket ID version as the old one, or a newer one. Check with
/app/pocket-id version. -
On the
pocket-idservice's Environment page:- Set
ENCRYPTION_KEYto the old instance's key. The export keeps secrets encrypted with it. - Set
APP_URLto the old domain, then add that domain under Settings → Custom Domains. Passkeys and OIDC clients only work on the domain they were set up with.
The service may fail to start after this, because its first boot encrypted a new signing key with the generated key. The import replaces that key.
- Set
-
Suspend the
pocket-idservice. If it is running, Render restarts it during the import, and the restarted server writes to the database while the import is replacing it. The import then fails withduplicate key value violates unique constraint. -
On
pocket-id-db, under Info → Access Control, allow your old host's public IP. Copy the External Database URL. -
On the old host, run the import with the same image version:
docker run --rm -i \ -e DB_CONNECTION_STRING='<External Database URL>' \ -e ENCRYPTION_KEY='<old key>' \ -e FILE_BACKEND=database \ pocketid/pocket-id:v2.19.2 \ /app/pocket-id import --path - --yes --forcefully-acquire-lock < pocket-id-export.zip
--forcefully-acquire-lockclears any lock or registration a stopped instance left behind. Each query crosses the internet, so expect a minute or two. Wait forImport completed successfully. -
Resume the service, remove your IP from Access Control, then point the domain's DNS at Render.
If the import fails, keep the service suspended and run it again. The import clears the database first. A resumed service would start on the partial data and generate a new signing key.
| Variable | Source | Description |
|---|---|---|
ENCRYPTION_KEY |
Generated | Encrypts private keys and secrets in the database. Never change it. |
APP_URL |
Wired | The service's RENDER_EXTERNAL_URL. The OIDC issuer and passkey domain. |
DB_CONNECTION_STRING |
Wired | Internal URL of pocket-id-db. |
FILE_BACKEND |
Blueprint | database. Stores uploaded logos and profile pictures in Postgres, so database backups cover everything. |
TRUST_PROXY |
Blueprint | true. Reads client IPs from Render's proxy for audit logs and rate limiting. |
PORT |
Blueprint | 1411. |
ANALYTICS_DISABLED |
Blueprint | true. Turns off the anonymous instance heartbeat. |
MAXMIND_LICENSE_KEY |
Optional | Adds IP geolocation to audit logs. See .env.example. |
generateValue runs once, on the first Blueprint apply. Email (SMTP) is configured in the admin UI, not with env vars.
Prices from Render's pricing page.
| Resource | Approx. monthly |
|---|---|
pocket-id (Starter) |
$7 |
pocket-id-db (Basic 256 MB) |
$6 |
| 1 GB disk | $0.25 |
| Total | ~$13.25 |
| Problem | Solution |
|---|---|
| Passkey sign-in fails after adding a custom domain | Passkeys are bound to the old domain. Users must register new passkeys on the new domain. |
| OIDC clients reject the issuer | APP_URL must match the URL the clients use. Update it after you add a custom domain. |
| First deploy fails to connect to Postgres | The service started before pocket-id-db accepted connections. Wait for the database to be Available and click Manual Deploy. |
Deploy fails with already one instance of Pocket ID running |
The disk is missing. Pocket ID can't overlap with the old instance during a deploy, and the disk makes Render stop the old one first. |
| Deploy takes about a minute of downtime | Expected. A service with a disk stops the old instance before it starts the new one. |
render.yaml Blueprint. One image-backed web service with a disk, and Postgres.
README.md This file
LICENSE Wrapper license
.env.example Optional extras
MIT for this wrapper. Pocket ID is licensed under BSD-2-Clause.