لماذا تنشر Node.js على VPS بدلًا من منصة مُدارة؟
منصات serverless وPaaS عملية في البداية، لكن كلفتها ترتفع مع حجم الزيارات، وهي تفرض قيودًا مثل المهل الزمنية (timeouts) والإقلاع البارد (cold starts) وحدود اتصالات WebSocket. يمنحك VPS عملية Node.js دائمة، واتصالات طويلة (WebSocket وSSE) بلا حدود مصطنعة، وموارد مخصّصة، وكلفة ثابتة يمكن توقّعها. أنت من يختار إصدار Node ومدير العمليات والوكيل العكسي واستراتيجية التحديث، دون الارتهان لأعراف مزوّد بعينه.
يتيح النموذج غير المتزامن وغير الحاجب في Node.js لعملية واحدة أن تتعامل مع عدد كبير من الاتصالات المتزامنة، وهذا ما يجعله خيارًا مناسبًا لواجهات REST والتطبيقات الآنية والخدمات المصغّرة، بشرط أن تبقى العملية حيّة. وهذا بالضبط ما ترفضه أغلب الاستضافات المشتركة حين تمنع العمليات الدائمة أو WebSocket. على VPS لا تُشارَك اعتماديات npm ولا متغيّرات البيئة ولا إعدادات PM2 مع أيّ حساب آخر.
ما الذي تكسبه مع VPS لتشغيل Node.js
- عملية Node.js دائمة، مثالية لـ WebSocket وSSE والمهام الطويلة دون إقلاع بارد
- إعادة تشغيل تلقائية بعد الانهيار وعند إعادة إقلاع الخادم بفضل PM2
- وضع cluster لاستغلال كل أنوية المعالج في VPS دون تعديل شيفرتك
- وكيل عكسي Nginx أمام Node لتولّي HTTPS والضغط والملفات الثابتة
- عمّال طوابير (BullMQ وAgenda) ومهام مجدولة تعمل بجوار الواجهة البرمجية على الجهاز نفسه
- عدّة تطبيقات على VPS واحد، لكلّ منها منفذ داخلي (3000 و3001…) ونطاق فرعي خلف Nginx
- تصيير من جهة الخادم لأطر مثل Nuxt أو Next.js، يقدّمه الأساس نفسه من Node.js وPM2
- كلفة ثابتة يمكن توقّعها، بلا فوترة لكل طلب ولا مفاجآت عند التوسّع
- تحكّم كامل في إصدار Node.js واعتماديات النظام
المتطلبات المسبقة
يعمل تطبيق Node.js قياسي (واجهة Express أو Nest أو Fastify) براحة على VPS بمعالج أو معالجين افتراضيين و1 إلى 2 غيغابايت من الذاكرة؛ خطّط لذاكرة أكبر إن كنت تبني واجهة أمامية ثقيلة أثناء البناء أو تشغّل عمّالًا. استهدف 2 vCPU و2 غيغابايت حين تتقاسم الواجهة البرمجية الجهاز مع تصيير SSR أو قاعدة PostgreSQL.
اختر خطّ LTS ما زال مدعومًا: لم يعد Node.js 20 يتلقّى إصلاحات منذ 30 أبريل 2026، وانتقل Node.js 24 إلى مرحلة الصيانة في 20 أكتوبر 2026، وصار Node.js 26 هو إصدار LTS النشط منذ 28 أكتوبر 2026. ثبّته عبر nvm أو مستودع NodeSource لتثبيت الإصدار، مع PM2 مديرًا للعمليات وNginx وكيلًا عكسيًا. تحتاج إلى نطاق يشير إلى عنوان IP الخاص بـ VPS وإلى فتح المنفذين 80/443.
الانطلاق من قالب Node.js Stack
يجهّز قالب Node.js Stack في Marketplace الخاص بـ ServOrbit خادم VPS بنظام Ubuntu 24.04 مع Node.js من خطّ LTS الحالي في مستودع NodeSource، وnpm، وPM2 مثبّتًا على مستوى النظام، وNginx، وPostgreSQL. يعفيك القالب من الخطوة الأولى أدناه، ويبقى باقي الدليل صالحًا كما هو.
ليس هذا تطبيقًا بشاشة تسجيل دخول بل بيئة تشغيل: تدخلها عبر SSH (ssh root@<vps-ip>) لرفع شيفرتك. عند الدخول الأول يعرض Nginx صفحة الترحيب الافتراضية على المنفذ 80، لأنه لم يُكتب بعد أيّ virtual host لنطاقك، كما أن Certbot غير مثبّت: تبقى خطوتا Nginx وHTTPS عليك. لا يستمع PostgreSQL إلا محليًا، ويصادق على مستخدم النظام postgres بطريقة peer: يفتح الأمر sudo -u postgres psql الطرفية دون كلمة مرور.
نشر تطبيق Node.js في الإنتاج
تجهيز VPS وتثبيت Node.js
على VPS بنظام Ubuntu 24.04، حدّث النظام وثبّت إصدار LTS من Node عبر nvm (
nvm install --lts) أو مستودع NodeSource. تحقّق بالأمرينnode -vوnpm -v. أنشئ مستخدمًا غير root مخصّصًا للتطبيق بدل تشغيل كل شيء بصلاحيات root: هذا المستخدم هو من سيملك الشيفرة وملف.envوعمليات PM2. مع قالب Node.js Stack تكون Node.js وnpm وPM2 مثبّتة مسبقًا، فلا يبقى إلا إنشاء المستخدم.جلب الشيفرة والبناء
استنسخ مستودعك على VPS، وثبّت اعتماديات الإنتاج (
npm ci --omit=dev)، ثم شغّل البناء إن لزم (npm run build).ضع المتغيّرات الحسّاسة في ملف
.envغير مُودَع في Git، لا يقرؤه إلا مستخدم التطبيق (chmod 600 .env). لا حاجة إلى مكتبة لقراءته: منذ Node.js 20.6 يحمّل الخيار--env-fileهذا الملف فيprocess.envعند الإقلاع.تجهيز قاعدة PostgreSQL
إن احتاج التطبيق إلى قاعدة بيانات، فأنشئ دورًا وقاعدة مخصّصين بدل العمل بالمستخدم الخارق:
sudo -u postgres createuser --pwprompt monapp sudo -u postgres createdb --owner=monapp monappثم اضبط
DATABASE_URL=postgres://monapp:<password>@127.0.0.1:5432/monappفي ملف.env. وبما أن PostgreSQL لا يستمع إلا محليًا، فلا حاجة إلى فتح أيّ منفذ لقاعدة البيانات في الجدار الناري.تشغيل التطبيق باستخدام PM2
ثبّت PM2 على مستوى النظام (
npm install -g pm2، وهو مثبّت مسبقًا في القالب)، ثم شغّل التطبيق من مجلّد المشروع مع مطالبة Node بتحميل ملف.env:pm2 start app.js --name monapp -i max --node-args="--env-file=.env"يفعّل الخيار
-i maxوضع cluster على كل الأنوية، ويعيد PM2 تشغيل العملية تلقائيًا عند انهيارها.تفعيل التشغيل التلقائي عند الإقلاع
ولّد سكربت systemd بالأمر
pm2 startupمنفّذًا بحساب مستخدم التطبيق، ثم نفّذ بصلاحيات sudo الأمر الذي يعرضه، وبعدها ثبّت الحالة الراهنة بالأمرpm2 save. عند إعادة إقلاع VPS التالية سيُعاد تشغيل تطبيقك تلقائيًا دون أيّ تدخّل.إعداد Nginx وكيلًا عكسيًا
ثبّت Nginx إن لم يكن موجودًا، وأنشئ الملف
/etc/nginx/sites-available/monapp، وهو virtual host يمرّر الطلبات إلى منفذ Node المحلي. الترويستانUpgradeوConnectionمخصّصتان لـ WebSocket، أماX-Forwarded-Protoفتُعلم التطبيق بأن العميل يتّصل عبر HTTPS:server { listen 80; server_name yourdomain.com; 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; } }فعّل الموقع، واحذف الصفحة الافتراضية، واختبر الإعداد، ثم أعد التحميل:
ln -s /etc/nginx/sites-available/monapp /etc/nginx/sites-enabled/ rm /etc/nginx/sites-enabled/default nginx -t && systemctl reload nginxالتأمين بـ HTTPS عبر Let's Encrypt
ثبّت Certbot وإضافته الخاصة بـ Nginx، ثم اطلب الشهادة:
apt install certbot python3-certbot-nginx certbot --nginx -d yourdomain.comيحصل Certbot على الشهادة، ويضبط التحويل من HTTP إلى HTTPS، ويجدول التجديد التلقائي. صار تطبيق Node يُقدَّم الآن عبر HTTPS.
توحيد الإعداد في ecosystem.config.js
حين يستضيف VPS أكثر من تطبيق، أو حين ترافق الواجهةَ البرمجية عمليةُ عامل، يصعب تتبّع الخيارات المُمرَّرة في سطر الأوامر. يقرأ PM2 ملف ecosystem.config.js الذي يصف كل عملية: السكربت، والمجلّد، وعدد النسخ، وحدّ الذاكرة، والمتغيّرات غير الحسّاسة.
module.exports = {
apps: [
{
name: 'api',
cwd: '/home/monapp/app',
script: 'app.js',
instances: 'max',
exec_mode: 'cluster',
node_args: '--env-file=.env',
max_memory_restart: '500M',
env: { NODE_ENV: 'production', PORT: 3000 }
},
{
name: 'worker',
cwd: '/home/monapp/app',
script: 'worker.js',
node_args: '--env-file=.env'
}
]
}يشغّل الأمر pm2 start ecosystem.config.js الكل، ويحدّثه pm2 reload ecosystem.config.js، ويستهدف الخيار --only api تطبيقًا واحدًا. تعمل الواجهة البرمجية هنا في وضع cluster على كل الأنوية، والعامل في عملية واحدة. أودِع هذا الملف في المستودع مع الشيفرة، أما الأسرار فتبقى في ملف .env خارج المستودع.
السجلّات: قراءتها ثم تدويرها
يلتقط PM2 المخرجات القياسية ومخرجات الأخطاء لكل عملية في ~/.pm2/logs/، بملف -out.log وملف -error.log لكل تطبيق. يتابعها الأمر pm2 logs monapp مباشرة، ويعرض pm2 logs monapp --lines 100 آخر الأسطر.
من دون تدوير تكبر هذه الملفات حتى يمتلئ القرص. تتكفّل الوحدة pm2-logrotate بذلك؛ فهي افتراضيًا تدوّر الملف بعد 10 ميغابايت وتحتفظ بثلاثين نسخة، وهي عتبات يسهل تعديلها:
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 7من جهة الوكيل، يظهر خطأ 502 في /var/log/nginx/error.log، وهو يعني في الغالب أن عملية Node.js متوقّفة أو أنها تستمع على منفذ غير المذكور في proxy_pass.
لعمليات نشر دون انقطاع، اجمع بين وضع cluster في PM2 والأمر pm2 reload monapp بدل restart: يعيد PM2 تشغيل العمّال واحدًا تلو الآخر، فتبقى نسخة واحدة على الأقل متاحة طوال التحديث. راقب بعد ذلك الذاكرة بالأمر pm2 monit، واضبط حدًّا لإعادة التشغيل التلقائي (--max-memory-restart 500M) للوقاية من تسرّب الذاكرة في الإنتاج.
التوثيق الرسمي
للخيارات الخاصة بكل أداة وتغييرات الإصدارات، ارجع إلى التوثيق الرسمي لـ Node.js، وإلى جدول إصدارات LTS في Node.js، وإلى توثيق PM2. يغطّي هذا الدليل وضع التطبيق في الإنتاج على VPS، ويبقى توثيق الناشر المرجع في الضبط الدقيق.