Guide: The Silent Corruption Killer – A High-Safety ZFS Backup Architecture
As the “Cheerio Lab” storage needs expanded, I faced a classic homelab dilemma: My high-performance “Hot NAS” is a low-power Mini PC running non-ECC RAM. While fast and efficient, it carries the risk of “Silent Data Corruption”—where a flipped bit in RAM gets written to disk with a valid checksum, effectively “poisoning” the file forever.
The solution wasn’t to throw money at enterprise ECC hardware immediately. Instead, I engineered a ZFS Pull-Backup Architecture that leverages Sanoid/Syncoid and aggressive snapshot retention to act as a “Time Machine” against corruption.
1. The Core Architecture: Split-Brain Deep Storage
The strategy treats the Main NAS as a “Production” environment and the Cold NAS (Deep NAS) as a dual-purpose “Vault.” Because the Deep NAS has significantly more storage than the Hot NAS, it serves two distinct roles:
- Role A: The Safety Net (Replica)
- Source: Hot NAS (
tank) - Destination: Deep NAS (
deeptank/nas-replica) - Function: A direct mirror of the Hot NAS. If the Hot NAS corrupts a file today, the Deep NAS holds a healthy snapshot from last month.
- Source: Hot NAS (
- Role B: The Deep Archive (Expansion)
- Source: None (Exists only on Deep NAS)
- Destination: Deep NAS (
deeptank/archive) - Function: Storage for massive files that don’t fit on the Hot NAS (old ISOs, raw footage). These live exclusively on the ECC-protected server.
2. The Secret Sauce: “Raw” Send & Pull Methodology
To minimize the risk of the Hot NAS corrupting data during the backup process, I use a “Pull” configuration. The Deep NAS wakes up, reaches out to the Hot NAS, and grabs the data.
Why “Raw” Send (-w) is Critical:
Standard ZFS replication might decompress and re-process data in RAM. By using the raw flag, we tell the Hot NAS to read the encrypted/compressed blocks directly from the disk and stream them over the network without processing them. This bypasses the non-ECC RAM vulnerability almost entirely during transfer.
3. Implementation Steps
A. Configure the Hot NAS (Source)
The Hot NAS needs to take local snapshots so users can recover accidentally deleted files quickly. We use Sanoid for this.
File: /etc/sanoid/sanoid.conf
[tank/media]
use_template = media
recursive = yes
[template_media]
# We must be explicit because defaults are hidden
autosnap = yes
autoprune = yes
# Keep local snapshots for immediate "oops" recovery
daily = 30
monthly = 12
yearly = 2
B. Configure the Deep NAS (The Vault)
This is where the “Split” architecture matters. We need two different policies: one that receives snapshots (Replica) and one that creates them (Archive).
File: /etc/sanoid/sanoid.conf (on Deep NAS)
# 1. THE REPLICA (Incoming from Main NAS)
[deeptank/nas-replica]
use_template = passive_replica
recursive = yes
# 2. THE ARCHIVE (Your "Excess" Data)
[deeptank/archive]
use_template = active_archive
recursive = yes
# --- TEMPLATES ---
[template_passive_replica]
# Syncoid brings snapshots from the Hot NAS, so we DON'T create them here.
autosnap = no
autoprune = yes
daily = 60
monthly = 12
yearly = 10
[template_active_archive]
# This data only exists here, so we MUST create snapshots locally.
autosnap = yes
autoprune = yes
daily = 30
monthly = 12
yearly = 10
C. User Permissions (The “Rootless” Approach)
Running backups as root is bad practice. I used ZFS Permission Delegation to allow a dedicated user (cheerio) to perform backups without full sudo access.
On the Deep NAS:
# Allow cheerio to manage the backup dataset without sudo
sudo zfs allow -u cheerio compression,create,destroy,mount,mountpoint,receive,rollback,send,snapshot,hold,bookmark deeptank
- Critical: Do not forget
snapshot,hold, andbookmark.- For Replica: Syncoid needs these to track replication state.
- For Archive: Sanoid needs
snapshotto create the daily history for your archival files.
D. The “One-Shot” Systemd Service
Instead of leaving the Deep NAS on 24/7 (wasting electricity), I built a systemd service that:
-
- Boots up (via Wake-on-LAN or BIOS timer).
-
- Runs the Backup (Syncoid) for the Replica.
-
- Runs the Cleanup/Creation (Sanoid) for the Archive.
-
- Shuts down.
File: /etc/systemd/system/deep-backup-shutdown.service
[Unit]
Description=Pull Backup from Main NAS and Shutdown
After=network-online.target
Wants=network-online.target
[Service]
User=cheerio
Group=cheerio
Type=oneshot
# The Magic Command
# 1. Syncoid pulls data for the Replica (using "Raw" send)
# 2. Sanoid runs cleanup on Replica AND creates new snapshots for Archive
# 3. Echo statements provide logs
ExecStart=/bin/bash -c 'echo "--- START $(date) ---"; \
/usr/sbin/syncoid -r --no-privilege-elevation --sendoptions="w" cheerio@nas:tank deeptank/nas-replica --recvoptions="-x sharenfs -x sharesmb -x mountpoint"; \
echo "--- CLEANUP & ARCHIVE SNAPSHOTS $(date) ---"; \
/usr/sbin/sanoid --cron; \
echo "--- FINISHED $(date) ---"'
# The Kill Switch
# Requires 'cheerio' to have NOPASSWD for poweroff in /etc/sudoers
ExecStopPost=/usr/bin/sudo /usr/sbin/poweroff
StandardOutput=append:/var/log/deep-backup-shutdown.log
StandardError=append:/var/log/deep-backup-shutdown.log
4. Why Future Me Is Doing This
- The “Silent Corruption” Mitigation: By keeping 2+ years of monthly snapshots on the Deep NAS, I have a massive window to discover corrupted files. Even if the Hot NAS writes garbage, the Deep NAS holds the history.
- Automation Anxiety: The
ExecStopPostensures the server always turns off, even if the backup crashes, saving money on the electric bill. - Least Privilege: If the
cheerioaccount is compromised, the attacker can only mess with ZFS datasets, not the entire root OS.
Troubleshooting Tips
- Permission Denied? Check
zfs allow. Ifsnapshotis missing, Sanoid will fail to protect yourdeeptank/archivedata. - Server won’t shutdown? Check
/etc/sudoers. The usercheeriomust haveNOPASSWD: /usr/sbin/poweroff. - Disk filling up? Check if
sanoid.timeris running on the Hot NAS, and ensuresanoid --cronis explicitly called in the Deep NAS script.