Installation
Migracja danych do PostgreSQL 18
Jednorazowa, bezpieczna dla danych procedura przeniesienia istniejącego wdrożenia PostgreSQL 16 na PostgreSQL 18 bez utraty danych.
Migracja danych do PostgreSQL 18
Kogo to dotyczy: tylko wdrożeń, które już działają na PostgreSQL 16 i chcą przejść na wersję QuoteNode wymagającą PostgreSQL 18. Świeże instalacje nie wymagają niczego — startują od razu na PostgreSQL 18.
Serwer PostgreSQL 18 nie wystartuje na katalogu danych PostgreSQL 16. Aplikacja odmawia startu na niekompatybilnym katalogu i nigdy nie przepisuje go automatycznie, więc istniejąca instalacja musi raz przenieść swoje dane do przodu. To celowa, jednorazowa procedura operatora.
Migracja jest bezpieczna dla danych z założenia: Twój istniejący wolumen danych (nazwijmy go A) nigdy nie jest kasowany, odtwarzany ani nadpisywany. Dane są kopiowane do nowego wolumenu PostgreSQL 18 (B); A pozostaje nietknięte jako natychmiastowy rollback, dopóki nie potwierdzisz, że B działa.
Są trzy ścieżki (ścieżka C to niezalecana ostateczność). Wybierz wg tego, czy masz dostęp do shella hosta:
| Ścieżka | Kiedy użyć | Mechanizm |
|---|---|---|
| A — konsola hosta | Możesz uruchomić docker na hoście (Ubuntu VPS, LAN Linux, zwykły Compose) | scripts/migrate-postgres-major.sh |
| B — stack tylko-UI | Masz tylko UI (Coolify, Portainer, QNAP, Synology) | infra/docker-compose.upgrade.yml |
| C — auto-upgrade in-place ⚠️ | Tylko ostateczność; ani A, ani B nie działa i akceptujesz ryzyko in-place | obraz pgautoupgrade (niezalecane) |
Ścieżki A i B są bezpieczne dla danych: kopiują do nowego wolumenu i zachowują oryginał nietknięty. Ścieżka C aktualizuje in-place — nie ma nietkniętego rollbacku, jest tylko Twój backup poza hostem. Preferuj A lub B.
Zanim zaczniesz (wszystkie ścieżki)
- Zrób kopię
.env. Zawiera sekrety, których nie da się odtworzyć, zwłaszczaDB_ENCRYPTION_KEY. - Zrób kopię bazy danych (dodatkowo do automatycznej kopii bezpieczeństwa robionej przez migrację):
docker compose exec postgres pg_dump -U quotenode quotenode > backup-before-pg18.sql - Wstrzymaj auto-deploy, jeśli Twoja platforma wdraża ponownie na ruchomym tagu (np. auto-deploy
Coolify,
:latestw Portainerze). Patrz Kolejność wydania — obraz wymagający PG18 nie może pojawić się przed wykonaniem migracji. - Zatrzymaj stack aplikacji (bez usuwania wolumenów). Nigdy nie uruchamiaj
docker compose down -v.
Ścieżka A — konsola hosta (zalecana, gdy masz shell)
To najprostsza ścieżka dla Ubuntu VPS, LAN Linux i każdego zwykłego hosta Docker Compose.
- Znajdź nazwę wolumenu danych:
docker volume ls | grep postgres - Zatrzymaj stack (zachowaj wolumeny):
docker compose down - Uruchom migrację. Przechwytuje dwa artefakty bezpieczeństwa (logiczny dump oraz surowy tarball
wolumenu) i weryfikuje oba przed dotknięciem czegokolwiek, wykonuje fail-closed preflight na żywym
klastrze, a następnie odtwarza wolumen na PostgreSQL 18 i przywraca dane:
Skrypt nie wykona niczego destrukcyjnego bezDB_NAME=quotenode DB_USERNAME=quotenode DB_PASSWORD='...' \ POSTGRES_VOLUME=<your_postgres_volume> \ DB_MAJOR_MIGRATE_CONFIRM=yes \ ./scripts/migrate-postgres-major.shDB_MAJOR_MIGRATE_CONFIRM=yesi przerywa (zostawiając dane nietknięte), jeśli wykryje nieoczekiwane role/extensiony lub za mało miejsca na dysku. - Wystartuj stack normalnie. Wstanie na PostgreSQL 18 i zastosuje wszelkie oczekujące migracje:
docker compose up -d - Wykonaj Weryfikację, a potem usuń artefakty bezpieczeństwa wypisane przez skrypt, gdy nabierzesz pewności.
Locale inne niż domyślne: jeśli źródłowa baza używa locale innego niż domyślne w obrazie, skrypt fail-closuje na sprawdzeniu encoding/collation (ciche zmiany sortowania mogą uszkodzić indeksy tekstowe). Uruchom ponownie z pasującym
POSTGRES_INITDB_ARGS(np.--locale=...).
Uwagi dla Coolify (hosty zarządzane)
Ścieżka A uruchomiona przez SSH na serwerze Coolify to najprostsza droga na hoście Coolify. Kilka specyfik Coolify, potwierdzonych w praktyce:
- Stan „Failed” przed migracją jest oczekiwany. Gdy Coolify wdraża wersję wymagającą PostgreSQL 18
na istniejącym wolumenie PostgreSQL 16, kontener
postgreswpada w pętlę restartów (FATAL: database files are incompatible with server) i Coolify oznacza wdrożenie jako Failed. Twoje dane nie są naruszone — PostgreSQL po prostu odmawia startu na starszym katalogu danych. - Aby zmigrować, musisz usunąć kontener
postgres. Ścieżka A odtwarza wolumen danych, a Docker odmawia usunięcia wolumenu, dopóki jakikolwiek kontener — nawet zatrzymany — wciąż się do niego odwołuje. Dlatego procedura najpierw usuwa kontenerpostgresCoolify; samo zatrzymanie nie wystarcza. - Po migracji bazy podnieś stack przez „Redeploy” — nie „Restart”. Ponieważ kontener
postgreszostał usunięty, Coolify musi go odtworzyć. Kliknij Redeploy na zasobie; Coolify wystartuje wtedypostgres:18-alpinena zmigrowanym wolumenie i podniesie aplikację z powrotem. - Coolify ponownie używa zmigrowanego wolumenu. Odtworzenie wolumenu gubi etykiety compose Coolify, ale Docker Compose (a więc i redeploy Coolify) dopasowuje wolumeny po nazwie i używa istniejącego — Twoje dane zostają. Zachowaj artefakty bezpieczeństwa, dopóki nie zweryfikujesz ponownie wdrożonej aplikacji.
- Znajdź prawdziwą nazwę wolumenu w zakładce Storages zasobu; ma prefiks UUID, np.
<uuid>_postgres-prod-data(zwróć uwagę na myślniki). Jeśli na jednym hoście masz kilka stacków (np. dev i main), migruj każdy po jego własnej nazwie wolumenu i nie dotykaj drugiego.
Ścieżka B — stack migracyjny tylko-UI (Coolify, Portainer, NAS)
Gdy nie masz shella hosta, użyj dedykowanego, tymczasowego stacku migracyjnego
infra/docker-compose.upgrade.yml. Nie jest częścią żadnego normalnego wdrożenia — podnoś go wyłącznie
na czas migracji i usuń po niej. Używa stockowych obrazów PostgreSQL, bez gniazda Dockera, w całości
sterowany przez entrypointy kontenerów.
Wykonuje te fazy automatycznie i zatrzymuje się na zweryfikowanym raporcie — nigdy nie promuje sam:
- cold-backup — niezmienny tar wolumenu A (obraz rollbacku), robiony zanim cokolwiek odczyta A.
- dump — fail-closed preflight żywego klastra + logiczny dump.
- restore — przywrócenie do świeżego wolumenu PostgreSQL 18 B.
- verify — sprawdza wersję główną, schemat, extensiony i parytet locale oraz zapisuje
REPORT.txtdo wolumenu artefaktów.
Kroki
-
Zatrzymaj stack aplikacji w UI swojej platformy (nie usuwaj wolumenów).
-
Znajdź nazwę istniejącego wolumenu danych (to A, używane jako
SOURCE_VOLUME_NAME):- Coolify: wolumen ma prefiks UUID zasobu, np.
<uuid>_postgres_prod_data. - Portainer / QNAP / Synology: prefiksem jest nazwa stacku, np.
<stack>_postgres_data. - Domyślnie w Compose:
<project>_postgres_prod_data.
- Coolify: wolumen ma prefiks UUID zasobu, np.
-
Wdróż stack migracyjny z tymi zmiennymi środowiskowymi:
Zmienna Wartość SOURCE_VOLUME_NAMEistniejący wolumen A z kroku 2 DB_NAMEDB_USERNAMEDB_PASSWORDTwoje istniejące dane logowania do bazy TARGET_VOLUME_NAMEopcjonalne; domyślnie quotenode_postgres_upgraded(to B)# odpowiednik czystego Dockera tego, co uruchamia UI: docker compose -f infra/docker-compose.upgrade.yml up -
Przeczytaj raport. Gdy faza
verifysię zakończy, odczytajREPORT.txtz wolumenu artefaktów (quotenode_pg_upgrade_artifacts). Musi mówićPASS. Jeśli któraś faza zawiedzie, stack zatrzymuje się bezpiecznie; A jest nietknięte — usuń przyczynę i ponów (najpierw skasuj B, patrz Odzyskiwanie). -
Usuń stack migracyjny (zachowaj jego wolumeny), a potem Promocja B.
Widoczność wolumenu na platformie (zweryfikuj u siebie): Ścieżka B zakłada, że stack migracyjny może zamontować istniejący wolumen A aplikacji jako wolumen
externalpo nazwie. Większość hostów Docker na to pozwala. Jeśli Twoja platforma izoluje wolumeny per stack/zasób i stack migracyjny nie widzi A, użyj wariantu in-place: tymczasowo podmień compose aplikacji nadocker-compose.upgrade.ymlw tym samym zasobie/projekcie, aby dziedziczył tę samą przestrzeń nazw wolumenów. Potwierdź to na swojej konkretnej platformie, zanim zaufasz temu na produkcji.
Ścieżka C — obraz auto-upgrade in-place (wygląda najprościej, ale ryzykowna — niezalecana) ⚠️
Tylko ostateczność. Użyj tego wyłącznie, gdy ani ścieżka A, ani B nie wchodzi w grę i akceptujesz poniższe ryzyko. W przeciwieństwie do A i B, ta ścieżka aktualizuje in-place na istniejącym wolumenie A, więc nie ma nietkniętego wolumenu rollbacku — Twój backup poza hostem to jedyna siatka bezpieczeństwa.
Pomysł wygląda najprościej: pozwól auto-aktualizującemu obrazowi
(pgautoupgrade) wykonać pg_upgrade raz na
Twoim istniejącym wolumenie, a potem wystartuj na nim normalny postgres:18-alpine. Ale to nie jest
zmiana jednego tagu w Twoim normalnym compose — serwis postgres ma tam override command: (który
całkowicie tłumi entrypoint upgrade’u), restart: unless-stopped (który zapętliłby zakończony
one-shot) i healthcheck (który oznacza aktualizujący się serwer jako unhealthy). Wszystkie trzy psują
upgrade in-place.
Repo dostarcza gotowy, samodzielny plik one-shot, który omija je wszystkie:
infra/docker-compose.pgautoupgrade.unsafe.yml (.unsafe jest celowe). Kluczowe fakty:
pg_upgradejest destrukcyjny in-place — po sukcesie stary klaster znika, a awaria w połowie może zostawić katalog danych w połowie skonwertowany. Twój backup poza hostem to jedyny rollback.- Plik utrzymuje
PGAUTO_ONESHOT=yes, legacyPGDATA=/var/lib/postgresql/data, brak healthchecka orazrestart: no, a obraz pinuje po digeście.
Kroki
- Zatrzymaj stack i zrób surową kopię wolumenu A poza hostem — to Twój jedyny rollback:
Skopiujdocker run --rm -v <your_postgres_volume>:/d:ro -v "$PWD:/out" alpine:3.23 \ tar czf /out/pg16-volume-backup.tar.gz -C /d .pg16-volume-backup.tar.gzpoza host. - Uruchom gotowy plik one-shot na swoim żywym wolumenie (
SOURCE_VOLUME_NAME= wolumen A):
Poczekaj, aż kontener zakończy się z exit 0.SOURCE_VOLUME_NAME=<your_postgres_volume> \ DB_NAME=quotenode DB_USERNAME=quotenode DB_PASSWORD='...' \ docker compose -f infra/docker-compose.pgautoupgrade.unsafe.yml up - Zweryfikuj układ — wolumen musi być teraz PostgreSQL 18 na legacy ścieżce:
docker run --rm -v <your_postgres_volume>:/d:ro alpine:3.23 cat /d/PG_VERSION # -> 18 - Wystartuj normalny stack (
postgres:18-alpine). Używa tego samego wolumenu, teraz zaktualizowanego — krok promocji nie jest potrzebny (upgrade był in-place). - Wykonaj Weryfikację. Jeśli coś jest nie tak, przywróć A z
pg16-volume-backup.tar.gz.
Platformy tylko-UI (Coolify/Portainer): wklej zawartość
infra/docker-compose.pgautoupgrade.unsafe.ymldo nowego, tymczasowego stacku, ustaw powyższe zmienne, wdróż raz, potwierdź zakończenie, usuń ten stack, a potem skieruj normalny stack na ten sam wolumen.
Promocja B
Po weryfikacji skieruj główny stack na zmigrowany wolumen B. Bazowe pliki compose nigdy nie są edytowane (więc świeże instalacje są nienaruszone); promocja jest override’em.
- Czysty Docker / Compose: dodaj override promocji i ustaw
POSTGRES_VOLUME_NAMEna B:
(dla pliku prod użyjPOSTGRES_VOLUME_NAME=quotenode_postgres_upgraded \ docker compose -f infra/docker-compose.coolify.yml \ -f infra/docker-compose.promote.yml up -ddocker-compose.prod.ymlzamiastcoolify.yml). Aby cofnąć, usuń-f docker-compose.promote.ymli wystartuj ponownie — jesteś z powrotem na A. - UI Coolify / Portainer: w UI nie ma łańcucha
-f. Zamiast tego przemapuj storage serwisupostgresna wolumen B w ustawieniach storage platformy albo dodajexternal: true+name: <B>dla wolumenu postgresa w edytorze compose stacku.
Weryfikacja
Po tym, jak główny stack działa na B:
- Otwórz
/health— powinno zwrócićUP. - Zaloguj się i potwierdź, że Twoje dane (oferty, klienci, ustawienia) są obecne.
- Utwórz testową ofertę i wygeneruj PDF.
- Otwórz link publiczny, aby potwierdzić routing.
Dopiero po tym usuwaj wolumen A i artefakty migracji.
Kolejność wydania (ważne)
Jeśli Twoja platforma auto-wdraża na ruchomym tagu, obraz wymagający PG18 mógłby pojawić się przed migracją, psując stack. Zawsze:
- Wstrzymaj auto-deploy / przypnij bieżący tag obrazu.
- Wykonaj migrację (ścieżka A lub B) i wypromuj B.
- Dopiero potem zaktualizuj aplikację do wersji na PostgreSQL 18 i ponownie włącz auto-deploy.
Odzyskiwanie
Wolumen źródłowy A nigdy nie jest modyfikowany, więc odzyskiwanie jest proste:
- Migracja przerwana przed promocją (timeout, redeploy, zatrzymanie): usuń wolumen B i wolumen artefaktów, a potem zacznij od nowa. Nie ma częściowego stanu do wznowienia.
- Problem po promocji: cofnij promocję (usuń override / wyczyść
POSTGRES_VOLUME_NAMElub przemapuj storage z powrotem na A) i wystartuj stack — jesteś z powrotem na PostgreSQL 16 ze wszystkimi danymi. - Ostateczność: przywróć z
backup-before-pg18.sql(patrz Aktualizacja QuoteNode → Przywracanie poprzedniej wersji).
Nigdy nie uruchamiaj docker compose down -v podczas tej procedury — usuwa wolumeny.