首页
/ Dokku 定时任务(Scheduled Cron Tasks)完全指南:从 app.json 声明到 cron:run 实战

Dokku 定时任务(Scheduled Cron Tasks)完全指南:从 app.json 声明到 cron:run 实战

2026-09-09 16:00:27作者:田桥桑Industrious

本篇技术指南围绕 Dokku 内置的定时任务(Scheduled Cron Tasks)能力展开,详细讲解如何通过 app.json 中的 cron 键为应用声明周期性执行的命令,如何用 cron:setcron:listcron:suspendcron:resumecron:runcron:report 等命令管理任务生命周期,以及如何通过 vector 集成持久化任务输出。读完本文,你将能够为任何 Dokku 应用配置可靠的定时任务,理解其执行环境、超时回收机制与调度器差异,并掌握自管理 cron 的高级用法。

功能概述与命令速览

Dokku 从 0.23.0 版本开始提供内置的定时任务支持(cron 插件,位于 plugins/cron)。它把应用 app.json 中的 cron 声明转换为调度器可执行的定时任务:对使用宿主 crontab 的调度器(如 docker-local)写入 dokku 用户 crontab,对自带 cron 后端的调度器(如 k3s)则原生调度。所有命令如下:

cron:list <app> [--format json|stdout]                      # 列出应用的定时任务
cron:report [<app>] [<flag>]                                # 显示应用 cron 报告
cron:resume <app> <cron_id>                                 # 恢复一个 cron 任务
cron:run <app> <cron_id> [--detach] [--ttl-seconds SECONDS] # 即时运行一个 cron 任务
cron:set [--global|<app>] <key> <value>                     # 设置或清除应用的 cron 属性
cron:suspend <app> <cron_id>                                # 挂起一个 cron 任务

Dokku 托管 Cron:通过 app.json 声明任务

Dokku 自动调度 dokku run 命令,其入口是应用 app.json 文件中的 cron 键。从源码看,app.jsonplugins/app-json/appjson.go 解析,其中 AppJSON.CronCronTask 的列表,每个任务包含 commandmaintenancescheduleconcurrency_policy 四个字段。

声明任务

以下 app.json 示例等效于每天执行一次 dokku run $APP npm run send-email

{
  "cron": [
    {
      "command": "npm run send-email",
      "schedule": "@daily"
    }
  ]
}

app.json 的默认搜索路径与部署方式相关;如需从 monorepo 等场景指定其他位置,可通过 app-json:set <app> appjson-path <path> 设置(值为相对于基础搜索目录的路径),详见 deployment-tasks.md

任务属性说明

每个 cron 任务支持以下属性:

属性 说明
command 在构建出的应用镜像内执行的命令,也可以直接引用 Procfile 条目
schedule cron 兼容的调度定义,决定命令何时运行。秒通常不支持
maintenance 布尔值,决定该任务是否处于维护(不可执行)状态
concurrency_policy 字符串(默认 allow),控制任务与自身是否可并发执行。合法值:allow(允许并发)、forbid(已有任务运行则新任务直接退出)、replace(终止已有任务并启动新任务)

每个应用可以声明零个或多个 cron 任务。任务的验证发生在构建产物生成之后、应用部署之前;cron 调度表则在部署后阶段(post-deploy)更新。也就是说,一份非法的时间表或命令会在部署时直接报错,而不是等到调度触发时才暴露。

plugins/cron/cron.go 的实现可以看到,schedule 使用 robfig/cron/v3 解析器校验,其标志位组合为 Minute | Hour | Dom | Month | Dow | Descriptor——因此不支持秒字段,但支持 @daily@hourly 等描述符。concurrency_policyallow/forbid/replace 时会返回"Invalid cron concurrency policy"错误;commandschedule 为空时也会在部署阶段报错(WarnToFailure 模式)。

任务执行时长上限与回收

cron 任务最长可运行 24 小时,超过后会被系统回收:

  • docker-local 调度器通过每 5 分钟运行一次的 dokku ps:retire 扫描回收超时任务,因此任务实际可能超时最多约 5 分钟;
  • k3s 调度器则直接通过 Job 的 activeDeadlineSeconds 强制执行期限。

源码中 DefaultTTLSeconds 常量定义为 86400plugins/cron/cron.go),docker-local 会将其作为 com.dokku.active-deadline-seconds 标签盖印到容器上,k3s 则渲染为 CronJob 的 activeDeadlineSeconds

任务执行环境须知

运行定时任务时有以下几点需要注意:

  • 定时任务在应用运行时的环境中执行;如果应用镜像不存在,命令可能执行失败;
  • 调度基于宿主服务器时区(通常为 UTC);
  • 目前 cron 模板中只指定了 PATHSHELL 两个环境变量:
    • MAILTO 可通过 cron:set 设置;
    • MAILFROM 可通过 cron:set 设置;
  • 每个定时任务都在一个一次性 run 容器内执行,因此会继承为 run 容器配置的所有 docker-options;任务之间绝不共享资源;
  • 定时任务按调度器分别支持:使用宿主 crontab 的调度器(如 docker-local)会把 app.json 中的 cron 任务写入 dokku 用户 crontab;自己管理 cron 后端的调度器(如 k3s)则原生调度;
  • 宿主 crontab 调度器(如 docker-local)管理的所有应用的任务都写入同一个、归属于 dokku 用户的 crontab 文件,该 crontab 应视为 Dokku 专用,不要手动写入其他条目;
  • command 会被分词(tokenize)后直接在容器内 exec,不会解释 ;&&|> 等 shell 特性。包含裸 shell 操作符的命令在部署时验证 app.json 会被拒绝,因此格式错误的 cron 命令会导致部署失败而非静默运行失败。如果需要 shell 语义,请显式包裹命令,例如 "sh -c 'do-thing > /var/log/x.log'"
  • 任务输出写入容器的 stdout 和 stderr,可通过 Dokku 的 vector 集成持久化(见下文);
  • cron 任务不能在 app.json 中声明日志文件路径。写入 dokku 用户 crontab 的只有 dokku cron:run <app> <cron_id> 行,任何来自部署仓库的路径都不会被插值进去。

关于命令分词,plugins/cron/cron.goValidateCronCommand 使用 mvdan.cc/sh/v3/shellshell.Fields 解析命令;cron:run 在派发时使用同一解析器,因此部署时能通过校验的命令一定可以执行。对应的单元测试见 plugins/cron/cron_test.go"sh -c 'echo CRON_OK; echo hi > /tmp/x.txt'" 这类显式包裹的命令会被接受,而 "echo CRON_OK; echo hi > /tmp/x.txt""cmd1 && cmd2""cmd | other""cmd > file""cmd $(other)" 都会被拒绝。

持久化 Cron 任务输出

如果不做额外配置,任务输出只会投递到 cron 配置的 MAILTO 地址。要保留输出,可以通过 Dokku 的 vector 集成配置一个 sink,详见 logs.md 的 vector 日志投递章节

为应用配置的任何 sink 都会与应用的其余日志一起收到 cron 任务输出:

dokku logs:set node-js-app vector-sink "console://?encoding[codec]=json"

若要单独保留 cron 输出,改用 vector-cron-sink,cron 输出就会被路由到这里而不是应用主 sink:

dokku logs:set node-js-app vector-cron-sink "console://?encoding[codec]=text"

要写入宿主机上的文件,可指向 /var/log/dokku/apps 目录(该目录已挂载进 vector 容器)。dokku_cron_id 字段可用于模板化,让每个任务拥有自己的日志文件:

dokku logs:set node-js-app vector-cron-sink "file://?path=/var/log/dokku/apps/node-js-app/cron-{{ dokku_cron_id }}.log&encoding[codec]=text"

需要注意的路由规则(logs.md):设置 cron sink 是移动而非复制 cron 输出——vector 把每行日志恰好路由到两个 sink 之一;仅设置 vector-cron-sink 时 cron 输出去 cron sink、其余输出无处可去;两者都设置时 cron 输出去 cron sink、其余去 vector-sink。cron 分支的事件额外带 dokku_appdokku_cron_id 两个字段(只有它们保证存在,模板中引用其他字段可能导致日志被静默丢弃);对非常短命的任务还存在相关注意事项。

管理 cron 设置:cron:set

cron 插件提供若干可按应用管理的设置项。下表列出了本文其他章节未覆盖的属性:

名称 描述 级别 全局默认值
mailfrom 在 cron 文件中设置 MAILFROM 变量,用于 cron 报告 仅全局 空字符串
maintenance 是否让应用运行 cron 应用与全局 false
mailto 在 cron 文件中设置 MAILTO 变量,用于 cron 报告 仅全局 空字符串

所有设置都通过 cron:set 命令完成。以 maintenance 为例:

dokku cron:set node-js-app maintenance true

传入空值即可恢复默认值:

dokku cron:set node-js-app maintenance

如果属性可以全局设置(如 mailto),使用 --global 标志;应用未设置时,若全局值存在则生效:

dokku cron:set --global maintenance true

同样,传空值可恢复全局默认值:

dokku cron:set --global maintenance

从实现看(plugins/cron/subcommands.go),cron:set 在写入属性后还会触发 scheduler-cron-write 触发器(对 --global 只传调度器参数),让调度器重新生成 crontab;同时它也是 cron:suspend/cron:resume 的底层实现。属性定义见 plugins/cron/cron.go:默认属性包含 mailfrommailtomaintenance,其中 mailfrommailto 为全局属性。

列出 Cron 任务:cron:list

使用 cron:list 命令列出应用的 cron 任务,命令接收 app 参数:

dokku cron:list node-js-app
ID                                    Schedule   Command
cGhwPT09cGhwIHRlc3QucGhwPT09QGRhaWx5  @daily     node index.js
cGhwPT09dHJ1ZT09PSogKiAqICogKg==      * * * * *  true

输出也支持 JSON 格式:

dokku cron:list node-js-app --format json
[{"id":"cGhwPT09cGhwIHRlc3QucGhwPT09QGRhaWx5","app":"node-js-app","command":"node index.js","schedule":"@daily"}]

获取全局任务,使用 --global 标志:

dokku cron:list --global
ID                            Schedule  Command
5cruaotm4yzzpnjlsdunblj8qyjp  @daily    /bin/true

从源码看(plugins/cron/subcommands.go),stdout 格式的表格会额外展示 ConcurrencyMaintenance 列;Maintenance 列对任务级挂起显示 true (task),对应用级维护显示 true (app)--format 仅支持 stdoutjson 两种值。任务 ID 由 GenerateCommandID 生成:对 appName + "===" + Command + "===" + Schedule 做 base36 编码(plugins/cron/cron.go),这也是为什么示例中同样的命令与时间表在不同应用会得到不同 ID。

挂起与恢复指定 Cron 任务

cron 任务可以临时挂起(暂停按计划执行),之后恢复,适用于维护或调试场景。

挂起指定任务,使用 cron:suspend 并带上应用名与 cron ID:

dokku cron:suspend node-js-app cGhwPT09cGhwIHRlc3QucGhwPT09QGRhaWx5

被挂起的任务将不再按计划执行。可通过 cron:list 输出的 Maintenance 列确认任务已挂起——挂起的任务会显示 true (task)

恢复挂起的任务,使用 cron:resume

dokku cron:resume node-js-app cGhwPT09cGhwIHRlc3QucGhwPT09QGRhaWx5

恢复后任务将重新按计划执行。cron ID 可从 cron:list 输出获取。

实现细节:cron:suspend 等价于 cron:set <app> maintenance.<cron_id> truecron:resume 等价于 cron:set <app> maintenance.<cron_id>(清除该属性)(plugins/cron/subcommands.goplugins/cron/subcommands.go)。属性前缀 maintenance. 定义于 plugins/cron/cron.go。任务级维护属性不能全局设置(cron:set --global maintenance.<id> 会报错),且仅当属性值为 true 时才会覆盖 app.json 中声明的 maintenance(见 FetchCronTasks 中的合并逻辑,plugins/cron/cron.go)。

即时执行 Cron 任务:cron:run

cron:run 命令可以即时调用 cron 任务,接收 app 参数和 cron ID(可从 cron:list 输出获取):

dokku cron:run node-js-app cGhwPT09cGhwIHRlc3QucGhwPT09QGRhaWx5

默认情况下任务在附加(attached)容器中运行(视调度器支持而定)。要在后台分离容器中运行,指定 --detach 标志:

dokku cron:run node-js-app cGhwPT09cGhwIHRlc3QucGhwPT09QGRhaWx5 --detach

即时调用默认也有 24 小时(86400 秒)的运行上限,与计划调度的任务相同。可用 --ttl-seconds 指定不同期限:

dokku cron:run node-js-app cGhwPT09cGhwIHRlc3QucGhwPT09QGRhaWx5 --detach --ttl-seconds 600

该值只作用于本次调用——由调度计划启动的任务仍保持 24 小时默认值。所有一次性 cron 执行的容器在调用结束后都会被终止。

实现细节(plugins/cron/subcommands.go):cron:run 会先校验 --ttl-seconds 必须为正整数(validateTTLSecondsplugins/cron/cron.go,对应测试见 plugins/cron/cron_test.go),校验任务 ID 存在,然后用 shell.Fields 对命令分词,设置 DOKKU_DETACH_CONTAINERDOKKU_DISABLE_TTY(分离模式)、DOKKU_CONCURRENCY_POLICYDOKKU_CRON_IDDOKKU_RM_CONTAINER=1DOKKU_RUN_TTL_SECONDS 等环境变量,最终通过 scheduler-run 触发器派发给应用的调度器执行。

查看 Cron 报告:cron:report

使用 cron:report 命令查看应用的 cron 配置报告:

dokku cron:report
=====> node-js-app cron information
       Cron task count:               2
=====> python-sample cron information
       Cron task count:               0
=====> ruby-sample cron information
       Cron task count:               10

也可以只查看指定应用:

dokku cron:report node-js-app
=====> node-js-app cron information
       Cron task count:               2

还可以传标志,只输出你关心的特定信息:

dokku cron:report node-js-app --cron-task-count

可设置的属性及其对应的 report 标志、JSON 键名详见下文"属性参考"一节(完整实现见 plugins/cron/report.go)。

属性参考

以下属性可通过 cron:set 设置,并通过 cron:report 查看:

[!NOTE] Report flags 列是 cron:report 接受的 CLI 参数名。cron:report --format json 输出的 JSON 键为去掉 --cron- 前缀后的同名(如 global-mailtocomputed-mailtomaintenance)。带 cron- 前缀的旧键(如 cron-global-mailto)在 0.38.x 弃用窗口期仍会输出,并将在未来大版本中移除。

属性 作用域 默认值 Report flags 描述
mailfrom 仅全局 --cron-global-mailfrom--cron-computed-mailfrom cron 失败邮件使用的 From: 地址
mailto 仅全局 --cron-global-mailto--cron-computed-mailto cron 失败邮件的收件地址;为空则禁用邮件
maintenance 应用 + 全局 false --cron-maintenance--cron-global-maintenance--cron-computed-maintenance true 时挂起应用(或全局)的所有 cron 任务
maintenance.<cron-id> 仅应用 false --cron-maintenance-<cron-id>(按任务动态生成) 按计算出的 ID 挂起单个 cron 任务(每个任务一行);由 cron:suspend/cron:resume 写入

底层原理:crontab 生成与调度器协作

理解"谁能把任务写进 crontab"有助于排障。核心逻辑位于 plugins/cron/crontab.go

  • usesHostCron 通过 scheduler-uses-host-cron 触发器询问某调度器是否使用宿主 crontab(空调度器或未实现该触发器的视为 falsecrontab.go);
  • generateCronTasks 会收集所有使用宿主 crontab 的应用的 app.json 任务,再加上通过 cron-entries 触发器注入的任务(格式为 $SCHEDULE;$COMMAND[;$LOGFILE]),并过滤掉处于维护状态的任务(crontab.go);
  • writeCronTab 每次都是全量重新生成 dokku 用户 crontab(先 crontab -r -u dokku 再写入),因此多个调度器共用宿主 crontab 时不会互相覆盖;任务列表为空时直接删除 crontab(crontab.go)。

最终渲染使用的模板为 plugins/cron/templates/cron.tmpl,内容大致为:

MAILFROM={{ .Mailfrom }}
MAILTO={{ .Mailto }}
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash

{{ $task.Schedule }} {{ $task.DokkuRunCommand }}

DokkuRunCommand 对应用任务恒输出 dokku cron:run <app> <cron_id>plugins/cron/cron.go),绝不把用户命令直接写进 crontab——测试 plugins/cron/cron_test.go 专门断言 crontab 行不包含 ;>|&`$ 等 shell 元字符,且不会把用户命令泄漏到 crontab 行中。

自管理 Cron(高级用法)

[!WARNING] 自管理 cron 属于高级用法。虽然下文提供操作说明,但强烈建议优先使用内置定时任务支持,除非确有必要。

某些安装场景可能需要更细粒度的 cron 控制,以下是配置 cron 的高级指引。

使用 run 执行 cron 任务

可以随时使用一次性容器运行应用任务:

dokku run node-js-app some-command

对于不应被打断的任务,run 是处理 cron 任务的首选方式,因为即使发生部署或扩缩容事件,容器也会继续运行。代价是多个并发任务同时运行时内存占用会增加。

使用 enter 执行 cron 任务

Procfile 中加入以下条目:

cron: sleep infinity

cron 进程扩到 1

dokku ps:scale node-js-app cron=1

然后即可在该容器中运行所有命令:

dokku enter node-js-app cron some-command

注意也可以同时运行多个命令以减少内存占用,但这可能会污染容器环境。

对于需要正确恢复的任务,应当使用上述方式——因为部署和扩缩容事件会中断正在运行的任务,且后续命令始终运行在最新容器中。注意如果把 cron 容器缩容,可能会中断任务正常运行。

通用 cron 建议

定期任务在 Dokku 上需要一些额外注意,以下通用建议有助于保证任务成功运行:

  • 在 cron 任务中使用 dokku 用户;
    • 否则 dokku 二进制会尝试用 sudo 执行,cron 运行会失败并报 sudo: no tty present and no askpass program specified
  • 添加 MAILTO 环境变量,把 cron 邮件发给自己;
  • 添加 PATH 环境变量,或指定宿主机上二进制文件的完整路径;
  • 添加 SHELL 环境变量,运行命令时指定 Bash;
  • 让 cron 任务按时间排序存放;
  • 保持服务器时间为 UTC,读取 cronfile 时无需换算夏令时;
  • 尽量在流量最低的时段运行任务;
  • 用 cron 来触发任务,而不是运行任务本体——用 rabbitmq 之类的真实队列系统处理实际任务;
  • 尽量让任务保持安静,只在出错时发邮件;
  • 不要屏蔽标准错误或标准输出:屏蔽前者会错过失败信息;屏蔽后者意味着你其实应该通过修改应用来调整日志级别;
  • 使用 Dead Man's Snitch 之类的服务验证 cron 任务是否成功完成;
  • 在 cronfile 中写大量注释,说明每个任务在做什么,免得日后花时间解读文件;
  • 将 cronfile 放在如 /etc/cron.d/APP 的模式路径下;
  • 不要在 cronfile 文件名中使用非 ASCII 字符,cron 对此很挑剔;
  • 记得 cronfile 末尾要有换行符,cron 同样很挑剔。

以下是一份可直接参考的应用 cronfile 示例:

# server cron jobs
MAILTO="mail@dokku.me"
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash

# m   h   dom mon dow   username command
# *   *   *   *   *     dokku    command to be executed
# -   -   -   -   -
# |   |   |   |   |
# |   |   |   |   +----- day of week (0 - 6) (Sunday=0)
# |   |   |   +------- month (1 - 12)
# |   |   +--------- day of month (1 - 31)
# |   +----------- hour (0 - 23)
# +----------- min (0 - 59)

### HIGH TRAFFIC TIME IS B/W 00:00 - 04:00 AND 14:00 - 23:59
### RUN YOUR TASKS FROM 04:00 - 14:00
### KEEP SORTED IN TIME ORDER

### PLACE ALL CRON TASKS BELOW

# removes unresponsive users from the subscriber list to decrease bounce rates
0 0 * * * dokku dokku run node-js-app some-command

# sends out our email alerts to users
0 1 * * * dokku dokku ps:scale node-js-app cron=1 && dokku enter node-js-app cron some-other-command && dokku ps:scale node-js-app cron=0

### PLACE ALL CRON TASKS ABOVE, DO NOT REMOVE THE WHITESPACE AFTER THIS LINE

小结

Dokku 的定时任务体系由"声明(app.jsoncron 键)— 验证(部署期校验时间表与命令分词)— 调度(宿主 crontab 或调度器原生后端)— 执行(一次性 run 容器)— 回收(24 小时 TTL)"五段组成。日常使用推荐完全走内置方案:用 app.json 声明、用 cron:list/cron:report 观察、用 cron:suspend/cron:resume 维护、用 cron:run 应急触发,并配合 vector-cron-sink 持久化输出;只有在需要细粒度控制或特殊约束时才考虑自管理 cron。相关可进一步阅读的仓库资料包括 cron 插件源码cron 单元测试app.json 格式定义vector 日志集成文档

热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
927
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.94 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
603
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
396
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
527