prajwal a181625c89 Initial commit: backup + mirror automation for self-hosted Gitea
Includes:
- gitea-backups/bin/backup.sh (per-push bundle + DB snapshot to local + S3)
- gitea-backups/bin/install-hooks.sh (idempotent post-receive shim installer)
- gitea-backups/bin/retention.sh (count-based retention: keep newest 7 dates)
- gitea-mirror/bin/auto-mirror.sh (Gitea -> GitHub push mirror automation,
  hardened against Gitea outages)
- crontab.txt (reference for the 3 cron entries)
- README.md (architecture, layout, bootstrap)
2026-05-09 06:02:09 +00:00

gitea-ops

Source of truth for the Gitea install at /home/ubuntu/gitea/ on this server, and the automation around it.

Layout

gitea-ops/
├── README.md
├── crontab.txt                       # cron entries (install with: crontab crontab.txt)
├── gitea-backups/bin/
│   ├── backup.sh                     # called per push from each repo's post-receive hook
│   ├── install-hooks.sh              # cron, every minute: ensures hook shim in every repo
│   └── retention.sh                  # cron, daily 03:00 UTC: keeps newest 7 date-folders
└── gitea-mirror/bin/
    └── auto-mirror.sh                # cron, every minute: ensures GitHub push-mirror per repo

The deployment target is fixed: scripts run from /home/ubuntu/gitea-backups/bin/ and /home/ubuntu/gitea-mirror/bin/.

Components

Script Trigger Purpose
backup.sh per git push (via post-receive hook) Bundle repo + snapshot SQLite, upload both to local + S3
install-hooks.sh cron, 1 min Drop the zzz-backup shim into every repo's hooks/post-receive.d/
retention.sh cron, daily 03:00 UTC Keep newest 7 calendar dates of backups (S3 + local), prune older
auto-mirror.sh cron, 1 min For every Gitea repo, ensure matching private GitHub repo + push mirror exist

Backup destinations

  1. Local/home/ubuntu/gitea-backups/repos/<owner>/<repo>/*.bundle and /home/ubuntu/gitea-backups/db/*-gitea.db.gz
  2. S3s3://toqqer-gitea-backup/<YYYY-MM-DD>/repos/... and .../<YYYY-MM-DD>/db/... (region ap-south-1)
  3. GitHub — live mirror at github.com/prajwalpatil-toqqer/<repo> for every Gitea repo (real-time via sync_on_commit)

Retention semantics

Keep the most recent 7 calendar dates that have backups, regardless of how old they are. Quiet periods don't empty the store — the last 7 active dates always persist.

Required outside-of-repo state

Not committed (see .gitignore):

  • /home/ubuntu/gitea-mirror/gitea.token — Gitea PAT (scopes: repo, user)
  • /home/ubuntu/gitea-mirror/github.token — GitHub classic PAT (scope: repo)
  • /home/ubuntu/.aws/credentials — IAM gitea-backup-bot (scoped to one bucket)
  • /home/ubuntu/.config/rclone/rclone.conf — same key, rclone format

Service unit

Gitea itself runs under user systemd:

  • Unit: ~/.config/systemd/user/gitea.service
  • Persistence: loginctl enable-linger ubuntu
  • Status: systemctl --user status gitea
  • Logs: journalctl --user -u gitea -f

Bootstrap a fresh server

# 1. Place the secret files (gitea.token, github.token, aws creds, rclone.conf) outside the repo
# 2. Mirror the directory layout:
mkdir -p /home/ubuntu/gitea-backups/{bin,db,repos,logs}
mkdir -p /home/ubuntu/gitea-mirror/{bin,logs}
cp gitea-backups/bin/* /home/ubuntu/gitea-backups/bin/
cp gitea-mirror/bin/*  /home/ubuntu/gitea-mirror/bin/
chmod +x /home/ubuntu/gitea-backups/bin/*.sh /home/ubuntu/gitea-mirror/bin/*.sh
# 3. Install cron
crontab crontab.txt
S
Description
Self-hosted Gitea install: backup + mirror automation
Readme 37 KiB
Languages
Shell 100%