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.


