How Do I Add Passkey Login to My Self-Hosted Apps?

2026-11-09

Short answer: For self hosted passkey login, run a small login server called Pocket ID next to your apps. It signs you in with a passkey (a login that uses your fingerprint, face or PIN instead of a password). Then it tells each app who you are through OIDC (a standard way for one app to vouch for you to another). Any app with a “Login with OpenID” setting can use it.

You open your home server dashboard and count the logins. Photos app, media server, notes app, file sync, the router. Five apps, five passwords, and at least two of them are the same password.

That reused password is the real risk. If one app leaks it, every app with the same password is open. This post shows how to replace the whole pile with one passkey, explained from the ground up so every step makes sense.

What is a passkey, really?

Mind map of Pocket ID: passkeys only, OIDC, port 1411, vs Authelia and Keycloak

For a different shared-login risk, see did my coding agent send my API key. Homelab cages: Microsoft execution containers.

A passkey is a pair of matching keys: one stays locked on your device, the other is handed to the website. To see why that beats a password, start with how a password works.

A password is a shared secret. You know it, the server stores a copy (or a scrambled version of it), and you prove who you are by typing it. Anyone who steals that copy, or tricks you into typing it on a fake site, can log in as you.

Now the base idea behind passkeys: public-key cryptography (a math trick where one key locks and a different, matching key checks). You get two keys that are made together:

  • Private key: never leaves your phone, laptop or hardware key. It can sign (put a stamp on) a message.
  • Public key: given to the website. It can only check that a stamp came from the matching private key. It cannot make stamps.

So when you log in, the server sends a random challenge (a one-time puzzle number). Your device stamps it with the private key after you touch the fingerprint sensor. The server checks the stamp with the public key. Nothing secret ever travels, so there is nothing to steal from the server.

You plus a passkey to Pocket ID to apps over OIDC

One more detail matters later: each passkey is tied to one domain name (the web address, like id.example.com). Your phone will refuse to use it on any other address. That is why a fake look-alike site cannot trick a passkey.

Why not add passkeys to every app one by one?

Because most self-hosted apps don’t support passkeys, but almost all of them support “Login with OpenID”. That gap is the whole trick.

Think of a hotel. You don’t show your passport at every door. You show it once at the front desk, get a key card, and every door trusts the card.

In software, the front desk is called an identity provider (one server whose only job is checking who you are). The key card system is called OIDC, short for OpenID Connect (the common language apps use to trust that front desk).

Single sign-on, or SSO (log in once, get into many apps), is what you get when every app trusts the same front desk. So instead of teaching five apps about passkeys, you teach one front desk, and the five apps just trust it.

Browser to reverse proxy HTTPS to Pocket ID on port 1411

What is Pocket ID and what does it replace?

Pocket ID is a free, open source front desk that only accepts passkeys. No password field exists at all, so there is no password to reuse, phish or forget.

It does the same job as Keycloak or Authentik, two well-known identity providers. Those are full platforms built for companies, with many settings and moving parts. Pocket ID is the small version: one container, a short settings file, and a clean web page for adding apps and users.

The basics you will want to know before trying it:

QuestionAnswer
Cost and licenseFree, BSD 2-Clause (you can use it commercially)
How it runsDocker container or a single program file
StorageOne SQLite file by default (a database that lives in a single file); Postgres is also supported
Container sizeabout 35 MB compressed on Docker Hub for tag v2 (expanded size not re-measured without a local pull)
What apps needA “Login with OpenID / OIDC” setting
Passkey storagePhone, laptop, password manager, or a hardware key like a YubiKey

I also read through its source code to check the one setting that can hurt you later. More on that in the catch section.

How do you set up Pocket ID?

Four steps: download two files, fill in two lines, start it, open the setup page. It needs a real web address with HTTPS. Browsers only allow passkeys on secure pages, or on localhost while you test.

  1. Download the compose file (a recipe that tells Docker what to run) and the example settings file:
# From https://pocket-id.org/docs/setup/installation — copy docker-compose.yml
# and .env.example from the pocket-id/pocket-id repo (v2 branch), then:
cp docker-compose.yml .env.example ./   # or download from the docs page
mv .env.example .env
  1. Open .env and set your address and an encryption key (a random string that locks the stored data):
APP_URL=https://id.example.com
ENCRYPTION_KEY=paste-the-output-of: openssl rand -base64 32
  1. Start it with docker compose up -d. It listens on port 1411.
  2. Point a reverse proxy at it. A reverse proxy (a front server like Caddy or Nginx that adds HTTPS and forwards traffic) turns id.example.com into a secure address. Then open https://id.example.com/setup, create your admin account, and add your first passkey.
Simplified OIDC handshake with a passkey

The setup page only works until the first account exists, so do step 4 right away.

How do you connect an app to it?

Each app gets a client ID and secret from Pocket ID, and gives Pocket ID a callback URL. That exchange is the whole handshake, and it is the same for every app.

First, the two words you will see. A client ID is the app’s name tag at the front desk. A client secret is a password only the app and Pocket ID know, so a fake app cannot pretend to be yours. The callback URL (the page the app wants you sent back to after login) tells Pocket ID where to return you.

The steps:

  1. In Pocket ID, open the admin area, go to OIDC Clients, and add a new client.
  2. Paste the app’s callback URL. The app’s docs list it, usually something like https://photos.example.com/auth/callback.
  3. Copy the client ID and client secret Pocket ID shows you.
  4. In the app’s settings, turn on OpenID / OIDC login and paste three things: the issuer URL (https://id.example.com), the client ID, and the secret.

The app finds everything else on its own. Pocket ID publishes a standard address, https://id.example.com/.well-known/openid-configuration, that lists every endpoint an app needs. Open it in a browser and you will see the list.

Three catches: HTTPS, OIDC apps, recovery for lost keys

You can also limit who gets into which app. Put users in groups, then allow only some groups on each client. The kids get the media server, not the router.

What is the catch?

Pocket ID is simple because it skips things, and three of them can bite you. Know them before you move every app over.

Your domain is part of every passkey. In the source code, the passkey “home address” is read straight from APP_URL. Remember that passkeys are tied to one domain. If you later move from id.example.com to login.example.com, every saved passkey stops working and every user has to add a new one. Pick the address once and keep it.

Losing your only passkey locks you out. There is no “forgot password” link because there is no password. Pocket ID has a fix built in: an admin can create a one-time login link from the command line (docker compose exec pocket-id /app/pocket-id one-time-access-token <username>), or turn on email login codes. Still, add two passkeys on day one, for example your phone and a password manager.

Apps without OIDC need extra help. Some apps have no “Login with OpenID” setting at all. For those you need an auth proxy (a gatekeeper that sits in front of the app and checks your login first). Pocket ID can work with one, but that is a second piece to set up.

One more habit: back up the data folder. With SQLite, that one folder is your whole user list.

Pocket ID vs Authelia vs Keycloak: which one fits?

Pick by how much you need, not by features you might use someday. All three are free and self-hosted.

Pocket IDAutheliaKeycloak
Built forHomelabs, small teamsHomelabs that want a login gate in front of appsCompanies with many users and rules
PasswordsNone, passkeys onlyYes, plus second factorsYes, plus many options
Passkey-only loginYes, by defaultPossible, but by default still asks for a password tooPossible with setup
Setup effortSmallMedium (config file heavy)Large
Best whenYour apps support OIDC and you want zero passwordsMany of your apps have no login of their ownYou need user federation, fine-grained roles, enterprise features

If most of your apps already show a “Login with OpenID” option, Pocket ID is the shortest path to a passwordless home server. If most don’t, Authelia’s gatekeeper style may suit you better.

Common questions about self-hosted passkey login

Is Pocket ID free?

Yes. It is open source under the BSD 2-Clause license, with no paid tier.

Do I need HTTPS for Pocket ID?

Yes for real use. Browsers only allow passkeys on HTTPS pages, with localhost as the exception for testing.

What happens if I lose my phone with my passkey?

An admin can create a one-time login link from the command line or send an email login code, then you add a new passkey. Adding two passkeys from the start avoids the problem.

Can I use a YubiKey with Pocket ID?

Yes. Any passkey works, including ones stored on a hardware key, a phone, or a password manager.

Back on that dashboard, the five apps still have their own login pages. But now each one has a single button, and behind it sits one passkey that no one can phish.

One passkey, every app you host. No passwords left to leak.

—

Leave a comment