{"version":"https://jsonfeed.org/version/1.1","title":"teqqy","home_page_url":"https://www.teqqy.de/","feed_url":"https://www.teqqy.de/feed.json","description":"A blog about technology and more","language":"en","authors":[{"name":"teqqy"}],"items":[{"id":"https://www.teqqy.de/en/selfhosted-tools-nobody-knows/","url":"https://www.teqqy.de/en/selfhosted-tools-nobody-knows/","title":"Selfhosted Tools (Almost) Nobody Knows About","summary":"Over the past few years I\u0026rsquo;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\u0026rsquo;d introduce you to a few tools that not everyone has running on their home network. Maybe it\u0026rsquo;s some inspiration for you.\n","content_html":"\u003cp\u003eOver the past few years I\u0026rsquo;ve written \u003ca href=\"/en/my-top-10-selfhosted-and-homelab-software-2025-favorite-tools-for-the-new-year/\"\u003ethe occasional post about my top 10 apps for my homelab\u003c/a\u003e\n. 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\u0026rsquo;d introduce you to a few tools that not everyone has running on their home network. Maybe it\u0026rsquo;s some inspiration for you.\u003c/p\u003e\n\u003ch2 id=\"autokuma\"\u003eAutoKuma\u003ca href=\"#autokuma\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAlmost everyone knows Uptime Kuma. What I don\u0026rsquo;t like is having to configure everything by clicking through the UI, especially when the work is basically repetitive. With \u003ca href=\"https://github.com/BigBoot/AutoKuma\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eAutoKuma\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n 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, \u003ca href=\"/en/gitops-without-komodo/\"\u003eyou run GitOps\u003c/a\u003e\n: 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.\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003eyaml\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"yaml\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003elabels\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;kuma.strava.http.name=\u0026#39;Strava Statistics\u0026#39;\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;kuma.strava.http.url=https://strava.casalani.de\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;kuma.strava.http.parent_name=apps\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;kuma.strava.http.max_retries=5\u0026#34;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003eThat\u0026rsquo;s a small example from Strava Statistics. In the end it\u0026rsquo;s easy to carry over to other systems with a simple copy \u0026amp; paste.\u003c/p\u003e\n\u003cdiv class=\"video-embed\" data-src=\"https://videos.teqqy.de/videos/embed/2WdshixFScUbm6ukUA9AUK?autoplay=1\u0026warningTitle=0\"\u003e\n  \u003cbutton 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\"\u003e\n    \u003csvg 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\"\u003e\u003ccircle cx=\"12\" cy=\"12\" r=\"12\" fill=\"rgba(0,0,0,0.6)\"/\u003e\u003cpath d=\"M9.5 7.5l7 4.5-7 4.5V7.5z\" fill=\"white\"/\u003e\u003c/svg\u003e\n    \u003cspan class=\"video-embed-title\"\u003eAutoKuma: automating Uptime Kuma monitoring with Docker labels\u003c/span\u003e\n    \u003cspan class=\"video-embed-note\"\u003ePeerTube \u0026middot; Click to load\u003c/span\u003e\n  \u003c/button\u003e\n\u003c/div\u003e\n\n\u003cp\u003eAutoKuma itself is of course just a Docker container, so it can easily be started alongside everything else on any host.\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003eyaml\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"yaml\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#7ee787\"\u003eservices\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e  \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eautokuma\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eimage\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eghcr.io/bigboot/autokuma:2.0.0\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003erestart\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eunless-stopped\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eenvironment\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eAUTOKUMA__KUMA__URL\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003ehttps://uptimekuma.example.com\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eAUTOKUMA__KUMA__USERNAME\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003ekumaadmin\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eAUTOKUMA__KUMA__PASSWORD\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eSTRENGGEHEIMHESPASSWORD\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003evolumes\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003e/var/run/docker.sock:/var/run/docker.sock\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003e./autokuma:/data\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003elabels\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;kuma.apps.group.name=Applications\u0026#34;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003eAnd really, that\u0026rsquo;s about all there is to it.\u003c/p\u003e\n\u003ch2 id=\"patchmon\"\u003ePatchmon\u003ca href=\"#patchmon\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI discovered \u003ca href=\"https://github.com/PatchMon/PatchMon\" target=\"_blank\" rel=\"noopener noreferrer\"\u003ePatchmon\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n 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.\u003c/p\u003e\n\u003cp\u003eThe 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.\u003c/p\u003e\n\u003cp\u003e\u003cimg 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\"\u003e\u003c/p\u003e\n\u003ch2 id=\"adguardhome-sync\"\u003eAdGuardHome-Sync\u003ca href=\"#adguardhome-sync\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eFor a while I ran several physical servers, each with its own \u003ca href=\"/en/3-dns-blocklists-for-pihole-adguard-or-blocky/\"\u003eAdGuard Home instance\u003c/a\u003e\n. These days only \u003ca href=\"/en/save-power-with-proxmox-4-tips/\"\u003eone Proxmox host\u003c/a\u003e\n is left, but several AdGuard instances are still running on it.\u003c/p\u003e\n\u003cp\u003eHere too, I don\u0026rsquo;t want to make every change manually, and definitely not twice. At some point I noticed that I\u0026rsquo;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\u0026rsquo;s exactly the kind of forgetting \u003ca href=\"https://github.com/bakito/adguardhome-sync\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eAdGuardHome-Sync\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n solves. If I change the blocklists or other settings on one host, they\u0026rsquo;re synced over to the second host shortly after.\u003c/p\u003e\n\u003cp\u003eThe whole thing is configured through a single YAML file with an origin and replica definition:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003eyaml\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"yaml\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#7ee787\"\u003ecron\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;*/10 * * * *\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#7ee787\"\u003eorigin\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e  \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eurl\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003ehttps://adguard-1.example.com\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e  \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eusername\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eadmin\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e  \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003epassword\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eSTRENGGEHEIMESPASSWORD\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#7ee787\"\u003ereplicas\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e  \u003c/span\u003e- \u003cspan style=\"color:#7ee787\"\u003eurl\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003ehttps://adguard-2.example.com\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eusername\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eadmin\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003epassword\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eSTRENGGEHEIMESPASSWORD\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eautoSetup\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#79c0ff\"\u003etrue\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003eThe \u003ccode\u003eautoSetup\u003c/code\u003e 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.\u003c/p\u003e\n\u003ch2 id=\"opencloud\"\u003eOpenCloud\u003ca href=\"#opencloud\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/opencloud-eu/opencloud\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eOpenCloud\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n is my (theoretically) leaner alternative to \u003ca href=\"/nextcloud-docker-tutorial-step-by-step-zum-erfolg/\"\u003eNextcloud\u003c/a\u003e\n. 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\u0026rsquo;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.\u003c/p\u003e\n\u003cp\u003eA while ago OpenCloud kept coming up more and more in various communities, and I figured I\u0026rsquo;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.\u003c/p\u003e\n\u003ch2 id=\"meerkat\"\u003eMeerkat\u003ca href=\"#meerkat\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eSome 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, \u003ca href=\"https://github.com/fbuchner/meerkat-crm\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eMeerkat\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n 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.\u003c/p\u003e\n\u003cp\u003eMeerkat 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\u0026rsquo;ve for example noted down that my brother-in-law can\u0026rsquo;t eat peanuts, because there\u0026rsquo;s no way I\u0026rsquo;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\u0026rsquo;s a fantastic system. As long as you actually keep it up to date\u0026hellip;\u003c/p\u003e\n\u003cp\u003eThrough 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.\u003c/p\u003e\n\u003ch2 id=\"sparkyfitness\"\u003eSparkyFitness\u003ca href=\"#sparkyfitness\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eApps 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\u0026rsquo;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\u0026rsquo;s an expense I don\u0026rsquo;t really need.\u003c/p\u003e\n\u003cp\u003eLuckily I came across \u003ca href=\"https://github.com/CodeWithCJ/SparkyFitness\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eSparkyFitness\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n a while back. The developer describes the app as a selfhosted MyFitnessPal alternative, and I\u0026rsquo;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.\u003c/p\u003e\n\u003ch2 id=\"adventurelog\"\u003eAdventureLog\u003ca href=\"#adventurelog\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eWith \u003ca href=\"https://github.com/seanmorley15/AdventureLog\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eAdventureLog\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n you can either plan your trips or just track them. I only use it for the latter. In AdventureLog I\u0026rsquo;ve logged all the trips I\u0026rsquo;ve taken with my partner.\u003c/p\u003e\n\u003cp\u003eOne 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.\u003c/p\u003e\n\u003cp\u003e\u003cimg 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\"\u003e\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003ca href=\"#conclusion\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eSeven tools that hopefully aren\u0026rsquo;t already on every other homelab top-10 list – from monitoring to patch management to a digital address book. Some of them I\u0026rsquo;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\u0026rsquo;m still trying things out myself.\u003c/p\u003e\n\u003cp\u003eWhat 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\u0026rsquo;re often the ones you end up appreciating most in everyday use.\u003c/p\u003e\n\u003cp\u003eIs 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.\u003c/p\u003e\n","date_published":"2026-07-23T00:00:00+02:00","date_modified":"2026-07-23T00:00:00+02:00","tags":["selfhosted","homelab"]},{"id":"https://www.teqqy.de/en/teslamate-setup-docker-fleet-api-grafana/","url":"https://www.teqqy.de/en/teslamate-setup-docker-fleet-api-grafana/","title":"Teslamate Setup: Docker, Fleet API \u0026 Grafana","summary":"Update, December 2025: This article has been revised to reflect the recent changes to the Tesla Fleet API and current Teslamate versions.\nModern 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\u0026rsquo;s data, stores it, and visualizes it with Grafana.\n","content_html":"\u003cp\u003e\u003cstrong\u003eUpdate, December 2025:\u003c/strong\u003e This article has been revised to reflect the recent changes to the Tesla Fleet API and current Teslamate versions.\u003c/p\u003e\n\u003cp\u003eModern 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\u0026rsquo;s data, stores it, and visualizes it with Grafana.\u003c/p\u003e\n\u003ch2 id=\"what-changed-in-2025\"\u003eWhat changed in 2025?\u003ca href=\"#what-changed-in-2025\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe key updates at a glance:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTesla Fleet API:\u003c/strong\u003e Tesla replaced the unofficial \u0026ldquo;Owner API\u0026rdquo; 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.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePostgreSQL 17:\u003c/strong\u003e Teslamate now requires at least PostgreSQL 16.7 or 17.3. The developers recommend PostgreSQL 17.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eFleet Telemetry:\u003c/strong\u003e 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.\u003c/p\u003e\n\u003ch2 id=\"teslamate--install-it-yourself-or-pay-for-a-service\"\u003eTeslamate – Install It Yourself, or Pay for a Service?\u003ca href=\"#teslamate--install-it-yourself-or-pay-for-a-service\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eTeslamate isn\u0026rsquo;t a software-as-a-service — you install and run it yourself. Whether that\u0026rsquo;s on your home homelab or on a VPS in the cloud, e.g. with \u003ca href=\"https://www.netcup.de/?ref=60644\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eNetcup\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n, both work fine. I host it on my own server on my home network.\u003c/p\u003e\n\u003cp\u003eInstallation is straightforward with \u003ca href=\"/wordpress-docker-performance/\"\u003eDocker\u003c/a\u003e\n. You need a database (PostgreSQL) and the application container. For visualization, Teslamate relies on Grafana with pre-configured dashboards.\u003c/p\u003e\n\u003ch2 id=\"prerequisites\"\u003ePrerequisites\u003ca href=\"#prerequisites\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eHardware \u0026amp; software:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA server with at least 2 GB RAM (Raspberry Pi 4/5, NAS, VPS)\u003c/li\u003e\n\u003cli\u003eDocker and Docker Compose installed\u003c/li\u003e\n\u003cli\u003eA permanently active internet connection\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eTesla account:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eTesla account with two-factor authentication\u003c/li\u003e\n\u003cli\u003eA Tesla Developer account (free at \u003ca href=\"https://developer.tesla.com\" target=\"_blank\" rel=\"noopener noreferrer\"\u003edeveloper.tesla.com\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n)\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"setting-up-a-tesla-developer-account\"\u003eSetting Up a Tesla Developer Account\u003ca href=\"#setting-up-a-tesla-developer-account\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eSince the switch to the Fleet API, you\u0026rsquo;ll need a developer account:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eSign in at \u003ca href=\"https://developer.tesla.com\" target=\"_blank\" rel=\"noopener noreferrer\"\u003edeveloper.tesla.com\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n with your Tesla account\u003c/li\u003e\n\u003cli\u003eCreate a new application (e.g. \u0026ldquo;Teslamate Homelab\u0026rdquo;)\u003c/li\u003e\n\u003cli\u003eProvide a short description\u003c/li\u003e\n\u003cli\u003eFor local use, you can use localhost as the domain\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eReview takes a few days; for private use, approval is usually straightforward. The monthly $10 allowance is real credit — you don\u0026rsquo;t need to add a credit card as long as you stay under it.\u003c/p\u003e\n\u003ch2 id=\"creating-tokens\"\u003eCreating Tokens\u003ca href=\"#creating-tokens\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAuthentication used to work with your username and password. Today you need a \u0026ldquo;Refresh Token\u0026rdquo; and an \u0026ldquo;Access Token.\u0026rdquo; The easiest way to generate these is with dedicated apps:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eiOS:\u003c/strong\u003e \u0026ldquo;Auth for Tesla\u0026rdquo; (Apple App Store)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAndroid:\u003c/strong\u003e \u0026ldquo;Tesla Tokens\u0026rdquo; or \u0026ldquo;Auth for Tesla\u0026rdquo;\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWindows/Mac:\u003c/strong\u003e \u0026ldquo;Tesla Auth\u0026rdquo; or web-based tools\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eSteps:\u003c/strong\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eInstall the app and sign in with your Tesla username\u003c/li\u003e\n\u003cli\u003eEnter your password\u003c/li\u003e\n\u003cli\u003eAt the second prompt, enter your 2FA code (not your password again!)\u003c/li\u003e\n\u003cli\u003eThe app shows a Refresh Token and Access Token — keep both somewhere safe\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThe tokens stay valid for several months. Teslamate refreshes the access token automatically.\u003c/p\u003e\n\u003cp\u003e\u003cimg 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\"\u003e\u003c/p\u003e\n\u003ch2 id=\"installation-with-docker-compose\"\u003eInstallation with Docker Compose\u003ca href=\"#installation-with-docker-compose\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe easiest way to install Teslamate is via Docker Compose. If you don\u0026rsquo;t already have Docker Compose installed:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#8b949e;font-style:italic\"\u003e# On Ubuntu/Debian\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install docker-compose-plugin\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker compose version\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003eCreate a working directory:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir ~/teslamate \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e cd ~/teslamate\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003eI\u0026rsquo;ll point you to the \u003ca href=\"https://docs.teslamate.org/docs/installation/docker\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eofficial documentation\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n here, since the configuration changes fairly regularly.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eImportant adjustments for 2025:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003eyaml\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"yaml\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#7ee787\"\u003eversion\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;3\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#7ee787\"\u003eservices\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e  \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eteslamate\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eimage\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eteslamate/teslamate:latest\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003erestart\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003ealways\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eenvironment\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eENCRYPTION_KEY=your_secure_key\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eDATABASE_USER=teslamate\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eDATABASE_PASS=secure_password\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eDATABASE_NAME=teslamate\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eDATABASE_HOST=database\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eTZ=Europe/Berlin \u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#8b949e;font-style:italic\"\u003e# Your timezone!\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eMQTT_DISABLE=true \u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#8b949e;font-style:italic\"\u003e# If you don\u0026#39;t need MQTT\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eports\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003e4000\u003c/span\u003e:\u003cspan style=\"color:#a5d6ff\"\u003e4000\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003ecap_drop\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eall\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e  \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003edatabase\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eimage\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003epostgres:17 \u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#8b949e;font-style:italic\"\u003e# At least 16.7 or 17.3!\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003erestart\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003ealways\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eenvironment\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003ePOSTGRES_USER=teslamate\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003ePOSTGRES_PASSWORD=secure_password\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003ePOSTGRES_DB=teslamate\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003evolumes\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eteslamate-db:/var/lib/postgresql/data\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e  \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003egrafana\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eimage\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eteslamate/grafana:latest\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003erestart\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003ealways\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eenvironment\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eDATABASE_USER=teslamate\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eDATABASE_PASS=secure_password\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eDATABASE_NAME=teslamate\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eDATABASE_HOST=database\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eports\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003e3000\u003c/span\u003e:\u003cspan style=\"color:#a5d6ff\"\u003e3000\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003evolumes\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e- \u003cspan style=\"color:#a5d6ff\"\u003eteslamate-grafana-data:/var/lib/grafana\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#7ee787\"\u003evolumes\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e  \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eteslamate-db\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e  \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eteslamate-grafana-data:\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003e\u003cstrong\u003eMQTT for Home Assistant:\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eIf you want to forward the data to an MQTT server (e.g. for \u003ca href=\"/tesla-ueberschussladen-mit-home-assistant/\"\u003eHome Assistant\u003c/a\u003e\n), replace \u003ccode\u003eMQTT_DISABLE=true\u003c/code\u003e with \u003ccode\u003eMQTT_HOST=mosquitto\u003c/code\u003e and add a Mosquitto container.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eStart the containers:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker compose up -d\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003eTeslamate is then reachable at \u003ccode\u003ehttp://{your-server-ip}:4000\u003c/code\u003e. 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.\u003c/p\u003e\n\u003ch2 id=\"analysis-with-grafana\"\u003eAnalysis with Grafana\u003ca href=\"#analysis-with-grafana\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eA big advantage of the pre-built Docker Compose file: Grafana is included out of the box, reachable by default at \u003ccode\u003ehttp://{your-server-ip}:3000\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eDefault login (change immediately!):\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eUsername: \u003ccode\u003eadmin\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003ePassword: \u003ccode\u003eadmin\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003ca href=\"/docker-monitoring-mit-prometheus-und-grafana/\"\u003eGrafana\u003c/a\u003e\n already has the PostgreSQL database configured as a data source, and all dashboards are pre-imported. If you\u0026rsquo;re running your own separate Grafana instance, you\u0026rsquo;ll need to import the JSON files manually from the \u003ca href=\"https://www.github.com/cbirkenbeul\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eGitHub\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n repository.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eKey dashboards:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eOverview:\u003c/strong\u003e an overall summary of all metrics — the perfect starting point\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDrives:\u003c/strong\u003e trip analysis with interactive maps, showing route, speed, and consumption\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCharges:\u003c/strong\u003e detailed charging history with cost analysis per charge, and comparisons between charging stations\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eBattery Health:\u003c/strong\u003e long-term battery capacity trends, showing degradation over time and mileage\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eEfficiency:\u003c/strong\u003e consumption analysis by speed, temperature, and elevation — see what\u0026rsquo;s actually affecting your range\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eVampire Drain:\u003c/strong\u003e standby consumption while parked, useful for spotting phantom drain\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eLocations:\u003c/strong\u003e frequently visited places, with automatic geofencing detection\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eUpdates:\u003c/strong\u003e a history of every software update, with timestamps\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"long-term-use-and-insights\"\u003eLong-term Use and Insights\u003ca href=\"#long-term-use-and-insights\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe real value shows up after several months. Here are some concrete examples from my own usage:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eBattery degradation:\u003c/strong\u003e 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).\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSeasonal differences:\u003c/strong\u003e in winter (around 0°C), my consumption sits at roughly 20-22 kWh/100km; in summer it\u0026rsquo;s 15-17 kWh/100km — about 30% more due to heating and a cold battery. That data is genuinely useful for route planning.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eCost optimization:\u003c/strong\u003e 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: \u003ca href=\"/tesla-ueberschussladen-mit-home-assistant/\"\u003echarging with solar surplus\u003c/a\u003e\n brings the cost down to about €0.08/kWh.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eEfficiency:\u003c/strong\u003e 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.\u003c/p\u003e\n\u003ch2 id=\"troubleshooting\"\u003eTroubleshooting\u003ca href=\"#troubleshooting\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eTeslamate shows \u0026ldquo;offline\u0026rdquo; instead of \u0026ldquo;asleep\u0026rdquo;:\u003c/strong\u003e\nThat\u0026rsquo;s expected behavior since Tesla firmware 2024.8+ — Tesla changed the API response, it\u0026rsquo;s not a Teslamate bug.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eToken isn\u0026rsquo;t accepted:\u003c/strong\u003e\nUse the Refresh Token (the longer of the two strings), not the Access Token.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eNo data:\u003c/strong\u003e\nCheck the Docker logs with \u003ccode\u003edocker compose logs teslamate\u003c/code\u003e. Common causes: the vehicle is in deep sleep, wrong timezone, or network issues.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHigh API costs:\u003c/strong\u003e\nReduce the polling frequency, switch to Fleet Telemetry, or check for unnecessary wake commands.\u003c/p\u003e\n\u003ch2 id=\"security-and-backup\"\u003eSecurity and Backup\u003ca href=\"#security-and-backup\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eNetwork:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eDon\u0026rsquo;t expose it directly to the internet\u003c/li\u003e\n\u003cli\u003eUse a reverse proxy with HTTPS (Nginx, Caddy, Traefik)\u003c/li\u003e\n\u003cli\u003eEnable firewall rules\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eBackup:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker compose exec database pg_dump -U teslamate teslamate \u0026gt; backup.sql\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003e\u003cstrong\u003eUpdates:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker compose pull\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker compose up -d\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch2 id=\"frequently-asked-questions\"\u003eFrequently Asked Questions\u003ca href=\"#frequently-asked-questions\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eDoes Teslamate cost money?\u003c/strong\u003e\nThe 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.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDoes it work with the new Fleet API?\u003c/strong\u003e\nYes, fully compatible and kept up to date.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDoes it drain the battery?\u003c/strong\u003e\nNo, the additional consumption is negligible.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eCan I track multiple vehicles?\u003c/strong\u003e\nYes, each one is logged separately.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003ca href=\"#conclusion\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eFor me, Teslamate is the best solution for in-depth vehicle data analysis. The upsides:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eFull data ownership through self-hosting\u003c/li\u003e\n\u003cli\u003eDetailed analysis with Grafana\u003c/li\u003e\n\u003cli\u003eLow cost thanks to the free API allowance\u003c/li\u003e\n\u003cli\u003eAn active community\u003c/li\u003e\n\u003cli\u003eGreat fit for Home Assistant integration\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe downsides: it requires technical know-how, your own server, and there\u0026rsquo;s no official support.\u003c/p\u003e\n\u003cp\u003eFor 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.\u003c/p\u003e\n\u003ch2 id=\"tips-for-advanced-users\"\u003eTips for Advanced Users\u003ca href=\"#tips-for-advanced-users\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eReverse proxy setup:\u003c/strong\u003e\nFor secure access on the go, I\u0026rsquo;d recommend a reverse proxy with a Let\u0026rsquo;s Encrypt SSL certificate. Traefik or Caddy fully automate the process.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDatabase optimization:\u003c/strong\u003e\nOn very old installations, a manual vacuum of the database can help:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker compose exec database vacuumdb -U teslamate -d teslamate -z -v\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003e\u003cstrong\u003eCustom dashboards:\u003c/strong\u003e\nGrafana offers endless possibilities. You could, for example, build comparisons with other Tesla drivers, or visualize correlations between weather and consumption.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eFurther reading:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://docs.teslamate.org\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eTeslamate Documentation\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/teslamate-org/teslamate\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eGitHub Repository\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/teslamate-org/teslamate/discussions\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eCommunity Forum\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n","date_published":"2026-07-18T00:00:00Z","date_modified":"2026-07-18T00:00:00Z","tags":["selfhosted","tesla","docker","grafana","homelab"]},{"id":"https://www.teqqy.de/en/gitops-without-komodo/","url":"https://www.teqqy.de/en/gitops-without-komodo/","title":"GitOps without Komodo: Webhook-based Auto-Deployment for Docker Compose","summary":"If you\u0026rsquo;ve read my previous post on my 2025 homelab setup , you know I\u0026rsquo;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\u0026rsquo;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\u0026rsquo;d had enough and wanted something leaner.\n","content_html":"\u003cp\u003eIf you\u0026rsquo;ve read my \u003ca href=\"/selfhosted-setup-2025-mein-neuer-workflow-mit-proxmox-komodo-und-gitops/\"\u003eprevious post on my 2025 homelab setup\u003c/a\u003e\n, you know I\u0026rsquo;ve been using \u003ca href=\"https://komo.do/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eKomodo\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n 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\u0026rsquo;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\u0026rsquo;d had enough and wanted something leaner.\u003c/p\u003e\n\u003cp\u003eThe 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.\u003c/p\u003e\n\u003ch2 id=\"the-basic-concept\"\u003eThe Basic Concept\u003ca href=\"#the-basic-concept\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe setup relies on three components working together:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ca href=\"https://about.gitea.com/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eGitea\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n\u003c/strong\u003e – my self-hosted Git server where the compose files live\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ca href=\"https://docs.renovatebot.com/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eRenovate\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n\u003c/strong\u003e – 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 \u003ccode\u003emain\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ca href=\"https://github.com/adnanh/webhook\" target=\"_blank\" rel=\"noopener noreferrer\"\u003ewebhook\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n\u003c/strong\u003e – 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.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThe data flow looks like this:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003e\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eRenovate detects new image tag\n  → PR in Gitea\n  → Merge\n  → Gitea fires webhook\n  → webhook binary calls deploy.sh\n  → git pull + docker compose up -d\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp\u003eNo polling, no daemon watching container registries, no framework with its own opinions about file permissions.\u003c/p\u003e\n\u003ch2 id=\"why-not-just-watchtower-or-a-cron-script\"\u003eWhy Not Just Watchtower or a Cron Script?\u003ca href=\"#why-not-just-watchtower-or-a-cron-script\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eBefore reaching for webhook, I briefly considered two other approaches.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ca href=\"https://containrrr.dev/watchtower/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eWatchtower\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n\u003c/strong\u003e 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\u0026rsquo;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.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eA cron script with \u003ccode\u003egit fetch\u003c/code\u003e\u003c/strong\u003e 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\u0026rsquo;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.\u003c/p\u003e\n\u003cp\u003ewebhook is the cleanest approach: event-driven, no extra daemon, a single binary with no dependencies.\u003c/p\u003e\n\u003ch2 id=\"repo-structure\"\u003eRepo Structure\u003ca href=\"#repo-structure\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI have one repository per server:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eapps-stack\u003c/code\u003e for \u003ccode\u003edocker01\u003c/code\u003e – running tools like \u003ca href=\"https://docs.paperless-ngx.com/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003ePaperless-ngx\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n, \u003ca href=\"https://immich.app/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eImmich\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n, \u003ca href=\"https://github.com/dani-garcia/vaultwarden\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eVaultwarden\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n, and similar\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003emedia-stack\u003c/code\u003e for \u003ccode\u003emedia01\u003c/code\u003e – all the \u003ccode\u003e*arr\u003c/code\u003e containers and everything else in the media stack\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eEach repo just contains the \u003ccode\u003ecompose.yaml\u003c/code\u003e and any additional config files individual applications need. Nothing special.\u003c/p\u003e\n\u003ch3 id=\"renovate-in-the-repos\"\u003eRenovate in the Repos\u003ca href=\"#renovate-in-the-repos\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eRenovate runs self-hosted and checks the image tags in the compose files every four hours via cronjob. A minimal \u003ccode\u003erenovate.json\u003c/code\u003e in the repo root is all it takes:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ejson\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"json\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#7ee787\"\u003e\u0026#34;$schema\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;https://docs.renovatebot.com/renovate-schema.json\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#7ee787\"\u003e\u0026#34;extends\u0026#34;\u003c/span\u003e: [\u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;config:base\u0026#34;\u003c/span\u003e],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#7ee787\"\u003e\u0026#34;docker-compose\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#7ee787\"\u003e\u0026#34;enabled\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#79c0ff\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003eRenovate automatically detects image tags in the compose file and opens PRs when a newer version is available. For software that uses semantic versioning (\u003ccode\u003emajor.minor.patch\u003c/code\u003e), this can be fine-tuned – for example, automatically merging patch updates while reviewing minor updates manually.\u003c/p\u003e\n\u003cp\u003eI 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.\u003c/p\u003e\n\u003ch2 id=\"installing-and-configuring-webhook\"\u003eInstalling and Configuring webhook\u003ca href=\"#installing-and-configuring-webhook\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003ewebhook\u003c/code\u003e is available as a single binary, or as a package:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapt install webhook\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003eThe configuration lives at \u003ccode\u003e/etc/webhook/webhook.conf\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003eyaml\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"yaml\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e- \u003cspan style=\"color:#7ee787\"\u003eid\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003edeploy\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e  \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eexecute-command\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003e/opt/scripts/deploy.sh\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e  \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003ecommand-working-directory\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003e/opt/media-stack\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e  \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003etrigger-rule\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e    \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003ematch\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003etype\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003epayload-hmac-sha256\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003esecret\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;{{getenv \\\u0026#34;WEBHOOK_SECRET\\\u0026#34;}}\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e      \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003eparameter\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e        \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003esource\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eheader\u003c/span\u003e\u003cspan style=\"color:#6e7681\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6e7681\"\u003e        \u003c/span\u003e\u003cspan style=\"color:#7ee787\"\u003ename\u003c/span\u003e:\u003cspan style=\"color:#6e7681\"\u003e \u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eX-Gitea-Signature\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003eA few notes on this:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHeader name:\u003c/strong\u003e Gitea sends the signature as \u003ccode\u003eX-Gitea-Signature\u003c/code\u003e – without a \u003ccode\u003e-256\u003c/code\u003e suffix and without a \u003ccode\u003esha256=\u003c/code\u003e prefix in the value. Sounds trivial, but it cost me a debugging session on the first try.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSecret as environment variable:\u003c/strong\u003e The HMAC secret doesn\u0026rsquo;t belong in the config file in plaintext, especially if the file is versioned in the repo. So it\u0026rsquo;s read from the environment via \u003ccode\u003egetenv\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eGenerate the secret:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eopenssl rand -hex \u003cspan style=\"color:#a5d6ff\"\u003e32\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003eWrite it to a secrets file that only root can read:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#8b949e;font-style:italic\"\u003e# /etc/webhook/secrets.env\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#79c0ff\"\u003eWEBHOOK_SECRET\u003c/span\u003e\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003eyour-generated-string\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echmod \u003cspan style=\"color:#a5d6ff\"\u003e600\u003c/span\u003e /etc/webhook/secrets.env\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch3 id=\"systemd-unit\"\u003eSystemd Unit\u003ca href=\"#systemd-unit\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003eini\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"ini\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#8b949e;font-style:italic\"\u003e# /etc/systemd/system/webhook.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ff7b72\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDescription\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eWebhook Receiver\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAfter\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003enetwork.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ff7b72\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eUser\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eteqqy  # Replace with your own username\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEnvironmentFile\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003e/etc/webhook/secrets.env\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eExecStart\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003e/usr/bin/webhook -hooks /etc/webhook/webhook.conf -port 9000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eRestart\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003eon-failure\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ff7b72\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eWantedBy\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003emulti-user.target\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003e\u003ccode\u003eUser=teqqy\u003c/code\u003e ensures the webhook daemon and the deploy script don\u0026rsquo;t run as root. That\u0026rsquo;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 \u003ccode\u003edocker\u003c/code\u003e group.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eEnvironmentFile\u003c/code\u003e loads \u003ccode\u003esecrets.env\u003c/code\u003e and makes \u003ccode\u003eWEBHOOK_SECRET\u003c/code\u003e available as an environment variable before the process starts – so the secret flows cleanly into the \u003ccode\u003egetenv\u003c/code\u003e call in the config. \u003ccode\u003eRestart=on-failure\u003c/code\u003e ensures the daemon automatically restarts after an unexpected crash without manual intervention.\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemctl enable --now webhook\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch2 id=\"setting-up-the-gitea-webhook\"\u003eSetting Up the Gitea Webhook\u003ca href=\"#setting-up-the-gitea-webhook\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIn Gitea under \u003ccode\u003eRepository → Settings → Webhooks → Add Webhook → Gitea\u003c/code\u003e:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eTarget URL:\u003c/strong\u003e \u003ccode\u003ehttp://media01.example.com:9000/hooks/deploy\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSecret:\u003c/strong\u003e the same string you generated above\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eTrigger:\u003c/strong\u003e Only \u003ccode\u003ePush\u003c/code\u003e events are needed\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eOn every push, Gitea automatically computes an HMAC-SHA256 signature over the request body and sends it as the \u003ccode\u003eX-Gitea-Signature\u003c/code\u003e header. The webhook binary verifies the signature before executing the script – a simple Authorization header would be significantly less secure since it doesn\u0026rsquo;t protect the payload against tampering.\u003c/p\u003e\n\u003cp\u003ePort 9000 shouldn\u0026rsquo;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\u0026rsquo;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.\u003c/p\u003e\n\u003ch2 id=\"the-deploy-script\"\u003eThe Deploy Script\u003ca href=\"#the-deploy-script\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -euo pipefail\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#79c0ff\"\u003eREPO\u003c/span\u003e\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e/opt/media-stack\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#79c0ff\"\u003eLOG\u003c/span\u003e\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e/opt/deploy-logs/deploy-\u003cspan style=\"color:#ff7b72\"\u003e$(\u003c/span\u003edate +%Y%m%d-%H%M%S\u003cspan style=\"color:#ff7b72\"\u003e)\u003c/span\u003e.log\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexec \u0026gt;\u0026gt; \u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#79c0ff\"\u003e$LOG\u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;\u003c/span\u003e 2\u0026gt;\u0026amp;\u003cspan style=\"color:#a5d6ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;=== Deploy \u003c/span\u003e\u003cspan style=\"color:#ff7b72\"\u003e$(\u003c/span\u003edate\u003cspan style=\"color:#ff7b72\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003e ===\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd \u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#79c0ff\"\u003e$REPO\u003c/span\u003e\u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit pull origin main\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker compose up -d --remove-orphans\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker compose ps\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;=== Done ===\u0026#34;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003e\u003ccode\u003eset -euo pipefail\u003c/code\u003e ensures the script immediately aborts on any error and returns a non-zero exit code. webhook logs that as well, so \u003ccode\u003ejournalctl -u webhook\u003c/code\u003e is the first place to look when something goes wrong.\u003c/p\u003e\n\u003cp\u003eThe log directory needs to be owned by the executing user:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir -p /opt/deploy-logs\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echown teqqy:teqqy /opt/deploy-logs\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch2 id=\"pitfalls-i-hit-along-the-way\"\u003ePitfalls I Hit Along the Way\u003ca href=\"#pitfalls-i-hit-along-the-way\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ccode\u003e.git\u003c/code\u003e directory ownership:\u003c/strong\u003e If the repo was ever cloned or pulled as a different user, Git refuses access with a warning about \u0026ldquo;dubious ownership\u0026rdquo;. Fix:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echown -R teqqy:teqqy /opt/media-stack/.git\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ccode\u003esafe.directory\u003c/code\u003e:\u003c/strong\u003e In some setups this helps additionally:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit config --global --add safe.directory /opt/media-stack\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ccode\u003e/var/log\u003c/code\u003e permissions:\u003c/strong\u003e The executing user doesn\u0026rsquo;t have write access there. Put the log directory somewhere the user owns – I use \u003ccode\u003e/opt/deploy-logs\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ccode\u003e*arr\u003c/code\u003e containers and UID/GID:\u003c/strong\u003e My \u003ccode\u003e*arr\u003c/code\u003e containers run as \u003ccode\u003e99:100\u003c/code\u003e (Unraid-compatible for NFS reasons). On the very first deployment, Docker creates bind mount directories with \u003ccode\u003e1000:1000\u003c/code\u003e if they don\u0026rsquo;t exist yet. One-time fix before the first start:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echown -R 99:100 /mnt/data/arr/\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003eAfter that it\u0026rsquo;s a non-issue since the directories already exist.\u003c/p\u003e\n\u003ch2 id=\"monitoring\"\u003eMonitoring\u003ca href=\"#monitoring\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI use \u003ca href=\"https://uptime.kuma.pet/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eUptime Kuma\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n to check whether containers are still running cleanly after a deployment. That\u0026rsquo;s enough for my use case: if a container stops responding after a failed update, Uptime Kuma alerts me. The deploy log and \u003ccode\u003ejournalctl -u webhook\u003c/code\u003e then provide the details on what went wrong.\u003c/p\u003e\n\u003cp\u003eA dead man\u0026rsquo;s switch (e.g. via Healthchecks.io) would be the next sensible step if you also want to be notified when a deploy simply \u003cem\u003edoesn\u0026rsquo;t happen\u003c/em\u003e – but for a media stack that\u0026rsquo;s not critical enough for me to add the overhead.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003ca href=\"#conclusion\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe setup has been running for a little while now and does exactly what it\u0026rsquo;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.\u003c/p\u003e\n\u003cp\u003eFor anyone with similar requirements who doesn\u0026rsquo;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.\u003c/p\u003e\n\u003cp\u003eIf you have questions or feedback, reach me through the links in the menu bar.\u003c/p\u003e\n","date_published":"2026-06-03T00:00:00Z","date_modified":"2026-06-03T00:00:00Z","tags":["selfhosted","gitops","docker","gitea","renovate","homelab"]},{"id":"https://www.teqqy.de/en/my-top-10-selfhosted-and-homelab-software-2025-favorite-tools-for-the-new-year/","url":"https://www.teqqy.de/en/my-top-10-selfhosted-and-homelab-software-2025-favorite-tools-for-the-new-year/","title":"My Top 10 Selfhosted \u0026 Homelab Software 2025 – Favorite Tools for the New Year","summary":"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.\n#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.\n","content_html":"\u003cp\u003eJust 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.\u003c/p\u003e\n\u003ch1 id=\"10-wanderer\"\u003e#10: Wanderer\u003ca href=\"#10-wanderer\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h1\u003e\n\u003cp\u003eLet’s start with \u003ca href=\"https://wanderer.to\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eWanderer\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n, 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.\u003c/p\u003e\n\u003cp\u003eSince 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.\u003c/p\u003e\n\u003cp\u003eI 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.\u003c/p\u003e\n\u003ch2 id=\"9-nextcloud\"\u003e9: Nextcloud\u003ca href=\"#9-nextcloud\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://nextcloud.com\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eNextcloud\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n 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.\u003c/p\u003e\n\u003cp\u003eIn my \u003ca href=\"https://teqqy.de/tags/homelab/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eHomelab\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n, 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.\u003c/p\u003e\n\u003ch2 id=\"8-jellyfin\"\u003e8: Jellyfin\u003ca href=\"#8-jellyfin\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://jellyfin.org\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eJellyfin\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n, 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.\u003c/p\u003e\n\u003cp\u003eJellyfin 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.\u003c/p\u003e\n\u003ch2 id=\"7-freshrss\"\u003e7: FreshRSS\u003ca href=\"#7-freshrss\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI’ve written about \u003ca href=\"https://freshrss.org\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eFreshRSS\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n 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.\u003c/p\u003e\n\u003ch2 id=\"6-mealie\"\u003e6: Mealie\u003ca href=\"#6-mealie\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAnyone 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 \u003ca href=\"https://mealie.io\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eMealie\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n – I collect all my favorite recipes there.\u003c/p\u003e\n\u003cp\u003e\u003cimg 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\"\u003e\u003c/p\u003e\n\u003cp\u003eMealie 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.\u003c/p\u003e\n\u003cp\u003eMealie 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.\u003c/p\u003e\n\u003ch2 id=\"5-immich\"\u003e5: Immich\u003ca href=\"#5-immich\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://immich.app\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eImmich\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n 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.\u003c/p\u003e\n\u003cp\u003eOne 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.\u003c/p\u003e\n\u003cp\u003eImmich 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.\u003c/p\u003e\n\u003ch2 id=\"4-adventure-log\"\u003e4: Adventure Log\u003ca href=\"#4-adventure-log\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://adventurelog.app\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eAdventure Log\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n was a lucky find on the r/selfhosted subreddit. The developer shared it there, and I’ve been a fan ever since.\u003c/p\u003e\n\u003cp\u003e\u003cimg 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\"\u003e\u003c/p\u003e\n\u003cp\u003eIt 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.\u003c/p\u003e\n\u003cp\u003eAdventure Log also integrates nicely with Immich: you can link albums from your Immich instance directly to your travel entries and view them in context.\u003c/p\u003e\n\u003cp\u003eIt’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.\u003c/p\u003e\n\u003ch2 id=\"3-statistics-for-strava\"\u003e3: Statistics for Strava\u003ca href=\"#3-statistics-for-strava\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI started running regularly again a few months ago. I’ve been using Strava for years, and \u003ca href=\"https://github.com/robiningelbrecht/statistics-for-strava\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eStatistics for Strava\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n 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.\u003c/p\u003e\n\u003cp\u003e\u003cimg 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\"\u003e\u003c/p\u003e\n\u003cp\u003eStill, 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.\u003c/p\u003e\n\u003cp\u003eThe app runs as a Docker container, though you’ll need a separate cron job to import data and generate HTML reports.\u003c/p\u003e\n\u003ch2 id=\"2-mastodon\"\u003e2: Mastodon\u003ca href=\"#2-mastodon\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://joinmastodon.org\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eMastodon\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n 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 \u003ca href=\"/en/gotosocial-as-an-activitypub-server-my-experiences/\"\u003eprevious post\u003c/a\u003e\n), but overall, it’s been great.\u003c/p\u003e\n\u003cp\u003eDespite 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.\u003c/p\u003e\n\u003cp\u003eI host Mastodon as a Docker container on my VPS, which also powers this blog. The containerized setup makes updating much easier.\u003c/p\u003e\n\u003ch2 id=\"1-proxmox\"\u003e1: Proxmox\u003ca href=\"#1-proxmox\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe most important piece of software in my Homelab is definitely \u003ca href=\"https://www.proxmox.com\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eProxmox\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n. 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.\u003c/p\u003e\n\u003cp\u003eWith 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. 😊\u003c/p\u003e\n\u003cp\u003eIt’s fascinating to see how my Homelab has evolved over the years – and that brings me to the conclusion.\u003c/p\u003e\n\u003ch2 id=\"conclusion--whats-changed-since-2024\"\u003eConclusion – What’s Changed Since 2024\u003ca href=\"#conclusion--whats-changed-since-2024\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eComparing 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.\u003c/p\u003e\n\u003cp\u003eThis 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.\u003c/p\u003e\n\u003cp\u003eSome 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.\u003c/p\u003e\n\u003cp\u003eAnother 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.\u003c/p\u003e\n\u003cp\u003eWho 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.\u003c/p\u003e\n","date_published":"2025-10-13T17:20:00+01:00","date_modified":"2025-10-13T17:20:00+01:00","tags":["selfhosted","homelab","proxmox","mastodon","nextcloud","jellyfin"]},{"id":"https://www.teqqy.de/en/gotosocial-as-an-activitypub-server-my-experiences/","url":"https://www.teqqy.de/en/gotosocial-as-an-activitypub-server-my-experiences/","title":"GoToSocial as an ActivityPub Server — My Experiences","summary":"I’ve been on Mastodon (external link) and in the ActivityPub universe ever since Elon Musk took over Twitter. The concept that there are many distributed servers in a social network really appeals to me. It’s, if you will, social networking rethought — decentralized, open, and community-driven. Most importantly, we avoid many of the built-in problems of the big mainstream networks, no matter which political direction one might lean toward.\n","content_html":"\u003cp\u003eI’ve been on \u003ca href=\"https://joinmastodon.org/de\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eMastodon\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n and in the ActivityPub universe ever since Elon Musk took over Twitter. The concept that there are many distributed servers in a social network really appeals to me. It’s, if you will, social networking rethought — decentralized, open, and community-driven. Most importantly, we avoid many of the built-in problems of the big mainstream networks, no matter which political direction one might lean toward.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/gotosocial-als-activitypub-server-meine-erfahrungen/images/mastodon-smartphone.webp\" alt=\"Foto von einem Smartphone mit der Mastodon Webseite\" loading=\"eager\" fetchpriority=\"high\" decoding=\"async\"\u003e\u003c/p\u003e\n\u003ch2 id=\"my-journey-in-the-fediverse\"\u003eMy Journey in the Fediverse\u003ca href=\"#my-journey-in-the-fediverse\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eFrom day one it was clear to me: if I’m active somewhere on the internet, it should be self-hosted. That meant I didn’t just want an account on \u003ca href=\"https://mastodon.social/explore\" target=\"_blank\" rel=\"noopener noreferrer\"\u003emastodon.social\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n or \u003ca href=\"https://chaos.social/explore\" target=\"_blank\" rel=\"noopener noreferrer\"\u003echaos.social\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n, but full control of my own instance. I started with a Mastodon server on a Netcup VPS, using a classic (non-containerized) setup.\nSome time later I migrated that installation into Docker containers. Both versions ran without issues; updating is simply easier with containers.\u003c/p\u003e\n\u003ch2 id=\"moving-into-the-homelab\"\u003eMoving into the Homelab\u003ca href=\"#moving-into-the-homelab\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eA few months later, my Kubernetes \u003ca href=\"https://teqqy.de/tags/homelab/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eHomelab\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n was finally stable — so what better time to move the Mastodon instance there? With a Cloudflare Tunnel, routing wasn’t a problem. That setup ran smoothly too. At some point I switched from the official Mastodon containers to the LinuxServer.io images — but functionally it made no real difference.\u003c/p\u003e\n\u003ch2 id=\"switching-to-gotosocial\"\u003eSwitching to GoToSocial\u003ca href=\"#switching-to-gotosocial\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eA few weeks ago \u003ca href=\"https://me.klein.ruhr/@matthias\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eMatthias\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n wrote about his experiment with \u003ca href=\"https://gotosocial.org/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eGoToSocial\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n. This software is essentially a lightweight alternative to Mastodon: written in Go, runs as a single binary. No Ruby, no external dependencies, no bloat. Perfect for small installations with just a few active accounts. GTS does not include its own frontend, so you need to rely on external apps (mobile or web) for UI.\u003c/p\u003e\n\u003cp\u003eMatthias shared some observations on performance and resource usage \u003ca href=\"https://blog.klein.ruhr/gotosocial-ready-for-prime-time\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eon his blog\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n — pretty fascinating to read. So I spun up my own small GoToSocial instance. Configuration was very simple: download the binary, create a config file, start — done. And yes — it ran surprisingly stably. That said, it becomes clear fast that while GTS supports ActivityPub, interoperability is not yet on par with Mastodon.\u003c/p\u003e\n\u003ch2 id=\"problem-1--wanderer-and-silence-in-the-feed\"\u003eProblem #1 – Wanderer and Silence in the Feed\u003ca href=\"#problem-1--wanderer-and-silence-in-the-feed\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eActivityPub — the protocol behind many Fediverse platforms — has gained traction in selfhosting communities lately, and I was excited when the app \u003ca href=\"https://wanderer.to/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eWanderer\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n added support for it. The idea: share hiking routes, upload GPS tracks, and post them into the Fediverse. Fully federated via ActivityPub.\nI had to try it. Following from my GoToSocial instance was no problem. I imported a GPX route, then waited to see the post in my feed… nothing. After some fiddling I tried the same from a regular Mastodon account — and lo and behold, the hike showed up in the timeline.\nI reported the bug to the developer, but as of today nothing has changed. This clearly illustrates a point: ActivityPub is powerful but complex. Not every implementation “speaks” the same dialect, and small deviations can cause compatibility breaks.\u003c/p\u003e\n\u003ch2 id=\"problem-2--peertube-and-follower-issues\"\u003eProblem #2 – PeerTube and Follower Issues\u003ca href=\"#problem-2--peertube-and-follower-issues\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAnother area I explored was my old YouTube channel. I wanted to publish videos not only on YouTube, but also in the Fediverse. \u003ca href=\"https://joinpeertube.org/de\" target=\"_blank\" rel=\"noopener noreferrer\"\u003ePeerTube\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n is a good choice for a decentralized video platform: it works similarly to Mastodon, but for video content.\nInstallation was fast, but configuration took work: roles, permissions, federation settings, and many tuning parameters. But once set, it was stable and the community is active.\nHowever: interoperability between PeerTube and GoToSocial is even more brittle. I could follow accounts or channels, but PeerTube didn’t handle the follow request properly. This bug was known — an issue on GitHub claimed it was fixed in version 7.2. I was already running 7.3 — and still hit the error.\nAgain, when I tried from a Mastodon account everything worked flawlessly.\nI could list more minor quirks (incompatibilities, HTTP header oddities, federation timeouts, etc.), but that would be nitpicking.\u003c/p\u003e\n\u003ch2 id=\"interim-conclusion--small-tools-big-hurdles\"\u003eInterim Conclusion — Small Tools, Big Hurdles\u003ca href=\"#interim-conclusion--small-tools-big-hurdles\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe bottom line: if you have minimal demands and just want to run a lean Fediverse account, GoToSocial is a great choice. It’s slim, resource efficient, and quite stable. But once you try to extend beyond the basics — integrating with PeerTube, Bookwyrm, Pixelfed, or Wanderer — you notice that many projects haven’t quite aligned yet.\nThat’s understandable. Every one of these tools is developed by small teams or individuals, often in their spare time. There’s no big corporate oversight. That’s part of the charm of the Fediverse — and also the source of many small frustrations.\u003c/p\u003e\n\u003ch2 id=\"final-thoughts\"\u003eFinal Thoughts\u003ca href=\"#final-thoughts\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI can\u0026rsquo;t really judge who is “at fault” for these issues. Sure, Mastodon likely sets the de facto standard, and ultimately all these smaller projects are built and maintained by people doing it for passion, not profit. I just hope that over time things will converge and that all these platforms can talk cleanly to one another.\u003c/p\u003e\n","date_published":"2025-10-05T11:20:00+01:00","date_modified":"2025-10-05T11:20:00+01:00","tags":["selfhosted","fediverse","mastodon"]},{"id":"https://www.teqqy.de/en/save-power-with-proxmox-4-tips/","url":"https://www.teqqy.de/en/save-power-with-proxmox-4-tips/","title":"Save Power with Proxmox: 4 Practical Tips for Lower Energy Consumption","summary":"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.\n","content_html":"\u003ch1 id=\"proxmox-reducing-power-consumption-with-4-practical-tips\"\u003eProxmox: Reducing Power Consumption with 4 Practical Tips\u003ca href=\"#proxmox-reducing-power-consumption-with-4-practical-tips\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h1\u003e\n\u003cp\u003eIn 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.\u003c/p\u003e\n\u003cp\u003eIf you, like me, use \u003cstrong\u003eProxmox\u003c/strong\u003e — an open-source tool for virtualization — there are fortunately several effective ways to optimize your setup’s power consumption without sacrificing performance.\u003c/p\u003e\n\u003cp\u003eIn 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.\u003c/p\u003e\n\u003cp\u003eLet’s get started right away and see how you can pull more efficiency out of your Proxmox server!\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"beforehand-measure-power-consumption\"\u003eBeforehand: Measure Power Consumption\u003ca href=\"#beforehand-measure-power-consumption\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eTo 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 \u003cstrong\u003echeap power meter\u003c/strong\u003e. Note please that cheaper devices naturally don’t always have high accuracy.\u003cbr\u003e\nIf 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.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"pay-attention-to-the-right-hardware\"\u003ePay Attention to the Right Hardware\u003ca href=\"#pay-attention-to-the-right-hardware\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eOf 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.\u003cbr\u003e\nAdditionally, 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.\u003cbr\u003e\nIt 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.\u003c/p\u003e\n\u003cp\u003eTDP 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:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eGraphics cards\u003c/li\u003e\n\u003cli\u003eRAID controllers / HBAs\u003c/li\u003e\n\u003cli\u003eNetwork cards with more than 10 Gbit speed\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFurther 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.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"set-bios-settings-correctly\"\u003eSet BIOS Settings Correctly\u003ca href=\"#set-bios-settings-correctly\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003ch3 id=\"disable-unnecessary-hardware\"\u003eDisable Unnecessary Hardware\u003ca href=\"#disable-unnecessary-hardware\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eIn 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.\u003cbr\u003e\nIf 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.\u003c/p\u003e\n\u003ch3 id=\"enable-aspm\"\u003eEnable ASPM\u003ca href=\"#enable-aspm\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eASPM 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.\u003c/p\u003e\n\u003ch3 id=\"enable-cpu-power-saving-functions\"\u003eEnable CPU Power-Saving Functions\u003ca href=\"#enable-cpu-power-saving-functions\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eIn 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.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"c-states\"\u003eC-States\u003ca href=\"#c-states\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003ch3 id=\"what-are-c-states\"\u003eWhat Are C-States\u003ca href=\"#what-are-c-states\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eC-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.\u003c/p\u003e\n\u003cp\u003eThere are different C-states, from C0 up to Cn (depending on the processor). Here are some of the most common:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eC0:\u003c/strong\u003e Active, executing tasks.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eC1:\u003c/strong\u003e Light sleep, can quickly return to active.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eC3:\u003c/strong\u003e Deeper sleep, more energy saved, slower wake.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eC6:\u003c/strong\u003e Very deep sleep, almost completely off, longest wake time.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe switch between these states is automatically handled by the operating system and hardware based on current CPU load and energy settings.\u003c/p\u003e\n\u003cp\u003eYou should always aim for \u003cstrong\u003eC10 status\u003c/strong\u003e. 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.\u003c/p\u003e\n\u003ch4 id=\"check-c-states-with-powertop\"\u003eCheck C-States with \u003ccode\u003epowertop\u003c/code\u003e\u003ca href=\"#check-c-states-with-powertop\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h4\u003e\n\u003cp\u003e\u003ccode\u003epowertop\u003c/code\u003e 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”.\u003c/p\u003e\n\u003cp\u003eOften it’s PCI-Express devices that don’t allow further C-states. With the command:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003ebash\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"bash\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elspci -vv | awk \u003cspan style=\"color:#a5d6ff\"\u003e\u0026#39;/ASPM/{print $0}\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#79c0ff\"\u003eRS\u003c/span\u003e\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e | grep --color -P \u003cspan style=\"color:#a5d6ff\"\u003e\u0026#39;(^[a-z0-9:.]+|ASPM )\u0026#39;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n","date_published":"2023-03-14T21:17:37+01:00","date_modified":"2023-03-14T21:17:37+01:00","tags":["proxmox","hardware","homelab"]},{"id":"https://www.teqqy.de/en/3-dns-blocklists-for-pihole-adguard-or-blocky/","url":"https://www.teqqy.de/en/3-dns-blocklists-for-pihole-adguard-or-blocky/","title":"3 DNS Blocklists for PiHole, AdGuard or blocky","summary":"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.\n","content_html":"\u003ch1 id=\"3-dns-blocklists-for-pihole-adguard-or-blocky\"\u003e3 DNS Blocklists for PiHole, AdGuard or blocky\u003ca href=\"#3-dns-blocklists-for-pihole-adguard-or-blocky\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h1\u003e\n\u003cp\u003eA 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.\u003c/p\u003e\n\u003cp\u003eOf 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.\u003c/p\u003e\n\u003cp\u003eWhich 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.\u003c/p\u003e\n\u003ch2 id=\"ads-and-trackers\"\u003eAds and Trackers\u003ca href=\"#ads-and-trackers\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eTo 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.\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003e\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://raw.githubusercontent.com/ookangzheng/dbl-oisd-nl/master/dbl.txt\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003ch2 id=\"tv-ads-and-tracking\"\u003eTV Ads and Tracking\u003ca href=\"#tv-ads-and-tracking\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eOf 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.\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003e\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://gist.githubusercontent.com/wassname/b594c63222f9e4c83ea23c818440901b/raw/1b0afd2aecf3a099f1681b1cf18fc0e6e2fa116a/Samsung%2520Smart-TV%2520Blocklist%2520Adlist%2520(for%2520PiHole)\nhttps://gist.githubusercontent.com/wassname/78eeaaad299dc4cddd04e372f20a9aa7/raw/d7863a978993e99d9c77c9001008ce670a3b4c29/LG%2520Smart-TV%2520Blocklist%2520Adlist%2520(for%2520PiHole)\nhttps://raw.githubusercontent.com/Perflyst/PiHoleBlocklist/master/SmartTV.txt\nhttps://gist.githubusercontent.com/hkamran80/779019103fcd306979411d44c8d38459/raw/e0f084b396bb8ffcb390c8e7272ae96a6c2\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003ch2 id=\"fake-news\"\u003eFake News\u003ca href=\"#fake-news\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThis list blocks domains known for fake news, conspiracy theories, and misinformation.\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"code-block-header\"\u003e\n    \u003cspan class=\"code-lang-label\"\u003e\u003c/span\u003e\n    \u003cbutton class=\"code-copy-btn\" type=\"button\" aria-label=\"Copy code\" data-umami-event=\"Code Copy\" data-umami-event-lang=\"\"\u003e\n      \u003csvg 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\"\u003e\u003crect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"\u003e\u003c/rect\u003e\u003cpath d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\u003e\u003c/path\u003e\u003c/svg\u003e\n      \u003csvg 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\"\u003e\u003cpolyline points=\"20 6 9 17 4 12\"\u003e\u003c/polyline\u003e\u003c/svg\u003e\n    \u003c/button\u003e\n  \u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://raw.githubusercontent.com/jojo321/hosts/master/fakenews\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003ch2 id=\"whitelist\"\u003eWhitelist\u003ca href=\"#whitelist\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eOf 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.\u003c/p\u003e\n\u003ch2 id=\"further-recommendations\"\u003eFurther Recommendations\u003ca href=\"#further-recommendations\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIf you want to explore even more blocklists, take a look at these collections:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://firebog.net/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003ehttps://firebog.net/\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://blocklistproject.github.io/Lists/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003ehttps://blocklistproject.github.io/Lists/\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/blocklistproject/Lists\" target=\"_blank\" rel=\"noopener noreferrer\"\u003ehttps://github.com/blocklistproject/Lists\u003csvg 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\"\u003e\n  \u003cpath d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/\u003e\n  \u003cpolyline points=\"15 3 21 3 21 9\"/\u003e\n  \u003cline x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/\u003e\n\u003c/svg\u003e\u003cspan class=\"visually-hidden\"\u003e (external link)\u003c/span\u003e\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n","date_published":"2022-03-02T20:28:52+01:00","date_modified":"2022-03-02T20:28:52+01:00","tags":["selfhosted","dns","homelab"]},{"id":"https://www.teqqy.de/en/imprint/","url":"https://www.teqqy.de/en/imprint/","title":"Imprint","summary":"This is a German-run website, and the legally authoritative imprint (Impressum) required under German law (§ 5 DDG) is maintained in German only.\nYou can find it here: Impressum ","content_html":"\u003cp\u003eThis is a German-run website, and the legally authoritative imprint (\u003cem\u003eImpressum\u003c/em\u003e) required under German law (§ 5 DDG) is maintained in German only.\u003c/p\u003e\n\u003cp\u003eYou can find it here: \u003ca href=\"/impressum/\"\u003eImpressum\u003c/a\u003e\n\u003c/p\u003e\n","date_published":"0001-01-01T00:00:00Z","date_modified":"0001-01-01T00:00:00Z"},{"id":"https://www.teqqy.de/en/privacy-policy/","url":"https://www.teqqy.de/en/privacy-policy/","title":"Privacy Policy","summary":"This is a German-run website, and the legally authoritative privacy policy (Datenschutzerklärung) required under the GDPR is maintained in German only.\nYou can find it here: Datenschutzerklärung ","content_html":"\u003cp\u003eThis is a German-run website, and the legally authoritative privacy policy (\u003cem\u003eDatenschutzerklärung\u003c/em\u003e) required under the GDPR is maintained in German only.\u003c/p\u003e\n\u003cp\u003eYou can find it here: \u003ca href=\"/datenschutz/\"\u003eDatenschutzerklärung\u003c/a\u003e\n\u003c/p\u003e\n","date_published":"0001-01-01T00:00:00Z","date_modified":"0001-01-01T00:00:00Z"},{"id":"https://www.teqqy.de/en/search/","url":"https://www.teqqy.de/en/search/","title":"Search","summary":"","content_html":"","date_published":"0001-01-01T00:00:00Z","date_modified":"0001-01-01T00:00:00Z"},{"id":"https://www.teqqy.de/en/start/","url":"https://www.teqqy.de/en/start/","title":"Start Here","summary":"I unfortunately post here far too rarely — if you\u0026rsquo;re new here, it\u0026rsquo;s worth checking out a few of my best posts rather than scrolling through the archive. Here\u0026rsquo;s a selection, sorted by topic.\nMy Current Stack GitOps without Komodo – how I replaced Komodo for my Docker stack with a simple webhook-based setup. My Top 10 Selfhosted \u0026amp; Homelab Software 2025 – the tools I actually use every day. Proxmox \u0026amp; Homelab Save Power with Proxmox: 4 Practical Tips Networking \u0026amp; DNS 3 DNS Blocklists for PiHole, AdGuard or blocky Tesla Teslamate Setup: Docker, Fleet API \u0026amp; Grafana Fediverse GoToSocial as an ActivityPub Server — My Experiences ","content_html":"\u003cp\u003eI unfortunately post here far too rarely — if you\u0026rsquo;re new here, it\u0026rsquo;s worth checking out a few of my best posts rather than scrolling through the archive. Here\u0026rsquo;s a selection, sorted by topic.\u003c/p\u003e\n\u003ch2 id=\"my-current-stack\"\u003eMy Current Stack\u003ca href=\"#my-current-stack\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"/en/gitops-without-komodo/\"\u003eGitOps without Komodo\u003c/a\u003e\n – how I replaced Komodo for my Docker stack with a simple webhook-based setup.\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/en/my-top-10-selfhosted-and-homelab-software-2025-favorite-tools-for-the-new-year/\"\u003eMy Top 10 Selfhosted \u0026amp; Homelab Software 2025\u003c/a\u003e\n – the tools I actually use every day.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"proxmox--homelab\"\u003eProxmox \u0026amp; Homelab\u003ca href=\"#proxmox--homelab\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"/en/save-power-with-proxmox-4-tips/\"\u003eSave Power with Proxmox: 4 Practical Tips\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"networking--dns\"\u003eNetworking \u0026amp; DNS\u003ca href=\"#networking--dns\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"/en/3-dns-blocklists-for-pihole-adguard-or-blocky/\"\u003e3 DNS Blocklists for PiHole, AdGuard or blocky\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"tesla\"\u003eTesla\u003ca href=\"#tesla\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"/en/teslamate-setup-docker-fleet-api-grafana/\"\u003eTeslamate Setup: Docker, Fleet API \u0026amp; Grafana\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"fediverse\"\u003eFediverse\u003ca href=\"#fediverse\" class=\"heading-anchor\" aria-label=\"Link to this section\"\u003e\u003csvg 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\"\u003e\u003cpath d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"\u003e\u003c/path\u003e\u003cpath d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"/en/gotosocial-as-an-activitypub-server-my-experiences/\"\u003eGoToSocial as an ActivityPub Server — My Experiences\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n","date_published":"0001-01-01T00:00:00Z","date_modified":"0001-01-01T00:00:00Z"}]}