يحتاج تطبيق Node.js في بيئة الإنتاج إلى أربعة أمور لا يوفرها خادم التطوير: أن يبدأ مع إقلاع النظام ويعود للعمل إذا تعطل، وأن يعمل بمستخدم محدود الصلاحيات، وأن يقف خلف خادم ويب يتولى TLS، وأن يترك سجلات يمكنك قراءتها. وعلى خادم افتراضي يعمل بـ Debian أو Ubuntu يوفّر systemd وNginx كل ذلك دون مديري عمليات إضافيين. يبني هذا الدليل الإعداد خطوة بخطوة.
1. ثبّت إصداراً مدعوماً من Node.js
تحقق مما توفره التوزيعة قبل التثبيت: تحزم Debian 13 (trixie) الإصدار 20.19.2، وDebian 12 (bookworm) الإصدار 18.20.4، وUbuntu 24.04 الإصدار 18.19.1. وكلها خطوط قديمة لم يعد مشروع Node.js نفسه يدعمها، وإن كانت التوزيعات تصدر تحديثات أمنية لحزمها. فإن احتاج تطبيقك خط LTS حديثاً (24 أو 26 حتى أواخر أكتوبر 2026) فاستخدم مستودع NodeSource، مع قراءة سكربت الإعداد قبل تشغيله:
curl -fsSL https://deb.nodesource.com/setup_24.x -o nodesource_setup.sh
less nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt install -y nodejs
node -v
command -v nodeدوّن المسار الذي يطبعه الأمر الأخير، فوحدة systemd تحتاج إليه.
2. أنشئ مستخدم خدمة وهيكلاً للمجلدات
شغّل التطبيق بحساب نظام لا يستطيع تسجيل الدخول ولا يملك شيفرته. وفي Debian ينشئ الأمر adduser --system حساباً صدفته /usr/sbin/nologin وبلا كلمة مرور:
sudo adduser --system --group --no-create-home myapp
sudo mkdir -p /srv/myapp/app /srv/myapp/data /etc/myapp
sudo chown myapp:myapp /srv/myapp/dataضع الشيفرة في /srv/myapp/app بملكية حساب النشر المعتاد لديك لا بملكية myapp، وثبّت فيها اعتماديات الإنتاج عبر npm ci --omit=dev. بهذا يقرأ التطبيق شيفرته ولا يستطيع تعديلها، ولا يكون قابلاً للكتابة إلا /srv/myapp/data للملفات المرفوعة أو ما يحتاج التطبيق إلى حفظه.
3. احفظ الإعدادات في ملف بيئة
الأسرار لا مكان لها في ملف الوحدة ولا في المستودع. أنشئ الملف /etc/myapp/myapp.env:
NODE_ENV=production
HOST=127.0.0.1
PORT=3000
DATABASE_URL=postgres://myapp:change-me@127.0.0.1:5432/myappsudo chown root:myapp /etc/myapp/myapp.env
sudo chmod 640 /etc/myapp/myapp.envتأكد من أن التطبيق يستمع على process.env.HOST وprocess.env.PORT كي لا يقبل الاتصالات إلا من Nginx على الجهاز نفسه. كما أن ضبط NODE_ENV=production مهم لأطر مثل Express، إذ يعدّه دليل الأداء لديها من أبسط التحسينات.
4. اكتب وحدة systemd
أنشئ الملف /etc/systemd/system/myapp.service مستخدماً مسار node من الخطوة الأولى:
[Unit]
Description=My Node.js app
After=network.target
[Service]
Type=exec
User=myapp
Group=myapp
WorkingDirectory=/srv/myapp/app
EnvironmentFile=/etc/myapp/myapp.env
ExecStart=/usr/bin/node server.js
Restart=on-failure
RestartSec=5s
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/srv/myapp/data
[Install]
WantedBy=multi-user.targetوظيفة الأسطر الأهم:
Type=execيجعلsystemctl startيبلغ عن الفشل إذا تعذر تشغيل الملف التنفيذي، كأن يكون المستخدم أو الملف غير موجود، ويصفه دليل systemd بأنه في العادة خيار أفضل من القيمة الافتراضيةsimple.Restart=on-failureيعيد تشغيل التطبيق إذا تعطل، لا حين توقفه عمداً.ProtectSystem=strictيجعل نظام الملفات كله للقراءة فقط بالنسبة للخدمة، وReadWritePaths=يفتح مجلد البيانات وحده، وProtectHome=trueيخفي/homeو/rootو/run/user.
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
systemctl status myapp
curl -I http://127.0.0.1:3000/5. ضع Nginx في الواجهة
يتولى Nginx إنهاء اتصالات HTTPS واستقبال المنافذ العامة وتمرير الطلبات إلى التطبيق. أضف أولاً المساعد الخاص بـ WebSocket من توثيق Nginx في الملف /etc/nginx/conf.d/connection_upgrade.conf (فملف nginx.conf في Debian يضمّن conf.d/*.conf داخل الكتلة http):
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}ثم أنشئ الملف /etc/nginx/sites-available/myapp:
server {
listen 80;
listen [::]:80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginxالسطر proxy_http_version 1.1 ضروري لـ WebSocket مع إصدارات Nginx التي تحزمها Debian (1.22 في bookworm و1.26 في trixie)، إذ لم يصبح 1.1 هو الافتراضي إلا في الإصدار 1.29.7. ولأن الترويستين Upgrade وConnection من نوع hop-by-hop فإن Nginx لا يمررهما ما لم تضبطهما صراحة. وإن كنت تستخدم Express فاضبط الإعداد trust proxy (على loopback مثلاً) ليقرأ عنوان العميل والبروتوكول من هذه الترويسات.
6. أضف HTTPS عبر certbot
يوصي مشروع certbot بحزمة snap الخاصة به، كما تحزم Debian أداة certbot وإضافتها لـ Nginx، وتثبّت حزمة Debian مؤقت systemd للتجديد:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d app.example.com
systemctl list-timers | grep certbot
sudo certbot renew --dry-runلا تفتح في الجدار الناري إلا المنفذين 80 و443، ويبقى منفذ التطبيق خاصاً لأنه يستمع على 127.0.0.1. وعلى خوادم Skyline Cloud الافتراضية يمكنك أيضاً تطبيق هذه القواعد في الجدار الناري السحابي من لوحة التحكم، فيُرشَّح المرور قبل وصوله إلى الخادم.
7. اقرأ السجلات
كل ما يكتبه التطبيق إلى المخرج القياسي أو مخرج الأخطاء يذهب إلى سجل systemd:
journalctl -u myapp -f
journalctl -u myapp -n 100
journalctl -u myapp --since "1 hour ago"ويحتفظ Nginx بسجلاته في /var/log/nginx/، وظهور الخطأ 502 فيها يعني غالباً أن التطبيق متوقف أو يستمع على منفذ آخر.
نشر التحديثات
الإصدار البسيط يكون هكذا: حدّث الشيفرة، ونفّذ npm ci --omit=dev وأي خطوة بناء، ثم sudo systemctl restart myapp. وتسبب إعادة التشغيل فجوة قصيرة ريثما تبدأ العملية؛ فإن لم يكن ذلك مقبولاً فشغّل نسختين على منفذين مختلفين خلف كتلة upstream في Nginx وأعد تشغيلهما واحدة تلو الأخرى.
وإن كنت تفضّل ألا تدير خادماً، فاستضافة المواقع لدى Skyline تشغّل تطبيقات Node.js 22 (حتى ثلاثة تطبيقات لكل حساب) من S Panel، وتعيد تشغيلها إذا تعطلت، وتعرض سجلاتها، وتخدمها على نطاقك بشهادة SSL مجانية.
ما الذي ينبغي فعله
- ثبّت إصداراً من Node.js ما زال مدعوماً من المشروع، ودوّن مساره.
- شغّل التطبيق بمستخدم نظام مخصص لا يستطيع الكتابة على شيفرته.
- احفظ الأسرار في ملف بيئة لا يقرؤه إلا root ومجموعة الخدمة.
- استخدم وحدة systemd تتضمن
Type=execوRestart=on-failureوخيارات العزل المذكورة. - اربط التطبيق بـ 127.0.0.1، وضع Nginx وcertbot في الواجهة، ولا تفتح إلا المنفذين 80 و443.
- راجع
journalctlبعد كل نشر، وتأكد من أن مؤقت تجديد certbot يعمل.
المصادر
- qa.debian.org/madison.php?package=nodejs
- packages.ubuntu.com/noble/nodejs
- raw.githubusercontent.com/nodejs/Release/main/schedule.json
- deb.nodesource.com/setup_24.x
- github.com/nodesource/distributions
- manpages.debian.org/trixie/adduser/adduser.8.en.html
- docs.npmjs.com/cli/v11/commands/npm-ci
- expressjs.com/en/advanced/best-practice-performance.html
- expressjs.com/en/guide/behind-proxies.html
- manpages.debian.org/trixie/systemd/systemd.service.5.en.html
- manpages.debian.org/trixie/systemd/systemd.exec.5.en.html
- manpages.debian.org/trixie/systemd/journalctl.1.en.html
- nginx.org/en/docs/http/ngx_http_proxy_module.html
- nginx.org/en/docs/http/websocket.html
- qa.debian.org/madison.php?package=nginx
- sources.debian.org/src/nginx/1.26.3-3%2Bdeb13u7/debian/conf/nginx.conf
- eff-certbot.readthedocs.io/en/stable/install.html
- eff-certbot.readthedocs.io/en/stable/using.html
- packages.debian.org/trixie/all/certbot/filelist
- cloud.alskyline.com/vps-saudi-arabia
- cloud.alskyline.com/nodejs-hosting-saudi-arabia
Comments
0 total · 0 threads