Bối cảnh & Tại sao cần dùng Traefik với DNS Challenge?
Chạy vài chục container trên một VPS mà quản lý SSL kiểu truyền thống? Đó là cơn ác mộng thực sự. Mỗi lần thêm service mới là một lần loay hoay: cấu hình server block trong Nginx, mở port 80, trỏ bản ghi A, rồi ngồi chờ Let’s Encrypt xác thực qua HTTP-01 challenge. Một subdomain mới mất cỡ 10–15 phút. Mười cái là mất cả buổi chiều.
Traefik v3 giải quyết vấn đề này khác hẳn. Nó lắng nghe Docker Socket liên tục — khi bạn start một container mới có gắn label đúng, Traefik tự phát hiện và cấp SSL mà không cần bạn làm gì thêm. Đặc biệt, khi kết hợp với Cloudflare DNS Challenge, chúng ta có thể:
- Cấp chứng chỉ Wildcard SSL (*.yourdomain.com): Một chứng chỉ dùng chung cho mọi subdomain.
- Không cần mở port 80 công khai — lý tưởng cho các service nội bộ không được expose ra internet.
- Tự động gia hạn trước khi hết hạn, không cần can thiệp thủ công.
Mình từng dùng qua Nginx Proxy Manager, nhưng khi chuyển sang Traefik v3, cảm giác như từ xe số lên xe tay ga vậy – mọi thứ tự động hóa hoàn toàn nhờ vào hệ thống Labels của Docker.
Cài đặt & Chuẩn bị tài nguyên
Thiếu 1 trong 3 thứ dưới đây là sẽ bị stuck giữa chừng, nên chuẩn bị kỹ trước khi bắt đầu:
1. Cloudflare API Token
Token này cho phép Traefik tạo TXT record tạm thời trên DNS để xác thực quyền sở hữu domain — Let’s Encrypt kiểm tra record đó rồi mới cấp cert. Đừng dùng Global API Key: nếu bị lộ, toàn bộ account Cloudflare của bạn có thể bị compromise.
Vào My Profile > API Tokens > Create Token. Chọn template “Edit zone DNS”, gán quyền chỉ cho zone (domain) cụ thể bạn muốn dùng — không nên cấp quyền cho toàn bộ account.
2. Cấu trúc thư mục
Mình thường tổ chức thư mục như sau để dễ quản lý và backup:
mkdir -p ~/traefik/data
touch ~/traefik/data/acme.json
chmod 600 ~/traefik/data/acme.json
touch ~/traefik/docker-compose.yml
touch ~/traefik/data/traefik.yml
Lưu ý: File acme.json là nơi lưu chứng chỉ SSL. Bắt buộc phải set quyền 600 — nếu không, Traefik sẽ từ chối khởi động với lỗi “acme.json” should have permissions 600.
3. Trỏ Domain về VPS
Thêm bản ghi A trỏ *.yourdomain.com về IP của VPS trên dashboard Cloudflare. Quan trọng: tắt Proxy (đám mây màu vàng) ở bước này. Nếu giữ Proxy bật, Traefik sẽ nhận IP của Cloudflare thay vì IP VPS — cert request sẽ fail ngay.
Cấu hình chi tiết Traefik v3
Phần này mình giải thích từng file một. Traefik v3 thay đổi một số cú pháp so với v2 — đặc biệt ở phần entryPoints và certResolver — nên đừng copy nguyên config cũ sang mà không kiểm tra lại.
Bước 1: File cấu hình tĩnh (traefik.yml)
File này chạy một lần khi Traefik khởi động, định nghĩa entrypoints, Docker provider, và cấu hình ACME cho việc cấp cert tự động.
api:
dashboard: true
debug: false
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
certificatesResolvers:
cloudflare:
acme:
email: [email protected]
storage: /acme.json
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"
Bước 2: File Docker Compose (docker-compose.yml)
File này khởi động Traefik và mount đúng 3 thứ cần thiết: config tĩnh, Docker socket (để đọc container labels), và acme.json (để lưu cert sau khi được cấp).
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: always
security_opt:
- no-new-privileges:true
networks:
- proxy
ports:
- 80:80
- 443:443
environment:
- CF_DNS_API_TOKEN=YOUR_CLOUDFLARE_API_TOKEN
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/traefik.yml:/traefik.yml:ro
- ./data/acme.json:/acme.json
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.rule=Host(`traefik.yourdomain.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.tls.certresolver=cloudflare"
- "traefik.http.routers.dashboard.tls.domains[0].main=yourdomain.com"
- "traefik.http.routers.dashboard.tls.domains[0].sans=*.yourdomain.com"
networks:
proxy:
external: true
Tạo network trước khi chạy: docker network create proxy. Network này dùng chung cho mọi container muốn expose qua Traefik — tạo một lần, dùng mãi mãi.
Khi debug Docker API response hoặc xem cấu hình dynamic của Traefik dưới dạng JSON, mình hay paste vào formatter tại toolcraft.app/vi/tools/developer/json-formatter để đọc dễ hơn — nhanh hơn cài extension trình duyệt nhiều.
Bước 3: Deploy ứng dụng mẫu để kiểm tra
Để biết hệ thống hoạt động chưa, thử deploy một container whoami nhỏ:
services:
whoami:
image: traefik/whoami
container_name: test-app
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.rule=Host(`whoami.yourdomain.com`)"
- "traefik.http.routers.whoami.tls.certresolver=cloudflare"
Kiểm tra & Monitoring
Chạy docker compose up -d xong, check log ngay:
docker logs -f traefik
Thấy dòng "Legitimate certificate retrieved for domain yourdomain.com" là cert đã về. Mở https://whoami.yourdomain.com — HTTPS xanh lá, không có cảnh báo gì là thành công.
Một vài điểm cần lưu ý khi monitoring:
- Dashboard: Vào
https://traefik.yourdomain.comđể xem trực quan các router và service đang chạy — rất hữu ích khi cần debug. - Rate Limit: Let’s Encrypt giới hạn 50 cert/domain/tuần. Cấu hình sai rồi restart liên tục là hết quota trong vài tiếng. Khi test, dùng môi trường
stagingbằng cách thêmcaServer: "https://acme-staging-v02.api.letsencrypt.org/directory"vào phầnacmetrongtraefik.yml. - Propagation: TXT record của Cloudflare cần 30 giây đến vài phút để propagate. Đừng restart container ngay nếu 1–2 phút đầu chưa thấy cert — cứ để Traefik tự xử lý.
Sau vài tuần chạy setup này, mình không còn phải đụng tay vào SSL nữa. Thêm service mới chỉ cần gắn đúng labels, còn lại Traefik lo hết. Nếu bạn gặp vướng mắc ở bước nào — đặc biệt là phần API Token permissions hoặc DNS propagation — cứ để lại comment bên dưới nhé.

