在 Debian 系統中讓程序/腳本開機自動運行,現代標準是 systemd 服務單元,兼容舊有 /etc/rc.local 與 SysVinit /etc/init.d/ 腳本。本文詳細演示三種方式的配置、優缺點、調試技巧、常見坑點。

# /etc/systemd/system/myapp.service
[Unit]
Description=My Application
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.yaml
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
# 環境變量
Environment=APP_ENV=production
#EnvironmentFile=/etc/myapp/env
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable myapp.service # 開機自啟
systemctl start myapp.service # 立即啟動
systemctl status myapp.service # 查看狀態
journalctl -u myapp.service -f # 實時日志
| Type | 適用場景 | 特點 |
|---|---|---|
| simple | 前台運行程序(默認) | systemd 認為啟動即服務就緒 |
| forking | 傳統雙重 fork 守護進程 | 需 PIDFile,父進程退出後子進程為主進程 |
| oneshot | 執行一次即退出的腳本 | 配合 RemainAfterExit=yes 保持 active 狀態 |
| notify | 程序調用 sd_notify() 通知就緒 | 最精確,需程序支持 |
| dbus | D-Bus 激活服務 | 較少用 |
# Debian 9+ 默認無 rc.local,需創建
cat > /etc/rc.local <<'EOF'
#!/bin/bash
# 這裡添加開機命令,需完整路徑
/opt/scripts/start_firewall.sh
/usr/local/bin/sslocal -c /etc/shadowsocks.json -d start
exit 0
EOF
chmod +x /etc/rc.local
# 創建 systemd 服務拉起 rc.local
cat > /etc/systemd/system/rc-local.service <<'EOF'
[Unit]
Description=/etc/rc.local Compatibility
After=network.target
[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
StandardOutput=tty
RemainAfterExit=yes
GuessMainPID=no
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable rc-local.service
systemctl start rc-local.service
chmod +x)、Shebang 指定解釋器(#!/bin/bash)。nohup cmd & 或 setsid,否則阻塞啟動流程。
#!/bin/bash
### BEGIN INIT INFO
# Provides: aria2c
# Required-Start: $network $local_fs $remote_fs
# Required-Stop: $network $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Aria2 Download Manager
# Description: Aria2 Download Manager
### END INIT INFO
NAME=aria2c
USER=pi
DAEMON=/usr/bin/$NAME
PIDFILE=/var/run/$NAME.pid
CONF=/home/$USER/.aria2/aria2.conf
ARGS="--conf-path=${CONF}"
. /lib/lsb/init-functions
case "$1" in
start)
log_daemon_msg "Starting $NAME" "$NAME"
start-stop-daemon --start --quiet --background --make-pidfile --pidfile $PIDFILE --chuid $USER --exec $DAEMON -- $ARGS
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping $NAME" "$NAME"
start-stop-daemon --stop --quiet --pidfile $PIDFILE
log_end_msg $?
;;
restart|reload|force-reload)
log_daemon_msg "Restarting $NAME" "$NAME"
start-stop-daemon --stop --quiet --retry 5 --pidfile $PIDFILE
start-stop-daemon --start --quiet --background --make-pidfile --pidfile $PIDFILE --chuid $USER --exec $DAEMON -- $ARGS
log_end_msg $?
;;
status)
status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $?
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 2
;;
esac
exit 0
chmod +x /etc/init.d/aria2c
update-rc.d aria2c defaults # 生成 rc?.d 鏈接
service aria2c start
service aria2c status
| 維度 | systemd | rc.local | SysVinit |
|---|---|---|---|
| 推薦度 | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐ |
| 依賴管理 | 完善 | 無 | 基礎 |
| 進程監管/重啟 | 內建 | 無 | 手動實現 |
| 日志集中化 | journalctl | 無 | 無 |
| 資源限制 | cgroups 完整支持 | 無 | 有限 |
| 調試便利性 | systemd-analyze, journalctl | 困難 | 一般 |
| 適用場景 | 所有生產服務 | 簡單一次性腳本、測試 | 極舊軟件、嵌入式 |
| 現象 | 原因 | 解決 |
|---|---|---|
| systemd 服務啟動即失敗 | ExecStart 路徑錯誤、權限不足、依賴服務未就緒 | systemctl status、journalctl -u、檢查 After= |
| rc.local 不執行 | rc-local.service 未啟用、腳本無執行權限、Shebang 缺失 | 檢查 systemctl status rc-local、chmod +x |
| 開機卡在「A start job is running」 | 服務 Type=simple 但程序後台化、或阻塞等待 | 改 Type=forking/notify、或程序前台運行 |
| 環境變量不生效 | systemd 不繼承 Shell 環境 | 用 Environment= / EnvironmentFile= 顯式聲明 |
# ~/.config/systemd/user/myapp.service
[Unit]
Description=My User Service
[Service]
ExecStart=/home/user/.local/bin/myapp
Restart=on-failure
[Install]
WantedBy=default.target
# 啟用 (用戶登錄後生效,或啟用 linger 開機即啟動)
systemctl --user daemon-reload
systemctl --user enable --now myapp.service
loginctl enable-linger user # 開機自啟用戶服務
選擇 systemd 服務單元作為首選,rc.local 臨時過渡,SysVinit 僅兼容舊系統。規範的服務化管理是 Linux 運維自動化、可觀測、高可用的基石。