Docker দিয়ে VPS-এ Chatwoot self-host করার পদ্ধতি
Docker Compose ও Traefik দিয়ে VPS-এ Chatwoot চালান। pinned tag, সত্যিই কাজ করা SMTP, Postgres ও uploads backup, এবং নিরাপদ upgrade-এর পূর্ণ নির্দেশনা এখানে।
আপনি যা তৈরি করবেন
একটি VPS-এ Chatwoot self-host করতে আপনাকে চারটি container চালাতে হবে: একটি Rails web process, একটি Sidekiq background worker, pgvector extension-সহ PostgreSQL, এবং Redis। Chatwoot একটি open source customer support desk। তাই আপনার নিয়ন্ত্রণাধীন server-এ shared team inbox এবং website chat widget পাবেন। ইনস্টল করতে প্রায় twenty minutes সময় লাগে। এরপর mail delivery, backups, upgrades এবং sizing-এর ব্যবস্থাপনাই নির্ধারণ করে এটি এক বছর পরেও সচল থাকবে কি না।
প্রতিটি container-এর একটি নির্দিষ্ট কাজ আছে। Rails agent dashboard এবং widget API (application programming interface) সরবরাহ করে। Sidekiq ধীরগতির কাজ চালায়: email পাঠানো, সংযুক্ত channel-গুলো poll করা, automation rule চালানো এবং report তৈরি করা। Postgres conversation, contact, agent account এবং dashboard-এ পরিবর্তন করা প্রতিটি setting সংরক্ষণ করে। Redis Sidekiq queue এবং ActionCable pub/sub channel সংরক্ষণ করে। এই channel page reload ছাড়াই খোলা dashboard-এ নতুন message পাঠায়। এখানে Redis কেবল অস্থায়ী cache নয়, কারণ এটি হারালে queued job-ও হারাবে।
upstream compose file-এ Postgres image হিসেবে pgvector/pgvector:pg16 ব্যবহার করা হয়েছে, stock postgres image নয়। কারণ Chatwoot-এর schema AI feature-এর জন্য vector extension সক্রিয় করে। stock Postgres ব্যবহার করলে প্রথম database run ERROR: extension "vector" is not available দিয়ে থেমে যায়, কারণ ওই image-এ extension-এর control file নেই। upstream যে image সরবরাহ করে, সেটিই ব্যবহার করুন।
এই guide ধরে নিচ্ছে যে server-এ Docker এবং reverse proxy ইতিমধ্যে কাজ করছে। এগুলো কাজ না করলে প্রথমে VPS-এ Docker Compose দিয়ে শুরু করুন, তারপর এখানে ফিরে আসুন।
একটি self-hosted Chatwoot-এর জন্য কত ক্ষমতার VPS প্রয়োজন?
August 2026 অনুযায়ী, upstream requirements page-এ minimum হিসেবে 4 GB RAM এবং 4 CPU core চাওয়া হয়েছে। এই ক্ষমতা দিয়ে দিনে সর্বোচ্চ 10,000টি conversation পরিচালনার কথা বলা হয়েছে। 8 GB RAM এবং 8 core দিয়ে দিনে সর্বোচ্চ 20,000টি conversation পরিচালনা করা যায়। সেখানে কমপক্ষে 1 GB swap-এর কথাও বলা হয়েছে। কারণটিও স্পষ্টভাবে দেওয়া আছে: upgrade চলাকালে যাতে মেশিনের memory শেষ না হয়ে যায়। File upload-এর storage বাদ দিয়ে Postgres-এর জন্য 5 GB থেকে 10 GB disk বরাদ্দ রাখুন।
এখন মূল কথায় আসি। 2 GB VPS-এ Chatwoot boot হবে। দুইজন agent এবং কম activity-র inbox থাকলে এটি স্বাভাবিকও মনে হতে পারে। কিন্তু দুটি পরিস্থিতিতে এটি ব্যর্থ হয়। প্রথমটি হলো Sidekiq। Upstream-এর পরিমাপ অনুযায়ী ব্যস্ত server-এ এটি 1 GB-এর বেশি memory ব্যবহার করতে পারে। ফলে email-এর হঠাৎ বৃদ্ধি বা report job চললে Rails, Postgres এবং Redis-এর memory usage যোগ হওয়ার আগেই মেশিনের memory অতিক্রম করে যায়। দ্বিতীয়টি হলো upgrade। কারণ db:chatwoot_prepare migration প্রয়োগ করার জন্য একটি নতুন Rails process চালু করে। এই image-এ Rails boot হওয়ার সময় কোনো কার্যকর কাজ করার আগেই কয়েকশো megabyte memory লাগে।
আগে কোনো স্পষ্ট সতর্কতা পাওয়া যায় না। Kernel-এর out of memory killer সবচেয়ে বড় process-এ SIGKILL পাঠায়। Docker container-টির বন্ধ হয়ে যাওয়া শনাক্ত করে এবং restart: always সেটি আবার চালু করে। এরপর docker compose ps এমন একটি container দেখায়, যা বারবার Exited (137) অবস্থায় ফিরে যাচ্ছে। এখানে 137 অর্থ signal 9 দ্বারা process বন্ধ হয়েছে। sudo dmesg -T | grep -i "killed process" দিয়ে এটি নিশ্চিত করুন। এই command-এ kernel কোন process নির্বাচন করেছিল তার নাম দেখা যায়।
4 GB-এর খরচ বাজেটের বাইরে হলে 2 GB-এর মেশিনে 2 GB swap ব্যবহার করতে পারেন। সে ক্ষেত্রে service পুরোপুরি বন্ধ না হয়ে load-এর সময় response time বেড়ে যাওয়া মেনে নিতে হবে। যেকোনো অবস্থায় প্রতিটি service-এর জন্য hard memory ceiling নির্ধারণ করা যুক্তিযুক্ত। এতে worker process database-কে সঙ্গে নিয়ে বন্ধ করে দিতে পারবে না। দেখুন Docker Compose-এ memory limit।
File upload-এর পরিমাণ আপনার নির্ধারিত কোনো সীমা ছাড়াই বাড়তে পারে। Customer সংযুক্ত করা প্রতিটি screenshot storage volume-এ জমা হয় এবং সেখানেই থাকে। তাই disk ভরার কারণ database—এমনটা ধরে না নিয়ে docker system df -v monitor করুন।
Compose ফাইল সংগ্রহ করুন এবং একটি version 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/storagelatest নির্ধারণ করে যে পরবর্তী docker compose pull ওই সকালে প্রকাশিত যেকোনো সংস্করণ আপনাকে দেবে। এতে এমন major version থাকতে পারে, যার migration সম্পর্কে আপনি জানেন না। বাস্তবে Chatwoot migration ফিরিয়ে নেওয়া যায় না। তাই ভুল করে version পরিবর্তিত হলে undo নয়, backup থেকে restore করতে হয়। একটি tag নির্দিষ্ট করুন এবং ইচ্ছাকৃতভাবে পরিবর্তন করুন। August 2026 অনুযায়ী v4.16.2 ছিল বর্তমান release। আজ কোন tag নির্দিষ্ট করবেন, তা জানতে releases page দেখুন।
base service হলো একটি YAML anchor, যা rails এবং sidekiq উভয়ই merge করে। তাই এক জায়গায় tag পরিবর্তন করলে দুটির tag-ই পরিবর্তিত হয়। ফাইলটিতে থাকতেই উপরের version: '3' লাইনটি মুছে দিন। আধুনিক Compose এটি উপেক্ষা করে এবং প্রতিটি command-এ the attribute 'version' is obsolete, it will be ignored দেখায়।
.env ফাইল পূরণ করুন
প্রথমে secret তৈরি করুন। Upstream alphanumeric value চায়, কারণ value shell বা YAML parser-এর মধ্য দিয়ে যাওয়ার সময় special character বিকৃত হতে পারে।
head /dev/urandom | tr -dc A-Za-z0-9 | head -c 63 ; echo ''এরপর .env-এ এই key-গুলো সেট করুন।
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 service name, যা project-এর default network-এ resolve হয়। FRONTEND_URL কোনো আনুষ্ঠানিকতা নয়। Chatwoot এটি ব্যবহার করে widget script URL এবং outgoing email-এর প্রতিটি link তৈরি করে। তাই ভুল value দিলে এমন password reset link তৈরি হবে, যা সাড়া দেয় না এমন host-এ নির্দেশ করে।
এখন upstream file-এর সমস্যাটি দেখুন। postgres service .env পড়ে না। এর নিজস্ব environment block আছে, যেখানে POSTGRES_PASSWORD= খালি রাখা হয়েছে। তাই শুধু .env-এ password সেট করলে database-এ password থাকে না, কিন্তু application একটি password ব্যবহার করে। একই variable-এ service-টিকে নির্দেশ করুন:
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 project directory থেকে .env পড়ে ${...} substitution সম্পন্ন করে। ফলে উভয় পাশে একই string ব্যবহৃত হয়। এটি ভুল হলে Rails PG::ConnectionBad: FATAL: password authentication failed for user "postgres" দিয়ে বন্ধ হয়ে যায়।
একটি আচরণ প্রায় সবাইকে অবাক করে: Postgres image খালি data directory initialize করার সময়ই কেবল POSTGRES_PASSWORD প্রয়োগ করে। পরে value পরিবর্তন করলে কোনো প্রভাব পড়ে না, কারণ initdb দ্বিতীয়বার চলে না। Stack ইতিমধ্যে একবার চালু করে থাকলে database-এর ভেতরে value পরিবর্তন করুন।
docker compose exec postgres psql -U postgres -c "ALTER USER postgres WITH PASSWORD 'the-new-password';"ENABLE_ACCOUNT_SIGNUP=true অস্থায়ী। প্রথম account তৈরি করার জন্য এটি public registration form খুলে দেয়। আপনার account তৈরি হওয়ার সঙ্গে সঙ্গে এটি false সেট করুন এবং আবার docker compose up -d চালান। নইলে URL খুঁজে পাওয়া যে কেউ আপনার support desk-এ register করতে পারবে। এরপর থেকে agent-রা invitation-এর মাধ্যমে যুক্ত হবে এবং তাদের password শুধু এই app-এ থাকবে। অর্ধ ডজন service চালানো শুরু করে প্রতিটিতে আলাদা account list রাখতে বিরক্ত না হওয়া পর্যন্ত এটি যথেষ্ট। তখন Authentik-এর মতো self-hosted identity provider এই account list-গুলোর বিকল্প হবে।
.env-এ এখন এই stack-এর সব secret plain text-এ আছে। তাই এর mode 600 রাখুন এবং git-এর বাইরে রাখুন। Compose কীভাবে env file পড়ে এবং secret কোথায় ফাঁস হয় অংশে গুরুত্বপূর্ণ বিষয়গুলো ব্যাখ্যা করা হয়েছে, যার মধ্যে env_file এবং environment-এর পার্থক্যও রয়েছে।
বিদ্যমান Traefik-এর পেছনে Chatwoot চালান
একটি অ্যাপ্লিকেশনের জন্য দ্বিতীয় reverse proxy তৈরি করবেন না। এই সার্ভারে অন্য container-এর জন্য Traefik যদি ইতিমধ্যে TLS (transport layer security) termination করে, তাহলে একটি label block-এর মাধ্যমে Chatwoot-কে একই ব্যবস্থায় যুক্ত করুন। এখনো সেটি না থাকলে একাধিক Docker Compose app-এর সামনে Traefik স্থাপন করুন অনুসরণ করে একবার সেটআপ করুন, তারপর এখানে ফিরে আসুন।
Upstream-এর docker-compose.yaml-কে মূল অবস্থার কাছাকাছি রাখুন, যাতে পরে নতুন কপির সঙ্গে পার্থক্য নির্ণয় করা যায়। আপনার পরিবর্তনগুলো override file-এ রাখুন। Compose স্বয়ংক্রিয়ভাবে docker-compose.override.yaml merge করে, এবং একাধিক file-এ Compose ভাগ করা অংশে merge-এর নিয়ম ব্যাখ্যা করা হয়েছে।
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-এর নাম ব্যবহার করুন। Container-টি Traefik-এর একই Docker network-এ থাকতে হবে। proxy entry এই network নির্ধারণ করে। একই সঙ্গে এটিকে default-এও রাখতে হবে, না হলে Postgres এবং Redis-এর সঙ্গে সংযোগ বিচ্ছিন্ন হবে। এই দ্বিতীয় লাইনটিই অনেকে ভুলে যান।
ports: block-টি অপরিবর্তিত রাখুন। Upstream এটিকে 127.0.0.1:3000-এ bind করে, যা শুধু loopback interface। তাই এটি Internet থেকে reachable নয় এবং curl -I http://127.0.0.1:3000 ব্যবহার করে সার্ভারের ভেতর থেকে testing-এর জন্য কার্যকর থাকে।
Live message delivery-এর জন্য agent dashboard /cable-এর সঙ্গে একটি websocket connection খোলা রাখে। Traefik অতিরিক্ত configuration ছাড়াই HTTP upgrade forward করে, তাই কিছু যোগ করার দরকার নেই। পরে Traefik-এর সামনে CDN বা অন্য কোনো proxy বসালে সেখানে websockets অনুমোদন করুন। অন্যথায় dashboard স্বাভাবিকভাবে load হলেও নতুন message শুধু manual refresh-এর পরে দেখা যাবে।
ডেটাবেস আরম্ভ করুন এবং stack চালু করুন
প্রথমে data service-গুলো চালু করুন এবং Postgres-এর প্রথমবারের কাজ শেষ হওয়া পর্যন্ত অপেক্ষা করুন।
docker compose up -d postgres redis
docker compose logs postgres | tail -n 5database system is ready to accept connections পর্যন্ত অপেক্ষা করুন। এরপর schema তৈরি করুন।
docker compose run --rm rails bundle exec rails db:chatwoot_prepareএটি database না থাকলে তৈরি করে। এরপর schema এবং default seed data লোড করে। এটি migration line দেখায় এবং পরিষ্কারভাবে বন্ধ হয়। যদি এটি postgres:5432 - no response দেখাতে থাকে, entrypoint এমন একটি database-এর জন্য অপেক্ষা করছে যা এখনো connection গ্রহণ করছে না। প্রথমবার চালানোর সময় এর অর্থ সাধারণত initdb এখনো কাজ করছে। অপেক্ষা করুন, Postgres-এর log পড়ুন, তারপর কমান্ডটি আবার চালান। যদি এটি vector extension-এ থেমে যায়, তাহলে আপনি pgvector image-এর বদলে stock Postgres ব্যবহার করেছেন।
docker compose up -d
docker compose ps
docker compose logs --tail 30 railsচারটি container-এর status Up হওয়া উচিত। rails log-এর শেষে http://0.0.0.0:3000-এ listening থাকা একটি Puma line থাকা উচিত। এরপর public path পরীক্ষা করুন:
curl -sI https://support.example.com | head -n 1HTTP/2 200 মানে সম্পূর্ণ chain কাজ করছে। Traefik থেকে 404 এলে router rule মেলেনি। সাধারণত hostname-এ বানান ভুল থাকে। 502 মানে Traefik router-এর সঙ্গে মিলেছে, কিন্তু container-এ পৌঁছাতে পারেনি। এর প্রায় সব ক্ষেত্রে কারণ হলো proxy network অনুপস্থিত, অথবা loadbalancer.server.port-এর মান 3000 নয়।
URL খুলুন, /app/auth/signup-এ আপনার account তৈরি করুন। এরপর ENABLE_ACCOUNT_SIGNUP=false সেট করুন এবং form বন্ধ করতে docker compose up -d চালান।
SMTP ছাড়া password reset এবং email conversation কেন ব্যর্থ হয়
SMTP (simple mail transfer protocol) সেটিংস ছাড়া Chatwoot এমন একটি support desk, যা mail পাঠাতে পারে না। এতে শুধু notification নয়, আরও অনেক কিছু ব্যাহত হয়। Password reset কাজ করা বন্ধ করে দেয়। ফলে কোনো admin account থেকে lock out হলে আর ঢুকতে পারেন না। Agent invitation-ও কাজ করে না, কারণ invitation একটি email। Email conversation-এ customer-কে reply পাঠানোও বন্ধ হয়ে যায়। ফলে conversation শুধু একমুখী থাকে। অনেকে এই ধাপটি বাদ দেন এবং পরে সবচেয়ে খারাপ সময়ে সমস্যাটি আবিষ্কার করেন।
প্রক্রিয়াটি সরল। SMTP সেটিংস না থাকলে ActionMailer localhost-এর port 25-এ mail পাঠানোর default ব্যবহার করে। Rails container-এর ভেতরে কোনো mail server নেই। তাই delivery job Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 25 তৈরি করে। Mail একটি background job থেকে পাঠানো হয়। ফলে ওই line Sidekiq log-এ দেখা যায়, Rails log-এ নয়। এদিকে "forgot password" নির্বাচনকারী ব্যক্তি একটি স্বাভাবিক confirmation দেখেন, কিন্তু কোনো mail পান না।
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=trueSTARTTLS সহ port 587 ব্যবহার করুন। এতে সংযোগটি plain text-এ শুরু হয় এবং authentication-এর আগে encrypted করা হয়। Spam সীমিত করতে অধিকাংশ VPS provider outbound port 25 block করে। তাই 587 port-এর relay-ই সাধারণত একমাত্র সংযোগযোগ্য বিকল্প। SMTP_DOMAIN হলো SMTP conversation-এর সময় আপনার server যে domain ঘোষণা করে। এই domain না মিললে কিছু relay request প্রত্যাখ্যান করে।
সেটিংস প্রয়োগ করুন এবং worker monitor করুন:
docker compose up -d rails sidekiq
docker compose logs -f sidekiqLogin page থেকে একটি password reset শুরু করুন। Delivery সঠিকভাবে কাজ করলে Sidekiq log-এ mailer job স্বাভাবিকভাবে শেষ হওয়া দেখা যাবে। ব্যর্থ হলে প্রথমে exception class দেখা যাবে। এরপর Sidekiq ক্রমবর্ধমান backoff ব্যবহার করে retry করবে। এ কারণেই একটি নষ্ট relay ঘণ্টার পর ঘণ্টা প্রতি কয়েক মিনিটে একই error তৈরি করে।
দুটি rejection সাধারণত দেখা যায়, এবং কোনোটিই Chatwoot-এর bug নয়। 535 Authentication failed অর্থ হলো ওই relay-এর জন্য username বা password ভুল। অনেক provider account password-এর বদলে application password চায়। 550 Sender address rejected অর্থ হলো MAILER_SENDER_EMAIL এমন একটি address, যেটি ব্যবহার করে relay mail পাঠাতে দেবে না। তাই এটি provider-এর কাছে verified mailbox বা domain হতে হবে।
Conversation-এ email গ্রহণ করা আলাদা কাজ। এর জন্য MAILER_INBOUND_EMAIL_DOMAIN এবং RAILS_INBOUND_EMAIL_SERVICE প্রয়োজন। পাশাপাশি এমন একটি mail server দরকার, যা incoming message Chatwoot-এর কাছে হস্তান্তর করবে। Relay ভাড়া নেওয়াই দ্রুততম পদ্ধতি। আপনি যদি সম্পূর্ণ mail path নিজে পরিচালনা করতে চান, Mailcow দিয়ে নিজস্ব mail server চালানো সেই ব্যবস্থাপনার প্রকৃত প্রয়োজনীয়তাগুলো ব্যাখ্যা করে।
কী ব্যাকআপ করবেন এবং restore কাজ করছে কীভাবে প্রমাণ করবেন
একটি Chatwoot ব্যাকআপের 4টি অংশ থাকে। এর যেকোনো একটি বাদ পড়লে restore করার বদলে নতুন করে সেটআপ করতে হবে।
- Postgres database, যেখানে conversation, contact, agent account এবং সব setting সংরক্ষিত থাকে।
storage_datavolume, কারণACTIVE_STORAGE_SERVICE=localupload করা file disk-এ লেখে এবং Postgres-এ শুধু একটি reference row রাখে।.envfile, কারণ এতেSECRET_KEY_BASEএবংACTIVE_RECORD_ENCRYPTION_*key থাকে।- compose file, কারণ এতে database schema-এর সঙ্গে সামঞ্জস্যপূর্ণ নির্দিষ্ট image tag উল্লেখ থাকে।
শুধু database restore করলে সব conversation ফিরে আসবে, কিন্তু attachment কাজ করবে না। কারণ row-গুলো এমন file-এর দিকে নির্দেশ করবে, যেগুলো আর disk-এ নেই।
cd ~/chatwoot
docker compose exec -T postgres pg_dump -U postgres -Fc chatwoot > db-$(date +%F).dump-T গুরুত্বপূর্ণ। এটি না থাকলে Compose একটি pseudo terminal বরাদ্দ করে। এর ফলে stream-এর newline byte পরিবর্তিত হয় এবং এমন একটি dump file তৈরি হয়, যা pg_restore গ্রহণ করে না। -Fc হলো custom format। এটি compression সমর্থন করে এবং pg_restore-কে নির্বাচিতভাবে কাজ করতে দেয়।
docker run --rm -v chatwoot_storage_data:/data:ro -v "$PWD":/backup alpine \
tar czf /backup/storage-$(date +%F).tgz -C /data .Volume-এর নাম আপনার project directory-এর নামের সঙ্গে _storage_data যুক্ত করে তৈরি হয়। কোনো command-কে বিশ্বাস করার আগে docker volume ls | grep storage_data দিয়ে নামটি নিশ্চিত করুন। কারণ অস্তিত্বহীন volume-এর নাম দিলে Docker ব্যর্থ না হয়ে একটি খালি volume তৈরি করে। আপনি একটি বৈধ কিন্তু খালি archive পাবেন এবং কোনো error দেখা যাবে না। এরপর ls -lh storage-*.tgz দিয়ে এর size পরীক্ষা করুন।
এখন দুটি file-ই সেই disk-এ রয়েছে, যে disk-এ সুরক্ষিত data রাখা হয়েছে। এতে কোনো সুরক্ষা যোগ হয় না। এগুলো server-এর বাইরে পাঠিয়ে encrypt করুন, কারণ database dump-এ প্রতিটি customer message plain text-এ থাকে। restic দিয়ে encrypted off-site backup-এ scheduling এবং retention সম্পর্কে দেখানো হয়েছে।
:::detailsRestore drill এটি প্রয়োজনের আগে চালান
Live server-এ নয়, দ্বিতীয় একটি VPS-এ restore করুন। .env, compose file এবং দুটি archive সেখানে copy করুন। তারপর চালান:
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 বিদ্যমান object মুছে ফেলে। তাই এটি কেবল এমন database-এ চালান, যেটি হারাতে আপনার আপত্তি নেই। এরপর sign in করে attachment-সহ একটি conversation খুলুন। Message list লোড হলে এবং file download হলে বুঝবেন ব্যাকআপটি কার্যকর।
ভিন্ন SECRET_KEY_BASE দিয়ে restore করলে প্রতিটি session cookie invalid হয়ে যায়। ফলে সবাই sign out হয়ে যায়। ভিন্ন ACTIVE_RECORD_ENCRYPTION_* key দিয়ে restore করা আরও গুরুতর সমস্যা তৈরি করে। Chatwoot channel credential থাকা column decrypt করতে পারে না এবং ActiveRecord::Encryption::Errors::Decryption দেখায়। এই কারণে .env ব্যাকআপ তালিকায় রাখা হয়েছে।
:::
Chatwoot-কে নতুন tag-এ আপগ্রেড করার পদ্ধতি
কমান্ডের চেয়ে ধাপগুলোর ক্রম বেশি গুরুত্বপূর্ণ।
- আপনার বর্তমান tag এবং লক্ষ্য tag-এর মধ্যবর্তী release notes পড়ুন। প্রয়োজনীয় manual step আছে কি না দেখুন।
- নতুন database dump এবং storage archive তৈরি করুন। উভয় ফাইলের আকার স্বাভাবিক কি না যাচাই করুন।
docker-compose.yaml-এbaseservice-এর image tag সম্পাদনা করুন।- নতুন image pull করুন, stack বন্ধ করুন, migration চালান, তারপর আবার চালু করুন।
docker compose pull
docker compose down
docker compose run --rm rails bundle exec rails db:chatwoot_prepare
docker compose up -d
docker compose imagesMigration চালানোর আগে image pull করুন। Migration-কে নতুন image থেকেই চলতে হয়, কারণ পুরনো image-এ নতুন migration file থাকে না। Migration চালানোর আগে stack বন্ধ করুন। পুরনো code এবং নতুন schema পরস্পরের সঙ্গে সামঞ্জস্যপূর্ণ নয়। ফলে চালু থাকা পুরনো Rails process error তৈরি করতে পারে বা এমন row লিখতে পারে যা নতুন schema গ্রহণ করবে না। Stack বন্ধ করলে migration-এর প্রয়োজনীয় memory-ও খালি হয়। Upstream swap ব্যবহারের পরামর্শ দেওয়ার মূল কারণ এটিই।
docker compose images প্রতিটি container বর্তমানে যে tag-এ চলছে, সেটি দেখায়। আপনি tag সম্পাদনা করে image pull করতে ভুলে গেলে এটি তা শনাক্ত করতে সাহায্য করে।
একসঙ্গে অনেক version অতিক্রম করবেন না। পুরনো install-এর জন্য Upstream-এর পরামর্শ হলো intermediate tag ধরে ধাপে ধাপে এগোনো। কারণ migration base schema-তে অন্তর্ভুক্ত হওয়ার পরে সেগুলো সরিয়ে ফেলা হয়। ফলে খুব পুরনো database এমন অবস্থায় পৌঁছাতে পারে যেখান থেকে আর এগোনোর কোনো পথ থাকে না। একবারে একটি minor version এগিয়ে যান এবং প্রতিটির পরে prepare step চালান।
Migration চলার আগে Rails চালু হলে এটি request পরিবেশন করতে অস্বীকার করে এবং ActiveRecord::PendingMigrationError: Migrations are pending log করে। restart: always সেট করা থাকলে container বারবার restart হয়। তাই docker compose ps-এ কয়েক সেকেন্ড পরপর reset হওয়া uptime দেখা যায়। Prepare step চালালে সমস্যাটি দূর হয়।
Rollback করতে পুরনো tag পুনরায় সেট করুন এবং dump restore করুন। নির্ভরযোগ্য reverse migration path নেই। এ কারণেই ধাপ 2 গুরুত্বপূর্ণ।
ব্যর্থতার ধরন এবং যে বার্তাগুলো দেখবেন
Traefik থেকে 502 Bad Gateway। Router অনুরোধের সঙ্গে মিলে গেছে, কিন্তু backend কোনো উত্তর দেয়নি। docker compose ps চালিয়ে Rails-এর অবস্থা Up হিসেবে দেখাচ্ছে কি না পরীক্ষা করুন। এরপর docker network inspect proxy চালান এবং নিশ্চিত করুন যে container তালিকায় Rails container দেখা যাচ্ছে। কোনো container সংযুক্ত না থাকলে Traefik সেটি দেখতে পায় না। ফলে অনুরোধটি router-এর সঙ্গে মিলে গেলেও কোনো backend-এ পৌঁছায় না।
Dashboard লোড হয়, কিন্তু নতুন বার্তা দেখতে refresh করতে হয়। /cable-এর websocket সংযোগ পার হচ্ছে না, অথবা FRONTEND_URL browser-এর address bar-এ থাকা ঠিকানার সঙ্গে মেলে না। ঠিকানায় অমিল থাকলে page ভিন্ন origin-এ websocket খুলতে চেষ্টা করে। Browser সেটি block করে।
FATAL: password authentication failed for user "postgres"। .env-এ থাকা password এবং Postgres data volume-এ সংরক্ষিত password আলাদা। চলমান container-এর ভিতরে ALTER USER ব্যবহার করে এটি ঠিক করুন। কারণ .env আবার সম্পাদনা করলেও আগে initialize করা database-এর password পরিবর্তন হবে না।
NOAUTH Authentication required.। Redis --requirepass দিয়ে চলছে, কিন্তু application password ছাড়াই সংযোগ করেছে। তাই .env-এ REDIS_PASSWORD অনুপস্থিত, অথবা সেটি কার্যকর হয়নি। docker compose exec redis redis-cli -a "$REDIS_PASSWORD" ping দিয়ে সরাসরি পরীক্ষা করুন। এর উত্তর PONG হওয়া উচিত।
Container code 137 দিয়ে বন্ধ হয়ে যায়। এটি SIGKILL। ছোট server-এ সাধারণত kernel-এর out-of-memory killer এর কারণ। Swap যোগ করুন, প্রতিটি service-এর জন্য memory limit নির্ধারণ করুন, অথবা বড় plan ব্যবহার করুন।
FAQ
একটি self-hosted Chatwoot VPS-এর কত RAM প্রয়োজন?
August 2026 অনুযায়ী, upstream-এর ন্যূনতম প্রয়োজন 4 GB RAM এবং 4 CPU core। এই কনফিগারেশন দিয়ে দিনে সর্বোচ্চ 10,000টি conversation পরিচালনা করা যায়। দিনে সর্বোচ্চ 20,000টি conversation-এর জন্য 8 GB RAM এবং 8 core প্রয়োজন। অন্তত 1 GB swap যোগ করুন। কারণ upgrade-এর সময় migration প্রয়োগ করতে দ্বিতীয় Rails process চালু হয়। ছোট VPS-এ তখন memory শেষ হয়ে যায়। 2 GB VPS boot হয়ে কয়েকজন agent-এর জন্য কাজ করতে পারে। কিন্তু load-এর সময় শুধু Sidekiq-ই 1 GB-এর বেশি memory ব্যবহার করতে পারে। তাই ব্যস্ত সময়ে এবং upgrade চলাকালে container-গুলো exit code 137 দিয়ে বন্ধ হয়ে যেতে পারে।
Chatwoot-এর password reset email কখনও পৌঁছায় না কেন?
কারণ কোনো SMTP setting configured নেই। তাই ActionMailer localhost-এ port 25 ব্যবহার করে email পাঠানোর চেষ্টা করে, কিন্তু container-এর ভিতরে কোনো mail server নেই। Browser তখনও success message দেখালেও Sidekiq-তে job Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 25-সহ ব্যর্থ হয়। .env-এ SMTP_ADDRESS, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD এবং MAILER_SENDER_EMAIL সেট করুন। এরপর rails এবং sidekiq service restart করুন। তারপর reset চালু করার সময় docker compose logs -f sidekiq monitor করুন।
Chatwoot পুনরুদ্ধার করতে কী কী backup নিতে হবে?
Postgres database, storage_data Docker volume, .env file এবং compose file-গুলো backup নিতে হবে। শুধু database যথেষ্ট নয়। Uploaded file-গুলো volume-এ থাকে, আর Postgres-এ থাকে শুধু সেগুলোর reference। তাই শুধু database restore করলে attachment ভাঙা অবস্থায় conversation পাওয়া যাবে। .env গুরুত্বপূর্ণ, কারণ ভিন্ন SECRET_KEY_BASE ব্যবহার করলে সব user sign out হয়ে যায়। ভিন্ন ACTIVE_RECORD_ENCRYPTION_* key ব্যবহার করলে encrypted column পড়া যায় না।
Database নষ্ট না করে Chatwoot কীভাবে upgrade করব?
প্রথমে backup নিন। এরপর compose file-এ image tag পরিবর্তন করুন। তারপর docker compose pull, docker compose down, docker compose run --rm rails bundle exec rails db:chatwoot_prepare এবং docker compose up -d চালান। আগে image pull করুন, কারণ migration নতুন image থেকেই চালাতে হবে। পুরোনো code যেন নতুন schema ব্যবহার না করে, সে জন্য আগে stack বন্ধ করুন; নইলে error হবে। পুরোনো install হলে একবারে একটি minor version করে upgrade করুন। কারণ migration base schema-তে অন্তর্ভুক্ত হয়ে গেলে সেগুলো সরিয়ে ফেলা হয়।
pgvector-এর পরিবর্তে standard postgres image ব্যবহার করা যাবে?
না। Chatwoot-এর schema vector extension enable করে। তাই stock postgres image-এ db:chatwoot_prepare চলার সময় ERROR: extension "vector" is not available error হয়, কারণ ওই image-এ extension-এর control file নেই। Upstream compose file-এর pgvector/pgvector:pg16 ব্যবহার করুন। অথবা আপনার Postgres major version-এর জন্য pgvector সরবরাহ করে এমন অন্য image ব্যবহার করুন।