服务器重启后,Nginx、数据库、自建应用或 Docker 相关服务没有自动起来,最容易把问题简单归结为“服务坏了”。实际排查要先分清三种情况:服务没有设置开机自启;服务被启用了,但启动时失败;服务启动顺序早于它依赖的网络、存储或数据库,随后退出或进入失败状态。处理路径不同,盲目重启往往只能临时恢复。
下面方法适用于主流 systemd Linux 发行版。示例里的 nginx.service 请替换成实际服务名;Docker 容器、云镜像初始化脚本、发行版自带服务可能还有自己的管理方式,需要同时确认。

一、先区分“没有启用”和“启动失败”
登录服务器后先执行两条命令:
systemctl status nginx.service
systemctl is-enabled nginx.service
status 看当前状态、PID、退出码和最近日志;is-enabled 只回答 unit 是否配置为开机加载。常见输出有四种:enabled 表示已创建开机启动链接;disabled 表示没有设置开机自启;static 表示 unit 通常由其他 unit 或定时器拉起;masked 表示 unit 被屏蔽,systemd 拒绝启动它。
如果 is-enabled 返回 disabled,先确认这个服务确实应该开机启动,再执行 sudo systemctl enable nginx.service。enable 只处理开机链接,通常不会立即启动服务。确认配置可以启动时,才使用 sudo systemctl enable --now nginx.service,因为 --now 会立即启动服务。
二、用 systemctl 找最近一次失败原因
服务已经 enabled,重启后却不在运行,重点看失败线索:
systemctl status nginx.service
systemctl list-units --state=failed
journalctl -b -u nginx.service --no-pager
status 里重点记录 Active、Process、since 和日志末尾,用来区分失败发生在开机阶段还是运行中,并确认退出码、信号、配置路径、权限、端口、证书或依赖错误。排查整机启动阶段时,可以再按错误级别过滤:
journalctl -b -p err --no-pager
日志里出现配置文件路径、端口占用、证书过期、权限不足、上游目录不存在、依赖地址连不上,都比反复重启服务有价值。
三、检查 unit 文件来源,不要直接改软件包文件
确认服务实际加载了哪份配置:
systemctl cat nginx.service
systemctl show nginx.service -p FragmentPath -p DropInPaths -p After -p Wants -p Requires
FragmentPath 是主 unit 文件,DropInPaths 是覆盖片段。发行版包常把 unit 放在 /usr/lib/systemd/system/,软件包升级可能覆盖这里的修改。自建服务适合放在 /etc/systemd/system/example.service;对软件包服务做少量覆盖,更适合使用 sudo systemctl edit nginx.service 创建 drop-in。
例如让服务等待网络目标并调整重启间隔,可以在编辑器中加入类似配置:
[Unit]
Wants=network-online.target
After=network-online.target
[Service]
Restart=on-failure
RestartSec=5
保存后必须执行 sudo systemctl daemon-reload,再执行 sudo systemctl restart nginx.service 验证。修改前保留原配置或 drop-in 备份;出问题时删掉新增片段、重载配置,再按原配置启动,就能回到修改前状态。
四、依赖和启动顺序不要混为一谈
Wants=、Requires= 和 After= 经常被一起写,但含义不同。Wants= 会尝试拉起依赖,依赖失败时本服务仍会启动;Requires= 是强依赖,依赖失败会带动本服务失败;After= 只定义顺序,不会自动拉起依赖。
要让应用等待网络就绪,通常至少同时有 Wants=network-online.target 和 After=network-online.target。应用依赖远端数据库时,只写 After=postgresql.service 也可能不够,因为数据库 unit 变为 active 不代表业务端口已可接受连接,应用自身还需要重试或启动前检查。
查看依赖关系:
systemctl list-dependencies nginx.service
systemctl list-dependencies nginx.service --reverse
依赖越强,故障传播范围越大。不要为了看起来稳妥给每个服务都加 Requires=;生产机上更可靠的做法是明确网络、存储、数据库的真实启动条件,并让应用在临时失败时按 Restart=on-failure 与合理间隔重试。
五、检查 Type、ExecStart 和 Restart 的常见误区
自建服务的启动失败常和这几项有关:
systemctl show example.service -p Type -p ExecStart -p Restart -p RestartSec -p User -p WorkingDirectory
Type=simple 只要把进程 fork 出去,systemd 就可能继续后续启动流程;主进程二进制不存在、脚本解释器错误、启动即退出,仍可能在之后才暴露。能用 Type=exec 时,它会更明确地等待执行阶段完成。运行一段命令后退出的初始化任务适合 Type=oneshot。
Restart= 决定失败后的行为。no 表示不自动拉起;on-failure 表示非零退出触发重启;always 连正常退出也会重启,适合需要长期驻留的进程。RestartSec= 设置间隔,避免进程每秒反复崩溃刷日志。
如果服务由 Docker 管理,还要区分两层:容器重启由 Docker restart policy 控制,宿主机 Docker daemon 由 systemd 控制。两层同时做“开机后自动执行脚本操作容器”时,容易产生重复启动或权限问题,需要确认服务归属。
六、配置变更后重新加载,再做重启验收
修改 unit、drop-in 或服务配置后,流程建议固定为:
sudo systemctl daemon-reload
sudo systemctl restart nginx.service
systemctl status nginx.service
journalctl -u nginx.service -n 100 --no-pager
仅执行 enable 不会让修改后的 unit 立即生效;仅改文件不执行 daemon-reload,systemd 也可能继续使用旧定义。Nginx 等能离线检查配置的软件,先执行自身语法检查,再交给 systemd。
修复后至少验证一次真实重启。重启前确认控制台、带外管理或备用 SSH 入口可用,避免把管理链路也依赖到本次调整的服务上。重启完成后检查:
systemctl is-enabled nginx.service
systemctl is-active nginx.service
journalctl -b -u nginx.service --no-pager
需要隔离启动目标做验证时,先确认目标含义和影响,例如 sudo systemctl isolate multi-user.target。这条命令会停止不属于目标的服务,风险比查看命令高,不应在未评估业务影响的生产机上直接执行。
七、把处理顺序固定下来
遇到服务器重启后服务没有自动启动,按顺序收集四类信息:is-enabled 的启用状态,status 的失败状态和退出码,journalctl -b -u 的本次开机日志,systemctl cat/show 的实际 unit 与依赖。确认原因后再启用服务、修改 drop-in、调整依赖顺序或重启策略,最后通过一次真实重启验收。
不要直接修改 /usr/lib/systemd/system/ 下的软件包 unit;不要在生产机上无备份地堆叠 Requires=;也不要把 enable --now 当成万能修复。开机自启的可靠性来自明确的启动条件、可观察的失败日志,以及重启后能复现并验证的处理结果。




