[PART-17] MailStack Enterprise v3
Posted: Sun Jun 14, 2026 11:54 am
NETAPORT MailStack Enterprise v3 — 17-backup-validation.sh
Final step. Installs Restic and builds a complete, *verified* backup system for the mail platform. Backs up mailboxes, a consistent MariaDB dump, a Redis snapshot, all service configs, DKIM keys and SSL certificates. Supports local, remote (SFTP/REST) and S3-style repositories. Schedules backups + prune via systemd timers, and — critically — VALIDATES restorability with restic check plus a real test restore, not just confirming snapshots exist.
What this script is for
This is stage 17, the last one — it makes the finished platform recoverable. Restic is an encrypted, deduplicating backup tool; this script points it at everything that matters (mailboxes, a transaction-consistent database dump, a Redis snapshot, every config, the DKIM keys and TLS certs) and schedules nightly backups with weekly pruning. The part that sets it apart from most backup setups is the verification: it doesn't just check that snapshots exist — it runs an integrity check and performs a real test restore of a known file, comparing it byte-for-byte against the live copy. That's the difference between "backups exist" and "backups are actually restorable". Running last means the very first snapshot captures the complete, hardened system.
Critical: the repository is encrypted, and the password lives in /etc/netaport/restic.env. If that password is lost, the backups are unrecoverable — copy it off-host immediately.
Requirements
What it does, stage by stage
1. Pre-flight
Confirms root, loads env and secrets, and resolves the repository (local by default, remote/S3 via env).
2. Install
Installs restic from EPEL.
3. Restic env
Generates the repository encryption password once into /etc/netaport/restic.env (0600 root), carrying through any S3 credentials. Warns loudly that this password is unrecoverable if lost.
4. Init repo
Initializes the repository if it isn't already (local path, SFTP or S3).
5. Backup helper
Writes the nightly runner: a transaction-consistent mysqldump of all databases, a Redis BGSAVE point-in-time snapshot, then a restic backup of mailboxes, the staged DB dump, Redis data, all service configs, DKIM keys, TLS certs and /etc/netaport — with retention (forget) and a shred of the plaintext DB dump afterward. A separate prune helper does weekly prune + integrity check.
6. Timers
Enables a nightly backup (02:30) and a weekly prune + check (Sunday 05:00), both low-priority oneshots.
7. First backup & validation
Runs the first backup immediately, confirms a snapshot exists, runs restic check over the structure plus a 5% data sample, and performs a test restore of /etc/postfix/main.cf, comparing it byte-for-byte against the live file.
8. Report
Writes a pass/warn/fail summary, the backup contents, the critical password warning, and the "deployment complete (01-17)" marker to /root/netaport-reports/.
What gets backed up
The full script
This is the final script — NETAPORT MailStack Enterprise v3 deployment is complete (01-17).
Final step. Installs Restic and builds a complete, *verified* backup system for the mail platform. Backs up mailboxes, a consistent MariaDB dump, a Redis snapshot, all service configs, DKIM keys and SSL certificates. Supports local, remote (SFTP/REST) and S3-style repositories. Schedules backups + prune via systemd timers, and — critically — VALIDATES restorability with restic check plus a real test restore, not just confirming snapshots exist.
What this script is for
This is stage 17, the last one — it makes the finished platform recoverable. Restic is an encrypted, deduplicating backup tool; this script points it at everything that matters (mailboxes, a transaction-consistent database dump, a Redis snapshot, every config, the DKIM keys and TLS certs) and schedules nightly backups with weekly pruning. The part that sets it apart from most backup setups is the verification: it doesn't just check that snapshots exist — it runs an integrity check and performs a real test restore of a known file, comparing it byte-for-byte against the live copy. That's the difference between "backups exist" and "backups are actually restorable". Running last means the very first snapshot captures the complete, hardened system.
Critical: the repository is encrypted, and the password lives in /etc/netaport/restic.env. If that password is lost, the backups are unrecoverable — copy it off-host immediately.
Requirements
- 01..16 completed (runs last so the first snapshot captures the finished, hardened system)
- For remote/S3 repos: network reachability + credentials supplied via env
Code: Select all
bash 17-backup-validation.sh # local repo (default)
# remote SFTP:
RESTIC_REPOSITORY="sftp:user@host:/srv/backups/netaport" bash 17-backup-validation.sh
# S3-compatible:
RESTIC_REPOSITORY="s3:https://s3.example.com/netaport" \
AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... bash 17-backup-validation.sh1. Pre-flight
Confirms root, loads env and secrets, and resolves the repository (local by default, remote/S3 via env).
2. Install
Installs restic from EPEL.
3. Restic env
Generates the repository encryption password once into /etc/netaport/restic.env (0600 root), carrying through any S3 credentials. Warns loudly that this password is unrecoverable if lost.
4. Init repo
Initializes the repository if it isn't already (local path, SFTP or S3).
5. Backup helper
Writes the nightly runner: a transaction-consistent mysqldump of all databases, a Redis BGSAVE point-in-time snapshot, then a restic backup of mailboxes, the staged DB dump, Redis data, all service configs, DKIM keys, TLS certs and /etc/netaport — with retention (forget) and a shred of the plaintext DB dump afterward. A separate prune helper does weekly prune + integrity check.
6. Timers
Enables a nightly backup (02:30) and a weekly prune + check (Sunday 05:00), both low-priority oneshots.
7. First backup & validation
Runs the first backup immediately, confirms a snapshot exists, runs restic check over the structure plus a 5% data sample, and performs a test restore of /etc/postfix/main.cf, comparing it byte-for-byte against the live file.
8. Report
Writes a pass/warn/fail summary, the backup contents, the critical password warning, and the "deployment complete (01-17)" marker to /root/netaport-reports/.
What gets backed up
- Mailboxes (/var/vmail) and a consistent MariaDB dump of all databases
- Redis snapshot; Postfix/Dovecot/Rspamd configs
- DKIM keys, TLS private keys + certs, Let's Encrypt, and /etc/netaport
- Repo password + S3 creds in a 0600 env file, never on argv or in the log
- Consistent DB backup via mysqldump --single-transaction; Redis BGSAVE before capture
- --one-file-system + cache/runtime excludes; forget+prune retention (7d/4w/6m)
- Restore is TESTED automatically so "backups exist" never masks "backups unrestorable"
- Backups nightly, prune weekly; both low-priority oneshots
- set -Eeuo pipefail with an ERR trap reporting the failing line number
Code: Select all
systemctl disable --now netaport-backup.timer netaport-backup-prune.timer
rm -f /etc/systemd/system/netaport-backup*.{service,timer} \
-rf /usr/local/sbin/netaport-backup* /etc/netaport/restic.env
# remove the repository directory/bucket manually
rm -f /var/lib/netaport/state/17-backup-validation.*.doneCode: Select all
# paste the complete contents of 17-backup-validation.sh here