Git 저장소를 clone해 두었다고 Gitea 전체를 복구할 수 있는 것은 아닙니다. 사용자, 권한, 이슈와 PR 정보는 DB에 있고, 첨부파일과 LFS 객체, 설정과 키는 별도 경로에 있을 수 있어요. 백업은 파일을 만드는 단계와 실제로 복원해 쓰는 단계를 함께 설계해야 합니다.
15편의 named volume 기반 Compose 구성을 사용합니다. 외부 객체 저장소, 패키지 레지스트리, 별도 LFS 경로를 추가했다면 그 데이터도 포함해야 합니다. 아래는 소규모 실습에서 쓰기를 멈추고 일관된 상태를 얻는 방식이며 무중단 백업 설계는 아닙니다.
Git·GitOps 전체 연재 · 16 / 24
이 글의 목차
무엇을 함께 보관해야 하나
| 대상 | 포함하는 내용 | 빠졌을 때의 영향 |
|---|---|---|
| PostgreSQL dump | 사용자·권한·이슈·PR 등 | 코드 외의 서비스 상태 복구 불가 |
Gitea /data | 저장소·설정·키·내부 첨부/LFS 등 | 파일·암호화 자료·서비스 식별 정보 손실 |
Compose와 .env | 이미지·볼륨·DB 접속 설정 | 동일 구성 재현과 DB 접속 어려움 |
| 외부 저장소·접속 정보 | 별도 객체 데이터와 운영 설정 | 일부 파일 또는 서비스 연동 누락 |
SECRET_KEY 같은 설정은 암호화된 자료를 해독하는 데 필요할 수 있습니다. 백업 자체가 민감한 데이터 묶음이므로 접근을 제한하고 암호화해 다른 장애 영역에 보관합니다. 원본 서버와 같은 디스크에만 둔 복사본은 서버 전체 장애에 대비하지 못합니다.
1. 쓰기를 멈추고 백업한다
예제 파일의 server/backup.sh를 Compose와 같은 디렉터리에 저장해 실행합니다. 백업 경로는 새 절대 경로로 지정합니다. Bash의 set -euo pipefail을 사용하므로 Linux Bash에서 실행합니다.
#!/usr/bin/env bash
set -euo pipefail
cd -- "$(dirname -- "$0")"
umask 077
backup_dir="${1:?Usage: ./backup.sh /absolute/new-backup-directory}"
[[ "$backup_dir" = /* ]] || { echo 'Use an absolute path' >&2; exit 1; }
mkdir -m 700 -- "$backup_dir"
docker compose stop gitea
trap 'docker compose start gitea >/dev/null' EXIT
docker compose exec -T db pg_dump -U gitea -d gitea -Fc > "$backup_dir/gitea.dump"
docker compose run --rm --no-deps --user 0 --entrypoint tar gitea -C /data -czf - . > "$backup_dir/gitea-data.tar.gz"
cp compose.yaml .env "$backup_dir/"
sha256sum "$backup_dir/gitea.dump" "$backup_dir/gitea-data.tar.gz" > "$backup_dir/SHA256SUMS"
echo "Backup created: $backup_dir"
chmod 700 backup.sh
./backup.sh "$HOME/gitea-backup-20261018"
스크립트는 Gitea를 멈추고 DB를 dump한 뒤 /data 전체를 tar로 보관합니다. 임시 컨테이너는 기존 Gitea 볼륨을 읽습니다. root로 읽는 이유는 파일 소유권과 접근 범위를 보존하기 위한 것이며, 이 컨테이너로 서비스를 제공하지 않습니다.
trap은 오류가 나더라도 Gitea를 다시 시작하도록 시도합니다. 백업 명령이 실패했으면 파일 크기만 보고 성공으로 취급하지 않습니다. DB dump와 tar 무결성, 체크섬을 확인한 뒤 상태와 로그에서 서비스를 확인합니다.
docker compose ps
tar -tzf "$HOME/gitea-backup-20261018/gitea-data.tar.gz" | head
2. 원본과 다른 프로젝트에 복원한다
같은 호스트에서 복원하려면 프로젝트 이름, 포트, URL을 다르게 지정합니다. 여기서는 새 디렉터리에 백업의 Compose와 .env를 복사한 뒤 name: maruops-git-restore, 웹 포트 13001, SSH 포트 12223으로 바꾼다고 가정합니다. ROOT_URL과 SSH_PORT도 각각 맞춥니다.
원본 maruops-git-lab의 볼륨을 external로 연결하지 않습니다. 새 프로젝트가 생성한 비어 있는 볼륨에서 진행합니다. 기존 데이터에 아래 명령을 덮어 실행하지 않습니다.
# Run in the new restore directory after editing its compose.yaml.
backup_dir="$HOME/gitea-backup-20261018"
sha256sum -c "$backup_dir/SHA256SUMS"
docker compose config --quiet
docker compose up -d db
# Wait until DB is healthy, then restore into its fresh gitea database.
docker compose exec -T db pg_restore -U gitea -d gitea --exit-on-error < "$backup_dir/gitea.dump"
docker compose run --rm --no-deps --user 0 --entrypoint tar gitea \
-C /data -xzf - < "$backup_dir/gitea-data.tar.gz"
docker compose up -d gitea
DB가 healthy가 되기 전에 복원을 시작하지 않습니다. 원본과 같은 Gitea 버전, 같은 PostgreSQL 메이저 버전으로 복원한 뒤 상태를 확인하고 업그레이드를 별도 단계로 진행합니다. 이미지 태그만 바꿔 DB 메이저를 올리는 방식은 데이터 디렉터리 호환 문제를 만들 수 있습니다.
3. 복원 성공은 사용 동작으로 판단한다
복원한 웹 주소에서 기존 계정으로 로그인하고 저장소, 이슈, PR이 보이는지 확인합니다. 새 경로에 clone하여 git log와 git fsck --full을 실행합니다. 사용하는 경우 LFS 파일 다운로드, 첨부파일, SSH clone도 각각 검사합니다. 목록이 보인다는 사실만으로 모든 데이터가 돌아왔다고 판단하지 않습니다.
복원 환경은 원본의 메일·웹훅·미러링 작업을 재실행할 수 있습니다. 외부 연결을 제한하거나 관련 기능을 먼저 꺼서 시험 환경이 운영 자동화를 건드리지 않게 합니다. 이 연재의 검증 범위는 안내 페이지에 실제 결과로 기록합니다.
보존 위치와 복구 시 주의사항은 Gitea 백업·복원 문서, DB 복원 옵션은 PostgreSQL pg_restore를 참고했습니다. 다음 백업 주기를 정할 때는 허용할 데이터 손실 시간과 복구 시간을 함께 적습니다.
이전 글 · [Git·GitOps 15] Gitea로 로컬 Git 웹 서버 구축하기: Docker Compose · 다음 글 · [Git·GitOps 17] GitHub Actions로 CI 연결하기: 테스트 실패에서 멈추는 흐름 · 연재 목록과 예제 파일

![[Git·GitOps 24] Git·CI/CD·GitOps 운영 흐름 완성하기: 장애별 판단과 복구](https://blog.maruops.com/wp-content/uploads/2026/10/maruops-git-photo-12-768x510.webp)
![[Git·GitOps 23] 비밀 값 관리: Secret의 base64와 SOPS·age 암호화](https://blog.maruops.com/wp-content/uploads/2026/10/maruops-git-photo-2-768x512.webp)
![[Git·GitOps 22] 변경 감지와 복구: 자동 Sync·selfHeal·prune·Git revert](https://blog.maruops.com/wp-content/uploads/2026/10/maruops-git-photo-10-768x576.webp)
답글 남기기