ce-infra/scripts/backup.sh

76 lines
2.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# backup.sh — Create a full backup of all Cloud Elves services
#
# Usage: ./scripts/backup.sh [destination_dir]
#
# Creates a timestamped tarball containing volume-level backups of all services.
# Run from the ce-infra repo root directory.
# Requires SSH access to box.cloud-elves.eu as root.
set -euo pipefail
HOST="root@box.cloud-elves.eu"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_ce}"
SSH="ssh -i $SSH_KEY $HOST"
SCP="scp -i $SSH_KEY"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
DEST_DIR="${1:-./backups}"
BACKUP_NAME="ce-backup-${TIMESTAMP}"
REMOTE_TMP="/tmp/${BACKUP_NAME}"
echo "=== Cloud Elves Backup — ${TIMESTAMP} ==="
echo ""
# Create local destination
mkdir -p "${DEST_DIR}"
# Create remote temp directory
echo "[1/7] Preparing..."
$SSH "mkdir -p ${REMOTE_TMP}"
# Stop services for consistent backup (databases stay up for pg_dump)
echo "[2/7] Stopping application services for consistent backup..."
$SSH "cd /opt/services && docker compose stop gitea outline dex lldap gitea-runner caddy"
# Gitea: pg_dump + data volume
echo "[3/7] Backing up Gitea (database + data)..."
$SSH "cd /opt/services && docker compose exec -T gitea-db pg_dump -U gitea gitea | gzip > ${REMOTE_TMP}/gitea-db.sql.gz"
$SSH "docker run --rm -v services_gitea-data:/data -v ${REMOTE_TMP}:/backup alpine tar czf /backup/gitea-data.tar.gz -C /data ."
# Outline: pg_dump + data volume
echo "[4/7] Backing up Outline (database + uploads)..."
$SSH "cd /opt/services && docker compose exec -T outline-db pg_dump -U outline outline | gzip > ${REMOTE_TMP}/outline-db.sql.gz"
$SSH "docker run --rm -v services_outline-data:/data -v ${REMOTE_TMP}:/backup alpine tar czf /backup/outline-data.tar.gz -C /data ."
# LLDAP: SQLite volume
echo "[5/7] Backing up LLDAP (user directory)..."
$SSH "docker run --rm -v services_lldap-data:/data -v ${REMOTE_TMP}:/backup alpine tar czf /backup/lldap-data.tar.gz -C /data ."
# Dex: SQLite volume
echo "[6/7] Backing up Dex (auth database)..."
$SSH "docker run --rm -v services_dex-data:/dex -v ${REMOTE_TMP}:/backup alpine tar czf /backup/dex-data.tar.gz -C /dex ."
# Restart services
echo "[7/7] Restarting services..."
$SSH "cd /opt/services && docker compose up -d"
# Bundle everything and download
echo ""
echo "Downloading backup..."
$SSH "cd /tmp && tar cf ${BACKUP_NAME}.tar -C ${REMOTE_TMP} ."
$SCP "${HOST}:/tmp/${BACKUP_NAME}.tar" "${DEST_DIR}/${BACKUP_NAME}.tar"
# Clean up remote
$SSH "rm -rf ${REMOTE_TMP} /tmp/${BACKUP_NAME}.tar"
# Show result
BACKUP_SIZE=$(du -h "${DEST_DIR}/${BACKUP_NAME}.tar" | cut -f1)
echo ""
echo "=== Backup complete ==="
echo "File: ${DEST_DIR}/${BACKUP_NAME}.tar (${BACKUP_SIZE})"
echo ""
echo "Contents:"
tar tf "${DEST_DIR}/${BACKUP_NAME}.tar" | sed 's/^/ /'
echo ""
echo "Downtime was ~30 seconds while services were stopped for consistency."