---
title: "Zálohy, obnova k určitému okamžiku a restore drill"
description: "Zálohujte Quire, obnovte ho k určitému okamžiku a ověřte obnovu pomocí restore drill."
image: "https://docs.quirelms.com/og.png"
---

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

# Zálohy, obnova k určitému okamžiku a restore drill

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

Architektura je popsána v části 8 dokumentu `docs/architecture/23-ops.md`. Toto je provozní příručka pro produkt Docker Compose. Je napsaná tak, aby se jí řídil člověk, který ji nevytvořil; pokud některý krok není jasný, jde o chybu dokumentu.

## Co je chráněno a jak <!--quire:what-is-protected-and-how-->

| Prostředek | Jak | Kde |
| --- | --- | --- |
| Databáze | WAL se průběžně archivuje, nejvýše každých 60 sekund, od prvního spuštění | Svazek `pgwal` |
| Databáze | Základní zálohy pomocí `pg_basebackup`, ve výchozím nastavení denně (`backup-scheduler`) | Svazek `pgbackup` |
| Databáze | Šifrované kopie základních záloh a WAL každých pět minut (`backup-offsite`) | Samostatné úložiště, které určíte |
| Soubory | Svazek `files`. Zkopírujte ho zálohovacím nástrojem hostitele nebo použijte verzované objektové úložiště | Svazek `files` |
| Tajné údaje | `docker/.env`, především `QUIRE_MASTER_KEY` (a případný stále používaný `QUIRE_MASTER_KEY_RETIRED`), `QUIRE_BACKUP_ENCRYPTION_KEY` a `docker/secrets/audit-signing-key.pem` | Kopii uchovávejte mimo tento hostitel |
| Indexy vyhledávání, cache a rendice | Nezálohují se; vytvoří se znovu | |

Cíle: bod obnovení nejvýše 60 sekund před selháním a obnova databáze o velikosti 500 GB do 60 minut.

Často se opakují dvě chyby. Obnovená databáze **bez svých souborů** zobrazuje poškozené stránky. Obnovená databáze **bez `QUIRE_MASTER_KEY`** nedokáže rozšifrovat uložené přístupové údaje SSO, webhooků a integrací. Dokud obměna hlavního klíče neskončí bez nevyřešených hodnot ([key-rotation.md](/cs/ops/key-rotation/)), patří mezi ně i starší klíče. Záloha musí obsahovat obojí.

## Vytváření záloh <!--quire:taking-backups-->

Základní záloha celého clusteru:

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

Uchová nejnovější základní zálohy v počtu `QUIRE_BACKUP_KEEP` (výchozí hodnota 5) a smaže starý WAL, který už nejstarší z nich nepotřebuje, takže archiv nemůže růst bez omezení. Naplánujte zálohu na každý den pomocí cron nebo časovače systemd na hostiteli:

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

Nebo plánování svěřte stacku: profil `backup` spustí `backup-scheduler`, který vytváří základní zálohu každých `QUIRE_BACKUP_INTERVAL_HOURS` hodin (výchozí hodnota 24), a také níže popsanou službu `backup-offsite`.

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

## Šifrované kopie mimo hostitele <!--quire:encrypted-off-host-copies-->

Oba svazky leží na stejném hostiteli jako databáze a záloha na stroji, který selhal, není záloha. Služba `backup-offsite` zkopíruje každou základní zálohu a každý archivovaný segment WAL přes port úložiště do samostatného úložiště, zašifruje je a ponechá podle pravidel uchovávání:

- **Šifrování.** AES-256-GCM s klíčem `QUIRE_BACKUP_ENCRYPTION_KEY` (nebo souborem určeným v `QUIRE_BACKUP_ENCRYPTION_KEY_FILE`): 32 bajtů vytvořených příkazem `openssl rand -hex 32`. Každý soubor má vlastní nonce a autentizační značku; bez klíče ho nelze přečíst a případnou změnu lze zjistit. Klíč uchovávejte společně s `QUIRE_MASTER_KEY`, mimo tento hostitel i záložní úložiště. Bez něj nelze obnovit data.
- **Umístění.** `QUIRE_BACKUP_STORAGE_DRIVER` má hodnotu `s3`, `azure` nebo `local` (připojený vzdálený disk v `QUIRE_BACKUP_STORAGE_ROOT`). Použijte stejná nastavení jako pro souborové úložiště, předponu mají `QUIRE_BACKUP_`: `QUIRE_BACKUP_S3_ENDPOINT`, `QUIRE_BACKUP_S3_BUCKET`, `QUIRE_BACKUP_S3_ACCESS_KEY_ID` a další. Zvolte jiný bucket než pro soubory a ideálně také jiný účet. Pokud to poskytovatel umožňuje, použijte údaje, které dovolují zápis, ale nikoli mazání.
- **Uchovávání.** Nejnovější počet základních záloh `QUIRE_BACKUP_OFFSITE_KEEP` (výchozí hodnota `QUIRE_BACKUP_KEEP`, jinak 7) a WAL, který potřebuje nejstarší z nich; starší sady a segmenty se z úložiště odstraní.
- **Interval.** Každých `QUIRE_BACKUP_SHIP_INTERVAL_SECONDS` sekund (výchozí hodnota 300). Odesílání je idempotentní: již uložené soubory se přeskočí a základní záloha se považuje za uloženou až po zapsání svého manifestu jako poslední položky.

Stejný příkaz lze spustit ručně:

```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
```

Chcete-li obnovovat na novém hostiteli, nejprve na něj přeneste záložní sadu. Potom postupujte podle kroků níže a místo svazku `pgbackup` použijte získaný adresář a místo archivu `wal-archive` použijte získaný svazek `pgwal`:

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

## Obnova k určitému okamžiku <!--quire:restoring-to-a-point-in-time-->

Tento postup použijte po ztrátě dat: chybném importu, smazání kurzu nebo migraci odebírající starou podobu, kterou potřebujete vrátit zpět. Nahradí živou databázi, proto si ho nejprve vyzkoušejte pomocí níže uvedeného drill.

1. **Zvolte cílový čas** v UTC těsně před poškozením: `2026-09-24 09:30:00+00`. Okamžik obvykle najdete v protokolu auditu (`/admin/audit`).
2. **Zastavte všechny služby, které zapisují**:
   `docker compose -f docker/compose.yaml stop web content worker scheduler collab`
3. **Zachovejte poškozený cluster**, dokud ověření obnovy neskončí:
   ```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. **Rozbalte nejnovější základní zálohu pořízenou před cílovým časem** do datového svazku a vyžádejte cílenou obnovu:
   ```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. **Proveďte obnovu**: spusťte Postgres jednou s nastavením obnovy v override souboru Compose, aby se běžný soubor nezměnil:
   ```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]
   ```
   Override nahrazuje celý příkaz, proto opakuje dvě nastavení nutná pro obnovu: `max_connections` nesmí být nižší než u primární databáze (jinak se obnova přeruší s chybou „insufficient parameter settings“) a musí se uvést připojený `pg_hba.conf`.
   ```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. **Zkontrolujte obnovenou databázi**, než kohokoli pustíte dovnitř: ověřte auditní řetězec (`docker compose -f docker/compose.yaml run --rm worker bun tooling/audit-verify/run.ts`) a zkontrolujte, že se ztracená data vrátila.
7. **Vraťte se k běžnému provozu**: `docker compose -f docker/compose.yaml up -d`. Tím se Postgres restartuje se zapnutou archivací a začne nová časová osa WAL. Ihned vytvořte novou základní zálohu.

Každý svazek má na začátku názvu prefix projektu `quire`; přesné názvy vypíše `docker volume ls`.

## Plánovaný ověřovací drill <!--quire:the-scheduled-verification-drill-->

Služba `backup-offsite` provádí drill každých `QUIRE_BACKUP_DRILL_INTERVAL_HOURS` hodin (výchozí hodnota 168, tedy týdně) a znovu při dalším průchodu po selhání. Stáhne nejnovější základní zálohu mimo hostitele a všechny navazující segmenty WAL, každý dešifruje (ověří, že ho klíč stále odemyká a soubor nebyl změněn), porovná soubory s jejich manifesty, zkontroluje, že archiv obsahuje datový adresář Postgresu, a ověří souvislou posloupnost WAL od zálohy. Report se zapíše do úložiště jako `reports/drill-<time>.json` a do protokolu služby. Při selhání drill uvede soubor nebo první chybějící segment.

## Restore drill <!--quire:the-restore-drill-->

Záloha, která nebyla nikdy obnovena, není ověřenou zálohou. Drill obnoví nejnovější úplnou základní zálohu vytvořenou před cílovým časem a archiv WAL do testovacího Postgresu, který nic nesdílí s živou databází. Pak ověří výsledek:

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

Cílový čas musí být UTC přesně v uvedeném formátu. Potřebuje základní zálohu starší než cíl a archivovaný WAL, který sahá dál. Při nové instalaci vytvořte základní zálohu a před volbou času po jejím dokončení počkejte na další archivovaný segment (při zápisech nejvýše minutu). Drill potřebuje na hostiteli Docker a bash, nic dalšího.

Drill selže při každém nesplněném kroku:

1. **Obnovitelnost**: testovací cluster se přehraje k cílovému času a otevře.
2. **Úplnost**: porovnají se počty řádků všech tabulek s živou databází (`tooling/restore-drill`). Živá databáze od cílového času pokročila, takže tabulka se může v obou směrech lišit o větší hodnotu z 500 řádků a desetiny své velikosti (zápisy obnovu opožďují, mazání znamená více řádků v obnovených datech); oddíl vytvořený po cílovém čase se nepovažuje za ztracenou tabulku. V rušnější instalaci toleranci rozšiřte pomocí `QUIRE_DRILL_MAX_BEHIND` a `QUIRE_DRILL_MAX_DRIFT_RATIO`. Chybějící nebo prázdná tabulka znamená selhání.
3. **Integrita**: auditní hashový řetězec se v obnovené kopii ověří.
4. **Použitelnost**: aplikační role dokáže číst data přes row-level security.
5. **Čas**: od spuštění po úspěšné dokončení, ve srovnání s `QUIRE_DRILL_RTO_SECONDS` (výchozí hodnota 3600).

Drill nikdy nezapisuje do živé databáze ani jejích svazků: záložní svazky a WAL se připojí jen pro čtení a testovací cluster se na konci odstraní, ať už drill uspěje, nebo selže.

Nastavte `QUIRE_DRILL_REPORT` na cestu k JSON reportu, který se zapíše při úspěchu i selhání, a naplánujte spuštění z hostitele Dockeru:

```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
```

Spouštějte ho každý měsíc a před každou aktualizací. Neúspěšný drill aktualizaci blokuje. Jednou za čtvrt roku nechte skutečné obnovení k určitému okamžiku na náhradním hostiteli provést někoho, kdo tento postup nepsal, a dovolte mu použít pouze tento dokument.

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

Místní soubory jsou ve svazku `files`. Zálohujte ho současně s databází a obnovte oba najednou:

```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 .
```

U objektového úložiště zapněte verzování bucketu a ponechte předchozí verze 35 dnů. Obnovu souborů k určitému okamžiku pak zajistí přímo bucket.

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