Files
pad/unraid
xarmian 4c62a27e3b fix(unraid): correct install instructions + add AI category (BUG-1181) (#427)
* fix(unraid): correct install instructions + add AI category (BUG-1181)

The "Add Repository" / "Template Repositories" feature in older Unraid
was removed in 6.10.0-rc1 — the install path documented as Route A
("Apps → Settings → Add Repository → paste URL") doesn't exist on any
modern Unraid. Caught during TASK-1171 smoke-test prep when the
operator couldn't find the option in CA's UI on a current Unraid
install.

Replaces both routes in unraid/README.md with the working sideload
paths:

- Route A → CA Private Folder
  /boot/config/plugins/community.applications/private/perpetualsoftware/pad.xml
  Template appears under CA's "Private" category. Closest experience
  to a CA-approved install — same listing UI, same install form.
- Route B → Docker tab sideload (CA-less fallback)
  /boot/config/plugins/dockerMan/templates-user/my-pad.xml
  Skips CA entirely. Works without the CA plugin installed.

Verified live: dave manually sideloaded via the new Route A; the Pad
card renders with icon + Overview in CA, install form opens cleanly.

Also updates <Category> from "Productivity: Tools:" to "Productivity:
AI:" — Tools was generic and unrelated; AI was added to CA's taxonomy
recently (114 apps in that category) and matches Pad's "agent era"
framing better. Verified syntax against the Unraid Docker Template
Schema wiki: space-separated categories within a single <Category>
element, each "Top:Sub" or "Top:" form.

Sibling pad-web PR fixes the same two routes in
/docs/self-hosting/unraid.

Source for the Template Repositories removal:
https://forums.unraid.net/topic/114809-i-want-to-make-my-own-private-template-repository-but-it-doesnt-work/

* fix(unraid): align pad.xml with dockerMan SAVE serializer (BUG-1181)

Verified against a real dockerMan SAVE round-trip on Unraid 7.x — diff
captured during TASK-1171 smoke test. Migrating structural divergences
back into the canonical template so we stay on Squid's good side per
the wiki's blacklist warning.

Element changes:
- Add <MyMAC/>, <ReadMe/>, <Requires/>, <TailscaleStateDir/> empty
  markers (recent dockerMan emits them all)
- Remove <Description> block — not part of the recognized schema;
  dockerMan strips it on SAVE. <Overview> is the canonical CA-displayed
  text and already covers the same ground.
- Switch 3 empty <Config></Config> blocks (Public URL, Maileroo API
  Key, Email From) to self-closing <Config .../> form — matches
  dockerMan's serializer output.
- Drop BBCode wrapping ([b]Logs[/b] -> Logs) in Overview — dockerMan
  strips BBCode during SAVE, so the formatting was dead weight.
- Remove all XML comments — dockerMan strips them on SAVE round-trip.
  Maintainer rationale moved to unraid/README.md's new "Template
  format conformity" section instead.

Final element order verified element-by-element to match dockerMan's
output: 33 top-level elements, position-aligned.

Cosmetic differences left as-is (XML-equivalent, won't trip Squid):
- Em dashes (literal — vs &#x2014;)
- Line endings inside <Overview> (LF-only vs CRLF as &#13;\n)

unraid/README.md gains a "Template format conformity" section
documenting the wiki guidance, the SAVE-diff verification workflow,
and the specific dockerMan behaviors that bit us (comments stripped,
<Description> dropped, BBCode stripped, self-closing empty elements).
Note: this README itself doesn't ship through CA so the maintainer
context is safe here.

Stacks on the existing BUG-1181 commit (install-route + AI category
fixes) — same theme of "Unraid template + docs correctness for CA
submission".

* fix(unraid): convert em dashes to numeric entities to match dockerMan SAVE output

Last cosmetic diff between our hand-written pad.xml and what dockerMan
emits on a SAVE round-trip. XML-equivalent (both render the same em
dash glyph), but eliminating the visual diff makes future
SAVE-roundtrip checks cleaner and removes any tail risk of CA
treating literal U+2014 differently from the numeric entity.

Three occurrences converted: Overview text + PUID/PGID Config
descriptions. After this, the only remaining diff against a
post-Apply dockerMan SAVE is <Overview> line endings (LF vs CRLF) and
<DateInstalled> (operator-stamped) — both XML-equivalent and
expected-to-differ respectively.

Last cosmetic touch on PR #427.
2026-05-06 10:25:48 -04:00
..

Pad on Unraid

Run Pad on your Unraid server — local-first project management for developers and AI coding agents. Single Go binary, SQLite by default, your data stays on your box.

This directory holds the Community Applications (CA) template (pad.xml) and supporting docs. The full install walkthrough lives at https://getpad.dev/docs/self-hosting/unraid.

Prerequisites

  • Unraid 6.12 or later.
  • Community Applications plugin installed.

Install

Once Pad is approved in the CA index (tracked in PLAN-1166), the easy path:

  1. Open Apps in the Unraid web UI.
  2. Search for Pad.
  3. Click Install → adjust the form fields if you want non-default ports / appdata path → Apply.
  4. Wait ~10 seconds for the container to come up healthy.
  5. Click the container's Logs button. Look for the box that starts with Pad first-run setup and copy the URL — it looks like http://<your-tower>:7777/setup#token=<TOKEN>.
  6. Paste that URL into your browser. The token is read from the URL fragment (browser-only — never sent to a server log) and stripped from the address bar before you create the admin account. Fill in email, name, password → done.

Pre-CA install (manual, for early adopters)

Before Pad lands in the 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.

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/pad/main/unraid/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/pad/main/unraid/pad.xml

Then in the Unraid web UI: Docker tab → Add ContainerTemplate dropdown → pick Pad.

Functionally identical container behavior; just skips the CA UX surface.

Where your data lives

All persistent state lives under your appdata path (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 in the template gives you all of this without thinking about it.

Backups

Always 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, so 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

In Unraid → Docker → click the Pad container → Force Update. CA pulls a fresh :latest, recreates the container, and your appdata persists.

Pad releases are at https://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 (recommended for HTTPS + a real hostname):

  1. In the template's Public URL field, enter your external URL: https://pad.example.com.
  2. In your reverse proxy, point pad.example.com at <unraid-host>:7777.
  3. Standard reverse-proxy headers are fine — Pad doesn't need any special config.

The Public URL is required if you want emailed invitations to point at your real hostname (otherwise links point at http://<unraid-ip>:7777, which recipients can't reach).

Email (optional)

If you want Pad to send workspace invitations by email, fill in:

  • Maileroo API Key — sign up at https://maileroo.com (free tier is fine).
  • Email From — sender address on a domain you control.
  • Email From Name — display name (defaults to "Pad").

Without these, invitations fall back to copyable join codes you paste into the invitee's CLI. Not worse — just different.

Troubleshooting

  • Port 7777 already in use → change the WebUI Port field to a free port (e.g. 7778).
  • /data write errors in the logs → the entrypoint validates PUID/PGID and rejects 0 or non-numeric. Stick to defaults (99/100) unless you have a specific reason; the entrypoint chowns appdata on every start, so a wrong-uid recovery is a single restart away.
  • Image won't pull → verify GHCR is publicly pullable: docker pull ghcr.io/perpetualsoftware/pad:latest from any machine.
  • Reverse proxy returns 502 → the Public URL field must match the public-facing scheme + host (e.g. https://pad.example.com, not http://); also verify your proxy passes the request to the container's mapped port.
  • First-run banner not showing in logs → it logs once on first start when zero users exist. If users already exist (you've already bootstrapped), the banner is suppressed. Restart only re-shows it on a fresh / wiped appdata.

Support

Icon

unraid/icon.png is a 256×256 PNG downsampled (LANCZOS) from web/static/icon-512.png — Pad's existing app icon. Reused rather than designed afresh so the CA listing matches getpad.dev favicons + PWA icons. Update both files in lockstep if the brand mark changes.

Template format conformity

The Unraid wiki (Docker Template Schema) is explicit:

"the only supported XML format is that which is generated by Unraid's docker tab when hitting SAVE on the template. Any manual editing of the XML files may present compatibility issues for CA and if they turn out to be incompatible, will result in the template being blacklisted within CA until brought into conformity with the dockerMan generated files."

To stay safely on Squid's good side, keep pad.xml aligned with what dockerMan emits when an operator hits Apply on the install form. If you're editing this file by hand, validate by:

  1. Sideload your edited copy via the Route A path above.
  2. Open it in CA, hit Apply to install it.
  3. Diff your edited copy against /boot/config/plugins/dockerMan/templates-user/<container-name>.xml (the post-SAVE serialized version).
  4. Migrate any structural diff (added/removed/renamed elements, attribute-form changes) back into the canonical file. Cosmetic diffs (line endings, em dash → numeric entity) are XML-equivalent and OK to leave.

Things to know about dockerMan's serializer that bit us on the first round:

  • XML comments don't survive a SAVE round-trip — dockerMan strips <!-- ... -->. Don't embed maintainer notes inside pad.xml; put them here in this README instead.
  • <Description> is not a recognized field — dockerMan drops it. <Overview> is the canonical CA-displayed text.
  • BBCode ([b]...[/b]) is stripped from <Overview> — use plain text for emphasis.
  • Empty <Config> elements should be self-closing (<Config .../> not <Config></Config>).
  • The schema includes empty marker elements<MyMAC/>, <ReadMe/>, <Requires/>, <TailscaleStateDir/> etc. dockerMan emits them all even when empty.

Last verified against Unraid 7.x dockerMan output on 2026-05-06.