diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8bf02b3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,42 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Self-hosted Gitea deployment using Docker Compose with a remote PostgreSQL database and automated daily backups to NAS storage. + +## Architecture + +Two-container setup via `docker-compose.yml`: + +- **gitea** — Main Gitea application (`gitea/gitea:latest`). Web UI on port 3008, SSH on port 2222. Connects to a remote PostgreSQL instance at `192.168.50.42:5432`. +- **gitea-backup** — Alpine sidecar that installs `docker-cli`, sets up a cron job (2 AM daily), and runs `backup.sh` via `docker exec` into the gitea container. + +Backup files (`gitea-dump-YYYY-MM-DD_HHMM.zip`) are written to `/mnt/nasen/backup/gitea` (NAS mount) with 7-day retention. + +## Common Commands + +```bash +# Start/stop services +docker-compose up -d +docker-compose down + +# View logs +docker-compose logs -f gitea +docker-compose logs -f gitea-backup + +# Run a manual backup +docker exec gitea-backup /usr/local/bin/backup.sh + +# Check backup logs inside sidecar +docker exec gitea-backup cat /var/log/backup.log + +# Access Gitea admin CLI +docker exec -u git gitea /app/gitea/gitea admin [subcommand] +``` + +## Key Files + +- `docker-compose.yml` — Service definitions, environment config, volume mounts +- `backup.sh` — Backup script: runs `gitea dump`, verifies output, enforces retention policy diff --git a/backup.sh b/backup.sh new file mode 100755 index 0000000..4e93a98 --- /dev/null +++ b/backup.sh @@ -0,0 +1,57 @@ +#!/bin/sh +set -e + +BACKUP_DIR="/backup" +RETAIN_DAYS="${BACKUP_RETAIN_DAYS:-7}" +TIMESTAMP="$(date +%Y-%m-%d_%H%M)" +BACKUP_FILE="gitea-dump-${TIMESTAMP}.zip" + +echo "========================================" +echo "Gitea backup started at $(date)" +echo "========================================" + +# Pre-flight check +if ! docker inspect gitea >/dev/null 2>&1; then + echo "ERROR: gitea container not found or not running." + exit 1 +fi + +# Ensure backup directory exists inside gitea container +docker exec -u git gitea sh -c "mkdir -p /tmp/backup" + +# Run gitea dump +echo "Running gitea dump..." +docker exec -u git -w /tmp gitea \ + /app/gitea/gitea dump \ + -c /data/gitea/conf/app.ini \ + --file "/tmp/backup/${BACKUP_FILE}" \ + --skip-log \ + --type zip + +# Verify backup was created +if [ ! -f "${BACKUP_DIR}/${BACKUP_FILE}" ]; then + echo "ERROR: Backup file not found at ${BACKUP_DIR}/${BACKUP_FILE}" + exit 1 +fi + +FILESIZE=$(stat -c%s "${BACKUP_DIR}/${BACKUP_FILE}" 2>/dev/null || stat -f%z "${BACKUP_DIR}/${BACKUP_FILE}" 2>/dev/null) +echo "Backup created: ${BACKUP_FILE} (${FILESIZE} bytes)" + +# Retention: delete backups older than N days +echo "Cleaning up backups older than ${RETAIN_DAYS} days..." +DELETED=$(find "${BACKUP_DIR}" -name "gitea-dump-*.zip" -mtime "+${RETAIN_DAYS}" -print -delete) +if [ -n "${DELETED}" ]; then + echo "Deleted old backups:" + echo "${DELETED}" +else + echo "No old backups to delete." +fi + +echo "" +echo "Current backups:" +ls -lh "${BACKUP_DIR}"/gitea-dump-*.zip 2>/dev/null || echo "(none)" + +echo "" +echo "========================================" +echo "Gitea backup completed at $(date)" +echo "========================================" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..39ddf18 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,47 @@ +services: + gitea: + image: gitea/gitea:latest + container_name: gitea + environment: + - USER_UID=1000 + - USER_GID=1000 + - GITEA__database__DB_TYPE=postgres + - GITEA__database__HOST=192.168.50.42:5432 + - GITEA__database__NAME=gitea + - GITEA__database__USER=postgres + - GITEA__database__PASSWD=ratata,123 + restart: unless-stopped + volumes: + - gitea_data:/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + - /mnt/nasen/backup/gitea:/tmp/backup + ports: + - "3008:3000" + - "2222:22" + + gitea-backup: + image: alpine:3.21 + container_name: gitea-backup + depends_on: + - gitea + restart: unless-stopped + environment: + - BACKUP_RETAIN_DAYS=7 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /mnt/nasen/backup/gitea:/backup + - ./backup.sh:/usr/local/bin/backup.sh:ro + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + entrypoint: /bin/sh + command: + - -c + - | + apk add --no-cache docker-cli + echo "0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1" > /etc/crontabs/root + touch /var/log/backup.log + crond -f -l 2 + +volumes: + gitea_data: