systemd timer 与 service 文件配对工作,timer 定义触发时间,service 定义执行动作;需显式声明环境、路径及 shell 特性,配合 persistent 和 remainafterexit 提升可靠性。

Systemd Timer 不是 crontab 的“升级版按钮”,而是另一套设计逻辑——它把“什么时候触发”和“执行什么”彻底拆开,靠两个文件协同工作。迁移不难,但必须理解这个基本结构,否则容易静默失败。
核心结构:timer 和 service 必须配对
每个定时任务需要两个文件,名字相同、后缀不同:
- myjob.timer:只管时间规则(比如每天凌晨2点),不执行任何命令
- myjob.service:只管具体动作(比如运行 /usr/local/bin/backup.sh),不关心时间
systemd 通过文件名自动关联两者。timer 文件里无需写路径或脚本,只需用 Unit=myjob.service 明确指定对应服务。
时间配置:OnCalendar 比 cron 更直观但语法稍异
常用格式直接对应日常表达,例如:
-
OnCalendar=*-*-* 02:00:00→ 每天凌晨2点整 -
OnCalendar=Mon..Fri *-*-* 09:00:00→ 工作日早上9点 -
OnCalendar=weekly→ 每周一凌晨0点(等价于*-*-* Mon *:00:00)
避免用 hourly 这类模糊词,它实际表示每小时整点,可能和预期有偏差。验证写法可用:systemd-analyze calendar "2026-06-15 03:00:00" 或 systemd-analyze calendar "Mon..Fri 08:30"。
环境与执行:必须显式声明,不能依赖默认
cron 默认用 /bin/sh 解析命令,而 systemd service 不走 shell —— 所以 ExecStart=echo $HOME | mail -s test 会失败(变量不展开、管道无效)。正确做法是:
- 在
.service中用Environment="PATH=/usr/local/bin:/usr/bin:/bin"显式设路径 - 用
WorkingDirectory=/root指定工作目录,避免相对路径出错 - 需要 shell 特性(如管道、变量、重定向)时,明确调用:
ExecStart=/bin/bash -c 'echo hello | logger -t mytask' - 输出建议交给 journalctl 管理:
StandardOutput=journal、StandardError=journal,查日志用journalctl -u myjob.service
可靠性增强:Persistent 和 RemainAfterExit
两个关键选项让任务更健壮:
-
Persistent=true(写在 timer 的 [Timer] 段):如果机器关机/重启错过触发时间,开机后立刻补执行一次 -
RemainAfterExit=yes(写在 service 的 [Service] 段):防止因脚本退出码非0导致 timer 停止后续触发
这对备份、清理类任务尤其重要,避免因某次失败就永久中断整个周期。











