رفتن به محتوا
LoopX

سرویس‌ها با systemd

توی این درس یاد می‌گیری سرویس (service یا daemon) برنامه‌ای است که همیشه در پس‌زمینه می‌ماند (وب‌سرور، دیتابیس، ssh) و systemd مدیر آن‌هاست. با systemctl status وضعیت یک سرویس را می‌خوانی؛ با start، stop، restart و reload کنترلش می‌کنی؛ فرق start (همین حالا) با enable (در هر بوت) را می‌فهمی و systemctl enable --now را می‌شناسی؛ یک سرویس با unit file خودت می‌نویسی؛ و لاگ‌هایش را با journalctl -u نام -f دنبال می‌کنی. تمرین: یک سرویس ساده می‌سازی که بعد از ریستارت سیستم خودش بالا بیاید.

مسئله: برنامه‌ای که باید همیشه زنده باشد

Section titled “مسئله: برنامه‌ای که باید همیشه زنده باشد”

در درس قبل دیدی اگر برنامه‌ای را با & یا nohup اجرا کنی، با خراب‌شدن یا ریبوت سرور تمام است و باید خودت دوباره راهش بیندازی. وب‌سرور و دیتابیس چنین چیزی نمی‌خواهند: باید هنگام بوت خودکار بالا بیایند، اگر خراب شدند دوباره راه بیفتند و لاگشان یک‌جا باشد. این کار مدیر سرویس‌هاست، و در تقریباً همه‌ی توزیع‌های امروزی (Ubuntu، Debian، Fedora…) نامش systemd است.

systemd مثل مدیر ساختمان است: لیستی دارد از «واحدها» (unit) که باید هر روز صبح روشن شوند (آسانسور، نگهبانی، روشنایی راه‌پله)، به ترتیب درست (اول برق، بعد آسانسور)، اگر چیزی خراب شد دوباره راه‌اندازی‌اش می‌کند، و در یک دفتر ثبت (journal) همه‌ی اتفاق‌ها را می‌نویسد. enable یعنی «این را در لیست صبح‌گاهی بگذار» و start یعنی «همین حالا روشنش کن».

systemd اولین پروسه‌ی سیستم (PID 1) است. هسته آن را اجرا می‌کند و systemd بقیه را: هر سرویسِ فعال‌شده (enabled) در هدف پیش‌فرض (default target) هنگام بوت راه می‌افتد.

همه‌ی این درس روی یک سرور Ubuntu با systemd واقعی (یک ماشین آزمایشی که ویژه‌ی همین درس‌ها ساخته‌ام) اجرا شده است؛ در آن من root هستم. روی سرور خودت اگر کاربر عادی (با sudo) هستی، قبل از دستورهای تغییردهنده (start، stop، enable، daemon-reload) sudo بگذار. (status و journalctl معمولاً بدون sudo هم کار می‌کنند.)

مثال ۱: systemd کیست و چه چیزهایی را مدیریت می‌کند؟

Section titled “مثال ۱: systemd کیست و چه چیزهایی را مدیریت می‌کند؟”
Terminal window
echo "--- اولین پروسه (PID 1):"
ps -p 1 -o pid,comm
systemctl --version | head -1
echo "--- هدف پیش‌فرض بوت:"
systemctl get-default
echo "--- سرویس‌های در حال اجرا:"
systemctl list-units --type=service --state=running --no-pager
خروجی
--- اولین پروسه (PID 1):
PID COMMAND
1 systemd
systemd 255 (255.4-1ubuntu8.17)
--- هدف پیش‌فرض بوت:
graphical.target
--- سرویس‌های در حال اجرا:
UNIT LOAD ACTIVE SUB DESCRIPTION
cron.service loaded active running Regular background program processing daemon
dbus.service loaded active running D-Bus System Message Bus
rsyslog.service loaded active running System Logging Service
systemd-journald.service loaded active running Journal Service
systemd-logind.service loaded active running User Login Management
Legend: LOAD → Reflects whether the unit definition was properly loaded.
ACTIVE → The high-level unit activation state, i.e. generalization of SUB.
SUB → The low-level unit activation state, values depend on unit type.
5 loaded units listed.

PID 1 همان systemd است. هر چیزی که systemd مدیریتش می‌کند یک unit نام دارد، با پسوندی که نوعش را می‌گوید:

Terminal window
echo "تعداد unit ها به تفکیک نوع:"
systemctl list-units --all --no-legend --plain --no-pager | awk '{print $1}' | sed 's/.*\.//' | sort | uniq -c | sort -rn
خروجی
تعداد unit ها به تفکیک نوع:
80 service
41 target
19 mount
13 socket
9 timer
4 slice
2 path
1 scope
1 device
1 automount
نوع کار
.service یک برنامه‌ی پس‌زمینه (مثل nginx، cron، ssh): موضوع این درس
.timer اجرای دوره‌ای یک سرویس (جایگزین cron؛ درس cron)
.socket بالا آوردن سرویس وقتی اولین اتصال می‌رسد (مثل ssh.socket)
.target گروهی از unit ها که با هم یک «حالت» را می‌سازند (مثل multi-user.target)
.mount یک مسیر mount شده

مثال ۲: systemctl status، وضعیت یک سرویس

Section titled “مثال ۲: systemctl status، وضعیت یک سرویس”

اول سرویسی که زنده است (cron)، بعد سرویسی که خاموش است (nginx):

Terminal window
systemctl status cron
echo "======"
systemctl status nginx
خروجی
● cron.service - Regular background program processing daemon
Loaded: loaded (/usr/lib/systemd/system/cron.service; enabled; preset: enabled)
Active: active (running) since Sat 2026-10-03 21:14:33 UTC; 25s ago
Docs: man:cron(8)
Main PID: 81 (cron)
Tasks: 1 (limit: 4742)
Memory: 320.0K (peak: 1.2M)
CPU: 3ms
CGroup: /system.slice/cron.service
└─81 /usr/sbin/cron -f -P
Oct 03 21:14:33 server systemd[1]: Started cron.service - Regular background program processing daemon.
Oct 03 21:14:33 server (cron)[81]: cron.service: Referenced but unset environment variable evaluates to an empty string: EXTRA_OPTS
Oct 03 21:14:33 server cron[81]: (CRON) INFO (pidfile fd = 3)
Oct 03 21:14:33 server cron[81]: (CRON) INFO (Running @reboot jobs)
======
○ nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; preset: enabled)
Active: inactive (dead)
Docs: man:nginx(8)
Oct 03 21:14:07 server systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...
Oct 03 21:14:07 server systemd[1]: Started nginx.service - A high performance web server and a reverse proxy server.
Oct 03 21:14:07 server systemd[1]: Stopping nginx.service - A high performance web server and a reverse proxy server...
Oct 03 21:14:07 server systemd[1]: nginx.service: Deactivated successfully.
Oct 03 21:14:07 server systemd[1]: Stopped nginx.service - A high performance web server and a reverse proxy server.
Oct 03 21:14:07 server systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...
Oct 03 21:14:07 server systemd[1]: Started nginx.service - A high performance web server and a reverse proxy server.
Oct 03 21:14:33 server systemd[1]: Stopping nginx.service - A high performance web server and a reverse proxy server...
Oct 03 21:14:33 server systemd[1]: nginx.service: Deactivated successfully.
Oct 03 21:14:33 server systemd[1]: Stopped nginx.service - A high performance web server and a reverse proxy server.

نقطه‌ی اول هر خروجی وضعیت را می‌گوید: ● سبز یعنی فعال، ○ یعنی غیرفعال، و × یعنی شکست‌خورده. خط‌ها:

  • Loaded: فایل unit از کجا خوانده شده، و enabled/disabled: آیا هنگام بوت خودکار شروع می‌شود (مثال ۴). preset پیش‌فرض توزیع است.
  • Active: active (running) در حال اجرا، inactive (dead) خاموش، failed خراب‌شده، و از چه زمانی.
  • Main PID، Tasks، Memory، CPU: پروسه‌ی اصلی و مصرفش.
  • CGroup: درخت همه‌ی پروسه‌های این سرویس (systemd با cgroup دقیقاً می‌داند کدام پروسه مال کدام سرویس است).
  • چند خط آخر: آخرین لاگ‌های همین سرویس از journal (مثال ۷).

مثال ۳: start، stop، restart و reload

Section titled “مثال ۳: start، stop، restart و reload”

nginx (وب‌سرور؛ دوره‌ی خودش را دارد) نصب است ولی خاموش. راهش بینداز و وضعیتش را (کوتاه‌شده) ببین:

Terminal window
systemctl start nginx
systemctl is-active nginx
echo "--- وب‌سرور جواب می‌دهد؟"
curl -sI http://localhost | head -1
echo "--- وضعیت (۱۲ خط اول):"
systemctl status nginx --no-pager | head -12
خروجی
active
--- وب‌سرور جواب می‌دهد؟
HTTP/1.1 200 OK
--- وضعیت (۱۲ خط اول):
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; preset: enabled)
Active: active (running) since Sat 2026-10-03 21:14:59 UTC; 16ms ago
Docs: man:nginx(8)
Process: 323 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Process: 325 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Main PID: 326 (nginx)
Tasks: 9 (limit: 4742)
Memory: 6.4M (peak: 6.4M)
CPU: 14ms
CGroup: /system.slice/nginx.service
├─326 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"

is-active فقط یک کلمه (active) می‌دهد و برای اسکریپت‌ها مناسب است. حالا فرق reload و restart:

Terminal window
before=$(systemctl show -p MainPID --value nginx)
systemctl reload nginx
echo "reload: PID اصلی قبل=$before | بعد=$(systemctl show -p MainPID --value nginx) (همان ماند)"
systemctl restart nginx
echo "restart: PID اصلی بعد=$(systemctl show -p MainPID --value nginx) (عوض شد)"
echo "--- stop:"
systemctl stop nginx
systemctl is-active nginx
curl -sI http://localhost || echo "curl: اتصال برقرار نشد (کد $?)"
خروجی
reload: PID اصلی قبل=326 | بعد=326 (همان ماند)
restart: PID اصلی بعد=360 (عوض شد)
--- stop:
inactive
curl: اتصال برقرار نشد (کد 7)
  • reload به سرویس می‌گوید تنظیماتش را دوباره بخواند، بدون خاموش‌شدن (PID همان می‌ماند، اتصال‌ها قطع نمی‌شوند)؛ همه‌ی سرویس‌ها پشتیبانی نمی‌کنند.
  • restart متوقف و دوباره شروع می‌کند (PID جدید، قطع کوتاه).
  • stop متوقف می‌کند؛ بعدش وب‌سرور جواب نمی‌دهد (curl کد ۷: اتصال رد شد).

این دو دو کار جدا هستند: start یعنی «همین حالا اجرا کن»، و enable یعنی «هر بار که سیستم بوت می‌شود اجرا کن». systemctl enable --now هر دو را با هم انجام می‌دهد:

Terminal window
echo "--- قبل:"
echo "enabled: $(systemctl is-enabled nginx) | active: $(systemctl is-active nginx)"
systemctl enable --now nginx
echo "--- بعد از enable --now:"
echo "enabled: $(systemctl is-enabled nginx) | active: $(systemctl is-active nginx)"
echo "--- enable فقط یک symlink می‌سازد:"
ls -l /etc/systemd/system/multi-user.target.wants/ | grep nginx
خروجی
--- قبل:
enabled: disabled | active: inactive
Synchronizing state of nginx.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install enable nginx
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.
--- بعد از enable --now:
enabled: enabled | active: active
--- enable فقط یک symlink می‌سازد:
lrwxrwxrwx 1 root root 37 Oct 3 21:14 nginx.service -> /usr/lib/systemd/system/nginx.service

سه خط اول خروجی enable اطلاعات SysV است (nginx یک اسکریپت قدیمی هم دارد). مهم خط Created symlink است: enable یعنی یک symlink در پوشه‌ی multi-user.target.wants؛ systemd هنگام بوت هر چه در این پوشه باشد را راه می‌اندازد. و disable فقط همان symlink را برمی‌دارد، نه بیشتر:

Terminal window
systemctl disable nginx
echo "enabled: $(systemctl is-enabled nginx) | active: $(systemctl is-active nginx)"
خروجی
Synchronizing state of nginx.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install disable nginx
Removed "/etc/systemd/system/multi-user.target.wants/nginx.service".
enabled: disabled | active: active

nginx الان disabled ولی هنوز در حال اجراست. این دو وضعیت مستقل‌اند:

enabled؟ active؟ معنی
بله بله اجراست و بعد از ریبوت هم بالا می‌آید (حالت عادیِ یک سرویس تولید)
بله نه الان خاموش، ولی بعد از ریبوت بالا می‌آید
نه بله الان اجراست، ولی بعد از ریبوت نیست (وضعیت الان nginx)
نه نه خاموش و خارج از بوت
⚡ بررسی سریع

سرویسی را فقط systemctl start کرده‌ای و enable نکرده‌ای. بعد از ریبوت چه می‌شود؟

مثال ۵: یک سرویس خودت، با unit file

Section titled “مثال ۵: یک سرویس خودت، با unit file”

سرویس‌های خودت را با نوشتن یک unit file می‌سازی (یک فایل متنی ساده). اول خود برنامه، یک اسکریپت کوچک که هر ۵ ثانیه یک خط چاپ می‌کند (هر چه روی خروجی استاندارد بنویسد، systemd در journal ثبت می‌کند):

/usr/local/bin/lx-hello.sh
#!/bin/bash
echo "lx-hello: شروع شد (PID $$)"
while true; do echo "lx-hello: زنده‌ام $(date +%T)"; sleep 5; done

و فایل unit، در /etc/systemd/system/ (جای unit های ساخته‌ی مدیر):

/etc/systemd/system/lx-hello.service
[Unit]
Description=LoopX hello demo
After=network.target
[Service]
ExecStart=/usr/local/bin/lx-hello.sh
Restart=on-failure
RestartSec=2
[Install]
WantedBy=multi-user.target
بخش و کلید معنی
[Unit] Description توضیح؛ در status دیده می‌شود
After=network.target بعد از بالا آمدن شبکه شروع شود (ترتیب)
[Service] ExecStart دستور اجرای سرویس؛ مسیر کامل بنویس
Restart=on-failure اگر با خطا یا سیگنال مُرد، دوباره راهش بینداز (مثال ۶)
RestartSec=2 قبل از شروع دوباره ۲ ثانیه صبر کن
[Install] WantedBy=multi-user.target وقتی enable شود، به همین هدف (حالت عادی سرور) وصل شود

فایل را می‌سازیم، به systemd خبر می‌دهیم (daemon-reload: فایل‌های unit را دوباره بخواند)، راه می‌اندازیم و وضعیت را می‌بینیم:

Terminal window
chmod +x /usr/local/bin/lx-hello.sh
systemd-analyze verify /etc/systemd/system/lx-hello.service && echo "unit file بدون خطا (systemd-analyze verify)"
systemctl daemon-reload
systemctl start lx-hello
sleep 1
systemctl status lx-hello
خروجی
unit file بدون خطا (systemd-analyze verify)
● lx-hello.service - LoopX hello demo
Loaded: loaded (/etc/systemd/system/lx-hello.service; disabled; preset: enabled)
Active: active (running) since Sat 2026-10-03 21:15:00 UTC; 1s ago
Main PID: 547 (lx-hello.sh)
Tasks: 2 (limit: 4742)
Memory: 1.0M (peak: 1.1M)
CPU: 2ms
CGroup: /system.slice/lx-hello.service
├─547 /bin/bash /usr/local/bin/lx-hello.sh
└─550 sleep 5
Oct 03 21:15:00 server systemd[1]: Started lx-hello.service - LoopX hello demo.
Oct 03 21:15:00 server lx-hello.sh[547]: lx-hello: شروع شد (PID 547)
Oct 03 21:15:00 server lx-hello.sh[547]: lx-hello: زنده‌ام 21:15:00

حالا enable تا بعد از ریبوت هم بالا بیاید (و is-enabled برای تأیید):

Terminal window
systemctl enable lx-hello
systemctl is-enabled lx-hello
ls -l /etc/systemd/system/multi-user.target.wants/lx-hello.service
خروجی
Created symlink /etc/systemd/system/multi-user.target.wants/lx-hello.service → /etc/systemd/system/lx-hello.service.
enabled
lrwxrwxrwx 1 root root 36 Oct 3 21:15 /etc/systemd/system/multi-user.target.wants/lx-hello.service -> /etc/systemd/system/lx-hello.service

مثال ۶: Restart=، سرویسی که خودش بالا می‌آید

Section titled “مثال ۶: Restart=، سرویسی که خودش بالا می‌آید”

وقتی سرویس خراب شود، Restart=on-failure آن را دوباره راه می‌اندازد. با kill -9 (در درس قبل: مرگ اجباری و بدون تمیزکاری) پروسه‌ی اصلی را می‌کشم و می‌بینم systemd چه می‌کند:

Terminal window
pid=$(systemctl show -p MainPID --value lx-hello)
echo "PID اصلی قبل: $pid"
kill -9 $pid
sleep 4
echo "PID اصلی بعد: $(systemctl show -p MainPID --value lx-hello) | تعداد ریستارت: $(systemctl show -p NRestarts --value lx-hello)"
echo "--- لاگ systemd از آنچه رخ داد:"
journalctl -u lx-hello --no-pager -o cat | grep -v 'زنده‌ام' | tail -6
خروجی
PID اصلی قبل: 547
PID اصلی بعد: 577 | تعداد ریستارت: 1
--- لاگ systemd از آنچه رخ داد:
lx-hello: شروع شد (PID 547)
lx-hello.service: Main process exited, code=killed, status=9/KILL
lx-hello.service: Failed with result 'signal'.
lx-hello.service: Scheduled restart job, restart counter is at 1.
Started lx-hello.service - LoopX hello demo.
lx-hello: شروع شد (PID 577)

systemd متوجه شد پروسه با سیگنال ۹ مرده (code=killed)، ۲ ثانیه صبر کرد (RestartSec) و دوباره راهش انداخت؛ PID جدید است و شمارنده‌ی ریستارت یکی بیشتر شد. گزینه‌های Restart=:

مقدار چه وقت دوباره راه می‌اندازد
no هرگز (پیش‌فرض)
on-failure کد خروج غیر صفر، مرگ با سیگنال یا timeout (توصیه برای بیشتر سرویس‌ها)
always همیشه، حتی اگر با کد ۰ تمام شود
on-abnormal فقط با سیگنال یا timeout، نه با کد خروج غیرصفر

مثال ۷: journalctl، لاگ سرویس‌ها

Section titled “مثال ۷: journalctl، لاگ سرویس‌ها”

systemd همه‌ی لاگ‌ها را در یک پایگاه داده‌ی یکپارچه (journal) نگه می‌دارد و journalctl می‌خواندشان. فیلترهای پرکاربرد:

Terminal window
echo "--- ۳ خط آخر لاگ یک سرویس (-u نام، -n تعداد):"
journalctl -u lx-hello -n 3 --no-pager
echo "--- فقط متن پیام‌ها (-o cat) از یک دقیقه‌ی اخیر، تعداد خط‌ها:"
journalctl -u lx-hello --since "1 minute ago" -o cat --no-pager | wc -l
echo "--- سرویس‌های دیگر هم همین‌طور:"
journalctl -u nginx --no-pager -o cat | tail -3
خروجی
--- ۳ خط آخر لاگ یک سرویس (-u نام، -n تعداد):
Oct 03 21:15:03 server systemd[1]: Started lx-hello.service - LoopX hello demo.
Oct 03 21:15:03 server lx-hello.sh[577]: lx-hello: شروع شد (PID 577)
Oct 03 21:15:03 server lx-hello.sh[577]: lx-hello: زنده‌ام 21:15:03
--- فقط متن پیام‌ها (-o cat) از یک دقیقه‌ی اخیر، تعداد خط‌ها:
48
--- سرویس‌های دیگر هم همین‌طور:
Stopped nginx.service - A high performance web server and a reverse proxy server.
Starting nginx.service - A high performance web server and a reverse proxy server...
Started nginx.service - A high performance web server and a reverse proxy server.

پرچم -f (follow) مثل tail -f لاگ را زنده دنبال می‌کند، و بهترین ابزار برای دیدن اتفاقات یک سرویس هنگام restart است. اینجا journalctl -u lx-hello -f را ۸ ثانیه اجرا می‌کنم و در همین فاصله سرویس را restart می‌کنم (در ترمینال واقعی با Ctrl+C متوقفش می‌کنی):

Terminal window
timeout 8 journalctl -u lx-hello -f -n 2 --no-pager &
sleep 1
echo "--- (حالا در ترمینال دیگری:) systemctl restart lx-hello"
systemctl restart lx-hello
wait
خروجی
Oct 03 21:15:03 server lx-hello.sh[577]: lx-hello: شروع شد (PID 577)
Oct 03 21:15:03 server lx-hello.sh[577]: lx-hello: زنده‌ام 21:15:03
--- (حالا در ترمینال دیگری:) systemctl restart lx-hello
Oct 03 21:15:06 server systemd[1]: Stopping lx-hello.service - LoopX hello demo...
Oct 03 21:15:06 server systemd[1]: lx-hello.service: Deactivated successfully.
Oct 03 21:15:06 server systemd[1]: Stopped lx-hello.service - LoopX hello demo.
Oct 03 21:15:06 server systemd[1]: Started lx-hello.service - LoopX hello demo.
Oct 03 21:15:06 server lx-hello.sh[595]: lx-hello: شروع شد (PID 595)
Oct 03 21:15:06 server lx-hello.sh[595]: lx-hello: زنده‌ام 21:15:06
Oct 03 21:15:11 server lx-hello.sh[595]: lx-hello: زنده‌ام 21:15:11

دو خط اول لاگ‌های قبلی‌اند (-n 2)؛ بعد از restart خط‌های Stopping، Stopped، Started و خروجی تازه‌ی خود برنامه را زنده می‌بینی. فرمان رایج برای عیب‌یابی: journalctl -u نام -f در یک ترمینال و systemctl restart نام در ترمینال دیگر. فیلترهای دیگر journalctl (-p err، --since، -b) را در درس لاگ‌ها و عیب‌یابی کامل‌تر می‌بینی.

مثال ۸: بعد از ریبوت چه می‌شود؟

Section titled “مثال ۸: بعد از ریبوت چه می‌شود؟”

حرف اصلی enable را باید با یک ریبوت واقعی دید. روی سرور واقعی sudo reboot می‌زنی و بعد از بالا آمدن دوباره وصل می‌شوی. ماشین آزمایشی من یک کانتینر است؛ معادل ریبوتش docker restart است که systemd را از صفر بوت می‌کند (این دستورها را از بیرون ماشین اجرا کرده‌ام، پس docker exec lxsys اول‌شان است). قبل از ریبوت: nginx را start کرده‌ام و enable نه؛ lx-hello را enable کرده‌ام:

Terminal window
echo "--- قبل از ریبوت:"
docker exec lxsys systemctl is-active nginx lx-hello
docker exec lxsys systemctl is-enabled nginx lx-hello
echo "--- ریبوت (بوت دوباره‌ی systemd)..."
docker restart lxsys > /dev/null
for i in $(seq 1 60); do docker exec lxsys systemctl is-system-running 2>/dev/null | grep -qE 'running|degraded' && break; sleep 1; done
echo "--- بعد از ریبوت:"
docker exec lxsys systemctl is-active nginx lx-hello
خروجی
--- قبل از ریبوت:
active
active
disabled
enabled
--- ریبوت (بوت دوباره‌ی systemd)...
--- بعد از ریبوت:
inactive
active

lx-hello (enable‌شده) خودش بالا آمد و nginx (فقط start‌شده) نیامد. این یعنی enable تنها چیزی است که یک سرویس را از ریبوت جان به‌در می‌برد. (ترتیب خروجی‌ها همان ترتیب اسم‌هاست: اول nginx، بعد lx-hello.) تمرین اصلی درس هم همین است: سرویسی که با ریبوت دوباره بالا بیاید.

پشت پرده: unit file ها از کجا می‌آیند؟

Section titled “پشت پرده: unit file ها از کجا می‌آیند؟”

هر unit یک فایل است. systemctl cat فایل واقعی را (به‌همراه مسیرش) نشان می‌دهد؛ بخش‌های اصلی unit خود nginx:

Terminal window
systemctl cat nginx --no-pager | grep -vE '^(#|$)'
خروجی
[Unit]
Description=A high performance web server and a reverse proxy server
Documentation=man:nginx(8)
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
ConditionFileIsExecutable=/usr/sbin/nginx
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
TimeoutStopSec=5
KillMode=mixed
[Install]
WantedBy=multi-user.target

دو چیز را ببین: Type=forking یعنی برنامه خودش را به پس‌زمینه می‌برد و PIDFile را می‌نویسد (سرویس‌هایی که در پیش‌زمینه می‌مانند مثل lx-hello از Type=simple پیش‌فرض استفاده می‌کنند)؛ و ExecStartPre قبل از شروع تنظیمات را آزمایش می‌کند، که اگر خراب بود سرویس اصلاً بالا نمی‌آید. unit ها در سه جا هستند و بالاترین اولویت با /etc/systemd/system/ است:

مسیر محتوا
/usr/lib/systemd/system/ unit های بسته‌های نصب‌شده (با به‌روزرسانی بسته عوض می‌شوند؛ دستکاری نکن)
/etc/systemd/system/ unit های مدیر؛ بر فایل‌های بسته اولویت دارد
/run/systemd/system/ unit های موقت (تا ریبوت بعدی)

برای تغییر یک سرویس بسته‌ای، فایل خود بسته را عوض نکن؛ یک drop-in بساز (یک فایل کوچک با فقط بخش‌های تغییریافته در نام.service.d/) تا به‌روزرسانی بسته آن را پاک نکند (systemctl edit نام همین را برایت می‌سازد):

Terminal window
mkdir -p /etc/systemd/system/nginx.service.d
printf '[Service]\nRestart=on-failure\nRestartSec=5\n' > /etc/systemd/system/nginx.service.d/override.conf
systemctl daemon-reload
echo "--- نمای ترکیبی (انتهای خروجی، فایل drop-in):"
systemctl cat nginx --no-pager | tail -5
echo "--- و مقدار مؤثر:"
systemctl show -p Restart,RestartUSec nginx
rm -rf /etc/systemd/system/nginx.service.d
systemctl daemon-reload
خروجی
--- نمای ترکیبی (انتهای خروجی، فایل drop-in):
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
Restart=on-failure
RestartSec=5
--- و مقدار مؤثر:
Restart=on-failure
RestartUSec=5s

و target ها که ترتیب بوت را می‌سازند (بخشی از وابستگی‌های multi-user.target، یعنی «حالت عادی سرور»):

Terminal window
systemctl list-dependencies multi-user.target --no-pager | head -12
خروجی
multi-user.target
● ├─cron.service
● ├─dbus.service
○ ├─dmesg.service
○ ├─e2scrub_reap.service
● ├─lx-hello.service
● ├─rsyslog.service
● ├─systemd-ask-password-wall.path
● ├─systemd-logind.service
○ ├─systemd-update-utmp-runlevel.service
● ├─systemd-user-sessions.service
● ├─ufw.service

سرویس‌هایی که enable شده‌اند (symlink در multi-user.target.wants) اینجا زیر هدف دیده می‌شوند و همه با هم بالا می‌آیند (● فعال، ○ غیرفعال).

دستور کار
systemctl status نام وضعیت و آخرین لاگ‌ها
systemctl start نام / stop / restart راه‌اندازی، توقف، راه‌اندازی دوباره
systemctl reload نام خواندن دوباره‌ی تنظیمات بدون قطع
systemctl enable نام / disable افزودن/حذف از بوت
systemctl enable --now نام enable و start با هم
systemctl is-active / is-enabled / is-failed وضعیت یک‌کلمه‌ای برای اسکریپت
systemctl daemon-reload خواندن دوباره‌ی unit file ها (بعد از هر تغییر)
systemctl cat نام فایل unit (و drop-in ها)
systemctl show -p MainPID نام یک ویژگی مشخص
systemctl list-units --type=service سرویس‌های در حال اجرا
systemctl reset-failed نام پاک‌کردن وضعیت failed
journalctl -u نام لاگ یک سرویس (-f زنده، -n 50 تعداد)
systemd-analyze verify فایل بررسی نحو یک unit file
کلید unit file کار
Description توضیح
After= / Wants= / Requires= ترتیب و وابستگی
ExecStart= دستور اصلی (مسیر کامل)
ExecStartPre= / ExecStop= / ExecReload= دستورهای قبل، توقف، reload
Type= simple (پیش‌فرض)، forking، oneshot، notify
Restart= / RestartSec= سیاست و تأخیر ریستارت
User= / WorkingDirectory= با چه کاربری و در کدام پوشه
Environment= / EnvironmentFile= متغیرهای محیطی
WantedBy= هدفی که با enable به آن وصل می‌شود
Terminal window
sed -i 's/RestartSec=2/RestartSec=3/' /etc/systemd/system/lx-hello.service
systemctl restart lx-hello
echo "--- RestartSec مؤثر هنوز قدیمی است:"
systemctl show -p RestartUSec --value lx-hello
systemctl daemon-reload
systemctl restart lx-hello
echo "--- بعد از daemon-reload:"
systemctl show -p RestartUSec --value lx-hello
خروجی
Warning: The unit file, source configuration file or drop-ins of lx-hello.service changed on disk. Run 'systemctl daemon-reload' to reload units.
--- RestartSec مؤثر هنوز قدیمی است:
2s
--- بعد از daemon-reload:
3s

systemd فایل‌ها را کش می‌کند؛ بعد از هر تغییر در unit file یا drop-in باید systemctl daemon-reload بزنی، وگرنه تغییرت اعمال نمی‌شود و این هشدار (changed on disk) را می‌بینی. راه‌حل: daemon-reload، بعد restart.

۲) status=203/EXEC، اسکریپت اجرایی نیست

Section titled “۲) status=203/EXEC، اسکریپت اجرایی نیست”
Terminal window
printf '#!/bin/bash\necho hi\n' > /usr/local/bin/lx-noexec.sh
chmod 644 /usr/local/bin/lx-noexec.sh
printf '[Unit]\nDescription=Demo: script without x\n[Service]\nExecStart=/usr/local/bin/lx-noexec.sh\n' > /etc/systemd/system/lx-noexec.service
systemctl daemon-reload
systemctl start lx-noexec
systemctl status lx-noexec --no-pager | head -8
echo "--- راه‌حل:"
chmod +x /usr/local/bin/lx-noexec.sh
systemctl start lx-noexec && echo "حالا شروع شد (کد خروج ۰)"
rm -f /etc/systemd/system/lx-noexec.service /usr/local/bin/lx-noexec.sh
systemctl daemon-reload
خروجی
× lx-noexec.service - Demo: script without x
Loaded: loaded (/etc/systemd/system/lx-noexec.service; static)
Active: failed (Result: exit-code) since Sat 2026-10-03 21:15:13 UTC; 4ms ago
Duration: 448us
Process: 704 ExecStart=/usr/local/bin/lx-noexec.sh (code=exited, status=203/EXEC)
Main PID: 704 (code=exited, status=203/EXEC)
CPU: 426us
--- راه‌حل:
حالا شروع شد (کد خروج ۰)

status=203/EXEC یعنی systemd نتوانست ExecStart را اجرا کند: مسیر غلط یا (اینجا) فایل بدون حق x (درس دسترسی‌ها). پس هر وقت سرویس همان لحظه failed شد، اول systemctl status و بعد journalctl -u را ببین. راه‌حل: chmod +x و مسیر کامل و درست.

Terminal window
su - ali -c 'systemctl restart cron; echo "کد خروج: $?"'
echo "--- با sudo (ali اجازه دارد):"
su - ali -c 'sudo systemctl restart cron; echo "کد خروج: $?"; systemctl is-active cron'
خروجی
Failed to restart cron.service: Access denied
See system logs and 'systemctl status cron.service' for details.
کد خروج: 4
--- با sudo (ali اجازه دارد):
کد خروج: 0
active

کاربر عادی نمی‌تواند سرویس را کنترل کند (Access denied؛ روی سرورهایی که polkit دارند ممکن است متن «Interactive authentication required» باشد). راه‌حل: sudo systemctl .... فقط خواندن وضعیت (status) بدون sudo ممکن است.

۴) System has not been booted with systemd

Section titled “۴) System has not been booted with systemd”
Terminal window
systemctl status 2>&1 | head -3
خروجی
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down

در کانتینرهای معمولی (و WSL قدیمی) PID 1 چیز دیگری است و systemctl کار نمی‌کند: این پیام یعنی «اینجا systemd نیست». راه‌حل: یک ماشین واقعی یا مجازی، یا WSL2 با systemd فعال؛ درس راه‌اندازی محیط. (کانتینر داکر هم برای هر برنامه یک پروسه‌ی اصلی دارد و به جای systemd از restart policy استفاده می‌کند.)

مثال ۸: بعد از ریبوت، nginx که فقط start شده بود نیامد. و برعکس: enable بدون start تا بوت بعدی چیزی را راه نمی‌اندازد. راه‌حل: systemctl enable --now نام.

✎ تمرینآسان

وضعیت سرویس cron را بگیر: آیا الان در حال اجراست و آیا هنگام بوت بالا می‌آید؟ (با دستورهای یک‌کلمه‌ای.)

دیدن جواب
Terminal window
echo "active: $(systemctl is-active cron) | enabled: $(systemctl is-enabled cron)"
systemctl status cron --no-pager | sed -n '1,3p'
خروجی
active: active | enabled: enabled
● cron.service - Regular background program processing daemon
Loaded: loaded (/usr/lib/systemd/system/cron.service; enabled; preset: enabled)
Active: active (running) since Sat 2026-10-03 21:15:13 UTC; 13ms ago
✎ تمرینمتوسط

تمرین اصلی: یک سرویس ساده با unit file بساز که بعد از ریستارت خودش بالا بیاید. سرویس lx-clock را بساز که هر ۵ ثانیه ساعت را به فایل /tmp/lx-clock.log اضافه کند، با enable --now راهش بینداز، و ثابت کن هم در حال اجراست و هم enable است (مثال ۸ نشان داد enable یعنی بعد از ریبوت می‌آید).

دیدن جواب
cat > /usr/local/bin/lx-clock.sh <<'EOF'
#!/bin/bash
while true; do date +%T >> /tmp/lx-clock.log; sleep 5; done
EOF
chmod +x /usr/local/bin/lx-clock.sh
cat > /etc/systemd/system/lx-clock.service <<'EOF'
[Unit]
Description=LoopX clock demo
[Service]
ExecStart=/usr/local/bin/lx-clock.sh
Restart=always
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now lx-clock
sleep 2
echo "active: $(systemctl is-active lx-clock) | enabled: $(systemctl is-enabled lx-clock)"
ls -l /etc/systemd/system/multi-user.target.wants/lx-clock.service
echo "--- خروجی سرویس:"
cat /tmp/lx-clock.log
systemctl disable --now lx-clock
rm -f /etc/systemd/system/lx-clock.service /usr/local/bin/lx-clock.sh /tmp/lx-clock.log
systemctl daemon-reload
خروجی
Created symlink /etc/systemd/system/multi-user.target.wants/lx-clock.service → /etc/systemd/system/lx-clock.service.
active: active | enabled: enabled
lrwxrwxrwx 1 root root 36 Oct 3 21:15 /etc/systemd/system/multi-user.target.wants/lx-clock.service -> /etc/systemd/system/lx-clock.service
--- خروجی سرویس:
21:15:13
Removed "/etc/systemd/system/multi-user.target.wants/lx-clock.service".
✎ تمرینسخت

سرویسی بساز که هر بار بعد از یک ثانیه با خطا تمام می‌شود، با Restart=on-failure. با StartLimitIntervalSec=20 و StartLimitBurst=3 تنظیمش کن و نشان بده systemd بعد از چند تلاش پشت‌سرهم از ریستارت دست می‌کشد و سرویس به وضعیت failed می‌رود. با reset-failed پاکش کن.

دیدن جواب
Terminal window
cat > /etc/systemd/system/lx-crash.service <<'EOF'
[Unit]
Description=Demo: crash loop
StartLimitIntervalSec=20
StartLimitBurst=3
[Service]
ExecStart=/bin/sh -c 'echo starting; sleep 1; echo crashing >&2; exit 1'
Restart=on-failure
RestartSec=1
EOF
systemctl daemon-reload
systemctl start lx-crash
sleep 9
systemctl status lx-crash --no-pager | head -12
systemctl show -p NRestarts,ActiveState,SubState lx-crash
systemctl reset-failed lx-crash
echo "--- بعد از reset-failed:"
systemctl is-active lx-crash
rm -f /etc/systemd/system/lx-crash.service
systemctl daemon-reload
خروجی
× lx-crash.service - Demo: crash loop
Loaded: loaded (/etc/systemd/system/lx-crash.service; static)
Active: failed (Result: exit-code) since Sat 2026-10-03 21:15:22 UTC; 2s ago
Duration: 1.016s
Process: 875 ExecStart=/bin/sh -c echo starting; sleep 1; echo crashing >&2; exit 1 (code=exited, status=1/FAILURE)
Main PID: 875 (code=exited, status=1/FAILURE)
CPU: 5ms
Oct 03 21:15:22 server systemd[1]: lx-crash.service: Scheduled restart job, restart counter is at 3.
Oct 03 21:15:22 server systemd[1]: lx-crash.service: Start request repeated too quickly.
Oct 03 21:15:22 server systemd[1]: lx-crash.service: Failed with result 'exit-code'.
Oct 03 21:15:22 server systemd[1]: Failed to start lx-crash.service - Demo: crash loop.
NRestarts=3
ActiveState=failed
SubState=failed
--- بعد از reset-failed:
inactive

Start request repeated too quickly: systemd برای جلوگیری از چرخه‌ی بی‌پایان، بعد از ۳ شکست در ۲۰ ثانیه دیگر تلاش نمی‌کند. دلیل خرابی را در journalctl -u lx-crash پیدا کن، رفعش کن، reset-failed بزن و دوباره start کن.

؟ آزمونک
  1. systemd چیست؟

  2. فرق systemctl start و systemctl enable؟

  3. systemctl enable --now nginx چه می‌کند؟

  4. بعد از ویرایش یک unit file چه باید بزنی؟

  5. journalctl -u nginx -f چه می‌کند؟

  6. Restart=on-failure در unit file یعنی…

  • systemd (PID 1) سرویس‌ها (unit) را هنگام بوت راه می‌اندازد، ریستارت می‌کند و لاگشان را در journal نگه می‌دارد.
  • systemctl status نام وضعیت، start/stop/restart/reload کنترل فوری، enable/disable رفتار هنگام بوت. enable --now هر دو. این دو مستقل‌اند: start بدون enable بعد از ریبوت نمی‌ماند.
  • سرویس خودت: فایل /etc/systemd/system/نام.service با [Unit]، [Service] (ExecStart مسیر کامل، Restart=) و [Install] (WantedBy=multi-user.target)؛ بعدش systemctl daemon-reload.
  • Restart=on-failure سرویس را بعد از خطا دوباره بالا می‌آورد؛ زیاد crash کند، start-limit آن را failed می‌کند (reset-failed).
  • journalctl -u نام (-f زنده، -n تعداد) لاگ سرویس؛ systemctl cat فایل unit؛ برای تغییر سرویس بسته‌ای از drop-in استفاده کن.
  • بدون sudo نمی‌توانی سرویس را کنترل کنی؛ روی کانتینر معمولی systemd نیست.
برگه‌ی تقلب این درس
دستورکاری که می‌کند
systemctl status ناموضعیت و آخرین لاگ‌ها
systemctl start|stop|restart نامکنترل فوری
systemctl reload نامخواندن دوباره‌ی تنظیمات بدون قطع
systemctl enable --now ناماجرا همین حالا و در هر بوت
systemctl disable نامبرداشتن از بوت (سرویس همین حالا می‌ماند)
systemctl is-active|is-enabled ناموضعیت یک‌کلمه‌ای
systemctl daemon-reloadبعد از هر تغییر در unit file
systemctl cat نامدیدن فایل unit
systemctl reset-failed نامپاک‌کردن وضعیت failed
journalctl -u نام -fلاگ زنده‌ی یک سرویس
journalctl -u نام -n 50 --no-pagerپنجاه خط آخر
systemd-analyze verify فایلبررسی نحو unit file