從 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

這裡其實包含兩件事:

可以使用以下指令查看狀態:

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

接著還要考慮:

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