Caddy Docker: автоматический reverse proxy для домашнего сервера
Больше года использую Synology NAS DSM720+ в качестве домашнего сервера. Добавил памяти до 10GB, установил SSD кеш для ускорения работы. Большим преимуществом данной версии NAS является возможность запускать Docker контейнеры, что открывает безграничные возможности для самохостинга.
Проблема: эволюция доступа к сервисам
Cloudflare Tunnel: первая попытка
Изначально для доступа к сервисам извне использовал Cloudflare Tunnel. Решение работало отлично, пока его не заблокировали РКН. Пришлось искать альтернативу.
Tailscale Funnel: неудобная реальность
Следующим шагом стал переход на Tailscale Funnel. Технически это работало, но архитектура оказалась неудобной: контейнер с Tailscale в режиме sidecar приходилось запускать рядом с каждым сервисом.
Проблемы накапливались:
- Много контейнеров: для каждого сервиса нужен отдельный Tailscale sidecar
- Сложные домены: домены от Tailscale вроде
my-server-12345.ts.netзапомнить невозможно - Нет кастомизации: использовать свои домены с Tailscale Funnel было невозможно
- Сложность управления: при добавлении нового сервиса нужно настраивать новый sidecar
Нужно было что-то более элегантное.
Решение: caddy-docker-proxy
Недавно узнал про caddy-docker-proxy — плагин для Caddy, который автоматически обнаруживает Docker контейнеры и настраивает reverse proxy на основе labels. Это именно то, чего не хватало!
Что это дает
- Автоматическое обнаружение: Caddy сам находит контейнеры в Docker сети
- Без перезапусков: добавление нового сервиса не требует перезапуска Caddy
- Свои домены: полный контроль над доменными именами
- SSL из коробки: автоматическое получение сертификатов через Let’s Encrypt
- Один контейнер: вместо множества sidecar’ов — один Caddy для всех сервисов
Настройка: пошаговая инструкция
Шаг 1: Подготовка Docker образа Caddy
Так как на Synology NAS не очень быстрый CPU, решил собирать Caddy с нужными плагинами через GitHub Actions. Использовал ранее подготовленный репозиторий juev/caddy , изменив только список плагинов.
Используемые плагины:
- caddy-dns/cloudflare — для автоматического получения SSL сертификатов через Cloudflare DNS challenge
- mholt/caddy-l4 — для L4 проксирования TCP-соединений (SSH, базы данных и т.д.)
- lucaslorentz/caddy-docker-proxy — для автоматического обнаружения и проксирования Docker сервисов
Шаг 2: Создание Docker сети
Создаем отдельную сеть для Caddy и всех сервисов:
docker network create caddy
Эта сеть будет использоваться всеми контейнерами, которые должны быть доступны через Caddy.
Шаг 3: Настройка docker-compose для Caddy
Создаем docker-compose.yml для Caddy:
services:
# Tailscale контейнер для VPN доступа
ts-caddy:
image: tailscale/tailscale:latest
hostname: caddy
container_name: caddy-tailscale
networks:
- caddy
environment:
# Ключ авторизации из Tailscale Admin Console
- TS_AUTHKEY=${TS_AUTHKEY}
# Теги для управления доступом
- TS_EXTRA_ARGS=--advertise-tags=tag:container
# Директория для хранения состояния
- TS_STATE_DIR=/var/lib/tailscale
volumes:
- tailscale-data:/var/lib/tailscale
devices:
- /dev/net/tun:/dev/net/tun
cap_add:
- net_admin # Для работы с сетью
- sys_module # Для загрузки модулей ядра
restart: unless-stopped
# Caddy reverse proxy
caddy:
image: ghcr.io/juev/caddy:latest
container_name: caddy
# Используем сетевой стек Tailscale контейнера
# Это позволяет Caddy использовать Tailscale IP напрямую
network_mode: "service:ts-caddy"
volumes:
# Основной конфигурационный файл
- ./Caddyfile:/etc/caddy/Caddyfile:ro
# Данные для хранения сертификатов
- caddy_data:/data
# Конфигурация Caddy
- caddy_config:/config
# Docker socket для автоматического обнаружения контейнеров
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
# Путь к Caddyfile (используется caddy-docker-proxy)
- CADDY_DOCKER_CADDYFILE_PATH=/etc/caddy/Caddyfile
# API токен Cloudflare для DNS challenge
- CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
# Не проксировать контейнеры без labels по умолчанию
- CADDY_DOCKER_EXPOSEDBYDEFAULT=false
# Сеть, в которой искать контейнеры
- CADDY_INGRESS_NETWORKS=caddy
restart: unless-stopped
networks:
caddy:
external: true
volumes:
caddy_data:
caddy_config:
tailscale-data:
driver: local
Шаг 4: Настройка переменных окружения
Создаем файл .env в той же директории:
# Cloudflare API Token с правами на редактирование DNS
# Создается в Cloudflare Dashboard → My Profile → API Tokens
CLOUDFLARE_API_TOKEN=your_cloudflare_api_token_here
# Tailscale Auth Key (одноразовый ключ)
# Создается в Tailscale Admin Console → Settings → Keys
TS_AUTHKEY=tskey-auth-xxxxxxxxx-xxxxxxxxx
Важно:
CLOUDFLARE_API_TOKENдолжен иметь права на редактирование DNS записей
Шаг 5: Настройка Caddyfile
Создаем Caddyfile с базовой конфигурацией:
{
# Email для уведомлений Let's Encrypt
email name@example.com
# Глобальная настройка DNS challenge через Cloudflare
# Работает для всех доменов автоматически
acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
# Отключаем HTTP/3 (QUIC) для совместимости с Safari iOS через Tailscale
# Safari iOS использует HTTP/3 по умолчанию, но QUIC может иметь проблемы
# через Tailscale VPN
servers {
protocols h1 h2
}
# L4 проксирование для TCP-соединений
# В данном случае проксируем SSH (порт 22) к Forgejo
layer4 {
:22 {
route {
proxy forgejo:22
}
}
}
}
# Статический домен для админки Synology NAS
# Используем внутренний IP Docker сети для доступа к хосту
nas.example.com {
reverse_proxy 172.27.0.1:5000
}
Объяснение настроек:
acme_dns cloudflare— автоматическое получение SSL сертификатов через Cloudflare DNS challenge (работает даже без открытых портов)protocols h1 h2— используем только HTTP/1.1 и HTTP/2, отключаем HTTP/3 для совместимостиlayer4— проксирование на уровне TCP (для SSH, баз данных и т.д.)172.27.0.1— это IP адрес хоста внутри Docker сети (стандартный gateway)
Шаг 6: Настройка DNS в Cloudflare
В Cloudflare DNS создаем A-запись:
- Имя:
nas(или любое другое) - Тип: A
- IPv4 адрес: Tailscale IP адрес контейнера
caddy-tailscale - Proxy: выключен (серый облачко)
После запуска контейнеров можно узнать Tailscale IP командой:
docker exec caddy-tailscale tailscale ip
Шаг 7: Запуск
Запускаем Caddy:
docker-compose up -d
Проверяем логи:
docker-compose logs -f caddy
Если все настроено правильно, через несколько секунд вы получите доступ к админке NAS по адресу https://nas.example.com с автоматически полученным SSL сертификатом!
Автоматическое обнаружение сервисов
Самое интересное начинается сейчас. Для контейнеров, которые хотим видеть в вебе, достаточно добавить labels в docker-compose.yml. Caddy автоматически обнаружит их и настроит reverse proxy.
Как это работает
- Caddy через Docker socket читает информацию о всех контейнерах в сети
caddy - Ищет контейнеры с label
caddy - Автоматически создает конфигурацию reverse proxy
- Получает SSL сертификат через Cloudflare DNS challenge
- Настраивает маршрутизацию без перезапуска
Пример: WebDAV сервис
Вот пример настройки WebDAV сервиса:
services:
webdav:
image: bytemark/webdav
container_name: webdav
restart: always
networks:
- caddy # Подключаем к сети Caddy
environment:
- TZ=Europe/Moscow
volumes:
- ./dav:/var/lib/dav
- /etc/localtime:/etc/localtime:ro
labels:
# Домен для доступа к сервису
caddy: "dav.example.com"
# Директива reverse_proxy с автоматическим определением upstream
# {{upstreams 80}} автоматически найдет IP:порт контейнера на порту 80
caddy.reverse_proxy: "{{upstreams 80}}"
networks:
caddy:
external: true
После запуска контейнера (docker-compose up -d) Caddy автоматически:
- Обнаружит новый контейнер
- Настроит reverse proxy на
dav.example.com - Получит SSL сертификат
- Настроит маршрутизацию
Никаких перезапусков Caddy не требуется!
Пример: веб-приложение на другом порту
Если ваш сервис работает на порту 8080:
services:
myapp:
image: myapp:latest
container_name: myapp
networks:
- caddy
labels:
caddy: "app.example.com"
# Указываем конкретный порт
caddy.reverse_proxy: "{{upstreams 8080}}"
Пример: несколько доменов для одного сервиса
services:
myapp:
image: myapp:latest
container_name: myapp
networks:
- caddy
labels:
caddy: "app.example.com, www.app.example.com"
caddy.reverse_proxy: "{{upstreams 80}}"
Пример: дополнительные директивы Caddy
Можно использовать любые директивы Caddy через labels:
services:
myapp:
image: myapp:latest
container_name: myapp
networks:
- caddy
labels:
caddy: "app.example.com"
caddy.reverse_proxy: "{{upstreams 80}}"
# Добавляем базовую аутентификацию
caddy.basicauth: "/admin admin:$2a$14$encrypted_password"
# Включаем сжатие
caddy.encode: "gzip zstd"
# Кастомные заголовки
caddy.header: "X-Custom-Header \"My Value\""
L4 проксирование для TCP
Плагин caddy-l4 позволяет проксировать TCP-соединения. Это полезно для:
- SSH доступа к контейнерам
- Проксирования баз данных
- Любых других TCP-протоколов
В примере выше мы настроили проксирование SSH к Forgejo:
layer4 {
:22 {
route {
proxy forgejo:22
}
}
}
Теперь можно подключаться по SSH через Tailscale IP на порт 22, и соединение будет автоматически проксироваться к Forgejo контейнеру.
Преимущества решения
После перехода на caddy-docker-proxy получил:
✅ Один контейнер вместо множества — больше не нужно запускать Tailscale sidecar для каждого сервиса
✅ Свои домены — полный контроль над доменными именами через Cloudflare
✅ Автоматические SSL сертификаты — Let’s Encrypt сертификаты получаются автоматически через Cloudflare DNS challenge
✅ Нет перезапусков — добавление нового сервиса не требует перезапуска Caddy
✅ Простота управления — все настройки через Docker labels
✅ L4 проксирование — поддержка TCP-протоколов (SSH, базы данных)
✅ Производительность — один reverse proxy для всех сервисов, меньше overhead
Troubleshooting
Проблема: Caddy не видит контейнеры
Решение:
- Убедитесь, что контейнер подключен к сети
caddy - Проверьте, что
CADDY_INGRESS_NETWORKS=caddyустановлен в environment - Убедитесь, что Docker socket монтирован:
/var/run/docker.sock:/var/run/docker.sock:ro
Проблема: SSL сертификат не получается
Решение:
- Проверьте правильность
CLOUDFLARE_API_TOKEN - Убедитесь, что токен имеет права на редактирование DNS
- Проверьте логи Caddy:
docker logs caddy
Проблема: домен не резолвится
Решение:
- Проверьте A-запись в Cloudflare DNS
- Убедитесь, что Tailscale IP правильный:
docker exec caddy-tailscale tailscale ip - Проверьте, что Proxy в Cloudflare выключен (серое облачко)
Проблема: Safari iOS не работает
Решение:
- Убедитесь, что HTTP/3 отключен в Caddyfile (
protocols h1 h2) - Это известная проблема с QUIC через Tailscale VPN
Безопасность
Рекомендации
- Docker socket: монтируйте только в режиме read-only (
:ro) - API токены: храните в
.envфайле, не коммитьте в Git - Сеть: используйте отдельную Docker сеть для изоляции
- Firewall: настройте firewall на Synology для дополнительной защиты
Ограничение доступа
Можно использовать Tailscale ACL для ограничения доступа:
environment:
- TS_EXTRA_ARGS=--advertise-tags=tag:container
Затем в Tailscale Admin Console настроить ACL правила для тега tag:container.
Производительность на Synology NAS
Оптимизация
- SSD кеш: установка SSD кеша значительно ускоряет работу Docker
- Память: 10GB RAM достаточно для множества контейнеров
- CPU: сборка Caddy через GitHub Actions экономит ресурсы NAS
- Сеть: использование одной сети
caddyуменьшает overhead
Мониторинг
Следите за использованием ресурсов:
# Использование ресурсов контейнеров
docker stats
# Логи Caddy
docker logs -f caddy
# Статус Tailscale
docker exec caddy-tailscale tailscale status
Заключение
Переход на caddy-docker-proxy кардинально упростил управление домашним сервером. Теперь добавление нового сервиса — это просто добавление нескольких labels в docker-compose.yml. Никаких перезапусков, никаких сложных конфигураций.
Решение отлично подходит для:
- Домашних серверов на Synology NAS
- Самохостинга множества сервисов
- Ситуаций, когда нужны свои домены
- Автоматизации получения SSL сертификатов
Если вы тоже используете Docker для самохостинга, обязательно попробуйте caddy-docker-proxy — это действительно меняет подход к управлению reverse proxy!