Pocket ID on Ubuntu 24.04 on Azure User Guide
Overview
This image runs Pocket ID 2.11.0 on Ubuntu 24.04 LTS. Pocket ID is a simple, self hosted OpenID Connect provider whose defining idea is that it authenticates people with passkeys rather than passwords. You point your existing applications at it as their identity provider, and your users sign in to all of them with the fingerprint reader, face unlock or hardware security key they already carry. There are no passwords to choose, rotate, leak or phish.
It ships as a single self contained Go binary that embeds its own web interface and stores everything in an embedded SQLite database, so the whole identity plane is one process and one file. That makes it a genuinely practical alternative to running a large identity suite when what you actually need is OIDC single sign on for a handful of self hosted applications.
Secure by default, with no admin hijack window. Pocket ID's initial admin setup route is open to whoever reaches it first while no user exists yet. On a fresh public IP that is a real risk: this is your identity provider, so whoever claims it owns every application behind it. This image therefore ships with no user, no passkey, no signing key and no secret at all, and pocket-id-firstboot.service creates the sole admin for your VM specifically during first boot, which closes the setup route before the VM is ever reachable. That admin has no password and no passkey. The only way in is a one time sign in link that exists solely on your VM in a root only file, or a fresh one you mint over SSH. Your SSH key is the root of trust.
What is included:
- Pocket ID 2.11.0, installed from the official upstream release binary and verified against the release's own published checksum
- Pocket ID listening on
127.0.0.1:1411, loopback only, so it is never exposed directly - nginx on port
80reverse proxying the interface, plus an unauthenticated/healthzendpoint for Azure Load Balancer health probes - A per VM admin account and one time sign in link generated on first boot and recorded at
/root/pocket-id-credentials.txt(mode0600) - A per VM encryption key generated on first boot, so no two VMs ever share one
pocket-id.service,nginx.serviceand a one shotpocket-id-firstboot.serviceas systemd units, enabled and activepocket-id-admin-linkandpocket-id-set-urlhelper commands- A fully patched Ubuntu 24.04 LTS base with unattended security upgrades enabled
- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is ample: Pocket ID is a single lightweight Go process. NSG inbound: allow 22/tcp from your management network and 80/tcp (plus 443/tcp once you add TLS). Port 1411 never needs opening because Pocket ID binds to loopback and is reached only through nginx.
You will need a DNS name. This is the one thing to plan for before you start. Passkeys are built on the WebAuthn standard, which requires a secure context (HTTPS) and a registrable domain as the relying party identifier. A bare IP address is not a valid relying party identifier, so a passkey cannot be registered against http://<vm-ip>/ in any browser. The VM comes up fully working on its IP address and you can sign in and configure it there, but to register passkeys and run this for real you need a domain name and a certificate. Step 8 covers that.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Pocket ID by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create then Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group my-rg \
--name my-pocket-id \
--image cloudimg:pocket-id-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
az vm open-port --resource-group my-rg --name my-pocket-id --port 22 --priority 1001
az vm open-port --resource-group my-rg --name my-pocket-id --port 80 --priority 1002
Step 3 - Connect to your VM
ssh azureuser@<vm-ip>
Step 4 - Confirm the services are running
All three units should report active, and Pocket ID itself should be listening on 127.0.0.1:1411 only, with nginx on port 80.
systemctl is-active pocket-id pocket-id-firstboot nginx
sudo ss -tlnp | grep -E ':80 |:1411 '

Step 5 - Retrieve your one time admin sign in link
First boot created the sole admin for this VM and minted a one time sign in link for it. The link is single use and expires one hour after first boot.
sudo ls -l /root/pocket-id-credentials.txt
The file is root owned and mode 0600. It looks like this, with the token shown masked here:
POCKET_ID_URL=http://<vm-ip>/
POCKET_ID_ADMIN_USERNAME=admin
POCKET_ID_ADMIN_SIGNIN_LINK=<POCKET_ID_ADMIN_LINK>

If the link has expired, or you simply want a fresh one, mint another at any time. This is upstream's own admin recovery path, and it is the reason a lost passkey can never lock you out of your own identity provider:
sudo pocket-id-admin-link
Step 6 - Confirm the provider is serving
The health endpoint answers, and the OIDC discovery document reports the issuer your applications will use plus the grant types the provider supports.
curl -sI http://127.0.0.1/healthz | head -1
curl -s http://127.0.0.1/.well-known/openid-configuration | jq -r '.issuer'
curl -s http://127.0.0.1/.well-known/openid-configuration | jq -r '.grant_types_supported[]'

Step 7 - Confirm no credential shipped in the image
This is worth verifying yourself, because it is the whole security argument for running an identity provider from a prebuilt image.
The initial admin setup route returns 404, meaning the admin was already created for this VM and nobody else can claim this instance. The API rejects unauthenticated callers with 401. And the passkey table is empty, because no passkey ships in the image.
curl -s -o /dev/null -w "initial-admin setup -> HTTP %{http_code}\n" http://127.0.0.1/api/signup/setup
curl -s -o /dev/null -w "unauthenticated API -> HTTP %{http_code}\n" http://127.0.0.1/api/users
sudo sqlite3 /var/lib/pocket-id/data/pocket-id.db 'SELECT COUNT(*) FROM webauthn_credentials;'
A sign in link works exactly once. Exchange one and then replay it: the first attempt returns 200, the second is rejected.
T=$(sudo pocket-id-admin-link --token-only); curl -s -o /dev/null -w "one-time link -> HTTP %{http_code}\n" -X POST http://127.0.0.1/api/one-time-access-token/$T; curl -s -o /dev/null -w "same link again -> HTTP %{http_code}\n" -X POST http://127.0.0.1/api/one-time-access-token/$T

Step 8 - Point your domain at the VM and enable HTTPS
Passkeys need a real domain and a secure context, so this step is what turns the VM into a usable identity provider rather than a working demo.
Create a DNS A record for your chosen name, for example id.example.com, pointing at the VM's public IP. Then tell Pocket ID that name. The helper refuses a bare IP address on purpose, because a passkey could never be registered against one:
sudo pocket-id-set-url https://id.example.com
Then obtain a certificate. Certbot edits the nginx site in place and reloads it:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d id.example.com --redirect --agree-tos -m you@example.com
Open 443/tcp in the NSG, and mint a fresh sign in link now that the URL has changed:
sudo pocket-id-admin-link
Everything from here happens at https://id.example.com.
Step 9 - Sign in and register your passkey
Open your sign in link in a browser. Pocket ID greets you with its passkey prompt.

The one time link signs you straight in without a passkey, which is precisely how you bootstrap. Go to My Account and choose Add Passkey, then follow your browser's prompt to use your fingerprint reader, face unlock, phone or hardware key. Once it is registered it appears in the Passkeys list and becomes your normal way in.

Register a second passkey while you are here, on another device. Pocket ID will remind you to. It is the difference between losing a laptop and losing an account, though sudo pocket-id-admin-link over SSH remains your backstop either way.
While you are in My Account, change the admin email from the placeholder admin@pocket-id.invalid to your own address, and adjust the username if you would like.
Step 10 - Add your users and groups
Under Administration then Users, choose Add User to create an account for each person, and use User Groups to organise them. Each user then registers their own passkey the same way you did: you send them a one time link, they sign in and enrol their device. No password is ever set, sent or stored.

Step 11 - Register your first OIDC application
Under Administration then OIDC Clients, choose Add OIDC Client. Give it a name and set its callback URL to the redirect URI your application expects, for example https://grafana.example.com/login/generic_oauth. Pocket ID issues a client ID and client secret; copy the secret then, because it is shown once.

In the application itself, configure a generic OIDC or OAuth provider with the client ID and secret you just created, and point it at the issuer from Step 6, which is now https://id.example.com. Most applications discover the rest automatically from /.well-known/openid-configuration. Enable PKCE where the application supports it.
Maintenance
The Ubuntu base applies security updates automatically through unattended upgrades. To upgrade Pocket ID itself, replace the binary with a newer release and restart the service:
sudo systemctl stop pocket-id
sudo curl -fsSL -o /usr/local/bin/pocket-id https://github.com/pocket-id/pocket-id/releases/latest/download/pocket-id_linux_amd64
sudo chmod 0755 /usr/local/bin/pocket-id
sudo systemctl start pocket-id
Your identity data lives entirely in /var/lib/pocket-id/data. Back up that directory together with /etc/pocket-id/encryption.key, because the key is what decrypts it. Neither is any use without the other:
sudo tar czf pocket-id-backup.tar.gz /var/lib/pocket-id/data /etc/pocket-id/encryption.key
Pocket ID is licensed under the BSD 2-Clause License. The upstream licence text ships on the image at /usr/share/doc/pocket-id/LICENSE.
Support
cloudimg provides 24/7 support for this image. Contact support@cloudimg.co.uk.