Get first-hand status: status is not the log
The beginner's habit is pasting systemctl status output into a forum, when status only gives a snapshot: Active state, main PID, and the last few exit codes. The few log lines it embeds are just "recent" and are easily overwritten by a later successful restart.
What you actually want is the full journal: journalctl -u servicename without a tight -n cap, sinking to the first error the service printed at startup—an assertion or a bad path usually appears in the first few lines. Also compare behavior between boot-time first start and a manual systemctl restart, because boot time often fails on ordering.
systemctl status myapp.service
systemctl is-failed myapp.service
journalctl -u myapp.service -n 500 --no-pager
# 只看启动那条线的完整输出
journalctl -u myapp.service --since=-2h | grep -iE "error|fail|denied|cannot" Key points for diagnostic(Assistant): four failure kinds
Kind one, "launcher error", is when systemd cannot exec at all: often a bad bin path, wrong permissions, or a mistyped shebang, and the journal shows Failed to execute. Kind two, "instant exit", means the main process started up and crashed within seconds—the crash stack lives in the app log, while status shows exit-code and uptime.
Kind three, "dependency not ready", typically bites when your service starts but the Redis/Mongo it should connect to is still booting, so several services appear to wait on each other. Kind four, "port never listening", is the classic bind failure—address in use or insufficient privilege, and ss -lnt never shows the entry. With these four pinned down, "why it won't start" collapses to one line.
ls -l /usr/lib/systemd/system/myapp.service
# 一二类的证据栏
cat /etc/systemd/system/myapp.service | grep -E "ExecStart|ExecStartPre"
ss -lnt | grep 8080
# 三类的依赖链
systemctl list-dependencies myapp.serviceDependency and ordering deadlocks: common misuses of After and Requires
Many treat After as "wait until I am up" and Requires as "I depend on it", but that is upside down: Requires only says "without it I refuse to start", while After expresses ordering. Requests with only Requires and no After let two services race to start in parallel, and the weak dependency silently fails.
The nastier failure is the ordering deadlock: if A has After=B and B has After=A, systemd rejects the whole transaction and drops both units together. When the system seems stuck in boot-pending, run systemd-analyze verify to spot cycles and systemd-analyze blame to see which unit drags the boot.
# 错误示范:只有 Requires 没有 After 导致弱依赖竞速
[Unit]
Requires=redis.service
# 修复对照:补上顺序与依赖语义
[Unit]
Requires=redis.service
After=redis.service network-online.target
Wants=network-online.target
# 校验周期/耗时
systemd-analyze verify myapp.service
systemd-analyze blame | head -10The sandbox options fault: ProtectedPaths/User locking the service in
systemd's sandbox options (ProtectSystem, ProtectHome, ReadOnlyPaths, User=) are great, yet they are also the sneaky cause of "looks configured but will not start". ProtectSystem=strict forces every path writable in your config under /var into read-only, and a database fails at the very first CREATE TABLE.
The fix is to halve first, then peer: comment out the suspicious isolation lines while keeping User/Group and core paths, confirm startup, then relax stepwise. Use systemd-analyze security to see the unit attack-surface score, and remember that a + prefix can explicitly allow a specific path without loosening the global rule.
systemd-analyze security myapp.service
# 错误示范:把 /var/lib 误设为只读
# ProtectSystem=strict
# 修复对照:只读保护 + 显式放行业务目录
ProtectSystem=strict
ReadWritePaths=/var/lib/myapp /run/myapp
# 或在路径前用 + 允许
[Service]
User=myapp
ProtectHome=read-onlyVerification: checklist from failed state to sustained running
After fixing, do not stop at a green Active: active (running). Confirm three things: a reboot shows active (running) with no escalating restart counter; the port stays listening in ss for at least 60 seconds; and the journal shows no new errors. For dependency-style faults, stop the dependency once and start it again to see whether the ordering reproduces deterministically.
If the service still exits occasionally, run systemctl reset-failed to drop stale counters before observing again, so historical failures are not mistaken for new ones. Finally, archive the before/after startup times as a regression baseline.
systemctl reset-failed myapp.service
systemctl restart myapp.service
# 观察 60s
sleep 60
systemctl show myapp.service -p NRestarts -p ActiveState
ss -lnt | grep 8080
journalctl -u myapp.service -n 30 --since=-5min | grep -c error