Disaster Recovery¶
Covers losing the server host and restoring its authoritative PocketBase data. Losing the server does not mean losing your workers — they keep running your stacks' containers on their own while the server is down.
All backup management lives in Settings → Backups.
The one rule that matters¶
SECRET_KEY must survive separately from your backups. It decrypts
every stored secret (git passwords, SSH keys, integration tokens). Lose it,
and a backup is just an encrypted brick — the data's there, permanently
unreadable.
- Keep
SECRET_KEYin a password manager / vault, never next to the backup file itself. - After restored data is loaded, the startup canary verifies the key. A mismatch makes wireops refuse to start instead of silently operating with unreadable secrets.
What a built-in backup contains¶
PocketBase creates the archive from PB_DATA_DIR (default
DATA_DIR/pb_data). With the default container layout:
| Data | In the archive? | Recovery behavior |
|---|---|---|
| SQLite database, users, workers, token hashes, settings, audit data | Yes | Restored directly |
| Encrypted repository, registry, integration, and environment credentials | Yes | Require the original SECRET_KEY |
Terminal transcripts in the default pb_data/terminal_sessions |
Yes | Restored directly |
Git working copies in DATA_DIR/repos |
No | Cloned again during reconciliation |
Rendered compose artifacts in DATA_DIR/stacks |
No | Regenerated by later renders/reconciles |
Worker-side WORKER_TOKEN environment and host files |
No | Must remain on or be restored to each worker |
The database records describing repositories, stacks, jobs, revisions, and
workers are backed up, but the default top-level repository and rendered-file
directories are siblings of PB_DATA_DIR and therefore are not inside the
archive. Snapshot the complete DATA_DIR as an additional layer when exact
historical artifacts matter.
Day to day¶
- Backups: manual (on demand) or scheduled (cron), with history/download/ delete in the UI.
- Storage: local disk, or S3-compatible (AWS S3, R2, MinIO, B2, ...) via the S3 Storage integration (Settings → Integrations) — off-host storage is strongly recommended.
- Retention: cap how many scheduled backups to keep.
- Restore: replaces the PocketBase data contained in the archive and restarts
the process. PocketBase rolls back the restore if it fails mid-way. It does
not replace the default
DATA_DIR/reposorDATA_DIR/stackssiblings.
Enabling S3¶
Settings → Integrations → S3 Storage → fill in:
| Field | Value |
|---|---|
| Endpoint | provider URL, e.g. https://s3.us-east-1.amazonaws.com |
| Bucket | must already exist — wireops won't create one |
| Region | e.g. us-east-1 |
| Prefix (optional) | sub-path within the bucket; created automatically on save if it doesn't exist |
| Access Key / Secret Key | scoped to that one bucket only, read+write+list+delete. Encrypted at rest under SECRET_KEY. |
| Force path-style | on for MinIO / most self-hosted S3 |
| Encrypt content | on by default — backup archives are AES-256-GCM-encrypted client-side before upload, under SECRET_KEY (or a KMS-managed key, see below) |
| KMS (optional) | wraps the content-encryption key with AWS KMS instead of SECRET_KEY directly — set a KMS Key ID (and Region, if different from the bucket's) |
Once enabled, every backup a server creates locally is also uploaded there —
mirroring replicates, the local copy stays. The backups list shows each
one's storage (Local or Local + S3), and deleting a backup removes it
from both places. On a new server, point the S3 integration at the same
bucket/prefix before restoring — restore prefers the local copy if present,
otherwise fetches it from S3.
Restoring an uploaded file¶
Upload is locked to a real PocketBase superuser (not a wireops admin role) — accepting an arbitrary file as a future full-restore target needs the extra bar. With superuser creds: "Upload Backup" button in Settings → Backups, or send the file with a superuser token straight to the upload endpoint. No superuser? Drop the file into the server's backups folder (or the configured S3 bucket) directly — it'll show up in the UI like any other backup.
No API/UI access at all (server down, locked out, etc.): copy the
backup .zip straight onto the host's disk, into <PB_DATA_DIR>/backups/
(default pb_data/backups/) — wireops creates this folder automatically on
boot. Once the file lands there, it appears in Settings → Backups like a
normal backup and can be restored from the UI. This bypasses the upload
endpoint's superuser gate entirely, since it requires filesystem access to
the host — the same trust level as SSH access to the server.
The backups list always includes local disk, so a file dropped into
<PB_DATA_DIR>/backups/ shows up in Settings → Backups regardless of
whether the S3 integration is enabled — mirroring only adds a copy, it never
replaces the local view.
Recovering onto a new server — checklist¶
- Run the same wireops version that created the backup. Upgrade only after the restore passes; see Upgrading.
SECRET_KEYfrom your vault (never from a backup).- Same S3 integration settings as the original server, if used (Settings → Integrations → S3 Storage).
- Start the empty server. At this point it can only validate that
SECRET_KEYis well formed because the restored canary is not loaded yet. - Settings → Backups → pick the backup → restore. The restart triggered by
restore validates the archive's canary against
SECRET_KEY. If it fails, stop and supply the original key; do not create or edit secrets. - Verify: login works, stacks/jobs/workers show up, a known secret decrypts, and reconciliation recreates repository working copies/rendered artifacts.
- Existing workers reconnect with their existing plaintext
WORKER_TOKEN: the matching token hashes were restored with the database. Issue a new token only for a worker that also lost its own runtime configuration or whose token was revoked. - Create a fresh post-recovery backup after validation.
Worker recovery¶
There is no automated flow for replacing a lost worker node or adopting its orphaned stacks. Build a replacement host, install the exact matching worker version, issue a new token, reproduce its tags/Docker access, and explicitly transfer or recreate affected stacks. Do not assume a tag match automatically moves a stack: stack assignment is currently explicit.
Restore drill¶
At least once per release line, restore the newest off-host archive into a
throwaway instance using the separately stored SECRET_KEY. Run the drill on
an isolated network with a disposable worker and a disposable token — never
expose the throwaway server through the production address, and never repoint
a production worker at it. Confirm login, collection counts, one secret
decryption, one repository reconciliation, and worker reconnect (using the
disposable worker/token only). A successful upload proves that an object
exists; only a restore drill proves it is usable.