How to Deploy Pocket ID: Lightweight Passwordless OIDC and Passkey Provider with Docker Compose

Pocket ID is an easy-to-use, OpenID Connect Certified identity provider built exclusively around passwordless passkey authentication. Learn how to deploy Pocket ID with Docker Compose, register WebAuthn passkeys, configure OIDC clients, and manage homelab single sign-on.

How to Deploy Pocket ID: Lightweight Passwordless OIDC and Passkey Provider with Docker Compose

Pocket ID is a lightweight, OpenID Connect Certified™ and OAuth 2.0 identity provider designed from the ground up around passwordless passkey authentication (FIDO2 / WebAuthn). While traditional self-hosted identity solutions such as Keycloak, Authentik, or Authelia offer extensive enterprise federation features, they often introduce significant complexity, heavy database footprints, and steep configuration learning curves for homelab and small-business environments. Pocket ID solves this by delivering a modern, responsive single sign-on (SSO) experience with zero password management, instant biometric logins, and minimal resource utilization.

Pocket ID My Apps Single Sign-On Portal

At its core, Pocket ID completely removes passwords from the authentication lifecycle. Users sign in using hardware security keys (such as YubiKeys) or built-in platform authenticators (such as Apple Touch ID / Face ID, Windows Hello, and Android biometrics). The backend is implemented as a single, highly performant Go binary that embeds a SvelteKit 5 web application and runs background tasks through an internal actor runtime, persisting state to an embedded SQLite database by default.

Key Capabilities and Architecture

  1. Passkey-First Authentication: Eliminates shared secrets, credential stuffing, and password resets by enforcing WebAuthn / FIDO2 authentication standards.
  2. OpenID Connect Certified™ & OAuth 2.0: Built on an optimized Ory Fosite foundation supporting Authorization Code Grant with PKCE, Refresh Token rotation, Device Flow (RFC 8628), and Back-Channel Logout (RFC 9474).
  3. Centralized "My Apps" Launchpad: Provides users with a clean application dashboard displaying authorized homelab applications with single-click launch buttons.
  4. Embedded Single-Container Architecture: Runs as an all-in-one container with zero external dependencies, leveraging embedded SQLite with Write-Ahead Logging (WAL) or optional external PostgreSQL.
  5. Built-in Operator CLI: Includes the /app/pocket-id management CLI inside the container for health checks, administrative bypass token creation, key rotation, and data export.

---

Prerequisites

Before deploying Pocket ID, verify that your host system satisfies the following requirements:

  • Operating System: Any modern Linux distribution (Debian 12+, Ubuntu 22.04/24.04 LTS, Rocky Linux, Alpine), macOS, or Windows with Docker Desktop / WSL2.
  • Container Engine: Docker Engine 24.0+ with Docker Compose v2 (docker compose version v2.24+).
  • Hardware Resources:

- CPU: 1 vCPU core (minimal baseline CPU usage under normal homelab workloads). - Memory: 128 MB RAM minimum; 256 MB to 512 MB recommended. - Storage: 200 MB persistent disk space for the SQLite database, cryptographic keys, and application assets.

  • Network Ports: Host port 3000 (or any chosen free host port) mapped to container internal port 1411.
  • Secure Context Requirement (WebAuthn): The WebAuthn API strictly requires a Secure Context. Browsers treat http://localhost as secure for local testing. When exposing Pocket ID over a local network or the public internet, you must place it behind a reverse proxy (such as Caddy, Traefik, Nginx, or Cloudflare Tunnel) configured with valid SSL/TLS certificates (HTTPS).

---

Exact Install Steps

The official Pocket ID container image is published on Docker Hub (pocketid/pocket-id:v2) and GitHub Container Registry (ghcr.io/pocket-id/pocket-id:v2). In this deployment, we run Pocket ID version 2.19.2 with an embedded SQLite database.

Step 1: Create Staging Directory Structure

Create an isolated directory on your host to store your deployment manifests and persistent SQLite data:

mkdir -p pocket-id/data
cd pocket-id
chmod 775 data

Pocket ID runs as an unprivileged user inside the container (PUID=1000, PGID=1000). Setting permissions ensures the container process can create and manage the database files without permission errors.

Step 2: Generate Environment Configuration (.env)

Pocket ID requires two primary environment variables:

  • APP_URL: The canonical public URL where users and client services reach Pocket ID (no trailing slash).
  • ENCRYPTION_KEY: A cryptographically secure random string of at least 16 bytes used to encrypt sensitive application secrets at rest.

Generate a 32-byte base64 encryption key using openssl and write the .env configuration file:

cat > .env <<EOF
COMPOSE_PROJECT_NAME=pocket-id
PORT=3000
APP_URL=http://localhost:3000
ENCRYPTION_KEY=$(openssl rand -base64 32)
PUID=1000
PGID=1000
LOG_LEVEL=info
EOF

chmod 600 .env

[!IMPORTANT]
Keep a secure backup of your ENCRYPTION_KEY. If this key is lost, Pocket ID will be unable to decrypt stored application secrets and OIDC client credentials.

Step 3: Create Docker Compose Specification (compose.yml)

Create the compose.yml file defining the Pocket ID container service, persistent storage volume, port binding, and health check:

cat > compose.yml <<'EOF'
services:
  pocket-id:
    container_name: ${COMPOSE_PROJECT_NAME}-app
    image: pocketid/pocket-id:v2
    restart: unless-stopped
    ports:
      - "${PORT:-3000}:1411"
    environment:
      - APP_URL=${APP_URL}
      - ENCRYPTION_KEY=${ENCRYPTION_KEY}
      - TRUST_PROXY=false
      - PUID=${PUID:-1000}
      - PGID=${PGID:-1000}
      - LOG_LEVEL=${LOG_LEVEL:-info}
    volumes:
      - ./data:/app/data
    healthcheck:
      test: ["CMD", "/app/pocket-id", "healthcheck"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    networks:
      - pocketid-net

networks:
  pocketid-net:
    name: ${COMPOSE_PROJECT_NAME}-net
    driver: bridge
EOF

Step 4: Launch the Service Stack

Start the container service in detached mode:

docker compose up -d

Step 5: Await Service Health

Pocket ID exposes an unauthenticated health check endpoint at /healthz that returns HTTP 204 once the backend and actor runtime are initialized. Poll the endpoint to confirm readiness:

for i in $(seq 1 30); do
  code=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/healthz || true)
  if [ "$code" = "204" ] || [ "$code" = "200" ]; then
    echo "Pocket ID is ready! (HTTP $code)"
    break
  fi
  sleep 2
done

Verify the container status:

docker compose ps

The container output should display Up (healthy) with port mapping 0.0.0.0:3000->1411/tcp.

---

Browser Setup and Onboarding

Once the container is healthy, navigate to Pocket ID in your browser to complete administrator setup and configure your OIDC applications.

Initial Administrator Setup Flow

When opening http://localhost:3000/ for the first time, Pocket ID detects that no administrator account exists and routes to the setup view (/signup/setup).

Pocket ID Landing and Login Screen

You can complete initial onboarding through either the web setup wizard or the command line:

  1. Option A: Web Form (/signup/setup)

- Provide your First Name, Last Name, Username (e.g. admin), and Email Address (e.g. admin@example.local). - Click Create admin account.

  1. Option B: Bootstrap via API

``sh curl -sf -X POST http://localhost:3000/api/signup/setup \ -H 'Content-Type: application/json' \ -H 'Origin: http://localhost:3000' \ -d '{"username":"admin","email":"admin@example.local","firstName":"Admin","lastName":"Operator"}' ``

Enrolling an Administrator Passkey

Because Pocket ID does not store or accept passwords, signing in requires either registering a WebAuthn passkey or using an emergency one-time access token.

To log in or enroll your passkey, generate a one-time sign-in link using the container CLI:

docker compose exec pocket-id /app/pocket-id one-time-access-token admin

The CLI prints a single-use login link valid for 1 hour:

A one-time access token valid for 1 hour has been created for "admin".
Use the following URL to sign in once: http://localhost:3000/lc/<token>

Open the printed URL in your browser. You will land on the "Set up your passkey" prompt:

Set Up Passkey Screen

Click Add Passkey. Your operating system or browser will trigger its native WebAuthn prompt, allowing you to authenticate with a fingerprint, facial scan, PIN, or hardware security key (YubiKey).

Once registered, your credential is listed in My Account:

Account Passkey Listed

---

Administration and OIDC Application Setup

With administrator privileges enrolled, click Administration in the left sidebar to manage users, groups, and client integrations.

1. User Management

Under Administration → Users, you can invite or manually create users, verify email addresses, and manage administrative privileges:

User Management Overview

2. User Groups and Access Control

Navigate to Administration → User Groups to segment permissions. For example, create a Homelab Admins group for administrative personnel and a Family group for general household users:

User Groups Overview

Groups allow you to restrict which OIDC applications specific users are permitted to access.

3. Registering OIDC Clients

Navigate to Administration → OIDC Clients to view registered relying parties:

OIDC Clients Overview

To connect a new self-hosted service (such as Immich, Grafana, or Jellyfin):

  1. Click New Client.
  2. Enter the application Name (e.g. Immich).
  3. Under Callback URLs, enter the redirect URI specified by your client service (e.g. http://localhost:2283/auth/login).
  4. Click Create Client. Pocket ID will generate a unique Client ID and Client Secret:

OIDC Client Created Confirmation

[!NOTE]
Copy and store the generated Client Secret securely. It will be required when configuring the OpenID Connect settings inside your downstream application.

4. Configuring Application Launch URLs and Group Permissions

To ensure an application displays properly on the My Apps user dashboard:

  1. Open the client settings for the newly created application.
  2. Enter the Launch URL (e.g. http://localhost:2283) and click Save:

Client Launch URL Configuration

  1. Under Allowed user groups, assign the user groups authorized to access this service (e.g. Homelab Admins). If a client has no allowed groups assigned and group restriction is active, users will not see the tile on their dashboard.

5. Accessing the "My Apps" Single Sign-On Portal

Navigate to /settings/apps or click My Apps in the navigation bar. You will be greeted with the centralized SSO dashboard:

My Apps Single Sign-On Dashboard

Each authorized service displays with its application tile and an interactive Launch button, allowing authenticated users to open their homelab services with passwordless single sign-on.

---

Verification

Confirm your deployment is functioning properly using these verification checks:

1. OpenID Connect Discovery Endpoint

Inspect the .well-known configuration document to verify that the issuer and authentication endpoints match your APP_URL:

curl -s http://localhost:3000/.well-known/openid-configuration | grep -o '"issuer":"[^"]*"'

Expected output:

"issuer":"http://localhost:3000"

You can verify key endpoints returned in the JSON document:

  • Authorization Endpoint: http://localhost:3000/authorize
  • Token Endpoint: http://localhost:3000/api/oidc/token
  • JWKS URI: http://localhost:3000/.well-known/jwks.json
  • Userinfo Endpoint: http://localhost:3000/api/oidc/userinfo

2. Public Application Configuration

Query the public application configuration endpoint:

curl -s http://localhost:3000/api/application-configuration

Expected output:

{
  "appName": "Pocket ID",
  "allowOwnAccountEdit": true,
  "emailVerificationEnabled": false
}

3. Container Built-in Healthcheck

Execute the internal healthcheck binary:

docker compose exec pocket-id /app/pocket-id healthcheck --verbose

Expected output:

Healthcheck succeeded

---

Troubleshooting

Symptom / ErrorRoot CauseResolution
Browser WebAuthn prompt fails with "NotAllowedError" or "The operation either timed out or was not allowed"Browser requires a Secure Context (HTTPS) for WebAuthn APIs when accessed from non-localhost addressesAccess Pocket ID via http://localhost:3000 for local development, or deploy a reverse proxy (Caddy / Traefik / Nginx) providing valid SSL/TLS certificates for LAN or domain access.
Applications do not appear on the "My Apps" user dashboardThe client application is missing a launchURL or the current user is not a member of an allowed user groupOpen the client settings under Administration → OIDC Clients, enter the application's base web address into Launch URL, and verify that the user's group is listed under Allowed user groups.
Container fails to start with "ENCRYPTION_KEY must be at least 16 bytes"Missing or insufficient ENCRYPTION_KEY environment variable in .envGenerate a fresh 32-byte key using openssl rand -base64 32 and update the ENCRYPTION_KEY parameter in .env.
Port binding error: "address already in use" on port 3000Another service (such as Grafana, Gitea, or an existing web app) is listening on host port 3000Modify PORT in .env to an unused host port (e.g. PORT=1411 or PORT=3002) and run docker compose up -d. Pocket ID continues listening on internal port 1411.
Administrator locked out or lost passkey deviceNo functional passkey enrolled for the administrator accountGenerate an emergency 1-hour sign-in token via the host shell: docker compose exec pocket-id /app/pocket-id one-time-access-token <username> and open the /lc/<token> link in your browser.
Infinite redirect loop behind reverse proxyReverse proxy headers are stripped or APP_URL does not match the external domainSet APP_URL=https://auth.yourdomain.com in .env, ensure the reverse proxy forwards X-Forwarded-For and X-Forwarded-Proto, and add TRUST_PROXY=true under environment in compose.yml.
Database write errors on startup (sqlite: database is locked or permission denied)Host directory ./data has restrictive permissions preventing the container non-root UID 1000 from writingRun chmod 775 data && chown -R 1000:1000 data on the host machine, then restart the container with docker compose restart.

---

Teardown and Clean Rollback

To stop the running container and remove all associated container networks:

docker compose down

To completely reset the deployment and delete all persistent database records, encryption keys, and staged files:

docker compose down -v --remove-orphans
rm -rf compose.yml .env data/

---

Assumptions & Decisions

  • Passkey-First Authentication: Pocket ID enforces WebAuthn and does not use passwords. To support initial access and recovery, the operator CLI (/app/pocket-id one-time-access-token admin) was utilized to generate single-use authentication links.
  • Persistence Selection: The official embedded SQLite engine (/app/data/pocket-id.db) was configured for zero-dependency simplicity and instant deployment, matching standard homelab architectures.
  • My Apps Visibility: The "My Apps" SSO grid populates only when OIDC clients include a configured launchURL and explicitly grant access to the user's assigned group. Both properties were validated and documented.
  • Secure Context Testing: Automated browser captures utilized the Chrome DevTools Protocol (CDP) virtual WebAuthn authenticator (ctap2/internal) to simulate physical security key enrollment in a headless container environment.