In diesem Beitrag beschreibe ich, wie ich meine indi-allsky Installation auf meinem Raspberry Pi vollautomatisiert auf einer externen Festplatte sichere.
Die offizielle indi-allsky-Dokumentation beschreibt sehr gut, was gesichert werden sollte – lässt aber offen, wie man daraus ein belastbares, automatisiertes Backup macht.
In meinem Setup läuft indi-allsky dauerhaft auf einem Raspberry Pi. Die Sicherung erfolgt nicht lokal, sondern auf einer externen SSD an einer Fritzbox, angebunden per SMB.
Ziel war es, die Empfehlungen aus der offiziellen Dokumentation: Backup and Recovery – indi-allsky konsequent umzusetzen und produktionsreif zu erweitern:
- externes Ziel
- automatische Backups
- Integritätsprüfungen
- zeitbasierte Retention
- Mail-Benachrichtigung bei Fehlern
…und so bin ich vorgegangen!
Was laut offizieller indi-allsky-Doku gesichert werden muss
Basierend auf der offiziellen Dokumentation sind relevant:
- SQLite-Datenbank – /var/lib/indi-allsky/indi-allsky.sqlite
- Flask-Konfiguration – /etc/indi-allsky/flask.json
- Service-Environment (optional) – /etc/indi-allsky/indi-allsky.env
- Database Migrations – /var/lib/indi-allsky/migrations/versions/
Videos, Fotos & Thumbnails sind zunächst nicht Teil dieser Anleitung. Hierzu folgt demnächst Phase 2!
Externes Laufwerk vorbereiten
Die SSD wird unter Linux mit ext4 formatiert:
sudo parted /dev/sda mklabel gpt mkpart BACKUP ext4 0% 100% quit sudo mkfs.ext4 -L backup_allsky /dev/sda1
Anschließend wird die SSD an die Fritzbox angeschlossen und über FRITZ!NAS (SMB) bereitgestellt. Wichtig: Es wird zwingend ein Nutzer benötigt, der Zugriff auf die NAS hat!
SMB-Mount auf dem Raspberry Pi
Mountpunkt anlegen:
sudo mkdir -p /mnt/backup_allsky
Benutzerdaten ablegen (Root-only) von dem Nutzer, der Zugriff auf die NAS hat:
sudo nano /root/.smbcredentials
username=DEIN_NUTZER password=DEIN_PASSWORT
/etc/fstab-Eintrag hinterlegen – Achtung: Eine Zeile:
//192.168.178.1/FRITZ.NAS /mnt/backup_allsky cifs credentials=/root/.smbcredentials,vers=3.1.1,iocharset=utf8,noforceuid,noforcegid,nounix,soft,_netdev,noserverino 0 0
noserverino ist gegen „stale file handle“ bei Fritzbox bewusst gesetzt!
Mount testen:
sudo mount /mnt/backup_allsky mountpoint /mnt/backup_allsky
Ausgabe sollte sein „is mountpoint“!
Backup-Script für indi-allsky (vollständig)
Datei anlegen:
sudo nano /usr/local/bin/backup_allsky.sh
Vollständiges, produktives Script – dieses
- führt ein Backup durch,
- prüft dieses auf Konsistenz
- und löscht alle Backups die älter sind als 6 Wochen.
Sollte es Fehler geben, geht eine Mail raus.
.sh
#!/bin/bash
set -euo pipefail
# ======================================================
# Configuration
# ======================================================
BACKUP_ROOT="/mnt/backup_allsky/backup_allsky"
DB_SRC="/var/lib/indi-allsky/indi-allsky.sqlite"
CONFIG_DIR="/etc/indi-allsky"
MIGRATIONS_DIR="/var/lib/indi-allsky/migrations/versions"
LOG="/var/log/backup_allsky.log"
MAIL_TO="mail@stefanwatzinger.de"
HOST="$(hostname)"
RETENTION_DAYS=42 # 6 weeks
# ======================================================
# Logging
# ======================================================
exec >>"$LOG" 2>&1
# ======================================================
# Error handling (MAIL ONLY ON FAILURE)
# ======================================================
error_handler() {
RC=$?
{
echo "Allsky Phase-1 Backup FEHLGESCHLAGEN"
echo "Host: $HOST"
echo "Zeit: $(date)"
echo "Exit-Code: $RC"
echo
echo "Letzte Logzeilen:"
tail -n 50 "$LOG"
} | mail -s "Allsky Backup FEHLER ($HOST)" "$MAIL_TO"
exit $RC
}
trap error_handler ERR
# ======================================================
# Start
# ======================================================
echo "=== Backup Start: $(date) ==="
# ======================================================
# Defensive Re-Mount (prevents stale handles)
# ======================================================
umount /mnt/backup_allsky || true
mount /mnt/backup_allsky
mountpoint -q /mnt/backup_allsky
# ======================================================
# Prepare directories
# ======================================================
mkdir -p \
"$BACKUP_ROOT/db" \
"$BACKUP_ROOT/config" \
"$BACKUP_ROOT/migrations"
# ======================================================
# Database backup (SQLite dump + gzip)
# ======================================================
echo "--- DB Backup ---"
DB_BACKUP="$BACKUP_ROOT/db/backup_indi-allsky_sqlite_$(date +%Y%m%d_%H%M%S).sql.gz"
sqlite3 "$DB_SRC" .dump \
| gzip -9 \
> "$DB_BACKUP"
echo "--- DB Integrity Check ---"
gunzip -t "$DB_BACKUP"
# ======================================================
# Config backup
# ======================================================
echo "--- Config Backup ---"
cp -v "$CONFIG_DIR/flask.json" "$BACKUP_ROOT/config/"
if [ -f "$CONFIG_DIR/indi-allsky.env" ]; then
cp -v "$CONFIG_DIR/indi-allsky.env" "$BACKUP_ROOT/config/"
fi
echo "--- Config Integrity Check ---"
test -s "$BACKUP_ROOT/config/flask.json"
# ======================================================
# Database migrations backup
# ======================================================
echo "--- Migrations Backup ---"
MIG_BACKUP="$BACKUP_ROOT/migrations/backup_migrations_$(date +%Y%m%d_%H%M%S).tgz"
tar -C "$MIGRATIONS_DIR" -czf "$MIG_BACKUP" .
echo "--- Migrations Integrity Check ---"
tar -tzf "$MIG_BACKUP" > /dev/null
# ======================================================
# Retention cleanup (AFTER successful backup)
# ======================================================
echo "--- Retention Cleanup (> ${RETENTION_DAYS} days) ---"
find "$BACKUP_ROOT/db" \
-type f -name "backup_indi-allsky_sqlite_*.sql.gz" \
-mtime +"$RETENTION_DAYS" -print -delete
find "$BACKUP_ROOT/migrations" \
-type f -name "backup_migrations_*.tgz" \
-mtime +"$RETENTION_DAYS" -print -delete
# ======================================================
# Finish
# ======================================================
echo "=== Backup Ende: $(date) ==="
Rechte setzen:
sudo chmod 700 /usr/local/bin/backup_allsky.sh
Cronjob (täglich, tagsüber)
sudo crontab -e
0 10 * * * /usr/local/bin/backup_allsky.sh
Logrotation in /etc/logrotate.d/backup_allsky
/var/log/backup_allsky.log {
weekly
rotate 8
compress
missingok
notifempty
create 640 root adm
}Check-Ups & Verifikation
Zur Sicherheit macht es Sinn, nach dem initialen Backup einmal die Konsistenz der Daten zu checken:
Datenbank:
gunzip -t backup_indi-allsky_sqlite_*.sql.gz
Migrations:
tar -tzf backup_migrations_*.tgz | head
Config:
python3 -m json.tool flask.json > /dev/null
Troubleshooting: Mailversand
Technisch basiert der Mailversand auf msmtp, das als leichtgewichtiger SMTP-Client auf dem Raspberry Pi installiert ist und sich als Ersatz für sendmail verhält.
Installation:
sudo apt install msmtp msmtp-mta bsd-mailx
msmtp wird so konfiguriert, dass es über den vorhandenen SMTP-Account des eigenen Mailproviders versendet. Die Konfiguration liegt systemweit in /etc/msmtprc und enthält Host, Port, Benutzer und Passwort des SMTP-Servers.
Im Backup-Script selbst wird keine eigene Mail-Logik implementiert, sondern ausschließlich mit:
set -euo pipefail trap error_handler ERR
gearbeitet.
Das bedeutet: Jeder Fehler (Mount-Probleme, defekte Archive, fehlgeschlagene Integritätschecks)
- führt zu einem sofortigen Abbruch des Scripts
- triggert die error_handler-Funktion
- und versendet eine konkrete Fehlermail mit Log-Auszug
Der Inhalt der Mail enthält:
- Hostname
- Zeitpunkt
- Exit-Code
- die letzten Logzeilen des Backup-Laufs
Erfolgreiche Backups erzeugen keine Mails!