Initial backup skill implementation
This commit is contained in:
169
SKILL.md
Normal file
169
SKILL.md
Normal file
@@ -0,0 +1,169 @@
|
||||
---
|
||||
name: backup
|
||||
description: Automated backup and restore using restic
|
||||
metadata:
|
||||
version: "1.0.0"
|
||||
vibestack:
|
||||
main: false
|
||||
---
|
||||
|
||||
# Backup Skill
|
||||
|
||||
Automated backup and restore for all VibeStack data using [restic](https://restic.net/).
|
||||
|
||||
## Features
|
||||
|
||||
- Incremental, encrypted backups
|
||||
- Multiple backup targets (local, S3, B2, SFTP)
|
||||
- Scheduled automatic backups via cron
|
||||
- Retention policy management
|
||||
- Point-in-time restore
|
||||
- PostgreSQL-aware backups (pg_dump)
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `BACKUP_SCHEDULE` | `0 3 * * *` | Cron schedule (default: 3am daily) |
|
||||
| `BACKUP_RETENTION` | `7d` | Retention period |
|
||||
| `BACKUP_TARGET` | `/backups` | Local backup directory |
|
||||
| `BACKUP_PASSWORD` | (required) | Encryption password |
|
||||
| `BACKUP_S3_BUCKET` | (none) | S3 bucket URL (e.g., `s3:bucket-name/path`) |
|
||||
| `BACKUP_S3_ACCESS_KEY` | (none) | S3 access key |
|
||||
| `BACKUP_S3_SECRET_KEY` | (none) | S3 secret key |
|
||||
| `BACKUP_B2_ACCOUNT_ID` | (none) | Backblaze B2 account ID |
|
||||
| `BACKUP_B2_ACCOUNT_KEY` | (none) | Backblaze B2 account key |
|
||||
| `BACKUP_B2_BUCKET` | (none) | B2 bucket name |
|
||||
| `BACKUP_SFTP_HOST` | (none) | SFTP host for remote backup |
|
||||
| `BACKUP_SFTP_USER` | (none) | SFTP username |
|
||||
| `BACKUP_SFTP_PATH` | (none) | SFTP path |
|
||||
|
||||
## What Gets Backed Up
|
||||
|
||||
| Path | Description |
|
||||
|------|-------------|
|
||||
| `/data/postgres` | PostgreSQL data (via pg_dump) |
|
||||
| `/data/redis` | Redis persistence files |
|
||||
| `/data/duckdb` | DuckDB databases |
|
||||
| `/data/loki` | Log data |
|
||||
| `/data/caddy` | TLS certificates |
|
||||
| `/personalities` | Agent personality configs |
|
||||
| `/workspaces` | Agent workspaces |
|
||||
|
||||
## Usage
|
||||
|
||||
### Manual Backup
|
||||
|
||||
```bash
|
||||
# Trigger immediate backup
|
||||
/skills/backup/scripts/backup.sh
|
||||
|
||||
# Backup specific path
|
||||
/skills/backup/scripts/backup.sh /data/postgres
|
||||
```
|
||||
|
||||
### Manual Restore
|
||||
|
||||
```bash
|
||||
# List available snapshots
|
||||
/skills/backup/scripts/restore.sh --list
|
||||
|
||||
# Restore latest snapshot
|
||||
/skills/backup/scripts/restore.sh --latest
|
||||
|
||||
# Restore specific snapshot
|
||||
/skills/backup/scripts/restore.sh --snapshot abc123
|
||||
|
||||
# Restore specific path
|
||||
/skills/backup/scripts/restore.sh --latest --path /data/postgres
|
||||
```
|
||||
|
||||
### Check Backup Status
|
||||
|
||||
```bash
|
||||
# Show backup stats
|
||||
restic -r "$BACKUP_TARGET" stats
|
||||
|
||||
# List snapshots
|
||||
restic -r "$BACKUP_TARGET" snapshots
|
||||
```
|
||||
|
||||
## Backup Targets
|
||||
|
||||
### Local (default)
|
||||
|
||||
```bash
|
||||
BACKUP_TARGET=/backups
|
||||
BACKUP_PASSWORD=your-secret-password
|
||||
```
|
||||
|
||||
### Amazon S3
|
||||
|
||||
```bash
|
||||
BACKUP_TARGET=s3:my-bucket/vibestack-backups
|
||||
BACKUP_S3_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE
|
||||
BACKUP_S3_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
|
||||
BACKUP_PASSWORD=your-secret-password
|
||||
```
|
||||
|
||||
### Backblaze B2
|
||||
|
||||
```bash
|
||||
BACKUP_TARGET=b2:my-bucket:/vibestack-backups
|
||||
BACKUP_B2_ACCOUNT_ID=your-account-id
|
||||
BACKUP_B2_ACCOUNT_KEY=your-account-key
|
||||
BACKUP_PASSWORD=your-secret-password
|
||||
```
|
||||
|
||||
### SFTP
|
||||
|
||||
```bash
|
||||
BACKUP_TARGET=sftp:user@host:/path/to/backups
|
||||
BACKUP_SFTP_HOST=backup.example.com
|
||||
BACKUP_SFTP_USER=backup
|
||||
BACKUP_PASSWORD=your-secret-password
|
||||
```
|
||||
|
||||
## Retention Policy
|
||||
|
||||
The `BACKUP_RETENTION` variable controls how long backups are kept:
|
||||
|
||||
| Format | Example | Description |
|
||||
|--------|---------|-------------|
|
||||
| `Xd` | `7d` | Keep backups for X days |
|
||||
| `Xw` | `4w` | Keep backups for X weeks |
|
||||
| `Xm` | `3m` | Keep backups for X months |
|
||||
|
||||
Restic's `forget` command with `--keep-within` is used to enforce retention.
|
||||
|
||||
## PostgreSQL Backups
|
||||
|
||||
When PostgreSQL is detected, the backup skill:
|
||||
1. Runs `pg_dump` to create a consistent SQL dump
|
||||
2. Stores the dump at `/data/postgres/backup.sql`
|
||||
3. Includes it in the restic backup
|
||||
|
||||
This ensures database consistency during backup.
|
||||
|
||||
## Monitoring
|
||||
|
||||
Backup status is written to `/run/vibestack/backup-status.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"last_backup": "2024-01-15T03:00:00Z",
|
||||
"last_status": "success",
|
||||
"snapshot_id": "abc123def",
|
||||
"duration_seconds": 45,
|
||||
"bytes_added": 1048576
|
||||
}
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
1. **Always set a strong `BACKUP_PASSWORD`** - backups are encrypted with this
|
||||
2. Store credentials securely (use environment variables, not files)
|
||||
3. Test restore procedure regularly
|
||||
4. Keep backup target separate from primary data
|
||||
91
scripts/autorun.sh
Normal file
91
scripts/autorun.sh
Normal file
@@ -0,0 +1,91 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
BACKUP_TARGET="${BACKUP_TARGET:-/backups}"
|
||||
BACKUP_PASSWORD="${BACKUP_PASSWORD:-}"
|
||||
|
||||
# Idempotent restic installation
|
||||
install_restic() {
|
||||
if command -v restic &>/dev/null; then
|
||||
echo "restic already installed: $(restic version)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Installing restic..."
|
||||
|
||||
# Install from official repository
|
||||
apt-get update
|
||||
apt-get install -y restic
|
||||
|
||||
# Update to latest version
|
||||
restic self-update 2>/dev/null || true
|
||||
|
||||
echo "restic installed: $(restic version)"
|
||||
}
|
||||
|
||||
# Install cron for scheduled backups
|
||||
install_cron() {
|
||||
if command -v cron &>/dev/null; then
|
||||
echo "cron already installed"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Installing cron..."
|
||||
apt-get update
|
||||
apt-get install -y cron
|
||||
|
||||
echo "cron installed"
|
||||
}
|
||||
|
||||
# Setup directories
|
||||
setup_dirs() {
|
||||
# Create local backup directory if using local target
|
||||
if [[ "$BACKUP_TARGET" == /* ]]; then
|
||||
mkdir -p "$BACKUP_TARGET"
|
||||
echo "Local backup directory: $BACKUP_TARGET"
|
||||
fi
|
||||
|
||||
# Create status directory
|
||||
mkdir -p /run/vibestack
|
||||
}
|
||||
|
||||
# Initialize restic repository
|
||||
init_repository() {
|
||||
if [ -z "$BACKUP_PASSWORD" ]; then
|
||||
echo "WARNING: BACKUP_PASSWORD not set - skipping repository init"
|
||||
echo "Set BACKUP_PASSWORD to enable backups"
|
||||
return 0
|
||||
fi
|
||||
|
||||
export RESTIC_PASSWORD="$BACKUP_PASSWORD"
|
||||
export RESTIC_REPOSITORY="$BACKUP_TARGET"
|
||||
|
||||
# Setup cloud credentials if provided
|
||||
if [ -n "$BACKUP_S3_ACCESS_KEY" ]; then
|
||||
export AWS_ACCESS_KEY_ID="$BACKUP_S3_ACCESS_KEY"
|
||||
export AWS_SECRET_ACCESS_KEY="$BACKUP_S3_SECRET_KEY"
|
||||
fi
|
||||
|
||||
if [ -n "$BACKUP_B2_ACCOUNT_ID" ]; then
|
||||
export B2_ACCOUNT_ID="$BACKUP_B2_ACCOUNT_ID"
|
||||
export B2_ACCOUNT_KEY="$BACKUP_B2_ACCOUNT_KEY"
|
||||
fi
|
||||
|
||||
# Check if repository exists
|
||||
if restic cat config &>/dev/null; then
|
||||
echo "Backup repository already initialized"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Initializing backup repository at $BACKUP_TARGET..."
|
||||
restic init
|
||||
|
||||
echo "Backup repository initialized"
|
||||
}
|
||||
|
||||
install_restic
|
||||
install_cron
|
||||
setup_dirs
|
||||
init_repository
|
||||
|
||||
echo "Backup setup complete"
|
||||
146
scripts/backup.sh
Normal file
146
scripts/backup.sh
Normal file
@@ -0,0 +1,146 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
BACKUP_TARGET="${BACKUP_TARGET:-/backups}"
|
||||
BACKUP_PASSWORD="${BACKUP_PASSWORD:-}"
|
||||
BACKUP_RETENTION="${BACKUP_RETENTION:-7d}"
|
||||
|
||||
# Paths to backup
|
||||
BACKUP_PATHS=(
|
||||
"/data/postgres"
|
||||
"/data/redis"
|
||||
"/data/duckdb"
|
||||
"/data/loki"
|
||||
"/data/caddy"
|
||||
"/personalities"
|
||||
"/workspaces"
|
||||
)
|
||||
|
||||
# Validate password
|
||||
if [ -z "$BACKUP_PASSWORD" ]; then
|
||||
echo "ERROR: BACKUP_PASSWORD is required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Setup restic environment
|
||||
export RESTIC_PASSWORD="$BACKUP_PASSWORD"
|
||||
export RESTIC_REPOSITORY="$BACKUP_TARGET"
|
||||
|
||||
# Setup cloud credentials if provided
|
||||
[ -n "$BACKUP_S3_ACCESS_KEY" ] && export AWS_ACCESS_KEY_ID="$BACKUP_S3_ACCESS_KEY"
|
||||
[ -n "$BACKUP_S3_SECRET_KEY" ] && export AWS_SECRET_ACCESS_KEY="$BACKUP_S3_SECRET_KEY"
|
||||
[ -n "$BACKUP_B2_ACCOUNT_ID" ] && export B2_ACCOUNT_ID="$BACKUP_B2_ACCOUNT_ID"
|
||||
[ -n "$BACKUP_B2_ACCOUNT_KEY" ] && export B2_ACCOUNT_KEY="$BACKUP_B2_ACCOUNT_KEY"
|
||||
|
||||
START_TIME=$(date +%s)
|
||||
echo "=== VibeStack Backup ==="
|
||||
echo "Time: $(date -Iseconds)"
|
||||
echo "Target: $BACKUP_TARGET"
|
||||
echo ""
|
||||
|
||||
# Pre-backup: dump PostgreSQL if running
|
||||
pre_backup_postgres() {
|
||||
if command -v pg_dump &>/dev/null && [ -d "/data/postgres" ]; then
|
||||
echo "Creating PostgreSQL dump..."
|
||||
|
||||
# Source postgres env if available
|
||||
[ -f /run/vibestack/postgres.env ] && source /run/vibestack/postgres.env
|
||||
|
||||
local pg_user="${POSTGRES_USER:-vibestack}"
|
||||
local pg_db="${POSTGRES_DB:-vibestack}"
|
||||
local dump_file="/data/postgres/backup.sql"
|
||||
|
||||
if pg_dump -U "$pg_user" -d "$pg_db" -f "$dump_file" 2>/dev/null; then
|
||||
echo " PostgreSQL dump created: $dump_file"
|
||||
else
|
||||
echo " PostgreSQL dump skipped (database not running or accessible)"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Pre-backup: trigger Redis BGSAVE if running
|
||||
pre_backup_redis() {
|
||||
if command -v redis-cli &>/dev/null; then
|
||||
echo "Triggering Redis BGSAVE..."
|
||||
|
||||
# Source redis env if available
|
||||
[ -f /run/vibestack/redis.env ] && source /run/vibestack/redis.env
|
||||
|
||||
local redis_pass="${REDIS_PASSWORD:-}"
|
||||
local auth_args=""
|
||||
[ -n "$redis_pass" ] && auth_args="-a $redis_pass"
|
||||
|
||||
if redis-cli $auth_args BGSAVE 2>/dev/null; then
|
||||
# Wait for save to complete
|
||||
sleep 2
|
||||
echo " Redis BGSAVE triggered"
|
||||
else
|
||||
echo " Redis BGSAVE skipped (not running)"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Run pre-backup hooks
|
||||
echo "Running pre-backup hooks..."
|
||||
pre_backup_postgres
|
||||
pre_backup_redis
|
||||
echo ""
|
||||
|
||||
# Build list of paths that exist
|
||||
existing_paths=()
|
||||
for path in "${BACKUP_PATHS[@]}"; do
|
||||
if [ -e "$path" ]; then
|
||||
existing_paths+=("$path")
|
||||
echo "Including: $path"
|
||||
fi
|
||||
done
|
||||
|
||||
# Allow overriding with specific path
|
||||
if [ -n "$1" ] && [ -e "$1" ]; then
|
||||
existing_paths=("$1")
|
||||
echo "Backing up only: $1"
|
||||
fi
|
||||
|
||||
if [ ${#existing_paths[@]} -eq 0 ]; then
|
||||
echo "WARNING: No paths to backup"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Starting backup..."
|
||||
|
||||
# Run restic backup
|
||||
BACKUP_OUTPUT=$(restic backup "${existing_paths[@]}" --json 2>&1 | tail -1)
|
||||
|
||||
# Parse results
|
||||
SNAPSHOT_ID=$(echo "$BACKUP_OUTPUT" | jq -r '.snapshot_id // empty' 2>/dev/null || echo "")
|
||||
BYTES_ADDED=$(echo "$BACKUP_OUTPUT" | jq -r '.data_added // 0' 2>/dev/null || echo "0")
|
||||
|
||||
END_TIME=$(date +%s)
|
||||
DURATION=$((END_TIME - START_TIME))
|
||||
|
||||
echo ""
|
||||
echo "Backup complete!"
|
||||
echo " Snapshot: ${SNAPSHOT_ID:-unknown}"
|
||||
echo " Duration: ${DURATION}s"
|
||||
echo " Data added: $BYTES_ADDED bytes"
|
||||
|
||||
# Apply retention policy
|
||||
echo ""
|
||||
echo "Applying retention policy: keep within $BACKUP_RETENTION..."
|
||||
restic forget --keep-within "$BACKUP_RETENTION" --prune
|
||||
|
||||
# Write status
|
||||
cat > /run/vibestack/backup-status.json << EOF
|
||||
{
|
||||
"status": "idle",
|
||||
"last_backup": "$(date -Iseconds)",
|
||||
"last_status": "success",
|
||||
"snapshot_id": "$SNAPSHOT_ID",
|
||||
"duration_seconds": $DURATION,
|
||||
"bytes_added": $BYTES_ADDED
|
||||
}
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "=== Backup Complete ==="
|
||||
160
scripts/restore.sh
Normal file
160
scripts/restore.sh
Normal file
@@ -0,0 +1,160 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
BACKUP_TARGET="${BACKUP_TARGET:-/backups}"
|
||||
BACKUP_PASSWORD="${BACKUP_PASSWORD:-}"
|
||||
|
||||
# Validate password
|
||||
if [ -z "$BACKUP_PASSWORD" ]; then
|
||||
echo "ERROR: BACKUP_PASSWORD is required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Setup restic environment
|
||||
export RESTIC_PASSWORD="$BACKUP_PASSWORD"
|
||||
export RESTIC_REPOSITORY="$BACKUP_TARGET"
|
||||
|
||||
# Setup cloud credentials if provided
|
||||
[ -n "$BACKUP_S3_ACCESS_KEY" ] && export AWS_ACCESS_KEY_ID="$BACKUP_S3_ACCESS_KEY"
|
||||
[ -n "$BACKUP_S3_SECRET_KEY" ] && export AWS_SECRET_ACCESS_KEY="$BACKUP_S3_SECRET_KEY"
|
||||
[ -n "$BACKUP_B2_ACCOUNT_ID" ] && export B2_ACCOUNT_ID="$BACKUP_B2_ACCOUNT_ID"
|
||||
[ -n "$BACKUP_B2_ACCOUNT_KEY" ] && export B2_ACCOUNT_KEY="$BACKUP_B2_ACCOUNT_KEY"
|
||||
|
||||
usage() {
|
||||
cat << EOF
|
||||
VibeStack Restore Tool
|
||||
|
||||
Usage: restore.sh [OPTIONS]
|
||||
|
||||
Options:
|
||||
--list List available snapshots
|
||||
--latest Restore from latest snapshot
|
||||
--snapshot ID Restore from specific snapshot
|
||||
--path PATH Restore only specific path
|
||||
--target DIR Restore to specific directory (default: /)
|
||||
--dry-run Show what would be restored without doing it
|
||||
--help Show this help message
|
||||
|
||||
Examples:
|
||||
# List all snapshots
|
||||
restore.sh --list
|
||||
|
||||
# Restore everything from latest snapshot
|
||||
restore.sh --latest
|
||||
|
||||
# Restore specific path from latest
|
||||
restore.sh --latest --path /data/postgres
|
||||
|
||||
# Restore from specific snapshot
|
||||
restore.sh --snapshot abc123def
|
||||
|
||||
# Restore to different directory
|
||||
restore.sh --latest --target /tmp/restore
|
||||
|
||||
# Preview restore
|
||||
restore.sh --latest --dry-run
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
SNAPSHOT=""
|
||||
RESTORE_PATH=""
|
||||
TARGET_DIR="/"
|
||||
DRY_RUN=""
|
||||
LIST_ONLY=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--list)
|
||||
LIST_ONLY="true"
|
||||
shift
|
||||
;;
|
||||
--latest)
|
||||
SNAPSHOT="latest"
|
||||
shift
|
||||
;;
|
||||
--snapshot)
|
||||
SNAPSHOT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--path)
|
||||
RESTORE_PATH="$2"
|
||||
shift 2
|
||||
;;
|
||||
--target)
|
||||
TARGET_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN="--dry-run"
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "=== VibeStack Restore ==="
|
||||
echo "Repository: $BACKUP_TARGET"
|
||||
echo ""
|
||||
|
||||
# List snapshots
|
||||
if [ "$LIST_ONLY" = "true" ]; then
|
||||
echo "Available snapshots:"
|
||||
echo ""
|
||||
restic snapshots
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Validate snapshot
|
||||
if [ -z "$SNAPSHOT" ]; then
|
||||
echo "ERROR: Specify --latest or --snapshot ID"
|
||||
echo ""
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Show what we're restoring
|
||||
echo "Snapshot: $SNAPSHOT"
|
||||
[ -n "$RESTORE_PATH" ] && echo "Path: $RESTORE_PATH"
|
||||
echo "Target: $TARGET_DIR"
|
||||
[ -n "$DRY_RUN" ] && echo "Mode: DRY RUN"
|
||||
echo ""
|
||||
|
||||
# Build restore command
|
||||
restore_args=("restore" "$SNAPSHOT" "--target" "$TARGET_DIR")
|
||||
[ -n "$RESTORE_PATH" ] && restore_args+=("--include" "$RESTORE_PATH")
|
||||
[ -n "$DRY_RUN" ] && restore_args+=("$DRY_RUN")
|
||||
|
||||
# Confirm unless dry run
|
||||
if [ -z "$DRY_RUN" ]; then
|
||||
echo "WARNING: This will overwrite existing files!"
|
||||
echo ""
|
||||
read -p "Continue? [y/N] " -n 1 -r
|
||||
echo ""
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Aborted"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Restoring..."
|
||||
restic "${restore_args[@]}"
|
||||
|
||||
# Post-restore for PostgreSQL
|
||||
if [ -z "$DRY_RUN" ] && [ -f "$TARGET_DIR/data/postgres/backup.sql" ]; then
|
||||
echo ""
|
||||
echo "PostgreSQL dump found at $TARGET_DIR/data/postgres/backup.sql"
|
||||
echo "To restore the database, run:"
|
||||
echo " psql -U vibestack -d vibestack -f $TARGET_DIR/data/postgres/backup.sql"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Restore Complete ==="
|
||||
67
scripts/run.sh
Normal file
67
scripts/run.sh
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
BACKUP_SCHEDULE="${BACKUP_SCHEDULE:-0 3 * * *}"
|
||||
SKILL_DIR="$(dirname "$(dirname "$0")")"
|
||||
|
||||
# Validate password is set
|
||||
if [ -z "$BACKUP_PASSWORD" ]; then
|
||||
echo "ERROR: BACKUP_PASSWORD is required"
|
||||
echo "Set BACKUP_PASSWORD environment variable to enable backups"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Setup cron job for scheduled backups
|
||||
setup_cron() {
|
||||
local cron_file="/etc/cron.d/vibestack-backup"
|
||||
local backup_script="$SKILL_DIR/scripts/backup.sh"
|
||||
|
||||
# Build environment exports for cron
|
||||
local env_exports=""
|
||||
env_exports+="BACKUP_PASSWORD='$BACKUP_PASSWORD' "
|
||||
env_exports+="BACKUP_TARGET='${BACKUP_TARGET:-/backups}' "
|
||||
env_exports+="BACKUP_RETENTION='${BACKUP_RETENTION:-7d}' "
|
||||
|
||||
[ -n "$BACKUP_S3_ACCESS_KEY" ] && env_exports+="BACKUP_S3_ACCESS_KEY='$BACKUP_S3_ACCESS_KEY' "
|
||||
[ -n "$BACKUP_S3_SECRET_KEY" ] && env_exports+="BACKUP_S3_SECRET_KEY='$BACKUP_S3_SECRET_KEY' "
|
||||
[ -n "$BACKUP_B2_ACCOUNT_ID" ] && env_exports+="BACKUP_B2_ACCOUNT_ID='$BACKUP_B2_ACCOUNT_ID' "
|
||||
[ -n "$BACKUP_B2_ACCOUNT_KEY" ] && env_exports+="BACKUP_B2_ACCOUNT_KEY='$BACKUP_B2_ACCOUNT_KEY' "
|
||||
|
||||
# Create cron job
|
||||
cat > "$cron_file" << EOF
|
||||
# VibeStack automated backup
|
||||
SHELL=/bin/bash
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
|
||||
$BACKUP_SCHEDULE root $env_exports $backup_script >> /var/log/vibestack-backup.log 2>&1
|
||||
EOF
|
||||
|
||||
chmod 644 "$cron_file"
|
||||
echo "Cron job configured: $BACKUP_SCHEDULE"
|
||||
}
|
||||
|
||||
# Write initial status
|
||||
write_status() {
|
||||
cat > /run/vibestack/backup-status.json << EOF
|
||||
{
|
||||
"status": "running",
|
||||
"schedule": "$BACKUP_SCHEDULE",
|
||||
"target": "${BACKUP_TARGET:-/backups}",
|
||||
"last_backup": null,
|
||||
"last_status": null
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
setup_cron
|
||||
write_status
|
||||
|
||||
echo "Starting cron daemon for scheduled backups..."
|
||||
echo "Schedule: $BACKUP_SCHEDULE"
|
||||
echo "Target: ${BACKUP_TARGET:-/backups}"
|
||||
echo ""
|
||||
echo "Manual backup: $SKILL_DIR/scripts/backup.sh"
|
||||
echo "Manual restore: $SKILL_DIR/scripts/restore.sh --list"
|
||||
|
||||
# Start cron in foreground
|
||||
exec cron -f
|
||||
Reference in New Issue
Block a user