서론
Docker Compose 로 Self-Hosted 서비스를 운영할 때 가장 중요한 것은 데이터 백업입니다. 서버 장애, 디스크 손상, 실수로 인한 데이터 삭제 — 언제든 발생할 수 있는 상황들에 대비하기 위해 체계적인 백업 전략이 필요합니다.
이번 글에서는 Docker Compose 기반의 Self-Hosted 인프라(PostgreSQL, n8n, Ghost Blog 등)에 대한 백업 자동화, 복구 절차, 그리고 주의사항 을 교육 자료 형식으로 정리했습니다. 실제 운영 중인 서비스의 경로나 비밀번호는 제외하고, 누구나 적용 가능한 일반적인 가이드로 작성했습니다.
백업 대상과 중요도
Docker Compose 환경에서 백업해야 할 대상은 크게 세 가지로 나뉩니다.
- 데이터베이스 — PostgreSQL, SQLite 등 구조화된 데이터 (최고 중요도)
- 애플리케이션 데이터 — 워크플로우, 블로그 콘텐츠, 미디어 파일 (높은 중요도)
- 설정 파일 — Nginx reverse proxy, SSL 인증서, 서비스 설정 (보통 ~ 높은 중요도)
| 서비스 | Volume 경로 | 백업 방식 | 중요도 |
|---|---|---|---|
| PostgreSQL | ./data/postgres | VACUUM FULL → pg_dumpall | 🔴 최고 |
| n8n | ./data/n8n | VACUUM → tar (*.log 제외) | 🔴 최고 |
| Ghost | content/ | tar 압축 | 🟡 높음 |
| Certbot | ./data/certbot | tar 압축 | 🟡 높음 |
| Nginx | ./nginx/conf | tar 압축 | 🟢 보통 |
| SearXNG | ./data/searxng | tar 압축 | 🟢 보통 |
백업 스크립트 구성
백업 스크립트는 bash 로 작성하며, 각 서비스의 특성에 맞는 백업 전략을 적용합니다. 핵심은 데이터 무결성 보장 입니다.
PostgreSQL — SQL Dump 방식 (핵심)
PostgreSQL 은 tar 로 직접 덤프하면 안 됩니다. WAL 파일이 섞여 복구 시 데이터 손상이 발생할 수 있으므로, 반드시 pg_dumpall 명령을 통해 SQL 덤프를 생성한 후 gzip 으로 압축합니다.
# VACUUM FULL: 데이터베이스 파일 크기 축소
docker exec postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} -c "VACUUM FULL;"
# pg_dumpall: 전체 SQL 덤프 생성
docker exec postgres pg_dumpall -U ${POSTGRES_USER} > backups/pg_all_${DATE}.sql
# gzip 압축
gzip -f backups/pg_all_${DATE}.sql
VACUUM FULL 은 데이터베이스 파일의 물리적 크기를 축소하여 백업 파일 크기를 최소화합니다. pg_dumpall 은 서비스 중단 없이 백업 가능합니다.
n8n — SQLite VACUUM + tar 방식
n8n 은 SQLite 를 사용하므로 PostgreSQL 과 다른 전략이 필요합니다. 컨테이너를 중지한 후 sqlite3 VACUUM 명령으로 데이터베이스 파일을 최적화하고, tar 로 압축합니다. 로그 파일은 백업에서 제외하여 용량을 절약합니다.
# 컨테이너 중지
docker stop n8n
# SQLite VACUUM: DB 파일 최적화
sqlite3 ./data/n8n/database.sqlite "VACUUM;"
# 컨테이너 재시작
docker start n8n
sleep 2
# tar 압축 (*.log 제외)
tar -czf backups/n8n_${DATE}.tar.gz \
--exclude='*.log' \
-C ./data n8n
기타 서비스 — tar 압축
Ghost 콘텐츠, Certbot SSL 인증서, Nginx 설정, SearXNG 설정 등은 tar.gz 로 압축하는 단순한 방식이 적합합니다.
# Ghost 블로그 콘텐츠
tar -czf backups/ghost_${DATE}.tar.gz \
-C ~/Developer/GhostBlog content
# Certbot SSL 인증서
tar -czf backups/certbot_${DATE}.tar.gz \
-C . data/certbot
# Nginx 설정
tar -czf backups/nginx_conf_${DATE}.tar.gz \
-C . nginx/conf
자동 백업 설정
- 스크립트에 실행 권한 부여: chmod +x backup.sh
- cron 에 등록하여 매일 새벽 자동 실행
- 백업 로그를 파일로 출력하여 이력 추적
# 실행 권한 부여
chmod +x ~/Developer/WebServer/backup.sh
# crontab 편집 — 매일 새벽 3:30 자동 백업
crontab -e
# 추가할 라인:
30 3 * * * /Users/yourname/Developer/WebServer/backup.sh
cron 등록 후 확인: crontab -l | grep backup
백업 로그 실시간 확인: tail -f backups/backup.log
복구 절차
백업의 가치는 복구할 때才知道입니다. 정기적인 복구 테스트가 필수적입니다.
# 사용법
./restore.sh <backup_date> [service]
# 전체 복구
./restore.sh 20250614_030000 all
# 개별 서비스만 복구
./restore.sh 20250614_030000 pg
./restore.sh 20250614_030000 n8n
./restore.sh 20250614_030000 ghost
PostgreSQL 복구
- 컨테이너 중지: docker stop postgres
- SQL 덤프 압축 해제: gunzip -kf backups/pg_all_YYYYMMDD_HHMMSS.sql.gz
- 데이터 적용: docker exec -i postgres psql -U ${USER} < backups/pg_all_*.sql
- 임시 SQL 파일 삭제: rm backups/pg_all_*.sql
- 컨테이너 재시작: docker start postgres
n8n 복구
- 컨테이너 중지: docker stop n8n
- 데이터 복구: tar -xzf backups/n8n_YYYYMMDD_HHMMSS.tar.gz -C ./WebServer
- 컨테이너 재시작: docker start n8n (로그 파일은 자동 재생성됨)
Ghost 복구
- 컨테이너 중지: docker stop ghost
- 콘텐츠 복구: tar -xzf backups/ghost_YYYYMMDD_HHMMSS.tar.gz -C ~/Developer/GhostBlog
- 컨테이너 재시작: docker start ghost
Nginx / Certbot / SearXNG 복구
설정 파일 복구는 tar 압축 해제 후 서비스 재시작만 필요합니다.
# Certbot 복구 후 Nginx 재시작
tar -xzf backups/certbot_${DATE}.tar.gz -C ./WebServer
docker restart nginx
# Nginx 설정 복구
tar -xzf backups/nginx_conf_${DATE}.tar.gz -C ./WebServer
docker restart nginx
# SearXNG 복구
tar -xzf backups/searxng_${DATE}.tar.gz -C ./WebServer
docker restart searxng
백업 파일 구조
backups/
├── backup.log # 백업 실행 로그 (누적 유지)
├── pg_all_20250614_030000.sql.gz # PostgreSQL 전체 dump
├── n8n_20250614_030000.tar.gz # n8n 데이터 (*.log 제외)
├── ghost_20250614_030000.tar.gz # Ghost 콘텐츠
├── certbot_20250614_030000.tar.gz # SSL 인증서
├── nginx_conf_20250614_030000.tar.gz # Nginx 설정
├── searxng_20250614_030000.tar.gz # SearXNG 설정
└── money_20250614_030000.tar.gz # 기타 앱 데이터
백업 파일은 30일 보관 후 스크립트가 자동으로 삭제합니다. 디스크 공간을 효율적으로 관리하려면 retention 기간을 환경에 맞게 조정하세요.
주의 사항과 모범 사례
PostgreSQL — tar 로 직접 덤프 금지
가장 중요한 규칙입니다. PostgreSQL 데이터 디렉토리를 tar 로 백업하면 WAL 파일이 포함되고, 복구 시 데이터 손상이 발생할 수 있습니다. 항상 pg_dumpall 또는 pg_dump 명령을 사용하세요.
n8n — 컨테이너 중지 후 백업
n8n 은 SQLite 를 사용하므로 컨테이너 실행 중 백업하면 데이터 손상이 가능합니다. 반드시 컨테이너를 중지한 후 VACUUM → tar 순서로 진행하세요.
백업 디렉토리는 Git 에서 제외
백업 파일은 절대 Git 에 커밋하지 마세요. 민감한 데이터가 포함될 수 있고, 리포지토리 용량을 급증시킵니다.
# .gitignore
backups/
원격 저장소 동기화
로컬 백업만으로는 디스크 손상 시 복구가 불가능합니다. 백업 파일을 S3, Dropbox, Google Drive 등의 원격 저장소와 동기화하는 것이 안전합니다. rsync 또는 rclone 을 활용하세요.
정기적인 복구 테스트
백업이 성공했다고 믿기 전에 반드시 복구 테스트를 해보세요. 테스트 환경에서 백업 파일을 복구하고 데이터가 제대로 읽히는지 확인하는 것이 가장 확실한 검증 방법입니다.
문제 해결
백업이 실패할 때
- 로그 확인: tail backups/backup.log
- Docker 컨테이너 상태: docker ps
- 디스크 공간: df -h
- 특정 서비스 백업이 실패하면 해당 경로의 존재 여부를 확인
cron 이 실행되지 않을 때 (macOS)
- cron 서비스 확인: launchctl list | grep cron
- crontab 확인: crontab -l
- 시스템 로그: log show --predicate 'eventMessage contains "cron"' --last 1h
복구가 실패할 때
- 백업 파일 존재 확인: ls backups/
- 파일 압축 해제 테스트: tar -tzf backups/XXX.tar.gz
- 컨테이너 상태 확인: docker ps -a
- 복구 스크립트를 단계별로 수동 실행하여 문제 지점 파악
마무리
백업은 '언젠가 해야 할 일' 이 아니라 '이미 해둔 일' 이어야 합니다. 오늘 백업 스크립트를 작성하고 cron 에 등록하면, 내일의 당신은 그 선택을 감사할 것입니다.
Docker Compose 환경에서는 스크립트 한 줄로 모든 서비스의 데이터를 보호할 수 있습니다. 복구 테스트를 잊지 마세요. 백업의 진짜 가치는 복구할 때 확인됩니다.
댓글 없음:
댓글 쓰기