Vaultwarden не синхронизируется: причины и решение
Пароль сохранён в браузере на ноутбуке, а на телефоне его нет ни через минуту, ни через час. Или наоборот: расширение подхватывает всё мгновенно, а десктопный клиент показывает вчерашний сейф. Почти всегда данные на сервере уже лежат — сломан не обмен, а канал уведомлений, которым сервер говорит клиенту «сходи обновись». Разберём каналы по очереди, с командами проверки и точными текстами ошибок.
Содержание
- Как Vaultwarden синхронизируется на самом деле
- WebSocket и /notifications/hub: чаще всего виноват старый конфиг
- Телефон не догоняет: мобильным нужен push-релей
- Клиент не доходит до сервера: сертификат, DOMAIN и часы
- Изменения не доезжают до сервера: revision-date стоит на месте
- Всё работает, но не у всех: организации и старые сессии
- Какой сервер под Vaultwarden брать в MAATRIX
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Как Vaultwarden синхронизируется на самом деле
Половину вопросов снимает одно: данные ходят только одним способом — клиент делает GET /api/sync?excludeDomains=true и забирает весь сейф целиком. Перед этим он спрашивает дешёвую метку: GET /api/accounts/revision-date возвращает голое число, Unix-время последнего изменения аккаунта в миллисекундах.
# access_token видно в devtools, во вкладке Network у любого запроса к /api
curl -s https://vault.example.com/api/accounts/revision-date \
-H "Authorization: Bearer $TOKEN"
# 1756374764000
Это точка отсечки: число не выросло — сервер изменение не принял, и пляски с вебсокетами бесполезны; число растёт, а второй клиент не обновился — виноват канал уведомлений. Каналов «пинка» ровно два, и они не заменяют друг друга. WebSocket на /notifications/hub обслуживает веб-хранилище, десктоп и расширение браузера. Push-релей Bitwarden обслуживает iOS и Android: эти клиенты WebSocket не используют вообще, никогда, и включать его нужно отдельно.
Тест на тридцать секунд — обновить «сломанный» клиент вручную: «Settings → Sync → Sync vault now» в расширении и десктопе, потянуть список вниз в мобильном, перезагрузить страницу в вебе.
| Что происходит | Где искать |
|---|---|
| После ручного Sync данные появились | Канал уведомлений: вебсокет или push |
| Ручной Sync ничего не меняет, ошибок нет | Изменения не доехали до сервера |
| Ручной Sync выдаёт ошибку или разлогинивает | TLS, DOMAIN, часы |
| Видно не все записи организации | Статус участника, а не синхронизация |
На сервере откройте /admin → Diagnostics: там сверяются часы сервера и браузера, DOMAIN с адресом захода, DNS, состояние WebSocket и доступность себя снаружи по HTTPS. Красная строка обычно и есть причина. Без авторизации работает curl -s https://vault.example.com/alive — вернёт время сервера строкой "2026-08-28T09:12:44.518Z" и докажет, что прокси доносит запросы до приложения.
WebSocket и /notifications/hub: чаще всего виноват старый конфиг
Начиная с версии 1.30.0 Vaultwarden отдаёт WebSocket через основной порт, на пути /notifications/hub. Отдельного порта 3012 больше нет, WEBSOCKET_ENABLED заменена на ENABLE_WEBSOCKET (по умолчанию true), WEBSOCKET_PORT игнорируется. Но в сети лежат мануалы 2021–2023 годов, где /notifications/hub проксируется на 127.0.0.1:3012 — на свежей сборке там никто не слушает. В консоли браузера (F12) это выглядит дословно так:
WebSocket connection to 'wss://vault.example.com/notifications/hub?access_token=eyJ...'
failed: Error during WebSocket handshake: Unexpected response code: 502
[SignalR] Error: Failed to start the connection: Error: WebSocket failed to connect.
Рабочий фрагмент для Nginx — оба location идут на один и тот же адрес:
map $http_upgrade $connection_upgrade { default upgrade; '' close; }
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /notifications/hub {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
}
Директива map обязана лежать в контексте http, а не внутри server, иначе nginx -t ответит "map" directive is not allowed here. Проверить апгрейд можно, не поднимая клиент:
curl -i -N -o /dev/null -D - \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: $(openssl rand -base64 16)" \
https://vault.example.com/notifications/hub 2>&1 | head -3
Ждём HTTP/1.1 101 Switching Protocols; токен мы не передавали, поэтому Vaultwarden тут же закроет соединение — важна только первая строка. 502 Bad Gateway — проксирование в несуществующий 3012. 400 или 200 — заголовки Upgrade и Connection не дошли, запрос обработал location /. Прочие грабли прокси — в статье про Nginx как реверс-прокси.
Caddy и Traefik апгрейд проксируют сами. За Cloudflare вебсокет разрешён на всех тарифах, но «Bot Fight Mode» и жёсткие правила WAF отдают клиенту challenge-страницу вместо JSON.
И то, что экономит часы отладки: заблокированный клиент уведомления не слушает. Расширение и десктоп рвут соединение при блокировке сейфа и подключаются заново после ввода мастер-пароля — ждать мгновенного обновления от залоченного клиента бессмысленно.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть VaultwardenТелефон не догоняет: мобильным нужен push-релей
Самая «непонятная» жалоба: в браузере всё мгновенно, а на телефоне записи появляются только после того, как потянешь список вниз. Так и должно быть, пока не настроен push: мобильные клиенты получают уведомления через сервис самой Bitwarden, и self-hosted сервер обязан там зарегистрироваться.
На bitwarden.com/host получаете Installation ID и Installation Key — ключ выдаётся один раз, сохраните сразу. Там же выбирается регион, US или EU, и он должен совпасть с адресами в конфиге:
PUSH_ENABLED=true
PUSH_INSTALLATION_ID=1f8bb2a4-0e5c-4d1b-9f77-2c0a1f5b7e33
PUSH_INSTALLATION_KEY=xxxxxxxxxxxxxxxxxxxx
# Только для региона Europe:
PUSH_RELAY_URI=https://push.bitwarden.eu
PUSH_IDENTITY_URI=https://identity.bitwarden.eu
По умолчанию используется американский релей (push.bitwarden.com, identity.bitwarden.com), при US-регистрации две последние строки не нужны. Перепутанный регион — типичная ошибка: ключ формально верный, но чужой релей его не признаёт.
После перезапуска смотрите журнал: docker logs --tail 200 vaultwarden 2>&1 | grep -i push. Запросы к identity.bitwarden.* с кодом 400 или 401 означают неверную пару ID/ключ либо не тот регион. Полная тишина при PUSH_ENABLED=true обычно значит, что исходящий 443 закрыт фаерволом — проверьте curl -sI https://push.bitwarden.eu.
Два подводных камня при верном конфиге:
- Устройство регистрируется при входе. Телефоны, залогиненные до включения
PUSH_ENABLED, токен не отправляли. На каждом нужно выйти из аккаунта и войти заново, иначе уведомления не придут никогда. - Экономия батареи на Android. Без разрешения «Unrestricted» система придерживает уведомления, и синхронизация выглядит случайной. На оболочках Xiaomi, Samsung и Huawei ограничение включено по умолчанию.
Честно про приватность: релей — третья сторона. Через него уходит идентификатор устройства и факт изменения сейфа; содержимое записей не передаётся и не может — оно зашифровано ключом, которого у Bitwarden нет. Но требование «сервер не общается с внешними сервисами вообще» означает push выключенным и ручное обновление телефона. Это осознанный размен, а не поломка.
И регулярное: адрес сервера в приложении задаётся до входа, через шестерёнку на экране логина. Заполняйте только «Server URL» — опечатка в отдельных полях «API URL» и «Identity URL» даёт успешный вход и мёртвую синхронизацию.
Клиент не доходит до сервера: сертификат, DOMAIN и часы
Симптомы этой группы громче: ошибка при входе, вылет из аккаунта, 401 на каждом обновлении.
Сертификат. Клиенты Bitwarden требуют TLS, доверенный операционной системой. Самоподписанный мобильное приложение не примет: на Android в деталях ошибки будет Trust anchor for certification path not found, на iOS — The certificate for this server is invalid. Ещё коварнее неполная цепочка: настольные браузеры докачивают промежуточный сертификат сами, а телефон нет. Отсюда классика «на компьютере синхронизируется, на телефоне нет».
echo | openssl s_client -connect vault.example.com:443 \
-servername vault.example.com 2>/dev/null | grep 'Verify return code'
Verify return code: 21 или unable to get local issuer certificate в ответе curl — цепочка неполная: в Nginx должен быть указан fullchain.pem, а не cert.pem. Выпуск и автопродление — Let's Encrypt SSL на VPS.
Переменная DOMAIN. Полный адрес со схемой и без слеша на конце: DOMAIN=https://vault.example.com. От неё зависит RP ID для двухфакторной аутентификации по WebAuthn: несовпадение ломает вход по аппаратному ключу, а без входа нет токена — и клиент честно показывает «не синхронизируется».
Часы. Vaultwarden выдаёт JWT с ограниченным сроком жизни, и расхождение времени ломает обновление токена: клиент ловит 401 на /api/sync и вылетает из аккаунта.
curl -s https://vault.example.com/alive; echo; date -u +%Y-%m-%dT%H:%M:%SZ
timedatectl status | grep -E 'System clock|NTP service'
Расхождение больше пары минут лечится командой timedatectl set-ntp true либо установкой chrony с проверкой chronyc tracking. Контейнер берёт время у хоста — чинить надо на хосте. Оговорка: на OpenVZ и LXC часы задаёт нода провайдера, своим NTP их не поправить, и регулярный уход времени там повод переехать на KVM.
Изменения не доезжают до сервера: revision-date стоит на месте
Если ручной Sync не помогает, а число из /api/accounts/revision-date после сохранения записи не выросло, канал уведомлений ни при чём. Клиент показывает лаконичное An error has occurred, подробности — только в журнале сервера.
База заблокирована. Vaultwarden по умолчанию живёт на SQLite: /data/db.sqlite3 плюс db.sqlite3-wal и db.sqlite3-shm. Если /data смонтирован по NFS, SMB или sshfs, файловые блокировки работают неверно и запись падает с DatabaseError(Unknown, "database is locked") — держите данные на локальном NVMe. При десятке активных пользователей поднимите DATABASE_MAX_CONNS=20 и DATABASE_TIMEOUT=60: утренний вход всей команды даёт всплеск параллельных /api/sync. Прочие ошибки каталога /data — в материале про частые ошибки Vaultwarden.
Кэширующий CDN. Обратная ситуация: сервер запись принял, revision-date растёт, а клиент видит старое. Cloudflare с правилом «Cache Everything» отдаёт ответ из кэша, и клиент получает вчерашний сейф с кодом 200.
curl -sI https://vault.example.com/api/accounts/revision-date | grep -i cf-cache
Заголовок cf-cache-status: HIT — приговор; лечится правилом Bypass Cache для /api/*, /identity/* и /notifications/*. То же бывает на корпоративном прокси с агрессивным кэшем.
Для разбора инцидента временно включите LOG_LEVEL=debug и LOG_FILE=/data/vaultwarden.log. Предупреждение: в debug-журнал попадают адреса почты пользователей — после диагностики верните info и удалите файл.
Всё работает, но не у всех: организации и старые сессии
Отдельный класс жалоб, к синхронизации отношения не имеющий, хотя выглядит один в один: у сотрудника пустой сейф или в нём нет общей коллекции.
Приглашение не подтверждено. У участника организации три состояния: Invited → Accepted → Confirmed. Человек принял приглашение и остановился на Accepted — администратор обязан нажать Confirm в списке участников. До этого пользователь не получает ключ организации, и записи коллекций для него физически нерасшифровываемы. Пересинхронизация не поможет: сначала подтверждение, потом Sync.
Приглашения не уходят. При SIGNUPS_ALLOWED=false и INVITATIONS_ALLOWED=true письмо шлёт сам Vaultwarden, и без настроенного SMTP (SMTP_HOST, SMTP_FROM, SMTP_USERNAME, SMTP_PASSWORD, SMTP_SECURITY=starttls) приглашение просто не создаётся. Кнопка тестового письма на /admin показывает дословную ошибку SMTP.
Смена мастер-пароля. Остальные сессии деавторизуются, и до нового входа устройство показывает старый локальный кэш — снаружи неотличимо от «перестало синхронизироваться». После ротации ключа шифрования владельцу придётся заново подтвердить всех участников организации.
Какой сервер под Vaultwarden брать в MAATRIX
Vaultwarden написан на Rust и нетребователен всерьёз: в простое процесс держит примерно 35–70 МБ RSS, всплеск даёт только массовый /api/sync при одновременном утреннем входе. Ресурсы съедают соседи — Nginx, certbot, Docker, бэкапы, — а не сам сейф.
Минимум честно рабочий: 1 vCPU, 1 ГБ RAM, 20 ГБ NVMe. Хватает семье или команде из 3–5 человек на SQLite, и ограничивает тут место под вложения, а не память. На 512 МБ Vaultwarden запустится, но обновление контейнера уронит его по OOM — в dmesg -T | grep -i oom будет Out of memory: Killed process ... vaultwarden. Гигабайт снимает риск целиком.
Комфортный вариант: 2 vCPU, 4 ГБ RAM, 40–60 ГБ NVMe. Помещается всё сразу: Vaultwarden, реверс-прокси с TLS, PostgreSQL вместо SQLite (он убирает весь класс блокировок из пятого раздела), ежедневные снимки базы и запас под вложения. Это рабочая конфигурация на 10–50 человек: перезапуск после обновления проходит незаметно, узкое место — диск и параллельные Sync. Организации с внешней СУБД и мониторингом закладывайте 4–8 ГБ: один PostgreSQL просит около гигабайта сверху.
Локация — Великобритания, Лондон. Для менеджера паролей это удачный компромисс: RTT из Москвы до лондонских площадок обычно укладывается в 45–70 мс, из ЕС — единицы миллисекунд, а обмен маленький, так что интерфейс ощущается мгновенным. Британский узел удобен коротким маршрутом до релея push.bitwarden.eu, стабильной валидацией Let's Encrypt и юрисдикцией внутри GDPR-контура. Если все пользователи и обязательства по 152-ФЗ в России — берите RU-локацию: пинг минимальный, а push продолжит работать, он ходит обычным HTTPS.
Ставить руками ничего не придётся: Vaultwarden есть в каталоге apps.maatrix.io и разворачивается автоматически при заказе — на Ubuntu и Debian. Доступы появляются в личном кабинете, в разделе «Доступ». Вам остаётся направить домен на выданный IP, выпустить сертификат, прописать DOMAIN и заглянуть на /admin/diagnostics. Дальше закройте лишнее (ufw allow 22/tcp, ufw allow 443/tcp, ufw deny 8080/tcp), выставьте SIGNUPS_ALLOWED=false и настройте резервное копирование сейфа: база паролей без бэкапа опаснее её отсутствия. Установка с нуля — Vaultwarden на VPS. Оплата картой российского банка, по СБП, криптой или токеном MAAT.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть VaultwardenОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Обязательно ли включать WebSocket, чтобы Vaultwarden работал?
Нет. Без /notifications/hub сейф полностью функционален, изменения просто приезжают при разблокировке клиента или ручном Sync. Сокет — удобство, а не условие работоспособности.
Почему на компьютере всё синхронизируется, а на телефоне нет?
Либо не настроен push-релей (PUSH_ENABLED, Installation ID и Key с bitwarden.com/host) — мобильные клиенты WebSocket не используют вовсе; либо у сертификата неполная цепочка, которую браузер докачивает сам, а телефон нет.
Как понять, что изменение вообще дошло до сервера?
Запросить GET /api/accounts/revision-date с токеном до и после сохранения записи. Число выросло — сервер принял данные, чинить надо канал уведомлений; не выросло — смотрите журнал Vaultwarden и раздел про SQLite.
Нужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.