Compare commits

..
11 Commits
Author SHA1 Message Date
magnusandClaude Opus 5 58dfcd2ebb Move to dedicated Postgres and netbird-only SSH; DB password to .env
docker-compose.yml had drifted from the committed version during the move to
this host: a dedicated gitea-postgres service replaces the shared postgres-db
on the LAN box, and SSH is now published on the netbird interface only
(100.75.252.111:2222) instead of 0.0.0.0.

The database password is no longer inline — both POSTGRES_PASSWORD and
GITEA__database__PASSWD read ${POSTGRES_PASSWORD} from .env (gitignored,
mode 0600). .env.example documents the key, and *.bak-* is now ignored.

Note: run docker compose from this directory, otherwise .env is not picked up
and the variable resolves empty.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kjn2zm5PJ5y9t4HjpGouWR
2026-09-08 16:06:44 +00:00
magnusandClaude Opus 4.6 242fa8a927 Change backup directory to /data/backup/gitea
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 15:53:44 +01:00
magnusandClaude Opus 4.6 ffdd8bb4b2 Fix backup permission denied by using docker cp instead of direct mount write
The git user inside the gitea container couldn't write to the NAS-mounted
/tmp/backup directory. Now dumps to /tmp first, then uses docker cp to
extract the file to the sidecar's backup directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 08:22:09 +01:00
magnusandClaude Opus 4.6 e17ee68fbd Change backup path from /mnt/nasen to /mnt/nas
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 10:47:48 +01:00
magnusandClaude Opus 4.6 1f06472140 Add README with setup and backup instructions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 10:15:26 +01:00
magnusandClaude Opus 4.6 26af7784e7 Add .gitignore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 10:14:07 +01:00
magnus 9650c36b69 Merge branch 'master' 2026-02-21 10:11:48 +01:00
magnusandClaude Opus 4.6 e3ba7fd62e Add CLAUDE.md with project guidance for Claude Code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 10:10:19 +01:00
magnus 06c9efe1a0 Backup in /mnt/nasen/backup 2026-02-20 13:16:57 +01:00
magnusandClaude Opus 4.6 cce5c54177 Add automated daily Gitea backup
Alpine sidecar container runs gitea dump via docker exec at 2 AM daily,
writing backups to /data/backup/gitea/ with 7-day retention.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 21:33:18 +01:00
magnus 9fc824e3ac Initial commit 2026-02-19 20:19:00 +01:00
6 changed files with 198 additions and 1 deletions
+2
View File
@@ -0,0 +1,2 @@
# Copy to .env and fill in. Used by docker-compose.yml.
POSTGRES_PASSWORD=change-me
+4
View File
@@ -0,0 +1,4 @@
*.zip
*.log
.env
*.bak-*
+42
View File
@@ -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
+29 -1
View File
@@ -1,2 +1,30 @@
# gitea # Gitea
Self-hosted Gitea instance running on Docker Compose with a remote PostgreSQL database and automated daily backups.
## Services
- **gitea** — Web UI on port `3008`, SSH on port `2222`
- **gitea-backup** — Alpine sidecar running a daily cron job at 2 AM
## Setup
```bash
docker-compose up -d
```
## Backups
Backups run automatically at 2 AM daily, writing `gitea-dump-*.zip` files to `/mnt/nasen/backup/gitea` with 7-day retention.
Run a manual backup:
```bash
docker exec gitea-backup /usr/local/bin/backup.sh
```
Check backup logs:
```bash
docker exec gitea-backup cat /var/log/backup.log
```
Executable
+60
View File
@@ -0,0 +1,60 @@
#!/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
# Run gitea dump to /tmp (writable by git user)
echo "Running gitea dump..."
docker exec -u git -w /tmp gitea \
/app/gitea/gitea dump \
-c /data/gitea/conf/app.ini \
--file "/tmp/${BACKUP_FILE}" \
--skip-log \
--type zip
# Copy dump out of gitea container into sidecar's backup dir
docker cp "gitea:/tmp/${BACKUP_FILE}" "${BACKUP_DIR}/${BACKUP_FILE}"
# Clean up temp file inside gitea container
docker exec gitea rm -f "/tmp/${BACKUP_FILE}"
# 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 "========================================"
+61
View File
@@ -0,0 +1,61 @@
services:
postgres:
image: postgres:16-alpine
container_name: gitea-postgres
restart: unless-stopped
environment:
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=gitea
volumes:
- postgres_data:/var/lib/postgresql/data
gitea:
image: gitea/gitea:latest
container_name: gitea
depends_on:
- postgres
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=postgres:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=${POSTGRES_PASSWORD}
restart: unless-stopped
volumes:
- gitea_data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- ./backup:/tmp/backup
ports:
- "3008:3000"
- "100.75.252.111: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
- ./backup:/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:
postgres_data: