Dokku 代理管理完全指南:从 nginx 切换到 Caddy/HAProxy/Traefik 与代理配置的深入解析
导读
本文以 Dokku 的 proxy 插件(核心命令与属性体系)为主线,系统讲解如何在 Dokku PaaS 中切换、启用/禁用、重建与清理应用代理配置,并深入到端口映射、容器网络绑定、代理实现插件编写等进阶话题。读完本文,你将掌握 proxy:set、proxy:build-config、proxy:clear-config、proxy:report 等命令的完整用法与属性取值规则,理解"应用级 → 全局级 → 内置默认"三级取值优先级背后的源码实现,并能独立排查与修复应用代理配置漂移问题。
一、背景:为什么需要独立的 Proxy 插件
Dokku 0.5.0 之前,端口代理逻辑耦合在 nginx-vhosts 插件内部。Dokku 0.5.0 将端口代理从 nginx-vhosts 插件中解耦出来,独立成 proxy 插件;Dokku 0.6.0 又引入将宿主机端口映射到具体容器端口的能力。这一架构变化的意义在于:代理实现不再被 nginx 绑定,HAProxy、Caddy、Traefik、OpenResty 等代理软件都可以作为 nginx 的替代品接入 Dokku。
在仓库中,proxy 插件的核心实现在 plugins/proxy/proxy.go,其 DefaultProperties 定义了全部可设置属性及其默认值:
DefaultProperties = map[string]string{
"disabled": "false",
"proxy-port": "",
"proxy-ssl-port": "",
"type": "",
}
其中 type 为空字符串即代表"未显式设置",实际生效时会回退到内置默认值 nginx(详见后文"取值优先级")。插件版本号可在 plugins/proxy/plugin.toml 中查看。
proxy 插件的完整命令一览(新版命令,自 0.5.0 引入、0.6.0 增强):
proxy:build-config [--parallel count] [--all|<app>] # (Re)builds config for given app
proxy:clear-config [--all|<app>] # Clears config for given app
proxy:disable [--parallel count] [--all|<app>] # Disable proxy for app
proxy:enable [--parallel count] [--all|<app>] # Enable proxy for app
proxy:report [<app>] [<flag>] # Displays a proxy report for one or more apps
proxy:set [<app>|--global] <key> (<value>) # Set or clear a proxy property for an app
二、切换代理实现(proxy:set)
2.1 按应用切换
Dokku 默认随附的代理是 nginx,可以通过 proxy:set 命令修改:
dokku proxy:set node-js-app type caddy
输出:
=====> Setting type to caddy
从源码看,CommandSet(见 plugins/proxy/subcommands.go)还保留了一个向后兼容的快捷写法:proxy:set <app> <proxy-type> 会被自动解释为 proxy:set <app> type <proxy-type>。也就是说下面的命令与上面的完全等价:
dokku proxy:set node-js-app caddy
源码中对应的兼容逻辑为:当传入的"属性名"不在 DefaultProperties 中且没有值(value == "")时,把该参数当作 type 的值处理。同时该逻辑还会做一次防护:如果参数中含有 :(疑似端口映射,例如 5000:5000),会报错提示改用 ports:set 命令:
if strings.Contains(property, ":") {
common.LogWarn("Detected potential port mapping instead of proxy type")
return errors.New("Consider using ports:set command or specifying a valid proxy")
}
2.2 全局切换
代理也可以全局设置。通常更推荐全局设置,因为在同一台 Dokku 主机上同时运行多个代理实现可能引发端口冲突:
dokku proxy:set --global type caddy
输出:
=====> Setting type to caddy
需要特别注意:切换代理并不会自动停止或启动任何代理实现。具体的启停流程需要参考你所选代理插件的文档(例如切换为 caddy 后,需要自行处理 caddy 服务的安装与启动)。
2.3 恢复默认值
将某个属性设置为空值即可恢复默认。例如恢复 type 的默认值(即回退到全局值,最终回退到 nginx):
dokku proxy:set node-js-app type
该行为由 common.CommandPropertySet 统一实现:传入空值即删除该属性记录,后续读取时回退到上一级。
三、禁用与启用代理(proxy:disable / proxy:enable)
代理可以按应用粒度禁用。禁用后,不会为应用生成任何代理配置,进入的请求也不会经过配置的代理路由:
dokku proxy:disable node-js-app
输出:
-----> Disabling proxy for app
重新启用:
dokku proxy:enable node-js-app
输出:
-----> Enabling proxy for app
这两个命令同样支持 --all 与 --parallel 参数,可批量操作(见 plugins/proxy/src/subcommands/subcommands.go 中的 proxy:disable / proxy:enable 分支)。
从源码看,禁用/启用的核心是读写 disabled 属性(plugins/proxy/proxy.go):
Disable:先通过IsAppProxyEnabled判断,已禁用则直接返回;否则写入disabled=true,随后触发proxy-disable钩子。Enable:删除disabled属性(回到默认false),随后触发proxy-enable钩子。IsAppProxyEnabled:读取disabled属性,默认值为false,即"只要不是显式true就视为启用"。
在 nginx 实现中,proxy-disable 触发钩子(见 plugins/nginx-vhosts/proxy-disable)会进一步调用 domains-disable 并重启应用,从而让应用不再通过 nginx 暴露。
四、设置代理端口(proxy-port / proxy-ssl-port)
当应用的域名被禁用(参见 域名配置文档)时,Dokku 仍会把应用暴露在一个高位端口上,以便内部服务在多次部署间通过稳定端口访问应用。用于此目的的非 SSL 与 SSL 端口可通过 proxy:set 配置:
dokku proxy:set node-js-app proxy-port 5000
dokku proxy:set node-js-app proxy-ssl-port 5443
这两个属性同样支持全局设置,应用级值优先于全局值:
dokku proxy:set --global proxy-port 5000
dokku proxy:set --global proxy-ssl-port 5443
恢复默认值(清空):
dokku proxy:set node-js-app proxy-port
在 plugins/proxy/proxy.go 的 GlobalProperties 中可以看到,proxy-port、proxy-ssl-port、type 三个属性允许全局作用域,而 disabled 仅限应用级:
GlobalProperties = map[string]bool{
"proxy-port": true,
"proxy-ssl-port": true,
"type": true,
}
proxy-port / proxy-ssl-port 的值最终会进入代理配置模板,作为生成的 nginx(或其他代理)配置中的 HTTP/HTTPS 监听端口(在 nginx 模板变量中对应 {{ .PROXY_PORT }} 与 {{ .PROXY_SSL_PORT }},见 nginx 代理文档 的"可用模板变量"一节)。
关于 Dokku 使用的端口映射方案,请参见 端口管理文档 中的 端口方案(Port Scheme) 一节;应用端口映射的具体管理方式见 端口管理文档。
五、重建代理配置(proxy:build-config)
某些情况下,应用的代理配置可能与应用的实际状态发生漂移(例如 web 容器重启后 IP 变化、手动编辑过配置等)。此时可随时用 proxy:build-config 重建配置。该命令会为给定应用触发当前代理实现(默认 nginx)的重建;如果应用当前没有 web 监听器,命令可能失败:
dokku proxy:build-config node-js-app
使用 --all 标志可重建所有应用的代理配置:
dokku proxy:build-config --all
默认情况下,批量重建是串行执行的,可通过 --parallel 控制并行度:
dokku proxy:build-config --all --parallel 2
将 --parallel 设为 -1 时,并行 worker 数会自动取为当前机器的 CPU 核数:
dokku proxy:build-config --all --parallel -1
源码佐证:在 plugins/proxy/src/subcommands/subcommands.go 中,--parallel 的默认值取自 proxy.RunInSerial(常量值为 0,见 plugins/proxy/proxy.go),即默认串行;-1 表示按 CPU 数匹配。批量操作通过 common.RunCommandAgainstAllApps 实现。
从 nginx 实现的触发钩子(plugins/nginx-vhosts/proxy-build-config)可以看到完整的执行链路:先通过 proxy-type 触发器确认应用使用的是 nginx(若不是则直接返回,避免干扰其他代理实现),再检查是否存在网络配置;若存在则先触发 network-build-config 重建网络配置,再调用 nginx_build_config 生成 nginx 配置,最后处理模板文件的原子替换与清理。
六、清理生成的代理配置(proxy:clear-config)
[!IMPORTANT] 自 0.27.0 引入
生成的代理配置也可以用 proxy:clear-config 命令清理:
dokku proxy:clear-config node-js-app
使用 --all 可清理所有应用:
dokku proxy:clear-config --all
清理代理配置的效果取决于当前使用的代理插件,具体行为请查阅对应代理实现的文档。以 nginx 为例,plugins/nginx-vhosts/proxy-clear-config 中先确认应用使用的是 nginx,然后触发 network-clear-config 并调用 nginx_clear_config 删除生成的 nginx 配置文件;--all 模式则遍历所有应用逐个清理,并汇总退出码。
七、查看代理报告(proxy:report)
[!IMPORTANT] 自 0.8.1 引入
使用 proxy:report 可以查看应用当前的代理状态报告:
dokku proxy:report
输出示例:
=====> node-js-app proxy information
Proxy computed type: nginx
Proxy enabled: true
Proxy global type:
Proxy type:
=====> python-sample proxy information
Proxy computed type: nginx
Proxy enabled: true
Proxy global type:
Proxy type:
=====> ruby-sample proxy information
Proxy computed type: nginx
Proxy enabled: true
Proxy global type:
Proxy type:
字段含义如下:
type与global-type分别保存原始的应用级值与全局值,未设置时为空;computed-type保存部署时实际生效的值:优先取应用级值,未设置则回退到全局值,再未设置则回退到内置默认nginx。
针对单个应用:
dokku proxy:report node-js-app
输出:
=====> node-js-app proxy information
Proxy computed type: nginx
Proxy enabled: true
Proxy global type:
Proxy type:
也可以传标志只输出某一项信息:
dokku proxy:report node-js-app --proxy-computed-type
从源码看,computed-type 的三级回退逻辑实现在 plugins/proxy/functions.go 的 getComputedProxyType 中:先读应用级 type,为空再读全局 type,仍为空则返回 "nginx"。proxy-port、proxy-ssl-port、disabled 等属性也遵循同样的"应用级 → 全局级 → 默认值"回退模式(对应 getComputedProxyPort、getComputedProxySSLPort、getComputedDisabled)。proxy:report 支持的全部标志在 plugins/proxy/report.go 中定义,例如 --proxy-type、--proxy-global-type、--proxy-computed-type、--proxy-enabled、--proxy-disabled、--proxy-proxy-port、--proxy-global-proxy-port、--proxy-computed-proxy-port 等。
另外 proxy:report 还支持 --format json(由 plugins/proxy/src/subcommands/subcommands.go 中的 --format 标志解析,取值 stdout 或 json),便于自动化脚本解析。JSON 输出的键为去掉 --proxy- 前缀后的名称(如 type、global-type、computed-type);在 0.38.x 的弃用窗口期内还会额外输出带 proxy- 前缀的旧键(如 proxy-type),这些旧键将在未来的主版本中移除。
7.1 容器网络接口绑定(历史行为变更)
自 0.11.0 起行为变更
在 Dokku 0.5.0 到 0.11.0 之间,启用或禁用应用的代理同时会控制应用是否绑定到所有网络接口(例如 0.0.0.0)。自 0.11.0 起,这一行为改由 network 插件控制。详见 网络文档中的容器网络接口绑定 一节。
八、属性参考(Properties)
8.1 可设置属性
[!NOTE]
Report flags列是proxy:report接受的 CLI 参数名。proxy:report --format json输出的 JSON 键为去掉--proxy-前缀后的同名(如type、global-type、computed-type)。带proxy-前缀的旧键(如proxy-type)在 0.38.x 弃用窗口期内仍会输出,将在未来主版本移除。
| 属性 | 作用域 | 默认值 | 报告标志 | 说明 |
|---|---|---|---|---|
disabled |
仅应用级 | false |
--proxy-disabled、--proxy-computed-disabled(同时以反转形式暴露为 --proxy-enabled) |
为 true 时禁用该应用的代理集成(proxy:enable / proxy:disable 即读写此属性) |
proxy-port |
应用 + 全局 | 无 | --proxy-proxy-port、--proxy-global-proxy-port、--proxy-computed-proxy-port |
覆盖生成的代理配置中 HTTP 监听端口 |
proxy-ssl-port |
应用 + 全局 | 无 | --proxy-proxy-ssl-port、--proxy-global-proxy-ssl-port、--proxy-computed-proxy-ssl-port |
覆盖生成的代理配置中 HTTPS 监听端口 |
type |
应用 + 全局 | nginx |
--proxy-type、--proxy-global-type、--proxy-computed-type |
处理应用流量的代理实现(nginx、caddy、haproxy、traefik、openresty 或自定义插件) |
8.2 只读标志
以下标志会出现在 proxy:report 输出中,但不由 proxy:set 管理:
| 标志 | 说明 |
|---|---|
--proxy-enabled |
当应用的 disabled 属性不为 true 时输出 true |
九、实现一个自定义代理插件
自定义插件名必须以 -vhosts 后缀结尾,否则通过 proxy:set 进行切换可能无法按预期工作。
以下 Dokku 命令用于与完整的代理实现交互,每个命令都对应相应的插件触发钩子:
| 命令 | 触发钩子 |
|---|---|
domains:add:为应用添加域名 |
post-domains-update |
domains:clear:清空应用的域名 |
post-domains-update |
domains:disable:禁用应用域名 |
pre-disable-vhost |
domains:enable:启用应用域名 |
pre-enable-vhost |
domains:remove:从应用移除域名 |
post-domains-update |
domains:reset:将应用域名重置为全局配置的域名 |
post-domains-update |
domains:set:设置应用的全部域名 |
post-domains-update |
proxy:build-config:构建(或重建)外部代理配置 |
proxy-build-config |
proxy:clear-config:清理外部代理配置 |
proxy-clear-config |
proxy:disable:禁用应用的代理配置 |
proxy-disable |
proxy:enable:启用应用的代理配置 |
proxy-enable |
ports:add:为应用添加一个或多个端口映射 |
post-proxy-ports-update |
ports:clear:清空应用的全部端口映射 |
post-proxy-ports-update |
ports:remove:从应用移除一个或多个端口映射 |
post-proxy-ports-update |
ports:set:设置应用的全部端口映射 |
post-proxy-ports-update |
代理实现可以根据需要省略部分功能,或者使用插件触发钩子从其他插件补充配置信息。此外:
- 单个代理实现可能触发应用重建,具体取决于代理元数据在代理实现中的暴露方式;
- 代理实现可以以任何合适的方式安装代理本身所需的额外软件——代理软件既可以运行在宿主机上,也可以运行在 Docker 容器中(通过暴露端口或使用宿主机网络)。
以 nginx 实现为参照:其 proxy-build-config、proxy-clear-config、proxy-disable 等触发钩子均会先通过 proxy-type 触发器校验应用使用的代理类型是否为 nginx,再执行相应动作(见 plugins/nginx-vhosts 目录下的同名文件),这种"类型校验 + 委托执行"的写法是编写自定义代理插件的良好范本。
十、深入 nginx 实现:代理配置的生成与生命周期
虽然 proxy 插件负责抽象与调度,但默认的 nginx 实现承载了绝大多数实际流量,理解其内部行为有助于排查代理相关问题。完整内容参见 nginx 代理文档,以下是与 proxy 插件直接相关的要点。
10.1 请求代理与负载均衡
默认情况下,只有 web 进程会被 nginx 代理实现转发。nginx 以**轮询(round-robin)**方式将请求分发到所有已部署(已扩容)的运行 web 进程类型的容器上,从而让宿主机资源在单线程应用中得以充分利用(例如在 4 核机器上执行 dokku ps:scale node-js-app web=4)。
[!NOTE] 由于插件的实现方式,如果应用成功启动了
web容器但其他容器部署失败,nginx 最终可能停止路由请求。此时应回滚代码,或手动执行dokku proxy:build-config $APP以确保请求路由到新的 web 容器。
10.2 未部署应用的 nginx 配置(自 0.38.0)
当应用已创建但尚未部署、没有 web 进程类型、或没有运行中的 web 进程时,Dokku 会生成一个返回 502 Bad Gateway 的最小化 nginx 配置,以确保:
- 应用的域名可解析并返回非 200 状态码,便于监控工具发现问题;
- 诸如 letsencrypt 之类的 SSL 证书签发工具可以正常工作(因为 nginx 已在监听该域名);
nginx.conf.d/包含目录可用于插件自定义。
该 502 错误页内置了自动重试的 JavaScript,应用可用时会自动刷新页面。一旦应用部署完成且有运行中的 web 进程,占位配置会被自动替换为完整代理配置。
10.3 自定义 nginx 配置(nginx.conf.sigil)
Dokku 使用名为 sigil 的模板库为每个应用生成 nginx 配置。可以提交一份名为 nginx.conf.sigil 的文件(内容基于 默认配置模板)来覆盖默认模板。模板查找位置取决于部署方式:
git:from-image和git:load-image部署:Docker 镜像的WORKDIR;- 其他所有部署(git push、
git:from-archive、git:sync):源码树根目录。
如需为单个应用指定其他路径(例如 monorepo 场景),可用 nginx:set 设置 nginx-conf-sigil-path(相对路径,不会按绝对路径处理;若仓库中不存在该文件,则按"无 nginx.conf.sigil"继续构建):
dokku nginx:set node-js-app nginx-conf-sigil-path .dokku/nginx.conf.sigil
模板中可用的变量包括:{{ .APP }}(应用名)、{{ .PROXY_PORT }}(非 SSL 监听端口,与 proxy-port 属性一致)、{{ .PROXY_SSL_PORT }}(SSL 监听端口,与 proxy-ssl-port 属性一致)、{{ .PROXY_PORT_MAP }}(端口映射列表)、{{ .PROXY_UPSTREAM_PORTS }}(上游端口列表)、{{ .SSL_INUSE }}、{{ .NOSSL_SERVER_NAME }}、{{ .SSL_SERVER_NAME }} 等。每个进程类型的网络监听器还会以 .DOKKU_APP_${PROCESS_TYPE}_LISTENERS 变量暴露(PROCESS_TYPE 大写化、连字符转下划线),可用于通过 nginx 暴露非 web 进程。应用环境变量则可通过 {{ var "FOO" }} 形式在模板中访问。
自定义 nginx.conf.sigil 会在每次部署开始时、模板从源码树提取后、构建阶段之前被自动预校验:模板经 sigil 渲染后,套上最小包装配置运行 nginx -t;校验失败则在整个构建工作开始前中止部署。可通过设置 disable-custom-config true 跳过此行为。注意:校验用包装配置不含全局 nginx 配置中的顶层 load_module 指令,因此依赖动态加载模块指令的 nginx.conf.sigil 即使对真实服务器配置 nginx -t 能通过,也可能在此预校验中失败;可参考 nginx-conf-sigil 文档中的自定义 nginx 模块 通过 nginx-app-template-source 触发钩子的 validate-config 模板类型提供自定义校验包装。
10.4 属性变更后记得重建配置
nginx 插件的全部属性(client-max-body-size、proxy-read-timeout、hsts、x-forwarded-* 系列等)都支持应用级与全局级设置,取值优先级为"应用级 → 全局级 → Dokku 默认值"。修改这些值后都需要通过 proxy:build-config 命令重建 nginx 配置才能生效(例如修改 access-log-path、client-max-body-size 等属性时)。nginx 插件完整属性表参见 nginx 代理文档的 Properties 一节。
[!WARNING] nginx 插件不对属性值做校验,值会原样用于生成的配置中,设置时需自行保证合法性。
10.5 nginx 服务生命周期管理(0.28.0+)
dokku nginx:start:启动 nginx 服务(自 0.28.0);dokku nginx:stop:停止 nginx 服务(自 0.28.0);dokku nginx:reload:校验当前 nginx 配置并在合法时触发优雅 reload(自 0.38.0),是直接编辑/etc/nginx/conf.d/下文件后的首选命令;配置非法时命令以非零退出码结束并打印校验错误,不执行 reload;dokku nginx:access-logs <app> [-t]/dokku nginx:error-logs <app> [-t]:查看(-t跟随)应用访问/错误日志,默认路径为/var/log/nginx/${APP}-access.log与/var/log/nginx/${APP}-error.log;dokku nginx:show-config <app>:显示应用的 nginx 配置,便于调试;dokku nginx:validate-config [<app>] [--clean]:逐个校验应用 nginx 配置(基于nginx -t);--clean会删除无效配置。该命令的退出码为对服务器真实 nginx 配置执行nginx -t的退出码。
10.6 默认站点(catch-all,自 0.38.0)
在全新 apt 安装中,Dokku 会在 /etc/nginx/conf.d/00-default-vhost.conf 放置一个捕获所有未知 Host 头的默认站点:HTTPS 使用 ssl_reject_handshake on 拒绝握手,HTTP 使用 return 444 直接关闭连接。00- 前缀保证该文件先于 /etc/nginx/conf.d/dokku.conf 加载,从而让各端口的 default_server 标记在任何应用 server 块之前生效。在 nginx 版本低于 1.19.4 的系统(如随 Debian Bullseye 提供的 nginx 1.18.0)上,postinst 会检测并安装仅 HTTP 的变体。若上游 nginx 自带默认 vhost 与 Dokku 的 catch-all 冲突,postinst 会将其重命名为 ${path}.dokku-disabled 而非删除,保留本地定制内容。安装时可通过 debconf 选择不安装(echo 'dokku dokku/install_default_site boolean false' | debconf-set-selections),或安装后编辑该文件并 dokku nginx:reload 自定义行为(例如将 return 444; 改为 return 410;)。
十一、关联资源导航
- 端口映射管理:见 端口管理文档,其中 端口方案 一节解释了 Dokku 使用的端口映射方案;
- 域名配置:见 域名配置文档,涵盖 自定义主机名 与 启用/禁用 VHOSTS;
- SSL 证书配置:见 SSL 文档;
- 容器网络接口绑定:见 网络文档;
- nginx 实现细节:见 nginx 代理文档;
- nginx 自定义模板格式:见 nginx-conf-sigil 文件格式文档;
- 源码入口:proxy 插件核心逻辑在 plugins/proxy/proxy.go、plugins/proxy/subcommands.go、plugins/proxy/functions.go、plugins/proxy/report.go;nginx 触发的代理钩子在 plugins/nginx-vhosts/proxy-build-config、plugins/nginx-vhosts/proxy-clear-config、plugins/nginx-vhosts/proxy-disable 等文件中。
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 StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00