云服务器配置 Traefik 教程

Traefik 适合用来做云服务器上的统一入口:所有 Web 服务都只需要加入同一个 Docker 网络,然后通过容器 label 声明域名、端口、HTTPS 证书策略即可。

本文默认场景:

  • 云服务器已经有公网 IP。
  • 域名已经解析到这台服务器。
  • 使用 Docker Compose 部署 Traefik。
  • 使用 Let’s Encrypt 自动签发 HTTPS 证书。
  • 服务都以 Docker 容器方式运行。

1. 准备服务器

先登录服务器:

ssh root@你的服务器IP

更新系统:

apt update && apt upgrade -y

安装常用工具:

apt install -y curl vim ufw apache2-utils

如果没有安装 Docker,可以使用官方安装脚本:

curl -fsSL https://get.docker.com | sh

确认 Docker 和 Compose 可用:

docker version
docker compose version

2. 配置防火墙

开放 SSH、HTTP、HTTPS:

ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status

Warning

不要把 Traefik Dashboard 的 8080 端口直接开放到公网。生产环境应该通过域名、HTTPS 和 Basic Auth 访问 Dashboard。

3. 准备域名解析

在 DNS 服务商中添加 A 记录:

记录名类型
traefik.example.comA服务器公网 IP
whoami.example.comA服务器公网 IP

把上面的 example.com 替换成自己的域名。

验证解析是否生效:

dig traefik.example.com +short
dig whoami.example.com +short

返回服务器公网 IP 即可继续。

3.1 接入 Cloudflare

如果域名托管在 Cloudflare,推荐先把域名接入 Cloudflare,再由 Cloudflare 管理 DNS 记录。

基本流程:

  1. 登录 Cloudflare Dashboard
  2. 点击 Add a domain,输入自己的根域名,例如 example.com
  3. 选择免费套餐或需要的套餐。
  4. Cloudflare 会扫描现有 DNS 记录,确认 A 记录是否正确。
  5. 到域名注册商后台,把 NS 服务器改成 Cloudflare 给出的两个 nameserver。
  6. 等待 Cloudflare 显示域名状态为 Active

DNS 记录建议:

记录名类型Proxy status
traefikA服务器公网 IPDNS only 或 Proxied
whoamiA服务器公网 IPDNS only 或 Proxied
appA服务器公网 IPDNS only 或 Proxied

Tip

如果使用 HTTP-01 证书挑战,调试阶段建议先把 Cloudflare 记录设为 DNS only。如果使用 DNS-01 证书挑战,可以使用 Proxied,也可以申请泛域名证书。

Cloudflare 的 SSL/TLS 模式建议设置为:

SSL/TLS -> Overview -> Full (strict)

Full (strict) 要求源站也有有效证书,正好由 Traefik + Let’s Encrypt 提供。

4. 创建目录

mkdir -p /opt/traefik/letsencrypt
cd /opt/traefik
touch letsencrypt/acme.json
chmod 600 letsencrypt/acme.json

acme.json 用来保存 Let’s Encrypt 证书。权限必须收紧,否则 Traefik 可能拒绝使用。

5. 创建外部 Docker 网络

docker network create traefik

之后需要被 Traefik 代理的业务容器,都加入这个网络。

6. 生成 Dashboard 登录密码

生成 Basic Auth 账号密码:

htpasswd -nbB admin '换成你的强密码'

命令会输出类似:

admin:$2y$05$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Docker Compose 的 label 中 $ 需要转义成 $$,例如:

admin:$$2y$$05$$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

7. 编写 Traefik 配置

7.1 创建 .env 文件

先在 /opt/traefik/.env 中保存公共参数:

TRAEFIK_IMAGE=traefik:v3.6
TRAEFIK_DOMAIN=traefik.example.com
ACME_EMAIL=你的邮箱@example.com
TRAEFIK_BASIC_AUTH_USERS='admin:$2y$05$替换成你生成的密码哈希'

如果要使用 Cloudflare DNS-01 挑战,再加上:

CF_DNS_API_TOKEN=替换成你的Cloudflare_API_Token

.env 文件里不要提交真实 token,也不要公开贴到笔记、仓库或群聊里。服务器上建议收紧权限:

chmod 600 /opt/traefik/.env

Warning

Basic Auth 密码哈希如果直接写在 docker-compose.yml 的 label 中,$ 需要写成 $$。如果放在 .env 且用单引号包起来,可以保留 admin:$2y$... 这种原始格式。

7.2 HTTP-01 版本

/opt/traefik/docker-compose.yml 中写入:

services:
  traefik:
    image: ${TRAEFIK_IMAGE}
    container_name: traefik
    restart: unless-stopped
    env_file:
      - .env
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=traefik"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.ssh.address=:2222"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
    ports:
      - "80:80"
      - "443:443"
      - "2222:2222"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(`${TRAEFIK_DOMAIN}`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.middlewares=traefik-auth"
      - "traefik.http.middlewares.traefik-auth.basicauth.users=${TRAEFIK_BASIC_AUTH_USERS}"
 
networks:
  traefik:
    external: true

需要替换的内容:

  • .env 中的 TRAEFIK_DOMAIN:换成 Dashboard 使用的域名。
  • .env 中的 ACME_EMAIL:换成真实邮箱,Let’s Encrypt 会用它发送证书相关通知。
  • .env 中的 TRAEFIK_BASIC_AUTH_USERS:换成上一步生成的 Basic Auth 字符串。

Tip

官方文档建议使用固定版本镜像,而不是 latest。这样升级 Traefik 时更可控。

7.3 Cloudflare DNS-01 版本

如果域名在 Cloudflare,推荐使用 DNS-01。优点:

  • 不依赖 80 端口完成证书验证。
  • 可以在 Cloudflare 开启橙云代理。
  • 可以申请泛域名证书,例如 *.example.com

先在 Cloudflare 创建 API Token:

  1. 进入 Cloudflare API Tokens
  2. 点击 Create Token
  3. 可以选择 Edit zone DNS 模板,或自定义 token。
  4. 权限至少包含:
    • Zone / Zone / Read
    • Zone / DNS / Edit
  5. Zone Resources 限制到需要管理的域名,例如 example.com
  6. 创建后复制 token,写入 /opt/traefik/.envCF_DNS_API_TOKEN

docker-compose.yml 中 ACME challenge 相关命令替换成 DNS-01:

services:
  traefik:
    image: ${TRAEFIK_IMAGE}
    container_name: traefik
    restart: unless-stopped
    env_file:
      - .env
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=traefik"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.ssh.address=:2222"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare"
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53"
    ports:
      - "80:80"
      - "443:443"
      - "2222:2222"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(`${TRAEFIK_DOMAIN}`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.middlewares=traefik-auth"
      - "traefik.http.middlewares.traefik-auth.basicauth.users=${TRAEFIK_BASIC_AUTH_USERS}"
 
networks:
  traefik:
    external: true

如果要给某个服务申请泛域名证书,可以在对应 router 上加:

labels:
  - "traefik.http.routers.app.tls.domains[0].main=example.com"
  - "traefik.http.routers.app.tls.domains[0].sans=*.example.com"

Note

Traefik 的 DNS-01 能力底层依赖 Lego。Cloudflare provider 支持 CF_DNS_API_TOKEN,也支持把 Zone Read 和 DNS Edit 拆成 CF_ZONE_API_TOKENCF_DNS_API_TOKEN 两个更细权限的 token。

8. 启动 Traefik

cd /opt/traefik
docker compose up -d
docker compose logs -f traefik

看到 Traefik 正常启动后,访问:

https://traefik.example.com/dashboard/

如果可以看到登录框和 Dashboard,说明入口代理已经配置成功。

9. 部署一个测试服务

创建 /opt/traefik/whoami.yml

services:
  whoami:
    image: traefik/whoami
    container_name: whoami
    restart: unless-stopped
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
      - "traefik.http.services.whoami.loadbalancer.server.port=80"
 
networks:
  traefik:
    external: true

启动测试服务:

docker compose -f /opt/traefik/whoami.yml up -d

验证:

curl -I https://whoami.example.com

返回 HTTP/2 200HTTP/1.1 200 OK 就说明 HTTPS 代理正常。

10. 接入自己的业务服务

业务服务的关键点只有三个:

  1. 加入 traefik 外部网络。
  2. 设置 traefik.enable=true
  3. 用 label 声明域名、入口、证书解析器、容器内部端口。

示例:

services:
  app:
    image: your-app:latest
    restart: unless-stopped
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.app.rule=Host(`app.example.com`)"
      - "traefik.http.routers.app.entrypoints=websecure"
      - "traefik.http.routers.app.tls.certresolver=letsencrypt"
      - "traefik.http.services.app.loadbalancer.server.port=8080"
 
networks:
  traefik:
    external: true

其中 loadbalancer.server.port=8080 指的是容器内部监听端口,不是宿主机端口。接入 Traefik 后,业务容器通常不需要再写:

ports:
  - "8080:8080"

除非这个端口确实需要绕过 Traefik 直接暴露。

11. 常见服务接入模板

11.1 普通 Web 服务

适合 Spring Boot、Node.js、Nginx、前端静态站点、管理后台等普通 HTTP 服务。

services:
  app:
    image: your-app:latest
    restart: unless-stopped
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.app.rule=Host(`app.example.com`)"
      - "traefik.http.routers.app.entrypoints=websecure"
      - "traefik.http.routers.app.tls.certresolver=letsencrypt"
      - "traefik.http.services.app.loadbalancer.server.port=8080"
 
networks:
  traefik:
    external: true

11.2 文件上传服务

Traefik 默认不会像 Nginx 那样用 client_max_body_size 限制上传大小。真正的上传大小限制通常在业务应用、Nginx、Spring Boot、网关、对象存储 SDK 或 Cloudflare 上。

如果希望在 Traefik 层主动限制上传大小,可以使用 buffering middleware:

services:
  file-api:
    image: your-file-api:latest
    restart: unless-stopped
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.file-api.rule=Host(`file.example.com`)"
      - "traefik.http.routers.file-api.entrypoints=websecure"
      - "traefik.http.routers.file-api.tls.certresolver=letsencrypt"
      - "traefik.http.routers.file-api.middlewares=file-upload-limit"
      - "traefik.http.middlewares.file-upload-limit.buffering.maxRequestBodyBytes=104857600"
      - "traefik.http.middlewares.file-upload-limit.buffering.memRequestBodyBytes=10485760"
      - "traefik.http.services.file-api.loadbalancer.server.port=8080"
 
networks:
  traefik:
    external: true

上面配置含义:

  • maxRequestBodyBytes=104857600:最大上传请求体 100 MB。
  • memRequestBodyBytes=10485760:超过 10 MB 后不全部放内存。

Warning

如果 Cloudflare 开了橙云代理,还要注意 Cloudflare 自身的上传大小限制。大文件上传场景更推荐客户端直传对象存储,例如 S3、R2、OSS,再由业务服务保存文件 URL。

11.3 文件下载服务

普通下载服务可以直接用 HTTP router。下载文件比较大时,重点是避免在应用层一次性把文件读入内存。

services:
  download:
    image: nginx:alpine
    restart: unless-stopped
    volumes:
      - ./downloads:/usr/share/nginx/html:ro
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.download.rule=Host(`download.example.com`)"
      - "traefik.http.routers.download.entrypoints=websecure"
      - "traefik.http.routers.download.tls.certresolver=letsencrypt"
      - "traefik.http.services.download.loadbalancer.server.port=80"
 
networks:
  traefik:
    external: true

如果需要限制下载响应大小,也可以使用 buffering.maxResponseBodyBytes

labels:
  - "traefik.http.routers.download.middlewares=download-limit"
  - "traefik.http.middlewares.download-limit.buffering.maxResponseBodyBytes=524288000"

Tip

大文件下载不建议随便设置 maxResponseBodyBytes,否则超过限制会被 Traefik 拒绝。更多时候应该把限制放在业务鉴权、对象存储签名 URL 或 CDN 规则里。

11.4 WebSocket 服务

Traefik 会自动处理 WebSocket 的 HTTP Upgrade,一般不需要额外配置 UpgradeConnection 请求头。

services:
  ws-app:
    image: your-ws-app:latest
    restart: unless-stopped
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.ws-app.rule=Host(`ws.example.com`)"
      - "traefik.http.routers.ws-app.entrypoints=websecure"
      - "traefik.http.routers.ws-app.tls.certresolver=letsencrypt"
      - "traefik.http.services.ws-app.loadbalancer.server.port=3000"
 
networks:
  traefik:
    external: true

客户端连接示例:

const socket = new WebSocket("wss://ws.example.com/socket");

如果 WebSocket 和普通 API 在同一个域名下,可以按路径拆路由:

labels:
  - "traefik.http.routers.app-api.rule=Host(`app.example.com`) && PathPrefix(`/api`)"
  - "traefik.http.routers.app-api.entrypoints=websecure"
  - "traefik.http.routers.app-api.tls.certresolver=letsencrypt"
  - "traefik.http.routers.app-api.service=app-api"
  - "traefik.http.services.app-api.loadbalancer.server.port=8080"
  - "traefik.http.routers.app-ws.rule=Host(`app.example.com`) && PathPrefix(`/socket`)"
  - "traefik.http.routers.app-ws.entrypoints=websecure"
  - "traefik.http.routers.app-ws.tls.certresolver=letsencrypt"
  - "traefik.http.routers.app-ws.service=app-ws"
  - "traefik.http.services.app-ws.loadbalancer.server.port=3000"

Note

Cloudflare 代理 WebSocket 时也需要在 Cloudflare 侧允许 WebSocket。大多数账号默认支持,但如果连接经常断开,需要检查 Cloudflare 规则、服务端心跳和应用超时设置。

11.5 gRPC 服务

gRPC 走 HTTP/2。Traefik 接 HTTPS 后,再转发到后端 gRPC 服务时,通常需要声明服务 scheme 为 h2c

services:
  grpc-api:
    image: your-grpc-api:latest
    restart: unless-stopped
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.grpc-api.rule=Host(`grpc.example.com`)"
      - "traefik.http.routers.grpc-api.entrypoints=websecure"
      - "traefik.http.routers.grpc-api.tls.certresolver=letsencrypt"
      - "traefik.http.services.grpc-api.loadbalancer.server.scheme=h2c"
      - "traefik.http.services.grpc-api.loadbalancer.server.port=50051"
 
networks:
  traefik:
    external: true

11.6 SSH 服务

SSH 不是 HTTP,不能用 traefik.http.* 标签,需要使用 TCP router。因为宿主机通常已经占用 22 端口,建议 Traefik 对外使用 2222,容器内部仍然转发到 22

Traefik 主配置需要有 SSH entrypoint:

command:
  - "--entrypoints.ssh.address=:2222"
ports:
  - "2222:2222"

Gitea SSH 示例:

services:
  gitea:
    image: gitea/gitea:latest
    restart: unless-stopped
    networks:
      - traefik
    volumes:
      - ./gitea:/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.gitea.rule=Host(`git.example.com`)"
      - "traefik.http.routers.gitea.entrypoints=websecure"
      - "traefik.http.routers.gitea.tls.certresolver=letsencrypt"
      - "traefik.http.services.gitea.loadbalancer.server.port=3000"
      - "traefik.tcp.routers.gitea-ssh.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.gitea-ssh.entrypoints=ssh"
      - "traefik.tcp.routers.gitea-ssh.service=gitea-ssh"
      - "traefik.tcp.services.gitea-ssh.loadbalancer.server.port=22"
 
networks:
  traefik:
    external: true

连接方式:

ssh -p 2222 [email protected]
git clone ssh://[email protected]:2222/用户名/仓库名.git

Warning

TCP router 的 HostSNI 只有在 TLS 连接中才能按域名区分。SSH 本身没有 SNI,所以常见做法是一个 SSH entrypoint 对应一个 SSH 服务;如果有多个 SSH 服务,就分配多个外部端口,例如 22222223

11.7 数据库或 Redis 这类 TCP 服务

数据库、Redis、MQTT 等也属于 TCP 服务,配置方式和 SSH 类似。通常不建议直接暴露到公网,更适合通过内网、VPN、堡垒机或 Cloudflare Tunnel 访问。

如果确实要暴露,例如临时暴露 PostgreSQL:

services:
  postgres:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: 强密码
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.tcp.routers.postgres.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.postgres.entrypoints=postgres"
      - "traefik.tcp.routers.postgres.service=postgres"
      - "traefik.tcp.services.postgres.loadbalancer.server.port=5432"
 
networks:
  traefik:
    external: true

Traefik 主配置需要增加:

command:
  - "--entrypoints.postgres.address=:5432"
ports:
  - "5432:5432"

Danger

数据库直接暴露公网风险很高。至少要限制安全组来源 IP、使用强密码、开启数据库自身 TLS,并优先考虑 VPN 或内网访问。

11.8 同一个服务同时提供 HTTP 和 TCP

有些系统同时有 Web 管理后台和 TCP 协议,例如 Gitea 的 Web + SSH、RabbitMQ 的管理台 + AMQP、MinIO 的 Console + S3 API。写法就是同一个容器上同时声明 traefik.http.*traefik.tcp.*

RabbitMQ 示例:

services:
  rabbitmq:
    image: rabbitmq:3-management
    restart: unless-stopped
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.rabbitmq.rule=Host(`rabbitmq.example.com`)"
      - "traefik.http.routers.rabbitmq.entrypoints=websecure"
      - "traefik.http.routers.rabbitmq.tls.certresolver=letsencrypt"
      - "traefik.http.services.rabbitmq.loadbalancer.server.port=15672"
      - "traefik.tcp.routers.rabbitmq-amqp.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.rabbitmq-amqp.entrypoints=amqp"
      - "traefik.tcp.routers.rabbitmq-amqp.service=rabbitmq-amqp"
      - "traefik.tcp.services.rabbitmq-amqp.loadbalancer.server.port=5672"
 
networks:
  traefik:
    external: true

Traefik 主配置需要增加:

command:
  - "--entrypoints.amqp.address=:5672"
ports:
  - "5672:5672"

12. 常用操作

查看 Traefik 日志:

docker logs -f traefik

查看当前路由:

docker exec traefik traefik version

重启 Traefik:

cd /opt/traefik
docker compose restart traefik

更新 Traefik:

cd /opt/traefik
docker compose pull
docker compose up -d

更新前建议先备份证书文件:

cp /opt/traefik/letsencrypt/acme.json /opt/traefik/letsencrypt/acme.json.bak

13. 常见问题

问题可能原因处理方式
证书一直签发失败域名没有解析到服务器、80 端口未开放、DNS 未生效检查 dig 域名 +short、安全组、ufw status
访问域名 404容器没有设置 traefik.enable=true 或 Host 规则写错检查容器 labels 和 Traefik Dashboard 的 Routers
访问域名 502Traefik 找不到业务容器端口或网络不通检查 loadbalancer.server.port 和容器是否加入 traefik 网络
Dashboard 无法访问Dashboard 域名未解析、Basic Auth 配置错误、路由 label 写错查看 docker logs -f traefik
HTTP 没有跳转 HTTPS缺少 entrypoint redirection 配置检查 entrypoints.web.http.redirections.entrypoint.*
Let’s Encrypt 频率限制反复失败后频繁重试排查配置后再启动;调试阶段可先使用 ACME staging server
Cloudflare DNS-01 失败API Token 权限不足或 Zone 范围不对确认 token 有 Zone:ReadDNS:Edit,并绑定到正确域名
Cloudflare 开橙云后访问异常SSL/TLS 模式不是 Full strict,或源站证书未签发设置 Cloudflare SSL/TLS -> Full (strict),检查 Traefik 证书日志
.env 变量没有生效文件不在 Compose 同目录,或变量名写错/opt/traefik 执行 docker compose config 检查最终配置
WebSocket 连接失败后端路径错误、服务端没有处理 Upgrade、Cloudflare 规则影响先绕过 Cloudflare 测试,再检查应用日志和路由规则
文件上传失败后端限制、Cloudflare 上传限制、Traefik buffering 限制分别检查应用配置、Cloudflare 计划限制和 maxRequestBodyBytes
SSH 连不上没有开放 TCP entrypoint,宿主机端口冲突,TCP service 端口写错检查 ports、安全组、ufwtraefik.tcp.services.*.port
多个 SSH 服务无法按域名分流SSH 没有 SNI给不同 SSH 服务配置不同 entrypoint 和外部端口

14. 目录结构参考

/opt/traefik
├── .env
├── docker-compose.yml
├── whoami.yml
└── letsencrypt
    └── acme.json

15. 最小安全清单

  • 不要开启 --api.insecure=true 给公网使用。
  • 不要开放 8080 到公网。
  • Docker socket 使用只读挂载:/var/run/docker.sock:/var/run/docker.sock:ro
  • 默认关闭自动暴露:--providers.docker.exposedbydefault=false
  • Dashboard 必须加 HTTPS 和 Basic Auth。
  • .env 权限设置为 600,不要公开 Cloudflare API Token。
  • Cloudflare API Token 只给需要的域名和最小权限。
  • 使用固定版本镜像,例如 traefik:v3.6
  • 业务容器优先只加入 Traefik 网络,不直接暴露宿主机端口。
  • SSH、数据库、MQTT 这类 TCP 服务要单独评估是否真的需要暴露公网。
  • 能用 VPN、内网、Cloudflare Tunnel 的场景,不要直接把数据库端口暴露公网。

参考