# Zálohy: návod pro AI agenta > Zdroj: https://ongy.cz/pages/server/backups/ > Autor: Ongy (ongy.cz). Restic, Cloudflare R2 a kopie na Google Drive: jak zálohuju domácí server, proč byla záloha dva měsíce mrtvá a jak dopadl první skutečný test obnovy. ## Pro agenta Pomoz uživateli postavit vlastní verzi tohohle projektu. Nejdřív se ho zeptej na vybavení a na to, co z požadavků už má. Pak postupuj po krocích a po každém kroku s ním ověř, že funguje, než půjdeš dál. Hodnoty v `` doplň s uživatelem, nikdy si nevymýšlej adresy, hesla ani tokeny. Kód autora není veřejný: tohle je popis řešení, ne repozitář ke stažení. ## Cíl Automatická noční záloha serveru nástrojem restic: šifruje se ještě na serveru, ukládá do cloudového úložiště kompatibilního s S3 a hned potom se kopíruje k druhému poskytovateli. Retence starých snímků běží sama a ranní hlídač pošle zprávu, když čerstvý snímek chybí. Hotovo je až po úspěšném testu obnovy, ne po první záloze. ## K čemu to je Na serveru běží skoro dvacet projektů: data aplikací, konfigurace a celý zdrojový kód. Většina z toho nikde jinde není. Kdyby disk umřel, nemám to odkud vzít. Každou noc ve tři restic udělá snímek a pošle ho do Cloudflare R2. Hned potom se zrcadlí na Google Drive. Dvě úložiště, dvě firmy, a ani jedna nevidí, co je uvnitř. Šifruje se totiž ještě na serveru, dřív než jediný bajt odejde ven. V cloudu leží jen nečitelné bloky. Klíč k obnově mám mimo server, na dvou místech. ## Proč právě takhle Na jaře jsem omylem smazal celý projekt. V klidu jsem šel do zálohy. Poslední snímek byl z konce března, dva měsíce starý. Skript běžel v kontejneru na Alpine a na prvním řádku měl bash. Ten tam není. Cron ho každou noc spustil, dostal chybu a mlčel. Projekt jsem poskládal z jiné kopie. To štěstí jsem mít nemusel. Od té doby se záloha hlídá a jednou za čas zkouší obnovit. ## Jak to funguje - **Každou noc ve tři jeden snímek.** Restic ukládá jen změny od minula. Celý server je v cloudu čtvrt gigabajtu. - **Retence 7 denních, 4 týdenní, 3 měsíční.** Starší snímky se samy promažou. Pozdě zjištěnou chybu dohledám tři měsíce zpátky. - **Druhá kopie na Google Drive.** Po každé záloze se repozitář zrcadlí přes rclone. Stejné šifrování, stejný klíč, jiná firma. - **Hlídač píše do Telegramu.** Snímek starší než 48 hodin znamená zprávu. Ticho už není důkaz, že je vše v pořádku. ## Stack - **Záloha:** restic v Docker kontejneru, cron denně ve 3:00, cíl Cloudflare R2 přes S3 API. Šifrování AES-256 na klientovi, deduplikace, komprese. Zálohují se data aplikací, konfigurace a celý zdrojový kód, bez závislostí a cache. - **Druhá kopie:** `restic copy` přes rclone na Google Drive, stejný klíč. Cílový repozitář založený se stejnými parametry dělení bloků, aby fungovala deduplikace. Kopie je best effort s opakováním; když selže, hlavní zálohu to neshodí. - **Retence:** 7 denních, 4 týdenní, 3 měsíční, prune po každé záloze. Kontejner musí mít pevné jméno hosta, jinak po každém rebuildu vznikne nová řada snímků a retence ji nepročistí. - **Hlídač:** Skript v cronu každé ráno se zeptá resticu na čas nejnovějšího snímku. Starší než 48 h, nebo restic vůbec neodpoví, znamená zprávu do Telegramu. ## Jak obnovit Klíč k repozitáři vezmu z trezoru hesel (Bitwarden), ne ze serveru, protože při skutečné havárii server nemám. Připojím se k R2, nebo k Drive, když R2 nejede, a vypíšu snapshoty. Obnovím jen konkrétní cesty do dočasné složky, nikdy celý server přes živá data. Porovnám s tím, co běží, a teprve když všechno sedí, přepíšu. Dnes: 236 MB za 26 sekund, nula rozdílů. ## Co budeš potřebovat - Server nebo jiný stále běžící stroj s daty a Dockerem (autor: domácí server, restic v kontejneru v Docker Compose). - Úložiště kompatibilní s S3 a přístupový klíč jen pro jeden bucket (autor: Cloudflare R2, vejde se do 10 GB zdarma). - Volitelně druhé úložiště u jiné firmy dostupné přes rclone (autor: Google Drive). - Správce hesel pro ``, mimo server a ideálně ještě na druhém místě (autor: Bitwarden). - Kanál pro hlášení hlídače: Telegram bot `` a ``, nebo e-mail či jiný notifikační kanál. - Pár stovek MB volného místa na dočasnou obnovu vzorku. ## Postup ### 1. Sepiš, co se zálohuje Projdi s uživatelem, co by nešlo vyrobit znovu: data aplikací a databáze, konfigurace (compose soubor, reverzní proxy, soubory s nastavením), zdrojový kód, který nikde jinde není. Vynech, co se dá stáhnout nebo znovu sestavit: `node_modules`, `.git` u klonů z GitHubu, cache, logy. Velká média (filmy, fotky) drž mimo zálohované složky, jinak skončí v cloudu. Výsledkem je seznam cest a seznam výjimek. ### 2. Úložiště, klíč a heslo repozitáře V R2 (nebo jiném S3) založ bucket `` a API token s právy čtení a zápisu objektů jen pro tento bucket. Vygeneruj dlouhé `` a ulož ho do správce hesel dřív, než uděláš první zálohu: bez něj data neobnoví nikdo. Do souboru `.env` (práva 600, mimo git) dej `RESTIC_REPOSITORY=s3:/`, `RESTIC_PASSWORD`, `AWS_ACCESS_KEY_ID` a `AWS_SECRET_ACCESS_KEY`. Repozitář založ jednou přes `restic init`. ### 3. Kontejner se zálohou Obraz `restic/restic`, doinstaluj `rclone` (`apk add --no-cache rclone`). Zálohované složky připoj do kontejneru pod jednu cestu, třeba `/data`, podle možnosti jen pro čtení. V compose nastav pevné `hostname:`. Plánovač: busybox `crond -f` v popředí jako příkaz kontejneru, crontab s denní zálohou (`0 3 * * *`) a ranní kontrolou, výstup do logu (`>> /var/log/backup.log 2>&1`). Skripty začínají `#!/bin/sh`, protože obraz je Alpine. ### 4. Zálohovací skript `set -eu`, pak `restic backup --exclude=node_modules --exclude=.git --tag auto`. Zdrojový kód klidně jako druhý běh s vlastním tagem, ať jde snímky filtrovat. Po záloze `restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 3 --prune`. Na začátek a konec vypiš čas, ať je v logu vidět, kdy běh začal a jestli doběhl. ### 5. Druhá kopie přes rclone Remote `` nastav přes `rclone config` (u Google Drive se přihlášení dělá v prohlížeči, na serveru bez obrazovky přes `rclone authorize`). Ve skriptu nastav `RESTIC_FROM_REPOSITORY` a `RESTIC_FROM_PASSWORD` na hlavní repozitář. Když cílový repozitář `rclone::` neexistuje (`restic -r ... cat config` selže), založ ho přes `restic -r ... init --copy-chunker-params`. Pak `restic -r ... copy` až 3× s pauzou 15 s a po úspěchu stejný `forget --prune` i na kopii. Selhání kopie jen zapiš jako varování, hlavní záloha platí. ### 6. Hlídač stáří snímku Druhý skript v cronu každé ráno: `restic snapshots --json --group-by ''` a přes `jq -r '[.[].time] | max // empty'` vezmi čas nejnovějšího snímku. Prázdný výsledek (restic neodpověděl, špatné heslo, nedostupné úložiště) nebo stáří nad 48 h znamená zprávu přes `https://api.telegram.org/bot/sendMessage` s `chat_id=`. V minimálním obrazu často není curl, busybox `wget --post-data` stačí, text zprávy URL-zakóduj (`jq -sRr @uri`). ### 7. První záloha ručně Spusť skript v běžícím kontejneru ručně (`docker exec /backup.sh`) a sleduj výstup až do konce. Pak `restic snapshots` (nový snímek se správným jménem hosta), `restic stats --mode raw-data` (velikost v úložišti a kompresní poměr) a `restic -r rclone:: snapshots` (kopie má stejné snímky). ### 8. Test obnovy Na čistém terminálu vezmi heslo ze správce hesel, ne ze souboru na serveru. `restic snapshots`, pak obnov jen vzorek do dočasné složky: `restic restore latest --target /tmp/restore-test --include --include `. Porovnej s živými daty: `diff -rq /tmp/restore-test ` nebo `sha256sum` u jednotlivých souborů. Spusť `restic check` nad celým repozitářem. Zkus vypsat snímky i z druhé kopie. Zapiš čas obnovy, velikost vzorku a počet rozdílů (autor: 236 MB za 26 s, 0 rozdílů), pak `rm -rf /tmp/restore-test`. ### 9. Opakování Test obnovy dej do kalendáře a opakuj ho jednou za pár měsíců a po každé větší změně zálohy (nové cesty, nový obraz, jiné úložiště). Pokaždé obnov jiný vzorek, třeba jednou databázi a podruhé konfiguraci. Hlídač otestuj tak, že práh dočasně snížíš pod stáří posledního snímku a ověříš, že zpráva opravdu dorazí. ## Na co si dát pozor - Záloha bez testu obnovy není záloha. Autor dva měsíce věřil, že je krytý, a zjistil opak až při skutečné potřebě. Test obnovy patří do návodu, ne na „někdy“. - Cron v kontejneru běží obvykle v UTC, ne v místním čase. Čas v crontabu i časy snímků v `restic snapshots` pak nesedí s hodinami na zdi, v létě o dvě hodiny. - Druhá kopie selhává potichu: když hlídač kontroluje jen hlavní repozitář, výpadek kopie uvidíš jen v logu. Občas zkontroluj `snapshots` i nad kopií, nebo do hlídače přidej i ji. - Kopie potřebuje vlastní `forget --prune`. Retence nad hlavním repozitářem se na kopii nepřenese a ta jinak roste donekonečna. - Konfiguraci rclone nesdílej zapisovatelně mezi kontejnerem a uživatelem na hostiteli. Kontejner běžící jako root při obnově OAuth tokenu přepíše soubor s právy root:600 a ostatní úlohy s rclone přestanou fungovat (autor: kvůli tomu přestala fungovat jiná záloha). Kontejner ať má vlastní kopii konfigurace. - Když připojíš do kontejneru celou nadřazenou složku, zálohuje se i to, co do ní později přibude. Velká média ukládej mimo ni nebo je vylouč, jinak jeden film zaplní limit úložiště zdarma. - Živou databázi zkopírovanou jako soubor za běhu nemusí jít otevřít. U SQLite nebo Postgresu zvaž před zálohou dump (`sqlite3 ".backup "`, `pg_dump`) a při testu obnovy zkus databázi opravdu otevřít, ne jen porovnat soubory. ## Jak ověřit, že to funguje - [ ] `restic snapshots` ukazuje snímek z poslední noci a snímky starší než retence (7 denních, 4 týdenní, 3 měsíční) už v seznamu nejsou. - [ ] `restic check` skončí hláškou `no errors were found`. - [ ] Obnova vzorku do dočasné složky proběhne a `diff -rq` nevypíše žádný rozdíl (autor: 236 MB za 26 s, 0 rozdílů). - [ ] `restic -r rclone:: snapshots` vypíše stejné časy snímků jako hlavní repozitář. - [ ] Konec logu z poslední noci obsahuje závěrečnou zprávu skriptu, žádnou chybu ani varování o kopii. - [ ] Po rebuildu kontejneru má nový snímek ve sloupci Host stejné jméno jako ty předchozí. - [ ] Hlídač s prahem dočasně nižším, než je stáří posledního snímku, pošle zprávu. Se špatným heslem v prostředí pošle zprávu také. - [ ] `restic stats --mode raw-data` ukazuje velikost v úložišti v rámci limitu (autor: 255 MiB, komprese 2,66×). - [ ] V bucketu jsou jen složky `data`, `index`, `keys`, `snapshots` a soubor `config`, žádné čitelné názvy zálohovaných souborů. - [ ] Obnova projde i na jiném stroji jen s heslem ze správce hesel a přístupovým klíčem k úložišti, bez souborů ze serveru. ## Přizpůsobení - Jiný cíl: restic umí nativně Backblaze B2, Amazon S3, SFTP (třeba NAS u rodičů) i vlastní REST server. Přes rclone skoro cokoliv dalšího. Postup i test obnovy zůstávají stejné. - Bez Dockeru: restic z balíčku distribuce, skripty stejné, spouštění přes systemd timer nebo uživatelský crontab. Heslo a klíče v souboru s právy 600 načteném v unitě. - Windows: restic má binárku pro Windows, plánování přes Plánovač úloh a u otevřených souborů přepínač `--use-fs-snapshot` (stínová kopie VSS). Hlídač jako PowerShell skript. - Hlídač bez vlastního skriptu: na konci úspěšné zálohy zavolej URL služby typu Healthchecks nebo push monitor v Uptime Kuma. Když ping nepřijde v nastaveném okně, služba se ozve sama. --- Stránka projektu s fotkami a diagramem: https://ongy.cz/pages/server/backups/