Coolify 中的 Horizon 作业标签与静默机制:Eloquent 自动标签、tags() 定制与 silenced 降噪
Coolify 作为基于 Laravel 的自托管 PaaS,使用 Laravel Horizon(composer.json 中锁定 laravel/horizon: ^5.48.2)监控其部署、备份等队列作业。本文围绕 Horizon 的作业标签(Tags)与静默(Silencing)机制展开:讲清 Eloquent 模型作业如何被自动打上 ModelClass:id 标签、何时需要手写 tags() 方法,以及 silenced / silenced_tags 两个配置项如何为已完成作业列表降噪。读完本文,你可以在 Horizon 面板中按标签过滤作业、复用 Coolify 源码中"用标签反查业务记录"的成熟做法,并知道如何把高频噪音作业从面板中隐藏而不影响其执行。
一、标签与静默在 Coolify 队列体系中的位置
Coolify 的后台作业(部署 ApplicationDeploymentJob、数据库备份 DatabaseBackupJob、哨兵检查 CheckAndStartSentinelJob 等)全部通过 Redis 队列驱动、由 Horizon 托管。config/horizon.php 定义了名为 s6 的监督器,队列默认取 high,default(由 HORIZON_QUEUES 环境变量控制),并按 production / local 环境配置了 autoScalingStrategy => 'size' 的进程弹性伸缩(maxProcesses 默认 4)。
在这个体系里,标签与静默解决的是两类问题:
- 可观测性:标签让每个作业携带业务上下文(操作了哪台服务器、哪条部署记录),在 Horizon 面板中可直接按标签过滤、定位;
- 降噪:
silenced/silenced_tags让高频、低价值的作业从"已完成"列表中消失,避免真正需要关注的部署作业被淹没。
需要注意的是,这两个机制都只作用于 Horizon 的展示层:标签改变的是作业的元数据,静默改变的是面板过滤结果,均不改变作业本身是否执行。
二、Eloquent 模型作业会被自动打标签,无需任何额外代码
这是标签机制中最容易被忽略、也最实用的行为:如果一个作业的构造函数接收 Eloquent 模型实例,Horizon 会自动为该作业打上 ModelClass:id 形式的标签,例如 App\Models\User:42。这些标签可以直接在面板中用于过滤,无需修改作业类本身。
Coolify 源码中大量作业正是这种模式,自动标签会直接生效:
- CheckAndStartSentinelJob:
__construct(public Server $server)—— 每次运行自动获得App\Models\Server:<id>标签; - CleanupHelperContainersJob、ConnectProxyToNetworksJob:同样以
public Server $server构造; - DatabaseBackupJob:
__construct(public ScheduledDatabaseBackup $backup)—— 自动获得App\Models\ScheduledDatabaseBackup:<id>标签,备份作业在面板中天然可回溯到具体备份任务。
也就是说,无需任何改动,你在 Horizon 面板中筛选 App\Models\Server:7,就能看到所有针对 7 号服务器执行的检查、清理、代理连接作业。
源码印证:Coolify 用自动标签反查业务记录
自动标签不只是面板上的好看——Coolify 在 HorizonServiceProvider 中监听 Horizon 的 JobReserved 事件,直接解析作业载荷中的 tags 字段来打通"Horizon 作业 ID ↔ 部署记录"的映射:
Event::listen(function (JobReserved $event) {
$payload = $event->payload->decoded;
$jobName = $payload['displayName'];
if ($jobName === 'App\Jobs\ApplicationDeploymentJob') {
$tags = $payload['tags'];
$id = $payload['id'];
$deploymentQueueId = collect($tags)->first(function ($tag) {
return str_contains($tag, 'App\Models\ApplicationDeploymentQueue');
});
if (blank($deploymentQueueId)) {
return;
}
$deploymentQueueId = explode(':', $deploymentQueueId)[1];
$deploymentQueue = ApplicationDeploymentQueue::find($deploymentQueueId);
$deploymentQueue->update([
'horizon_job_id' => $id,
]);
}
});
这段逻辑印证了两个事实:其一,作业载荷中的 tags 数组包含自动标签,格式为 标签键:值,可用 explode(':', $tag)[1] 取出 ID;其二,Coolify 借此在作业被 worker 领取的瞬间,就把 horizon_job_id 写入 ApplicationDeploymentQueue 记录,从而让前端部署详情页能跳转到 Horizon 面板中对应的作业。这是"标签即查询句柄"的典型用法。
三、何时手写 tags() 方法:Coolify 的自定义标签实践
自动标签覆盖了"构造函数里传入的模型"这一场景,但当你需要标签携带的信息不是构造参数、或者需要固定格式的识别标签时,就应添加 tags() 方法。参考文档给出的原则很明确:只有在需要自动标签之外的自定义标签时,才添加 tags() 方法。
Coolify 的 ApplicationDeploymentJob 就是一个例子——它的构造函数只接收一个 int 主键(而非 Eloquent 实例),因此自动标签无法表达"这是哪次部署",于是手写:
public function tags()
{
// Do not remove this one, it needs to properly identify which worker is running the job
return ['App\Models\ApplicationDeploymentQueue:'.$this->application_deployment_queue_id];
}
public function __construct(public int $application_deployment_queue_id)
{
$this->onQueue(deployment_queue());
// ...
}
源码注释写得很直白:"不要删除这个标签,它需要正确识别哪个 worker 正在运行该作业"。这个标签正是上面第二节中 JobReserved 监听器依赖的键——两处代码互为印证,构成一条完整的链路:作业侧声明标签 → 载荷携带标签 → 服务提供者解析标签回写业务表。
从源码结构看,手动 tags() 返回的标签与自动标签在格式上保持一致(类名:id),因此面板过滤、事件监听等消费方可以用同一套 str_contains / explode(':') 逻辑处理两类标签。
四、silenced:从"已完成"列表隐藏作业,但不影响执行
config/horizon.php 中的 silenced 选项(config/horizon.php)用于将指定作业类从 Horizon 面板的已完成作业视图中移除:
/*
| Silenced Jobs
|
| Silencing a job will instruct Horizon to not place the job in the list
| of completed jobs within the Horizon dashboard. This setting may be
| used to fully remove any noisy jobs from the completed jobs list.
|
*/
'silenced' => [
// App\Jobs\ExampleJob::class,
],
当前 Coolify 仓库中该数组为空(仅保留注释掉的示例),但用法非常直接:把作业类完整类名加入数组即可。
必须强调的边界(这也是参考文档反复提醒的点):silenced 是面板降噪工具,不是禁用手段。被静默的作业仍然会正常入队、被 worker 领取、正常执行,只是不再出现在已完成作业列表中。如果你想让某个作业停止运行,应该从业务侧不再派发它,或者删除/注释其调度逻辑,而不是把它加进 silenced。
典型适用场景是那些每次请求都会触发、量大但无需逐条查看的作业(例如心跳检查类、状态刷新类作业)——把它们静默后,s6 监督器下的已完成列表就能让 ApplicationDeploymentJob 这类关键作业的成败记录保持可读。
五、silenced_tags:按标签类别批量静默
按类静默只能逐个指定作业类;而 silenced_tags 提供更粗粒度但更省事的开关:任何携带匹配标签字符串的作业都会被隐藏出已完成列表。
以 Coolify 的标签体系举例,假设多个通知、遥测类作业都被打上 notifications 标签(通过第二节描述的模型自动标签或第三节的 tags() 方法),那么将该标签类别加入 silenced_tags,即可一次性隐藏整个类别的作业,而不是逐一枚举作业类名。这对应参考文档的表述:"适合静默一类作业,例如所有被打上 notifications 标签的作业,而非静默具体某个类"。
两者取舍可以归纳为:
| 需求 | 应使用的选项 | 匹配粒度 |
|---|---|---|
| 隐藏某一个(几个)特定作业类 | silenced 数组 |
作业类全名(::class) |
| 隐藏一类作业(按标签类别) | silenced_tags |
标签字符串 |
| 让作业不再运行 | 两者都不行 | 需从派发/调度侧处理 |
需要说明的前提:当前仓库的 config/horizon.php 仅内置了 silenced 键;silenced_tags 是 Horizon 配置结构中的兄弟选项,若你的 Horizon 版本配置文件中不存在该键,需要按版本文档确认其可用性后再添加,不要凭空假设旧版本支持。
六、实践要点小结
- 先利用自动标签,再考虑写代码:构造函数接收 Eloquent 模型的作业(如
CheckAndStartSentinelJob、DatabaseBackupJob)已经自带ModelClass:id标签,面板过滤即可使用,无需任何改动。 - 手写
tags()的唯一理由是携带自动标签之外的信息(参考 ApplicationDeploymentJob::tags());一旦写出标签并被其他代码依赖(如HorizonServiceProvider中的JobReserved监听器),注释中应像 Coolify 那样明确警示"勿删"。 - 静默只影响展示:
silenced与silenced_tags都只让作业从"已完成"视图中消失,作业照常执行;不要把它们当作禁用或限流手段。 - 标签格式即契约:
ModelClass:id的冒号分隔格式是消费侧(explode(':', $tag)、str_contains)解析的前提,自定义标签建议沿用同一格式,方便统一处理。 - 改动前先读 config/horizon.php:Coolify 的监督器配置(
s6、HORIZON_QUEUES、环境级进程伸缩)与silenced同处一个文件,理解当前结构再动配置,可避免误改队列与伸缩参数。
本文依据的来源:参考文档 .claude/skills/configuring-horizon/references/tags.md(标签与静默的原始说明)、技能索引 SKILL.md,以及仓库内的 config/horizon.php、app/Providers/HorizonServiceProvider.php、app/Jobs/ApplicationDeploymentJob.php 等实现文件。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00