多个 Docker 项目共用一个 Cloudflare Tunnel 的部署方案
如果你在一台 VPS 上跑了好几个 Docker 项目,又给每个项目都单独塞一个 cloudflared,那大概率很快就会觉得乱。
其实更舒服的做法,是把隧道独立出来,让它通过一个共享网络去接多个业务容器。结构更清楚,维护也轻松很多。
多个 Docker 项目共用一个 Cloudflare Tunnel 的部署方案
很多人第一次迁移多个 Docker 项目时,容易走进一个“看起来能用、后面越来越难管”的方向:
- MinIO 带一个
cloudflared - OneNav 带一个
cloudflared - 以后再加项目,再带一个
cloudflared
短期看没问题。长期看,容器数量会越来越乱,排错时也会分不清到底是谁在负责入口。
更推荐的方式是:只保留一个独立的 Cloudflare Tunnel 容器,让它通过 Docker 共享网络去连接多个业务服务。
目标架构长什么样
这套方案的核心很简单:
- 创建一个公共 Docker 网络,比如
cf_tunnel_net - 让
cloudflared容器接入这个网络 - 让需要暴露到公网的业务容器也接入这个网络
- 在 Cloudflare 后台直接把域名路由到对应容器名和端口
流量路径大致是这样:
用户请求
-> Cloudflare
-> Cloudflare Tunnel
-> cloudflared 容器
-> Docker 共享网络
-> 业务容器这个结构最舒服的地方在于:入口统一,业务分离。
第一步:创建共享网络
先在 VPS 上创建一个持久化的 Docker 网络:
docker network create cf_tunnel_net这个网络只需要建一次。
后面无论你再加多少项目,只要让它们加入这个网络,就都能被同一个隧道访问到。
第二步:单独部署 Cloudflare Tunnel
建议给隧道单独建一个目录,后面维护会清爽很多。
目录准备
mkdir -p /root/cloudflared
cd /root/cloudflareddocker-compose.yml 示例
version: '3.8'
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: main_tunnel
restart: always
command: tunnel --no-autoupdate run --token <你的_TUNNEL_TOKEN>
networks:
- cf_tunnel_net
networks:
cf_tunnel_net:
external: true然后启动:
docker compose up -d如果你的机器还是老环境,也可以继续用:
docker-compose up -d第三步:把业务容器接到同一个网络
重点不是“新增什么配置”,而是把每个业务项目里原本自带的 Tunnel 配置删掉,改成只保留业务本身,然后接入共享网络。
MinIO 示例
version: '3.8'
services:
minio:
image: minio/minio
container_name: minio
volumes:
- /root/minio-data:/data
command: server /data --console-address ":9001"
networks:
- cf_tunnel_net
networks:
cf_tunnel_net:
external: trueOneNav 示例
version: '3.8'
services:
onenav:
image: helloz/onenav
container_name: onenav
volumes:
- /opt/onenav/data:/data/wwwroot/default/data
networks:
- cf_tunnel_net
networks:
cf_tunnel_net:
external: true到这里,main_tunnel、minio、onenav 就在同一个 Docker 网络里了。
它们之间不需要走公网,也不需要额外映射一堆乱七八糟的端口给 Tunnel 用。
第四步:在 Cloudflare 后台配置路由
登录 Cloudflare Zero Trust 后,进入你的 Tunnel 详情页,给它添加 Public Hostname。
可以按下面这种方式配置:
| 域名 | Service Type | URL |
|---|---|---|
oss.yourdomain.com | HTTP | minio:9000 |
oss-admin.yourdomain.com | HTTP | minio:9001 |
nav.yourdomain.com | HTTP | onenav:80 |
这里最关键的一点是:
URL 写的是容器名和容器内部端口,不是宿主机 IP,也不是公网域名。
因为在同一个 Docker 网络里,cloudflared 可以直接通过 Docker 的内部 DNS 找到:
minioonenav- 其他未来接入的容器
这也是这套方案优雅的地方。
这种做法有什么好处
1. 入口统一
所有对外暴露的站点,都由一个 Tunnel 管。
以后排错时,你不用去想:
- 到底是哪一个
cloudflared容器挂了 - 哪一个 compose 文件里写了旧 token
- 哪个项目偷偷占了同一个域名
2. 容器更干净
业务容器只负责业务。
- MinIO 只跑存储
- OneNav 只跑导航
- 其他项目只做自己的事
边界会清楚很多。
3. 后期扩展更轻松
以后你再加一个项目,比如:
umamiuptime-kumaalistimmich
通常只要做两件事:
- 让容器加入
cf_tunnel_net - 在 Cloudflare 后台加一条新的 hostname 路由
不用再复制一整套 Tunnel 配置。
常用维护命令
一次性启动所有项目
如果你把隧道和业务分散在不同目录下,可以写个简单脚本统一启动:
for dir in /root/cloudflared /root/minio /root/onenav; do
cd "$dir" && docker compose up -d
done检查容器是不是都接进同一个网络
docker network inspect cf_tunnel_net正常情况下,你应该能在输出的 Containers 里看到类似这些名字:
main_tunnelminioonenav
如果某个服务打不开,先看它是不是根本没进这个网络。
容易踩的几个坑
容器名和后台路由写不一致
如果你的 Cloudflare 路由写的是 minio:9000,那容器在共享网络里就得真能被解析成 minio。
最稳的方式,就是把 container_name 和你在后台写的服务名保持一致。
业务容器没加入共享网络
这是最常见的问题之一。
Tunnel 没法访问业务容器,不一定是 Cloudflare 有问题,很可能只是两个容器根本不在同一张网络里。
旧项目里还残留自己的 cloudflared
迁移时最好把旧的隧道容器彻底清掉。
不然你表面上看已经切到统一入口了,实际上还有旧容器在后台跑,后面查问题会很烦。
总结
如果你的目标是:
- 一台 VPS 跑多个 Docker 服务
- 统一用 Cloudflare Tunnel 暴露到公网
- 后面还想继续扩展新项目
那么最省心的做法,就是:
一个独立的
cloudflared容器 + 一个共享 Docker 网络 + 多个接入该网络的业务容器。
结构一旦理顺,后面无论是加站点、换项目,还是排查网络问题,都会比“每个项目各带一个 Tunnel”轻松得多。
