应直接使用 systemd 服务而非 /etc/rc.local 或 /etc/profile.d/,因前者默认不启用,后者非开机机制;需编写最小化 .service 文件,指定 After=network-online.target、绝对路径 ExecStart、显式 User 和 RemainAfterExit=yes,并通过 systemctl 命令验证启用与执行。

直接用 systemd,别碰 /etc/rc.local 或 /etc/profile.d/ ——前者默认不执行,后者根本不是开机机制。
为什么 /etc/rc.local 经常失效
不是你脚本写错了,是 systemd 根本不启动它:rc-local.service 默认处于 inactive (dead) 状态。即使你加了 #!/bin/bash、chmod +x,也毫无作用。
-
PATH极其有限(仅/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin),没加载任何用户环境 - 脚本里用了
sudo、su - user -c或交互式命令?会卡住或静默失败 -
After=network.target不等于网络就绪——DNS、远程服务可能还没响应,得用After=network-online.target - 想依赖
$HOME、$DISPLAY或 shell 初始化变量?全都没有
用 systemd 写最简 .service 文件
别抄模板,只保留真正需要的字段。一个能立刻生效的最小配置:
[Unit] Description=My Startup Script After=network-online.target <p>[Service] Type=oneshot ExecStart=/opt/myscript.sh RemainAfterExit=yes User=deploy</p><p>[Install] WantedBy=multi-user.target
-
Type=oneshot适合一次性脚本;长期运行进程(如 Python Web 服务)改用Type=simple,且确保进程不自己fork成 daemon -
ExecStart=必须是绝对路径,不能用~或$HOME - 要以非 root 用户运行?加
User=deploy,别在脚本里用su -
RemainAfterExit=yes让 service 状态保持active,方便后续用systemctl is-active判断是否已执行
启用后怎么确认真跑起来了
别等重启,立刻验证:
- 重载配置:
systemctl daemon-reload - 启用开机自启:
systemctl enable myscript.service - 立即启动并看输出:
systemctl start myscript.service && journalctl -u myscript.service -n 20 -f - 检查是否注册成功:
systemctl is-enabled myscript.service应返回enabled - 查启动时机是否符合预期:
systemctl list-dependencies --after myscript.service,确认它确实在network-online.target之后
@reboot crontab 的适用边界
它不是 systemd 的替代方案,而是有明确限制的备选:
- 用户级任务(如个人定时初始化):用
crontab -e加@reboot /path/to/script.sh,安全且无需 root - 系统级任务(需 root 权限):编辑
/etc/crontab,格式为@reboot root /path/to/script.sh - 必须用绝对路径——
cron的PATH比rc.local还窄 - 它比
rc.local执行更晚(通常启动后 45–60 秒),适合对网络延迟不敏感、但需完整用户环境的任务
真正容易被忽略的是环境差异:无论用哪种方式,systemd 服务默认没有 $HOME、没有 $DISPLAY、PATH 极短;rc.local 几乎没 shell 初始化过程;@reboot cron 更接近登录 shell,但依然缺失很多变量。任何依赖这些的脚本,不显式设置就会静默失败。


















