Kontekst
Rok wcześniej zmodernizowałem temu klientowi backend porzuconego systemu do obsługi zamówień — opisałem to w osobnym case study Legacy Rescue. System wrócił do stanu, w którym dało się go rozwijać, ale fizycznie wciąż stał na tym samym serwerze co od lat. Migracja była wtedy wymieniona jako „dalsza możliwość" — poza zakresem umowy bazowej.
Po kilku tygodniach stabilnej pracy zmodernizowanego systemu klient wrócił, żeby tę możliwość zrealizować. Powstała osobna umowa: przeniesienie systemu na nową infrastrukturę, HTTPS, kopie zapasowe i tydzień nadzoru po wdrożeniu.
Problem: „działa" to nie to samo co „bezpieczne"
Serwer działał. Dla użytkownika nic nie wskazywało na problem. Pod spodem było inaczej:
- System operacyjny po końcu wsparcia. Ubuntu 16.04 — producent zakończył wydawanie łatek bezpieczeństwa w 2024. Maszyna przez około dwa lata nie dostawała żadnych poprawek.
- Brak HTTPS. Pracownice logowały się do systemu, a dane zamówień wędrowały między przeglądarką a serwerem otwartym tekstem.
- Zero kopii zapasowych. Pojedyncza awaria dysku oznaczała bezpowrotną utratę całej bazy — ponad 18 000 historycznych zamówień.
- Aktualizacje ręczne — czyli w praktyce żadne.
- Drogi serwer dedykowany — 240 zł miesięcznie. Cena i parametry dawno przerosły faktyczne potrzeby firmy.
To są ryzyka, które nie bolą — dopóki nie zabolą katastrofalnie. Brak backupu nie przeszkadza w niczym przez lata. Przeszkadza dokładnie raz.
Podejście
Migracja systemu, z którego firma korzysta codziennie w trzech lokalizacjach, ma jedno żelazne wymaganie: pracownice następnego dnia mają zastać działający system — najlepiej niczego nie zauważywszy. Plan oparłem na trzech zasadach:
- Identyczne środowisko na nowym serwerze — ta sama baza danych, ta sama wersja Node, ten sam reverse proxy. Zero ryzyka, że coś zadziała inaczej.
- Przełączenie w weekend, w oknie, w którym nikt nie pracuje.
- Stary serwer utrzymany jako siatka bezpieczeństwa — gotowy do natychmiastowego powrotu, dopóki nowy nie potwierdzi stabilności.
Przebieg migracji
1. Identyczne środowisko, nowa maszyna
Nowy serwer postawiłem w chmurze Hetznera na Ubuntu 24.04 — systemie ze wsparciem do 2029. Cały stack odtworzyłem jeden do jednego: MySQL w kontenerze Docker, Node w tej samej wersji, Caddy jako reverse proxy. Identyczność środowiska to nie wygoda — to eliminacja całej klasy ryzyka. Migracja nie była okazją do „przy okazji" zmieniania wersji bibliotek.
2. Cutover w 14 minut
Czas życia wpisu DNS (TTL) obniżyłem do 5 minut z kilkudniowym wyprzedzeniem — żeby w momencie przełączenia domena przepięła się na nowy serwer niemal natychmiast, a nie po godzinach propagacji. Właściwy cutover wykonałem w sobotni wieczór: zrzut i import bazy, przepięcie domeny, weryfikacja działania. Od 21:00 do 21:14. W poniedziałek pracownice weszły pod tym samym adresem, tymi samymi hasłami, do tego samego systemu.
3. HTTPS — i pułapka, której się nie spodziewałem
Caddy z Let's Encrypt wystawia i odnawia certyfikat automatycznie — klient dostaje kłódkę w przeglądarce i nigdy nie myśli o jej odnawianiu. Przy pierwszym podejściu walidacja domeny jednak padała.
Przyczyna: Let's Encrypt sprawdza domenę z kilku lokalizacji na świecie naraz, a część tych zapytań — przy DNS jeszcze w trakcie propagacji — trafiała na stary serwer. Rozwiązanie: zatrzymałem serwer WWW na starej maszynie. Wtedy każda ścieżka walidacji prowadziła już tylko do nowego serwera. Certyfikat wystawił się od ręki.
4. Stary serwer jako siatka bezpieczeństwa
Starego serwera nie skasowałem od razu po cutoverze. Zostawiłem go z wyłączoną częścią zapisującą dane — żeby nie dało się przypadkiem zapisać zamówienia w dwóch miejscach naraz i rozjechać baz — ale gotowego do przywrócenia w kilkanaście minut, gdyby na nowym serwerze coś wyszło nie tak. Wygasił się dopiero wtedy, gdy nowa infrastruktura potwierdziła stabilność przez pełny tydzień.
Backup, który naprawdę jest backupem
Umowa wymagała kopii zapasowej w drugiej lokalizacji. Tu kryje się pułapka, przez którą wiele firm ma „backup", który nie zabezpiecza przed niczym:
Kopia trzymana w tym samym centrum danych co serwer nie jest kopią w drugiej lokalizacji. Pożar, zalanie czy awaria zasilania całego ośrodka zabiera wtedy oryginał i kopię jednocześnie.
Dlatego kopie trafiają do dedykowanego magazynu w innym kraju niż serwer główny. Codziennie, automatycznie: zrzut bazy, kompresja, transfer szyfrowanym kanałem. Rotacja: 7 kopii dziennych, 4 tygodniowe i 12 miesięcznych — żeby dało się cofnąć zarówno do wczoraj, jak i do stanu sprzed pół roku.
I rzecz, którą pomija się najczęściej: kopię trzeba odtworzyć, żeby wiedzieć, że działa. Wykonałem testowe odtworzenie na czystej bazie — liczba rekordów zgodziła się co do wiersza. Backup, którego nikt nigdy nie odtworzył, to nie zabezpieczenie, tylko nadzieja.
Tydzień nadzoru i dostrojenie wydajności
Umowa obejmowała tydzień nadzoru po przełączeniu. Po kilku dniach pracownice zgłosiły, że zakładka z listą zleceń wczytuje się wolno. Diagnoza: przy ponad 18 000 rekordów interfejs za każdym wejściem pobierał całą historię zamówień.
Dwie zmiany: ograniczenie liczby zleceń pobieranych domyślnie oraz kompresja odpowiedzi serwera. Lista, która wcześniej ładowała się kilka sekund, zaczęła otwierać się w około pół sekundy. Wszystko w cenie umowy, w ramach nadzoru — bo na tym nadzór polega.
Przeniesienie własności
Konto nowego serwera przy cutoverze założyłem tymczasowo na siebie, żeby nie blokować prac przed weekendem. Po tygodniu przeniosłem je na firmę klienta: faktury idą teraz na firmę (VAT do odzyskania), a pełna własność infrastruktury jest po jego stronie. Ja zostałem z dostępem technicznym na potrzeby nadzoru. Klient ma być właścicielem swojej infrastruktury — nie zakładnikiem wykonawcy.
Stack
- Serwer: Hetzner Cloud, Ubuntu 24.04 LTS
- Aplikacja: Node.js + MySQL w kontenerze Docker, zarządzane przez PM2
- Reverse proxy / TLS: Caddy 2 + Let's Encrypt (auto-HTTPS)
- Kopie zapasowe: rclone → magazyn off-site w innym kraju, cron, rotacja 7 / 4 / 12
Rezultaty
- Cutover w 14 minut — pracownice następnego dnia pracowały bez żadnej zmiany w nawykach.
- System operacyjny ze wsparciem do 2029 i automatycznymi aktualizacjami bezpieczeństwa.
- HTTPS z certyfikatem odnawianym samoczynnie — wcześniej połączenie było nieszyfrowane.
- Codzienna kopia zapasowa w innym kraju, z potwierdzonym odtworzeniem — wcześniej nie było żadnej.
- Lista zleceń: z kilku sekund do około pół sekundy.
- Koszt utrzymania spadł z 240 zł do ~39 zł miesięcznie — około 200 zł oszczędności co miesiąc, blisko 2 400 zł rocznie.
- Własność infrastruktury i faktury — po stronie firmy klienta.
Wyzwania i lekcje
1. TTL DNS obniża się zawczasu, nie w dniu cutoveru
Gdyby TTL został domyślny, przełączenie domeny rozciągnęłoby się na godziny. Obniżenie go z kilkudniowym wyprzedzeniem sprawiło, że cutover zmieścił się w kilkunastu minutach.
2. Let's Encrypt waliduje z wielu lokalizacji naraz
Dopóki stary serwer odpowiadał na ruch WWW, część walidacji trafiała na niego i certyfikat się nie wystawiał. Lekcja: przy migracji zatrzymaj serwer WWW na starej maszynie, zanim nowy będzie pobierał certyfikat.
3. „Kopia zapasowa" u dostawcy ≠ druga lokalizacja
Snapshoty oferowane w tym samym centrum danych są wygodne, ale nie chronią przed awarią całego ośrodka. Wymóg „druga lokalizacja" spełnia dopiero kopia fizycznie poza nim — najlepiej w innym kraju.
4. Backup bez testu odtworzenia to nie backup
Kopia, której nigdy nie odtworzono, to założenie, nie zabezpieczenie. Pierwsze odtworzenie testowe jest częścią wdrożenia, nie opcją.
5. Identyczny stack = cutover bez dramatu
Migracja i modernizacja stacku to dwie różne operacje. Łączenie ich w jedną mnoży ryzyko. Najpierw bezpieczne przeniesienie na identycznym środowisku — aktualizacje wersji to osobna, świadoma decyzja na później.
Co to case study mówi o tym, jak pracuję
- Ryzyko, które nie boli, to wciąż ryzyko. System operacyjny po końcu wsparcia, brak HTTPS, brak backupu — „działa" usypia czujność, a koszt zaniechania pojawia się naraz i w najgorszym momencie.
- Udaną migrację produkcyjną poznaje się po braku dramatu. Najlepszy cutover to taki, którego użytkownicy w ogóle nie zauważyli.
- Siatka bezpieczeństwa przed skokiem. Stary serwer zostaje jako możliwość powrotu, dopóki nowy nie udowodni, że jest stabilny.
- Backup istnieje dopiero wtedy, gdy go odtworzysz. Reszta to nadzieja podpięta pod cron.
- Klient jest właścicielem swojej infrastruktury. Konto, faktury i pełna kontrola należą do firmy — nie do wykonawcy.
Zgodnie z umową o zachowaniu poufności niniejsze case study pomija: nazwę firmy, jej lokalizację, dane osobowe pracowników i klientów oraz adresy i poświadczenia infrastruktury. Skupia się wyłącznie na procesie, decyzjach technicznych i jakościowych rezultatach.