Как установить Vaultwarden на VPS через Docker
Инструкция по развертыванию Vaultwarden на VPS с использованием Docker. Настройка HTTPS, управление правами через admin token и создание резервных копий данных.
Что вы создаете
Менеджер паролей под вашим полным контролем: Vaultwarden, запущенный в одном контейнере за reverse proxy с поддержкой HTTPS, и официальные приложения Bitwarden на вашем телефоне, ноутбуке и в браузере, настроенные на этот сервер. Vaultwarden реализует API сервера Bitwarden на языке Rust и использует тот же протокол, что и bitwarden.com. Это позволяет использовать любые официальные клиенты без изменений. При этом Vaultwarden потребляет около 100 MB RAM, в отличие от официального стека с множеством контейнеров.
Процесс установки занимает всего несколько строк в Compose. Основные критические моменты, из-за которых могут возникнуть проблемы: наличие TLS обязательно до первого входа в веб-интерфейс; регистрация новых пользователей должна быть отключена сразу после создания вашей учетной записи; данные в volume необходимо регулярно архивировать и проверять процесс восстановления, так как в этой директории хранятся все ваши пароли.
Предварительные требования и важные нюансы
- VPS с установленными Docker Engine и Compose plugin на чистой системе Ubuntu 24.04 KVM с правами root или sudo. 512 MB RAM достаточно; 1 GB будет комфортно. Это один из самых легковесных сервисов — он занимает одно из первых мест в списке сервисов для self-hosting.
- Домен с A-записью (и AAAA, если используется IPv6), указывающей на
vault.example.comвашего VPS. TLS-сертификат выпускается именно на это имя, поэтому DNS-запись должна разрешаться до начала настройки. - Порты 80 и 443 должны быть открыты для внешнего доступа и направлены на ваш reverse proxy — никогда не на Vaultwarden напрямую. Порт 80 используется только для ACME-проверки сертификата и перенаправления HTTP на HTTPS.
- Основная сложность: клиенты Bitwarden не работают с серверами без HTTPS. Вариант «сначала протестировать через http» невозможен — этот путь не работает по конкретной причине, описанной ниже.
Почему Vaultwarden, а не официальный стек Bitwarden
Те же клиенты, но значительно меньшее потребление ресурсов. Официальный self-hosted Bitwarden поставляется в виде набора контейнеров (MSSQL, Nginx, Identity, Api, Admin и др.) и требует около 2 GB RAM. Vaultwarden представляет собой один бинарный файл, который по умолчанию использует базу данных SQLite и в состоянии ожидания потребляет всего несколько десятков мегабайт. Для одного пользователя, семьи или небольшой команды это очевидный выбор. Благодаря точной реализации Bitwarden API ваши данные остаются переносимыми между Vaultwarden и bitwarden.com.
Вы теряете большинство функций корпоративного уровня: отсутствует SCIM provisioning (хотя экспериментальная поддержка OpenID Connect SSO появилась в версии 1.35.0). Также вы являетесь администратором системы, поэтому обновление, настройка HTTPS и создание резервных копий — это ваши задачи. Данное руководство посвящено именно этим трем задачам.
Почему HTTPS является обязательным
Веб-хранилище Bitwarden и расширения для браузеров генерируют ключи шифрования в браузере с помощью Web Crypto API (window.crypto.subtle). Браузеры предоставляют доступ к crypto.subtle только в безопасном контексте — HTTPS или в особом случае http://localhost. При использовании обычного http://vault.example.com доступ запрещен (undefined). Как только приложение пытается сгенерировать ключ, возникает ошибка, которую можно увидеть в консоли:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'importKey')Страница зависает или выдает общую ошибку криптографии; вход в систему невозможен. Десктопные, мобильные и браузерные клиенты выполняют собственную проверку URL-адреса. При обращении к http-адресу (или недоступному эндпоинту) они выдают ошибку:
This is not a recognized Bitwarden server. You may need to check with your provider or update your server.Причина в обоих случаях одна: отсутствие валидного HTTPS. Поэтому сначала необходимо настроить TLS. Никогда не открывайте хранилище через http, даже для разовой проверки.
Шаг 1 — DNS и обратный прокси (сначала TLS)
Направьте запись на ваш VPS и убедитесь, что она разрешается в правильный адрес:
dig +short vault.example.comВыведенная строка должна содержать IP-адрес вашего VPS. Если строка пуста или содержит неверный адрес, исправьте настройки DNS и дождитесь истечения TTL — выпуск сертификата невозможен для имени, которое не разрешается.
Для HTTPS front end в данном руководстве используется Traefik. Он автоматически выпускает и обновляет сертификаты Let's Encrypt и интегрируется в Compose. Если вы еще не используете его, сначала выполните настройку обратного прокси Traefik и автоматического TLS; этот процесс создает внешнюю сеть Docker (proxy ниже) и ACME resolver (letsencrypt), к которым подключается сервис Vaultwarden. Обычный nginx с сертификатом, выданным вручную, работает для Vaultwarden аналогично.
Предпочитаете nginx и Certbot вместо Traefik? Разверните Vaultwarden на 127.0.0.1:8080 (добавьте ports: ["127.0.0.1:8080:80"] в сервис и удалите метки Traefik), затем выпустите сертификат и настройте проксирование на него. Процесс получения сертификатов описан в выпуске сертификатов Let's Encrypt с помощью Certbot и nginx. Важным дополнительным условием является обновление WebSocket на пути уведомлений:
server {
listen 443 ssl;
server_name vault.example.com;
client_max_body_size 525M;
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;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}Обратите внимание на строку X-Real-IP — именно она позволяет Fail2ban в дальнейшем видеть реального злоумышленника вместо 127.0.0.1. Все остальные инструкции в данном руководстве идентичны независимо от того, используется Traefik или nginx в качестве front end.
Шаг 2 — файл Compose
Сначала создайте директорию проекта. В данном руководстве используется /opt/vaultwarden, что делает имя проекта Compose — и, следовательно, том данных vaultwarden_vw-data — предсказуемым; последующие шаги по настройке Fail2ban и резервного копирования зависят от этого точного имени.
sudo mkdir -p /opt/vaultwarden
cd /opt/vaultwardenСоздайте .env для секретного ключа администратора и поместите файл Compose в эту директорию.
# .env
ADMIN_TOKEN=paste-a-strong-token-hereСгенерируйте токен с помощью openssl rand -base64 48 и вставьте его в файл. (Более надежный хешированный формат будет рассмотрен далее; для начала подойдет длинная случайная строка.)
# docker-compose.yml
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vault.example.com"
SIGNUPS_ALLOWED: "true" # closed in Step 4, keep true just to register
ADMIN_TOKEN: "${ADMIN_TOKEN}"
IP_HEADER: "X-Forwarded-For" # X-Real-IP if your proxy sends that instead
LOG_FILE: "/data/vaultwarden.log"
LOG_LEVEL: "warn"
volumes:
- vw-data:/data
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.vw.rule=Host(`vault.example.com`)"
- "traefik.http.routers.vw.entrypoints=websecure"
- "traefik.http.routers.vw.tls.certresolver=letsencrypt"
- "traefik.http.services.vw.loadbalancer.server.port=80"
volumes:
vw-data:
networks:
proxy:
external: trueВся архитектура основана на двух особенностях этого файла. В нем нет проброса портов ports:, поэтому Vaultwarden доступен только через Traefik и его TLS — публикация порта на хосте приводит к случайному использованию хранилища по протоколу http. Также DOMAIN должен быть полным публичным URL-адресом HTTPS: он используется в ссылках на вложения, WebAuthn 2FA и эндпоинте уведомлений, поэтому неверное значение или использование http нарушит их работу, даже если сайт загружается. Тег latest является намеренным исключением из обычного правила «никогда не-latest» — Vaultwarden выпускает стабильные версии в виде единого образа, а :testing используется как отдельный канал для предварительных версий — поэтому обновляйтесь осознанно и изучайте примечания к релизу перед выполнением команды pull.
Запустите проект и следите за логами:
docker compose up -d
docker compose logs -f vaultwardenПри успешном запуске последней строкой будет Rocket has launched from http://0.0.0.0:80. Подождите несколько секунд, пока Traefik получит сертификат, затем загрузите https://vault.example.com — вы должны увидеть веб-интерфейс Bitwarden с действительным значком замка и без предупреждений о сертификате.
Шаг 3 — надежный ADMIN_TOKEN и ловушка $$
ADMIN_TOKEN защищает /admin — панель, которая может прочитать данные любого пользователя и любые настройки вашего инстанса. Относитесь к этому токену как к паролю root. Допустимы два формата.
Простой формат — это случайная строка, которую вы уже создали с помощью openssl rand -base64 48. Поскольку base64 никогда не содержит символ $, строка сразу попадает в .env без необходимости экранирования.
Усиленный формат — это хеш Argon2 PHC, при котором исходный токен никогда не хранится на диске. Сгенерируйте его для того же образа:
docker run --rm -it vaultwarden/server /vaultwarden hash --preset owaspПрограмма дважды запросит ввод и выведет строку, начинающуюся с $argon2id$v=19$.... Вот ловушка, из-за которой пользователи теряют по часу времени: Docker Compose интерпретирует $ как интерполяцию переменных. Поэтому при вставке хеша в Compose file необходимо удвоить каждый символ $ до $$. Размещайте его непосредственно под environment:, а не через .env, и не заключайте в кавычки:
environment:
ADMIN_TOKEN: $$argon2id$$v=19$$m=19456,t=2,p=1$$c29tZXNhbHQ$$RdescudvJCsgt3ub+b+dWRWJTmaaJObGЕсли оставить одиночные знаки $, Compose выдаст предупреждение The "argon2id" variable is not set и очистит токен. В результате /admin отклонит ваш верный пароль. Выполните docker compose up -d и сохраните введенный в консоли пароль в вашем менеджере паролей.
Шаг 4 — регистрация аккаунта и ограничение доступа
С помощью SIGNUPS_ALLOWED: "true" откройте https://vault.example.com, нажмите Create account и зарегистрируйтесь, используя ваш email и надежный мастер-пароль. Мастер-пароль невозможно восстановить — функция сброса отсутствует — поэтому сначала сохраните его в надежном месте.
Теперь ограничьте доступ. Отредактируйте файл Compose, чтобы отключить регистрацию новых пользователей:
SIGNUPS_ALLOWED: "false"Примените изменения с помощью docker compose up -d. Это не критическая мера безопасности, которую можно отложить. Если оставить регистрацию открытой, любой, кто найдет URL — а поисковые роботы это сделают — сможет создать аккаунт на вашем сервере. Они не смогут прочитать ваш сейф, но они будут потреблять ресурсы и превратят ваш приватный инстанс в публичный сервис. Признак того, что регистрация осталась открытой: в /admin будут перечислены аккаунты, которые вы не создавали.
Чтобы добавить членов семьи или коллег позже без открытия публичной регистрации, используйте кнопку Invite User в /admin; для этого метода требуется настроенный SMTP, чтобы приглашенный пользователь получил ссылку.
Шаг 5 — переход в /admin
Перейдите по адресу https://vault.example.com/admin и введите токен администратора в открытом виде (случайную строку или пароль, который вы хешировали — не сам хеш). В этом разделе можно просматривать список пользователей, изменять настройки, отправлять тестовое письмо и создавать снимок базы данных.
Если страница возвращает 404 Not Found, это означает, что ADMIN_TOKEN пуста или не задана, что полностью отключает панель управления. Это допустимо, если панель вам не требуется. Если страница загружается, но отклоняет ваш токен, ознакомьтесь с проблемой экранирования $$ в списке ошибок ниже. Забыли токен? Функция восстановления отсутствует; отредактируйте .env или Compose file, установите новый токен и docker compose up -d.
Шаг 6 — подключение клиентов Bitwarden
Любой официальный клиент может работать с self-hosted сервером. Установите Bitwarden desktop, mobile или browser client из стандартных магазинов приложений — вам не требуется специальная сборка Vaultwarden.
Перед входом в систему нажмите на иконку настроек на экране входа (с надписью Self-hosted или Region → Self-hosted), установите значение Server URL на https://vault.example.com и сохраните изменения. Затем войдите в систему, используя зарегистрированный email и мастер-пароль; клиент должен сразу установить соединение и предложить функцию автозаполнения и сохранения учетных данных.
Если клиент отображает This is not a recognized Bitwarden server. You may need to check with your provider or update your server., это означает, что URL указан неверно, используется протокол http или сертификат не является доверенным. Сначала убедитесь, что https://vault.example.com корректно загружается в браузере. Медленное обновление данных на других устройствах вызвано задержками WebSocket push, это описано ниже.
Шаг 7 — создание jail для Fail2ban для эндпоинта входа
Vaultwarden записывает каждую неудачную попытку входа в файл, указанный в LOG_FILE — это именно то, что необходимо для защиты от brute-force атак. Если у вас еще не установлен Fail2ban, инструкции по установке и базовой настройке приведены в руководстве по защите SSH с помощью Fail2ban; здесь мы добавим один jail для Vaultwarden.
Сначала найдите путь к именованному volume на хосте, чтобы Fail2ban мог читать лог:
docker volume inspect vaultwarden_vw-data --format '{{ .Mountpoint }}'Команда выведет результат вида /var/lib/docker/volumes/vaultwarden_vw-data/_data; лог находится по пути vaultwarden.log внутри него. Создайте фильтр:
# /etc/fail2ban/filter.d/vaultwarden.conf
[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =И jail:
# /etc/fail2ban/jail.d/vaultwarden.local
[vaultwarden]
enabled = true
filter = vaultwarden
logpath = /var/lib/docker/volumes/vaultwarden_vw-data/_data/vaultwarden.log
banaction = iptables-allports
chain = DOCKER-USER
maxretry = 5
findtime = 600
bantime = 3600Перезагрузите конфигурацию с помощью sudo systemctl restart fail2ban и проверьте статус с помощью sudo fail2ban-client status vaultwarden.
Эффективность защиты зависит от трех параметров Docker. Во-первых, если в логах при каждой неудачной попытке отображается IP: 127.0.0.1 или IP-адрес вашего прокси, Vaultwarden будет блокировать прокси-сервер. Установите IP_HEADER в значение заголовка, который фактически передает ваш прокси (X-Forwarded-For для Traefik, X-Real-IP для блока nginx выше, CF-Connecting-IP для Cloudflare). Во-вторых, выбор цепочки iptables зависит от вашего прокси: если Traefik запущен в контейнере с опубликованными портами, трафик проходит через путь Docker FORWARD, поэтому бан должен применяться к цепочке DOCKER-USER, как указано выше. Если же в Шаге 1 вы выбрали вариант с nginx на хосте, соединения завершаются в nginx на цепочке хоста INPUT, и бан через DOCKER-USER их не увидит — в этом случае удалите строку chain = DOCKER-USER, чтобы Fail2ban использовал цепочку INPUT по умолчанию. В-третьих, используйте banaction = iptables-allports вместо стандартного метода блокировки по портам — данный jail не определяет порт, и блокировка всех портов в DOCKER-USER надежно закроет нарушителю доступ ко всем опубликованным сервисам на хосте.
Шаг 8 — создайте резервную копию хранилища, а затем выполните восстановление
Том vw-data является вашим менеджером паролей. В нем содержатся db.sqlite3 (все записи), директории attachments/ и sends/, файлы rsa_key.* для подписи сессий входа и config.json из панели администратора. Резервная копия, в которой отсутствуют эти данные, не сработает в случае необходимости.
Копирование db.sqlite3 во время записи Vaultwarden может привести к получению поврежденного файла. Используйте «холодный» снимок (snapshot) — время простоя составит всего несколько секунд:
#!/usr/bin/env bash
set -euo pipefail
STAMP=$(date +%F)
DEST=/root/vw-backups
VOL=$(docker volume inspect vaultwarden_vw-data --format '{{ .Mountpoint }}')
mkdir -p "$DEST"
docker compose -f /opt/vaultwarden/docker-compose.yml stop vaultwarden
tar czf "$DEST/vw-$STAMP.tgz" -C "$VOL" .
docker compose -f /opt/vaultwarden/docker-compose.yml start vaultwardenНастройте запуск по cron каждую ночь и копируйте .tgz с сервера. Резервная копия, хранящаяся только на защищаемом сервере, не является полноценной копией. Оптимальный способ передачи данных — ночной restic backup на другой сервер или в объектное хранилище; этот метод шифрует архив и выполняет дедупликацию снимков. Кнопка Backup Database в панели администратора позволяет быстро создать снимок только файла SQLite, но она не включает в себя вложения и ключи.
Теперь выполните процедуру, которая отличает реальную резервную копию от потенциальной — восстановите данные один раз, чтобы подтвердить работоспособность:
mkdir -p /tmp/vw-restore
tar xzf /root/vw-backups/vw-2026-07-15.tgz -C /tmp/vw-restore
docker run --rm -p 127.0.0.1:8888:80 -v /tmp/vw-restore:/data vaultwarden/serverС вашего ноутбука создайте туннель к серверу через ssh -L 8888:127.0.0.1:8888 you@your-vps и откройте http://localhost:8888. Поскольку localhost является безопасным контекстом, crypto.subtle доступен, и хранилище расшифровывается по протоколу http — это единственное разрешенное место. Войдите, используя мастер-пароль, и проверьте наличие записей. Если они на месте, значит, ваша база данных, ключи RSA и мастер-пароль успешно прошли цикл проверки, и вы сможете развернуть систему на новом VPS за считанные минуты. Остановите контейнер с помощью Ctrl-C и удалите /tmp/vw-restore.
Failure modes, with the strings you will see
Cannot read properties of undefined (reading 'importKey') in the browser console. The vault was loaded over http, so crypto.subtle is undefined; reach it only via https:// and add the HTTP-to-HTTPS redirect at the proxy.
This is not a recognized Bitwarden server... in a client. The Server URL is http, mistyped, or the certificate is untrusted; confirm https://vault.example.com shows a valid padlock, then re-enter it in the client's self-hosted settings.
/admin rejects the correct password. The Argon2 hash lost its escaping — every $ must be $$ in Compose — or you entered the hash instead of the plaintext it represents.
Slow cross-device sync; console shows WebSocket connection to 'wss://vault.example.com/notifications/hub' failed. The proxy is not forwarding the Upgrade/Connection headers; Traefik does this automatically, nginx needs the two upgrade lines from Step 1. The vault still works, just syncing on open. The old dedicated port 3012 is gone since v1.31.0, so no separate WebSocket route is needed.
Fail2ban reports a ban but the attacker keeps connecting. It is banning 127.0.0.1 because IP_HEADER is wrong, or the ban sits in the wrong iptables chain — set chain = DOCKER-USER and banaction = iptables-allports.
Обновления
Загрузите новый образ и пересоздайте контейнер; именованный volume и все ваши данные сохранятся:
docker compose pull
docker compose up -dVaultwarden часто выпускает обновления. Рекомендуется следить за примечаниями к релизу проекта, а не фиксировать конкретную версию патча, так как некоторые выпуски содержат инструкции по миграции. Перед любым крупным обновлением создайте свежую резервную копию; вы сможете выполнить откат, восстановив tarball в новый volume.
FAQ
Является ли Vaultwarden тем же самым, что и Bitwarden?
Это совместимый независимый сервер, а не официальный продукт. Vaultwarden реализует API сервера Bitwarden на языке Rust. Это позволяет использовать официальные клиенты для десктопа, мобильных устройств, браузеров и CLI, потребляя при этом значительно меньше ресурсов. Формат хранилища идентичен, поэтому вы можете выполнить миграцию в любом направлении с помощью экспорта и импорта.
Действительно ли мне нужен HTTPS, или можно использовать http в локальной сети (LAN)?
HTTPS необходим для любых целей, кроме localhost тестирования. Веб-интерфейс и расширения Bitwarden используют Web Crypto API браузера. Этот API доступен только в защищенном контексте. При использовании обычного http клиент выдает ошибку Cannot read properties of undefined и не позволяет войти в систему. Единственный рабочий http-адрес — http://localhost, поэтому в шаге 8 для теста восстановления используется SSH-туннель.
Как запретить посторонним регистрацию на моем сервере?
Установите параметр SIGNUPS_ALLOWED: "false" в Compose file и выполните docker compose up -d сразу после создания собственной учетной записи. После этого добавляйте новых пользователей через кнопку Invite User в /admin. Для получения ссылки-приглашения должен быть настроен SMTP. Периодически проверяйте список администраторов, чтобы убедиться в отсутствии посторонних аккаунтов.
Как сделать резервную копию хранилища Vaultwarden?
Кратковременно остановите контейнер и архивируйте весь том vw-data — файлы db.sqlite3, attachments/, sends/, config.json и rsa_key.*. Затем скопируйте архив с сервера, желательно с помощью ежедневного задания cron. Копирование активного файла SQLite при работающем сервере может привести к повреждению снимка, поэтому выполняйте копирование в остановленном состоянии. Самое важное: один раз восстановите данные в тестовом контейнере и войдите в систему. Это подтвердит работоспособность резервной копии до того, как она станет критически важной.
Безопасно ли самостоятельно хостить свои пароли?
Да, если вы выполните три условия, описанные в данном руководстве: используете настоящий HTTPS, закрываете регистрацию и используете надежный токен администратора, а также тестируете резервные копии. Ваше хранилище шифруется на стороне клиента с помощью вашего мастер-пароля. Сервер никогда не видит пароли в открытом виде — украденный db.sqlite3 бесполезен без него. Обратной стороной является то, что установка обновлений и создание бэкапов теперь лежат на вас. Именно поэтому использование Fail2ban и процедура восстановления являются обязательными.