{"id":1894,"date":"2026-10-09T00:12:05","date_gmt":"2026-10-09T05:12:05","guid":{"rendered":"https:\/\/zewwy.ca\/?p=1894"},"modified":"2026-10-09T00:12:05","modified_gmt":"2026-10-09T05:12:05","slug":"the-poor-mans-vaai-for-zfs-over-iscsi","status":"publish","type":"post","link":"https:\/\/zewwy.ca\/index.php\/2026\/10\/09\/the-poor-mans-vaai-for-zfs-over-iscsi\/","title":{"rendered":"The Poor Man\u2019s VAAI for ZFS-over-iSCSI"},"content":{"rendered":"<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><\/div>\n<h1 style=\"text-align: center;\" role=\"heading\" data-sfc-root=\"ep\"><span class=\"ez-toc-section\" id=\"The_Poor_Mans_VAAI_Orchestrating_an_Offline_ZFS-over-iSCSI_Storage_Migration_Manually\"><\/span>The Poor Man\u2019s VAAI: Orchestrating an Offline ZFS-over-iSCSI Storage Migration Manually<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h1>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">If you run a homelab or an enterprise environment utilizing <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Proxmox VE (PVE)<!--TgQPHd|||[]--><\/span> backed by a <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">TrueNAS<!--TgQPHd|||[]--><\/span> storage array via a community ZFS-over-iSCSI plugin (like the popular <em data-sfc-root=\"ep\" data-epip=\"\">TheGrandWazoo<!--TgQPHd|||[]--><\/em> implementation), you have likely run into the limits of the API abstraction layer.<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Try to perform a cross-pool storage migration (moving a virtual hard drive from one ZFS pool to another on the same TrueNAS machine), and you are likely to get slapped with this cryptic failure:<!--TgQPHd|||[]--><\/div>\n<div dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">\n<pre><code class=\"language-text\">TASK ERROR: storage migration failed: TrueNAS API POST \/iscsi\/extent: 422 Unprocessable Entity \u2014 iscsi_extent_create.name: Extent name must be unique\r\n<\/code><\/pre>\n<p><!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Here is a deep technical breakdown of why this happens, how the hypervisor configuration misleads you, and how to execute a manual, bare-metal block migration\u2014essentially building a <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">poor man\u2019s offline VAAI (vSphere Storage API for Array Integration)<!--TgQPHd|||[]--><\/span> for ZFS over iSCSI.<\/div>\n<h1 style=\"text-align: center;\" role=\"heading\" data-sfc-root=\"ep\"><span class=\"ez-toc-section\" id=\"The_Architectural_Root_Cause\"><\/span>The Architectural Root Cause<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h1>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">To understand the failure, you have to look at the intersection of three components: TrueNAS&#8217;s global iSCSI architecture, the Proxmox plugin layer, and the PVE VM configuration file.<!--TgQPHd|||[]--><\/div>\n<h2 role=\"heading\" data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span class=\"ez-toc-section\" id=\"1_TrueNAS_Manage_iSCSI_Globally\"><\/span>1. TrueNAS Manage iSCSI Globally<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">TrueNAS maintains a centralized, system-wide configuration database for its iSCSI daemon (<code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">ctld<!--TgQPHd|||[]--><\/code> or <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">SCST<!--TgQPHd|||[]--><\/code>). When an initiator requests a target, TrueNAS maps that connection globally. It enforces strict <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">namespace uniqueness<!--TgQPHd|||[]--><\/span> across the entire machine for Extents, Targets, and Associated Targets. TrueNAS does not care if you have two isolated ZFS pools; you cannot register the same extent name twice on the same machine.<!--TgQPHd|||[]--><\/div>\n<h2 role=\"heading\" data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span class=\"ez-toc-section\" id=\"2_The_Proxmox_Plugin_Collision\"><\/span>2. The Proxmox Plugin Collision<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">When you define two separate TrueNAS-backed iSCSI datastores in Proxmox, they usually point to the <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">same TrueNAS Host Portal IP<!--TgQPHd|||[]--><\/span>.<br data-sfc-root=\"ep\" data-epip=\"\" data-sfc-pl=\"|||[]\" \/>The storage plugin hardcodes the iSCSI extent naming convention using the VM ID and slot number: <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">vm-ID-disk-X<!--TgQPHd|||[]--><\/code>.<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">When you initiate a storage migration to the <em data-sfc-root=\"ep\" data-epip=\"\">other<!--TgQPHd|||[]--><\/em> datastore via the Proxmox GUI, the plugin tries to provision the new storage before tearing down the old one. It fires off a <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">POST<!--TgQPHd|||[]--><\/code> request to the TrueNAS API to create an extent named <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">vm-108-disk-1<!--TgQPHd|||[]--><\/code> on the destination pool. Because that exact name already exists in TrueNAS\u2019s global registry (pointing to the source pool), TrueNAS immediately rejects it with a <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">422 Unprocessable Entity<!--TgQPHd|||[]--><\/span>.<!--TgQPHd|||[]--><\/div>\n<h2 role=\"heading\" data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span class=\"ez-toc-section\" id=\"3_The_PVE_conf_File_Illusion\"><\/span>3. The PVE <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">.conf<!--TgQPHd|||[]--><\/code> File Illusion<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">In an iSCSI architecture, the storage configuration string in Proxmox is <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">just a pointer<!--TgQPHd|||[]--><\/span>. It tells Proxmox to talk to the storage daemon associated with that label and map a specific disk ID. Because both Proxmox datastore definitions map to the same target IP portal, Proxmox connects to the exact same network destination. TrueNAS sees the incoming request for <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">vm-108-disk-1<!--TgQPHd|||[]--><\/code> and points the hypervisor right back to the original active iSCSI session on the old pool.<\/div>\n<h1 style=\"text-align: center;\" role=\"heading\" data-sfc-root=\"ep\"><span class=\"ez-toc-section\" id=\"Poor_Mans_Offline_VAAI_The_Manual_Fix\"><\/span>Poor Man&#8217;s Offline VAAI: The Manual Fix<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h1>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">In an enterprise environment with a SAN that natively supports <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">VAAI block offloading<!--TgQPHd|||[]--><\/span>, a storage migration issues a single command telling the array to clone blocks and re-map the LUN at the hardware layer\u2014consuming zero hypervisor CPU or network bandwidth.<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Since the Proxmox plugin lacks the logic to orchestrate this natively across multiple TrueNAS pools, we can act as the orchestration engine ourselves using raw ZFS and iSCSI manipulation.<!--TgQPHd|||[]--><\/div>\n<h2 role=\"heading\" data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span class=\"ez-toc-section\" id=\"Step_1_Detach_the_Disk_in_Proxmox\"><\/span>Step 1: Detach the Disk in Proxmox<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">To safely move a block storage device without corrupting data, the file systems must be unmounted and the iSCSI lock freed.<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">\n<div><\/div>\n<ol>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Shut down the target VM completely.<!--TgQPHd|||[]--><\/li>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">In Proxmox, navigate to the VM \u2794 <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Hardware<!--TgQPHd|||[]--><\/span> \u2794 Select the target drive (e.g., <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">scsi0<!--TgQPHd|||[]--><\/code>) \u2794 Click <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Detach<!--TgQPHd|||[]--><\/span>.<!--TgQPHd|||[]--><\/li>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">The drive will drop down to the bottom of the list as an <em data-sfc-root=\"ep\" data-epip=\"\">Unused Disk<!--TgQPHd|||[]--><\/em>.<!--TgQPHd|||[]--><\/li>\n<\/ol>\n<p><!--TgQPHd|||[]--><\/div>\n<h2 role=\"heading\" data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span class=\"ez-toc-section\" id=\"Step_2_Backend_ZFS_Block_Migration_TrueNAS_CLI\"><\/span>Step 2: Backend ZFS Block Migration (TrueNAS CLI)<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Instead of forcing data across your network adapter via the hypervisor, leverage native ZFS snapshot streaming directly inside the TrueNAS kernel.<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">\n<div><\/div>\n<ol>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">SSH into TrueNAS or open the Web UI Shell.<!--TgQPHd|||[]--><\/li>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Escalate privileges to root using <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">sudo<!--TgQPHd|||[]--><\/code>.<!--TgQPHd|||[]--><\/li>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Take an atomic, read-only snapshot of the source Zvol:\n<div dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">\n<pre><code class=\"language-bash\">sudo zfs snapshot BigBurtha\/vm-108-disk-1@move\r\n<\/code><\/pre>\n<p><!--TgQPHd|||[]--><\/div>\n<p><!--TgQPHd|||[]--><\/li>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Stream the raw data blocks directly into the destination pool (<code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">LexarPool<!--TgQPHd|||[]--><\/code>) using a ZFS send\/receive pipe. The <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">-vR<!--TgQPHd|||[]--><\/code> flags enable a progress bar and maintain replication properties:\n<div dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">\n<pre><code class=\"language-bash\">sudo zfs send -vR BigBurtha\/vm-108-disk-1@move | sudo zfs recv LexarPool\/vm-108-disk-1\r\n<\/code><\/pre>\n<p><!--TgQPHd|||[]--><\/div>\n<p><!--TgQPHd|||[]--><\/li>\n<\/ol>\n<p><!--TgQPHd|||[]--><\/div>\n<h2 role=\"heading\" data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span class=\"ez-toc-section\" id=\"Step_3_Atomic_LUN_Mapping_Flip_TrueNAS_GUI\"><\/span>Step 3: Atomic LUN Mapping Flip (TrueNAS GUI)<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Now that the data blocks reside on the new physical disks, you must rewrite the iSCSI pointer inside the TrueNAS database so the global network target routes to the new destination pool.<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">\n<div><\/div>\n<ol>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">In the TrueNAS Web UI, navigate to <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Shares<!--TgQPHd|||[]--><\/span> \u2794 <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Block Shares (iSCSI)<!--TgQPHd|||[]--><\/span> \u2794 <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Extents<!--TgQPHd|||[]--><\/span>.<!--TgQPHd|||[]--><\/li>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Find the extent matching your disk (<code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">vm-108-disk-1<!--TgQPHd|||[]--><\/code>) and click <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Edit<!--TgQPHd|||[]--><\/span>.<!--TgQPHd|||[]--><\/li>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Locate the <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Source \/ Zvol Path<!--TgQPHd|||[]--><\/span> dropdown menu. It will currently point to your old pool path: <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">\/dev\/zvol\/BigBurtha\/vm-108-disk-1<!--TgQPHd|||[]--><\/code>.<!--TgQPHd|||[]--><\/li>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Flip this dropdown to point to the new pool path: <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">\/dev\/zvol\/LexarPool\/vm-108-disk-1<!--TgQPHd|||[]--><\/code><!--TgQPHd|||[]--><\/span>.<!--TgQPHd|||[]--><\/li>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Save the configuration.<!--TgQPHd|||[]--><\/li>\n<\/ol>\n<p><!--TgQPHd|||[]--><\/div>\n<h2 role=\"heading\" data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span class=\"ez-toc-section\" id=\"Step_4_Re-link_and_Boot\"><\/span>Step 4: Re-link and Boot<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Go back to the Proxmox Web UI. Select the <em data-sfc-root=\"ep\" data-epip=\"\">Unused Disk<!--TgQPHd|||[]--><\/em> at the bottom of the VM hardware configuration panel, click <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Edit\/Add<!--TgQPHd|||[]--><\/span>, and reassign it back to its original slot (e.g., <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">scsi0<!--TgQPHd|||[]--><\/code>).<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">When you boot the VM, Proxmox will query the iSCSI target portal exactly as it did before. TrueNAS receives the request, references its updated global registry database, and serves the blocks directly out of <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">LexarPool<!--TgQPHd|||[]--><\/code>. Run a disk I\/O benchmark utility inside the guest OS, and you will see the hardware statistics instantly register activity on the new storage array.<!--TgQPHd|||[]--><\/div>\n<h2 role=\"heading\" data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span class=\"ez-toc-section\" id=\"Step_5_Post-Migration_Cleanup\"><\/span>Step 5: Post-Migration Cleanup<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Once the VM is verified stable and running on the new pool, the iSCSI service will release its execution locks on the old Zvol path, allowing you to cleanly reclaim your storage capacity.<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">From the TrueNAS Shell, execute the final destruction commands:<!--TgQPHd|||[]--><\/div>\n<div dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">\n<pre><code class=\"language-bash\"># Delete the migration snapshot from the original pool\r\nsudo zfs destroy BigBurtha\/vm-108-disk-1@move\r\n\r\n# Wipe the old Zvol block device entirely\r\nsudo zfs destroy BigBurtha\/vm-108-disk-1\r\n\r\n# Optional: Clean up the temporary snapshot on the new pool\r\nsudo zfs destroy LexarPool\/vm-108-disk-1@move\r\n<\/code><\/pre>\n<p><!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">You might be wondering if utilizing zfs snapshot did I need to power down and detach the disk as step 1?<\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><mark data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Yes, you are 100% correct.<!--TgQPHd|||[]--><\/span><!--TgQPHd|||[]--><\/mark> You absolutely could have done the ZFS snapshot and copy task completely <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">live<!--TgQPHd|||[]--><\/span> while the VM was running, reducing your actual downtime to just a few seconds.<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Taking a ZFS snapshot is an <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">atomic operation<!--TgQPHd|||[]--><\/span> at the block layer; it takes less than a second to freeze the blocks, regardless of whether the VM is actively writing data or not.<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">By restructuring the order of operations, you can turn this into a <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">near-live storage migration<!--TgQPHd|||[]--><\/span> with minimal disruption.<!--TgQPHd|||[]--><\/div>\n<h2 role=\"heading\" data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span class=\"ez-toc-section\" id=\"The_Live_Migration_Workflow_Zero-Downtime_Copy\"><\/span>The Live Migration Workflow (Zero-Downtime Copy)<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Here is how you would optimize the steps for a blog post to achieve an almost seamless migration:<!--TgQPHd|||[]--><\/div>\n<h2 role=\"heading\" data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span class=\"ez-toc-section\" id=\"1_The_Live_ZFS_SendRecv_Zero_Downtime\"><\/span>1. The Live ZFS Send\/Recv (Zero Downtime)<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">While the VM is fully booted and handling active I\/O, you run the initial snapshot and block transfer. ZFS will copy all the existing data blocks over to <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">LexarPool<!--TgQPHd|||[]--><\/code>.<!--TgQPHd|||[]--><\/div>\n<div dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">\n<pre><code class=\"language-bash\">sudo zfs snapshot BigBurtha\/vm-108-disk-1@live-migrate\r\nsudo zfs send -vR BigBurtha\/vm-108-disk-1@live-migrate | sudo zfs recv LexarPool\/vm-108-disk-1\r\n<\/code><\/pre>\n<p><!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><em data-sfc-root=\"ep\" data-epip=\"\">(The VM stays online the entire time this data streams across your pools).<!--TgQPHd|||[]--><\/em><!--TgQPHd|||[]--><\/div>\n<h2 role=\"heading\" data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span class=\"ez-toc-section\" id=\"2_The_Catch-Up_Snapshot_Optional_but_Recommended\"><\/span>2. The Catch-Up Snapshot (Optional but Recommended)<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">If the block copy took a long time (e.g., a massive multi-terabyte drive), the VM will have written new data to <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">BigBurtha<!--TgQPHd|||[]--><\/code> while the copy was running. To minimize downtime even further, you take a second incremental snapshot to sync just the changes:<!--TgQPHd|||[]--><\/div>\n<div dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">\n<pre><code class=\"language-bash\">sudo zfs snapshot BigBurtha\/vm-108-disk-1@sync\r\nsudo zfs send -vR -i BigBurtha\/vm-108-disk-1@live-migrate BigBurtha\/vm-108-disk-1@sync | sudo zfs recv LexarPool\/vm-108-disk-1\r\n<\/code><\/pre>\n<p><!--TgQPHd|||[]--><\/div>\n<h2 role=\"heading\" data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span class=\"ez-toc-section\" id=\"3_The_%E2%80%9CMicro-Downtime%E2%80%9D_Cutover\"><\/span>3. The &#8220;Micro-Downtime&#8221; Cutover<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">This is the only part where you have to stop the VM, because you cannot re-route an active iSCSI session while TrueNAS holds an open kernel lock on the running file system.<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">\n<div><\/div>\n<ol>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Shut down the VM<!--TgQPHd|||[]--><\/span> in Proxmox.<!--TgQPHd|||[]--><\/li>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Run a final incremental ZFS sync to catch the last few blocks written during the shutdown sequence:\n<div dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">\n<pre><code class=\"language-bash\">sudo zfs snapshot BigBurtha\/vm-108-disk-1@final\r\nsudo zfs send -vR -i BigBurtha\/vm-108-disk-1@sync BigBurtha\/vm-108-disk-1@final | sudo zfs recv LexarPool\/vm-108-disk-1\r\n<\/code><\/pre>\n<p><!--TgQPHd|||[]--><\/div>\n<p><!--TgQPHd|||[]--><\/li>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">In the TrueNAS Web UI (<span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Shares \u2794 Block Shares \u2794 Extents<!--TgQPHd|||[]--><\/span>), edit the <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">vm-108-disk-1<!--TgQPHd|||[]--><\/code> extent and flip the dropdown path from <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">BigBurtha<!--TgQPHd|||[]--><\/code> to <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">LexarPool<!--TgQPHd|||[]--><\/code>.<!--TgQPHd|||[]--><\/li>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Update your Proxmox <code dir=\"ltr\" data-sfc-root=\"ep\" data-epip=\"\">.conf<!--TgQPHd|||[]--><\/code> file label if you want the storage names to look tidy (though as we discovered, this part is just cosmetic for the iSCSI portal routing).<!--TgQPHd|||[]--><\/li>\n<li data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">Boot the VM back up.<!--TgQPHd|||[]--><\/span><!--TgQPHd|||[]--><\/li>\n<\/ol>\n<p><!--TgQPHd|||[]--><\/div>\n<h2 role=\"heading\" data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><span class=\"ez-toc-section\" id=\"Why_this_is_even_closer_to_real_VAAI\"><\/span>Why this is even closer to real VAAI<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">By doing the heavy lifting (the bulk data transfer) while the VM is fully operational, your total downtime drops from <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">hours<!--TgQPHd|||[]--><\/span> (waiting for network copies or local block transfers to finish while the VM is offline) to <span data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">literally 30 seconds<!--TgQPHd|||[]--><\/span>\u2014just long enough to stop the VM, flip the TrueNAS iSCSI mapping dropdown, and hit start again.<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">This approach makes the &#8220;Poor Man&#8217;s VAAI&#8221; title fit even better, because it mirrors exactly how enterprise storage arrays handle asynchronous storage migration cutovers!<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">\n<!--TgQPHd|||[]--><\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">\n<h1 style=\"text-align: center;\" role=\"heading\" data-sfc-root=\"ep\"><span class=\"ez-toc-section\" id=\"Summary\"><\/span>Summary<!--TgQPHd|||[]--><span class=\"ez-toc-section-end\"><\/span><\/h1>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\">When software automation plugins fail to scale across architectural boundaries, understanding the underlying storage routing allows you to manipulate data directly at the block layer. By combining raw ZFS snapshot pipes with target remapping, you bypass API namespace limits completely\u2014achieving zero-network-overhead backend replication.<\/div>\n<\/div>\n<div data-sfc-root=\"ep\" data-epip=\"\">\n<!--TgQPHd|||[]--><\/div>\n<div data-sfc-root=\"ep\" data-epip=\"\"><!--TgQPHd|||[]--><\/div>\n<div data-sfc-root=\"ep\" data-epip=\"\"><!--TgQPHd|||[]--><\/div>\n<\/div>\n<div data-sfc-cp=\"\" data-sfc-root=\"ep\" data-epip=\"\"><\/div>\n<div data-sfc-root=\"ep\" data-epip=\"\">\n<!--TgQPHd|||[]--><\/div>\n<div data-sfc-root=\"ep\" data-epip=\"\"><!--TgQPHd|||[]--><\/div>\n<div data-sfc-root=\"ep\" data-epip=\"\"><!--TgQPHd|||[]--><\/div>\n","protected":false},"excerpt":{"rendered":"<p>The Poor Man\u2019s VAAI: Orchestrating an Offline ZFS-over-iSCSI Storage Migration Manually If you run a homelab or an enterprise environment utilizing Proxmox VE (PVE) backed by a TrueNAS storage array via a community ZFS-over-iSCSI plugin (like the popular TheGrandWazoo implementation), you have likely run into the limits of the API abstraction layer. Try to perform &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/zewwy.ca\/index.php\/2026\/10\/09\/the-poor-mans-vaai-for-zfs-over-iscsi\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;The Poor Man\u2019s VAAI for ZFS-over-iSCSI&#8221;<\/span><\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,6,8,7],"tags":[369,461,507,504],"class_list":["post-1894","post","type-post","status-publish","format-standard","hentry","category-hypervisors","category-networking","category-server-administration","category-storage","tag-iscsi","tag-pve","tag-vaai","tag-zfs"],"_links":{"self":[{"href":"https:\/\/zewwy.ca\/index.php\/wp-json\/wp\/v2\/posts\/1894","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zewwy.ca\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/zewwy.ca\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/zewwy.ca\/index.php\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/zewwy.ca\/index.php\/wp-json\/wp\/v2\/comments?post=1894"}],"version-history":[{"count":1,"href":"https:\/\/zewwy.ca\/index.php\/wp-json\/wp\/v2\/posts\/1894\/revisions"}],"predecessor-version":[{"id":1895,"href":"https:\/\/zewwy.ca\/index.php\/wp-json\/wp\/v2\/posts\/1894\/revisions\/1895"}],"wp:attachment":[{"href":"https:\/\/zewwy.ca\/index.php\/wp-json\/wp\/v2\/media?parent=1894"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/zewwy.ca\/index.php\/wp-json\/wp\/v2\/categories?post=1894"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/zewwy.ca\/index.php\/wp-json\/wp\/v2\/tags?post=1894"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}