
В предыдущей заметке я описал, как поднять Vaultwarden (Bitwarden-совместимый сервер на Rust) на Docker Compose вместе с Caddy, чтобы последний автоматически получал TLS-сертификаты у Let’s Encrypt и проксировал запросы.
Такая схема работала стабильно. Однако со временем появились причины заменить Caddy на nginx. В этой статье я расскажу, как выполнил миграцию.
Зачем менять Caddy на nginx#
Caddy — отличный инструмент, особенно для домашних серверов: минимальная конфигурация, автоматический TLS и мало зависимостей. Но у меня все завернуто через nginx все равно, лучше все в одном уж месте.
Архитектура после замены#
Было:
Сеть → Caddy:443 (TLS + ACME) → Vaultwarden:80 (внутри Docker)Стало:
Сеть → Nginx:443 (TLS, Let's Encrypt, статика) → Vaultwarden:8001 (HTTP)Vaultwarden больше не занимается терминацией TLS. Он обслуживает чистый HTTP на порту 8001, а nginx обеспечивает защищенное соединение. Это соответствует официальной рекомендации разработчиков Vaultwarden: использовать обратный прокси для TLS.
Требования#
- Сервер с работающим Vaultwarden (в моем случае — нативная установка через DietPi).
- Установленный nginx.
- Доменное имя (например,
vault.beaverbloger.com). - Действующие сертификаты Let’s Encrypt, выпущенные через
dietpi-letsencrypt. - Открытые порты 80 и 443 на фаерволе/роутере (или использование Tailscale).
Шаг 1. Отключаем встроенный TLS в Vaultwarden#
Если ранее Vaultwarden работал по HTTPS, необходимо убрать эту настройку. Параметры хранятся в файле окружения (для DietPi это обычно /mnt/dietpi_userdata/vaultwarden/vaultwarden.env).
Найдите и удалите или закомментируйте строку:
ROCKET_TLS={certs="./fullchain.pem",key="./privkey.pem"}Убедитесь, что порт указан верно (по умолчанию 8001):
ROCKET_PORT=8001Перезапустите сервис:
dietpi-services restart vaultwardenПроверьте, что сервис слушает только HTTP:
curl http://127.0.0.1:8001Шаг 2. Установка nginx#
Если nginx еще не установлен, используйте DietPi Software:
dietpi-software install 85Проверьте версию и статус службы:
nginx -v
dietpi-services status nginxУбедитесь, что порты 80 и 443 свободны. Если вы ранее использовали Caddy, остановите и отключите его службу.
Шаг 3. Получение сертификатов через dietpi-letsencrypt#
Вместо ручной настройки certbot используем штатную утилиту DietPi.
Запустите мастер настройки:
dietpi-letsencrypt- Укажите свой домен (например,
vault.beaverbloger.com). - Утилита предложит настроить nginx. Поскольку мы будем использовать кастомный конфиг, можно согласиться на базовую настройку.
dietpi-letsencrypt использует certbot под капотом. Сертификаты будут обновляться автоматически по расписанию, но нам нужно научить nginx подхватывать новые файлы.
Шаг 4. Настройка nginx#
Конфигурация состоит из upstream-блока и server-блоков
Upstream и поддержка WebSocket#
Vaultwarden использует WebSocket для push-уведомлений. Nginx должен корректно обрабатывать апгрейд соединения. Создайте файл /etc/nginx/conf.d/vaultwarden_upstream.conf:
upstream vaultwarden {
server 127.0.0.1:8001;
keepalive 2;
}
map $http_upgrade $connection_upgrade {
default upgrade;
"" "";
}Виртуальный хост#
Создайте файл /etc/nginx/sites-available/vaultwarden:
# HTTP -> HTTPS редирект
server {
listen 80;
listen [::]:80;
server_name vault.beaverbloger.com;
return 301 https://$host$request_uri;
}
# HTTPS
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name vault.beaverbloger.com;
# Пути к сертификатам (копии для nginx)
ssl_certificate /etc/ssl/vaultwarden/fullchain.pem;
ssl_certificate_key /etc/ssl/vaultwarden/privkey.pem;
# Безопасные настройки TLS
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384";
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=604800; includeSubDomains" always;
# Лимит размера загружаемых файлов (вложения)
client_max_body_size 525M;
location / {
proxy_pass http://vaultwarden;
proxy_http_version 1.1;
# Заголовки для WebSocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Стандартные прокси-заголовки
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;
}
}Активируйте конфиг и проверьте синтаксис:
ln -sf /etc/nginx/sites-available/vaultwarden /etc/nginx/sites-enabled/
nginx -t
dietpi-services restart nginxШаг 5. Автоматическое обновление сертификатов#
Поскольку мы используем dietpi-letsencrypt, сертификаты обновляются автоматически. Однако nginx не увидит новые файлы, если они просто заменятся в папке /etc/letsencrypt/ (из-за прав доступа и символических ссылок).
Решение ленивое: копировать сертификаты в отдельную директорию, доступную nginx, и перезагружать веб-сервер через deploy-хук.
Создайте скрипт /etc/letsencrypt/renewal-hooks/deploy/vaultwarden.sh:
#!/bin/bash
DOMAIN="vault.beaverbloger.com"
SSL_DEST="/etc/ssl/vaultwarden"
mkdir -p "$SSL_DEST"
cp "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" "$SSL_DEST/fullchain.pem"
cp "/etc/letsencrypt/live/$DOMAIN/privkey.pem" "$SSL_DEST/privkey.pem"
chown root:www-data "$SSL_DEST/privkey.pem"
chmod 640 "$SSL_DEST/privkey.pem"
chmod 644 "$SSL_DEST/fullchain.pem"
dietpi-services restart nginxСделайте скрипт исполняемым:
chmod +x /etc/letsencrypt/renewal-hooks/deploy/vaultwarden.shТеперь при каждом успешном обновлении сертификата через dietpi-letsencrypt (который вызывает certbot) этот скрипт выполнится автоматически, скопирует свежие ключи и мягко перезагрузит nginx.
Проверка работы#
- TLS:
echo | openssl s_client -connect vault.beaverbloger.com:443 2>/dev/null | openssl x509 -noout -subject - Доступность API:
curl https://vault.beaverbloger.com/api/config - WebSocket: Откройте веб-интерфейс Vaultwarden в браузере
- Логи:
tail -f /var/log/nginx/error.log
Заключение#
Миграция с Caddy на nginx в экосистеме DietPi требует немного больше ручной настройки, но дает полный контроль над параметрами. Использование штатной утилиты dietpi-letsencrypt для выпуска сертификатов и кастомного deploy-хука для их доставки в nginx позволяет сохранить автоматизацию обновлений, не жертвуя гибкостью конфигурации веб-сервера.


