Home
avatar

nax

多个 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/cloudflared

docker-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: true

OneNav 示例

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_tunnelminioonenav 就在同一个 Docker 网络里了。

它们之间不需要走公网,也不需要额外映射一堆乱七八糟的端口给 Tunnel 用。


第四步:在 Cloudflare 后台配置路由

登录 Cloudflare Zero Trust 后,进入你的 Tunnel 详情页,给它添加 Public Hostname

可以按下面这种方式配置:

域名Service TypeURL
oss.yourdomain.comHTTPminio:9000
oss-admin.yourdomain.comHTTPminio:9001
nav.yourdomain.comHTTPonenav:80

这里最关键的一点是:

URL 写的是容器名和容器内部端口,不是宿主机 IP,也不是公网域名。

因为在同一个 Docker 网络里,cloudflared 可以直接通过 Docker 的内部 DNS 找到:

  • minio
  • onenav
  • 其他未来接入的容器

这也是这套方案优雅的地方。


这种做法有什么好处

1. 入口统一

所有对外暴露的站点,都由一个 Tunnel 管。

以后排错时,你不用去想:

  • 到底是哪一个 cloudflared 容器挂了
  • 哪一个 compose 文件里写了旧 token
  • 哪个项目偷偷占了同一个域名

2. 容器更干净

业务容器只负责业务。

  • MinIO 只跑存储
  • OneNav 只跑导航
  • 其他项目只做自己的事

边界会清楚很多。

3. 后期扩展更轻松

以后你再加一个项目,比如:

  • umami
  • uptime-kuma
  • alist
  • immich

通常只要做两件事:

  1. 让容器加入 cf_tunnel_net
  2. 在 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_tunnel
  • minio
  • onenav

如果某个服务打不开,先看它是不是根本没进这个网络。


容易踩的几个坑

容器名和后台路由写不一致

如果你的 Cloudflare 路由写的是 minio:9000,那容器在共享网络里就得真能被解析成 minio

最稳的方式,就是把 container_name 和你在后台写的服务名保持一致。

业务容器没加入共享网络

这是最常见的问题之一。

Tunnel 没法访问业务容器,不一定是 Cloudflare 有问题,很可能只是两个容器根本不在同一张网络里。

旧项目里还残留自己的 cloudflared

迁移时最好把旧的隧道容器彻底清掉。

不然你表面上看已经切到统一入口了,实际上还有旧容器在后台跑,后面查问题会很烦。

总结

如果你的目标是:

  • 一台 VPS 跑多个 Docker 服务
  • 统一用 Cloudflare Tunnel 暴露到公网
  • 后面还想继续扩展新项目

那么最省心的做法,就是:

一个独立的 cloudflared 容器 + 一个共享 Docker 网络 + 多个接入该网络的业务容器。

结构一旦理顺,后面无论是加站点、换项目,还是排查网络问题,都会比“每个项目各带一个 Tunnel”轻松得多。

Docker Cloudflare Tunnel cloudflared 容器网络 运维