【免费下载链接】watchtower
A process for automating Docker container base image updates.
Watchtower 是一个用于自动化 Docker 容器基础镜像更新的进程:当你在 Docker Hub 或自建镜像仓库中推送新镜像后,Watchtower 会自动拉取新镜像、优雅地关闭旧容器,并以最初部署时相同的参数重启容器。本文以仓库 README.md 为骨架,结合 docs 目录下的完整文档与 Go 源码实现,系统讲解 Watchtower 的安装部署、工作原理、容器筛选、全部命令行参数与高级运行模式,帮助你在一台 Docker 主机上搭建起一套"推送即更新"的镜像自动更新链路。
一、项目定位与适用场景
Watchtower 的自我定位非常明确(见 README.md):
A process for automating Docker container base image updates.
它是一个自动化 Docker 容器基础镜像更新的进程,设计目标是让"运行中的容器应用保持最新"这件事变得几乎零成本:你只需要把新镜像推送到 Docker Hub 或自己的镜像仓库,Watchtower 就会完成后续的拉取、停机与重启。
典型工作流:一次完整的更新循环
以仓库 docs/introduction.md 中的示例为例,假设你同时运行了 centurylink/wetty-cli 与 containrrr/watchtower 两个容器:
$ docker ps
CONTAINER ID IMAGE STATUS PORTS NAMES
967848166a45 centurylink/wetty-cli Up 10 minutes 0.0.0.0:8080->3000/tcp wetty
6cc4d2a9d1a5 containrrr/watchtower Up 15 minutes watchtower
Watchtower 每天会拉取最新的 centurylink/wetty-cli 镜像,并与其启动容器时所用的镜像进行比较。一旦发现镜像发生变化,它就会:
- 停止并移除旧的
wetty容器; - 使用新镜像,以及当初启动容器时所用的同一条
docker run参数(例如这里的-p 8080:3000端口映射)重新创建并启动容器。
这套"记住原容器启动配置、按原配置重建"的机制,正是 Watchtower 的核心价值所在——它保证了更新前后的容器行为完全一致。
官方推荐与不推荐的使用环境
README 中对适用环境给出了明确边界,这也是部署前必须了解的事实:
- 推荐使用场景:家庭实验室(homelab)、媒体中心、本地开发环境等非关键环境;
- 不推荐场景:商业或生产环境。官方明确表示 不推荐在商业或生产环境中使用 Watchtower;
- 生产环境的替代方向:官方建议生产环境应转向 Kubernetes;如果觉得直接上 K8s 步子太大,可以关注 MicroK8s、k3s 这类大幅降低集群运维负担的单机 Kubernetes 发行版。
二、快速开始:60 秒跑起第一个 Watchtower
README 的 Quick Start 给出了最小可用的启动命令:
$ docker run --detach \
--name watchtower \
--volume /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower
这条命令做了三件事:
--detach:以守护进程方式在后台运行;--name watchtower:为容器命名;--volume /var/run/docker.sock:/var/run/docker.sock:把宿主机的 Docker 套接字挂载进容器——这是 Watchtower 能够工作最关键的一步。
为什么必须挂载 docker.sock?
Watchtower 本质上是 Docker API 的一个客户端:它通过 Docker API 列出容器、拉取镜像、停止和重启容器。挂载 /var/run/docker.sock 之后,容器内的进程才能直接与宿主机的 Docker 守护进程对话(详见 docs/usage-overview.md)。不挂载这个套接字,Watchtower 将无法监控任何容器。
从源码也可以印证这一点:默认的 Docker 连接地址就是 unix:///var/run/docker.sock(见 docs/arguments.md 中 --host 参数的默认值),而程序的入口 cmd/root.go 在启动时通过 container.NewClient 构建 Docker 客户端,并通过 pkg/container/client.go 中的客户端实现完成与守护进程的通信。
三、安装与运行方式详解
Watchtower 本身被打包为 Docker 镜像(containrrr/watchtower),安装即拉取镜像。
基础运行
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower
私有镜像仓库认证
如果要从私有 Docker 仓库拉取镜像,有两种认证方式:
方式一:环境变量认证(见 docs/usage-overview.md)
通过 REPO_USER 与 REPO_PASS 两个环境变量传入仓库凭据:
docker run -d \
--name watchtower \
-e REPO_USER=username \
-e REPO_PASS=password \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower container_to_watch --debug
注意:如果 Docker Hub 开启了 2FA 双重认证,仅靠用户名密码是不够的。
方式二:挂载 Docker 配置文件(推荐,支持 2FA)
先在宿主机执行 docker login,将凭据保存到 $HOME/.docker/config.json,然后把这个配置文件挂载进 Watchtower 容器(挂载到容器根目录 / 下的 config.json):
docker run -d \
--name watchtower \
-v $HOME/.docker/config.json:/config.json \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower container_to_watch --debug
⚠️ 关于 config.json 的热更新陷阱(docs/usage-overview.md 有专门说明): 按上述方式挂载
config.json时,宿主机上的改动通常不会传播到运行中的容器。原因是 Docker 对单个文件的挂载是绑定挂载(bind mount),其机制基于 inode。而大多数应用程序(包括docker login和vim)在保存文件时并不是直接编辑原文件,而是"复制一份再替换原文件",这会生成新的 inode,从而破坏绑定挂载。解决办法:先为
config.json创建符号链接,再挂载这个符号链接。符号链接本身的 inode 始终不变,绑定挂载因此保持有效,无论源文件的 inode 如何变化,改动都能同步到容器中。
docker-compose 完整示例
挂载配置文件方式下,还需要在启动被监控镜像时带上仓库 URL 前缀(可省略 https://)。下面是一个完整的 docker-compose.yml:同时启动一个来自 GitHub Registry 私有仓库的容器与负责监控它的 Watchtower,并将轮询间隔从默认的 24 小时调整为 30 秒(示例来自 docs/usage-overview.md):
version: "3"
services:
cavo:
image: ghcr.io/<org>/<image>:<tag>
ports:
- "443:3443"
- "80:3080"
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /root/.docker/config.json:/config.json
command: --interval 30
仓库根目录的 docker-compose.yml 还提供了一个更丰富的开发环境示例:它同时编排了 watchtower、Prometheus、Grafana,以及一对具有 depends-on 依赖关系的 parent/child 容器,并展示了 --interval 10 --http-api-metrics --http-api-token demotoken --debug 等参数的组合用法,可作为多服务场景的参考。
四、容器筛选:让 Watchtower 只更新你想更新的容器
默认情况下,Watchtower 会监控 Docker 守护进程内的全部运行中容器(见 docs/arguments.md)。但实际部署中,往往只希望对部分容器进行自动更新。仓库提供了两层控制手段,详见 docs/container-selection.md:
- 完全排除(Full Exclude):让某些容器彻底不参与监控;
- 仅监控(Monitor Only):Watchtower 检查更新、发送通知、调用 pre-check/post-check 钩子,但不执行更新。
通过容器名称指定监控目标
可以在启动命令的末尾追加容器名作为参数,只监控这些容器:
$ docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
nginx redis
以上命令中,Watchtower 只监控名为 nginx 和 redis 的容器,其他容器一律忽略。不指定任何名称时,则监控全部运行中容器。
通过标签控制(核心机制)
Watchtower 使用 com.centurylinklabs.watchtower.enable 标签进行容器级别的开关控制。注意:这个标签要打在业务容器上,而不是 Watchtower 自己身上。
排除特定容器(三种方式等价):
# dockerfile 中
LABEL com.centurylinklabs.watchtower.enable="false"
# docker run 命令行
docker run -d --label=com.centurylinklabs.watchtower.enable=false someimage
# docker-compose
version: "3"
services:
someimage:
container_name: someimage
labels:
- "com.centurylinklabs.watchtower.enable=false"
白名单模式(只更新打标容器):给 Watchtower 传入 --label-enable(或环境变量 WATCHTOWER_LABEL_ENABLE),同时给想监控的容器打上值为 true 的 enable 标签:
# watchtower 启动参数
docker run -d --label=com.centurylinklabs.watchtower.enable=true someimage
# 或在 docker-compose 中
labels:
- "com.centurylinklabs.watchtower.enable=true"
标签过滤的判定逻辑
Watchtower 会对每个运行中的容器逐一测试各项筛选条件,所有条件同时满足才会被监控(见 docs/container-selection.md):
- 如果容器名在监控名单中(
--name参数非空),但其enable标签为false,则不会被监控; - 如果容器名不在监控名单中,即使它打了
enable=true且 Watchtower 开了--label-enable,也不会被监控。
单容器"仅监控"模式
给某个容器打上 com.centurylinklabs.watchtower.monitor-only=true 标签,效果等同于对该容器单独设置了 WATCHTOWER_MONITOR_ONLY,即只检查更新、不执行更新:
LABEL com.centurylinklabs.watchtower.monitor-only="true"
docker run -d --label=com.centurylinklabs.watchtower.monitor-only=true someimage
五、命令行参数全景:从轮询间隔到优雅停机
Watchtower 的全部参数定义在 docs/arguments.md,它们既可以作为 CLI 参数传入,也可以作为 WATCHTOWER_ 前缀的环境变量传入。下面按功能分组完整列出。
5.1 时间与调度
| 参数 | 环境变量 | 类型 / 默认值 | 说明 |
|---|---|---|---|
--interval, -i | WATCHTOWER_POLL_INTERVAL | Integer / 86400(24 小时) | 轮询间隔(秒)。与 --schedule 二选一 |
--schedule, -s | WATCHTOWER_SCHEDULE | String / - | 6 字段 Cron 表达式(比传统 5 字段多一个秒字段),如 --schedule "0 0 4 * * *" 表示每天凌晨 4 点检查 |
TZ | TZ | String / "UTC" | 日志与 --schedule 使用的时区,如 Europe/Rome;也可通过 -v /etc/localtime:/etc/localtime:ro 挂载宿主机时区文件 |
--stop-timeout | WATCHTOWER_TIMEOUT | Duration / 10s | 容器被强制停止前的等待超时,如 --stop-timeout 30s |
调度与轮询的实际执行在 cmd/root.go 中完成:runUpgradesOnSchedule 使用 robfig/cron 调度器注册任务,并配合一个容量为 1 的 channel 作为互斥锁——同一时刻只允许一次更新运行,若上次更新尚未结束则跳过本轮(源码中会输出 "Skipped another update already running." 的调试日志)。--interval 最终也会被转换为 cron 表达式执行。
5.2 运行模式
| 参数 | 环境变量 | 类型 / 默认值 | 说明 |
|---|---|---|---|
--run-once, -R | WATCHTOWER_RUN_ONCE | Boolean / false | 对容器名单立即执行一次更新尝试后退出 |
--monitor-only | WATCHTOWER_MONITOR_ONLY | Boolean / false | 只监控、发通知、调用钩子,不更新容器。注意:由于 Docker API 限制,最新镜像仍会被拉取(HEAD digest 检查可跳过无变化时的拉取) |
--no-restart | WATCHTOWER_NO_RESTART | Boolean / false | 更新后不重启容器,适用于由 systemd 等外部系统管理容器启动的场景 |
--no-pull | WATCHTOWER_NO_PULL | Boolean / false | 不拉取新镜像,仅监控本地镜像缓存变化,适合直接在宿主机构建镜像而不推送仓库的场景。也可用 com.centurylinklabs.watchtower.no-pull 标签按容器设置 |
--no-startup-message | WATCHTOWER_NO_STARTUP_MESSAGE | Boolean / false | 启动时不发送通知消息 |
--rolling-restart | WATCHTOWER_ROLLING_RESTART | Boolean / false | 一次只重启一个容器(而非全部一起停止再启动),配合生命周期钩子可实现零停机部署 |
--health-check | - | - | 返回成功退出码,供 Docker HEALTHCHECK 使用;绝不能放在主容器可执行命令行上 |
--porcelain, -P | WATCHTOWER_PORCELAIN | 取值 v1 / - | 以稳定、机器可读的格式向 STDOUT 输出会话结果,等价于 --notification-url logger:// + --notification-log-stdout + --notification-report + --notification-template porcelain.VERSION.summary-no-log |
--run-once 是交互式手动更新最常用的模式(见 docs/arguments.md):它会启用调试输出显示所有执行动作,完成后容器退出;配合 --rm 标志容器会被自动删除:
$ docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
--run-once \
nginx redis
上面的命令会立即对 nginx 与 redis 执行一次更新尝试。
5.3 更新行为与清理
| 参数 | 环境变量 | 类型 / 默认值 | 说明 |
|---|---|---|---|
--cleanup | WATCHTOWER_CLEANUP | Boolean / false | 更新并重启容器后删除旧镜像,防止孤立镜像堆积 |
--remove-volumes | WATCHTOWER_REMOVE_VOLUMES | Boolean / false | 更新后移除容器中的匿名卷(命名卷不会被移除) |
--include-restarting | WATCHTOWER_INCLUDE_RESTARTING | Boolean / false | 将"重启中"的容器也纳入监控 |
--include-stopped, -S | WATCHTOWER_INCLUDE_STOPPED | Boolean / false | 将"已创建"和"已退出"的容器也纳入监控 |
--revive-stopped | WATCHTOWER_REVIVE_STOPPED | Boolean / false | 镜像更新后启动已停止的容器(仅与 --include-stopped 搭配使用) |
5.4 容器筛选
| 参数 | 环境变量 | 类型 / 默认值 | 说明 |
|---|---|---|---|
--label-enable | WATCHTOWER_LABEL_ENABLE | Boolean / false | 只监控打了 com.centurylinklabs.watchtower.enable=true 标签的容器 |
--disable-containers, -x | WATCHTOWER_DISABLE_CONTAINERS | 逗号或空格分隔的字符串列表 / "" | 排除指定名称的容器;即使这些容器 enable 标签为 true 也会被排除 |
--scope | WATCHTOWER_SCOPE | String / - | 只更新 com.centurylinklabs.watchtower.scope 标签值与参数相同的容器,用于支持多实例部署;设为 none 时其他实例会忽略这些带 scope 的容器;省略时更新所有容器(无论 scope) |
--label-take-precedence | WATCHTOWER_LABEL_TAKE_PRECEDENCE | Boolean / false | 让标签优先于参数:默认参数优先(如 --monitor-only 会覆盖容器上的 monitor-only=false 标签),开启后则以容器标签为准 |
5.5 日志与连接
| 参数 | 环境变量 | 类型 / 默认值 | 说明 |
|---|---|---|---|
--debug, -d | WATCHTOWER_DEBUG | Boolean / false | 调试模式,详细日志;是 --log-level debug 的别名,作为参数时不接收值(--debug true 无效) |
--trace | WATCHTOWER_TRACE | Boolean / false | 极详细日志;是 --log-level trace 的别名。注意:会暴露凭据! |
--log-level | WATCHTOWER_LOG_LEVEL | panic/fatal/error/warn/info/debug/trace / info | 写入 STDERR 的最大日志级别(容器内即 docker log 可见内容) |
--log-format, -l | WATCHTOWER_LOG_FORMAT | Auto/LogFmt/Pretty/JSON / Auto | 控制台日志输出格式 |
--no-color | NO_COLOR | Boolean / false | 禁用日志中的 ANSI 颜色转义码 |
--host, -H | DOCKER_HOST | String / unix:///var/run/docker.sock | Docker 守护进程套接字;可填 tcp://hostname:port 连接远程 Docker |
--api-version, -a | DOCKER_API_VERSION | String / 1.24 | Docker API 版本,最低支持 1.24 |
--tlsverify | DOCKER_TLS_VERIFY | Boolean / false | 连接 Docker 套接字时使用 TLS 并校验服务器证书 |
--warn-on-head-failure | WATCHTOWER_WARN_ON_HEAD_FAILURE | always/auto/never / auto | HEAD 拉取失败时的告警策略;auto 表示仅在已知会处理请求且可能限流拉取的仓库(主要是 docker.io)发出告警 |
5.6 HTTP API 模式
| 参数 | 环境变量 | 类型 / 默认值 | 说明 |
|---|---|---|---|
--http-api-update | WATCHTOWER_HTTP_API_UPDATE | Boolean / false | 以 HTTP API 模式运行,仅允许通过 HTTP 请求触发镜像更新 |
--http-api-token | WATCHTOWER_HTTP_API_TOKEN | String / - | HTTP API 请求的认证令牌;可引用文件,此时使用文件内容作为值 |
--http-api-periodic-polls | WATCHTOWER_HTTP_API_PERIODIC_POLLS | Boolean / false | HTTP API 模式下仍保持周期轮询(否则 API 模式会阻止周期轮询) |
--http-api-metrics | WATCHTOWER_HTTP_API_METRICS | Boolean / false | 启用 Prometheus 指标端点 |
关于 HTTP API 模式的完整用法(端点、请求格式、令牌认证),请参阅仓库文档 docs/http-api-mode.md;指标端点细节见 docs/metrics.md。HTTP API 服务器的初始化与路由注册在 cmd/root.go 的 Run 函数中完成(api.New(apiToken)、更新处理器与指标处理器通过 RegisterFunc / RegisterHandler 挂载)。
5.7 帮助信息与配置文件取密
--help:显示所有支持的标志说明。- Secrets/Files:部分参数支持"值来自文件",以避免把机密写进配置文件或命令行。以下参数及其对应
WATCHTOWER_环境变量均支持此特性:notification-url、notification-email-server-password、notification-slack-hook-url、notification-msteams-hook、notification-gotify-token、http-api-token。
docker-compose 中通过 Docker Secrets 传递令牌的示例(见 docs/arguments.md):
secrets:
access_token:
file: access_token
services:
watchtower:
secrets:
- access_token
environment:
- WATCHTOWER_HTTP_API_TOKEN=/run/secrets/access_token
六、更新流程的源码级拆解:拉取 → 停止 → 重启
README 只描述了"拉取新镜像、优雅停机、用相同参数重启"这层表象,其底层实现位于 internal/actions/update.go 的 Update 函数,一次完整会话包含以下阶段:
- 生命周期钩子前置检查:若启用了 lifecycle hooks(
--enable-lifecycle-hooks),先执行lifecycle.ExecutePreChecks; - 列出候选容器:通过
client.ListContainers(params.Filter)按筛选条件获取容器列表; - 逐个判定是否过期:对每个容器调用
client.IsContainerStale,判断"是否过期 + 是否需要更新"(stale && !NoRestart && !MonitorOnly),并尝试VerifyConfiguration()校验重建容器所需的完整信息——如果镜像信息不完整且开了 trace 日志,会输出容器与镜像的详细诊断信息; - 依赖排序:通过 pkg/sorter/sort.go 的
SortByDependencies按依赖关系排序,并由UpdateImplicitRestart将"链接到待重启容器"的容器也标记为需要重启(这保证了依赖容器的正确更新顺序); - 执行更新:
- 非滚动模式:先按逆序停止所有待更新容器(
stopContainersInReversedOrder),再按依赖排序结果重启(restartContainersInSortedOrder); - 滚动模式(
--rolling-restart):调用performRollingRestart,一次只处理一个容器,停止成功后立即重启,实现逐个滚动;
- 非滚动模式:先按逆序停止所有待更新容器(
- 清理旧镜像:若开启
--cleanup,通过cleanupImages删除已更新容器对应的旧镜像; - 生命周期钩子后置检查:
lifecycle.ExecutePostChecks。
值得注意的细节是 internal/actions/check.go 中的两重前置检查:
CheckForSanity:滚动重启模式下,若发现任何容器依赖其他容器(Links()非空)会直接报错——滚动重启与容器链接不兼容;CheckForMultipleWatchtowerInstances:启动时检测是否存在多个 Watchtower 实例,若存在(且未设置 scope),会停止并移除除最近启动实例外的其他实例,从而避免多个实例互相打架。
而针对 Watchtower 容器自身的更新,restartStaleContainer 中有专门处理:由于无法立即关闭自身,Watchtower 会先把当前实例重命名(client.RenameContainer(container, util.RandName())),让新实例接管旧名字,实现自我更新。
七、常见问题与使用注意事项
- 生产环境请慎重:README 明确不推荐商业/生产环境使用,建议生产场景评估 Kubernetes(或 MicroK8s、k3s 等轻量发行版)。
--monitor-only与--no-pull同时使用可能毫无动作:源码 cmd/root.go 中对此会输出警告日志:"UsingWATCHTOWER_NO_PULLandWATCHTOWER_MONITOR_ONLYsimultaneously might lead to no action being taken at all"。--rolling-restart与全局monitor-only不兼容:二者同时设置会直接导致进程退出(log.Fatal)。- 健康检查标志的误用风险:
--health-check只能用于 DockerHEALTHCHECK指令,放在主进程命令行会导致错误退出(源码中通过os.Getpid() == 1检测并拒绝)。 - 时区默认 UTC:日志时间与 cron 调度默认使用 UTC,可通过
TZ环境变量或挂载/etc/localtime调整。 - 认证信息更新的 inode 陷阱:见第三节中关于
config.json绑定挂载与符号链接的说明。
八、延伸阅读与源码地图
Watchtower 的完整文档目录位于 docs,按需取用:
- 安装与私有仓库认证:docs/usage-overview.md
- 全部参数说明:docs/arguments.md
- 容器筛选与标签:docs/container-selection.md
- 生命周期钩子:docs/lifecycle-hooks.md
- 通知渠道(Slack、邮件、Gotify、MS Teams、JSON 等):docs/notifications.md
- HTTP API 触发模式:docs/http-api-mode.md
- Prometheus 指标:docs/metrics.md
- 多实例与 scope 隔离:docs/running-multiple-instances.md
- 远程 Docker 主机:docs/remote-hosts.md
- 私有仓库认证:docs/private-registries.md
关键源码位置:
- 程序入口与调度:main.go、cmd/root.go
- 更新动作核心实现:internal/actions/update.go
- 启动前置检查与多实例清理:internal/actions/check.go
- 容器客户端与 Docker API 交互:pkg/container/client.go
- 依赖排序算法:pkg/sorter/sort.go
- 开发环境完整编排示例:docker-compose.yml
通过本文,你应该已经掌握了 Watchtower 的完整部署路径:从一条 docker run 快速拉起监控、通过标签与参数精确控制监控范围、按需组合轮询/Cron/单次/HTTP API 等运行模式,并理解了其"拉取—停旧—按原配置启新"的更新内核。对于家庭实验室与本地开发环境,这足以构成一套低成本、可维护的镜像自动更新基础设施。
【免费下载链接】watchtower
A process for automating Docker container base image updates.
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



