بازگشت به وبلاگ

سرویس systemd استارت نمی‌شود؟ چطور رفعش کنید

۲۳ شهریور ۱۴۰۵

سرویسی که بالا نمی‌آید تقریباً هیچ‌وقت رازآلود نیست. systemctl status <unit> را اجرا کنید، بعد 50 خط آخر journal مربوط به آن unit را با journalctl -u <unit> -n 50 --no-pager بخوانید — همین دو تا، یکی از پنج علت را معمولاً صریح نام می‌برد: مسیر غلط، باینری غایب، مجوز غلط، انکار SELinux/AppArmor، یا خطای سینتکس فایل unit. علت شکست در لاگ است؛ رفع‌های زیر فقط pattern matching روی همان‌اند.

چطور ببینم سرویس systemd چرا شکست خورده؟

اول status، بعد journal:

systemctl status myapp.service
journalctl -u myapp.service -n 50 --no-pager

status وضعیت را می‌دهد (inactive (dead)، failed (exit-code)، activating (auto-restart)) و چند خط آخر لاگ. journal داستان کامل را می‌دهد: stdout، stderr و گلایه‌های خود systemd درباره unit.

اگر unit قبلاً هم شکست خورده و رکورد آن اجرا را می‌خواهید، -b برای boot فعلی یا --since today اضافه کنید:

journalctl -u myapp.service -b --no-pager

جعبه‌ابزار کامل — boot ها، اولویت‌ها، دنبال کردن زنده خروجی — در چیت‌شیت journalctl: دنبال کردن، فیلتر کردن و پایش لاگ‌های Linux است. همان عضله حافظه است.

یک جفت دیگر هم دانستنش می‌ارزد:

systemctl list-units --failed
systemctl reset-failed myapp.service

اولی هر unit قرص صحنه را فهرست می‌کند. دومی بعد از رفع مشکل، وضعیت failed را پاک می‌کند — تزئینی است، ولی جلوی جیغ‌کشیدن بی‌دلیل صفحات status را می‌گیرد.

شایع‌ترین علل کدام‌اند؟

بعد از اینکه لاگ علامت را نام برد، علت تقریباً همیشه یکی از این پنج‌تاست:

لاگ می‌گویدعلت محتملرفع
status=203/EXECمسیر ExecStart= غلط یا interpreter غایبمسیر absolute، chmod +x، چک shebang
status=203/EXEC روی اسکریپتاسکریپت خط‌های CRLF دارد یا shebang خرابdos2unix script.sh، خط اول را درست کنید
status=1/FAILURE بدون لاگ اپپوشه کاری یا متغیر env غایبWorkingDirectory= را ست کنید، Environment= اضافه کنید
Permission deniedکاربر نمی‌تواند فایل بخواند یا پورت را bind کندمالکیت را درست کنید؛ پورت‌های زیر 1024 یا AmbientCapabilities=CAP_NET_BIND_SERVICE می‌خواهند یا root
Unit is maskedیکی systemctl mask زدهsystemctl unmask myapp.service
ویرایش فایل unit «هیچ نمی‌کند»daemon reload نشدهsystemctl daemon-reload

خانواده 203/EXEC ذکر ویژه می‌طلبد چون همان است که عصرها را می‌بلعد. systemd برای اجرای ExecStart= از shell شما استفاده نمی‌کند. یعنی:

# Wrong — no shell, ~ never expands, no PATH lookup
ExecStart=~/app/run.sh

# Right
ExecStart=/opt/app/run.sh

و خود اسکریپت باید executable باشد و با shebang واقعی شروع شود (#!/bin/bash یا #!/usr/bin/env bash). اسکریپتی که در ترمینال شما بی‌نقص اجرا می‌شود ولی زیر systemd با 203 می‌میرد، تقریباً همیشه یکی از این‌هاست: executable نیست، انتهای CRLF دارد، shebang به هیچ جا اشاره می‌کند، یا مسیرش relative است.

چرا دستی بالا می‌آید ولی در boot نه؟

باگ کلاسیک ترتیب. اگر لاگ بلافاصله بعد از boot شکست نشان می‌دهد ولی unit با systemctl start دستی راحت بالا می‌آید، سرویس شما دارد یک مسابقه می‌بازد — سراغ شبکه، دیسک mount‌شده یا دیتابیسی می‌رود که هنوز وجود ندارد.

رفع، اعلام وابستگی است به‌جای امیدواری:

[Unit]
After=network-online.target postgresql.service
Wants=network-online.target

[Service]
ExecStartPre=/usr/bin/test -f /opt/app/config.toml

After= شروع را مرتب می‌کند؛ Wants= باعث می‌شود systemd واقعاً وابستگی را بالا بیاورد. network-online.target فقط وقتی کار می‌کند که سرویس network-wait روی توزیع شما فعال باشد، پس systemctl is-enabled NetworkManager-wait-online.service را چک کنید (یا معادل systemd-networkd). گارد ExecStartPre= راه ارزان و صادقانه‌ای است که به‌جای stack trace با پیام خوانا بلند بلند بشکند.

نسخه دوم: سرویس در boot بالا می‌آید ولی بلافاصله می‌میرد. دنبال چیزهایی بگردید که محیط تعاملی‌تان داشت و boot ندارد — تفاوت PATH، یک virtualenv، یک HOME. آنچه لازم دارید صریح ست کنید:

[Service]
Environment="PATH=/opt/app/venv/bin:/usr/bin"
User=appuser
WorkingDirectory=/opt/app

چرا سرویس من در journal لاگ نمی‌کند؟

اگر journalctl -u هیچ چیز نشان نمی‌دهد، سه چیز را به ترتیب چک کنید:

  1. StandardOutput= و StandardError= داخل unit — باید journal باشند (پیش‌فرض) یا journal+console. ممکن است یکی آن‌ها را null گذاشته باشد.
  2. اپ به‌جای stdout در فایل می‌نویسد. systemd فقط stdout/stderr را می‌گیرد؛ نویسنده‌های فایل لاگ کلاً از journal رد می‌شوند. یا اپ را به stdout بچرخانید یا فایل را بخوانید.
  3. محدودیت ذخیره‌سازی خط‌های قدیمی را انداخته: journalctl --disk-usage، و اگر journal دارد خفه می‌شود SystemMaxUse= در /etc/systemd/journald.conf.

برای دیباگ خودِ شروع، هیچ چیز مثل انداختن یک shell در همان context بوت نمی‌ماند:

[Service]
ExecStart=/bin/bash -c 'exec /opt/app/bin/server 2>&1'

یا برای معماهای واقعی، دستور دقیق ExecStart= را با کاربر همان unit در یک shell اجرا کنید — بیشتر تفاوت‌های environment در ده ثانیه اول لو می‌روند.

چطور بعد از crash خودکار ری‌استارت شود؟

سیاست پیش‌فرض Restart=no است: سرویس crash شده مرده می‌ماند و شما از زبان کاربر خبردار می‌شوید. برای هر سرویس درستش کنید:

[Service]
Restart=on-failure
RestartSec=5
تنظیمکِی ری‌استارت می‌کند
no (پیش‌فرض)هرگز
on-failureخروج غیرصفر، سیگنال، timeout
alwaysهر خروجی، حتی تمیز
on-watchdogفقط timeout مربوط به watchdog

Restart=on-failure را با گارد نرخ شروع جفت کنید تا حلقه crash‌زننده جعبه را کوبیدن نگیرد: StartLimitIntervalSec= و StartLimitBurst= در بخش [Unit]. پنج شکست در شصت ثانیه باید یک انسان را page کند، نه یک CPU را بچرخاند.

اگر این را به‌عنوان job زمان‌بندی‌شده می‌چینید نه daemon، اول cron در مقابل timer های systemd را بسنجید — timer ها لاگ در journal و ترتیب وابستگی‌ها را مجانی می‌دهند، دقیقاً همان‌هایی که این مقاله مدام سراغشان می‌رود.

چک‌لیست 60 ثانیه‌ای

  1. systemctl status <unit> — وضعیت و خط‌های آخر را بخوانید.
  2. journalctl -u <unit> -n 50 --no-pager — خطای واقعی را پیدا کنید.
  3. 203/EXEC؟ مسیر، shebang، مجوزها. Permission denied؟ کاربر و مالکیت فایل.
  4. فقط در boot می‌شکند؟ After=/Wants=network-online.target و یک گارد ExecStartPre= اضافه کنید.
  5. فایل unit عوض شد؟ systemctl daemon-reload && systemctl restart <unit>.
  6. Restart=on-failure اضافه کنید تا crash بعدی خودش را اعلام کند نه پنهان.

بیشتر لحظه‌های «systemd پیچیده است» به همین حرف خلاصه می‌شوند: خطا از اول در journal بود. قبل از ویرایش فایل unit بخوانیدش، نه بعد.

FAQ

چطور ببینم سرویس systemd چرا شکست خورده؟

systemctl status <unit> برای خلاصه، journalctl -u <unit> -e برای خط‌های واقعی خطا.

کد خروج 203 EXEC یعنی چه؟

systemd نتوانست باینری را اجرا کند — مسیر غلط، interpreter غایب، یا بیت exec ندارد. مسیر ExecStart باید absolute باشد.

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

Environment متفاوت است: نه HOME، PATH دیگر، کاربر یا cwd دیگر. User= و WorkingDirectory= و Environment= را صریح ست کنید.

— mrsaynothing

— mrsaynothing

یادداشت‌های میدانی درباره AI، لینوکس و self-hosting.

این نوشته را در dev.to بحث کنید dev.to ↗

آموزش بعدی با ایمیل

هر نوشته یک ایمیل. درستش کن، برو سراغ بعدی.

self-hosted · بدون واسطه‌های ثالث · لغو اشتراک با یک کلیک

این چیست؟

کوانتیزاسیون GGUF: کدام سطح را به‌کار ببرید؟

این نوشته‌ها را می‌پسندید؟ این همان کاری است که برای زندگی از آن درمی‌آورم. استخدامم کنید