استضافة Rocket.Chat ذاتياً باستخدام Docker Compose
شغّل Rocket.Chat على VPS مع Docker Compose، واضبط MongoDB كـ replica set من عقدة واحدة، ثم أضف TLS والنسخ الاحتياطية وحلول خطأ كل مشكلة شائعة.
ما الذي ستبنيه
دردشة خاصة لفريقك تملكها بالكامل: ستشغّل Rocket.Chat على VPS خاص بك باستخدام Docker Compose، مع إنهاء TLS، بينما تُخزَّن كل رسالة في قاعدة بيانات MongoDB يمكنك نسخها احتياطياً ونقلها. يُعد Rocket.Chat بديلاً ناضجاً ومفتوح المصدر لـSlack وTeams، ويوفّر القنوات والرسائل المباشرة وسلاسل المحادثات ومشاركة الملفات والمكالمات الصوتية والمرئية، وكل ذلك على عتاد تستأجره وتتحكم فيه. لكنه ليس الخيار الموثوق الوحيد، وإذا كنت لا تزال تختار، فستعرض لك مقارنة بين Mattermost وRocket.Chat وSynapse وZulip الفروق في الجوانب التي تسبب المشكلات لاحقاً: ذاكرة RAM وقاعدة البيانات وإشعارات الدفع على الأجهزة المحمولة وSSO والترقيات. التطبيق عبارة عن حاوية واحدة تبدأ خلال دقائق. وكل ما يتعطل فعلياً يوجد في قاعدة البيانات المجاورة لها، لذلك يتناول معظم هذا الدليل MongoDB، وبالأخص المتطلب الوحيد الذي يفاجئ الجميع في المرة الأولى: لن يعمل Rocket.Chat مع MongoDB مستقل. فهو يحتاج إلى replica set، حتى إذا كان هذا "set" يتكون من عقدة واحدة.
المتطلبات الأساسية، وحساب الذاكرة العشوائية الذي لا يخبرك به أحد
حدّد مواصفات الخادم بواقعية. الحد الأدنى العملي لفريق صغير هو 2 vCPU و4 GB من RAM. تحتاج عملية Node.js الخاصة بـRocket.Chat إلى نحو 1 إلى 1.5 GB بمفردها، بينما تحجز ذاكرة WiredTiger المؤقتة في MongoDB افتراضياً نحو نصف RAM المتبقية. في VPS بسعة 2 GB، يتسع الاثنان عند الإقلاع، ثم يتصادمان فور وصول حركة مرور فعلية: تكبّر MongoDB ذاكرتها المؤقتة، وتكبّر Node مساحة heap، وتن5فد صفحات الذاكرة من kernel، ثم يقتل مزيل العمليات عند نفاد الذاكرة العملية الأكبر، وعادةً mongod. تطبع الحاوية Killed، ويعيد Docker تشغيلها، فتحصل على خادم محادثة ينقطع كل بضع دقائق تحت حمل كان ينبغي أن يتعامل معه بسهولة. تكفي سعة 2 GB لتجربة الخادم مع شخصين، لكنها لا تكفي لخادم فريق. ابدأ بسعة 4 GB، واستخدم 8 GB إذا توقعت عشرات المستخدمين المتزامنين، أو مكالمات فيديو، أو سجلاً متزايداً للملفات المرفوعة. احسب أيضاً احتياجات أي خدمات أخرى سيشغّلها الخادم: إن وضعت مساحة عمل AFFiNE بأسلوب Notion على VPS نفسه، فستضيف أربع حاويات أخرى تتنافس على صفحات الذاكرة نفسها، لذلك يجب إضافة ذاكرتها إلى ذاكرة Rocket.Chat، لا اقتطاعها منها.
تحتاج أيضاً إلى تجهيز ثلاثة أمور قبل البدء. اسم نطاق يتضمن A record يشير إلى عنوان IP العام لـVPS، لأن ميزات الوقت الفعلي في Rocket.Chat وتطبيقات الهاتف تحتاج إلى اسم مضيف ثابت، لا إلى عنوان IP مجرد. يجب فتح المنفذين 80 و443 في جدار حماية الخادم وفي جدار حماية شبكة مزوّد الخدمة، وهو عنصر تحكم منفصل في معظم لوحات الإدارة. وتحتاج إلى VPS جديد يعمل بنظام Ubuntu 24.04 ويدعم KVM، مع حساب root أو sudo. إذا كنت لا تزال تقرر ما إذا كان خادم المحادثة هو الخدمة الأولى المناسبة للتشغيل، فسيعرض الدليل إلى الخدمات المجدية للاستضافة الذاتية في 2026 المفاضلات.
ثبّت محرك Docker والمكوّن الإضافي Compose
استخدم مستودع apt الخاص بـDocker، وليس حزمة docker.io التي توفّرها Ubuntu، ولا ملف Python المستقل والقديم docker-compose. إن Compose الحديث هو مكوّن إضافي لـDocker تستدعيه باستخدام docker compose، مع مسافة وليس واصلة. الإصدار القديم docker-compose v1 انتهى عمره الافتراضي، ولا يتعامل بصورة صحيحة مع صياغة 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 v2.x بواسطة docker compose version هي الاختبار المهم. إذا ظهر الخطأ docker: 'compose' is not a docker command، فهذا يعني أن المكوّن الإضافي لم يُثبّت، وستواجه لاحقاً أخطاء مربكة. أصلح المشكلة الآن.
ملف Compose: MongoDB كـ replica set بعقدة واحدة
هذا هو الجزء الذي يخطئ فيه كثيرون، لذلك اقرأه ببطء. يستخدم Rocket.Chat change streams لدفع الرسائل الجديدة إلى العملاء المتصلين في الوقت الفعلي، ولا تتوفر change streams إلا على replica set. وجّه Rocket.Chat إلى mongod مستقل عادي، وسيتصل به ثم يفشل في فتح change stream ويدخل في حلقة إعادة تشغيل لا تنتهي. الإصلاح ليس معقداً: شغّل حاوية 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 الموجود على الخادم نفسه؛ إذ إن ربطه بكل الواجهات سيعرض صفحة تسجيل دخول غير مشفّرة مباشرةً على الإنترنت العام. لا يُنشر MongoDB إلى المضيف إطلاقاً؛ ويمكن الوصول إليه فقط عبر شبكة Compose الداخلية باستخدام الاسم mongodb، وهو اسم المضيف الذي يستخدمه MONGO_URL تحديداً. يحمّل MONGO_URL القيمة ?replicaSet=rs0؛ إذا حذفت هذا الإعداد، فسيتعامل برنامج التشغيل مع الخادم على أنه مستقل، رغم أنه replica set، وستظل change streams تفشل. يشير 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}' القيمة compatibleMongoVersions: ["8.0"] للإصدار 8.5.1، ولذلك فإن mongo:8.0 هو محرك قاعدة البيانات المدعوم الوحيد، إضافةً إلى علامة lts التي توضّح ما إذا كان الإصدار إصدار دعم طويل الأمد يستحق التثبيت لخادم لا تريد مراقبته باستمرار. لا ينشر كل مشروع صورة مقيّدة بإصدار، وفي هذه الحالة ينتقل التثبيت إلى المصدر: الاستضافة الذاتية لمتتبّع التمارين openGym تعني سحب وسم git محدداً وبناء المشروع منه بدلاً من متابعة فرع متغير.
تهيئة مجموعة النسخ المتماثلة
شغّل المكدس:
sudo docker compose up -dستبدأ Rocket.Chat في التعطل فوراً، وسيواصل Docker إعادة تشغيلها. هذا متوقع لأن مجموعة النسخ المتماثلة غير موجودة بعد. أنشئها مرة واحدة يدوياً:
sudo docker compose exec mongodb mongosh --eval 'rs.initiate({_id: "rs0", members: [{_id: 0, host: "mongodb:27017"}]})'النتيجة الصحيحة هي { ok: 1 }. خلال بضع ثوانٍ، تعيّن العقدة الوحيدة نفسها كعقدة أساسية. أكّد ذلك باستخدام:
sudo docker compose exec mongodb mongosh --quiet --eval 'rs.status().members[0].stateStr'يجب أن ترى PRIMARY. أهم تفصيل في هذه الصفحة هو الوسيطة host: "mongodb:27017". إذا شغّلت rs.initiate() مجردة من دون قائمة أعضاء، يعلن MongoDB عن مجموعة النسخ المتماثلة باستخدام اسم المضيف الداخلي للحاوية، وهو تجزئة عشوائية مثل a1b2c3d4e5f6. لا يستطيع Rocket.Chat، عند اتصاله من حاويته الخاصة، تحليل اسم المضيف هذا، لذلك يفشل برنامج تشغيل MongoDB في DNS عند محاولة الوصول إليه، ويستمر في تسجيل MongoServerSelectionError: getaddrinfo ENOTFOUND a1b2c3d4e5f6 إلى ما لا نهاية. ابدأ دائماً باستخدام اسم الخدمة الصريح المطابق لـMONGO_URL.
الإقلاع الأول: راقب بدء التشغيل
بعد أن تصبح المجموعة أساسية، يتصل Rocket.Chat بنجاح عند إعادة التشغيل التالية ويبدأ عمليات الترحيل الأولى. راقب السجلات:
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، فهذا يعني أن مجموعة النسخ المتماثلة لم تتم تهيئتها. وإذا كرر getaddrinfo ENOTFOUND مع تجزئة عشوائية، فهذا يعني أنها هُيئت باسم مضيف غير صحيح. في كلتا الحالتين، ارجع خطوة واحدة. بعد ظهور SERVER RUNNING، سيستمع Rocket.Chat على 127.0.0.1:3000، وحينها يحين وقت وضع اسم مضيف فعلي وTLS أمامه.
ضعه خلف TLS
لا تعرّض Rocket.Chat مطلقاً عبر HTTP العادي. إذا سجّلت الدخول عبر http:// مرة واحدة، فقد سلّمت كلمة مرور المسؤول إلى أي جهة يمكنها اعتراض المسار. أنهِ TLS في reverse proxy على الخادم نفسه، ثم وجّه الطلبات إلى 127.0.0.1:3000. هناك أمران مهمان: يجب أن يمرّر الـproxy رؤوس ترقية WebSocket، لأن Rocket.Chat يعمل في الوقت الفعلي ويتعطل من دونها، كما يجب أن يطابق ROOT_URL في الحاوية عنوان HTTPS العام الذي يدخله المستخدمون حرفياً.
ابدأ بكتلة خادم nginx تستخدم HTTP العادي، وتوجّه الطلبات إلى التطبيق، وتمرّر رؤوس الترقية. احفظها باسم /etc/nginx/sites-available/rocketchat، وأنشئ رابطاً رمزياً لها داخل sites-enabled، ثم أعد تحميل nginx:
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 باستخدام sudo nginx -t && sudo systemctl reload nginx، ثم أصدر الشهادة. المسار الأنظف على Ubuntu هو شهادات TLS من Let's Encrypt باستخدام Certbot وnginx: يعيد certbot --nginx كتابة الكتلة السابقة في مكانها، ويضيف listen 443 ssl; وأسطر ssl_certificate، ويعدّ تحويلاً تلقائياً من 80 إلى 443، كما يضبط التجديد نيابةً عنك. إذا كنت تشغّل عدة حاويات خلف proxy واحد، فخيار Traefik مع TLS تلقائي لعدة تطبيقات Docker أكثر ترتيباً. أضف تسميات router وservice إلى خدمة rocketchat، وسيتولى Traefik طلب الشهادة وتجديدها نيابةً عنك، من دون أي كتلة nginx. في كلتا الحالتين، اضبط ROOT_URL على https://chat.example.com في compose.yml، ثم أعد تشغيل sudo docker compose up -d لكي تلتقط الحاوية التغيير. إذا أردت أن يكون الخادم قابلاً للوصول من داخل شبكتك فقط، وليس من الإنترنت العام، فضع أمامه شبكة WireGuard VPN مستضافة ذاتياً على VPS، واربط الـproxy بعنوان النفق.
معالج الإعداد عند التشغيل الأول
انتقل إلى https://chat.example.com، وسيقودك Rocket.Chat خلال معالج إعداد قصير. أولاً، أدخل حساب المسؤول والاسم الحقيقي واسم المستخدم والبريد الإلكتروني وكلمة مرور قوية. هذا هو الحساب الوحيد الموجود، لذلك لا تفقد بياناته. بعد ذلك، أدخل معلومات المؤسسة والخادم، مثل الاسم والقطاع والحجم واسم الموقع واللغة الافتراضية. هذه معلومات شكلية؛ املأها وتابع. ثم اختر الخيار المهم فعلياً: تسجيل مساحة العمل لدى Rocket.Chat Cloud، أو إبقاؤها مستقلة.
يتيح التسجيل إشعارات الدفع على الأجهزة المحمولة عبر بوابة Rocket.Chat، وسوق الإضافات، لكنه ينشئ علاقة مع مستوى التحكم السحابي لدى Rocket.Chat. يحافظ الوضع المستقل على خصوصية الخادم بالكامل ومن دون تبعيات، لكن إشعارات الدفع على iOS وAndroid تتوقف عن العمل، لأن Apple وGoogle لا تسمحان لتطبيق مبني ذاتياً باستخدام شهادات الدفع. أما التطبيقات الرسمية فتمر عبر البوابة السحابية. اختر الوضع المستقل إذا كانت الخصوصية هي الهدف الأساسي وكان المستخدمون يعتمدون على تطبيق الويب؛ واختر التسجيل إذا كانت إشعارات الدفع على الأجهزة المحمولة ضرورية. يمكنك تغيير هذا الاختيار لاحقاً من قسم Admin.
أمّنه قبل دعوة أي شخص
يأتي Rocket.Chat مع تفعيل التسجيل المفتوح افتراضياً، ويكون نموذج التسجيل مضبوطاً على Public، لذلك يمكن لأي شخص يعثر على عنوان URL إنشاء حساب. وعلى اسم مضيف عام، يُعد ذلك منفذاً مفتوحاً. انتقل إلى Admin → Settings → Accounts → Registration واضبط Registration Form على Disabled، حتى تنشئ الحسابات يدوياً أو عبر رابط دعوة، أو على Secret URL. وأثناء ذلك، عطّل Allow Anonymous Read وAllow Anonymous Write، إلا إذا كنت تريد تحديداً قناة عامة للقراءة فقط. إذا بدا إنشاء كل حساب يدوياً مرهقاً، ولم تكن هذه الخدمة الوحيدة التي يسجّل فريقك الدخول إليها، فاضبط تسجيل الدخول عبر OAuth في Rocket.Chat لاستخدام خادم Authentik SSO مستضاف ذاتياً بدلاً من ذلك، حتى تتولى إضافة المستخدمين وإزالتهم مرة واحدة وفي مكان واحد، لا تطبيقاً بعد تطبيق.
حدّد أيضاً المكان الذي ستُخزّن فيه الملفات المرفوعة. تخزين File Upload الافتراضي هو GridFS، الذي يخزّن كل صورة ومرفق داخل MongoDB نفسه. هذا بسيط، لكنه يعني أن قاعدة بياناتك، وكل mongodump تنشئه، سيزداد حجمه بلا حدود مع استمرار الأشخاص في لصق لقطات الشاشة. ضمن Admin → Settings → File Upload، يمكنك تبديل التخزين إلى نظام الملفات المحلي أو حاوية متوافقة مع S3، وتعيين حداً أقصى مناسباً لحجم الملف. إذا كان فريقك يتبادل مكتبات صور كاملة بدلاً من لقطات الشاشة العرضية، فمن الأفضل تخزينها في خادم صور مخصص لا في قاعدة بيانات دردشة، وتوضح مقارنة PhotoPrism وImmich تكلفة كل منهما من الذاكرة مقابل أوامر النسخ الاحتياطي التي يحتاج إليها. بالنسبة إلى فريق صغير، يُعد GridFS مناسباً؛ لكن تذكّر أن نسخك الاحتياطية ستصبح أكبر بمرور الوقت.
النسخ الاحتياطية باستخدام mongodump
توجد جميع بياناتك في وحدة التخزين mongodb_data. لا تنسخ وحدة التخزين مباشرةً أثناء تشغيل قاعدة البيانات. أنشئ تفريغاً متسقاً باستخدام mongodump، وابثثه إلى ملف على الخادم المضيف:
sudo docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > rocketchat-$(date +%F).archive.gzهذا الأرشيف المضغوط باستخدام gzip هو مساحة العمل الكاملة: المستخدمون والقنوات والرسائل والإعدادات، والملفات أيضاً إذا أبقيت عمليات الرفع على GridFS. إذا نقلت عمليات الرفع إلى نظام الملفات أو S3، فانسخ مخزنها احتياطياً بشكل منفصل. لاستعادة البيانات على مكدس جديد، هيّئ مجموعة النسخ المتماثلة أولاً، ثم:
sudo docker compose exec -T mongodb mongorestore --archive --gzip --drop < rocketchat-2026-07-15.archive.gzانسخ الأرشيف خارج الخادم إلى تخزين الكائنات أو خادم آخر أو أي مكان لا يؤدي فيه تعطل VPS إلى فقدان النسخة الاحتياطية معه، وشغّل التفريغ من cron كل ليلة. النسخة الاحتياطية التي لم تستعدها من قبل ليست نسخة احتياطية، بل مجرد أمل. طبّق الاستعادة مرة على VPS مؤقت للتأكد من أنها تعمل قبل أن تحتاج إليها.
الترقيات: ثبّت الوسوم، واقرأ الملاحظات، والتزم بمصفوفة دعم MongoDB
تجعل قاعدتان الترقيات عملية روتينية. أولاً، رقِّ Rocket.Chat إصداراً رئيسياً واحداً في كل مرة. ينفّذ عمليات ترحيل المخطط عند الإقلاع، ويرفض عمداً تخطي الإصدارات الرئيسية؛ فإذا حاولت الانتقال مباشرةً من 6.x إلى 8.x، فسيتوقف بخطأ ترحيل بدلاً من إتلاف بياناتك. حدّث وسم الصورة إلى أحدث إصدار من الإصدار الرئيسي التالي، واقرأ ملاحظات ذلك الإصدار لمعرفة التغييرات التي قد تكسر التوافق، ثم شغّل docker compose up -d وراقب السجلات حتى تكتمل عملية الترحيل قبل المتابعة. ثانياً، التزم بمصفوفة دعم MongoDB. يدعم كل إصدار من Rocket.Chat مجموعة محددة من إصدارات MongoDB، ويبيّن لك curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions الإصدارات المناسبة. عند ترقية MongoDB، مثلاً من 7.0 إلى 8.0، انتقل بمقدار إصدار رئيسي واحد في كل مرة، واضبط إصدار توافق الميزات بعد كل انتقال. في 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، ويمتلئ docker compose logs rocketchat بـMongoServerSelectionError. يعمل MongoDB، لكن برنامج التشغيل لا يستطيع اختيار عقدة أساسية، وتوضح السلسلة النصية الدقيقة الخطأ الذي ارتكبته. يعني Server selection timed out after 30000 ms مع نوع طوبولوجيا هو ReplicaSetNoPrimary أنك لم تشغّل rs.initiate()، ولذلك لا تحتوي المجموعة على إعداد بعد. أما getaddrinfo ENOTFOUND متبوعاً بتجزئة عشوائية، فيعني أنك بدأت التشغيل من دون host: "mongodb:27017" الصريح، ولذلك أعلن MongoDB عن اسم مضيف للحاوية لا يمكن حله. شخّص المشكلة باستخدام 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 أو وجود وكيل لا يمرر رؤوس الترقية. تأكد من أن 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 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، ثم أوقف العملية أو الحاوية، أو غيّر جانب المضيف من الربط إلى 127.0.0.1:3001:3000، وحدّث proxy_pass في الوكيل العكسي ليتطابق معه.
FAQ
هل يحتاج Rocket.Chat فعلاً إلى replica set في MongoDB؟
نعم، حتى على خادم واحد يضم عقدة قاعدة بيانات واحدة. يرسل Rocket.Chat الرسائل في الوقت الفعلي باستخدام change streams في MongoDB. وهذه الميزة متاحة فقط في replica set، ولا يستطيع mongod المستقل فتحها. لا تحتاج إلى عدة أجهزة. شغّل حاوية MongoDB واحدة باستخدام --replSet rs0، ثم أنشئ مجموعة تضم عضواً واحداً باستخدام rs.initiate(). إذا تجاوزت هذه الخطوة، فلن يعثر برنامج التشغيل على primary. لذلك يدخل Rocket.Chat في حلقة إعادة تشغيل باستخدام MongoServerSelectionError: Server selection timed out، ولا يكتمل إقلاعه.
ما مقدار RAM الذي يحتاج إليه Rocket.Chat المستضاف ذاتياً؟
خطط لتوفير 4 GB كحد أدنى عملي، و8 GB لفريق نشط. تستخدم عملية Node في Rocket.Chat نحو 1 إلى 1.5 GB. وتحجز MongoDB نحو نصف RAM المتبقية لذاكرة التخزين المؤقت WiredTiger. لذلك تتنافس العمليتان على الذاكرة في خادم بسعة 2 GB، وينهي قاتل نفاد الذاكرة mongod عند أي حمل فعلي. ستظهر Killed في السجلات، مع رمز الخروج 137. تكفي 2 GB فقط لتقييم البرنامج باستخدام بضعة مستخدمين تجريبيين.
كيف أضع Rocket.Chat خلف HTTPS؟
شغّل reverse proxy على VPS نفسه لإنهاء TLS وإعادة التوجيه إلى 127.0.0.1:3000، واضبط ROOT_URL في الحاوية على عنوان https:// العام. يجب أن يعيد الـproxy توجيه رؤوس ترقية WebSocket، وإلا فستتوقف عملية تسجيل الدخول. يُعد Certbot مع nginx أبسط إعداد لتطبيق واحد. ويكون Traefik أنسب إذا كنت تشغّل عدة حاويات خلف proxy واحد وتريد إدارة الشهادات تلقائياً.
كيف أنشئ نسخة احتياطية من Rocket.Chat المستضاف ذاتياً؟
أنشئ تفريغاً متسقاً لقاعدة البيانات باستخدام mongodump بدلاً من نسخ volume: docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > backup.archive.gz. يحتوي هذا الأرشيف على المستخدمين والقنوات والرسائل والإعدادات، إضافة إلى الملفات المرفوعة إذا أبقيت التخزين على GridFS. انسخه خارج الخادم، وأتمت إنشاءه كل ليلة باستخدام cron، وجرّب mongorestore على خادم مؤقت حتى تتأكد من أن الاستعادة تعمل فعلاً.
كيف أرقّي Rocket.Chat من دون تعطيل MongoDB؟
رقِّ Rocket.Chat إصداراً رئيسياً واحداً في كل مرة. فهو يشغّل عمليات الترحيل عند الإقلاع، ويرفض تجاوز الإصدارات الرئيسية. اقرأ ملاحظات كل إصدار قبل تغيير وسم الصورة المثبّت. تحقق من إصدارات MongoDB التي يدعمها الإصدار المستهدف باستخدام curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions. وعند ترقية MongoDB، انتقل إصداراً رئيسياً واحداً في كل مرة، واضبط setFeatureCompatibilityVersion باستخدام confirm: true بعد كل انتقال. أنشئ دائماً mongodump أولاً.