VPSРейтинг
Docker12 февраля 2026 · 14 мин чтения

Traefik на VPS: reverse proxy для Docker-стека с автоматическим SSL

Когда на VPS несколько Docker-сервисов, Nginx превращается в рутину: для каждого нового приложения — отдельный конфиг, certbot, reload. Traefik делает это автоматически: увидел контейнер, прочитал labels, настроил роутинг и выдал SSL-сертификат. Всё сам.

1. Зачем Traefik — в чём разница с Nginx

Представьте: у вас на VPS запущены n8n, Gitea, Vaultwarden и Grafana — каждый в своём Docker-контейнере. С Nginx вы пишете четыре отдельных конфига, четыре раза запускаете certbot, и при добавлении пятого сервиса повторяете всё заново.

С Traefik вы запускаете его один раз. Дальше просто добавляете labels к контейнеру — Traefik сам подхватывает сервис и получает SSL-сертификат от Let's Encrypt.

ЗадачаNginxTraefik
Добавить новый сервисНаписать конфиг → 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 ещё не установлен — устанавливаем одной командой:

Установка Docker на Ubuntu
curl -fsSL https://get.docker.com | sh

Шаг 2: создать общую Docker-сеть

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

Создать внешнюю сеть для Traefik
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. Убедитесь что они открыты:

Открыть порты для Traefik
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 отправит уведомление если сертификат не обновится.

/opt/traefik/docker-compose.yml
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

Запускаем:

Запустить Traefik
cd /opt/traefik
docker compose up -d

Проверяем что запустился без ошибок:

Проверить логи Traefik
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 проверяет это в момент выдачи сертификата.

/opt/whoami/docker-compose.yml
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: true
Запустить тестовый сервис
mkdir -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:

/opt/n8n/docker-compose.yml
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: true

Gitea с HTTPS

Gitea — свой Git-сервер. Добавляем на git.example.com. Обратите внимание: SSH-порт (2222) Traefik не трогает — он проходит напрямую.

/opt/gitea/docker-compose.yml
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: true
Запустить оба сервиса
cd /opt/n8n && docker compose up -d
cd /opt/gitea && docker compose up -d

Traefik увидит новые контейнеры автоматически — через несколько секунд оба сервиса будут доступны по HTTPS с валидными сертификатами. Перезапускать Traefik не нужно.

BasicAuth для закрытых сервисов

Если хотите закрыть сервис паролём через Traefik — добавьте BasicAuth middleware. Сначала сгенерируйте хеш пароля:

Генерация хеша пароля (без установки htpasswd)
# Запускаем временный контейнер для генерации хеша
docker run --rm httpd:alpine htpasswd -nbB admin "ВАШ_ПАРОЛЬ"

Вы получите строку вида admin:$2y$05$abc123.... В docker-compose.yml знаки $ нужно заменить на $$:

Labels для BasicAuth middleware
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-контейнера:

Дополнения к /opt/traefik/docker-compose.yml
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..."
Перезапустить Traefik с новой конфигурацией
cd /opt/traefik
docker compose up -d --force-recreate

Дашборд без пароля — опасно

Никогда не открывайте дашборд без BasicAuth или другой защиты. Дашборд показывает полную топологию вашего стека, включая имена сервисов и правила роутинга. Если не нужен в браузере — просто не включайте --api.dashboard=true.

Альтернатива: только через SSH-туннель

Если не хотите открывать дашборд публично — включите его только локально (небезопасный режим, только для локального доступа):

Локальный дашборд без SSL — только через SSH-туннель
# В command секции Traefik:
- --api.insecure=true

# В ports (привязываем ТОЛЬКО к localhost — Docker обходит UFW!):
- "127.0.0.1:8080:8080"
Открыть дашборд через SSH-туннель с вашего компьютера
# Запустите на своём компьютере:
ssh -L 8080:localhost:8080 user@ВАШ_IP_СЕРВЕРА

# Затем откройте в браузере:
# http://localhost:8080/dashboard/

Так порт 8080 не открыт в интернете — только доступен через зашифрованный SSH-туннель с вашей машины.

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

Нужен VPS под Docker-стек? Смотрите лучшие варианты

VPS для Docker →

Смотрите также