Docker-контейнеры обычно не обрабатывают SSL самостоятельно — обратный прокси перед ними завершает TLS и пересылает расшифрованный трафик в контейнер. В этом руководстве описаны три наиболее распространённых подхода.
Подход 1: Обратный прокси Nginx с ручным сертификатом
Самый простой подход для небольших деплоев. Получите сертификат с GetHTTPS и смонтируйте его в контейнер Nginx.
docker-compose.yml
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./certs/fullchain.pem:/etc/ssl/fullchain.pem:ro
- ./certs/privkey.pem:/etc/ssl/privkey.pem:ro
depends_on:
- app
app:
image: your-app:latest
expose:
- "3000"
nginx.conf
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /etc/ssl/fullchain.pem;
ssl_certificate_key /etc/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://app: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;
}
}
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
Для обновления: Замените файлы в ./certs/ новыми от GetHTTPS, затем выполните docker compose exec nginx nginx -s reload.
Подход 2: Traefik с автоматическим Let’s Encrypt
Traefik — это обратный прокси, созданный для контейнеров. Он может автоматически получать и обновлять сертификаты Let’s Encrypt.
docker-compose.yml
services:
traefik:
image: traefik:v3.0
command:
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/acme/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik-acme:/acme
app:
image: your-app:latest
labels:
- "traefik.http.routers.app.rule=Host(`yourdomain.com`)"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
- "traefik.http.services.app.loadbalancer.server.port=3000"
volumes:
traefik-acme:
Traefik берёт на себя всё: выпуск сертификата, обновление и терминацию HTTPS. Ручное управление сертификатами не требуется.
Подход 3: Caddy (HTTPS без настройки)
Caddy автоматически получает и обновляет сертификаты Let’s Encrypt без какой-либо настройки.
docker-compose.yml
services:
caddy:
image: caddy:2
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
depends_on:
- app
app:
image: your-app:latest
expose:
- "3000"
volumes:
caddy-data:
Caddyfile
yourdomain.com {
reverse_proxy app:3000
}
Это вся конфигурация. Caddy получает сертификат Let’s Encrypt и обновляет его автоматически.
Какой подход выбрать?
| Nginx + ручной сертификат | Traefik | Caddy | |
|---|---|---|---|
| Настройка | Средняя (конфигурация + файлы сертификата) | Средняя (labels) | Минимальная |
| Автообновление | ❌ (вручную) | ✅ | ✅ |
| Гибкость | Высокая | Высокая | Средняя |
| Кривая обучения | Низкая (знакомый инструмент) | Средняя | Низкая |
| Лучше всего подходит для | Небольших деплоев, полный контроль | Динамических контейнеров, микросервисов | Простых сайтов, минимум конфигурации |
Обновление сертификатов в Docker
Подход 1 (Nginx + вручную):
# Замените файлы сертификатов в локальной директории ./certs/
cp new-fullchain.pem ./certs/fullchain.pem
cp new-privkey.pem ./certs/privkey.pem
# Перезагрузите Nginx внутри контейнера (перезапуск не нужен)
docker compose exec nginx nginx -s reload
Подход 2 (Traefik) и 3 (Caddy):
Автоматически — они обрабатывают обновление внутренне. Сертификаты хранятся в Docker-томах (traefik-acme или caddy-data) и сохраняются при перезапуске контейнеров.
Docker Swarm: использование секретов
Для деплоев в Docker Swarm используйте Docker secrets вместо bind-монтирования файлов сертификатов:
# Создайте секреты из файлов сертификата
docker secret create ssl_cert fullchain.pem
docker secret create ssl_key privkey.pem
# В docker-compose.yml (режим deploy)
services:
nginx:
image: nginx:alpine
secrets:
- ssl_cert
- ssl_key
configs:
- source: nginx_conf
target: /etc/nginx/conf.d/default.conf
secrets:
ssl_cert:
external: true
ssl_key:
external: true
Секреты монтируются по пути /run/secrets/ssl_cert и /run/secrets/ssl_key внутри контейнера — зашифрованы при хранении и при передаче.
Распространённые ошибки
Встраивание сертификатов в Docker-образы
# ❌ НИКОГДА так не делайте
COPY fullchain.pem /etc/ssl/fullchain.pem
COPY privkey.pem /etc/ssl/privkey.pem
Сертификаты истекают каждые 90 дней. Встраивание их в образ означает пересборку и переразвёртывание каждые 60 дней. Всегда монтируйте сертификаты как тома или секреты.
Не открыт порт 80
Порт 80 необходим для:
- Редиректа HTTP → HTTPS
- HTTP-01 challenge Let’s Encrypt (для автообновления Traefik/Caddy)
ports:
- "80:80" # Не забудьте об этом
- "443:443"
Отсутствие сохранения данных ACME
Traefik и Caddy хранят свои сертификаты Let’s Encrypt и ключи аккаунта в томах. Без сохранения данных они запрашивают сертификаты заново при каждом перезапуске контейнера — и упрутся в лимиты запросов:
volumes:
traefik-acme: # ОБЯЗАТЕЛЬНО должен быть именованным томом, не анонимным
caddy-data:
Часто задаваемые вопросы
Может ли контейнер с приложением обрабатывать SSL самостоятельно?
Может, но не рекомендуется. Паттерн обратного прокси (терминация TLS на границе, пересылка обычного HTTP внутрь) является стандартом, потому что: приложению не нужно знать о сертификатах, обновление не требует перезапуска приложения, и вы централизуете конфигурацию TLS.
Как настроить SSL для нескольких контейнеров?
С Traefik или Caddy добавьте labels/конфигурацию для каждого сервиса — они автоматически получат отдельные сертификаты. С Nginx добавьте несколько блоков server в конфигурацию.
Использовать GetHTTPS или встроенный ACME Traefik?
Используйте встроенный ACME Traefik/Caddy для продакшена — он обрабатывает обновление автоматически. Используйте GetHTTPS, когда вам нужен сертификат для подхода с обратным прокси Nginx (Подход 1) или для сред, где контейнер не имеет прямого доступа в интернет для ACME-проверок.
Как безопасно монтировать сертификаты в Docker?
Монтируйте в режиме только для чтения (:ro), используйте Docker secrets для закрытого ключа в Swarm-режиме и убедитесь, что только контейнер обратного прокси имеет доступ к файлам сертификатов. Никогда не встраивайте сертификаты в Docker-образ — они истекут и вам придётся пересобирать.
Можно ли использовать сертификаты GetHTTPS с Kubernetes?
Да. Создайте Kubernetes TLS secret из файлов сертификата GetHTTPS:
kubectl create secret tls my-tls-cert --cert=fullchain.pem --key=privkey.pem
Затем укажите его в вашем Ingress-ресурсе. Для автоматического обновления в Kubernetes рассмотрите cert-manager с Let’s Encrypt ClusterIssuer.