SSD Nodes Learn 🎉 VPS від $5.50/міс
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-13

Резервне копіювання та відновлення Vaultwarden на VPS

Дізнайтеся, як копіювати активну базу Vaultwarden через sqlite3 .backup, зберегти attachments, config.json і rsa_key та перевірити відновлення.

Що має містити резервна копія Vaultwarden

Резервна копія Vaultwarden — це копія всієї папки даних. Базу даних у ній потрібно копіювати правильно. Використовуйте sqlite3 db.sqlite3 ".backup out.sqlite3" замість cp, оскільки звичайне копіювання бази даних під час запису може створити файл, який не відкриється. Також збережіть файли, що містяться поруч із нею. Саме про них часто забувають.

У Docker-інсталяції папка даних — це каталог, змонтований у /data. Це може бути шлях на хості або іменований том. Різниця між bind mount і іменованими томами визначає, де саме на диску зберігаються дані вашого сховища. Ось що містить ця папка.

  • db.sqlite3: усі облікові записи, усі елементи сховища, усі папки та всі організації. Втрата цього файлу означає втрату сховища.
  • db.sqlite3-wal і db.sqlite3-shm: журнал попереднього запису (WAL) та його індекс у спільній пам’яті. Нові записи зберігаються тут, доки SQLite не об’єднає їх з основним файлом.
  • attachments/: файли, прикріплені користувачами до елементів сховища, у зашифрованому вигляді, в окремому каталозі для кожного елемента.
  • sends/: файли, пов’язані з посиланнями Bitwarden Send.
  • config.json: усі налаштування, збережені на сторінці адміністрування.
  • rsa_key.pem, а в старих інсталяціях також rsa_key.der і rsa_key.pub.der: ключ, яким підписуються токени входу.
  • icon_cache/: завантажені піктограми вебсайтів. Це єдиний каталог, який можна не копіювати, оскільки Vaultwarden завантажує ці піктограми повторно за потреби.

Чи безпечна база даних Vaultwarden? Що насправді містить файл

На це відповідають дві команди. Ви можете виконати обидві прямо зараз.

sudo apt update && sudo apt install -y sqlite3
sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select email from users;"
sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select name from ciphers limit 1;"

Перша виводить email-адреси ваших користувачів у відкритому вигляді. Друга виводить назву одного елемента. Результат має такий вигляд:

2.k9Qw1nQ0y7Yy2Xw==|E1r0J3l5s7d9f1g3h5j7k9==|Lm4nOp6qRs8tUv0wXy2zAb4cDe6fGh8i=

Назви елементів, імена користувачів, паролі та нотатки клієнт шифрує до їх надсилання на сервер, тому сервер зберігає зашифровані дані, які не може прочитати. Префікс 2. позначає тип шифрування Bitwarden. Після нього йдуть вектор ініціалізації (IV), зашифровані дані та MAC (код автентифікації повідомлення). Кожен компонент закодований у base64, а розділювачем між ними є |. Ключ для розшифрування виводиться з головного пароля облікового запису. Сам головний пароль ніколи не надходить на сервер у придатній для використання формі. Ця частина однакова для Vaultwarden і офіційного сервера, як докладно описано в порівнянні Vaultwarden і self-hosted Bitwarden.

Решта бази даних не зашифрована. Email-адреси, імена облікових записів, підказки до паролів і коди відновлення двофакторної автентифікації зберігаються у відкритому вигляді разом із метаданими, такими як час створення та організація, якій належить елемент. Тому сам файл резервної копії є секретом. Будь-хто, хто отримає цей файл, дізнається, хто є вашими користувачами, і зможе атакувати зашифровані дані офлайн із максимальною швидкістю, яку забезпечує його обладнання. Саме тому подальші правила зберігання такі: копію потрібно зашифрувати до того, як вона залишить сервер.

Чому копіювання db.sqlite3 під час роботи Vaultwarden не є резервною копією

Vaultwarden за замовчуванням використовує SQLite у режимі WAL (ENABLE_DB_WAL=true). Операція запису спочатку потрапляє до db.sqlite3-wal, і лише контрольна точка переносить її до db.sqlite3. Якщо скопіювати лише db.sqlite3, ви отримаєте стан бази даних на момент останньої контрольної точки. Тому пароль, збережений десять хвилин тому, може бути відсутнім у вашому архіві, і система не повідомить про це.

Копіювання всіх трьох файлів за допомогою cp також не вирішує проблему. Копії створюються в дещо різні моменти часу. Тому збережений WAL може містити версії сторінок, які вже не відповідають збереженому основному файлу. Потім SQLite відновлює один файл на основі іншого, і результат є некоректним. Ви виявите це значно пізніше:

Error: database disk image is malformed

.backup усуває цю проблему, оскільки використовує SQLite Online Backup API. У документації SQLite цей API описано як спосіб копіювання бази даних, яка може активно використовуватися. Він читає сторінки під блокуванням на читання та починає операцію спочатку, якщо записувач змінює файл під час копіювання. Тому на диск записується узгоджений стан бази даних на один момент часу.

Створіть копію бази даних за допомогою sqlite3 .backup

sudo apt update && sudo apt install -y sqlite3
sudo install -d -m 700 /var/backups/vaultwarden
OUT=/var/backups/vaultwarden/db-$(date '+%Y%m%d-%H%M').sqlite3
sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 ".backup '$OUT'"
sudo sqlite3 "$OUT" "PRAGMA integrity_check;"

Остання команда виводить ok в окремому рядку. Будь-який інший результат означає, що копія непридатна. Не залишайте її та не видаляйте попередню копію. Уся послідовність виконується на працюючому сервері, тому користувачі не виходять із системи, а контейнери не перезапускаються.

Інструмент sqlite3 не входить до контейнера Vaultwarden. Образ побудовано на debian:trixie-slim із ca-certificates, curl, libmariadb3, libpq5 і openssl, тому docker exec vaultwarden sqlite3 ... завершується помилкою:

exec: "sqlite3": executable file not found in $PATH

Натомість виконайте його на хості для змонтованого шляху. Саме це роблять наведені вище команди. Якщо дані зберігаються в іменованому томі, docker volume inspect <name> виводить шлях на хості під /var/lib/docker/volumes/.

Починаючи з версії 1.32.1, Vaultwarden також має власну команду резервного копіювання. На сервері виконайте:

docker exec -it vaultwarden /vaultwarden backup

Команда запускає VACUUM INTO і записує db_YYYYMMDD_HHMMSS.sqlite3 до каталогу даних. Це має два наслідки. Копія зберігається поруч з оригіналом на тому самому диску, тому це лише етап підготовки, а не резервна копія. Крім того, команда працює лише із SQLite: для MariaDB або PostgreSQL вона завершується помилкою The database type is not SQLite. Backups only works for SQLite databases.

Файли, про які часто забувають

attachments/ містить зашифровані дані під непрозорими іменами. Рядок бази даних для кожного вкладення містить його зашифроване ім’я файлу та ключовий матеріал, потрібний клієнту для розшифрування файлу. Без бази даних вкладення перетворюються на непридатний для читання шум, а без вкладень у базі даних у користувачів залишаються елементи, завантаження яких завершується помилкою. Створюйте резервні копії обох компонентів під час одного запуску.

config.json містить усі дані, збережені зі сторінки адміністратора, і його значення мають пріоритет над відповідними змінними середовища. Це може мати небажані наслідки: відновлений старий config.json непомітно перевизначає параметри у вашому compose-файлі, а сам файл є конфіденційним, оскільки може містити пароль SMTP і токен адміністратора. Зберігайте цей токен як рядок Argon2id PHC (password hashing competition), а не у відкритому вигляді. docker run --rm -it vaultwarden/server /vaultwarden hash створить такий рядок.

rsa_key.pem підписує JSON web tokens (JWT), які дають змогу клієнтам залишатися авторизованими. Якщо під час запуску файл відсутній, Vaultwarden генерує новий ключ. Тому всі токени, підписані старим ключем, перестають проходити перевірку, а всі клієнти виходять із системи. Вміст сховища Vault не втрачається, оскільки його зашифровано ключами, похідними від головного пароля. Відновлення файлу ключа запобігає масовому виходу з системи.

sends/ містить файли, доступні через посилання Send. Їхня відсутність порушує завантаження цих файлів і більше нічого.

Зведіть усе в один скрипт

#!/bin/bash
set -euo pipefail

DATA=/opt/vaultwarden/data
DEST=/var/backups/vaultwarden
STAMP=$(date '+%Y%m%d-%H%M%S')
STAGE=$(mktemp -d /tmp/vw-stage.XXXXXX)

install -d -m 700 "$DEST"
sqlite3 "$DATA/db.sqlite3" ".backup '$STAGE/db.sqlite3'"
test "$(sqlite3 "$STAGE/db.sqlite3" 'PRAGMA integrity_check;')" = "ok"
cp -a "$DATA"/rsa_key* "$STAGE/"
for extra in config.json attachments sends; do
  if [ -e "$DATA/$extra" ]; then cp -a "$DATA/$extra" "$STAGE/"; fi
done
tar -C "$STAGE" -czf "$DEST/vw-$STAMP.tar.gz" .
chmod 600 "$DEST/vw-$STAMP.tar.gz"
rm -rf "$STAGE"
tar -tzf "$DEST/vw-$STAMP.tar.gz"

Збережіть його як /usr/local/sbin/vw-backup.sh, зробіть його виконуваним за допомогою chmod 700 і запустіть від імені root. Рядок test виконує основну роботу: sqlite3 завершується з кодом 0, навіть коли PRAGMA integrity_check повідомляє про пошкодження, тому порівняння виводу з ok перетворює пошкоджену копію на помилку скрипту. Після цього set -euo pipefail зупиняє всі операції, замість того щоб дозволити tar створити коректний архів навколо пошкодженої бази даних.

Остання команда tar -tzf показує, що саме було збережено. Уперше уважно перевірте її вивід. Переконайтеся, що присутні ./db.sqlite3, ./rsa_key.pem, ./config.json і ./attachments/, а ./db.sqlite3-wal відсутній. Запускайте цей скрипт щоночі за допомогою служби та таймера systemd, а не cron, якщо потрібні journalctl вивід і unit, який повідомляє про помилку.

Перевірте резервну копію, відновивши її в тимчасовий каталог

Неперевірена резервна копія — це лише припущення. Відновлення в тимчасовий каталог займає хвилину й не змінює робочі дані.

sudo install -d -m 700 /tmp/vw-check
sudo tar -C /tmp/vw-check -xzf /var/backups/vaultwarden/vw-20260805-030000.tar.gz
ls -l /tmp/vw-check
sudo sqlite3 /tmp/vw-check/db.sqlite3 "PRAGMA integrity_check;"
sudo sqlite3 /tmp/vw-check/db.sqlite3 "select count(*) from users;"
sudo sqlite3 /tmp/vw-check/db.sqlite3 "select count(*) from ciphers;"
sudo du -sh /tmp/vw-check/attachments

Важливі чотири результати. integrity_check виводить ok. Кількість користувачів відповідає відомій вам кількості облікових записів. Кількість шифротекстів близька до робочого значення з sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select count(*) from ciphers;" і ніколи не дорівнює нулю для сховища, яке використовується. Каталог вкладень має приблизно очікуваний розмір. Цю перевірку можна пропустити, якщо ніхто не завантажує вкладення. Потім виконайте sudo rm -rf /tmp/vw-check, оскільки тепер цей каталог містить другу копію всіх даних.

Під час відновлення будь-якого каталогу з даними, скопійованого вручну, дотримуйтеся одного правила: перед запуском сервера видаліть db.sqlite3-wal і db.sqlite3-shm. Інакше SQLite спробує відновити відновлену базу даних за допомогою журналу, який належить іншій її копії. Це пошкодить базу даних, яка спочатку була справною. Архіви, створені наведеним вище скриптом, ніколи не містять цих файлів, оскільки .backup записує одну повну базу даних.

Відновлення на сервер

Ці команди потрібно виконувати на власному сервері, коли контейнер зупинено. Vaultwarden не повинен записувати дані під час змін у папці даних.

cd /opt/vaultwarden
docker compose stop vaultwarden
sudo mv data data.old.$(date '+%Y%m%d-%H%M%S')
sudo install -d -m 700 data
sudo tar -C data -xzf /var/backups/vaultwarden/vw-20260805-030000.tar.gz
sudo chown -R root:root data
docker compose start vaultwarden
docker compose logs --tail 20 vaultwarden

У chown потрібно вказати користувача, від імені якого працює контейнер. Стандартний образ працює від імені root, тому root:root є правильним значенням, якщо тільки у файлі compose не задано user:. У такому разі використовуйте відповідні uid і gid. Якщо сервер не може записувати до папки даних, сторінка входу відкривається, але кожен запит завершується помилкою. У журналах це буде зазначено.

Успішний запуск завершується рядком Rocket:

[INFO] Rocket has launched from http://0.0.0.0:80

Потім увійдіть через браузер, відкрийте один елемент і завантажте одне вкладення. Якщо вхід працює, але завантаження вкладень завершується помилкою, це означає, що архів містив базу даних, але не attachments/. Залиште data.old.*, доки не перевірите все, а потім видаліть його. Відновлення попереднього стану виконується тими самими трьома кроками, але каталоги потрібно поміняти місцями.

Якщо ваші шляхи відрізняються від наведених тут, у посібнику зі встановлення Vaultwarden на VPS показано файл compose, на який розраховані ці команди.

Куди не слід зберігати резервну копію

  • Не на тому самому диску, що й папка з даними. Одна відмова тому призведе до втрати обох копій, як і один rm -rf за неправильним шляхом.
  • Не на тому самому сервері, навіть на другому томі. Зловмисник, який отримав доступ до root, у тому самому сеансі отримає доступ і до резервних копій.
  • Не в object storage без шифрування, оскільки архів містить адреси електронної пошти, підказки до паролів, коди відновлення та шифротекст сховища, який можна атакувати офлайн.
  • Не лише у snapshots вашого провайдера. Вони швидко відновлюються, тому їх варто мати, але вони перебувають у тому самому обліковому записі, що й сервер, тому проблема з обліковим записом призведе до їхньої втрати разом із сервером.

Для offsite-копії підходить restic, оскільки репозиторій restic шифрується на машині ще до завантаження. На сервері:

sudo apt install -y restic
export RESTIC_REPOSITORY=s3:https://s3.example.com/vaultwarden-backups
export RESTIC_PASSWORD_FILE=/root/.restic-password
restic init
restic backup /var/backups/vaultwarden --tag vaultwarden
restic snapshots --tag vaultwarden
restic forget --tag vaultwarden --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

Вкажіть restic каталог з архівами, а не папку з робочими даними, щоб завантажувалася узгоджена копія, яку ви вже перевірили. Зберігайте пароль репозиторію не на тому сервері, який він захищає: якщо втратити цей пароль, snapshots неможливо прочитати — це зроблено навмисно. Якщо сховище це підтримує, надайте серверу облікові дані, які дають змогу записувати, але не видаляти дані, щоб у разі компрометації сервера зловмисник не міг стерти історію резервних копій. У матеріалі Налаштування резервних копій restic на VPS повністю описано репозиторій і розклад, а в матеріалі restic у порівнянні з BorgBackup розглянуто вибір, якщо ви ще його не зробили.

Перевіряйте відновлення за розкладом

Оберіть один день на місяць. Завантажте найновіший snapshot у тимчасовий каталог за допомогою restic restore latest --tag vaultwarden --target /tmp/vw-check, виконайте той самий PRAGMA integrity_check, перевірте ті самі кількості рядків і запишіть дату та результати. Резервна копія, з якої ніхто не виконував відновлення протягом шести місяців, має невідомий стан. Її стан з’ясовується під час аварії, коли робити це найгірше.

Раз на рік виконуйте повну перевірку. Запустіть другий контейнер Vaultwarden на вільному порту з відновленим каталогом даних і увійдіть за допомогою реального облікового запису. Це перевіряє весь шлях використання майстер-пароля, чого не може зробити жодна перевірка кількості рядків. restic check --read-data-subset=10% за тим самим розкладом підтверджує, що збережені дані можна прочитати, а не лише побачити в переліку.

FAQ

Чи можна скопіювати db.sqlite3 за допомогою cp, коли Vaultwarden працює?

Ні. Vaultwarden використовує SQLite у режимі WAL, тому останні зміни зберігаються в db.sqlite3-wal і ще не перенесені до db.sqlite3. cp лише основного файла непомітно втратить ці зміни, а копіювання двох файлів окремо може створити неузгоджену пару, що згодом спричинить Error: database disk image is malformed. Замість цього використовуйте sqlite3 /path/db.sqlite3 ".backup '/path/out.sqlite3'". Він застосовує SQLite Online Backup API і створює один узгоджений файл, поки сервер продовжує обслуговувати запити.

Чи потрібно зупиняти контейнер Vaultwarden, щоб створити резервну копію?

Ні. У цьому і полягає перевага .backup. Копіювати базу даних безпечно на запущеному сервері. Вкладення та файли Send записуються, коли користувач їх завантажує. Тому файл, доданий між копіюванням бази даних і tar, може не потрапити до архіву за цю ніч. У найгіршому разі ви втратите одне вкладення. Якщо кілька секунд простою для вас прийнятні, виконайте docker compose stop перед скриптом і docker compose start після нього. Це усуне навіть такий ризик.

Що станеться, якщо відновити дані без файлів rsa_key?

Vaultwarden створить новий ключ під час запуску. Цей ключ підписує JSON web tokens (JWT), які підтримують активність сеансів. Тому всі наявні токени перестануть проходити перевірку, а всі клієнти буде відключено. Користувачам доведеться ввійти знову. Вміст сховища не зміниться, оскільки він шифрується ключами, похідними від головного пароля кожного користувача, а не RSA-ключем. Відновіть rsa_key.pem разом з рештою каталогу даних, і ніхто не помітить відновлення.

Чи безпечно завантажувати архів резервної копії до object storage без додаткового захисту?

Ні. Назви елементів, паролі та нотатки є шифротекстом, але адреси електронної пошти, назви облікових записів, підказки до паролів і коди відновлення двофакторної автентифікації зберігаються в базі даних у відкритому вигляді. Зловмисник, який отримав автономний доступ, може підбирати шифротекст у власному темпі. Зашифруйте архів перед передаванням з машини. Репозиторій restic робить це автоматично, а gpg --symmetric --cipher-algo AES256 vw-20260805-030000.tar.gz створює один зашифрований файл, який можна передати до будь-якого сховища.

Як створювати резервні копії Vaultwarden у PostgreSQL або MariaDB?

Інструкції для SQLite тут не застосовуються, а вбудована команда завершується помилкою The database type is not SQLite. Backups only works for SQLite databases. Створіть дамп бази даних за допомогою штатного інструмента: pg_dump або mysqldump. Усі інші правила залишаються незмінними. Дамп має входити до одного архіву разом із attachments/, sends/, config.json і файлами rsa_key. Створіть їх під час одного запуску, зашифруйте архів і збережіть його не на тому самому сервері, який його створив.