# ZFS BuddyBackup — Comprehensive Plugin & User Guide > **Official Forum Guide Thread**: [Unraid Community Forums - ZFS BuddyBackup Plugin Guide](https://forums.unraid.net/topic/186256-zfs-buddybackup-plugin-guide/) > **Source Code**: [GitHub - Piratkopia13/unraid-buddybackup](https://github.com/Piratkopia13/unraid-buddybackup) > **Compatibility**: Unraid 6.12.0 or newer with ZFS --- ## Overview **ZFS BuddyBackup** is an Unraid plugin designed to make ZFS snapshot maintenance, local pool replication, and offsite backups between two Unraid servers (or between Unraid and generic OpenZFS systems) simple, automated, and secure: - **Raw Encrypted Transfers**: Remote transfers use raw ZFS send (`zfs send -w`). Data remains encrypted in transit and at rest on the remote server without requiring the destination host to possess your encryption keys (zero-knowledge backup destination). - **Least-Privilege Security**: Remote connections run as a restricted non-root `buddybackup` user locked to an SSH forced-command allowlist (`restrict_zfs`). Interactive shell access and arbitrary commands are blocked. - **Remote Immutability**: Destructive commands (`zfs destroy`, `zfs rollback`) are strictly blocked on the receiver to protect against remote compromises and ransomware. > [!CAUTION] > **Disclaimer**: I take no responsibility for data loss or damage caused by using this plugin. Please review the open-source code and report any bugs or suggestions on GitHub or the Unraid forum. --- ## Prerequisites & Installation 1. **Unraid Version**: Unraid 6.12.0 or newer with OpenZFS. 2. **Installation**: Install **ZFS BuddyBackup** via the Unraid **Community Applications (Apps)** tab. 3. **Encryption**: By default, remote backups require an encrypted source ZFS dataset. If you must back up unencrypted datasets remotely over a trusted private network, enable **Allow unencrypted remote** in **Advanced Settings**. 4. **Network Connection**: Connect remote servers over a secure network (e.g. Tailscale, WireGuard, or private LAN). Avoid exposing SSH directly to the public internet. --- ## Interface Walkthrough In the Unraid WebGUI, access the plugin under **Tools → ZFS Buddy Backup**. The interface is organized into five tabs: 1. [Backup and restore](#1-backup-and-restore) 2. [Snapshot creation and pruning](#2-snapshot-creation-and-pruning) 3. [Buddy's Backups](#3-buddys-backups-receiving-backups) 4. [Advanced Settings](#4-advanced-settings) 5. [Log](#5-log) *(A live status panel is also available on the main Unraid [Dashboard](#6-unraid-dashboard-integration)).* --- ### 1. Backup and restore The main command center for configuring, monitoring, and restoring backups. ![Backup and restore Overview](images/screenshot_01_backups_overview.png) #### Your SSH Public Key Send your SSH public key to your buddy to add under their **Buddy's Backups** page (or use it when running the setup helper on a generic receiver). #### Backup Tasks Each configured backup task displays: - **Status**: Status indicators for `Healthy`, `Triggered by snapshot`, `Warning` (overdue), `Alert` (critical overdue), `Failed` (last run error), or `Disabled`. - **Route & Telemetry**: Source dataset `pool/dataset` → destination, last run timestamp, destination size, cron schedule, and snapshot trigger badges. - **Alert Banners**: Displays failure date, error details, and log link if a task is overdue or failed. ![Backup Task Details](images/screenshot_02_backup_card_expanded.png) #### Source Settings - **Enable on cron schedule**: Enables scheduled automated runs. - **ZFS dataset to backup**: Local dataset to replicate (encrypted datasets by default). - **Backup child datasets**: Recursively replicate child datasets and snapshots. - **Skip parent dataset**: Replicate only child datasets, skipping the root parent dataset (useful when replicating container datasets or pools without duplicating the parent). - **Cron schedule**: Cron expression defining when the backup runs (default: `0 0 * * *` for midnight). #### Backup Hooks (Advanced) Optional scripts or commands that run **locally on this Unraid host** around the replication lifecycle of a backup task. Use them for pre-sync infrastructure preparation (e.g. Wake-on-LAN, pinging the buddy host, establishing a VPN tunnel) and post-sync notifications (e.g. custom webhooks, monitoring heartbeats, email or chat alerts). > [!NOTE] > Backup hooks run strictly on the local Unraid server, never on the remote buddy host. To pause or flush workloads (VM freeze, container pause, database flush) during snapshot creation, configure **Snapshot Hooks** under [Snapshot creation and pruning](#2-snapshot-creation-and-pruning). **Execution Lifecycle**: 1. **Pre-backup hook**: Runs immediately before preflight checks and Syncoid replication start. - If the pre-backup hook exits non-zero and **Hook failure mode** is set to `Abort backup`, replication is aborted and the backup task is marked as failed. - If **Hook failure mode** is set to `Warn and continue`, a warning is logged and replication proceeds. 2. **Replication**: Syncoid syncs datasets to the local or remote destination. 3. **Post-backup hook**: Guaranteed to run when replication concludes, **on both success and failure**. - Contextual environment variables are exported to the command environment: - `BB_TASK_UID`: Unique identifier of the backup task (e.g. `aa4ed6a1`). - `BB_TASK_TYPE`: Destination type (`local`, `remote`, or `remote_generic`). - `BB_SOURCE_DATASET`: Source dataset path (e.g. `tank/data/appdata`). - `BB_DEST_DATASET`: Destination dataset path (e.g. `tank/backups/appdata`). - `BB_STATUS`: Result status (`success` or `failed`). - `BB_EXIT_CODE`: Integer exit status of the replication command (`0` on success). - `BB_ERROR_MESSAGE`: Clean error summary if replication failed (empty on success). - A failing post-backup hook logs a warning but never alters the recorded backup outcome. **Fields**: - **Pre-backup hook**: Absolute path to an executable script (e.g. `/boot/config/plugins/buddybackup/hooks/pre-backup.sh`), or a direct inline command (e.g. `wakeonlan 00:11:22:33:44:55`). Leave empty to disable. - **Post-backup hook**: Absolute path to an executable script or a direct inline command (e.g. `curl -fsS "https://status.example.com/ping?status=${BB_STATUS}"`). Leave empty to disable. - **Hook timeout (seconds)**: Maximum execution time before the script or command is forcibly terminated (default: `30`). - **Hook failure mode**: Applies to the **pre-backup** hook only: - `Abort backup` (default): Aborts replication immediately if the pre-backup hook exits non-zero. - `Warn and continue`: Logs a warning on non-zero exit and continues replication. **Requirements**: Hooks execute locally in Bash under the configured timeout. When referencing a standalone script file, it must be an absolute path starting with `/`. Scripts stored on persistent array or pool storage must be marked executable (`chmod 755`) and not group- or world-writable; scripts on the Unraid flash drive (`/boot/config/...`) are executed automatically via their shebang interpreter or Bash without requiring POSIX `+x` permissions. Commands and chained commands (`cmd1 && cmd2`) are executed directly. ##### Example Recipes > [!TIP] > Keep scripts on persistent storage (e.g. `/boot/config/plugins/buddybackup/hooks/`) so they persist across Unraid reboots. Scripts stored under `/boot` are executed automatically via their shebang interpreter or Bash. If storing scripts on an array or pool share, ensure they are executable (`chmod 755`). **1. Wake-on-LAN before backup** (`pre-backup.sh`): Wake the remote destination server before replication starts and wait for its SSH port to become responsive: ```bash #!/bin/bash TARGET_MAC="AA:BB:CC:DD:EE:FF" TARGET_HOST="192.168.1.50" TARGET_PORT="22" echo "Sending Wake-on-LAN magic packet to ${TARGET_MAC}..." wakeonlan "${TARGET_MAC}" || etherwake "${TARGET_MAC}" # Wait up to 60 seconds for SSH port to respond for i in {1..30}; do if nc -z -w 2 "${TARGET_HOST}" "${TARGET_PORT}" 2>/dev/null; then echo "Target host is reachable on port ${TARGET_PORT}." exit 0 fi sleep 2 done echo "Target host failed to respond within 60 seconds." exit 1 ``` **2. Webhook Notification** (`post-backup.sh`): Send rich status payloads to a webhook endpoint using the `BB_*` environment variables: ```bash #!/bin/bash WEBHOOK_URL="https://example.com/api/webhooks/YOUR_WEBHOOK_URL" if [[ "${BB_STATUS}" == "success" ]]; then COLOR=3066993 # Green TITLE="BuddyBackup Succeeded" DESC="Task **${BB_TASK_UID}** synced \`${BB_SOURCE_DATASET}\` → \`${BB_DEST_DATASET}\` successfully." else COLOR=15158332 # Red TITLE="BuddyBackup Failed" DESC="Task **${BB_TASK_UID}** failed (exit code ${BB_EXIT_CODE}):\n\`${BB_ERROR_MESSAGE}\`" fi PAYLOAD=$(cat </dev/null 2>&1 || true ``` **3. Monitoring Heartbeat Ping** (`post-backup.sh`): Ping your uptime/health monitoring service on completion, notifying on both success and failure: ```bash #!/bin/bash PING_URL="https://status.example.com/ping/your-check-uuid" if [[ "${BB_STATUS}" == "success" ]]; then curl -fsS -m 10 --retry 3 "${PING_URL}" >/dev/null 2>&1 || true else curl -fsS -m 10 --retry 3 --data-raw "${BB_ERROR_MESSAGE}" "${PING_URL}/fail" >/dev/null 2>&1 || true fi ``` #### Destination Settings 1. **Remote (BuddyBackup)** (Unraid → Unraid): - **Buddy's hostname or IP**: Remote Unraid VPN/LAN IP. - **Test connection**: Verifies SSH connectivity and command restriction (`restrict_zfs`). - **Destination dataset**: Must start with or match your buddy's configured parent dataset (e.g. `tank/backups/buddy/my-data`, or matching the parent dataset directly when replicating child datasets with **Skip parent dataset**). 2. **Remote (generic ZFS host)** (Unraid → TrueNAS SCALE / Proxmox VE / Linux): - **Buddy's hostname or IP**: Remote host IP or hostname. - **Remote username** & **SSH port**: SSH user (default: `buddybackup`) and port (default: `22`). - **Destination dataset**: Target dataset path on remote host (e.g. `tank/backups/unraid/appdata`). - **Remote host one-time setup**: Generates the root command to configure the remote host. - **Test connection**: Verifies SSH connectivity, ZFS version, dataset existence, encryption support, resume tokens, and delegated permissions. ![Generic ZFS Host Setup Helper](images/screenshot_03_generic_host_helper.png) 3. **Local** (Pool-to-Pool on the same Unraid server): - **Destination dataset**: Local target dataset (autocomplete provided). - **Write Protection**: Local destination datasets are automatically set to `readonly=on` upon sync to prevent accidental tampering and ransomware corruption. #### Task Actions - **Send backup now**: Runs preflight checks (verifying source dataset, encryption, and snapshots). If no snapshots exist yet, prompts you to create one and send immediately. - **Create fresh snapshot and send now**: Takes an immediate local snapshot (`autosnap_YYYY-MM-DD_HH:MM:SS_hourly`) and starts replication. - **Restore data from destination**: Opens the **Restore Snapshot Wizard**. #### Restore Snapshot Wizard ![Restore Snapshot Wizard](images/screenshot_04_restore_wizard.png) 1. Select a snapshot from the dropdown (grouped by dataset). 2. Choose a restore mode: - **Restore selected snapshot**: Restores the single selected snapshot. - **Restore all snapshots in selected dataset**: Restores the complete snapshot history for that dataset. 3. Choose the destination: - **New dataset**: Restores to a new local dataset name. - **Restore to selected**: Restores into an existing local dataset (requires at least one common snapshot). 4. Live progress streams in a terminal; restores continue in the background if closed. --- ### 2. Snapshot creation and pruning Automated snapshot creation and retention policies powered by **Sanoid**. ![Snapshot Creation and Pruning](images/screenshot_05_snapshots_page.png) - **Dataset** & **Recursive**: Dataset to snapshot, with optional recursion. - **Create snapshots automatically** (`autosnap`) & **Prune snapshots automatically** (`autoprune`): Toggle automated snapshot creation and retention pruning. - **Snapshot retention**: Number of snapshots to retain for Hourly, Daily, Weekly, Monthly, and Yearly intervals. - **Trigger backup after snapshot**: Select a backup task to run immediately whenever Sanoid creates a new snapshot of this dataset. #### Snapshot Hooks (Advanced) Configure pre- and post-snapshot scripts or direct commands per dataset under the collapsible **Advanced: snapshot hooks** card settings. These run locally to ensure point-in-time consistency for active workloads (e.g. freezing VMs, pausing containers, or flushing databases). > [!IMPORTANT] > **Immediate Resume Guarantee**: > In BuddyBackup, workload pausing is tightly coupled to the snapshot lifecycle—**never** the network replication transfer. The pre-snapshot hook pauses the workload milliseconds before `zfs snapshot` executes, and the post-snapshot hook resumes it **immediately** (~50ms) afterwards. > Even if snapshot creation fails or exits with an error, the post-snapshot resume hook is **guaranteed to run via an EXIT trap**. Your VMs and containers will never remain frozen during network replication or after an error. **Chaining with Backup Triggers**: When both a post-snapshot resume hook and a **Trigger backup after snapshot** task are configured on the same dataset, BuddyBackup automatically sequences execution: 1. Workload is paused or flushed via `pre_snapshot_script`. 2. `zfs snapshot` creates the point-in-time snapshot. 3. Workload is **resumed immediately** via `post_snapshot_script`. 4. BuddyBackup dispatches the triggered backup task (`send_backup`) for network replication while your application continues running with zero downtime. **Fields**: - **Pre-snapshot hook**: Executable script path or inline shell command (e.g. `docker pause nextcloud`) run milliseconds before `zfs snapshot`. Leave empty to disable. - **Post-snapshot hook**: Executable script path or inline shell command (e.g. `docker unpause nextcloud`) run milliseconds after `zfs snapshot` finishes to resume the workload. Guaranteed to run on snapshot success or failure. - **Hook timeout (seconds)**: Timeout for each hook script or command (default: `30`). - **Hook failure mode**: - `Abort on hook failure` (default): Aborts snapshot creation (`no_inconsistent_snapshot = 1` in Sanoid) if the pre-snapshot hook fails. - `Warn and proceed anyway`: Logs a warning on failure and takes the snapshot anyway. ##### Workload Pause & Resume Recipes > [!TIP] > Keep scripts on persistent storage (e.g. `/boot/config/plugins/buddybackup/hooks/`). Scripts stored under `/boot` are executed automatically via their shebang interpreter or Bash; scripts on array or pool shares must be marked executable (`chmod 755`). Always make thaw scripts idempotent (using `|| true`) so they cleanly succeed even if the workload was already running. **1. Freeze a KVM / QEMU Virtual Machine** (`virsh domfsfreeze` / `domfsthaw`): `pre-freeze.sh`: ```bash #!/bin/bash # Flush and freeze VM guest filesystems via QEMU guest agent virsh domfsfreeze my-vm || exit 1 ``` `post-thaw.sh`: ```bash #!/bin/bash # Always thaw, guaranteed to run immediately after zfs snapshot virsh domfsthaw my-vm || true ``` **2. Pause a Docker Container** (`docker pause` / `docker unpause`): `pre-pause.sh`: ```bash #!/bin/bash docker pause my-database-container ``` `post-unpause.sh`: ```bash #!/bin/bash docker unpause my-database-container ``` **3. Flush Database Tables** (MySQL / MariaDB): `pre-flush.sh`: ```bash #!/bin/bash mysql -e 'FLUSH TABLES WITH READ LOCK; SYSTEM sleep 1;' || exit 1 ``` `post-unlock.sh`: ```bash #!/bin/bash mysql -e 'UNLOCK TABLES;' || true ``` > [!NOTE] > Sanoid runs automatically every 15 minutes (`*/15 * * * *`) via cron whenever snapshot or incoming buddy tasks are enabled. --- ### 3. Buddy's Backups (Receiving Backups) Configures your Unraid server to receive incoming backups from buddies or external systems. You can add multiple buddies with independent public keys, datasets, and retention policies. ![Buddy's Backups Receiver Configuration](images/screenshot_06_buddys_backups.png) - **Buddy Name**: Friendly label to identify this buddy (e.g. `Alex (Offsite)`, `Bob (TrueNAS)`). - **Enable**: Allow incoming connections for this buddy. - **Buddy's SSH public key**: Paste buddy's public SSH key (restricted to `restrict_zfs` in `authorized_keys`). - **Destination parent dataset**: Dedicated sub-dataset where your buddy's backups live (e.g. `disk1/buddy_backups` or `cache/appdata`). - Must be a dedicated sub-dataset (contains a `/`), not a bare root disk or pool. Overlapping datasets across buddies are disallowed for isolation. - **Immutable dataset** (default: `Yes`): - When enabled (`Yes`), the dataset is configured with `readonly=on`, `mountpoint=none`, `setuid=off`, and `exec=off` to keep incoming backups immutable and isolated from Unraid's VFS tree. Restoring via the Restore Wizard creates writable datasets. - When disabled (`No`), datasets remain mounted at `/mnt/...`, write protection is disabled (`readonly=off`), and binary execution is allowed (`exec=on`). - **Live Dataset Status Indicators**: - **Locked** (grey): Immutable, unmounted, and write-protected (`mountpoint=none`, `readonly=on`). - **Mounted** (green): Mounted and accessible at `/mnt/...` (`readonly=off`). - **Key Locked** (orange): Encrypted dataset (or parent dataset whose received child datasets are encrypted) whose encryption key is not loaded on this server. The data cannot be accessed or mounted without the key. - **Mounted (Partial)** (orange): Parent is mounted, but some received child datasets are encrypted and locked. - **Lock Failed** (red): The dataset could not be unmounted because active processes on the host (e.g. Docker containers, open terminal sessions, or SMB clients) are currently using files in this mount path. Stop processes using the path and re-apply. - **Not Mounted** (red): Immutability is disabled, but the dataset is currently unmounted. > [!WARNING] > Disabling immutability allows received datasets to mount directly into `/mnt/...`, enables write access, and allows binary execution. Only disable this if you fully trust the sender and understand the security and data integrity risks. - **Snapshot retention**: Local Sanoid policy for how long to keep your buddy's snapshots (Hourly, Daily, Weekly, Monthly, Yearly). > [!NOTE] > **Receiving from TrueNAS SCALE or Proxmox VE?** > Sudo mode is not supported on Unraid (`restrict_zfs` blocks `sudo`). In TrueNAS, keep **Use Sudo For ZFS Commands** unchecked and **Snapshot Retention Policy** set to **None** (destructive `zfs destroy` is rejected on Unraid; Unraid Sanoid manages retention locally). --- ### 4. Advanced Settings Fine-tune daemon settings, alert thresholds, and security overrides. ![Advanced Settings](images/screenshot_07_advanced_settings.png) #### General - **Use UTC timezone** (default: `No`): Uses UTC timestamps (`TZ=UTC`) for snapshot creation, Sanoid cron, and manual snapshot naming. Useful to align timestamps between servers in different timezones. #### Dashboard Panel & Alert Thresholds Configure when status indicators turn warning or alert on the dashboard and task cards (leave empty or `0` to disable): - **Backup warning** (days, default: `7`): Mark outgoing backup as warning if inactive for this many days. - **Backup alert** (days, default: `30`): Mark outgoing backup as alert if inactive for this many days. - **Buddy's backup warning** (days, default: `7`): Mark incoming backup as warning if inactive for this many days. - **Buddy's backup alert** (days, default: `30`): Mark incoming backup as alert if inactive for this many days. #### Security Overrides (Danger Zone) - **Allow unencrypted remote** (default: `No`): Overrides the requirement for remote datasets to be encrypted (`zfs send -w`). Enables sending unencrypted datasets over trusted networks. > [!WARNING] > Only enable **Allow unencrypted remote** over trusted private networks (e.g. WireGuard/Tailscale) if you understand the risks. Data is transferred and stored without native ZFS encryption. --- ### 5. Log Displays live output from `/var/log/buddybackup.log`, streaming updates every 2 seconds. Useful for monitoring active replications and diagnosing connection or delegation issues. ![Replication Log Viewer](images/screenshot_08_log_viewer.png) --- ### 6. Unraid Dashboard Integration A dashboard widget summarizing your backup health at a glance: ![Unraid Dashboard Integration](images/screenshot_09_dashboard_tile.png) - **Header**: Live aggregate counts (`X backup(s), Y autosnap, Z autoprune, Incoming active`). - **Table Columns**: - **Source**: Source dataset or buddy label. - **Destination**: Target host and dataset path. - **Status**: Status indicator with schedule and trigger badges. - **Last run**: Human-readable relative timestamp (e.g. `Today, 06:00`). - **Size**: Used storage on the destination. --- ## Generic ZFS Hosts Deep-Dive BuddyBackup replicates with non-Unraid OpenZFS hosts like **TrueNAS SCALE**, **Proxmox VE**, or standard **Debian/Ubuntu** servers. ### Scenario A: Unraid Pushes Backups to Generic Host 1. In Unraid (**Tools → ZFS Buddy Backup → Backup and restore**), add a backup with **Type: Remote (generic ZFS host)**. 2. Enter the remote host IP, username (`buddybackup`), SSH port (`22`), and destination dataset (e.g. `tank/backups/unraid/appdata`). 3. Run the generated **Remote host one-time setup** command as root on the remote host: ```bash curl -fsSL https://raw.githubusercontent.com/Piratkopia13/unraid-buddybackup/refs/heads/main/src/usr/local/emhttp/plugins/buddybackup/deps/generic_host_setup.sh | bash -s -- --dataset 'tank/backups/unraid' --user 'buddybackup' --port '22' --pubkey 'ssh-ed25519 AAAAC3...' ``` #### Script Options The `generic_host_setup.sh` script is idempotent and self-verifying: - `--dataset NAME`: Target parent dataset (creates with `mountpoint=none` and `readonly=on` if missing). Repeatable. - `--user NAME`: Restricted user account (default: `buddybackup`; UID 0/root refused). - `--port N`: SSH port (default: `22`). - `--pubkey KEY`: Unraid SSH public key. - `--sudo-mode auto|yes|no`: Allows running validated commands via sudo when `zfs` is not directly executable. - `--allowlist-dir PATH`: Script location (default: `/usr/local/sbin`; TrueNAS SCALE defaults to `/mnt//.buddybackup`). - `--verify`: Run validation checks without changes. - `--revoke NAME`: Revoke delegations on a dataset. - `--clean`: Completely uninstall delegations, allowlist files, and SSH entries. #### Platform-Specific Notes: - **Proxmox VE / Debian / Ubuntu**: Run directly as root. Creates user, applies ZFS delegations (`create,mount,receive` and raw `send`), installs the allowlist script to `/usr/local/sbin/`, and adds an SSH drop-in to `/etc/ssh/sshd_config.d/`. - **TrueNAS SCALE**: 1. *Persistent Directory*: The setup script places the allowlist in `/mnt//.buddybackup/` to survive reboots on SCALE 24.04+'s read-only root filesystem. 2. *Create User*: In *Credentials → Users*, create user `buddybackup`, shell `/usr/bin/bash`, disable password login, and paste Unraid's public key. 3. *Run Script*: Run the setup command in the TrueNAS root shell (`sudo -i`). 4. *Sudo Command Grant*: In TrueNAS UI (*Credentials → Users*), add `/usr/bin/bash -c *` to *Allowed sudo commands with no password* (the allowlist script remains the strict fine-grained gate). 5. *SSH Parameters*: Paste the printed `Match User buddybackup` block into *System Settings → Services → SSH → Auxiliary Parameters* and restart SSH. 4. In Unraid, use **Test connection** on the backup entry to verify access. --- ### Scenario B: Generic Host Pushes Backups to Unraid Your Unraid server acts as the secure receiver. > [!IMPORTANT] > **Sudo mode is not supported on Unraid**: Unraid grants dataset permissions via native OpenZFS delegation (`zfs allow`). The `buddybackup` user has no sudo privileges, and `restrict_zfs` strictly blocks any `sudo` commands. #### 1. Prepare Unraid (Receiver) Under **Tools → ZFS Buddy Backup → Buddy's Backups**, configure a buddy with a **Destination parent dataset** (e.g. `disk1/buddy_backups`) and your desired **Snapshot retention** policy. #### 2. Configure TrueNAS SCALE (WebGUI Sender) 1. **SSH Connection**: In **Credentials → Backup Credentials → SSH Connections**, create a connection (Host: Unraid IP, Port: `22`, User: `buddybackup`). Paste the public key into Unraid's **Buddy's Backups**. 2. **Periodic Snapshot Task**: In **Data Protection → Periodic Snapshot Tasks**, set the naming schema to: ``` autosnap_%Y-%m-%d_%H:%M:%S_daily ``` *(Use `_hourly`, `_daily`, `_weekly`, or `_monthly` to match your schedule. Sanoid requires the `autosnap_` prefix and frequency suffix to manage retention).* 3. **Replication Task**: In **Data Protection → Replication Tasks**: - Destination: Unraid SSH connection and target dataset (e.g. `tank/backups/buddy/truenas-appdata`). - ⚠️ **Use Sudo For ZFS Commands**: Must be **DISABLED** (Unraid blocks sudo). - ⚠️ **Snapshot Retention Policy**: Set to **"None"** (TrueNAS must not execute `zfs destroy` on Unraid; Unraid Sanoid manages retention). - **Read-Only**: Leave set to **"Set"** or **"Ignore"**. #### 3. Configure Proxmox VE / Debian (CLI Sender) 1. Generate an SSH keypair on the Linux host: ```bash ssh-keygen -t ed25519 -f /root/.ssh/id_buddybackup -N "" ``` 2. Paste `/root/.ssh/id_buddybackup.pub` into Unraid's **Buddy's Backups**. 3. Push snapshots with `syncoid`: ```bash syncoid --no-privilege-elevation --sshkey=/root/.ssh/id_buddybackup pool/dataset buddybackup@:tank/backups/buddy/dataset ``` *(Always pass `--no-privilege-elevation`. Sudo is blocked on Unraid).* 4. Schedule the command via cron or systemd. Older snapshots named `autosnap_*` will be automatically pruned on Unraid according to your retention policy.