Vaultwarden на сервере: частые ошибки и решения
Хранилище паролей ломается всегда не вовремя: утром расширение молча отдаёт Failed to fetch, панель /admin уверяет, что она отключена, а контейнер бесконечно перезапускается. Хорошая новость — стек у Vaultwarden короткий, и почти каждая поломка садится ровно на один стык. Разберём типовые ошибки по слоям: с дословными текстами из логов, командами проверки и честными оговорками там, где чинить уже нечего.
Содержание
- Триаж за две минуты: где именно сломалось
- Контейнер падает по кругу: права, порт, память и пропавший том
- Версии разъехались: сервер старее клиентов
- Настройка «не применяется»: переменная не доехала до процесса
- Виноват прокси, а не Vaultwarden: белая страница, TLS и подменённый IP
- Вход и 2FA: неверный код, часы и потерянный доступ
- Какой сервер под Vaultwarden брать в MAATRIX
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Триаж за две минуты: где именно сломалось
Путь запроса выглядит так: клиент → DNS и TLS → обратный прокси → контейнер Vaultwarden → SQLite на диске. Диагностика — это отсечение слоёв, а не перебор гайдов.
| Что видит пользователь | Вероятный слой | Первая проверка |
|---|---|---|
ERR_CONNECTION_REFUSED | контейнер или порт | docker ps |
502 Bad Gateway | прокси ↔ контейнер | curl на /alive мимо прокси |
Вход прошёл, дальше An error has occurred | версии сервера и клиента | /api/config |
/admin пишет, что панель отключена | переменные не доехали | docker exec ... printenv |
В расширении Failed to fetch | TLS-цепочка | openssl s_client |
| Открывается, но изменения не долетают | вебсокеты и push | отдельный разбор |
Три команды, которые сужают поиск быстрее любого чтения логов:
docker inspect -f 'status={{.State.Status}} restarts={{.State.Restarts}} oom={{.State.OOMKilled}}' vaultwarden
curl -s -o /dev/null -w '%{http_code} %{time_total}\n' http://127.0.0.1:8080/alive
curl -s https://vault.example.com/api/config | jq '.server, .version'
/alive — единственный эндпоинт без авторизации, он возвращает текущее время UTC. Обращение по 127.0.0.1 намеренно минует Nginx: 200 изнутри и 502 снаружи означают, что Vaultwarden жив, а виноват прокси, и читать надо /var/log/nginx/error.log. На локальной петле здоровый ответ укладывается в 5–10 мс; выросший до секунд time_total — сервер упирается в диск или память.
Растущий счётчик restarts — это рестарт-луп, и логи текущего запуска бесполезны: процесс не дожил до интересного. Смотрите docker logs --tail 50 --timestamps vaultwarden. Успешный старт всегда заканчивается одинаково:
[INFO] Rocket has launched from http://0.0.0.0:80
Нет этой строки — до обслуживания HTTP дело не дошло, и раздел про прокси можно пропустить.
Контейнер падает по кругу: права, порт, память и пропавший том
Три текста в логе покрывают почти все случаи, когда Vaultwarden не поднимается.
Error: unable to open database file или attempt to write a readonly database. Права на каталог данных. Тонкость, из-за которой правку делают наполовину: SQLite нужен доступ на запись не только к db.sqlite3, но и к самому каталогу — рядом создаются db.sqlite3-wal и db.sqlite3-shm. Сверяйте владельца с UID процесса внутри контейнера:
docker exec vaultwarden id
stat -c '%U:%G %a' /opt/vaultwarden/vw-data
chown -R 1000:1000 /opt/vaultwarden/vw-data && chmod 750 /opt/vaultwarden/vw-data
По умолчанию образ работает от root, и проблема появляется после того, как в compose добавили user: "1000:1000", а владельца каталога не поменяли.
failed to bind port 0.0.0.0:8080/tcp: bind: address already in use. Порт занят — чаще всего забытым вторым контейнером; виновника показывает ss -lntp | grep :8080. Помните, что внутри контейнера Vaultwarden слушает 80-й порт: меняется он переменной ROCKET_PORT, правка левой части ports: на это не влияет.
oom=true в выводе docker inspect. Процесс убит по нехватке памяти. Сам Vaultwarden в простое держится в районе 40–60 МБ, но проверка Argon2-хеша при входе в /admin с пресетом bitwarden требует 64 МБ единовременно на каждую попытку — на машине с 512 МБ без свопа этого достаточно.
Отдельный, самый болезненный случай: контейнер работает, а сейф пустой. Почти всегда это отсутствующий том — данные писались в слой контейнера и исчезли при пересоздании.
docker inspect -f '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{println}}{{end}}' vaultwarden
Строки с -> /data нет — тома нет. Если старый контейнер ещё существует (docker ps -a), данные вытаскиваются через docker cp vaultwarden_old:/data ./rescue, и делать это надо до любых docker compose down. Честно: если контейнер запускали с --rm или уже снесли командой docker compose down -v (флаг -v удаляет именованные тома), восстанавливать нечего. Общая механика падений — в статье про контейнер, который сразу падает.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть VaultwardenВерсии разъехались: сервер старее клиентов
Главная новая ошибка 2026 года, и на ошибку она не похожа. Vaultwarden — не Bitwarden, а независимая реализация его API. Клиенты (расширение, десктоп, мобильное приложение) обновляются сами и без спроса, сервер стоит на закреплённом теге годами. В какой-то момент клиент стучится в эндпоинт, которого в вашей сборке ещё нет, и получает 404.
Выглядит так: вход проходит, дальше клиент показывает An error has occurred, сейф открывается пустым или часть записей не расшифровывается. Данные целы — их просто некому отдать в новом формате.
Разработчик публикует соответствие явно, его стоит держать под рукой:
| Версия Vaultwarden | Под какие клиенты | Когда вышла |
|---|---|---|
| 1.36.0 | web-vault 2026.4.1, клиенты до 2026.6 | весна 2026 |
| 1.37.0 | клиенты 2026.7.0+ | июль 2026 |
| 1.37.2 | клиенты 2026.8.0+ | 22 августа 2026 |
Проверка занимает полминуты: docker exec vaultwarden /vaultwarden --version — версия сервера, curl -s https://vault.example.com/api/config | jq -r '.server.version' — то же снаружи, версия клиента лежит внизу страницы настроек расширения. Расхождение поколений и есть диагноз.
Неприятная честность: откат назад не поддерживается. При старте новая версия прогоняет миграции схемы SQLite, и они односторонние. Порядок обновления поэтому ровно такой:
sqlite3 /opt/vaultwarden/vw-data/db.sqlite3 ".backup '/root/vw-$(date +%F).sqlite3'"
sed -i 's|vaultwarden/server:1.36.0|vaultwarden/server:1.37.2|' docker-compose.yml
docker compose pull && docker compose up -d
docker compose logs -f --tail=50 vaultwarden
Первая строка обязательна: если миграция упадёт на середине, вернуть базу можно только из копии, снятой правильным способом (бэкап и восстановление). Веб-хранилище вшито в образ и руками не обновляется. Обе стратегии тегов ломаются, просто по-разному: latest однажды приедет с изменением, ломающим конфиг посреди рабочего дня, а закреплённый тег однажды отстанет от клиентов. Компромисс — фиксированный тег плюс окно обновления раз в месяц.
Настройка «не применяется»: переменная не доехала до процесса
Классика: вы правите docker-compose.yml, перезапускаете, ошибок нет — и ничего не меняется. Причин ровно три, и все проверяются изнутри контейнера, а не в редакторе.
Первая: переменной у процесса нет. Смотрите не свой шелл и не файл, а окружение работающего контейнера.
docker exec vaultwarden printenv | sort | grep -E 'DOMAIN|SIGNUPS|ADMIN|SMTP'
Пусто там, где ожидалось значение, — env_file не подхватился или указан не тот путь. Отдельная ловушка Compose: правка файла, на который ссылается env_file, не всегда приводит к пересозданию контейнера. Привыкайте к docker compose up -d --force-recreate.
Вторая: опечатка в имени. Vaultwarden читает только известные ему имена и не ругается на незнакомые. Строка SIGNUP_ALLOWED=false вместо SIGNUPS_ALLOWED=false не даст ни ошибки, ни предупреждения — регистрация останется открытой, и через неделю в базе появятся чужие учётки. То же с префиксами: сервис ждёт ADMIN_TOKEN, а не VAULTWARDEN_ADMIN_TOKEN.
Третья: /data/config.json перекрывает переменные. Если вы хоть раз нажали «Сохранить» в веб-панели, настройки легли в файл, и с этого момента окружение проигрывает. Сервис честно предупреждает об этом при старте:
[WARN] The following environment variables are being overridden by the config.json file
[WARN] Please use the admin panel to make changes to them
Дальше идёт список имён. Решение — выбрать один источник: либо cat vw-data/config.json показывает пустой {} и всё живёт в переменных, либо всё настраивается панелью. Про первичную настройку — в статье как установить и настроить Vaultwarden на VPS.
Отсюда же растут две частые жалобы на панель. Страница /admin отвечает фразой The admin panel is disabled, please configure the 'ADMIN_TOKEN' variable to enable it — значит переменной у процесса нет, смотрите пункт первый. А Invalid admin token при заведомо правильном пароле проверяется одной командой:
docker exec vaultwarden sh -c 'echo "[$ADMIN_TOKEN]"'
Значение должно начинаться с $argon2id$ и целиком помещаться между скобками. Начинается с $argon2id — Compose или Portainer доэкранировал доллары; видны кавычки — они уехали в значение. И банальная, но регулярная причина: в форму вводят сам хеш вместо пароля, из которого он сделан. А 429 Too Many Requests на /admin — не поломка, а сработавший ADMIN_RATELIMIT_SECONDS: счётчик живёт в памяти и обнуляется перезапуском.
Виноват прокси, а не Vaultwarden: белая страница, TLS и подменённый IP
Если /alive на 127.0.0.1 отвечает 200, а снаружи всё плохо — дальше про Nginx.
Failed to fetch в расширении и «нет доверия» в мобильном приложении. Клиенты Bitwarden не работают с самоподписанным сертификатом и с неполной цепочкой. Браузер такую цепочку часто достраивает сам и показывает замочек, а расширение — нет; отсюда «в браузере работает, в расширении нет».
openssl s_client -connect vault.example.com:443 -servername vault.example.com </dev/null 2>&1 | grep -E 'Verify return code|depth='
Нужен Verify return code: 0 (ok) и минимум две ступени depth. В конфиге Nginx должен стоять fullchain.pem, а не cert.pem.
Белая страница веб-хранилища при рабочем /alive. Vaultwarden отдаёт собственный Content-Security-Policy, рассчитанный на встроенный web-vault. Добавленный «для безопасности» на прокси add_header Content-Security-Policy "default-src 'self'"; ломает интерфейс насмерть — в консоли браузера появится Refused to execute inline script because it violates the following Content Security Policy directive. Убирайте чужие CSP и X-Frame-Options с этого хоста; тот же эффект дают Rocket Loader и автоминификация JS у Cloudflare. Прочие грабли проксирования — в разборе Nginx как реверс-прокси.
В логах и банах фигурирует адрес прокси. Vaultwarden берёт адрес клиента из заголовка, имя которого задаёт IP_HEADER (по умолчанию X-Real-IP). Не передали заголовок — во всех попытках входа в журнале будет 172.18.0.1, и fail2ban либо не банит никого, либо банит шлюз Docker и выносит всех разом. Лечится строкой proxy_set_header X-Real-IP $remote_addr; в Nginx, а за Cloudflare — IP_HEADER=CF-Connecting-IP.
И мелочь, которую регулярно принимают за поломку: иконки сайтов Vaultwarden тянет сам, наружу — без исходящего HTTP их не будет, а ICON_BLACKLIST_NON_GLOBAL_IPS=true по умолчанию отсекает внутренние адреса. Это косметика, на работу хранилища не влияет.
Вход и 2FA: неверный код, часы и потерянный доступ
Invalid TOTP code при заведомо правильном коде. Код действует 30 секунд, и допустимый разбег — примерно один шаг. Расхождение часов на минуту уже даёт стабильный отказ, а контейнер берёт время у хоста, так что чинить надо систему:
timedatectl # ждём "System clock synchronized: yes"
timedatectl set-ntp true
chronyc tracking | grep -E 'System time|Leap'
Типичный сценарий — виртуалка, поднятая из снапшота или мигрировавшая между гипервизорами: часы уезжают на минуты. В журнале Vaultwarden при этом мелькают предупреждения про time drift.
Пасскей или ключ безопасности перестал работать. В консоли браузера — SecurityError: The relying party ID is not a registrable domain suffix of, nor equal to the current domain. WebAuthn привязан к домену из переменной DOMAIN, и тот обязан совпадать с адресом в строке браузера дословно — со схемой и без завершающего слэша. Сменили домен или ходите по IP: ключ придётся регистрировать заново, перенести его нельзя.
Потеряли второй фактор. Через панель: /admin → пользователь → снять двухфакторную аутентификацию. Без панели — только напрямую в базе, с копией и остановленным контейнером:
docker compose stop vaultwarden
sqlite3 /opt/vaultwarden/vw-data/db.sqlite3 \
"DELETE FROM twofactor WHERE user_uuid=(SELECT uuid FROM users WHERE email='user@example.com');"
docker compose start vaultwarden
«Разлогинило всех разом». После восстановления из бэкапа или переноса клиенты требуют повторного входа, а в логе идут ошибки вида Invalid claim. Причина — потерянные ключи подписи токенов (rsa_key.pem и соседние файлы в /data): без них старые сессии недействительны. Это не потеря данных, а следствие неполной копии: в каталоге данных нужны не только база и вложения.
Ещё два симптома, похожих на поломку входа. Ответ 429 после серии попыток — это LOGIN_RATELIMIT_SECONDS и LOGIN_RATELIMIT_MAX_BURST, они работают как задумано. А Registration not allowed or user already exists при попытке завести коллегу означает SIGNUPS_ALLOWED=false: приглашайте через организацию с INVITATIONS_ALLOWED=true и настроенным SMTP, а не открывайте регистрацию заново.
Какой сервер под Vaultwarden брать в MAATRIX
Половина ошибок из этой статьи — не про Vaultwarden, а про то, как его ставили: тома, права на каталог, занятые порты, переменные, не доехавшие до процесса. Приложения из каталога apps.maatrix.io разворачиваются на сервере автоматически при заказе, и эти грабли туда не попадают: том смонтирован, владелец каталога согласован с процессом, порт свободен, переменные лежат в отдельном файле окружения. Автоустановка работает на Ubuntu и Debian, доступы появляются в личном кабинете, в разделе «Доступ». Команды выше нужны для диагностики, а не для установки.
Минимум: 1 vCPU, 1 ГБ RAM, 20 ГБ NVMe. Хватает команде до двух-трёх десятков человек: контейнер держится в районе 40–60 МБ, ещё 150–200 МБ уходит на демон Docker и Nginx. Ограничения честные и прямо связаны с ошибками выше. Своп на 1–2 ГБ включайте сразу, иначе oom=true из второй секции поймается на первой же серии попыток входа в /admin. И следите за диском: каждое обновление оставляет старый образ, а на заполненном диске SQLite падает с database or disk is full. Привычка — docker image prune -f после успешного обновления.
Комфортный вариант: 2 vCPU, 2–4 ГБ RAM, 40–60 ГБ NVMe. Разница не в скорости входа, она не изменится, а в том, что помещается рядом: копии базы за месяц, место под резервную копию перед обновлением, fail2ban, Uptime Kuma и вторая копия контейнера на другом порту — проверить новую версию до того, как её увидят пользователи.
Локация — Великобритания, Лондон. Скорость здесь ни при чём: синхронизация хранилища — килобайты JSON, лишние 30 мс не заметит никто. Аргументы другие: европейская юрисдикция и соседство с GDPR, чистый IP без чужой репутации, прямой доступ к европейскому push-ретранслятору bitwarden.eu. RTT из Москвы — 45–55 мс, из Берлина и Амстердама единицы миллисекунд. Если в хранилище персональные данные российских сотрудников и вы под 152-ФЗ, берите российскую локацию. Оплата — картами российских банков, по СБП, криптовалютой или токеном MAAT.
Трезвая оговорка: автоустановка снимает ошибки развёртывания, но не отменяет вашей части — домен и сертификат, закрытие регистрации, копия базы на другую машину и своевременное обновление под новые клиенты. Эти четыре пункта дают почти все инциденты, которые потом приходится разбирать.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть VaultwardenОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Обновил контейнер, и клиенты начали отдавать «An error has occurred». Откатиться на прошлый тег?
Не выйдет: миграции схемы базы односторонние, и откат после них не поддерживается. Сравните версии через curl -s https://vault.example.com/api/config | jq -r '.server.version' и версию расширения — скорее всего, двигаться надо вперёд, к сборке под ваши клиенты (1.37.2 для клиентов 2026.8.0+), а не назад.
Правлю переменные, перезапускаю — ничего не меняется. Где смотреть?
По порядку: docker exec vaultwarden printenv | grep ИМЯ (переменная вообще доехала?), затем опечатка в имени — незнакомые имена Vaultwarden молча игнорирует, затем cat vw-data/config.json — сохранённые панелью настройки перекрывают окружение. И пересоздавайте контейнер через docker compose up -d --force-recreate.
Как за минуту понять, кто виноват — Vaultwarden или Nginx?
Постучитесь в контейнер напрямую, минуя прокси: curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/alive. Код 200 изнутри при ошибке снаружи означает, что сервис исправен и разбираться надо с прокси и сертификатом.
Нужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.