不用公网 IP,也能把 Docker 服务安全挂到域名上:Cloudflare Tunnel 实战教程
Cloudflare Docker部署教程|附源码
在日常开发与运维中,我们经常会遇到这样的需求:本地服务、内网服务、家庭服务器、NAS、Docker 容器应用想要安全地暴露到公网访问,但又不想折腾公网 IP、端口映射、动态 DNS、Nginx 反向代理、SSL 证书等复杂配置。此时,Cloudflare Tunnel 是一个非常实用的方案。
Cloudflare Tunnel 可以让你的本地服务通过 Cloudflare 的边缘网络安全地对外提供访问能力。它不需要服务器拥有公网 IP,也不需要在路由器上开放端口,只需要在本地运行一个 cloudflared 客户端,即可将内网服务映射到你自己的域名上。
本文将详细介绍如何使用 Docker 部署 Cloudflare Tunnel,并提供完整源码示例,包括 docker-compose.yml、配置文件、常用命令以及部署注意事项。
一、什么是 Cloudflare Tunnel?
Cloudflare Tunnel 是 Cloudflare 提供的一种安全隧道服务,底层通过 cloudflared 客户端与 Cloudflare 网络建立出站连接。用户访问你的域名时,请求会先进入 Cloudflare,再通过加密隧道转发到你本地的服务。
简单来说,它的访问链路如下:
用户浏览器
↓
Cloudflare CDN / DNS / WAF
↓
Cloudflare Tunnel
↓
本地 cloudflared 容器
↓
内网服务 / Docker 应用
例如,你本地运行了一个 Web 服务:
http://localhost:8080
通过 Cloudflare Tunnel,可以将它映射为:
https://app.example.com
用户访问 https://app.example.com 时,实际上访问的是你本地的 8080 服务。
二、Cloudflare Tunnel 的优势
相比传统公网部署方式,Cloudflare Tunnel 有很多优势。
1. 不需要公网 IP
很多家庭宽带、内网服务器、公司测试环境并没有公网 IP,或者公网 IP 经常变化。使用 Cloudflare Tunnel 后,只要服务器能访问互联网,就可以对外提供服务。
2. 不需要开放端口
传统方式通常需要在路由器、防火墙或云服务器安全组中开放 80、443、8080 等端口。Cloudflare Tunnel 使用出站连接,不需要外部主动连接你的服务器,因此更加安全。
3. 自动 HTTPS
只要域名托管在 Cloudflare,Cloudflare 会自动提供 HTTPS 证书。你本地服务即使是 HTTP,也可以通过 Cloudflare 对外提供 HTTPS 访问。
4. 支持 Docker 部署
cloudflared 官方提供 Docker 镜像,可以很方便地使用 Docker 或 Docker Compose 进行部署、迁移和维护。
5. 可结合 Cloudflare Zero Trust
Cloudflare Tunnel 还可以结合 Cloudflare Access,实现登录认证、邮箱验证、身份访问控制等功能,适合企业内部系统、管理后台、私有服务等场景。
三、适用场景
Cloudflare Tunnel 适合以下场景:
- 家庭服务器对外访问;
- NAS 服务暴露到公网;
- Docker 应用映射到域名;
- 本地开发环境临时演示;
- 内网管理后台安全访问;
- 自建博客、图床、监控面板;
- Home Assistant、Jellyfin、qBittorrent Web UI 等服务访问;
- 无公网 IP 环境下部署 Web 服务。
不过需要注意,Cloudflare Tunnel 更适合 Web 类服务。如果你要暴露游戏服务器、大流量下载、特殊 TCP/UDP 服务,则需要根据 Cloudflare 的规则和套餐情况谨慎使用。
四、部署前准备
在开始之前,你需要准备以下内容:
1. 一个 Cloudflare 账号
访问 Cloudflare 官网注册账号:
https://www.cloudflare.com/
2. 一个已接入 Cloudflare 的域名
你需要拥有一个域名,并将域名 DNS 托管到 Cloudflare。
例如:
example.com
后续我们会创建子域名:
app.example.com
3. 一台安装 Docker 的服务器
可以是:
- 云服务器;
- 家庭服务器;
- NAS;
- 树莓派;
- 本地 Linux 主机;
- Windows / macOS Docker Desktop。
检查 Docker 是否安装成功:
docker -v
检查 Docker Compose 是否可用:
docker compose version
如果显示版本号,说明环境正常。
五、部署方案说明
本文提供两种常见部署方式:
- Token 方式部署:最简单,推荐新手使用;
- 配置文件方式部署:更灵活,适合多服务、多域名场景。
如果你只是想快速把一个服务暴露出去,推荐使用 Token 方式。
如果你想将多个子域名映射到不同容器,建议使用配置文件方式。
方案一:使用 Token 快速部署 Cloudflare Tunnel
六、创建 Cloudflare Tunnel
登录 Cloudflare 后,进入 Zero Trust 控制台:
https://one.dash.cloudflare.com/
然后按以下步骤操作:
- 进入左侧菜单:
Networks; - 点击:
Tunnels; - 点击:
Create a tunnel; - 选择:
Cloudflared; - 输入隧道名称,例如:
docker-tunnel
- 选择 Docker 部署方式;
- Cloudflare 会生成一段运行命令,里面包含一个很长的 Token。
命令大概长这样:
docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token xxxxxxxxxxxxxxxxxxxxxxxxx
其中 xxxxxxxxxxxxxxxxxxxxxxxxx 就是你的 Tunnel Token。
七、Docker Compose 部署源码
为了方便管理,我们不直接使用 docker run,而是使用 docker-compose.yml。
创建项目目录:
mkdir -p /opt/cloudflare-tunnel
cd /opt/cloudflare-tunnel
创建 docker-compose.yml 文件:
vim docker-compose.yml
写入以下内容:
version: "3.8"
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
environment:
- TZ=Asia/Shanghai
再创建 .env 文件,用于存放 Token:
vim .env
写入:
TUNNEL_TOKEN=你的Cloudflare_Tunnel_Token
例如:
TUNNEL_TOKEN=eyJhIjoi......这里是你的token......abc
注意:
.env文件中不要加引号,也不要在等号两边加空格。
八、启动 Cloudflare Tunnel
在 /opt/cloudflare-tunnel 目录下执行:
docker compose up -d
查看容器状态:
docker ps
查看日志:
docker logs -f cloudflared
如果看到类似以下日志,说明连接成功:
Registered tunnel connection
Connection established
此时,你的 Cloudflare Tunnel 已经在 Docker 中运行。
九、配置公网访问域名
回到 Cloudflare Zero Trust 面板,在刚才创建的 Tunnel 中,找到 Public Hostname,添加一个访问规则。
例如:
| 配置项 | 示例 |
|---|---|
| Subdomain | app |
| Domain | example.com |
| Path | 留空 |
| Service Type | HTTP |
| URL | http://192.168.1.100:8080 |
如果你的目标服务和 cloudflared 在同一台机器上,也可以填写:
http://host.docker.internal:8080
但在 Linux 环境下,host.docker.internal 并不总是默认可用。因此更推荐使用服务器的局域网 IP,例如:
http://192.168.1.100:8080
如果目标服务也是 Docker 容器,并且与 cloudflared 在同一个 Docker 网络下,可以使用容器名访问,例如:
http://nginx:80
方案二:使用配置文件部署 Cloudflare Tunnel
Token 方式非常简单,但所有路由规则都在 Cloudflare 后台配置。如果你希望将配置文件纳入版本管理,或者想更清晰地管理多服务路由,可以使用配置文件方式。
十、创建本地配置目录
创建目录:
mkdir -p /opt/cloudflare-tunnel/config
cd /opt/cloudflare-tunnel
目录结构建议如下:
/opt/cloudflare-tunnel
├── docker-compose.yml
└── config
├── config.yml
└── tunnel.json
其中:
config.yml:隧道路由配置;tunnel.json:Cloudflare Tunnel 凭证文件。
十一、登录 cloudflared
如果你本机还没有 cloudflared 命令,也可以直接用 Docker 执行登录。
执行:
docker run --rm -it \
-v /opt/cloudflare-tunnel/config:/home/nonroot/.cloudflared \
cloudflare/cloudflared:latest tunnel login
执行后,终端会输出一个登录链接。
复制链接到浏览器打开,选择你的域名并授权。授权完成后,配置目录中会生成一个证书文件:
cert.pem
十二、创建 Tunnel
执行:
docker run --rm -it \
-v /opt/cloudflare-tunnel/config:/home/nonroot/.cloudflared \
cloudflare/cloudflared:latest tunnel create docker-tunnel
创建成功后,会生成一个类似下面的凭证文件:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json
同时命令行会输出 Tunnel ID,例如:
Created tunnel docker-tunnel with id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
你需要记录这个 Tunnel ID。
为了方便管理,可以将凭证文件重命名为:
mv /opt/cloudflare-tunnel/config/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json \
/opt/cloudflare-tunnel/config/tunnel.json
十三、编写 config.yml 源码
创建配置文件:
vim /opt/cloudflare-tunnel/config/config.yml
写入以下内容:
tunnel: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
credentials-file: /home/nonroot/.cloudflared/tunnel.json
ingress:
- hostname: app.example.com
service: http://web:80
- hostname: api.example.com
service: http://api:3000
- hostname: monitor.example.com
service: http://192.168.1.100:9090
- service: http_status:404
需要将:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
替换为你的 Tunnel ID。
配置说明:
| 字段 | 说明 |
|---|---|
tunnel |
Tunnel ID |
credentials-file |
凭证文件路径 |
ingress |
入口转发规则 |
hostname |
对外访问域名 |
service |
本地目标服务 |
http_status:404 |
默认兜底规则 |
最后一条:
- service: http_status:404
非常重要。如果没有匹配到任何 hostname,就返回 404,避免错误暴露服务。
十四、创建 DNS 路由
配置文件写好后,还需要将域名绑定到 Tunnel。
执行:
docker run --rm -it \
-v /opt/cloudflare-tunnel/config:/home/nonroot/.cloudflared \
cloudflare/cloudflared:latest tunnel route dns docker-tunnel app.example.com
继续添加其他域名:
docker run --rm -it \
-v /opt/cloudflare-tunnel/config:/home/nonroot/.cloudflared \
cloudflare/cloudflared:latest tunnel route dns docker-tunnel api.example.com
docker run --rm -it \
-v /opt/cloudflare-tunnel/config:/home/nonroot/.cloudflared \
cloudflare/cloudflared:latest tunnel route dns docker-tunnel monitor.example.com
执行成功后,Cloudflare DNS 中会自动生成对应的 CNAME 记录。
十五、Docker Compose 完整源码
下面给出一个完整示例:同时部署 cloudflared、nginx 和一个简单的 API 服务。
项目目录结构:
cloudflare-docker-demo
├── docker-compose.yml
├── config
│ ├── config.yml
│ └── tunnel.json
├── nginx
│ └── index.html
└── api
├── Dockerfile
└── server.js
1. docker-compose.yml
version: "3.8"
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel --config /home/nonroot/.cloudflared/config.yml run
volumes:
- ./config:/home/nonroot/.cloudflared
networks:
- tunnel-net
environment:
- TZ=Asia/Shanghai
web:
image: nginx:alpine
container_name: demo-web
restart: unless-stopped
volumes:
- ./nginx/index.html:/usr/share/nginx/html/index.html:ro
networks:
- tunnel-net
api:
build:
context: ./api
dockerfile: Dockerfile
container_name: demo-api
restart: unless-stopped
networks:
- tunnel-net
environment:
- NODE_ENV=production
- PORT=3000
networks:
tunnel-net:
driver: bridge
2. config/config.yml
tunnel: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
credentials-file: /home/nonroot/.cloudflared/tunnel.json
ingress:
- hostname: app.example.com
service: http://web:80
- hostname: api.example.com
service: http://api:3000
- service: http_status:404
3. nginx/index.html
Cloudflare Tunnel Docker Demo
Cloudflare Tunnel 部署成功
如果你能看到这个页面,说明 Nginx 已经通过 Cloudflare Tunnel 成功暴露到公网。
当前服务地址:app.example.com
4. api/Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY server.js .
EXPOSE 3000
CMD ["node", "server.js"]
5. api/server.js
const http = require("http");
const port = process.env.PORT || 3000;
const server = http.createServer((req, res) => {
const data = {
message: "Hello from Cloudflare Tunnel API",
path: req.url,
time: new Date().toISOString(),
};
res.writeHead(200, {
"Content-Type": "application/json; charset=utf-8",
});
res.end(JSON.stringify(data, null, 2));
});
server.listen(port, () => {
console.log(`API server is running on port ${port}`);
});
十六、启动完整项目
进入项目目录:
cd /opt/cloudflare-tunnel
启动:
docker compose up -d --build
查看容器:
docker compose ps
查看日志:
docker compose logs -f cloudflared
访问:
https://app.example.com
应该能看到 Nginx 页面。
访问:
https://api.example.com
应该能看到 JSON 响应。
十七、常用维护命令
1. 停止服务
docker compose down
2. 重启服务
docker compose restart
3. 更新镜像
docker compose pull
docker compose up -d
4. 查看日志
docker logs -f cloudflared
或者:
docker compose logs -f
5. 查看 Tunnel 列表
docker run --rm -it \
-v /opt/cloudflare-tunnel/config:/home/nonroot/.cloudflared \
cloudflare/cloudflared:latest tunnel list
6. 删除 Tunnel
docker run --rm -it \
-v /opt/cloudflare-tunnel/config:/home/nonroot/.cloudflared \
cloudflare/cloudflared:latest tunnel delete docker-tunnel
删除前请确认不再使用该隧道。
十八、常见问题排查
1. 访问域名显示 502 Bad Gateway
通常原因是 Cloudflare Tunnel 能连上,但后端服务不可达。
检查内容:
- 目标容器是否正常运行;
config.yml中 service 地址是否正确;- 是否在同一个 Docker network;
- 本地服务端口是否正确;
- 服务是否只监听了
127.0.0.1。
如果你的服务在 Docker 容器里,推荐使用容器名访问:
service: http://web:80
如果你的服务在宿主机上,推荐使用局域网 IP:
service: http://192.168.1.100:8080
2. 日志提示 credentials file not found
说明凭证文件路径配置错误。
检查:
credentials-file: /home/nonroot/.cloudflared/tunnel.json
同时确认本地目录中是否存在:
./config/tunnel.json
并且 Docker Compose 是否正确挂载:
volumes:
- ./config:/home/nonroot/.cloudflared
3. 域名无法解析
检查 Cloudflare DNS 中是否已经生成 CNAME 记录。
也可以重新执行:
docker run --rm -it \
-v /opt/cloudflare-tunnel/config:/home/nonroot/.cloudflared \
cloudflare/cloudflared:latest tunnel route dns docker-tunnel app.example.com
4. 容器一直重启
查看日志:
docker logs cloudflared
常见原因包括:
- Token 错误;
- Tunnel 已被删除;
- 配置文件语法错误;
- 凭证文件路径错误;
- 网络无法访问 Cloudflare。
可以先执行:
docker compose config
检查 Compose 文件是否语法正确。
十九、安全建议
Cloudflare Tunnel 虽然降低了公网暴露风险,但并不意味着可以忽视服务本身的安全。
建议遵循以下原则:
1. 不要暴露高风险后台
例如数据库管理工具、服务器面板、Docker 管理界面等,如果必须暴露,建议加上 Cloudflare Access 身份认证。
2. 使用强密码和二次验证
所有对外服务都应配置强密码。如果应用支持 MFA,应尽量开启。
3. 设置访问策略
在 Cloudflare Zero Trust 中,可以为特定域名添加访问控制,例如仅允许指定邮箱、指定组织、指定国家或指定 IP 访问。
4. 不要泄露 Tunnel Token
Token 相当于隧道的钥匙。如果使用 Token 方式部署,请妥善保管 .env 文件,不要上传到公开 Git 仓库。
可以在 .gitignore 中加入:
.env
config/*.json
config/cert.pem
5. 定期更新镜像
建议定期更新 cloudflared 镜像:
docker compose pull
docker compose up -d
二十、推荐生产环境配置
如果用于长期运行,推荐使用以下结构:
/opt/cloudflare-tunnel
├── docker-compose.yml
├── .env
├── config
│ ├── config.yml
│ └── tunnel.json
└── README.md
并配合以下措施:
- 使用
restart: unless-stopped; - 不直接暴露业务容器端口;
- 所有服务放在独立 Docker 网络;
- 敏感配置使用
.env; - 对管理后台启用 Cloudflare Access;
- 定期备份
config目录; - 定期更新 Docker 镜像。
二十一、总结
本文完整介绍了如何使用 Docker 部署 Cloudflare Tunnel,并提供了两种部署方式:
- Token 快速部署:适合新手,配置简单,直接在 Cloudflare 后台管理访问规则;
- 配置文件部署:适合进阶用户,可以将多域名、多服务转发规则写入本地配置文件,便于维护和迁移。
如果你的服务器没有公网 IP,或者你不想开放路由器端口,Cloudflare Tunnel 是一个非常优秀的解决方案。它可以让内网服务通过 HTTPS 域名安全访问,同时借助 Cloudflare 的 DNS、CDN、WAF、Zero Trust 等能力提升安全性和可用性。
对于 Docker 用户来说,cloudflare/cloudflared 镜像已经足够成熟,配合 Docker Compose 可以实现稳定、可复制、易维护的部署方式。只要正确配置 Tunnel、DNS 和后端服务地址,就能快速完成内网服务公网化。
最后,如果你只是部署单个服务,推荐使用 Token 方式;如果你需要管理多个应用、多个子域名,推荐使用配置文件方式。实际生产环境中,也建议配合 Cloudflare Access 做身份认证,避免敏感后台直接暴露到公网。