Add initial infrastructure and backup scripts for Gitea and homelab deployment

- Create README.md with project layout and quick start instructions
- Implement backup scripts for Gitea, including database and repository exports
- Add systemd service and timer for automated Gitea backups
- Develop bootstrap scripts for homelab and VPS setup
- Document architecture and restore procedures
- Configure Caddy reverse proxy and Docker Compose for service management
- Establish secrets management guidelines
This commit is contained in:
2026-03-04 14:42:46 -05:00
commit c93dcb5daf
21 changed files with 531 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
#!/usr/bin/env bash
set -euo pipefail
BASE="${BASE:-/srv/backups}"
KEEP_DAILY_DAYS="${KEEP_DAILY_DAYS:-7}"
KEEP_WEEKLY_DAYS="${KEEP_WEEKLY_DAYS:-365}"
python3 - <<'PY' "$BASE" "$KEEP_DAILY_DAYS" "$KEEP_WEEKLY_DAYS"
import datetime as dt
import os
import sys
base = sys.argv[1]
keep_daily_days = int(sys.argv[2])
keep_weekly_days = int(sys.argv[3])
now = dt.datetime.now().timestamp()
if not os.path.isdir(base):
print(f"Backup base not found: {base}")
sys.exit(0)
files = []
for root, _, names in os.walk(base):
parts = set(os.path.normpath(root).split(os.sep))
preserve_latest = "latest" in parts
for name in names:
path = os.path.join(root, name)
if not os.path.isfile(path):
continue
try:
mtime = os.path.getmtime(path)
except OSError:
continue
age_days = (now - mtime) / 86400
files.append((path, root, mtime, age_days, preserve_latest))
to_delete = set()
# Delete anything older than weekly window (unless it's in a latest folder)
for path, _, _, age_days, preserve_latest in files:
if not preserve_latest and age_days > keep_weekly_days:
to_delete.add(path)
# For weekly window, keep one file per week per directory (latest by mtime)
weekly_candidates = {}
for path, root, mtime, age_days, preserve_latest in files:
if preserve_latest:
continue
if keep_daily_days < age_days <= keep_weekly_days:
iso = dt.datetime.fromtimestamp(mtime).isocalendar()
key = (root, iso.year, iso.week)
best = weekly_candidates.get(key)
if best is None or mtime > best[1]:
weekly_candidates[key] = (path, mtime)
weekly_keep = {v[0] for v in weekly_candidates.values()}
for path, _, _, age_days, preserve_latest in files:
if preserve_latest:
continue
if keep_daily_days < age_days <= keep_weekly_days and path not in weekly_keep:
to_delete.add(path)
deleted = 0
for path in sorted(to_delete):
try:
os.remove(path)
print(path)
deleted += 1
except OSError:
pass
print(f"Retention complete for {base} (deleted {deleted} files)")
PY
echo "Policy: daily <= ${KEEP_DAILY_DAYS}d, weekly <= ${KEEP_WEEKLY_DAYS}d"