Что такое Vaultwarden и зачем разворачивать его самостоятельно
Vaultwarden - это серверная реализация API Bitwarden на Rust. Один контейнер, SQLite в роли базы по умолчанию, 30-40 МБ на диске и 100-200 МБ оперативной памяти на 10-20 активных пользователей. Официальные клиенты Bitwarden работают с ним без модификаций: веб-хранилище, десктопные приложения для Windows, macOS и Linux, мобильные клиенты Android и iOS, расширения для Chrome, Firefox, Edge и Safari, а также CLI bw. Протокол тот же, поэтому клиент видит обычный Bitwarden, нужно только указать адрес self-hosted сервера в настройках вместо bitwarden.com.
Практический выигрыш: база паролей лежит на вашем железе, подписка не нужна, TOTP-коды, вложения, Send и коллекции организаций доступны без платного тарифа. Платите только за VPS: 1 vCPU и 1 ГБ RAM хватает команде до 20 человек. Bitwarden Teams стоит около $4 за пользователя в месяц, то есть 15 человек обойдутся примерно в $720 в год против нескольких тысяч рублей за аренду сервера.
Обратная сторона: обновления, резервные копии и восстановление после сбоя лежат на вас. Официальной поддержки нет, только сообщество и трекер проекта. Если сервер погибнет вместе с бэкапами, пароли восстановить не получится: ключи шифрования есть только у клиентов.
Чем Vaultwarden отличается от официального Bitwarden
Совместимость двусторонняя: клиенты Bitwarden подключаются к Vaultwarden, но не наоборот, потому что часть серверных ответов Vaultwarden шире, чем требует апстрим. Что реально работает без лицензии: TOTP-генератор внутри записей, вложения, Bitwarden Send, организации с коллекциями и ролями, API-ключи для CLI, WebAuthn и YubiKey в роли второго фактора, журнал событий в админке.
Ограничения, о которых стоит знать заранее:
- Мобильные push-уведомления требуют отдельной настройки через push-сервис Bitwarden, без него клиент синхронизируется при открытии и по расписанию.
- Синхронизации каталога с LDAP или Active Directory (Directory Connector) нет.
- SSO есть в варианте OpenID Connect, SAML не поддержан.
- Отдельные функции повторяют апстрим с задержкой в несколько недель или месяцев.
- Ответственность за доступность и целостность данных переходит к вам.
Кому подходит self-hosted хранилище паролей
Разворачивайте сервер, если: команда до 50 человек делит секреты через коллекции; нужна домашняя лаборатория с хранилищем на своём NAS или мини-ПК; внутренние регламенты или требования безопасности запрещают выносить секреты за периметр организации.
Не беритесь, если нет ресурса на обновления раз в месяц, контроль бэкапов и разбор инцидентов, а также если нужен SLA и юридическая поддержка вендора. Подробное сравнение моделей доверия и классов хранилищ собрано в материале о системах хранения паролей и критериях выбора.
| Критерий | Облачный Bitwarden | Vaultwarden на своём сервере |
|---|---|---|
| Стоимость | Premium $10 в год, Teams около $4 за пользователя в месяц | Только аренда VPS и домен |
| Где лежат данные | Инфраструктура Bitwarden | Ваш сервер, доступ по HTTPS |
| TOTP, вложения, Send, коллекции | Часть функций только в платных тарифах | Доступны без лицензии |
| Обновления | Автоматические | docker compose pull в вашем расписании |
| Корпоративные функции | SAML, Directory Connector, аудит | Политики, OIDC и журнал событий |
| Бэкапы | На стороне сервиса | На вас: db.sqlite3, attachments, rsa_key.pem |
Подготовка сервера и установка Docker
Чек-лист перед началом: Ubuntu 24.04 LTS или Debian 12, 1 vCPU, 1 ГБ RAM (2 ГБ, если планируются вложения и больше 30 клиентов), 10-20 ГБ SSD, статический IP, домен с A-записью на этот IP, открытые порты 80 и 443. Минимальной конфигурации VPS в Timeweb Cloud достаточно для старта, для продакшена берите 2 vCPU, чтобы переиндексация и бэкапы не мешали работе.
Требования к серверу и домену
Проверьте DNS до установки: команда dig +short vault.example.com должна вернуть IP сервера. Если домен обслуживает Cloudflare в режиме проксирования (оранжевое облако), выпуск сертификата по HTTP-01 не пройдёт: переключите запись в DNS-only либо используйте challenge DNS-01.
Доступ по SSH настройте заранее: вход по ключу, отдельный пользователь с sudo, пароль root и вход root по SSH отключены. Firewall: sudo ufw allow OpenSSH и sudo ufw allow "Nginx Full". Пользователи, sudoers, ufw и systemd разобраны в практическом руководстве по администрированию Linux.
Установка Docker и Docker Compose
- sudo apt update && sudo apt upgrade -y
- sudo apt install -y ca-certificates curl gnupg
- sudo install -m 0755 -d /etc/apt/keyrings
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
- sudo chmod a+r /etc/apt/keyrings/docker.gpg
- Добавьте репозиторий (для Debian 12 замените noble на bookworm): echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu noble stable" | sudo tee /etc/apt/sources.list.d/docker.list
- sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
- sudo systemctl enable --now docker
- Проверка: docker --version и docker compose version. Ожидайте Docker 27.x и Compose v2.x или новее.
- sudo usermod -aG docker $USER, затем выйдите из сессии и зайдите снова, чтобы группа применилась.
Развёртывание Vaultwarden через Docker Compose
Создайте рабочий каталог и токен администратора: sudo mkdir -p /opt/vaultwarden && cd /opt/vaultwarden, затем openssl rand -base64 48. Полученную строку вставьте как ADMIN_TOKEN. Альтернатива: сгенерировать argon2-хеш на странице /admin и хранить его вместо открытого токена.
Файл docker-compose.yml:
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vault.example.com"
SIGNUPS_ALLOWED: "false"
INVITATIONS_ALLOWED: "true"
ADMIN_TOKEN: "вставьте_свой_токен_из_openssl"
TZ: "Europe/Moscow"
LOG_LEVEL: "warn"
volumes:
- ./vw-data:/data
ports:
- "127.0.0.1:8080:80"
Порт публикуется только на localhost: снаружи сервис виден исключительно через Nginx. Каталог ./vw-data останется рядом с compose-файлом, его же вы будете архивировать в бэкапах.
Ключевые переменные окружения
- DOMAIN: полный адрес со схемой https. Попадает в письма-приглашения, ссылки на вложения и адрес WebSocket для live-синхронизации.
- SIGNUPS_ALLOWED=false: запрет самостоятельной регистрации.
- INVITATIONS_ALLOWED=true: администратор приглашает пользователей из панели /admin, приглашённый сам задаёт мастер-пароль.
- ADMIN_TOKEN: доступ к /admin. Если указан argon2-хеш, знаки $ в compose-файле экранируйте удвоением.
- TZ и LOG_LEVEL: часовой пояс для отметок в журнале и уровень логирования.
DOMAIN меняется только пересозданием контейнера, а после смены адреса потребуется перевыпустить сертификат и заново прописать новый адрес сервера в каждом клиенте. Выбирайте имя домена один раз и надолго.
Первый запуск и проверка контейнера
Запуск: docker compose up -d. Проверка состояния: docker compose ps показывает State: running и Status: Up. Логи: docker compose logs -f vaultwarden. Локальный отклик: curl -I http://127.0.0.1:8080 возвращает 200 или 302, эндпоинт http://127.0.0.1:8080/alive отдаёт 200.
Если контейнер циклически перезапускается, смотрите логи на ошибки доступа к ./vw-data. Типичный случай: каталог принадлежит другому пользователю, и процесс не может открыть db.sqlite3.
Настройка Nginx как reverse proxy с TLS Let's Encrypt
Установка пакетов: sudo apt install -y nginx certbot python3-certbot-nginx. Сначала создайте HTTP-конфиг с проксированием, получите сертификат, затем добавьте TLS-параметры и заголовки безопасности.
Конфиг Nginx с поддержкой WebSocket
Файл /etc/nginx/sites-available/vaultwarden в финальном виде:
server {
listen 80;
server_name vault.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name vault.example.com;
ssl_certificate /etc/letsencrypt/live/vault.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vault.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
client_max_body_size 128M;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
location /notifications/hub {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
}
}
Блок /notifications/hub отвечает за WebSocket: без заголовков Upgrade и Connection клиенты теряют live-обновления, и запись, добавленная на телефоне, появится на ноутбуке только после ручной синхронизации. Заголовки X-Forwarded-For и X-Forwarded-Proto нужны, чтобы Vaultwarden видел реальные IP для rate-limit и не считал соединение небезопасным.
Панель /admin полезно закрыть по IP: внутри server-блока добавьте location /admin с директивами allow 203.0.113.44; и deny all;, а proxy_set_header повторите как в блоке /. Клиент с другого адреса получит 403, и это правильное поведение.
Активация: sudo ln -s /etc/nginx/sites-available/vaultwarden /etc/nginx/sites-enabled/, затем sudo nginx -t и sudo systemctl reload nginx.
Выпуск и автопродление сертификата
Выпуск: sudo certbot --nginx -d vault.example.com --agree-tos -m admin@example.com --redirect. Certbot сам добавит ssl-директивы и редирект с 80 на 443. Проверка таймера продления: systemctl status certbot.timer, состояние должно быть active (waiting). Тестовый прогон продления: sudo certbot renew --dry-run. Успешный dry-run означает, что через 30 дней до истечения срока сертификат продлится автоматически.
Базовая настройка безопасности и SMTP
Сервер, поднятый с SIGNUPS_ALLOWED=true и оставленный без присмотра, открыт для регистрации любому, кто знает адрес. Закрывайте регистрацию сразу после проверки работоспособности.
Отключение публичной регистрации
В compose-файле выставьте SIGNUPS_ALLOWED: "false" и INVITATIONS_ALLOWED: "true". Первого пользователя создают приглашением: /admin, вкладка Users, кнопка Invite. Приглашённый получает письмо со ссылкой и сам задаёт мастер-пароль.
Как проверить, что регистрация закрыта: откройте адрес сервера, перейдите к созданию аккаунта и введите любой email. Ответ должен быть вида "Registration not allowed or user already exists". Дополнительные ограничители: SIGNUPS_DOMAINS_WHITELIST для списка разрешённых доменов и SIGNUPS_VERIFY=true для подтверждения email при регистрации.
После правки переменных окружения выполните docker compose up -d: команда restart не перечитывает environment и изменения не применятся.
Настройка SMTP для приглашений и уведомлений
Почта нужна для приглашений, подтверждения email, кодов двухфакторной аутентификации по email и подсказок к паролю. Набор переменных:
SMTP_HOST: "smtp.example.com"
SMTP_FROM: "vault@example.com"
SMTP_FROM_NAME: "Vaultwarden"
SMTP_PORT: "465"
SMTP_SECURITY: "force_tls"
SMTP_USERNAME: "vault@example.com"
SMTP_PASSWORD: "app-пароль_из_панели_провайдера"
SMTP_AUTH_MECHANISM: "Plain"
SMTP_TIMEOUT: "15"
Соответствие порта и режима: 465 с SMTP_SECURITY=force_tls, 587 с SMTP_SECURITY=starttls, 25 с off (провайдеры её часто блокируют). Пароль приложения берите в панели почтового сервиса, обычный пароль учётной записи обычно не подходит. Чтобы письма не улетали в спам, добавьте для домена отправителя записи SPF, DKIM и DMARC.
Проверка: отправьте тестовое приглашение из /admin и посмотрите логи docker compose logs -f vaultwarden, строки с ошибкой SMTP видны сразу. Усильте защиту самого аккаунта вторым фактором, включая аппаратные ключи и TOTP, по сравнению open-source аутентификаторов.
Миграция данных из облачного Bitwarden
Перенос 500 записей занимает 10-15 минут, включая проверку. Порядок одинаков для личного хранилища и для организации.
Экспорт из Bitwarden
В веб-интерфейсе: Tools, Export vault, формат .json, ввод мастер-пароля, кнопка Export vault. Файл скачивается локально. Для организации экспорт делает владелец из консоли организации, коллекции сохраняются в файле.
Что не попадает в экспорт: вложения (их скачивают вручную по записи), passkeys (создаются заново), API-ключи, история изменений. Зашифрованный .json с паролем импортируется только через CLI bw, обычный импорт его не читает.
Экспортированный JSON содержит пароли в открытом виде. Сохраняйте его только на локальном диске, не пересылайте почтой и не оставляйте в облачных синхронизируемых папках.
Импорт в Vaultwarden и проверка
- Войдите в веб-хранилище на своём домене (адрес из DOMAIN).
- Tools, Import data, File format: Bitwarden (json), затем Choose file и Import data.
- Сверьте количество элементов с числом записей в облачном аккаунте, проверьте папки и коллекции.
- Откройте 2-3 записи и убедитесь, что TOTP-коды генерируются.
- Скачайте нужные вложения вручную и приложите их к соответствующим записям.
- Удалите файл экспорта без возможности восстановления: shred -u ~/bitwarden-export.json.
- Установите клиенты Bitwarden на всех устройствах на новый адрес сервера и выполните синхронизацию.
Облачный аккаунт не удаляйте 2-4 недели: за это время всплывут забытые записи и вложения. Для переноса из других менеджеров пригодится руководство по миграции из KeePass, LastPass и Bitwarden: там разобраны форматы CSV и KDBX, слияние без дубликатов и очистка промежуточных файлов.
Резервное копирование и восстановление
Все данные Vaultwarden лежат в одном каталоге, в примере это ./vw-data. Отдельной базы данных на сервере нет, поэтому бэкап сводится к архивированию каталога.
Что именно нужно бэкапить
- db.sqlite3 вместе с файлами -wal и -shm: пользователи, записи, настройки организаций.
- attachments/: вложения к записям.
- sends/: одноразовые ссылки Bitwarden Send.
- rsa_key.pem и rsa_key.pub.pem: приватный ключ сервера.
- config.json: появляется, если настройки менялись из панели /admin, а не только через переменные окружения.
- Каталог icon_cache/ можно не копировать, он заполнится заново.
rsa_key.pem подписывает токены доступа и шифрует служебные данные, включая пароль SMTP, сохранённый из админки. Без него все выданные токены и сессии станут недействительными, а часть служебных настроек придётся вводить заново. Храните ключ только вместе с базой и всегда в зашифрованном архиве: открытый файл ключа даёт доступ к служебным механизмам сервера.
Скрипт и расписание бэкапа
Скрипт /opt/vaultwarden/backup.sh (остановка контейнера даёт согласованный снимок SQLite):
#!/usr/bin/env bash set -euo pipefail cd /opt/vaultwarden STAMP=$(date +%F-%H%M) mkdir -p /var/backups/vaultwarden docker compose stop tar czf /var/backups/vaultwarden/vw-$STAMP.tar.gz ./vw-data docker compose start find /var/backups/vaultwarden -name "vw-*.tar.gz" -mtime +7 -delete
Сделайте файл исполняемым: chmod +x /opt/vaultwarden/backup.sh. Расписание через cron (пользователь с правами на docker): 0 3 * * * /opt/vaultwarden/backup.sh >> /var/log/vw-backup.log 2>&1. Семь дневных копий на том же диске защищают от ошибок, но не от потери сервера: раз в неделю копию нужно вывозить на внешнее хранилище и шифровать, например через age или gpg.
Восстановление: остановите контейнер (docker compose stop), перенесите текущий ./vw-data в сторону, распакуйте архив в /opt/vaultwarden, проверьте владельца каталога, запустите docker compose start и войдите в веб-интерфейс. Проверку восстановления проводите на отдельном каталоге и свободном порту, чтобы не тронуть рабочие данные.
Проверка работоспособности и типичные ошибки
Чек-лист после установки
- Откройте https://vault.example.com: сертификат валиден, браузер не показывает предупреждений.
- Войдите под учётной записью, созданной по приглашению, и добавьте тестовую запись.
- Проверьте синхронизацию с мобильным клиентом: запись появляется после обновления.
- Откройте консоль разработчика в браузере и убедитесь, что соединение WebSocket по адресу /notifications/hub установлено со статусом 101.
- Отправьте тестовое приглашение и убедитесь, что письмо дошло, а не осело в спаме.
- Выполните sudo certbot renew --dry-run и systemctl status certbot.timer.
- Проверьте эндпоинт curl -I https://vault.example.com/alive.
Разбор частых ошибок первого запуска
| Симптом | Причина | Исправление |
|---|---|---|
| 502 Bad Gateway | Контейнер не отвечает на 127.0.0.1:8080: упал, неверный порт или имя сервиса | docker compose ps, docker compose logs -f vaultwarden, curl -I http://127.0.0.1:8080 |
| Клиент не синхронизируется, в консоли ошибка WebSocket | В Nginx нет location /notifications/hub или потеряны заголовки Upgrade и Connection | Добавить блок WebSocket, затем nginx -t и systemctl reload nginx |
| permission denied при старте, контейнер в цикле перезапуска | Каталог ./vw-data принадлежит другому пользователю | chown -R 1000:1000 ./vw-data и директива user: "1000:1000" в сервисе; на RHEL и SELinux добавьте суффикс :Z к volume |
| Certbot не выпускает сертификат | Закрыт порт 80, неверная A-запись или проксирование Cloudflare | ufw allow "Nginx Full", проверить dig +short, переключить запись в DNS-only или выбрать challenge DNS-01 |
| Письма не приходят | Неверный SMTP_SECURITY или порт, пароль вместо пароля приложения, нет SPF, DKIM и DMARC | 465 с force_tls или 587 с starttls, пароль приложения, добавить DNS-записи, смотреть строки SMTP в логах |
| Не создаётся приглашение, регистрация отклоняется | SIGNUPS_ALLOWED=false при INVITATIONS_ALLOWED=false | Включить INVITATIONS_ALLOWED=true и пересоздать контейнер командой docker compose up -d |
| Изменения переменных не применяются | Использован restart вместо пересоздания контейнера | docker compose up -d, при необходимости с флагом --force-recreate |
| Мобильный клиент отказывается подключаться | Клиент требует HTTPS, а сервер отвечает по HTTP или с самоподписанным сертификатом | Указать в DOMAIN адрес с https и использовать сертификат Let's Encrypt |
| Панель /admin не открывается при заданном ADMIN_TOKEN | Знаки $ в argon2-хеше интерпретируются Compose | Экранировать их удвоением или использовать токен из openssl rand -base64 48 |
Что делать дальше: обновления и поддержка
Обновление занимает минуты: docker compose pull, затем docker compose up -d. Перед каждым обновлением делайте свежий бэкап и читайте release notes образа: в минорных релизах Vaultwarden иногда поднимает минимальные требования к версиям клиентов, меняет поведение переменных окружения и структуру каталога данных. Версию можно посмотреть в админке на странице Diagnostics или командой docker compose exec vaultwarden /vaultwarden --version.
Мониторинг держите простым: внешний чекер пингует https://vault.example.com/alive и ждёт 200, отдельный алерт следит за сроком сертификата, логи контейнера пишутся с LOG_LEVEL=warn. Раз в квартал проверяйте, что бэкап разворачивается в тестовом каталоге, иначе копии остаются неизученными файлами.
Для команд следующий шаг: распределить записи по коллекциям, выдать роли и включить обязательный второй фактор. Если секреты нужны ещё и в пайплайнах, посмотрите материал об интеграции хранилищ секретов с CI/CD: там разобраны выдача токенов на время выполнения задания, маскирование логов и ротация.
Инструкция проверена на связке Ubuntu 24.04, Docker Compose v2 и ветке образов vaultwarden/server 2026 года. Набор переменных окружения и порт внутри контейнера остаются стабильными годами, но перед обновлением сверяйтесь с release notes: так вы не поймаете сюрприз на рабочем инстансе.