آموزش پشتیبانگیری و بازیابی دیتابیس Vaultwarden
برای پشتیبانگیری امن از Vaultwarden به جای کپی مستقیم از دستور sqlite3 .backup استفاده کنید. این راهنما نحوه حفظ فایلهای db و attachments را برای بازیابی موفق توضیح میدهد.
محتویات ضروری برای پشتیبانگیری از Vaultwarden
پشتیبانگیری از Vaultwarden شامل کپیبرداری از کل پوشهٔ دادهها است و دیتابیس موجود در آن باید به روش صحیح کپی شود. به جای cp از sqlite3 db.sqlite3 ".backup out.sqlite3" استفاده کنید، زیرا کپی ساده از دیتابیسی که در حال نوشتن است، ممکن است فایلی ایجاد کند که باز نشود. سپس فایلهای کنار آن را نیز حفظ کنید؛ این همان بخشی است که اغلب فراموش میشود.
در نصب Docker، پوشهٔ دادهها همان مسیری است که در /data mount کردهاید. این مسیر یا یک آدرس روی host است یا یک named volume، و تفاوت بین bind mountها و named volumeها تعیین میکند که vault شما واقعاً در کجای دیسک قرار دارد. محتویات آن به شرح زیر است:
db.sqlite3: شامل تمام حسابهای کاربری، آیتمهای vault، پوشهها و سازمانها. از دست دادن این فایل به معنای از دست رفتن کل vault است.db.sqlite3-walوdb.sqlite3-shm: شامل write-ahead log (WAL) و ایندکس حافظهٔ اشتراکی آن. نوشتههای اخیر تا زمانی که SQLite آنها را در فایل اصلی ادغام کند، در اینجا قرار دارند.attachments/: فایلهایی که کاربران به آیتمهای vault پیوست کردهاند؛ این فایلها بهصورت رمزنگاریشده و در قالب یک دایرکتوری برای هر آیتم ذخیره میشوند.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;"دستور اول آدرس ایمیل کاربران شما را به صورت متن ساده (cleartext) چاپ میکند. دستور دوم نام یک آیتم را چاپ میکند که به این شکل است:
2.k9Qw1nQ0y7Yy2Xw==|E1r0J3l5s7d9f1g3h5j7k9==|Lm4nOp6qRs8tUv0wXy2zAb4cDe6fGh8i=نام آیتمها، نامهای کاربری، رمزهای عبور و یادداشتها پیش از ارسال توسط کلاینت رمزنگاری میشوند؛ بنابراین سرور دادههای رمزنگاریشدهای (ciphertext) را ذخیره میکند که قادر به خواندن آنها نیست. پیشوند 2. نوع رمزنگاری Bitwarden است که به دنبال آن یک بردار اولیه (IV)، متن رمزنگاریشده و یک کد احراز اصالت پیام (MAC) قرار میگیرد؛ هر کدام به صورت base64 و جدا شده با | هستند. کلیدی که این دادهها را رمزگشایی میکند، از رمز عبور اصلی (master password) حساب مشتق میشود که هرگز به شکل قابل استفاده به سرور نمیرسد. همانطور که در مقایسه Vaultwarden و Bitwarden خودمیزبان آمده است، این بخش در Vaultwarden و سرور رسمی یکسان است.
باقی دیتابیس رمزنگاری نمیشود. آدرسهای ایمیل، نام حسابها، راهنمای رمز عبور و کدهای بازیابی دو مرحلهای به صورت متن ساده ذخیره میشوند؛ در کنار متادیتایی مانند زمان ایجاد و اینکه کدام سازمان مالک آیتم است. بنابراین، خودِ فایل پشتیبان یک راز محسوب میشود. هر کسی که به آن دسترسی داشته باشد، متوجه میشود کاربران شما چه کسانی هستند و میتواند با هر سرعتی که سختافزارش اجازه میدهد، به صورت آفلاین به بلوکهای رمزنگاریشده حمله کند. همین واقعیت، قوانین ذخیرهسازی در ادامه را تعیین میکند: نسخه پشتیبان پیش از خروج از سرور باید رمزنگاری شود. توکن مدیریت (admin token) نیمه دیگر همین مشکل است و مراحل ایمنسازی Vaultwarden خودمیزبان هر دو مورد را پوشش میدهد.
چرا کپی کردن db.sqlite3 در حین اجرای Vaultwarden یک نسخه پشتیبان محسوب نمیشود
Vaultwarden بهصورت پیشفرض از SQLite در حالت WAL استفاده میکند (ENABLE_DB_WAL=true). هر عملیات نوشتن ابتدا در db.sqlite3-wal ثبت میشود و تنها پس از یک checkpoint است که دادهها به db.sqlite3 منتقل میشوند. اگر فقط db.sqlite3 را کپی کنید، پایگاه دادهای که در اختیار خواهید داشت مربوط به آخرین checkpoint است؛ بنابراین ممکن است رمز عبوری که 10 دقیقه پیش ذخیره کردهاید در آرشیو شما نباشد و هیچ هشداری هم دریافت نکنید.
کپی کردن هر سه فایل با cp نیز راهحل درستی نیست. این کپیها در لحظات متفاوتی گرفته میشوند؛ بنابراین فایل WAL که ذخیره کردهاید ممکن است نسخههایی از صفحات را توصیف کند که دیگر با فایل اصلی ذخیرهشده همخوانی ندارند. در این حالت SQLite سعی میکند یکی را با دیگری بازیابی کند و نتیجه نهایی نادرست خواهد بود. شما این موضوع را بسیار دیرتر متوجه خواهید شد:
Error: database disk image is malformed.backup از این مشکل جلوگیری میکند، زیرا از Online Backup API در SQLite استفاده میکند؛ روشی که خود SQLite آن را برای کپی کردن پایگاه دادهای که ممکن است در حال استفاده فعال باشد، توصیه کرده است. این ابزار صفحات را تحت یک read lock میخواند و اگر در حین عملیات، نویسندهای فایل را تغییر دهد، فرآیند را از نو آغاز میکند. در نتیجه، آنچه روی دیسک ذخیره میشود، یک نسخه کاملاً منسجم از یک لحظه خاص است.
تهیه نسخه پشتیبان از پایگاه داده با استفاده از 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به جای آن، دستور را روی میزبان (host) و برای مسیر mount شده اجرا کنید؛ کاری که دستورات بالا انجام میدهند. اگر دادهها در یک volume نامگذاری شده قرار دارند، docker volume inspect <name> مسیر میزبان را در زیر /var/lib/docker/volumes/ چاپ میکند.
Vaultwarden از نسخه 1.32.1 به بعد، دستور پشتیبانگیری اختصاصی خود را نیز ارائه کرده است. روی سرور خود:
docker exec -it vaultwarden /vaultwarden backupاین دستور VACUUM INTO را اجرا کرده و db_YYYYMMDD_HHMMSS.sqlite3 را در پوشه دادهها مینویسد. دو نکته در اینجا حائز اهمیت است: اول اینکه نسخه کپی در کنار فایل اصلی و روی همان دیسک قرار میگیرد، بنابراین این یک مرحله آمادهسازی (staging) است و هنوز یک نسخه پشتیبان کامل محسوب نمیشود. دوم اینکه این قابلیت فقط برای SQLite است: در صورت استفاده از MariaDB یا PostgreSQL، عملیات با خطای The database type is not SQLite. Backups only works for SQLite databases متوقف میشود.
فایلهایی که فراموش میشوند
attachments/ دادههای رمزنگاریشده را با نامهای مبهم نگهداری میکند. ردیف دیتابیس برای هر پیوست، شامل نام فایل رمزنگاریشده و کلید مورد نیاز کلاینت برای رمزگشایی آن است. پیوستهای بدون دیتابیس، دادههای غیرقابلخواندن هستند و دیتابیس بدون پیوستها، آیتمهایی را به کاربر نشان میدهد که دانلود آنها با شکست مواجه میشود. از هر دو در یک نوبت بکآپ بگیرید.
config.json تمام تنظیماتی را که از صفحه مدیریت ذخیره کردهاید در خود نگه میدارد و مقادیر آن بر متغیرهای محیطی (environment variables) مشابه اولویت دارند. این موضوع دو جنبه دارد: بازیابی یک config.json قدیمی، تنظیمات موجود در فایل compose شما را بیسروصدا نادیده میگیرد و خود فایل نیز حساس است، زیرا میتواند شامل رمز عبور SMTP و توکن مدیریت شما باشد. این توکن را بهجای متن ساده، بهصورت یک رشته Argon2id PHC (مسابقه هش کردن رمز عبور) ذخیره کنید. docker run --rm -it vaultwarden/server /vaultwarden hash یک نمونه برای شما تولید میکند.
rsa_key.pem توکنهای وب JSON (JWT) را امضا میکند که وضعیت ورود کلاینتها را حفظ میکنند. اگر این فایل هنگام راهاندازی موجود نباشد، Vaultwarden یک کلید جدید تولید میکند؛ در نتیجه تمام توکنهایی که با کلید قبلی امضا شدهاند دیگر معتبر نخواهند بود و همه کلاینتها از سیستم خارج (logout) میشوند. محتویات Vault از این اتفاق جان سالم به در میبرند، زیرا با کلیدهایی که از رمز عبور اصلی (master password) مشتق شدهاند، رمزنگاری شدهاند. بازیابی فایل کلید از خروج دستهجمعی کاربران جلوگیری میکند.
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 حتی در صورت گزارش خرابی توسط PRAGMA integrity_check، کد خروجی 0 را برمیگرداند؛ بنابراین مقایسه خروجی با ok همان چیزی است که یک کپی ناقص را به یک اسکریپت شکستخورده تبدیل میکند. دستور set -euo pipefail سپس از ادامه کار جلوگیری میکند تا tar یک آرشیو تمیز حول یک دیتابیس خراب ایجاد نکند.
دستور نهایی tar -tzf لیست مواردی که واقعاً دریافت کردهاید را نمایش میدهد. آن را برای اولین بار بررسی کنید. شما باید به دنبال ./db.sqlite3، ./rsa_key.pem، ./config.json و ./attachments/ باشید و مطمئن شوید که ./db.sqlite3-wal وجود ندارد. اگر به خروجی journalctl و واحدی که خرابی را گزارش کند نیاز دارید، آن را به جای cron، با استفاده از یک سرویس و تایمر systemd به صورت شبانه اجرا کنید.
تأیید نسخه پشتیبان از طریق بازیابی در یک دایرکتوری موقت
نسخهای که تست نشده باشد، صرفاً یک حدس است. بازیابی در یک دایرکتوری موقت تنها یک دقیقه زمان میبرد و هیچ تغییری در دادههای زنده ایجاد نمیکند.
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 را چاپ میکند. تعداد کاربر باید با تعداد حسابهایی که میشناسید مطابقت داشته باشد. تعداد رمزنگاریها (cipher count) باید به مقدار زنده در sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select count(*) from ciphers;" نزدیک باشد و در یک vault فعال، هرگز نباید صفر باشد. حجم دایرکتوری پیوستها (attachments) باید تقریباً همان اندازهای باشد که انتظار دارید؛ اگر کسی پیوستی آپلود نکرده است، میتوانید از این مورد صرفنظر کنید. سپس 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.* را نگه دارید و پس از آن حذفش کنید. بازگشت به وضعیت قبل (Rollback) دقیقاً با همان سه مرحله و با جابهجایی مسیر دایرکتوریها انجام میشود.
اگر مسیرهای شما با مسیرهای ذکر شده در اینجا مطابقت ندارند، راهنمای نصب Vaultwarden برای VPS فایل compose مورد استفاده در این دستورات را نشان میدهد.
کجا نباید از نسخه پشتیبان نگهداری کرد
- روی همان دیسکی که پوشه دادهها قرار دارد، ذخیره نکنید. خرابی یک volume باعث از دست رفتن هر دو نسخه میشود؛ همین اتفاق با یک
rm -rfدر مسیر اشتباه نیز رخ میدهد. - روی همان سرور ذخیره نکنید، حتی اگر در volume دوم باشد. مهاجمی که به دسترسی root برسد، در همان نشست به نسخههای پشتیبان شما نیز دسترسی پیدا میکند.
- بدون رمزنگاری در object storage قرار ندهید، زیرا آرشیو حاوی آدرسهای ایمیل، راهنمای رمز عبور، کدهای بازیابی و متن رمزنگاریشده vault است که میتواند بهصورت آفلاین مورد حمله قرار گیرد.
- فقط به snapshotهای ارائهدهنده سرویس خود تکیه نکنید. این snapshotها سریع بازیابی میشوند و داشتن آنها مفید است، اما چون در همان حساب کاربری سرور قرار دارند، بروز مشکل در حساب کاربری باعث از دست رفتن آنها نیز میشود.
یک نسخه خارج از سایت (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 --prunerestic را به سمت دایرکتوری آرشیو هدایت کنید، نه به سمت پوشه دادههای زنده؛ بنابراین آنچه آپلود میشود، همان نسخه منسجمی است که قبلاً بررسی کردهاید. رمز عبور مخزن را در جایی غیر از سروری که از آن محافظت میکند نگه دارید: طبق طراحی، با گم کردن آن رمز عبور، snapshotها غیرقابل خواندن خواهند بود. در جایی که فضای ذخیرهسازی پشتیبانی میکند، به سرور اعتبارنامههایی بدهید که فقط اجازه نوشتن داشته باشند و نه حذف، تا در صورت نفوذ به سیستم، مهاجم نتواند تاریخچه خود را پاک کند. راهاندازی نسخههای پشتیبان با restic روی VPS مخزن و زمانبندی را بهطور کامل پوشش میدهد و مقایسه restic با BorgBackup در صورتی که هنوز انتخابی نکردهاید، به شما کمک میکند.
تست بازیابی طبق برنامه زمانی
یک روز در ماه را انتخاب کنید. جدیدترین snapshot را با استفاده از restic restore latest --tag vaultwarden --target /tmp/vw-check به یک دایرکتوری موقت منتقل کنید، همان PRAGMA integrity_check را اجرا کرده و تعداد ردیفها را بشمارید، سپس تاریخ و تعداد ردیفها را یادداشت کنید. بکآپی که در شش ماه گذشته بازیابی نشده باشد، بکآپی با وضعیت نامشخص است؛ شما وضعیت آن را در زمان بروز قطعی متوجه خواهید شد که بدترین زمان ممکن برای این کار است.
سالی یکبار، تست کامل را انجام دهید. یک container دوم از Vaultwarden را روی یک پورت آزاد با پوشه دادههای بازیابیشده بالا بیاورید و با یک حساب کاربری واقعی وارد شوید. این کار مسیر master password را از ابتدا تا انتها تأیید میکند، کاری که هیچ شمارش ردیفی قادر به انجام آن نیست. اجرای restic check --read-data-subset=10% طبق همان برنامه زمانی، تأیید میکند که دادههای ذخیرهشده قابل خواندن هستند و صرفاً در لیست فایلها وجود ندارند.
FAQ
Can I copy db.sqlite3 with cp while Vaultwarden is running?
No. Vaultwarden runs SQLite in WAL mode, so recent writes sit in db.sqlite3-wal and are not yet in db.sqlite3. A cp of the main file alone loses them silently, and copying the two files separately can leave a mismatched pair that surfaces later as Error: database disk image is malformed. Use sqlite3 /path/db.sqlite3 ".backup '/path/out.sqlite3'" instead. It uses SQLite's Online Backup API and produces one consistent file while the server keeps serving.
Do I have to stop the Vaultwarden container to take a backup?
No, and that is the point of .backup. The database copy is safe on a running server. Attachments and Send files are written when a user uploads one, so a file added between the database copy and the tar could miss that night's archive, which costs you one attachment at worst. If a few seconds of downtime does not bother you, docker compose stop before the script and docker compose start after it removes even that.
What happens if I restore without the rsa_key files?
Vaultwarden generates a new key at startup. That key signs the JSON web tokens (JWT) that keep sessions alive, so every existing token stops validating and all clients are logged out and must sign in again. Vault contents are unaffected, because they are encrypted with keys derived from each user's master password rather than with the RSA key. Restore rsa_key.pem along with the rest of the data folder and nobody notices the restore.
Is the backup archive safe to upload to object storage as it is?
No. Item names, passwords and notes are ciphertext, but email addresses, account names, password hints and two-factor recovery codes are plain text in the database, and an offline attacker can grind the ciphertext at their own pace. Encrypt the archive before it leaves the machine. A restic repository does that for you, and gpg --symmetric --cipher-algo AES256 vw-20260805-030000.tar.gz produces a single encrypted file you can hand to any storage.
How do I back up Vaultwarden on PostgreSQL or MariaDB?
The SQLite steps do not apply, and the built-in command refuses with The database type is not SQLite. Backups only works for SQLite databases. Dump the database with its native tool, pg_dump or mysqldump, and keep every other rule the same. The dump belongs in one archive with attachments/, sends/, config.json and the rsa_key files, taken in the same run, encrypted, and stored somewhere other than the server that made it.