Własny serwer Chatwoot na VPS z Docker Compose
Instrukcja samodzielnego hostowania Chatwoot na VPS przy użyciu Docker Compose i Traefik. Poradnik obejmuje konfigurację SMTP, PostgreSQL, kopii zapasowych oraz bezpieczne aktualizacje.
Co budujesz
W celu samodzielnego hostowania Chatwoot na VPS uruchamia się cztery kontenery: proces webowy Rails, proces przetwarzania w tle Sidekiq, PostgreSQL z rozszerzeniem pgvector oraz Redis. Chatwoot to otwartoźródłowe narzędzie typu customer support desk, zapewniające współdzieloną skrzynkę odbiorczą zespołu oraz widget czatu na stronie WWW na serwerze pod Twoją kontrolą. Instalacja zajmuje około dwudziestu minut. Wszystkie późniejsze działania, takie jak dostarczanie poczty, kopie zapasowe, aktualizacje i skalowanie, decydują o tym, czy rozwiązanie będzie działać po roku.
Każdy kontener pełni jedną funkcję. Rails obsługuje panel agenta oraz API (application programming interface) widgetu. Sidekiq wykonuje zadania wymagające czasu: wysyłkę e-maili, odpytywanie podłączonych kanałów, uruchamianie reguł automatyzacji oraz generowanie raportów. Postgres przechowuje konwersacje, kontakty, konta agentów oraz wszystkie ustawienia zmieniane w panelu. Redis obsługuje kolejki Sidekiq oraz kanał pub/sub ActionCable, który przesyła nową wiadomość do otwartego panelu bez konieczności odświeżania strony. Redis nie pełni tu roli tymczasowej pamięci podręcznej, ponieważ jego utrata oznacza utratę zakolejkowanych zadań.
Obraz Postgres w dostarczonym pliku compose to pgvector/pgvector:pg16, a nie standardowy obraz postgres, ponieważ schemat bazy danych Chatwoot wymaga rozszerzenia vector dla funkcji AI. Użycie standardowego obrazu Postgres spowoduje zatrzymanie pierwszego uruchomienia bazy danych z błędem ERROR: extension "vector" is not available, ponieważ plik kontrolny rozszerzenia nie znajduje się w tym obrazie. Należy używać obrazu dostarczonego przez twórców oprogramowania.
Ten przewodnik zakłada, że Docker oraz reverse proxy są już skonfigurowane na serwerze. Jeśli tak nie jest, rozpocznij od Docker Compose na VPS, a następnie wróć do tego materiału.
Jakich zasobów VPS wymaga samodzielnie hostowany Chatwoot?
Według stanu na sierpień 2026, oficjalna dokumentacja wymagań wskazuje 4 GB pamięci RAM oraz 4 rdzenie CPU jako minimum, co ma wystarczyć do obsługi 10 000 konwersacji dziennie. Konfiguracja z 8 GB RAM i 8 rdzeniami CPU pozwala na obsługę do 20 000 konwersacji dziennie. Wymagane jest również co najmniej 1 GB przestrzeni swap, co ma zapobiec wyczerpaniu pamięci podczas aktualizacji. Należy zaplanować od 5 GB do 10 GB miejsca na dysku dla Postgres, nie wliczając w to przesyłanych plików.
Przejdźmy do konkretów. VPS z 2 GB RAM uruchomi Chatwoot, a przy dwóch agentach i niewielkim ruchu system będzie działał poprawnie. Problemy pojawiają się w dwóch sytuacjach. Pierwszą jest Sidekiq, który według twórców zużywa ponad 1 GB RAM na obciążonym serwerze, więc nagły napływ wiadomości e-mail lub generowanie raportów spowoduje przekroczenie dostępnej pamięci, zanim Rails, Postgres i Redis otrzymają swój przydział. Drugą sytuacją jest aktualizacja, ponieważ db:chatwoot_prepare uruchamia nowy proces Rails w celu wykonania migracji, a sam start Rails w tym obrazie zużywa setki megabajtów pamięci, zanim zacznie wykonywać jakiekolwiek zadania.
System nie wyświetli ostrzeżenia. Mechanizm OOM killer (Out of Memory) jądra systemu wyśle sygnał SIGKILL do największego procesu, Docker wykryje zatrzymanie kontenera, a restart: always uruchomi go ponownie. docker compose ps pokaże wtedy kontener, który ciągle wraca do stanu Exited (137), gdzie kod 137 oznacza zakończenie przez sygnał 9. Można to potwierdzić za pomocą sudo dmesg -T | grep -i "killed process", który wskaże proces wybrany przez jądro.
Jeśli 4 GB RAM przekracza budżet, można uruchomić serwer z 2 GB RAM i 2 GB swap, akceptując fakt, że pod obciążeniem czas odpowiedzi wzrośnie, zamiast całkowitego zatrzymania usługi. Warto w obu przypadkach ustawić twarde limity pamięci dla każdej usługi, aby proces roboczy nie spowodował awarii bazy danych. Zobacz limity pamięci w Docker Compose.
Przesyłane pliki to element, którego rozmiar rośnie bez narzuconego limitu. Każdy zrzut ekranu załączony przez klienta trafia do wolumenu pamięci masowej i tam pozostaje, dlatego należy monitorować docker system df -v, zamiast zakładać, że to baza danych zapełniła dysk.
Pobranie pliku compose i przypięcie wersji
mkdir -p ~/chatwoot && cd ~/chatwoot
wget -O .env https://raw.githubusercontent.com/chatwoot/chatwoot/develop/.env.example
wget -O docker-compose.yaml https://raw.githubusercontent.com/chatwoot/chatwoot/develop/docker-compose.production.yaml
chmod 600 .envPobrany przed chwilą plik zawiera image: chatwoot/chatwoot:latest. Należy to zmienić przed wykonaniem jakichkolwiek innych działań.
services:
base: &base
image: chatwoot/chatwoot:v4.16.2
env_file: .env
volumes:
- storage_data:/app/storagelatest oznacza, że przy kolejnym docker compose pull system pobierze wersję opublikowaną danego dnia. Może to być wydanie główne zawierające migracje, z którymi użytkownik nie miał okazji się zapoznać. Migracje Chatwoot są w praktyce nieodwracalne, więc przypadkowa aktualizacja wymusza przywrócenie danych z kopii zapasowej, a nie zwykłe cofnięcie zmian. Należy przypiąć konkretny tag i zmieniać go w sposób świadomy. W sierpniu 2026 roku aktualnym wydaniem była wersja v4.16.2; należy sprawdzić stronę wydań, aby ustalić tag, który powinien zostać przypięty obecnie.
Usługa base to kotwica YAML, z której korzystają zarówno rails, jak i sidekiq, więc zmiana tagu w jednym miejscu aktualizuje go dla obu. Podczas edycji pliku należy usunąć linię version: '3' znajdującą się na samej górze. Nowoczesne wersje Compose ignorują ten wpis i wyświetlają ostrzeżenie the attribute 'version' is obsolete, it will be ignored przy każdym wywołaniu polecenia.
Wypełnianie pliku .env
Najpierw wygeneruj klucz tajny. Producent wymaga wartości alfanumerycznej, ponieważ znaki specjalne mogą zostać błędnie zinterpretowane podczas przekazywania wartości przez powłokę lub parser YAML.
head /dev/urandom | tr -dc A-Za-z0-9 | head -c 63 ; echo ''Następnie ustaw poniższe klucze w .env.
SECRET_KEY_BASE=<the 63 characters you just generated>
FRONTEND_URL=https://support.example.com
FORCE_SSL=true
DEFAULT_LOCALE=en
ENABLE_ACCOUNT_SIGNUP=true
POSTGRES_HOST=postgres
POSTGRES_USERNAME=postgres
POSTGRES_PASSWORD=<long random string>
POSTGRES_DATABASE=chatwoot
REDIS_URL=redis://redis:6379
REDIS_PASSWORD=<a different long random string>
RAILS_ENV=production
INSTALLATION_ENV=docker
ACTIVE_STORAGE_SERVICE=localPOSTGRES_HOST=postgres oraz redis://redis:6379 to nazwy usług w Compose, które są rozpoznawane w domyślnej sieci projektu. FRONTEND_URL nie jest elementem dekoracyjnym. Chatwoot używa tej wartości do budowania adresu URL skryptu widgetu oraz każdego odnośnika w wychodzących wiadomościach e-mail. Błędna wartość spowoduje, że linki do resetowania hasła będą wskazywać na hosta, który nie odpowiada.
Oto pułapka w pliku dostarczonym przez producenta. Usługa postgres nie odczytuje .env. Posiada ona własny blok environment z pozostawionym pustym polem POSTGRES_PASSWORD=, więc ustawienie hasła wyłącznie w .env sprawi, że baza danych pozostanie bez hasła, a aplikacja będzie próbowała użyć własnego. Wskaż usłudze tę samą zmienną:
postgres:
image: pgvector/pgvector:pg16
restart: always
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=chatwoot
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}Compose odczytuje .env z katalogu projektu w celu podstawienia ${...}, dzięki czemu obie strony otrzymują ten sam ciąg znaków. Błąd w tym miejscu spowoduje zatrzymanie Rails z błędem PG::ConnectionBad: FATAL: password authentication failed for user "postgres".
Jedno zachowanie zaskakuje niemal każdego: obraz Postgres stosuje POSTGRES_PASSWORD tylko podczas inicjalizacji pustego katalogu danych. Późniejsza zmiana wartości nie przynosi efektu, ponieważ initdb nigdy nie uruchamia się ponownie. Jeśli stos został już raz uruchomiony, zmień hasło bezpośrednio w bazie danych.
docker compose exec postgres psql -U postgres -c "ALTER USER postgres WITH PASSWORD 'the-new-password';"ENABLE_ACCOUNT_SIGNUP=true jest tymczasowe. Otwiera ono publiczny formularz rejestracji, umożliwiając utworzenie pierwszego konta. Ustaw tę wartość na false i uruchom ponownie docker compose up -d natychmiast po utworzeniu konta, w przeciwnym razie każdy, kto znajdzie adres URL, będzie mógł zarejestrować się w Twoim systemie zgłoszeń. Od tego momentu agenci będą dodawani przez zaproszenia, a ich hasła będą przechowywane wyłącznie w tej aplikacji. Jest to akceptowalne do momentu, gdy zarządzasz kilkoma usługami i chcesz uniknąć utrzymywania osobnej listy kont w każdej z nich; wtedy rozwiązaniem staje się samodzielnie hostowany dostawca tożsamości, taki jak Authentik.
.env zawiera teraz wszystkie sekrety tego stosu w postaci czystego tekstu, dlatego należy ustawić dla niego uprawnienia 600 i nie umieszczać go w git. Artykuł Jak Compose odczytuje pliki env i gdzie wyciekają sekrety omawia potencjalne zagrożenia, w tym różnicę między env_file a environment.
Konfiguracja Chatwoot za istniejącym Traefik
Nie należy budować drugiego reverse proxy dla pojedynczej aplikacji. Jeśli Traefik już obsługuje terminację TLS (transport layer security) dla innych kontenerów na tym serwerze, Chatwoot dołącza do niego za pomocą bloku etykiet. Jeśli taka konfiguracja jeszcze nie istnieje, należy ją najpierw przygotować zgodnie z Traefik przed kilkoma aplikacjami Docker Compose, a następnie wrócić do tego miejsca.
Należy zachować plik docker-compose.yaml dostarczony przez producenta w stanie zbliżonym do oryginału, aby umożliwić późniejsze porównywanie zmian (diff) z nowszymi wersjami, a własne modyfikacje umieścić w pliku nadpisującym. Compose automatycznie scala docker-compose.override.yaml, a zasady tego procesu opisano w dzieleniu Compose na wiele plików.
services:
rails:
networks:
- default
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.chatwoot.rule=Host(`support.example.com`)"
- "traefik.http.routers.chatwoot.entrypoints=websecure"
- "traefik.http.routers.chatwoot.tls.certresolver=letsencrypt"
- "traefik.http.services.chatwoot.loadbalancer.server.port=3000"
networks:
proxy:
external: trueNależy użyć własnych nazw entrypoint oraz certresolver. Kontener musi znajdować się w tej samej sieci Docker co Traefik, co zapewnia wpis proxy, a także musi pozostać w default, w przeciwnym razie utraci dostęp do Postgres i Redis. To właśnie ten drugi wiersz jest najczęściej pomijany.
Blok ports: należy pozostawić bez zmian. Producent wiąże go z 127.0.0.1:3000, co oznacza adresację wyłącznie loopback, dzięki czemu usługa nie jest dostępna z Internetu, a jednocześnie pozostaje użyteczna do testów z poziomu serwera za pomocą curl -I http://127.0.0.1:3000.
Panel agenta utrzymuje otwarte połączenie websocket do /cable w celu dostarczania wiadomości w czasie rzeczywistym. Traefik przekazuje żądanie HTTP upgrade bez dodatkowej konfiguracji, więc nie ma potrzeby wprowadzania zmian. Jeśli w przyszłości przed Traefik zostanie umieszczona sieć CDN lub inne proxy, należy tam zezwolić na obsługę websocketów, ponieważ w przeciwnym razie panel będzie ładował się poprawnie, ale nowe wiadomości pojawią się dopiero po ręcznym odświeżeniu strony.
Inicjalizacja bazy danych i uruchomienie stosu
Najpierw uruchom usługi danych i pozwól Postgres zakończyć pierwsze uruchomienie.
docker compose up -d postgres redis
docker compose logs postgres | tail -n 5Poczekaj na database system is ready to accept connections. Następnie utwórz schemat.
docker compose run --rm rails bundle exec rails db:chatwoot_prepareTo polecenie tworzy bazę danych, jeśli jej brakuje, a następnie ładuje schemat i domyślne dane początkowe. Wyświetla ono wiersze migracji i kończy działanie w sposób prawidłowy. Jeśli proces zatrzyma się na wyświetlaniu postgres:5432 - no response, punkt wejścia oczekuje na bazę danych, która jeszcze nie akceptuje połączeń, co przy pierwszym uruchomieniu zazwyczaj oznacza, że initdb wciąż pracuje. Należy poczekać, sprawdzić logi Postgres, a następnie uruchomić polecenie ponownie. Jeśli proces zatrzyma się na rozszerzeniu vector, oznacza to, że obraz pgvector został zastąpiony standardowym obrazem Postgres.
docker compose up -d
docker compose ps
docker compose logs --tail 30 railsWszystkie cztery kontenery powinny wskazywać Up, a log rails powinien kończyć się wierszem Puma nasłuchującym na http://0.0.0.0:3000. Następnie sprawdź ścieżkę publiczną:
curl -sI https://support.example.com | head -n 1HTTP/2 200 oznacza, że cały łańcuch działa poprawnie. Błąd 404 z Traefik oznacza, że reguła routera nie została dopasowana, co zazwyczaj wynika z literówki w nazwie hosta. Błąd 502 oznacza, że Traefik dopasował router, ale nie mógł nawiązać połączenia z kontenerem, co niemal zawsze wynika z braku sieci proxy lub portu loadbalancer.server.port innego niż 3000.
Otwórz adres URL, utwórz konto pod /app/auth/signup, a następnie ustaw ENABLE_ACCOUNT_SIGNUP=false i uruchom docker compose up -d, aby zamknąć formularz.
Dlaczego resetowanie haseł i konwersacje e-mail nie działają bez SMTP
Chatwoot bez skonfigurowanego protokołu SMTP (Simple Mail Transfer Protocol) to system obsługi zgłoszeń, który nie może wysyłać wiadomości, co wykracza poza brak powiadomień. Resetowanie haseł przestaje działać, więc administrator, który utraci dostęp do konta, nie może go odzyskać. Zaproszenia dla agentów również przestają działać, ponieważ zaproszenie jest wiadomością e-mail. Odpowiadanie klientowi w konwersacji e-mailowej staje się niemożliwe, co sprawia, że komunikacja staje się jednostronna. Jest to krok, który użytkownicy często pomijają, a jego brak odkrywają w najmniej odpowiednim momencie.
Mechanizm działania jest prosty. Bez ustawień SMTP, ActionMailer zachowuje domyślne ustawienie dostarczania do localhost na porcie 25. Wewnątrz kontenera Rails nie ma serwera pocztowego, więc zadanie dostarczania zgłasza błąd Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 25. Poczta jest wysyłana przez zadanie w tle, więc wpis ten trafia do dziennika Sidekiq, a nie do dziennika Rails. W tym samym czasie osoba klikająca "forgot password" widzi komunikat potwierdzający wysłanie wiadomości, której nigdy nie otrzyma.
MAILER_SENDER_EMAIL=Support <support@example.com>
SMTP_DOMAIN=example.com
SMTP_ADDRESS=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=support@example.com
SMTP_PASSWORD=<the relay password>
SMTP_AUTHENTICATION=plain
SMTP_ENABLE_STARTTLS_AUTO=trueNależy użyć portu 587 z protokołem STARTTLS, który otwiera połączenie w postaci czystego tekstu i aktualizuje je do szyfrowanego przed uwierzytelnieniem. Większość dostawców VPS blokuje wychodzący port 25 w celu ograniczenia spamu, więc przekaźnik (relay) na porcie 587 jest zazwyczaj jedyną opcją, która pozwoli na nawiązanie połączenia. SMTP_DOMAIN to domena, którą serwer ogłasza podczas konwersacji SMTP, a niektóre przekaźniki odrzucają połączenia w przypadku niezgodności.
Zastosuj ustawienia i monitoruj proces roboczy:
docker compose up -d rails sidekiq
docker compose logs -f sidekiqWywołaj resetowanie hasła ze strony logowania. Prawidłowe dostarczenie wiadomości oznacza, że zadanie mailera kończy się pomyślnie w dzienniku Sidekiq. W przypadku błędu widoczna będzie klasa wyjątku, a następnie Sidekiq podejmie próbę ponowienia z rosnącym opóźnieniem, dlatego uszkodzony przekaźnik generuje ten sam błąd co kilka minut przez wiele godzin.
Dwa rodzaje odrzuceń są powszechne i żaden z nich nie jest błędem Chatwoot. 535 Authentication failed oznacza, że nazwa użytkownika lub hasło dla danego przekaźnika są nieprawidłowe; wielu dostawców wymaga użycia hasła aplikacji zamiast hasła do konta. 550 Sender address rejected oznacza, że MAILER_SENDER_EMAIL jest adresem, z którego przekaźnik nie zezwala na wysyłkę, więc musi to być skrzynka pocztowa lub domena zweryfikowana u danego dostawcy.
Odbieranie wiadomości e-mail w ramach konwersacji to oddzielne zadanie. Wymaga ono MAILER_INBOUND_EMAIL_DOMAIN oraz RAILS_INBOUND_EMAIL_SERVICE, a także serwera pocztowego, który przekazuje przychodzące wiadomości do Chatwoot. Wynajęcie przekaźnika to najszybsza droga. Jeśli wolisz samodzielnie zarządzać całą ścieżką poczty, uruchomienie własnego serwera pocztowego z Mailcow wyjaśnia, z czym wiąże się takie zobowiązanie.
Co należy archiwizować i jak zweryfikować poprawność przywracania
Kopia zapasowa Chatwoot składa się z czterech części. Pominięcie którejkolwiek z nich sprawia, że przywracanie staje się procesem odbudowy systemu od zera.
- Baza danych Postgres, która przechowuje konwersacje, kontakty, konta agentów oraz wszystkie ustawienia.
- Wolumen
storage_data, ponieważACTIVE_STORAGE_SERVICE=localzapisuje przesłane pliki na dysku, przechowując w Postgres jedynie odniesienia do nich. - Plik
.env, ponieważ zawiera onSECRET_KEY_BASEoraz kluczeACTIVE_RECORD_ENCRYPTION_*. - Pliki Compose, ponieważ rejestrują one dokładną wersję (tag) obrazu, z którą zgodny jest schemat bazy danych.
Przywrócenie samej bazy danych spowoduje, że wszystkie konwersacje będą miały uszkodzone załączniki, ponieważ rekordy wskazują na pliki, których nie ma już na dysku.
cd ~/chatwoot
docker compose exec -T postgres pg_dump -U postgres -Fc chatwoot > db-$(date +%F).dump-T ma kluczowe znaczenie. Bez tego parametru Compose przydziela pseudo-terminal, który nadpisuje bajty nowej linii w strumieniu, co prowadzi do powstania pliku zrzutu odrzucanego przez pg_restore. -Fc to format niestandardowy, który zapewnia kompresję i pozwala pg_restore na selektywne działanie.
docker run --rm -v chatwoot_storage_data:/data:ro -v "$PWD":/backup alpine \
tar czf /backup/storage-$(date +%F).tgz -C /data .Nazwa wolumenu składa się z nazwy katalogu projektu oraz _storage_data. Potwierdź ją za pomocą docker volume ls | grep storage_data, zanim zaufasz temu poleceniu, ponieważ Docker tworzy pusty wolumen zamiast zgłosić błąd, gdy podana nazwa nie istnieje. W rezultacie otrzymasz poprawne, ale puste archiwum bez żadnego komunikatu o błędzie. Po zakończeniu sprawdź rozmiar pliku za pomocą ls -lh storage-*.tgz.
Oba pliki znajdują się teraz na tym samym dysku, co chronione dane, co nie zapewnia żadnej ochrony. Prześlij je poza serwer i zaszyfruj, ponieważ zrzut bazy danych zawiera wszystkie wiadomości klientów w postaci jawnej. Szyfrowane kopie zapasowe poza serwerem z użyciem restic opisuje kwestie harmonogramowania i retencji danych.
Ćwiczenie przywracania danych – wykonaj je, zanim będzie potrzebne
Przywracaj dane na drugim serwerze VPS, a nie na działającej instancji produkcyjnej. Skopiuj .env, pliki Compose oraz oba archiwa, a następnie wykonaj:
docker compose up -d postgres
docker compose exec -T postgres pg_restore -U postgres -d chatwoot --clean --if-exists < db-2026-08-10.dump
docker run --rm -v chatwoot_storage_data:/data -v "$PWD":/backup alpine \
sh -c 'rm -rf /data/* && tar xzf /backup/storage-2026-08-10.tgz -C /data'
docker compose up -d--clean --if-exists usuwa istniejące obiekty przed załadowaniem danych, dlatego używaj tego polecenia tylko w odniesieniu do bazy danych, której utrata jest akceptowalna. Następnie zaloguj się i otwórz konwersację zawierającą załącznik. Jeśli lista wiadomości się wczyta, a plik pobierze, kopia zapasowa jest poprawna.
Przywrócenie danych z innym SECRET_KEY_BASE unieważnia wszystkie ciasteczka sesji, co powoduje wylogowanie wszystkich użytkowników. Przywrócenie danych z innymi kluczami ACTIVE_RECORD_ENCRYPTION_* jest bardziej problematyczne: Chatwoot nie może odszyfrować kolumn zawierających dane uwierzytelniające kanałów i zgłasza ActiveRecord::Encryption::Errors::Decryption. Dlatego .env znajduje się na liście elementów do archiwizacji.
Jak zaktualizować Chatwoot do nowej wersji (tagu)
Kolejność działań jest ważniejsza niż same polecenia.
- Przeczytaj informacje o wydaniu (release notes) pomiędzy obecną a docelową wersją, zwracając uwagę na wymagane kroki ręczne.
- Wykonaj świeży zrzut bazy danych oraz kopię archiwum plików i sprawdź, czy rozmiary obu plików są poprawne.
- Edytuj tag obrazu dla usługi
basew plikudocker-compose.yaml. - Pobierz nowy obraz, zatrzymaj stos, uruchom migracje, a następnie uruchom ponownie.
docker compose pull
docker compose down
docker compose run --rm rails bundle exec rails db:chatwoot_prepare
docker compose up -d
docker compose imagesPobierz obraz przed uruchomieniem migracji, ponieważ migracja musi zostać wykonana z poziomu nowego obrazu: stary obraz nie zawiera nowych plików migracji. Zatrzymaj stos przed migracją, ponieważ stary kod i nowy schemat bazy danych są ze sobą niezgodne; działający proces Rails może zgłaszać błędy lub zapisywać wiersze, których nowy schemat nie zaakceptuje. Zatrzymanie usług zwalnia również pamięć potrzebną do migracji, co jest głównym powodem, dla którego twórcy oprogramowania zalecają posiadanie przestrzeni wymiany (swap).
Polecenie docker compose images wyświetla tag, na którym faktycznie działa każdy kontener, co pozwala wykryć sytuację, w której edytowano tag, ale zapomniano pobrać nowy obraz.
Nie przeskakuj wielu wersji jednocześnie. Zaleceniem twórców dla starszych instalacji jest przechodzenie przez kolejne wersje pośrednie, ponieważ migracje są usuwane, gdy zostaną scalone z bazowym schematem. Bardzo stara baza danych może osiągnąć stan, z którego nie ma ścieżki aktualizacji. Przechodź o jedną wersję minor na raz i po każdej z nich wykonuj krok przygotowawczy.
Jeśli Rails uruchomi się przed wykonaniem migracji, odmówi obsługi żądań i zapisze w dzienniku ActiveRecord::PendingMigrationError: Migrations are pending. Przy ustawionej opcji restart: always, kontener będzie się restartował, więc docker compose ps pokaże czas pracy (uptime), który resetuje się co kilka sekund. Uruchomienie kroku przygotowawczego rozwiązuje ten problem.
Wycofanie zmian oznacza przywrócenie starego tagu i odtworzenie zrzutu bazy danych. Nie istnieje niezawodna ścieżka odwrotnej migracji, dlatego krok 2 jest niezbędny.
Tryby awarii i komunikaty, które zobaczysz
502 Bad Gateway z Traefik. Router dopasował żądanie, ale backend nie odpowiedział. Sprawdź docker compose ps, czy pokazuje rails jako Up, a następnie uruchom docker network inspect proxy i potwierdź, że kontener rails znajduje się na liście kontenerów. Kontener, który nie jest uruchomiony, jest niewidoczny dla Traefik, więc żądanie trafia do routera, a następnie nie ma dokąd zostać przekierowane.
Panel sterowania ładuje się, ale nowe wiadomości wymagają odświeżenia strony. Połączenie websocket do /cable nie przechodzi lub FRONTEND_URL nie zgadza się z adresem w pasku przeglądarki. Niezgodność oznacza, że strona próbuje otworzyć websocket do innego źródła, co jest blokowane przez przeglądarkę.
FATAL: password authentication failed for user "postgres". Hasło w .env różni się od tego zapisanego w wolumenie danych Postgres. Napraw to za pomocą ALTER USER wewnątrz uruchomionego kontenera, ponieważ ponowna edycja .env nie zmieni już zainicjowanej bazy danych.
NOAUTH Authentication required. Redis działa z --requirepass, ale aplikacja połączyła się bez hasła, co oznacza, że REDIS_PASSWORD brakuje w .env lub nie zostało wczytane. Przetestuj to bezpośrednio za pomocą docker compose exec redis redis-cli -a "$REDIS_PASSWORD" ping, co powinno zwrócić PONG.
Kontenery kończące pracę z kodem 137. Jest to sygnał SIGKILL, co na małym serwerze oznacza działanie mechanizmu OOM (Out of Memory) jądra systemu. Dodaj swap, ustaw limity pamięci dla poszczególnych usług lub przenieś się na większy plan.
FAQ
Ile pamięci RAM wymaga VPS z samodzielnie hostowanym Chatwoot?
Według stanu na sierpień 2026, producent wymaga minimum 4 GB pamięci RAM oraz 4 rdzeni CPU dla obsługi do 10 000 konwersacji dziennie, natomiast 8 GB i 8 rdzeni dla 20 000 konwersacji. Należy dodać co najmniej 1 GB przestrzeni swap, ponieważ proces aktualizacji uruchamia drugą instancję Rails w celu wykonania migracji, co powoduje wyczerpanie pamięci w małych jednostkach. VPS z 2 GB RAM uruchomi się i obsłuży kilku agentów, jednak sam Sidekiq pod obciążeniem może przekroczyć 1 GB, co skutkuje zabijaniem kontenerów z kodem wyjścia 137 w okresach wzmożonego ruchu oraz podczas aktualizacji.
Dlaczego e-maile z resetem hasła do Chatwoot nie docierają?
Ponieważ nie skonfigurowano ustawień SMTP, więc ActionMailer próbuje dostarczyć wiadomość na localhost przez port 25, a wewnątrz kontenera nie ma serwera pocztowego. Zadanie kończy się niepowodzeniem w Sidekiq z błędem Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 25, podczas gdy przeglądarka wyświetla komunikat o sukcesie. Należy ustawić SMTP_ADDRESS, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD oraz MAILER_SENDER_EMAIL w pliku .env, zrestartować usługi rails i sidekiq, a następnie monitorować docker compose logs -f sidekiq podczas wyzwalania resetu.
Co należy zabezpieczyć, aby przywrócić Chatwoot?
Bazę danych Postgres, wolumen Docker storage_data, plik .env oraz pliki compose. Sama baza danych nie wystarczy, ponieważ przesłane pliki znajdują się w wolumenie, a Postgres przechowuje jedynie odwołania do nich; przywrócenie samej bazy spowoduje, że konwersacje będą miały uszkodzone załączniki. Plik .env jest istotny, ponieważ inny SECRET_KEY_BASE wyloguje wszystkich użytkowników, a inne klucze ACTIVE_RECORD_ENCRYPTION_* sprawią, że zaszyfrowane kolumny staną się niemożliwe do odczytania.
Jak zaktualizować Chatwoot bez uszkodzenia bazy danych?
Wykonaj kopię zapasową, zmień tag obrazu w pliku compose, a następnie uruchom docker compose pull, docker compose down, docker compose run --rm rails bundle exec rails db:chatwoot_prepare oraz docker compose up -d. Najpierw pobierz obraz, ponieważ migracje muszą zostać wykonane z poziomu nowej wersji, a następnie zatrzymaj stos, gdyż stary kod działający na nowym schemacie generuje błędy. W przypadku starszych instalacji należy przechodzić przez kolejne wersje minor, ponieważ migracje są usuwane po włączeniu ich do głównego schematu bazy.
Czy można użyć standardowego obrazu postgres zamiast pgvector?
Nie. Schemat Chatwoot wymaga rozszerzenia vector, więc standardowy obraz postgres zawiedzie podczas db:chatwoot_prepare z błędem ERROR: extension "vector" is not available, ponieważ plik kontrolny rozszerzenia nie znajduje się w tym obrazie. Należy zachować pgvector/pgvector:pg16 z oryginalnego pliku compose lub użyć innego obrazu, który zawiera pgvector dla danej wersji głównej Postgres.