# Traefik: reverse proxy с автоматическим TLS для Docker

> Traefik сам находит контейнеры Docker и выпускает TLS-сертификаты через Let's Encrypt без ручных конфигов. Установка, labels и типичные ошибки.

Traefik — reverse proxy, который сам находит контейнеры Docker и сам выпускает TLS-сертификаты, без ручной правки конфигов при каждом новом сайте. Вместо статичного файла с блоками server, как у nginx, Traefik читает labels прямо на контейнерах и перестраивает маршруты на лету. Разберём установку в Docker Compose, автоматический TLS через Let's Encrypt и типичные ошибки.

## Что такое Traefik и чем он отличается от nginx

nginx требует отдельного конфиг-файла на каждый сайт и перезагрузки процесса при изменениях. Traefik подключается к Docker через сокет, видит все запущенные контейнеры и строит маршруты из их labels автоматически — без reload и без отдельных файлов конфигурации на каждый домен. Новый сайт добавляется одной строкой labels в `docker-compose.yml`, а не отдельным конфигом reverse proxy.

Обратная сторона: Traefik добавляет ещё один слой абстракции поверх привычного HTTP-сервера, и отладку маршрутов приходится вести через его собственный дашборд, а не через привычные логи nginx.

## Установка Traefik в Docker Compose

Базовый сервис Traefik в `docker-compose.yml` с включённым Docker-провайдером:

```
services:
  traefik:
    image: traefik:v3.1
    command:
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    restart: unless-stopped
```

Флаг `exposedbydefault=false` обязателен: без него Traefik открывает наружу вообще все контейнеры на сервере, а не только те, что явно помечены labels.

## Автоматический TLS через Let's Encrypt

Traefik сам запрашивает и продлевает сертификаты через протокол ACME, без cron-задачи и отдельного certbot. Для этого в команду сервиса добавляют resolver:

```
command:
  - --certificatesresolvers.le.acme.email=admin@example.com
  - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
  - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
```

Файл `acme.json` обязан иметь права 600 — Let's Encrypt хранит в нём приватные ключи сертификатов, и при более открытых правах Traefik откажется его использовать.

## Как Traefik находит контейнеры: labels на сервисах

Каждый контейнер, который должен получить маршрут, помечается labels прямо в описании сервиса:

```
services:
  app:
    image: myapp:latest
    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=le"
      - "traefik.http.services.app.loadbalancer.server.port=8080"
```

Traefik сам подхватывает контейнер после `docker compose up -d`: не нужно отдельно редактировать конфиг proxy и перезапускать его.

## Несколько сайтов на одном сервере

Маршрутизация по доменам держится на правиле `Host()` в labels: разные контейнеры отвечают на разные домены через один и тот же порт 443 сервиса Traefik. Для каждого домена задаётся свой `certresolver`, и Traefik выпускает отдельный сертификат на каждый Host автоматически при первом запросе.

| Критерий | Traefik | Caddy |
| --- | --- | --- |
| Обнаружение сервисов | Автоматически через Docker labels | Через статичный Caddyfile |
| Автоматический TLS | Да, через ACME resolver | Да, из коробки для любого домена |
| Перезагрузка при новом сайте | Не требуется | Требуется reload конфига |

## Дашборд и наблюдение за маршрутами

Встроенный дашборд на порту 8080 показывает список роутеров, сервисов и middleware в реальном времени — удобно проверить, что labels распознаны правильно. В продакшене дашборд закрывают тем же механизмом labels с basic-auth middleware, а не оставляют открытым наружу.

- Включить дашборд флагом `--api.dashboard=true` только на этапе отладки
- Закрыть путь `/dashboard/` отдельным роутером с middleware авторизации
- Смотреть логи access через `--accesslog=true` при разборе проблем с маршрутизацией

## Частые ошибки конфигурации

Первая ошибка — забытый `exposedbydefault=false`, из-за чего наружу открываются служебные контейнеры вроде баз данных. Вторая — неправильные права на `acme.json`, из-за которых Traefik тихо не может сохранить сертификат и перевыпускает его при каждом рестарте, упираясь в лимиты Let's Encrypt. Третья — отсутствие `traefik.enable=true` на нужном контейнере, из-за чего сервис остаётся недоступным, хотя все остальные labels выставлены верно.

Если сайтов на сервере много и нужен кластер из нескольких VDS, тот же Traefik работает и поверх [Docker Swarm](https://www.zevshost.net/ru/kb/docker-swarm-nastroika/), читая labels уже из режима stack. Для сравнения с более простым по конфигурации reverse proxy смотрите статью про [Caddy Server](https://www.zevshost.net/ru/kb/caddy-server-nastroika/). Если инфраструктура уже перешла на полноценный Kubernetes, аналогичную роль там выполняет [ingress-nginx](https://www.zevshost.net/ru/kb/kubernetes-ingress-nginx/).

## Итог: чек-лист запуска Traefik

Traefik избавляет от ручного редактирования конфигов reverse proxy при каждом новом контейнере, если labels настроены аккуратно с самого начала.

- Флаг `exposedbydefault=false` в конфигурации Docker-провайдера
- Права 600 на файл `acme.json` с сертификатами
- `traefik.enable=true` явно на каждом контейнере, который должен быть виден снаружи
- Дашборд закрыт middleware авторизации, а не открыт на весь интернет

## Частые вопросы

### Можно ли через Traefik проксировать трафик не на Docker-контейнер, а на внешний сервер или IP-адрес?

Механизм labels, описанный в статье, рассчитан именно на контейнеры Docker, которые Traefik видит через сокет сам. Для маршрутизации на внешний сервер или адрес вне Docker нужен отдельный провайдер статической конфигурации, а не labels на контейнере — настройка в этом случае получается более ручной и не такой автоматической, как для локальных сервисов.

### Контейнер запущен с верными labels, но снаружи всё равно недоступен — в чём может быть причина?

Чаще всего не хватает одной строки в labels, которая явно разрешает Traefik видеть именно этот контейнер — без неё сервис остаётся невидимым, даже если остальные правила маршрутизации описаны верно. Стоит также проверить, что флаг, ограничивающий автоматический показ контейнеров наружу, не блокирует конкретно этот сервис по умолчанию.

### Нужно ли вручную продлевать сертификаты Let's Encrypt, выпущенные через Traefik?

Нет, именно в этом и состоит удобство встроенного ACME resolver'а: Traefik сам следит за сроком действия сертификата и запрашивает новый заблаговременно, без отдельного cron-задания и без участия человека. Единственное условие — файл, где хранятся сертификаты, должен сохранять верные права доступа, иначе Traefik не сможет записать туда обновлённые данные при продлении.

### Стоит ли переходить на Traefik, если на сервере уже работает nginx и сайтов немного?

Если сайтов мало и конфиги почти не меняются, выгода от автоматического обнаружения контейнеров невелика, а разбираться с ещё одним слоем абстракции и отдельным дашбордом вместо привычных логов nginx — лишние усилия. Переход оправдан, когда новые сайты и контейнеры появляются часто и ручная правка конфига reverse proxy каждый раз становится заметной рутиной.

---

Источник: https://www.zevshost.net/ru/kb/traefik-reverse-proxy/
