
服务器关机维护时,原定凌晨运行的备份没执行;开机后,任务也没有自动补上。如果任务由 systemd timer 调度,可以先检查它使用的时间规则,以及是否启用了 Persistent=true。这个选项能为符合条件的日历定时器补一次触发,但不会逐条重放停机期间所有遗漏的时间点,也不负责保证任务成功。[1]
本文面向使用 systemd 系统服务管理器的 Linux 服务器,讨论系统级 .timer 与 .service。用户级定时器还涉及用户服务管理器的生命周期,不直接套用本文的开机启用步骤。文档核验日期:2026 年 10 月 19 日;操作前请以服务器上的 man systemd.timer 和已安装版本为准。
先区分调度没发生,还是脚本执行失败
以下示例把任务命名为 site-report,请替换为实际单元名。这些命令用于查看状态,不会主动执行任务。
systemctl list-timers --all
systemctl status site-report.timer site-report.service --no-pager
systemctl cat site-report.timer site-report.service
journalctl -u site-report.timer -u site-report.service --since '2 days ago' --no-pager
检查定时器是否已加载、是否处于活动状态、下一次触发时间是什么,以及服务有没有对应的启动和结束记录。任务触发过但脚本失败,需要检查脚本路径、运行用户、目录权限、依赖和退出结果;仅修改定时器不会修好脚本。
对于普通的 Type=oneshot 服务,如果没有设置 RemainAfterExit=yes,任务完成后回到非活动状态是预期行为。不能单凭它没有长期显示 active 就判断执行失败,应结合日志和执行结果。[4]
Persistent 只影响 OnCalendar 定时器
OnCalendar= 按日历时间安排任务,例如每天某个时刻。OnBootSec=、OnActiveSec=、OnUnitActiveSec= 等则相对于开机、单元激活或上一次激活等事件计时。[1]
官方文档说明,Persistent=true 会把服务上次被触发的时间保存到磁盘。定时器再次激活时,如果在它未活动的期间本来至少应该触发一次,就补一次服务触发。它只对配置了 OnCalendar= 的定时器生效,默认值为 false。[1]
例如,一个每天执行一次的任务停机三天,重新激活定时器后,不能依赖这个设置自动生成三份按历史日期划分的报表。如果业务需要逐日补齐,应由程序维护处理进度和待处理日期,并让一次执行能够安全处理积压。
补触发也不等于失败重试。任务被触发后返回错误,Persistent 不会因为业务失败就把这次执行自动变成待补队列。重试需要另行设计,并考虑重复写入的影响。
一份可审阅的系统级配置示例
下面的示例用于每天生成一份站点报告。sitejobs 用户和组、脚本及工作目录需要事先准备,并按最小权限授权;它不是可以不改路径就直接部署的备份工具。
/etc/systemd/system/site-report.service:
[Unit]
Description=Generate site report
[Service]
Type=oneshot
User=sitejobs
Group=sitejobs
WorkingDirectory=/srv/site-jobs
ExecStart=/usr/local/sbin/site-report
/etc/systemd/system/site-report.timer:
[Unit]
Description=Schedule site report
[Timer]
OnCalendar=*-*-* 02:30:00 Asia/Shanghai
Persistent=true
AccuracySec=1min
Unit=site-report.service
[Install]
WantedBy=timers.target
这里明确使用上海时区的每日 02:30,避免迁移到其他时区的主机后含义变化。systemd 日历表达式支持时区,实际解析结果应使用目标服务器的工具确认。[2][3]
AccuracySec 允许调度在指定窗口内合并,并不意味着必须精确到该时刻的第一秒。若另有 RandomizedDelaySec,补触发也受随机延迟影响。检查实际配置时,应连同 drop-in 覆盖文件一起看,不能只看单个原始文件。[1]
周期任务一般不需要 RemainAfterExit=yes。定时器到点时,如果目标服务已经处于活动状态,systemd 不会因此重新启动它,也不会额外创建一个并发实例。因此,持续保持 active 的 oneshot 配置可能妨碍后续触发。[1][4]
启用之前,先检查表达式和补执行影响
先备份已有单元文件及其覆盖配置,再在测试环境准备脚本、用户和目录。下面两条命令用于检查配置及时间表达式,本身不启动业务脚本。
systemd-analyze verify /etc/systemd/system/site-report.service /etc/systemd/system/site-report.timer
systemd-analyze calendar --iterations=5 '*-*-* 02:30:00 Asia/Shanghai'
检查输出是否包含预期的时区和后续触发点。如果本机版本不支持相关参数,应查看本机帮助和手册调整,不要以较新在线文档代替本机能力判断。[3]
确认任务现在执行也安全后,再加载并启用定时器:
sudo systemctl daemon-reload
sudo systemctl enable --now site-report.timer
--now 会立即激活定时器。对满足补触发条件的持久化定时器,服务可能很快执行,因此应在执行这条命令前确认容量、依赖和维护窗口。daemon-reload 用于重新加载单元定义;它本身不代表任务已经成功运行。
时间、长任务和重复调度要分别排查
当下一次触发时间不符合预期时,查看服务器时间与同步状态,再对照日历表达式:
timedatectl status
systemd-analyze calendar '*-*-* 02:30:00 Asia/Shanghai'
日历定时器依赖墙上时钟。systemd 为这类定时器设置了与时间初始化相关的排序依赖,但机器时钟是否已经准确,仍需结合实际时间同步服务核查。[1]
如果脚本的执行时间超过调度间隔,目标服务运行期间的后续触发不会创建同一服务的并发副本。这也意味着定时器不会替业务保存完整的积压任务列表。程序需要自行记录处理到哪里,以及下一次应继续处理什么。[1]
还应检查是否存在同一脚本的 cron 条目、旧定时器、其他服务器上的调度器或人工执行入口。单个服务单元的运行状态不能阻止其他入口重复处理业务。报表、备份和结算类程序应结合任务批次标识、持久化进度或合适的锁机制设计幂等处理。
验收补执行时,使用无副作用的测试任务
建议在测试环境创建只记录时间的临时任务,先让定时器正常触发一次,再停用一段时间,跨过一个或多个计划时间后重新激活。核对日志,确认补触发行为符合预期,而非期待每个遗漏时间点都单独重放。
随后再验证真实业务脚本的退出结果、产物和完成记录。本文提供的是验收方法,没有对你的服务器执行重启或生产任务测试。
如果需要回滚,可以先停用该定时器:
sudo systemctl disable --now site-report.timer
停用定时器不会自动终止已经开始的目标服务;是否需要停止正在运行的任务,应依据业务的中断和恢复能力决定。恢复备份的配置后重新加载,再确认应启用哪一套调度入口。
为日历定时器配置 Persistent=true,可以补上停机期间遗漏的一次触发。历史批次补齐、失败重试、执行结果校验和重复任务控制,则应由业务脚本或任务队列明确实现。
官方参考资料
[1] systemd.timer:时间规则、Persistent、精度与目标服务行为
https://www.freedesktop.org/software/systemd/man/latest/systemd.timer.html
[2] systemd.time:日历表达式与时区
https://www.freedesktop.org/software/systemd/man/latest/systemd.time.html
[3] systemd-analyze:calendar、verify 与参数
https://www.freedesktop.org/software/systemd/man/latest/systemd-analyze.html
[4] systemd.service:Type=oneshot 与 RemainAfterExit
https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html




