백업은 복구를 해봐야 백업이다 — 홈서버 복구 리허설 체크리스트

파일 복구·서비스 복구·전체 복구 3단계가 계단처럼 올라가며 난이도와 검증 범위가 커지는 복구 리허설 구조

이 블로그에 백업 글을 네 개 썼다. 3-2-1 전략, 하드링크 스냅샷, NAS 역할 분담, USB 디스크 함정까지. 그런데 전부 “어떻게 백업하나”에 관한 글이었다. 정작 가장 중요한 질문은 따로 있다. 그 백업, 복구돼? 복구를 한 번도 해보지 않은 백업은 백업이 아니라 “백업이 있다는 믿음”이다. 그리고 그 믿음이 가장 위험하다.

매일 도는 백업이 조용히 실패하는 법

백업 스크립트의 초록불(정상 종료)은 “데이터가 안전하다”가 아니라 “스크립트가 끝까지 실행됐다”만 보장한다. 이 둘 사이에는 생각보다 넓은 틈이 있고, 그 틈에서 백업은 에러 한 줄 없이 망가진다.

  • 빈 백업: 원본 경로에 오타가 있거나, 백업 직전 원본 디스크가 마운트 해제됐다면, rsync는 “비어 있는 원본을 충실히 복사”하고 정상 종료한다. 로그는 초록불, 내용은 0바이트.
  • 과한 제외: --exclude 패턴이 너무 넓어서 정작 중요한 폴더까지 걸러낸 경우. 백업은 빠르고 가볍게 끝나지만, 복구할 것이 거기 없다.
  • 권한·소유권 불일치: 파일은 복원됐는데 소유자·권한이 어긋나 서비스가 그 파일을 읽지 못한다. 데이터는 살았는데 서비스는 안 뜬다.
  • 일관성 깨진 DB: 실행 중인 데이터베이스 파일을 그대로 복사하면, 쓰기 도중의 어중간한 상태가 박제된다. 복원해도 DB가 손상 판정으로 기동을 거부할 수 있다.

이 네 가지의 공통점은 백업 시점에는 아무 신호도 없다는 것이다. 오직 복구를 시도해야만 드러난다. 그래서 복구는 “재해가 났을 때 하는 일”이 아니라 “재해 전에 미리 해보는 일”이어야 한다.

복구 리허설은 3개 층이다

복구를 “백업을 되돌리는 단일 행동”으로 뭉뚱그리면 안 된다. 난이도와 검증 범위가 전혀 다른 세 개의 층이 있고, 아래층 통과가 위층을 보장하지 않는다.

1층 — 파일 복구

스냅샷에서 파일 하나를 꺼내는 일이다. 하드링크 스냅샷 방식이라면 복구랄 것도 없이 그냥 복사다.

ls /mnt/hdd4t/backup/daily/                       # 날짜 목록
cp /mnt/hdd4t/backup/daily/2026-06-01/config/.bashrc ~/restore-test/

검증 포인트: 꺼낸 파일이 그 날짜의 내용이 맞는지, 그리고 깨지지 않았는지. 가장 쉽고, 그래서 가장 자주 해야 하는 층이다.

2층 — 서비스 복구

서비스 하나의 데이터 폴더를 복원해서 그 서비스가 실제로 뜨는지까지 확인한다. 여기서부터 1층이 못 잡는 것들이 드러난다 — 권한·소유권, DB 일관성, 빠진 설정 파일. 임시 디렉토리에 데이터를 복원하고, compose 파일의 마운트 경로를 그쪽으로 돌린 별도 인스턴스를 띄워보는 식이다. “파일이 있다”와 “서비스가 그 파일로 기동한다”는 다른 명제다.

3층 — 전체(베어메탈) 복구

디스크가 통째로 죽은 상황을 가정한다. 새 디스크에 OS를 깔고, 백업에서 서비스 정의·설정·크론·방화벽 규칙까지 다시 세워 서버를 처음부터 재건하는 층이다. 가장 어렵고 가장 드물게 하지만, 진짜 재해는 항상 이 층이다. 여기서 비로소 “백업에 OS 복구에 필요한 것이 다 들어 있었나”라는 질문의 답이 나온다.

분기별 복구 리허설 체크리스트

리허설은 빈도를 층마다 다르게 잡는 것이 현실적이다. 자주 하는 1층이 일상 점검이라면, 드물게 하는 3층은 연례 행사다.

권장 빈도확인 항목
1층 파일월 1회임의 날짜 스냅샷에서 파일 꺼내 내용·무결성 확인
2층 서비스분기 1회데이터 복원 후 서비스 기동, DB 읽기, 로그인까지 성공
3층 전체반기~연 1회빈 디스크/VM에 OS + 서비스 + 설정 + 크론 전부 재건

각 리허설마다 딱 하나만 기록하자: 소요 시간. “복구에 얼마나 걸리는가(RTO)“는 재해 한복판에서 알면 너무 늦다. 평온할 때 재본 그 숫자가, 진짜 장애 때 “지금 복구 중이니 3시간 뒤 정상화”라고 말할 수 있는 근거가 된다.

복구를 가로막는 함정들

리허설을 처음 해보면 대개 같은 곳에서 막힌다.

  • DB는 파일만으로 안 돈다: 데이터베이스는 실행 중 복사본이 아니라 덤프(예: PostgreSQL의 pg_dump)로 백업하고 그 덤프로 복원하는 것이 정석이다. PostgreSQL 공식 문서도 일관된 백업의 기본으로 논리 덤프와 일관성 있는 파일시스템 스냅샷을 구분해 설명한다(PostgreSQL 백업 문서).
  • 복구에 필요한 것이 백업에 없다: .env, 암호화 키, 인증서 같은 “비밀”을 보안상 백업에서 빼두었다면, 정작 복구 시 그 서비스를 못 세운다. 비밀은 별도 경로로라도 반드시 백업 대상에 포함해야 한다.
  • 권한·소유권: rsync -a(아카이브 모드)는 권한·소유권·타임스탬프를 보존하지만(rsync 매뉴얼), 복원 대상 사용자나 UID가 바뀌면 어긋난다. 복원 후 서비스 계정이 실제로 읽을 수 있는지 확인한다.
  • 복구 매체 자체의 신뢰성: 백업이 USB 브리지 뒤의 외장 디스크에 있다면, 그 연결 계층의 불안정성이 곧 복구 성공률이다(USB 디스크 함정). 복구하려고 꽂은 디스크가 그날 인식이 안 되면 백업은 없는 것과 같다.
  • 부팅까지가 복구다: 3층에서는 부트로더와 fstab도 복구 대상이다. 외장 디스크 항목에 nofail이 없으면, 복원한 서버가 그 디스크 부재로 부팅에서 멈춘다(재부팅 후 자동 복구에서 다룬 그 옵션이다).

마무리: 백업의 완성은 복구다

솔직히 적으면, 나도 오랫동안 1층조차 정기적으로 하지 않았다. 백업이 도는 초록불만 확인하고 안심했다. 이 글을 쓰며 실제로 1층을 해봤다 — 내 백업 디스크의 daily 스냅샷에서 설정 파일 하나를 복원 디렉토리로 꺼내보니 정상으로 나왔고, stat으로 그 파일의 하드링크 수를 보니 24였다. 스냅샷 23개와 latest가 디스크 위의 한 실체를 함께 가리키고 있다는 뜻이다. 다만 거기까지였다. 2층(서비스 통째 기동)과 3층(베어메탈 재건)은 아직 리허설하지 않았고, 그게 내 다음 숙제다. 한 가지 위안은 백업이 docker compose 스택과 실행 중 컨테이너 목록, 크론 작업까지 함께 떠 가도록 만들어 둔 것 — 3층 복구의 재료는 갖춰져 있다. 재료가 있는 것과 실제로 조립해본 것은 다르다는 걸 알면서도, 그 차이를 메우는 것이 다음 분기의 일이다.

백업은 데이터를 복사하는 순간이 아니라 데이터를 되찾는 순간에 완성된다. 초록불을 믿지 말고, 분기에 한 번은 꺼내보자. 그리고 그 리허설 결과조차 텔레그램 알림으로 자동 보고받게 만들면, “복구되는 백업”을 유지하는 일이 의지가 아니라 습관이 된다.