diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index c7122e1..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,3 +0,0 @@ -include: - - path: ./immich-app/docker-compose.yml - diff --git a/gitea/.env b/gitea/.env new file mode 100644 index 0000000..cc5134d --- /dev/null +++ b/gitea/.env @@ -0,0 +1,7 @@ +GITEA_DATA=/mnt/misc/gitea +SSH_KEY_PATH=/home/git/.ssh +DB_DATA_LOCATION=/mnt/ssd/dbs/gitea/postgres +DB_USER=gitea +DB_PASSWORD=gitea +DB_NAME=gitea +BACKUP_DIR=/mnt/backups/gitea diff --git a/gitea/backup.sh b/gitea/backup.sh new file mode 100755 index 0000000..de8e5ac --- /dev/null +++ b/gitea/backup.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "$SCRIPT_DIR/.env" + +if [ ! -d "$BACKUP_DIR" ]; then + echo "Error: BACKUP_DIR does not exist: $BACKUP_DIR" >&2 + exit 1 +fi +if ! docker info > /dev/null 2>&1; then + echo "Error: Docker is not accessible" >&2 + exit 1 +fi + +cleanup() { + echo "Restarting all services..." + docker compose -f "$SCRIPT_DIR/docker-compose.yml" up -d +} +trap cleanup EXIT + +echo "Stopping all services..." +docker compose -f "$SCRIPT_DIR/docker-compose.yml" down + +echo "Starting database only..." +docker compose -f "$SCRIPT_DIR/docker-compose.yml" up -d db +sleep 5 + +echo "Running gitea dump..." +docker run --rm \ + --network gitea_gitea \ + -e USER_UID=1001 \ + -e USER_GID=1001 \ + -v "${GITEA_DATA}:/data" \ + -v "${BACKUP_DIR}:/backup" \ + docker.gitea.com/gitea:1.25.3 \ + /bin/sh -c "chown 1001:1001 /tmp && su-exec 1001:1001 /bin/sh -c 'cd /tmp && gitea dump -c /data/gitea/conf/app.ini --tempdir /tmp' > /backup/backup.log 2>&1 && cp /tmp/gitea-dump-*.zip /backup/" + +echo "Backup completed successfully" diff --git a/gitea/docker-compose.yml b/gitea/docker-compose.yml index 4f4f962..4a5a1d1 100644 --- a/gitea/docker-compose.yml +++ b/gitea/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3" - networks: gitea: external: false @@ -13,15 +11,16 @@ services: - USER_GID=1001 - GITEA__database__DB_TYPE=postgres - GITEA__database__HOST=db:5432 - - GITEA__database__NAME=gitea - - GITEA__database__USER=gitea - - GITEA__database__PASSWD=gitea + - GITEA__database__NAME=${DB_NAME} + - GITEA__database__USER=${DB_USER} + - GITEA__database__PASSWD=${DB_PASSWORD} restart: always networks: - gitea volumes: - - /home/git/.ssh/:/data/git/.ssh - - /mnt/misc/gitea:/data + - ${SSH_KEY_PATH}:/data/git/.ssh + - ${GITEA_DATA}:/data + - ${BACKUP_DIR}:/backup - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro ports: @@ -34,10 +33,10 @@ services: image: docker.io/library/postgres:14 restart: always environment: - - POSTGRES_USER=gitea - - POSTGRES_PASSWORD=gitea - - POSTGRES_DB=gitea + - POSTGRES_USER=${DB_USER} + - POSTGRES_PASSWORD=${DB_PASSWORD} + - POSTGRES_DB=${DB_NAME} networks: - gitea volumes: - - ./postgres:/var/lib/postgresql/data + - ${DB_DATA_LOCATION}:/var/lib/postgresql/data diff --git a/gitea/restore.sh b/gitea/restore.sh new file mode 100755 index 0000000..a7da50c --- /dev/null +++ b/gitea/restore.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "$SCRIPT_DIR/.env" + +# --- Argument validation --- +if [ $# -lt 1 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +DUMP_ZIP="$(realpath "$1")" +if [ ! -f "$DUMP_ZIP" ]; then + echo "Error: dump file not found: $DUMP_ZIP" >&2 + exit 1 +fi + +if ! docker info > /dev/null 2>&1; then + echo "Error: Docker is not accessible" >&2 + exit 1 +fi + +# --- Cleanup trap: always bring services back up --- +cleanup() { + echo "Starting all services..." + docker compose -f "$SCRIPT_DIR/docker-compose.yml" up -d +} +trap cleanup EXIT + +# --- Stop everything --- +echo "Stopping all services..." +docker compose -f "$SCRIPT_DIR/docker-compose.yml" down + +# --- Start only the database --- +echo "Starting database only..." +docker compose -f "$SCRIPT_DIR/docker-compose.yml" up -d db +echo "Waiting for database to be ready..." +for i in $(seq 1 30); do + if docker compose -f "$SCRIPT_DIR/docker-compose.yml" exec -T db \ + pg_isready -U "$DB_USER" -d "$DB_NAME" > /dev/null 2>&1; then + break + fi + if [ "$i" -eq 30 ]; then + echo "Error: database not ready after 30 seconds" >&2 + exit 1 + fi + sleep 1 +done + +# --- Restore database --- +echo "Restoring database..." +docker compose -f "$SCRIPT_DIR/docker-compose.yml" exec -T db \ + psql -U "$DB_USER" -d postgres -c "DROP DATABASE IF EXISTS \"$DB_NAME\";" +docker compose -f "$SCRIPT_DIR/docker-compose.yml" exec -T db \ + psql -U "$DB_USER" -d postgres -c "CREATE DATABASE \"$DB_NAME\" OWNER \"$DB_USER\";" + +unzip -p "$DUMP_ZIP" gitea-db.sql | \ + docker compose -f "$SCRIPT_DIR/docker-compose.yml" exec -T db \ + psql -U "$DB_USER" -d "$DB_NAME" + +# --- Restore data files --- +echo "Restoring data files..." +docker run --rm \ + -v "${GITEA_DATA}:/data" \ + -v "${DUMP_ZIP}:/backup/dump.zip:ro" \ + docker.gitea.com/gitea:1.25.3 \ + /bin/sh -c ' + set -e + apk add --no-cache unzip > /dev/null 2>&1 || true + mkdir -p /tmp/restore + unzip -o /backup/dump.zip -d /tmp/restore + + # Clear old data + rm -rf /data/gitea/attachments /data/gitea/avatars /data/gitea/jwt \ + /data/gitea/indexers /data/gitea/queues /data/gitea/lfs \ + /data/gitea/packages /data/gitea/tmp + rm -rf /data/git/repositories/* + + # Restore data directory contents + if [ -d /tmp/restore/data ]; then + cp -a /tmp/restore/data/* /data/gitea/ 2>/dev/null || true + fi + + # Restore repositories + if [ -d /tmp/restore/repos ]; then + cp -a /tmp/restore/repos/* /data/git/repositories/ 2>/dev/null || true + fi + + # Restore app.ini + if [ -f /tmp/restore/app.ini ]; then + mkdir -p /data/gitea/conf + cp -a /tmp/restore/app.ini /data/gitea/conf/app.ini + fi + + # Fix ownership + chown -R 1001:1001 /data + + rm -rf /tmp/restore + ' + +# --- Bring everything up (trap will handle this) --- +# Trap fires on exit, which starts all services. +# After services are up, regenerate hooks. +trap - EXIT +echo "Starting all services..." +docker compose -f "$SCRIPT_DIR/docker-compose.yml" up -d + +echo "Waiting for Gitea to start..." +for i in $(seq 1 60); do + if docker exec gitea curl -sf http://localhost:3000/ > /dev/null 2>&1; then + break + fi + if [ "$i" -eq 60 ]; then + echo "Warning: Gitea not responding after 60s, attempting hook regeneration anyway" >&2 + break + fi + sleep 1 +done + +echo "Regenerating git hooks..." +docker exec gitea gitea admin regenerate hooks + +echo "Restore completed successfully" diff --git a/.env b/immich-app/.env similarity index 88% rename from .env rename to immich-app/.env index 3aed925..92db2f0 100644 --- a/.env +++ b/immich-app/.env @@ -6,11 +6,11 @@ # The location where your uploaded files are stored UPLOAD_LOCATION=/mnt/media/upload -THUMB_LOCATION=/mnt/ssd1/media/thumbs -ENCODED_VIDEO_LOCATION=/mnt/ssd1/media/encoded-video +THUMB_LOCATION=/mnt/ssd/media/thumbs +ENCODED_VIDEO_LOCATION=/mnt/ssd/media/encoded-video # The location where your database files are stored. Network shares are not supported for the database -DB_DATA_LOCATION=/mnt/ssd1/media/postgres +DB_DATA_LOCATION=/mnt/ssd/media/postgres # To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List # TZ=Etc/UTC diff --git a/immich-app/docker-compose.yml b/immich-app/docker-compose.yml index af4ac79..5cf736b 100644 --- a/immich-app/docker-compose.yml +++ b/immich-app/docker-compose.yml @@ -23,7 +23,7 @@ services: - ${ENCODED_VIDEO_LOCATION}:/data/encoded-video - /etc/localtime:/etc/localtime:ro env_file: - - ../.env + - .env ports: - '2283:2283' depends_on: @@ -44,7 +44,7 @@ services: volumes: - model-cache:/cache env_file: - - ../.env + - .env restart: always healthcheck: disable: false