Przejdź do treści
Q
QuoteNode

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żkaKiedy użyćMechanizm
A — konsola hostaMożesz uruchomić docker na hoście (Ubuntu VPS, LAN Linux, zwykły Compose)scripts/migrate-postgres-major.sh
B — stack tylko-UIMasz 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-placeobraz 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)

  1. Zrób kopię .env. Zawiera sekrety, których nie da się odtworzyć, zwłaszcza DB_ENCRYPTION_KEY.
  2. 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
  3. Wstrzymaj auto-deploy, jeśli Twoja platforma wdraża ponownie na ruchomym tagu (np. auto-deploy Coolify, :latest w Portainerze). Patrz Kolejność wydania — obraz wymagający PG18 nie może pojawić się przed wykonaniem migracji.
  4. 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.

  1. Znajdź nazwę wolumenu danych:
    docker volume ls | grep postgres
  2. Zatrzymaj stack (zachowaj wolumeny):
    docker compose down
  3. 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:
    DB_NAME=quotenode DB_USERNAME=quotenode DB_PASSWORD='...' \
    POSTGRES_VOLUME=<your_postgres_volume> \
    DB_MAJOR_MIGRATE_CONFIRM=yes \
      ./scripts/migrate-postgres-major.sh
    Skrypt nie wykona niczego destrukcyjnego bez DB_MAJOR_MIGRATE_CONFIRM=yes i przerywa (zostawiając dane nietknięte), jeśli wykryje nieoczekiwane role/extensiony lub za mało miejsca na dysku.
  4. Wystartuj stack normalnie. Wstanie na PostgreSQL 18 i zastosuje wszelkie oczekujące migracje:
    docker compose up -d
  5. 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 postgres wpada 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 kontener postgres Coolify; samo zatrzymanie nie wystarcza.
  • Po migracji bazy podnieś stack przez „Redeploy” — nie „Restart”. Ponieważ kontener postgres został usunięty, Coolify musi go odtworzyć. Kliknij Redeploy na zasobie; Coolify wystartuje wtedy postgres:18-alpine na 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:

  1. cold-backup — niezmienny tar wolumenu A (obraz rollbacku), robiony zanim cokolwiek odczyta A.
  2. dump — fail-closed preflight żywego klastra + logiczny dump.
  3. restore — przywrócenie do świeżego wolumenu PostgreSQL 18 B.
  4. verify — sprawdza wersję główną, schemat, extensiony i parytet locale oraz zapisuje REPORT.txt do wolumenu artefaktów.

Kroki

  1. Zatrzymaj stack aplikacji w UI swojej platformy (nie usuwaj wolumenów).

  2. 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.
  3. Wdróż stack migracyjny z tymi zmiennymi środowiskowymi:

    ZmiennaWartość
    SOURCE_VOLUME_NAMEistniejący wolumen A z kroku 2
    DB_NAME DB_USERNAME DB_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
  4. Przeczytaj raport. Gdy faza verify się zakończy, odczytaj REPORT.txt z 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).

  5. 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 external po 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 na docker-compose.upgrade.yml w 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_upgrade jest 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, legacy PGDATA=/var/lib/postgresql/data, brak healthchecka oraz restart: no, a obraz pinuje po digeście.

Kroki

  1. Zatrzymaj stack i zrób surową kopię wolumenu A poza hostem — to Twój jedyny rollback:
    docker run --rm -v <your_postgres_volume>:/d:ro -v "$PWD:/out" alpine:3.23 \
      tar czf /out/pg16-volume-backup.tar.gz -C /d .
    Skopiuj pg16-volume-backup.tar.gz poza host.
  2. Uruchom gotowy plik one-shot na swoim żywym wolumenie (SOURCE_VOLUME_NAME = wolumen A):
    SOURCE_VOLUME_NAME=<your_postgres_volume> \
    DB_NAME=quotenode DB_USERNAME=quotenode DB_PASSWORD='...' \
      docker compose -f infra/docker-compose.pgautoupgrade.unsafe.yml up
    Poczekaj, aż kontener zakończy się z exit 0.
  3. 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
  4. Wystartuj normalny stack (postgres:18-alpine). Używa tego samego wolumenu, teraz zaktualizowanego — krok promocji nie jest potrzebny (upgrade był in-place).
  5. 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.yml do 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_NAME na B:
    POSTGRES_VOLUME_NAME=quotenode_postgres_upgraded \
      docker compose -f infra/docker-compose.coolify.yml \
                     -f infra/docker-compose.promote.yml up -d
    (dla pliku prod użyj docker-compose.prod.yml zamiast coolify.yml). Aby cofnąć, usuń -f docker-compose.promote.yml i wystartuj ponownie — jesteś z powrotem na A.
  • UI Coolify / Portainer: w UI nie ma łańcucha -f. Zamiast tego przemapuj storage serwisu postgres na wolumen B w ustawieniach storage platformy albo dodaj external: true + name: <B> dla wolumenu postgresa w edytorze compose stacku.

Weryfikacja

Po tym, jak główny stack działa na B:

  1. Otwórz /health — powinno zwrócić UP.
  2. Zaloguj się i potwierdź, że Twoje dane (oferty, klienci, ustawienia) są obecne.
  3. Utwórz testową ofertę i wygeneruj PDF.
  4. 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:

  1. Wstrzymaj auto-deploy / przypnij bieżący tag obrazu.
  2. Wykonaj migrację (ścieżka A lub B) i wypromuj B.
  3. 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_NAME lub 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.

Last reviewed: Recently