# Переход nginx → Caddy + Let's Encrypt (proxy.bildme.ru) Дата: 2026-09-20 · Профиль: refactoring · Репозиторий: `pub/elevenlabs.io` ## Задача Заменить nginx на Caddy, оставить порт 8082 как есть, параллельно открыть 443 с сертификатом Let's Encrypt на `proxy.bildme.ru`. Предыстория — таймаут `api.deepseek.com` у клиента, из-за чего в прокси был добавлен маршрут `/deepseek/`. ## Аудит прода (62.84.98.250) - 80/443 занимал хостовый nginx 1.18 (systemd): заглушка `/var/www/html` + vhost `tg.bildme.ru`, чей DNS уже указывал на `45.151.70.121`, сертификат истёк 14.07.2026 → потерь нет. - Контейнер `api-proxy` из `/srv/elevenlabs` (не git), проброс `8082:8080`, образ из `git.realmanual.ru`. - `docker-compose` 1.29.2 + Docker 28.2.2 → известная несовместимость (`KeyError: 'ContainerConfig'`). ## Изменения в репозитории | Файл | Что | |------|-----| | `Caddyfile` | новый конфиг: сниппеты `(scrub)`, `(api)`, `(routes)`; маршруты в `route {}` — guard'ы отрабатывают до `handle_path` | | `docker-entrypoint.sh` | генерация `matchers.caddy` / `guard.caddy` из `ALLOWED_CIDR` и `PROXY_SECRET`; обязательные `DOMAIN`, `ACME_EMAIL`; `caddy validate` перед стартом | | `Dockerfile` | `caddy:2.11.4-alpine`, EXPOSE 80/443/8080 | | `docker-compose.yml` | порты `80`, `443` (tcp+udp), `8082:8080`; тома `caddy_data`, `caddy_config` | | `scripts/migrate-host-nginx.sh` | бэкап `/etc/nginx` + `/etc/letsencrypt`, отключение nginx и `certbot.timer`, проверка портов | | `scripts/deploy.sh` | реальный прод-путь `/srv/elevenlabs`, приоритет `docker compose` v2 | | `nginx/`, `ssl/` | удалены | | `VERSION` | 0.2.0 | Коммиты: `d71001a`, `e90d673`. ## Выполнено на сервере 1. Бэкап хостового nginx → `/root/nginx-backup-20260920-093758` (`nginx-T.dump`, `etc-nginx.tar.gz`, `letsencrypt.tar.gz`). 2. `systemctl disable --now nginx`, `systemctl disable --now certbot.timer`. 3. Установлен `docker-compose-v2` 2.40.3 (v1 ломался на Docker 28). 4. `/srv/elevenlabs/docker-compose.yaml` и `.env` обновлены (бэкапы `.bak-2026-09-20`), добавлены `DOMAIN=proxy.bildme.ru`, `ACME_EMAIL=vasyakrg@gmail.com`. 5. Контейнер пересоздан из образа `0.2.0` / `latest`. ## Проверки (прод) | Проверка | Результат | |----------|-----------| | Сертификат | `CN=proxy.bildme.ru`, issuer Let's Encrypt YE1, до 19.12.2026 | | `https://proxy.bildme.ru/health` | 200 | | `http://62.84.98.250:8082/health` | 200 | | `http://proxy.bildme.ru/health` | 308 → https | | Без `X-Proxy-Token` | 403 `invalid_token` | | IP вне `ALLOWED_CIDR` | 403 `ip_not_allowed` | | `/deepseek/chat/completions` | 401 от DeepSeek (проброс работает, исходный i/o timeout снят) | | `/telegram/`, `/openrouter/`, `/elevenlabs/` | 401 / 200 / 404 от апстримов — проброс работает | | Неизвестный префикс | 404 `unknown_upstream` | | Пересоздание контейнера | сертификат переиспользован из тома, повторного выпуска не было | | Healthcheck | `healthy` | ## Риски и открытые пункты - **8082 остаётся plain HTTP** — токен и ключи апстримов идут открытым текстом. Клиентов нужно перевести на `https://proxy.bildme.ru`, после чего порт закрыть (ufw на VM сейчас `inactive`). - **Том `caddy_data`** содержит сертификаты и ACME-аккаунт. Его удаление = повторный выпуск и риск упереться в лимиты Let's Encrypt. - **Хостовый nginx остановлен и отключён**, конфиги в бэкапе. Если на VM появятся новые сайты — их конфиг переносится в `Caddyfile`, а не обратно в nginx. - **`certbot.timer` отключён** — старый серт `tg.bildme.ru` больше не обновляется (домен и так живёт на другом сервере). - Локальный каталог `ssl/` с `tls.crt`/`tls.key` удалён — эти файлы не были в git и на проде не монтировались.