Vaultwarden Backup at Restore sa VPS: SQLite Guide
Alamin ang tamang sqlite3 .backup para sa live vault, kasama ang attachments, config.json at rsa_key files, at kung paano patunayan ang restore bago kailanganin.
Mga Dapat Nilalaman ng Vaultwarden Backup
Ang Vaultwarden backup ay kopya ng buong data folder, at dapat kopyahin nang tama ang database na nasa loob nito. Patakbuhin ang sqlite3 db.sqlite3 ".backup out.sqlite3" sa halip na cp, dahil ang direktang pagkopya ng database habang sinusulatan ito ay maaaring lumikha ng file na hindi mabuksan. Isama rin ang mga file na nasa tabi nito. Ito ang bahaging madalas nakalilimutan.
Sa Docker installation, ang data folder ay ang anumang directory na ni-mount mo sa /data. Maaaring path ito sa host o named volume. Ang pagkakaiba ng bind mounts at named volumes ang tumutukoy kung saan talaga naka-store ang vault sa disk. Narito ang laman nito.
db.sqlite3: lahat ng account, vault item, folder, at organisation. Kapag nawala ang file na ito, mawawala ang vault.db.sqlite3-walatdb.sqlite3-shm: ang write-ahead log (WAL) at shared memory index nito. Dito pansamantalang naka-store ang mga bagong write hanggang maisama ng SQLite sa pangunahing file.attachments/: ang mga file na in-attach ng mga user sa vault item, naka-encrypt at nasa magkakahiwalay na directory para sa bawat item.sends/: ang mga file sa likod ng Bitwarden Send links.config.json: lahat ng setting na sine-save mo mula sa admin page.rsa_key.pem, patirsa_key.deratrsa_key.pub.dersa mga mas lumang installation: ang key na ginagamit para pumirma sa login tokens.icon_cache/: mga icon ng website na na-download. Ito ang isang directory na maaari mong laktawan, dahil muling dina-download ng Vaultwarden ang mga ito kapag kailangan.
Ligtas ba ang Vaultwarden database ko? Ano talaga ang laman ng file
Dalawang command ang makakasagot dito, at maaari mong patakbuhin ang mga ito ngayon.
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;"Ipinapakita ng una ang mga email address ng iyong mga user bilang cleartext. Ipinapakita naman ng pangalawa ang pangalan ng isang item, na ganito ang hitsura:
2.k9Qw1nQ0y7Yy2Xw==|E1r0J3l5s7d9f1g3h5j7k9==|Lm4nOp6qRs8tUv0wXy2zAb4cDe6fGh8i=Ang mga pangalan ng item, username, password, at note ay ine-encrypt ng client bago ipadala. Kaya ciphertext ang iniimbak ng server at hindi ito mababasa ng server. Ang prefix na 2. ay encryption type ng Bitwarden. Sinusundan ito ng initialisation vector (IV), ciphertext, at MAC (message authentication code). Naka-base64 ang bawat isa at pinaghihiwalay ng |. Ang key na nagde-decrypt nito ay dine-derive mula sa master password ng account. Hindi kailanman nakararating sa server ang master password sa usable na anyo. Pareho ito kahit Vaultwarden o official server ang pinapatakbo mo, gaya ng ipinapaliwanag sa paghahambing ng Vaultwarden at self-hosted Bitwarden.
Hindi encrypted ang natitirang bahagi ng database. Naka-store bilang plain text ang mga email address, pangalan ng account, password hint, at two-factor recovery code, kasama ng metadata gaya ng mga oras ng paglikha at kung aling organization ang may-ari ng isang item. Kaya lihim mismo ang backup file. Malalaman ng sinumang may hawak nito kung sino ang iyong mga user, at maaari niyang atakihin offline ang encrypted blob sa bilis na kaya ng hardware niya. Dahil dito, may epekto ang simpleng katotohanang ito sa storage rules sa ibaba: ine-encrypt ang kopya bago ito umalis sa server.
Bakit hindi backup ang pagkopya ng db.sqlite3 habang tumatakbo ang Vaultwarden
Ginagamit ng Vaultwarden ang SQLite sa WAL mode bilang default (ENABLE_DB_WAL=true). Unang napupunta ang isang write sa db.sqlite3-wal, at isinasama lamang ito sa db.sqlite3 kapag may checkpoint. Kapag db.sqlite3 lang ang kinopya mo, makukuha mo ang database sa oras ng huling checkpoint. Dahil dito, maaaring mawala sa archive mo ang password na na-save sampung minuto ang nakalipas nang walang anumang babala.
Hindi rin solusyon ang pagkopya sa lahat ng 3 file gamit ang cp. Kinokopya ang mga file sa bahagyang magkakaibang oras. Dahil dito, maaaring maglaman ang na-save mong WAL ng mga page version na hindi na tumutugma sa na-save mong main file. Susubukan ng SQLite na i-recover ang isa gamit ang isa pa, at magiging mali ang resulta. Maaaring matuklasan mo ito makalipas pa ang mahabang panahon:
Error: database disk image is malformedIniiwasan ito ng .backup dahil ginagamit nito ang SQLite Online Backup API. Ito ang paraang idinodokumento ng SQLite para sa pagkopya ng database na maaaring aktibong ginagamit. Binabasa nito ang mga page habang may read lock. Magsisimula itong muli kung may writer na magbago sa file habang isinasagawa ang pagkopya. Dahil dito, isang consistent na punto lamang ng oras ang mapupunta sa disk.
Kunin ang kopya ng database gamit ang 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;"Ini-print ng huling command ang ok sa sarili nitong linya. Anumang ibang output ay nangangahulugang hindi magagamit ang kopya. Huwag itong itago, at huwag burahin ang naunang kopya. Isinasagawa ang buong sequence laban sa live server, kaya walang nilo-log out at walang nire-restart na container.
Wala sa loob ng Vaultwarden container ang tool na sqlite3. Binuo ang image gamit ang debian:trixie-slim, kasama ang ca-certificates, curl, libmariadb3, libpq5 at openssl, kaya nagfa-fail ang docker exec vaultwarden sqlite3 ... na may ganitong output:
exec: "sqlite3": executable file not found in $PATHPatakbuhin ito sa host laban sa mounted path. Ito ang ginagawa ng mga command sa itaas. Kung nasa named volume ang data, ini-print ng docker volume inspect <name> ang host path sa ilalim ng /var/lib/docker/volumes/.
May sarili ring backup command ang Vaultwarden mula pa noong version 1.32.1. Sa server mo:
docker exec -it vaultwarden /vaultwarden backupPinapatakbo nito ang VACUUM INTO at isinusulat ang db_YYYYMMDD_HHMMSS.sqlite3 sa data folder. Dalawang bagay ang dapat tandaan. Napupunta ang kopya sa tabi ng orihinal sa parehong disk, kaya staging step pa lamang ito at hindi pa aktuwal na backup. SQLite lamang ang sinusuportahan nito. Sa MariaDB o PostgreSQL, humihinto ito at naglalabas ng The database type is not SQLite. Backups only works for SQLite databases.
Mga file na madalas nakalilimutan
attachments/ ang naglalaman ng ciphertext sa ilalim ng mga opaque na pangalan. Nasa database row para sa bawat attachment ang encrypted file name nito at key material na kailangan ng client para i-decrypt ang file. Kapag wala ang mga attachment, hindi mababasa ang laman ng database. Kapag wala naman ang database, magkakaroon ang mga user ng mga item na hindi mada-download. Isama ang dalawang ito sa parehong run.
config.json ang naglalaman ng lahat ng sine-save mo mula sa admin page, at mas mataas ang priority ng mga value nito kaysa sa katugmang environment variables. Parehong may pakinabang at panganib ito: tahimik na ino-override ng lumang config.json ang mga setting sa compose file mo, at sensitibo rin ang file dahil maaaring naglalaman ito ng SMTP password at admin token. I-store ang token bilang Argon2id PHC (password hashing competition) string, hindi bilang plain text. Ang docker run --rm -it vaultwarden/server /vaultwarden hash ang magpi-print nito para sa iyo.
rsa_key.pem ang lumalagda sa JSON web tokens (JWT) na nagpapanatiling naka-log in ang mga client. Kung nawawala ang file sa startup, gumagawa ang Vaultwarden ng bagong key. Dahil dito, hindi na nagva-validate ang bawat token na nilagdaan ng lumang key, at nala-log out ang lahat ng client. Nananatili ang laman ng Vault dahil naka-encrypt ito gamit ang mga key na nagmula sa master password. Naiiwasan ang mass logout kapag na-restore ang key file.
sends/ ang naglalaman ng mga file sa likod ng Send links. Kapag nawawala ang mga ito, masisira ang mga download na iyon at wala nang iba.
Ilagay ang lahat sa isang script
#!/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"I-save ito bilang /usr/local/sbin/vw-backup.sh, chmod 700 ito, at patakbuhin bilang root. May aktuwal na ginagawa ang linyang test: nag-e-exit ang sqlite3 sa 0 kahit mag-ulat ang PRAGMA integrity_check ng corruption, kaya ang paghahambing ng output sa ok ang nagiging dahilan para mag-fail ang script kapag may maling kopya. Pagkatapos, pinapahinto ng set -euo pipefail ang lahat sa halip na hayaan ang tar na bumuo ng maayos na archive sa paligid ng sirang database.
Inililista ng huling tar -tzf ang aktuwal na nakuhanan mo ng kopya. Basahin ito sa unang pagpapatakbo. Hanapin ang ./db.sqlite3, ./rsa_key.pem, ./config.json at ./attachments/, at tiyaking wala ang ./db.sqlite3-wal. Patakbuhin ito gabi-gabi gamit ang systemd service at timer sa halip na cron kung gusto mo ng output na journalctl at isang unit na nag-uulat kapag nag-fail.
I-verify ang backup sa pamamagitan ng pag-restore nito sa isang scratch directory
Ang backup na hindi nasusubukan ay hula lamang. Ang pag-restore sa isang scratch directory ay tumatagal ng isang minuto at walang binabago sa live system.
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/attachmentsApat na resulta ang mahalaga. Ini-print ng integrity_check ang ok. Dapat tumugma ang bilang ng user sa bilang ng mga account na alam mong mayroon. Dapat malapit ang bilang ng cipher sa live na value mula sa sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select count(*) from ciphers;", at hindi ito dapat maging zero sa isang vault na ginagamit. Dapat humigit-kumulang tumugma ang laki ng attachments directory sa inaasahan mo. Maaari mo itong laktawan kung walang nag-a-upload ng attachments. Pagkatapos, patakbuhin ang sudo rm -rf /tmp/vw-check dahil may pangalawang kopya na ngayon ng lahat ang directory na iyon.
May isang panuntunan kapag nag-restore ka ng anumang data folder na mano-manong kinopya: tanggalin ang db.sqlite3-wal at db.sqlite3-shm bago simulan ang server. Kung hindi, susubukan ng SQLite na i-recover ang na-restore na database gamit ang log na kabilang sa ibang kopya nito. Maaari nitong ma-corrupt ang database na dumating nang buo. Hindi kailanman kasama sa mga archive na ginagawa ng script sa itaas ang mga file na iyon dahil isang kumpletong database ang sinusulat ng .backup.
Ibalik sa server
Isagawa ang mga ito sa sarili mong server habang nakahinto ang container. Hindi dapat nagsusulat ang Vaultwarden habang binabago ang data folder nito.
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 vaultwardenDapat tukuyin ng chown ang user na ginagamit ng container. Ang stock image ay tumatakbo bilang root, kaya tama ang root:root maliban kung itinakda mo ang user: sa compose file. Kung ganoon, gamitin ang uid at gid na iyon. Kapag hindi maisulat ng server ang data folder, magpapakita ang login page ngunit mabibigo ang bawat request. Makikita ito sa logs.
Matagumpay ang pagsisimula kapag lumabas ang Rocket line:
[INFO] Rocket has launched from http://0.0.0.0:80Pagkatapos, mag-log in gamit ang browser, magbukas ng item, at mag-download ng isang attachment. Kung gumagana ang login ngunit nabibigo ang pag-download ng attachment, ibig sabihin ay kasama sa archive ang database ngunit hindi ang attachments/. Panatilihin ang data.old.* hanggang makumpirma ang lahat ng ito, saka ito i-delete. Pareho ang tatlong hakbang sa pag-rollback, ngunit pagpapalitin ang mga directory sa kabilang direksiyon.
Kung hindi tugma ang mga path mo sa mga nasa itaas, ipinapakita ng guide sa pag-install ng Vaultwarden para sa VPS ang compose file na ipinapalagay ng mga command na ito.
Saan hindi dapat ilagay ang backup
- Huwag sa parehong disk kung nasaan ang data folder. Kapag nag-fail ang isang volume, mawawala ang parehong kopya. Ganito rin ang mangyayari kapag may isang
rm -rfsa maling path. - Huwag sa parehong server, kahit nasa second volume. Kapag nakakuha ng access sa root ang attacker, maaabot din nito ang iyong mga backup sa parehong session.
- Huwag sa object storage na walang encryption, dahil naglalaman ang archive ng mga email address, password hint, recovery code, at vault ciphertext na maaaring atakehin offline.
- Huwag umasa lamang sa snapshots ng iyong provider. Mabilis itong mag-restore, kaya mainam pa ring mayroon nito. Ngunit nasa parehong account sila ng server, kaya kapag nagkaproblema ang account, kasama rin silang maaapektuhan.
Dito kapaki-pakinabang ang offsite copy at restic, dahil ine-encrypt ang restic repository sa machine bago ito i-upload. Sa iyong server:
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 --pruneIturo ang restic sa archive directory, hindi sa live data folder, upang ang i-upload nito ay ang consistent na kopyang nasuri mo na. Itago ang repository password sa ibang lugar kaysa sa server na pinoprotektahan nito. Kapag nawala ang password na iyon, hindi mababasa ang snapshots, ayon sa disenyo. Kung sinusuportahan ito ng storage, bigyan ang server ng credentials na maaaring magsulat ngunit hindi mag-delete, upang hindi mabura ng isang compromised na machine ang sarili nitong history. Sinasaklaw nang buo ng Pag-set up ng restic backups sa isang VPS ang repository at schedule. Tinalakay naman sa Paghahambing ng restic at BorgBackup ang pagpili kung hindi ka pa nakakapagpasya.
Subukan ang restore ayon sa iskedyul
Pumili ng isang araw bawat buwan. I-pull ang pinakabagong snapshot sa isang scratch directory gamit ang restic restore latest --tag vaultwarden --target /tmp/vw-check, patakbuhin ang parehong PRAGMA integrity_check, patakbuhin ang parehong row counts, at isulat ang petsa at mga count. Ang backup na hindi na-restore sa loob ng anim na buwan ay may hindi tiyak na estado. Malalaman mo lamang ang estado nito kapag may outage, na siyang pinakamasamang oras para ito matuklasan.
Minsan bawat taon, gawin ang buong bersyon ng test. Mag-start ng pangalawang Vaultwarden container sa isang ekstrang port gamit ang na-restore na data folder, at mag-log in gamit ang isang totoong account. Pinatutunayan nito ang master password path mula end to end, na hindi kayang patunayan ng anumang row count. Bine-verify ng restic check --read-data-subset=10% ayon sa parehong iskedyul na nababasa ang naka-store na data, at hindi lamang ito naililista.
FAQ
Maaari ko bang kopyahin ang db.sqlite3 gamit ang cp habang tumatakbo ang Vaultwarden?
Hindi. Tumatakbo ang Vaultwarden gamit ang SQLite sa WAL mode, kaya ang mga bagong write ay nasa db.sqlite3-wal pa at wala pa sa db.sqlite3. Kapag cp lamang ng pangunahing file ang ginawa, tahimik na mawawala ang mga ito, at kapag magkahiwalay na kinopya ang dalawang file, maaaring magkaroon ng hindi magkatugmang pares na lalabas sa kalaunan bilang Error: database disk image is malformed. Sa halip, gamitin ang sqlite3 /path/db.sqlite3 ".backup '/path/out.sqlite3'". Ginagamit nito ang SQLite Online Backup API at gumagawa ng isang consistent na file habang patuloy na nagsisilbi ang server.
Kailangan ko bang ihinto ang Vaultwarden container para gumawa ng backup?
Hindi, at iyon ang layunin ng .backup. Ligtas kopyahin ang database habang tumatakbo ang server. Isinusulat ang Attachments at Send files kapag may ina-upload ang user, kaya maaaring hindi mapasama sa archive ng gabing iyon ang file na nadagdag sa pagitan ng pagkopya ng database at ng tar. Sa pinakamasamang kaso, isang attachment lamang ang mawawala. Kung hindi problema sa iyo ang ilang segundong downtime, ang docker compose stop bago ang script at docker compose start pagkatapos nito ay nag-aalis pati sa posibilidad na iyon.
Ano ang mangyayari kung mag-restore ako nang wala ang mga rsa_key file?
Gagawa ang Vaultwarden ng bagong key sa pagsisimula. Nilalagdaan ng key na iyon ang JSON web tokens (JWT) na nagpapanatili sa mga session, kaya hindi na mava-validate ang lahat ng kasalukuyang token at madi-disconnect ang lahat ng client; kailangan nilang mag-sign in muli. Hindi maaapektuhan ang laman ng vault dahil naka-encrypt ito gamit ang mga key na hinango mula sa master password ng bawat user, hindi gamit ang RSA key. I-restore ang rsa_key.pem kasama ng iba pang laman ng data folder para walang makapansin sa restore.
Ligtas bang i-upload ang backup archive sa object storage nang walang pagbabago?
Hindi. Ciphertext ang mga pangalan ng item, password, at note, pero plain text sa database ang mga email address, account name, password hint, at two-factor recovery code. Maaari ring subukan ng offline attacker ang ciphertext sa sarili nitong bilis. I-encrypt ang archive bago ito umalis sa machine. Awtomatikong ginagawa iyon ng isang restic repository, at gumagawa ang gpg --symmetric --cipher-algo AES256 vw-20260805-030000.tar.gz ng isang encrypted file na maaari mong ibigay sa anumang storage.
Paano ko iba-back up ang Vaultwarden sa PostgreSQL o MariaDB?
Hindi naaangkop ang mga hakbang para sa SQLite, at tatanggi ang built-in command gamit ang The database type is not SQLite. Backups only works for SQLite databases. I-dump ang database gamit ang native tool nito, pg_dump o mysqldump, at panatilihin ang lahat ng iba pang panuntunan. Dapat ilagay ang dump sa isang archive kasama ng attachments/, sends/, config.json, at mga rsa_key file, na kinuha sa parehong run, naka-encrypt, at naka-store sa ibang lokasyon kaysa sa server na gumawa nito.