راهنمای نصب Rocket.Chat با Docker Compose روی VPS
آموزش گامبهگام راهاندازی Rocket.Chat با Docker Compose. رفع خطای Replica Set در MongoDB، تنظیمات TLS و مدیریت منابع رم برای اجرای پایدار روی سرور شخصی شما.
آنچه در حال ساخت آن هستید
یک سرویس چت تیمی خصوصی که مالکیت کامل آن با شماست: Rocket.Chat که روی VPS شخصی شما و با استفاده از Docker Compose اجرا میشود، با TLS termination محافظت شده و تمامی پیامها در یک دیتابیس MongoDB ذخیره میشوند که میتوانید از آن نسخه پشتیبان تهیه کرده و جابهجا کنید. Rocket.Chat جایگزین متنباز و بالغ برای Slack و Teams است که امکاناتی نظیر کانالها، پیامهای مستقیم، رشتههای گفتگو (threads)، اشتراکگذاری فایل، و تماس صوتی و تصویری را روی سختافزاری که اجاره کردهاید و کنترل آن را در دست دارید، فراهم میکند. این برنامه یک کانتینر واحد است که در عرض چند دقیقه بالا میآید. هر مشکلی که واقعاً رخ میدهد مربوط به دیتابیس کنار آن است؛ بنابراین بخش عمدهای از این راهنما به MongoDB اختصاص دارد و بهویژه به یک پیشنیاز که در ابتدا همه را غافلگیر میکند: Rocket.Chat روی یک MongoDB مستقل (standalone) اجرا نمیشود. این برنامه به یک replica set نیاز دارد، حتی اگر این "مجموعه" فقط شامل یک گره (node) باشد.
پیشنیازها و محاسبات رم که کسی به شما نمیگوید
سختافزار را واقعبینانه انتخاب کنید. حداقلِ منطقی برای یک تیم کوچک، 2 هسته vCPU و 4 گیگابایت رم است. پردازش Node.js در Rocket.Chat بهتنهایی حدود 1 تا 1.5 گیگابایت رم نیاز دارد و کش WiredTiger در MongoDB بهصورت پیشفرض حدود نیمی از رم باقیمانده را اشغال میکند. در یک VPS با 2 گیگابایت رم، این دو سرویس هنگام بالا آمدن سیستم در کنار هم قرار میگیرند، اما بهمحض ورود ترافیک واقعی، با هم تداخل پیدا میکنند: MongoDB کش خود را گسترش میدهد، Node حافظه heap خود را افزایش میدهد، کرنل با کمبود صفحه (page) مواجه میشود و قابلیت out-of-memory killer، بزرگترین پردازش را که معمولاً mongod است، متوقف میکند. کانتینر پیام Killed را چاپ میکند، Docker آن را ریاستارت میکند و شما با یک سرور چت مواجه میشوید که زیر بارِ کاریِ ناچیز، هر چند دقیقه یکبار قطع میشود. 2 گیگابایت رم برای تست اولیه با دو کاربر مناسب است، اما برای یک سرور تیمی کافی نیست. با 4 گیگابایت شروع کنید و اگر انتظار دهها کاربر همزمان، تماسهای ویدیویی یا تاریخچه آپلود رو به رشد دارید، 8 گیگابایت رم اختصاص دهید.
پیش از شروع، باید سه مورد را آماده کنید. یک نام دامنه با رکورد A که به IP عمومی VPS اشاره میکند؛ ویژگیهای بلادرنگ Rocket.Chat و کلاینتهای موبایل به یک نام میزبان (hostname) پایدار نیاز دارند، نه یک IP خام. پورتهای 80 و 443 باید هم در فایروال سرور و هم در فایروال شبکه ارائهدهنده شما باز باشند (که در اکثر پنلها یک تنظیم جداگانه است). همچنین به یک VPS تازه با سیستمعامل Ubuntu 24.04 از نوع KVM با دسترسی root یا sudo نیاز دارید. اگر هنوز در حال تصمیمگیری هستید که آیا سرور چت گزینه مناسبی برای اولین سرویس جهت میزبانی شخصی است یا خیر، راهنمای آنچه ارزش میزبانی شخصی در سال 2026 را دارد، مزایا و معایب آن را بررسی کرده است.
نصب موتور Docker و افزونه Compose
از مخزن apt رسمی Docker استفاده کنید، نه بسته docker.io که در توزیع Ubuntu ارائه میشود و نه فایل باینری قدیمی و مستقل docker-compose که با پایتون نوشته شده است. نسخه مدرن Compose یک افزونه برای Docker است که آن را به صورت docker compose (با فاصله، نه خط تیره) فراخوانی میکنید. نسخه قدیمی docker-compose v1 به پایان عمر خود رسیده است و نحو (syntax) مربوط به healthcheck و وابستگیها را که در ادامه میآید، به درستی پردازش نمیکند.
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginتأیید کنید که هر دو بخش نصب شده باشند:
sudo docker version
sudo docker compose versiondocker compose version که خروجی مشابه Docker Compose version v2.x نمایش میدهد، بررسی اصلی و مهم است. اگر با خطای docker: 'compose' is not a docker command مواجه شدید، یعنی افزونه نصب نشده است و در ادامه با خطاهای گیجکنندهای روبرو خواهید شد؛ پس همینجا آن را اصلاح کنید.
فایل compose: استفاده از MongoDB به عنوان یک replica set تکگره
این بخشی است که افراد در آن دچار اشتباه میشوند، پس آن را با دقت بخوانید. Rocket.Chat از change streams در MongoDB برای ارسال پیامهای جدید به کلاینتهای متصل در لحظه استفاده میکند و change streams تنها در یک replica set در دسترس هستند. اگر Rocket.Chat را به یک mongod ساده و مستقل متصل کنید، ارتباط برقرار میشود اما در باز کردن change stream شکست میخورد و در یک حلقهٔ بیپایان restart گیر میکند. راهحل پیچیده نیست: شما یک کانتینر معمولی MongoDB را اجرا میکنید، اما آن را با --replSet استارت میزنید و سپس یک مجموعهٔ تکعضوی را مقداردهی اولیه میکنید.
یک دایرکتوری کاری و یک compose.yml ایجاد کنید:
services:
mongodb:
image: mongo:8.0
restart: always
command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--oplogSize", "128"]
volumes:
- mongodb_data:/data/db
- mongodb_config:/data/configdb
healthcheck:
test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 10s
retries: 12
rocketchat:
image: registry.rocket.chat/rocketchat/rocket.chat:8.5.1
restart: always
depends_on:
mongodb:
condition: service_healthy
environment:
MONGO_URL: "mongodb://mongodb:27017/rocketchat?replicaSet=rs0"
MONGO_OPLOG_URL: "mongodb://mongodb:27017/local?replicaSet=rs0"
ROOT_URL: "https://chat.example.com"
PORT: "3000"
ports:
- "127.0.0.1:3000:3000"
volumes:
mongodb_data:
mongodb_config:چند انتخاب در اینجا آگاهانه انجام شده است. پورت Rocket.Chat روی 127.0.0.1:3000 منتشر میشود، نه 0.0.0.0؛ خود برنامه فاقد TLS است، بنابراین فقط reverse proxy روی همان سیستم باید به آن دسترسی داشته باشد؛ bind کردن آن روی تمام اینترفیسها باعث میشود صفحهٔ ورود به صورت متن ساده (plaintext) مستقیماً روی اینترنت عمومی قرار بگیرد. MongoDB اصلاً روی هاست منتشر نمیشود؛ این سرویس فقط از طریق شبکهٔ داخلی Compose با نام mongodb در دسترس است که دقیقاً همان نام هاستی است که MONGO_URL از آن استفاده میکند. MONGO_URL شامل ?replicaSet=rs0 است؛ اگر آن را حذف کنید، درایور سرور را به عنوان یک نمونهٔ مستقل (standalone) در نظر میگیرد، حتی اگر یک replica set باشد و در نتیجه change streams همچنان با خطا مواجه میشوند. MONGO_OPLOG_URL به دیتابیس local اشاره میکند که oplog در آن قرار دارد؛ Rocket.Chat مدرن change streams را ترجیح میدهد، اما تنظیم این مورد بیضرر است و مسیرهای کد قدیمیتر را نیز راضی نگه میدارد. depends_on از condition: service_healthy استفاده میکند، بنابراین Compose تا زمانی که MongoDB به یک ping پاسخ ندهد، Rocket.Chat را اجرا نمیکند؛ این همان هدفی است که healthcheck برای آن در نظر گرفته شده است.
روی هر دو image، tagهای نسخهٔ واقعی را ثابت کنید؛ یعنی mongo:8.0 و یک release صریح از Rocket.Chat مانند 8.5.1 را مشخص کنید. هرگز از :latest استفاده نکنید، زیرا این کار یک docker pull بدون نظارت را به upgrade تصادفی و غیرقابلمهاجرت تبدیل میکند. پیش از ثابتکردن نسخه، release پایدار فعلی Rocket.Chat و نسخههای MongoDB سازگار با آن را بررسی کنید. Rocket.Chat برای هر release یک سند اطلاعاتی machine-readable منتشر میکند: curl -s https://releases.rocket.chat/8.5.1/info | jq '{compatibleMongoVersions, lts}' برای 8.5.1 مقدار compatibleMongoVersions: ["8.0"] را برمیگرداند؛ بنابراین mongo:8.0 تنها engine پشتیبانیشده است. این سند همچنین یک flag با نام lts دارد که مشخص میکند آیا آن release یک build با پشتیبانی بلندمدت است یا نه؛ چنین نسخهای برای سروری که نمیخواهید دائماً مراقب آن باشید، گزینهٔ مناسبی برای pin کردن است. همهٔ پروژهها image نسخهگذاریشده منتشر نمیکنند. در این حالت، pin کردن باید روی source انجام شود: self-hosting ردیاب تمرین openGym یعنی یک git tag مشخص را checkout کنید و از همان build بگیرید، نه اینکه یک branch در حال تغییر را دنبال کنید.
راهاندازی اولیه replica set
پشته (stack) را بالا بیاورید:
sudo docker compose up -dRocket.Chat بلافاصله دچار کرش میشود و Docker مدام آن را ریاستارت میکند؛ این رفتار مورد انتظار است، زیرا replica set هنوز وجود ندارد. آن را یکبار بهصورت دستی ایجاد کنید:
sudo docker compose exec mongodb mongosh --eval 'rs.initiate({_id: "rs0", members: [{_id: 0, host: "mongodb:27017"}]})'نتیجه صحیح { ok: 1 } است. ظرف چند ثانیه، تکگره (single node) خود را بهعنوان primary انتخاب میکند؛ این موضوع را با دستور زیر تأیید کنید:
sudo docker compose exec mongodb mongosh --quiet --eval 'rs.status().members[0].stateStr'شما باید PRIMARY را مشاهده کنید. مهمترین جزئیات در کل این صفحه، آرگومان host: "mongodb:27017" است. اگر دستور rs.initiate() را بدون لیست اعضا اجرا کنید، MongoDB نام replica set را با hostname داخلی کانتینر تبلیغ میکند که یک هش تصادفی مانند a1b2c3d4e5f6 است. Rocket.Chat که از کانتینر خودش متصل میشود، نمیتواند این نام را resolve کند، بنابراین درایور MongoDB در DNS با خطا مواجه شده و دائماً پیام MongoServerSelectionError: getaddrinfo ENOTFOUND a1b2c3d4e5f6 را لاگ میکند. همیشه مقداردهی اولیه را با نام سرویس صریحی انجام دهید که با MONGO_URL شما مطابقت دارد.
اولین بوت: نظارت بر بالا آمدن سرویس
هنگامی که مجموعه به عنوان primary تنظیم شد، راهاندازی مجدد Rocket.Chat به درستی متصل شده و مهاجرتهای اولیه (first-run migrations) را آغاز میکند. لاگها را دنبال کنید:
sudo docker compose logs -f rocketchatخطی که باید منتظر آن باشید، بنر شروع به کار است:
+--------------------------------------------+
SERVER RUNNING
Rocket.Chat Version: 8.5.1
NodeJS Version: 22.22.3 - x64
+--------------------------------------------+اولین بوت کند است، زیرا برنامه مهاجرتهای دیتابیس را اجرا کرده و ایندکسها را میسازد؛ بنابراین پیش از نگرانی، یک یا دو دقیقه به آن زمان بدهید. اگر لاگ به جای آن، MongoServerSelectionError: Server selection timed out after 30000 ms را با توصیف توپولوژی از نوع ReplicaSetNoPrimary تکرار میکند، به این معنی است که replica set مقداردهی اولیه نشده است؛ اگر getaddrinfo ENOTFOUND را با یک هش تصادفی تکرار میکند، به این معنی است که با host اشتباه مقداردهی شده است. در هر دو صورت، یک مرحله به عقب بازگردید. هنگامی که SERVER RUNNING را مشاهده کردید، Rocket.Chat روی 127.0.0.1:3000 در حال گوش دادن است و زمان آن رسیده که یک hostname واقعی و TLS را در مقابل آن قرار دهید.
قرار دادن پشت TLS
هرگز Rocket.Chat را روی HTTP ساده در معرض دید قرار ندهید. یک بار ورود از طریق http:// کافی است تا رمز عبور مدیریت خود را به دست هر کسی که در مسیر شبکه قرار دارد، بسپارید. TLS را در یک reverse proxy روی همان سرور خاتمه دهید و درخواستها را به 127.0.0.1:3000 هدایت کنید. دو نکته اهمیت دارد: پروکسی باید هدرهای WebSocket upgrade را فوروارد کند، زیرا Rocket.Chat یک سرویس بلادرنگ است و بدون این هدرها از کار میافتد؛ همچنین ROOT_URL کانتینر باید دقیقاً با آدرس HTTPS عمومی که کاربران وارد میکنند، مطابقت داشته باشد.
با یک بلاک سرور HTTP ساده در nginx شروع کنید که درخواستها را به برنامه پروکسی کرده و هدرهای upgrade را فوروارد میکند. آن را در /etc/nginx/sites-available/rocketchat ذخیره کنید، یک symlink به sites-enabled ایجاد کنید و nginx را reload کنید:
server {
listen 80;
server_name chat.example.com;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}فعلاً آن را روی پورت 80 نگه دارید؛ بلاکی با listen 443 ssl; و بدون گواهی، حتی از sudo nginx -t عبور نخواهد کرد. nginx را reload کنید (sudo nginx -t && sudo systemctl reload nginx) و سپس گواهی را صادر کنید. تمیزترین روش در Ubuntu استفاده از گواهیهای TLS Let's Encrypt با Certbot و nginx است: certbot --nginx بلاک بالا را در محل بازنویسی کرده، listen 443 ssl;، خطوط ssl_certificate و یک تغییر مسیر خودکار از 80 به 443 را اضافه میکند و تمدید گواهی را برای شما زمانبندی میکند. اگر در حال حاضر چندین کانتینر را پشت یک پروکسی اجرا میکنید، Traefik با TLS خودکار برای چندین برنامه Docker گزینه مرتبتری است؛ کافی است برچسبهای router و service را به سرویس rocketchat اضافه کنید تا Traefik درخواست گواهی را انجام داده و آن را برای شما تمدید کند، بدون اینکه نیازی به هیچ بلاک nginx باشد. در هر صورت، ROOT_URL را در compose.yml روی https://chat.example.com تنظیم کنید و sudo docker compose up -d را دوباره اجرا کنید تا کانتینر تغییرات را اعمال کند. اگر میخواهید سرور فقط از داخل شبکه خودتان قابل دسترسی باشد و نه از اینترنت عمومی، آن را پشت یک VPN شخصی WireGuard روی VPS قرار دهید و پروکسی را به آدرس تونل bind کنید.
ویزارد راهاندازی اولیه
به https://chat.example.com بروید تا Rocket.Chat شما را از طریق یک ویزارد کوتاه راهنمایی کند. ابتدا، حساب کاربری مدیر (admin account)، شامل نام واقعی، نام کاربری، ایمیل و یک رمز عبور قوی را وارد کنید؛ این تنها حسابی است که در ابتدا وجود دارد، پس آن را فراموش نکنید. سپس، اطلاعات سازمان و سرور، شامل نام، صنعت، اندازه، نام سایت و زبان پیشفرض را وارد کنید؛ این موارد ظاهری هستند، آنها را پر کنید و ادامه دهید. سپس انتخابی که واقعاً اهمیت دارد: ثبت این فضای کاری (workspace) در Rocket.Chat Cloud یا انتخاب حالت مستقل (standalone).
ثبتنام، امکان دریافت اعلانهای فشاری (push notifications) موبایل را از طریق درگاه Rocket.Chat و دسترسی به بازار افزونهها فراهم میکند، اما در مقابل، یک رابطه کنترلی با فضای ابری Rocket.Chat ایجاد میشود. حالت مستقل، سرور را کاملاً خصوصی و بدون وابستگی نگه میدارد، اما اعلانهای فشاری iOS و اندروید از کار میافتند؛ زیرا اپل و گوگل اجازه نمیدهند یک اپلیکیشن که خودتان ساختهاید گواهیهای push را نگه دارد و اپلیکیشنهای رسمی از طریق درگاه ابری هدایت میشوند. اگر حریم خصوصی اولویت اصلی شماست و کاربران از طریق وباپلیکیشن فعالیت میکنند، حالت مستقل را انتخاب کنید؛ اگر اعلانهای موبایل غیرقابلچشمپوشی هستند، گزینه ثبتنام را انتخاب کنید. شما میتوانید بعداً در بخش Admin نظر خود را تغییر دهید.
پیش از دعوت هر کسی، دسترسیها را محدود کنید
Rocket.Chat بهصورت پیشفرض با ثبتنام باز عرضه میشود؛ فرم ثبتنام (Registration Form) روی Public تنظیم شده است، بنابراین هر کسی که URL را پیدا کند میتواند حساب کاربری بسازد. روی یک نام دامنه عمومی، این به معنای باز گذاشتن در است. به مسیر Admin → Settings → Accounts → Registration بروید و Registration Form را روی Disabled تنظیم کنید تا بتوانید حسابها را بهصورت دستی یا با لینک دعوت بسازید، یا آن را روی Secret URL قرار دهید. در همان بخش، گزینههای Allow Anonymous Read و Allow Anonymous Write را غیرفعال کنید، مگر اینکه بهطور مشخص قصد داشته باشید یک کانال عمومی فقطخواندنی داشته باشید. اگر ایجاد دستی تکتک حسابها برایتان دشوار است و این تنها سرویسی نیست که تیم شما از آن استفاده میکند، احراز هویت Rocket.Chat را به یک سرور SSO خودمیزبان Authentik متصل کنید تا مدیریت ورود و خروج کاربران بهجای انجام در هر برنامه بهصورت جداگانه، یکبار و در یک نقطه متمرکز انجام شود.
همچنین تصمیم بگیرید که فایلهای آپلود شده کجا ذخیره شوند. فضای ذخیرهسازی پیشفرض برای File Upload، سیستم GridFS است که تمام تصاویر و پیوستها را مستقیماً درون خود MongoDB ذخیره میکند. این روش ساده است، اما باعث میشود دیتابیس شما و هر mongodump که تهیه میکنید، با ارسال اسکرینشات توسط کاربران، بدون محدودیت رشد کند. در مسیر Admin → Settings → File Upload میتوانید فضای ذخیرهسازی را به سیستم فایل محلی (local filesystem) یا یک bucket سازگار با S3 تغییر دهید و حداکثر اندازه معقولی برای فایلها تعیین کنید. برای یک تیم کوچک، GridFS مناسب است؛ فقط به یاد داشته باشید که حجم بکآپهای شما بهمرور زمان افزایش مییابد.
تهیه نسخه پشتیبان با mongodump
تمام دادههای شما در volume مربوط به mongodb_data قرار دارد. هرگز volume را در حالی که دیتابیس در حال اجرا است کپی نکنید؛ بلکه یک dump منسجم با استفاده از mongodump تهیه کرده و آن را به صورت stream در فایلی روی host ذخیره کنید:
sudo docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > rocketchat-$(date +%F).archive.gzآن فایل آرشیو فشرده (gzipped)، کل فضای کاری شما شامل کاربران، کانالها، پیامها، تنظیمات و در صورت استفاده از GridFS، فایلهای آپلود شده را در بر میگیرد. اگر فایلهای آپلود شده را به filesystem یا S3 منتقل کردهاید، از آن محل ذخیرهسازی نیز جداگانه نسخه پشتیبان تهیه کنید. برای بازیابی روی یک stack جدید، ابتدا replica set را مقداردهی اولیه کرده و سپس دستور زیر را اجرا کنید:
sudo docker compose exec -T mongodb mongorestore --archive --gzip --drop < rocketchat-2026-07-15.archive.gzآرشیو را از سرور خارج کنید؛ آن را در object storage، یک سرور دیگر یا هر جای امن دیگری که با از کار افتادن VPS از بین نرود، کپی کنید و dump را به صورت شبانه از طریق cron اجرا نمایید. نسخهای که هرگز بازیابی نشده است، تنها یک امید است، نه نسخه پشتیبان؛ بازیابی را یک بار روی یک VPS موقت تمرین کنید تا پیش از نیاز واقعی، از صحت عملکرد آن اطمینان حاصل کنید.
ارتقاها: پین کردن تگها، مطالعه یادداشتها و رعایت ماتریس MongoDB
دو قانون باعث میشود ارتقاها بدون دردسر انجام شوند. اول، Rocket.Chat را هر بار یک نسخه اصلی (Major) ارتقا دهید. این برنامه در زمان بوت، مهاجرتهای اسکیما (schema migrations) را اجرا میکند و عمداً از پرش مستقیم بین نسخههای اصلی خودداری میکند؛ اگر سعی کنید مستقیماً از 6.x به 8.x بروید، برنامه با خطای مهاجرت متوقف میشود تا از آسیب دیدن دادههای شما جلوگیری کند. تگ image را به آخرین نسخه از نسخه اصلی بعدی تغییر دهید، یادداشتهای آن نسخه را برای تغییرات ساختاری (breaking changes) مطالعه کنید، docker compose up -d را اجرا کنید و تا پایان مهاجرت در لاگها، منتظر بمانید. دوم، ماتریس پشتیبانی MongoDB را رعایت کنید. هر نسخه از Rocket.Chat از مجموعه خاصی از نسخههای MongoDB پشتیبانی میکند و curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions به شما میگوید کدام نسخه مناسب است. هنگام ارتقای MongoDB، مثلاً از 7.0 به 8.0، هر بار یک نسخه اصلی جلو بروید و پس از هر مرحله، feature-compatibility version را تنظیم کنید. در MongoDB 8.0، این دستور نیازمند یک confirm: true صریح است، در غیر این صورت با پیامی مواجه میشوید که از شما میخواهد دستور را با فلگ تأیید مجدداً اجرا کنید:
sudo docker compose exec mongodb mongosh --eval 'db.adminCommand({setFeatureCompatibilityVersion: "8.0", confirm: true})'پیش از هر ارتقا در هر یک از این دو مؤلفه، یک mongodump تهیه کنید. این تنها بیمهٔ شما در برابر مشکلات احتمالی است.
حالتهای شکست، همراه با رشتههای دقیق
سرویس Rocket.Chat بلافاصله پس از docker compose up وارد حلقه راهاندازی مجدد (restart-loop) میشود و docker compose logs rocketchat با یک MongoServerSelectionError پر میشود. سرویس MongoDB در حال اجراست اما درایور نمیتواند یک primary انتخاب کند و رشته دقیق، اشتباهی که مرتکب شدهاید را مشخص میکند. خطای Server selection timed out after 30000 ms با نوع توپولوژی ReplicaSetNoPrimary به این معنی است که شما هرگز rs.initiate() را اجرا نکردهاید و مجموعه هنوز هیچ پیکربندی ندارد. خطای getaddrinfo ENOTFOUND که به دنبال آن یک هش تصادفی میآید، به این معنی است که شما بدون تعیین صریح host: "mongodb:27017" اقدام به مقداردهی اولیه کردهاید، بنابراین MongoDB نام میزبان (hostname) کانتینری را تبلیغ کرده که قابل حل (resolvable) نیست. با استفاده از sudo docker compose exec mongodb mongosh --eval 'rs.status()' عیبیابی کنید: اگر خطای MongoServerError: no replset config has been received دریافت کردید، مجموعه را مقداردهی اولیه کنید؛ اگر عضوی را نشان میدهد که name آن یک هش تصادفی است، با استفاده از نام سرویس مجدداً آن را مقداردهی کنید.
رابط کاربری وب بارگذاری میشود اما عملیات ورود برای همیشه در حال چرخش باقی میماند و هرگز کامل نمیشود. کنسول مرورگر را باز کنید تا WebSocket connection to 'wss://chat.example.com/websocket' failed را مشاهده کنید. این مشکل تقریباً همیشه ناشی از عدم تطابق ROOT_URL یا پروکسی است که هدرهای upgrade را فوروارد نمیکند. تأیید کنید که ROOT_URL با آدرس عمومی دقیق شامل https:// برابر است و بلوک location در nginx شما، Upgrade و Connection "upgrade" را با proxy_http_version 1.1 تنظیم میکند. هر کدام را تغییر دهید و سپس docker compose up -d را دوباره اجرا کنید.
یک کانتینر مدام متوقف میشود و docker compose ps نشان میدهد که Restarting. خروجی docker compose logs در میانه خط قطع میشود و sudo dmesg | tail مقدار Out of memory: Killed process 12345 (mongod) را از oom-killer نشان میدهد؛ کد خروج 137 است. سرور با کمبود RAM مواجه است. راهحل واقعی، ارتقای VPS به حداقل 4 گیگابایت رم است. بهعنوان یک راهحل موقت، swap اضافه کنید و کش MongoDB را با --wiredTigerCacheSizeGB 1 در فایل command محدود کنید، اما swap فقط وقوع OOM بعدی را تحت بار واقعی به تأخیر میاندازد:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfileدستور docker compose up با خطای Error response from daemon: driver failed programming external connectivity ... bind: address already in use شکست میخورد. چیزی در حال حاضر پورت 3000 را اشغال کرده است؛ اغلب یک کانتینر Rocket.Chat قبلی که بهدرستی متوقف نشده یا برنامه دیگری است. آن را با sudo ss -ltnp | grep :3000 پیدا کنید، آن پردازش یا کانتینر را متوقف کنید، یا سمت میزبان (host side) نگاشت پورت را به 127.0.0.1:3001:3000 تغییر دهید و proxy_pass پروکسی خود را مطابق با آن بهروزرسانی کنید.
FAQ
آیا Rocket.Chat واقعاً به یک MongoDB replica set نیاز دارد؟
بله، حتی برای یک سرور تکی با یک گره دیتابیس. Rocket.Chat پیامها را بهصورت بلادرنگ با استفاده از change streamهای MongoDB ارسال میکند و change streamها قابلیتی هستند که فقط در replica setها وجود دارند؛ یک mongod مستقل نمیتواند آن را باز کند. نیازی به چندین ماشین ندارید؛ کافی است یک کانتینر MongoDB را با --replSet rs0 اجرا کنید و یک مجموعه تکعضوی را با rs.initiate() مقداردهی اولیه کنید. اگر این مرحله را نادیده بگیرید، درایور هرگز یک primary پیدا نمیکند و در نتیجه Rocket.Chat با MongoServerSelectionError: Server selection timed out در حلقه راهاندازی مجدد گیر میکند و هرگز بوت نمیشود.
Rocket.Chat خودمیزبان به چه مقدار RAM نیاز دارد؟
حداقل 4 گیگابایت را بهعنوان حداقل عملیاتی و 8 گیگابایت را برای یک تیم فعال در نظر بگیرید. فرآیند Node در Rocket.Chat حدود 1 تا 1.5 گیگابایت مصرف میکند و MongoDB تقریباً نیمی از RAM باقیمانده را برای کش WiredTiger خود اشغال میکند؛ بنابراین در یک سرور 2 گیگابایتی، این دو با هم تداخل پیدا میکنند و out-of-memory killer تحت هر بار کاری واقعی، mongod را متوقف میکند که در لاگها با Killed و کد خروج 137 نمایش داده میشود. 2 گیگابایت فقط برای ارزیابی نرمافزار با چند کاربر آزمایشی کافی است.
چگونه Rocket.Chat را پشت HTTPS قرار دهم؟
یک reverse proxy روی همان VPS اجرا کنید که TLS را خاتمه دهد و درخواستها را به 127.0.0.1:3000 هدایت کند، سپس ROOT_URL کانتینر را روی آدرس https:// عمومی خود تنظیم کنید. پروکسی باید هدرهای WebSocket upgrade را فوروارد کند، در غیر این صورت فرآیند ورود متوقف میشود. استفاده از Certbot به همراه nginx سادهترین تنظیم برای یک برنامه تکی است؛ اگر چندین کانتینر را پشت یک پروکسی اجرا میکنید و مدیریت خودکار گواهی میخواهید، Traefik گزینه تمیزتری است.
چگونه از یک Rocket.Chat خودمیزبان نسخه پشتیبان تهیه کنم؟
بهجای کپی کردن volume، یک dump منسجم از دیتابیس با mongodump بگیرید: docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > backup.archive.gz. آن آرشیو شامل کاربران، کانالها، پیامها و تنظیمات است، بهعلاوه فایلهای آپلود شده اگر ذخیرهسازی را روی GridFS باقی گذاشته باشید. آن را از سرور خارج کنید، با cron بهصورت شبانه خودکار کنید و یک mongorestore را روی یک سرور موقت تمرین کنید تا مطمئن شوید که بازیابی واقعاً کار میکند.
چگونه Rocket.Chat را بدون آسیب زدن به MongoDB ارتقا دهم؟
Rocket.Chat را هر بار یک نسخه اصلی (major version) ارتقا دهید؛ این برنامه هنگام بوت، مهاجرتها (migrations) را اجرا میکند و از پرش بین نسخههای اصلی خودداری میکند. پیش از تغییر تگ image پینشده، یادداشتهای هر نسخه را بخوانید. با curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions بررسی کنید که نسخه هدف شما از کدام نسخههای MongoDB پشتیبانی میکند و هنگام ارتقای MongoDB، هر بار یک نسخه اصلی جلو بروید و پس از هر مرحله، setFeatureCompatibilityVersion را با confirm: true تنظیم کنید. همیشه ابتدا یک mongodump تهیه کنید.