Backup, restore & upgrades
An Indelible instance keeps state in exactly two places: PostgreSQL (documents, highlights, users, jobs) and the object store (archived pages, images, PDFs, audio). Back up both, plus your compose file and secrets, and the instance is fully recoverable.
Service and volume names below match the compose file from the
install guide: postgres with the pgdata
volume, minio with the miniodata volume.
Backup
Section titled “Backup”PostgreSQL
Section titled “PostgreSQL”Dump the database from the running container:
docker compose exec postgres pg_dump -U indelible -Fc indelible > indelible-$(date +%F).dump-Fc produces a compressed custom-format dump that pg_restore can restore
selectively. Schedule this with cron and rotate old dumps; nightly is a sensible
default.
Object store
Section titled “Object store”If you run the bundled MinIO, mirror the bucket to a local directory with a
one-off mc container. The minio-init service already has the client image
and network access, so reuse it:
docker compose run --rm --entrypoint sh -v "$(pwd)/minio-backup:/backup" minio-init \ -c 'mc alias set src http://minio:9000 minioadmin YOUR_MINIO_PASSWORD && mc mirror src/indelible /backup'Simpler and equally valid: stop the stack and copy the volume directly.
docker compose stopdocker run --rm -v miniodata:/data -v "$(pwd)":/backup alpine tar czf /backup/miniodata-$(date +%F).tar.gz -C /data .docker compose startIf you pointed Indelible at an external S3 provider instead of MinIO, use that provider’s replication or lifecycle tooling and skip this section.
Configuration
Section titled “Configuration”Keep a copy of your docker-compose.yml and every secret in it (JWT_SECRET,
CSRF_SECRET, ASSET_COOKIE_SECRET, database and MinIO passwords). Losing
ASSET_COOKIE_SECRET or JWT_SECRET does not lose data, but it invalidates
active sessions when you rotate them.
Restore
Section titled “Restore”On a fresh host, restore configuration first, then data, then start the stack:
# 1. Bring up only the stateful servicesdocker compose up -d postgres minio minio-init
# 2. Restore the databasedocker compose exec -T postgres pg_restore -U indelible -d indelible --clean --if-exists < indelible-2026-07-29.dump
# 3. Restore the object store volume (if you used the tar method)docker run --rm -v miniodata:/data -v "$(pwd)":/backup alpine sh -c 'cd /data && tar xzf /backup/miniodata-2026-07-29.tar.gz'
# 4. Start everythingdocker compose up -dThe database and the object store must come from the same point in time. Restoring a newer database against an older bucket leaves documents whose archived assets are missing; the reverse leaves orphaned files, which is harmless but wastes space.
Upgrades
Section titled “Upgrades”Database migrations are embedded in the server binaries and run automatically at startup, so an upgrade is a pull and a restart:
docker compose pulldocker compose up -dTake a database dump before upgrading. Migrations are forward-only; rolling back to an older image after a migration has run is not supported, so the dump is your way back.
Pinning versions
Section titled “Pinning versions”Every release publishes semver tags alongside latest:
image: ghcr.io/useindelible/ind-api:0.1.0 # exact releaseimage: ghcr.io/useindelible/ind-api:0.1 # latest patch of 0.1image: ghcr.io/useindelible/ind-api:latest # tip of mainFor production, pin ind-api, ind-worker, and ind-renderer to the same
semver tag and upgrade them together. The three images are released as a set;
mixing versions across a migration boundary is untested.
Health and logs
Section titled “Health and logs”GET /api/healthreturns200when the API is up; point your uptime monitor at it.docker compose logs -f api worker renderertails the services. Logs go to stdout, so any Docker log driver works.docker compose psshows the health state ofpostgresandminiofrom their built-in healthchecks.