<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Recv on inetshell</title><link>https://inet.sh/en/tags/recv/</link><description>Recent content in Recv on inetshell</description><generator>Hugo</generator><language>en</language><lastBuildDate>Sat, 26 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://inet.sh/en/tags/recv/index.xml" rel="self" type="application/rss+xml"/><item><title>ZFS: resume an interrupted zfs send | zfs recv (receive_resume_token)</title><link>https://inet.sh/en/posts/zfs/resume-zfs-send-recv/</link><pubDate>Sat, 26 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/zfs/resume-zfs-send-recv/</guid><description>A multi-TB zfs send that drops halfway does not have to start over: with zfs recv -s the receiver keeps the partial state and publishes receive_resume_token; zfs send -t &amp;lt;token&amp;gt; picks up exactly where it stopped, for incremental and raw (-w) sends too. How to use it, how to abort with zfs recv -A, and what invalidates the token.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># Receiver ready to resume: -s on the recv</span>
</span></span><span class="line"><span class="cl">zfs send -v -w tank/data@snap1 <span class="p">|</span> ssh backup-host zfs recv -s backup/data
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># It dropped. On the receiver, ask for the token:</span>
</span></span><span class="line"><span class="cl">zfs get -H -o value receive_resume_token backup/data
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># On the sender, resume from that point (no dataset or snapshot named: the token carries it all):</span>
</span></span><span class="line"><span class="cl">zfs send -v -t 1-d4f61d893-c0-789c636064000310a500c4ec50360710e72765a5… <span class="p">|</span> ssh backup-host zfs recv -s backup/data
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Give up and free what was half received:</span>
</span></span><span class="line"><span class="cl">zfs recv -A backup/data
</span></span></code></pre></td></tr></table>
</div>
</div><p>It works the same for incremental sends (<code>-i</code> / <code>-I</code>) and for raw sends of encrypted datasets (<code>-w</code>).</p>
<h2 id="why">Why</h2>
<p>Without <code>-s</code>, an interrupted <code>zfs recv</code> throws away everything it had received: hours of transfer gone because of a network blip or a reboot. With <code>-s</code> (OpenZFS since 2017; Linux 0.7+, FreeBSD 12+) the receiver keeps the received piece in a hidden dataset (<code>backup/data/%recv</code>) and exposes the <strong><code>receive_resume_token</code></strong> property.</p>
<p>The token is the complete description of what is missing: it identifies the source snapshot, the object and offset where it stopped, and the stream options (raw, compressed, large blocks…). That is why <code>zfs send -t</code> needs no other arguments, and why you do not have to remember which flags the original <code>send</code> carried: they are rebuilt from the token.</p>
<h2 id="step-by-step">Step by step</h2>
<ol>
<li>
<p><strong>Always receive with <code>-s</code>.</strong> It costs nothing and it is the only thing to remember before the drop.</p>
</li>
<li>
<p><strong>After the interruption</strong>, on the receiver:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs get -H -o value receive_resume_token backup/data
</span></span></code></pre></td></tr></table>
</div>
</div><p>If it returns <code>-</code>, there is no partial state (the send finished or never started).</p>
</li>
<li>
<p><strong>On the sender</strong>, <code>zfs send -t &lt;token&gt;</code> with the usual pipeline into <code>zfs recv -s</code>. You can cut and resume as many times as needed; the token changes each time.</p>
</li>
<li>
<p><strong>Check the end</strong>: <code>zfs get receive_resume_token</code> goes back to <code>-</code> and the snapshot shows up in <code>zfs list -t snapshot -r backup/data</code>.</p>
</li>
</ol>
<p>For progress, <code>-v</code> on the <code>send</code> (bytes sent) or <code>pv</code> in the middle; to buffer latency on slow links, <code>mbuffer</code> between <code>send</code> and <code>ssh</code>.</p>
<h2 id="aborting">Aborting</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zfs recv -A backup/data
</span></span></code></pre></td></tr></table>
</div>
</div><p>Deletes the <code>%recv</code> and frees the space. Needed when the token no longer works (below) or when you decide to resend from another snapshot.</p>
<h2 id="with-syncoid">With syncoid</h2>
<p><code>syncoid</code> uses <code>recv -s</code> and the tokens on its own: if a send drops, the next run resumes it by itself. <code>--no-resume</code> turns it off if it ever gets in the way. Sanoid + syncoid are the comfortable way to do this in production.</p>
<h2 id="traps">Traps</h2>
<ul>
<li><strong>Destroying the source snapshot invalidates the token.</strong> If your snapshot policy (or a manual <code>zfs destroy</code>) takes <code>@snap1</code> away mid-send, <code>send -t</code> fails: <code>zfs recv -A</code> and start from a snapshot that exists. A <a href="/en/posts/zfs/hold-protect-snapshot/"><code>hold</code></a> on the snapshot you are sending prevents it.</li>
<li><strong>Both ends must support it</strong>: modern OpenZFS on both. Old Solaris and Illumos do not understand <code>-s</code>.</li>
<li><strong><code>-w</code> is preserved in the token</strong>: a resumed raw send stays raw; do not try to mix a <code>send -t</code> with a <code>recv</code> into a target different from the original.</li>
<li><strong>While partial state exists</strong>, <code>zfs destroy</code> of the target dataset complains: <code>recv -A</code> first.</li>
<li><strong><code>-R</code> streams</strong> (packages with several snapshots) resume too, but it is simpler to send snapshot by snapshot with <code>-i</code>, which is what syncoid does.</li>
</ul>
<h2 id="see-also">See also</h2>
<ul>
<li><a href="/en/posts/zfs/backups-to-s3/">Send ZFS snapshots to another host (and on to S3)</a></li>
<li><a href="/en/posts/zfs/send-unencrypted-to-encrypted/">zfs send/recv from an unencrypted dataset to an encrypted one</a></li>
<li><a href="/en/posts/zfs/hold-protect-snapshot/">Protect snapshots with hold</a></li>
</ul>
]]></content:encoded></item></channel></rss>