🖥️ 自訂 systemd 服務

撰寫 .service 單元讓應用成為系統服務

學習撰寫 systemd service unit 檔案,將自己的應用程式設定為系統服務,實現開機自動啟動、崩潰自動重啟(Restart=on-failure)與資源限制。

Service 檔案結構

systemd service unit 檔案放在 /etc/systemd/system/,由三個主要區塊組成:

[Unit]       # 服務的描述與依賴關係
[Service]    # 實際的執行設定
[Install]    # 安裝設定(設定 enable 時的行為)

完整範例(Node.js API 服務)

$ sudo vim /etc/systemd/system/myapi.service
[Unit]
Description=My Node.js API Server
Documentation=https://github.com/example/myapi
# 網路就緒後才啟動
After=network.target
# 若 MySQL 存在,則在其啟動後才啟動
After=mysql.service
Wants=mysql.service

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/myapi

# 環境變數(機密資訊用 EnvironmentFile)
Environment="NODE_ENV=production"
Environment="PORT=3000"
EnvironmentFile=-/opt/myapi/.env    # - 前綴表示檔案不存在時不報錯

ExecStart=/usr/bin/node /opt/myapi/server.js
ExecReload=/bin/kill -HUP $MAINPID  # reload 指令(可選)

# 崩潰時自動重啟
Restart=on-failure
RestartSec=5s                         # 重啟前等待 5 秒
StartLimitIntervalSec=60             # 60 秒內
StartLimitBurst=5                    # 最多重啟 5 次(防止無限重啟)

# 資源限制
LimitNOFILE=65536      # 最大開啟檔案數
MemoryMax=512M         # 記憶體上限(超過則被終止)

# 標準輸出導向 systemd journal
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapi

[Install]
WantedBy=multi-user.target
$ sudo systemctl daemon-reload        # 必須在修改 service 檔案後執行
sudo systemctl enable --now myapi  # 啟用並立即啟動

Type 類型說明

Type說明適用情境
simple(預設)ExecStart 啟動後即視為就緒大多數常駐程序
forking主程序 fork 後退出(傳統 daemon)Nginx、Apache
oneshot執行完就結束,適合一次性工作資料庫 migration、初始化腳本
notify程序用 sd_notify() 告知 systemd 已就緒支援此協定的程式(如 gunicorn)
exec等待 ExecStart 執行完成才視為就緒需要確保啟動完成後才繼續的場景

Restart 策略

Restart=重啟時機
no(預設)不重啟
always無論退出原因,總是重啟
on-failure非正常退出(回傳碼非 0、被訊號殺死)時重啟
on-abnormal被訊號殺死或逾時時重啟
on-watchdogwatchdog 逾時時重啟

環境變數設定

# 方式 1:直接在 service 檔案中設定
Environment="KEY=value"
Environment="KEY2=value2"

# 方式 2:使用獨立的環境變數檔案(推薦機密設定)
EnvironmentFile=/opt/myapp/.env
# .env 格式:
# DB_PASSWORD=secret
# API_KEY=abc123
⚠️ 機密資訊(密碼、API key)不要直接寫在 service 檔案中,改用 EnvironmentFile 並設定嚴格的檔案權限(chmod 600 .env)。

管理與除錯

操作指令
重新載入 unit 設定(修改後必做)sudo systemctl daemon-reload
查看服務狀態(含最近 log)sudo systemctl status myapi
即時查看服務日誌sudo journalctl -u myapi -f
查看最近 100 行日誌sudo journalctl -u myapi -n 100
查看啟動失敗原因sudo journalctl -u myapi -b --priority=err
驗證 service 檔案語法systemd-analyze verify /etc/systemd/system/myapi.service
查看啟動時間systemd-analyze blame