From 4d9b6f9d90f7d5bda0a9a659bd2305aa6f8e240f Mon Sep 17 00:00:00 2001 From: Ole-Morten Duesund Date: Wed, 26 Aug 2026 14:41:16 +0200 Subject: [PATCH] run.sh: automatisk backup med rotasjon, og --restart=always MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Før hver oppstart eksporteres datavolumet til SOAPBOX_BACKUP_DIR/soapbox-data-.tar.gz; de SOAPBOX_BACKUP_KEEP nyeste beholdes (standard 5, 0 = av). Bind-mounts pakkes med tar, navngitte volum med podman volume export. --restart unless-stopped byttet til --restart=always i run.sh og compose.yaml: podman håndterer ikke unless-stopped som docker. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR --- .env.example | 4 ++++ .gitignore | 1 + README.md | 4 ++++ compose.yaml | 2 +- run.sh | 50 +++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index abd93dc..98684e6 100644 --- a/.env.example +++ b/.env.example @@ -31,3 +31,7 @@ SOAPBOX_PORT=8080 SOAPBOX_VOLUME=soapbox-data # Minnegrense for containeren (bildebehandling er det som bruker mest) SOAPBOX_MEMORY=512m +# run.sh tar backup av datavolumet før hver oppstart, med tidsstempel i +# filnavnet, og beholder de N nyeste. 0 = ingen backup. +SOAPBOX_BACKUP_DIR=./backups +SOAPBOX_BACKUP_KEEP=5 diff --git a/.gitignore b/.gitignore index 110b8ce..14d176d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ dist/ .playwright-mcp/ *.png .env +backups/ diff --git a/README.md b/README.md index 2fa6ab9..0406582 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,10 @@ cp .env.example .env # sett SOAPBOX_SITE_URL og hovedbruker Ny versjon: `git pull && ./build.sh && ./run.sh`. Data ligger i podman-volumet `soapbox-data` (`podman volume inspect soapbox-data` viser stien). +`run.sh` tar automatisk backup av volumet før hver oppstart til `SOAPBOX_BACKUP_DIR` +(standard `./backups/soapbox-data-.tar.gz`) og beholder de `SOAPBOX_BACKUP_KEEP` +nyeste (standard 5). Gjenopprett med `podman volume import soapbox-data `. + Caddy er ren reverse proxy, se `Caddyfile.example`. Ved sub-path skal Caddy **ikke** strippe prefikset – appen monteres selv under `SOAPBOX_BASE_PATH`. diff --git a/compose.yaml b/compose.yaml index 8784b20..1660571 100644 --- a/compose.yaml +++ b/compose.yaml @@ -4,7 +4,7 @@ services: soapbox: image: localhost/soapbox:latest container_name: soapbox - restart: unless-stopped + restart: always ports: - "127.0.0.1:${SOAPBOX_PORT:-8080}:8080" volumes: diff --git a/run.sh b/run.sh index 53ae1b9..0b3c6c5 100755 --- a/run.sh +++ b/run.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash # Starter (eller erstatter) soapbox-containeren med innstillinger fra .env. # Idempotent: kjør på nytt etter build.sh for å rulle ut nytt image. +# +# Før oppstart tas en backup av datavolumet til SOAPBOX_BACKUP_DIR med +# tidsstempel i navnet; de SOAPBOX_BACKUP_KEEP nyeste beholdes (standard 5). +# Sett SOAPBOX_BACKUP_KEEP=0 for å skru av backup. set -euo pipefail cd "$(dirname "$0")" @@ -11,16 +15,48 @@ if [[ ! -f "$ENV_FILE" ]]; then fi # Verdier som bare gjelder for podman-kjøringen (ikke appen) -PORT="$(grep -E '^SOAPBOX_PORT=' "$ENV_FILE" | cut -d= -f2- || true)" -VOLUME="$(grep -E '^SOAPBOX_VOLUME=' "$ENV_FILE" | cut -d= -f2- || true)" -MEMORY="$(grep -E '^SOAPBOX_MEMORY=' "$ENV_FILE" | cut -d= -f2- || true)" -PORT="${PORT:-8080}" -VOLUME="${VOLUME:-soapbox-data}" -MEMORY="${MEMORY:-512m}" +envval() { grep -E "^$1=" "$ENV_FILE" | head -n1 | cut -d= -f2- || true; } +PORT="$(envval SOAPBOX_PORT)"; PORT="${PORT:-8080}" +VOLUME="$(envval SOAPBOX_VOLUME)"; VOLUME="${VOLUME:-soapbox-data}" +MEMORY="$(envval SOAPBOX_MEMORY)"; MEMORY="${MEMORY:-512m}" +BACKUP_DIR="$(envval SOAPBOX_BACKUP_DIR)"; BACKUP_DIR="${BACKUP_DIR:-./backups}" +BACKUP_KEEP="$(envval SOAPBOX_BACKUP_KEEP)"; BACKUP_KEEP="${BACKUP_KEEP:-5}" IMAGE="${1:-localhost/soapbox:latest}" +backup() { + local stamp file + stamp="$(date -u +%Y%m%d-%H%M%S)" + file="$BACKUP_DIR/soapbox-data-$stamp.tar.gz" + mkdir -p "$BACKUP_DIR" + if [[ "$VOLUME" == /* ]]; then + # Bind-mount: pakk katalogen direkte + [[ -d "$VOLUME" ]] || { echo "Ingen data å sikkerhetskopiere ennå ($VOLUME)"; return; } + tar -C "$VOLUME" -czf "$file" . + else + # Navngitt volum: podman eksporterer innholdet som tar + podman volume exists "$VOLUME" || { echo "Ingen data å sikkerhetskopiere ennå ($VOLUME)"; return; } + podman volume export "$VOLUME" | gzip > "$file" + fi + echo "Backup: $file ($(du -h "$file" | cut -f1))" + + # Rotasjon: behold de N nyeste (navnene sorterer kronologisk pga. tidsstempelet) + local old + old="$(find "$BACKUP_DIR" -maxdepth 1 -name "soapbox-data-*.tar.gz" | sort | head -n "-$BACKUP_KEEP" || true)" + if [[ -n "$old" ]]; then + echo "$old" | xargs -r rm -f -- + echo "Slettet $(echo "$old" | wc -l) gamle backuper (beholder $BACKUP_KEEP)" + fi +} + +if [[ "$BACKUP_KEEP" -gt 0 ]]; then + backup +fi + +# --restart=always: podman håndterer ikke "unless-stopped" som docker gjør; +# "always" er det som faktisk starter containeren igjen (sammen med +# podman-restart.service eller en systemd-enhet for reboot). podman run -d --replace --name soapbox \ - --restart unless-stopped \ + --restart=always \ --cap-drop=all --security-opt=no-new-privileges \ --memory "$MEMORY" \ -p "127.0.0.1:${PORT}:8080" \