<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>– teqqy</title><link>https://www.teqqy.de/en/tags/homelab/</link><description>A blog about technology and more</description><language>en</language><managingEditor>teqqy</managingEditor><lastBuildDate>Sun, 26 Jul 2026 07:23:07 +0000</lastBuildDate><generator>Hugo 0.164.0</generator><atom:link href="https://www.teqqy.de/en/tags/homelab/index.xml" rel="self" type="application/rss+xml"/><item><title>Selfhosted Tools (Almost) Nobody Knows About</title><link>https://www.teqqy.de/en/selfhosted-tools-nobody-knows/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0200</pubDate><lastBuildDate>Thu, 23 Jul 2026 00:00:00 +0200</lastBuildDate><guid isPermaLink="true">https://www.teqqy.de/en/selfhosted-tools-nobody-knows/</guid><description>Over the past few years I&amp;rsquo;ve written the occasional post about my top 10 apps for my homelab . This time I wanted to write a slightly different post. After all, the top 10 always end up looking pretty similar, especially once you look at what other homelab folks are posting. So I figured I&amp;rsquo;d introduce you to a few tools that not everyone has running on their home network. Maybe it&amp;rsquo;s some inspiration for you.</description><content:encoded>&lt;![CDATA[<p>Over the past few years I&rsquo;ve written<a href="/en/my-top-10-selfhosted-and-homelab-software-2025-favorite-tools-for-the-new-year/">the occasional post about my top 10 apps for my homelab</a>
. This time I wanted to write a slightly different post. After all, the top 10 always end up looking pretty similar, especially once you look at what other homelab folks are posting. So I figured I&rsquo;d introduce you to a few tools that not everyone has running on their home network. Maybe it&rsquo;s some inspiration for you.</p><h2 id="autokuma">AutoKuma<a href="#autokuma" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>Almost everyone knows Uptime Kuma. What I don&rsquo;t like is having to configure everything by clicking through the UI, especially when the work is basically repetitive. With<a href="https://github.com/BigBoot/AutoKuma" target="_blank" rel="noopener noreferrer">AutoKuma<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
you can configure Uptime Kuma directly via Docker labels. That eliminates the extra configuration step after starting the app. This is especially handy if, like me,<a href="/en/gitops-without-komodo/">you run GitOps</a>
: the availability monitoring gets set up right along with the deployment of the application. Naturally all the usual Uptime Kuma monitor types work here too.</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">yaml</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="yaml"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">labels</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">"kuma.strava.http.name='Strava Statistics'"</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">"kuma.strava.http.url=https://strava.casalani.de"</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">"kuma.strava.http.parent_name=apps"</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">"kuma.strava.http.max_retries=5"</span></span></span></code></pre></div></div><p>That&rsquo;s a small example from Strava Statistics. In the end it&rsquo;s easy to carry over to other systems with a simple copy &amp; paste.</p><div class="video-embed" data-src="https://videos.teqqy.de/videos/embed/2WdshixFScUbm6ukUA9AUK?autoplay=1&warningTitle=0"><button class="video-embed-trigger" type="button" data-umami-event="Video Load" data-umami-event-provider="PeerTube" data-umami-event-url="https://videos.teqqy.de/videos/embed/2WdshixFScUbm6ukUA9AUK"><svg class="video-embed-play-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" width="48" height="48"><circle cx="12" cy="12" r="12" fill="rgba(0,0,0,0.6)"/><path d="M9.5 7.5l7 4.5-7 4.5V7.5z" fill="white"/></svg><span class="video-embed-title">AutoKuma: automating Uptime Kuma monitoring with Docker labels</span><span class="video-embed-note">PeerTube &middot; Click to load</span></button></div><p>AutoKuma itself is of course just a Docker container, so it can easily be started alongside everything else on any host.</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">yaml</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="yaml"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#7ee787">services</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">autokuma</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">image</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">ghcr.io/bigboot/autokuma:2.0.0</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">restart</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">unless-stopped</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">environment</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">AUTOKUMA__KUMA__URL</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">https://uptimekuma.example.com</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">AUTOKUMA__KUMA__USERNAME</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">kumaadmin</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">AUTOKUMA__KUMA__PASSWORD</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">STRENGGEHEIMHESPASSWORD</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">volumes</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">/var/run/docker.sock:/var/run/docker.sock</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">./autokuma:/data</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">labels</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">"kuma.apps.group.name=Applications"</span></span></span></code></pre></div></div><p>And really, that&rsquo;s about all there is to it.</p><h2 id="patchmon">Patchmon<a href="#patchmon" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>I discovered<a href="https://github.com/PatchMon/PatchMon" target="_blank" rel="noopener noreferrer">Patchmon<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
a while ago. This still fairly young piece of software can handle patch management for your Linux and Windows systems. On top of that, a small vulnerability scanner can show you various hardening measures directly – though implementing them is of course up to you.</p><p>The Docker Compose stack for Patchmon is a bit bigger, since it consists of the app, a database, Redis, and Guacamole. The compose file provided by the vendor works really well though; I took it, including the accompanying .env file, pretty much as-is and barely had to adjust anything.</p><p><img src="/en/selfhosted-tools-nobody-knows/patchmon-dashboard_hu_4b70651a58312a66.webp" srcset="/en/selfhosted-tools-nobody-knows/patchmon-dashboard_hu_a563d2283a29324a.webp 384w, /en/selfhosted-tools-nobody-knows/patchmon-dashboard_hu_4b70651a58312a66.webp 768w, /en/selfhosted-tools-nobody-knows/patchmon-dashboard_hu_b05ca7c79d8ce5f5.webp 1536w" sizes="(max-width: 768px) 100vw, 768px" alt="Patchmon dashboard" loading="eager" fetchpriority="high" decoding="async" width="768" height="408"/><h2 id="adguardhome-sync">AdGuardHome-Sync<a href="#adguardhome-sync" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>For a while I ran several physical servers, each with its own<a href="/en/3-dns-blocklists-for-pihole-adguard-or-blocky/">AdGuard Home instance</a>
. These days only<a href="/en/save-power-with-proxmox-4-tips/">one Proxmox host</a>
is left, but several AdGuard instances are still running on it.</p><p>Here too, I don&rsquo;t want to make every change manually, and definitely not twice. At some point I noticed that I&rsquo;d added a new filter list to one instance, and only days later, while debugging a completely unrelated problem, realized the second instance had never picked it up. That&rsquo;s exactly the kind of forgetting<a href="https://github.com/bakito/adguardhome-sync" target="_blank" rel="noopener noreferrer">AdGuardHome-Sync<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
solves. If I change the blocklists or other settings on one host, they&rsquo;re synced over to the second host shortly after.</p><p>The whole thing is configured through a single YAML file with an origin and replica definition:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">yaml</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="yaml"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#7ee787">cron</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">"*/10 * * * *"</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#7ee787">origin</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">url</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">https://adguard-1.example.com</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">username</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">admin</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">password</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">STRENGGEHEIMESPASSWORD</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#7ee787">replicas</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#7ee787">url</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">https://adguard-2.example.com</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">username</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">admin</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">password</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">STRENGGEHEIMESPASSWORD</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">autoSetup</span>:<span style="color:#6e7681"/><span style="color:#79c0ff">true</span></span></span></code></pre></div></div><p>The<code>autoSetup</code> parameter is particularly handy: if a brand-new AdGuard Home instance joins the mix, the replica gets set up automatically on the first sync, instead of me having to click through the setup wizard by hand.</p><h2 id="opencloud">OpenCloud<a href="#opencloud" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p><a href="https://github.com/opencloud-eu/opencloud" target="_blank" rel="noopener noreferrer">OpenCloud<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
is my (theoretically) leaner alternative to<a href="/nextcloud-docker-tutorial-step-by-step-zum-erfolg/">Nextcloud</a>
. I ran the latter for many months using the all-in-one container setup. It was definitely stable – unlike a lot of other people, I couldn&rsquo;t really complain about it. But at some point, running a whole extra VM with the big Nextcloud stack just to manage a handful of files I want to access from outside my network started to feel like overkill.</p><p>A while ago OpenCloud kept coming up more and more in various communities, and I figured I&rsquo;d give it a shot. It needs noticeably fewer resources, but in exchange the basic setup is quite a bit more complicated, especially if, like me, you want to manage a lot through Single Sign-On. Setting the whole thing up with Authentik is already a bit more involved, which is why I might write a separate post about that at some point.</p><h2 id="meerkat">Meerkat<a href="#meerkat" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>Some of you have probably run into Monica as a CRM at some point. Unfortunately, development seems to have stalled, and its successor Chandler seems to have met the same fate. By contrast,<a href="https://github.com/fbuchner/meerkat-crm" target="_blank" rel="noopener noreferrer">Meerkat<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
feels pleasantly lean and actively maintained: it ships as a single container instead of a big stack, which lowers the bar quite a bit for actually keeping it alive long-term.</p><p>Meerkat is a system that lets you manage your family, friends, acquaintances, coworkers, and everyone else as contacts. And not just addresses or phone numbers, but relationships, all kinds of anniversaries, and above all personal information about each person. I&rsquo;ve for example noted down that my brother-in-law can&rsquo;t eat peanuts, because there&rsquo;s no way I&rsquo;d remember that on my own by the next barbecue. For people like me, who struggle to keep track of a lot of information, it&rsquo;s a fantastic system. As long as you actually keep it up to date&hellip;</p><p>Through the optional CardDAV server you can even sync the contacts to your phone. That turns Meerkat from just a digital notepad into something that can genuinely replace your address book.</p><h2 id="sparkyfitness">SparkyFitness<a href="#sparkyfitness" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>Apps for tracking calorie burn, activities, and the like are a dime a dozen. Unfortunately most of them cost a fair bit of money these days. If you&rsquo;re chasing a very ambitious fitness goal, those apps might be exactly right for you. For me, someone who just wants a bit of an overview, that&rsquo;s an expense I don&rsquo;t really need.</p><p>Luckily I came across<a href="https://github.com/CodeWithCJ/SparkyFitness" target="_blank" rel="noopener noreferrer">SparkyFitness<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
a while back. The developer describes the app as a selfhosted MyFitnessPal alternative, and I&rsquo;d agree with that. The app can really do a lot. The downside is that onboarding, or rather the first few steps, are fairly tricky. You really have to sit down and carefully enter the foods you eat.</p><h2 id="adventurelog">AdventureLog<a href="#adventurelog" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>With<a href="https://github.com/seanmorley15/AdventureLog" target="_blank" rel="noopener noreferrer">AdventureLog<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
you can either plan your trips or just track them. I only use it for the latter. In AdventureLog I&rsquo;ve logged all the trips I&rsquo;ve taken with my partner.</p><p>One really nice feature in AdventureLog is how your trips are displayed on a map. I printed it out and hung it on a wall in our living room together with various vacation photos, so guests can guess which photo belongs to which point on the map.</p><p><img src="/en/selfhosted-tools-nobody-knows/adventurelog-karte_hu_3d875dcf5f184548.webp" srcset="/en/selfhosted-tools-nobody-knows/adventurelog-karte_hu_10d9ba8b7ebfb516.webp 384w, /en/selfhosted-tools-nobody-knows/adventurelog-karte_hu_3d875dcf5f184548.webp 768w, /en/selfhosted-tools-nobody-knows/adventurelog-karte_hu_8773850ba85b8216.webp 1536w" sizes="(max-width: 768px) 100vw, 768px" alt="Map view of our trips in AdventureLog" loading="lazy" fetchpriority="auto" decoding="async" width="768" height="410"/><h2 id="conclusion">Conclusion<a href="#conclusion" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>Seven tools that hopefully aren&rsquo;t already on every other homelab top-10 list – from monitoring to patch management to a digital address book. Some of them I&rsquo;m confident are here to stay (AutoKuma and AdGuardHome-Sync have been running quietly in the background for months), while with others like OpenCloud or SparkyFitness I&rsquo;m still trying things out myself.</p><p>What almost all of them have in common: they solve one specific problem really well, instead of trying to be a giant all-in-one solution. Those are exactly the kind of tools that tend to get lost in the usual top-10 lists – even though they&rsquo;re often the ones you end up appreciating most in everyday use.</p><p>Is there something running on your own network that hardly anyone talks about? Feel free to let me know – it might just turn into a second part of this list.</p>
]]></content:encoded><category>selfhosted</category><category>homelab</category></item><item><title>Teslamate Setup: Docker, Fleet API &amp; Grafana</title><link>https://www.teqqy.de/en/teslamate-setup-docker-fleet-api-grafana/</link><pubDate>Sat, 18 Jul 2026 00:00:00 +0000</pubDate><lastBuildDate>Sat, 18 Jul 2026 00:00:00 +0000</lastBuildDate><guid isPermaLink="true">https://www.teqqy.de/en/teslamate-setup-docker-fleet-api-grafana/</guid><description>Update, December 2025: This article has been revised to reflect the recent changes to the Tesla Fleet API and current Teslamate versions.
Modern vehicles now offer API interfaces that let you pull all kinds of data. Tesla was one of the first manufacturers to expose this comprehensively, and software like Teslamate grew out of that. Teslamate is a self-hosted, open-source data logger that continuously captures your Tesla&amp;rsquo;s data, stores it, and visualizes it with Grafana.</description><content:encoded>&lt;![CDATA[<p><strong>Update, December 2025:</strong> This article has been revised to reflect the recent changes to the Tesla Fleet API and current Teslamate versions.</p><p>Modern vehicles now offer API interfaces that let you pull all kinds of data. Tesla was one of the first manufacturers to expose this comprehensively, and software like Teslamate grew out of that. Teslamate is a self-hosted, open-source data logger that continuously captures your Tesla&rsquo;s data, stores it, and visualizes it with Grafana.</p><h2 id="what-changed-in-2025">What changed in 2025?<a href="#what-changed-in-2025" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>The key updates at a glance:</p><p><strong>Tesla Fleet API:</strong> Tesla replaced the unofficial &ldquo;Owner API&rdquo; with the official Fleet API. Since February 2025, a pay-per-use model applies, with a $10/month free allowance. For private users with 1-2 vehicles, that allowance is usually enough; heavier usage adds roughly $2-5/month.</p><p><strong>PostgreSQL 17:</strong> Teslamate now requires at least PostgreSQL 16.7 or 17.3. The developers recommend PostgreSQL 17.</p><p><strong>Fleet Telemetry:</strong> New is the option for the vehicle to stream data directly. This is more precise and cheaper than classic polling, but more complex to set up. Fleet Telemetry requires your own server endpoint and TLS certificates — the vehicle then streams data via WebSocket whenever values change. That saves on API costs since you no longer have to poll actively, and you get high-frequency data with minimal latency, especially while driving. For most users, though, the tried-and-tested polling approach still works fine and is considerably easier to set up.</p><h2 id="teslamate--install-it-yourself-or-pay-for-a-service">Teslamate – Install It Yourself, or Pay for a Service?<a href="#teslamate--install-it-yourself-or-pay-for-a-service" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>Teslamate isn&rsquo;t a software-as-a-service — you install and run it yourself. Whether that&rsquo;s on your home homelab or on a VPS in the cloud, e.g. with<a href="https://www.netcup.de/?ref=60644" target="_blank" rel="noopener noreferrer">Netcup<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
, both work fine. I host it on my own server on my home network.</p><p>Installation is straightforward with<a href="/wordpress-docker-performance/">Docker</a>
. You need a database (PostgreSQL) and the application container. For visualization, Teslamate relies on Grafana with pre-configured dashboards.</p><h2 id="prerequisites">Prerequisites<a href="#prerequisites" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p><strong>Hardware &amp; software:</strong></p><ul><li>A server with at least 2 GB RAM (Raspberry Pi 4/5, NAS, VPS)</li><li>Docker and Docker Compose installed</li><li>A permanently active internet connection</li></ul><p><strong>Tesla account:</strong></p><ul><li>Tesla account with two-factor authentication</li><li>A Tesla Developer account (free at<a href="https://developer.tesla.com" target="_blank" rel="noopener noreferrer">developer.tesla.com<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
)</li></ul><h2 id="setting-up-a-tesla-developer-account">Setting Up a Tesla Developer Account<a href="#setting-up-a-tesla-developer-account" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>Since the switch to the Fleet API, you&rsquo;ll need a developer account:</p><ol><li>Sign in at<a href="https://developer.tesla.com" target="_blank" rel="noopener noreferrer">developer.tesla.com<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
with your Tesla account</li><li>Create a new application (e.g. &ldquo;Teslamate Homelab&rdquo;)</li><li>Provide a short description</li><li>For local use, you can use localhost as the domain</li></ol><p>Review takes a few days; for private use, approval is usually straightforward. The monthly $10 allowance is real credit — you don&rsquo;t need to add a credit card as long as you stay under it.</p><h2 id="creating-tokens">Creating Tokens<a href="#creating-tokens" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>Authentication used to work with your username and password. Today you need a &ldquo;Refresh Token&rdquo; and an &ldquo;Access Token.&rdquo; The easiest way to generate these is with dedicated apps:</p><ul><li><strong>iOS:</strong> &ldquo;Auth for Tesla&rdquo; (Apple App Store)</li><li><strong>Android:</strong> &ldquo;Tesla Tokens&rdquo; or &ldquo;Auth for Tesla&rdquo;</li><li><strong>Windows/Mac:</strong> &ldquo;Tesla Auth&rdquo; or web-based tools</li></ul><p><strong>Steps:</strong></p><ol><li>Install the app and sign in with your Tesla username</li><li>Enter your password</li><li>At the second prompt, enter your 2FA code (not your password again!)</li><li>The app shows a Refresh Token and Access Token — keep both somewhere safe</li></ol><p>The tokens stay valid for several months. Teslamate refreshes the access token automatically.</p><p><img src="/en/teslamate-setup-docker-fleet-api-grafana/images/teslamate-tesla-token_hu_b53628674ab82b23.webp" srcset="/en/teslamate-setup-docker-fleet-api-grafana/images/teslamate-tesla-token_hu_ecfb7a72ba31093e.webp 384w, /en/teslamate-setup-docker-fleet-api-grafana/images/teslamate-tesla-token_hu_b53628674ab82b23.webp 768w" sizes="(max-width: 768px) 100vw, 768px" alt="" loading="eager" fetchpriority="high" decoding="async" width="768" height="595"/><h2 id="installation-with-docker-compose">Installation with Docker Compose<a href="#installation-with-docker-compose" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>The easiest way to install Teslamate is via Docker Compose. If you don&rsquo;t already have Docker Compose installed:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># On Ubuntu/Debian</span></span></span><span style="display:flex;"><span>sudo apt install docker-compose-plugin</span></span><span style="display:flex;"><span>docker compose version</span></span></code></pre></div></div><p>Create a working directory:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir ~/teslamate<span style="color:#ff7b72;font-weight:bold">&amp;&amp;</span> cd ~/teslamate</span></span></code></pre></div></div><p>I&rsquo;ll point you to the<a href="https://docs.teslamate.org/docs/installation/docker" target="_blank" rel="noopener noreferrer">official documentation<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
here, since the configuration changes fairly regularly.</p><p><strong>Important adjustments for 2025:</strong></p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">yaml</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="yaml"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#7ee787">version</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">"3"</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#7ee787">services</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">teslamate</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">image</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">teslamate/teslamate:latest</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">restart</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">always</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">environment</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">ENCRYPTION_KEY=your_secure_key</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">DATABASE_USER=teslamate</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">DATABASE_PASS=secure_password</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">DATABASE_NAME=teslamate</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">DATABASE_HOST=database</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">TZ=Europe/Berlin</span><span style="color:#6e7681"/><span style="color:#8b949e;font-style:italic"># Your timezone!</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">MQTT_DISABLE=true</span><span style="color:#6e7681"/><span style="color:#8b949e;font-style:italic"># If you don't need MQTT</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">ports</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">4000</span>:<span style="color:#a5d6ff">4000</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">cap_drop</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">all</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">database</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">image</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">postgres:17</span><span style="color:#6e7681"/><span style="color:#8b949e;font-style:italic"># At least 16.7 or 17.3!</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">restart</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">always</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">environment</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">POSTGRES_USER=teslamate</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">POSTGRES_PASSWORD=secure_password</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">POSTGRES_DB=teslamate</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">volumes</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">teslamate-db:/var/lib/postgresql/data</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">grafana</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">image</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">teslamate/grafana:latest</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">restart</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">always</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">environment</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">DATABASE_USER=teslamate</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">DATABASE_PASS=secure_password</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">DATABASE_NAME=teslamate</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">DATABASE_HOST=database</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">ports</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">3000</span>:<span style="color:#a5d6ff">3000</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">volumes</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">teslamate-grafana-data:/var/lib/grafana</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#7ee787">volumes</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">teslamate-db</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#a5d6ff">teslamate-grafana-data:</span></span></span></code></pre></div></div><p><strong>MQTT for Home Assistant:</strong></p><p>If you want to forward the data to an MQTT server (e.g. for<a href="/tesla-ueberschussladen-mit-home-assistant/">Home Assistant</a>
), replace<code>MQTT_DISABLE=true</code> with<code>MQTT_HOST=mosquitto</code> and add a Mosquitto container.</p><p><strong>Start the containers:</strong></p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose up -d</span></span></code></pre></div></div><p>Teslamate is then reachable at<code>http://{your-server-ip}:4000</code>. On first launch, enter the tokens you generated earlier. After 1-2 minutes (waking the vehicle via the app may help), you should start seeing data.</p><h2 id="analysis-with-grafana">Analysis with Grafana<a href="#analysis-with-grafana" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>A big advantage of the pre-built Docker Compose file: Grafana is included out of the box, reachable by default at<code>http://{your-server-ip}:3000</code>.</p><p>Default login (change immediately!):</p><ul><li>Username:<code>admin</code></li><li>Password:<code>admin</code></li></ul><p><a href="/docker-monitoring-mit-prometheus-und-grafana/">Grafana</a>
already has the PostgreSQL database configured as a data source, and all dashboards are pre-imported. If you&rsquo;re running your own separate Grafana instance, you&rsquo;ll need to import the JSON files manually from the<a href="https://www.github.com/cbirkenbeul" target="_blank" rel="noopener noreferrer">GitHub<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
repository.</p><p><strong>Key dashboards:</strong></p><ul><li><strong>Overview:</strong> an overall summary of all metrics — the perfect starting point</li><li><strong>Drives:</strong> trip analysis with interactive maps, showing route, speed, and consumption</li><li><strong>Charges:</strong> detailed charging history with cost analysis per charge, and comparisons between charging stations</li><li><strong>Battery Health:</strong> long-term battery capacity trends, showing degradation over time and mileage</li><li><strong>Efficiency:</strong> consumption analysis by speed, temperature, and elevation — see what&rsquo;s actually affecting your range</li><li><strong>Vampire Drain:</strong> standby consumption while parked, useful for spotting phantom drain</li><li><strong>Locations:</strong> frequently visited places, with automatic geofencing detection</li><li><strong>Updates:</strong> a history of every software update, with timestamps</li></ul><h2 id="long-term-use-and-insights">Long-term Use and Insights<a href="#long-term-use-and-insights" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>The real value shows up after several months. Here are some concrete examples from my own usage:</p><p><strong>Battery degradation:</strong> my Model Y showed about 4% capacity loss after 50,000 km. 3-5% in the first 50,000 km is typical, and degradation slows noticeably afterward. With Teslamate you can see the exact trend and spot outliers (e.g. from BMS calibration).</p><p><strong>Seasonal differences:</strong> in winter (around 0°C), my consumption sits at roughly 20-22 kWh/100km; in summer it&rsquo;s 15-17 kWh/100km — about 30% more due to heating and a cold battery. That data is genuinely useful for route planning.</p><p><strong>Cost optimization:</strong> home charging (€0.30/kWh) vs. Supercharger (€0.52/kWh) — the difference adds up. At 15,000 km a year, I save roughly €400 annually by charging at home. Even better:<a href="/tesla-ueberschussladen-mit-home-assistant/">charging with solar surplus</a>
brings the cost down to about €0.08/kWh.</p><p><strong>Efficiency:</strong> surprisingly, my optimal speed range turned out to be 90-110 km/h, not 70 km/h as I expected. Below 70 km/h, relative consumption creeps back up slightly due to auxiliary systems.</p><h2 id="troubleshooting">Troubleshooting<a href="#troubleshooting" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p><strong>Teslamate shows &ldquo;offline&rdquo; instead of &ldquo;asleep&rdquo;:</strong>
That&rsquo;s expected behavior since Tesla firmware 2024.8+ — Tesla changed the API response, it&rsquo;s not a Teslamate bug.</p><p><strong>Token isn&rsquo;t accepted:</strong>
Use the Refresh Token (the longer of the two strings), not the Access Token.</p><p><strong>No data:</strong>
Check the Docker logs with<code>docker compose logs teslamate</code>. Common causes: the vehicle is in deep sleep, wrong timezone, or network issues.</p><p><strong>High API costs:</strong>
Reduce the polling frequency, switch to Fleet Telemetry, or check for unnecessary wake commands.</p><h2 id="security-and-backup">Security and Backup<a href="#security-and-backup" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p><strong>Network:</strong></p><ul><li>Don&rsquo;t expose it directly to the internet</li><li>Use a reverse proxy with HTTPS (Nginx, Caddy, Traefik)</li><li>Enable firewall rules</li></ul><p><strong>Backup:</strong></p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose exec database pg_dump -U teslamate teslamate &gt; backup.sql</span></span></code></pre></div></div><p><strong>Updates:</strong></p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose pull</span></span><span style="display:flex;"><span>docker compose up -d</span></span></code></pre></div></div><h2 id="frequently-asked-questions">Frequently Asked Questions<a href="#frequently-asked-questions" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p><strong>Does Teslamate cost money?</strong>
The software itself is free. Tesla API costs apply, but are usually covered by the $10 free allowance. Only heavy usage adds roughly $2-5/month.</p><p><strong>Does it work with the new Fleet API?</strong>
Yes, fully compatible and kept up to date.</p><p><strong>Does it drain the battery?</strong>
No, the additional consumption is negligible.</p><p><strong>Can I track multiple vehicles?</strong>
Yes, each one is logged separately.</p><h2 id="conclusion">Conclusion<a href="#conclusion" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>For me, Teslamate is the best solution for in-depth vehicle data analysis. The upsides:</p><ul><li>Full data ownership through self-hosting</li><li>Detailed analysis with Grafana</li><li>Low cost thanks to the free API allowance</li><li>An active community</li><li>Great fit for Home Assistant integration</li></ul><p>The downsides: it requires technical know-how, your own server, and there&rsquo;s no official support.</p><p>For Tesla owners with a smart-home or homelab streak, Teslamate is an excellent piece of software. After a few months of use, the analytics get genuinely interesting, once trends and long-term patterns start to show. Even while driving, the Grafana dashboards surface plenty of interesting data.</p><h2 id="tips-for-advanced-users">Tips for Advanced Users<a href="#tips-for-advanced-users" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p><strong>Reverse proxy setup:</strong>
For secure access on the go, I&rsquo;d recommend a reverse proxy with a Let&rsquo;s Encrypt SSL certificate. Traefik or Caddy fully automate the process.</p><p><strong>Database optimization:</strong>
On very old installations, a manual vacuum of the database can help:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose exec database vacuumdb -U teslamate -d teslamate -z -v</span></span></code></pre></div></div><p><strong>Custom dashboards:</strong>
Grafana offers endless possibilities. You could, for example, build comparisons with other Tesla drivers, or visualize correlations between weather and consumption.</p><p><strong>Further reading:</strong></p><ul><li><a href="https://docs.teslamate.org" target="_blank" rel="noopener noreferrer">Teslamate Documentation<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a></li><li><a href="https://github.com/teslamate-org/teslamate" target="_blank" rel="noopener noreferrer">GitHub Repository<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a></li><li><a href="https://github.com/teslamate-org/teslamate/discussions" target="_blank" rel="noopener noreferrer">Community Forum<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a></li></ul>
]]></content:encoded><category>selfhosted</category><category>tesla</category><category>docker</category><category>grafana</category><category>homelab</category></item><item><title>GitOps without Komodo: Webhook-based Auto-Deployment for Docker Compose</title><link>https://www.teqqy.de/en/gitops-without-komodo/</link><pubDate>Wed, 03 Jun 2026 00:00:00 +0000</pubDate><lastBuildDate>Wed, 03 Jun 2026 00:00:00 +0000</lastBuildDate><guid isPermaLink="true">https://www.teqqy.de/en/gitops-without-komodo/</guid><description>If you&amp;rsquo;ve read my previous post on my 2025 homelab setup , you know I&amp;rsquo;ve been using Komodo (external link) as my GitOps tool for Docker Compose stacks. The concept is solid: Git as a single source of truth, Renovate for automated version updates, and Komodo deploying changes to the target system on every new commit. It mostly works – but Komodo has been driving me up the wall with one thing: file permissions. It simply doesn&amp;rsquo;t work consistently. Either the permissions on the host are off, or Komodo complains during deployment, or something in between. After the nth time dealing with it, I&amp;rsquo;d had enough and wanted something leaner.</description><content:encoded>&lt;![CDATA[<p>If you&rsquo;ve read my<a href="/selfhosted-setup-2025-mein-neuer-workflow-mit-proxmox-komodo-und-gitops/">previous post on my 2025 homelab setup</a>
, you know I&rsquo;ve been using<a href="https://komo.do/" target="_blank" rel="noopener noreferrer">Komodo<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
as my GitOps tool for Docker Compose stacks. The concept is solid: Git as a single source of truth, Renovate for automated version updates, and Komodo deploying changes to the target system on every new commit. It mostly works – but Komodo has been driving me up the wall with one thing: file permissions. It simply doesn&rsquo;t work consistently. Either the permissions on the host are off, or Komodo complains during deployment, or something in between. After the nth time dealing with it, I&rsquo;d had enough and wanted something leaner.</p><p>The requirement is straightforward: when Renovate pushes a new commit to the repo (after I merge the pull request), the compose file on the server should be updated and the affected containers restarted. No Kubernetes, no additional framework. Just git pull and docker compose up.</p><h2 id="the-basic-concept">The Basic Concept<a href="#the-basic-concept" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>The setup relies on three components working together:</p><ol><li><strong><a href="https://about.gitea.com/" target="_blank" rel="noopener noreferrer">Gitea<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a></strong> – my self-hosted Git server where the compose files live</li><li><strong><a href="https://docs.renovatebot.com/" target="_blank" rel="noopener noreferrer">Renovate<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a></strong> – checks the image tags in the compose files against the respective container registries every four hours via cronjob, and automatically opens pull requests when newer versions are available. I merge the PR, the commit lands on<code>main</code>.</li><li><strong><a href="https://github.com/adnanh/webhook" target="_blank" rel="noopener noreferrer">webhook<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a></strong> – a single Go binary that exposes an HTTP endpoint on the target server. Gitea fires a webhook on every push, which triggers the deploy script.</li></ol><p>The data flow looks like this:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label"/><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang=""><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><pre tabindex="0"><code>Renovate detects new image tag
→ PR in Gitea
→ Merge
→ Gitea fires webhook
→ webhook binary calls deploy.sh
→ git pull + docker compose up -d</code></pre></div><p>No polling, no daemon watching container registries, no framework with its own opinions about file permissions.</p><h2 id="why-not-just-watchtower-or-a-cron-script">Why Not Just Watchtower or a Cron Script?<a href="#why-not-just-watchtower-or-a-cron-script" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>Before reaching for webhook, I briefly considered two other approaches.</p><p><strong><a href="https://containrrr.dev/watchtower/" target="_blank" rel="noopener noreferrer">Watchtower<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a></strong> monitors running containers and pulls new images automatically. The problem: Watchtower reacts to new images in the registry, not to Git commits. When Renovate opens a PR and I merge it, Watchtower has no idea – it just pulls whenever it decides to check. That doesn&rsquo;t fit a setup where Git is supposed to be the single source of truth. I also want to control the merge timing, not Watchtower.</p><p><strong>A cron script with<code>git fetch</code></strong> would be the other option – check for changes every few minutes and deploy if there are any. It works, but has a conceptual drawback: it&rsquo;s polling. I already have Gitea infrastructure that can deliver push events, so I should use it. A webhook reacts within seconds; a cron job with a 5-minute interval adds unnecessary delay.</p><p>webhook is the cleanest approach: event-driven, no extra daemon, a single binary with no dependencies.</p><h2 id="repo-structure">Repo Structure<a href="#repo-structure" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>I have one repository per server:</p><ul><li><code>apps-stack</code> for<code>docker01</code> – running tools like<a href="https://docs.paperless-ngx.com/" target="_blank" rel="noopener noreferrer">Paperless-ngx<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
,<a href="https://immich.app/" target="_blank" rel="noopener noreferrer">Immich<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
,<a href="https://github.com/dani-garcia/vaultwarden" target="_blank" rel="noopener noreferrer">Vaultwarden<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
, and similar</li><li><code>media-stack</code> for<code>media01</code> – all the<code>*arr</code> containers and everything else in the media stack</li></ul><p>Each repo just contains the<code>compose.yaml</code> and any additional config files individual applications need. Nothing special.</p><h3 id="renovate-in-the-repos">Renovate in the Repos<a href="#renovate-in-the-repos" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h3><p>Renovate runs self-hosted and checks the image tags in the compose files every four hours via cronjob. A minimal<code>renovate.json</code> in the repo root is all it takes:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">json</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="json"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{</span></span><span style="display:flex;"><span><span style="color:#7ee787">"$schema"</span>:<span style="color:#a5d6ff">"https://docs.renovatebot.com/renovate-schema.json"</span>,</span></span><span style="display:flex;"><span><span style="color:#7ee787">"extends"</span>: [<span style="color:#a5d6ff">"config:base"</span>],</span></span><span style="display:flex;"><span><span style="color:#7ee787">"docker-compose"</span>: {</span></span><span style="display:flex;"><span><span style="color:#7ee787">"enabled"</span>:<span style="color:#79c0ff">true</span></span></span><span style="display:flex;"><span> }</span></span><span style="display:flex;"><span>}</span></span></code></pre></div></div><p>Renovate automatically detects image tags in the compose file and opens PRs when a newer version is available. For software that uses semantic versioning (<code>major.minor.patch</code>), this can be fine-tuned – for example, automatically merging patch updates while reviewing minor updates manually.</p><p>I exclude Postgres containers from Renovate. Major upgrades between Postgres versions require a manual database dump and restore – not something I want a bot to trigger automatically.</p><h2 id="installing-and-configuring-webhook">Installing and Configuring webhook<a href="#installing-and-configuring-webhook" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p><code>webhook</code> is available as a single binary, or as a package:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>apt install webhook</span></span></code></pre></div></div><p>The configuration lives at<code>/etc/webhook/webhook.conf</code>:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">yaml</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="yaml"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>-<span style="color:#7ee787">id</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">deploy</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">execute-command</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">/opt/scripts/deploy.sh</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">command-working-directory</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">/opt/media-stack</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">trigger-rule</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">match</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">type</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">payload-hmac-sha256</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">secret</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">"{{getenv \"WEBHOOK_SECRET\"}}"</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">parameter</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">source</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">header</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">name</span>:<span style="color:#6e7681"/><span style="color:#a5d6ff">X-Gitea-Signature</span></span></span></code></pre></div></div><p>A few notes on this:</p><p><strong>Header name:</strong> Gitea sends the signature as<code>X-Gitea-Signature</code> – without a<code>-256</code> suffix and without a<code>sha256=</code> prefix in the value. Sounds trivial, but it cost me a debugging session on the first try.</p><p><strong>Secret as environment variable:</strong> The HMAC secret doesn&rsquo;t belong in the config file in plaintext, especially if the file is versioned in the repo. So it&rsquo;s read from the environment via<code>getenv</code>.</p><p>Generate the secret:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>openssl rand -hex<span style="color:#a5d6ff">32</span></span></span></code></pre></div></div><p>Write it to a secrets file that only root can read:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># /etc/webhook/secrets.env</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">WEBHOOK_SECRET</span><span style="color:#ff7b72;font-weight:bold">=</span>your-generated-string</span></span></code></pre></div></div><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>chmod<span style="color:#a5d6ff">600</span> /etc/webhook/secrets.env</span></span></code></pre></div></div><h3 id="systemd-unit">Systemd Unit<a href="#systemd-unit" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h3><div class="code-block"><div class="code-block-header"><span class="code-lang-label">ini</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="ini"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># /etc/systemd/system/webhook.service</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">[Unit]</span></span></span><span style="display:flex;"><span>Description<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">Webhook Receiver</span></span></span><span style="display:flex;"><span>After<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">network.target</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#ff7b72">[Service]</span></span></span><span style="display:flex;"><span>User<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">teqqy # Replace with your own username</span></span></span><span style="display:flex;"><span>EnvironmentFile<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">/etc/webhook/secrets.env</span></span></span><span style="display:flex;"><span>ExecStart<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">/usr/bin/webhook -hooks /etc/webhook/webhook.conf -port 9000</span></span></span><span style="display:flex;"><span>Restart<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">on-failure</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#ff7b72">[Install]</span></span></span><span style="display:flex;"><span>WantedBy<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">multi-user.target</span></span></span></code></pre></div></div><p><code>User=teqqy</code> ensures the webhook daemon and the deploy script don&rsquo;t run as root. That&rsquo;s important to me – processes that only need to run git pull and docker compose up have no business running as root. Replace the username with your own; the user needs access to the repo directory and must be a member of the<code>docker</code> group.</p><p><code>EnvironmentFile</code> loads<code>secrets.env</code> and makes<code>WEBHOOK_SECRET</code> available as an environment variable before the process starts – so the secret flows cleanly into the<code>getenv</code> call in the config.<code>Restart=on-failure</code> ensures the daemon automatically restarts after an unexpected crash without manual intervention.</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>systemctl enable --now webhook</span></span></code></pre></div></div><h2 id="setting-up-the-gitea-webhook">Setting Up the Gitea Webhook<a href="#setting-up-the-gitea-webhook" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>In Gitea under<code>Repository → Settings → Webhooks → Add Webhook → Gitea</code>:</p><ul><li><strong>Target URL:</strong><code>http://media01.example.com:9000/hooks/deploy</code></li><li><strong>Secret:</strong> the same string you generated above</li><li><strong>Trigger:</strong> Only<code>Push</code> events are needed</li></ul><p>On every push, Gitea automatically computes an HMAC-SHA256 signature over the request body and sends it as the<code>X-Gitea-Signature</code> header. The webhook binary verifies the signature before executing the script – a simple Authorization header would be significantly less secure since it doesn&rsquo;t protect the payload against tampering.</p><p>Port 9000 shouldn&rsquo;t be exposed directly to the internet. In my setup, Gitea and the webhook daemon are on the same internal network and the port isn&rsquo;t reachable from outside. Anyone who needs to expose the endpoint for an external Gitea instance or GitHub should at minimum put it behind a reverse proxy with IP restrictions.</p><h2 id="the-deploy-script">The Deploy Script<a href="#the-deploy-script" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-weight:bold;font-style:italic">#!/bin/bash</span></span></span><span style="display:flex;"><span>set -euo pipefail</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">REPO</span><span style="color:#ff7b72;font-weight:bold">=</span>/opt/media-stack</span></span><span style="display:flex;"><span><span style="color:#79c0ff">LOG</span><span style="color:#ff7b72;font-weight:bold">=</span>/opt/deploy-logs/deploy-<span style="color:#ff7b72">$(</span>date +%Y%m%d-%H%M%S<span style="color:#ff7b72">)</span>.log</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span>exec &gt;&gt;<span style="color:#a5d6ff">"</span><span style="color:#79c0ff">$LOG</span><span style="color:#a5d6ff">"</span> 2&gt;&amp;<span style="color:#a5d6ff">1</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span>echo<span style="color:#a5d6ff">"=== Deploy</span><span style="color:#ff7b72">$(</span>date<span style="color:#ff7b72">)</span><span style="color:#a5d6ff"> ==="</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span>cd<span style="color:#a5d6ff">"</span><span style="color:#79c0ff">$REPO</span><span style="color:#a5d6ff">"</span></span></span><span style="display:flex;"><span>git pull origin main</span></span><span style="display:flex;"><span>docker compose up -d --remove-orphans</span></span><span style="display:flex;"><span>docker compose ps</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span>echo<span style="color:#a5d6ff">"=== Done ==="</span></span></span></code></pre></div></div><p><code>set -euo pipefail</code> ensures the script immediately aborts on any error and returns a non-zero exit code. webhook logs that as well, so<code>journalctl -u webhook</code> is the first place to look when something goes wrong.</p><p>The log directory needs to be owned by the executing user:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir -p /opt/deploy-logs</span></span><span style="display:flex;"><span>chown teqqy:teqqy /opt/deploy-logs</span></span></code></pre></div></div><h2 id="pitfalls-i-hit-along-the-way">Pitfalls I Hit Along the Way<a href="#pitfalls-i-hit-along-the-way" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p><strong><code>.git</code> directory ownership:</strong> If the repo was ever cloned or pulled as a different user, Git refuses access with a warning about &ldquo;dubious ownership&rdquo;. Fix:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>chown -R teqqy:teqqy /opt/media-stack/.git</span></span></code></pre></div></div><p><strong><code>safe.directory</code>:</strong> In some setups this helps additionally:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git config --global --add safe.directory /opt/media-stack</span></span></code></pre></div></div><p><strong><code>/var/log</code> permissions:</strong> The executing user doesn&rsquo;t have write access there. Put the log directory somewhere the user owns – I use<code>/opt/deploy-logs</code>.</p><p><strong><code>*arr</code> containers and UID/GID:</strong> My<code>*arr</code> containers run as<code>99:100</code> (Unraid-compatible for NFS reasons). On the very first deployment, Docker creates bind mount directories with<code>1000:1000</code> if they don&rsquo;t exist yet. One-time fix before the first start:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>chown -R 99:100 /mnt/data/arr/</span></span></code></pre></div></div><p>After that it&rsquo;s a non-issue since the directories already exist.</p><h2 id="monitoring">Monitoring<a href="#monitoring" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>I use<a href="https://uptime.kuma.pet/" target="_blank" rel="noopener noreferrer">Uptime Kuma<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
to check whether containers are still running cleanly after a deployment. That&rsquo;s enough for my use case: if a container stops responding after a failed update, Uptime Kuma alerts me. The deploy log and<code>journalctl -u webhook</code> then provide the details on what went wrong.</p><p>A dead man&rsquo;s switch (e.g. via Healthchecks.io) would be the next sensible step if you also want to be notified when a deploy simply<em>doesn&rsquo;t happen</em> – but for a media stack that&rsquo;s not critical enough for me to add the overhead.</p><h2 id="conclusion">Conclusion<a href="#conclusion" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>The setup has been running for a little while now and does exactly what it&rsquo;s supposed to – without me having to debug anything in Komodo. The webhook binary is stable, the deployment flow is deterministic, and the total overhead is a handful of config files and a shell script.</p><p>For anyone with similar requirements who doesn&rsquo;t want to run Kubernetes: this is, in my opinion, the most pragmatic approach. No magic, no frameworks, no overengineering. Just git pull and docker compose up.</p><p>If you have questions or feedback, reach me through the links in the menu bar.</p>
]]></content:encoded><category>selfhosted</category><category>gitops</category><category>docker</category><category>gitea</category><category>renovate</category><category>homelab</category></item><item><title>My Top 10 Selfhosted &amp; Homelab Software 2025 – Favorite Tools for the New Year</title><link>https://www.teqqy.de/en/my-top-10-selfhosted-and-homelab-software-2025-favorite-tools-for-the-new-year/</link><pubDate>Mon, 13 Oct 2025 17:20:00 +0100</pubDate><lastBuildDate>Mon, 13 Oct 2025 17:20:00 +0100</lastBuildDate><guid isPermaLink="true">https://www.teqqy.de/en/my-top-10-selfhosted-and-homelab-software-2025-favorite-tools-for-the-new-year/</guid><description>Just like in 2021 and 2024, I’m once again sharing a list of my favorite software products of the year. I always enjoy reading posts like this because you can often discover a few hidden gems. Usually – let’s be honest – these lists are filled with the usual suspects. Nevertheless, this year’s list includes three selfhosted projects that you might not have heard of before.
#10: Wanderer Let’s start with Wanderer (external link) , an app for collecting your hiking routes – basically a selfhosted Komoot. The app itself doesn’t record routes, but you can easily import GPX files or data from Strava or Komoot and enrich them with additional details.</description><content:encoded>&lt;![CDATA[<p>Just like in 2021 and 2024, I’m once again sharing a list of my favorite software products of the year. I always enjoy reading posts like this because you can often discover a few hidden gems. Usually – let’s be honest – these lists are filled with the usual suspects. Nevertheless, this year’s list includes three selfhosted projects that you might not have heard of before.</p><h1 id="10-wanderer">#10: Wanderer<a href="#10-wanderer" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h1><p>Let’s start with<a href="https://wanderer.to" target="_blank" rel="noopener noreferrer">Wanderer<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
, an app for collecting your hiking routes – basically a selfhosted Komoot. The app itself doesn’t record routes, but you can easily import GPX files or data from Strava or Komoot and enrich them with additional details.</p><p>Since one of the latest updates, you can even share your new activities in the Fediverse, making it easy to discover hiking routes from people all over the world.</p><p>I like this software because after Komoot’s acquisition and price hikes, I lost interest in using it and went looking for alternatives. Now, when I go hiking, I track the route with Strava and then import it into Wanderer.</p><h2 id="9-nextcloud">9: Nextcloud<a href="#9-nextcloud" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p><a href="https://nextcloud.com" target="_blank" rel="noopener noreferrer">Nextcloud<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
really needs no introduction. For me, it’s a bit of a love-hate relationship. On one hand, it’s completely overpowered for simple file storage; on the other hand, it’s incredibly versatile and extensible with lots of small apps. I’ve tried several alternatives, but I always end up coming back to Nextcloud.</p><p>In my<a href="https://teqqy.de/tags/homelab/" target="_blank" rel="noopener noreferrer">Homelab<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
, I’m currently running the All-in-one installation, which has proven to be more stable than the usual Docker Compose or manual setups. To be honest, I mainly use Nextcloud for calendars and file syncing; I don’t run additional apps permanently at the moment.</p><h2 id="8-jellyfin">8: Jellyfin<a href="#8-jellyfin" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p><a href="https://jellyfin.org" target="_blank" rel="noopener noreferrer">Jellyfin<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
, the well-known media player. I’ve already written a separate post comparing it to Plex, the market leader. Personally, I’m very happy with Jellyfin – it does exactly what I need. And since there’s no cloud component, I don’t have to worry about hacks or data leaks.</p><p>Jellyfin runs as an LXC container on my Proxmox host. This allows me to use the iGPU of my thin client instead of a dedicated GPU for transcoding. However, I also make sure that all my media files are stored in a format that doesn’t require any transcoding in the first place.</p><h2 id="7-freshrss">7: FreshRSS<a href="#7-freshrss" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>I’ve written about<a href="https://freshrss.org" target="_blank" rel="noopener noreferrer">FreshRSS<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
a few times before. It’s still the software for me when it comes to selfhosted RSS readers. FreshRSS aggregates all my feeds (which are a bit empty at the moment), but I don’t actually read them there. On my Apple devices, I use the Reeder app, which integrates flawlessly with my FreshRSS instance – as it always has.</p><h2 id="6-mealie">6: Mealie<a href="#6-mealie" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>Anyone who enjoys cooking probably knows the struggle: recipes scattered everywhere, and modern recipe sites overloaded with ads and trackers. That’s why I rely on<a href="https://mealie.io" target="_blank" rel="noopener noreferrer">Mealie<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
– I collect all my favorite recipes there.</p><p><img src="../../meine-top-10-selfhosted-und-homelab-software-2025-lieblings-tools-fuers-neue-jahr/images/homelab-top-10-mealie.webp" alt="Top 5 Adventure Log meiner selfhosted Apps 2025" loading="eager" fetchpriority="high" decoding="async"/><p>Mealie does an excellent job extracting recipe data from websites and presenting it in a clean, structured way. I often tweak steps and ingredients manually, but overall, it works great. As my collection grows, I’ve started organizing everything properly – otherwise, finding things becomes a mess over time.</p><p>Mealie runs as a Docker container using SQLite (no separate database). I’ve learned that smaller apps often don’t need a full-fledged database. Just make sure never to host SQLite databases on NFS shares – trust me on that one.</p><h2 id="5-immich">5: Immich<a href="#5-immich" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p><a href="https://immich.app" target="_blank" rel="noopener noreferrer">Immich<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
is one of the rising stars in the selfhosted world. It’s a photo management app that just works incredibly well. Of course, like any complex software, it has the occasional hiccup, but the developer team fixes issues quickly. I chose Immich because more and more apps are integrating with it – allowing you to display albums or photos directly in other software.</p><p>One of my favorite features is “Memories” – similar to Facebook’s flashbacks (for those still on there). It shows photos taken on the same date in previous years, letting you revisit old moments – like the thousandth cat picture from two years ago.</p><p>Immich is important enough to me that it runs in its own VM, though still as a Docker container. That way, I have all data in one place, and backup or restoration is super simple using snapshots.</p><h2 id="4-adventure-log">4: Adventure Log<a href="#4-adventure-log" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p><a href="https://adventurelog.app" target="_blank" rel="noopener noreferrer">Adventure Log<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
was a lucky find on the r/selfhosted subreddit. The developer shared it there, and I’ve been a fan ever since.</p><p><img src="../../meine-top-10-selfhosted-und-homelab-software-2025-lieblings-tools-fuers-neue-jahr/images/homelab-top-10-adventure-log.webp" alt="Top 5 Adventure Log meiner selfhosted Apps 2025" loading="lazy" fetchpriority="auto" decoding="async"/><p>It lets you plan and track your adventures (trips, vacations, etc.). You can now even import routes from Wanderer, though other import options are still missing. For people who love to travel, it’s a great tool.</p><p>Adventure Log also integrates nicely with Immich: you can link albums from your Immich instance directly to your travel entries and view them in context.</p><p>It’s currently the only app I run using Proxmox Helper Scripts, mainly because of its more complex setup. I might switch to the Docker version later, though.</p><h2 id="3-statistics-for-strava">3: Statistics for Strava<a href="#3-statistics-for-strava" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>I started running regularly again a few months ago. I’ve been using Strava for years, and<a href="https://github.com/robiningelbrecht/statistics-for-strava" target="_blank" rel="noopener noreferrer">Statistics for Strava<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
extends its standard functionality with additional analytics. The developer notes that, due to API restrictions, they can’t offer features that directly compete with Strava Premium.</p><p><img src="../../meine-top-10-selfhosted-und-homelab-software-2025-lieblings-tools-fuers-neue-jahr/images/homelab-top-10-statistics-for-strava.webp" alt="Top 5 Adventure Log meiner selfhosted Apps 2025" loading="lazy" fetchpriority="auto" decoding="async"/><p>Still, the app provides tons of useful data – personal records, heart rate analysis, and more. It originally focused on cycling but now supports many endurance sports.</p><p>The app runs as a Docker container, though you’ll need a separate cron job to import data and generate HTML reports.</p><h2 id="2-mastodon">2: Mastodon<a href="#2-mastodon" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p><a href="https://joinmastodon.org" target="_blank" rel="noopener noreferrer">Mastodon<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
has been my main social network since Musk’s takeover of Twitter. Over the past few years, many new ActivityPub- and Fediverse-based apps have appeared, all of which integrate nicely with Mastodon. There are still some technical quirks (as I mentioned in a<a href="/en/gotosocial-as-an-activitypub-server-my-experiences/">previous post</a>
), but overall, it’s been great.</p><p>Despite having an account on Bluesky, Mastodon remains my main platform. One of its downsides – which might also be an advantage – is that there are no algorithms. You don’t get suggestions or “people you might like,” which makes discovery harder but keeps the feed authentic.</p><p>I host Mastodon as a Docker container on my VPS, which also powers this blog. The containerized setup makes updating much easier.</p><h2 id="1-proxmox">1: Proxmox<a href="#1-proxmox" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>The most important piece of software in my Homelab is definitely<a href="https://www.proxmox.com" target="_blank" rel="noopener noreferrer">Proxmox<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a>
. The virtualization platform keeps all my virtual machines running smoothly and has done so for years without major issues. Its built-in backup tools let me restore files or even just single configuration files when something goes wrong.</p><p>With that, my Top 10 list for this year is complete. As always, it wasn’t easy to decide which apps to include – there are plenty of smaller tools running in my setup that could easily have made the list too. But at some point, you have to draw a line. 😊</p><p>It’s fascinating to see how my Homelab has evolved over the years – and that brings me to the conclusion.</p><h2 id="conclusion--whats-changed-since-2024">Conclusion – What’s Changed Since 2024<a href="#conclusion--whats-changed-since-2024" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>Comparing this year’s list to last year’s shows how much both my Homelab and my priorities have evolved. In 2024, many of my top picks were more “classic” infrastructure tools: Home Assistant, Paperless, Minio, EVCC – the backbone of any functional smart home or Homelab.</p><p>This year, the focus shifted slightly – away from automation and more toward tools that add fun and personal value. With apps like Wanderer, Adventure Log, and Statistics for Strava, several of my favorites are now tied to hobbies and everyday life. My Homelab isn’t just supposed to run efficiently anymore; it’s supposed to accompany me through daily routines – whether I’m hiking, cooking, or exercising.</p><p>Some old friends stayed on the list: Nextcloud, Immich, and Mastodon are still going strong. Others, like Home Assistant or Paperless-NGX, didn’t make the list this time – not because they’ve become worse, but because they’ve become invisible workhorses. They’re no longer highlights, but foundational parts of my setup.</p><p>Another interesting shift is that many of my 2025 favorites are more social. Through the Fediverse, shared photo albums, or activity feeds, my Homelab now connects me with others who share the same passions.</p><p>Who knows what 2026 will bring – maybe it’ll get more technical again, maybe not. But one thing’s for sure: life in the Homelab is never boring.</p>
]]></content:encoded><category>selfhosted</category><category>homelab</category><category>proxmox</category><category>mastodon</category><category>nextcloud</category><category>jellyfin</category></item><item><title>Save Power with Proxmox: 4 Practical Tips for Lower Energy Consumption</title><link>https://www.teqqy.de/en/save-power-with-proxmox-4-tips/</link><pubDate>Tue, 14 Mar 2023 21:17:37 +0100</pubDate><lastBuildDate>Tue, 14 Mar 2023 21:17:37 +0100</lastBuildDate><guid isPermaLink="true">https://www.teqqy.de/en/save-power-with-proxmox-4-tips/</guid><description>Proxmox: Reducing Power Consumption with 4 Practical Tips In times of rising energy costs and increasing environmental awareness, many of us are looking for ways to reduce electricity usage in our IT infrastructure. Especially in home networks and small servers that run around the clock, energy demands can quickly become a non-negligible cost factor.</description><content:encoded>&lt;![CDATA[<h1 id="proxmox-reducing-power-consumption-with-4-practical-tips">Proxmox: Reducing Power Consumption with 4 Practical Tips<a href="#proxmox-reducing-power-consumption-with-4-practical-tips" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h1><p>In times of rising energy costs and increasing environmental awareness, many of us are looking for ways to reduce electricity usage in our IT infrastructure. Especially in home networks and small servers that run around the clock, energy demands can quickly become a non-negligible cost factor.</p><p>If you, like me, use<strong>Proxmox</strong> — an open-source tool for virtualization — there are fortunately several effective ways to optimize your setup’s power consumption without sacrificing performance.</p><p>In this blog post I share four proven tips to make your Proxmox installation more energy efficient. It’s not just about hardware adjustments, but also about smart software optimizations that make your VMs and containers more power-friendly. Whether you’re an experienced Proxmox user or just starting out with the platform, these tips will help you reduce your electricity bill and at the same time make a small contribution to environmental protection.</p><p>Let’s get started right away and see how you can pull more efficiency out of your Proxmox server!</p><hr><h2 id="beforehand-measure-power-consumption">Beforehand: Measure Power Consumption<a href="#beforehand-measure-power-consumption" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>To even know how much electricity your devices are using, you should measure it. It’s not about perfect accuracy here, but rather getting a feeling for how much power each device consumes. For this you can purchase a<strong>cheap power meter</strong>. Note please that cheaper devices naturally don’t always have high accuracy.<br>
If you have a smart home setup, you can plug a smart-power-plug with measurement function in front of your homelab. That way you can monitor the real-time power consumption via software.</p><hr><h2 id="pay-attention-to-the-right-hardware">Pay Attention to the Right Hardware<a href="#pay-attention-to-the-right-hardware" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>Of course everything ultimately stands and falls with the right hardware. Old enterprise hardware naturally uses a lot of power. By contrast, so-called thin clients are a very good solution for building a power-efficient server. I have once presented them in a video.<br>
Additionally, you can build a good server yourself with the right components. In the Hardwareluxx forum there’s a linked Google Sheets file listing systems that idle at under 30 Watts.<br>
It is also important, in a self-assembled PC / server, to pay attention to all components. It starts with an efficient power supply, goes through a good motherboard, up to the right choice of CPU. Especially the latter: Intel still has a slight edge when it comes to lower idle power consumption. It is also important that Intel CPUs with a “T” in the designation only have a lower TDP.</p><p>TDP itself does not directly equate to power consumption, but is the maximum heat output. A “T” CPU is thus suitable for systems that are intended to run fully passively. Components that tend to speak for higher power consumption would be:</p><ul><li>Graphics cards</li><li>RAID controllers / HBAs</li><li>Network cards with more than 10 Gbit speed</li></ul><p>Further down in the article I discuss ASPM status. ASPM (Active State Power Management) means that a component can or cannot support certain power-saving functions. Roughly put, enterprise hardware often is not equipped with these functions.</p><hr><h2 id="set-bios-settings-correctly">Set BIOS Settings Correctly<a href="#set-bios-settings-correctly" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><h3 id="disable-unnecessary-hardware">Disable Unnecessary Hardware<a href="#disable-unnecessary-hardware" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h3><p>In most BIOS settings you can disable various devices. For a virtualization host, for example, audio is often not needed. So it would be advisable to disable the sound card in BIOS. The same applies to COM or serial ports.<br>
If you do not have a graphics card or other add-in card requiring a high PCI-Express standard installed, you could also reduce the PCIe slots from version 4 to version 3, for example. This too saves some watts.</p><h3 id="enable-aspm">Enable ASPM<a href="#enable-aspm" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h3><p>ASPM stands for Active State Power Management and is a technology used in computers to improve the energy efficiency of PCIe connections. PCIe is a high-speed interface used in computers to attach various hardware components such as graphics cards, network cards, or SSDs. ASPM aims to reduce the power consumption of PCIe devices by dynamically adjusting power usage in connection lines between the CPU (or chipset) and connected PCIe devices. This is done by automatically switching between different performance states depending on utilization and current requirements.</p><h3 id="enable-cpu-power-saving-functions">Enable CPU Power-Saving Functions<a href="#enable-cpu-power-saving-functions" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h3><p>In BIOS always enable C-States. Depending on manufacturer and version you should be able to enable up to C10. But don’t worry if your BIOS only allows e.g. up to C6 — you’re not missing out on much potential.</p><hr><h2 id="c-states">C-States<a href="#c-states" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><h3 id="what-are-c-states">What Are C-States<a href="#what-are-c-states" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h3><p>C-States, or CPU sleep states, are states into which a CPU (Central Processing Unit) can enter when it’s not being actively used. They are part of modern CPUs’ performance control functions and serve to reduce power consumption when full CPU performance is not needed.</p><p>There are different C-states, from C0 up to Cn (depending on the processor). Here are some of the most common:</p><ul><li><strong>C0:</strong> Active, executing tasks.</li><li><strong>C1:</strong> Light sleep, can quickly return to active.</li><li><strong>C3:</strong> Deeper sleep, more energy saved, slower wake.</li><li><strong>C6:</strong> Very deep sleep, almost completely off, longest wake time.</li></ul><p>The switch between these states is automatically handled by the operating system and hardware based on current CPU load and energy settings.</p><p>You should always aim for<strong>C10 status</strong>. In a Proxmox server you likely won’t achieve that — after all the system is typically designed to run continuously, especially if you’re running many virtual machines or LXCs.</p><h4 id="check-c-states-with-powertop">Check C-States with<code>powertop</code><a href="#check-c-states-with-powertop" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h4><p><code>powertop</code> is a great Linux command that allows you to check the C-states of your server. If they only go up to e.g. C3, you are still wasting energy “unnecessarily”.</p><p>Often it’s PCI-Express devices that don’t allow further C-states. With the command:</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label">bash</span><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang="bash"><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>lspci -vv | awk<span style="color:#a5d6ff">'/ASPM/{print $0}'</span><span style="color:#79c0ff">RS</span><span style="color:#ff7b72;font-weight:bold">=</span> | grep --color -P<span style="color:#a5d6ff">'(^[a-z0-9:.]+|ASPM )'</span></span></span></code></pre></div></div>
]]></content:encoded><category>proxmox</category><category>hardware</category><category>homelab</category></item><item><title>3 DNS Blocklists for PiHole, AdGuard or blocky</title><link>https://www.teqqy.de/en/3-dns-blocklists-for-pihole-adguard-or-blocky/</link><pubDate>Wed, 02 Mar 2022 20:28:52 +0100</pubDate><lastBuildDate>Wed, 02 Mar 2022 20:28:52 +0100</lastBuildDate><guid isPermaLink="true">https://www.teqqy.de/en/3-dns-blocklists-for-pihole-adguard-or-blocky/</guid><description>3 DNS Blocklists for PiHole, AdGuard or blocky A DNS ad blocker has become part of the standard setup in every homelab. Of course, these ad blockers require DNS blocklists that should be maintained. In this post, I’ll show you my lists and explain why I recommend them.</description><content:encoded>&lt;![CDATA[<h1 id="3-dns-blocklists-for-pihole-adguard-or-blocky">3 DNS Blocklists for PiHole, AdGuard or blocky<a href="#3-dns-blocklists-for-pihole-adguard-or-blocky" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h1><p>A DNS ad blocker has become part of the standard setup in every homelab. Of course, these ad blockers require DNS blocklists that should be maintained. In this post, I’ll show you my lists and explain why I recommend them.</p><p>Of course, you can maintain many more lists than the three I recommend in this post, but more lists also cause more load and can impact your ad blocker’s performance.</p><p>Which software you use to block ads is, of course, a matter of preference. Since I run GitOps in my Kubernetes cluster and prefer config-as-code (enough buzzwords for now), I use blocky. However, these lists work just as well in PiHole or AdGuard.</p><h2 id="ads-and-trackers">Ads and Trackers<a href="#ads-and-trackers" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>To block ads and trackers, this single list is usually sufficient. So far, I haven’t noticed any ads slipping through that could be filtered by an ad blocker.</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label"/><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang=""><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><pre tabindex="0"><code>https://raw.githubusercontent.com/ookangzheng/dbl-oisd-nl/master/dbl.txt</code></pre></div><h2 id="tv-ads-and-tracking">TV Ads and Tracking<a href="#tv-ads-and-tracking" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>Of course, no ads are blocked on regular TV, but modern smart TVs — if connected to the network and the internet — use various trackers to, for example, display ads via the red button feature. I’ve configured several lists for this on my setup, but generally, the first list would already be enough.</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label"/><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang=""><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><pre tabindex="0"><code>https://gist.githubusercontent.com/wassname/b594c63222f9e4c83ea23c818440901b/raw/1b0afd2aecf3a099f1681b1cf18fc0e6e2fa116a/Samsung%2520Smart-TV%2520Blocklist%2520Adlist%2520(for%2520PiHole)
https://gist.githubusercontent.com/wassname/78eeaaad299dc4cddd04e372f20a9aa7/raw/d7863a978993e99d9c77c9001008ce670a3b4c29/LG%2520Smart-TV%2520Blocklist%2520Adlist%2520(for%2520PiHole)
https://raw.githubusercontent.com/Perflyst/PiHoleBlocklist/master/SmartTV.txt
https://gist.githubusercontent.com/hkamran80/779019103fcd306979411d44c8d38459/raw/e0f084b396bb8ffcb390c8e7272ae96a6c2</code></pre></div><h2 id="fake-news">Fake News<a href="#fake-news" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>This list blocks domains known for fake news, conspiracy theories, and misinformation.</p><div class="code-block"><div class="code-block-header"><span class="code-lang-label"/><button class="code-copy-btn" type="button" aria-label="Copy code" data-umami-event="Code Copy" data-umami-event-lang=""><svg class="icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><svg class="icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg></button></div><pre tabindex="0"><code>https://raw.githubusercontent.com/jojo321/hosts/master/fakenews</code></pre></div><h2 id="whitelist">Whitelist<a href="#whitelist" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>Of course, you should also maintain a whitelist — otherwise, you’ll likely block legitimate services. For example, in my environment, this includes Apple and Google services that are sometimes blocked by generic lists.</p><h2 id="further-recommendations">Further Recommendations<a href="#further-recommendations" class="heading-anchor" aria-label="Link to this section"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></a></h2><p>If you want to explore even more blocklists, take a look at these collections:</p><ul><li><a href="https://firebog.net/" target="_blank" rel="noopener noreferrer">https://firebog.net/<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a></li><li><a href="https://blocklistproject.github.io/Lists/" target="_blank" rel="noopener noreferrer">https://blocklistproject.github.io/Lists/<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a></li><li><a href="https://github.com/blocklistproject/Lists" target="_blank" rel="noopener noreferrer">https://github.com/blocklistproject/Lists<svg class="link-external-icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg><span class="visually-hidden"> (external link)</span></a></li></ul>
]]></content:encoded><category>selfhosted</category><category>dns</category><category>homelab</category></item></channel></rss>