Maintenance
Running the stack day to day, clearing history, wiping data, health checks, and upgrades.
The Settings page holds the destructive operations, plus storage config. Everything here is worth reading before clicking.
Clearing history
Two narrow, safe operations:
| Action | Deletes | Keeps |
|---|---|---|
| Clear restore history | Your restore job rows | Everything else |
| Clear migration history | Your migration job rows | Everything else |
Both are scoped to your user and touch no artifacts — restores and migrations don't produce any. They're bookkeeping: clearing clutter, not deleting data.
There's no equivalent for backup history, because a backup row owns an artifact. Delete backups from the Backups page, where deletion removes the artifact too.
Wipe everything
The nuke option deletes, for your user:
- Every database connection
- Every schedule, and unregisters its cron scheduler in Redis
- Every backup, restore, and migration job row
- Every artifact those jobs referenced, resolved from the rows themselves rather than by wiping the bucket's prefixes
It keeps your user account and your storage settings — you're left with a working, empty install rather than a broken one. Another account's databases, schedules, history, and objects are untouched.
There is no undo, and pgbr has no backup of its own metadata. "Your data" is still all of it on a single-user install — which is the normal case.
The row deletions run in one transaction, so metadata is all-or-nothing. Object deletion happens after and is best-effort — if the store is unreachable, the rows are gone and the objects are orphaned.
If your bucket has versioning enabled, wiped objects are recoverable from the provider. That's one more reason to turn it on.
Health checks
GET /api/health returns { "status": "ok" } with no authentication. It's an
unconditional liveness probe — it confirms the process is serving HTTP, not that
Postgres, Redis, or the store are reachable.
healthcheck:
test: ["CMD-SHELL", "wget -q -O - http://localhost:3000/api/health || exit 1"]
interval: 10s
timeout: 5s
retries: 3Don't read too much into a green health check. A dashboard with an unreachable
Redis still returns ok. For real assurance, watch whether scheduled backups are
completing — that exercises the whole path.
The worker has no health endpoint. It's a queue consumer with no HTTP surface; monitor it through its logs and through jobs completing.
Running the stack
Every one of these runs from your install directory — the one holding .env —
and takes -f compose.prod.yaml:
| Goal | Command |
|---|---|
| Start it, or apply changes | docker compose -f compose.prod.yaml up -d |
| Pause, keeping containers | docker compose -f compose.prod.yaml stop |
| Resume after a stop | docker compose -f compose.prod.yaml start |
| Stop and remove containers, keeping data | docker compose -f compose.prod.yaml down |
| Restart in place | docker compose -f compose.prod.yaml restart |
| Status | docker compose -f compose.prod.yaml ps |
| Follow logs | docker compose -f compose.prod.yaml logs -f |
stop and start are for a quick pause. down followed by up -d is what you
want after editing .env or the compose file. Both keep every volume, so neither
loses data.
down -v is the exception. The -v deletes the volumes — pgbr's metadata
database and every backup artifact held in the SeaweedFS service. There is
no undo, and nothing else on this list destroys anything.
Two mistakes that don't announce themselves as mistakes:
- Run these from anywhere else and Compose never reads your
.env, so it fails withrequired variable POSTGRES_PASSWORD is missing a valuerather than anything about the directory you're in. - Drop the
-f compose.prod.yamlinside a checkout of the repository and you are driving the developmentcompose.yamlinstead.
Upgrades
If you installed with the one-liner, re-run
it from your install directory and it does all of this for you — it refreshes
compose.prod.yaml, pulls both images, and restarts in the right order without
touching a single value in your .env. Run it somewhere else and you get a
second install rather than an upgrade:
curl -fsSL https://raw.githubusercontent.com/darseen/pgbr/main/scripts/install.sh | shBy hand:
Pull matching tags
docker pull ghcr.io/darseen/pgbr-dashboard:latest
docker pull ghcr.io/darseen/pgbr-worker:latestBoth images ship from the same commit. Never run mismatched versions — they share a database schema and a queue payload format.
Restart the dashboard first
Its entrypoint runs pending database migrations before the server starts. Letting it go first means the schema is current before workers touch it.
Restart the worker
It reconciles schedules and re-checks the bucket on boot.
Migrations run automatically and are not reversible. Back up pgbr's own Postgres database before upgrading — it holds your connections, schedules, and history. Losing it doesn't lose your artifacts, but it loses everything that tells you what they are.
Restarting a worker mid-job is safe: BullMQ's stalled-job detection re-delivers to
another worker, and processors are idempotent. It isn't free — a re-delivered
backup re-runs pg_dump from the start.
Backing up pgbr itself
pgbr's metadata database is a normal PostgreSQL database. You can add it to pgbr as a connection and let it back itself up.
Circular, and it fails exactly when you need it: if pgbr's own database is lost,
the backup of it is recorded in the database that's gone. Keep an independent
dump of pgbr's Postgres somewhere pgbr doesn't manage — and keep ENCRYPTION_KEY
with it, since the dump is useless without it.
Monitoring
Worth watching:
| Signal | Why |
|---|---|
| Failed jobs | Job History. A schedule failing silently is the main way backups quietly stop existing. |
| Backup sizes | A sudden drop suggests a filter change or a partial dump. |
| Bucket growth | Retention only applies through live schedules — manual backups accumulate forever. |
| Worker logs | Where reconciliation, bucket provisioning, and cleanup failures surface. |
| Redis persistence | Queue state is rebuildable, but in-flight jobs aren't. |
The single best monitor for a backup system is a restore. Restore a recent backup into a scratch database on a schedule you actually keep. Everything else tells you a file exists; only a restore tells you it's a backup.
pgbr