SSD Nodes Learn Hosting plans →
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-08-23

كيفية نسخ Vaultwarden احتياطياً واستعادته على VPS

استخدم sqlite3 .backup لنسخ خزنة Vaultwarden قيد التشغيل، واحتفظ بالمرفقات وملفات config.json وrsa_key، ثم اختبر الاستعادة قبل الحاجة إليها.

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

ما الذي يجب أن تتضمنه نسخة Vaultwarden الاحتياطية

نسخة Vaultwarden الاحتياطية هي نسخة من مجلد البيانات بالكامل، ويجب نسخ قاعدة البيانات الموجودة فيه بالطريقة الصحيحة. شغّل sqlite3 db.sqlite3 ".backup out.sqlite3" بدلاً من cp، لأن نسخ قاعدة بيانات قيد الكتابة مباشرةً قد ينتج ملفاً لا يمكن فتحه. ثم احتفظ بالملفات الموجودة إلى جانبها، فهذا هو الجزء الذي ينساه كثيرون.

في تثبيت Docker، يكون مجلد البيانات هو المجلد الذي ربطته بالمسار /data. وقد يكون هذا المسار موجوداً على المضيف أو يكون volume مسمّى، ويحدد الفرق بين bind mounts وnamed volumes مكان وجود خزنتك فعلياً على القرص. إليك محتوياته.

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

يطبع الأمر الأول عناوين البريد الإلكتروني للمستخدمين بنص واضح. ويطبع الأمر الثاني اسم عنصر واحد، ويبدو بالشكل التالي:

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

تُشفَّر أسماء العناصر وأسماء المستخدمين وكلمات المرور والملاحظات على العميل قبل إرسالها، لذلك يخزّن الخادم نصاً مشفراً لا يستطيع قراءته. تمثل البادئة 2. نوع التشفير في Bitwarden، وتليها قيمة متجه التهيئة (IV)، ثم النص المشفر، ثم MAC (رمز مصادقة الرسالة)، وكل منها بترميز base64 ومفصول بالرمز |. ويُشتق المفتاح الذي يفك تشفيره من كلمة مرور الحساب الرئيسية، التي لا تصل إلى الخادم بصيغة قابلة للاستخدام. هذا الجزء متطابق سواء شغّلت Vaultwarden أو الخادم الرسمي، كما يوضّح مقارنة Vaultwarden بـBitwarden المستضاف ذاتياً.

أما بقية قاعدة البيانات فليست مشفرة. تُخزَّن عناوين البريد الإلكتروني وأسماء الحسابات وتلميحات كلمات المرور ورموز استرداد المصادقة الثنائية بنص واضح، إلى جانب بيانات وصفية مثل أوقات الإنشاء والمنظمة المالكة للعنصر. لذلك يُعد ملف النسخ الاحتياطي سراً بحد ذاته. فأي شخص يحوز هذا الملف يعرف هوية مستخدميك، ويمكنه مهاجمة الكتل المشفرة دون اتصال وبالسرعة التي يتيحها عتاده. وهذه الحقيقة وحدها تحدد قواعد التخزين الواردة أدناه: يجب تشفير النسخة قبل مغادرتها الخادم. ورمز المسؤول هو الجزء الآخر من المشكلة نفسها، وتتناول خطوات تحصين Vaultwarden المستضاف ذاتياً كلا الجانبين.

لماذا لا يُعد نسخ db.sqlite3 أثناء تشغيل Vaultwarden نسخة احتياطية

يشغّل Vaultwarden قاعدة SQLite في وضع WAL افتراضياً (ENABLE_DB_WAL=true). تُكتب البيانات أولاً إلى db.sqlite3-wal، ولا تُدمج في db.sqlite3 إلا عند تنفيذ checkpoint. إذا نسخت db.sqlite3 وحده، فستحصل على حالة قاعدة البيانات عند آخر checkpoint. لذلك قد تكون كلمة مرور حُفظت قبل عشر دقائق مفقودة من الأرشيف من دون أي تحذير.

ولا يُعد نسخ الملفات الثلاثة كلها باستخدام cp حلاً أيضاً. تُنشأ النسخ في لحظات مختلفة قليلاً، لذلك قد يصف ملف WAL الذي حفظته إصدارات صفحات لا يتطابق معها الملف الرئيسي الذي حفظته. عندئذ تستعيد SQLite أحد الملفين من الآخر، وتكون النتيجة غير صحيحة. وقد لا تكتشف ذلك إلا بعد وقت طويل:

Error: database disk image is malformed

يتجنب .backup هذه المشكلة لأنه يستخدم SQLite Online Backup API، الذي توثّقه SQLite بوصفه الطريقة المخصصة لنسخ قاعدة بيانات قد تكون قيد الاستخدام. يقرأ الصفحات أثناء وجود قفل قراءة، ويعيد العملية من البداية إذا غيّر كاتبٌ الملف أثناء النسخ. لذلك تكون البيانات المكتوبة على القرص لقطة متسقة للحظة واحدة.

أخذ نسخة من قاعدة البيانات باستخدام 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

شغّلها على المضيف مقابل المسار الموصول بدلاً من ذلك، وهذا ما تفعله الأوامر أعلاه. إذا كانت البيانات موجودة في 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 داخل مجلد البيانات. وينتج عن ذلك أمران. توضع النسخة بجوار النسخة الأصلية على القرص نفسه، ولذلك فهي خطوة مرحلية وليست نسخة احتياطية بعد. كما أنها تعمل مع SQLite فقط؛ أما عند استخدام MariaDB أو PostgreSQL فتتوقف وتعرض The database type is not SQLite. Backups only works for SQLite databases.

الملفات التي ينساها المستخدمون

attachments/ يحتوي على النص المشفّر تحت أسماء غير واضحة. يحتوي صف قاعدة البيانات الخاص بكل مرفق على اسم الملف المشفّر ومواد المفتاح التي يحتاج إليها العميل لفك تشفير الملف. تكون المرفقات غير المقترنة بقاعدة البيانات غير قابلة للقراءة، كما أن قاعدة البيانات التي تفتقر إلى المرفقات تمنح المستخدمين عناصر تفشل تنزيلاتها. خذ النسختين في العملية نفسها.

config.json يحتوي على كل ما حفظته من صفحة الإدارة، وتكون قيمه ذات أولوية على متغيرات البيئة المطابقة. ولهذا الأمر جانبان: تؤدي استعادة ملف config.json قديم إلى تجاوز الإعدادات الموجودة في ملف compose بصمت، كما أن الملف نفسه حساس لأنه قد يحتوي على كلمة مرور SMTP ورمز admin المميز. خزّن ذلك الرمز كسلسلة Argon2id PHC (مسابقة تجزئة كلمات المرور) بدلاً من تخزينه كنص عادي. يطبع 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 ووحدة تُبلغ عن الفشل.

تحقّق من النسخة الاحتياطية باستعادتها إلى دليل مؤقت

النسخة الاحتياطية غير المختبرة مجرد افتراض. تستغرق الاستعادة إلى دليل مؤقت دقيقة واحدة ولا تلمس أي شيء قيد التشغيل.

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 صحيحاً ما لم تضبط user: في ملف compose. في هذه الحالة، استخدم uid وgid المحددين هناك. إذا تعذّر على الخادم الكتابة إلى مجلد البيانات، فستظهر لك صفحة تسجيل دخول تفشل معها كل الطلبات، وستوضح السجلات السبب.

تنتهي عملية التشغيل السليمة بسطر Rocket:

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

بعد ذلك، سجّل الدخول من متصفح، وافتح عنصراً، ونزّل مرفقاً واحداً. إذا نجح تسجيل الدخول وفشلت تنزيلات المرفقات، فهذا يعني أن الأرشيف احتوى قاعدة البيانات، لكنه لم يحتوِ على attachments/. احتفظ بـdata.old.* إلى أن تتحقق من كل ذلك، ثم احذفه. يتطلب التراجع تنفيذ الخطوات الثلاث نفسها مع تبديل المجلدين في الاتجاه الآخر.

إذا لم تتطابق مساراتك مع المسارات الواردة هنا، يوضّح دليل تثبيت Vaultwarden على VPS ملف compose الذي تفترضه هذه الأوامر.

أين لا تضع النسخة الاحتياطية

  • لا تضعها على القرص نفسه الذي يوجد عليه مجلد البيانات. فأي وحدة تخزين تتعطل ستؤدي إلى فقدان النسختين، وينطبق الأمر نفسه على أي rm -rf في المسار الخطأ.
  • لا تضعها على الخادم نفسه، حتى على وحدة تخزين ثانية. فالمهاجم الذي يصل إلى root يصل إلى نسخك الاحتياطية في الجلسة نفسها.
  • لا تضعها في تخزين الكائنات من دون تشفير، لأن الأرشيف يحتوي على عناوين بريد إلكتروني، وتلميحات كلمات المرور، ورموز الاسترداد، ونصوص vault المشفرة التي يمكن مهاجمتها دون اتصال.
  • لا تعتمد فقط على snapshots التي يوفرها مزود الخدمة. فهي تُستعاد بسرعة، وهذا مفيد، لكنها توجد في الحساب نفسه الذي يوجد فيه الخادم، لذلك تؤدي مشكلة في الحساب إلى فقدانها أيضاً.

تُستخدم نسخة خارج الموقع هنا، لأن مستودع 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's 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، وأن تُؤخذ جميعها في التشغيل نفسه، وتُشفّر، وتُخزّن في مكان آخر غير الخادم الذي أنشأها.