Backups and restore
What to back up, how, and how to restore — with OCI's automated backups or your own.
What to back up
| State | Where | Notes |
|---|---|---|
| PostgreSQL | The database | Accounts, sign-in state, settings, providers and models, conversations, budgets, file metadata, the audit log, embeddings. |
| Files | The local storage volume or the S3 bucket | Uploads, project files, logos. Copying only the database's file metadata is not enough. |
| Secrets | Your secret manager | AUTH_SECRET, ENCRYPTION_KEY, database credentials and deployment configuration. Without the same ENCRYPTION_KEY, stored credentials cannot be decrypted. |
Redis holds nothing that needs a backup.
Option 1: OCI's automated backups
Admin → Data & storage → Backups runs a verified pg_dump to S3 every day, with a checksummed manifest of every file object and daily and weekly retention. With Copy attachment files on, each backup also copies the files themselves (attachments, thumbnails and the uploaded logo), incrementally and by content, so a backup restores everything except your secrets. See Backups.
Copying is on for a new backup configuration. An instance that saved backup settings before v0.10 only lists files until an administrator turns copying on; while it is off, you still need bucket versioning (and ideally replication) or volume snapshots for files.
Option 2: your own
For the bundled Compose stack:
cd docker
docker compose exec -T postgres \
pg_dump --format=custom --no-owner --username=oci oci > "oci-$(date +%F).dump"For local file storage, stop API writes and archive the storage_data volume. For S3, use versioning or your provider's replication or export.
Periodically restore both the database and the files into an isolated environment and check that files download. That is the only proof a backup works.
PostgreSQL client tools
The API image includes the PostgreSQL 17 client tools. A newer pg_dump can dump an older server, never the reverse, so a PostgreSQL 18 server needs newer tools. Outside the image, install the client tools for your server version and, if they are not on PATH, set BACKUP_PG_BIN_DIR (such as /usr/lib/postgresql/17/bin). The Backups page shows the version it found.
Restoring an automated backup
Download and check
aws s3 cp --recursive s3://oci-backups/oci-backups/2026-10-02T03-00-04-123Z-ab12cd34/ ./restore/
jq -r .database.sha256 restore/manifest.json
sha256sum restore/database.dumpRestore into a new, empty database
pgvector must be available if meaning-based search was on.
createdb oci_restore
pg_restore --no-owner --role=oci --dbname=oci_restore restore/database.dumpThe dump was written to a stream, so restore it serially (no --jobs).
Restore the files
If the backup copied files (jq .files.copied restore/manifest.json prints true), use the restore-backup-files script in the API image. It reads the folder's attachments.jsonl, streams each objects/<sha256> copy back to its original storage key, checks every file's SHA-256 on the way, and leaves objects already present alone (--overwrite replaces them). It needs no database. Credentials come from the environment only:
export BACKUP_S3_BUCKET=oci-backups BACKUP_S3_REGION=us-east-1 \
BACKUP_S3_ACCESS_KEY_ID=... BACKUP_S3_SECRET_ACCESS_KEY=...
# BACKUP_S3_ENDPOINT and BACKUP_S3_FORCE_PATH_STYLE=true for MinIO and similar.
FOLDER=oci-backups/2026-10-02T03-00-04-123Z-ab12cd34/
# Local file storage (the directory STORAGE_LOCAL_PATH names), in the bundled stack.
# -e NAME passes the variable from your shell into the container.
docker compose exec -e BACKUP_S3_BUCKET -e BACKUP_S3_REGION \
-e BACKUP_S3_ACCESS_KEY_ID -e BACKUP_S3_SECRET_ACCESS_KEY \
api node dist/scripts/restore-backup-files.js "$FOLDER" --to-local /data/storage
# S3 file storage, named by TARGET_S3_BUCKET, TARGET_S3_REGION,
# TARGET_S3_ENDPOINT, TARGET_S3_ACCESS_KEY_ID and TARGET_S3_SECRET_ACCESS_KEY
# (run wherever the API image runs, with those variables set):
node dist/scripts/restore-backup-files.js "$FOLDER" --to-s3--help lists every option and variable.
Run it with --dry-run first to see what would be restored. It reports how many files were restored, already present, listed as missing when the backup was made, and failed (naming each failed key), and exits with status 1 if any failed.
The layout is simple enough for any other tool too: for each line of attachments.jsonl without "missing", copy objects/<sha256> (beside the backup folders, under the same prefix) to <key> in file storage, then check its SHA-256.
If the backup did not copy files, check them instead: every key in attachments.jsonl should exist in file storage with the listed size and SHA-256. With S3 versioning, recover a missing key from its previous version.
Start a test deployment
Point it at the restored database with the same ENCRYPTION_KEY and AUTH_SECRET and the restored file storage. Run the same or a newer OCI version: startup applies any newer migrations.
Verify, then switch
Check sign-in, a conversation, a file download and System health, run Check for orphans under storage reconciliation, then switch production over.
Restoring your own pg_dump works the same way from step 2.