從 cron 到 systemd timer:Linux 排程任務實務入門
分類:Linux
在 Linux 上需要定期執行程式時,第一個想到的工具通常是 cron。
例如每天凌晨 3 點執行備份:
0 3 * * * /root/bin/backup.sh
簡單、可靠,而且幾乎所有 Linux 管理者都知道怎麼使用。
但在採用 systemd 的現代 Linux 發行版中,其實還有另一套排程機制:
systemd timer
如果只是「每天執行一個 command」,cron 已經完全足夠。systemd timer 真正的優勢在於,它把「排程」與「服務執行」整合進 systemd,因此可以更容易追蹤執行狀態、查看 log、處理停機期間錯過的排程,以及管理任務本身。
這篇文章就從一個熟悉 cron 的 Linux 使用者角度,看看 systemd timer 如何使用,以及它和 cron 的差異。
systemd timer 的基本概念
cron 把「什麼時候執行」與「執行什麼」寫在同一行:
0 3 * * * /root/bin/backup.sh
systemd 則把這兩件事情拆開。
通常會有兩個 unit:
backup.timer
backup.service
它們的關係可以理解成:
backup.timer
│
│ 到達指定時間
▼
backup.service
│
▼
/root/bin/backup.sh
.timer 負責:
什麼時候執行?
.service 負責:
要執行什麼?
這是理解 systemd timer 最重要的一個概念。
建立第一個 systemd timer
假設有一支備份程式:
/root/bin/backup.sh
希望每天凌晨 3 點執行。
先建立 service:
vim /etc/systemd/system/backup.service
內容:
[Unit]
Description=Daily Backup
[Service]
Type=oneshot
ExecStart=/root/bin/backup.sh
Type=oneshot 表示這是一個執行完成後就結束的工作,而不是像 Web Server 那樣長時間常駐的 daemon。
接著建立 timer:
vim /etc/systemd/system/backup.timer
內容:
[Unit]
Description=Run Daily Backup
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
其中:
OnCalendar=*-*-* 03:00:00
表示:
每天 03:00 執行。
概念上相當於 cron:
0 3 * * *
在沒有另外指定 Unit= 的情況下,backup.timer 預設會觸發同名的 backup.service。
啟用 timer
新增或修改 systemd unit 後,先讓 systemd 重新讀取設定:
systemctl daemon-reload
接著啟用並立即啟動 timer:
systemctl enable --now backup.timer
這裡其實包含兩件事:
enable:設定開機時自動啟用--now:現在立即啟動 timer
可以使用以下指令查看狀態:
systemctl status backup.timer
要注意的是,啟動 backup.timer 並不代表現在立刻執行備份,而是讓 timer 開始等待下一個符合條件的觸發時間。
查看目前有哪些 timer
systemd timer 很方便的一點,就是可以直接查看目前的排程狀態。
systemctl list-timers
輸出大致會像:
NEXT LEFT LAST PASSED UNIT
Mon 2026-09-28 03:00:00 8h left Sun 2026-09-27 03:00:01 16h ago backup.timer
幾個主要欄位:
| 欄位 | 說明 |
|---|---|
NEXT |
下一次執行時間 |
LEFT |
距離下一次執行還有多久 |
LAST |
上一次觸發時間 |
PASSED |
距離上一次觸發經過多久 |
UNIT |
timer unit 名稱 |
如果連目前 inactive 的 timer 都想看:
systemctl list-timers --all
只查看特定 timer:
systemctl list-timers backup.timer
相比之下,看到:
0 3 * * *
雖然知道它每天凌晨 3 點執行,但 cron expression 本身並不直接告訴你:
- 上一次什麼時候觸發?
- 下一次什麼時候觸發?
- 工作執行結果如何?
systemd 在排程狀態的可觀察性上直觀許多。
手動測試 service
建立 timer 之後,不需要真的等到凌晨 3 點才能測試。
直接執行:
systemctl start backup.service
就可以手動執行一次工作。
接著查看結果:
systemctl status backup.service
成功完成的 oneshot service 通常可以從狀態與 exit code 確認執行結果,例如:
status=0/SUCCESS
如果程式以非零 exit code 結束,也可以從 service 狀態與 journal 看出失敗。
因此實際維護時,我通常會把「排程」與「工作本身」分開看待。
想測試工作:
systemctl start backup.service
想管理排程:
systemctl start backup.timer
systemctl stop backup.timer
這樣在除錯時會清楚很多。
使用 journal 管理執行紀錄
cron 常見的寫法可能是:
0 3 * * * /root/bin/backup.sh >> /var/log/backup.log 2>&1
接著還要考慮:
backup.log是否會一直增長?- 是否需要設定 logrotate?
- stdout / stderr 是否都有正確 redirect?
systemd service 的 stdout 與 stderr 預設會進入 journal,因此可以直接透過 journalctl 查詢。
查看某個 service 的執行紀錄:
journalctl -u backup.service
查看最近 100 行:
journalctl -u backup.service -n 100
即時追蹤:
journalctl -fu backup.service
查看今天的紀錄:
journalctl -u backup.service --since today
因此排程工作的 stdout、stderr 與 service 執行資訊,可以直接整合進 systemd journal,不需要每個工作都自行建立一套 log redirect。
至於 journal 的保存時間、容量與是否持久化,則仍取決於系統本身的 journald 設定。
Persistent=true:避免停機期間直接漏掉排程
假設備份設定為:
OnCalendar=*-*-* 03:00:00
也就是每天凌晨 3 點執行。
但某一天 NAS 的狀態是:
02:00 NAS 關機
03:00 原本應該執行備份
08:00 NAS 開機
傳統 cron 在 03:00 時機器沒有運作,這次排程通常就直接錯過,必須等到下一個符合 cron expression 的時間才會再次執行。
systemd timer 則可以設定:
Persistent=true
對 OnCalendar= 這類 calendar timer 而言,systemd 會保存 timer 上一次觸發的相關資訊。如果 timer 在 inactive 或系統停機期間錯過應執行的時間,當 timer 再次啟動時,可以補觸發一次工作。
對 Desktop 來說可能只是方便,但對 NAS 的每日備份而言非常實用。
我真正關心的通常不是:
一定要凌晨 3 點執行。
而是:
每天的備份不要因為凌晨 3 點剛好關機,就直接漏掉。
這也是我認為 systemd timer 很適合備份工作的原因之一。
systemd timer 的時間寫法
cron:
0 3 * * *
systemd:
OnCalendar=*-*-* 03:00:00
每天午夜:
OnCalendar=*-*-* 00:00:00
每天凌晨 2:30:
OnCalendar=*-*-* 02:30:00
每週日凌晨 3 點:
OnCalendar=Sun *-*-* 03:00:00
每個月 1 日凌晨 4 點:
OnCalendar=*-*-01 04:00:00
systemd 也支援較容易閱讀的特殊 expression,例如:
OnCalendar=daily
或:
OnCalendar=weekly
如果不確定自己的 calendar expression 是否正確,可以使用:
systemd-analyze calendar '*-*-* 03:00:00'
它會解析 expression,並顯示下一次觸發時間。
在正式修改 timer 前,先用 systemd-analyze calendar 驗證時間設定,是很實用的習慣。
修改 timer 後要做什麼
例如修改:
vim /etc/systemd/system/backup.timer
把:
OnCalendar=*-*-* 03:00:00
改成:
OnCalendar=*-*-* 04:00:00
修改 unit file 後執行:
systemctl daemon-reload
systemctl restart backup.timer
接著確認:
systemctl list-timers backup.timer
檢查 NEXT 是否已經變成預期的時間。
也可以先用:
systemd-analyze calendar '*-*-* 04:00:00'
確認 expression 本身是否符合預期。
常用管理指令
啟用並立即啟動 timer:
systemctl enable --now backup.timer
停止 timer:
systemctl stop backup.timer
停用開機自動啟動:
systemctl disable backup.timer
重新啟動:
systemctl restart backup.timer
查看 timer 狀態:
systemctl status backup.timer
查看下一次觸發時間:
systemctl list-timers backup.timer
手動執行工作:
systemctl start backup.service
查看工作狀態:
systemctl status backup.service
查看 log:
journalctl -u backup.service
修改 unit 後重新讀取:
systemctl daemon-reload
對一般日常管理而言,熟悉這幾組指令基本上就已經足夠。
cron 與 systemd timer 比較
兩者其實不是「誰淘汰誰」的關係。
| 功能 | cron | systemd timer |
|---|---|---|
| 固定時間排程 | ✓ | ✓ |
| 設定簡單 | ✓✓✓ | ✓✓ |
| 查看下次觸發時間 | 較不直觀 | ✓ |
| 查看上次觸發時間 | 需額外確認 | ✓ |
| Service 執行狀態 | 需自行處理 | ✓ |
| Journal log | 需自行整合 | ✓ |
| 錯過 calendar 排程後補跑 | 通常需其他機制 | Persistent=true |
| systemd dependency | — | ✓ |
| Timeout / resource control | 需外部工具或額外設定 | ✓ |
| 與 Linux service 管理整合 | — | ✓ |
cron 最大的優勢仍然是:
簡單。
例如:
*/5 * * * * /root/bin/check.sh
一行就完成。
如果只是簡單的小工具,我不認為有必要為了「比較新」就全部改成 systemd timer。
什麼時候值得使用 systemd timer
我的分法很簡單。
如果工作只是:
每隔一段時間
│
▼
執行一個簡單 command
│
▼
完成
cron 非常適合。
但如果這個工作屬於:
Backup
Database dump
Synchronization
Maintenance
Log processing
重要的自動化工作
而且你會在意:
有沒有執行?
什麼時候執行?
有沒有成功?
失敗原因是什麼?
關機時錯過怎麼辦?
那 systemd timer 就開始展現它的優勢。
重點不是 systemd timer 能不能「定時執行」,而是它能不能讓這個排程工作更容易被管理與觀察。
實際備份架構
例如 NAS 每天要把 Cloud Storage 同步到本地,再建立備份:
Cloud Storage
│
│ rclone
▼
Local Mirror
│
│ Restic
▼
Backup Repository
可以設計成:
cloud-backup.timer
│
▼
cloud-backup.service
│
▼
backup script
│
├── rclone sync/copy
├── restic backup
└── restic retention
如果第一階段同步失敗,script 可以直接以非零 exit code 結束,避免後續流程繼續執行。
systemd 則會保留這次 service 的執行結果,相關 stdout / stderr 也能從 journal 查詢。
這時候關心的已經不只是:
cron 今天應該有跑。
而是:
今天的備份工作實際上有沒有成功完成?
這就是 systemd timer 與 service 搭配後,在實務維運上更有價值的地方。
結論
如果已經用了很多年 cron,第一次看到 systemd timer 可能會有一個很直接的疑問:
為什麼原本一行 cron 可以解決的事情,現在要寫
.timer和.service兩個檔案?
這個疑問很合理。
systemd timer 的價值並不是讓:
0 3 * * *
變得更短。
它真正改善的是排程工作執行之後的管理與可觀察性。
透過 systemd,可以比較容易確認:
上次什麼時候觸發
下一次什麼時候觸發
工作成功還是失敗
stdout / stderr 輸出了什麼
停機期間是否錯過排程
service 本身目前是什麼狀態
所以我的使用原則是:
簡單、即使漏掉也沒關係的工作,用 cron。
備份、同步、維護等需要確認執行結果的重要工作,用 systemd timer。
cron 依然很好用,也沒有必要因為 systemd timer 的存在就全面取代它。
systemd timer 的價值,是把「定時執行一個 command」進一步整合成「管理一個有排程、有狀態、有 log 的系統工作」。
對 NAS 備份這類需要確認執行結果的任務而言,這個差異就很有價值。
Tags:Linux · systemd · cron · systemd timer