Skip to content

How to Deploy Pocket ID on a VPS

Updated Sep 2026

verified on Ubuntu 26.04 · Sep 2026
We earn commissions when you shop through the links below. Full disclosure →

Self-host Pocket ID, a small passkey-only OIDC provider, on a VPS. It runs as one container with SQLite, with the encryption key generated up front and HTTPS through Caddy, which passkeys require.

Before you start
  • A small VPS; Pocket ID idles at about 14 MB of RAM
  • A fresh Ubuntu 24.04 or 26.04 server with root/sudo SSH access
  • A domain you can point at the server, such as id.example.com; passkeys only work over HTTPS
  • A device with passkey support (a phone, a password manager or a security key)
  • Docker Engine + Compose installed (see the base guide below)
Need a box for this guide? Kamatera's free tier lets you spin one up now.Start free on Kamatera → (opens in new tab)

What Pocket ID is

Pocket ID is a small OpenID Connect provider. Users sign in with passkeys only. There are no passwords to set, reset or leak. You register Pocket ID as the OIDC provider in apps that support "Sign in with OIDC", and your users authenticate with a fingerprint, a phone or a hardware key. It is written in Go with a SvelteKit frontend and licensed BSD-2-Clause.

It is built for small self-hosted setups: a homelab, a family, a small team. It has user and group management, per-client access restrictions, and an audit log. It doesn't federate LDAP or speak SAML, and it doesn't try to be Keycloak. If you need a login wall in front of apps that have no OIDC support, you need a forward-auth tool instead, such as Tinyauth or Authelia. Pocket ID vs Authelia and Authentik vs Pocket ID cover the trade-offs.

Server sizing

Measured idle on our test box, Pocket ID used about 14 MB of RAM and about 110 MB of disk for the image. The catalog's conservative floor is 256 MB RAM. Any entry-level VPS works, and Pocket ID can easily share a box with the apps that use it. The SQLite database stays small: it holds users, passkey public keys, OIDC clients and the audit log.

Prepare the server

This guide assumes Docker Engine, the Compose plugin and a ufw firewall are set up. If they aren't, work through Docker & Compose on Ubuntu. Open only SSH and the reverse-proxy ports:

sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw status verbose

Create an A record for id.example.com pointing at the server.

Where to host itaffiliate disclosure
Hetzner Cloudrun it on
2 vCPU · 4 GB RAM · 80 GB SSD · $23.59/mo
Get Hetzner Cloud (opens in new tab)
Kamaterafree trial
1 vCPU · 1 GB RAM · 20 GB SSD · $4.00/mo
Start free on Kamatera → (opens in new tab)
DigitalOceanalso works on
1 vCPU · 1 GB RAM · 25 GB SSD · $6.00/mo
Deploy on DigitalOcean → (opens in new tab)

Paid link — we earn a commission if you shop through it.

Install Pocket ID (Docker Compose)

Pocket ID publishes an official compose file and an environment template. Start from those:

mkdir -p ~/pocket-id && cd ~/pocket-id
curl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/pocket-id/pocket-id/main/docker-compose.yml
[ -f .env ] || curl -fsSL -o .env https://raw.githubusercontent.com/pocket-id/pocket-id/main/.env.example
cat docker-compose.yml

The compose file runs pocketid/pocket-id:v2, stores everything in ./data, and has a built-in health check. Change two things before the first start.

1. The environment. The template leaves ENCRYPTION_KEY empty, and Pocket ID v2 refuses to start without one. The key encrypts sensitive data stored by Pocket ID, so generate it once and keep it. APP_URL must be the public HTTPS address. TRUST_PROXY=true lets Pocket ID read the client IP and scheme from the forwarded headers. Only set it when, as below, nothing except the reverse proxy can reach the container.

grep -q '^ENCRYPTION_KEY=.\+' .env || sed -i "s|^ENCRYPTION_KEY=.*|ENCRYPTION_KEY=$(openssl rand -base64 32)|" .env
sed -i \
  -e 's|^APP_URL=.*|APP_URL=https://id.example.com|' \
  -e 's|^TRUST_PROXY=.*|TRUST_PROXY=true|' \
  .env
chmod 600 .env
grep -E '^(APP_URL|TRUST_PROXY|PUID|PGID)=' .env

The grep -q guard means a re-run never replaces a key that is already in use.

2. The port. Upstream publishes port 1411 on every interface. Bind it to the loopback interface so that Caddy is the only way in:

sed -i 's|^\(\s*\)- 1411:1411|\1- "127.0.0.1:1411:1411"|' docker-compose.yml
grep -n 1411 docker-compose.yml

Start it and check the health endpoint:

docker compose up -d
timeout 90 bash -c 'until curl -fsS -o /dev/null http://127.0.0.1:1411/healthz; do sleep 3; done' && echo "healthz OK"
docker compose ps

HTTPS + domain via Caddy

HTTPS is required, not just recommended. Browsers only offer WebAuthn, the API behind passkeys, on secure origins, so a Pocket ID served over plain HTTP on a public IP can't register or use a passkey. Install Caddy as described in Automatic HTTPS with Caddy and add:

id.example.com {
    reverse_proxy 127.0.0.1:1411
}

The hostname in the Caddyfile, APP_URL in .env, and the address you open in the browser must all be the same. A passkey is bound to the domain it was created on, so passkeys registered on one hostname don't work on another.

If Caddy runs as a container, put it on the same Docker network and use reverse_proxy pocket-id:1411.

First-run setup

Open https://id.example.com/setup. This one-time page creates the first admin account: enter your name and email, then register a passkey when the browser asks. After that, /setup is closed and you sign in at the root URL.

Then, in the admin area:

  1. Add users and groups. Invite users by email (needs SMTP) or create a one-time login link to send them. They register their own passkey when they first sign in.
  2. Create an OIDC client for each app. Pocket ID shows the client ID, the client secret and the discovery URL, https://id.example.com/.well-known/openid-configuration, which most apps ask for.
  3. Restrict the client to groups if not every user should reach every app.

Securing it

  • Register at least two passkeys for the admin account, for example a phone and a hardware key. There is no password to fall back on, so a single lost device means you are locked out.
  • Keep the port on the loopback interface while TRUST_PROXY=true is set. A directly reachable container would accept forged forwarded headers.
  • Configure SMTP under the application settings. Pocket ID can then email sign-in alerts and one-time access codes.
  • Keep .env private. It holds ENCRYPTION_KEY. If you lose it, the data Pocket ID encrypted with it can't be recovered, and anyone who has it can decrypt that data from a stolen backup.

Backups

Everything lives in ./data: the SQLite database pocket-id.db and uploads/ with logos and profile pictures. .env holds the encryption key. The container writes ./data as UID 1000, which may not be your login user, so the archive is created with sudo. Stop the container for a clean copy of the database. It is back up within seconds:

cd ~/pocket-id
docker compose stop
sudo tar czf pocket-id-backup-$(date +%F).tar.gz data .env docker-compose.yml
docker compose start
sudo chown "$USER": pocket-id-backup-*.tar.gz
tar tzf pocket-id-backup-$(date +%F).tar.gz | grep -c pocket-id.db

The archive contains the encryption key. Encrypt it (for example with gpg --symmetric) before you copy it off the server.

Upgrades

The v2 tag follows the latest Pocket ID 2.x release, so upgrading is a pull:

cd ~/pocket-id
docker compose pull
docker compose up -d

Back up first. Pocket ID migrates its database on startup, and an older version refuses to run against a newer schema unless you explicitly set ALLOW_DOWNGRADE. Read the changelog before you move to a new major version (a new image tag such as v3).

Troubleshooting

The container exits right after starting. Run docker compose logs pocket-id. The usual cause is an empty or missing ENCRYPTION_KEY, or a ./data folder the container can't write to. Upstream's compose sets PUID/PGID to 1000. If the files belong to another user, fix it with sudo chown -R 1000:1000 data.

The browser never offers to create a passkey. You're not on HTTPS, or the hostname differs from APP_URL. Open the exact https:// URL from .env.

An app rejects the login with a redirect or callback error. The app's callback URL must match one registered on the OIDC client in Pocket ID character for character, including the scheme and any trailing path.

Audit log shows the proxy's IP for every sign-in. TRUST_PROXY isn't enabled, or the proxy doesn't send X-Forwarded-For. Caddy sends it by default.

Verification + next steps

You're done when:

  • https://id.example.com loads with a valid certificate;
  • you can sign in with a passkey from two different devices;
  • https://id.example.com/.well-known/openid-configuration returns the discovery document;
  • an encrypted backup of data and .env is stored off the server.

Next, connect your first app as an OIDC client. Many self-hosted apps have a generic OIDC login option. For apps that don't, put a forward-auth layer such as Tinyauth in front of them. Tinyauth can itself use Pocket ID as its OAuth provider.

Next steps

How to self-host Pocket ID →More self-hosted sso & identity tools →Best VPS for Keycloak →Automatic HTTPS with Caddy →Run Claude Code with Ollama on Your Own VPS →Deploy Coolify on a VPS →How to Deploy Actual Budget on a VPS →How to Deploy AnythingLLM on a VPS →How to Deploy Appwrite on a VPS →How to Deploy Audiobookshelf on a VPS →How to Deploy Authelia on a VPS →How to Deploy authentik on a VPS →How to Deploy Baserow on a VPS →How to Deploy Beszel on a VPS →How to Deploy Bitwarden on a VPS →How to Deploy BookStack on a VPS →How to Deploy CapRover on a VPS →How to Deploy Checkmate on a VPS →How to Deploy Directus on a VPS →How to Deploy docker-mailserver on a VPS →How to Deploy Docmost on a VPS →How to Deploy Dokku on a VPS →How to Deploy Dokploy on a VPS →How to Deploy Firefly III on a VPS →How to Deploy Forgejo on a VPS →How to Deploy Gatus on a VPS →How to Deploy Ghostfolio on a VPS →How to Deploy Gitea on a VPS →How to Deploy GitLab on a VPS →How to Deploy GlitchTip on a VPS →How to Deploy Grafana on a VPS →How to Deploy Graylog on a VPS →How to Deploy Headscale on a VPS →How to Deploy Healthchecks on a VPS →How to Deploy Home Assistant on a VPS →How to Deploy Immich on a VPS →How to Deploy Jan on a VPS →How to Deploy Jellyfin on a VPS →How to Deploy Karakeep on a VPS →How to Deploy Keycloak on a VPS →How to Deploy Leantime on a VPS →How to Deploy LibreChat on a VPS →How to Deploy Linkwarden on a VPS →How to Deploy LocalAI on a VPS →How to Deploy Mailcow on a VPS →How to Deploy Mailu on a VPS →How to Deploy Matomo on a VPS →How to Deploy Mattermost on a VPS →How to Deploy Meilisearch on a VPS →How to Deploy Memos on a VPS →How to Deploy n8n on a VPS →How to Deploy Navidrome on a VPS →How to Deploy NetBird on a VPS →How to Deploy Netdata on a VPS →How to Deploy Nextcloud on a VPS →How to Deploy Next.js to a VPS →How to Deploy Nginx Proxy Manager on a VPS →How to Deploy NocoDB on a VPS →How to Deploy ntfy on a VPS →How to Deploy Ollama on a VPS →How to Deploy Open WebUI on a VPS →How to Deploy OpenHands on a VPS →How to Deploy OpenObserve on a VPS →How to Deploy OpenProject on a VPS →How to Deploy Outline on a VPS →How to Deploy Pangolin on a VPS →How to Deploy Paperless-ngx on a VPS →How to Deploy Passbolt on a VPS →How to Deploy Plane on a VPS →How to Deploy Plausible Analytics on a VPS →How to Deploy PocketBase on a VPS →How to Deploy Prometheus on a VPS →How to Deploy Psono on a VPS →How to Deploy Radarr on a VPS →How to Deploy Rocket.Chat on a VPS →How to Deploy SigNoz on a VPS →How to Deploy Sonarr on a VPS →How to Deploy Stalwart on a VPS →How to Deploy Stirling-PDF on a VPS →How to Deploy Supabase on a VPS →How to Deploy Synapse on a VPS →How to Deploy Taiga on a VPS →How to Deploy TeamPass on a VPS →How to Deploy Tinyauth on a VPS →How to Deploy Traefik on a VPS →How to Deploy Trilium on a VPS →How to Deploy Twenty CRM on a VPS →How to Deploy Umami on a VPS →How to Deploy Uptime Kuma on a VPS →How to Deploy Vaultwarden on a VPS →How to Deploy Vikunja on a VPS →How to Deploy wg-easy on a VPS →How to Deploy Wiki.js on a VPS →How to Deploy Zabbix on a VPS →How to Deploy Zitadel on a VPS →How to Deploy Zulip on a VPS →Docker & Compose on Ubuntu 26.04 →Building AI Workflows with n8n →Install Open WebUI with Ollama →Adding AI-Powered Insights to Plausible Analytics →Building AI-Powered Apps with Supabase and pgvector →

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.