<?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>Etcd on inetshell</title><link>https://inet.sh/en/tags/etcd/</link><description>Recent content in Etcd on inetshell</description><generator>Hugo</generator><language>en</language><lastBuildDate>Fri, 25 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://inet.sh/en/tags/etcd/index.xml" rel="self" type="application/rss+xml"/><item><title>k3s: recover an HA cluster with embedded etcd that lost quorum (--cluster-reset)</title><link>https://inet.sh/en/posts/k3s/cluster-reset-lost-quorum/</link><pubDate>Fri, 25 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/k3s/cluster-reset-lost-quorum/</guid><description>When two of three k3s servers with embedded etcd die, the API stops answering. k3s server --cluster-reset turns the survivor into the only etcd member while keeping its data; the other servers wipe their db and rejoin. Also how to restore from a snapshot with --cluster-reset-restore-path, what happens with the token, and how to avoid ending up with two clusters by accident.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<p>On <strong>a single</strong> server, the one with the most recent data:</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><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</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">systemctl stop k3s
</span></span><span class="line"><span class="cl">k3s server --cluster-reset
</span></span><span class="line"><span class="cl"><span class="c1"># ... ends with:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Managed etcd cluster membership has been reset, restart without --cluster-reset flag now</span>
</span></span><span class="line"><span class="cl">systemctl start k3s
</span></span></code></pre></td></tr></table>
</div>
</div><p>On <strong>each of the other servers</strong>, with their <code>/etc/rancher/k3s/config.yaml</code> pointing at the one you reset (<code>server: https://node1:6443</code>):</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><span class="lnt">2
</span><span class="lnt">3
</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">systemctl stop k3s
</span></span><span class="line"><span class="cl">rm -rf /var/lib/rancher/k3s/server/db
</span></span><span class="line"><span class="cl">systemctl start k3s
</span></span></code></pre></td></tr></table>
</div>
</div><p>Agents need nothing; they reconnect on their own.</p>
<h2 id="when-it-is-needed">When it is needed</h2>
<p>etcd needs a majority: with three servers, two alive; with five, three. If you lose the majority (two dead nodes, a network partition, an IP change on several at once, an attempt to remove nodes by hand), the remaining one cannot write and the API freezes:</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><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">kubectl get nodes
</span></span><span class="line"><span class="cl">Error from server: etcdserver: request timed out
</span></span></code></pre></td></tr></table>
</div>
</div><p>In <code>journalctl -u k3s</code> you will see <code>etcd cluster join failed</code>, <code>failed to get raft status</code>, or <code>waiting for etcd quorum</code>. <code>--cluster-reset</code> is the way out k3s documents for exactly this.</p>
<h2 id="what---cluster-reset-does">What <code>--cluster-reset</code> does</h2>
<p>It rewrites the etcd member list so the current node is <strong>the only one</strong>, keeping all of its data (the Kubernetes objects, secrets, everything). It does not touch pods or agents. The only thing lost is writes that did not get replicated to this node before the outage, which in practice are few or none if you pick the most up-to-date one.</p>
<p>With <code>--cluster-reset-restore-path</code> it does the same but <strong>replaces</strong> the data with a snapshot&rsquo;s; everything after the snapshot disappears.</p>
<h2 id="step-by-step">Step by step</h2>
<ol>
<li>
<p><strong>Pick the node.</strong> If several servers are still alive but without quorum, keep the one with the most recent snapshot or <code>db</code>: <code>ls -la /var/lib/rancher/k3s/server/db/</code> and <code>k3s etcd-snapshot ls</code>.</p>
</li>
<li>
<p><strong>Keep a copy just in case</strong>, token included:</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><span class="lnt">2
</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">cp -a /var/lib/rancher/k3s/server/db /root/k3s-db-<span class="k">$(</span>date +%F<span class="k">)</span>
</span></span><span class="line"><span class="cl">cp /var/lib/rancher/k3s/server/token /root/k3s-token
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p><strong>Reset and start</strong> (the TL;DR block). The command takes a few seconds; do not interrupt it.</p>
</li>
<li>
<p><strong>Check</strong> that the API answers and the node is the only etcd member:</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><span class="lnt">2
</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">kubectl get nodes -o wide
</span></span><span class="line"><span class="cl">kubectl get nodes -l node-role.kubernetes.io/etcd<span class="o">=</span><span class="nb">true</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The other servers show <code>NotReady</code> until they join; agents go back to <code>Ready</code> on their own.</p>
</li>
<li>
<p><strong>Review the other servers&rsquo; configuration before starting them.</strong> The cluster&rsquo;s first node usually has <code>cluster-init: true</code> and <strong>no</strong> <code>server:</code>; if you reset a different node, that first one would start a new cluster of its own. Make sure every node about to join has:</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><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># /etc/rancher/k3s/config.yaml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://node1:6443    </span><span class="w"> </span><span class="c"># the reset node (or the VIP, if it points there)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">token</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;the same token&gt;</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p><strong>Join them one at a time</strong>: stop, delete <code>server/db</code>, start. Each one comes in as a <em>learner</em> and takes a minute or two to become a full member; wait for <code>Ready</code> before the next.</p>
</li>
<li>
<p><strong>Fresh snapshot</strong> once everything is green: <code>k3s etcd-snapshot save --name post-reset</code>.</p>
</li>
</ol>
<h2 id="restoring-from-a-snapshot">Restoring from a snapshot</h2>
<p>If the survivor&rsquo;s <code>db</code> is corrupt or you want to go back to an earlier point:</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><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</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">k3s etcd-snapshot ls
</span></span><span class="line"><span class="cl">systemctl stop k3s
</span></span><span class="line"><span class="cl">k3s server --cluster-reset --cluster-reset-restore-path<span class="o">=</span>/var/lib/rancher/k3s/server/db/snapshots/etcd-snapshot-node1-1758760000
</span></span><span class="line"><span class="cl">systemctl start k3s
</span></span></code></pre></td></tr></table>
</div>
</div><p>Then the same rejoin of the other servers. By default k3s takes a snapshot every 12 hours (<code>0 */12 * * *</code>) and keeps 5, under <code>/var/lib/rancher/k3s/server/db/snapshots</code>; <code>--etcd-s3</code> ships them to S3.</p>
<h2 id="traps">Traps</h2>
<ul>
<li><strong>Never on two nodes.</strong> <code>--cluster-reset</code> on two servers gives two clusters with the same name and agents confused between them. One resets, the rest join.</li>
<li><strong>The token rules.</strong> The snapshot carries the certificates and encryption keys protected with the cluster token. Restoring on a new node without the same token (<code>--token</code>) does not start. Keep <code>/var/lib/rancher/k3s/server/token</code> outside the cluster.</li>
<li><strong>SQLite does not have this.</strong> A single server without etcd neither needs nor accepts <code>--cluster-reset</code>; its backup is copying <code>/var/lib/rancher/k3s/server/db/</code>.</li>
<li><strong>An orphaned <code>cluster-init</code></strong> on a node about to rejoin is the easiest way to end up with two clusters. Check step 5.</li>
<li><strong>A load balancer or VIP in front of the servers</strong>: if it points at a dead node, the agents will not come back either; check it before blaming etcd.</li>
</ul>
<p>Reference: <a href="https://docs.k3s.io/cli/etcd-snapshot">etcd-snapshot</a> and <a href="https://docs.k3s.io/datastore/backup-restore">Backup and Restore</a> in the k3s documentation.</p>
]]></content:encoded></item></channel></rss>