Backup di un server ISPConfig: database, siti, mail, configurazioni e retention

Uno script Bash completo per il backup di un server ISPConfig: MariaDB, web, mail, configurazioni, lock, retention, log, verifica degli archivi e test di restore.

Quando diciamo “ho il backup del server”, la domanda successiva dovrebbe essere: backup di cosa, esattamente? In un server ISPConfig il sito web è soltanto una parte dello stato. Esistono database, caselle mail, configurazioni Apache/nginx, PHP, Postfix, Dovecot, certificati, cron, unità systemd e naturalmente i dati con cui ISPConfig descrive clienti, siti e servizi.

Copiare soltanto /var/www può quindi salvare i file applicativi e lasciare irrecuperabile tutto ciò che rende quei file un servizio funzionante. L’obiettivo corretto non è “avere dei tar.gz”, ma essere in grado di ricostruire una macchina in un tempo compatibile con l’attività.

RPO e RTO: due domande prima dello script

L’RPO indica quanti dati possiamo permetterci di perdere. Con un backup ogni 24 ore dobbiamo accettare, nel caso peggiore, quasi una giornata di modifiche perse. L’RTO indica invece quanto tempo possiamo restare senza servizio mentre ripristiniamo. Questi due valori dovrebbero decidere frequenza, numero di copie e complessità del sistema molto prima della scelta tra tar e rsync.

Consistenza: copiare un database non è come copiare un’immagine

Un file statico può essere copiato byte per byte. Un database sta cambiando mentre l’applicazione lavora. Copiare semplicemente i file di MariaDB a caldo può produrre una fotografia incoerente. Per questo lo script usa mariadb-dump --single-transaction per i database compatibili, creando una vista logica consistente senza fermare il servizio.

Backup locale e copia off-site

Scrivere il backup su un secondo disco dello stesso server protegge dal guasto di un filesystem, ma non dalla perdita dell’intera macchina, da un ransomware con privilegi sufficienti o da un errore amministrativo che cancella sia produzione sia backup. Per questo almeno una copia deve uscire dal server e, idealmente, avere credenziali e retention indipendenti.

Il restore è parte del backup

Un archivio creato senza errori non dimostra che il ripristino funzioni. Può mancare un database, un certificato, il proprietario corretto di un file o semplicemente la procedura con cui rimettere insieme i pezzi. Il test periodico di restore è quindi parte integrante della strategia, non un’attività da fare soltanto dopo un disastro.

Con questi obiettivi definiti possiamo costruire uno script che salvi le componenti giuste, mantenga una retention e ci permetta di verificare gli archivi.

Tre copie non significano tre backup indipendenti

Se lo stesso processo con gli stessi privilegi può cancellare produzione, backup locale e copia remota, abbiamo più copie ma un unico dominio di rischio. Una strategia robusta cerca di separare almeno una copia: server differente, credenziali differenti, retention non modificabile dal normale account di produzione oppure supporto offline.

La famosa regola “3-2-1” è un buon promemoria, non una formula magica: più copie, su supporti o sistemi differenti, con almeno una copia separata dal server originale. Il punto non è contare fino a tre ma evitare che lo stesso incidente possa distruggere tutto nello stesso momento.

Full, incrementale e retention

Lo script di questa guida produce fotografie complete delle componenti che salva. È semplice da capire e da ripristinare, ma consuma più spazio. In ambienti molto grandi può essere preferibile combinare backup completi periodici con incrementali o snapshot. Qualunque strategia scegliamo deve però avere una retention esplicita: senza una politica di conservazione il repository cresce finché finisce lo spazio, spesso proprio nel momento peggiore.

La retention non serve soltanto a risparmiare disco. Conservare più punti temporali protegge anche dagli incidenti scoperti tardi. Se un file è stato corrotto una settimana fa e abbiamo soltanto l’ultima copia della notte precedente, abbiamo copiato diligentemente la corruzione.

Cosa salviamo

/var/www/clients
/var/vmail
/usr/local/ispconfig
/etc/apache2
/etc/nginx
/etc/php
/etc/postfix
/etc/dovecot
/etc/letsencrypt
/etc/cron.d
/etc/systemd/system

Credenziali database

Evitiamo password dentro lo script. Usiamo un file root-only, per esempio /root/.my.cnf:

[client]
user=root
password=PASSWORD_FORTE
host=localhost
chmod 600 /root/.my.cnf

Script

#!/usr/bin/env bash
set -Eeuo pipefail

BACKUP_ROOT="/srv/backup/ispconfig"
DATE="$(date +%F_%H-%M-%S)"
DEST="${BACKUP_ROOT}/${DATE}"
LOG="/var/log/ispconfig-backup.log"
LOCK="/run/lock/ispconfig-backup.lock"
RETENTION_DAYS=14

exec 9>"$LOCK"
flock -n 9 || {
  echo "Backup già in esecuzione" >&2
  exit 1
}

exec >>"$LOG" 2>&1
echo "=== $(date -Is) START ==="

mkdir -p "$DEST/db" "$DEST/fs"

mariadb --batch --skip-column-names   -e 'SHOW DATABASES' |
grep -Ev '^(information_schema|performance_schema|mysql|sys)$' |
while read -r DB; do
  echo "Dump DB: $DB"
  mariadb-dump     --single-transaction     --routines     --events     --triggers     "$DB" |
  gzip -1 > "$DEST/db/${DB}.sql.gz"
done

tar --xattrs --acls --numeric-owner -czf "$DEST/fs/www-clients.tar.gz"   /var/www/clients

[ -d /var/vmail ] && tar --xattrs --acls --numeric-owner -czf "$DEST/fs/vmail.tar.gz"   /var/vmail

tar --xattrs --acls --numeric-owner -czf "$DEST/fs/config.tar.gz"   /usr/local/ispconfig   /etc/apache2   /etc/php   /etc/postfix   /etc/dovecot   /etc/letsencrypt   /etc/cron.d   /etc/systemd/system

find "$DEST" -type f -print0 | sort -z | xargs -0 sha256sum   > "$DEST/SHA256SUMS"

find "$DEST" -name '*.gz' -type f -print0 |
while IFS= read -r -d '' F; do
  gzip -t "$F"
done

find "$BACKUP_ROOT"   -mindepth 1 -maxdepth 1 -type d   -mtime +"$RETENTION_DAYS"   -print -exec rm -rf -- {} +

echo "=== $(date -Is) OK ==="

Installazione e timer systemd

install -o root -g root -m 0750 ispconfig-backup.sh /usr/local/sbin/ispconfig-backup
mkdir -p /srv/backup/ispconfig
chmod 700 /srv/backup/ispconfig
# /etc/systemd/system/ispconfig-backup.service
[Unit]
Description=Backup ISPConfig

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/ispconfig-backup

# /etc/systemd/system/ispconfig-backup.timer
[Unit]
Description=Backup ISPConfig giornaliero

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=10m

[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now ispconfig-backup.timer
systemctl list-timers | grep ispconfig-backup

Copia fuori dal server

Un backup sullo stesso disco del server non è sufficiente. Almeno una copia deve uscire dalla macchina, per esempio verso NAS o host dedicato tramite SSH/rsync.

rsync -aH --delete-delay   /srv/backup/ispconfig/   backup@nas.example:/backup/ispconfig/

Verifica e restore

cd /srv/backup/ispconfig/2026-09-24_02-30-00
sha256sum -c SHA256SUMS

tar -tzf fs/www-clients.tar.gz | head
zcat db/dbispconfig.sql.gz | head

Periodicamente va fatto un restore in una macchina isolata. Finché non abbiamo ripristinato almeno una volta un sito e un database, sappiamo soltanto che abbiamo creato file compressi, non che possediamo un piano di recupero.