Pocket ID with Docker: passkey-only OIDC login for your apps (Linux, Windows, Mac)
Before you start
- 20 min read
Boxes marked Run in terminal are what you type; plain text explains why. Check what you need first

What you get in one minute#
Pocket ID is a free, self-hosted login server for your apps: they send users to Pocket ID, users sign in with a passkey (fingerprint, face, PIN or a hardware key), and Pocket ID sends them back signed in. There are no passwords to reuse, leak or reset.
Logo: the official Pocket ID project (github.com/pocket-id/pocket-id)
It is an OpenID Connect (OIDC) provider, so any app with an "OIDC", "SSO" or "OAuth" sign-in can use it. The project made it for small teams and homelabs that find Keycloak or authentik too much: one container with a built-in SQLite database. The price is two rules: passkeys only and HTTPS (browsers refuse passkeys on plain HTTP, except on localhost).
| What | Value (official docs and release, read November 2026) |
|---|---|
| Current version | Pocket ID v2.19.2, released 2 November 2026 (latest stable, a patch release) |
| Image to pin | ghcr.io/pocket-id/pocket-id:v2.19.2 (checked on the registry: linux amd64 and arm64) |
| Time needed | About 20 minutes (our estimate, not measured) |
| Size and RAM | Image about 35 MB compressed (amd64, Docker Hub). RAM and CPU: not documented by the project |
| Ports | TCP 1411 (web and API, kept off the internet; the proxy serves HTTPS on 443) |
| Licence | BSD 2-Clause (LICENSE at the release tag) |
| Project status | Active: not archived, not renamed. A v3 with a new interface is announced |
| Official docs | pocket-id.org/docs and the repository |
At a glance:
- What it is: an OIDC and OAuth 2.0 provider (OpenID Connect Certified, per the README). Each app is an "OIDC client".
- Passkeys only: no passwords. A passkey lives in a password manager, phone or hardware key. Add a second one, or you can lock yourself out.
- HTTPS is required: the passkey is tied to the exact address in
APP_URL. - Pin the tag: v3 will bring breaking changes. This guide uses
v2.19.2. - Small: one container, one data folder. OIDC only: SAML or LDAP-only apps need an authentication proxy.
How it fits together#
The app sends the browser to Pocket ID, you prove who you are with a passkey, and Pocket ID sends you back with a one-time code. The app then fetches its tokens from Pocket ID directly, so the browser and the app must both reach its HTTPS address.
Drawn for this guide from the official docs. Pocket ID stores only the public half of your passkey.
Before you start#
A real setup is a small Linux server with a domain; for a trial, any computer with Docker works.
| System | What you need |
|---|---|
| Linux | Docker Engine and Compose v2, x86_64 or arm64, curl, openssl |
| Windows | Docker Desktop, PowerShell, curl.exe and tar (in Windows 10 and 11). Fine for a trial |
| Mac | Docker Desktop, Terminal. Fine for a trial |
Check Docker:
docker --version docker compose version
Checklist:
- [ ]
docker compose versionprints a v2 version - [ ] The address is decided:
http://localhost:1411(trial) orhttps://id.example.com(real) - [ ] For HTTPS: a DNS record for that name, and ports 80 and 443 free
- [ ] A password manager, phone or hardware key for passkeys, and a second one
Tested by DIY Lab Hub: not yet. The commands come from the official docs, the compose file and the release files read on 5 November 2026. We did not run them.
Step 1: Create the data folder#
Pocket ID keeps its database and uploads in one folder. We use ~/docker_volumes/pocket-id, as all DIY Lab Hub guides do.
Linux#
mkdir -p ~/docker_volumes/pocket-id/{data,backups}
cd ~/docker_volumes/pocket-id
Windows (PowerShell)#
New-Item -ItemType Directory -Force "$HOME\docker_volumes\pocket-id\data","$HOME\docker_volumes\pocket-id\backups" | Out-Null Set-Location "$HOME\docker_volumes\pocket-id"
Mac (Terminal)#
Same commands as Linux:
mkdir -p ~/docker_volumes/pocket-id/{data,backups}
cd ~/docker_volumes/pocket-id
Check it worked. The folder exists: ls -d ~/docker_volumes/pocket-id/data (Windows: Test-Path "$HOME\docker_volumes\pocket-id\data" prints True).
Tip: Keep the data folder on the machine's own disk. The docs warn against SQLite on a network share (NFS, SMB).
Step 2: Write the settings and the Compose file#
.env holds the address and a secret key; docker-compose.yml runs the container. Choose your route first:
| Route | APP_URL | What to do |
|---|---|---|
| Try it on this computer | http://localhost:1411 | Follow Steps 2 to 6 as written |
| Real address | https://id.example.com | Set this value in .env, do the HTTPS section after Step 3, then continue at Step 4 |
ENCRYPTION_KEY is mandatory in v2: it encrypts the token-signing keys. The docs generate one with openssl rand -base64 32. The compose file is the official one with two changes: the image is pinned to v2.19.2, and port 1411 is published on 127.0.0.1 only (the official file uses every interface).
Linux#
cd ~/docker_volumes/pocket-id
cat > .env << EOF
APP_URL=http://localhost:1411
ENCRYPTION_KEY=$(openssl rand -base64 32)
TRUST_PROXY=false
EOF
chmod 600 .env
cat > docker-compose.yml << 'EOF'
services:
pocket-id:
image: ghcr.io/pocket-id/pocket-id:v2.19.2
container_name: pocket-id
restart: unless-stopped
env_file: .env
ports:
- "127.0.0.1:1411:1411"
volumes:
- ./data:/app/data
healthcheck:
test: ["CMD", "/app/pocket-id", "healthcheck"]
interval: 1m30s
timeout: 5s
retries: 2
start_period: 10s
EOF
Windows (PowerShell)#
Windows has no openssl by default, so this uses .NET's random generator (our adaptation; the docs only show openssl). The files are written as plain UTF-8 with Unix line endings so the key has no stray characters.
Set-Location "$HOME\docker_volumes\pocket-id"
$b = New-Object byte[] 32
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b)
$key = [Convert]::ToBase64String($b)
[System.IO.File]::WriteAllText("$PWD\.env", "APP_URL=http://localhost:1411`nENCRYPTION_KEY=$key`nTRUST_PROXY=false`n")
$compose = @'
services:
pocket-id:
image: ghcr.io/pocket-id/pocket-id:v2.19.2
container_name: pocket-id
restart: unless-stopped
env_file: .env
ports:
- "127.0.0.1:1411:1411"
volumes:
- ./data:/app/data
healthcheck:
test: ["CMD", "/app/pocket-id", "healthcheck"]
interval: 1m30s
timeout: 5s
retries: 2
start_period: 10s
'@
[System.IO.File]::WriteAllText("$PWD\docker-compose.yml", ($compose -replace "`r`n", "`n"))
Mac (Terminal)#
Same commands as Linux. macOS ships openssl, so openssl rand -base64 32 works as written.
Check it worked. The compose file has the pinned tag (grep image docker-compose.yml; Windows: Select-String image docker-compose.yml) and .env has a key line. Never paste the key into a chat or a screenshot.
Tip: Back up
.envthe moment you create it, in your password manager. If the key changes or goes missing, Pocket ID cannot read its signing keys and will not start (see Common mistakes).
Step 3: Start Pocket ID#
Linux#
cd ~/docker_volumes/pocket-id docker compose up -d docker compose ps docker compose logs --tail=30 pocket-id
Windows (PowerShell)#
Set-Location "$HOME\docker_volumes\pocket-id" docker compose up -d docker compose ps docker compose logs --tail=30 pocket-id
Mac (Terminal)#
Same commands as Linux.
Check it worked. docker compose ps lists pocket-id as running. The health check first runs after the 1m30s interval, so it can say health: starting for about a minute and a half and then healthy. The docs publish no expected log text; read the log for errors. Run the health check yourself, exit code 0 means healthy:
docker compose exec pocket-id /app/pocket-id healthcheck
Step 4: Create the admin account and its first passkey#
Open /setup at your APP_URL: http://localhost:1411/setup for the trial, https://id.example.com/setup for the real address (after the HTTPS section). It works only until the first account exists, so the first visitor becomes the admin.
Linux#
- Enter a username, email address and name, then click Sign Up.
- Click Add Passkey and save it in your password manager, phone or hardware key.
Windows (PowerShell)#
Same browser steps. Windows Hello, a phone or a hardware key work; a password manager extension must support passkeys (see Troubleshooting).
Mac (Terminal)#
Same browser steps; the passkey can go in iCloud Keychain or a password manager.
Check it worked. You land on the admin pages. Open a private window, go to your APP_URL and sign in with the passkey.
Tip: Tried
localhostand now want a real domain? Start clean:docker compose down, delete the trialdatafolder, set the newAPP_URL, repeat Steps 3 and 4. A passkey made for one domain does not work on another.
Step 5: Add a second passkey and learn the recovery route#
Passkey-only has one real danger: losing the only passkey. Do both now.
Linux#
- In Pocket ID, open My Account and add a second passkey on another device or manager.
- Practise recovery from the server. This prints a link that signs the user in once, valid for one hour:
cd ~/docker_volumes/pocket-id docker compose exec pocket-id /app/pocket-id one-time-access-token "<username-or-email>"
Anyone with that link can sign in as that user. Discard it unused.
Windows (PowerShell)#
Same two steps:
Set-Location "$HOME\docker_volumes\pocket-id" docker compose exec pocket-id /app/pocket-id one-time-access-token "<username-or-email>"
Mac (Terminal)#
Same commands as Linux.
Check it worked. My Account lists two passkeys, and the command printed a /lc/ link. Other users get a login code from the admin pages: Administration > Users, the menu of the user, Login Code.
Tip: Add other users with a login code and ask them to add a second passkey right away.
Step 6: Connect your first app#
Every app that signs in with Pocket ID is an OIDC client. You create it in Pocket ID and copy the client ID, client secret and discovery URL into the app. The docs use Immich as the example:
| Where | What to do |
|---|---|
| Pocket ID: Administration > OIDC Clients > Add OIDC Client | Enter a name and the app's callback URL (the app's docs name it; Immich needs https://immich.example.com/auth/login, https://immich.example.com/user-settings and app.immich:///oauth-callback) |
| After Create | Copy the Client ID, the Client secret and the discovery URL. The secret is shown only once |
| The client's Access tab | Pick the groups that may sign in, or All Users. A new client lets nobody in until you do |
| In the app | Issuer or authority: your APP_URL. Discovery: <APP_URL>/.well-known/openid-configuration. Scopes: openid profile email, plus groups if the app assigns roles by group |
Linux#
Check that the app's server can reach Pocket ID's discovery document (from the app's host or container):
curl -s https://id.example.com/.well-known/openid-configuration | head -c 400
Windows (PowerShell)#
curl.exe -s https://id.example.com/.well-known/openid-configuration
Mac (Terminal)#
Same command as Linux.
Check it worked. The output is JSON whose issuer is your APP_URL, and the app's SSO button takes you to Pocket ID and back.
The Pocket ID docs have client examples for nearly a hundred apps, including Immich, Beszel and Vaultwarden, which we have guides for.
Put it behind HTTPS (with or without Traefik)#
Passkeys work only on HTTPS (or localhost), so a real setup needs a reverse proxy in front of port 1411. Trials on localhost can skip this section. Both routes need a domain with a DNS record pointing to the server, and APP_URL must be the exact address in the browser bar.
Both routes make Pocket ID join an external Docker network called proxy and drop the host port. Create the network once and find its address range for TRUST_PROXY (behind a proxy every request comes from the proxy, so Pocket ID must know which proxy to trust for the real client IP):
docker network create proxy
docker network inspect proxy --format "{{range .IPAM.Config}}{{.Subnet}} {{end}}"
Edit ~/docker_volumes/pocket-id/.env so it reads (use your own domain and the subnet printed above; 172.18.0.0/16 is only the docs' example):
APP_URL=https://id.example.com ENCRYPTION_KEY=<keep-the-key-you-already-have> TRUST_PROXY=172.18.0.0/16
Tip: Do not set
TRUST_PROXY=trueunless Pocket ID can only be reached through your proxy: the docs say it trusts every address.
With Traefik#
For a server that already runs Traefik v3 with the Docker provider, add labels and remove the published port.
The labels follow the official reverse-proxy page, which names the entrypoint websecure and the resolver letsencrypt; use the names from your own Traefik (we use https). Replace docker-compose.yml with:
services:
pocket-id:
image: ghcr.io/pocket-id/pocket-id:v2.19.2
container_name: pocket-id
restart: unless-stopped
env_file: .env
volumes:
- ./data:/app/data
healthcheck:
test: ["CMD", "/app/pocket-id", "healthcheck"]
interval: 1m30s
timeout: 5s
retries: 2
start_period: 10s
networks:
- proxy
labels:
traefik.enable: "true"
traefik.docker.network: proxy
traefik.http.routers.pocket-id.rule: "Host(`id.example.com`)"
traefik.http.routers.pocket-id.entrypoints: https
traefik.http.routers.pocket-id.tls.certresolver: letsencrypt
traefik.http.services.pocket-id.loadbalancer.server.port: "1411"
networks:
proxy:
external: true
Start it with docker compose up -d in ~/docker_volumes/pocket-id (Windows and Mac: the same command in your project folder).
Check it worked. curl -I https://id.example.com/ returns an HTTP status line over HTTPS and the browser shows a valid certificate.
Without Traefik: Caddy#
Choose this if you have no Traefik. Caddy gets and renews its certificates itself.
Caddy gets Let's Encrypt certificates for a public domain automatically, and tls internal gives a local certificate at home. Use the compose file above without the labels: block and run Caddy on the same proxy network. Caddy's Docker docs warn against mounting a single Caddyfile, so we mount a conf folder:
mkdir -p ~/docker_volumes/caddy/{conf,data,config}
cd ~/docker_volumes/caddy
Windows: New-Item -ItemType Directory -Force "$HOME\docker_volumes\caddy\conf","$HOME\docker_volumes\caddy\data","$HOME\docker_volumes\caddy\config". Save as docker-compose.yml in that folder (caddy:2.11.8 is the latest Caddy release read in November 2026):
services:
caddy:
image: caddy:2.11.8
container_name: caddy
restart: unless-stopped
ports:
- "80:80/tcp"
- "443:443/tcp"
- "443:443/udp"
volumes:
- ./conf:/etc/caddy
- ./data:/data
- ./config:/config
networks:
- proxy
networks:
proxy:
external: true
Save this as conf/Caddyfile for a public domain (the official Pocket ID block; pocket-id:1411 works because both containers share proxy):
id.example.com {
reverse_proxy pocket-id:1411
}
For a home-only name use tls internal. The name must resolve to the server on every device (a local DNS record or a hosts-file line), and we did not test passkeys on this exact name:
id.home.arpa {
tls internal
reverse_proxy pocket-id:1411
}
Start both stacks:
cd ~/docker_volumes/caddy && docker compose up -d cd ~/docker_volumes/pocket-id && docker compose up -d
For tls internal, each device must trust Caddy's root certificate. The Caddy docs give these commands, run in ~/docker_volumes/caddy:
# Linux (Debian/Ubuntu style; copied to /tmp first because the target folder needs root) docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt /tmp/root.crt && sudo cp /tmp/root.crt /usr/local/share/ca-certificates/root.crt && sudo update-ca-certificates # Mac docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt /tmp/root.crt && sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /tmp/root.crt
On Windows, in an administrator PowerShell (adapted from the cmd form in the docs):
docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt "$env:TEMP\root.crt" certutil -addstore -f "ROOT" "$env:TEMP\root.crt"
Firefox uses its own store and needs the file imported by hand.
Check it worked. curl -I https://id.example.com/ returns an HTTP status line over HTTPS (Windows: curl.exe -I). With tls internal, the padlock appears only after the root is trusted on that device. Then go to Step 4 and open https://id.example.com/setup.
Tip: Apps that call Pocket ID from their own server must also trust a private certificate. With
tls internalthis is the usual reason an app login fails while the browser looks fine.
Passkeys only: what it means for you and your users#
| Question | Answer from the docs |
|---|---|
| What is a passkey? | A key pair: the private half stays on your device, manager or key; Pocket ID stores the public half. It works only on its own domain |
| Can users have a password? | No. Pocket ID supports passwordless methods, primarily passkeys |
| A device is lost? | Add several passkeys per user. An admin can issue a one-time login code, or you create one on the server (Step 5) |
| Borrowed laptop or TV? | Sign in with another device: scan the QR code with your phone and approve (valid 5 minutes) |
| Which passkeys? | Admins choose under Application Configuration > Passkeys: user verification, synced passkeys, device or external keys |
| Email codes? | Optional, and the docs warn they weaken security: anyone who reads the mail can sign in |
Tip: A hardware key without a PIN fails with "Your passkey couldn't verify you" when user verification is Required. Set a FIDO2 PIN or choose Preferred.
Pocket ID or authentik?#
Both give your apps one login. Our authentik guide covers the bigger tool.
| Pocket ID | authentik (our guide) | |
|---|---|---|
| Containers | One (SQLite built in) | Server, worker and PostgreSQL |
| Login method | Passkeys only | Flows with passwords, MFA and more |
| Protocols | OIDC and OAuth 2.0 | OIDC, SAML, LDAP, forward auth |
| Apps without OIDC | Authentication proxy (Tinyauth, caddy-security, OAuth2 Proxy, Traefik forward auth) | Built-in proxy outpost |
| RAM | Not documented | Docs ask for 2 cores and 2 GB |
| Licence | BSD 2-Clause | MIT core |
Pick Pocket ID if every app supports OIDC and you want the shortest path. Pick authentik if you need SAML, LDAP or detailed policies.
Security and testing#
| Control | How |
|---|---|
| Port 1411 | 127.0.0.1 only (Step 2), or no host port behind a proxy. Never forward it on the router |
ENCRYPTION_KEY | Random, kept in your password manager. ENCRYPTION_KEY_FILE works too; write it with printf '%s', since a trailing newline becomes part of the key |
.env file | chmod 600 .env; never commit it or paste it in a chat |
| Who may sign in | A client lets nobody in until you pick groups. Prefer groups to All Users |
| Callback URLs | ALLOW_INSECURE_CALLBACK_URLS defaults to true; the docs say set false unless a client needs http:// |
| Open signup | Only on a private instance; prefer signup links with an expiry |
| Rate limits | Built in; leave DISABLE_RATE_LIMITING alone |
| Container | Optional: -distroless image, read_only: true, non-root user (see the hardening page) |
| Outgoing requests | VERSION_CHECK_DISABLED=true, ANALYTICS_DISABLED=true, ICON_LIBRARY_URL=disabled |
Quick tests after each change (Windows: curl.exe):
cd ~/docker_volumes/pocket-id docker compose ps docker compose logs --tail=100 pocket-id docker compose exec pocket-id /app/pocket-id healthcheck curl -I https://id.example.com/
Common mistakes#
These come from the docs' own warnings, the GitHub issue tracker (read November 2026) and common AI assistant answers.
| Mistake | Why it happens | Do this instead |
|---|---|---|
Opening Pocket ID over plain HTTP or on another address than APP_URL | Passkeys need HTTPS (or localhost) and are bound to the domain. Issue #1767: "Passkeys are not configured correctly for this domain"; one user had not set APP_URL | Serve HTTPS and set APP_URL to the exact address in the browser bar |
Losing or changing ENCRYPTION_KEY | It protects the signing keys. Issue #1863 (open): failed to decrypt key after an update; the maintainer says the variable changed or is missing | Keep the key in your password manager; docker compose exec pocket-id env shows what the container has |
Using latest or an unpinned tag | v3 will change behaviour; the v2.19.2 notes say to pin a major version | Pin v2.19.2 (or v2) and read the changelog before upgrading |
| One passkey per admin | There is no "forgot password", and /setup disappears after the first account | Add a second passkey (Step 5) and know the one-time-access-token command |
App cannot reach APP_URL | The app's server fetches tokens itself; split DNS or a missing hairpin NAT breaks it | Make the name resolve and the certificate trusted inside the app's container, or set INTERNAL_APP_URL |
redirect_uri mismatch | An app behind a proxy thinks it runs on http:// and sends that callback | Add the exact URL from the error; make the app use its public https:// address |
| New client nobody can use | New clients start with "Selected user groups" and none ticked | Pick groups on the client's Access tab |
| SQLite on NFS or SMB | The docs call it dangerous; issues #1274 and #1598 report lock-failure crashes | Keep ./data on a local disk, or use PostgreSQL via DB_CONNECTION_STRING |
| Proxy IP in the audit log | Behind a proxy every request comes from it (issue #380) | Set TRUST_PROXY to the proxy's address or subnet |
| AI assistants: inventing a default admin password | Other tools have one; Pocket ID has none | Create the first account at /setup |
AI assistants: writing v1 settings (DB_PROVIDER, KEYS_STORAGE) or omitting ENCRYPTION_KEY | Old examples; v2 removed those and made the key mandatory | Use only the variables on the official environment variables page |
| AI assistants: promising SAML or LDAP login | Pocket ID is OIDC only; LDAP is a read-only sync into it | Put such apps behind an authentication proxy |
Tip: After every change run
docker compose logs --tail=100 pocket-id. A bad key orAPP_URLprints there.
Tip: Test the login in a private browser window before telling anyone it works; a normal window may reuse an old session.
Tip: Search the exact error text on the docs' Common issues page first: callback mismatch, "not allowed", Content Security Policy errors and nginx 502 are covered.
Troubleshooting#
| Problem | Solution |
|---|---|
| Browser refuses to create a passkey | The page must be HTTPS (or localhost) and APP_URL must equal the address bar |
/setup shows nothing | It works only until the first account exists. Use the login-code command from Step 5 |
| Container exits at start | docker compose logs --tail=100 pocket-id; look for the key error below |
| Locked out of the admin | docker compose exec pocket-id /app/pocket-id one-time-access-token "<username-or-email>", then open the link within an hour |
The redirect_uri is not registered for this client | Add the URL shown in the error to the client's callback URLs |
You are not allowed to access this service | Add the user's group on the client's Access tab, or allow all users |
nginx 502, upstream sent too big header | Raise proxy_buffer_size, proxy_buffers and proxy_busy_buffers_size as in the official nginx example |
| Audit log shows the proxy's IP | Set TRUST_PROXY to the proxy's address or subnet |
| Downgrade refused | Pocket ID will not start an older version on a newer database. Set ALLOW_DOWNGRADE=true once, then remove it |
Key error from issue #1863: failed to decrypt key: failed to decrypt data. The maintainer's answer: ENCRYPTION_KEY changed or is missing in the container.
Known bugs (open, November 2026)#
- Issue #1740: a Bitwarden-managed passkey cannot be registered in Brave, though a demo site works. Test your password manager, or use a phone or hardware key.
- Issue #1880: Outlook link scanning can use up the one-time email login link. Give such users an admin-created login code.
Updates and backups#
Update. Pocket ID migrates its database by itself when a new version starts. Within 2.x no setup changes are needed. Read the changelog, back up first, change the tag in docker-compose.yml and pull:
cd ~/docker_volumes/pocket-id docker compose pull docker compose up -d docker compose logs --tail=100 pocket-id
A major release (v3) comes with its own migration guide; the pinned tag keeps you from moving by accident.
Back up. Stop the container so the SQLite file is quiet, archive the data folder and .env, and start again. The archive holds your encryption key, so treat it as secret.
Linux#
cd ~/docker_volumes/pocket-id docker compose down tar -czf "backups/pocket-id-$(date +%F).tar.gz" data .env chmod 600 backups/pocket-id-*.tar.gz docker compose up -d
Nightly at 03:00: save those lines as backup.sh in the folder, run chmod +x backup.sh, then crontab -e and add:
0 3 * * * /home/<your-user>/docker_volumes/pocket-id/backup.sh
Keep a copy of the archive on another machine.
Windows (PowerShell)#
Set-Location "$HOME\docker_volumes\pocket-id" docker compose down $d = Get-Date -Format "yyyy-MM-dd" tar -czf "backups\pocket-id-$d.tar.gz" data .env docker compose up -d
For a nightly run, save these lines as backup.ps1 and register a task:
schtasks /Create /SC DAILY /ST 03:00 /TN "PocketID backup" /TR "powershell -NoProfile -File $HOME\docker_volumes\pocket-id\backup.ps1"
Mac (Terminal)#
Same commands as Linux. For the nightly run use cron or launchd.
The docs also describe an experimental pocket-id export for moving instances; we rely on the folder archive.
Restore test. Unpack one archive into a new empty folder, never over the live one:
T=$(mktemp -d) tar -xzf backups/pocket-id-<date>.tar.gz -C "$T" ls -la "$T" "$T/data" rm -rf "$T"
Windows: unpack into a new folder with tar -xzf backups\pocket-id-<date>.tar.gz -C <new-empty-folder>, look, then delete the folder. To restore for real: docker compose down, replace data and .env from the archive, then docker compose up -d.
Performance and resources#
The docs publish no RAM, CPU or disk figures, so this guide gives none. Measure your own:
- Live use:
docker stats pocket-idshows CPU and memory. - Image: about 35 MB compressed (amd64, Docker Hub).
- Ports: 1411 for web and API; an internal task runtime uses UDP 1414 (
ACTORS_PORT). - Database: SQLite is fine for most installs. PostgreSQL is supported; only one instance may use a database. Issue #1598 links lock failures to a slow or busy host; fixes shipped in 2.14.
- Logs:
LOG_LEVEL,LOG_JSON=true; keepLOG_QUERY_ARGSoff.
FAQ#
Is Pocket ID free to use? Yes, under the BSD 2-Clause licence.
Can users have a password as a fallback? No. Only passwordless methods: passkeys, plus admin login codes and optional email codes for first sign-in and recovery.
Do my apps have to support passkeys? No. They only need OIDC; Pocket ID handles the passkey.
Does it send data to the project? Three kinds of outgoing requests: a GitHub version check, a daily anonymous heartbeat and an icon search on jsDelivr. Each has a switch.
Will v3 break my setup? A major release can change settings and will have a migration guide. A pinned 2.x tag keeps you safe until you choose to move.
Sources#
- Pocket ID v2.19.2: README, docker-compose.yml, .env.example, LICENSE - read 5 November 2026
- Pocket ID docs: introduction, installation, reverse proxy, connect an app, user management, upgrading, data export, migrate to v2, sign-in methods, authentication proxies, environment variables, hardening, common issues, account recovery, Immich example - read 5 November 2026
- Images: ghcr.io/pocket-id/pocket-id and Docker Hub pocketid/pocket-id (tag
v2.19.2checked) - Caddy v2.11.8, automatic HTTPS, Caddy Docker image - read 5 November 2026
- GitHub issues #380, #1274, #1598, #1740, #1767, #1863, #1880
- Related guides: authentik, Caddy
Checked against the official docs on 2026-11-05.