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.
*** Update – meine neue Backup-Strategie findest du hier! ***
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!