#!/bin/sh # Restores a Siftlode Postgres dump created by backup.sh into the running db container. # Usage: ./scripts/restore.sh backups/siftlode-YYYYMMDD-HHMMSS.dump # # Restoring the database does NOT restore the secrets that decrypt it. If this dump is from an # instance with a different TOKEN_ENCRYPTION_KEY than the current .env, the encrypted sysconfig # values and refresh tokens will not be readable. See docs/self-hosting.md § Backups. set -e # See backup.sh: the documented self-host layout ships only docker-compose.selfhost.yml, so plain # `docker compose` has no configuration file to find there. if [ -z "${COMPOSE_FILE:-}" ] && [ ! -f docker-compose.yml ] && [ -f docker-compose.selfhost.yml ]; then COMPOSE_FILE=docker-compose.selfhost.yml export COMPOSE_FILE fi FILE="$1" if [ -z "$FILE" ]; then echo "Usage: restore.sh "; exit 1; fi if [ ! -f "$FILE" ]; then echo "Dump file not found: $FILE"; exit 1; fi USER_NAME="${POSTGRES_USER:-siftlode}" DB_NAME="${POSTGRES_DB:-siftlode}" # Stop the app before dropping its tables. `--clean` DROPs each object in its own transaction, and a # running api (with its scheduler) holds locks that make those DROPs queue behind it — abort the wait # and the database is left half-dropped. It also keeps the app from writing into a schema that is # only partly back. # # It is NOT restarted automatically, on purpose. `docker compose start` re-runs the container's # command, and that runs `alembic upgrade head` — which on the documented rollback path (restore a # pre-upgrade dump, THEN pin the older IMAGE_TAG) would immediately re-apply the very migrations the # restore just undid. And on a FAILED restore it would put the app back on a half-restored schema, # which is the second thing the stop exists to prevent. The operator starts it when they are ready. if ! docker compose stop api worker; then echo "Could not stop api/worker — refusing to restore while the app may still be writing." >&2 exit 1 fi # pg_restore exits non-zero for ignorable warnings too (--clean --if-exists on a fresh database # reports every missing object it tried to drop), so a non-zero code is reported as "check this", # not swallowed and not dressed up as success. if docker compose exec -T db pg_restore -U "$USER_NAME" -d "$DB_NAME" --clean --if-exists < "$FILE"; then echo "Restored $FILE" echo "api and worker are stopped. Start them with: docker compose start api worker" else echo "pg_restore reported errors while restoring $FILE — read the output above before treating this instance as restored." >&2 echo "api and worker are left STOPPED: the database may be half-restored. Fix the dump and re-run before starting them." >&2 exit 1 fi