Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| omv8:omv8_plugins:zfs [2025/12/24 15:10] – chente | omv8:omv8_plugins:zfs [2026/05/27 12:23] (current) – [Note] ryecoaaron | ||
|---|---|---|---|
| Line 7: | Line 7: | ||
| \\ | \\ | ||
| \\ | \\ | ||
| - | === Note === | + | This guide covers installation, |
| + | It assumes OpenMediaVault 8 (OMV8) is already installed and operational. | ||
| - | This plugin is now available for OMV8. | + | ---- |
| - | The documentation | + | ===== Table of Contents ===== |
| + | |||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | - [[# | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 1. What is ZFS? ===== | ||
| + | |||
| + | ZFS (Zettabyte File System) is a mature, enterprise-grade filesystem and logical volume manager | ||
| + | originally developed by Sun Microsystems for Solaris and now maintained by the OpenZFS project | ||
| + | on Linux. It combines the roles of a traditional filesystem and a volume manager into a single | ||
| + | unified layer, which gives it capabilities that older filesystems like ext4 or XFS cannot match. | ||
| + | |||
| + | ==== Key capabilities ==== | ||
| + | |||
| + | **Copy-on-write with checksums.** Every block is checksummed when written and verified when | ||
| + | read. If a checksum mismatch is detected (bit rot, failing drive, firmware bug) ZFS knows | ||
| + | exactly which block is corrupt. In a redundant pool it can automatically repair the corruption | ||
| + | from the good copy without any user intervention. | ||
| + | |||
| + | **Snapshots and clones.** A snapshot is a read-only, point-in-time copy of a filesystem. It | ||
| + | consumes no space at creation — space is only used as data in the original filesystem diverges | ||
| + | from the snapshot. Clones are writable copies derived from a snapshot. | ||
| + | |||
| + | **Pooled storage.** Disks are combined into a //pool//. One or more // | ||
| + | live inside the pool and share its available space. There are no fixed partition sizes to worry | ||
| + | about. | ||
| + | |||
| + | **Built-in RAID.** RAID is handled by ZFS natively through its VDEV system. No separate hardware | ||
| + | RAID controller is required or recommended. | ||
| + | |||
| + | **Compression.** Transparent, | ||
| + | storage consumption and often // | ||
| + | |||
| + | **Self-healing.** When a scrub detects corruption in a redundant pool, ZFS restores the damaged | ||
| + | data automatically from the surviving copy. | ||
| + | |||
| + | ==== Common misconceptions ==== | ||
| + | |||
| + | **RAM requirements.** ZFS does not require large amounts of RAM to function correctly. It will | ||
| + | use available RAM for the Adaptive Replacement Cache (ARC) to speed up reads, but this is a | ||
| + | benefit rather than a burden — the ARC is sized dynamically and returns memory to other processes | ||
| + | when needed. A system with 4–8 GB of RAM running a home NAS workload is perfectly fine. | ||
| + | |||
| + | **ECC memory.** ECC RAM is desirable because it prevents the CPU from writing corrupted data to | ||
| + | disk in the first place. It is not required — ZFS's checksumming still catches errors that reach | ||
| + | storage regardless. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 2. Prerequisites ===== | ||
| + | |||
| + | * **64-bit (amd64) system.** ZFS on Linux is not supported on 32-bit or ARM single-board computers such as the Raspberry Pi. | ||
| + | * **OMV-Extras** plugin installed (provides access to additional plugins including this one and the Kernel plugin). | ||
| + | * **Proxmox kernel** (strongly recommended). | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 3. Installation ===== | ||
| + | |||
| + | - In the OMV web interface, go to **System → Update Management** and apply all pending updates. | ||
| + | - If you have not already done so, install the **Kernel** plugin from **System → Plugins**, then use it to install the Proxmox kernel and reboot. | ||
| + | - Go to **System → Plugins**, search | ||
| + | - After installation the **Storage → ZFS** section appears in the navigation. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 4. Understanding ZFS Structure ===== | ||
| + | |||
| + | Before creating anything, it helps to understand how ZFS organises storage. | ||
| + | |||
| + | < | ||
| + | Pool (tank) | ||
| + | ├── VDEV 1 — mirror: sda, sdb ← redundancy lives here | ||
| + | ├── VDEV 2 — mirror: sdc, sdd ← adding a second VDEV expands capacity | ||
| + | ├── [log VDEV] ← optional sync write accelerator (SLOG) | ||
| + | ├── [cache VDEV] ← optional read cache (L2ARC) | ||
| + | └── [spare] | ||
| + | |||
| + | Datasets inside the pool | ||
| + | ├── tank ← pool root (itself a dataset) | ||
| + | ├── tank/ | ||
| + | ├── tank/ | ||
| + | │ | ||
| + | │ | ||
| + | └── tank/ | ||
| + | └── tank/ | ||
| + | </ | ||
| + | |||
| + | ==== Pools ==== | ||
| + | |||
| + | A pool is the top-level storage container. It is built from one or more **VDEVs**. | ||
| + | |||
| + | ==== VDEVs ==== | ||
| + | |||
| + | A VDEV (Virtual Device) is the redundancy unit. The most important rule: **if a VDEV fails | ||
| + | entirely, the pool is lost**. Redundancy protects against individual disk failures //within// a | ||
| + | VDEV, not between VDEVs. | ||
| + | |||
| + | ^ VDEV type ^ Redundancy ^ Min disks ^ Notes ^ | ||
| + | | Basic (stripe) | None | 1 | Any single disk failure loses the pool. Enable '' | ||
| + | | Mirror | 1 copy | 2 | All data exists on every disk. Recommended for most home NAS setups. | | ||
| + | | RAIDZ1 | 1 parity | 3 | Tolerates 1 disk loss. Keep to ≤7 disks per VDEV. | | ||
| + | | RAIDZ2 | 2 parity | 4 | Tolerates 2 disk losses. Keep to ≤11 disks per VDEV. | | ||
| + | | RAIDZ3 | 3 parity | 5 | Tolerates 3 disk losses. Keep to ≤15 disks per VDEV. | | ||
| + | |||
| + | A basic VDEV can be converted to mirror, but a mirror VDEV cannot be converted to a RAIDZ VDEV and RAIDZ1 cannot later become a RAIDZ2 VDEV. | ||
| + | Adding more disks to expand a RAIDZ VDEV is possible via RAIDZ expansion (if the pool has | ||
| + | '' | ||
| + | |||
| + | ==== Datasets ==== | ||
| + | |||
| + | Datasets are the filesystems and volumes that live inside a pool. There are three types: | ||
| + | |||
| + | * **Filesystem** — a mounted directory tree, like a normal folder. | ||
| + | * **Volume (zvol)** — a block device that appears as a raw disk. Used for virtual machine images, iSCSI targets, or anything that needs a block interface. | ||
| + | * **Snapshot** — a read-only point-in-time copy of a filesystem or volume. | ||
| + | |||
| + | Datasets inherit properties from their parent unless overridden. Creating a child dataset | ||
| + | '' | ||
| + | on '' | ||
| + | |||
| + | ==== Best practice: never use the pool root directly ==== | ||
| + | |||
| + | Always create at least one named filesystem under the pool and use that for data. The pool root | ||
| + | dataset is typically left with minimal configuration and its mountpoint set to '' | ||
| + | This keeps administration cleaner and preserves flexibility for property inheritance. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 5. Pool Management ===== | ||
| + | |||
| + | Navigate to **Storage → ZFS → Pools**. | ||
| + | |||
| + | ==== Creating a pool ==== | ||
| + | |||
| + | Click **Pool → Add pool**. The form fields are: | ||
| + | |||
| + | ^ Field ^ Description ^ | ||
| + | | **Name** | Pool name. Letters, numbers, hyphens, and underscores. Cannot start with a number. | | ||
| + | | **VDEV type** | Basic, Mirror, RAIDZ1, RAIDZ2, or RAIDZ3. | | ||
| + | | **Devices** | Disks to include. The plugin lists unpartitioned, | ||
| + | | **Device identification** | How disks are identified in the pool: '' | ||
| + | | **Ashift** | Sector size hint. Do NOT rely on auto-detection. Set to '' | ||
| + | | **Force** | Pass '' | ||
| + | | **Compression** | Applied to the pool root dataset and inherited by all children unless overridden. '' | ||
| + | | **Mountpoint** | Where the pool root mounts. Defaults to ''/< | ||
| + | | **Case sensitivity** | '' | ||
| + | |||
| + | After the pool is created it appears in the table with its health state, size, free space, and | ||
| + | fragmentation. | ||
| + | |||
| + | ==== Pool health states ==== | ||
| + | |||
| + | ^ State ^ Meaning ^ | ||
| + | | ONLINE | All devices present and functioning normally. | | ||
| + | | DEGRADED | One or more devices have failed but the pool is still readable/ | ||
| + | | FAULTED | The pool is completely unavailable. | | ||
| + | | OFFLINE | The pool has been exported or manually taken offline. | | ||
| + | | REMOVED | A device was physically removed. | | ||
| + | | UNAVAIL | A device cannot be opened. | | ||
| + | |||
| + | ==== Pool actions ==== | ||
| + | |||
| + | Select a pool and use the action menus: | ||
| + | |||
| + | === Pool menu === | ||
| + | |||
| + | ^ Action ^ Description ^ | ||
| + | | **Properties** | View and edit ZFS properties on the pool. | | ||
| + | | **Details** | Raw '' | ||
| + | | **History** | Chronological log of all ZFS operations performed on the pool. | | ||
| + | | **Expand pool** | Add a new data VDEV to increase the pool's total capacity. The new VDEV must match the redundancy level you want (adding a mirror VDEV to a mirror pool is common). | | ||
| + | | **Clear errors** | Reset error counters and clear the pool's error log ('' | ||
| + | | **Trim** | Send TRIM commands to all SSDs in the pool to reclaim space from deleted data. SSDs only — harmless but ineffective on spinning disks. | | ||
| + | | **Upgrade** | Enable all supported ZFS feature flags. **One-way operation** — the pool cannot be imported by an older ZFS version afterward. Only upgrade when all systems that may need to import this pool are running the same or newer ZFS version. | | ||
| + | | **Fix packages** | Attempt to reinstall or repair corrupted ZFS package installations. | | ||
| + | | **Delete** | Destroy the pool and wipe labels and filesystem signatures from all member disks. **Irreversible.** Deletes all data. | | ||
| + | |||
| + | === Add VDEV menu === | ||
| + | |||
| + | ^ Action ^ Description ^ | ||
| + | | **Cache** | Add an L2ARC (Level 2 Adaptive Replacement Cache) VDEV using one or more SSDs. Extends the read cache beyond RAM. Most beneficial for workloads with large working sets and random reads. | | ||
| + | | **Log** | Add a ZIL (ZFS Intent Log) device using an SSD or NVMe. Accelerates synchronous writes. The plugin automatically mirrors the log if two devices are provided to prevent pool loss on log device failure. | | ||
| + | | **Spare** | Add one or more hot spare disks. If a VDEV member fails, ZFS automatically begins resilver using the spare. | | ||
| + | | **Special** | Add a special allocation class VDEV for metadata and small blocks. Speeds up metadata operations when backed by fast storage (NVMe). The plugin automatically mirrors the special VDEV when two devices are given. | | ||
| + | |||
| + | === Remove VDEV menu === | ||
| + | |||
| + | ^ Action ^ Description ^ | ||
| + | | **Remove vdev** | Initiate removal of a VDEV. ZFS evacuates all data from the VDEV to the remaining pool before it is detached. Only certain VDEV types support removal (not RAIDZ). The pool must have enough free space to hold the evacuated data. | | ||
| + | | **Cancel removal** | Abort an in-progress VDEV removal and restore the VDEV to active use. | | ||
| + | | **Removal status** | View the current progress of a VDEV removal: bytes remaining, estimated time, and status. | | ||
| + | |||
| + | === Device menu === | ||
| + | |||
| + | ^ Action ^ Description ^ | ||
| + | | **Offline** | Temporarily stop using a device without removing it from the pool. The pool remains healthy (DEGRADED) if it has enough redundancy. Use before a planned maintenance swap. | | ||
| + | | **Online** | Bring an offlined device back into service. ZFS resilvered any writes that occurred while the device was offline. | | ||
| + | | **Attach** | Add a device to an existing single-disk (basic) VDEV to convert it into a mirror, or add a disk to an existing mirror to expand it. Add to existing RAIDZ device. | | ||
| + | | **Detach** | Remove one disk from a mirror, leaving the remaining disk(s) in place. The mirror is downgraded accordingly. | | ||
| + | | **Replace** | Swap a failed or unwanted device for a new one. ZFS begins resilvering immediately. The pool remains DEGRADED until resilvering completes. | | ||
| + | |||
| + | === Scrub menu === | ||
| + | |||
| + | ^ Action ^ Description ^ | ||
| + | | **Start scrub** | Begin reading every block in the pool and verifying checksums. Repairs any corruption found using redundant copies. | | ||
| + | | **Stop scrub** | Abort the current scrub immediately. | | ||
| + | | **Pause scrub** | Suspend the scrub. Progress is preserved and the scrub can be resumed later. | | ||
| + | | **Resume scrub** | Continue a paused scrub from where it left off. | | ||
| + | | **Scrub status** | View detailed scrub progress: bytes scanned, errors found, elapsed time, and estimated completion. | | ||
| + | |||
| + | === Import/ | ||
| + | |||
| + | ^ Action ^ Description ^ | ||
| + | | **Import pool** | Import a pool that is not currently active. Options: import all available pools, import a specific named pool, or force-import a pool that appears to be in use (e.g. after an unclean shutdown on another system). | | ||
| + | | **Export pool** | Flush all pending writes, unmount all datasets, and release the pool so it can be moved to another system or safely powered down. | | ||
| + | |||
| + | ==== Expanding pool capacity by replacing disks ==== | ||
| + | |||
| + | To replace all disks in a VDEV with larger ones (for example, upgrading from 4 TB to 8 TB drives | ||
| + | in a mirror): | ||
| + | |||
| + | - Replace one disk at a time using **Device → Replace**. Wait for resilvering to complete ('' | ||
| + | - Repeat for each disk in the VDEV. | ||
| + | - Once all disks are replaced, the pool will automatically expand to use the full capacity because '' | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 6. Dataset Management ===== | ||
| + | |||
| + | Navigate to **Storage → ZFS → Datasets**. The table shows all pools, filesystems, | ||
| + | snapshots in a hierarchy. Use the **Type** filter at the top to show only filesystems, | ||
| + | snapshots, etc. | ||
| + | |||
| + | ==== Adding a filesystem ==== | ||
| + | |||
| + | Select a pool or parent filesystem and click **Add → Add filesystem**. | ||
| + | |||
| + | ^ Field ^ Description ^ | ||
| + | | **Name** | The dataset name component (not the full path). | | ||
| + | | **Mountpoint** | Where the filesystem mounts. Leave blank to use the default (''/< | ||
| + | | **Compression** | Override the inherited compression algorithm. '' | ||
| + | | **Record size** | The internal block size for this filesystem. Defaults to 128K (good for general use). Use 4K–16K for databases with small random I/O. Use 1M for media files and sequential workloads. | | ||
| + | | **Case sensitivity** | '' | ||
| + | |||
| + | After creating a filesystem it can be used as a shared folder target in OMV | ||
| + | (**Storage → Shared Folders**) once registered with **Discover → Add new** (see [[# | ||
| + | |||
| + | ==== Adding a volume (zvol) ==== | ||
| + | |||
| + | Select a pool or parent filesystem and click **Add → Add volume**. | ||
| + | |||
| + | ^ Field ^ Description ^ | ||
| + | | **Name** | The dataset name component. | | ||
| + | | **Size** | Volume size in binary units (e.g. '' | ||
| + | | **Thin provisioning** | If checked, the zvol is sparse — it does not reserve all its space upfront. The pool reports the full volume size as used but blocks are only actually allocated as data is written. | ||
| + | |||
| + | Volumes appear as block devices under ''/ | ||
| + | |||
| + | ==== Adding a snapshot ==== | ||
| + | |||
| + | Select a filesystem or volume and click **Add → Add snapshot**. | ||
| + | |||
| + | ^ Field ^ Description ^ | ||
| + | | **Name** | Snapshot name appended after '' | ||
| + | | **Recursive** | Also snapshot all child datasets in one atomic operation. | | ||
| + | |||
| + | Alternatively, | ||
| + | timestamp name (''< | ||
| + | **Add → Quick recursive snapshot** does the same recursively. | ||
| + | |||
| + | ==== Cloning a dataset ==== | ||
| + | |||
| + | A clone is a writable filesystem derived from a snapshot. | ||
| + | |||
| + | Select a filesystem and click **Clone**. The plugin automatically creates a snapshot of the | ||
| + | source dataset (named ''< | ||
| + | The clone initially shares all data blocks with the source and uses no additional space. | ||
| + | |||
| + | Cloned datasets cannot be encrypted independently — they inherit the encryption root of their | ||
| + | origin. To create an independent encrypted dataset, promote the clone first. | ||
| + | |||
| + | ==== Promoting a clone ==== | ||
| + | |||
| + | A clone depends on its origin snapshot — the origin cannot be deleted while the clone exists. | ||
| + | **Promote** reverses this relationship: | ||
| + | becomes dependent on it instead. | ||
| + | |||
| + | Select a clone and click **Promote**. After promotion the original source dataset becomes the | ||
| + | dependent and can be deleted independently. | ||
| + | |||
| + | ==== Renaming a dataset ==== | ||
| + | |||
| + | Select a filesystem or volume and click **Rename**. The dataset is renamed in ZFS, its mountpoint | ||
| + | is updated, and OMV's fstab database is updated to reflect the new path. | ||
| + | |||
| + | Renaming a dataset that has active shared folders is blocked — remove the shares first. | ||
| + | |||
| + | ==== Deleting a dataset ==== | ||
| + | |||
| + | Select any dataset and click **Delete**. The plugin checks for dependent clones and active shares | ||
| + | before proceeding. Snapshots of a filesystem are listed and must be removed (or confirmed) | ||
| + | before the parent can be deleted. | ||
| + | |||
| + | For encrypted datasets with auto-unlock configured, always delete through the plugin rather than | ||
| + | via '' | ||
| + | ZFS list cache correctly. See the README for manual cleanup steps if needed. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 7. Snapshots ===== | ||
| + | |||
| + | ==== Snapshot tab ==== | ||
| + | |||
| + | **Storage → ZFS → Snapshots** shows all snapshots across all pools with their size, referenced | ||
| + | data, creation time, and origin. | ||
| + | |||
| + | ==== Operations on snapshots ==== | ||
| + | |||
| + | Select a snapshot to access: | ||
| + | |||
| + | ^ Action ^ Description ^ | ||
| + | | **Rollback** | Revert the parent dataset to the state at the time of this snapshot. All data written after the snapshot is permanently discarded. The snapshot itself is preserved. | | ||
| + | | **Clone** | Create a writable copy derived from this snapshot. | | ||
| + | | **Diff** | Show which files were created, modified, or deleted between this snapshot and the current state of the dataset (or between two snapshots). Output is presented as a text report. | | ||
| + | | **Delete** | Remove the snapshot and reclaim its space (only space not referenced by other snapshots or the current dataset). | | ||
| + | | **Delete range** | Delete a batch of snapshots relative to the selected one: all snapshots //earlier// than this one, all snapshots //later// than this one, or //all// snapshots on the parent dataset with the same prefix. Useful for bulk cleanup. | | ||
| + | |||
| + | ==== Recovering files from a snapshot ==== | ||
| + | |||
| + | ZFS snapshots are accessible in the '' | ||
| + | filesystem' | ||
| + | snapshot '' | ||
| + | |||
| + | < | ||
| + | / | ||
| + | </ | ||
| + | |||
| + | No mounting or special tools are needed — simply copy the file back from the snapshot directory. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 8. Encryption ===== | ||
| + | |||
| + | ZFS native encryption protects dataset contents at rest. The encryption key is required to mount | ||
| + | and read a dataset. Without the key the data is unreadable even with physical access to the disks. | ||
| + | |||
| + | ==== Enabling encryption ==== | ||
| + | |||
| + | Select a pool or parent filesystem and click **Add → Add encrypted filesystem**. | ||
| + | |||
| + | ^ Field ^ Description ^ | ||
| + | | **Name** | Dataset name component. | | ||
| + | | **Encryption algorithm** | AES-128-CCM, | ||
| + | | **Passphrase** | Must be at least 8 characters. Use a strong, unique passphrase. | | ||
| + | | **Auto-unlock** | Automatically unlock and mount the dataset at boot (stores passphrase in ''/ | ||
| + | | **Compression** | Compression algorithm (applied before encryption). | | ||
| + | | **Record size** | Internal block size. | | ||
| + | | **Case sensitivity** | '' | ||
| + | | **Mountpoint** | Where the dataset mounts. | | ||
| + | |||
| + | < | ||
| + | encryption and key of their parent. They share the same encryption root and are unlocked and | ||
| + | locked together with the parent.</ | ||
| + | |||
| + | ==== Locking and unlocking ==== | ||
| + | |||
| + | A locked dataset is unmounted and its contents are inaccessible. To lock a dataset, select it | ||
| + | and click **Encryption → Unload key (lock)**. To unlock it, click **Encryption → Load key | ||
| + | (unlock)** and enter the passphrase. | ||
| + | |||
| + | Cloned datasets that are //not// the encryption root cannot be independently locked or unlocked — | ||
| + | use the original encryption root instead. | ||
| + | |||
| + | ==== Auto-unlock: | ||
| + | |||
| + | Local auto-unlock stores the passphrase as a file in ''/ | ||
| + | load the key automatically at boot. This protects against drive theft (an attacker who steals | ||
| + | only the disks cannot read them without the server) but does not protect against someone who | ||
| + | steals the entire server. | ||
| + | |||
| + | Select an encrypted dataset and click **Encryption → Enable auto-unlock → Local file**. Enter | ||
| + | the passphrase to confirm. The plugin: | ||
| + | |||
| + | - Writes the keyfile to ''/ | ||
| + | - Sets '' | ||
| + | - Unmasks the '' | ||
| + | - Writes the dataset path into the ZFS list cache so '' | ||
| + | |||
| + | To remove auto-unlock, | ||
| + | '' | ||
| + | appears at boot (the dataset simply stays locked until manually unlocked). | ||
| + | |||
| + | ==== Auto-unlock: | ||
| + | |||
| + | Remote auto-unlock fetches the encryption key from an HTTPS server at boot. The key is never | ||
| + | stored on the local machine. This protects against both drive theft and full server theft — | ||
| + | an attacker cannot decrypt the data without network access to the key server. | ||
| + | |||
| + | Select an encrypted dataset and click **Encryption → Enable auto-unlock → Remote HTTPS**. | ||
| + | |||
| + | ^ Field ^ Description ^ | ||
| + | | **Key URL** | Full HTTPS URL where the key material is served. Must begin with '' | ||
| + | | **Skip certificate verification** | Disable TLS certificate validation. Use only with self-signed certificates on a trusted network. | | ||
| + | | **Decryption passphrase** | If the key file at the URL is OpenSSL-encrypted, | ||
| + | | **Network timeout** | How long (in seconds) to wait for the key server before giving up. Range 10–600, default 60. Allow enough time for the network interface to come up. | | ||
| + | |||
| + | The '' | ||
| + | mount units start. | ||
| + | |||
| + | ==== Changing the encryption key ==== | ||
| + | |||
| + | Select an encrypted dataset and click **Encryption → Change encryption key**. Enter the current | ||
| + | passphrase and the new passphrase. The key change applies to the encryption root; all child | ||
| + | datasets sharing the root are automatically covered. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 9. Scheduled Snapshot Jobs ===== | ||
| + | |||
| + | Navigate to **Storage → ZFS → Snapshot Jobs**. | ||
| + | |||
| + | Snapshot jobs run '' | ||
| + | old ones according to your retention policy. | ||
| + | |||
| + | ==== Creating a snapshot job ==== | ||
| + | |||
| + | Click **Add** to open the form. | ||
| + | |||
| + | === Settings === | ||
| + | |||
| + | ^ Field ^ Description ^ | ||
| + | | **Enabled** | Toggle the job on or off without deleting it. | | ||
| + | | **Dataset** | The dataset to snapshot. | | ||
| + | | **Snapshot prefix** | A label prepended to every snapshot name. Pattern: alphanumeric, | ||
| + | | **Keep** | How many or how long to retain snapshots (see below). | | ||
| + | | **Retention unit** | '' | ||
| + | | **Recursive** | Snapshot this dataset and all its children in one atomic operation. Retention counting is done at the parent level only — child snapshots with the same name are pruned when the parent generation is pruned. | | ||
| + | |||
| + | === Retention behaviour === | ||
| + | |||
| + | * **Count mode:** After creating the new snapshot, count all snapshots whose names match the prefix. Delete the oldest ones until only '' | ||
| + | * **Time mode:** Delete any snapshot whose embedded timestamp is older than '' | ||
| + | |||
| + | === Schedule === | ||
| + | |||
| + | ^ Field ^ Description ^ | ||
| + | | **Time of execution** | '' | ||
| + | | **Minute/ | ||
| + | |||
| + | === Notifications === | ||
| + | |||
| + | ^ Field ^ Description ^ | ||
| + | | **Send output via email** | Email the job output (stdout + stderr) to root on completion. Requires a working mail relay configured in OMV (**System → Notification**). | | ||
| + | | **Send email on error only** | Only send an email if the job exits with a non-zero status. Suppresses routine success emails. | | ||
| + | | **Comment** | Appended to the email subject line to help identify the job. | | ||
| + | |||
| + | ==== Running a job manually ==== | ||
| + | |||
| + | Select a job in the table and click **Run**. The job executes immediately in the background. | ||
| + | Check **Storage → ZFS → Logs → ZFS Snapshot** to see the output. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 10. Scheduled Scrub Jobs ===== | ||
| + | |||
| + | Navigate to **Storage → ZFS → Scrub Jobs**. | ||
| + | |||
| + | Scrub jobs run '' | ||
| + | reads every block, checks its checksum, and repairs any corruption it finds using redundant | ||
| + | copies. Regular scrubbing is the primary mechanism for detecting and correcting silent | ||
| + | corruption before it spreads. | ||
| + | |||
| + | <note important> | ||
| + | asynchronously in the background and may take hours on a large pool. Monitor progress via | ||
| + | **Pool → Scrub → Scrub status** or '' | ||
| + | |||
| + | ==== Creating a scrub job ==== | ||
| + | |||
| + | ^ Field ^ Description ^ | ||
| + | | **Enabled** | Toggle the job on or off. | | ||
| + | | **Pool** | The pool to scrub. | | ||
| + | | **Schedule** | Same scheduling options as snapshot jobs. Default: Weekly. | | ||
| + | | **Send output via email** | Email a brief start notification to root. | | ||
| + | | **Send email on error only** | Only email if the scrub fails to start. | | ||
| + | | **Comment** | Appended to the email subject. | | ||
| + | |||
| + | **Recommended schedule:** Monthly is sufficient for healthy pools on consumer hardware. Weekly | ||
| + | is appropriate for pools under heavy write load. Scrubbing is I/ | ||
| + | off-peak hours. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 11. Replication Jobs ===== | ||
| + | |||
| + | Navigate to **Storage → ZFS → Replication Jobs**. | ||
| + | |||
| + | Replication jobs copy a ZFS dataset (and its snapshots) to another location using '' | ||
| + | '' | ||
| + | making them efficient even for large datasets. Replication can target a local pool or a remote | ||
| + | host over SSH. | ||
| + | |||
| + | ==== Creating a replication job ==== | ||
| + | |||
| + | === Source === | ||
| + | |||
| + | ^ Field ^ Description ^ | ||
| + | | **Enabled** | Toggle the job on or off. | | ||
| + | | **Source dataset** | The dataset to replicate. | | ||
| + | | **Snapshot prefix** | Prefix for replication snapshots. Default: '' | ||
| + | | **Recursive** | Replicate the dataset and all its children. | | ||
| + | | **Raw send** | Use '' | ||
| + | | **Use existing snapshots** | Instead of creating a new snapshot, find the most recent snapshot matching the prefix and send it. Recommended when a separate snapshot job is already managing snapshots for this dataset. Prevents two jobs from fighting over snapshot creation and avoids breaking the incremental chain. | | ||
| + | | **Keep snapshots on source** | Number of replication snapshots to retain on the source after a successful send. '' | ||
| + | | **Use ZFS bookmarks** | After each successful send, create a ZFS bookmark as the incremental base for the next run. Bookmarks survive snapshot deletion, so you can aggressively prune the source while still sending incremental data. **Cannot be used with Recursive mode** (ZFS limitation). | | ||
| + | |||
| + | === Destination === | ||
| + | |||
| + | ^ Field ^ Description ^ | ||
| + | | **Type** | '' | ||
| + | | **Destination dataset** | Path on the destination where data is received (e.g. '' | ||
| + | | **Remote host** | Hostname or IP address of the destination server. Remote only. | | ||
| + | | **SSH Certificate** | The SSH key pair to authenticate with. Create one under **System → Certificates → SSH** and copy it to the remote host before creating the replication job. | | ||
| + | | **SSH user** | Remote user account. Default: '' | ||
| + | | **SSH port** | SSH port on the destination. Default: 22. | | ||
| + | |||
| + | === Schedule and Notifications === | ||
| + | |||
| + | Same scheduling and email options as snapshot and scrub jobs. | ||
| + | |||
| + | ==== Replication strategies ==== | ||
| + | |||
| + | **Scenario: daily replication with independent snapshot retention** | ||
| + | |||
| + | This is the most common setup. A snapshot job manages snapshots on the source; the replication | ||
| + | job sends them to the destination without creating its own. | ||
| + | |||
| + | - Create a snapshot job for '' | ||
| + | - Create a replication job for '' | ||
| + | |||
| + | The snapshot job runs first (or within the same cron minute), then the replication job sends | ||
| + | the newest '' | ||
| + | |||
| + | **Scenario: self-contained replication with source pruning** | ||
| + | |||
| + | When no separate snapshot job exists: | ||
| + | |||
| + | - Create a replication job for '' | ||
| + | |||
| + | The replication job creates its own snapshot, sends it, then prunes old replication snapshots | ||
| + | on the source to keep only 3. | ||
| + | |||
| + | **Scenario: efficient replication with bookmarks** | ||
| + | |||
| + | Suitable when disk space on the source is limited: | ||
| + | |||
| + | - Create a snapshot job for '' | ||
| + | - Create a replication job for '' | ||
| + | |||
| + | The replication job uses the bookmark from the previous run as its incremental base, so the | ||
| + | single retained snapshot is sufficient — there is no need to keep an older snapshot just for | ||
| + | the next send. | ||
| + | |||
| + | ==== Remote replication: | ||
| + | |||
| + | Before creating a remote replication job: | ||
| + | |||
| + | - In OMV, go to **System → Certificates → SSH** and generate a new SSH key pair. | ||
| + | - Click **Copy to Remote** for that certificate, | ||
| + | - In the replication job form, select that certificate under **SSH Certificate**. | ||
| + | |||
| + | The replication script uses strict host key checking — the first connection to a new host is | ||
| + | accepted automatically, | ||
| + | |||
| + | ==== Replication logs ==== | ||
| + | |||
| + | Job output is written to ''/ | ||
| + | web interface under **Storage → ZFS → Logs → ZFS Replicate**. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 12. Properties ===== | ||
| + | |||
| + | ZFS properties control the behaviour of pools and datasets. They can be inherited from a parent | ||
| + | (shown as source '' | ||
| + | |||
| + | ==== Viewing and editing properties ==== | ||
| + | |||
| + | Select any pool, filesystem, volume, or snapshot and click **Properties**. The table shows every | ||
| + | property, its current value, its source, and whether it can be edited. Click the edit icon on | ||
| + | a row to change a property' | ||
| + | |||
| + | Custom user-defined properties (in '' | ||
| + | |||
| + | ==== Frequently adjusted properties ==== | ||
| + | |||
| + | ^ Property ^ Values ^ Notes ^ | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | |||
| + | ==== Raw details ==== | ||
| + | |||
| + | Select a dataset and click **Details** to see the raw output of '' | ||
| + | '' | ||
| + | the exact value of any property without the filtered view. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 13. Discovery (OMV Sync) ===== | ||
| + | |||
| + | ZFS filesystems must be registered in OMV's internal database before they can be used as shared | ||
| + | folder targets. **Discovery** synchronises OMV's fstab database with the live ZFS state. | ||
| + | |||
| + | This is accessible from both the Pools table and the Datasets table under **Discover**. | ||
| + | |||
| + | ^ Action ^ Description ^ | ||
| + | | **Add new** | Find any ZFS filesystems that exist in ZFS but are not yet in OMV's database, and register them. Use this after creating a new dataset that you want to share. | | ||
| + | | **Add new + delete missing** | Full bidirectional sync: adds unregistered datasets //and// removes stale entries for datasets that no longer exist. | | ||
| + | | **Delete missing** | Remove OMV fstab entries for datasets that have been deleted from ZFS but whose entries still remain in the database. | | ||
| + | |||
| + | < | ||
| + | recent backups are kept at ''/ | ||
| + | |||
| + | < | ||
| + | that a dataset may simply be locked rather than deleted.</ | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 14. Dashboard Widgets and ARC Statistics ===== | ||
| + | |||
| + | ==== Dashboard widgets ==== | ||
| + | |||
| + | The plugin adds four dashboard widgets. Enable them via **Dashboard → Configure widgets** | ||
| + | (the gear icon on the dashboard). | ||
| + | |||
| + | ^ Widget ^ Description ^ | ||
| + | | **Pool Health** | A table showing the health status of every pool (ONLINE, DEGRADED, FAULTED, etc.). | | ||
| + | | **Pool Status** | A compact table showing each pool's health, capacity percentage, and used/ | ||
| + | | **ARC Size** | A single number showing the current total ARC (Adaptive Replacement Cache) size. | | ||
| + | | **ZFS ARC Hit/ | ||
| + | |||
| + | ==== ARC Statistics page ==== | ||
| + | |||
| + | Navigate to **Storage → ZFS → ARC Stats** for a full text dump of ARC internals: total size, | ||
| + | MRU/MFU cache sizes, hit/miss counters, prefetch statistics, and more. This is the same output | ||
| + | as the '' | ||
| + | |||
| + | ==== ZFS Logs ==== | ||
| + | |||
| + | Five log viewers are available under **Storage → ZFS → Logs**: | ||
| + | |||
| + | ^ Log ^ Contents ^ | ||
| + | | **ZFS Events** | Output from the ZFS Event Daemon (ZED). Includes pool state changes, device errors, scrub completions, | ||
| + | | **ZFS Snapshot** | Output from all snapshot job runs (''/ | ||
| + | | **ZFS Scrub** | Output from all scrub job runs (''/ | ||
| + | | **ZFS Replicate** | Output from all replication job runs (''/ | ||
| + | | **ZFS Remote Unlock** | Output from the remote HTTPS key-fetch service ('' | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 15. Settings ===== | ||
| + | |||
| + | Navigate to **Storage → ZFS → Settings**. | ||
| + | |||
| + | This page controls system-level ZFS tuning that applies globally, independent of any individual pool or dataset. | ||
| + | |||
| + | ^ Field ^ Description ^ | ||
| + | | **Maximum ARC size (MiB)** | Hard ceiling on how much RAM the Adaptive Replacement Cache may use. Set to '' | ||
| + | | **Minimum ARC size (MiB)** | The floor the ARC will not shrink below even under memory pressure. Set to '' | ||
| + | |||
| + | Changes are written to ''/ | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 16. Tips and Best Practices ===== | ||
| + | |||
| + | ==== Pool design ==== | ||
| + | |||
| + | Designing a ZFS pool layout is about balancing **performance, | ||
| + | |||
| + | **The optimum pool layout depends on how the data will be used.** For sequential workloads (media, backups, archive) RAIDZ works well, but prefer RAIDZ2 for larger drives to reduce resilver second-failure risk. For random I/O (containers, | ||
| + | |||
| + | **Do not add optional devices (log, cache, and/or special VDEV) without justification and understanding.** For example, a "log device" | ||
| + | |||
| + | **For a mix of SSDs and HDDs, consider using the SSDs in a mirror pool and the HDDs in a separate RAIDZ pool.** In a home NAS this separation is preferable to trying to enhance RAIDZ performance by using a log, cache, and/or special VDEV, which only adds complexity and cost (typical consumer SSD/NVMe devices are not suitable for these optional VDEVs). | ||
| + | |||
| + | **Beware adding a basic VDEV to an existing mirror/ | ||
| + | |||
| + | * **Use '' | ||
| + | * **Enable compression on all datasets.** '' | ||
| + | |||
| + | ==== Dataset organisation ==== | ||
| + | |||
| + | * **Create a filesystem per use case** rather than dumping everything into the pool root. Separate datasets for documents, media, backups, and VM images let you tune '' | ||
| + | * **Set '' | ||
| + | * **Set '' | ||
| + | * **Disable '' | ||
| + | |||
| + | ==== Snapshots ==== | ||
| + | |||
| + | * **Automate snapshots with scheduled jobs** rather than relying on manual creation. A daily snapshot job with 30-day retention costs almost nothing in disk space for typical NAS data and provides a safety net against accidental deletion. | ||
| + | * **Name snapshot prefixes clearly.** Use '' | ||
| + | * **Do not delete the most recent common snapshot before replication.** The replication script needs at least one snapshot that exists on both source and destination to perform an incremental send. Deleting it forces a full send. | ||
| + | |||
| + | ==== Scrubbing ==== | ||
| + | |||
| + | * **Schedule monthly scrubs** at minimum. On systems with heavy write loads or older disks, weekly is better. Scrubs are the only way to find silent corruption before it becomes unrecoverable. | ||
| + | * **Check scrub results** in the ZFS Events log or via '' | ||
| + | |||
| + | ==== Encryption ==== | ||
| + | |||
| + | * **Choose AES-256-GCM.** It is the strongest available option and is hardware-accelerated on all x86-64 CPUs with AES-NI (essentially all modern systems). | ||
| + | * **Keep a copy of your passphrase offline.** A lost passphrase means permanently lost data. Store it in a password manager and print a copy kept in a physically secure location. | ||
| + | * **Use remote HTTPS auto-unlock for maximum security.** Local keyfile auto-unlock is convenient but an attacker who steals the whole server gets the key with the disks. A remote key server eliminates this risk. | ||
| + | * **Always delete encrypted datasets through the plugin**, not via '' | ||
| + | |||
| + | ==== Replication ==== | ||
| + | |||
| + | * **Test your replication jobs** by running them manually and verifying the destination has the expected datasets. Check the replication log for errors. | ||
| + | * **Use a separate snapshot job with '' | ||
| + | * **Remote replication requires a working SSH trust relationship.** Use OMV's **System → Certificates → SSH → Copy to Remote** to set this up before creating the job. | ||
| + | === Note === | ||
| - | For now, you can use the documentation written for OMV7 as a reference. | + | You can use the documentation written for OMV7 as a reference |
| Go to -> [[omv7: | Go to -> [[omv7: | ||