Run Pad on Unraid
Pad runs as a Community Applications template on Unraid. Single container, SQLite by default, no telemetry, your data stays on your tower. About 60 seconds from Apps tab to first item.
Prerequisites
- Unraid 6.12 or later
- Community Applications plugin installed
Install — recommended (CA-approved)
Once Pad is approved in the CA index, this is the path:
- Open Apps in the Unraid web UI.
- Search for Pad.
- Click Install, review the form, click Apply.
- Wait ~10 seconds for the container to come up healthy.
- Click Logs, find the banner that starts with
Pad first-run setup, copy the URL.
The URL looks like http://<your-tower>:7777/setup#token=<TOKEN>. Paste it in your browser, fill in email/name/password, you’re in.
Install — manual (pre-CA approval)
Before Pad lands in the public CA index, you have two routes for installing the template manually. The “Add Repository” / “Template Repositories” feature in older Unraid versions was removed in Unraid 6.10.0-rc1 and is not coming back, so the recommended path is now a direct sideload.
Route A — CA Private Folder (recommended)
The closest experience to what you’ll get post-CA-approval. The template appears under CA’s Private category and uses the same install form CA users will see.
ssh root@<your-tower>
mkdir -p /boot/config/plugins/community.applications/private/perpetualsoftware
wget -O /boot/config/plugins/community.applications/private/perpetualsoftware/pad.xml
https://raw.githubusercontent.com/PerpetualSoftware/unraid-templates/main/pad/pad.xml Then in the Unraid web UI: open Apps, search for “Pad” (or browse to the Private category). The listing shows the icon + Overview just like a CA-published app. Click Install → form → Apply — same flow as a CA-approved install.
To remove later: delete the file. To update: re-run the wget (CA picks up the new file on next page load).
Route B — Docker tab sideload (CA-less fallback)
If you don’t have CA installed, or want to skip CA’s listing UI entirely:
ssh root@<your-tower>
wget -O /boot/config/plugins/dockerMan/templates-user/my-pad.xml
https://raw.githubusercontent.com/PerpetualSoftware/unraid-templates/main/pad/pad.xml Then in the Unraid web UI: Docker tab → Add Container → Template dropdown → pick Pad.
Functionally identical container behavior; just skips the CA UX surface.
First-run setup
Pad’s bootstrap is gated to the container’s loopback interface, so the standard “open the URL and create an admin” path doesn’t work for a remote container — the request would come from your laptop, not from inside the container itself. Pad solves this with a one-time token logged on first start.
The container generates the token automatically when it starts up with no users. It writes the token to its log output, the appdata file .bootstrap-token (mode 0600), and includes it in a banner that’s easy to spot:
========================================================================
Pad first-run setup
========================================================================
No users exist yet. To create the first admin account, visit:
http://<your-host>:7777/setup#token=<TOKEN>
This token is one-time. After the first admin is created the token
is consumed and this banner stops appearing.
To regenerate, delete /data/.bootstrap-token and restart.
======================================================================== To find it: in Unraid → Docker → click the Pad container → Logs. Scroll for “Pad first-run setup”. Copy the URL.
The #token= part is a URL fragment. Fragments are browser-only — they’re never transmitted to a server, so the token can’t end up in proxy logs, access logs, or browser history shared by URL-shortener tools. The frontend reads the fragment, scrubs it from the address bar before paint, and submits the token via an X-Bootstrap-Token header.
Token regeneration only works on never-bootstrapped instances. Once a user exists, the token file is automatically cleaned up on every startup. To “regenerate” on an already-claimed instance you’d need to wipe the user table — that’s a wholly different operation.
Bypass the token (open setup)
If the container is on a network you already trust — Unraid behind your home firewall, a Tailscale-only deployment, anything that isn’t reachable from the open internet — flipping Bypass Setup Token to true lets you skip the copy-from-logs step. The first time you visit http://<your-tower>:7777/setup, the form takes your email + name + password directly and creates the admin in one round-trip.
What changes when the field is true:
- No bootstrap token is generated at startup; no
.bootstrap-tokenfile is written. - The container logs a different banner:
Pad first-run setup (open mode)plus a WARN-level slog entry so log aggregators can flag it. POST /api/v1/auth/bootstrapaccepts the first-admin request from any IP without anX-Bootstrap-Tokenheader — but only while zero users exist. The moment you create the admin, the surface closes; subsequent bootstrap requests get a409 Conflictregardless of the flag.
When NOT to use it:
- The WebUI port is exposed to the public internet (port-forwarded on your router, exposed on a cloud provider, etc.). Anyone who races you to
/setupbecomes admin. - The Pad instance is in cloud mode (
PAD_CLOUD=true/PAD_MODE=cloud) — the flag is ignored there by design and the loopback-only gate stays on.
You can flip it off after the admin exists; it has no effect at that point but unsetting it keeps the configuration honest.
Form fields explained
| Field | Default | What it does |
|---|---|---|
| WebUI Port | 7777 | Host port Pad listens on. Change if 7777 is in use. |
| Appdata | /mnt/user/appdata/pad/ | Where Pad stores its database, attachments, encryption key, logs, and config. |
| PUID | 99 | UID the pad process runs as inside the container. 99 = Unraid nobody (matches appdata default). |
| PGID | 100 | GID similarly. 100 = Unraid users. |
| Log Level | info | Verbosity for the server log. Set to debug when troubleshooting. |
| Bypass Setup Token | false | If true, the first admin can be created directly via http://<your-tower>:7777/setup — no bootstrap token required. Default false (token required from container logs). Only safe on trusted networks; see First-run setup below. |
| Public URL | (empty) | Set if reverse-proxying. Required so emailed invitation links point at the real hostname (not http://<unraid-ip>:7777). |
| Maileroo API Key | (empty) | Optional. Enables email invitations. Without it, invites use copyable join codes. |
| Email From | (empty) | Sender address (required if Maileroo key is set). Must be on a domain you control. |
| Email From Name | Pad | Display name on outbound emails. |
PUID/PGID are validated by the entrypoint — 0 (root), empty, and non-numeric values are rejected with a clear error. The default 99:100 matches Unraid’s nobody:users convention; you should rarely need to change them.
Where your data lives
All persistent state lives under the appdata path you set in the form (default /mnt/user/appdata/pad/):
| Path | What | Critical? |
|---|---|---|
pad.db + pad.db-wal + pad.db-shm | SQLite database | yes |
encryption.key | Encryption key for sensitive fields (TOTP seeds, OAuth tokens) | yes — losing this bricks encrypted data |
attachments/ | Uploaded attachment blobs | yes |
logs/server.log | Server log | nice-to-have |
config.toml | Workspace config | nice-to-have |
pad.pid | PID file | ephemeral |
.bootstrap-token | First-run setup token (auto-deleted on first admin claim) | one-time |
One mount, complete coverage. The default appdata field gives you all of this without thinking about it.
Backups
Stop the container first so SQLite isn’t mid-write. Then:
# Backup
tar -C /mnt/user/appdata -czf "pad-backup-$(date +%F).tar.gz" pad/
# Restore
tar -C /mnt/user/appdata -xzf "pad-backup-2026-05-06.tar.gz" The -C flag makes both archive and restore relative to /mnt/user/appdata. The pad/ directory inside the tarball always lands at /mnt/user/appdata/pad/ regardless of where you run the command. Without -C, GNU tar warns “Removing leading /” and stores relative paths anyway, but extracting from a different cwd would scatter the data — easy to get wrong.
The container’s entrypoint runs a chown -R to your configured PUID:PGID on every start, so PUID/PGID don’t have to match between source and destination Unraid hosts.
Upgrading
Unraid → Docker → click the Pad container → Force Update. CA pulls a fresh :latest, recreates the container, and your appdata persists.
Pad releases are at github.com/PerpetualSoftware/pad/releases. Watching that repo on GitHub gets you a notification when a new version ships.
Reverse proxy
If you front Pad with SWAG or NGINX Proxy Manager for HTTPS + a real hostname:
- In the template’s Public URL field, enter your external URL:
https://pad.example.com. - In your reverse proxy, point
pad.example.comat<unraid-host>:7777. - Standard reverse-proxy headers are fine — Pad doesn’t need any special config beyond Public URL.
The Public URL field is required if you want emailed invitations to point at your real hostname. Without it, invitation links default to http://<unraid-ip>:7777, which recipients off your network can’t reach.
For the broader self-hosting reverse-proxy guide (Caddy, Nginx full config, TLS hardening), see Self-Hosting → Reverse Proxy.
Email (optional)
If you want Pad to send workspace invitations by email:
- Sign up at maileroo.com (free tier is fine for low volumes).
- Paste the API key into the Maileroo API Key field (it’s masked in the form — won’t show in container logs).
- Set Email From to a sender address on a domain you control.
- Optionally customize Email From Name (defaults to “Pad”).
Without these, invitations fall back to copyable join codes you paste into the invitee’s CLI. Not worse — just different.
Connect your CLI and agents
Once Pad is running and you’ve claimed the first admin, point your local CLI + AI coding tool at the Unraid instance.
CLI + skill setup (on your laptop, NOT in the container):
pad init --url http://<your-tower>:7777 --workspace <your-workspace-slug>
pad agent install claude # installs the /pad skill into Claude Code
# use "cursor" / "windsurf" / "codex" / etc. for other tools pad init --url configures the local CLI to talk to your remote Pad instance. pad agent install installs the natural-language /pad skill into your AI tool of choice. Reach across LAN with <your-tower>:7777, or use Tailscale / Cloudflare Tunnel for off-network access.
MCP setup (separate from the skill above) — if your tool of choice is an MCP client (Claude Desktop, Cursor, Windsurf with MCP), Pad also runs as a local MCP server:
pad mcp install claude-desktop # writes the MCP config entry See MCP — Local for the full MCP setup walkthrough (per-client config files, troubleshooting). The skill (pad agent install) and MCP (pad mcp install) are independent integrations — you can use either, both, or neither.
Public URL ≠ agent target. The Public URL template field controls server-generated links (emailed invitations, share URLs);
pad init --urlcontrols how the CLI on each device finds the server. You typically want both filled in: Public URL for clean email links,pad init --urlon each agent host.
For per-tool walkthroughs see Agent Integration, Connect a Workspace, and MCP — Local.
Troubleshooting
- Port 7777 already in use → change the WebUI Port field to a free port (e.g. 7778).
- Container starts but UI returns 500 → check the Logs for entrypoint errors. Common causes: invalid PUID/PGID (entrypoint validates and rejects
0, empty, or non-numeric values with a clear message at the top of the log). - Image won’t pull → verify GHCR is publicly pullable:
docker pull ghcr.io/perpetualsoftware/pad:latestfrom any machine. - Reverse proxy returns 502 → it’s a reachability problem, not a Pad config problem. Check:
- The proxy’s
proxy_pass/reverse_proxyupstream points at<unraid-host>:7777(or whatever WebUI Port you set) with schemehttp://for the in-LAN hop. - The Pad container is actually running and healthy (Docker tab → green health check).
- If your proxy is in a different Docker network, the two need to share a network (or the proxy needs to reach Unraid’s host IP on the published port).
PAD_TRUSTED_PROXIESdoes NOT cause 502s — when unset, Pad just ignores forwarded headers (client IP falls back to the TCP peer). Set it only if you want accurate client IPs in rate limits / audit logs / the bootstrap loopback check.
- The proxy’s
- Emailed invitation links point at the wrong hostname → set the Public URL field to the URL users actually browse to (e.g.
https://pad.example.com). - First-run banner not showing in logs → it logs once on first start when zero users exist; already-bootstrapped instances suppress it. On a not-yet-bootstrapped instance: stop, delete
/mnt/user/appdata/pad/.bootstrap-token, restart (only works pre-bootstrap; once a user exists, the token file is auto-cleaned on every startup). If you set Bypass Setup Token totrueyou’ll see the open-mode banner instead — same gate, different message. - /setup keeps asking for a token even though Bypass Setup Token is set → check that the env var actually reached the container: Docker → click Pad → Edit → confirm
PAD_BYPASS_SETUP_TOKEN=true. Then Force Update (a value change without recreate doesn’t re-evaluate startup-time env vars). Cloud mode also ignores the flag — make surePAD_CLOUD/PAD_MODEaren’t set.
Going further
- Connect a workspace — wire your laptop CLI into the Unraid instance.
- Agent integration — Claude Code / Cursor / Windsurf / Codex setup.
- MCP — Local — Pad as an MCP server for MCP-aware tools.
- Self-Hosting — fundamentals (auth, security, Pad Cloud comparison) that apply to any host, not just Unraid.
- Pad source: github.com/PerpetualSoftware/pad
- Unraid template source: github.com/PerpetualSoftware/unraid-templates — the canonical home of
pad.xml. PRs to tweak the template (typo, new env var, polished overview) go there.