התקנת Chatwoot על שרת VPS באמצעות Docker
למדו איך להריץ Chatwoot עם Docker Compose ו-Traefik. המדריך כולל הגדרת SMTP תקין, גיבויים ל-Postgres, שימוש בגרסאות קבועות למכולות וביצוע שדרוגים בטוחים ללא אובדן מידע.
מה אתם בונים
כדי לארח Chatwoot באופן עצמי על גבי VPS, עליכם להריץ ארבע מכולות: תהליך Rails web, עובד רקע Sidekiq, מסד נתונים PostgreSQL עם הרחבת pgvector, ו־Redis. Chatwoot הוא פתרון קוד פתוח לניהול תמיכת לקוחות, כך שתקבלו תיבת דואר נכנס משותפת לצוות ווידג'ט צ'אט לאתר על שרת שבשליטתכם. ההתקנה אורכת כעשרים דקות. כל מה שבא לאחר מכן – משלוח דואר, גיבויים, שדרוגים והתאמת משאבים – הוא שיקבע אם המערכת תמשיך לפעול גם בעוד שנה.
לכל מכולה תפקיד אחד. Rails מגיש את לוח הבקרה של הנציגים ואת ה־API (ממשק תכנות יישומים) של הווידג'ט. Sidekiq מבצע את המשימות האיטיות: שליחת דואר אלקטרוני, תשאול ערוצים מחוברים, הרצת חוקי אוטומציה והפקת דוחות. Postgres מאחסן את השיחות, אנשי הקשר, חשבונות הנציגים וכל הגדרה שתשנו בלוח הבקרה. Redis מחזיק את התורים של Sidekiq ואת ערוץ ה־pub/sub של ActionCable, שדוחף הודעות חדשות ללוח בקרה פתוח ללא צורך בטעינה מחדש של הדף. Redis אינו משמש כאן כמטמון זמני בלבד, שכן אובדן הנתונים בו משמעו אובדן משימות בתור.
תמונת ה־Postgres בקובץ ה־compose הרשמי היא pgvector/pgvector:pg16 ולא תמונת ה־postgres הסטנדרטית, כיוון שהסכימה של Chatwoot מפעילה את הרחבת vector עבור תכונות ה־AI שלה. אם תחליפו ל־Postgres סטנדרטי, הרצת מסד הנתונים הראשונה תיכשל עם ERROR: extension "vector" is not available, כיוון שקובץ הבקרה של ההרחבה אינו קיים בתמונה ההיא. השתמשו בתמונה שהמפתחים מספקים.
מדריך זה מניח ש־Docker ו־reverse proxy כבר פועלים על השרת. אם לא, התחילו ב-Docker Compose על גבי VPS וחזרו לכאן.
כמה משאבי VPS נדרשים לאירוח עצמי של Chatwoot?
נכון לאוגוסט 2026, דרישות המערכת הרשמיות מציינות מינימום של 4 GB RAM ו-4 ליבות CPU, עבור עומס של עד 10,000 שיחות ביום. עבור עומס של עד 20,000 שיחות ביום, הדרישה עולה ל-8 GB RAM ו-8 ליבות. המערכת דורשת גם לפחות 1 GB של swap, והסיבה לכך ברורה: מניעת קריסת המכונה מחוסר זיכרון במהלך תהליך שדרוג. יש להקצות בין 5 GB ל-10 GB שטח דיסק עבור Postgres, עוד לפני חישוב נפח העלאות הקבצים.
כעת, האמת ללא כחל וסרק: שרת VPS עם 2 GB RAM יצליח להעלות את Chatwoot, והוא ייראה תקין עם שני נציגים ותיבת דואר שקטה. עם זאת, הוא יקרוס בשני מקרים. הראשון הוא Sidekiq, שצורך מעל 1 GB בשרת עמוס; פרץ של הודעות דוא"ל או הרצת דוח ידחפו את השרת מעבר למגבלת הזיכרון שלו, עוד לפני ש-Rails, Postgres ו-Redis לקחו את חלקם. השני הוא תהליך השדרוג, כיוון ש-db:chatwoot_prepare מפעיל תהליך Rails חדש לצורך החלת מיגרציות, ועליית Rails בגרסה זו צורכת מאות מגה-בייטים עוד לפני ביצוע פעולה מועילה כלשהי.
לא תקבלו אזהרה מנומסת לפני הקריסה. מנגנון ה-out of memory killer של ה-kernel שולח SIGKILL לתהליך הגדול ביותר, Docker מזהה שהמכולה קרסה, ו-restart: always מפעיל אותה מחדש. לאחר מכן, docker compose ps יציג מכולה שחוזרת ללא הרף למצב Exited (137), כאשר קוד 137 מציין שהתהליך נהרג על ידי signal 9. ניתן לאמת זאת באמצעות sudo dmesg -T | grep -i "killed process", שמציג את שם התהליך שה-kernel בחר להפסיק.
אם 4 GB חורגים מהתקציב שלכם, ניתן להריץ את המערכת על שרת עם 2 GB RAM ו-2 GB של swap, תוך קבלת העובדה שזמני התגובה יואטו תחת עומס במקום שהשירות יקרוס כליל. בכל מקרה, כדאי להגדיר מגבלת זיכרון קשיחה לכל שירות, כדי שה-worker לא יפיל את מסד הנתונים יחד איתו. ראו מגבלות זיכרון ב-Docker Compose.
העלאות קבצים הן החלק שגדל ללא מגבלה מוגדרת מראש. כל צילום מסך שלקוח מצרף נשמר בנפח האחסון ונשאר שם, לכן יש לנטר את docker system df -v במקום להניח שמסד הנתונים הוא הגורם למילוי הדיסק.
הורדת קובץ ה-compose וקיבוע גרסה (tag)
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 .envהקובץ שהורדת זה עתה מציין image: chatwoot/chatwoot:latest. שנה זאת לפני שתבצע פעולה כלשהי.
services:
base: &base
image: chatwoot/chatwoot:v4.16.2
env_file: .env
volumes:
- storage_data:/app/storageהמשמעות של latest היא שפקודת docker compose pull הבאה תספק לך את הגרסה שפורסמה באותו בוקר, מה שעלול לכלול גרסה ראשית עם תהליכי הגירה (migrations) שלא קראת עליהם. בפועל, לא ניתן לבטל תהליכי הגירה ב-Chatwoot, לכן קפיצה לא מכוונת לגרסה חדשה מחייבת שחזור מגיבוי ולא ביטול פשוט. קבע את ה-tag באופן ידני ושנה אותו רק כשאתה מתכוון לכך. נכון לאוגוסט 2026, הגרסה העדכנית היא v4.16.2; בדוק ב-דף ה-releases מהו ה-tag שעליך לקבע היום.
השירות base הוא עוגן YAML שגם rails וגם sidekiq משתמשים בו, לכן שינוי ה-tag במקום אחד ישנה אותו עבור שניהם. בזמן שאתה עורך את הקובץ, מחק את השורה version: '3' בראש הקובץ. גרסאות מודרניות של Compose מתעלמות ממנה ומדפיסות the attribute 'version' is obsolete, it will be ignored בכל הרצה של פקודה.
מילוי קובץ ה-env.
תחילה צרו את ה-secret. הספק ממליץ על ערך אלפא-נומרי, כיוון שתווים מיוחדים עלולים להשתבש בעת מעבר הערך דרך shell או מנתח YAML.
head /dev/urandom | tr -dc A-Za-z0-9 | head -c 63 ; echo ''לאחר מכן הגדירו את המפתחות הבאים ב-.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 ו-redis://redis:6379 הם שמות שירותי ה-Compose, אשר נפתרים ברשת ברירת המחדל של הפרויקט. FRONTEND_URL אינו קישוט. Chatwoot בונה את ה-URL של סקריפט ה-widget ואת כל הקישורים בתוך הודעות דוא"ל יוצאות ממנו, לכן ערך שגוי יגרום לקישורי איפוס סיסמה להפנות למארח שאינו מגיב.
כעת, המלכוד בקובץ של הספק. שירות ה-postgres אינו קורא את .env. הוא נושא בלוק environment משלו עם POSTGRES_PASSWORD= ריק, כך שהגדרת הסיסמה ב-.env בלבד תשאיר את מסד הנתונים ללא סיסמה ואת היישום עם סיסמה. הפנו את השירות לאותו משתנה:
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 קורא את .env מספריית הפרויקט עבור החלפת ${...}, כך ששני הצדדים מקבלים כעת את אותה מחרוזת. טעות כאן תגרום ל-Rails להיעצר עם PG::ConnectionBad: FATAL: password authentication failed for user "postgres".
התנהגות אחת מפתיעה כמעט את כולם: אימג' ה-Postgres מיישם את POSTGRES_PASSWORD רק כאשר הוא מאתחל ספריית נתונים ריקה. לשינוי הערך לאחר מכן אין השפעה, כיוון ש-initdb לעולם אינו רץ פעם שנייה. אם כבר הפעלתם את ה-stack פעם אחת, שנו זאת בתוך מסד הנתונים עצמו.
docker compose exec postgres psql -U postgres -c "ALTER USER postgres WITH PASSWORD 'the-new-password';"ENABLE_ACCOUNT_SIGNUP=true הוא זמני. הוא פותח את טופס ההרשמה הציבורי כדי שתוכלו ליצור את החשבון הראשון. הגדירו אותו ל-false והריצו את docker compose up -d שוב ברגע שהחשבון שלכם קיים, אחרת כל מי שימצא את ה-URL יוכל להירשם למערכת התמיכה שלכם. מאותו רגע, סוכנים יגיעו באמצעות הזמנה והסיסמאות שלהם יחיו רק באפליקציה זו, מה שמתאים עד שתריצו חצי תריסר שירותים ותתעייפו מרשימת חשבונות נפרדת בכל אחד מהם; בנקודה זו ספק זהות בניהול עצמי כמו Authentik הוא הרכיב שמחליף אותם.
.env מכיל כעת כל secret שיש ל-stack הזה, בטקסט גלוי, לכן שמרו אותו במצב 600 והרחיקו אותו מ-git. כיצד Compose קורא קובצי env, והיכן דולפים סודות מכסה את הקצוות החדים, כולל ההבדל בין env_file ל-environment.
הגדרת Chatwoot מאחורי Traefik קיים
אל תבנו reverse proxy נוסף עבור יישום בודד. אם Traefik כבר מבצע TLS termination עבור מכולות אחרות בשרת זה, צרפו את Chatwoot אליו באמצעות בלוק labels. אם טרם הגדרתם זאת, בצעו זאת פעם אחת לפי המדריך Traefik לפני כמה יישומי Docker Compose, ולאחר מכן חזרו לכאן.
שמרו על קובץ ה-docker-compose.yaml המקורי של המפתח קרוב ככל האפשר לגרסת ה-stock כדי שתוכלו להשוות אותו (diff) מול גרסה חדשה בעתיד, והכניסו את השינויים שלכם לקובץ override. Compose ממזג את docker-compose.override.yaml באופן אוטומטי, והמדריך פיצול Compose למספר קבצים מסביר את כללי המיזוג.
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: trueהשתמשו בשמות ה-entrypoint וה-certresolver שלכם. המכולה חייבת להיות באותה רשת Docker שבה נמצא Traefik, וזהו התפקיד של הערך proxy; עליה להישאר גם ב-default, אחרת היא תאבד את הגישה ל-Postgres ול-Redis. זו השורה השנייה שאנשים נוטים לשכוח.
השאירו את בלוק ה-ports: ללא שינוי. המפתח מגדיר אותו ל-127.0.0.1:3000, שהוא loopback בלבד, לכן הוא אינו נגיש מהאינטרנט ונותר שימושי לבדיקות מתוך השרת באמצעות curl -I http://127.0.0.1:3000.
לוח הבקרה של הסוכן מחזיק חיבור WebSocket פתוח ל-/cable לצורך העברת הודעות בזמן אמת. Traefik מעביר את ה-HTTP upgrade ללא צורך בתצורה נוספת, כך שאין מה להוסיף. אם בעתיד תציבו CDN או proxy נוסף לפני Traefik, אפשרו שם WebSocket, שכן התסמין לכך הוא לוח בקרה שנטען כרגיל, בעוד הודעות חדשות מופיעות רק לאחר רענון ידני.
אתחול מסד הנתונים והפעלת ה-stack
הפעילו תחילה את שירותי הנתונים ואפשרו ל-Postgres לסיים את הריצה הראשונה שלו.
docker compose up -d postgres redis
docker compose logs postgres | tail -n 5המתינו ל-database system is ready to accept connections. לאחר מכן צרו את ה-schema.
docker compose run --rm rails bundle exec rails db:chatwoot_prepareפעולה זו יוצרת את מסד הנתונים אם הוא חסר, ולאחר מכן טוענת את ה-schema ואת נתוני ה-seed המוגדרים כברירת מחדל. הפקודה מדפיסה שורות migration ומסתיימת בצורה תקינה. אם התהליך נתקע בהדפסת postgres:5432 - no response, ה-entrypoint ממתין למסד נתונים שעדיין אינו מקבל חיבורים; בהרצה ראשונה, המשמעות היא בדרך כלל ש-initdb עדיין בתהליך עבודה. המתינו, קראו את הלוגים של Postgres, ולאחר מכן הריצו שוב. אם התהליך נעצר בתוסף vector, סימן שהחלפתם את ה-image של pgvector ב-Postgres סטנדרטי.
docker compose up -d
docker compose ps
docker compose logs --tail 30 railsכל ארבעת ה-containers צריכים להציג Up, והלוג של rails צריך להסתיים בשורת Puma המציינת האזנה ב-http://0.0.0.0:3000. לאחר מכן בדקו את הנתיב הציבורי:
curl -sI https://support.example.com | head -n 1HTTP/2 200 מציין שכל השרשרת עובדת. שגיאת 404 מ-Traefik משמעותה שחוק הניתוב (router rule) לא תאם, בדרך כלל עקב שגיאת הקלדה בשם המארח. שגיאת 502 משמעותה ש-Traefik זיהה את ה-router אך לא הצליח להגיע ל-container; זה קורה כמעט תמיד בגלל רשת proxy חסרה או loadbalancer.server.port שאינו 3000.
פתחו את ה-URL, צרו את החשבון שלכם ב-/app/auth/signup, לאחר מכן הגדירו את ENABLE_ACCOUNT_SIGNUP=false והריצו את docker compose up -d כדי לסגור את הטופס.
מדוע איפוס סיסמאות ותכתובות דוא"ל נכשלים ללא SMTP
Chatwoot ללא הגדרות SMTP (פרוטוקול העברת דואר פשוט) הוא מוקד תמיכה שאינו מסוגל לשלוח דואר, וזה משבש הרבה יותר מאשר רק התראות. איפוס סיסמאות מפסיק לעבוד, כך שמנהל מערכת שננעל מחוץ למערכת נשאר נעול. הזמנות לסוכנים מפסיקות לעבוד, כיוון שהזמנה היא הודעת דוא"ל. מענה ללקוח בתוך תכתובת דוא"ל מפסיק לעבוד, כך שהתקשורת הופכת לחד-צדדית. זהו השלב שאנשים מדלגים עליו ומגלים את חשיבותו ברגע הכי פחות מתאים.
המנגנון פשוט. ללא הגדרות SMTP, ActionMailer נשאר עם ברירת המחדל שלו לשליחה ל-localhost בפורט 25. אין שרת דואר בתוך ה-container של Rails, לכן משימת המשלוח מעלה שגיאת Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 25. הדואר יוצא ממשימת רקע, לכן השורה הזו מופיעה בלוג של Sidekiq ולא בלוג של Rails. בינתיים, האדם שלוחץ על "שכחתי סיסמה" רואה הודעת אישור עליזה ולא מקבל דבר.
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=trueהשתמשו בפורט 587 עם STARTTLS, שפותח את החיבור בטקסט גלוי ומשדרג אותו להצפנה לפני האימות. רוב ספקי ה-VPS חוסמים תעבורה יוצאת בפורט 25 כדי להגביל דואר זבל, לכן ממסר (relay) בפורט 587 הוא בדרך כלל הדרך היחידה ליצור חיבור. SMTP_DOMAIN הוא המתחם שהשרת שלכם מצהיר עליו במהלך שיחת ה-SMTP, וחלק מהממסרים דוחים חוסר התאמה.
החילו את ההגדרות ועקבו אחר ה-worker:
docker compose up -d rails sidekiq
docker compose logs -f sidekiqהפעילו איפוס סיסמה מדף ההתחברות. משלוח תקין יציג את משימת ה-mailer מסתיימת כראוי בלוג של Sidekiq. כשל יציג את מחלקת החריגה, ולאחר מכן Sidekiq ינסה שוב עם זמן המתנה גדל (backoff), וזו הסיבה שממסר תקול מייצר את אותה שגיאה בכל כמה דקות במשך שעות.
שתי דחיות הן נפוצות, ואף אחת מהן אינה באג ב-Chatwoot. 535 Authentication failed משמעותה ששם המשתמש או הסיסמה שגויים עבור אותו ממסר, וספקים רבים דורשים סיסמת אפליקציה במקום סיסמת החשבון. 550 Sender address rejected משמעותה ש-MAILER_SENDER_EMAIL היא כתובת שהממסר לא יסכים לשלוח בשמה, לכן עליה להיות תיבת דואר או מתחם שאימתתם מולם.
קבלת דוא"ל לתוך תכתובת היא משימה נפרדת. היא דורשת את MAILER_INBOUND_EMAIL_DOMAIN ו-RAILS_INBOUND_EMAIL_SERVICE, בתוספת שרת דואר שמעביר הודעות נכנסות ל-Chatwoot. שכירת ממסר היא הדרך המהירה. אם אתם מעדיפים לשלוט בכל נתיב הדואר, הרצת שרת דואר עצמאי עם Mailcow מכסה את מה שמחויבות כזו באמת דורשת.
מה לגבות, ואיך לוודא שהשחזור עובד
גיבוי של Chatwoot מורכב מארבעה חלקים; דילוג על אחד מהם יהפוך את השחזור לבנייה מחדש.
- מסד הנתונים Postgres, המכיל שיחות, אנשי קשר, חשבונות סוכנים וכל הגדרה.
- הווליום
storage_data, מכיוון ש־ACTIVE_STORAGE_SERVICE=localכותב קבצים שהועלו לדיסק ושומר ב־Postgres רק הפניה אליהם. - הקובץ
.env, מכיוון שהוא מכיל אתSECRET_KEY_BASEואת מפתחות ה־ACTIVE_RECORD_ENCRYPTION_*. - קובצי ה-compose, מכיוון שהם מתעדים את תגית ה-image המדויקת שתואמת לסכימת מסד הנתונים שלכם.
אם תשחזרו רק את מסד הנתונים, כל שיחה תחזור עם קבצים מצורפים שבורים, כיוון שהשורות מצביעות על קבצים שכבר אינם קיימים על הדיסק.
cd ~/chatwoot
docker compose exec -T postgres pg_dump -U postgres -Fc chatwoot > db-$(date +%F).dumpהשימוש ב־-T הוא קריטי. בלעדיו, Compose מקצה מסוף פסאודו (pseudo terminal) שמשכתב בייטים של ירידת שורה בזרם הנתונים, מה שיוצר קובץ dump ש־pg_restore דוחה. -Fc הוא הפורמט הייעודי, המאפשר דחיסה ושימוש ב־pg_restore לשחזור סלקטיבי.
docker run --rm -v chatwoot_storage_data:/data:ro -v "$PWD":/backup alpine \
tar czf /backup/storage-$(date +%F).tgz -C /data .שם הווליום הוא שם תיקיית הפרויקט שלכם בתוספת _storage_data. אשרו את השם באמצעות docker volume ls | grep storage_data לפני שאתם סומכים על הפקודה, כיוון ש-Docker יוצר ווליום ריק במקום להיכשל אם תציינו שם שלא קיים. אתם עלולים לקבל ארכיון תקין אך ריק ללא כל הודעת שגיאה. בדקו את הגודל לאחר מכן באמצעות ls -lh storage-*.tgz.
שני הקבצים נמצאים כעת על אותו דיסק שהם אמורים להגן עליו, מה שלא מספק הגנה כלל. העבירו אותם מחוץ לשרת והצפינו אותם, כיוון ש-dump של מסד נתונים מכיל את כל הודעות הלקוחות בטקסט גלוי. גיבויים מוצפנים מחוץ לאתר עם restic מכסה את נושאי התזמון ושמירת הגיבויים.
תרגול שחזור: בצעו אותו לפני שתזדקקו לו
בצעו שחזור לשרת VPS משני, לא לשרת הפעיל. העתיקו את .env, את קובצי ה-compose ואת שני הארכיונים, ולאחר מכן הריצו:
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 מוחקת את האובייקטים הקיימים לפני הטעינה, לכן הפנו אותה רק למסד נתונים שאתם מוכנים לאבד. לאחר מכן, התחברו ופתחו שיחה שיש בה קובץ מצורף. אם רשימת ההודעות נטענת והקובץ יורד, הגיבוי תקין.
שחזור עם SECRET_KEY_BASE שונה מבטל את כל עוגיות ה-session, ולכן כל המשתמשים ינותקו. שחזור עם מפתחות ACTIVE_RECORD_ENCRYPTION_* שונים הוא בעייתי אף יותר: Chatwoot לא יוכל לפענח את העמודות המכילות את פרטי הגישה לערוצים ויציג את השגיאה ActiveRecord::Encryption::Errors::Decryption. זו הסיבה ש-.env נמצא ברשימת הגיבויים.
כיצד לשדרג את Chatwoot לגרסה חדשה
סדר הפעולות חשוב יותר מהפקודות עצמן.
- קראו את הערות השחרור (release notes) שבין הגרסה הנוכחית שלכם לגרסת היעד, וחפשו שלבים ידניים נדרשים.
- בצעו גיבוי טרי של מסד הנתונים וארכיון של תיקיית האחסון, וודאו שגודל הקבצים נראה תקין.
- עדכנו את ה-tag של ה-image עבור השירות
baseבתוךdocker-compose.yaml. - משכו את ה-image החדש, עצרו את ה-stack, הריצו את ה-migrations, ולאחר מכן הפעילו מחדש.
docker compose pull
docker compose down
docker compose run --rm rails bundle exec rails db:chatwoot_prepare
docker compose up -d
docker compose imagesמשכו את ה-image לפני ביצוע ה-migration, כיוון שה-migration חייב לרוץ מתוך ה-image החדש: ה-image הישן אינו מכיל את קובצי ה-migration החדשים. עצרו את ה-stack לפני ה-migration, כיוון שהקוד הישן וה-schema החדש אינם תואמים; תהליך Rails ישן שרץ עלול להפיק שגיאות או לכתוב רשומות שה-schema החדש לא יקבל. עצירת השירותים גם מפנה את הזיכרון הדרוש ל-migration, וזו הסיבה המלאה לכך שהמפתחים ממליצים על שימוש ב-swap.
docker compose images מציג את ה-tag שכל container מריץ בפועל, מה שעוזר לזהות מקרים שבהם עדכנתם את ה-tag אך שכחתם לבצע pull.
אל תקפצו על פני גרסאות רבות בבת אחת. ההמלצה עבור התקנות ישנות היא לעבור דרך גרסאות ביניים, כיוון ש-migrations מוסרים לאחר שהם מוטמעים ב-schema הבסיסי. לכן, מסד נתונים ישן מאוד עלול להגיע למצב ללא נתיב שדרוג תקין. עברו גרסה מינורית אחת בכל פעם והריצו את שלב ה-prepare לאחר כל אחת.
אם Rails עולה לפני הרצת ה-migration, הוא יסרב לשרת בקשות וירשום ActiveRecord::PendingMigrationError: Migrations are pending בלוגים. כאשר restart: always מוגדר, ה-container יבצע מחזור הפעלה מחדש, כך ש-docker compose ps יציג uptime שמתאפס בכל כמה שניות. הרצת שלב ה-prepare תפתור זאת.
ביצוע Rollback משמעו החזרת ה-tag הישן ושחזור הגיבוי. אין נתיב migration הפוך שניתן להסתמך עליו, וזו הסיבה לקיום שלב 2.
מצבי כשל והודעות שתיתקלו בהן
502 Bad Gateway מ-Traefik. הנתב זיהה את הבקשה אך ה-backend לא השיב. בדקו ש-docker compose ps מציג את ה-rails כ-Up, לאחר מכן הריצו את docker network inspect proxy וודאו שמכולת ה-rails מופיעה ברשימת המכולות. מכולה שאינה פעילה אינה גלויה ל-Traefik, לכן הבקשה מותאמת לנתב אך אין לה לאן להמשיך.
לוח הבקרה נטען אך הודעות חדשות דורשות רענון. ה-websocket ל-/cable אינו עובר, או ש-FRONTEND_URL אינו תואם לכתובת בשורת הכתובות בדפדפן. חוסר התאמה גורם לדף לנסות לפתוח websocket למקור (origin) שונה, מה שהדפדפן חוסם.
FATAL: password authentication failed for user "postgres". הסיסמה ב-.env שונה מזו שמוגדרת בתוך ה-volume של נתוני Postgres. תקנו זאת באמצעות ALTER USER בתוך המכולה הרצה, כיוון שעריכה חוזרת של .env לא תשנה מסד נתונים שכבר אותחל.
NOAUTH Authentication required. Redis רץ עם --requirepass אך היישום התחבר ללא סיסמה, כלומר REDIS_PASSWORD חסר ב-.env או שלא נטען. בדקו זאת ישירות עם docker compose exec redis redis-cli -a "$REDIS_PASSWORD" ping, שאמור להשיב PONG.
מכולות יוצאות עם קוד 137. זהו סיגנל SIGKILL; בשרתים קטנים מדובר ב-OOM Killer (מנגנון הריגת תהליכים עקב חוסר בזיכרון) של ה-kernel. הוסיפו swap, הגדירו מגבלות זיכרון לכל שירות, או עברו לתוכנית אירוח גדולה יותר.
FAQ
כמה זיכרון RAM דרוש לשרת VPS שמריץ Chatwoot בהתקנה עצמית?
נכון לאוגוסט 2026, הדרישה המינימלית של המפתחים היא 4 GB של RAM ו-4 ליבות CPU, עבור נפח של עד 10,000 שיחות ביום, ו-8 GB עם 8 ליבות עבור עד 20,000 שיחות. יש להוסיף לפחות 1 GB של swap, כיוון שתהליך השדרוג מריץ תהליך Rails נוסף לצורך החלת מיגרציות, וזהו השלב שבו שרתים קטנים נתקעים מחוסר זיכרון. שרת VPS עם 2 GB יאתחל ויעבוד עבור מספר מצומצם של נציגים, אך Sidekiq לבדו עלול לצרוך מעל 1 GB תחת עומס; לכן, צפו לקריסת מכולות עם קוד יציאה 137 בתקופות עמוסות או במהלך שדרוגים.
מדוע הודעות איפוס סיסמה של Chatwoot לא מגיעות?
הסיבה היא שלא הוגדרו הגדרות SMTP, ולכן ActionMailer מנסה לשלוח הודעות ל-localhost בפורט 25, אך אין שרת דואר בתוך המכולה. המשימה נכשלת ב-Sidekiq עם השגיאה Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 25, בעוד הדפדפן עדיין מציג הודעת הצלחה. הגדירו את SMTP_ADDRESS, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD ו-MAILER_SENDER_EMAIL בתוך .env, בצעו הפעלה מחדש לשירותי ה-rails וה-sidekiq, ולאחר מכן עקבו אחר docker compose logs -f sidekiq בזמן שאתם מפעילים איפוס סיסמה.
מה עלי לגבות כדי לשחזר את Chatwoot?
יש לגבות את מסד הנתונים Postgres, את ה-volume של Docker בשם storage_data, את הקובץ .env ואת קובצי ה-compose. גיבוי מסד הנתונים בלבד אינו מספיק, כיוון שקבצים שהועלו נשמרים ב-volume בעוד Postgres מחזיק רק הפניות אליהם; לכן, שחזור של מסד הנתונים בלבד יותיר אתכם עם שיחות בעלות קבצים מצורפים שבורים. הקובץ .env קריטי, כיוון שערך SECRET_KEY_BASE שונה ינתק את כל המשתמשים, וערכי ACTIVE_RECORD_ENCRYPTION_* שונים יהפכו את העמודות המוצפנות לבלתי קריאות.
כיצד ניתן לשדרג את Chatwoot מבלי לפגוע במסד הנתונים?
בצעו גיבוי, שנו את תגית ה-image בקובץ ה-compose שלכם, ולאחר מכן הריצו את docker compose pull, docker compose down, docker compose run --rm rails bundle exec rails db:chatwoot_prepare ו-docker compose up -d. בצעו pull תחילה, כיוון שהמיגרציות חייבות לרוץ מה-image החדש, ועצרו את ה-stack לפני כן, כיוון שקוד ישן מול סכימה חדשה יגרום לשגיאות. בהתקנות ישנות, עברו גרסה מינורית אחת בכל פעם, כיוון שמיגרציות מוסרות לאחר שהן מוטמעות בסכימת הבסיס.
האם ניתן להשתמש ב-image הסטנדרטי של postgres במקום ב-pgvector?
לא. הסכימה של Chatwoot מפעילה את התוסף vector, ולכן ה-image הרשמי postgres ייכשל במהלך db:chatwoot_prepare עם השגיאה ERROR: extension "vector" is not available, כיוון שקובץ הבקרה של התוסף אינו קיים ב-image זה. השתמשו ב-pgvector/pgvector:pg16 כפי שמופיע בקובץ ה-compose המקורי, או השתמשו ב-image אחר הכולל את pgvector עבור גרסת ה-Postgres שלכם.