Надёжная схема для сервера с контейнерами: приложения слушают только 127.0.0.1 или внутреннюю сеть Docker, а наружу смотрит один обратный прокси на портах 80 и 443. Он принимает HTTPS, выбирает сервис по домену и передаёт запрос дальше.
Вариант 1: Nginx на хосте
Подходит, если Nginx уже стоит или вы привыкли к Certbot. Контейнер публикует порт только на локальном адресе:
services: app: image: ghcr.io/example/myapp:1.4.2 restart: unless-stopped ports: - "127.0.0.1:3000:3000"server { listen 80; server_name app.example.com; location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }}$ ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/$ nginx -t && systemctl reload nginx$ certbot --nginx -d app.example.comВариант 2: Caddy в контейнере
Caddy сам получает и продлевает сертификаты Let’s Encrypt. Прокси и приложения живут в одной сети Compose, поэтому порты приложений вообще не публикуются.
services: caddy: image: caddy:2 restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy-data:/data - caddy-config:/config app: image: ghcr.io/example/myapp:1.4.2 restart: unless-stoppedvolumes: caddy-data: caddy-config:app.example.com { reverse_proxy app:3000}Каждый новый сервис — ещё один блок с доменом в Caddyfile. После правки перечитайте конфигурацию: docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile.
DNS и фаервол
Перед выпуском сертификата A-запись домена должна указывать на IP сервера, а порты 80 и 443 — быть открыты:
$ ufw allow 80/tcp$ ufw allow 443/tcp$ dig +short app.example.comПроверьте результат
$ curl -I https://app.example.com$ ss -tlnp | grep -E ":(80|443|3000) "По HTTPS должен прийти ответ приложения; порт 3000 — только на 127.0.0.1 или не опубликован вовсе.
Частые ошибки
- 502 Bad Gateway — приложение не запущено или слушает другой порт. Проверьте docker compose ps и логи.
- Приложение видит IP прокси вместо IP клиента — включите в нём доверие к заголовку X-Forwarded-For от адреса прокси.
- Caddy не получает сертификат — DNS ещё не обновился или порт 80 закрыт. Смотрите docker compose logs caddy.