SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-26

Установка Vaultwarden на VPS через Docker

Разверните менеджер паролей Vaultwarden на собственном VPS. В статье описана настройка TLS, использование admin token, защита Fail2ban и надежная схема резервного копирования.

Что вы создаете

Менеджер паролей, который полностью находится под вашим контролем: Vaultwarden, работающий в одном небольшом контейнере за reverse proxy, который выполняет TLS termination. Официальные приложения Bitwarden на вашем телефоне, ноутбуке и в браузере будут обращаться к этому серверу. Vaultwarden реализует API сервера Bitwarden на языке Rust и использует тот же протокол, что и bitwarden.com. Благодаря этому все официальные клиенты работают с ним без изменений, но при этом он потребляет около 100 MB оперативной памяти, в отличие от официального стека из множества контейнеров.

Установка состоит из десятка строк в Compose. Три вещи, которые действительно важны и которые могут привести к сбоям: TLS должен быть настроен до того, как вы впервые откроете web vault; публичная регистрация должна быть закрыта сразу после создания вашей учетной записи; данные в томе должны регулярно копироваться с проверкой восстановления, так как в этой директории хранятся все ваши пароли.

Предварительные требования и важные нюансы

  • VPS с установленным Docker Engine и плагином Compose на чистой системе Ubuntu 24.04 KVM с доступом root или sudo. 512 МБ оперативной памяти вполне достаточно; 1 ГБ обеспечит комфортную работу. Это одно из самых легковесных решений, которое входит в краткий список сервисов, пригодных для self-hosting. Однако выбирайте размер сервера с учетом других задач: размещение self-hosted фотогалереи, такой как PhotoPrism или Immich, на том же VPS потребует уже нескольких гигабайт оперативной памяти, в то время как Vaultwarden потребляет минимум ресурсов. Тот же расчет применим к медиа-серверам, которые вы добавите позже, так как превращение медиатеки Jellyfin в виртуальный видеопрокат 90-х означает запуск еще одного постоянно работающего контейнера и необходимость запаса ресурсов для транскодирования.
  • Домен с 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 ГБ оперативной памяти. Vaultwarden — это один бинарный файл, который по умолчанию хранит данные в базе SQLite и в режиме ожидания потребляет всего несколько десятков мегабайт. Для одного пользователя, семьи или небольшой команды это очевидный выбор. Поскольку он точно реализует API Bitwarden, ваши данные остаются переносимыми между ним и bitwarden.com.

Вы отказываетесь от большинства корпоративных функций: нет поддержки SCIM-провижининга (хотя экспериментальный OpenID Connect SSO появился в версии 1.35.0), а поскольку вы являетесь оператором, то обновление, настройка HTTPS и резервное копирование — ваша задача. Это руководство посвящено именно этим трём задачам.

Почему HTTPS не является опциональным

Веб-хранилище Bitwarden и расширения для браузеров генерируют ваши ключи шифрования в браузере с помощью Web Crypto API (window.crypto.subtle). Браузеры предоставляют доступ к crypto.subtle только в безопасном контексте (secure context), то есть через HTTPS или в особом случае http://localhost. При использовании обычного http://vault.example.com доступ к API 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-фронтенда в этом руководстве используется Traefik, который автоматически выпускает и обновляет сертификаты Let's Encrypt и легко интегрируется в Compose. Если вы его еще не используете, сначала выполните настройку обратного прокси Traefik и автоматического TLS; это создаст внешнюю сеть Docker (proxy ниже) и ACME-резолвер (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.

Шаг 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 должно быть полным публичным HTTPS URL: оно встраивается в ссылки на вложения, WebAuthn 2FA и эндпоинт уведомлений, поэтому неверное или http-значение нарушит их работу, даже если сайт загружается. Тег latest является намеренным исключением из обычного правила «никогда не используйте latest»: Vaultwarden выпускает свои стабильные релизы как единый rolling-образ, а :testing является отдельным каналом предварительных версий, поэтому обновляйтесь осознанно и просматривайте примечания к релизу перед выполнением pull. Однако это исключение узкое: большинство долгоживущих контейнеров лучше фиксировать на конкретном теге, что делает агент, работающий на том же VPS, предсказуемым при перезагрузках и обновлениях.

Запустите проект и следите за логом:

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. Укажите его непосредственно в 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 и зарегистрируйтесь, указав адрес электронной почты и надежный мастер-пароль. Мастер-пароль невозможно восстановить или сбросить, поэтому сначала сохраните его в надежном месте.

Теперь ограничьте доступ. Отредактируйте файл 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, установите новое значение и выполните docker compose up -d.

Шаг 6, подключение клиентов Bitwarden

Любой официальный клиент поддерживает работу с self-hosted сервером. Установите десктопное, мобильное или браузерное приложение Bitwarden из стандартных магазинов; специальная сборка 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, что идеально подходит для защиты от брутфорса. Если Fail2ban у вас еще не запущен, инструкции по установке и базовой настройке можно найти в руководстве по защите SSH с помощью Fail2ban; здесь мы добавим один jail для хранилища.

Сначала определите путь к именованному тому на хосте, чтобы 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 запущен как контейнер с опубликованными портами, трафик проходит через путь FORWARD в Docker, поэтому блокировка должна находиться в DOCKER-USER, как показано выше. Если же вы выбрали вариант с host-nginx из Шага 1, соединения завершаются на цепочке 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 может привести к получению поврежденного файла, поэтому делайте «холодный» снимок; время простоя составит всего несколько секунд:

#!/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 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. Используйте этот метод туннелирования для любого другого административного интерфейса на сервере, который не должен быть доступен из Интернета. Таким же образом вы сможете получить доступ к самостоятельно размещенному сканеру безопасности open-kritt на порту 5173.

Типичные ошибки и сообщения, которые вы увидите

Cannot read properties of undefined (reading 'importKey') в консоли браузера. Хранилище было загружено по протоколу http, поэтому crypto.subtle не определено; обращайтесь к нему только через https:// и добавьте редирект с HTTP на HTTPS на стороне прокси.

This is not a recognized Bitwarden server... в клиенте. Указан URL сервера с http, допущена опечатка или сертификат не является доверенным; убедитесь, что https://vault.example.com отображает корректный значок замка, затем повторно введите адрес в настройках self-hosted клиента.

/admin отклоняет верный пароль. Хэш Argon2 потерял экранирование, каждый $ должен быть $$ в Compose, либо вы ввели сам хэш вместо открытого текста, который он представляет.

Медленная синхронизация между устройствами; в консоли WebSocket connection to 'wss://vault.example.com/notifications/hub' failed. Прокси не передает заголовки Upgrade/Connection; Traefik делает это автоматически, для nginx необходимо добавить две строки upgrade из шага 1. Хранилище продолжает работать, но синхронизация происходит только при открытии. Старый выделенный порт 3012 больше не используется начиная с версии v1.31.0, поэтому отдельный маршрут для WebSocket не требуется.

Fail2ban сообщает о блокировке, но атакующий продолжает подключаться. Блокируется 127.0.0.1 из-за неверной настройки IP_HEADER, либо блокировка попадает не в ту цепочку iptables; настройте chain = DOCKER-USER и banaction = iptables-allports.

Обновления

Загрузите новый образ и пересоздайте контейнер; именованный том и все ваши данные сохранятся:

docker compose pull
docker compose up -d

Vaultwarden выпускает обновления часто. Следите за заметками о релизах проекта, а не фиксируйте конкретную версию патча, так как некоторые релизы содержат инструкции по миграции. Перед любым мажорным обновлением сделайте свежую резервную копию; вы сможете выполнить откат, восстановив архив tarball в новый том.

FAQ

Является ли Vaultwarden тем же самым, что и Bitwarden?

Это совместимый независимый сервер, а не официальный продукт. Vaultwarden переписывает API сервера Bitwarden на языке Rust, поэтому официальные клиенты для десктопа, мобильных устройств, браузеров и CLI полностью с ним работают, потребляя при этом значительно меньше ресурсов, чем официальный стек. Формат хранилища идентичен, поэтому вы можете мигрировать в любом направлении с помощью экспорта и импорта данных.

Действительно ли мне нужен HTTPS или можно запустить его по http в локальной сети?

HTTPS необходим для всего, кроме localhost тестирования. Веб-хранилище Bitwarden и расширения используют Web Crypto API браузера, который доступен только в защищенном контексте. При использовании обычного http клиент выдает ошибку Cannot read properties of undefined и не позволяет войти в систему. Единственный адрес http, который работает — это http://localhost, поэтому для теста восстановления в шаге 8 используется SSH-туннель.

Как запретить посторонним регистрироваться на моем сервере?

Установите SIGNUPS_ALLOWED: "false" в файле Compose и выполните 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 и процедура восстановления здесь обязательны. Как только эти меры приняты, более детальный разбор векторов атак на самохостируемое хранилище станет полезным следующим шагом, так как при шифровании записей на стороне клиента защищать остается только токен администратора и архив резервной копии.