|
1 | 1 | #!/usr/bin/env bash |
2 | | -# Backup lux-mon MariaDB + settings + .env |
| 2 | +# Backup lux-mon: MariaDB + InfluxDB v2 + Grafana provisioning/dashboards + .env + settings. |
| 3 | +# The resulting archive can be restored with scripts/restore.sh on a fresh install. |
3 | 4 | set -euo pipefail |
4 | 5 |
|
5 | 6 | SRC_DIR="${LUX_SRC_DIR:-$(cd "$(dirname "$0")/.." && pwd)}" |
6 | 7 |
|
7 | | -# Load .env if present so DB credentials are available |
| 8 | +# Load .env if present so DB credentials and paths are available. |
8 | 9 | if [ -f "$SRC_DIR/.env" ]; then |
9 | 10 | set -a |
10 | 11 | . "$SRC_DIR/.env" |
|
13 | 14 |
|
14 | 15 | BACKUP_DIR="${LUX_BACKUP_DIR:-/var/backups/lux-mon}" |
15 | 16 | KEEP_DAYS="${LUX_BACKUP_KEEP_DAYS:-30}" |
| 17 | +REMOTE_DEST="${LUX_BACKUP_REMOTE:-}" |
| 18 | + |
16 | 19 | DB_NAME="${LUX_MARIADB_DATABASE:-luxmon}" |
17 | 20 | DB_USER="${LUX_MARIADB_USER:-luxmon}" |
18 | 21 | DB_PASS="${LUX_MARIADB_PASSWORD:-}" |
19 | 22 | DB_HOST="${LUX_MARIADB_HOST:-localhost}" |
20 | 23 | DB_PORT="${LUX_MARIADB_PORT:-3306}" |
| 24 | + |
| 25 | +INFLUX_URL="${LUX_INFLUX_URL:-http://localhost:8086}" |
| 26 | +INFLUX_TOKEN="${LUX_INFLUX_TOKEN:-}" |
| 27 | +INFLUX_ORG="${LUX_INFLUX_ORG:-luxmon}" |
| 28 | +INFLUX_BUCKET="${LUX_INFLUX_BUCKET:-luxmon}" |
| 29 | + |
| 30 | +GRAFANA_PROVISIONING="${LUX_GRAFANA_PROVISIONING:-/etc/grafana/provisioning}" |
| 31 | +GRAFANA_DASHBOARDS="${LUX_GRAFANA_DASHBOARDS:-/var/lib/grafana/dashboards}" |
| 32 | + |
21 | 33 | TIMESTAMP=$(date +%Y%m%d-%H%M%S) |
22 | 34 | NAME="luxmon-backup-${TIMESTAMP}" |
23 | 35 | TMP=$(mktemp -d) |
24 | 36 | trap 'rm -rf "$TMP"' EXIT |
25 | 37 |
|
26 | 38 | mkdir -p "$BACKUP_DIR" |
27 | 39 |
|
| 40 | +echo "[backup] Starting lux-mon backup: ${NAME}" |
| 41 | + |
| 42 | +# 1. MariaDB dump |
28 | 43 | mysqldump -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USER" ${DB_PASS:+-p"$DB_PASS"} "$DB_NAME" \ |
29 | 44 | > "$TMP/luxmon.sql" |
| 45 | +echo "[backup] MariaDB dump OK" |
| 46 | + |
| 47 | +# 2. InfluxDB v2 backup (works whether running natively or in Docker) |
| 48 | +if [ -n "$INFLUX_TOKEN" ]; then |
| 49 | + INFLUX_BACKUP_DIR="$TMP/influxdb-backup" |
| 50 | + mkdir -p "$INFLUX_BACKUP_DIR" |
| 51 | + |
| 52 | + # Prefer a local influx CLI if installed. |
| 53 | + if command -v influx >/dev/null 2>&1; then |
| 54 | + influx backup \ |
| 55 | + --host "$INFLUX_URL" \ |
| 56 | + --org "$INFLUX_ORG" \ |
| 57 | + --token "$INFLUX_TOKEN" \ |
| 58 | + "$INFLUX_BACKUP_DIR" |
| 59 | + else |
| 60 | + # Fall back to running the official influxdb container against the live API. |
| 61 | + docker run --rm --network host \ |
| 62 | + -e INFLUX_TOKEN="$INFLUX_TOKEN" \ |
| 63 | + influxdb:2.7 influx backup \ |
| 64 | + --host "$INFLUX_URL" \ |
| 65 | + --org "$INFLUX_ORG" \ |
| 66 | + --token "$INFLUX_TOKEN" \ |
| 67 | + /out |
| 68 | + # Docker bind-mount the output directory into the container. |
| 69 | + docker run --rm --network host \ |
| 70 | + -v "$INFLUX_BACKUP_DIR:/out" \ |
| 71 | + -e INFLUX_TOKEN="$INFLUX_TOKEN" \ |
| 72 | + influxdb:2.7 influx backup \ |
| 73 | + --host "$INFLUX_URL" \ |
| 74 | + --org "$INFLUX_ORG" \ |
| 75 | + --token "$INFLUX_TOKEN" \ |
| 76 | + /out |
| 77 | + fi |
| 78 | + echo "[backup] InfluxDB backup OK" |
| 79 | +else |
| 80 | + echo "[backup] Skipping InfluxDB backup: LUX_INFLUX_TOKEN not set" |
| 81 | +fi |
| 82 | + |
| 83 | +# 3. Grafana provisioning and dashboard JSON files |
| 84 | +if [ -d "$GRAFANA_PROVISIONING" ]; then |
| 85 | + cp -a "$GRAFANA_PROVISIONING" "$TMP/grafana-provisioning" |
| 86 | + echo "[backup] Grafana provisioning OK" |
| 87 | +fi |
| 88 | +if [ -d "$GRAFANA_DASHBOARDS" ]; then |
| 89 | + cp -a "$GRAFANA_DASHBOARDS" "$TMP/grafana-dashboards" |
| 90 | + echo "[backup] Grafana dashboards OK" |
| 91 | +fi |
30 | 92 |
|
| 93 | +# 4. lux-mon .env configuration |
31 | 94 | if [ -f "$SRC_DIR/.env" ]; then |
32 | 95 | cp "$SRC_DIR/.env" "$TMP/env" |
| 96 | + echo "[backup] .env OK" |
33 | 97 | fi |
34 | 98 |
|
35 | | -curl -sf http://127.0.0.1:8080/api/settings > "$TMP/settings.json" || true |
| 99 | +# 5. Runtime settings from the API |
| 100 | +curl -sf "${LUX_API_URL:-http://127.0.0.1:8080}/api/settings" > "$TMP/settings.json" || true |
36 | 101 |
|
37 | | -tar -czf "$BACKUP_DIR/${NAME}.tar.gz" -C "$TMP" . |
| 102 | +# 6. Metadata |
| 103 | +{ |
| 104 | + echo "NAME=${NAME}" |
| 105 | + echo "TIMESTAMP=${TIMESTAMP}" |
| 106 | + echo "HOSTNAME=$(hostname)" |
| 107 | + echo "SRC_DIR=${SRC_DIR}" |
| 108 | + echo "DB_HOST=${DB_HOST}" |
| 109 | + echo "DB_NAME=${DB_NAME}" |
| 110 | + echo "INFLUX_URL=${INFLUX_URL}" |
| 111 | + echo "INFLUX_ORG=${INFLUX_ORG}" |
| 112 | + echo "INFLUX_BUCKET=${INFLUX_BUCKET}" |
| 113 | +} > "$TMP/backup.meta" |
38 | 114 |
|
| 115 | +# 7. Create archive |
| 116 | +ARCHIVE="$BACKUP_DIR/${NAME}.tar.gz" |
| 117 | +tar -czf "$ARCHIVE" -C "$TMP" . |
| 118 | +echo "[backup] Archive created: ${ARCHIVE}" |
| 119 | + |
| 120 | +# 8. Rotate local backups |
39 | 121 | find "$BACKUP_DIR" -maxdepth 1 -name 'luxmon-backup-*.tar.gz' -mtime +"$KEEP_DAYS" -delete || true |
40 | 122 |
|
41 | | -echo "$BACKUP_DIR/${NAME}.tar.gz" |
| 123 | +# 9. Optional remote copy |
| 124 | +if [ -n "$REMOTE_DEST" ]; then |
| 125 | + echo "[backup] Copying to remote: ${REMOTE_DEST}" |
| 126 | + if [[ "$REMOTE_DEST" == rsync://* ]]; then |
| 127 | + rsync -a --mkpath "$ARCHIVE" "${REMOTE_DEST#rsync://}" |
| 128 | + else |
| 129 | + # Treat as scp-style destination, e.g. user@host:/path or /mnt/usb |
| 130 | + if [[ "$REMOTE_DEST" == *@*:* ]]; then |
| 131 | + scp "$ARCHIVE" "$REMOTE_DEST/" |
| 132 | + else |
| 133 | + mkdir -p "$REMOTE_DEST" |
| 134 | + cp -a "$ARCHIVE" "$REMOTE_DEST/" |
| 135 | + fi |
| 136 | + fi |
| 137 | + echo "[backup] Remote copy OK" |
| 138 | +fi |
| 139 | + |
| 140 | +echo "[backup] Done: ${ARCHIVE}" |
0 commit comments