سرویسی که بالا نمیآید تقریباً هیچوقت رازآلود نیست. 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 هیچ چیز نشان نمیدهد، سه چیز را به ترتیب چک کنید:
StandardOutput=وStandardError=داخل unit — بایدjournalباشند (پیشفرض) یاjournal+console. ممکن است یکی آنها راnullگذاشته باشد.- اپ بهجای stdout در فایل مینویسد. systemd فقط stdout/stderr را میگیرد؛ نویسندههای فایل لاگ کلاً از journal رد میشوند. یا اپ را به stdout بچرخانید یا فایل را بخوانید.
- محدودیت ذخیرهسازی خطهای قدیمی را انداخته:
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 ثانیهای
systemctl status <unit>— وضعیت و خطهای آخر را بخوانید.journalctl -u <unit> -n 50 --no-pager— خطای واقعی را پیدا کنید.203/EXEC؟ مسیر، shebang، مجوزها.Permission denied؟ کاربر و مالکیت فایل.- فقط در boot میشکند؟
After=/Wants=network-online.targetو یک گاردExecStartPre=اضافه کنید. - فایل unit عوض شد؟
systemctl daemon-reload && systemctl restart <unit>. 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 ↗
آموزش بعدی با ایمیل
هر نوشته یک ایمیل. درستش کن، برو سراغ بعدی.
این چیست؟کوانتیزاسیون GGUF: کدام سطح را بهکار ببرید؟
این نوشتهها را میپسندید؟ این همان کاری است که برای زندگی از آن درمیآورم. استخدامم کنید