Lucas Janin | Pocket-ID: Bare Metal Installation on Debian - Lucas Janin

Pocket-ID: Bare Metal Installation on Debian

2 June 2025
  • PocketID VM Feature

After using PocketID for several months with an LXC installation and Proxmox Helper Scripts, I noticed that the service runs as root. I also learned that a VM installation is more secure than an LXC. This article will guide you on installing Pocket-ID as a non-root service on Debian.

Presentation

First, if you’re not familiar with Pocket-ID, it’s a modern and lightweight OIDC client designed for managing authentication for services like Authentic and Aurelia. Its unique feature is that it exclusively supports passkeys. I use it with several self-hosted services, including Proxmox, Proxmox Backup Server, Komodo, Betszel, Karakeep, and, of course, Headscale/Headplane. For more details, the official website will surely answer many of your questions. If a Docker installation is more suitable for your environment, I invite you to visit BlackVoid’s excellent post. It also details the configuration for using Pocket-ID to connect to Synology DSM.

Preamble

Since I’m not short on memory on my Proxmox node and I’m very particular about the security of my home lab, I set about installing a VM. This was laborious, as up to version 0.53, there were two services, numerous dependencies, and required compilations. Despite the many obstacles, I succeeded in my mission! Then, a few hours later, without even having time to savour my small victory, version 1.0 was released. This major revision greatly simplifies things, as it’s an executable. Here is the procedure for a streamlined installation as a service with a non-root user in a Proxmox Debian VM. This procedure should be relatively easy to adapt to other types of configurations. There are many other methods for installing Pocket-ID; I invite you to consult the installation page on the Pocket-ID website.

Preparing the VM in Proxmox

If you are in Proxmox, I invite you to install a Debian VM using the Proxmox Helper Scripts. It is also possible to do this with a lighter Alpine, but I have not yet embarked on this adventure. Choose the advanced mode and adjust for your situation, and change the hostname to “pocketid”.

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-vm.sh)"

QEMU

It’s a good practice to install the QEMU Guest Agent on your VM. You can follow these steps in the Proxmox console of the VM.

Update your package list:

sudo apt update && sudo apt upgrade -y

Install the Proxmox QEMU Guest Agent package:

sudo apt install qemu-guest-agent -y

Start and enable the service:

sudo systemctl start qemu-guest-agent
sudo systemctl enable emu-guest-agent

Verify that the service is running:

sudo systemctl status qemu-guest-agent

This will ensure that the Proxmox QEMU Guest Agent is successfully installed and running on your Debian system.

Installing Pocket-ID

Get Debian ready

apt update && apt upgrade -y

Create a dedicated user

useradd -r -s /bin/false -m -d /opt/pocket-id pocketid

Install curl needed for the installation

apt install -y curl

Downloading and Installing Pocket-ID

cd /opt/pocket-id
ARCH="amd64"
LATEST_RELEASE_URL=$(curl -s https://api.github.com/repos/pocket-id/pocket-id/releases/latest | grep "browser_download_url.*pocket-id-linux-${ARCH}" | cut -d '"' -f 4)
sudo curl -L -o pocket-id "${LATEST_RELEASE_URL}"
sudo chmod +x pocket-id
sudo chown pocketid:pocketid pocket-id

Creating directories for Pocket-ID data

sudo mkdir -p /opt/pocket-id/data/uploads

Pocket-ID configuration file. To locate visitors’ IP addresses, I invite you to create an API key on the Maxmind website.

sudo nano /opt/pocket-id/.env
APP_URL=https://id.xxxx.xx
PORT=1411

# Database: SQLite, file located at /opt/pocket-id/data/db.sqlite
# (relative to WorkingDirectory=/opt/pocket-id)
DB_CONNECTION_STRING=file:data/db.sqlite?_journal_mode=WAL&_busy_timeout=2500&_txlock=immediate

# Optional: Maxmind License Key for IP Geolocation
MAXMIND_LICENSE_KEY="YOUR-MAXMIND-LICENSE-KEY"

# Optional: Logging level (debug, info, warn, error)
LOG_LEVEL=info

Make sure all Pocket-ID files have the correct user permissions.

sudo chown pocketid:pocketid /opt/pocket-id/.env
sudo chmod 600 /opt/pocket-id/.env

Setting up the Pocket-ID service

sudo nano /etc/systemd/system/pocketid.service
[Unit]
Description=Pocket ID Application Server
After=network.target

[Service]
Type=simple
User=pocketid
Group=pocketid
WorkingDirectory=/opt/pocket-id
ExecStart=/opt/pocket-id/pocket-id
EnvironmentFile=/opt/pocket-id/.env

Restart=always
RestartSec=10

NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable pocketid.service
sudo systemctl start pocketid.service
sudo systemctl status pocketid.service

If everything goes as planned, you should receive a confirmation.

● pocketid.service - Pocket ID Application Server
     Loaded: loaded (/etc/systemd/system/pocketid.service; enabled; preset: enabled)
     Active: active (running) since Sun 2025-06-01 07:12:57 EDT; 7s ago
   Main PID: 28699 (pocket-id)
      Tasks: 8 (limit: 2309)
     Memory: 8.7M
        CPU: 65ms
     CGroup: /system.slice/pocketid.service
             └─28699 /opt/pocket-id/pocket-id

Jun 01 07:12:57 pocketid systemd[1]: Started pocketid.service - Pocket ID Application Server.
Jun 01 07:12:57 pocketid pocket-id[28699]: 2025/06/01 07:12:57 Starting job scheduler
Jun 01 07:12:57 pocketid pocket-id[28699]: 2025/06/01 07:12:57 Server listening on 0.0.0.0:1411
Jun 01 07:12:57 pocketid pocket-id[28699]: 2025/06/01 07:12:57 GeoLite2 City database is up-to-date
Jun 01 07:12:57 pocketid pocket-id[28699]: 2025/06/01 07:12:57 Job "UpdateGeoLiteDB" run successfully
Jun 01 07:12:57 pocketid pocket-id[28699]: 2025/06/01 07:12:57 Job "SyncLdap" run successfully
Jun 01 07:12:57 pocketid pocket-id[28699]: 2025/06/01 07:12:57 Job "SendHeartbeat" run successfully

Caddy Reverse Proxy

Here is my Caddyfile with the /robots.txt file in case it is respected (it’s nice to dream).

# Snippet for robots.txt
(common_robots_txt) {
	handle /robots.txt {
		# Set the Content-Type header
		header Content-Type "text/plain; charset=utf-8"
		# Respond with the body and status code 200
		respond `User-agent: *
		Disallow: /` 200
	}
}

# Pocket-ID
id.xxxx.xx {
	import common_robots_txt
	# Fallback to reverse proxy for other requests
	reverse_proxy 192.168.x.yyy:1411 [xxxx:xxxx:xxxx:xxxx::yyyy]:1411
}
systemctl reload caddy.service 

For the first setup of your Pocket-ID instance, I invite you to create your administrator account at https://id.xxxx.xx/login/setup.It is strongly encouraged to have two passkeys since this is the only way to authenticate. I have one in Bitwarden/Vaultwarden and another in iCloud Keychain (via Safari).

Update

Since we’re not using Docker, updating can be a bit more complicated. However, it’s nothing insurmountable, especially since version 1.0 is already compiled. Here’s a small update script that allowed me to move from version 1.0 to 1.1.0 without a hitch.

nano /root/update-pocketid.sh
#!/bin/bash
# update-pocketid.sh

# --- Configuration ---
INSTALL_DIR="/opt/pocket-id"
SERVICE_NAME="pocketid.service"
USER="pocketid"
GROUP="pocketid"
VERSION_FILE="${INSTALL_DIR}/version.txt" 
ARCHITECTURE="amd64" # Change if needed (e.g., arm64)
# --- End Configuration ---

echo "Checking for the latest version of PocketID..."
LATEST_TAG_JSON=$(curl -s https://api.github.com/repos/pocket-id/pocket-id/releases/latest)
LATEST_TAG=$(echo "$LATEST_TAG_JSON" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/') # Version without 'v'
LATEST_TAG_WITH_V=$(echo "$LATEST_TAG_JSON" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') # Version with 'v'

if [ -z "$LATEST_TAG" ]; then
    echo "Could not retrieve the latest version from GitHub."
    exit 1
fi

echo "Latest version available: v${LATEST_TAG}"

CURRENT_VERSION="0" # Default to 0 if no version file
if [ -f "$VERSION_FILE" ]; then
    CURRENT_VERSION=$(cat "$VERSION_FILE")
fi
echo "Currently installed version: v${CURRENT_VERSION}"

if [ "$LATEST_TAG" = "$CURRENT_VERSION" ]; then
    echo "PocketID is already up to date (v${CURRENT_VERSION})."
    exit 0
fi

echo "New version v${LATEST_TAG} available. Updating..."

DOWNLOAD_URL=$(echo "$LATEST_TAG_JSON" | grep -E "browser_download_url.*pocket-id-linux-${ARCHITECTURE}" | cut -d '"' -f 4)

if [ -z "$DOWNLOAD_URL" ]; then
    echo "Could not find the download URL for linux-${ARCHITECTURE} and version v${LATEST_TAG}."
    exit 1
fi

echo "Stopping service ${SERVICE_NAME}..."
sudo systemctl stop "${SERVICE_NAME}"

echo "Backing up the old binary..."
BACKUP_NAME="pocket-id_backup_v${CURRENT_VERSION}_$(date +%Y%m%d_%H%M%S)"
sudo cp "${INSTALL_DIR}/pocket-id" "${INSTALL_DIR}/${BACKUP_NAME}"
echo "Old binary backed up to ${INSTALL_DIR}/${BACKUP_NAME}"

Make the script executable

sudo chmod +x /root/update-pocketid.sh

Create an alias

alias update='/root/update-pocketid.sh'

Consider updating the system before updating Pocket-ID.

sudo apt update && sudo apt upgrade -y

If you are using Proxmox, I encourage you to take a snapshot just before the update and have regular backups (you never know :-).

Conclusion

Now, you are ready to step into the future with OIDC and Passkey. You can visit the Client Examples page to easily configure your services with Pocket-ID. I wish you a safe journey into the exciting world of self-hosting!

Leave a Reply

Your feedback is valuable for us. Your email will not be published.