Skip to content

Add backup and restore commands with Postgres 18 upgrade - #174

Merged
jouwdan merged 4 commits into
mainfrom
claude/backups-3uqgst
Aug 20, 2026
Merged

Add backup and restore commands with Postgres 18 upgrade#174
jouwdan merged 4 commits into
mainfrom
claude/backups-3uqgst

Conversation

@jouwdan

@jouwdan jouwdan commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR introduces community backup and community restore commands that bundle database dumps and uploads together for consistent disaster recovery. It also upgrades the stack from Postgres 16 to Postgres 18 and adds the postgres client tools to the runtime image.

Key Changes

  • New backup/restore system (apps/cli/src/backup.ts):

    • community backup command creates a single .tar.gz bundle containing a pg_dump, uploads (local or S3), and a manifest
    • community restore command restores bundles into a new, empty database with uploads
    • Handles both local disk and S3 uploads with sensible defaults (include local, skip S3 unless explicitly requested)
    • Uses DIRECT_DATABASE_URL for dumps to avoid transaction pooler issues
    • Includes comprehensive validation and user-friendly error messages
  • S3FileStore enhancement (packages/drivers/src/files/s3-file-store.ts):

    • Added listKeys() async generator to paginate through all objects in a bucket
    • Enables pulling entire S3 buckets into backup bundles with --uploads include
  • Postgres 18 upgrade:

    • Updated all compose files and CI workflows from postgres:16-alpine to postgres:18-alpine
    • Added postgresql18-client to the runtime Docker image so pg_dump, pg_restore, and psql are available
    • Documented migration path for existing deployments crossing major versions
  • CLI integration (apps/cli/src/index.ts):

    • Registered backup and restore commands with appropriate usage strings
  • Comprehensive testing:

    • Unit tests for manifest parsing, bundle naming, content type detection, and byte formatting
    • Full round-trip integration test in CI that seeds a board, backs it up, restores to a fresh database, and verifies content survival
    • Tests verify refusal to restore over non-empty databases
  • Documentation updates:

    • Updated operating guide with backup/restore procedures and rationale
    • Added upgrading guide section for Postgres major version transitions
    • Updated disaster recovery procedures to use new commands
    • Clarified that backups are operator responsibility, not panel-only

Notable Implementation Details

  • Backup manifests are JSON with format versioning to support future changes
  • Bundles use standard tar.gz and pg_dump --format=custom for portability and selective restore
  • Uploads are included by default for local deployments (where they live on the machine) but skipped for S3 (where they're already off-machine)
  • The restore command validates that target databases are empty to prevent accidental overwrites
  • Post-restore migrations are applied automatically and reported to the user
  • All postgres client tools are in the image so the commands work everywhere the CLI works

https://claude.ai/code/session_01LYancK1YxtRYHHEmtoqRPA

claude added 4 commits August 20, 2026 14:21
One bundle carrying a custom-format pg_dump, the uploads, and a
manifest; taken over DIRECT_DATABASE_URL when set, the same selection
migrations make. On S3 the bundle records the bucket and pulls nothing
unless --uploads include asks for every object.

restore goes only into a new, empty database, applies any migrations
the dump predates, counts the restored posts, and puts the uploads
back — onto local disk or up to the configured bucket.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYancK1YxtRYHHEmtoqRPA
postgres:18-alpine everywhere postgres:16-alpine was pinned, with the
pgdata mounts moved to /var/lib/postgresql because the 18 image keeps
its cluster below that path rather than at /data. The runtime image
adds postgresql18-client so the backup and restore verbs work where
the CLI works.

BREAKING CHANGE: an existing board does not cross the Postgres major
by redeploying — the old data directory would be ignored, not
upgraded. docs/upgrading.md#when-a-release-moves-postgres is the
backup-and-restore path across.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYancK1YxtRYHHEmtoqRPA
A backup job seeds the demo board plus an upload, takes a bundle,
restores it into a fresh database and a fresh uploads directory,
proves the restore refuses a database that is not empty, boots the
restored board and reads a seeded thread. The compose job also asserts
the image carries the pg_dump the backup verb shells out to, and the
Postgres services run 18 to match the shipped stack.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYancK1YxtRYHHEmtoqRPA
The runner image ships postgresql-16 with a configured cluster, so
/usr/bin/pg_dump is pg_wrapper resolving to 16 even after
postgresql-client-18 is installed — and pg_dump 16 aborts against the
18 service. Prepend the 18 binaries via GITHUB_PATH and fail the setup
step if version 18 is not what installed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYancK1YxtRYHHEmtoqRPA
@jouwdan
jouwdan merged commit a02be27 into main Aug 20, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants