<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <atom:link href="https://admin.brennt.net/rss.xml" rel="self" type="application/rss+xml"/>
    <title>Feuerfest</title>
    <link>https://admin.brennt.net</link>
    <description>Linux, Automation, Cooking</description>
    <lastBuildDate>Wed, 07 Oct 2026 02:48:53 +0200</lastBuildDate>
    <item>
      <title>Is Microsoft sabotaging the VLC project on purpose?</title>
      <link>https://admin.brennt.net/is-microsoft-sabotaging-the-vlc-project-on-purpose</link>
      <image/>
      <description>&lt;p&gt;Yesterday I learned something which left in quite a bit of shock.&lt;/p&gt;
&lt;p&gt;I have noticed that it takes a considerable amount of time (like 30 seconds) for the video to start playing when I watch movies stored on my NAS from my Windows 10 gaming PC. And stupid me was really searching for any error in my network, my NAS, etc. As VLC worked fine for years. Never ever would I have suspected that Microsoft's corporate policy is likely to blame.&lt;/p&gt;
&lt;p&gt;Huh? What?&lt;/p&gt;
&lt;h2&gt;Why VLC suddenly takes 30 seconds to start (and no it is NOT VLC's fault)&lt;/h2&gt;
&lt;p&gt;VLC isn't one big monolithic .exe file. It's built almost entirely out of plugins for codecs, demuxers, I/O modules all loaded dynamically at runtime. After all one reason for the popularity of VLC is the vast support for any imaginable media file format. Parsing every single plugin binary on every startup would be slow, so VLC keeps an index of them in a file called &lt;code&gt;plugins.dat&lt;/code&gt;. Read the index, load what you need, done in milliseconds.&lt;/p&gt;
&lt;p&gt;That's the theory. In practice it means &lt;code&gt;plugins.dat&lt;/code&gt; is a single point of failure. If something gets between VLC and that file VLC can't read its index. And what could get into the way? Well.. Maybe.. Microsoft Defender flagging it, scanning it, locking it during a background check. That kind of things. No index means VLC falls back to manually scanning the entire plugin directory from scratch. That's your 30 second hang, right there, every time Defender decides to interfere again. Great! Anti-Viruses make us so secure, right?&lt;/p&gt;
&lt;p&gt;This also explains the classic pattern some people report: First launch of the day is agonizingly slow, then it's instant for the rest of the session. Once the cache scan clears and VLC gets its index back, it stays fine until Defender flags it again on some later background pass.&lt;/p&gt;
&lt;h2&gt;Why the story got messy and I suspect an evil actor to be at play&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://code.videolan.org/videolan/vlc/-/work_items/29685" target="_blank" rel="noopener"&gt;VideoLAN pinned this on a Defender bug introduced in a Windows 11 update&lt;/a&gt;, and pushed &lt;code&gt;vlc-cache-gen.exe&lt;/code&gt; (regenerates the plugin cache) as the fix. Fair enough. Except their own bug tracker tells a slightly different story. At least one reporter said regenerating &lt;code&gt;plugins.dat&lt;/code&gt; did nothing, but dropping in a known-good cache file did fix it, even though a &lt;code&gt;plugins.dat&lt;/code&gt; already existed on disk. That's not "missing cache," that's "Defender interfering with an existing, valid cache." Different failure mode, same symptom.&lt;/p&gt;
&lt;p&gt;So both camps in the bug tracker were right for their own case. Some machines just need a cache rebuild. Others have Defender actively poisoning a perfectly fine cache. Either way, Defender is the common denominator.&lt;/p&gt;
&lt;p&gt;I however find it suspecting that immediately some people in the Internet used this to start defamatory campaigns against OpenSource software. Claiming &lt;em&gt;"A bug like this should never exist"&lt;/em&gt; or &lt;em&gt;"Why VLC needs a plugin-cache at all"&lt;/em&gt;. Which both are pretty stupid arguments in my view. Anti-Virus scanners are known to be one of the main sources for pain in regards to software stability. The whole AV industry is just a big mess. I've seen enough bad AV-Kernel modules on Linux machines. And if you search for "Anti-Virus scanner bricks Windows PCs" plenty of results will show up. So yeah..&lt;/p&gt;
&lt;p&gt;What made me realise something other might be going on is this: One person even claimed he now uses the Microsoft Media Player. Wow! That piece of garbage which can't play many commonly used formats? And lacks so many features VLC has?&lt;/p&gt;
&lt;p&gt;Bold claim!&lt;/p&gt;
&lt;p&gt;Let's just say I notice this pattern regularly towards OpenSource software. If you can't attack the quality of the software itself.. Go create an artificial fault and use that as base argument for your critic.. Yeah..&lt;/p&gt;
&lt;p&gt;And it's not that Microsoft hasn't shown this nasty pattern of force-pushing their services and software onto their users. Even against their will! Even when the users explicitly disabled - or even removed - a feature Microsoft just deliberately enables it again at the next opportunity..&lt;/p&gt;
&lt;h2&gt;The actual fix I use&lt;/h2&gt;
&lt;p&gt;Regenerating the cache or reinstalling VLC treats the symptom. It WILL come back the next time Defender scans the folder again. If you want it to actually stay fixed, exclude the VLC folder from Defender scanning:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Windows Security → Virus &amp;amp; threat protection → Manage settings → Exclusions → Add an exclusion → Folder: C:\Program Files\VideoLAN\VLC&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Or via PowerShell, if you're scripting this:&lt;/p&gt;
&lt;pre class="line-numbers language-powershell"&gt;&lt;code&gt;Add-MpPreference -ExclusionPath "C:\Program Files\VideoLAN\VLC"&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sadly I trust the VLC project more, than Microsoft fixing this &lt;em&gt;bug&lt;/em&gt;...&lt;/p&gt;</description>
      <pubDate>Sun, 30 Aug 2026 19:29:11 +0200</pubDate>
      <guid isPermaLink="false">b69166b6dd036416f303b7a54de64df8</guid>
    </item>
    <item>
      <title>Configuring Termux after a ROM-Update</title>
      <link>https://admin.brennt.net/configuring-termux-after-a-rom-update</link>
      <image/>
      <description>&lt;p&gt;Just a reminder for myself..&lt;/p&gt;
&lt;p&gt;I recently updated the LineageOS on my mobile and hence had to revert to the official Stock ROM first. Due to this I, of course, also had to reinstall all apps.&lt;/p&gt;
&lt;p&gt;These are the steps I do to configure Termux (&lt;a href="https://github.com/termux/termux-app" target="_blank" rel="noopener"&gt;Github&lt;/a&gt;, &lt;a href="https://f-droid.org/en/packages/com.termux/" target="_blank" rel="noopener"&gt;F-Droid&lt;/a&gt;) on my mobile.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;After install &lt;code&gt;pkg update&lt;/code&gt; followed by &lt;code&gt;pkg upgrade&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Allow Termux to access the SD-Card storage, so it can read my private SSH-keys: &lt;code&gt;termux-setup-storage&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Then either provide that path to the keyfile in the &lt;code&gt;.ssh/config&lt;/code&gt; like this: &lt;code&gt;/storage/emulated/0/&amp;lt;folder on SD-Card&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Or copy the private keyfile to &lt;code&gt;~/.ssh/&lt;/code&gt;, then you are able to revoke the storage permissions again.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Install correct SSH package and agent: &lt;code&gt;pkg install ssh-agent openssh&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;~/.ssh/config&lt;/code&gt; and test it&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Test SSH-Connections to various servers&lt;/li&gt;
&lt;li&gt;Install various tools&lt;code&gt;&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pkg install dnsutils&lt;/code&gt; &lt;code&gt;openssl-tool nmap traceroute&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <pubDate>Sun, 30 Aug 2026 19:25:50 +0200</pubDate>
      <guid isPermaLink="false">0b1e99e89995bd35596227f5d8918ea2</guid>
    </item>
    <item>
      <title>trustscope.app corrects the Google reviews situation in Germany at least a little bit</title>
      <link>https://admin.brennt.net/trustscope-app-corrects-the-google-reviews-situation-in-germany-at-least-a-little-bit</link>
      <image/>
      <description>&lt;p&gt;If you ever reviewed a business on Google in Germany you may be familiar with this mail:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://admin.brennt.net/bl-content/uploads/pages/55c29e51743dc33a74867ff020dd1f4e/google-review-deleted-png.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;In Germany it is pretty easy to get rid of bad, but honest, reviews. The business owner just needs to claim the review is defamatory and Google has to remove it. Shifting the burden of prove towards the reviewer. Often this includes lengthy discussions with lawyers as many business use the services of specialised law-firms.&lt;/p&gt;
&lt;p&gt;Which also creates a scenario of financial ruin for people as legal battles can be costly and mistakes are easily made..&lt;/p&gt;
&lt;p&gt;As I wrote recently &lt;a href="https://admin.brennt.net/google-maps-now-displaying-the-number-of-deleted-reviews-due-to-defamation" target="_blank" rel="noopener"&gt;Google now started to display the approximately number of deleted reviews due to defamation&lt;/a&gt;. Creating a crucial metric which enables Google Map users to reach an informed decision.&lt;/p&gt;
&lt;p&gt;And it didn't take long for someone to scrape that data and take it into account when calculating the "real" score of a business. Of course these numbers are just an approximation, and we have no data on rightfully vs. wrongfully deleted reviews. After all, it is still a nice project: &lt;a href="https://trustscope.app/" target="_blank" rel="noopener"&gt;https://trustscope.app/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://admin.brennt.net/bl-content/uploads/pages/55c29e51743dc33a74867ff020dd1f4e/trustscoreapp-png.png"&gt;&lt;/p&gt;</description>
      <pubDate>Sun, 30 Aug 2026 19:20:32 +0200</pubDate>
      <guid isPermaLink="false">55c29e51743dc33a74867ff020dd1f4e</guid>
    </item>
    <item>
      <title>Using PowerShell to get the size of folders (Get-FolderSize function)</title>
      <link>https://admin.brennt.net/using-powershell-to-get-the-size-of-folders-get-foldersize-function</link>
      <image/>
      <description>&lt;p&gt;On my gaming PC Windows complained that &lt;code&gt;C:\&lt;/code&gt; had only 10GB of free space left. Time to free up some space! In WindowsXP times I used a separate Program called GetFolderSize for this, but nowadays with PowerShell I was like: &lt;em&gt;"Hey, I bet I can do this with PowerShell alone."&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;After a bit of back &amp;amp; forth I came up with the following function:&lt;/p&gt;
&lt;pre class="line-numbers language-powershell"&gt;&lt;code&gt;function Get-FolderSize {
    param([string]$Path = ".")
    Get-ChildItem $Path -Directory | ForEach-Object {
        $size = (Get-ChildItem $_.FullName -Recurse -File -ErrorAction SilentlyContinue |
                 Measure-Object -Property Length -Sum).Sum
        [PSCustomObject]@{
            Folder = $_.Name
            "Size(MB)" = "{0:N2}" -f ($size / 1MB)
            "Size(GB)" = "{0:N2}" -f ($size / 1GB)
        }
    } | Sort-Object { [double]($_."Size(MB)") } -Descending | Format-Table -AutoSize
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It worked fine. However I always needed to copy &amp;amp; paste it into the current PowerShell window to have the function available. Hence I had a new question: How do I make this function available for my local Windows? How do I add this function to the PowerShell equivalent of a &lt;code&gt;.bashrc&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;I myself was surprised I didn't know how to do this. Never needed it before apparently. And at customers PowerShell and/or UserProfiles are always locked down so that I can't do such stuff.&lt;/p&gt;
&lt;h2&gt;Making a PowerShell function constantly available&lt;/h2&gt;
&lt;p&gt;Microsoft has some documentation available about the &lt;code&gt;$PROFILE&lt;/code&gt; variable: &lt;a href="https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7.6" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7.6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;First I tested if I already have a &lt;code&gt;$PROFILE&lt;/code&gt; for the current user on the current host. This turned out to be false:&lt;/p&gt;
&lt;pre class="line-numbers language-powershell"&gt;&lt;code&gt;PS C:\Users\USER&amp;gt; Test-Path $PROFILE
False&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hence I simply create the &lt;code&gt;$PROFILE&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="line-numbers language-powershell"&gt;&lt;code&gt;PS C:\Users\USER&amp;gt; New-Item -ItemType File -Path $PROFILE -Force


    Verzeichnis: C:\Users\USER\Documents\WindowsPowerShell


Mode        LastWriteTime         Length  Name
----        -------------         ------  ----
-a----      8/8/2026   4:44 PM         0  Microsoft.PowerShell_profile.ps1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I can edit my &lt;code&gt;$PROFILE&lt;/code&gt; and the &lt;code&gt;Get-FolderSize&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class="line-numbers language-powershell"&gt;&lt;code&gt;PS C:\Users\USER&amp;gt; notepad $PROFILE&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then just copy&amp;amp;paste the function in the &lt;code&gt;Microsoft.PowerShell_profile.ps1&lt;/code&gt; file:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://admin.brennt.net/bl-content/uploads/pages/46d4028493b7aaa6afb00a18b44806b8/powershell-profile-png.png"&gt;&lt;/p&gt;
&lt;p&gt;After that we need to source the&amp;nbsp;&lt;code&gt;$PROFILE&lt;/code&gt; again to have it available. Just like on Linux:&lt;/p&gt;
&lt;pre class="line-numbers language-powershell"&gt;&lt;code&gt;PS C:\Users\USER&amp;gt; . $PROFILE&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it works just fine:&lt;/p&gt;
&lt;pre class="line-numbers language-powershell"&gt;&lt;code&gt;PS C:\Users\USER\AppData&amp;gt; Get-FolderSize .

Folder   Size(MB)  Size(GB)
------   --------  --------
Local    53,609.24 52.35
Roaming  10,956.81 10.70
LocalLow 9,207.82  8.99
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Alternatives&lt;/h2&gt;
&lt;h3&gt;PowerShell Gallery&lt;/h3&gt;
&lt;p&gt;Only later did I learn of the site &lt;a href="https://www.powershellgallery.com/" target="_blank" rel="noopener"&gt;https://www.powershellgallery.com/&lt;/a&gt;, they collect scripts from the community and make them available. There seems to be &lt;a href="https://www.powershellgallery.com/packages/PSFolderSize/1.5/Content/Public%5CGet-FolderSize.ps1" target="_blank" rel="noopener"&gt;a good script here&lt;/a&gt;, however I didn't use it.&lt;/p&gt;
&lt;h3&gt;Microsoft SysInternal DiskUsage Utility&lt;/h3&gt;
&lt;p&gt;There is a &lt;code&gt;du&lt;/code&gt; tool from SysInternals available here: &lt;a href="https://learn.microsoft.com/en-us/sysinternals/downloads/du" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/en-us/sysinternals/downloads/du&lt;/a&gt;. Downloading that and putting the binary in the &lt;code&gt;$PATH&lt;/code&gt; should do the trick to. However I did not check if the output matches my desired format.&lt;/p&gt;</description>
      <pubDate>Sat, 08 Aug 2026 16:48:23 +0200</pubDate>
      <guid isPermaLink="false">46d4028493b7aaa6afb00a18b44806b8</guid>
    </item>
    <item>
      <title>How to counter enshittyfication in consumer products</title>
      <link>https://admin.brennt.net/how-to-counter-enshittyfication-in-consumer-products</link>
      <image>https://admin.brennt.net/bl-content/uploads/pages/dfea30e7d4f4e6f6c476e27ba2192785/chefn-palmzester-png.png</image>
      <description>&lt;h2&gt;The start: Knockoff browser extension&lt;/h2&gt;
&lt;p&gt;A few weeks ago I wrote a post about &lt;a href="https://admin.brennt.net/filter-bogus-brands-from-amazons-product-search-with-knockoff-browser-extension" target="_blank" rel="noopener"&gt;the Knockoff browser extension which helps consumers to filter out cheap and bogus brands from the Amazon search&lt;/a&gt; results. Now I found two websites which help me even more in a) filtering out/recognising bad brands and b) find good brands that still produce quality products which are worth their price.&lt;/p&gt;
&lt;h2&gt;Discovering: Worse on Purpose.com&lt;/h2&gt;
&lt;p&gt;It all started when I noticed that &lt;a href="https://www.wmf.com/de/en/clip-close-food-conservation-3100514549.html" target="_blank" rel="noopener"&gt;the WMF Clip&amp;amp;Close ice cube box&lt;/a&gt; is similar to the one &lt;a href="https://www.emsa.com/produkt/clip-close-frischhaltedosen-eiswuerfelform" target="_blank" rel="noopener"&gt;produced by EMSA&lt;/a&gt;. And both had the same worse reviews as the plastic doesn't seem to be made for storing in freezes as it breaks easily and constantly. Naturally I was curious why WMF sells such crappy products.&lt;/p&gt;
&lt;p&gt;And I found the solution on a webpage called &lt;a href="https://www.worseonpurpose.com/" target="_blank" rel="noopener"&gt;Worse on Purpose&lt;/a&gt;. In the article &lt;a href="https://www.worseonpurpose.com/p/your-cookware-got-worse-on-purpose" target="_blank" rel="noopener"&gt;Your Cookware Got Worse On Purpose&lt;/a&gt; author &lt;a href="https://www.worseonpurpose.com/authors/keyana-sapp" target="_blank" rel="noopener"&gt;Keyana Sapp&lt;/a&gt; writes in detail about how family-owned companies are bought by international conglomerates who just seek to own the established brand name known for it's high quality products, to sell their cheap &amp;amp; crappy stuff.&lt;/p&gt;
&lt;p&gt;The solution to this curiosity was rather boring. Under the headline &lt;a href="https://www.worseonpurpose.com/p/your-cookware-got-worse-on-purpose#six-companies-eightyone-brands" target="_blank" rel="noopener"&gt;Six companies, eighty-one brands&lt;/a&gt; we learn that both companies are owned by &lt;a href="https://www.groupeseb.com/en" target="_blank" rel="noopener"&gt;Groupe SEB&lt;/a&gt; (&lt;a href="https://en.wikipedia.org/wiki/Groupe_SEB" target="_blank" rel="noopener"&gt;Wikipedia&lt;/a&gt;) who bought WMF &amp;amp; &lt;a href="https://en.wikipedia.org/wiki/Emsa_(company)" target="_blank" rel="noopener"&gt;EMSA&lt;/a&gt; in 2016 and is, according to Wikipedia, the world's largest manufacturer of cookware.&lt;/p&gt;
&lt;p&gt;Great, that will definitely not have any impace on the quality of WMF products rights? Oh wait.. &lt;a href="https://www.hna.de/wirtschaft/schliesst-filialen-mit-raeumungsverkauf-die-streich-liste-wmf-94266124.html" target="_blank" rel="noopener"&gt;There have been numerous factories closed since 2025&lt;/a&gt;...&lt;/p&gt;
&lt;h2&gt;The Brand Legder&lt;/h2&gt;
&lt;p&gt;Worse on Purpose operates &lt;a href="https://ledger.worseonpurpose.com/" target="_blank" rel="noopener"&gt;The Brand Ledge&lt;/a&gt; where you can search for brands and companies names to get a small overview and can now decided for yourself if you still trust that brand. Takes this example for WMF: &lt;a href="https://ledger.worseonpurpose.com/brands/wmf" target="_blank" rel="noopener"&gt;https://ledger.worseonpurpose.com/brands/wmf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Neat! And very handy!&lt;/p&gt;
&lt;h2&gt;BuyItForLife.com, the Chef'n Juicer &amp;amp; WMF&lt;/h2&gt;
&lt;p&gt;I don't really know what to make out of &lt;a href="https://buyitforlife.com/" target="_blank" rel="noopener"&gt;buyitforlife.com&lt;/a&gt;. It's a website seemingly vetting products the website owner has used/is using himself.&lt;/p&gt;
&lt;p&gt;He states and the frontpage:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Browse 500+ Buy It For Life products I've personally vetted for quality and longevity. Compare where they're made, warranty, category, and price in seconds.&lt;br&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://buyitforlife.com/" target="_blank" rel="noopener"&gt;https://buyitforlife.com/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And on the about page:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This list isn't sponsored or biased. It's just me, someone who likes well-built products, going down a rabbit hole to find things that are actually built to last.&lt;br&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://buyitforlife.com/about" target="_blank" rel="noopener"&gt;https://buyitforlife.com/about&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why I am experiencing such an uneasy feeling when reading that site?&lt;/p&gt;
&lt;p&gt;Well, I found the site today. I was search for a manually operated lemon/citrus squeezer/press. The &lt;a href="https://www.amazon.de/dp/B004R9PRNK/" target="_blank" rel="noopener"&gt;WMF one looked nice at first&lt;/a&gt; for 38,99€ but luckily the &lt;a href="https://www.amazon.de/dp/B002XOB0P0" target="_blank" rel="noopener"&gt; Chef'n PalmZester&lt;/a&gt; (22,79€) was displayed right next to it.. And.. Well look for yourself:&lt;/p&gt;
&lt;p&gt;First the Chef'n model in yellow, than the WMF one in black. They are exactly the same model, just different colours. And apparently black is 16€ more expensive.. Yeah, are you kidding me!?&lt;/p&gt;
&lt;p&gt;&lt;img src="https://admin.brennt.net/bl-content/uploads/pages/dfea30e7d4f4e6f6c476e27ba2192785/chefn-palmzester-png.png" style="border-style: solid;" width="605" height="427"&gt;     &lt;img src="https://admin.brennt.net/bl-content/uploads/pages/dfea30e7d4f4e6f6c476e27ba2192785/wmf-top-tools-zitronenpresse-png.png" style="border-style: dotted;" width="390" height="396"&gt;&lt;/p&gt;
&lt;p&gt;Amazon reviews on both products state, that it breaks after just 1 year of regular usage. As apparently the plastic can't deal with the stress and breaks at some point. Wow.&lt;/p&gt;
&lt;p&gt;What is even more interesting: Chef'n is owned by &lt;a href="https://lifetimebrands.com/" target="_blank" rel="noopener"&gt;Lifetime Brands Inc.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://admin.brennt.net/bl-content/uploads/pages/dfea30e7d4f4e6f6c476e27ba2192785/chefn-owner-png.png" style="border-style: solid; display: block; margin-left: auto; margin-right: auto;" width="495" height="306"&gt;&lt;/p&gt;
&lt;p&gt;And that is a totally different company which as nothing to do with Group SEB who owns WMF. This proves, again, that the brand WMF now stands for nothing. Just a shell used to sell cheap products for a higher price.&lt;/p&gt;
&lt;p&gt;Good job capitalism!&lt;/p&gt;
&lt;p&gt;BuyItForLife however has this product listed on it's site. Among many other products from companies which Worse on Purpose has on it's Watchlist or where people on Reddit say for years that the products became shittier over the time. I think this is my problem with that site. BuyItForLife is just a single person without any special knowledge. Just recommending products he finds useful. That's fair. Nothing against that, but it isn't as objective as it claims to be. It's just a single persons opinion - marketed as something more...&lt;/p&gt;
&lt;p&gt;Nowhere on the site can I read a detailed review about a product. All links point directly to Amazon. No "This is why I recommend that item", nothing. For me, personally, this simply doesn't add up. So no, I'm not going to recommend that site.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;</description>
      <pubDate>Mon, 03 Aug 2026 21:25:46 +0200</pubDate>
      <guid isPermaLink="false">dfea30e7d4f4e6f6c476e27ba2192785</guid>
    </item>
    <item>
      <title>TinyMCE: Configure iframe sandboxing to allow YouTube domains</title>
      <link>https://admin.brennt.net/tinymce-configure-iframe-sandboxing-to-allow-youtube-domains</link>
      <image/>
      <description>&lt;p&gt;In TinyMCE 6.8.1 (the WYSIWYG editor used by Bludit) iframe sandboxing was introduced. This automatically adds the &lt;code&gt;sandbox=""&lt;/code&gt; parameter to all inserted &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;-tags. This blocks all embedded videos from playing, even when the CSP-Headers are correct.&lt;/p&gt;
&lt;p&gt;As I just wanted to write a short blogpost about a YouTube video which discusses why we Germans are able to eat raw pork ("&lt;a href="https://en.wikipedia.org/wiki/Mett" target="_blank" rel="noopener"&gt;Mett&lt;/a&gt;") and suddenly the video wasn't displayed in the editor-view. Browser console showed no problems with CSP-Headers and so I was left to searching..&lt;/p&gt;
&lt;p&gt;YouTube gave me the following &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; block for embedding the video:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;&amp;lt;iframe width="560" height="315"
 src="https://www.youtube-nocookie.com/embed/azdV7EzP0v4?si=sKj7zfe0OkVNxjsi" title="YouTube video player"
 frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
 referrerpolicy="strict-origin-when-cross-origin"
 allowfullscreen&amp;gt;
&amp;lt;/iframe&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, after copy&amp;amp;pasting that into the TinyMCE in Bludit and saving the article, it changed to:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;&amp;lt;iframe width="560" height="315"
 src="https://www.youtube-nocookie.com/embed/azdV7EzP0v4?si=sKj7zfe0OkVNxjsi" title="YouTube video player"
 frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
 referrerpolicy="strict-origin-when-cross-origin"
 allowfullscreen="allowfullscreen"
 sandbox=""&amp;gt;
&amp;lt;/iframe&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the added &lt;code&gt;sandbox&lt;/code&gt;-parameter triggers the following error in the browser console:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;GET https://www.youtube-nocookie.com/img/meh7.png NS_BINDING_ABORTED
A resource is blocked by OpaqueResponseBlocking, please check browser console for details.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of to the search engine I went and luckily the TineMCE folks&amp;nbsp;wrote so in their release notes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; sandbox_iframes: is set to false by default, which is the existing behavior. This is because enabling sandbox_iframes may break existing media embeds such as YouTube, Vimeo, and Codepen, as actions such as scripting and same-origin access are prevented.&lt;br&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://www.tiny.cloud/docs/tinymce/6/6.8.1-release-notes/#new-sandbox_iframes-option-that-controls-whether-iframe-elements-will-be-added-a-sandbox-attribute-to-mitigate-malicious-intent" target="_blank" rel="noopener"&gt;https://www.tiny.cloud/docs/tinymce/6/6.8.1-release-notes/#new-sandbox_iframes-option-that-controls-whether-iframe-elements-will-be-added-a-sandbox-attribute-to-mitigate-malicious-intent&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which is why I was able to find it so quickly.&lt;/p&gt;
&lt;h2&gt;The fix&lt;/h2&gt;
&lt;p&gt;I fixed it, by adding the YouTube domains to the iframe-exclusion tag inside the File &lt;code&gt;bludit-folder/bl-plugins/tinymce/plugins.php&lt;/code&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add the &lt;code&gt;sandbox_iframes: true,&lt;/code&gt; line&lt;/li&gt;
&lt;li&gt;The exclusions are defined for the three most used YouTube-domains: &lt;code&gt;youtube.com&lt;/code&gt;, &lt;code&gt;youtube-nocookie.com&lt;/code&gt;, &lt;code&gt;youtu.be&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;        tinymce.init({
                selector: "#jseditor",
                auto_focus: "jseditor",
[...]
                link_default_target: '_blank',
                sandbox_iframes: true,
                sandbox_iframes_exclusions: [
                        'youtube.com',
                        'youtube-nocookie.com',
                        'youtu.be'
                ]
        });&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your TinyMCE behaves like before and embedding videos works again.&lt;/p&gt;</description>
      <pubDate>Mon, 03 Aug 2026 00:13:38 +0200</pubDate>
      <guid isPermaLink="false">69f8807d4aa954b6886865df35e1a4c6</guid>
    </item>
    <item>
      <title>Why we Germans can eat Mett (aka: raw pork)</title>
      <link>https://admin.brennt.net/why-we-germans-can-eat-mett-aka-raw-pork</link>
      <image/>
      <description>&lt;p&gt;Everything I ever wanted to know about &lt;a href="https://en.wikipedia.org/wiki/Mett" target="_blank" rel="noopener"&gt;Mett&lt;/a&gt;. Honestly I was just bored when I clicked on that video, thought of it to have a bit of over-promising/misleading title.&lt;/p&gt;
&lt;p&gt;But that turned out to be wrong. Oliver Kim or Microbehunter explains in detail why we Germans are able to eat Mett. Which is somewhat of a oddity, given that raw pork can be dangerous and is frowned upon in many cultures and countries.&lt;/p&gt;
&lt;p&gt;He explains the bacteria involved and also the system Germany has in place to ensure the meat is healthy. And this was really something new for me. I know that Germany is often praised for it's good infrastructure, however never had I considered uninterrupted cold-chains and strict meat controls to be a part of that. Turns out, Oliver is right, to offer raw pork at any butchery or even bread rolls with Mett as ready-to-eat items in bakeries is quite an achievement.&lt;/p&gt;
&lt;p&gt;&lt;iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/azdV7EzP0v4" title="YouTube video player" frameborder="0" allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="allowfullscreen"&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p&gt;Source: &lt;a href="https://www.youtube.com/watch?v=azdV7EzP0v4" target="_blank" rel="noopener"&gt;https://www.youtube.com/watch?v=azdV7EzP0v4&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sun, 02 Aug 2026 23:33:08 +0200</pubDate>
      <guid isPermaLink="false">9e613c9d159d49ac4d8bfd062bd24b58</guid>
    </item>
    <item>
      <title>Booting an Ubuntu LTS cloud-init QEMU VM inside a Proxmox VM</title>
      <link>https://admin.brennt.net/booting-an-ubuntu-lts-cloud-init-qemu-vm-inside-a-proxmox-vm</link>
      <image/>
      <description>&lt;p&gt;I needed a quick throwaway Ubuntu VM for some testing, so I reached for the classic combo: an Ubuntu cloud image, a local &lt;code&gt;cloud-init&lt;/code&gt; NoCloud datasource served over HTTP, and a bare &lt;code&gt;qemu-system-x86_64&lt;/code&gt; invocation. Nothing fancy, I've done this a dozen times before - just that the last time was ages ago..&lt;/p&gt;
&lt;p&gt;My first attempt looked like this:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@vm:~# qemu-system-x86_64 -net nic \
    -net user -machine accel=kvm:tcg -m 512 \
    -nographic -hda noble-server-cloudimg-amd64.img \
    -smbios type=1,serial=ds='nocloud;s=http://127.0.0.1:8000/'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And of course, it didn't just work. Last time I did was ages ago and directly on the hardware of my rootserver, not my local Proxmox server. As it turned out, I had two separate problems in this command, and to make things even more interesting: Everything is executed inside a Debian 13 VM, which itself lives on top of Proxmox. A VM inside a VM. So every weirdness I hit had to be checked twice. Once for&amp;nbsp;&lt;em&gt;"Is this a QEMU problem?"&lt;/em&gt; and once for &lt;em&gt;"Is this a nested-virtualization problem?"&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Preface&lt;/h2&gt;
&lt;p&gt;I created the directory &lt;code&gt;/root/temp&lt;/code&gt; and but the files there according to the &lt;a href="https://docs.cloud-init.io/en/latest/tutorial/qemu.html" target="_blank" rel="noopener"&gt;cloud-init QEMU tutorial&lt;/a&gt;. Then I started a webserver via the http.server Python module.&lt;/p&gt;
&lt;p&gt;Have a look at &lt;a href="https://docs.cloud-init.io/en/latest/tutorial/qemu.html#define-the-configuration-data-files" target="_blank" rel="noopener"&gt;https://docs.cloud-init.io/en/latest/tutorial/qemu.html#define-the-configuration-data-files&lt;/a&gt; regarding the files and the content.&lt;/p&gt;
&lt;h2&gt;Problem 1: Stuck at "Booting from Hard Disk..."&lt;/h2&gt;
&lt;p&gt;The VM wouldn't get past the SeaBIOS boot message &lt;em&gt;"Booting from Hard Disk..."&lt;/em&gt;. It became stuck there, forever. Additionally I saw no request to the local webserver on port 8000/tcp.&lt;/p&gt;
&lt;p&gt;Turns out this is entirely expected, once you know why. Ubuntu's released Noble Numbat (LTS) cloud image is built for UEFI boot. SeaBIOS, the legacy BIOS QEMU uses by default, has no idea what to do with the GPT-partitioned disk and just... stops. No error, no hint, just sitting at "Booting from Hard Disk...".&lt;/p&gt;
&lt;p&gt;The fix is to hand the VM the proper UEFI firmware via the &lt;a href="https://packages.debian.org/trixie/ovmf" target="_blank" rel="noopener"&gt;Open Virtual Machine Firmware (OVMF) package&lt;/a&gt; instead of relying on SeaBIOS:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;apt-get install ovmf&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If you find older posts, they mention the &lt;code&gt;/usr/share/OVMF/OVMF_VARS.id&lt;/code&gt; file, but on Debian 13 there's a small surprise waiting here too, the package no longer ships the plain &lt;code&gt;OVMF_CODE.fd&lt;/code&gt; or &lt;code&gt;OVMF_VARS.fd&lt;/code&gt; files I was used to. Instead you get the 4M variants:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@vm:~# ls /usr/share/OVMF/OVMF_VARS_4M.*
/usr/share/OVMF/OVMF_VARS_4M.fd  /usr/share/OVMF/OVMF_VARS_4M.ms.fd  /usr/share/OVMF/OVMF_VARS_4M.snakeoil.fd&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;.ms.fd&lt;/code&gt; variant ships with Microsoft's Secure Boot certificates pre-enrolled, and &lt;code&gt;.snakeoil.fd&lt;/code&gt; is pre-signed with a test certificate for Secure Boot development. Neither is what I wanted, so plain &lt;code&gt;OVMF_VARS_4M.fd&lt;/code&gt; it is, paired with the matching &lt;code&gt;OVMF_CODE_4M.fd&lt;/code&gt;. &lt;code&gt;CODE&lt;/code&gt; and &lt;code&gt;VARS&lt;/code&gt; need to come from the same "generation" (2M vs. 4M), mixing them doesn't end well.&lt;/p&gt;
&lt;p&gt;You also don't want to point QEMU directly at the VARS file in &lt;code&gt;/usr/share/OVMF/&lt;/code&gt;, copy it locally first, since QEMU will write to it (that's where your EFI variables actually live):&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@vm:~# cp /usr/share/OVMF/OVMF_VARS_4M.fd .&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Problem 2: Wrong IP for the webserver&lt;/h2&gt;
&lt;p&gt;While I was at it, I remembered that the datasource URL is used inside the guest and therefore refers to the guest itself, not my Proxmox VM serving the cloud-init files. With &lt;a href="https://www.qemu.org/docs/master/system/devices/net.html#using-the-user-mode-network-stack" target="_blank" rel="noopener"&gt;QEMU's user-mode networking&lt;/a&gt; &lt;code&gt;-net user&lt;/code&gt;, the host is reachable from inside the guest at the fixed address &lt;code&gt;10.0.2.2&lt;/code&gt;. And since I'm serving the metadata over HTTP rather than a local path, &lt;code&gt;nocloud-net&lt;/code&gt; is the correct datasource identifier, not &lt;code&gt;nocloud&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Putting it together:&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@vm:~# qemu-system-x86_64 \
  -machine q35,accel=kvm:tcg \
  -m 512 \
  -nographic \
  -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
  -drive if=pflash,format=raw,file=./OVMF_VARS_4M.fd \
  -drive file=noble-server-cloudimg-amd64.img,if=virtio,format=qcow2 \
  -net nic -net user \
  -smbios type=1,serial=ds='nocloud-net;s=http://10.0.2.2:8000/'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That got me past the boot prompt. Progress.&lt;/p&gt;
&lt;h2&gt;Problem 3: It boots, but agonizingly slowly&lt;/h2&gt;
&lt;p&gt;The VM eventually made it into the kernel, but everything crawled. The console was full of entries like this:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;[  116.771971] workqueue: drm_fb_helper_damage_work hogged CPU for &amp;gt;10000us 32 times, consider switching to WQ_UNBOUND
Starting systemd-udevd version 255.4-1ubuntu8.16
[  148.409180] workqueue: drm_fb_helper_damage_work hogged CPU for &amp;gt;10000us 64 times, consider switching to WQ_UNBOUND
[  226.621365] workqueue: drm_fb_helper_damage_work hogged CPU for &amp;gt;10000us 128 times, consider switching to WQ_UNBOUND&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These particular messages are a symptom, not the disease. What's actually going on: &lt;code&gt;accel=kvm:tcg&lt;/code&gt; tells QEMU &lt;em&gt;"Use KVM if you can, otherwise silently fall back to TCG (software emulation)"&lt;/em&gt;. If, for whatever reason, KVM isn't available inside the nested VM, QEMU just quietly emulates the entire CPU in software which is orders of magnitude slower and explains why a workqueue would get starved for 10ms stretches and longer.&lt;/p&gt;
&lt;p&gt;Since this whole setup is a VM inside a VM, the first suspect is always nested virtualization. So, checks on the Proxmox-hosted Debian VM:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@vm:~# ls -la /dev/kvm
ls: cannot access '/dev/kvm': No such file or directory
root@vm:~# lscpu | grep -i virtualization
Virtualization type:                     full&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As the &lt;code&gt;/dev/kvm&lt;/code&gt; device isn't present, we now have confirmed that QEMU falls back to software emulation for the CPU.&lt;/p&gt;
&lt;p&gt;The solution? Changing the CPU for the VM in Proxmox from &lt;code&gt;x86-64-v2-AES&lt;/code&gt; (or whatever is configured) to&amp;nbsp;&lt;code&gt;host&lt;/code&gt;. This enables the VM to directly use the CPU of my Proxmox host.&lt;/p&gt;
&lt;p&gt;This can be done comfortably in the WebUI of Proxmox. Just power down the VM.&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://admin.brennt.net/bl-content/uploads/pages/d951bb0c7343b91a9030b9e4a6940890/cpu-beforenew-png.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;And after:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://admin.brennt.net/bl-content/uploads/pages/d951bb0c7343b91a9030b9e4a6940890/cpu-after-png.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;After a reboot we have a &lt;code&gt;/dev/kvm&lt;/code&gt; device and full support for Intel VT-x virtualisation. This confirms Proxmox is passing VT-x through cleanly to the nested VM. So KVM should be usable in principle, meaning if the inner QEMU VM is still falling back to TCG, it's not because nested virt is broken at the Proxmox level.&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@vm:~# ls -la /dev/kvm
crw-rw---- 1 root kvm 10, 232 Jul 27 00:48 /dev/kvm
root@vm:~# lscpu | grep -i virtualization
Virtualization:                          VT-x
Virtualization type:                     full&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To actually find out, instead of trusting the silent &lt;code&gt;kvm:tcg&lt;/code&gt; fallback, it's better to force KVM explicitly and let it fail loudly if something's wrong:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@vm:~# qemu-system-x86_64 \
  -machine q35,accel=kvm \
  -cpu host \
  -m 512 \
  -nographic \
  -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
  -drive if=pflash,format=raw,file=./OVMF_VARS_4M.fd \
  -drive file=noble-server-cloudimg-amd64.img,if=virtio,format=qcow2 \
  -net nic -net user \
  -smbios type=1,serial=ds='nocloud-net;s=http://10.0.2.2:8000/'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Removing the&amp;nbsp;&lt;code&gt;:tcg&lt;/code&gt; fallback means any KVM problem (permissions, missing nested virt on the Proxmox host CPU type, whatever) surfaces immediately as a hard error instead of a mysteriously slow VM. The parameter &lt;code&gt;-cpu host&lt;/code&gt; on top makes sure the guest actually sees the hosts CPU features rather than a generic, lowest-common-denominator CPU model.&lt;/p&gt;
&lt;p&gt;And the command worked, however, as we are still booting a VM inside a VM expect it to take some time. 😅&lt;/p&gt;
&lt;h2&gt;My learnings&lt;/h2&gt;
&lt;p&gt;For anyone hitting the same wall on their own Proxmox nested-VM setup, the checklist is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Is &lt;code&gt;/dev/kvm&lt;/code&gt; present on the outer (Proxmox) VM at all?&lt;/li&gt;
&lt;li&gt;Is nested virtualization actually enabled on the Proxmox host itself?
&lt;ul&gt;
&lt;li&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;# For Intel-CPUs - prints Y if enabled/available
root@proxmox:~# cat /sys/module/kvm_intel/parameters/nested
Y
# For AMD-CPUs - prints Y if enabled/available
root@proxmox:~# cat /sys/module/kvm_amd/parameters/nested
Y&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Is the outer VM's CPU type set to &lt;code&gt;host&lt;/code&gt; in the Proxmox config? So VMX/SVM flags get passed through
&lt;ul&gt;
&lt;li&gt;Check in the Hardware tab of the VM inside Proxmox's WebUI, or:&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Check with &lt;code&gt;qm config VM-ID&lt;/code&gt; und verify the cpu line says &lt;code&gt;host&lt;/code&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@proxmox:~# qm config 100
[...]
cores: 1
cpu: host
[...]&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Does the inner QEMU call use &lt;code&gt;accel=kvm&lt;/code&gt; (not &lt;code&gt;kvm:tcg&lt;/code&gt;) plus &lt;code&gt;-cpu host&lt;/code&gt;, so a broken KVM path fails loudly instead of silently degrading to software emulation?&lt;/li&gt;
&lt;li&gt;Is the &lt;code&gt;/dev/kvm&lt;/code&gt; device present on the VM where you want to start the QEMU VM?&lt;/li&gt;
&lt;li&gt;Is CPU virtualization supported?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Nothing here is exotic once you know to look for it, but "silent TCG fallback" combined with "one VM inside another" is a pretty effective way to make a simple performance problem look mysterious for a while.&lt;/p&gt;</description>
      <pubDate>Mon, 27 Jul 2026 00:55:20 +0200</pubDate>
      <guid isPermaLink="false">d951bb0c7343b91a9030b9e4a6940890</guid>
    </item>
    <item>
      <title>Why too many automatisms in DNS are bad (Pi-hole, FTL (dns.reply.host), mDNS/Avahi, etc.)</title>
      <link>https://admin.brennt.net/why-too-many-automatisms-in-dns-are-bad-pihole-ftl-dns-reply-host-mdns-avahi</link>
      <image/>
      <description>&lt;h2&gt;A small pre-preface for users who followed the "Ultimate Pi-hole Setup" tutorial from the YouTuber WunderTech&lt;/h2&gt;
&lt;p&gt;If you used the configuration files he provided on his homepage: &lt;a href="https://www.wundertech.net/ultimate-pi-hole-setup/" target="_blank" rel="noopener"&gt;https://www.wundertech.net/ultimate-pi-hole-setup/&lt;/a&gt; you will experience the exact same problems sooner or later.&lt;/p&gt;
&lt;p&gt;The reason is that the VIPs from keepalived are not bound on a separate dummy interface and hence the pihole-FTL process will take them into account when dynamically building the hostname and choosing the "correct IPs".&lt;/p&gt;
&lt;p&gt;You HAVE to at least enable &lt;code&gt;dns.reply.host&lt;/code&gt; in the &lt;code&gt;/etc/pihole/pihole.toml&lt;/code&gt; to mark the static IP used for the server Pi-hole is running on.&lt;/p&gt;
&lt;p&gt;Jump to &lt;a href="#the-solution"&gt;The solution&lt;/a&gt; if you are not interested in the details.&lt;/p&gt;
&lt;h2&gt;Preface&lt;/h2&gt;
&lt;p&gt;One of the main reasons why I have my homelab is to hone my skills. And today was a day this happened.&lt;/p&gt;
&lt;p&gt;From my one of my LAN hosts I wanted to connect to my Raspberry4 (&lt;code&gt;raspi4.lan, IP: 192.168.178.8&lt;/code&gt;) via SSH. This host is configured as the secondary/backup instance in keepalived for the DNS VIP (&lt;code&gt;192.168.178.100&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The reality however was different:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;user@lanadmin:~$ ssh raspi4.lan
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Please contact your system administrator.
Add correct host key in /home/user/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /home/user/.ssh/known_hosts:10
  remove with:
  ssh-keygen -f '/home/user/.ssh/known_hosts' -R 'raspi4.lan'
Host key for raspi4.lan has changed and you have requested strict checking.
Host key verification failed.
user@lanadmin:~$  ssh-keygen -f '/home/user/.ssh/known_hosts' -R 'raspi4.lan'
# Host raspi4.lan found: line 8
# Host raspi4.lan found: line 9
# Host raspi4.lan found: line 10
/home/user/.ssh/known_hosts updated.
Original contents retained as /home/user/.ssh/known_hosts.old&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Granted I don't log on often onto &lt;code&gt;raspi4.lan&lt;/code&gt; as everything is automated and monitored and the Pi-hole config is synced via Nebula-Sync from &lt;code&gt;raspi3.lan&lt;/code&gt;. So I suspected I didn't purge the entries related to &lt;code&gt;raspi4.lan&lt;/code&gt; from my &lt;code&gt;~/.ssh/known_hosts&lt;/code&gt; file after I re-installed that system a while ago.&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;user@lanadmin:~$ ssh raspi4.lan
The authenticity of host 'raspi4.lan (ULA:ffff)' can't be established.
ED25519 key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
This host key is known by the following other names/addresses:
    ~/.ssh/known_hosts:5: raspi3.lan
    ~/.ssh/known_hosts:12: 192.168.178.9
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'raspi4.lan' (ED25519) to the list of known hosts.
Linux raspi3 6.1.21-v8+ #1642 SMP PREEMPT Mon Apr  3 17:24:16 BST 2023 aarch64
###############################
## Primary Pi-hole instance! ##
###############################
Last login: Fri Jul 24 21:46:36 2026 from w.x.y.z

user@raspi3:~$&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Huh? How did I end up on raspi3.lan, when I clearly entered raspi4.lan as the host to connect to? Something is very wrong.&lt;/p&gt;
&lt;p&gt;Sadly I overlooked that the IP SSH provided did list the IPv6 VIP (ending in &lt;code&gt;:ffff&lt;/code&gt;) which is plain wrong, but it shouldn't take long for me to discover that..&lt;/p&gt;
&lt;h2&gt;Overview over the current setup&lt;/h2&gt;
&lt;p&gt;We have two Raspberry Pi's in this setup.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Host A:&lt;/strong&gt; Raspberry 3&lt;br&gt;IPv4: 192.168.178.9/24&lt;br&gt;IPv6: ULA:9/64&lt;br&gt;Hostname: raspi3.lan&lt;br&gt;Keepalived: Primary&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Host B:&lt;/strong&gt; Raspberry 4&lt;br&gt;IPv4: 192.168.178.8/24&lt;br&gt;IPv6: ULA:8/64&lt;br&gt;Hostname: raspi4.lan&lt;br&gt;Keepalived: Secondary&lt;/p&gt;
&lt;p&gt;The three used VIPs are:&lt;br&gt;&lt;strong&gt;VIPv4:&lt;/strong&gt; 192.168.178.100/32 (DNS: pihole.lan) and 192.168.178.101/32 unused, just for testing&lt;br&gt;&lt;strong&gt;VIPv6:&lt;/strong&gt; ULA:ffff/128 (DNS: pihole.lan)&lt;/p&gt;
&lt;p&gt;Both Raspberries run a Pi-hole instance with unbound and keepalived. The VIPs are automatically configured on eth0 on the primary instance. raspi3.lan has the primary keepalived role.&lt;/p&gt;
&lt;h2&gt;It's always DNS™&lt;/h2&gt;
&lt;p&gt;At first I suspected that I screwed up when I created my local DNS entries, but no the Local DNS records in my Pi-Hole setup are fine. The IPs for the hosts and vip match. The config is synced with nebula-sync from &lt;code&gt;raspi3.lan&lt;/code&gt; to &lt;code&gt;raspi4.lan&lt;/code&gt;, so no room for typos there. Nevertheless I accessed both WebUIs and checked independently, wouldn't be the first time a sync-mechanism failed. However, everything was in order.&lt;/p&gt;
&lt;p&gt;A dig however showed the problem clearly:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@lanadmin:~# dig -t a raspi3.lan +noall +answer
raspi3.lan.             0       IN      A       192.168.178.9
root@lanadmin:~# dig -t a raspi4.lan +noall +answer
raspi4.lan.             0       IN      A       192.168.178.100&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;/etc/resolv.conf&lt;/code&gt; of that system is:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@lanadmin:~# cat /etc/resolv.conf
domain lan
search lan
nameserver 192.168.178.8
nameserver 192.168.178.9&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So &lt;code&gt;raspi4.lan&lt;/code&gt; is queried first, than &lt;code&gt;raspi3.lan&lt;/code&gt;. And I forgot to change the nameserver to the VIP...&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/hosts&lt;/code&gt; was also fine. Only standard entries for the&amp;nbsp;&lt;code&gt;.8&lt;/code&gt; on &lt;code&gt;raspi4.lan&lt;/code&gt; and&amp;nbsp;&lt;code&gt;.9&lt;/code&gt; on &lt;code&gt;raspi3.lan&lt;/code&gt;. In short: the local eth0 system IPs with the corresponding FQDN and hostname.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;getent hosts&lt;/code&gt; however was a bit strange too. It returned the loopback address when each host asked for it's own name:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@raspi3:~# getent hosts raspi3.lan
::1             raspi3.lan
root@raspi3:~# getent hosts raspi4.lan
192.168.178.8   raspi4.lan

root@raspi4:~# getent hosts raspi4.lan
::1             raspi4.lan
root@raspi4:~# getent hosts raspi3.lan
192.168.178.9   raspi3.lan&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hence I suspected Avahi (mDNS) as it's installed and listed before the dns resolution in&amp;nbsp;&lt;code&gt;/etc/nsswitch.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@raspi4:~# grep hosts /etc/nsswitch.conf
hosts:          files mdns4_minimal [NOTFOUND=return] dns&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But several hard facts speak against this.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Avahi only works for entries ending in &lt;code&gt;.local&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dig&lt;/code&gt; doesn't use libnss and therefore doesn't honor mDNS/Avahi at all, but it still showed the wrong IPs&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These two facts effectively eliminated Avahi/mDNS as the source of the problem. Avahi was however responsible for returning the loopback address when the host queried for it's own IP. After all &lt;code&gt;mdns4_minimal&lt;/code&gt; was listed before &lt;code&gt;dns&lt;/code&gt; in &lt;code&gt;/etc/nsswitch.conf&lt;/code&gt;. So that works as designed, but doesn't help at all during troubleshooting as it just adds to the confusion..&lt;/p&gt;
&lt;h2&gt;The hosts don't know themselves...&lt;/h2&gt;
&lt;p&gt;At my wits end I took a step back and decided to check DNS from a third host against both &lt;code&gt;raspi3.lan (192.168.178.9)&lt;/code&gt; and &lt;code&gt;raspi4.lan (192.168.178.8)&lt;/code&gt; how both resolve the DNS A-Records of each other.&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;# Querying for the IPv4 of raspi3.lan
# Against raspi3.lan
root@lanadmin:~# dig @192.168.178.9 raspi3.lan +noall +answer
raspi3.lan.             0       IN      A       192.168.178.101
# Against raspi4.lan
root@lanadmin:~# dig @192.168.178.8 raspi3.lan +noall +answer
raspi3.lan.             0       IN      A       192.168.178.9

# Querying for the IPv4 of raspi4.lan
# Against raspi4.lan
root@lanadmin:~# dig @192.168.178.8 raspi4.lan +noall +answer
raspi4.lan.             0       IN      A       192.168.178.100
# Against raspi3.lan
root@lanadmin:~# dig @192.168.178.9 raspi4.lan +noall +answer
raspi4.lan.             0       IN      A       192.168.178.8&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is looks strange.&lt;/p&gt;
&lt;p&gt;Whenever we ask a Raspberry itself for it's own IP we get a wrong result.&lt;br&gt;Querying 192.168.178.9 (raspi3.lan) to resolve raspi3.lan returns 192.168.178.101.&lt;br&gt;Querying 192.168.178.8 (raspi4.lan) to resolve raspi4.lan returns 192.168.178.100.&lt;/p&gt;
&lt;p&gt;How? Avahi was ruled out. There was no DHCP at play and the static DNS entries are correct.&lt;/p&gt;
&lt;p&gt;Something was messing with my setup.&lt;/p&gt;
&lt;h2&gt;Is it Pi-hole?&lt;/h2&gt;
&lt;p&gt;I diff'd the &lt;code&gt;/etc/pihole/pihole.toml&lt;/code&gt; suspecting I missed something in that, as I knew that the &lt;code&gt;pihole.toml&lt;/code&gt; isn't sync by nebula-sync, but there was nothing.&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;user@lanadmin:~$ diff -u &amp;lt;(ssh 192.168.178.8 sudo cat /etc/pihole/pihole.toml) &amp;lt;(ssh 192.168.178.9 sudo cat /etc/pihole/pihole.toml)
--- /dev/fd/63  2026-07-25 03:44:15.628030101 +0200
+++ /dev/fd/62  2026-07-25 03:44:15.628030101 +0200
@@ -1,7 +1,7 @@
 # Pi-hole configuration file (v6.7)
 # Encoding: UTF-8
 # This file is managed by pihole-FTL
-# Last updated on 2026-07-14 10:31:50 CEST
+# Last updated on 2026-07-24 23:26:54 CEST

 [dns]
   # Upstream DNS Servers to be used by Pi-hole. If this is not set, Pi-hole will not&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I searched a bit and stumbled upon a setting regarding FTL:&amp;nbsp;&lt;code&gt;dns.domain&lt;/code&gt; and &lt;code&gt;dns.expandHosts&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;  [dns.domain]
    # The DNS domain used by your Pi-hole.
    #
    # This DNS domain is purely local. FTL may answer queries from its local cache and
    # configuration but *never* forwards any requests upstream *unless* you have
    # configured a dns.revServer exactly for this domain. In the latter case, all queries
    # for this domain are sent exclusively to this server (including reverse lookups).
    #
    # For DHCP, this has two effects; firstly it causes the DHCP server to return the
[... removed as DHCP is not relevant in this case ...]
    #
    # You can disable setting a domain by setting this option to an empty string.
    #
    # Allowed values are:
    #     Any valid domain
    name = "lan"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So the FTL-Cache will be queried for records ending in &lt;code&gt;.lan&lt;/code&gt;, which is fine as it's my local domain and requests for &lt;code&gt;.lan&lt;/code&gt; shouldn't leave my home network. Additionally I understood the sentence &lt;em&gt;"but never forwards any requests upstream"&lt;/em&gt; as: These requests don't even hit &lt;code&gt;Unbound&lt;/code&gt; or &lt;code&gt;dnsmasq&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;expandHosts&lt;/code&gt; makes sure the FQDN is added to &lt;code&gt;/etc/hosts&lt;/code&gt;. Something I already did manually (or the Debian installer).&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;  # If set, the domain is added to simple names (without a period) in /etc/hosts in the
  # same way as for DHCP-derived names
  #
  # Allowed values are:
  #     true or false
  expandHosts = true ### CHANGED, default = false&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From what I read online &lt;code&gt;pihole-FTL&lt;/code&gt; builds the FQDN of the local system itself completely independent from settings in &lt;code&gt;/etc/hosts&lt;/code&gt; + &lt;code&gt;dns.domain&lt;/code&gt;. Could this be a lead?&lt;/p&gt;
&lt;p&gt;How do we verify the entry is actually in the cache and contains the wrong IP? Glad I asked myself! The command &lt;code&gt;killall -USR1 pihole-FTL&lt;/code&gt;&lt;br&gt;dumps the cache entries from &lt;code&gt;dnsmasq&lt;/code&gt; into &lt;code&gt;/var/log/pihole/pihole.log&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@raspi4:~# killall -USR1 pihole-FTL
root@raspi4:~# vi /var/log/pihole/pihole.log
Jul 25 02:49:12 dnsmasq[1147]: time 1784940552
Jul 25 02:49:12 dnsmasq[1147]: cache size 10000, 0/141 cache insertions re-used unexpired cache entries.
[...]
Jul 25 02:49:12 dnsmasq[1147]: Host        Address            Flags      Expires      Source
Jul 25 02:49:12 dnsmasq[1147]: ----------- ------------------ ---------- ------------ ------------
Jul 25 02:49:12 dnsmasq[1147]: pihole.lan  192.168.178.100    4FRI   H                /etc/pihole/hosts/custom.list
Jul 25 02:49:12 dnsmasq[1125]: raspi4.lan  192.168.178.8      4FRI   H                /etc/hosts
Jul 25 02:49:12 dnsmasq[1125]: raspi3.lan  192.168.178.9      4FRI   H                /etc/pihole/hosts/custom.list&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Well, that only proves my assumption that local static DNS records are NOT honored, if the hostname matches the host on which Pi-hole is running. We can clearly see that the entry for &lt;code&gt;raspi4.lan&lt;/code&gt; has a source of &lt;code&gt;/etc/hosts&lt;/code&gt; and not &lt;code&gt;/etc/pihole/hosts/custom.list&lt;/code&gt;, while the record for &lt;code&gt;raspi3.lan&lt;/code&gt; is taken from &lt;code&gt;/etc/pihole/hosts/custom.list&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This proves that some, currently unknown, automatism is at work and goes horribly wrong.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;If nothing helps, try rebooting&lt;/h2&gt;
&lt;p&gt;As I had no real trace of where to look next, I now focused on trying to re-produce the issue. After all, if it was just some quirk of a non-restarted service utilizing some old file - only present in the cache of it's processes file handles.. As &lt;code&gt;raspi4.lan&lt;/code&gt; currently was the secondary node for keepalived, it didn't own the VIPs. Hence I stopped the keepalived process on &lt;code&gt;raspi3.lan&lt;/code&gt;, forcing a failover to &lt;code&gt;raspi4.lan&lt;/code&gt;. After making sure &lt;code&gt;raspi4.lan&lt;/code&gt; had the VIPs I rebooted the system.&lt;/p&gt;
&lt;h2&gt;It gets stranger...&lt;/h2&gt;
&lt;p&gt;After the reboot, in order to get a bit more insight, I executed several dig queries and watched the log simultaneously. All&amp;nbsp;&lt;code&gt;@ip&lt;/code&gt;'s are IPs which are currently present on the eth0 interface of the &lt;code&gt;raspi4.lan&lt;/code&gt; host.&lt;/p&gt;
&lt;p&gt;Those were the commands:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@lanadmin:~# dig @192.168.178.8 raspi4.lan +noall +answer
raspi4.lan.             0       IN      A       192.168.178.100
root@lanadmin:~# dig @192.168.178.100 raspi4.lan +noall +answer
raspi4.lan.             0       IN      A       192.168.178.100
root@lanadmin:~# dig @192.168.178.101 raspi4.lan +noall +answer
raspi4.lan.             0       IN      A       192.168.178.100

# On raspi4.lan, just to have everything neatly together:
root@raspi4:~# killall -USR1 pihole-FTL&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this showed up in the logfile:&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;root@raspi4:~# tail -f /var/log/pihole/pihole.log |grep "raspi4.lan"
Jul 25 02:58:17 dnsmasq[1125]: query[A] raspi4.lan from 192.168.178.7
Jul 25 02:58:17 dnsmasq[1125]: Pi-hole hostname raspi4.lan is 192.168.178.100
Jul 25 02:58:17 dnsmasq[1125]: query[A] raspi4.lan from 192.168.178.7
Jul 25 02:58:17 dnsmasq[1125]: Pi-hole hostname raspi4.lan is 192.168.178.100
Jul 25 02:58:17 dnsmasq[1125]: query[A] raspi4.lan from 192.168.178.7
Jul 25 02:58:17 dnsmasq[1125]: Pi-hole hostname raspi4.lan is 192.168.178.100
Jul 25 02:58:36 dnsmasq[1125]: raspi4.lan   192.168.178.8     4FRI   H     /etc/hosts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here I tilted a bit. Why does the log state &lt;em&gt;"Pi-hole hostname raspi4.lan is 192.168.178.100"&lt;/em&gt; but then, just seconds later state that the IP retrieved from /etc/hosts is 192.168.178.8? At least the IP 192.168.178.100 was reliably returned for all queries towards IPs on the eth0 interface of &lt;code&gt;raspi4.lan&lt;/code&gt; with the goal to resolve the name &lt;code&gt;raspi4.lan&lt;/code&gt;. It wasn't some kind of race-condition nor did it feel like a bug.&lt;/p&gt;
&lt;p&gt;And just to be sure, I tried logging into raspi4.lan from lanadmin.lan:&lt;/p&gt;
&lt;pre class="language-sh line-numbers"&gt;&lt;code&gt;user@lanadmin:~$ ssh raspi4.lan
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Please contact your system administrator.
Add correct host key in /home/user/.ssh/known_hosts to get rid of this message.
Offending ED25519 key in /home/user/.ssh/known_hosts:16
  remove with:
  ssh-keygen -f '/home/user/.ssh/known_hosts' -R 'raspi4.lan'
Host key for raspi4.lan has changed and you have requested strict checking.
Host key verification failed.
user@lanadmin:~$ host raspi4.lan
raspi4.lan has address 192.168.178.100
raspi4.lan has IPv6 address fd6d:ULA:ffff&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nope, still the same problem. Only that this time I would have actually logged on to the right host, as the IP was currently owned by raspi4.lan. Nonetheless did the hostname still resolve to the wrong IP.&lt;/p&gt;
&lt;p&gt;I had no idea why the IP kept changing. Yes, I had provided no specific IP for pihole-FTL to listen on, but this should never lead to such an behaviour. I decided to read through&amp;nbsp;&lt;code&gt;/etc/pihole/pihole.toml&lt;/code&gt;, if only to set a specific listener address and do a bit more troubleshooting.&lt;/p&gt;
&lt;h2&gt;Finally: Enlightenment&lt;/h2&gt;
&lt;p&gt;Revelation hit me, when I read the &lt;code&gt;[dns.reply.host]&lt;/code&gt; block. The describe behaviour matched the observed one perfectly!&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;    [dns.reply.host]
      # Use a specific IPv4 address for the Pi-hole host? By default, FTL determines the
      # address of the interface a query arrived on and uses this address for replying to A
      # queries with the most suitable address for the requesting client.
      #
      # This setting can be used to use a fixed, rather than the dynamically obtained,
      # address when Pi-hole responds to the following names:
      # - "pi.hole"
      # - "&amp;lt;the device's hostname&amp;gt;"
      # - "pi.hole.&amp;lt;local domain&amp;gt;"
      # - "&amp;lt;the device's hostname&amp;gt;.&amp;lt;local domain&amp;gt;"
      #
      # Allowed values are:
      #     true or false
      force4 = false

      # Custom IPv4 address for the Pi-hole host
      #
      # Allowed values are:
      #     A valid IPv4 address or empty string ("")
      IPv4 = ""

      # Use a specific IPv6 address for the Pi-hole host? See description for the IPv4
      # variant above for further details.
      #
      # Allowed values are:
      #     true or false
      force6 = false

      # Custom IPv6 address for the Pi-hole host
      #
      # Allowed values are:
      #     A valid IPv6 address or empty string ("")
      IPv6 = ""&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As it can be clearly seen, static IPs for the local hostname were disabled. This made FTL choose a new "best matching" IP for each received query. Which is such a strange mechanism to implement! Why obscure such things!?&lt;/p&gt;
&lt;h2&gt;Why design such a ... mechanism?&lt;/h2&gt;
&lt;p&gt;And then it hit me.. Novice and inexperienced users. Pi-hole is a DNS and Ad-Blocker. Primarily aimed at home users. And those lack knowledge and experience. I'm a frequent reader of subreddits like &lt;a href="https://www.reddit.com/r/selfhosted/" target="_blank" rel="noopener"&gt;r/selfhosted&lt;/a&gt; or &lt;a href="https://www.reddit.com/r/HomeServer/" target="_blank" rel="noopener"&gt;r/HomeServer&lt;/a&gt; so I know full well how many users struggle with IPs, interface bindings, file rights, etc. All the basic stuff one learns over time but can be pretty hard for people new to Linux.&lt;/p&gt;
&lt;p&gt;I suspect this mechanism was developed to ease the usage of Pi-hole, to just "make it work" no matter what. Alas.. This caused way more trouble for an experienced user this way. And this is why I don't really like that they implemented this mechanism. It's just one of these automatisms which ignore standards and work without following an established process. Effectively hindering novice users to learn &lt;em&gt;"How it is normally done"&lt;/em&gt;?&lt;/p&gt;
&lt;h2&gt;&lt;a id="the-solution"&gt;&lt;/a&gt;The solution&lt;/h2&gt;
&lt;p&gt;The fix was rather easy. Just enable &lt;code&gt;dns.reply.host&lt;/code&gt; for IPv4 and IPv6 and set the corresponding IPs. Then restart the service. Done. Below is the config for &lt;code&gt;raspi4.lan&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="line-numbers language-sh"&gt;&lt;code&gt;    [dns.reply.host]
      # Use a specific IPv4 address for the Pi-hole host? By default, FTL determines the
      # address of the interface a query arrived on and uses this address for replying to A
      # queries with the most suitable address for the requesting client.
      #
      # This setting can be used to use a fixed, rather than the dynamically obtained,
      # address when Pi-hole responds to the following names:
      # - "pi.hole"
      # - "&amp;lt;the device's hostname&amp;gt;"
      # - "pi.hole.&amp;lt;local domain&amp;gt;"
      # - "&amp;lt;the device's hostname&amp;gt;.&amp;lt;local domain&amp;gt;"
      #
      # Allowed values are:
      #     true or false
      force4 = true ### CHANGED, default = false

      # Custom IPv4 address for the Pi-hole host
      #
      # Allowed values are:
      #     A valid IPv4 address or empty string ("")
      IPv4 = "192.168.178.8" ### CHANGED, default = ""

      # Use a specific IPv6 address for the Pi-hole host? See description for the IPv4
      # variant above for further details.
      #
      # Allowed values are:
      #     true or false
      force6 = true ### CHANGED, default = false

      # Custom IPv6 address for the Pi-hole host
      #
      # Allowed values are:
      #     A valid IPv6 address or empty string ("")
      IPv6 = "fd6d:ULA:8" ### CHANGED, default = ""&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Please note that I obscured parts of my ULA IPv6 address.&lt;/p&gt;
&lt;h2&gt;Why didn't I notice sooner?&lt;/h2&gt;
&lt;p&gt;Then there is always this question which creeps into ones mind: Why didn't I notice it sooner? Why did it work for so long?&lt;/p&gt;
&lt;p&gt;At least in this case the answer is simple: I rarely need to login into these systems.&lt;/p&gt;
&lt;h2&gt;Other improvements&lt;/h2&gt;
&lt;h3&gt;Dummy network interface for VIPs&lt;/h3&gt;
&lt;p&gt;Apparently it is also better to create a dummy network device and let keepalived bind the VIPs to that interface. One big advantage is that dummy interfaces don't reply to ARP-Requests at all. Which is a crucial problem in HA setup. And also one point WunderTech didn't mention with one word in his tutorial..&lt;/p&gt;
&lt;h3&gt;VRRP-Scripts to check DNS service availbility/healthiness&lt;/h3&gt;
&lt;p&gt;In the current setup keepalived will only switch to another machine if it stops sending out VRRP-Announcements, which usually only happens when a machine fails completely (power cut or really catastrophic failures). If just the pihole-FTL service, Unbound or any other piece of software - apart from keepalived - fails nothing will happen.&lt;/p&gt;
&lt;p&gt;For this, &lt;a href="https://keepalived.org/documentation/keepalived-conf/#vrrp-scripts" target="_blank" rel="noopener"&gt;keepalived supports the execution of VRRP-scripts&lt;/a&gt;. These will be executed every few seconds and are there to check service availbility/healthiness and trigger a failover if the script execution fails or doesn't exit successfully.&lt;/p&gt;
&lt;p&gt;I plan to write a blog post about that too. When it is ready, I will link it here.&lt;/p&gt;</description>
      <pubDate>Fri, 24 Jul 2026 20:59:56 +0200</pubDate>
      <guid isPermaLink="false">226b31042e0757598dbfb0d272dfa54d</guid>
    </item>
    <item>
      <title>Finally installed OPNsense on my Edge4Go</title>
      <link>https://admin.brennt.net/finally-installed-opnsense-on-my-edge4go</link>
      <image>https://admin.brennt.net/bl-content/uploads/pages/0a60ccda272a8f77832e19523f40c034/opnsense-png.png</image>
      <description>&lt;p&gt;A little while ago Kevin mentioned he saw the &lt;a href="https://www.thomas-krenn.com/en/products/low-energy-systems/edge4go-8gb" target="_blank" rel="noopener"&gt;Edge4Go OPNsense hardware from Thomas Krenn&lt;/a&gt; at a Linux convention. And yeah, it looked nice. The specs are really nice compared to the price. Especially the Intel Celeron which beats most China-hardware sold on Amazon.&lt;/p&gt;
&lt;p&gt;The problem? Thomas Krenn only sells to other businesses (B2B). Luckily my friends over at &lt;a href="https://www.qwertiko.de/" target="_blank" rel="noopener"&gt;qwertiko&lt;/a&gt; were so nice to order one and sell it to me.&lt;/p&gt;
&lt;p&gt;And today I finally installed OPNsense on my Edge4Go.&lt;/p&gt;
&lt;p&gt;The general idea is to have OPNsense between my DSL-Router and all my LAN devices having more control over the network and isolating devices like my TV or certain IoT devices.&lt;/p&gt;
&lt;p&gt;So expect a few more OPNsense posts in the coming weeks. 😅&lt;/p&gt;
&lt;p&gt;&lt;a href="https://admin.brennt.net/bl-content/uploads/pages/0a60ccda272a8f77832e19523f40c034/opnsense-png.png"&gt;&lt;/a&gt;&lt;a href="https://admin.brennt.net/bl-content/uploads/pages/0a60ccda272a8f77832e19523f40c034/opnsense-png.png" target="_blank" rel="noopener"&gt;&lt;img src="https://admin.brennt.net/bl-content/uploads/pages/0a60ccda272a8f77832e19523f40c034/opnsense-png.png" alt="" role="presentation" style="max-width: 100%; height: auto;"&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Mon, 20 Jul 2026 23:42:53 +0200</pubDate>
      <guid isPermaLink="false">0a60ccda272a8f77832e19523f40c034</guid>
    </item>
  </channel>
</rss>
