Kopie zapasowe na serwerze: dobre praktyki, baza danych i test odtwarzania

Mateusz Bogolubow

Mateusz Bogolubow

12-11-2026

Jak robić kopie zapasowe serwera i bazy danych: zasada 3-2-1, pg_dump, mysqldump, SQLite, restic i test odtwarzania. Z wynikami lokalnych testów.

Kopie zapasowe na serwerze: dobre praktyki, baza danych i test odtwarzania
Kopie zapasowe serwera trzymaj według zasady 3-2-1: trzy kopie, na dwóch różnych nośnikach, jedna poza serwerem. Bazę danych kopiuj narzędziem bazy (np. pg_dump, mysqldump, sqlite3 .backup), a nie przez zwykłe kopiowanie plików działającej bazy. Najważniejszy jest test odtwarzania: kopia, której nie odtworzyłeś, może okazać się bezużyteczna.

Dysk się zepsuje, ktoś skasuje plik albo źle skonfigurujesz serwer. Kopia zapasowa decyduje, czy to drobna niedogodność, czy koniec projektu. W artykule o serwerze VPS pisałem, że przy VPS niezarządzanym za kopie odpowiadasz sam. Tu pokazuję, jak to zrobić dobrze, ze szczególnym naciskiem na bazę danych. Polecenia dla PostgreSQL, SQLite i narzędzia restic sprawdziłem lokalnie na macOS (na danych testowych), a MySQL opisałem z dokumentacji, bo nie uruchamiałem go. Na prawdziwym serwerze z Linuxem nie testowałem niczego, więc czasy i rozmiary nie są reprezentatywne.

Co oznacza zasada 3-2-1?

Zasada 3-2-1 mówi, że trzymasz trzy kopie danych (jedną podstawową i dwie zapasowe), na co najmniej dwóch różnych rodzajach nośników, z czego jedna kopia leży poza miejscem, w którym stoi serwer. Tak opisuje ją brytyjskie DfE Cyber Security Hub (strona sprawdzana w lipcu 2026), które powołuje się na rekomendację NCSC, że nawet przy rozwiązaniach chmurowych warto stosować 3-2-1. Dla VPS oznacza to na przykład:

KopiaGdzie
1 (podstawowa)dane na serwerze
2kopia na tym samym serwerze lub w jego migawce, do szybkiego odtwarzania
3kopia poza serwerem: inny dostawca, chmura albo Twój komputer

Dwa dopowiedzenia z dokumentów rządowych. Według przewodnika CISA #StopRansomware (wydanie z września 2023) warto trzymać kopie offline i szyfrowane, bo wiele odmian ransomware szuka dostępnych kopii, żeby je usunąć lub zaszyfrować. A według DfE jedna kopia powinna być odłączona od sieci, żeby przynajmniej ona pozostała nietknięta. Oba źródła zalecają też regularne testy odtwarzania.

Co kopiować na serwerze?

Nie kopiuj wszystkiego na ślepo. Zdecyduj, co jest trudne do odtworzenia:

CoCzy kopiowaćDlaczego
Baza danychtak, zawszeto są Twoje dane, których nie odtworzysz z kodu
Pliki wgrane przez użytkownikówtakteż nie ma ich nigdzie indziej
Konfiguracja (np. /etc, pliki .env)takodtworzenie serwera trwa o wiele krócej
Klucze i certyfikatytak, w bezpiecznym miejscukopię z kluczami szyfruj (restic robi to domyślnie), bo to wrażliwe dane
Kod aplikacjizwykle nie, jeśli jest w Giciewystarczy repozytorium, ale sprawdź, czy ma kopię
Zależności i cache (node_modules, .cache)nieodtworzysz je jednym poleceniem

Dla każdej z tych rzeczy zadaj sobie pytanie: „ile godzin zajmie mi odtworzenie tego od zera?”. Jeśli dużo, kopiuj.

Jak zrobić kopię bazy danych?

To najważniejsza część. Nie kopiuj plików działającej bazy zwykłym cp, bo mogą być w trakcie zapisu, a kopia nie będzie spójna. Zamiast tego użyj narzędzia samej bazy, które robi kopię logiczną (zrzut, dump) albo spójną migawkę. Poniżej trzy najczęstsze bazy.

Jak zrobić kopię PostgreSQL (pg_dump)?

Według dokumentacji PostgreSQL pg_dump tworzy spójną kopię bazy: odpowiada stanowi z momentu startu programu, a w trakcie nie blokuje pozostałych operacji (wyjątkiem są operacje wymagające blokady ekskluzywnej, np. większość ALTER TABLE). Dwa najważniejsze formaty:

# format tekstowy (SQL), skompresowany
pg_dump sklep | gzip > sklep.sql.gz

# format "custom": skompresowany, pozwala odtwarzać wybrane tabele
pg_dump -Fc sklep > sklep.dump

Odtwarzanie jest osobnym krokiem i baza docelowa musi istnieć, bo kopia jej nie tworzy:

# odtwarzanie formatu custom do NOWEJ bazy
createdb sklep_odtworzona
pg_restore -d sklep_odtworzona sklep.dump

# odtwarzanie z pliku tekstowego, z zatrzymaniem przy pierwszym błędzie
createdb sklep_z_sql
gunzip -c sklep.sql.gz | psql -X --set ON_ERROR_STOP=on -d sklep_z_sql

Sprawdziłem to na PostgreSQL 16 na własnej, tymczasowej instancji: tabela z 1000 wierszy została skopiowana do pliku -Fc (ok. 6,6 KB) i do tekstowego z gzip (ok. 4,9 KB), a po odtworzeniu do nowej bazy obie kopie dały 1000 wierszy. Obcięty plik kopii dał wyraźny błąd „could not read from input file: end of file”, co pokazuje, że uszkodzoną kopię widać dopiero przy odtwarzaniu.

Pułapka z rolami. Według dokumentacji pg_dump kopiuje tylko jedną bazę i nie zawiera informacji o rolach ani tablespace'ach. W moim teście plik kopii miał 0 wierszy CREATE ROLE, ale zawierał GRANT SELECT … TO aplikacja i OWNER TO, więc przy odtwarzaniu na świeżym serwerze role muszą już istnieć. Dlatego obok kopii bazy zrób kopię ról: pg_dumpall --globals-only (u mnie zawierała CREATE ROLE aplikacja;). Przy dużych bazach dokumentacja zaleca format katalogowy i równoległość (pg_dump -j, pg_restore -j) oraz uruchomienie ANALYZE po odtworzeniu.

Jak zrobić kopię MySQL (mysqldump)?

Według dokumentacji MySQL mysqldump wykonuje kopie logiczne, czyli zestaw instrukcji SQL, które odtwarzają obiekty i dane. Dokumentacja zaznacza, że nie jest to narzędzie do szybkich ani skalowalnych kopii dużych ilości danych, a dla dużych baz lepsza jest kopia fizyczna. Typowe polecenie wygląda tak:

mysqldump --single-transaction --routines --triggers --databases sklep > sklep.sql

Co znaczą opcje według dokumentacji: --single-transaction wysyła BEGIN przed zrzutem danych (bez niej potrzebne jest uprawnienie LOCK TABLES), --routines dodaje procedury i funkcje, --triggers dodaje wyzwalacze (domyślnie włączone), a --databases sprawia, że w pliku są instrukcje CREATE DATABASE. Przywracasz poleceniem mysql < sklep.sql i musisz mieć uprawnienia do wykonania instrukcji z pliku. Tego narzędzia nie uruchamiałem, więc nie podaję wyników, a spójność kopii InnoDB przy --single-transaction warto sprawdzić w pełnej dokumentacji.

Jak zrobić kopię SQLite?

SQLite to jeden plik, więc kuszące jest zwykłe cp. Dokumentacja SQLite opisuje, że takie kopiowanie wymaga blokady bazy na czas operacji, a awaria w trakcie może uszkodzić kopię. Bezpieczniej użyć mechanizmu samej bazy. W powłoce sqlite3 służy do tego polecenie .backup:

sqlite3 app.db ".backup 'app_backup.db'"
sqlite3 app_backup.db "pragma integrity_check"

Sprawdziłem to na SQLite 3.51: kopia miała 500 wierszy tak jak oryginał, a integrity_check zwrócił ok. Dokumentacja wspomina też o VACUUM INTO, które robi kopię działającej bazy do osobnego pliku (u mnie również 500 wierszy). Samego polecenia .backup dokumentacja SQLite na stronie o kopiach nie opisuje, więc opieram się na własnym teście.

Jak zrobić spójną kopię, gdy baza jest duża?

Zrzut logiczny przy dużej bazie trwa długo, a odtwarzanie jeszcze dłużej (dokumentacja PostgreSQL mówi o „wielu godzinach”). Wtedy rozważ kopie fizyczne (dla PostgreSQL i MySQL istnieją osobne narzędzia, których tu nie opisuję i nie testowałem) oraz równoległość tam, gdzie dokumentacja ją przewiduje. Zawsze zmierz, ile trwa pełne odtworzenie, bo to ono decyduje, jak długo będzie przestój po awarii.

Jak wysłać kopię poza serwer?

Trzecia kopia ma leżeć poza serwerem. Dwa proste sposoby:

  1. Archiwum wysłane przez SFTP lub rsync. Spakuj, wyślij i sprawdź sumę kontrolną. W moim teście skrót SHA-256 archiwum przed i po skopiowaniu był identyczny. Wysyłanie plików opisuje artykuł o FTP i SFTP, a łączenie z serwerem o SSH.
  2. Narzędzie do kopii z szyfrowaniem i wersjami, np. restic (opisane niżej).

Przy rsync pamiętaj o opcji --dry-run, która pokazuje, co zostanie skopiowane, zanim cokolwiek zmienisz (u mnie pokazała tylko nowy plik).

Jak działa restic i dlaczego warto go znać?

Restic to program do kopii, który szyfruje repozytorium hasłem, robi migawki (snapshots) i deduplikuje dane: tego samego pliku nie zapisuje dwa razy. Dokumentacja definiuje snapshot jako zawartość katalogu w określonym momencie. Przetestowałem na lokalnym repozytorium:

export RESTIC_REPOSITORY=/sciezka/do/repozytorium
export RESTIC_PASSWORD="haslo-do-repozytorium"

restic init                      # utwórz zaszyfrowane repozytorium
restic backup dane               # pierwsza kopia
restic backup dane               # druga, bez zmian: nic nie dopisuje
restic check                     # sprawdź integralność
restic restore latest --target odtworzone
restic forget --keep-last 1 --dry-run   # co zostałoby usunięte

Wyniki z mojego testu:

  • Pierwsza kopia dodała 4 nowe pliki. Druga, bez zmian, dodała 0 B, czyli deduplikacja zadziałała. Po zmianie jednego pliku trzecia kopia dodała tylko około 1,8 KiB.
  • restic check zakończył się komunikatem „no errors were found”.
  • restore do nowego katalogu dał identyczną zawartość (diff -r bez różnic).
  • forget --keep-last 1 --dry-run pokazał „keep 1 snapshots” i „remove 2 snapshots”, czyli politykę retencji można sprawdzić bez usuwania.
  • Przy złym haśle: „wrong password or no key found”. Zapisz hasło do repozytorium poza serwerem, np. w menedżerze haseł, bo bez niego nie odtworzysz kopii.

Testowałem tylko lokalne repozytorium. Restic obsługuje też zdalne repozytoria (m.in. przez SFTP i S3), ale tego nie sprawdzałem.

Jak zautomatyzować kopie?

Kopia robiona ręcznie bywa pomijana, więc ustaw harmonogram. Na Linuxie służy do tego cron. Poniżej przykład skryptu, który robi zrzut bazy PostgreSQL i usuwa stare pliki. To przykład z wiedzy ogólnej, którego nie uruchamiałem na serwerze (uruchamiałem tylko pojedyncze polecenia pg_dump i pg_restore):

#!/usr/bin/env bash
set -euo pipefail

DIR=/var/backups/baza
mkdir -p "$DIR"

# zrzut bazy z datą w nazwie
pg_dump -Fc sklep > "$DIR/sklep-$(date +%F).dump"

# usuń zrzuty starsze niż 14 dni
find "$DIR" -name 'sklep-*.dump' -mtime +14 -delete

Do uruchamiania co noc możesz użyć wpisu w crontab -e, np. 0 3 * * * /usr/local/bin/backup-baza.sh (codziennie o 3:00). Pamiętaj, że sam zrzut leży jeszcze na serwerze, więc drugim krokiem harmonogramu musi być wysłanie go poza serwer. Uprawnienia plików i skryptów (chmod) opisuje artykuł o chmod w Linuksie. Hasło do bazy do skryptu przekazuj przez plik konfiguracyjny z ograniczonymi uprawnieniami, a nie w treści polecenia.

Jak testować odtwarzanie kopii?

Uwaga mentora: najpierw wybierz, kiedy robisz test odtwarzania, a dopiero potem narzędzie do kopii. Kopia bez zapisanego terminu testu zwykle nigdy nie zostanie sprawdzona.

To najważniejszy krok i najczęściej pomijany. Dokumenty CISA i DfE zalecają regularne testy, choć CISA nie podaje, jak często. Test polega na tym, że naprawdę odtwarzasz kopię w osobnym miejscu i sprawdzasz, czy dane są kompletne:

  1. Utwórz nową, pustą bazę lub środowisko (nie nadpisuj produkcji).
  2. Odtwórz kopię (pg_restore, psql, mysql lub restic restore).
  3. Sprawdź liczbę rekordów i kilka znanych wartości. U mnie zwykłe select count(*) pokazało 1000 wierszy w oryginale i w każdej odtworzonej bazie.
  4. Zmierz czas odtwarzania, bo od niego zależy, jak długo potrwa przestój.
  5. Zapisz wynik i termin kolejnego testu.

Pamiętaj też o rolach i uprawnieniach bazy, bo bez nich odtworzona baza może się nie uruchomić z aplikacją.

Jakie błędy popełniają początkujący przy kopiach zapasowych?

BłądSkutekJak uniknąć
Kopia leży tylko na tym samym serwerzeawaria serwera lub atak niszczy i dane, i kopiętrzymaj kopię poza serwerem (3-2-1)
Nigdy nie testowano odtwarzaniakopia okazuje się pusta lub uszkodzona dopiero w awariiharmonogram testów i zapis wyników
Kopiowanie plików działającej bazy zwykłym cpniespójna kopiaużyj pg_dump, mysqldump lub sqlite3 .backup
Brak ról i uprawnień bazy (PostgreSQL)odtworzona baza zgłasza błędy uprawnieńdodaj pg_dumpall --globals-only
Zgubione hasło do repozytoriumzaszyfrowanej kopii nie odtworzyszzapisz hasło poza serwerem
Kopie bez retencjidysk się zapełnia i kopie przestają się robićpolityka usuwania starych kopii
Kopia dostępna z serwera z prawem zapisuatak lub błąd może ją skasowaćkonto z minimalnymi uprawnieniami, kopia offline lub niezmienna
Hasło bazy w skrypcie widocznym dla innychwyciek hasłaplik z uprawnieniami 600

Jak wygląda minimalny, dobry plan kopii dla VPS?

  1. Baza: codzienny zrzut narzędziem bazy do pliku z datą.
  2. Pliki i konfiguracja: kopia katalogów, których nie odtworzysz z kodu.
  3. Poza serwer: wysłanie kopii na drugi serwer, do chmury albo na własny komputer (SFTP, rsync lub restic).
  4. Retencja: np. 14 dziennych kopii i kilka starszych, a kopię offline raz w miesiącu.
  5. Test odtwarzania: co kwartał lub po każdej większej zmianie, z zapisanym wynikiem.
  6. Zabezpieczenie: szyfrowanie, osobne konto do kopii i hasło do repozytorium poza serwerem.

Przykładowy harmonogram, który możesz dopasować do własnego projektu. To moja propozycja, a nie standard:

CoJak częstoGdzie leży kopiaKto lub co sprawdza
Zrzut bazy danychcodziennie, np. w nocyserwer i poza serweremalert, gdy zrzut się nie utworzył
Pliki wgrane przez użytkowników i konfiguracjacodziennie lub tygodniowopoza serwerem (np. restic lub rsync)restic check lub porównanie sum kontrolnych
Kopia offlineraz w miesiącunośnik lub konto odłączone od siecizapis daty w notatkach
Test odtwarzaniaco kwartał i po większej zmianieosobna baza lub środowisko testowezapisany wynik: liczba rekordów i czas
Przegląd retencji i miejsca na dyskuraz w miesiącuserwer i miejsce docelowesprawdzenie wolnego miejsca

Pełny obraz bezpieczeństwa serwera tworzą: zapora (artykuł o firewallu na serwerze Linux), bezpieczne logowanie (SSH) i kopie, o których jest ten artykuł. Wdrożenie aplikacji na prawdziwym serwerze ćwiczysz w module wdrożenie produkcyjne w chmurze w kursie AI Engineer.

Podsumowanie

  • Zasada 3-2-1: trzy kopie, dwa rodzaje nośników, jedna poza serwerem. Dokumenty CISA i DfE zalecają też kopię offline i szyfrowanie.
  • Bazę kopiuj narzędziem bazy: pg_dump (PostgreSQL), mysqldump (MySQL), sqlite3 .backup (SQLite), a nie zwykłym kopiowaniem plików działającej bazy.
  • PostgreSQL: pg_dump nie zawiera ról, więc dodaj pg_dumpall --globals-only. Docelowa baza musi istnieć przed odtwarzaniem.
  • Restic daje szyfrowanie, migawki i deduplikację, ale bez hasła nie odtworzysz kopii.
  • Najważniejszy jest test odtwarzania: odtwórz kopię w osobnym miejscu i sprawdź dane. Obcięta kopia daje błąd dopiero przy odtwarzaniu.
  • Polecenia dla PostgreSQL, SQLite i restic sprawdziłem lokalnie na danych testowych, MySQL opisałem z dokumentacji, a na prawdziwym serwerze nie testowałem niczego.

Masz pytanie do artykułu? Chcesz zgłosić błąd?

Pytaj ogólnie o temat artykułu albo napisz, co poprawić. Jeśli chcesz omówić swój konkretny przypadek, umów się na rozmowę lub . Ze względów bezpieczeństwa nie przeglądamy ani nie publikujemy żadnych linków. Wiadomości z linkami nie są zapisywane. Limit: 2 pytania na godzinę.

Wyślemy na niego link do potwierdzenia pytania i odpowiedź.

Ostateczną decyzję o opublikowaniu pytania podejmuje administrator.

Konsultacja i warsztaty devmentor.pl

Masz pytania?

Partnerzy

Firmy, z którymi współpracujemy

To partnerzy odpowiedzialni za staże po naszych kursach. Organizują je dla najlepszych absolwentów.

Zobacz wszystkich partnerów →
  • VR Medical
  • Sophona
  • Robot Force