NoctisNode
← ГайдыSelf-hosting2026-10-239 мин

Caddy как reverse proxy для Docker: compose с автоматическим HTTPS

Запускаем Caddy как reverse proxy для Docker: один compose-стек, общая сеть, reverse_proxy по именам сервисов и автоматический HTTPS, плюс когда помогают метки caddy-docker-proxy.

Коротко: запустите Caddy в отдельном контейнере, который публикует порты 80 и 443, подключите его и контейнеры приложений к одной общей Docker-сети и направьте `reverse_proxy` на имя каждого сервиса и его внутренний порт (например, `reverse_proxy app:3000`). Caddy разрешает имя через встроенный DNS Docker и автоматически получает TLS-сертификат для каждого адреса сайта.

В этом руководстве сначала показан подход с обычным Caddyfile, затем вариант caddy-docker-proxy на метках, и объясняется, когда какой подходит для одного VPS. Если вы запускаете Caddy прямо на хосте, а не в Docker, см. базовое руководство по Caddy как reverse proxy.

Как всё связано

Порты наружу публикует только контейнер Caddy. Каждый бэкенд-контейнер подключается к пользовательской bridge-сети (здесь — `web`) и открывает свой порт только в этой сети. Встроенный DNS-сервер Docker, доступный в пользовательских сетях, позволяет Caddy обращаться к `app:3000` по имени сервиса или контейнера, так что вам не нужно прописывать IP контейнеров, которые меняются при каждом пересоздании.

Не публиковать бэкенды важнее, чем кажется: Docker пишет собственные правила iptables, поэтому порт, опубликованный через `ports:`, может быть доступен из интернета, даже если ufw показывает, что он закрыт. Для всего, что должно стоять за прокси, используйте `expose:` или вообще не пробрасывайте порты.

КомпонентРольОпубликован на хосте?
контейнер caddyТерминация TLS, маршрутизация по имени хоста, редирект с HTTP на HTTPSДа: 80/tcp, 443/tcp, 443/udp
сеть webОбщая bridge-сеть со встроенным DNS для имён сервисовНет
контейнеры приложенийВаши сервисы (веб-приложения, API, дашборды)Нет, доступны только через Caddy
том caddy_dataСертификаты, приватные ключи, ACME-аккаунтНет, но должен сохраняться

Пошагово: обычный Caddyfile с docker compose

  1. 01.До запуска Caddy направьте A-запись (и AAAA-запись, если используете IPv6) каждого имени хоста на VPS, иначе выпуск сертификата не удастся.
  2. 02.Один раз создайте общую сеть: `docker network create web`.
  3. 03.Создайте `/opt/proxy/compose.yaml` и `/opt/proxy/Caddyfile`, как показано ниже, затем выполните `docker compose up -d` в `/opt/proxy`.
  4. 04.Подключите каждый стек приложения к внешней сети `web` и запустите его через `docker compose up -d`.
  5. 05.Следите за выпуском первого сертификата командой `docker compose logs -f caddy` в `/opt/proxy`.
  6. 06.После правки Caddyfile перезагрузите конфиг без простоя: `docker compose exec -w /etc/caddy caddy caddy reload`.
# /opt/proxy/compose.yaml
services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"   # HTTP/3
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data      # certificates and ACME account keys
      - caddy_config:/config
    networks:
      - web

volumes:
  caddy_data:
  caddy_config:

networks:
  web:
    external: true

Подключите приложение и напишите Caddyfile

Каждый стек приложения объявляет ту же внешнюю сеть. Имя сервиса становится именем хоста, к которому подключается Caddy. Том `caddy_data` терять нельзя ни в коем случае: его удаление заставит Caddy заново запросить все сертификаты, что может упереться в лимиты ACME.

# /opt/app/compose.yaml
services:
  app:
    image: ghcr.io/example/app:latest
    restart: unless-stopped
    expose:
      - "3000"        # reachable on the network, NOT published on the host
    networks:
      - web

networks:
  web:
    external: true

# /opt/proxy/Caddyfile
{
	email [email protected]
}

app.example.com {
	reverse_proxy app:3000
}

grafana.example.com {
	reverse_proxy grafana:3000
}

api.example.com {
	reverse_proxy api-1:8080 api-2:8080 {
		lb_policy round_robin
		health_uri /healthz
		health_interval 10s
	}
}

Бэкенды Caddy: апстримы, балансировка и health checks

В терминах Caddy бэкенд — это апстрим `reverse_proxy`. В одной директиве можно перечислить несколько апстримов, выбрать `lb_policy`, например `round_robin`, `least_conn` или `ip_hash`, и добавить активные health checks через `health_uri`, чтобы упавшие контейнеры выпадали из ротации. Пассивные проверки (`fail_duration`, `max_fails`) тоже полезны, если у приложения нет health-эндпоинта.

По умолчанию Caddy выставляет на проксируемых запросах `X-Forwarded-For`, `X-Forwarded-Proto` и `X-Forwarded-Host`. Настройте приложение доверять диапазону сети прокси, чтобы оно видело реальный IP клиента и строило ссылки `https://`. WebSocket и gRPC работают через `reverse_proxy` без дополнительных настроек; для gRPC без шифрования используйте `reverse_proxy h2c://service:50051`.

Метки вместо Caddyfile: caddy-docker-proxy

Те, кто ищет «Caddy proxy manager», обычно хотят того же, что даёт Nginx Proxy Manager: добавил контейнер — получил имя хоста и сертификат, не редактируя центральный файл. Официального GUI у Caddy нет, но проект сообщества caddy-docker-proxy (github.com/lucaslorentz/caddy-docker-proxy) генерирует Caddyfile из меток контейнеров и перезагружает Caddy при запуске и остановке контейнеров.

Ему нужен доступ на чтение к Docker-сокету, а это фактически root на хосте. Монтируйте сокет только для чтения, обновляйте образ прокси и предпочитайте обычный Caddyfile, если у вас всего несколько сервисов.

services:
  caddy:
    image: lucaslorentz/caddy-docker-proxy:ci-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    environment:
      - CADDY_INGRESS_NETWORKS=web
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - caddy_data:/data
    networks:
      - web

  whoami:
    image: traefik/whoami
    networks:
      - web
    labels:
      caddy: whoami.example.com
      caddy.reverse_proxy: "{{upstreams 80}}"

volumes:
  caddy_data:

networks:
  web:
    external: true

Обычный Caddyfile против caddy-docker-proxy

Обычный CaddyfileМетки caddy-docker-proxy
Где живёт конфигурацияОдин файл, который вы версионируете в gitМетки, разбросанные по compose-файлам
Доступ к Docker-сокетуНе нуженОбязателен (чувствительно для безопасности)
Новый сервисПравка Caddyfile, `caddy reload`Добавить метки, запустить контейнер
ОбразОфициальный `caddy:2`Образ сообщества на базе Caddy
ОтладкаЧитаете файл напрямуюСмотрите сгенерированный конфиг в логах
Лучше всего дляНебольших стабильных VPSМножества короткоживущих или часто меняющихся сервисов

Запуск на VPS

На KVM VPS NoctisNode у вас полный root и выделенный IPv4, поэтому опубликовать 80 и 443 из одного контейнера Caddy и спрятать всё остальное несложно. Wisp (1 vCPU, 1 GB RAM) хватит для Caddy и пары лёгких контейнеров; более тяжёлые стеки поместятся на Pulse или Umbra. Детали выпуска сертификатов и типичные сбои описаны в статье как работает автоматический HTTPS в Caddy, а пересылка сырого TCP и SSH — в проксировании TCP и SSH в Caddy. Закройте SSH хоста по руководству по защите SSH.

FAQ

QКак настроить Caddy как reverse proxy для Docker-контейнера?

AПоместите Caddy и контейнер в одну пользовательскую Docker-сеть и используйте в Caddyfile `reverse_proxy service-name:port`. Не используйте `localhost`: внутри контейнера Caddy он указывает на сам Caddy.

QПочему Caddy отдаёт 502 Bad Gateway для моего контейнера?

AОбычно контейнеры не в общей сети, порт указан неверно (нужен внутренний порт контейнера, а не опубликованный порт хоста) или приложение внутри контейнера слушает только 127.0.0.1. Пусть приложение слушает 0.0.0.0, и проверьте `docker network inspect web`.

QБезопасно ли использовать caddy-docker-proxy?

AОн работает хорошо, но требует Docker-сокет, а это контроль над хостом на уровне root. Монтируйте сокет только для чтения, обновляйте образ и не открывайте наружу admin-эндпоинт Caddy.

QГде Caddy хранит сертификаты в Docker?

AВ `/data` внутри официального образа, поэтому смонтируйте туда именованный том. Потеря тома означает перевыпуск всех сертификатов и возможное упирание в лимиты ACME.

QЕсть ли Caddy proxy manager с веб-интерфейсом?

AУ самого Caddy нет официального веб-интерфейса; он настраивается через Caddyfile, JSON или admin API. Инструменты на метках вроде caddy-docker-proxy дают похожий сценарий «добавил контейнер — и готово».

Caddy как reverse proxy для Docker: compose с автоматическим HTTPS · NoctisNode