SSD Nodes Learn Hosting plans →
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-08-23

كيف تشغّل برنامجًا كخدمة systemd على VPS

أنشئ خدمة systemd تشغّل برنامجك عند الإقلاع، تعيد تشغيله بعد التعطل، وتسجل مخرجاته في journal، مع مؤقت للتشغيل الدوري وتقليل الصلاحيات.

ما هي خدمة systemd، ولماذا تحتاج إليها

خدمة systemd هي ملف نصي صغير يحدد للخادم طريقة تشغيل برنامج: تشغيله عند الإقلاع، وإعادة تشغيله إذا تعطل، وإرسال مخرجاته إلى سجل النظام. هذه هي وظيفتها بالكامل. يتوقف البرنامج الذي تشغله يدوياً في جلسة SSH بمجرد تسجيل الخروج أو إعادة تشغيل الخادم. أما البرنامج الذي تشغله خدمة systemd فيستمر في العمل، لأن الخادم نفسه يديره بدلاً من الصدفة التي تستخدمها.

يُعد systemd نظام التهيئة في Ubuntu وDebian وFedora ومعظم خوادم Linux الحديثة. وهو أول عملية تبدأ، كما يشرف على جميع العمليات الأخرى. لم يكن ذلك صحيحاً دائماً، ويستحق التعرّف إلى كيفية استبدال systemd لبرامج init النصية التي سبقته بعد أن تعرف وظيفة ملف الوحدة. عندما تكتب ملف خدمة، فإنك تسلّم برنامجك إلى ذلك المشرف. يوضح هذا الدليل أصغر وحدة عملية، والأقسام الثلاثة الموجودة في كل وحدة، وكيفية تفعيلها وقراءة سجلاتها، وكيفية تشغيلها وفق جدول زمني باستخدام مؤقت، وكيفية تقييدها لتعمل بأقل قدر ممكن من الصلاحيات.

أصغر خدمة تعمل

يوجد ملف الخدمة في /etc/systemd/system/، وينتهي بالامتداد .service، ولا يحتاج إلا إلى بضعة أسطر. أنشئ ملفاً لبرنامج موجود في /usr/local/bin/myapp:

sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My application

[Service]
ExecStart=/usr/local/bin/myapp

[Install]
WantedBy=multi-user.target

هذه وحدة مكتملة وتعمل. يمثّل ExecStart الأمر الذي يجب تشغيله. ويعني WantedBy=multi-user.target بدء الخدمة بعد وصول الخادم إلى التشغيل العادي متعدد المستخدمين، وهذا ما يجعلها تبدأ عند الإقلاع. وما عدا ذلك تحسينات إضافية.

الأقسام الثلاثة، والغرض من كل منها

يُقسَّم كل ملف وحدة إلى أقسام بين قوسين مربعين. تستخدم الخدمة ثلاثة أقسام.

[Unit] يصف الخدمة وعلاقاتها. السطران اللذان ستستخدمهما غالباً هما:

[Unit]
Description=My application
After=network-online.target
Wants=network-online.target

Description هو الاسم الوصفي الذي تراه في systemctl status. ويخبر After=network-online.target systemd بألّا يبدأ برنامجك قبل أن تصبح الشبكة جاهزة. وهذا مهم لأي برنامج يستمع على منفذ أو ينشئ اتصالاً صادراً.

[Service] يحدد طريقة تشغيل البرنامج. تضع معظم إعداداتك هنا:

[Service]
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.toml
WorkingDirectory=/opt/myapp
User=myapp
Restart=on-failure
RestartSec=5
Environment=LOG_LEVEL=info

يشغّل User=myapp البرنامج باستخدام حساب غير مميّز بدلاً من root، وهو أهم سطر منفرد للأمان. لا يوجد هنا سطر Type=، لذلك يستخدم systemd القيمة simple ويفترض أن عملية ExecStart تبقى في الواجهة الأمامية. أما البرنامج الذي ينشئ عملية فرعية ويعمل في الخلفية، فيحتاج إلى قيمة Type= المناسبة لطريقة بدء تشغيله، وإلا فستبلغ الوحدة عن حالتها كـactive بينما يكون daemon الفعلي قد توقف بالفعل. يحصل Restart=on-failure وRestartSec=5 على قسم مستقل أدناه، لأنهما سبب كتابة معظم الأشخاص لملف خدمة أصلاً.

[Install] يحدد ما يحدث عند تفعيل الخدمة:

[Install]
WantedBy=multi-user.target

يربط WantedBy=multi-user.target الخدمة بعملية الإقلاع عند تشغيل systemctl enable. من دون قسم [Install]، يمكن بدء الخدمة يدوياً، لكنها لن تبدأ تلقائياً بعد إعادة التشغيل.

فعِّلها وراقبها

بعد كتابة أي ملف وحدة أو تعديله، أعد تحميل systemd لكي يقرأ التغيير، ثم فعِّل الخدمة وابدأ تشغيلها في خطوة واحدة:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service

daemon-reload هي الخطوة التي ينساها الناس: يحتفظ systemd بملفات الوحدات مؤقتاً، لذلك لا يُطبَّق التعديل قبل إعادة التحميل. ويعمل enable --now على تفعيل الخدمة عند الإقلاع وبدء تشغيلها فوراً. تحقّق من ذلك:

sudo systemctl status myapp.service
* myapp.service - My application
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
     Active: active (running) since Wed 2026-07-15 22:40:11 UTC; 3s ago
   Main PID: 4123 (myapp)

Active: active (running) وenabled هما القيمتان المطلوبتان. لقراءة مخرجات البرنامج، اطلب من journal عرض هذه الوحدة فقط:

sudo journalctl -u myapp.service -f

يتابع -f الأسطر الجديدة عند وصولها، مثل tail -f. كل ما يكتبه برنامجك إلى standard output أو standard error يظهر هنا، من دون الحاجة إلى إعداد logging من جانبك.

إعادة التشغيل عند الفشل، وهذا هو سبب وجودك هنا

الفائدة الأساسية من الخدمة هي أن systemd يعيد تشغيل برنامجك عندما يتوقف بشكل غير متوقع. ينفّذ ذلك سطران:

[Service]
Restart=on-failure
RestartSec=5

يعيد Restart=on-failure تشغيل البرنامج عندما يخرج برمز غير صفري أو يتوقف بسبب إشارة تعطل مثل SIGKILL أو SIGSEGV. لا يؤدي الخروج السليم أو الإيقاف بواسطة SIGTERM أو SIGINT أو SIGHUP أو SIGPIPE إلى تشغيله. ينتظر RestartSec=5 خمس ثوانٍ بين المحاولات، حتى لا يدخل البرنامج الذي يتعطل فوراً في حلقة إعادة تشغيل متواصلة. تأكد من ذلك بإنهاء العملية ومراقبة systemd وهو يعيد تشغيلها. استخدم SIGKILL، لأن SIGTERM الافتراضية تُحتسب إيقافاً سليماً، ولذلك لن يعيد on-failure تشغيل الخدمة:

sudo systemctl kill -s SIGKILL myapp.service
sudo systemctl status myapp.service

خلال خمس ثوانٍ، تعرض الحالة Main PID جديداً، وتعرض active (running) مرة أخرى. هذه هي الوظيفة كاملة، ولهذا تكون الخدمة أفضل من ترك برنامج يعمل في tmux أو screen.

Run it as an unprivileged user, and harden it

A service that runs as root can do anything to your server if the program is ever exploited. Run it as its own user, and give systemd a few directives that fence it in. First create a system account with no login and no home:

sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp

Then set User=myapp and add hardening lines to [Service]:

[Service]
User=myapp
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

Each line removes something the program does not need. NoNewPrivileges=true stops the process from ever gaining new privileges, even through a setuid binary. PrivateTmp=true gives it a private /tmp no other process can see. ProtectSystem=strict makes the whole filesystem read-only except for a few paths you name with ReadWritePaths=. ProtectHome=true hides /home from it entirely. This is the same least-privilege thinking as putting a service behind a firewall: give it only what it needs. If you have read the guide on closing the IPv6 firewall gap on a VPS, this is the on-host half of the same idea. For a service that faces the internet, pair this hardening with Fail2ban in front of SSH and a default-deny firewall.

Rather than type all of this by hand and misremember a directive, generate a complete, hardened unit and copy it out:

Toolsystemd service and timer generator

المؤقتات: cron الحديثة

يشغّل مؤقت systemd خدمة وفق جدول زمني، وهو البديل الحديث لمهمة cron. يتكوّن المؤقت من ملفين: .service ينفّذ المهمة، و.timer يحدّد موعد تنفيذها. لنفترض أنك تريد إجراء نسخة احتياطية عند الساعة 3 صباحاً كل يوم. تنفّذ الخدمة المهمة مرة واحدة ثم تنتهي:

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

يخبر Type=oneshot systemd بأن البرنامج يعمل وينتهي ثم يتوقف، بدلاً من بقائه قيد التشغيل. ويحدّد المؤقت جدوله:

# /etc/systemd/system/backup.timer
[Unit]
Description=Run the nightly backup

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

يعني OnCalendar=*-*-* 03:00:00 الساعة 3 صباحاً كل يوم. اختبر أي تعبير تقويمي باستخدام systemd-analyze calendar "*-*-* 03:00:00"؛ إذ يتحقق من تحليله ويطبع المواعيد التالية لتشغيله. يشغّل Persistent=true المهمة الفائتة فور عودة الخادم إذا كان متوقفاً عند الساعة 3 صباحاً، وهو ما لا يستطيع cron فعله. لاحظ أن تفعيل المؤقت يتم عبر timers.target، وليس عبر multi-user.target. فعّل المؤقت، لا الخدمة:

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers

يعرض list-timers جميع المؤقتات مع موعد التشغيل التالي والسابق، بحيث يمكنك معرفة موعد تشغيل مهمتك التالي بسرعة. ينشئ الأمر السابق زوج .service و.timer عند تفعيل وضع المؤقت. مقارنةً بسطر cron، يوفّر المؤقت سجلات فعلية في journal، وتوجيهات التحصين نفسها المتاحة لأي خدمة، كما يوفّر تشغيل المهام الفائتة الذي يقدمه Persistent=true. يظل cron مناسباً لمهمة بسيطة، لكن المؤقت يصبح الأداة الأفضل عندما تكون المهمة مهمة.

FAQ

ما الفرق بين خدمة systemd ومهمة cron؟

تحافظ الخدمة على تشغيل برنامج طويل الأمد: تبدأ عند الإقلاع، وتُعاد محاولتها عند الفشل، وتكتب السجلات في journal. تنفّذ مهمة cron أمراً قصيراً وفق جدول زمني ثم تنتهي. عندما تريد الجدولة، وتحتاج أيضاً إلى سجلات journal، وتعزيز الأمان، وتشغيل المهام الفائتة، فاستخدم مؤقت systemd. يربط مؤقت systemd جدول .timer بخدمة oneshot، ويستبدل cron في معظم مهام الخوادم.

أين أضع ملف خدمة systemd؟

ضع الوحدات التي تنشئها في /etc/systemd/system/، على أن ينتهي اسم الملف بالامتداد .service. هذا الدليل مخصص للوحدات التي يضيفها المسؤول، وله أولوية على الوحدات التي توفرها الحزم في /lib/systemd/system/. بعد إنشاء ملف أو تعديله هناك، شغّل sudo systemctl daemon-reload لكي يلتقط systemd التغيير.

كيف أجعل الخدمة تُعاد محاولتها إذا تعطلت؟

أضف Restart=on-failure وRestartSec=5 إلى قسم [Service]، ثم شغّل sudo systemctl daemon-reload وأعد تشغيل الخدمة. يعيد systemd تشغيل البرنامج عندما ينتهي برمز غير صفري أو يموت بسبب إشارة تعطل، وينتظر خمس ثوانٍ بين المحاولات. اختبر ذلك باستخدام sudo systemctl kill -s SIGKILL myapp.service. وتُعد SIGTERM، وهي الإشارة الافتراضية، إيقافاً سليماً ولا تؤدي إلى تشغيل on-failure. وراقب systemctl status لتظهر قيمة PID جديدة خلال بضع ثوانٍ.

كيف أشغّل خدمة systemd كمستخدم غير root؟

أنشئ حساب نظام باستخدام sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp، ثم أضف User=myapp إلى قسم [Service]. أضف NoNewPrivileges=true وPrivateTmp=true وProtectSystem=strict لكي تعمل العملية بأقل قدر من الصلاحيات التي تحتاج إليها. إن تشغيل الخدمة كمستخدم غير متمتع بصلاحيات هو أهم تغيير منفرد يمكنك إجراؤه لتحسين أمانها.

لماذا فشلت خدمتي في البدء؟

شغّل systemctl status myapp.service لعرض الملخص، وjournalctl -u myapp.service لعرض المخرجات الكاملة. أكثر الأسباب شيوعاً هي وجود مسار خاطئ في ExecStart، أو غياب WorkingDirectory، أو خطأ في الصلاحيات لأن User= لا يستطيع قراءة ملف، أو نسيان sudo systemctl daemon-reload بعد التعديل. يعرض journal رسالة الخطأ الخاصة بالبرنامج، وهي تسمّي المشكلة مباشرةً في العادة.