SSD Nodes Learn Hosting plans →
가이드 Matt Connor작성자 Matt Connor · 업데이트됨 2026-08-27

Vaultwarden VPS 백업 및 복구 가이드

sqlite3 .backup 명령어를 사용하여 Vaultwarden 데이터를 안전하게 백업하는 방법을 설명합니다. db.sqlite3 파일뿐만 아니라 attachments, config.json, rsa_key 등 필수 포함 항목과 복구 검증 절차를 상세히 안내합니다.

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

Vaultwarden 백업에 포함되어야 할 항목

Vaultwarden 백업은 전체 데이터 폴더의 복사본이어야 하며, 내부 데이터베이스는 올바른 방식으로 복사해야 합니다. 데이터베이스에 쓰기 작업이 진행 중일 때 단순 복사를 수행하면 파일을 열 수 없게 될 수 있으므로, cp 대신 sqlite3 db.sqlite3 ".backup out.sqlite3"을 실행하십시오. 그런 다음 데이터베이스 파일과 함께 위치한 파일들을 보관해야 합니다. 많은 사용자가 이 부분을 간과합니다.

Docker 설치 환경에서 데이터 폴더는 /data에 마운트한 경로입니다. 이는 호스트의 경로이거나 명명된 볼륨(named volume)일 수 있으며, 바인드 마운트와 명명된 볼륨의 차이에 따라 실제 볼트 데이터가 디스크의 어디에 저장되는지가 결정됩니다. 데이터 폴더에는 다음 항목이 포함됩니다.

  • 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 비교에서 자세히 다룹니다.

데이터베이스의 나머지 부분은 암호화되지 않습니다. 이메일 주소, 계정 이름, 비밀번호 힌트, 2단계 인증 복구 코드는 평문으로 저장되며, 생성 시간이나 항목을 소유한 조직과 같은 메타데이터도 함께 저장됩니다. 따라서 백업 파일 자체가 하나의 기밀 정보입니다. 파일을 탈취한 사람은 누가 사용자인지 알 수 있으며, 하드웨어 성능이 허용하는 속도로 암호화된 데이터 덩어리를 오프라인에서 공격할 수 있습니다. 이러한 사실 때문에 아래에 설명할 저장 규칙이 중요합니다. 즉, 서버를 떠나기 전에 복사본을 암호화해야 합니다. 관리자 토큰 또한 동일한 문제의 일부이며, Vaultwarden 자체 호스팅 보안 강화에서 이 두 가지 문제를 모두 다룹니다.

Vaultwarden 실행 중에 db.sqlite3를 복사하는 것이 백업이 아닌 이유

Vaultwarden은 기본적으로 SQLite를 WAL 모드로 실행합니다(ENABLE_DB_WAL=true). 쓰기 작업은 먼저 db.sqlite3-wal에 기록되며, 체크포인트가 수행되어야만 db.sqlite3로 병합됩니다. db.sqlite3만 단독으로 복사하면 마지막 체크포인트 시점의 데이터베이스만 확보하게 됩니다. 따라서 10분 전에 저장한 비밀번호가 아카이브에서 누락될 수 있으며, 이에 대한 경고도 표시되지 않습니다.

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

대신 호스트에서 마운트된 경로를 대상으로 명령을 실행하십시오. 위에서 제시한 명령들이 이 방식을 따릅니다. 데이터가 네임드 볼륨(named 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 step)에 해당합니다. 둘째, 이 기능은 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는 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 서비스 및 타이머를 사용하여 매일 밤 실행하십시오.

스크래치 디렉터리에 복원하여 백업 검증하기

테스트하지 않은 백업은 추측에 불과합니다. 스크래치 디렉터리에 복원하는 작업은 1분이면 충분하며, 실제 운영 환경에는 아무런 영향을 주지 않습니다.

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;"에서 확인한 실제 운영 수치와 비슷해야 하며, 사용 중인 볼트(vault)에서 0이 될 수는 없습니다. 첨부 파일 디렉터리의 크기는 예상하는 수준과 비슷해야 합니다(첨부 파일을 업로드하지 않는 환경이라면 건너뛰어도 됩니다). 그 후 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.*을 보관한 뒤 삭제하십시오. 롤백은 디렉터리 위치를 반대로 바꾸어 동일한 세 단계를 수행하면 됩니다.

경로가 여기와 일치하지 않는다면, VPS용 Vaultwarden 설치 가이드에서 이 명령들이 가정하는 compose 파일을 확인할 수 있습니다.

백업을 저장하면 안 되는 곳

  • 데이터 폴더와 동일한 디스크에는 저장하지 마십시오. 볼륨 하나만 고장 나도 두 복사본이 모두 손실되며, 잘못된 경로에 rm -rf를 실행해도 마찬가지입니다.
  • 두 번째 볼륨이라 하더라도 동일한 서버에는 저장하지 마십시오. root 권한을 획득한 공격자는 동일한 세션 내에서 백업 데이터까지 접근할 수 있습니다.
  • 암호화하지 않은 상태로 오브젝트 스토리지에 저장하지 마십시오. 아카이브에는 이메일 주소, 비밀번호 힌트, 복구 코드, 볼트 암호문이 포함되어 있어 오프라인 공격의 대상이 될 수 있습니다.
  • 클라우드 제공업체의 스냅샷에만 의존하지 마십시오. 스냅샷은 복구가 빨라 유용하지만 서버와 동일한 계정에 귀속되므로, 계정 자체에 문제가 생기면 백업도 함께 사라집니다.

오프사이트(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이 실시간 데이터 폴더가 아닌 아카이브 디렉터리를 가리키도록 설정하십시오. 그래야 이미 검증을 마친 일관된 복사본이 업로드됩니다. 저장소 비밀번호는 보호 대상인 서버가 아닌 다른 곳에 보관하십시오. 설계상 비밀번호를 분실하면 스냅샷을 읽을 수 없습니다. 스토리지에서 지원한다면 서버에 쓰기 권한만 부여하고 삭제 권한은 주지 마십시오. 이렇게 하면 서버가 침해당하더라도 공격자가 자신의 백업 기록을 삭제할 수 없습니다. VPS에서 restic 백업 설정하기에서 저장소 구성과 일정 설정을 상세히 다루며, 아직 도구를 선택하지 않았다면 restic과 BorgBackup 비교를 참고하십시오.

정기적인 복구 테스트 수행

한 달에 하루를 정해 정기적으로 테스트를 수행합니다. restic restore latest --tag vaultwarden --target /tmp/vw-check을 사용하여 가장 최신 스냅샷을 임시 디렉터리로 가져온 뒤, 동일한 PRAGMA integrity_check을 실행하고 행 개수를 확인하여 날짜와 결과를 기록합니다. 6개월 동안 한 번도 복구해 보지 않은 백업은 상태를 알 수 없는 백업과 다름없습니다. 장애가 발생한 최악의 상황에서 백업 상태를 확인하게 되는 일은 피해야 합니다.

일 년에 한 번은 전체 복구 과정을 수행합니다. 여분의 포트에서 두 번째 Vaultwarden 컨테이너를 시작하고 복구된 데이터 폴더를 연결한 뒤, 실제 계정으로 로그인합니다. 이 과정은 행 개수 확인만으로는 알 수 없는 마스터 비밀번호 경로의 정상 작동 여부를 처음부터 끝까지 검증합니다. 동일한 주기로 restic check --read-data-subset=10%를 실행하면 저장된 데이터가 단순히 목록에 존재하는 것을 넘어 실제로 읽기 가능한 상태인지 확인할 수 있습니다.

FAQ

Vaultwarden이 실행 중일 때 cp 명령어로 db.sqlite3를 복사해도 됩니까?

아니요. 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를 복원하면 사용자는 복원 사실을 인지하지 못합니다.

백업 아카이브를 그대로 객체 스토리지에 업로드해도 안전합니까?

아니요. 항목 이름, 비밀번호, 메모는 암호문 상태이지만 이메일 주소, 계정 이름, 비밀번호 힌트, 2단계 인증 복구 코드는 데이터베이스 내에서 평문으로 존재하며, 오프라인 공격자는 암호문을 무제한으로 해독 시도할 수 있습니다. 아카이브가 서버를 떠나기 전에 암호화하십시오. restic 저장소는 이를 자동으로 처리하며, 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 파일과 함께 하나의 아카이브에 포함되어야 하며, 동일한 작업 과정에서 암호화되어 원본 서버가 아닌 다른 곳에 저장되어야 합니다.