1. Зачем Traefik — в чём разница с Nginx
Представьте: у вас на VPS запущены n8n, Gitea, Vaultwarden и Grafana — каждый в своём Docker-контейнере. С Nginx вы пишете четыре отдельных конфига, четыре раза запускаете certbot, и при добавлении пятого сервиса повторяете всё заново.
С Traefik вы запускаете его один раз. Дальше просто добавляете labels к контейнеру — Traefik сам подхватывает сервис и получает SSL-сертификат от Let's Encrypt.
| Задача | Nginx | Traefik |
|---|---|---|
| Добавить новый сервис | Написать конфиг → certbot → nginx -s reload | Добавить 4 строки labels к контейнеру |
| SSL-сертификаты | Certbot вручную, cron для обновления | Автоматически от Let's Encrypt |
| Обнаружение контейнеров | Нет — всё вручную | Автоматически через Docker API |
| HTTP → HTTPS редирект | Отдельный server-блок | Одна строка в конфиге |
| Балансировка нагрузки | Настраивается вручную | Автоматически при нескольких репликах |
| Дашборд | Нет | Встроенный веб-интерфейс |
| Порог вхождения | Низкий для простых задач | Чуть выше первый раз, потом проще |
Когда Nginx лучше
Если у вас один сайт на VPS без Docker — Nginx проще. Traefik раскрывается именно в Docker-окружении с несколькими сервисами. Также Nginx лучше для кэширования статики и сложных rewrite-правил.
2. Как работает: провайдеры, роутеры, middleware
Traefik состоит из трёх понятий, которые важно понять до настройки:
Провайдер (Provider)
Источник конфигурации. В нашем случае — Docker. Traefik подключается к Docker API и читает labels всех запущенных контейнеров. Есть также file-провайдер (YAML-файл) и Kubernetes-провайдер.
Точка входа (Entrypoint)
Порт, на котором Traefik слушает трафик. Обычно два: web (порт 80, HTTP) и websecure (порт 443, HTTPS). Трафик с web автоматически редиректится на websecure.
Роутер (Router)
Правило: какой запрос куда направить. Например: Host(`app.example.com`) → контейнер "myapp". Роутер указывает через labels прямо на контейнере.
Middleware
Обработка запроса между роутером и сервисом: BasicAuth, rate-limiting, редиректы, заголовки. Тоже настраивается через labels.
Путь запроса: Интернет → Entrypoint (443) → Router (Host rule) → Middleware (опционально) → Контейнер
3. Подготовка: Docker, сеть, acme.json
Шаг 1: установить Docker
Если Docker ещё не установлен — устанавливаем одной командой:
curl -fsSL https://get.docker.com | shШаг 2: создать общую Docker-сеть
Traefik и все ваши сервисы должны находиться в одной Docker-сети, чтобы Traefik мог к ним подключаться. Создаём её один раз — она будет существовать независимо от конкретных compose-файлов:
docker network create traefik-proxyШаг 3: создать директорию и файл для сертификатов
Traefik хранит SSL-сертификаты в файле acme.json. Файл должен существовать заранее и иметь права 600 — иначе Traefik откажется запускаться:
mkdir -p /opt/traefik
cd /opt/traefik
touch acme.json
chmod 600 acme.jsonШаг 4: открыть порты в файрволле
Traefik слушает порты 80 и 443. Убедитесь что они открыты:
ufw allow 80/tcp
ufw allow 443/tcpВажно: Nginx на 80/443
Если на сервере уже запущен Nginx и слушает порты 80/443 — сначала остановите его: systemctl stop nginx && systemctl disable nginx. Traefik полностью берёт на себя входящий трафик и заменяет Nginx для Docker-сервисов.
4. Запуск Traefik
Создаём docker-compose.yml для Traefik. Замените admin@example.com на свой реальный email — Let's Encrypt отправит уведомление если сертификат не обновится.
services:
traefik:
image: traefik:v3
container_name: traefik
restart: unless-stopped
command:
# Уровень логов (DEBUG, INFO, WARN, ERROR)
- --log.level=INFO
# Подключиться к Docker — читать labels контейнеров
- --providers.docker=true
# Не публиковать контейнеры без traefik.enable=true
- --providers.docker.exposedbydefault=false
# Только контейнеры в сети traefik-proxy
- --providers.docker.network=traefik-proxy
# Точка входа HTTP (порт 80) — редирект на HTTPS
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.web.http.redirections.entryPoint.scheme=https
# Точка входа HTTPS (порт 443)
- --entrypoints.websecure.address=:443
# Let's Encrypt: TLS-ALPN challenge через порт 443
- --certificatesresolvers.le.acme.tlschallenge=true
- --certificatesresolvers.le.acme.email=admin@example.com
- --certificatesresolvers.le.acme.storage=/acme.json
ports:
- "80:80"
- "443:443"
volumes:
# Доступ к Docker API (только чтение)
- /var/run/docker.sock:/var/run/docker.sock:ro
# Файл для хранения SSL-сертификатов
- ./acme.json:/acme.json
networks:
- traefik-proxy
networks:
traefik-proxy:
external: trueЗапускаем:
cd /opt/traefik
docker compose up -dПроверяем что запустился без ошибок:
docker compose logs traefikВ логах должна быть строка Starting provider aggregator provider.ProvidersThrottleDuration — это значит Traefik успешно стартовал и слушает Docker.
Что делает каждая настройка
exposedbydefault=false— безопасность: Traefik видит только контейнеры с явным разрешениемnetwork=traefik-proxy— Traefik смотрит IP контейнеров именно в этой сетиtlschallenge=true— Let's Encrypt проверяет владение доменом через порт 443 (без HTTP)docker.sock:ro— монтируем сокет только для чтения
5. Первый сервис с HTTPS
Для теста используем traefik/whoami — лёгкий контейнер, который отвечает информацией о запросе. Убедитесь что DNS-запись вашего домена уже указывает на IP сервера — Let's Encrypt проверяет это в момент выдачи сертификата.
services:
whoami:
image: traefik/whoami
container_name: whoami
restart: unless-stopped
networks:
- traefik-proxy
labels:
# Разрешить Traefik видеть этот контейнер
- "traefik.enable=true"
# Правило роутинга: на каком домене слушаем
- "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
# Точка входа — HTTPS
- "traefik.http.routers.whoami.entrypoints=websecure"
# Использовать наш Let's Encrypt resolver
- "traefik.http.routers.whoami.tls.certresolver=le"
# Порт приложения внутри контейнера
- "traefik.http.services.whoami.loadbalancer.server.port=80"
networks:
traefik-proxy:
external: truemkdir -p /opt/whoami
cd /opt/whoami
# Создайте docker-compose.yml с конфигом выше
docker compose up -dЧерез 20–30 секунд (Traefik получает сертификат) откройте https://whoami.example.com в браузере. Вы увидите информацию о запросе — это значит всё работает.
Сертификат получен — удаляем whoami
После проверки работы можно удалить тестовый контейнер: docker compose down. Сертификат для domain.com уже сохранён в acme.json — он будет использоваться повторно.
Анатомия labels
Каждый label строится по шаблону: traefik.http.{тип}.{имя}.{параметр}
| Label | Что делает |
|---|---|
| traefik.enable=true | Разрешить Traefik видеть этот контейнер |
| traefik.http.routers.NAME.rule=Host(`dom.ru`) | По какому домену направлять запросы |
| traefik.http.routers.NAME.entrypoints=websecure | Слушать только HTTPS (порт 443) |
| traefik.http.routers.NAME.tls.certresolver=le | Получить SSL от Let's Encrypt |
| traefik.http.services.NAME.loadbalancer.server.port=3000 | Порт приложения внутри контейнера |
| traefik.http.routers.NAME.middlewares=mw-name | Применить middleware к роутеру |
NAME — произвольное имя роутера/сервиса, должно совпадать внутри одного контейнера. Обычно используют имя сервиса (например, gitea, n8n).
6. Реальный пример: несколько сервисов
Показываем как запустить n8n и Gitea под одним Traefik — каждый на своём домене, каждый с HTTPS. Это типичная ситуация: несколько приложений на одном VPS.
n8n с HTTPS
n8n — платформа автоматизации. Запускаем её на поддомене n8n.example.com:
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
environment:
- N8N_HOST=n8n.example.com
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.example.com/
volumes:
- n8n_data:/home/node/.n8n
networks:
- traefik-proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.n8n.rule=Host(`n8n.example.com`)"
- "traefik.http.routers.n8n.entrypoints=websecure"
- "traefik.http.routers.n8n.tls.certresolver=le"
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
volumes:
n8n_data:
networks:
traefik-proxy:
external: trueGitea с HTTPS
Gitea — свой Git-сервер. Добавляем на git.example.com. Обратите внимание: SSH-порт (2222) Traefik не трогает — он проходит напрямую.
services:
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: unless-stopped
environment:
- GITEA__server__DOMAIN=git.example.com
- GITEA__server__ROOT_URL=https://git.example.com/
- GITEA__server__SSH_PORT=2222
volumes:
- gitea_data:/data
ports:
# SSH для git push/pull — напрямую, без Traefik
- "2222:22"
networks:
- traefik-proxy
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=le"
# Порт веб-интерфейса Gitea внутри контейнера
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
volumes:
gitea_data:
networks:
traefik-proxy:
external: truecd /opt/n8n && docker compose up -d
cd /opt/gitea && docker compose up -dTraefik увидит новые контейнеры автоматически — через несколько секунд оба сервиса будут доступны по HTTPS с валидными сертификатами. Перезапускать Traefik не нужно.
BasicAuth для закрытых сервисов
Если хотите закрыть сервис паролём через Traefik — добавьте BasicAuth middleware. Сначала сгенерируйте хеш пароля:
# Запускаем временный контейнер для генерации хеша
docker run --rm httpd:alpine htpasswd -nbB admin "ВАШ_ПАРОЛЬ"Вы получите строку вида admin:$2y$05$abc123.... В docker-compose.yml знаки $ нужно заменить на $$:
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`app.example.com`)"
- "traefik.http.routers.myapp.entrypoints=websecure"
- "traefik.http.routers.myapp.tls.certresolver=le"
- "traefik.http.services.myapp.loadbalancer.server.port=8080"
# Применить BasicAuth к этому роутеру
- "traefik.http.routers.myapp.middlewares=myapp-auth"
# Определить middleware (все $ заменены на $$)
- "traefik.http.middlewares.myapp-auth.basicauth.users=admin:$$2y$$05$$abc123..."7. Дашборд Traefik
Traefik имеет встроенный веб-дашборд — показывает все роутеры, сервисы, middleware и их статус в реальном времени. По умолчанию он выключен.
Чтобы включить дашборд с защитой паролём, добавьте в command и labels Traefik-контейнера:
services:
traefik:
image: traefik:v3
container_name: traefik
restart: unless-stopped
command:
# ... предыдущие настройки ...
# Включить дашборд
- --api.dashboard=true
# ... ports, volumes, networks ...
labels:
- "traefik.enable=true"
# Роутер для дашборда
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=le"
# Встроенный сервис дашборда (api@internal)
- "traefik.http.routers.dashboard.service=api@internal"
# Защита паролём (обязательно!)
- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
# Хеш пароля: admin / ВАШ_ПАРОЛЬ ($ → $$)
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$2y$$05$$abc123..."cd /opt/traefik
docker compose up -d --force-recreateДашборд без пароля — опасно
Никогда не открывайте дашборд без BasicAuth или другой защиты. Дашборд показывает полную топологию вашего стека, включая имена сервисов и правила роутинга. Если не нужен в браузере — просто не включайте --api.dashboard=true.
Альтернатива: только через SSH-туннель
Если не хотите открывать дашборд публично — включите его только локально (небезопасный режим, только для локального доступа):
# В command секции Traefik:
- --api.insecure=true
# В ports (привязываем ТОЛЬКО к localhost — Docker обходит UFW!):
- "127.0.0.1:8080:8080"# Запустите на своём компьютере:
ssh -L 8080:localhost:8080 user@ВАШ_IP_СЕРВЕРА
# Затем откройте в браузере:
# http://localhost:8080/dashboard/Так порт 8080 не открыт в интернете — только доступен через зашифрованный SSH-туннель с вашей машины.