NordBastion의 북극곰 마스코트가 어두운 노르딕 석조 금고 안, 서버 랙 옆에 서서 빛나는 시안색 열쇠를 들어 올리고 있다. 자물쇠 무늬가 새겨진 다섯 개의 시안색 홀로그램 블록으로 이루어진 사슬이 룬 문자가 새겨진 아치를 통과해, 희미한 오로라 아래 멀리 보이는 두 번째 서버 랙을 향해 호를 그리며 뻗어 있다
방법 가이드 · Opsec·읽기 16분 · 실습 45분

암호화 VPS 백업.
오프사이트에, 중복 제거되어, 손댈 수 없는 곳에.

암호화 VPS 백업을 완성하는 여섯 단계 — 보호되지 않은 서버에서 시작해 두 번째 관할권에 있는 두 번째 머신의 검증된 암호화 저장소까지: restic과 Borg 비교, 깨끗하게 복원되는 데이터베이스 덤프, systemd 타이머, 그리고 침해된 서버도 지울 수 없는 추가 전용 저장소. 두 번째 서버 비용은 월 $3.90. Debian 12에서 테스트를 마쳤습니다.

6단계
  1. 01

    인벤토리

    재구축할 수 없는 것

  2. 02

    설치

    restic 또는 Borg

  3. 03

    목적지

    두 번째 요새

  4. 04

    초기화

    저장소 + 첫 실행

  5. 05

    자동화

    덤프, 타이머, 보존

  6. 06

    훈련

    복원하고, 시간을 재십시오

시작하기 전에 · 이 가이드의 목적

데이터가 사라지는 네 가지 방식. 그중 고장 난 디스크는 단 하나뿐입니다.

백업에 관한 논쟁은 거의 항상 첫 1분 만에 어긋납니다. 대화하는 두 사람이 서로 다른 장애를 머릿속에 그리고 있기 때문입니다. 한 사람은 죽은 드라이브를 떠올리고, 다른 사람은 계정이 사라진 어느 화요일 아침을 떠올립니다. 둘은 서로 다른 답을 필요로 하며, 첫 번째 장애에서만 살아남는 구성은 두 번째 장애로 시험받기 전까지는 안전해 보일 뿐입니다.

설계 단계에서 대비할 가치가 있는 장애 모드는 네 가지이며, 이들은 하나의 변주가 아닙니다 — 각각 서로 다른 방어 수단을 무력화합니다.

하드웨어. NVMe가 고장 나거나, 호스트 노드가 죽거나, 어레이에서 두 멤버가 동시에 빠질 수 있습니다. 모두가 대비하는 장애이지만, 최신 가상화 인프라에서는 가장 드문 장애이기도 합니다. 중복 스토리지가 이를 처리합니다. 중복 스토리지가 처리하는 것은 그것뿐이며, 바로 그래서 RAID는 백업이 아니고 한 번도 백업이었던 적이 없습니다: RAID는 모든 쓰기를 충실히 복제하는데, “모두 삭제”라는 쓰기 역시 예외가 아닙니다.

사람. 운영 환경에 대고 실행한 마이그레이션 스크립트. 엉뚱한 터미널에서 실행한 DROP TABLE. 인수 순서가 뒤바뀐 rsync. 이것이 실제 데이터 손실의 가장 흔한 원인이며, 압도적인 차이로 그렇습니다. 이에 대한 유일한 방어는 이력(history)입니다 — 실수가 일어나기 전의 사본을, 그 실수가 닿지 않은 곳에 보관하는 것입니다. 핵심 재료는 시간입니다: 유일한 사본이 30초 전 상태의 미러라면, 그 사본에는 이미 그 실수가 포함되어 있습니다.

악의적 행위. 누군가 루트 권한을 획득하거나, 패치되지 않은 애플리케이션을 통해 랜섬웨어가 침투합니다. 최신 랜섬웨어가 가장 먼저 하는 일은 백업 설정 — 자격 증명, 마운트된 공유, 클라우드 키 — 을 찾아 파괴하는 것입니다. 정상 작동하는 복원이야말로 재앙을 반나절짜리 사건으로 바꾸기 때문입니다. 삭제 권한이 있는 백업 자격 증명은 공격자가 그대로 물려받는 백업 자격 증명입니다. 이것이 바로 이 가이드의 추가 전용 챕터가 존재하는 이유입니다.

수탁형. 아무것도 고장 나지 않았고 공격도 없었습니다 — 그저 접근 권한을 잃었을 뿐입니다. 자동화된 사기 탐지 신호로 인한 계정 정지, 여행 중 결제 실패, 분쟁, 컴플라이언스 심사, 제공업체에 송달된 명령. 머신은 멀쩡하지만 닿을 수 없고, 그 계정 안의 모든 스냅샷도 함께 닿을 수 없게 됩니다. 이것이 바로 한 제공업체 안에서 아무리 이중화를 해도 대응할 수 없는 장애이며, 제대로 된 백업이 다른 지붕 아래에 있어야 하는 이유입니다.

방어 하드웨어 인적 오류 랜섬웨어 계정 상실
RAID / 중복 스토리지아니오아니오아니오
제공업체 스냅샷, 동일 계정부분적으로아니오아니오
오프사이트 저장소, 쓰기 가능한 키아니오
오프사이트 저장소, 추가 전용, 다른 관할권

마지막 행이 바로 이 가이드의 나머지 부분이 만들어가는 결과입니다. 바로 위 행보다 더 비싸지도 않습니다 — 추가 전용 설정은 무료이고, 두 번째 관할권도 첫 번째와 똑같이 $3.90입니다.

3-2-1 규칙을 단일 VPS에 맞게 다시 설명합니다. 데이터 사본 세 개를, 서로 다른 두 종류의 스토리지에, 그중 하나는 오프사이트에 둡니다. 이 고전적인 공식은 테이프와 오피스 서버 시대에서 왔지만, 문구보다 정신이 더 잘 살아남습니다. 단일 셀프호스팅 서버에 적용하면 이렇게 됩니다: VPS 위의 실제 데이터, 어딘가 다른 곳의 두 번째 머신에 있는 저장소, 그리고 — 진짜로 대체 불가능한 것에 한해 — 집에 있는 디스크나 서랍 속에 직접 보관하는 세 번째 사본. 실제로 중요한 숫자는 3이 아닙니다. 데이터가 사라지기까지 얼마나 많은 독립적인 요소가 동시에 잘못되어야 하는가입니다. 갖출 가치가 있는 최소치는 2입니다.

시작하기 전에 · 도구 선택

restic, Borg, 또는 rsync. 이 중 둘만이 백업입니다.

여러분이 찾는 범주는 중복 제거·암호화 스냅샷 도구라고 불립니다. 모든 파일을 콘텐츠 기반 청크로 분할하고, 데이터를 소유한 머신에서 각 청크를 암호화한 다음, 몇 개의 스냅샷이 참조하든 단 한 번만 저장합니다. 이로써 세 가지 속성을 동시에 얻습니다: 하나 분량에 가까운 공간으로 얻는 수많은 복원 지점, 소스를 절대 벗어나지 않는 평문, 그리고 변경분만 이동하는 전송.

이 분야는 두 프로그램이 지배하고 있으며 둘 다 훌륭합니다. restic은 다양한 스토리지 백엔드를 지원하는 단일 정적 Go 바이너리입니다. BorgBackup은 원단(far end)에 있는 자기 자신의 사본과 SSH로 통신하는 Python 프로그램입니다. 아래 내용은 둘 사이의 솔직한 차이점입니다.

속성 restic BorgBackup rsync / rclone
클라이언트 측 암호화항상 켜짐, 선택 사항 아님항상 켜짐, 선택 사항 아님rclone crypt 원격 저장소를 통해서만
중복 제거콘텐츠 기반 청크콘텐츠 기반 청크없음, 또는 하드링크 트리
스냅샷 기록예, 보존 정책과 함께예, 보존 정책과 함께지금 이 순간의 미러
목적지 요구 사항필요 없음 — SFTP, S3, REST, rclone양쪽 모두에 설치된 BorgSSH 계정 또는 API
추가 전용 강제rest-server 또는 오브젝트 락을 통해일반 SSH 위에 내장됨없음
적합한 대상거의 모든 사람, 대부분의 백엔드직접 소유한 SSH 서버, 추가 전용미러링, 백업 아님

rsync가 이 표에 있는 이유는 대부분의 사람이 가장 먼저 손을 대는 도구이면서, 동시에 명백히 잘못된 도구이기 때문입니다: 미러는 삭제까지 충실하게 그대로 복제하며, 저장 시 암호화된 디스크의 미러는 그 자체로는 어디에서도 암호화되어 있지 않습니다. rsync는 아카이브 자체가 아니라, 완성된 아카이브를 옮기는 수단으로서 백업 안에서 제자리를 찾습니다.

이 가이드는 명령어가 다른 모든 지점에서 restic과 Borg를 함께 보여주므로, 둘 중 어느 쪽을 쓰든 그대로 따라갈 수 있습니다. 예제, 타이머, 스크립트처럼 하나만 선택해야 하는 경우에는 restic을 사용합니다. 그러면 목적지 서버에는 SSH 계정 하나만 있으면 되고, 두 번째 머신을 계속 단순하게 유지할 수 있기 때문입니다.

1단계 · 인벤토리

재구축할 수 없는 것. 자동화하기 전에 먼저 적어 두십시오.

파일시스템 전체를 백업하는 것은 나름대로 정당화할 수 있는 선택이지만, 동시에 낭비이기도 합니다. 서버 대부분은 패키지 관리자 한 번이면 다시 만들어낼 수 있으며, 거기서 나르는 기가바이트 하나하나가 저장소의 정리(prune)를 더 느리게, 보관 비용을 더 비싸게, 급할 때의 검색을 더 느리게 만듭니다. 유용한 접근은 그 반대입니다: 새로 설치했을 때 되돌려받지 못할 것들만 나열하는 것입니다.

일반적인 셀프호스팅 VPS라면 그 목록은 짧으며, 언제나 다음 다섯 가지를 포함합니다:

  • 01데이터베이스 내용. 데이터 디렉터리가 아니라 덤프입니다. 이유는 5단계에서 다룹니다.
  • 02사용자가 업로드한 파일. Docker 볼륨, 업로드나 미디어 디렉터리, 메일 저장소. 대개 용량의 대부분을 차지합니다.
  • 03설정과 비밀 값. Compose 파일, .env 파일, 암호화 키, API 토큰. 용량은 작지만, 두 시간짜리 복원과 이틀짜리 복원의 차이를 만듭니다.
  • 04직접 수정한 시스템 상태. /etc 디렉터리 전체는 통째로 포함해도 될 만큼 작습니다 — sshd 설정, 방화벽 규칙, systemd 유닛, cron 항목, 그리고 저녁 시간을 통째로 들여 구성한 메일 라우팅까지.
  • 05신원이 결부된 모든 것. Tor 히든 서비스 키, Matrix 서명 키, WireGuard 개인 키, 노드 지갑. 이 중 하나라도 잃으면 서비스는 돌아오지 않습니다 — 같은 이름을 가진 다른 서비스가 돌아올 뿐입니다.

그 목록을 소스 서버에 두 개의 파일로 나누십시오. 포함 목록은 의도를 명확히 드러내고, 제외 목록은 잡음을 걸러냅니다. 두 파일 모두 백업 명령어가 읽으며, 둘 다 백업 대상 자체에 포함되어야 합니다.

/etc/backup/include.txt

/etc
/opt
/root
/var/backups
/var/lib/docker/volumes
/home

/etc/backup/exclude.txt

# Caches and rebuildable artefacts
**/node_modules
**/.cache
**/*.tmp
/var/lib/docker/volumes/*/_data/cache
# Log noise — keep the config, drop the volume
/var/log/journal
# Never back up the mount point you restore into
/mnt/restore

그 Docker 줄에 대해 두 가지를 짚어두겠습니다. 다들 여기서 걸려 넘어지기 때문입니다. 네임드 볼륨은 /var/lib/docker/volumes 아래에 있으며 여러분이 원하는 대상이 바로 이것입니다. 바인드 마운트는 여러분이 지정한 위치에 있는데, 보통 Compose 파일 옆이며 /opt로 커버됩니다. 컨테이너 이미지는 데이터가 아닙니다 — pull 한 번이면 다시 받아올 수 있으며, 이를 백업에 포함시키는 것은 필요 없는 기가바이트를 짊어지는 것입니다.

마지막으로, 무언가를 그 위에 쌓아 올리기 전에 결과를 직접 측정하십시오. 이 숫자가 어떤 저장소 등급을 주문해야 하는지, 그리고 첫 실행이 4분이 걸릴지 40분이 걸릴지를 알려줍니다:

du -sh --exclude=/var/lib/docker/overlay2 /etc /opt /root /home /var/lib/docker/volumes
2단계 · 설치

소스에는 바이너리 하나. 그 외에는 아무것도 바뀌지 않습니다.

데이터를 소유한 머신에 도구를 설치하십시오. Debian과 Ubuntu 모두에 두 도구가 패키지로 제공되지만, restic의 경우 배포판 패키지가 한두 릴리스 뒤처져 있는 경우가 많습니다 — 저장소 기능과 성능 개선이 포인트 릴리스에서 이루어지므로 이는 중요합니다. 패키지를 설치한 다음, restic이 스스로 그 자리에서 업데이트하도록 두십시오:

apt update && apt install -y restic
restic self-update
restic version

Borg의 경우 배포판 패키지가 올바른 선택입니다. 소스의 버전과 목적지의 버전이 서로를 이해해야 하는데, 같은 배포판 릴리스에서 나온 짝을 맞춘 쌍을 쓰는 것이 이를 가장 덜 고통스럽게 얻는 방법이기 때문입니다:

apt install -y borgbackup
borg --version

저장소에 수년치 이력을 맡기기 전에 메이저 버전에 대해 한마디. Borg의 저장소 형식은 메이저 라인 사이에서 변경되었으며, 그 경계를 넘어 저장소를 옮기는 것은 업그레이드가 아니라 변환(conversion)입니다. 설치하려는 버전의 릴리스 노트를 읽고, 목적지를 소스와 같은 메이저 라인으로 유지하십시오. restic은 릴리스 전반에 걸쳐 이전 버전과 호환되며 새 기능은 명시적인 저장소 버전 뒤에 추가되므로, 이에 상응하는 우려는 더 작습니다.

두 도구 모두 소스에 데몬도, 에이전트도, 열린 포트도 필요로 하지 않습니다. 이는 분명히 짚고 넘어갈 가치가 있습니다: 지금 설치하는 백업 시스템은 그것이 보호하는 머신에 어떤 리스닝 서비스도, 새로운 공격 표면도 추가하지 않습니다. 하는 일은 모두 일정에 따라 SSH를 통해 나가는 아웃바운드 트래픽뿐입니다.

3단계 · 목적지

두 번째 요새, 그리고 삭제할 수 없는 키. 여기가 정말 중요한 단계입니다.

저장소 호스트가 할 일은 하나뿐이며, 요구 사항도 거의 없습니다. 코어는 필요 없습니다. 여러분의 데이터를 절대 읽지 않으니까요 — 열 수 없는 암호화된 블롭을 보관할 뿐입니다. 메모리도 필요 없습니다. 중복 제거 작업은 소스 쪽에서 이루어지기 때문입니다. 필요한 것은 디스크, 고정된 주소, 그리고 소스 머신의 문제가 따라오지 않는 위치뿐입니다.

패널에서: 주문 → VPS → Sentinel, 이미지는 Debian 12, 그리고 — 바로 이것이 핵심입니다 — 소스가 실행 중인 요새와는 다른 요새를 선택하십시오. 작업은 Helsinki에서, 보관은 Reykjavík에서. 계정을 개설하는 데 이메일 주소가 필요하지 않으며, 청구서는 Monero, Bitcoin, Lightning, 그리고 그 밖에 지원되는 다른 자산으로 결제됩니다. 즉, 두 번째 머신이 첫 번째 머신에서 그토록 신경 썼던 신원을 다시 끌어들이지 않는다는 뜻입니다.

저장소 대상 월별 스토리지 보관하는 작업 세트 복원 비용
Sentinel$3.90120 GB NVMe~30 GB없음 — 무제한
Garrison$7.90240 GB NVMe~70 GB없음 — 무제한
Ravelin$16.90480 GB NVMe~150 GB없음 — 무제한
Bulwark$32.90960 GB NVMe~300 GB없음 — 무제한
S3급 오브젝트 스토리지~$0.023/GB탄력적무엇이든종량제 아웃바운드

작업 세트 열은 일반적인 변경률로 1년간 보관되는 일일 스냅샷을 가정합니다. 거의 변하지 않는 파일이 대부분인 저장소는 이보다 훨씬 더 오래 버티고, 대용량 바이너리가 자주 다시 쓰이는 저장소는 그만큼 빨리 한계에 도달합니다. 마지막 행은 규모 감을 위한 것이자, 최악의 날이 오기 전까지는 다들 잊고 있는 세부 사항을 위한 것이기도 합니다: 오브젝트 스토리지는 다운로드에도 비용을 청구하며, 다운로드야말로 비상 상황에서 실제로 하게 되는 일입니다.

목적지에서는 — 권한 없는 사용자 하나와 디렉터리 하나.

adduser --disabled-password --gecos "" backup
install -d -m 0700 -o backup -g backup /srv/restic/web01

백업 사용자는 비밀번호도, sudo 권한도, 로그인할 대상도 없습니다. 오직 디렉터리를 소유하기 위해 존재할 뿐입니다. 여러 소스 머신을 백업한다면 소스마다 별도의 디렉터리를 부여하십시오 — 소스마다 별도의 저장소를 두면 한 서버가 침해되어도 다른 서버의 이력에는 영향을 주지 않습니다.

소스에서는 — 다른 어떤 용도로도 쓰이지 않는 키 하나.

ssh-keygen -t ed25519 -f /root/.ssh/id_backup -N "" -C "backup:web01"
cat /root/.ssh/id_backup.pub

로그인 키를 재사용하지 마십시오. 이 키는 새벽 3시에 무인 타이머가 사용해야 하기 때문에 디스크에 암호화되지 않은 채로 존재하는데, 바로 그렇기 때문에 단 하나의 목적지와 단 하나의 용도로만 범위를 좁혀야 하는 키입니다.

다시 목적지에서 — 그 키가 할 수 있는 일을 제한하십시오. 공개 키를 /home/backup/.ssh/authorized_keys에 붙여넣되, 앞에 접두사를 추가하십시오. restic을 SFTP로 사용할 경우:

restrict,from="198.51.100.7" ssh-ed25519 AAAAC3NzaC1lZDI1... backup:web01

restrict 옵션 하나로 포트 포워딩, 에이전트 포워딩, X11, PTY 할당이 모두 꺼지고 파일 전송만 남습니다. from= 절은 키를 소스 주소에 고정하므로, 서버에서 키가 유출되더라도 다른 곳에서는 쓸모가 없습니다. Borg의 경우 한 걸음 더 나아가 명령어 자체를 강제하십시오. 바로 여기에 추가 전용 모드가 자리합니다:

command="borg serve --append-only --restrict-to-path /srv/borg/web01",restrict ssh-ed25519 AAAAC3NzaC1lZDI1... borg:web01

이 한 줄이 랜섬웨어 방어의 전부입니다: 소스가 무엇을 보내든, 목적지는 오직 borg serve만 실행하며, 오직 그 경로 안에서만, 오직 추가만 가능한 모드로만 동작합니다. 소스에서 루트 셸을 획득하더라도 이 키로는 지난달 백업을 지울 수 없습니다.

목적지 이름을 소스에서 한 번만 지정하십시오. /root/.ssh/config에 넣어 이후의 모든 명령을 짧게 만들고, 주소가 단 하나의 파일에만 존재하도록 하십시오:

Host rkv-repo
    HostName 198.51.100.42
    User backup
    IdentityFile /root/.ssh/id_backup
    IdentitiesOnly yes

그런 다음 한 번은 직접 손으로 접속해서 — ssh rkv-repo — 호스트 키를 수락하십시오. 무인 타이머는 지문 확인 프롬프트에 응답할 수 없으며, 첫 백업이 일주일 동안 조용히 멈춰 있는 것은 흔한 사고입니다. 목적지 서버에 접속한 김에, 여러분이 소유한 다른 모든 머신과 똑같이 대우하십시오: 이 서버에도 첫 시간 보안 강화 체크리스트를 적용하십시오. 이 서버는 모든 것의 사본을 담고 있으니, 온전히 한 시간을 들일 가치가 있습니다.

4단계 · 초기화

패스프레이즈, 그다음 첫 스냅샷. 이 순서대로, 그리고 해당 서버 밖에 보관하십시오.

직접 입력할 일이 없는 패스프레이즈를 생성하십시오. 스크립트가 파일에서 읽어들이므로 길이에는 아무 비용도 들지 않고, 외우기 쉽게 만들 이유도 없습니다:

openssl rand -base64 32 > /root/.restic-pass
chmod 600 /root/.restic-pass
cat /root/.restic-pass

이제 멈추고, 그 문자열을 이 서버가 아닌 다른 곳에 복사해 두십시오. 패스워드 관리자, 서랍 속 종이 메모, 두 번째 머신 — 소스를 잃어도 살아남는 곳이면 어디든 좋습니다. 이는 셀프호스팅 볼트와 자동화 엔진을 무너뜨리는 것과 똑같은 실패입니다: 모든 것을 복호화하는 키가 그 모든 것 바로 옆에 보관되어 있으면, 둘 다 함께 사라집니다. 패스프레이즈가 자신이 보호하던 바로 그 머신에만 존재했던 저장소는 백업이 아닙니다. 매우 잘 정돈된 잡음 더미일 뿐입니다.

이후의 모든 명령어가 읽어들일 환경 파일을 작성하십시오:

/etc/backup/restic.env

RESTIC_REPOSITORY=sftp:rkv-repo:/srv/restic/web01
RESTIC_PASSWORD_FILE=/root/.restic-pass
chmod 600 /etc/backup/restic.env
set -a; . /etc/backup/restic.env; set +a
restic init

Borg의 대응 개념 — repokey 모드는 암호화 키를 패스프레이즈로 보호한 채 저장소 내부에 보관합니다. 편리하지만, 바로 그 이유 때문에 사본도 함께 내보내야 합니다:

export BORG_REPO=ssh://borg@rkv-repo/srv/borg/web01
export BORG_PASSCOMMAND="cat /root/.restic-pass"
borg init --encryption=repokey-blake2
borg key export ::  /root/borg-key.txt   # copy this off the server too

첫 스냅샷. 직접 손으로 실행해서 지켜보고, 자동화하기 전에 끝까지 완료되도록 두십시오. 이번 실행은 느립니다 — 모든 청크가 새로 생성되기 때문입니다 — 그리고 포함 목록이 올바른지 알려주는 실행이기도 합니다:

restic backup \
  --files-from /etc/backup/include.txt \
  --exclude-file /etc/backup/exclude.txt \
  --tag nightly --one-file-system --verbose

소스가 트래픽을 처리하는 중이고 업로드가 회선을 포화시키고 있다면 속도를 제한하십시오. 이 플래그는 초당 키비바이트(KiB/s) 단위를 받으므로, 20000이면 대략 20 MB/s입니다:

restic backup --limit-upload 20000 --files-from /etc/backup/include.txt

작업이 끝나면 실제로 무엇을 얻었는지 확인하십시오. 다음 세 명령어를 지금 실행해보고, 이 모든 걸 다 잊어버릴 6개월 뒤에 다시 한번 실행해보십시오:

restic snapshots                    # the list, with dates and tags
restic stats latest                 # what the newest snapshot contains
restic ls latest /etc/backup        # confirm a path you expected is in there

세 번째 명령어는 잠시 짚고 넘어갈 가치가 있습니다. 망가진 백업의 절반은 사실 전혀 망가지지 않았습니다 — 잘못된 디렉터리를 완전하고 정상적으로 담고 있는 저장소일 뿐입니다. 첫 스냅샷 시점에 예상한 경로가 실제로 나열되는지 확인하면, 포함 목록이 아직 머릿속에 생생할 때 이를 바로 잡아낼 수 있습니다.

5단계 · 자동화

먼저 덤프하고, 그다음 스냅샷을 뜨십시오. 스크립트 하나, 타이머 하나, 보존 정책 하나.

데이터베이스 규칙, 한 번만 말씀드립니다. 실행 중인 데이터베이스 엔진은 상태를 메모리에 유지하다가 자신만의 시점에 디스크에 기록합니다. 그렇게 하는 도중에 파일을 읽으면 서로 다른 여러 순간이 뒤섞인 페이지 집합을 얻게 됩니다 — 복원될 수도, 손상된 채로 복원될 수도, 혹은 멀쩡히 열리지만 지난 한 시간이 조용히 빠진 상태로 복원될 수도 있는 파일입니다. 외부에서는 어느 쪽인지 알 방법이 없습니다. 그래서 백업은 운영 중인 파일을 절대 건드리지 않습니다: 먼저 덤프를 디스크에 기록하고, 백업은 그 덤프를 가져갑니다.

모든 것을 스크립트 하나에 담습니다. /usr/local/sbin/nb-backup.sh에 두고 실행 권한을 부여한 다음, 그 자체도 백업에 포함시키십시오:

#!/bin/sh
set -eu

# ---- 1. Consistent dumps, written where the include list will find them.
install -d -m 0700 /var/backups/db

# PostgreSQL in a container:
docker exec -t app-db pg_dumpall -U postgres | gzip -9 > /var/backups/db/postgres.sql.gz

# MariaDB / MySQL, InnoDB tables:
# docker exec -t mail-db mariadb-dump --single-transaction --all-databases \
#   | gzip -9 > /var/backups/db/mariadb.sql.gz

# SQLite — never a plain file copy:
# sqlite3 /opt/app/data/app.db ".backup '/var/backups/db/app.db'"

# ---- 2. The snapshot.
restic backup \
  --files-from /etc/backup/include.txt \
  --exclude-file /etc/backup/exclude.txt \
  --tag nightly --one-file-system

# ---- 3. Retention, then reclaim the space it freed.
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune

# ---- 4. Structural check every night; read 5% of the data on Sundays.
restic check
[ "$(date +%u)" = "7" ] && restic check --read-data-subset=5%

echo "backup ok: $(date -u +%FT%TZ)"

그 스크립트 안의 세 가지 세부 사항은 겉보기보다 훨씬 많은 일을 합니다. 맨 위의 set -eu는 덤프가 실패하면 실행을 중단시켜, 어제 덤프를 앞으로 6개월간 조용히 스냅샷으로 남기는 일을 막아줍니다. forget과 prune이 한 명령어로 묶여 있는 이유는, 공간을 회수하지 않는 보존 정책은 결국 가득 차는 디스크이기 때문입니다. 그리고 일요일 점검은 인덱스만이 아니라 실제 데이터의 일부를 읽습니다 — 저장소 손상은 드물고 조용하며, 이를 찾아내는 유일한 방법은 실제로 읽어보는 것뿐입니다.

타이머. 작은 유닛 파일 두 개. 여기서는 cron이 아니라 systemd가 올바른 스케줄러입니다. 타이머는 Persistent 옵션을 제공하기 때문입니다 — 머신이 꺼져 있던 동안 놓친 실행은 조용히 건너뛰어지는 대신 다음 부팅 시 실행됩니다 — 그리고 journalctl이 모든 실행 이력을 보관해주기 때문이기도 합니다.

/etc/systemd/system/nb-backup.service

[Unit]
Description=Nightly encrypted offsite backup
Wants=network-online.target
After=network-online.target docker.service

[Service]
Type=oneshot
EnvironmentFile=/etc/backup/restic.env
ExecStart=/usr/local/sbin/nb-backup.sh
Nice=10
IOSchedulingClass=idle

/etc/systemd/system/nb-backup.timer

[Unit]
Description=Nightly encrypted offsite backup

[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=45m
Persistent=true

[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now nb-backup.timer
systemctl list-timers nb-backup.timer
systemctl start nb-backup.service && journalctl -u nb-backup.service -n 40 --no-pager

무작위 지연은 장식이 아닙니다. 인터넷상의 모든 셀프호스팅 서버는 정확히 03:00에 유지보수를 실행하며, 로그 로테이션, 인증서 갱신, 컨테이너 업데이트와 정확히 같은 초에 시작하는 백업은 밤새 이들과 I/O를 두고 싸우게 됩니다. 시작 시각을 45분에 걸쳐 분산시키는 데는 아무 비용도 들지 않으면서, 원인 모를 밤샘 지연이라는 문제 유형 전체를 없애줍니다.

그런 다음, 실패를 요란하게 만드십시오. 이것은 모두가 건너뛰는 단계이지만, 위의 모든 것이 할 가치가 있었는지를 결정짓는 단계이기도 합니다. 작동을 멈춘 백업은 아무것도 알려주지 않습니다: 타이머는 계속 실행되고, 서비스는 계속 실패하며, 마지막으로 정상이었던 스냅샷은 점점 과거로 밀려나는데도 대시보드는 계속 초록색입니다. 실제로 눈에 띄는 알림을 보내는 OnFailure= 유닛을 서비스에 추가하고, 한 달에 한 번 스냅샷 목록 맨 위를 확인하십시오. 한 줄, 그리고 하나의 습관입니다.

restic snapshots --latest 3        # is the newest one from last night?
systemctl list-timers nb-backup.timer   # is the timer still armed?
6단계 · 훈련

지금, 아무 일도 터지지 않은 상태에서 복원해 보십시오. 검증되지 않은 백업은 소문에 불과합니다.

훈련은 두 가지이며, 서로 다른 질문에 답합니다. 작은 훈련은 “데이터가 실제로 그 안에 있고 읽을 수 있는가?”를 묻고 90초가 걸립니다. 큰 훈련은 “실제로 서비스를 복구하는 데 얼마나 걸리는가?”를 묻고 1년에 한 번, 반나절이 걸립니다. 작은 훈련은 매달 하십시오. 큰 훈련은 최소 한 번은 하십시오. 그 답은 사람들이 짐작하는 것과는 언제나 다르기 때문입니다.

작은 훈련. 디렉터리 하나와 덤프 하나를 임시 경로로 가져온 다음, 실제 운영 중인 것과 비교하십시오:

install -d -m 0700 /mnt/restore
restic restore latest --target /mnt/restore --include /etc/backup
restic restore latest --target /mnt/restore --include /var/backups/db

diff -r /etc/backup /mnt/restore/etc/backup && echo "config identical"
gzip -t /mnt/restore/var/backups/db/postgres.sql.gz && echo "dump readable"

도구가 지원한다면 이보다 더 나은 방법이 있습니다: 저장소를 읽기 전용으로 마운트하고 파일시스템처럼 탐색하는 것입니다. 모든 스냅샷이 날짜가 붙은 디렉터리로 나타나며, 아무것도 복원하지 않고도 ls와 cat만으로 이력을 훑어볼 수 있습니다. 마운트를 수행하는 머신에는 FUSE가 필요합니다:

restic mount /mnt/snapshots      # then: ls /mnt/snapshots/snapshots/latest/
borg mount ::  /mnt/snapshots     # the Borg equivalent

큰 훈련. 시간 단위로 임시(scratch) VPS를 대여하고, 저장소만으로 그 위에 서비스를 재구축하십시오 — 기억에 의존한 메모도, 운영 중인 서버에서 복사한 파일도 없이. 문서화된 런북을 기준으로 작업하고, 진행하면서 런북을 수정하십시오. 발견하는 빈틈이야말로 이 훈련의 핵심이기 때문입니다. 흔히 발견되는 문제들을 빈도순으로 나열하면: 패스프레이즈가 죽은 머신에만 있었다, DNS 레코드가 어디에도 기록되지 않았다, 바인드 마운트된 디렉터리가 포함 경로 밖에 있었다, 복원에 필요한 패키지 버전이 더 이상 기본값이 아니었다, 어떤 컨테이너를 먼저 시작해야 하는지 아무도 몰랐다.

시간을 재십시오. 그 숫자를 날짜와 함께 런북 맨 위에 적어 두십시오. 저장소 크기도, 스냅샷 개수도 아닌 바로 그 숫자야말로, 서비스가 실제로 얼마나 보호되고 있는지를 보여주는 유일하고 정직한 척도입니다.

그리고 데이터를 이따금 실제로 읽어보십시오. 매일 밤 실행되는 점검은 구조만 검증합니다: 인덱스가 스스로와 일치하는지, 누락된 블롭이 없는지. 블롭 자체는 읽지 않습니다. 한 달에 한 번, 혹은 일요일 실행 때 일부를 실제로 읽으십시오 — 5%씩 돌아가며 읽으면 몇 달에 걸쳐 저장소 전체를 커버하면서도 밤을 새울 일은 없습니다:

restic check --read-data-subset=5%
borg check --verify-data              # the Borg equivalent, slower and thorough
어려운 부분 · 추가 전용

쓸 수는 있지만 지울 수는 없는 키. 힘든 한 주와 폐업 사이의 차이입니다.

지금까지의 모든 내용은 사고를 방어합니다. 이번 장은 공격자를 방어하며, 설계 문제는 한번 보고 나면 불편해집니다: 소스 머신은 매일 밤 저장소에 접속해야 하므로, 소스 머신은 저장소에 대한 작동하는 자격 증명을 가지고 있어야 합니다. 소스를 장악한 자가 그 자격 증명도 장악합니다. 자격 증명이 삭제할 수 있다면, 공격자도 삭제할 수 있습니다 — 그리고 최신 랜섬웨어는 무엇이든 암호화하기 전에 정확히 바로 이 일을 의도적으로 수행합니다. 복원 가능한 피해자는 몸값을 지불하지 않기 때문입니다.

해결책은 더 나은 비밀번호가 아닙니다. 비대칭 권한입니다: 소스는 데이터를 추가할 수는 있지만 제거할 수는 없습니다. 이를 구현하는 세 가지 방법을, 제대로 구현하기 쉬운 순서대로 소개합니다.

첫째 — SSH를 통한 강제 추가 전용 방식. 이것은 Borg의 홈그라운드이며, 여러분이 소유한 머신에서 얻을 수 있는 가장 깔끔한 답입니다. 3단계에서 만든 authorized_keys 항목이 borg serve --append-only를 강제하므로, 근단(near end)이 무엇을 요청하든 원단(far end)은 삭제를 거부합니다. 정리(pruning)는 여전히 필요하지만, 이는 목적지 쪽에서, 소스가 영향을 미칠 수 없는 일정에 따라, 소스가 접근할 수 없는 계정으로 실행됩니다. 소스에서 루트 권한을 얻은 공격자는 저장소를 쓰레기로 채울 수는 있어도, 자신이 도착하기 전날 밤의 아카이브는 지울 수 없습니다.

둘째 — 추가 전용 REST 엔드포인트. restic의 동반 도구인 rest-server에도 동일한 성질을 가진 --append-only 모드가 있습니다: 새 블롭은 받아들이지만 기존 블롭은 제거할 수 없습니다. 목적지에서 TLS 뒤에 이를 실행하고, RESTIC_REPOSITORY가 sftp: 대신 https:// URL을 가리키게 하면, 방어의 형태는 동일합니다. 목적지에 서비스 하나가 더 필요할 뿐, 얻는 보장은 똑같습니다.

셋째 — 오브젝트 락. S3 호환 스토리지에서는 버저닝과 오브젝트 락 보존 기간을 함께 쓰면, 프로그램이 아니라 스토리지 계층 자체가 강제하는 방식으로 그 기간이 끝날 때까지 삭제가 불가능해집니다. 세 가지 방법 중 가장 강력하지만 가장 비싸며, 제공업체 계정을 다시 끌어들입니다 — 이는 이 가이드 전체가 분산시키려던 바로 그 수탁 위험을 되돌려놓는 셈입니다. 세 번째 사본으로는 합리적이지만, 유일한 사본으로 쓰기에는 어색합니다.

이 중 어느 것도 맞지 않는다면 — 순수 SFTP, 즉 쓸 수 있어서 지울 수도 있는 키뿐이라면 — 그렇지 않은 척하지 말고, 소스가 전혀 손댈 수 없는 독립적인 두 번째 사본을 추가하십시오. push 대신 pull 방식을 쓰십시오: 목적지가 소스에 로그인해서 필요한 것을 읽고 저장하게 하는 것입니다. 그러면 자격 증명은 노출되지 않은 머신에 있게 되고, 소스에 있는 공격자는 백업을 가리키는 어떤 것도 찾지 못합니다. 설정하는 데 조금 더 손이 가지만, 위험을 정확히 옳은 방향으로 뒤집어 줍니다.

무엇을 선택하든, 다른 보안 통제를 검증할 때와 똑같은 방식으로 검증하십시오 — 실제로 깨뜨려 보는 것입니다. 소스에서 백업 키로 삭제를 시도해보십시오. 올바른 결과는 거부입니다.

borg delete ::name-of-an-old-archive
# → Remote: Repository is in append-only mode. Refusing to delete.
현장 노트 · 서비스별

각 서비스가 없이는 절대 되살아날 수 없는 단 하나의 파일. 그것은 좀처럼 용량이 큰 파일이 아닙니다.

모든 서비스에는 데이터도 설정도 아닌 작은 상태 조각이 하나씩 있으며, 이를 잃으면 서비스가 손상되는 것이 아니라 우연히 같은 이름을 가진 다른 서비스로 대체됩니다. 기존 링크는 끊어지고, 연합된(federated) 피어는 새 신원을 거부하며, 클라이언트는 키를 다시 발급받습니다. 이 사이트가 가이드를 제공하는 각 스택별로, 바로 이 조각들이 무엇인지 정리했습니다.

Nextcloud

데이터 디렉터리, config/config.php, 그리고 데이터베이스 덤프. 덤프를 뜨는 동안에는 유지보수 모드를 켜고, 끝나면 끄십시오 — 그러지 않으면 Nextcloud가 두 곳에 동시에 씁니다.

Vaultwarden

전체 데이터 디렉터리: 복사가 아니라 .backup으로 뜬 db.sqlite3, 그리고 rsa_key 파일, 첨부 파일과 전송 파일.

Matrix Synapse

서명 키, homeserver.yaml, 미디어 저장소, 그리고 PostgreSQL 덤프. 서명 키를 잃으면 연합(federation) ID는 영구히 사라집니다.

메일 서버

메일 저장소 자체, 가상 사용자 테이블, 그리고 DKIM 개인 키. DKIM 키를 새로 만들면 DNS가 반영될 때까지 모든 메일이 서명되지 않은 상태가 됩니다.

n8n

N8N_ENCRYPTION_KEY를 먼저, 그다음 PostgreSQL 덤프를. 이 키가 없는 데이터베이스는 모든 자격 증명을 읽을 수 없는 워크플로 목록에 불과합니다.

Tor onion 서비스

히든 서비스 디렉터리. 이 몇 개의 파일이 곧 .onion 주소입니다 — 이것이 없으면 서비스는 다른 이름으로 되살아나고, 기존의 모든 링크는 죽은 링크가 됩니다.

WireGuard

/etc/wireguard 전체. 작고 평범하지만, 5분 만의 재구축과 보유한 모든 기기의 키를 다시 발급하는 일 사이의 차이를 만듭니다.

Lightning node

시드와 채널 상태는 해당 노드 구현 방식을 그대로 따르십시오. 오래된 채널 데이터를 복원하면 자금 손실로 이어질 수 있습니다 — 일반적인 파일 복사가 아니라 해당 구현체의 백업 절차를 따르십시오.

스토리지 아래 계층

거리는 기술적인 답입니다. 관할권은 나머지 절반입니다.

백업이 왜 다른 곳에 있어야 하는지 대부분의 사람에게 물으면 화재, 홍수, 혹은 데이터센터의 정전이라는 답이 돌아옵니다. 모두 사실이고, 모두 점점 더 드물어지고 있으며, 모두 백 킬로미터의 거리만으로 해결됩니다. 그러나 2026년에 실제로 비즈니스를 무너뜨리는 장애는 행정적인 것이며, 거리만으로는 이에 아무 대응도 되지 않습니다.

계정 하나는 단일 장애점입니다. 자동화된 사기 탐지 신호로 인한 정지, 비행기를 타고 있는 동안 발생한 지불 거절(chargeback), 컴플라이언스 심사, 머신이 아니라 회사 앞으로 도착한 삭제 요청(takedown) — 이 중 무엇이든 그 계정 아래 있는 모든 서버에 동시에 영향을 미치며, 스냅샷을 보관하는 서버도 예외가 아닙니다. 기술적 이중화는 완벽했지만 무의미해집니다. 이에 대한 유일한 대응 구조는 서로 다른 두 제공업체, 혹은 적어도 서로 다른 두 법적 주체 아래의 두 계정입니다.

관할권 하나는 하나의 법적 틀입니다. 네 곳의 노르딕 요새는 서로 바꿔 쓸 수 없습니다 — 스웨덴, 핀란드, 노르웨이, 아이슬란드는 각기 조금씩 다른 것을 보호하며, 노르웨이와 아이슬란드는 아예 유럽연합 밖에 있습니다. 작업 머신과 그 저장소를 이 중 두 곳에 나누어 두면, 사본이 단순히 다른 곳에 있는 것이 아니라 완전히 독립된 두 번째 규정 체계 아래 놓이게 됩니다. 노르딕 관할권 레퍼런스에서 법령을 하나씩 살펴봅니다.

그리고 목적지는 아무것도 알지 못합니다. 바로 이것이 분리를 트레이드오프가 아니라 저렴한 선택으로 만들어 주는 이유입니다. restic과 Borg 모두 데이터가 소스를 떠나기 전에 암호화하므로, 저장소 호스트는 자신이 열 수 없는 블롭을 보관할 뿐입니다. 어떤 파일을 담고 있는지도, 몇 개인지도, 이름이 무엇인지도 알지 못합니다. 여러분은 제3자에게 신뢰를 확장하는 것이 아니라, 자기 자신조차 읽을 수 없는 디스크를 빌리는 것입니다. 이는 “완전히 신뢰하지 않는 제공업체를 백업 대상으로 써도 되는가?”라는 질문에 대한 솔직한 답이기도 합니다 — 암호화된 저장소라면, 신뢰는 거의 개입할 여지가 없습니다.

지연 시간은 문제가 되지 않습니다. Helsinki에서 Reykjavík까지는 백본상으로 30 ms, Helsinki에서 Stockholm까지는 8 ms, Stockholm에서 Oslo까지는 11 ms입니다. 매일 밤 실행되는 백업은 이 숫자들 중 어느 것에도 신경 쓰지 않으며, 디스크 처리량에 의해 제한되는 복원 역시 마찬가지입니다. 네트워크 거리가 아니라 법적 거리를 기준으로 짝을 선택하십시오 — 노르딕 지역 안에서 네트워크 거리는 반올림 오차 수준에 불과합니다.

한 가지 더 분명히 짚어둘 가치가 있습니다. 이것이 바로 이 가이드가 일반적인 시스템 관리자 블로그가 아니라 이 사이트에 실려 있는 이유이기 때문입니다: 두 번째 머신은 첫 번째 머신이 이미 답했던 모든 질문을 다시 열어놓습니다. 작업용 VPS를 신분증 없이 Monero로 결제해 주문했는데, 그것을 위한 백업 서버는 법인 카드와 여권 스캔본으로 주문했다면, 이 쌍은 정확히 더 약한 절반만큼만 식별 불가능합니다. 익명 VPS 호스팅 축 가이드는 가입, 결제, 네트워크라는 세 개의 층을 다루며, 이는 웹 서버와 마찬가지로 백업 대상에도 그대로 적용됩니다.

현장 노트 · 여섯 가지 함정

백업이 사실은 백업이 아니었던 여섯 가지 경우. 모두 최악의 날에야 발견됩니다.

함정 01 · 되돌릴 수 없음

패스프레이즈가 죽은 머신에만 있었습니다

저장소는 온전하고, 암호화되어 있으며, 영구히 읽을 수 없는 상태입니다. 첫 스냅샷을 뜨기 전에 패스프레이즈를 — Borg의 경우 내보낸 키까지 — 패스워드 관리자와 종이에 보관하십시오.

함정 02 · 일관성

데이터베이스는 복원되지만, 첫 쿼리에서 실패합니다

덤프하지 않고 실행 중인 데이터 파일을 그대로 복사했습니다. 백업 전 단계에서 덤프를 작성하고 그 덤프를 백업하십시오 — 포함 목록이 실행 중인 엔진의 데이터 디렉터리를 직접 가리키게 해서는 절대 안 됩니다.

함정 03 · 폭발 반경

침입자가 백업부터 삭제했습니다

침해된 서버에 있는 쓰기 가능한 자격 증명은 곧 공격자를 위한 쓰기 가능한 자격 증명입니다. 목적지에서 추가 전용을 강제하거나, 두 번째 사본을 push가 아니라 pull 방식으로 가져오십시오.

함정 04 · 용량

저장소가 서버보다 더 큽니다

prune 없이 forget만 하면 스냅샷에 표시만 남을 뿐 공간은 전혀 회수되지 않습니다. 둘을 함께 실행하고, 목적지에 여유 공간을 유지하며, 컨테이너 이미지나 node_modules는 절대 백업하지 마십시오.

함정 05 · 침묵

마지막으로 정상이었던 스냅샷이 다섯 달 전 것입니다

업그레이드 후 타이머가 실패했는데 아무 알림도 없었습니다. OnFailure= 유닛을 추가하고, 한 달에 한 번 스냅샷 목록 맨 위를 확인하십시오 — 10초면 끝납니다.

함정 06 · 범위

디렉터리 하나만 빼고 모든 것이 복원됨

/opt 밖에 있는 바인드 마운트, 마이그레이션 도중 옮겨진 볼륨, 의도한 것보다 더 많이 걸린 제외 패턴. 새 스냅샷마다 알고 있는 경로 하나를 나열해 확인하고, 스택을 변경할 때마다 포함 파일을 다시 읽어보십시오.

자주 묻는 질문 · VPS 백업

질문들, 답변됨.

설정하는 동안 나오는 열 가지 질문 — 그리고 나중에 딱 한 번 나오는 두 가지 질문.

제공업체 스냅샷도 백업인가요?

아닙니다 — 그것은 롤백이며, 유용한 롤백이긴 하지만, 백업이 존재하는 바로 그 순간에 실패합니다. 스냅샷은 그것이 복제하는 서버와 같은 제공업체 계정 안에 존재합니다. 계정이 정지되거나, 결제가 분쟁 중이거나, 지원 담당자가 실수로 삭제 버튼을 누르거나, 공격자가 패널 자격 증명을 손에 넣으면 스냅샷도 그 머신과 함께 사라집니다. 또한 세밀함도 전혀 제공하지 않습니다: 어제 삭제된 파일 하나만 꺼낼 수는 없고, 디스크 전체를 되돌리면서 그 이후의 모든 것을 잃게 될 뿐입니다. 스냅샷은 유지하십시오 — 잘못된 업그레이드를 되돌리는 가장 빠른 방법입니다 — 하지만 원래 문제가 닿지 않는 곳에 존재하는 사본과 혼동하지는 마십시오. 경험칙은 이렇습니다: 침해된 자격 증명 하나로 두 사본을 모두 파괴할 수 있다면, 사본은 사실 하나뿐입니다.

restic와 Borg 중 실제로 어떤 것을 써야 하나요?

둘 다 클라이언트 쪽에서 암호화하고, 둘 다 청크 단위로 중복 제거를 수행하며, 둘 다 성숙한 도구이고, 둘 다 제 역할을 합니다. 정적 바이너리 하나만 두고, 저장소를 SFTP, S3 호환 오브젝트 스토리지, REST 서버, 또는 rclone이 닿는 어디에든 둘 수 있으며, 목적지에는 아무것도 설치하고 싶지 않다면 restic을 선택하십시오. 목적지가 직접 관리하는 SSH 서버이고, 오브젝트 락 스토리지 없이 사용할 수 있는 가장 강력한 추가 전용 강제를 원하며, 조금 더 촘촘한 압축을 선호한다면 Borg를 선택하십시오. 결정을 가르는 실질적인 차이: Borg는 양쪽 모두에 borg 설치가 필요하며 SSH나 로컬 경로만 사용할 수 있습니다. restic은 저장소에 잠시 배타적 잠금을 거는 정리(prune) 과정이 필요합니다. 결정하기 어렵다면 restic을 쓰십시오 — 목적지에 움직이는 부품이 적다는 것은 어떤 벤치마크보다도 가치가 있습니다.

백업 서버에는 디스크가 얼마나 필요한가요?

실제로 백업하는 데이터의 크기에서 시작하십시오 — 그 데이터가 놓인 디스크의 크기가 아닙니다. 일반적인 셀프호스팅 스택은 데이터베이스와 설정 파일 몇 기가바이트에 사용자가 업로드한 파일을 더한 정도입니다. 여기서 중복 제거와 압축이 진가를 발휘합니다: 12개월 보존 정책을 적용한 20 GB 작업 세트의 일일 스냅샷은 대개 40~80 GB의 저장소 용량으로 수렴합니다. 변경된 청크만 다시 저장되기 때문입니다. Sentinel($3.90/월, 120 GB NVMe)이면 이를 여유 있게 감당합니다. 여러 서버가 같은 저장소 호스트에 백업할 때는 Garrison(240 GB, $7.90)으로, 작업 세트 자체가 수백 기가바이트에 이를 때는 Bulwark(960 GB, $32.90)로 옮기십시오. 저장소 용량을 산정한 다음 두 배로 잡으십시오 — 여유 공간이 없는 저장소는 정리(prune)할 수 없고, 정리할 수 없는 저장소는 계속 커지기만 합니다.

실행 중인 데이터베이스를 파일 복사만으로 백업해도 되나요?

파일을 복사하는 것은 가능합니다. 하지만 복원은 안 될 수도 있습니다. 여러분이 읽는 동안에도 디스크에 계속 쓰고 있는 데이터베이스는 서로 다른 여러 시점이 뒤섞인 파일 집합을 넘겨주는데, 이것이 바로 찢어진(torn) 백업의 정의입니다 — 복원하면 손상된 데이터베이스가 되거나, 더 나쁘게는 멀쩡히 열리지만 행이 조용히 빠져 있는 데이터베이스가 됩니다. 해결책은 덤프입니다: PostgreSQL은 pg_dump 또는 pg_dumpall, InnoDB 테이블을 사용하는 MariaDB와 MySQL은 mariadb-dump --single-transaction, SQLite는 sqlite3 db .backup out.db 또는 VACUUM INTO를 사용하십시오. 덤프를 파일로 저장한 다음, 그 파일을 백업하십시오. 데이터베이스가 너무 커서 매일 밤 덤프하기 어렵다면, 대안은 엔진을 잠시 정지시킨 상태에서 뜨는 파일시스템 스냅샷이나 엔진 자체의 물리 백업 도구입니다 — 하지만 단일 VPS가 감당하는 정도라면 덤프가 정답이고, 방법도 단순합니다.

백업 저장소 패스프레이즈를 분실하면 어떻게 되나요?

백업이 사라집니다. restic과 Borg 모두 클라이언트 측에서 암호화하며, 저장소를 대신 풀어줄 복구 경로도, 마스터 키도, 지원 티켓도 존재하지 않습니다. 이것이 핵심입니다: 목적지 호스트는 — 여러분 소유가 아닌 호스트를 포함해서 — 여러분의 평문을 절대 보지 못합니다. 이는 또한 패스프레이즈가 이제 그것이 보호하는 모든 것과 정확히 같은 가치를 지닌 데이터가 되었으며, 백업 대상 머신에만 존재해서는 안 된다는 뜻이기도 합니다. 패스워드 관리자에 넣고, 물리적인 사본도 어딘가에 두십시오. Borg의 경우 borg key export로 저장소 키도 함께 내보내 보관하십시오. repokey 모드에서는 키가 저장소 내부에 있으므로, 더 이상 접근할 수 없는 저장소는 키까지 함께 가져가 버립니다.

침해된 서버가 자신의 백업을 삭제하지 못하게 하려면 어떻게 해야 하나요?

추가는 되지만 제거는 안 되는 자격 증명을 부여하는 것입니다. 이는 이 훈련 전체에서 가장 중요한 설계 결정입니다. 최신 랜섬웨어는 백업 설정부터 찾아내어 그것을 따라 들어오기 때문입니다. 이를 구현하는 세 가지 방법. SSH를 통한 추가 전용: 목적지의 authorized_keys에서 명령어 borg serve --append-only --restrict-to-path /srv/borg를 강제하십시오 — 소스는 새 아카이브를 쓸 수는 있지만 기존 아카이브는 삭제할 수 없습니다. REST를 통한 추가 전용: restic의 rest-server를 --append-only 옵션으로 실행하고, 저장소가 HTTPS로 이를 가리키게 하십시오. 오브젝트 락: 버저닝과 보존 잠금이 설정된 S3 호환 버킷. 순수 SFTP는 이런 보호를 전혀 제공하지 않습니다 — 저장소에 쓸 수 있는 키는 지울 수도 있는 키이기 때문입니다 — 따라서 SFTP를 전송 수단으로 쓴다면, 목적지 쪽에서 pull 방식으로 가져오는 두 번째 사본과 짝을 지으십시오. 이 경우 공격자가 소스에서 탈취한 자격 증명은 거기까지 닿지 않습니다.

백업은 얼마나 자주 실행해야 하고, 얼마나 오래 보관해야 하나요?

질문을 반대로 던져보십시오: 다시 해야 하는 일이 얼마나 늘어나도 괜찮습니까? 그 값이 바로 여러분의 복구 시점 목표이며, 이것이 주기를 결정합니다. 거의 모든 셀프호스팅 서비스 — 메일 서버, Nextcloud, Matrix 홈서버, 워크플로 엔진 — 에는 매일 밤이 적절합니다. 데이터가 트랜잭션성이고 재입력이 불가능한 경우에는 매시간도 가치가 있습니다. 보존 정책으로는 현실과 부딪혀도 살아남는 패턴인 --keep-daily 7 --keep-weekly 4 --keep-monthly 12가 있습니다: 세밀한 되돌리기가 가능한 1주일, 주간 체크포인트가 있는 1개월, 월간 체크포인트가 있는 1년, 합쳐서 약 23개의 스냅샷입니다. 긴 꼬리(long tail)는 사람들이 예상하는 것보다 더 중요합니다. 이것이 잡아내는 장애는 죽은 디스크가 아니라, 6주 동안 아무도 알아채지 못한 손상이나 삭제이기 때문입니다.

두 번째 사본이 정말로 다른 나라에 있어야 하나요?

다른 건물은 기술적 최소 조건일 뿐이며, 다른 관할권이야말로 대부분의 사람이 건너뛰었다가 나중에 후회하는 부분입니다. 화재, 홍수, 랙 단위 장애는 거리만으로 해결됩니다. 거리로 해결되지 않는 것은 법적·상업적 문제입니다: 한 제공업체에 의한 계정 정지, 결제 분쟁, 같은 회사 산하의 모든 머신에 미치는 삭제 요청, 한 회사에 송달된 명령. 두 사본이 같은 제공업체 아래에 있다면, 편지 한 통이 둘 다에 도달합니다. 한 쌍을 두 개의 노르딕 요새로 나누면 — 작업 머신은 Helsinki에, 저장소는 Reykjavík에, 백본상으로 30 ms 거리 — 지연 시간에서는 아무 비용도 들지 않으면서 두 번째 법적 틀을 얻게 됩니다. 어느 쪽이든 데이터는 소스를 떠나기 전에 암호화되므로, 목적지는 그것을 보관하는 것만으로는 아무것도 알지 못합니다.

첫 백업은 얼마나 걸리며, 암호화가 속도를 늦추나요?

암호화는 병목이 아닙니다 — 최신 CPU는 기가비트 회선이 그 결과를 실어 나를 수 있는 속도보다 더 빠르게 AES를 처리하며, 두 도구 모두 가능한 곳에서는 하드웨어 가속을 사용합니다. 병목은 첫 업로드입니다. 모든 것이 새롭기 때문입니다: 1 Gbps 업링크에서 20 GB 작업 세트는 회선 속도로 몇 분이면 끝나지만, 목적지가 제한되어 있거나 파일 수가 많고 작다면 더 오래 걸립니다. 이후의 모든 실행은 변경된 청크만 전송하며, 일반적인 스택이라면 수십 메가바이트 수준으로 1분 이내에 끝납니다. 첫 실행이 운영 워크로드와 경합한다면 속도를 제한하십시오 — restic은 KiB/s 단위의 --limit-upload를, Borg는 --upload-ratelimit를 제공합니다 — 그리고 niceionice 아래에서 실행하십시오.

정말로 복원을 테스트해야 하나요?

예, 그리고 이는 편집증이 아니라 — 흔한 장애들이 조용히 일어나기 때문입니다. 패키지 업그레이드 후 멈춰버린 타이머. 업로드 디렉터리를 조용히 통째로 삼켜버린 제외 패턴. 백업이 한 번도 커버한 적 없는 경로에 쓰인 데이터베이스 덤프. 아무도 읽지 않는 로그 속에서 몇 주째 무결성 검사에 실패하고 있는 저장소. 이 중 어느 것도 스스로 알려오지 않습니다. 모두 파일 하나를 복원해서 들여다보는 90초 안에 발견됩니다. 매달 소규모 복원을 해보십시오 — 데이터베이스 덤프 하나와 데이터 디렉터리 하나를 임시 경로로 가져와 비교하는 것입니다 — 그리고 1년에 한 번은 새 VPS에서 전체 재구축을 시간까지 재며 해보십시오. 그 재구축에서 나온 숫자가 여러분의 진짜 복구 시간이며, 짐작했던 숫자와 같은 경우는 거의 없습니다.

두 번째 머신을 확보하십시오

두 번째 노르딕 관할권에 있는 저장소 요새. KYC 없음, 암호화폐 결제.

Sentinel — Helsinki, Stockholm, Oslo, Reykjavík에서 월 $3.90에 제공되는 120 GB NVMe. 대역폭 무제한이므로 복원에 비용이 들지 않습니다. 가입 시 이메일도, 신분증도 요구하지 않으며, 보관하는 데이터를 단 1바이트도 읽을 수 없는 목적지입니다.

마지막 검토일 · 2026-08-24 · 출처 · restic 및 BorgBackup 공식 문서, PostgreSQL과 MariaDB 백업 매뉴얼, sshd authorized_keys(5), systemd.timer(5) · 주기 · 연간