SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-26

VaultwardenのVPSバックアップと復元方法

Vaultwardenのdataフォルダーを安全にバックアップする方法を解説します。sqlite3 .backupで稼働中のSQLiteをコピーし、attachments、config.json、rsa_keyファイルを含めて復元まで検証します。

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, August 5, 2026.

Vaultwarden のバックアップに含めるもの

Vaultwarden のバックアップは、data フォルダー全体のコピーです。その中のデータベースは、正しい方法でコピーする必要があります。書き込み中のデータベースを単純にコピーすると、開けないファイルになることがあるため、cp ではなく sqlite3 db.sqlite3 ".backup out.sqlite3" を実行します。そのうえで、データベースと同じ場所にあるファイルも保持します。このファイル群は、バックアップから漏れやすい部分です。

Docker でインストールしている場合、data フォルダーは /data にマウントした場所です。ホスト上のパスの場合もあれば、名前付きボリュームの場合もあります。bind mount と名前付きボリュームの違いによって、vault が実際に保存されるディスク上の場所が決まります。data フォルダーには次のものが含まれます。

  • db.sqlite3: すべてのアカウント、vault item、フォルダー、organization。これを失うと vault を失います。
  • db.sqlite3-wal と db.sqlite3-shm: write-ahead log(WAL)と、その共有メモリインデックス。SQLite が最近の書き込みをメインファイルに統合するまで、ここに保存されます。
  • attachments/: vault item にユーザーが添付したファイル。暗号化され、item ごとに 1 つのディレクトリに保存されます。
  • sends/: Bitwarden Send のリンク先にあるファイル。
  • config.json: admin page で保存したすべての設定。
  • rsa_key.pem、古いインストールでは rsa_key.der と rsa_key.pub.der も含む: ログイントークンの署名に使う key。
  • icon_cache/: ダウンロードした Web サイトのアイコン。Vaultwarden が必要に応じて再取得するため、このディレクトリだけはバックアップから除外できます。

Vaultwarden のデータベースは安全ですか?ファイルに実際に保存される内容

この点は、2 つのコマンドですぐに確認できます。

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;"

1 つ目のコマンドは、ユーザーのメールアドレスを平文で表示します。2 つ目のコマンドは、アイテム名を 1 件表示します。表示例は次のとおりです。

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

アイテム名、ユーザー名、パスワード、メモは、クライアントで暗号化してから送信されます。そのため、サーバーに保存されるのは、サーバーが読み取れない暗号文です。2. プレフィックスは Bitwarden の暗号化方式を示します。その後に、初期化ベクトル (IV)、暗号文、MAC (メッセージ認証コード) が続きます。各要素は base64 でエンコードされ、| で区切られます。復号に使う鍵はアカウントの master password から導出されます。master password が、利用可能な形式でサーバーに送られることはありません。Vaultwarden を実行する場合も公式サーバーを実行する場合も、この部分は同じです。詳しくは、Vaultwarden と self-hosted Bitwarden の比較で説明します。

データベースの残りの部分は暗号化されていません。メールアドレス、アカウント名、パスワードのヒント、2 要素認証のリカバリーコードは平文で保存されます。作成日時や、アイテムを所有する組織などのメタデータも同様です。そのため、バックアップファイル自体が秘密情報になります。これを入手した者は、ユーザーが誰であるかを把握できます。また、ハードウェアが許す速度で、暗号化されたデータをオフラインで攻撃できます。この事実が、後述するストレージのルールを決めます。コピーはサーバーから持ち出す前に暗号化します。admin token も同じ問題に関係します。self-hosted Vaultwarden の hardening 手順では、この 2 つをまとめて対策します。

Vaultwarden の実行中に db.sqlite3 をコピーしてもバックアップにならない理由

Vaultwarden はデフォルトで SQLite を WAL モードで実行します(ENABLE_DB_WAL=true)。書き込み内容はまず db.sqlite3-wal に記録され、チェックポイントが実行されたときにのみ db.sqlite3 へ反映されます。db.sqlite3 だけをコピーすると、最後のチェックポイント時点のデータベースしか取得できません。そのため、10 分前に保存したパスワードが、警告なしにアーカイブから欠落する可能性があります。

cp を使用して 3 つのファイルをすべてコピーしても解決しません。各ファイルはわずかに異なる時点でコピーされるため、保存した 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/ 配下のホストパスを表示できます。

Vaultwarden には、version 1.32.1 以降、独自のバックアップコマンドも用意されています。サーバー上で次を実行します。

docker exec -it vaultwarden /vaultwarden backup

このコマンドは VACUUM INTO を実行し、db_YYYYMMDD_HHMMSS.sqlite3 をデータフォルダーに書き込みます。ここから2点が分かります。コピーは同じディスク上で元のファイルの隣に作成されるため、これはステージング処理であり、まだバックアップではありません。また、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 token (JWT) に署名します。起動時にこのファイルがない場合、Vaultwarden は新しい鍵を生成します。そのため、古い鍵で署名されたすべてのトークンが検証に失敗し、すべてのクライアントがログアウトされます。Vault の内容はマスターパスワードから導出した鍵で暗号化されているため、この影響を受けずに残ります。鍵ファイルを復元すれば、一斉ログアウトを回避できます。

sends/ には、Send リンクの背後にあるファイルが格納されています。これがないと該当するダウンロードだけが失敗し、それ以外には影響しません。

全体を1つのスクリプトにまとめる

#!/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はPRAGMA integrity_checkが破損を報告しても終了ステータス 0 を返すため、出力をokと比較して、コピーの失敗をスクリプトの失敗として扱います。set -euo pipefailによってすべての処理が停止するため、tarが破損したデータベースを正常なアーカイブとしてまとめてしまうこともありません。

最後のtar -tzfで、実際に取得できた内容を一覧表示します。最初は必ず内容を確認してください。./db.sqlite3、./rsa_key.pem、./config.json、./attachments/が含まれていることと、./db.sqlite3-walが存在しないことを確認します。journalctlの出力と失敗を報告する unit が必要であれば、cron ではなくsystemd の service と timerを使って毎晩実行します。

バックアップを一時ディレクトリに復元して検証する

テストしていないバックアップは推測にすぎません。一時ディレクトリへの復元なら数分で完了し、稼働中のデータには一切触れません。

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

重要なのは 4 つの結果です。integrity_check は ok を出力します。ユーザー数は、把握しているアカウント数と一致します。暗号文の数は sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select count(*) from ciphers;" で確認できる稼働中の数に近く、使用中の vault で 0 になることはありません。attachments ディレクトリのサイズは想定値とおおむね一致します。誰も attachments をアップロードしない場合は、この確認を省略できます。続いて sudo rm -rf /tmp/vw-check を実行します。このディレクトリには、すべてのデータのコピーがもう 1 つ存在するためです。

手動でコピーしたデータディレクトリを復元するときは、必ず db.sqlite3-wal と db.sqlite3-shm を削除してからサーバーを起動してください。そうしないと SQLite は、別のコピーに属するログを使って復元したデータベースをリカバリしようとします。その結果、正常な状態で復元されたデータベースが破損します。上記のスクリプトが作成するアーカイブには、これらのファイルは含まれません。.backup が完全なデータベースを 1 つ書き出すためです。

サーバーへの復元

これらのコマンドは、コンテナを停止した状態で、自分のサーバー上で実行します。データフォルダーの内容を変更している間、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 として実行されるため、compose ファイルで user: を設定していない場合は root:root で問題ありません。user: を設定している場合は、その uid と gid を使用します。サーバーから書き込めないデータフォルダーを指定すると、ログインページへのすべてのリクエストが失敗します。ログにもその原因が記録されます。

正常に起動すると、最後に次の Rocket 行が表示されます。

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

その後、ブラウザーからログインし、アイテムを開いて添付ファイルを1つダウンロードします。ログインは成功するのに添付ファイルのダウンロードだけが失敗する場合、アーカイブにはデータベースは含まれていても attachments/ が含まれていません。すべての確認が終わるまで data.old.* を保持し、完了後に削除します。ロールバックも同じ3手順で、ディレクトリの指定を逆にして実行します。

パスがここで示しているものと異なる場合は、VPS向けの Vaultwarden インストールガイドに、これらのコマンドが前提とする compose ファイルを掲載しています。

バックアップを置いてはいけない場所

  • データディレクトリと同じディスク上には置かないでください。1 つのボリューム障害で両方のコピーを失います。誤ったパスに対する 1 つの rm -rf でも同じ結果になります。
  • 同じサーバー上には置かないでください。2 つ目のボリューム上でも同様です。攻撃者に root へのアクセスを許すと、同じセッションでバックアップにも到達されます。
  • 暗号化せずにオブジェクトストレージへ置かないでください。アーカイブにはメールアドレス、パスワードのヒント、リカバリコード、vault の暗号文が含まれており、オフラインで攻撃される可能性があります。
  • プロバイダーのスナップショットだけに依存しないでください。高速に復元できるため用意する価値はありますが、サーバーと同じアカウント内にあるため、アカウントの問題が発生するとスナップショットも失われます。

オフサイトコピーには 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 の対象にはライブデータフォルダーではなくアーカイブディレクトリを指定してください。これにより、すでに確認した一貫性のあるコピーがアップロードされます。リポジトリのパスワードは、保護対象のサーバー以外の場所に保管してください。そのパスワードを失うと、設計上、スナップショットを読み取れなくなります。ストレージが対応している場合は、書き込みはできても削除はできない認証情報をサーバーに付与してください。これにより、サーバーが侵害されても、自身のバックアップ履歴を消去されにくくなります。VPS で restic バックアップを設定する方法では、リポジトリとスケジュールを詳しく説明しています。まだ選択していない場合は、restic と BorgBackup の比較で選択基準を確認できます。

スケジュールに従ってリストアをテストする

月に 1 日を選びます。最新のスナップショットを restic restore latest --tag vaultwarden --target /tmp/vw-check で一時ディレクトリに取得し、同じ PRAGMA integrity_check を実行します。同じ行数も確認し、日付と件数を記録します。6 か月間リストアしていないバックアップは、状態が不明なバックアップです。障害発生時に初めてその状態を確認することになり、最悪のタイミングでの確認になります。

年に 1 回は、完全な手順を実行します。リストアしたデータフォルダーを使い、空いているポートで 2 つ目の Vaultwarden コンテナを起動して、実際のアカウントでログインします。これにより、マスターパスワードの処理経路を最初から最後まで検証できます。行数の確認だけでは検証できません。同じスケジュールで restic check --read-data-subset=10% も実行すると、保存されたデータが単に一覧表示できるだけでなく、読み取り可能であることを確認できます。

FAQ

実行中の Vaultwarden で cp を使って db.sqlite3 をコピーできますか?

いいえ。Vaultwarden は SQLite を WAL モードで実行するため、直近の書き込みは db.sqlite3-wal に残っており、まだ db.sqlite3 には反映されていません。メインファイルだけを cp すると、書き込み内容が通知なく失われます。また、2 つのファイルを別々にコピーすると、後から Error: database disk image is malformed として現れる不整合な組み合わせになる可能性があります。代わりに sqlite3 /path/db.sqlite3 ".backup '/path/out.sqlite3'" を使用してください。これは SQLite の Online Backup API を使用し、サーバーがサービスを継続している間に整合性のある単一ファイルを作成します。

バックアップを取得するために Vaultwarden コンテナを停止する必要がありますか?

いいえ。それが .backup の目的です。実行中のサーバーからデータベースをコピーしても安全です。Attachments と Send files はユーザーがアップロードした時点で書き込まれるため、データベースのコピーと tar の間に追加されたファイルは、その夜のアーカイブに含まれない可能性があります。その場合の損失は、最大で 1 つの添付ファイルです。数秒間の停止が問題にならない場合は、スクリプトの前に docker compose stop、後に docker compose start を実行すると、その可能性もなくせます。

rsa_key ファイルなしで復元するとどうなりますか?

Vaultwarden は起動時に新しい鍵を生成します。この鍵は、セッションを維持する JSON web tokens (JWT) に署名するため、既存のすべてのトークンが検証できなくなり、すべてのクライアントがログアウトされて再度サインインする必要があります。Vault の内容には影響ありません。Vault の内容は RSA 鍵ではなく、各ユーザーのマスターパスワードから導出された鍵で暗号化されているためです。rsa_key.pem をデータフォルダーの他の内容と一緒に復元すれば、復元したことに気付かれません。

バックアップアーカイブをそのままオブジェクトストレージにアップロードしても安全ですか?

いいえ。アイテム名、パスワード、メモは暗号文ですが、メールアドレス、アカウント名、パスワードヒント、2 要素認証のリカバリーコードはデータベース内に平文で保存されています。また、オフライン攻撃者は自分のペースで暗号文の解析を試行できます。マシンから持ち出す前にアーカイブを暗号化してください。restic repository なら自動的に暗号化できます。gpg --symmetric --cipher-algo AES256 vw-20260805-030000.tar.gz は、任意のストレージに渡せる暗号化済みの単一ファイルを作成します。

PostgreSQL または MariaDB 上の Vaultwarden をバックアップするにはどうすればよいですか?

SQLite の手順は適用できず、組み込みコマンドは The database type is not SQLite. Backups only works for SQLite databases で拒否されます。ネイティブツールを使用してデータベースをダンプします。pg_dump または mysqldump を使用し、それ以外のルールはすべて同じにしてください。ダンプは attachments/、sends/、config.json、rsa_key ファイルと同じアーカイブに含めます。これらは同じ実行で取得し、暗号化したうえで、作成元のサーバー以外の場所に保存してください。