---
title: "Copia di salvezza, ripresa à un mumentu precisu è prova di risturazione"
description: "Fate copie di salvezza di Quire, risturate lu à un mumentu precisu è dimustrate la cù a prova di risturazione."
image: "https://docs.quirelms.com/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.quirelms.com/co/llms.txt
> Use this file to discover all available pages before exploring further.

# Copia di salvezza, ripresa à un mumentu precisu è prova di risturazione

<span id="backup-point-in-time-recovery-and-the-restore-drill"></span>

U disignu hè in a sezzione 8 di `docs/architecture/23-ops.md`. Questu hè u manuale
per u pruduttu Docker Compose. Hè scrittu per esse seguitatu da qualchissia chì
ùn l'hà micca scrittu; s'è un passu ùn hè chjaru, hè un difettu di stu ducumentu.

## Ciò chì hè prutettu, è cumu <!--quire:what-is-protected-and-how-->

| Risorsa | Cumu | Induve |
| --- | --- | --- |
| A basa di dati | WAL archiviatu di cuntinuu, almenu ogni 60 seconde, da u primu avviu | Volume `pgwal` |
| A basa di dati | Copie di salvezza di basa cù `pg_basebackup`, predefinitu ogni ghjornu (`backup-scheduler`) | Volume `pgbackup` |
| A basa di dati | Copie cifrate di e copie di salvezza di basa è di u WAL, ogni cinque minuti (`backup-offsite`) | Un magazinu separatu chì sceglite |
| Schedarii | U volume `files`. Copiatelu cù u strumentu di copia di salvezza di u vostru host, o aduprate un almacenamentu d'ogetti cù versioni | Volume `files` |
| Sicreti | `docker/.env`, soprattuttu `QUIRE_MASTER_KEY` (è qualsiasi `QUIRE_MASTER_KEY_RETIRED` sempre adupratu), `QUIRE_BACKUP_ENCRYPTION_KEY`, è `docker/secrets/audit-signing-key.pem` | Cunservate una copia fora di questu host |
| Indici di ricerca, cache, rendizioni | Ùn sò micca salvati; ricustruiti | |

Obiettivi: un puntu di ripresa à 60 seconde à u più da u guastu, è una risturazione
in 60 minuti per una basa di dati di 500 GB.

Dui sbagli sò cumuni. Una basa di dati risturata **senza i so schedarii** mostra
pagine rotte. Una basa di dati risturata **senza `QUIRE_MASTER_KEY`** ùn pò
decifrà e credenziali SSO, webhook è d'integrazione ch'ella cuntene; finch'è a rotazione di a chjave maestra ùn hè finita senza valori irrisolvibili ([key-rotation.md](/co/ops/key-rotation/)),
ciò include e chjave ritirate. Tramindui facenu parte di a copia di salvezza.

## Fà copie di salvezza <!--quire:taking-backups-->

Una copia di salvezza di basa di tuttu u cluster:

```sh
docker compose -f docker/compose.yaml --profile backup run --rm backup
```

Cunserva e copie di salvezza di basa più recenti indicate da `QUIRE_BACKUP_KEEP` (predefinitu 5) è sguassa
u WAL chì a più vechja ùn hà più bisognu, cusì l'archiviu ùn pò cresce senza limite.
Pianificate lu ogni ghjornu cù cron o un timer systemd nant'à l'host:

```cron
15 2 * * * cd /srv/quire && docker compose -f docker/compose.yaml --profile backup run --rm backup >> /var/log/quire-backup.log 2>&1
```

O lasciate chì u stack u pianifichi: u prufilu `backup` esegue `backup-scheduler`,
chì face una copia di salvezza di basa ogni `QUIRE_BACKUP_INTERVAL_HOURS` (predefinitu 24),
è `backup-offsite`, discrittu dopu.

```sh
docker compose -f docker/compose.yaml --profile backup up -d
```

## Copie cifrate fora di l'host <!--quire:encrypted-off-host-copies-->

I dui volumi sò nant'à u listessu host chè a basa di dati, è una copia di salvezza nant'à a
macchina guasta ùn hè micca una copia di salvezza. `backup-offsite` copia ogni copia di salvezza di basa
è ogni segmentu WAL archiviatu in un magazinu separatu via a porta di almacenamentu,
cifrati, è i cunserva quì secondu a pulitica di retenzione:

- **Cifratura.** AES-256-GCM cù `QUIRE_BACKUP_ENCRYPTION_KEY` (o u schedariu
  indicatu da `QUIRE_BACKUP_ENCRYPTION_KEY_FILE`): 32 byte, generati cù
  `openssl rand -hex 32`. Ogni schedariu hà u so propiu nonce è una
  etichetta d'autentificazione, dunque una copia hè illeggibile senza a chjave è ogni mudificazione
  hè rilevata. Cunservate a chjave cù `QUIRE_MASTER_KEY`, luntanu da questu host è luntanu
  da u magazinu di salvezza. Senza a chjave ùn si pò risturà.
- **Locu.** `QUIRE_BACKUP_STORAGE_DRIVER` hè `s3`, `azure` o `local` (un
  discu remotu muntatu in `QUIRE_BACKUP_STORAGE_ROOT`). I paràmetri sò quelli
  di almacenamentu di schedarii cù u prefissu `QUIRE_BACKUP_`: `QUIRE_BACKUP_S3_ENDPOINT`,
  `QUIRE_BACKUP_S3_BUCKET`, `QUIRE_BACKUP_S3_ACCESS_KEY_ID`, è cetara. Aduprate un
  bucket sfarente è, di preferenza, un contu sfarente da quellu di i schedarii, cù
  credenziali chì ponu scrive ma micca sguassà, s'è u fornitore u permette.
- **Retenzione.** E copie di salvezza di basa più recenti indicate da `QUIRE_BACKUP_OFFSITE_KEEP` (predefinitu
  `QUIRE_BACKUP_KEEP`, altrimenti 7) è u WAL chì a più vechja di elle hà bisognu; l'insemi
  è i segmenti più vechji sò sguassati da u magazinu.
- **Quandu.** Ogni `QUIRE_BACKUP_SHIP_INTERVAL_SECONDS` (predefinitu 300). L'inviu
  hè idempotente: ciò chì hè digià cunservatu hè saltatu, è una copia di salvezza di basa conta cum'è cunservata solu quandu u so manifestu hè scrittu, à a fine.

U listessu cumandu pò esse eseguitu à manu:

```sh
docker compose -f docker/compose.yaml run --rm backup-offsite bun apps/worker/src/backups/main.ts ship
docker compose -f docker/compose.yaml run --rm backup-offsite bun apps/worker/src/backups/main.ts verify
```

Per risturà nant'à un host novu, ricuperate prima un inseme, dopu seguitate i passi sottu
cù u cartulare ricuperatu à u locu di u volume `pgbackup` è u
`wal-archive` ricuperatu à u locu di `pgwal`:

```sh
bun apps/worker/src/backups/main.ts fetch base-20260924T021500Z /srv/restore
```

## Risturà à un mumentu precisu <!--quire:restoring-to-a-point-in-time-->

Aduprate questu dopu una perdita di dati: una impurtazione sbagliata, un corsu sguassatu, una migrazione di
cuntrazione ch'è vo avete bisognu d'annullà. Rimpiazza a basa di dati attiva, dunque pruvate lu
prima cù l'eserciziu sottu.

1. **Sceglite u mumentu di destinazione**, in UTC, ghjustu nanzu à u dannu:
   `2026-09-24 09:30:00+00`. U registru d'audit (`/admin/audit`) mostra di solitu u
   mumentu.
2. **Arrestate tuttu ciò chì scrive**:
   `docker compose -f docker/compose.yaml stop web content worker scheduler collab`
3. **Cunservate u cluster dannighjatu** finch'è a risturazione ùn hè verificata:
   ```sh
   docker compose -f docker/compose.yaml stop postgres
   docker run --rm -v quire_postgres18-data:/from -v quire_postgres-damaged:/to alpine cp -a /from/. /to/
   ```
4. **Scumpressate a copia di salvezza di basa più recente, più vechja chè u mumentu di destinazione**, in u volume
   di dati, è dumandate una ripresa mirata:
   ```sh
   docker run --rm -v quire_pgbackup:/backups:ro -v quire_postgres18-data:/var/lib/postgresql postgres:18-alpine sh -euc '
     base="$(ls -1d /backups/base-* | sort | tail -n 1)"   # or the one before the target
     rm -rf /var/lib/postgresql/18/docker && mkdir -p /var/lib/postgresql/18/docker
     tar -xzf "$base/base.tar.gz" -C /var/lib/postgresql/18/docker
     touch /var/lib/postgresql/18/docker/recovery.signal
     chown -R postgres:postgres /var/lib/postgresql/18/docker && chmod 700 /var/lib/postgresql/18/docker'
   ```
5. **Ripigliate**: avviate Postgres una volta cù i paràmetri di ripresa, da un
   override Compose affinchì u schedariu nurmale resti intattu:
   ```yaml
   # docker/compose.recover.yaml
   services:
     postgres:
       command: [postgres, -c, "restore_command=cp /var/lib/postgresql/wal-archive/%f %p",
                 -c, "recovery_target_time=2026-09-24 09:30:00+00",
                 -c, recovery_target_action=promote, -c, archive_mode=off,
                 -c, max_connections=200, -c, hba_file=/etc/postgresql/pg_hba.conf]
   ```
   L'override rimpiazza tuttu u cumandu, dunque ripete i dui paràmetri chì a
   ripresa richiede: `max_connections` micca più bassu chè quellu di a basa primaria
   (altrimenti a ripresa si ferma cù «insufficient parameter settings») è
   u `pg_hba.conf` muntatu.
   ```sh
   docker compose -f docker/compose.yaml -f docker/compose.recover.yaml up -d postgres
   docker compose -f docker/compose.yaml logs -f postgres   # wait for "database system is ready"
   ```
6. **Verificate la** prima di lascià entre qualchissia: a catena d'audit
   (`docker compose -f docker/compose.yaml run --rm worker bun tooling/audit-verify/run.ts`),
   è chì i dati persi sò stati ricuperati.
7. **Tornate à u funziunamentu nurmale**: `docker compose -f docker/compose.yaml up -d`. Questu
   riavvia Postgres cù l'archiviazione attivata, è principia una nova cronulugia WAL. Fate subitu una
   nova copia di salvezza di basa.

U nome di u prughjettu `quire` hè prefissatu à ogni volume; `docker volume ls` mostra i
nomi esatti.

## Prova di verificazione pianificata <!--quire:the-scheduled-verification-drill-->

`backup-offsite` esegue ancu una prova ogni `QUIRE_BACKUP_DRILL_INTERVAL_HOURS`
(predefinitu 168, una volta à settimana), è torna à a prossima esecuzione dopu à un fiascu. Ricupera
l'ultima copia di salvezza di basa da fora di l'host è ogni segmentu WAL dopu à ella, decifra ogni schedariu
(ciò chì prova chì a chjave pò sempre apre li è ch'elli ùn sò micca stati alterati), paraguneghja ogni
schedariu cù u so manifestu, verifica chì l'archiviu sia un cartulare di dati Postgres, è
verifica chì ùn ci sia lacune in u WAL da a copia di salvezza in avanti. U rapportu hè scrittu in
u magazinu cum'è `reports/drill-<time>.json` è in u registru di u serviziu; una prova fallita
nomina u schedariu o u primu segmentu mancante.

## Prova di risturazione <!--quire:the-restore-drill-->

Una copia di salvezza chì ùn hè mai stata risturata ùn hè micca una copia di salvezza. A prova ristura a
copia di salvezza di basa cumpleta più recente fatta prima di u mumentu di destinazione, cù l'archiviu WAL,
in un Postgres pruvisoriu chì ùn hà nunda in cumunu cù quellu attivu, è prova u
risultatu:

```sh
docker/scripts/restore-drill.sh                                  # to ninety minutes ago
docker/scripts/restore-drill.sh --target "2026-09-24 09:30:00+00"
```

U mumentu di destinazione hè UTC esattamente in quella forma. Ci vole una copia di salvezza di basa più vechja chè ella
è WAL archiviatu dopu à ella: nant'à una nova installazione, fate una copia di salvezza di basa è aspettate u prossimu
segmentu archiviatu (à u più un minutu s'ellu ci sò scritture) prima di sceglie un mumentu di destinazione dopu à a copia. A prova hà bisognu di Docker è bash nant'à l'host, nunda
altru.

Ogni passu face fallisce a prova:

1. **Pussibilità di risturazione**: u cluster pruvisoriu riproduce i registri finu à u mumentu sceltu è s'avvia.
2. **Cumpletezza**: u numeru di filari di ogni tavula hè paragunatu à a basa attiva
   (`tooling/restore-drill`). A basa attiva hè avanzata dapoi u mumentu sceltu,
   dunque una tavula pò differisce di u più grande trà 500 filari è un decimu di a so dimensione, in
   e duie direzzioni (e scritture facenu chì a risturazione sia in ritardu, e cancellazioni facenu chì cuntene di più);
   una partizione creata dopu à u mumentu ùn hè micca una tavula persa. Allargate a
   tolleranza in un'installazione più attiva cù `QUIRE_DRILL_MAX_BEHIND` è
   `QUIRE_DRILL_MAX_DRIFT_RATIO`. Una tavula mancante o sviutata face fallisce a prova.
3. **Integrità**: a catena d'hash d'audit hè verificata nantu à a copia risturata.
4. **Usabilità**: u rollu d'applicazione leghje cù a sicurezza à livellu di fila.
5. **Durata**: da l'iniziu à u risultatu verde, secondu `QUIRE_DRILL_RTO_SECONDS`
   (predefinitu 3600).

Ùn scrive mai in a basa attiva o in i so volumi: i volumi di copia di salvezza è di WAL
sò muntati in sola lettura, è u cluster pruvisoriu hè sguassatu à a fine,
sia ch'ella passi sia ch'ella fallisca.

Stabilite `QUIRE_DRILL_REPORT` à un percorsu per scrive un rapportu JSON, ch'ella passi o
fallisca, è pianificate l'eserciziu da l'host Docker:

```cron
30 3 1 * * cd /srv/quire && QUIRE_DRILL_REPORT=/var/log/quire-drill.json docker/scripts/restore-drill.sh >> /var/log/quire-drill.log 2>&1
```

Eseguite lu ogni mese è nanzu à ogni aghjurnamentu. Una prova fallita impedisce l'aghjurnamentu.
Una volta à u trimestre, fate chì qualchissia chì ùn hà micca scrittu stu manuale eseguisca una
risturazione vera à un mumentu precisu nant'à un host di riserva, aduprendu solu stu ducumentu.

## Schedarii <!--quire:files-->

I schedarii lucali sò in u volume `files`. Fate ne una copia di salvezza à tempu chè a basa di dati,
è risturate tramindui inseme:

```sh
docker run --rm -v quire_files:/files:ro -v "$PWD":/out alpine tar -czf /out/files-$(date -u +%Y%m%d).tar.gz -C /files .
```

Cù un almacenamentu d'ogetti, attivate e versioni di bucket è cunservate 35 ghjorni di
versioni precedenti; a ripresa à un mumentu precisu di i schedarii hè tandu quella propria di u bucket.

Source: https://docs.quirelms.com/co/ops/backup-restore/index.mdx
