SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor

راهنمای نصب 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)، اشتراک‌گذاری فایل و تماس‌های صوتی و تصویری را روی سخت‌افزاری که اجاره کرده‌اید و کنترل آن را در دست دارید، ارائه می‌دهد. البته این تنها گزینه معتبر نیست و اگر هنوز در حال انتخاب هستید، مقایسه Mattermost، Rocket.Chat، Synapse و Zulip این گزینه‌ها را بر اساس مواردی که بعداً چالش‌برانگیز می‌شوند بررسی می‌کند: میزان مصرف RAM، پایگاه داده، اعلان‌های موبایل (push)، احراز هویت یکپارچه (SSO) و ارتقاها. این برنامه یک کانتینر واحد است که در عرض چند دقیقه بالا می‌آید. هر مشکلی که واقعاً رخ دهد در پایگاه داده کنار آن نهفته است، بنابراین بخش عمده‌ای از این راهنما به MongoDB اختصاص دارد و به‌ویژه به یک پیش‌نیاز که در اولین تجربه همه را غافلگیر می‌کند: Rocket.Chat روی یک MongoDB مستقل (standalone) اجرا نمی‌شود. این برنامه به یک replica set نیاز دارد، حتی اگر این "مجموعه" فقط شامل یک گره (node) باشد.

پیش‌نیازها و محاسبات رم که کسی به شما نمی‌گوید

سخت‌افزار را واقع‌بینانه انتخاب کنید. حداقل منابع منطقی برای یک تیم کوچک، 2 هسته vCPU و 4 گیگابایت رم است. پردازش Node.js در Rocket.Chat به‌تنهایی حدود 1 تا 1.5 گیگابایت رم نیاز دارد و حافظه کش WiredTiger در MongoDB به‌صورت پیش‌فرض بزرگ‌ترین مقدار بین «50 درصد از (کل رم منهای 1 گیگابایت)» یا 0.256 گیگابایت را اشغال می‌کند. این مقدار پیش‌فرض فرض را بر این می‌گذارد که mongod تمام منابع سرور را در اختیار دارد؛ این محاسبه بر اساس کل رم انجام می‌شود و هیچ توجهی به میزان مصرف فعلی Node ندارد. بنابراین در یک سرور 4 گیگابایتی، 1.5 گیگابایت و در یک سرور 2 گیگابایتی حدود 0.5 گیگابایت رم را اشغال می‌کند که این مقدار علاوه بر heap مربوط به Node، حافظه خارج از کش mongod و سیستم‌عامل است. در یک VPS با 2 گیگابایت رم، این دو سرویس هنگام بوت بالا می‌آیند اما به محض ورود ترافیک واقعی، با هم تداخل پیدا می‌کنند: MongoDB کش خود را بزرگ می‌کند، Node حافظه heap خود را افزایش می‌دهد، کرنل با کمبود صفحه (page) مواجه می‌شود و OOM Killer بزرگ‌ترین پردازش، یعنی معمولاً mongod را می‌کشد. کانتینر پیام Killed را چاپ می‌کند، Docker آن را ری‌استارت می‌کند و شما با یک سرور چت مواجه می‌شوید که زیر بارِ کاریِ عادی، هر چند دقیقه یک‌بار قطع می‌شود. 2 گیگابایت رم فقط برای تست اولیه با 2 نفر مناسب است و برای یک سرور تیمی کافی نیست. با 4 گیگابایت شروع کنید و اگر انتظار ده‌ها کاربر همزمان، تماس‌های ویدیویی یا تاریخچه آپلود رو به رشد دارید، 8 گیگابایت رم در نظر بگیرید. برای سایر سرویس‌هایی که روی سرور اجرا می‌شوند نیز بودجه‌بندی کنید: قرار دادن یک فضای کاری AFFiNE مشابه Notion روی همان VPS، چهار کانتینر دیگر اضافه می‌کند که برای همان صفحات رم رقابت می‌کنند، بنابراین رم آن‌ها باید علاوه بر رم Rocket.Chat تأمین شود.

پیش از شروع، باید سه مورد را آماده کنید. یک نام دامنه با A record که به 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 که با Python نوشته شده است. نسخه مدرن 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 version

چاپ یک خط نسخه توسط docker compose version، بررسی اصلی و مهم است. نسخه‌های فعلی Docker Compose version v5.x را چاپ می‌کنند (نسخه v5.6.0 که در تاریخ 2 October 2026 منتشر شد، تا تاریخ 2026-10-10 آخرین نسخه بوده است)؛ نصب‌های قدیمی‌تر ممکن است همچنان v2.x را چاپ کنند و Docker از هر دو پشتیبانی می‌کند. Docker نسخه Compose v5 را از نظر عملکرد کاملاً مشابه v2 توصیف می‌کند و شماره‌گذاری آن به v5 صرفاً برای جلوگیری از اشتباه با فرمت‌های قدیمی فایل compose یعنی v2 و v3 است، بنابراین هر دو نسخه برای این راهنما مناسب هستند. اگر با خطای 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-loop) بی‌پایان گیر می‌کند. راه‌حل پیچیده نیست: شما یک کانتینر معمولی 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 روی همان سیستم باید به آن دسترسی داشته باشد؛ اتصال آن به تمام اینترفیس‌ها باعث می‌شود صفحه ورود به صورت متن ساده (plaintext) مستقیماً روی اینترنت عمومی قرار بگیرد. MongoDB اصلاً روی هاست منتشر نمی‌شود؛ بلکه فقط از طریق شبکه داخلی Compose با نام mongodb قابل دسترسی است که دقیقاً همان نام هاستی است که MONGO_URL از آن استفاده می‌کند. MONGO_URL شامل ?replicaSet=rs0 است که نام مجموعه را به‌طور صریح مشخص می‌کند؛ MongoDB توصیه می‌کند این مورد را برای رفتار ثابت در درایورهای مختلف تنظیم کنید. حذف آن به تنهایی باعث خرابی نمی‌شود: directConnection به‌طور پیش‌فرض از false استفاده می‌کند، بنابراین درایور همچنان replica set را شناسایی کرده و عملیات را به primary می‌فرستد، به شرطی که مجموعه با نام هاستی که Rocket.Chat قادر به resolve کردن آن است مقداردهی شده باشد، که بخش بعدی به آن می‌پردازد. MONGO_OPLOG_URL به دیتابیس local اشاره دارد که oplog در آن قرار دارد؛ Rocket.Chat مدرن change streams را ترجیح می‌دهد، اما تنظیم این مورد بی‌ضرر است و مسیرهای کد قدیمی‌تر را نیز راضی نگه می‌دارد. depends_on از condition: service_healthy استفاده می‌کند، بنابراین Compose تا زمانی که MongoDB به یک ping پاسخ ندهد، Rocket.Chat را شروع نمی‌کند؛ این همان هدف healthcheck است.

تگ‌های نسخه واقعی را روی هر دو ایمیج پین کنید، مانند mongo:8.0 و یک نسخه صریح از Rocket.Chat مانند 8.5.1 در اینجا، و هرگز از :latest استفاده نکنید، زیرا یک docker pull بدون نظارت را به یک ارتقای تصادفی و غیرقابل‌مهاجرت تبدیل می‌کند. پیش از پین کردن، نسخه پایدار فعلی Rocket.Chat و نسخه‌های MongoDB که پشتیبانی می‌کند را بررسی کنید. Rocket.Chat برای هر نسخه یک سند اطلاعاتی قابل خواندن توسط ماشین منتشر می‌کند: curl -s https://releases.rocket.chat/8.5.1/info | jq '{compatibleMongoVersions, lts}' برای نسخه 8.5.1 مقدار compatibleMongoVersions: ["8.0"] را برمی‌گرداند، بنابراین mongo:8.0 تنها موتور پشتیبانی‌شده است، به علاوه یک فلگ lts که به شما می‌گوید آیا آن نسخه یک build با پشتیبانی بلندمدت (LTS) است که ارزش پین کردن برای سروری که نمی‌خواهید مدام به آن رسیدگی کنید را دارد یا خیر. هر پروژه‌ای ایمیج نسخه‌بندی‌شده منتشر نمی‌کند، در چنین مواردی پین کردن به سورس منتقل می‌شود: self-hosting the openGym workout tracker به معنای checkout کردن یک تگ خاص git و build گرفتن از آن به جای دنبال کردن یک branch متغیر است.

راه‌اندازی اولیه replica set

پشته (stack) را بالا بیاورید:

sudo docker compose up -d

Rocket.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 و Android دیگر اعلان فشاری دریافت نمی‌کنند، زیرا طبق مستندات Rocket.Chat، آن اپلیکیشن‌ها فقط با درگاه ابری کار می‌کنند و درگاه روی محیط کاری ثبت‌نشده اجرا نمی‌شود. راه حل این موضوع، ساخت نسخه اختصاصی (white-labeled) خودتان از اپلیکیشن موبایل است: درگاه را در تنظیمات Push غیرفعال کنید، گواهی‌های APNs و اعتبارنامه‌های Firebase Cloud Messaging خود را وارد کنید و از کاربران بخواهید به جای اپلیکیشن رسمی، اپلیکیشن شما را نصب کنند. اگر حریم خصوصی اولویت اصلی است و کاربران شما از نسخه وب استفاده می‌کنند، یا آمادگی انتشار اپلیکیشن موبایل اختصاصی خود را دارید، حالت مستقل را انتخاب کنید؛ اگر دریافت اعلان روی اپلیکیشن‌های رسمی غیرقابل‌چشم‌پوشی است، گزینه ثبت‌نام را انتخاب کنید. شما می‌توانید بعداً از طریق بخش Admin نظر خود را تغییر دهید.

پیش از دعوت هر کسی، دسترسی‌ها را محدود کنید

Rocket.Chat به‌صورت پیش‌فرض با open registration on عرضه می‌شود؛ یعنی Registration Form روی Public تنظیم شده است و هر کسی که URL را پیدا کند می‌تواند حساب کاربری بسازد. روی یک hostname عمومی، این به معنای باز بودن در است. به مسیر 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 می‌توانید محل ذخیره‌سازی را به فایل‌سیستم محلی یا یک bucket سازگار با S3 تغییر دهید و یک حداکثر حجم معقول برای فایل‌ها تعیین کنید. اگر تیم شما به‌جای اسکرین‌شات‌های گاه‌به‌گاه، کل کتابخانه‌های عکس را جابه‌جا می‌کند، این فایل‌ها باید در یک سرور اختصاصی عکس ذخیره شوند، نه در دیتابیس چت. مطلب مقایسه PhotoPrism و Immich بررسی می‌کند که هر کدام چه میزان RAM مصرف می‌کنند و چه دستورات پشتیبان‌گیری (backup) نیاز دارند. برای یک تیم کوچک، 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) را اجرا می‌کند و مستندات Rocket.Chat تأکید دارند که همیشه باید به‌صورت متوالی بین نسخه‌های اصلی حرکت کنید؛ چرا که پرش از نسخه‌های اصلی می‌تواند باعث مشکلات جدی در سازگاری شود. به این تکیه نکنید که نرم‌افزار جلوی شما را می‌گیرد: هیچ تضمینی در مستندات وجود ندارد که پرش مستقیم از 6.x به 8.x به‌صورت ایمن مسدود شود و اگر مهاجرت در میانه راه شکست بخورد، قفل (lock) باقی می‌ماند و نیاز به تعمیر دستی پیدا می‌کند. تگ ایمیج را به آخرین نسخه از نسخه اصلی بعدی تغییر دهید، یادداشت‌های انتشار آن نسخه را برای تغییرات ساختاری (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() را اجرا نکرده‌اید و مجموعه (set) هنوز هیچ پیکربندی ندارد. getaddrinfo ENOTFOUND که با یک هش تصادفی دنبال می‌شود به این معنی است که شما بدون host: "mongodb:27017" صریح، عملیات را آغاز کرده‌اید؛ بنابراین MongoDB نام میزبان (hostname) کانتینری را تبلیغ کرده که قابل حل (resolvable) نیست. با استفاده از sudo docker compose exec mongodb mongosh --eval 'rs.status()' عیب‌یابی کنید: اگر خطای MongoServerError: no replset config has been received دریافت کردید، مجموعه را initiate کنید؛ اگر عضوی را نشان می‌دهد که name آن یک هش تصادفی است، با استفاده از نام سرویس، عملیات را مجدداً آغاز (re-initiate) کنید.

رابط کاربری وب بارگذاری می‌شود اما فرآیند ورود برای همیشه می‌چرخد و هرگز کامل نمی‌شود. کنسول مرورگر را باز کنید؛ شما 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 GB رم است. به‌عنوان یک راهکار موقت، swap اضافه کنید و کش MongoDB را با --wiredTigerCacheSizeGB در فایل command محدود کنید. این مقدار را کمتر از پیش‌فرض تنظیم کنید، نه بیشتر؛ برای مثال 1 روی یک سرور 4 GB (پیش‌فرض 1.5) یا 0.3 روی یک سرور 2 GB (پیش‌فرض حدود 0.5). توجه داشته باشید که 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 streams در MongoDB ارسال می‌کند و change streams قابلیتی است که فقط در replica set در دسترس است؛ یک نمونه standalone از mongod نمی‌تواند آن را باز کند. نیازی به چندین ماشین ندارید؛ کافی است یک کانتینر MongoDB را با --replSet rs0 اجرا کرده و یک مجموعه تک‌عضوی را با rs.initiate() مقداردهی اولیه کنید. اگر این مرحله را نادیده بگیرید، درایور هرگز یک primary پیدا نمی‌کند و در نتیجه Rocket.Chat با خطای MongoServerSelectionError: Server selection timed out در حلقه راه‌اندازی مجدد گیر کرده و هرگز بالا نمی‌آید.

Rocket.Chat خودمیزبان به چه مقدار رم نیاز دارد؟

حداقل 4 گیگابایت رم را به عنوان مقدار عملیاتی در نظر بگیرید و برای تیم‌های پرکار، 8 گیگابایت پیشنهاد می‌شود. پردازش Node در Rocket.Chat حدود 1 تا 1.5 گیگابایت رم مصرف می‌کند و کش پیش‌فرض WiredTiger در MongoDB برابر است با نصفِ (کل رم منهای 1 گیگابایت)، به‌طوری که انگار هیچ برنامه دیگری روی سرور در حال اجرا نیست. در یک سرور 2 گیگابایتی، این کش، heap مربوط به Node و سربار خودِ mongod با هم تداخل پیدا می‌کنند و OOM 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) ارتقا دهید. این برنامه هنگام بالا آمدن، مهاجرت‌های دیتابیس را اجرا می‌کند و مستندات آن هشدار می‌دهند که پریدن از روی نسخه‌های اصلی می‌تواند باعث مشکلات جدی سازگاری شود. پیش از تغییر تگ image، یادداشت‌های هر نسخه (release notes) را مطالعه کنید. با استفاده از curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions بررسی کنید که نسخه هدف شما از کدام نسخه‌های MongoDB پشتیبانی می‌کند و هنگام ارتقای MongoDB، هر بار یک نسخه اصلی جلو بروید و پس از هر مرحله، setFeatureCompatibilityVersion را با confirm: true تنظیم کنید. همیشه پیش از شروع، یک mongodump تهیه کنید.