<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://marksimpson82.github.io/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://marksimpson82.github.io/blog/" rel="alternate" type="text/html" /><updated>2026-07-25T05:36:09+00:00</updated><id>https://marksimpson82.github.io/blog/feed.xml</id><title type="html">Mark’s Devblog</title><subtitle>Thoughts about development. Oh and games stuff, too.</subtitle><author><name>Mark Simpson</name></author><entry><title type="html">git rebase –onto</title><link href="https://marksimpson82.github.io/blog/2026/07/25/git-rebase-onto.html" rel="alternate" type="text/html" title="git rebase –onto" /><published>2026-07-25T00:00:00+00:00</published><updated>2026-07-25T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2026/07/25/git-rebase-onto</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2026/07/25/git-rebase-onto.html"><![CDATA[<h2 id="git-rebase-onto">git rebase –onto</h2>
<p>If you’re not familiar with <code class="language-plaintext highlighter-rouge">git rebase</code>, I wrote a bit of a primer in <a href="/blog/2026/07/24/git-rebase-trick-fewer-commands.html">a previous post about basic rebasing and git fetch tricks</a>, so it might be worth reading that first.</p>

<p>The rest of this post is for git users who’re comfortable with basic rebasing. I’m going to explain how to use <code class="language-plaintext highlighter-rouge">git rebase --onto</code>, the problem it solves and why you might find it useful.</p>

<p><strong>Warning</strong>: As ever, rebase-based workflows are intended for branches with only a single contributor. If multiple developers are pushing to the same branch, I wouldn’t recommend rebasing it.</p>

<h3 id="what-does-the---onto-option-do">What does the <code class="language-plaintext highlighter-rouge">--onto</code> option do?</h3>
<p>While the <a href="https://git-scm.com/docs/git-rebase#_options">documentation</a> is fairly dry, there is a <a href="https://git-scm.com/docs/git-rebase#_transplanting_a_topic_branch_with_onto">worked example</a> that’s more illuminating, too.</p>

<p>Docs aside, what do <em>I</em> use <code class="language-plaintext highlighter-rouge">--onto</code> for? I use it when I have ‘stacked’ branches and Pull Requests (PRs). I like to create easily reviewable, self-contained commits and PRs. Also, for the purposes of this post, we’ll assume that the smallest mergeable unit is a PR.</p>

<p>The thing with creating small PRs is that, unfortunately this sometimes means branching off a branch. E.g. let’s say we’re implementing a feature and realise the code we’re extending is a bit of a mess. While we could create a PR containing both the refactor and the feature addition, this causes problems:</p>

<ol>
  <li>It mixes non-functional (refactoring) and functional (feature work) changes</li>
  <li>It increases the PR size</li>
  <li>It makes the PR harder to read, understand and review</li>
</ol>

<p>If we have a good idea of what’s to be done, we could structure our changes such that we have <code class="language-plaintext highlighter-rouge">main &lt;- refactor &lt;- feature</code>, e.g.:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>m1  m2  m3
o---o---o branch: main
         \
          r1  r2
          o---o branch: refactor
               \
                f1  f2
                o---o branch: feature               
</code></pre></div></div>

<p><strong>Aside</strong>: There are multiple ways to structure this. You don’t even necessarily need multiple local branches, but that’s for another day :-)</p>

<p>These PRs are smaller and easier to review. The downside is that there’s more dependencies involved, and landing those PRs back into <code class="language-plaintext highlighter-rouge">main</code> requires extra care.</p>

<h3 id="how-do-we-land-dependent-prs-into-main">How do we land dependent PRs into main?</h3>
<p>In the above example with <code class="language-plaintext highlighter-rouge">refactor</code> and <code class="language-plaintext highlighter-rouge">feature</code> PRs, the latter builds on the former, so we need to get our <code class="language-plaintext highlighter-rouge">refactor</code> branch’s changes into <code class="language-plaintext highlighter-rouge">main</code> as the first step.</p>

<p>Firstly, let’s merge <code class="language-plaintext highlighter-rouge">refactor</code> into <code class="language-plaintext highlighter-rouge">main</code> using a squash commit. If we’re using GitHub, BitBucket or any other forge that uses PR flows, we use the UI or CLI to merge the <code class="language-plaintext highlighter-rouge">refactor</code> branch into <code class="language-plaintext highlighter-rouge">main</code>, then pull <code class="language-plaintext highlighter-rouge">main</code>.</p>

<p>If we’re using plain ol’ git and no forge, we can run the commands:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git switch main
git merge <span class="nt">--squash</span> refactor
git commit
</code></pre></div></div>

<p>After the first squash merge, our local repo will look like this, as the content from the <code class="language-plaintext highlighter-rouge">refactor</code> branch’s commits <code class="language-plaintext highlighter-rouge">[r1, r2]</code> is now in commit <code class="language-plaintext highlighter-rouge">m4</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>m1  m2  m3  m4 (squash: r1 + r2)
o---o---o---o branch: main
         \
          r1  r2
          o---o branch: refactor
               \
                f1  f2
                o---o branch: feature               
</code></pre></div></div>

<p>So <code class="language-plaintext highlighter-rouge">refactor</code>’s contents are in <code class="language-plaintext highlighter-rouge">main</code>.  We’re halfway there. Unfortunately, we’ve got a few problems:</p>
<ol>
  <li>The <code class="language-plaintext highlighter-rouge">refactor</code> branch is stale and just kinda existing despite its contents existing in <code class="language-plaintext highlighter-rouge">main</code> via our squash merge</li>
  <li>Our <code class="language-plaintext highlighter-rouge">feature</code> branch is still connected to the tip of <code class="language-plaintext highlighter-rouge">refactor</code></li>
</ol>

<p>Before we can merge <code class="language-plaintext highlighter-rouge">feature</code> into <code class="language-plaintext highlighter-rouge">main</code>, we need to rebase it. Instead of <code class="language-plaintext highlighter-rouge">f1</code>’s parent being <code class="language-plaintext highlighter-rouge">r2</code>, we need to re-parent it to <code class="language-plaintext highlighter-rouge">m4</code> (remember, <code class="language-plaintext highlighter-rouge">m4</code> contains the contents of <code class="language-plaintext highlighter-rouge">[r1, r2]</code>).</p>

<h3 id="lets-use-git-rebase---onto">Let’s use <code class="language-plaintext highlighter-rouge">git rebase --onto</code></h3>
<p>The documentation for <code class="language-plaintext highlighter-rouge">git rebase --onto</code> isn’t the easiest to follow, but the usual use case isn’t too hard to remember once you try it a few times.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git rebase <span class="nt">--onto</span> &lt;new-base&gt; &lt;old-base&gt; &lt;branch-to-move&gt;
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">&lt;new-base&gt;</code>: The new starting point for our commits</li>
  <li><code class="language-plaintext highlighter-rouge">&lt;old-base&gt;</code>: The old starting point – this basically tells <code class="language-plaintext highlighter-rouge">git</code>, “don’t take any commits before this one”</li>
  <li><code class="language-plaintext highlighter-rouge">&lt;branch-to-move&gt;</code>: The branch we’re rebasing</li>
</ul>

<p>In our example, run the following to get the desired effect:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git rebase <span class="nt">--onto</span> main refactor feature
</code></pre></div></div>

<p>This tells git rebase:</p>
<ul>
  <li>The commit we want as our new parent is the tip of the <code class="language-plaintext highlighter-rouge">main</code> branch</li>
  <li>The old parent commit is the tip of the <code class="language-plaintext highlighter-rouge">refactor</code> branch (this effectively excludes the branch’s commits from the rebase operation)</li>
  <li>The commit we want to rebase up to is the tip of <code class="language-plaintext highlighter-rouge">feature</code></li>
</ul>

<p>After running the command, we get the following structure:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>m1  m2  m3  m4 (squash: r1 + r2)
o---o---o---o branch: main
             \
              f1  f2
              o---o branch: feature               
</code></pre></div></div>

<p>You can now push the updated <code class="language-plaintext highlighter-rouge">feature</code> branch to the remote via:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git push origin feature <span class="nt">--force-with-lease</span>
</code></pre></div></div>

<p><strong>Note</strong>: The old <code class="language-plaintext highlighter-rouge">refactor</code> branch still exists at this point; I omitted it from the diagram for brevity. You can delete it via your forge’s controls and/or through git itself:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git branch <span class="nt">-D</span> refactor
git push origin <span class="nt">--delete</span> refactor
</code></pre></div></div>

<h3 id="references">References</h3>
<p>I found GitHub dev <a href="https://news.ycombinator.com/item?id=47759587">sameenkarim’s comment on HN</a> really useful for understanding how this all worked.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Yes, we handle this both in the CLI and server using git rebase --onto

  git rebase --onto &lt;new_commit_sha_generated_by_squash&gt; &lt;original_commit_sha_from_tip_of_merged_branch&gt; &lt;branch_name&gt;

So for ex in this scenario:

  PR1: main &lt;- A, B              (branch1)
  PR2: main &lt;- A, B, C, D        (branch2)
  PR3: main &lt;- A, B, C, D, E, F  (branch3)

When PR 1 and 2 are squash merged, main now looks like:

  S1 (squash of A+B), S2 (squash of C+D)

Then we run the following:

  git rebase --onto S2 D branch3

Which rewrites branch3 to:

  S1, S2, E, F

This operation moves the unique commits from the unmerged branch and replays them on top of the newly squashed commits on the base branch, avoiding any merge conflicts.
</code></pre></div></div>

<p><strong>Next time:</strong> This reminds me that I should also write about why squash “merges” are not actually merge commits (they have one parent, a merge commit by definition must have two or more parents), and how forges retain context and links to long-merged PRs despite this lack of linkage in the git commit history.</p>

<p><strong>Note</strong>: This blog post was hand-written – I am not a robot 🤖🔫.</p>]]></content><author><name>Mark Simpson</name></author><category term="git" /><category term="rebase" /><category term="vcs" /><category term="scm" /><summary type="html"><![CDATA[git rebase –onto If you’re not familiar with git rebase, I wrote a bit of a primer in a previous post about basic rebasing and git fetch tricks, so it might be worth reading that first.]]></summary></entry><entry><title type="html">git trick – git fetch origin main:main</title><link href="https://marksimpson82.github.io/blog/2026/07/24/git-rebase-trick-fewer-commands.html" rel="alternate" type="text/html" title="git trick – git fetch origin main:main" /><published>2026-07-24T00:00:00+00:00</published><updated>2026-07-24T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2026/07/24/git-rebase-trick-fewer-commands</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2026/07/24/git-rebase-trick-fewer-commands.html"><![CDATA[<h2 id="git-rebase-basics">Git rebase basics</h2>
<p>A decent percentage of git users know what rebase does. In its simplest form, it takes one series of commits – typically from a branch –, detaches them from their parent commit and glues them onto the tip of another parent commit (which is frequently just the updated tip of same branch).</p>

<p>I started writing this post about <code class="language-plaintext highlighter-rouge">rebase</code>, but it ended up being partly about the curious <code class="language-plaintext highlighter-rouge">git fetch origin &lt;src&gt;:&lt;dest&gt;</code> syntax – the <code class="language-plaintext highlighter-rouge">&lt;src&gt;:&lt;dest&gt;</code> part is a <a href="https://git-scm.com/docs/gitglossary#def_refspec">refspec</a>. Read on for details.</p>

<p>This post will take you through:</p>
<ul>
  <li>The concept of rebasing</li>
  <li>The 4 commands that most tutorials contain</li>
  <li>How to cut that down to 2 commands and eliminate branch switches</li>
  <li>What the odd-looking <code class="language-plaintext highlighter-rouge">git fetch origin &lt;src&gt;:&lt;dest&gt;</code> command does</li>
</ul>

<h3 id="example-rebasing-the-long-way">Example: rebasing (the long way)</h3>
<p><strong>Note</strong>: if you’re familiar with rebase, skip on to the next bit.</p>

<p>We branched off <code class="language-plaintext highlighter-rouge">main</code> a while back (commit <code class="language-plaintext highlighter-rouge">m3</code> to be precise), but after fetching the latest version of <code class="language-plaintext highlighter-rouge">main</code>, we can see new commits were made: <code class="language-plaintext highlighter-rouge">[m4, m5]</code>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>m1  m2  m3  m4  m5
o---o---o---o---o branch: main
         \
          a1  a2
          o---o branch: a
</code></pre></div></div>

<p>We want to update our branch <code class="language-plaintext highlighter-rouge">a</code> (with commits <code class="language-plaintext highlighter-rouge">[a1, a2]</code>) so that it’s branched off the latest version of <code class="language-plaintext highlighter-rouge">main</code>, aka commit <code class="language-plaintext highlighter-rouge">m5</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>m1  m2  m3  m4  m5
o---o---o---o---o branch: main
                 \
                  a1' a2'
                  o---o branch: a
</code></pre></div></div>
<p><strong>Note</strong>: The commits <code class="language-plaintext highlighter-rouge">[a1', a2']</code> (pronounced a1 prime, a2 prime) no longer have the same commit hashes, even if the underlying change content is identical – a commit hash is calculated using the parent commit(s), message, file contents and more. The parent commit has changed, so the hash has too.</p>

<p>What commands do we need to execute to achieve this goal state? Most tutorials will tell you to run a series of commands similar to the following (assuming we’ve got branch <code class="language-plaintext highlighter-rouge">a</code> checked out and active):</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># we're on branch `a`, so let's switch to the main branch</span>
~/example <span class="o">(</span>a<span class="o">)</span> <span class="nv">$ </span>git switch main

<span class="c"># fetch latest changes and fast-forward the main branch</span>
<span class="c"># this isn't the rebase we're interested in, though</span>
~/example <span class="o">(</span>main<span class="o">)</span> <span class="nv">$ </span>git pull origin main <span class="nt">--rebase</span>

<span class="c"># switch back to last active branch, equiv. to `git switch a`</span>
~/example <span class="o">(</span>main<span class="o">)</span> <span class="nv">$ </span>git switch -

<span class="c"># rebase, which basically says "glue branch a to the tip of main`</span>
~/example <span class="o">(</span>a<span class="o">)</span> <span class="nv">$ </span>git rebase main
</code></pre></div></div>

<h3 id="a-slicker-way--use-git-fetch-origin-mainmain">A slicker way – use <code class="language-plaintext highlighter-rouge">git fetch origin main:main</code></h3>
<p>If you’re like 95% of GitHub/BitBucket/Whatever projects these days, developers directly commits to the <code class="language-plaintext highlighter-rouge">main</code> branch or push it (or whatever your trunk branch is called). Consequently, the <code class="language-plaintext highlighter-rouge">main</code> branch is effectively readonly. Our Continuous Integration (CI) processes handle the merges to <code class="language-plaintext highlighter-rouge">main</code>.</p>

<p>This helps because we’ll never be locally editing <code class="language-plaintext highlighter-rouge">main</code>, so all updates will be saying to git, “please make my version of <code class="language-plaintext highlighter-rouge">main</code> reflect the remote’s version of things”.</p>

<p>Since we no longer need to merge or resolve conflicts on <code class="language-plaintext highlighter-rouge">main</code>, we can use the following (slightly odd-looking) <code class="language-plaintext highlighter-rouge">git fetch</code> syntax to streamline things:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># again, we've got branch `a` checked out and active</span>
~/example <span class="o">(</span>a<span class="o">)</span> <span class="nv">$ </span>git fetch origin main:main

<span class="c"># rebase our `a` branch against freshly updated `main`</span>
~/example <span class="o">(</span>a<span class="o">)</span> <span class="nv">$ </span>git rebase main
</code></pre></div></div>

<p>“Bringo.” We’ve halved the number of required commands. As there’s no need to switch to <code class="language-plaintext highlighter-rouge">main</code>, there’s no need to switch back to <code class="language-plaintext highlighter-rouge">a</code> – we were never switched to <code class="language-plaintext highlighter-rouge">main</code> in the first place!</p>

<p>The slightly odd-looking <code class="language-plaintext highlighter-rouge">git fetch origin &lt;src&gt;:&lt;dst&gt;</code> <a href="https://git-scm.com/docs/gitglossary#def_refspec">refspec</a> syntax fetches <code class="language-plaintext highlighter-rouge">main</code> from the remote and updates our local copy to match it. If your local <code class="language-plaintext highlighter-rouge">main</code> branch has commits not present on the remote, the command will be rejected so as to preserve your local repo’s history.</p>

<p>If you’re a devil-may-care type, you can overwrite your local history by prepending <code class="language-plaintext highlighter-rouge">+</code> to the start of the refspec:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># I wouldn't recommend doing this, as it can be destructive</span>
~/example <span class="o">(</span>a<span class="o">)</span> <span class="nv">$ </span>git fetch origin +main:main
</code></pre></div></div>

<p>I’ve been carping on about <code class="language-plaintext highlighter-rouge">git rebase</code>, but we can use the same <code class="language-plaintext highlighter-rouge">git fetch</code> trick even if we’re going to use <code class="language-plaintext highlighter-rouge">git merge</code> to fold the latest <code class="language-plaintext highlighter-rouge">main</code> contents into <code class="language-plaintext highlighter-rouge">a</code>, instead. I just so happen to prefer rebasing by default:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># again, we've got branch `a` checked out and active</span>
~/example <span class="o">(</span>a<span class="o">)</span> <span class="nv">$ </span>git fetch origin main:main

<span class="c"># merge the latest main into `a`</span>
~/example <span class="o">(</span>a<span class="o">)</span> <span class="nv">$ </span>git merge main
</code></pre></div></div>

<p>So there we go. You can now rebase or merge faster and while using fewer commands. You also now understand what the funky refspec syntax does.</p>

<p><strong>Note</strong>: This blog post was hand-written – I am not a robot 🤖🔫.</p>]]></content><author><name>Mark Simpson</name></author><category term="git" /><category term="rebase" /><category term="vcs" /><category term="scm" /><summary type="html"><![CDATA[Git rebase basics A decent percentage of git users know what rebase does. In its simplest form, it takes one series of commits – typically from a branch –, detaches them from their parent commit and glues them onto the tip of another parent commit (which is frequently just the updated tip of same branch).]]></summary></entry><entry><title type="html">The new git history commands</title><link href="https://marksimpson82.github.io/blog/2026/07/16/git-history-and-jj.html" rel="alternate" type="text/html" title="The new git history commands" /><published>2026-07-16T00:00:00+00:00</published><updated>2026-07-16T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2026/07/16/git-history-and-jj</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2026/07/16/git-history-and-jj.html"><![CDATA[<h2 id="git-has-some-rough-edges">git has some rough edges</h2>
<p>Git has been around a long time (I’ve been using it since around 2011, where’d the time go?) and doesn’t seem to be going anywhere. The main advantage with git is the flexibility – if you can think of doing something, you probably <em>can</em> do it. The downside is that its cli commands often lack comfort and consistency (and I’m saying that as someone who has a good grasp of its internals and is often the friendly “please help me fix my git disaster” guy).</p>

<p>Sometimes fairly simple things require incantations and a small sacrifice. I’m not sure if “un-ergonomic” is the right term, but git certainly can be trying – there’s a reason https://ohshitgit.com/ is a thing. I occasionally still backup branches before I perform git surgery. Still, it beats using Perforce, SVN or CVS.</p>

<h2 id="problem-git-checkout">Problem: <code class="language-plaintext highlighter-rouge">git checkout</code></h2>
<p>Even everyday commands can be unwieldy. Here’s an example of a particularly old, overloaded command: <a href="https://git-scm.com/docs/git-checkout"><code class="language-plaintext highlighter-rouge">git checkout</code></a>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout [-q] [-f] [-m] [&lt;branch&gt;]
git checkout [-q] [-f] [-m] --detach [&lt;branch&gt;]
git checkout [-q] [-f] [-m] [--detach] &lt;commit&gt;
git checkout [-q] [-f] [-m] [[-b|-B|--orphan] &lt;new-branch&gt;] [&lt;start-point&gt;]
git checkout &lt;tree-ish&gt; [--] &lt;pathspec&gt;…​
git checkout &lt;tree-ish&gt; --pathspec-from-file=&lt;file&gt; [--pathspec-file-nul]
git checkout [-f|--ours|--theirs|-m|--conflict=&lt;style&gt;] [--] &lt;pathspec&gt;…​
git checkout [-f|--ours|--theirs|-m|--conflict=&lt;style&gt;] --pathspec-from-file=&lt;file&gt; [--pathspec-file-nul]
git checkout (-p|--patch) [&lt;tree-ish&gt;] [--] [&lt;pathspec&gt;…​]
</code></pre></div></div>

<p>If you’re anything like me, you sort of internalised the cruftiness and forgot that one command does so much. <code class="language-plaintext highlighter-rouge">git checkout</code> can switch branches, swap to a <a href="https://git-scm.com/docs/gitglossary#def_tree-ish">treeish object</a> (which itself can point to a <a href="https://git-scm.com/docs/gitglossary#def_commit-ish">commitish</a> object!) and also restore working tree files. The same command that swaps branches also undoes your edits on a pathspec.</p>

<p>I remember someone getting confused as to why their new CI script worked, because they accidentally passed a tag instead of a branch name. <code class="language-plaintext highlighter-rouge">git checkout</code> will happily work with either…</p>

<h2 id="solution-git-switch-and-git-restore">Solution: <code class="language-plaintext highlighter-rouge">git switch</code> and <code class="language-plaintext highlighter-rouge">git restore</code></h2>
<p>The good news is that the maintainers have been slowly chipping away at this weakness. I’ve noticed several quality of life changes over the years, including for the aforementioned problems with <code class="language-plaintext highlighter-rouge">git commit</code>:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">git switch [-c] &lt;name&gt;</code> for changing branches</li>
  <li><code class="language-plaintext highlighter-rouge">git restore</code> for restoring working tree files</li>
</ul>

<p>Anyway, that’s just a small example of git’s irritations and the things the devs are doing to improve matters. Let’s get on to the main topic: rebasing.</p>

<h2 id="problem-git-rebase">Problem: <code class="language-plaintext highlighter-rouge">git rebase</code></h2>
<p>Rebasing is a powerful and flexible command, but a lot of the things we do with <code class="language-plaintext highlighter-rouge">git rebase</code> are totally mundane. Unfortunately, everyday operations come with extra steps and friction.</p>

<p>Let’s have a look at my git repo’s history:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git log <span class="nt">--oneline</span>

d9aaeea <span class="o">(</span>main<span class="o">)</span> Bump concurrent-ruby from 1.3.6 to 1.3.7 <span class="o">(</span><span class="c">#26)</span>
c4c0a27 Bump faraday from 2.14.2 to 2.14.3 <span class="o">(</span><span class="c">#27)</span>
b5b03ce Bump faraday from 2.14.1 to 2.14.2 <span class="o">(</span><span class="c">#25)</span>
1660b81 jellyfin database repair
fbeef58 <span class="o">(</span>origin/main<span class="o">)</span> Use <span class="sb">`</span>main<span class="sb">`</span> instead of <span class="sb">`</span>master<span class="sb">`</span> branch
</code></pre></div></div>

<p>Let’s imagine I’ve not yet pushed the top 4 commits, and I want to edit commit <code class="language-plaintext highlighter-rouge">1660b81</code>. With <code class="language-plaintext highlighter-rouge">git rebase</code>, I’d have do one of the following:</p>

<p><strong>Option A: Create a fixup commit</strong></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># make changes to working copy</span>
git add <span class="nt">-p</span> blah.md
git commit <span class="nt">--fixup</span> 1650b81

<span class="c"># rebase to squash contents of our new fixup commit into the old one</span>
git rebase <span class="nt">-i</span> <span class="nt">--autosquash</span> 1650b81^
</code></pre></div></div>

<p><strong>Option B: Edit the commit itself</strong></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># stash our changes to blah.md</span>
git stash

<span class="c"># rebase to pick the commit for editing. You might also want to throw in</span>
<span class="c"># a cheeky --update-refs to force dependent branches to rebase, too</span>
<span class="c"># but this is already complicated enough</span>
git rebase <span class="nt">-i</span> 1660b81^
</code></pre></div></div>

<p>Then select commit 1660b81 for editing in the interactive rebase menu:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># git opens a text editor with a list of commits to manipulate
pick d9aaeea # Bump concurrent-ruby from 1.3.6 to 1.3.7 (#26)
pick c4c0a27 # Bump faraday from 2.14.2 to 2.14.3 (#27)
pick b5b03ce # Bump faraday from 2.14.1 to 2.14.2 (#25)
edit 1660b81 # jellyfin database repair
pick fbeef58 # Use `main` instead of `master` branch
</code></pre></div></div>

<p>Then a load more annoying steps:</p>
<ul>
  <li>Unstash the stashed edits with <code class="language-plaintext highlighter-rouge">git stash apply</code> or <code class="language-plaintext highlighter-rouge">pop</code></li>
  <li>Stage them via <code class="language-plaintext highlighter-rouge">git add</code></li>
  <li>Commit them via <code class="language-plaintext highlighter-rouge">git commit</code></li>
  <li>Resume the <code class="language-plaintext highlighter-rouge">rebase</code> via <code class="language-plaintext highlighter-rouge">git rebase --continue</code></li>
</ul>

<h2 id="solution-git-history">Solution: <code class="language-plaintext highlighter-rouge">git history</code></h2>
<p><a href="https://gitlab.com/git-scm/git/-/blob/HEAD/Documentation/RelNotes/2.54.0.adoc">Git v2.54.0</a> quietly introduced a new <code class="language-plaintext highlighter-rouge">git history</code> command:</p>
<blockquote>
  <p>“git history” history rewriting (experimental) command has been added.</p>
</blockquote>

<p><a href="https://gitlab.com/git-scm/git/-/blob/HEAD/Documentation/RelNotes/2.55.0.adoc">Git v2.55.0</a> went one further, extending it with the <code class="language-plaintext highlighter-rouge">fixup</code> subcommand:</p>
<blockquote>
  <p>“git history” learned “fixup” command.</p>
</blockquote>

<p>Let’s edit our commit using <code class="language-plaintext highlighter-rouge">git history fixup</code>:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># we're on d9aaeea -- the tip of main</span>
git add <span class="nt">-p</span> blah.md
git <span class="nb">history </span>fixup 1660b81
</code></pre></div></div>

<p>And we’re done. With these two commands we’ve done the following:</p>
<ul>
  <li>Squashed the staged changes to <code class="language-plaintext highlighter-rouge">blah.md</code> into the commit <code class="language-plaintext highlighter-rouge">1660b81</code></li>
  <li>Rebased the rest of the branch’s changes relative to the edit</li>
  <li>Rebased any dependent branches, too (similar to <code class="language-plaintext highlighter-rouge">git rebase --update-refs</code>, I think?)</li>
</ul>

<p>In addition to <code class="language-plaintext highlighter-rouge">git history fixup</code>, you can also use <code class="language-plaintext highlighter-rouge">git history reword</code> to rewrite the commit message <em>without</em> faffing about with the interactive rebase process. It’s so much nicer.</p>

<p>Finally, there’s <code class="language-plaintext highlighter-rouge">git history split &lt;commit&gt; [--] [&lt;pathspec&gt;]</code> which is a little more complicated. It takes a commit hash plus an optional pathspec, then opens an interactive session that functions similarly to patch mode (<code class="language-plaintext highlighter-rouge">git add -p</code>). Any hunks that you stage will be moved into a new parent commit of the one chosen by the command.</p>

<p>E.g. let’s say we made a commit that contained a mixture of documentation changes and code changes. We want to split the docs into a new parent commit.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># select all the .md files in the existing commit, this will open a session asking you to choose the hunks to move to a new commit</span>
git <span class="nb">history split </span>1660b81 <span class="nt">--</span> <span class="s1">'*.md'</span>

<span class="c"># accept the .md changes via choosing y/n and we're done!</span>
</code></pre></div></div>

<h2 id="an-old-dog-learning-new-tricks">An old dog learning new tricks?</h2>
<p>Git is taking a leaf out of <a href="https://docs.jj-vcs.dev/latest/"><code class="language-plaintext highlighter-rouge">jj</code> aka <code class="language-plaintext highlighter-rouge">jujutsu</code></a>’s book by upgrading its history editing ergonomics. This is a positive for users regardless of which tool ‘wins’.</p>

<p><strong>Aside</strong>: I’ve been playing with <code class="language-plaintext highlighter-rouge">jj</code> at home, and I’m quietly impressed. I’ll write more on that shortly.</p>

<p><strong>Note</strong>: This blog post was hand-written – I am not a robot 🤖🔫.</p>]]></content><author><name>Mark Simpson</name></author><category term="git" /><category term="history" /><category term="jj" /><summary type="html"><![CDATA[git has some rough edges Git has been around a long time (I’ve been using it since around 2011, where’d the time go?) and doesn’t seem to be going anywhere. The main advantage with git is the flexibility – if you can think of doing something, you probably can do it. The downside is that its cli commands often lack comfort and consistency (and I’m saying that as someone who has a good grasp of its internals and is often the friendly “please help me fix my git disaster” guy).]]></summary></entry><entry><title type="html">Repairing a corrupt jellyfin.db file</title><link href="https://marksimpson82.github.io/blog/2026/07/15/jellyfin-database-repair.html" rel="alternate" type="text/html" title="Repairing a corrupt jellyfin.db file" /><published>2026-07-15T00:00:00+00:00</published><updated>2026-07-15T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2026/07/15/jellyfin-database-repair</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2026/07/15/jellyfin-database-repair.html"><![CDATA[<p>I recently encountered a problem where my Jellyfin media server stopped processing / displaying new media. For my particular case, it was a simple fix.</p>

<p>At the time of writing, my Jellyfin version was: <code class="language-plaintext highlighter-rouge">10.11.11</code></p>

<h2 id="symptom-recently-added-shows-did-not-appear-in-the-ui">Symptom: Recently added shows did not appear in the UI</h2>
<p>The files were in place, and Jellyfin continued to (mostly) function, tracking watch progress and serving existing media. New media did not get picked up, though.</p>

<h2 id="diagnosis">Diagnosis</h2>
<p>I’m running via a Synology NAS with DSM. To investigate, I looked at the Jellyfin logs:</p>
<ul>
  <li>Open DSM container manager</li>
  <li>Open the running Jellyfin container</li>
  <li>Select ‘logs’ and examine the content</li>
</ul>

<p>Your investigation will obviously look a bit different if you’re running Jellyfin via other means, or if you have to <code class="language-plaintext highlighter-rouge">ssh</code> in to see the logs.</p>

<p>In my case, I saw numerous errors like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 11: 'database disk image is malformed'. 
</code></pre></div></div>

<p>Hmm, not too healthy-looking.</p>

<h2 id="jellyfin-version-ymmv">Jellyfin version: YMMV</h2>
<p>Recent versions of Jellyfin moved to a single database file called <code class="language-plaintext highlighter-rouge">jellyfin.db</code>. My version only has the single <code class="language-plaintext highlighter-rouge">.db</code> file, plus some old backup files present in the data directory.</p>

<p>If you’re running an older version, you may have multiple database files to contend with.</p>

<h2 id="fixing-it">Fixing it</h2>
<ul>
  <li>Stop the Jellyfin service</li>
  <li>Open a ssh connection to the jellyfin host</li>
  <li>Navigate to the <code class="language-plaintext highlighter-rouge">jellyfin/config/data</code> directory</li>
  <li>Backup the main <code class="language-plaintext highlighter-rouge">jellyfin.db</code> file via:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">mv jellyfin.db jellyfin.db.corrupt</code></li>
    </ul>
  </li>
  <li>Also take a note of the file permissions</li>
  <li>Backup all other <code class="language-plaintext highlighter-rouge">*.db*</code> files (including any <code class="language-plaintext highlighter-rouge">db-shm</code> and <code class="language-plaintext highlighter-rouge">db-wal</code> files), moving them out of the root data directory to a backup dir</li>
  <li>Run the SQLite dump command to extract the readable data into a SQL file:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">sqlite3 jellyfin.db.corrupt ".recover" | sqlite3 jellyfin.db</code></li>
    </ul>
  </li>
  <li>Import the extracted data into a new database file:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">sqlite3 jellyfin.db &lt; dump.sql</code></li>
    </ul>
  </li>
  <li>Ensure the newly created <code class="language-plaintext highlighter-rouge">jellyfin.db</code> file has the correct ownership and permissions for the Jellyfin user, or whatever your old setup was using
    <ul>
      <li>I had to <code class="language-plaintext highlighter-rouge">chown</code> it, similar to <code class="language-plaintext highlighter-rouge">chown jellyfin:jellyfin jellyfin.db</code></li>
    </ul>
  </li>
  <li>Start the Jellyfin service</li>
  <li>Navigate to the Jellyfin UI using a web browser, then start a media scan and wait a while(tm)</li>
  <li>Confirm that the missing media appears</li>
</ul>

<h2 id="i-really-like-jellyfin">I <em>really</em> like Jellyfin</h2>
<p>This was the first issue I’ve run into after a few years of Jellyfin bliss. Kudos to the maintainers.</p>

<p><strong>Note</strong>: This blog post was hand-written – I am not a robot 🤖🔫. I used Gemini to get direction on running the <code class="language-plaintext highlighter-rouge">sqlite3</code> command, though.</p>]]></content><author><name>Mark Simpson</name></author><category term="dsm" /><category term="jellyfin" /><category term="nas" /><category term="synology" /><summary type="html"><![CDATA[I recently encountered a problem where my Jellyfin media server stopped processing / displaying new media. For my particular case, it was a simple fix.]]></summary></entry><entry><title type="html">On Long COVID</title><link href="https://marksimpson82.github.io/blog/2026/03/19/on-long-covid.html" rel="alternate" type="text/html" title="On Long COVID" /><published>2026-03-19T00:00:00+00:00</published><updated>2026-03-19T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2026/03/19/on-long-covid</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2026/03/19/on-long-covid.html"><![CDATA[<p>I’ve been meaning to write something about long COVID for a while since I’m directly affected, but it’s difficult to summon the energy. I partly want to write this just so I’ve got a record of it. Long COVID is tumultuous and confounding. Sometimes you doubt yourself.</p>

<p>Before I start, here’s an amazingly on-the-money primer on how Chronic Fatigue Syndrome (CFS) is often (mis)perceived by the general public (The Armando Iannucci Shows is a largely forgotten gem):</p>

<!-- Courtesy of embedresponsively.com -->

<div class="responsive-video-container">
    <iframe src="https://www.youtube-nocookie.com/embed/BDhoM_vhIeo" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
  </div>

<p>I should also mention that <a href="https://bmjopenrespres.bmj.com/content/11/1/e001907">long COVID encompasses multiple phenotypes</a>. By this, researchers find there are common sub-groups of long COVID patients who have one or more symptom clusters (e.g. Fatigue, Cardiovascular, Neurological).</p>

<p>The CFS ‘phenotype’ of long COVID closely tracks with CFS itself. Sufferers are often misunderstood or forgotten.</p>

<h2 id="deaths-are-down-but-covid-19-is-not-over">Deaths are down, but COVID-19 is not “over”</h2>
<p>The world’s collective leadership seems to be pretending that COVID is over, and we’ve all moved on.</p>

<p>Firstly, I will acknowledge that COVID is now endemic and no longer represents a major risk to the average person in terms of deaths and hospitalisations. The figures don’t lie in that respect.</p>

<p><img src="/blog/assets/images/2026/03/hospitalised-and-deaths-covid.png" alt="Historical hospitalisation and mortality rates for COVID-19 in England" /></p>

<p>There are several reasons for the drops in death rates:</p>
<ol>
  <li>Many at-risk/infirm people died early in the pandemic, and cannot die again (pithy, but you get the gist – I still miss <a href="https://www.youtube.com/watch?v=nXbEFTv9zr0">John Prine</a> and many other folks who we sadly lost)</li>
  <li>Rapidly improved treatment protocols for high-risk patients (e.g. drugs like <a href="https://www.gov.uk/government/news/world-first-coronavirus-treatment-approved-for-nhs-use-by-government">Dexamethasone</a> and IL-6 Inhibitors, antivirals like Paxlovid etc.)</li>
  <li>From mid-2021 onwards, vaccinations protect against hospitalisation and death (look at that Omicron wave in 2022 compared to 2020!)</li>
  <li>Prior COVID infections also protect against hospitalisation and death</li>
</ol>

<p><strong>Aside</strong>: Most people (falsely!) believe the Omicron variant of COVID was significantly less deadly compared to the original variant, but this is a misconception. Omicron was highly <em>transmissible</em> (it spread freely) and was just as <em>severe</em> as the <strong>original</strong> variant of COVID; it just didn’t feel like it because:</p>
<ol>
  <li>The Delta variant was much nastier</li>
  <li>The population was benefitting from (I sound like a COVID estate agent, sorry) some of the things I’d previously mentioned.</li>
</ol>

<p>It’s not all good news, though. We also know that, amongst other things:</p>
<ul>
  <li>Each successive COVID infection has a chance of causing long COVID</li>
  <li>All-cause mortality increases for a prolonged period after a person is infected with COVID</li>
  <li>COVID infections cause a reduction of grey matter in the brain</li>
</ul>

<p>We also know that so-called “long haulers” are in the millions in most countries. While most recover inside a year, many do not.</p>

<h2 id="my-problems-with-long-covid">My problems with long COVID</h2>
<p>I’ve been infected with COVID twice, and both infections resulted in long COVID with a range of symptoms.</p>

<h3 id="infection-one-april-2020">Infection one: April 2020</h3>
<p>I would’ve been 37 at the time. I weighed around 89 kg (196 lbs) at 6’6 (198 cm). I was pretty fit and strong, could hit a 30 kg weighted pull-up and a 50 kg weighted dip – hardly record-breaking stuff, but good going for me. Around that time, I ran 3 half-marathons in successive weeks for fun. This is all to say, I was fitter than the average Joe. While I’m sure being fit and healthy helped, it did not stop COVID doing a number on me.</p>

<p>I was well-briefed on COVID and was generally pretty careful. I prioritised ventilation, stopped taking public transport &amp; taxis and generally played it safe. I <em>thought</em> the gym was relatively safe, as it was quiet and the big door(tm) at the front was open. This was hubris – I should’ve been even more cautious.</p>

<p>Looking back, we didn’t know a great deal about COVID. The major broadcasters in the UK weren’t mentioning that losing your sense of taste and smell was a symptom. This was a new experience for me, so I did a bit of digging on the web and decided to self-isolate for two weeks. I’m glad I did because I was due to visit my family.</p>

<p>The bout of COVID itself was a non-event. It felt no worse than a cold, plus the aforementioned loss of taste and smell. I was back doing home gym stuff and running immediately afterwards with seemingly no ill effects. Easy.</p>

<h3 id="long-covid-one">Long COVID one</h3>
<p>A few weeks or perhaps a month later (I didn’t keep a diary, sadly), things took an unexpected turn: I was struck down by crippling fatigue. The exhaustion was so all-encompassing that I didn’t even register the headache – the two were entwined, inseparable and crushing.</p>

<p>The only way I can explain the feeling is that you’re a toddler that’s run a marathon, you’ve got the raging abdabs and you’re basically crabby as fuck – you will fall asleep at the drop of a hat. 50% of your cells are already asleep. 25% are in that dozing phase (where deep sleepers are nominally awake, but can have conversations they don’t remember) and the final 25% are technically awake, but in power saving mode and soon to shut down.</p>

<p>There was no known thing called “Long COVID” back then, so I was confused and perturbed in equal measure. My former flatmate had suffered from a prolonged bout of post-viral fatigue after contracting glandular fever, and that was the only thing I could think of that was comparable.</p>

<p>I would clamber out of bed in the mornings and ‘go’ to work (part-time, remote working as a software engineer), but found I couldn’t stay awake long enough to be productive. I was supposed to work two days a week as a temporary gig, but had to split my two days of work into four or five smaller sessions. Even two hours a day was a challenge – it took an age to complete simple tasks. I’d often wake up, ‘work’ for an hour, and then go back to bed, defeated.</p>

<p>Bed is good when you’re ill though, right? In a cruel twist of fate, bed was arguably worse. When lying down, there was a sensation of pressure on my chest. My upper chest ached. The moment I drifted off, I would wake up feeling like I’d not taken a breath for minutes. I didn’t know it at the time, but the symptoms were possibly pericarditis (inflammation of the heart lining) – more on this later.</p>

<p>Anyway, this went on for weeks, and then months. I don’t know how long exactly because, again, I didn’t keep a symptom diary. I saw the GP multiple times in June, so it likely tailed off after that and I recovered.</p>

<h3 id="recovery-one">Recovery one</h3>
<p>The problem with COVID, as we were beginning to find out, is that it’s not a simple respiratory virus. While most people shrug it off, a minority do not.</p>

<p>COVID makes it its business to get into everything. Lungs? Sure. But how about heart, stomach, bowels, brain and the works? It gets into your blood. It even trips up your vascular system.</p>

<p>I felt largely fine by day, but as the months dragged on I still had bouts of intermittent chest pressure/pain and woke up minutes after falling asleep, gasping. I’ve always been a deep sleeper and a non-worrier. I hit the pillow and it’s lights-out, so it was annoying.</p>

<p>Anyway, I noticed something new: heart palpitations. Several times a day, apropos of nothing, my heart would add extra beats. The sensation was very unpleasant: it was like being thumped in the chest. Each time, I’d wait to see if it’d keep on beating. “Unpleasant, but likely benign”, was my self-diagnosis and the GP wasn’t concerned – they had a lot of ground to cover.</p>

<p>I returned to exercise. I ran, I lifted weights. But when I raised my heart-rate, I noticed the palpitations increased in frequency. Exercise was now tied to negative physical effects.</p>

<p>This went on for years, and I finally got a 24 hour heart monitor in 2023 that allowed a cardiologist to diagnose me with intermittent first-degree heart block (along with a bunch of other scary-sounding but ultimately benign conditions). COVID likely was to blame.</p>

<p>As I previously mentioned, myocardial infarctions (heart attacks) occur at an elevated rate in people who’ve recently had COVID. I didn’t have a heart attack, but it’s yet another thing that COVID can cause, and it shows up in every country’s catch-all excess mortality figures.</p>

<h3 id="life-from-2021-2024">Life from 2021-2024</h3>
<p>A lot happened in this period. I quit eeGeo (aka WRLD) – a company that I’d worked for from 2010-2022 – and joined Infinity Works as a senior consultant. This meant travel and in-person working was back on the cards.</p>

<p>After my first bout with long COVID, I took precautions:</p>
<ul>
  <li>I insisted on eating and drinking outside (weather permitting!)</li>
  <li>I got all of my COVID vaccinations/boosters</li>
  <li>I purchased a large supply of N99/FFP3 face masks and wore them when shopping, using public transport, visiting the doctor and for other things I saw as ‘utilitarian’</li>
  <li>I bought an <a href="https://aranet.com/en/home/products/aranet4-home">Aranet4 air quality meter</a> to gauge risk when at work and in unfamiliar client offices (highly recommended).</li>
</ul>

<p>I got some funny looks. As of early 2022 COVID was deemed ‘over’ and a chunk of the population viewed masking as an unpleasant reminder that COVID still existed.</p>

<p>There’s a certain group who assume that because COVID didn’t harm <em>them</em>, being careful with masking / air quality to protect yourself is simply high theatre – a twatty affectation. The calculus changes after you’ve spent months being unable to stay awake, only to wake up, gasping, with an elephant on your chest.</p>

<p>Some nutter tried to remove my mask as I walked out of the shops and was summarily told to go fuck himself. He ranted at me, telling me that “people like me” were the reason his children were home from school. These are the toss-arses you see on Facebook confidently talking about “vaccine injury” because Bev from 3 doors down “definitely knows someone high up at the hospital”, and “it’s all a big cover-up, yeah?”</p>

<p>On the more mundane end of the spectrum, a few people in client offices made snarky remarks and treated me like I was crazy for wearing a mask in a crowded meeting room when the CO₂ hit 1200ppm and continued increasing. Someone said, “you know masks don’t work, right?”, as he thought my N99 mask was a cloth one or had no idea of the differences. No point arguing, though. In my experience, people get over it when you stick to your guns and quietly get on with it.</p>

<p>I didn’t wear a mask 24/7 mind, e.g. I didn’t mask in open-plan areas at work where CO₂ was at reasonable levels.</p>

<p>So anyway, I’d had some unpleasant months and my quality of life was slightly diminished after the heart debacle, but life goes on. Until…</p>

<h3 id="infection-two-september-2024">Infection two: September 2024</h3>
<p>Time flies. I was 41. I’m pretty sure I picked up the COVID infection on the train back from Edinburgh. I’d been on a whisky-tasting tour with a friend and succumbed to pint-mania after an excellent day out. At least I got infected doing something fun rather than buying reduced price fishcakes in Lidl.</p>

<p>On the train back, I broke my rule: I was half-cut and dozed off without putting on my mask. I was rudely woken 20 minutes later by a rude berk with a barking, hacking cough. They were clearly ill but travelled without a mask. I put on my mask and moved carriage but alas, too little, too late.</p>

<p>Unlike my first go around, this COVID infection was hellish. It was like proper flu. If you’ve ever had the proper flu (where you are bed-bound, sweating yet freezing, coughing to the point where your throat is raw and feeling like you’ve been swaddled in a sleeping bag and summarily beaten with hammers), you’ll know what I mean. It lasted two weeks.</p>

<p>Just as suddenly as it struck me down, the clouds cleared. I felt tired and weak after weeks of indolence and eating much of nothing, but not too bad considering.</p>

<p>This time around, I knew there were recommendations for e.g. athletes to take it easy after a COVID infection; I decided gentle walks would suffice, at least until I’d waited three weeks or so. Just as with my first COVID infection, there was a short period of good health afterwards.</p>

<h3 id="non-recovery-two">Non-Recovery two</h3>
<p>Some sequels are occasionally better than the original (The Dark Knight, The Bourne Ultimatum, arguably Terminator 2). This one was straight to DVD, starring a rotund and panting Steven Seagal.</p>

<h4 id="symptoms">Symptoms</h4>
<p>Three to four weeks in, a creeping tiredness set in. I’d started doing some (very!) light jogging at lunchtime and figured I needed to shake off the rust. The round trip to my sandwich shop of choice is roughly 3.5k (around 2.2 miles). I decided to jog downhill and walk back.</p>

<p>Over the course of the week, crushing fatigue duly pressed me into the ground; each day got progressively harder. The chest pain returned. A headache joined the party and never left – my eyes feel like they’re too big for my skull, my sinuses hurt and there’s a band of pain across my forehead. Once again, the headache and the fatigue are usually entwined, one, all-consuming. Worse still, this time sleep was not refreshing – I’d open my eyes in the morning and feel as tired as the previous night. This made the fatigue relentless.</p>

<p>I’ve twice had my eyes tested at the optician to rule out eye-strain as a contributing factor. The dentist is pretty confident that my unerupted wisdom teeth are not causing referred pain, as such pain is rarely bilateral. A brain MRI came back showing that, yes, I have a brain and no, there’s nothing sinister on the scan. Last time I took my blood pressure, it was bang-on 120/60.</p>

<p>The annoying thing with long COVID is that many laboratory and diagnostic tests look normal. Doctors may doubt you (luckily, mine has been supportive). You may even doubt yourself at times. Sometimes I wonder if I just need to pull myself together, but a few hours or days later I am forcefully disavowed of those doubts as I struggle to decant myself from bed. We go round again.</p>

<h4 id="fatigue-fatigue-fatigue">Fatigue, Fatigue, Fatigue</h4>
<p>Here’s where long COVID confounds you. Many people with long COVID <em>can</em> do things in the moment. But, like buying <em>Big TV</em> on tick or drinking 4 pints in an hour, you will pay for it down the line.</p>

<p>I liken this to Wile E. Coyote from <em>The Road Runner</em>: he runs out over the cliff and stands in thin air. Gravity is paused for a little while, but not long.</p>

<p><img src="/blog/assets/images/2026/03/wile-e-coyote.png" alt="Wile E. Coyote hanging in mid-air" /></p>

<p>It’s also a bit like having a malfunctioning battery with no charge level shown. Sometimes the capacity is as expected. Other times, it is flat and you’re unknowingly running on fumes.</p>

<p>Each CFS sufferer has to mind their energy levels on multiple fronts, including:</p>
<ul>
  <li>Physical activity (exercise, walking, standing upright, even sitting upright in bed etc.)</li>
  <li>Cognitive effort (thinking, working, writing – this blog post is depleting my reserves as I type)</li>
  <li>Social (meeting friends, speaking to people)</li>
  <li>General sensory stimulus (being in a loud place, bright lights, etc.)</li>
</ul>

<p>The interplay between these aspects is unpredictable. Sometimes I’m fine, sometimes I do the same thing again and crash. Maybe things were different somehow? Maybe my energy levels were already depleted? Hard to say. As John Lydon once sang (before he started advertising butter), <a href="https://www.youtube.com/watch?v=NJZe_AVtD2c">“Don’t ask me - ‘cause I don’t know”</a></p>

<h4 id="symptom-tracking">Symptom Tracking</h4>
<p>For stat and symptom tracking, I bought myself a <a href="https://www.ebay.co.uk/sch/i.html?_nkw=fitbit+inspire+3">cheap FitBit Inspire 3</a> (you can pick these up refurbished for cheap on eBay - around £30) in early 2025 and also track my HRV/pulse with the free <a href="https://www.makevisible.com/">Visible app</a>. Combined, I can track things like:</p>
<ul>
  <li>Sleep quality and duration (Awake, Light Sleep, REM Sleep, Deep Sleep)</li>
  <li>Resting Heart Rate (RHR)</li>
  <li>Heart Rate Variability (HRV)</li>
</ul>

<p>I can recommend this combination because it can offer insight into your physical state – either warning you that something is off (RHR is high, HRV is low) when you’re unaware you should be taking it easy, or offering confirmation that yes, you are indeed feeling ‘off’. For example, I picked up a viral infection in January and …</p>

<p><img src="/blog/assets/images/2026/03/visible-hrv-rhr.jpg" alt="Visible app showing RHR/HRV changes" /></p>

<p>I have symptoms suggesting <a href="https://www.rdash.nhs.uk/services/long-covid/dysautonomia-in-long-covid/">dysautonomia </a>, too. Sometimes my heart will race when I stand or walk, but not always. I’ve had a heart-rate reading of 130 bpm after a quick shower. My heart-rate peaked at 158 bpm while walking up a hill a few weeks back. Sometimes gentle activity causes spikes. Other days it is normal. If you don’t track, you don’t know.</p>

<h4 id="quality-of-life">Quality of Life</h4>
<p>Here’s a graph from the <a href="https://en.wikipedia.org/wiki/Myalgic_encephalomyelitis/chronic_fatigue_syndrome#Illness_severity">ME/CFS Wiki Page</a> that tells a story (scroll down…):</p>

<p><img src="/blog/assets/images/2026/03/cfs-quality-of-life.png" alt="Health-Related Quality of Life" /></p>

<p>Misery is not a competition, but I want people to understand that dealing with chronic fatigue that permeates most areas of your life is generally a shite experience and I wouldn’t recommend it.</p>

<p>Everyone is affected differently. I’m lucky in that many basics I take for granted aren’t badly affected, e.g.:</p>
<ul>
  <li>Cooking</li>
  <li>Reading for half an hour</li>
  <li>Passively watching TV</li>
  <li>Playing familiar games</li>
  <li>Walking slowly (I try to keep the distances short and my heart-rate low)</li>
</ul>

<h4 id="how-people-perceive-cfs">How people perceive CFS</h4>
<p>Things can look normal to an observer. However, bear in mind that when you meet someone who has chronic fatigue or any hidden illness, you will likely be seeing them on a good day. If they were having a bad day, they’d be at home.</p>

<p>You do not see that person when they’re crashing, sleeping all hours and generally feeling terrible.</p>

<h4 id="some-positives-for-me-at-least">Some positives (for me, at least!)</h4>
<p>Firstly, I’m quite lucky compared to some. Many long haulers cannot get out of bed for very long (or at all), hold a conversation, read a book or smell the literal or metaphorical roses. They exist and not much more.</p>

<p>Fatigue comes in self-moderating waves. It’s self-moderating because when you’re down, you can’t do much of anything. I’ve had good weeks where I felt upbeat for the future and dared dream of working up to a light jog. But I’ve also had many bad weeks where I spent 16 hour stretches in bed feeling wretched. I’m writing this after spending two such days in bed and feeling pretty pissed off about it.</p>

<p>I’m not a stoic by any means. I frequently feel pretty bad about the whole thing, but then I bounce back, Partridge-style. A-Ha! And write annoying blog posts. A-Ha!</p>

<p>Finally, I likely responded to some treatments – though it’s hard to say for sure, as sometimes you just get better by coincidence and the treatment had no effect.</p>

<h4 id="treatments">Treatments</h4>
<p>There’s a lot of experimental treatments flying about and this is a whole other post to make, but in short:</p>
<ul>
  <li><a href="https://en.wikipedia.org/wiki/Low-dose_naltrexone">Low-dose Naltrexone</a> <a href="https://shop.dicksonchemist.co.uk/the-ldn-private-prescription-dept/">via Dickson Chemist</a>
    <ul>
      <li>For sleep and fatigue</li>
    </ul>
  </li>
  <li><a href="https://en.wikipedia.org/wiki/Amitriptyline">Amitriptyline</a> (via NHS), <a href="https://health4all.co.uk/product/vitamin-b2-riboflavin-400mg-capsules/">Vitamin B2, 400 mg</a> and Magnesium (370 mg from Lidl)
    <ul>
      <li>For headaches – this is a fairly standard migraine medication stack</li>
      <li>I unfortunately had to get referred to a neurologist to access this</li>
    </ul>
  </li>
  <li><a href="https://g-niib-uk.com/">G-NiiB (SIM01) Probiotics</a>
    <ul>
      <li>For fatigue. See <a href="https://www.thelancet.com/journals/laninf/article/PIIS1473-3099(23)00685-0/fulltext">this paper</a>, but no idea if it does anything just yet</li>
    </ul>
  </li>
</ul>

<p>For general wellbeing and mildly reducing depression: (this is the useful version of stuff you see plastered on the back of a bus, asking, <em>“Feeling Tired?”</em>, except you’re not getting rinsed for a useless multi-vitamin):</p>
<ul>
  <li><a href="https://nutravita.com/products/vitamin-d3-4000iu-softgel-capsules-400-days-supply">Vitamin D3</a> (4000 IU)
    <ul>
      <li>Vitamin D3 is more easily absorbed by the body</li>
    </ul>
  </li>
  <li><a href="https://nutravita.com/products/omega-3">Omega-3</a>
    <ul>
      <li>The recommendation I read was to go for EPA at around ~60% of the Omega-3 total, and ~40% DHA.</li>
      <li>Take between 1,000 and 1,500 mg of Omega-3 per day (2 or 3 of those caps does the trick, but you may want to start with one cap and ramp up over time)</li>
    </ul>
  </li>
</ul>

<p>There are many potential and promising treatments, but this post is long enough as it is. The main thing is to periodically review the literature and only plump for low-risk and accessible treatments. There are a lot of things I  <em>want</em> to try, but I can’t get access to them via the NHS, and there are no clinical trials in my area.</p>

<p>For me, the combination of LDN and Amitriptyline likely improved my sleep quality. I was around 10 months in when I started taking them, and sleep rapidly became productive and refreshing which was a godsend, as I was depressed and cracking up. My REM sleep metrics also improved a great deal, going from 40% of expected up to more like 80%.</p>

<h4 id="low-dose-naltrexone-dreaming">Low-dose Naltrexone Dreaming</h4>
<p>It’s not all misery. One of LDN’s side-effects is lucid (and often bizarre) dreams, of which I had many. This is proper season 6 of the <em>Sopranos</em> stuff.</p>

<p><strong>Dream</strong>: I walk home in my dream. People are asleep in shop doorways, pulling newspapers over them for warmth. Cyclists spit at me. My Welsh friend’s dog scoots under a bus and is turned into a furry pancake. He begs with me to borrow my phone, as he needs to alert the authorities, but I’m too busy studying a menu in a restaurant window to respond. Dean (the Welshman) stares at the menu, bewildered. “What’s a starter?”, he asks. “It’s like the opposite of a dessert”, I matter-of-factly reply. Also, Dean doesn’t have a dog IRL.</p>

<p><strong>Dream</strong>: I’m on an Eastern European rail holiday with the much-loved, but long-departed socialist <a href="https://en.wikipedia.org/wiki/Harry_Leslie_Smith">Harry-Leslie Smith</a>. I suspect HLS was a stand-in for Alexei Sayle, as I’d read his autobiography around that time (<a href="https://www.theguardian.com/books/2010/oct/16/stalin-ate-homework-alexei-sayle-review">“Stalin Ate My Homework”</a>) which featured a lot of this sort of thing, and very enjoyable it was too.</p>

<p><strong>Dream</strong>: Two of my friends have a dog. The dog goes missing. All friends and acquaintances are gathered and we’re told to systematically comb Dundee. We divide the larger area into parcels of land and each person gets cracking. I am told to search the (non-existent) railway station at Magdalen Green. I don’t bother. They don’t find the dog. I am wracked with guilt.</p>

<p><strong>Dream</strong>: I see a friend (I’ll call them ‘X’) on the street. They’ve been in seclusion. “Good to hear from you”, I said. We make plans to meet up. “Maybe we can invite X along, too?” (I’m asking him to invite himself for some reason!) “Oh no, he’s the reason I’ve been avoiding everyone”, X replies. We do not meet up.</p>

<p><strong>Dream</strong>: A grizzly bear is burgling my parents’ house and threatening us with violence. I try to smooth-talk the bear so that I can fetch our shotgun from an outbuilding and save the day. We don’t have a shotgun or an outbuilding.</p>

<p>This nonsense goes on for months, but I don’t mind it :-)</p>]]></content><author><name>Mark Simpson</name></author><category term="covid" /><category term="long_covid" /><summary type="html"><![CDATA[I’ve been meaning to write something about long COVID for a while since I’m directly affected, but it’s difficult to summon the energy. I partly want to write this just so I’ve got a record of it. Long COVID is tumultuous and confounding. Sometimes you doubt yourself.]]></summary></entry><entry><title type="html">Protecting SSH keys with BitWarden</title><link href="https://marksimpson82.github.io/blog/2025/12/22/bitwarden-vault-ssh-agent-keys.html" rel="alternate" type="text/html" title="Protecting SSH keys with BitWarden" /><published>2025-12-22T00:00:00+00:00</published><updated>2025-12-22T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2025/12/22/bitwarden-vault-ssh-agent-keys</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2025/12/22/bitwarden-vault-ssh-agent-keys.html"><![CDATA[<h2 id="lots-of-hacks-of-late-some-commonality">Lots of hacks of late, some commonality</h2>
<p>I’ve been reading a fair number of post-mortems of late due to the number of <code class="language-plaintext highlighter-rouge">npm</code> supply chain hacks. The hackers often run <a href="https://docs.npmjs.com/cli/v8/using-npm/scripts"><code class="language-plaintext highlighter-rouge">post-install</code> scripts</a> to harvest credentials from the victim’s machine. That is: once you’ve been pwned, the hacker will iterate through a number of directories that commonly store sensitive credentials (e.g. <code class="language-plaintext highlighter-rouge">~/.aws</code>, <code class="language-plaintext highlighter-rouge">~/.ssh</code>, etc.)</p>

<h2 id="by-default-ssh-keys-live-on-the-filesystem">By default, SSH keys live on the filesystem</h2>
<p>If you’ve ever set up a GitHub account or used ssh for anything, you’ll probably be aware of the fact that, by default, both your public <strong>and private</strong> SSH keys are stored on the filesystem, unencrypted.</p>

<p>Yup, <code class="language-plaintext highlighter-rouge">ls ~/.ssh/</code> and you’ll see what I mean.</p>

<h2 id="can-we-do-better">Can we do better?</h2>
<p>Yes. We can store SSH keys in a password manager such as <a href="https://bitwarden.com">BitWarden</a>. BitWarden has its own ssh agent built into its Desktop App (while BitWarden also offers a CLI, I’m not sure it offers SSH key integration like the desktop app).</p>

<p>Once enabled, an attacker cannot trivially hoover up SSH files from our <code class="language-plaintext highlighter-rouge">~/.ssh</code> directory because they no longer live there, in the clear.</p>

<p>The only downside is that you must run BitWarden desktop to enable the ssh keys. It adds a little bit of friction for a bit more security; I think it’s a reasonable tradeoff. If the BitWarden app is open but locked, using an SSH key will cause the app to flash.</p>

<p>If you’re not running BitWarden or fail to unlock the vault, you’ll see something like the following (which isn’t very user-friendly):</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># git auth error example when BitWarden not running / unlocked</span>
<span class="nv">$ </span>git pull
git@github.com: Permission denied <span class="o">(</span>publickey<span class="o">)</span><span class="nb">.</span>

<span class="c"># or something like</span>
<span class="nv">$ </span>ssh-add <span class="nt">-L</span>
Could not open a connection to your authentication agent.
</code></pre></div></div>

<h2 id="short-version-for-windows-users">Short version for Windows users</h2>
<ol>
  <li>Follow the official <a href="https://bitwarden.com/help/ssh-agent/">BitWarden tutorial steps</a></li>
  <li>However, it’ll fail when trying to test the SSH agent via <code class="language-plaintext highlighter-rouge">ssh-add -L</code></li>
  <li>Skip ahead to the tutorial step that details how to change the <code class="language-plaintext highlighter-rouge">core.sshCommand</code></li>
</ol>

<p>It should then work.</p>

<p>The <code class="language-plaintext highlighter-rouge">core.sshCommand</code> bit is currently tucked away under a git commit signing section later in the page, but this configuration step is needed to get the basic BitWarden ssh agent working when using git bash.</p>

<p>I’ve submitted feedback and a suggested fix to the page (on 2025-12-21), so hopefully they’ll re-organise the steps.</p>

<h2 id="longer-version">Longer version</h2>
<p>Again, this is for Windows. I don’t think this affects Mac/Linux users.</p>

<p>The <a href="https://bitwarden.com/help/ssh-agent/">BitWarden SSH documentation</a> has the instructions, but my opinion is the steps are a little out of order. Why? Because if you’re using git bash on Windows, you absolutely <strong>need</strong> to set the following option regardless of whether you’re signing git commits:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git config <span class="nt">--global</span> core.sshCommand <span class="s2">"C:/Windows/System32/OpenSSH/ssh.exe"</span>
</code></pre></div></div>

<p>If you do not set this option, you’ll receive an error message even when BitWarden is running and you’ve entered your master password to unlock the vault:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Could not open a connection to your authentication agent.
</code></pre></div></div>

<p>This is because the default <code class="language-plaintext highlighter-rouge">ssh</code> / <code class="language-plaintext highlighter-rouge">ssh-add</code> executables for git bash on Windows are different from the Windows defaults, and BitWarden is hooking into the OpenSSH versions, not the Git Bash ones.</p>

<p>Let’s use a standard windows cmd prompt and see where our <code class="language-plaintext highlighter-rouge">ssh</code> / <code class="language-plaintext highlighter-rouge">ssh-add</code> binaries live:</p>

<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code># <span class="kd">windows</span> <span class="nb">cmd</span> <span class="o">--</span> <span class="kd">you</span> <span class="kd">can</span> <span class="kd">see</span> <span class="kd">the</span> <span class="kd">OpenSSH</span> <span class="kd">binaries</span> <span class="kd">are</span> <span class="kd">first</span> <span class="k">in</span> <span class="nv">%PATH%</span>
<span class="nb">where</span> <span class="kd">ssh</span>
<span class="kd">C</span>:\Windows\System32\OpenSSH\ssh.exe <span class="o">&lt;-</span> <span class="kd">this</span> <span class="kd">is</span> <span class="kd">first</span> <span class="k">in</span> <span class="nv">%PATH%</span> <span class="kd">and</span> <span class="kd">correct</span>
<span class="kd">C</span>:\Program <span class="kd">Files</span>\Git\usr\bin\ssh.exe 

<span class="nb">where</span> <span class="kd">ssh</span><span class="na">-add
</span><span class="kd">C</span>:\Windows\System32\OpenSSH\ssh<span class="na">-add</span>.exe <span class="o">&lt;-</span> <span class="kd">same</span>
<span class="kd">C</span>:\Program <span class="kd">Files</span>\Git\usr\bin\ssh<span class="na">-add</span>.exe

<span class="kd">ssh</span><span class="na">-add -L  </span># <span class="kd">succeeds</span>
<span class="kd">ssh</span><span class="na">-ed</span><span class="m">25519</span> ... <span class="kd">etc</span>

<span class="kd">git</span> <span class="kd">pull</span>  # <span class="kd">succeeds</span> <span class="kd">as</span> <span class="kd">is</span> <span class="kd">using</span> <span class="kd">the</span> <span class="kd">expected</span> <span class="kd">ssh</span> <span class="kd">binaries</span>
</code></pre></div></div>

<p>However, let’s do the same thing using git bash or similar (e.g. I have a Windows Terminal config that launches <code class="language-plaintext highlighter-rouge">C:\Program Files\Git\bin\bash.exe</code>).</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># git bash -- note: defaults to bundled git-provided ssh/ssh-add!</span>
<span class="nv">$ </span>which ssh
/usr/bin/ssh  <span class="c"># really C:\Program Files\Git\usr\bin</span>

<span class="nv">$ </span>which ssh-add
/usr/bin/ssh-add  <span class="c"># really C:\Program Files\Git\usr\bin</span>

<span class="nv">$ </span>ssh-add <span class="nt">-L</span>  <span class="c"># fails because it's using the wrong ssh binary</span>
Could not open a connection to your authentication agent.

<span class="nv">$ </span>git pull  <span class="c"># fails because using the wrong ssh binaries</span>
</code></pre></div></div>

<p>Running the <code class="language-plaintext highlighter-rouge">git config</code> line above (or manually editing the <code class="language-plaintext highlighter-rouge">~/.gitconfig</code> setting) fixes the problem and tells git where to find the correct ssh binaries which play nice with BitWarden.</p>

<p>After making the config fix, I could run <code class="language-plaintext highlighter-rouge">ssh-add -L</code> and also <code class="language-plaintext highlighter-rouge">git pull/fetch/push</code> as expected.</p>]]></content><author><name>Mark Simpson</name></author><category term="bitwarden" /><category term="ssh" /><category term="security" /><summary type="html"><![CDATA[Lots of hacks of late, some commonality I’ve been reading a fair number of post-mortems of late due to the number of npm supply chain hacks. The hackers often run post-install scripts to harvest credentials from the victim’s machine. That is: once you’ve been pwned, the hacker will iterate through a number of directories that commonly store sensitive credentials (e.g. ~/.aws, ~/.ssh, etc.)]]></summary></entry><entry><title type="html">Airplay on Windows with TuneBlade</title><link href="https://marksimpson82.github.io/blog/2025/12/21/tune-blade-windows-air-play.html" rel="alternate" type="text/html" title="Airplay on Windows with TuneBlade" /><published>2025-12-21T00:00:00+00:00</published><updated>2025-12-21T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2025/12/21/tune-blade-windows-air-play</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2025/12/21/tune-blade-windows-air-play.html"><![CDATA[<p>I’ve got a stereo with a <a href="https://uk.yamaha.com/en/audio/home-audio/products/wireless-streaming-amplifiers/wxc-50/">Yamaha WXC-50</a> streaming setup. It works great when you’re using a Mac, as you can use <a href="https://en.wikipedia.org/wiki/AirPlay">AirPlay</a> to stream audio directly over Wifi. This setup is seamless – you choose your Mac’s audio output as “LivingRoom” (or whatever you called your streaming receiver) and boom, you can stream your system audio to your stereo. No apps, no drama. Computer -&gt; WXC-50 -&gt; Stereo. Done!</p>

<p>When you’re on Windows, the suck factor is much higher because Windows doesn’t natively support AirPlay. Le sigh. I was reduced to a bunch of different (and often lesser) options:</p>

<h2 id="option-use-the-wxc-50s-built-in-web-app">Option: Use the WXC-50’s built-in web app</h2>
<p>Yeah, you can use this to play mp3s and whatnot, but it’s borderline unusable.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><img src="/blog/assets/images/2025/12/yamaha_wxc50_oh_dear.jpg" alt="The WXC-50 web interface is borderline unusable" /></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center"><em>A decidedly odd interface</em></td>
    </tr>
  </tbody>
</table>

<h2 id="option-use-yamaha-musiccast-app">Option: Use Yamaha MusicCast app</h2>
<p>The <a href="https://uk.yamaha.com/en/audio/home-audio/explore/musiccast/">Yamaha Music Cast</a> Android app is functional but clunky. It’s only a feasible option if you’re hosting your music in a location that is accessible via the network. In my case, I now have some of my music on my Synology NAS with discovery/indexing turned on. This means I can play music via the MusicCast Android app, but:</p>

<ol>
  <li>It’s clunky</li>
  <li>It doesn’t let me stream my Windows PC audio to my receiver, as that’s not what it was designed to do</li>
</ol>

<p>If I’m sitting at my PC, I don’t want to use my phone to navigate directories and pick songs. I want to play my music using foorbar2000 or whatever and hear my PC audio output.</p>

<h2 id="option-use-integrations-in-spotify-or-other-apps">Option: Use integrations in Spotify or other apps</h2>
<p>Like MusicCast, it works for certain use-cases but it doesn’t really suit my needs. I can’t play my own MP3s. I can’t stream my PC audio to my stereo.</p>

<h2 id="option-run-a-cable-from-the-pc-sound-card-to-the-stereo">Option: Run a cable from the PC sound card to the stereo</h2>
<p>No. I’ve got enough cables around the place, thanks.</p>

<h2 id="solution-tuneblade">Solution: <a href="https://www.tuneblade.com/">TuneBlade</a></h2>
<p>I honestly feel like a bit of a dope, as <a href="https://www.tuneblade.com/">TuneBlade</a> has been around for a while and is an AirPlay-compatible implementation for Windows that can stream to receivers. A license is a mere £8 – great value!</p>

<p>Steps:</p>
<ol>
  <li>Install TuneBlade</li>
  <li>Select your AirPlay-compatible device</li>
  <li>Reduce the audio delay down to sub-second (default is a 2s delay) to reduce lag</li>
  <li>Play some music</li>
</ol>

<p>That’s all there is to it! I can now stream my Windows PC audio to my Yamaha WXC-50 receiver. It’s not <em>quite</em> as slick as using a Mac, but it’s close.</p>]]></content><author><name>Mark Simpson</name></author><category term="windows" /><category term="airplay" /><category term="yamaha" /><category term="wxc50" /><summary type="html"><![CDATA[I’ve got a stereo with a Yamaha WXC-50 streaming setup. It works great when you’re using a Mac, as you can use AirPlay to stream audio directly over Wifi. This setup is seamless – you choose your Mac’s audio output as “LivingRoom” (or whatever you called your streaming receiver) and boom, you can stream your system audio to your stereo. No apps, no drama. Computer -&gt; WXC-50 -&gt; Stereo. Done!]]></summary></entry><entry><title type="html">Fixing AMD 7800X3D YouTube Crashes</title><link href="https://marksimpson82.github.io/blog/2025/11/22/amd-7800x3d-idle-crash.html" rel="alternate" type="text/html" title="Fixing AMD 7800X3D YouTube Crashes" /><published>2025-11-22T00:00:00+00:00</published><updated>2025-11-22T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2025/11/22/amd-7800x3d-idle-crash</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2025/11/22/amd-7800x3d-idle-crash.html"><![CDATA[<p><strong>Note</strong>: I used <a href="https://gemini.google.com/app">Gemini</a> when researching and solving the problem. However, I did <strong>not</strong> use AI to write this blog post. My voice is my own!</p>

<h2 id="7800x3d-and-youtube-instability">7800X3D and YouTube Instability</h2>
<p>This is a quick post about troubleshooting <a href="https://www.amd.com/en/products/processors/desktops/ryzen/7000-series/amd-ryzen-7-7800x3d.html">AMD 7800X3D CPU</a> crashes that occurred when watching YouTube, and YouTube only!</p>

<p>tl;dr: Try increasing the <code class="language-plaintext highlighter-rouge">CPU VDDCR_SOC Voltage</code> in the BIOS. I bumped it from default to 1.1v.</p>

<h2 id="new-pc">New PC</h2>
<p>Some of my earliest PC builds used AMD CPUs (opterons, bartons, whateverons), but my last couple of PCs have used Intel &amp; NVIDIA components.</p>

<p>My last PC was an Intel E8400 paired with an NVIDIA 1060 GTX (a workhorse!) which I stuck with for a long time due to the insanity of GPU pricing. It was rock-solid. I have had a few tempramental NVIDIA cards over the years, but nothing too bad.</p>

<p>I recently put together a new gaming PC with the following specs, and switched back to AMD:</p>

<table>
  <thead>
    <tr>
      <th>Type</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>CPU</td>
      <td>AMD Ryzen 7 7800X3D</td>
    </tr>
    <tr>
      <td>CPU Cooler</td>
      <td>Thermalright Phantom Spirit 120 SE</td>
    </tr>
    <tr>
      <td>Motherboard</td>
      <td>Gigabyte B650 PLUS</td>
    </tr>
    <tr>
      <td>PSU</td>
      <td>Corsair 850W ATX - RM850x</td>
    </tr>
    <tr>
      <td>Memory</td>
      <td>Silicon Power XPOWER Zenith Gaming 32 GB DDR5-6000 CL30</td>
    </tr>
    <tr>
      <td>HDD</td>
      <td>Silicon Power UD90 2 TB M.2-2280 PCIe 4.0 X4 NVME</td>
    </tr>
    <tr>
      <td>GPU</td>
      <td>Sapphire PULSE Radeon RX 9060 XT 16 GB Video Card</td>
    </tr>
  </tbody>
</table>

<p>This is notable because it’s the first time I’ve run an AMD CPU since around the year 2010.</p>

<p><strong>Aside</strong>: I got 50% off the PSU by buying an official Corsair refurb product and probably 30% off the CPU by buying OEM from eBay. There are bargains to be had, you just need to test them carefully. I tested the PSU in my old PC build to minimise the risk of frying my new components.</p>

<h2 id="new-pc-new-problems">New PC, New Problems</h2>
<p>Here’s where things get irritating! Gaming? Rock-solid. Prime 95? Rock-solid. Everything else? Rock-solid. Watching YouTube videos? Not so much.</p>

<p>When watching YouTube videos, the PC reset itself multiple times per week. Sometimes I’d go a day or two between resets, but not much longer. The screen would switch off momentarily, then it’d boot back into Windows. Grrr!</p>

<h2 id="gathering-information">Gathering Information</h2>
<p>Event Viewer showed a Kernel-Power event with Level: <strong>Critical</strong>.</p>
<blockquote>
  <p>The system has rebooted without cleanly shutting down first. This error could be caused if the system stopped responding, crashed, or lost power unexpectedly.</p>
</blockquote>

<p>The interesting thing is that when under consistent load, the PC behaved itself and never once crashed or reset.</p>

<p>I wondered if perhaps there may be some tell-tale signs in logging information, so I installed <a href="https://www.hwinfo.com/">HWiNFO for Windows</a> which is a wonderful and lightweight hardware monitoring tool that can log an exhaustive list of metrics to a file.</p>

<p>I then captured a couple of crashes in the wild and graphed a bunch of metrics (temperatures, voltages, load values etc.) in the moments leading up to the crash. The problem is that it showed nothing of note. There was no smoking gun. Each crash seemed to show a spike in CPU usage a few seconds before the crash, then a reduction in load.</p>

<p>I originally did this by hand. I then fed the CSV file into <a href="https://gemini.google.com/app">Gemini 3</a> (I had a free month to mess with it) and asked it perform an analysis. I did not mention my own findings. It agreed with my analysis – going by the evidence, it was not a temperature or voltage issue.</p>

<h2 id="ok-so--what-now">OK, so … what now?</h2>
<p>I <del>googled</del> <a href="https://kagi.com/">Kagi</a> around and also asked Gemini for suggestions, and it came up with some plausible ideas, but nothing worked.</p>

<p>I’d already tried the following:</p>
<ul>
  <li>❌ Updating the Motherboard BIOS (did this after building the system)</li>
  <li>❌ Ensuring Precision Boost Overdrive (PBO) was disabled (it was)</li>
</ul>

<p>Gemini also suggested the following, but it didn’t help:</p>
<ul>
  <li>❌ Change “Power Supply Idle Control” to “Typical Current Idle”</li>
</ul>

<p>I searched around a bit more and found some new avenues, then returned to Gemini.</p>

<h2 id="eureka">Eureka</h2>
<p>The turning point was when I told Gemini that YouTube was always in the mix.</p>

<blockquote>
  <p>Me: The crash I described only happens when watching YouTube videos. Does that change your thinking on anything?</p>
</blockquote>

<blockquote>
  <p>Gemini: This detail is extremely significant. The fact that it happens specifically during YouTube playback (a light, fluctuating workload) strongly reinforces the “Low-Load Instability” diagnosis, but it also points to a very specific culprit: Hardware Acceleration and the SoC.</p>
</blockquote>

<p>Gemini then suggested making a bunch of software changes, such as:</p>
<ul>
  <li>Disabling hardware acceleration in Firefox</li>
  <li>Disabling Multi-Plane Overview (MPO)</li>
</ul>

<p>I thought these were pretty wide of the mark, as A) I have a GPU and intend to use it B) disabling MPO can affect FreeSync and other important functionality.</p>

<p>✅ Instead, I went with increasing the SoC voltage a notch, from 1.0v -&gt; 1.1v.</p>

<p>The results were pretty much instant (also: the “View Reliability History” tool is super useful and I had no idea it was a thing):</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><img src="/blog/assets/images/2025/12/system_reliability.jpg" alt="System Reliability Dialog showing the crashing stopping" /></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center"><em>No more crashes after the 15th</em></td>
    </tr>
  </tbody>
</table>

<h2 id="other-thoughts-re-amdintelnvidia">Other Thoughts re: AMD/Intel/NVIDIA</h2>
<p>I was not particularly impressed with my return to AMD CPU land! It was a pain in the arse, and a non-technical user would <strong>not</strong> figure this out – it would be an RMA.</p>

<p>Furthermore, AMD’s GPU software (Adrenaline) feels unimpressive and bloated. NVIDIA’s control panel has been a laggy POS for a decade+ at this point, but at least I don’t have to install the kitchen sink just to enable anti-lag mode and configure G-Sync.</p>

<p>No AMD, I don’t want your AI me-too chat bot in my GPU driver suite, thanks.</p>]]></content><author><name>Mark Simpson</name></author><category term="amd" /><category term="7800X3D" /><category term="crash" /><category term="youtube" /><summary type="html"><![CDATA[Note: I used Gemini when researching and solving the problem. However, I did not use AI to write this blog post. My voice is my own!]]></summary></entry><entry><title type="html">Windows 10 support ending? Linux Mint for my parents</title><link href="https://marksimpson82.github.io/blog/2025/10/27/linux-mint.html" rel="alternate" type="text/html" title="Windows 10 support ending? Linux Mint for my parents" /><published>2025-10-27T00:00:00+00:00</published><updated>2025-10-27T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2025/10/27/linux-mint</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2025/10/27/linux-mint.html"><![CDATA[<p>As a long-in-the-tooth software engineer, I’m getting to the end of my tether with Windows. I’ve lived through (and used) practically every version of Windows from 3.1 through to Windows 11, and the direction of travel is negative. Into the sewer, even.</p>

<p>I’ve used numerous versions as concerned citizen, student, adult and alleged professional:</p>
<ul>
  <li>Windows 3.1</li>
  <li>Windows 95</li>
  <li>Windows 98</li>
  <li>Windows 2k</li>
  <li>Windows ME (lol)</li>
  <li>Windows XP</li>
  <li>Windows Vista</li>
  <li>Windows 7 (I didn’t even bother with 8)</li>
  <li>Windows 10</li>
  <li>Windows 11</li>
</ul>

<p>There were a few high points in there. Windows 7 is up there, as is 98 and 2k (which I used because the mouse acceleration promised to make me a God tier FPS player – it did not). Hell, even the last years of Vista seemed vaguely pleasant. After the relative simplicity of Windows 7 it’s been downhill all the way, though.</p>

<h2 id="the-slow-death-of-windows-10">The Slow Death of Windows 10</h2>
<p>My memory of the first revision of Windows 10 is unreliable. What I can say for sure is that each new revision of Windows 10 came crammed full of bloat, advertising, spyware and even bullshit like pre-installed LinkedIn apps. I’ve got a PowerShell script (plus some manual instructions) to run after installing Windows, and each time I have to install Windows, the list needs updated.</p>

<p>Here’s a summary:</p>

<p>How do I…</p>
<ul>
  <li>Avoid creating a Microsoft account when installing the OS?</li>
  <li>Remove adverts from my lock screen?</li>
  <li>Remove celebrity news from my taskbar?</li>
  <li>Delete all of the bundled bullshit apps and bloatware?</li>
  <li>Uninstall all the XBox shit?</li>
  <li>Disable Cortana?</li>
  <li>Remove Bing search integration from the start menu?</li>
  <li>Lock down all of the privacy settings?</li>
</ul>

<p>This is hostile to the nth degree and completely direspects the user.</p>

<h2 id="windows-11---even-worse">Windows 11 - Even Worse</h2>
<p>I ‘upgraded’ to Windows 11 a few years back and it was just as bad, if not worse. There were a few more usability issues around context menus and UI consistency (how many flavours of menus are there now? When doing any kind of involved configuration, you have to drop back to Windows 95 era menus anyway!)</p>

<p>I binned it and went back to Windows 10.</p>

<h2 id="why-stay-on-windows-and-can-we-use-linux">Why Stay on Windows, and can we use Linux?</h2>
<p>I use MacOS at work and the only thing keeping me on Windows on my desktop is gaming. However, Linux is becoming more and more of a realistic player when it comes to gaming. I play FPS games that often have kernel anti-cheat (which don’t tend to work with non-Windows OSes), so there’s still a bit of a gap.</p>

<p>However, my parents have been running Windows since the 7 days, and guess what, they just tend to use their PC for web browsing and not much else. Could I chuck Linux on it and save them the Windows 11 experience? Also, many users will be running old hardware with no <a href="https://learn.microsoft.com/en-us/windows/security/hardware-security/tpm/trusted-platform-module-overview">Trusted Platform Module (TPM)</a>.</p>

<p>If you don’t have a motherboard with TPM, you cannot install Windows 11 (if you’re unsure, go check your BIOS – my 2017 motherboard had one, but it was disabled by default). Anyway, if you just want to browse the web, old hardware shouldn’t be thrown away to satisfy the spyware gods! If the hardware is capable, let’s use Linux!</p>

<h2 id="fedora-in-the-year-2018">Fedora in the Year 2018</h2>
<p>I’ve had a few aborted attempts at the Linux Desktop. The last one was ~2018 using Fedora for AI work stuff, and it was still a pain in the arse. My install ended with a bricked machine.</p>

<p>I installed the OS and spent ages dicking about getting the NVIDIA GPU drivers installed correctly. I then customised the OS and programs to my liking. A few hours later, I didn’t pin my NVIDIA driver package and bricked the machine doing <code class="language-plaintext highlighter-rouge">dnf upgrade</code> and wasted the best part of a day. Oops. Anyway, my point is it was rough around the edges.</p>

<h2 id="linux-mint-in-the-year-2025">Linux Mint in the Year 2025</h2>
<p>This time around, I plumped for <a href="https://linuxmint.com/">Linux Mint</a>. I followed the instructions, created a bootable USB pen drive and got stuck in. 15 minutes later I had a working Linux install with fully updated NVIDIA drivers, rolling backups and all of the basics working. Firefox was ready to go with uBlock Origin and audio worked perfectly. I didn’t notice any jank, advertising or spyware being slyly included, either (fancy that).</p>

<p>All in all, a very smooth experience. If you have friends or relatives that are in the same situation, it’s well worth a go. I can’t guarantee that your games will work with steam or <a href="https://www.winehq.org/">Wine</a>, but if you’re just browsing it’ll do the trick.</p>

<p>Might be worth trying a dual boot soon on my home desktop, too.</p>]]></content><author><name>Mark Simpson</name></author><category term="linux" /><category term="windows" /><category term="tpm" /><summary type="html"><![CDATA[As a long-in-the-tooth software engineer, I’m getting to the end of my tether with Windows. I’ve lived through (and used) practically every version of Windows from 3.1 through to Windows 11, and the direction of travel is negative. Into the sewer, even.]]></summary></entry><entry><title type="html">Making my Synology DS224+ NAS hibernate</title><link href="https://marksimpson82.github.io/blog/2025/10/27/synology-nas-hibernate.html" rel="alternate" type="text/html" title="Making my Synology DS224+ NAS hibernate" /><published>2025-10-27T00:00:00+00:00</published><updated>2025-10-27T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2025/10/27/synology-nas-hibernate</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2025/10/27/synology-nas-hibernate.html"><![CDATA[<h2 id="my-old-setup-intel-nuc-jellyfin-and-a-roku-stick">My old setup: Intel NUC, Jellyfin and a Roku stick</h2>
<p>I’ve run an Intel NUC with the <a href="https://en.wikipedia.org/wiki/OpenMediaVault">Open Media Vault (OMV)</a> distro and an external USB HDD since 2022. For my media server software, I started out with Plex and used it for a few years, but ultimately tired of the constant drip of stealthy on-by-default social features, live TV and so on. I swapped from Plex to Jellyfin and have been very happy with my choice. Jellyfin feels like a much more pleasant experience where my preferences are respected.</p>

<p>The final piece in the puzzle was a Roku 4k stick which meant I could disconnected my smart TV from the Internet. The Roku stick has superior Wi-Fi connectivity which meant I could ditch another Ethernet cable. While I do have concerns about Roku’s business ethics and approach to data collection, it’s better than relying on my archaic LG smart TV with its laggy interface and frequent crashes.</p>

<p>The NUC hardware was capable for transcoding, but I wasn’t having much fun on a few fronts:</p>
<ol>
  <li>Maintaining the OMV install (a bit of a, “don’t touch it” situation due to the configuration via GUIs)</li>
  <li>The NUC’s limited connectivity options meant I was in a bit of a storage dead-end.</li>
</ol>

<p>In short, I had a media PC with no Network Addressable Storage. My options were:</p>
<ol>
  <li>Buy a Direct Attached Storage (DAS) solution and run the NUC 24/7, turning it into a combination of poor man’s NAS and a media server</li>
  <li>Buy a Network Addressable Storage (NAS), but retain the NUC as a dedicated media server</li>
  <li>Buy a dedicated NAS and run the media server software on it</li>
</ol>

<p>I chose option 3, meaning I could retire my NUC.</p>

<h2 id="moving-to-the-ds224-nas">Moving to the DS224+ NAS</h2>
<p>I purchased a Synology DS224+ NAS and chucked a second-hand <code class="language-plaintext highlighter-rouge">WD HUH721212ALE600</code> 12TB drive in it on account of <a href="https://www.backblaze.com/blog/backblaze-drive-stats-for-q2-2025/">its excellent longevity</a> record (I also added a 12TB drive into my desktop PC for backup).</p>

<p>The DS224+ hardware is expensive for what it is, but it’s sufficient to run Jellyfin and direct-play 1080p x265/HEVC media.</p>

<h3 id="hardware-and-performance">Hardware and performance</h3>
<p>The hardware does struggle with transcoding at higher resolutions, but I avoid this by:</p>
<ol>
  <li>Favouring 1080p HEVC media</li>
  <li>Using bog-standard <a href="https://en.wikipedia.org/wiki/SubRip">SubRip</a> subtitles</li>
</ol>

<p>If you want to play 4k media, use PGS subtitles or transcode from other formats, I would recommend beefier hardware and/or a dedicated media server. E.g., when I tried to play a 4k AV1 file, it resulted in a locked system and then an error message. Oops!</p>

<h3 id="adding-cheap-unofficial-ram">Adding cheap, unofficial RAM</h3>
<p>The DS224+ comes with a meagre 2GB of RAM. I added another 4GB via <a href="https://www.mrmemory.co.uk/memory-ram-upgrades/synology/nas/ds224_">Mr. Memory</a> for £13 rather than the obscene official Synology RAM prices (£92 at the time of writing!)</p>

<p><strong>Note</strong>: While the official documents claim that 4GB is the maximum supported DIMM size, various posts on reddit claim that larger DIMMS will work.</p>

<h3 id="the-synology-os-diskstation-manager-dsm">The Synology OS: DiskStation Manager (DSM)</h3>
<p>Synology devices use <a href="https://en.wikipedia.org/wiki/Synology#DiskStation_Manager">DSM</a>: a Linux-derived OS that’s chiefly configured via web browser (depending on your preferences, this is either a pro or a con). While you can ssh into your NAS, don’t expect to find everything you expect from a Linux distro. E.g. when I was debugging the lack of hibernation, many standard unix tools were missing.</p>

<h3 id="dsms-file-browsing-and-shares-are-a-bit-weird">DSM’s file browsing and shares are a bit weird</h3>
<p>DSM has a package called <code class="language-plaintext highlighter-rouge">File Station</code> where you can browse your files. However, when you’ve got multiple drives and volumes, the association between volumes and folders/files is hidden. This information is available via right-clicking a file/folder and choosing “properties”, but it’s tucked away out of view.</p>

<p>To create a shared folder and choose the containing volume, you need to browse to <code class="language-plaintext highlighter-rouge">Control Panel</code> &gt; <code class="language-plaintext highlighter-rouge">Shared Folder</code>.</p>

<p>E.g. I have a few shared folders set up as follows:</p>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Volume</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Media</td>
      <td>Volume 1 (12TB IDE)</td>
      <td>Media files (TV Shows, Films, Music)</td>
    </tr>
    <tr>
      <td>Docker</td>
      <td>Volume 2 (400GB SSD)</td>
      <td>Docker config, image files, jellyfin config etc</td>
    </tr>
  </tbody>
</table>

<h3 id="adding-an-ssd">Adding an SSD</h3>
<p>The DS224+ has two drive bays. I had an old 400GB SSD laying around, so I installed it as a second drive. My thinking was, “let’s move the OS and packages onto the SSD to reduce the disk chuntering”, but DSM doesn’t work like that.</p>

<p>Certain parts of the OS and its base packages are installed on all volumes. This is done for good reason: it means you can swap disks in and out, and you won’t brick the OS.</p>

<p>You can see the installation location of packages via browsing to <code class="language-plaintext highlighter-rouge">Package Center</code> &gt; <code class="language-plaintext highlighter-rouge">Installed</code> and selecting a package. Base packages will be listed as, “Installed volume: System partition”.</p>

<p>However, you <em>can</em> make your SSD the default installation location for custom packages to reduce the usage of your fatter, noisier disks. This is under <code class="language-plaintext highlighter-rouge">Package Center</code> &gt; <code class="language-plaintext highlighter-rouge">Settings</code> &gt; <code class="language-plaintext highlighter-rouge">Default Volume</code>.</p>

<p>I would strongly advise getting your drive/volume choices sorted out before you install and configure packages, as while tutorials exist for migrating packages between volumes, it’s not fool-proof or comprehensive. DSM uses symlinks for various pieces of configuration and it’s easy to break.</p>

<p>If you do find yourself needing to move packages between volumes, I would recommend doing the following:</p>

<ol>
  <li>Back up the configuration data</li>
  <li>Manually uninstall the package from the old volume</li>
  <li>Re-install the package on the new volume</li>
  <li>Restore the configuration data to the new volume</li>
</ol>

<p>Like I said, it’s much easier to get your volumes and their purpose configured up-front and save yourself the hassle.</p>

<h3 id="installing-jellyfin">Installing Jellyfin</h3>
<p>Installing Jellyfin was straightforward:</p>

<ol>
  <li>Using <code class="language-plaintext highlighter-rouge">Control Panel</code> &gt; <code class="language-plaintext highlighter-rouge">Shared Folder</code>, create shares for docker and your media on the appropriate volumes (already covered above)</li>
  <li>Install and Open <a href="https://www.synology.com/en-br/dsm/feature/docker">Container Manager</a></li>
  <li>Click the <code class="language-plaintext highlighter-rouge">Projects</code> tab</li>
  <li>Add a project called “Jellyfin”, and the path should be using the fastest/quietest drive volume (for me: <code class="language-plaintext highlighter-rouge">/volume2/docker/jellyfin</code>)</li>
  <li>Add a docker-compose.yaml (while you can naively just start a container based on the <code class="language-plaintext highlighter-rouge">jellyfin/jellyfin</code> image, there’s a load of implicit config involved that won’t be reproducible – better to just use a docker-compose file from the start).</li>
</ol>

<p>A sample docker-compose file:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
  <span class="na">jellyfin</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">jellyfin/jellyfin</span>
    <span class="na">container_name</span><span class="pi">:</span> <span class="s">jellyfin</span>
    <span class="na">healthcheck</span><span class="pi">:</span>
      <span class="c1"># disable health check to avoid excess disk activity</span>
      <span class="na">disable</span><span class="pi">:</span> <span class="kc">true</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="c1"># standard ports</span>
      <span class="pi">-</span> <span class="s">8096:8096/tcp</span>
      <span class="pi">-</span> <span class="s">7359:7359/udp</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="c1"># config &amp; cache uses a fast/quiet SSD if possible</span>
      <span class="pi">-</span> <span class="s">/volume2/docker/jellyfin/config:/config:rw</span>
      <span class="pi">-</span> <span class="s">/volume2/docker/jellyfin/cache:/cache:rw</span>
      <span class="c1"># media is stored on my fat 12TB spinning rusk disk</span>
      <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">bind</span>
        <span class="na">source</span><span class="pi">:</span> <span class="s">/volume1/media</span>
        <span class="na">target</span><span class="pi">:</span> <span class="s">/media</span>
        <span class="na">read_only</span><span class="pi">:</span> <span class="kc">true</span>
    <span class="na">restart</span><span class="pi">:</span> <span class="s1">'</span><span class="s">unless-stopped'</span>    
</code></pre></div></div>

<h3 id="fixing-hibernation">Fixing Hibernation</h3>
<p>There was one fly in the ointment: my DS224+ refused to hibernate, and this seems to be a common problem. My intial install used the 12TB IDE disk for everything, and it’s <em>loud</em>. Not good.</p>

<p>Also, with all drives running, we’re probably talking an extra 10-15W of power usage 24/7. At 25p/KWh that adds up to £2-3 per month, plus the extra wear and tear on the drives.</p>

<p>I started working through the <a href="https://kb.synology.com/en-uk/DSM/tutorial/What_stops_my_Synology_NAS_from_entering_System_Hibernation">official documentation / checklist of items that may prevent hibernation</a> but it’s a bit of a kitchen sink affair.</p>

<p>Here’s what worked for me (though your mileage may vary):</p>
<ol>
  <li>Move packages (ContainerManager) and config (Docker) to my SSD</li>
  <li>Disable Jellyfin’s docker healthcheck via <code class="language-plaintext highlighter-rouge">healthcheck: disable</code></li>
  <li>Stop all non-essential packages</li>
  <li>Disable the SSH service via <code class="language-plaintext highlighter-rouge">Control Panel</code> &gt; <code class="language-plaintext highlighter-rouge">Terminal &amp; SNMP</code> &gt; <code class="language-plaintext highlighter-rouge">Terminal</code> &gt; uncheck <code class="language-plaintext highlighter-rouge">Enable SSH Service</code></li>
  <li>Disable the bonjour service via <code class="language-plaintext highlighter-rouge">Control Panel</code> &gt; <code class="language-plaintext highlighter-rouge">File Services</code> &gt; <code class="language-plaintext highlighter-rouge">Bonjour</code> &gt; uncheck <code class="language-plaintext highlighter-rouge">Enable Bonjour</code></li>
  <li>Unmap any network drives (I had my NAS mapped via my desktop PC)</li>
  <li>Configure tasks to run less frequently via this <a href="https://www.reddit.com/r/synology/comments/10cpbqd/making_disk_hibernation_work_on_synology_dsm_7/">reddit thread</a> (I moved some of the daily tasks to run weekly and stopped there).</li>
</ol>

<p>After running through these steps, my NAS now hibernates after idling for a while. Success!</p>

<p><strong>Note</strong>: It <em>does</em> take ~30 seconds to wake up and become interactive. If that’s too sluggish for you, then maybe just leave it running 24/7.</p>]]></content><author><name>Mark Simpson</name></author><category term="dsm" /><category term="synology" /><category term="nas" /><summary type="html"><![CDATA[My old setup: Intel NUC, Jellyfin and a Roku stick I’ve run an Intel NUC with the Open Media Vault (OMV) distro and an external USB HDD since 2022. For my media server software, I started out with Plex and used it for a few years, but ultimately tired of the constant drip of stealthy on-by-default social features, live TV and so on. I swapped from Plex to Jellyfin and have been very happy with my choice. Jellyfin feels like a much more pleasant experience where my preferences are respected.]]></summary></entry></feed>