Self-Hosting
Pad is local-first. By default it runs on your laptop and binds to 127.0.0.1:7777 — that’s the recommended setup for solo use.
If you want to reach your own Pad from multiple devices you control — laptop + phone + work machine on the same LAN, Tailscale, or home VPN — running it on a small server or NAS works well. This page covers that single-user, multi-device setup.
Setting up Pad for a team? Pad Cloud is the supported path for multi-user setups — managed hosting, OAuth, team billing, automatic backups, no operator burden. The recipes below assume one user across multiple devices.
Running as a Service
systemd (Linux)
Create a service file at /etc/systemd/system/pad.service:
[Unit]
Description=Pad
After=network.target
[Service]
Type=simple
User=pad
Group=pad
ExecStart=/usr/local/bin/pad server start --host 0.0.0.0 --port 7777
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target Enable and start:
sudo systemctl enable pad
sudo systemctl start pad The Pad data directory defaults to $HOME/.pad for the user running the service — with User=pad, that’s /home/pad/.pad/pad.db. Override with PAD_DATA_DIR=/srv/pad (or similar) in the service Environment= if you want a different location.
Docker
docker run -p 7777:7777 -v pad-data:/data ghcr.io/xarmian/pad Or build the image yourself:
git clone https://github.com/xarmian/pad
cd pad
docker build -t pad .
docker run -p 7777:7777 -v pad-data:/data pad Inside the container, Pad writes to /data — back up that volume. The default port publishing above (-p 7777:7777) exposes Pad on every host interface; for a single-user host bind to a specific interface (e.g. your Tailscale IP) or stick with -p 127.0.0.1:7777:7777 and reach Pad through a reverse proxy.
Reverse Proxy
A reverse proxy in front gives you HTTPS — required if you want session cookies to be flagged Secure (set PAD_SECURE_COOKIES=true).
Caddy
pad.example.com {
reverse_proxy localhost:7777
} Caddy automatically handles TLS via Let’s Encrypt. Simplest option.
Nginx
server {
listen 443 ssl;
server_name pad.example.com;
ssl_certificate /etc/ssl/certs/pad.pem;
ssl_certificate_key /etc/ssl/private/pad.key;
location / {
proxy_pass http://127.0.0.1:7777;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_buffering off; # Required for SSE
}
} If you put any reverse proxy in front, also set PAD_TRUSTED_PROXIES to the proxy’s IP/CIDR so Pad will honor the forwarded headers (rate limits, audit logs, the bootstrap loopback check).
Data & Backups
Pad stores everything in a single SQLite database file. The path depends on how you’re running it:
| Setup | Database path |
|---|---|
| Local install (the default) | ~/.pad/pad.db |
systemd (User=pad) | /home/pad/.pad/pad.db |
| Docker | /data/pad.db (inside the container; persisted by the named volume) |
Override the location with PAD_DATA_DIR=/some/path — the database lives at $PAD_DATA_DIR/pad.db.
Backing up
# Local install — stop Pad first OR use SQLite's online backup
sqlite3 ~/.pad/pad.db ".backup '~/.pad/pad.db.backup'"
# systemd
sudo systemctl stop pad
sudo cp /home/pad/.pad/pad.db /home/pad/.pad/pad.db.backup
sudo systemctl start pad
# Docker — copy the volume contents
docker run --rm -v pad-data:/data -v "$PWD":/backup alpine
cp /data/pad.db /backup/pad.db.backup The database file is the single source of truth. There are no external dependencies to back up.
Authentication
Pad uses user accounts with email + password. On a fresh install, there are no users — the server runs unauthenticated until the first admin is bootstrapped, then every API request and the web UI require auth.
First-time setup
# On the server host (loopback-only — see note below):
pad auth setup pad auth setup is gated on the loopback check: it runs only when the request comes from the server itself. That’s deliberate — it creates the unauthenticated window during which the first admin is minted, and forces you to be physically on the server (or docker exec-ing into the container) to do it.
Subsequent logins
pad auth login # email + password prompt
pad auth whoami # show current user
pad auth logout Credentials are stored at ~/.pad/credentials.json (mode 0600). The CLI auto-attaches the auth token to all API requests.
User accounts and roles
Once the first admin exists, additional users can be created two ways:
# Admin creates an account directly
pad auth register # admin-only after setup
# Or invite a user to a workspace (preferred for collaboration)
pad workspace invite [email protected] --role editor
pad workspace join <code> # accepter runs this Roles:
| Role | Permissions |
|---|---|
owner | Full workspace access including settings, member management |
editor | Create / edit / delete items |
viewer | Read-only |
API tokens
For CI, agents, or scripts:
# Create a token, optionally scoped
pad auth tokens create "ci-bot" --scopes '["read"]' Scopes: ["*"] for full access (use sparingly), ["read"] for read-only. Unrecognized scopes are denied.
Security Considerations
- Run
pad auth setupimmediately after first start. Until you do, the server accepts unauthenticated requests from loopback. - For anything reachable beyond your laptop, terminate TLS in front (reverse proxy) and set
PAD_SECURE_COOKIES=trueso session cookies are flaggedSecure. - Set
PAD_CORS_ORIGINSto the comma-separated origins of your web UI hosts. Without it, cookie-authenticated browser fetches from other origins are rejected. - The SQLite database is unencrypted on disk. Protect it with filesystem permissions (the default
0600is correct). - The default bind is
127.0.0.1— Pad won’t accept remote connections unless you opt in with--host 0.0.0.0orPAD_HOST=0.0.0.0.