Vai all’articolo

Backup e ripristino

Conserva database, Storage, configurazione e chiavi di crittografia, scegli un metodo di backup e ripristina un insieme completo su una destinazione vuota.

Un backup recuperabile comprende database, byte di Storage, configurazione e chiavi corrispondenti. Scegli la procedura fisica del profilo full oppure quella logica o del provider in base alla tua installazione. Il ripristino richiede una destinazione vuota e sostituisce i suoi dati; prova l’insieme completo prima di affidarti alla copia.

Eseguire un backup a freddo del profilo full su file

Questa procedura Linux riguarda solo il profilo full con lo Storage filesystem della distribuzione Supabase ufficiale fissata dalla matrice. Il ripristino fisico richiede la stessa architettura e l’esatta immagine PostgreSQL. Per managed, S3 e installazioni dai sorgenti usa il percorso logico o del provider. Annuncia l’interruzione delle scritture e ferma anche i worker esterni. Scegli una destinazione cifrata, sufficientemente capiente e fuori dai dati attivi. Non eseguire mai down --volumes sulla fonte. La copia comprende database, Auth, metadati e byte Storage, migrazioni, policy, chiavi Vault, configurazione e identità delle versioni.

Impostare il contesto Compose installato

Sostituisci i percorsi e la release dell’esempio con quelli dell’istanza installata. Imposta MODE=full e conserva l’eventuale override di ripristino in ogni comando Compose. Usare soltanto il Compose upstream omette l’overlay e l’ambiente minddy. Non eseguire il file di ambiente come codice shell e non stamparlo. La sequenza seguente ferma tutti i servizi prima di copiare i file PostgreSQL.

Bash
set -euo pipefail
export MODE=full
export CURRENT_RELEASE_DIR=/srv/minddy/releases/vX.Y.Z
export SUPABASE_DIR=/srv/minddy/supabase
export MINDDY_ENV_FILE=/etc/minddy/instance.env
export BACKUP_ROOT=/mnt/backup/minddy
compose() {
  local files=()
  if [ "$MODE" = full ]; then
    files+=(-f "$SUPABASE_DIR/docker/docker-compose.yml")
  fi
  files+=(-f "$CURRENT_RELEASE_DIR/deploy/self-hosted/compose.$MODE.yml")
  if [ -n "${RESTORE_OVERRIDE:-}" ]; then
    files+=(-f "$RESTORE_OVERRIDE")
  fi
  docker compose --env-file "$MINDDY_ENV_FILE" "${files[@]}" "$@"
}
compose ps

Completare e verificare il backup

Esegui la copia seguente mentre le scritture sono ferme. Registra codice e immagini, archivia i dati e il volume db-config e conserva i file di deployment e l’ambiente protetto. Se non trovi db-config, interrompi la procedura. Conserva gli asset verificati, i digest OCI e l’immagine database per digest. Poiché il backup comprende sia la chiave radice sia i dati cifrati, la sua riservatezza dipende dalla cifratura esterna e dai controlli di accesso.

Bash
export BACKUP_DIR="$BACKUP_ROOT/$(date -u +%Y%m%dT%H%M%SZ)"
test ! -e "$BACKUP_DIR"
install -d -m 0700 "$BACKUP_DIR"
compose ps --format json > "$BACKUP_DIR/services.json"
compose images --format json > "$BACKUP_DIR/images.json"
git -C "$CURRENT_RELEASE_DIR" rev-parse HEAD > "$BACKUP_DIR/source-commit.txt"
git -C "$SUPABASE_DIR" rev-parse HEAD > "$BACKUP_DIR/supabase-commit.txt"
DB_CONTAINER="$(compose ps -q db)"
test -n "$DB_CONTAINER"
DB_CONFIG_VOLUME="$(docker inspect "$DB_CONTAINER" --format '{{range .Mounts}}{{if eq .Destination "/etc/postgresql-custom"}}{{.Name}}{{end}}{{end}}')"
test -n "$DB_CONFIG_VOLUME"
BACKUP_HELPER_IMAGE="$(docker inspect "$DB_CONTAINER" --format '{{.Image}}')"
printf '%s\n' "$BACKUP_HELPER_IMAGE" > "$BACKUP_DIR/postgres-image-id.txt"
STORAGE_CONTAINER="$(compose ps -q storage)"
test -n "$STORAGE_CONTAINER"
STORAGE_VOLUME="$(docker inspect "$STORAGE_CONTAINER" --format '{{range .Mounts}}{{if eq .Destination "/var/lib/storage"}}{{.Name}}{{end}}{{end}}')"
compose stop
sudo tar --numeric-owner --acls --xattrs -C "$SUPABASE_DIR" \
  -czf "$BACKUP_DIR/supabase-docker.tar.gz" docker
install -m 0600 "$MINDDY_ENV_FILE" "$BACKUP_DIR/instance.env"
docker run --rm --network none --user 0:0 --entrypoint tar \
  --mount "type=volume,src=$DB_CONFIG_VOLUME,dst=/keys,readonly" \
  "$BACKUP_HELPER_IMAGE" -czf - -C /keys . > "$BACKUP_DIR/db-config.tar.gz"
if [ -n "$STORAGE_VOLUME" ]; then
  docker run --rm --network none --user 0:0 --entrypoint tar \
    --mount "type=volume,src=$STORAGE_VOLUME,dst=/objects,readonly" \
    "$BACKUP_HELPER_IMAGE" --numeric-owner --acls --xattrs -czf - -C /objects . \
    > "$BACKUP_DIR/storage-volume.tar.gz"
fi
tar -C "$CURRENT_RELEASE_DIR" -czf "$BACKUP_DIR/deployment.tar.gz" deploy/self-hosted
if [ -n "${RESTORE_OVERRIDE:-}" ]; then
  install -m 0600 "$RESTORE_OVERRIDE" "$BACKUP_DIR/source-volume-override.yml"
fi
sudo chown "$(id -u):$(id -g)" "$BACKUP_DIR/supabase-docker.tar.gz"
(
  cd "$BACKUP_DIR"
  find . -type f ! -name SHA256SUMS -print0 | sort -z | xargs -0 sha256sum > SHA256SUMS
  sha256sum --check SHA256SUMS
)

Verificare il risultato

Copia l’insieme sigillato su un disco o host indipendente e verifica SHA256SUMS sulla destinazione. Una seconda copia sullo stesso host non protegge dalla perdita dell’host. Conserva separatamente i certificati TLS se la tua politica lo richiede. Se stai eseguendo solo un backup, riavvia con compose up -d --wait dopo la verifica. Prima di affidarti alla copia, ripristinala su una destinazione vuota e isolata, accedi con MFA e controlla ticket e allegati con gli stessi SHA-256. Conserva anche le chiavi storiche.

Il comando di backup a freddo rileva anche un volume con nome per Storage su filesystem e ne archivia i byte separatamente. Un bind mount rimane incluso in supabase-docker.tar.gz. Nessuno dei due casi copre un backend S3 o gestito da un fornitore.

Creare backup logici o gestiti dal provider

Per installazioni dai sorgenti, database gestiti e Storage personalizzato, associa comandi di database, byte e proxy alla stessa istanza. self-host:backup, self-host:update e self-host:restore sono controlli preliminari di sola lettura: non eseguono queste operazioni. Un backup completo conserva PostgreSQL con auth, storage e storia delle migrazioni, byte grezzi dei file, configurazione, chiavi e identità esatte di minddy e Supabase allo stesso punto, senza scritture concorrenti.

Supabase gestito da un provider

Per Supabase gestito registra progetto, versione database, identificatore del backup o snapshot e punto di recupero. Chiudi minddy, worker e scritture pianificate; usa poi i controlli di coerenza o manutenzione supportati dal provider per le scritture dirette di Auth, PostgREST e Storage. Se non sono disponibili, documenta il limite: fermare soltanto l’applicazione non basta. Usa gli export SQL seguenti solo se ruolo database e provider li supportano, includendo Auth, metadati Storage, storia delle migrazioni e policy gestite. Conserva separatamente i byte grezzi degli oggetti tramite export supportato o snapshot immutabile del backend. Conserva ambiente protetto e chiavi minddy, oltre alle impostazioni Auth/SMTP, URL, proxy e job sotto il tuo controllo. Ruoli di piattaforma e chiavi pgsodium possono appartenere al provider: non presentarli come file locali. I blocchi seguenti con SUPABASE_COMPOSE_DIR, docker compose, supabase.env, pgsodium e tar filesystem riguardano solo un backend controllato dall’operatore. Valida il ripristino completo in un progetto vuoto separato prima di affidarti alla copia. Per questa revisione non è stato eseguito alcun backup o ripristino del provider.

Bloccare scritture ed esportare SQL

Chiudi l’applicazione, i worker, lo scheduler e l’API pubblica. Fermare solo il server web lascia aperte le scritture di PostgREST e Storage. Imposta SUPABASE_DB_URL in privato, FROM_TAG sul tag della release attualmente installata e scegli una BACKUP_DIR nuova e cifrata. Esporta SQL con gli strumenti della versione installata. Ogni file SQL deve essere non vuoto; data.sql deve contenere le istruzioni COPY per auth.users, storage.objects e le tabelle dell’applicazione. L’export ordinario omette la storia delle migrazioni: servono i due export separati mostrati. Conserva anche le policy gestite di Storage e Realtime.

Bash
set -euo pipefail
export MINDDY_REPO=/srv/minddy/source
export MINDDY_CURRENT_DIR=/srv/minddy/current
export SUPABASE_COMPOSE_DIR=/srv/supabase/docker
export MINDDY_ENV_FILE=/etc/minddy/minddy.env
export BACKUP_ROOT=/mnt/backup/minddy
export FROM_TAG=v0.11.0
Bash
export BACKUP_ID="$(date -u +%Y%m%dT%H%M%SZ)-${FROM_TAG}"
export BACKUP_DIR="$BACKUP_ROOT/$BACKUP_ID"
test ! -e "$BACKUP_DIR"
install -d -m 0700 "$BACKUP_DIR/database" "$BACKUP_DIR/config"
git -C "$MINDDY_CURRENT_DIR" rev-parse HEAD > "$BACKUP_DIR/minddy-commit.txt"
git -C "$MINDDY_CURRENT_DIR" describe --tags --always --dirty > "$BACKUP_DIR/minddy-version.txt"
cd "$SUPABASE_COMPOSE_DIR"
git -C "$SUPABASE_COMPOSE_DIR" rev-parse HEAD > "$BACKUP_DIR/supabase-commit.txt"
docker compose images > "$BACKUP_DIR/supabase-images.txt"
docker compose ps > "$BACKUP_DIR/supabase-services.txt"
Bash
cd "$MINDDY_REPO"
supabase db dump --db-url "$SUPABASE_DB_URL" \
  -f "$BACKUP_DIR/database/roles.sql" --role-only
supabase db dump --db-url "$SUPABASE_DB_URL" \
  -f "$BACKUP_DIR/database/schema.sql"
supabase db dump --db-url "$SUPABASE_DB_URL" \
  -f "$BACKUP_DIR/database/data.sql" --use-copy --data-only \
  -x 'storage.buckets_vectors' -x 'storage.vector_indexes'
supabase db dump --db-url "$SUPABASE_DB_URL" \
  -f "$BACKUP_DIR/database/history_schema.sql" --schema supabase_migrations
supabase db dump --db-url "$SUPABASE_DB_URL" \
  -f "$BACKUP_DIR/database/history_data.sql" --use-copy --data-only \
  --schema supabase_migrations
psql "$SUPABASE_DB_URL" -X -v ON_ERROR_STOP=1 -At \
  -f scripts/export-managed-policies.sql \
  > "$BACKUP_DIR/database/managed_policies.sql"
psql "$SUPABASE_DB_URL" -X -v ON_ERROR_STOP=1 -Atc "
  select jsonb_build_object(
    'auth.users', (select count(*) from auth.users),
    'public.projects', (select count(*) from public.projects),
    'public.issues', (select count(*) from public.issues),
    'public.attachments', (select count(*) from public.attachments),
    'storage.buckets', (select count(*) from storage.buckets),
    'storage.objects', (select count(*) from storage.objects)
  )::text
" > "$BACKUP_DIR/database/counts.json"

Conservare byte e configurazione

Se gestisci uno Storage filesystem, ferma storage e imgproxy dopo l’export SQL e archivia la directory preservando proprietari numerici, ACL e attributi estesi. Su S3 crea uno snapshot o una versione immutabile dei byte grezzi. Non ripristinare tramite /storage/v1/s3, che crea metadati in conflitto con quelli ripristinati. Il provider controlla ruoli e file di piattaforma: usa la sua procedura supportata e registrane la portata. Copia in privato configurazione minddy e Supabase, proxy, job, template Auth, SMTP ed eventuale chiave radice pgsodium. Includi MINDDY_DATA_ROOT_KEY e gli altri segreti conservati. L’SQL non contiene i byte degli allegati.

Per il backend fissato e controllato dall’operatore, conserva l’intero volume db-config invece del solo file pgsodium_root.key. L’helper seguente preserva proprietari numerici, permessi, ACL e attributi estesi; conserva la sua esatta immagine PostgreSQL per il recupero. Un diverso mount delle chiavi richiede una procedura verificata specifica del backend. Non omettere le chiavi e non presumere che una nuova radice possa leggere i dati Vault ripristinati.

Bash
install -m 0600 "$MINDDY_ENV_FILE" "$BACKUP_DIR/config/minddy.env"
install -m 0600 "$SUPABASE_COMPOSE_DIR/.env" "$BACKUP_DIR/config/supabase.env"
tar --exclude='./volumes' --exclude='./.git' \
  -C "$SUPABASE_COMPOSE_DIR" \
  -czf "$BACKUP_DIR/config/supabase-compose.tar.gz" .
cd "$SUPABASE_COMPOSE_DIR"
DB_CONTAINER="$(docker compose ps -q db)"
test -n "$DB_CONTAINER"
DB_CONFIG_VOLUME="$(docker inspect "$DB_CONTAINER" --format '{{range .Mounts}}{{if eq .Destination "/etc/postgresql-custom"}}{{.Name}}{{end}}{{end}}')"
BACKUP_HELPER_IMAGE="$(docker inspect "$DB_CONTAINER" --format '{{.Image}}')"
printf '%s\n' "$BACKUP_HELPER_IMAGE" > "$BACKUP_DIR/config/postgres-image-id.txt"
if [ -z "$DB_CONFIG_VOLUME" ]; then
  echo "This key mount needs a verified backend-specific backup and restore procedure." >&2
  exit 1
fi
docker run --rm --network none --user 0:0 --entrypoint tar \
  --mount "type=volume,src=$DB_CONFIG_VOLUME,dst=/keys,readonly" \
  "$BACKUP_HELPER_IMAGE" --numeric-owner --acls --xattrs -czf - -C /keys . \
  > "$BACKUP_DIR/config/db-config.tar.gz"
Bash
cd "$SUPABASE_COMPOSE_DIR"
docker compose stop storage imgproxy
test -d "$SUPABASE_COMPOSE_DIR/volumes/storage"
tar --numeric-owner --acls --xattrs \
  -C "$SUPABASE_COMPOSE_DIR/volumes" \
  -czf "$BACKUP_DIR/storage-files.tar.gz" storage
tar -tzf "$BACKUP_DIR/storage-files.tar.gz" > "$BACKUP_DIR/storage-files.list"
cd "$BACKUP_DIR"
find . -type f ! -name SHA256SUMS -print0 | sort -z | xargs -0 sha256sum > SHA256SUMS
sha256sum --check SHA256SUMS

Verificare il risultato

Genera e verifica SHA256SUMS sull’insieme completo, cifralo, copialo fuori dall’host e verifica di nuovo. Registra versioni, immagini, conteggi e hash dei campioni. Un export riuscito non prova il ripristino. Esegui una prova su una destinazione vuota con le versioni e la configurazione salvate prima di affidarti al backup; documenta i limiti del provider.

Ripristinare un insieme completo su una destinazione vuota

Il ripristino sostituisce i dati della destinazione e non unisce due istanze. Recupera la copia esterna sigillata, verifica SHA256SUMS e lascia intatta la fonte. Ferma ingressi, scheduler e worker della destinazione. Il ripristino fisico full richiede l’architettura e l’immagine database salvate. Prepara il tag e l’upstream fissato in directory nuove, con dipendenze bloccate. Accertati che non esistano directory database, oggetti Storage o un volume db-config di destinazione. Non avviare il normale installer o bootstrap prima di estrarre il backup fisico.

Backup completo esterno verificato
Destinazione vuota isolata e versioni uguali
Ripristinare database, byte e chiavi insieme
Verificare account, contenuti e file prima di aprire
Segui le fasi in questo ordine. Backup completo esterno verificato. Destinazione vuota isolata e versioni uguali. Ripristinare database, byte e chiavi insieme. Verificare account, contenuti e file prima di aprire.

Ripristinare i file del profilo full

Prima della sequenza fisica, definisci il contesto Compose della fonte come nell’articolo sul backup a freddo. Usa poi percorsi e volumi di destinazione nuovi. La sequenza rimuove i container della fonte già fermi senza rimuoverne i volumi, ripristina insieme PostgreSQL a freddo e Storage, recupera db-config con l’immagine salvata e confronta l’immagine prima dell’avvio. Nel file protetto cambia solo percorsi, origini e redirect Auth. Conserva JWT, password, chiavi Vault e dell’applicazione e identità di release e immagine. Includi sempre RESTORE_OVERRIDE: ometterlo può selezionare i volumi della fonte. Ricompila la funzione offline, avvia dipendenze e applicazione e verifica in manutenzione prima di aprire gli ingressi.

Prima del primo compose down, definisci il contesto Compose installato dell’istanza fonte, includendo l’override esistente.

Se esiste storage-volume.tar.gz, ripristinalo in un nuovo volume Linux con nome e collega quel volume esatto tramite l’override persistente prima di avviare Storage. Il controllo che la destinazione sia vuota si applica anche a questo volume.

Bash
compose down
export CURRENT_RELEASE_DIR=/srv/minddy/releases/saved-source
export SUPABASE_DIR=/srv/minddy/supabase-restored
export MINDDY_ENV_FILE=/etc/minddy/restored.env
export RESTORE_OVERRIDE=/etc/minddy/restore-volumes.yml
export RESTORE_DB_CONFIG=minddy-restored-db-config
(cd "$BACKUP_DIR"; sha256sum --check SHA256SUMS)
test ! -d "$SUPABASE_DIR/docker/volumes/db/data"
test -z "$(find "$SUPABASE_DIR/docker/volumes/storage" -type f -print -quit 2>/dev/null)"
if docker volume inspect "$RESTORE_DB_CONFIG" >/dev/null 2>&1; then
  echo "Refusing to overwrite an existing restore volume." >&2
  exit 1
fi
sudo tar --numeric-owner --acls --xattrs -xzf "$BACKUP_DIR/supabase-docker.tar.gz" \
  -C "$SUPABASE_DIR"
install -m 0600 "$BACKUP_DIR/instance.env" "$MINDDY_ENV_FILE"
cat > "$RESTORE_OVERRIDE" <<EOF
volumes:
  db-config:
    name: $RESTORE_DB_CONFIG
  deno-cache:
    name: minddy-restored-deno-cache
  caddy_data:
    name: minddy-restored-caddy-data
  caddy_config:
    name: minddy-restored-caddy-config
EOF
compose() {
  local files=(
    -f "$SUPABASE_DIR/docker/docker-compose.yml"
    -f "$CURRENT_RELEASE_DIR/deploy/self-hosted/compose.full.yml"
    -f "$RESTORE_OVERRIDE"
  )
  docker compose --env-file "$MINDDY_ENV_FILE" "${files[@]}" "$@"
}
BACKUP_HELPER_IMAGE="$(cat "$BACKUP_DIR/postgres-image-id.txt")"
docker image inspect "$BACKUP_HELPER_IMAGE" >/dev/null
docker volume create "$RESTORE_DB_CONFIG"
docker run --rm -i --network none --user 0:0 --entrypoint tar \
  --mount "type=volume,src=$RESTORE_DB_CONFIG,dst=/keys" \
  "$BACKUP_HELPER_IMAGE" -xzf - -C /keys < "$BACKUP_DIR/db-config.tar.gz"
if [ -f "$BACKUP_DIR/storage-volume.tar.gz" ]; then
  export RESTORE_STORAGE_VOLUME=minddy-restored-filesystem-storage
  if docker volume inspect "$RESTORE_STORAGE_VOLUME" >/dev/null 2>&1; then
    echo "Refusing to overwrite an existing Storage restore volume." >&2
    exit 1
  fi
  docker volume create "$RESTORE_STORAGE_VOLUME"
  docker run --rm -i --network none --user 0:0 --entrypoint tar \
    --mount "type=volume,src=$RESTORE_STORAGE_VOLUME,dst=/objects" \
    "$BACKUP_HELPER_IMAGE" --numeric-owner --acls --xattrs -xzf - -C /objects \
    < "$BACKUP_DIR/storage-volume.tar.gz"
  cat >> "$RESTORE_OVERRIDE" <<EOF
  $RESTORE_STORAGE_VOLUME:
    external: true
services:
  storage:
    volumes:
      - $RESTORE_STORAGE_VOLUME:/var/lib/storage
EOF
fi
compose create db
test "$(docker inspect "$(compose ps --all -q db)" --format '{{.Image}}')" = \
  "$BACKUP_HELPER_IMAGE"
cd "$CURRENT_RELEASE_DIR"
pnpm install --frozen-lockfile
node scripts/prepare-self-hosted-functions.mjs --supabase-dir "$SUPABASE_DIR" \
  --env-file "$MINDDY_ENV_FILE"
compose up -d --wait db database-access kong auth rest storage imgproxy
compose up -d --wait minddy
pnpm self-host:doctor -- --mode full --env-file "$MINDDY_ENV_FILE" \
  --supabase-compose "$SUPABASE_DIR/docker/docker-compose.yml" --skip-network --maintenance
compose up -d --wait
pnpm self-host:doctor -- --mode full --env-file "$MINDDY_ENV_FILE" \
  --supabase-compose "$SUPABASE_DIR/docker/docker-compose.yml"

Ripristinare un insieme logico

Per un ripristino logico, prepara uno stack vuoto con versioni, configurazioni e chiavi registrate. Prima di importare SQL, verifica l’identità del database e del backend Storage grezzo di destinazione. Ripristina ruoli, schema e dati in transazione, poi storia delle migrazioni e policy. Ferma Storage e recupera i file o lo snapshot S3 in un backend nuovo e vuoto; non caricarli tramite API. Managed richiede il processo supportato dal provider, non l’estrazione filesystem dell’esempio. Usa il commit minddy salvato e configura chiavi e origini della destinazione isolata.

I comandi seguenti per estrazione logica, run.sh e Storage filesystem riguardano solo un backend controllato dall’operatore. Con Supabase gestito ripristina database e byte grezzi su una destinazione vuota del provider con il suo processo supportato, poi configura e avvia minddy con la procedura dai sorgenti. Non eseguire Compose locale per un progetto supabase.com.

Prima del primo avvio del database, la sequenza seguente ripristina l’archivio delle chiavi in un nuovo volume e aggiunge un file univoco docker-compose.restore-keys.*.yml all’elenco Compose installato. Mantieni questo override in ogni operazione successiva. I vecchi backup contenenti solo pgsodium_root.key richiedono una procedura verificata che ripristini proprietari, permessi e mount originali prima dell’avvio; la sequenza si ferma invece di generare chiavi sostitutive.

Prepara prima il checkout Supabase originale salvato con i suoi file statici sotto volumes/, poi imposta RESTORE_SUPABASE_DIR sulla sua directory docker. L’archivio di configurazione esclude volutamente volumes/ e non può preparare da solo questo checkout. I controlli seguenti richiedono il commit salvato e i file di inizializzazione e rifiutano dati già presenti nel database o in Storage. Mantieni chiuso l’accesso pubblico ed esegui la sequenza Bash con set -euo pipefail, così un controllo o un’estrazione falliti interrompono la procedura prima dell’avvio.

Bash
set -euo pipefail
export RESTORE_DB_URL='postgresql://postgres:...@restore-db:5432/postgres'
export RESTORE_SUPABASE_URL='https://restore-supabase.example.test'
export RESTORE_APP_URL='https://restore-tickets.example.test'
export RESTORE_ANON_KEY='...'
export RESTORE_SERVICE_ROLE_KEY='...'
export RESTORE_SUPABASE_DIR=/srv/restore/supabase/docker
test "$(git -C "$RESTORE_SUPABASE_DIR" rev-parse HEAD)" = \
  "$(cat "$BACKUP_DIR/supabase-commit.txt")"
for file in volumes/db/realtime.sql volumes/db/webhooks.sql volumes/db/roles.sql \
  volumes/db/jwt.sql volumes/db/_supabase.sql volumes/db/logs.sql \
  volumes/db/pooler.sql volumes/api/kong.yml volumes/api/kong-entrypoint.sh \
  volumes/pooler/pooler.exs; do
  test -f "$RESTORE_SUPABASE_DIR/$file"
done
test ! -d "$RESTORE_SUPABASE_DIR/volumes/db/data"
if [ -e "$RESTORE_SUPABASE_DIR/volumes/storage" ]; then
  STORAGE_FIRST_OBJECT="$(find "$RESTORE_SUPABASE_DIR/volumes/storage" -type f -print -quit)"
  test -z "$STORAGE_FIRST_OBJECT"
fi
tar -C "$RESTORE_SUPABASE_DIR" \
  -xzf "$BACKUP_DIR/config/supabase-compose.tar.gz"
install -m 0600 "$BACKUP_DIR/config/supabase.env" "$RESTORE_SUPABASE_DIR/.env"
cd "$RESTORE_SUPABASE_DIR"
if [ -f "$BACKUP_DIR/config/pgsodium_root.key" ] && \
   [ ! -f "$BACKUP_DIR/config/db-config.tar.gz" ]; then
  echo "Legacy standalone key backup: restore its original ownership and mount before startup." >&2
  exit 1
fi
test -f "$BACKUP_DIR/config/db-config.tar.gz"
test -f "$BACKUP_DIR/config/postgres-image-id.txt"
export RESTORE_DB_CONFIG=minddy-logical-restored-db-config
if docker volume inspect "$RESTORE_DB_CONFIG" >/dev/null 2>&1; then
  echo "Refusing to overwrite an existing key restore volume." >&2
  exit 1
fi
RESTORE_KEYS_OVERRIDE="$(mktemp docker-compose.restore-keys.XXXXXX.yml)"
BACKUP_HELPER_IMAGE="$(cat "$BACKUP_DIR/config/postgres-image-id.txt")"
docker image inspect "$BACKUP_HELPER_IMAGE" >/dev/null
docker volume create "$RESTORE_DB_CONFIG"
docker run --rm -i --network none --user 0:0 --entrypoint tar \
  --mount "type=volume,src=$RESTORE_DB_CONFIG,dst=/keys" \
  "$BACKUP_HELPER_IMAGE" --numeric-owner --acls --xattrs -xzf - -C /keys \
  < "$BACKUP_DIR/config/db-config.tar.gz"
cat > "$RESTORE_KEYS_OVERRIDE" <<EOF
volumes:
  db-config:
    name: $RESTORE_DB_CONFIG
EOF
sh run.sh config add "$RESTORE_KEYS_OVERRIDE"
sh run.sh start
psql "$RESTORE_DB_URL" -X -v ON_ERROR_STOP=1 -Atc \
  'select current_database(), inet_server_addr(), version()'
Bash
psql --single-transaction --variable ON_ERROR_STOP=1 \
  --file "$BACKUP_DIR/database/roles.sql" \
  --file "$BACKUP_DIR/database/schema.sql" \
  --command 'SET session_replication_role = replica' \
  --file "$BACKUP_DIR/database/data.sql" \
  --dbname "$RESTORE_DB_URL"
psql --single-transaction --variable ON_ERROR_STOP=1 \
  --file "$BACKUP_DIR/database/history_schema.sql" \
  --file "$BACKUP_DIR/database/history_data.sql" \
  --dbname "$RESTORE_DB_URL"
psql --single-transaction --variable ON_ERROR_STOP=1 \
  --file "$BACKUP_DIR/database/managed_policies.sql" \
  --dbname "$RESTORE_DB_URL"
Bash
cd "$RESTORE_SUPABASE_DIR"
docker compose stop storage imgproxy
test -d "$RESTORE_SUPABASE_DIR/volumes/storage"
tar --numeric-owner --acls --xattrs \
  -C "$RESTORE_SUPABASE_DIR/volumes" \
  -xzf "$BACKUP_DIR/storage-files.tar.gz"
docker compose up -d storage imgproxy
Bash
export RESTORE_RELEASE_DIR="/srv/restore/minddy/releases/$BACKUP_ID"
git -C "$MINDDY_REPO" worktree add --detach "$RESTORE_RELEASE_DIR" \
  "$(cat "$BACKUP_DIR/minddy-commit.txt")"
cd "$RESTORE_RELEASE_DIR"
install -m 0600 "$BACKUP_DIR/config/minddy.env" .env.local
export MINDDY_PUBLIC_SUPABASE_URL="$RESTORE_SUPABASE_URL"
export MINDDY_PUBLIC_SUPABASE_ANON_KEY="$RESTORE_ANON_KEY"
export SUPABASE_SERVICE_ROLE_KEY="$RESTORE_SERVICE_ROLE_KEY"
export MINDDY_PUBLIC_APP_URL="$RESTORE_APP_URL"
pnpm install --frozen-lockfile
pnpm build
pnpm verify:supabase --db-url "$RESTORE_DB_URL" \
  --supabase-url "$MINDDY_PUBLIC_SUPABASE_URL" \
  --service-role-key "$SUPABASE_SERVICE_ROLE_KEY"

Dopo il build dai sorgenti e la verifica precedente, avvia l’applicazione recuperata con pnpm start o con il supervisore registrato nella procedura dai sorgenti. Mantieni ingressi pubblici e job chiusi fino al superamento dei controlli seguenti.

Verificare il risultato

Confronta i conteggi, accedi con gli account e la MFA ripristinati, leggi progetto, ticket e pagina cifrati e scarica un campione di ogni bucket verificandone lo SHA-256. Prova integrazioni, revoche, Realtime, job, OAuth, MCP e callback. Registra percorsi, volumi, versioni, durata e finestra di perdita dei dati osservata. Dopo un errore di migrazione, usa l’insieme precedente. Le scritture successive alla copia possono andare perse durante il rollback. Non inventare SQL inverso e non considerare i container sani una prova di ripristino.

Articoli correlati