<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Software on SecretMine.de</title><link>https://secretmine.de/tags/software/</link><description>Recent content in Software on SecretMine.de</description><generator>Hugo -- gohugo.io</generator><language>de-DE</language><lastBuildDate>Mon, 10 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://secretmine.de/tags/software/index.xml" rel="self" type="application/rss+xml"/><item><title>Planka - leider keine Empfehlung mehr</title><link>https://secretmine.de/blog/2026/2026-08-10-planka-keine-empfehlung-mehr/</link><pubDate>Mon, 10 Aug 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-08-10-planka-keine-empfehlung-mehr/</guid><description>&lt;p&gt;Ich habe Planka lange gerne eingesetzt. Das hat sich leider geändert, da sich die Entwickler dazu entschlossen haben, Single-Sign-On in die kostenpflichtige Pro-Version zu verschieben und aus der Community-Version zu entfernen. Die Erläuterung der Entwickler lässt sich hier nachlesen: &lt;a class="link" href="https://github.com/plankanban/planka/issues/1754" target="_blank" rel="noopener"
 &gt;https://github.com/plankanban/planka/issues/1754&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;SSO ist für mich ein ziemlich grundlegendes Feature, vor allem bei OIDC. Ich kann generell auch verstehen, wenn Entwickler nicht alle Features kostenfrei anbieten möchten, aber ein bereits bestehendes Feature aus der kostenfreien Version zu entfernen bzw. kostenpflichtig zu machen, gefällt mir gar nicht. Außerdem ist SSO bei mir quasi Voraussetzung, vor allem bei den Tools, die meine Partnerin ebenfalls nutzt.&lt;/p&gt;
&lt;p&gt;Daher suche ich gerade eine Alternative. Mein Bruder ist mit Vikunja soweit glücklich, das werde ich mir selbst genauer ansehen. Auch gibt es Fork von einer früheren Version von Planka, das steht auch auf der Liste. Eine Migration ist für mich leider nicht so ganz einfach, da ich bspw. mit dem &lt;a class="link" href="https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/" &gt;PlankaPrinter&lt;/a&gt; die API von Planka nutze, um damit zu interagieren. Nextcloud Deck ist für mich leider keine Alternative, das kann zu wenig.&lt;/p&gt;
&lt;p&gt;Es wird ein weiterer Post folgen, wenn ich mich für etwas entschieden habe.&lt;/p&gt;</description></item><item><title>Firefly III - Self-Hosted und Open Source Haushaltsbuch</title><link>https://secretmine.de/blog/2026/2026-06-06-firefly-iii/</link><pubDate>Sat, 06 Jun 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-06-06-firefly-iii/</guid><description>&lt;p&gt;Firefly III ist ein Open-Source Tool zur Verwaltung persönlicher Finanzen. Es eignet sich ideal als Haushaltsbuch für den privaten Einsatz oder geteilte Konten in der Familie (oder für Paare) und bietet detaillierte Berichte für einen klaren Überblick.&lt;/p&gt;
&lt;h2 id="warum-überhaupt-so-ein-tool"&gt;Warum überhaupt so ein Tool?
&lt;/h2&gt;&lt;p&gt;Vorher fand das ganze in einer Excel-Tabelle statt, die wurde aber mit der Zeit immer komplexer. Deshalb habe ich mir eine Software dafür gesucht.&lt;/p&gt;
&lt;p&gt;Voraussetzung war aufgrund der Sensibilität ganz klar, dass ich es selbst betreiben können muss. Außerdem wollte ich ein verbreitetes System haben, wofür es auch schon Apps und ähnliches gibt. Gute Auswertungsmöglichkeiten waren mir auch wichtig.&lt;/p&gt;
&lt;p&gt;Firefly III ist eher auf Niveau einer Buchhaltungssoftware als ein Haushaltsbuch. Es gibt auch einfachere Tools wie beispielsweise Actual Budget. Alternativen hatten für mich aber zu wenig Features.&lt;/p&gt;
&lt;h2 id="installation-und-betrieb"&gt;Installation und Betrieb
&lt;/h2&gt;&lt;p&gt;Wie bei den meisten Dingen gibt es auch hierfür fertige Docker-Container, man findet auch offizielle Compose-Vorlagen. Der Stack besteht dabei aus der Anwendung, einer Datenbank (MariaDB) sowie einem Cron-Container. Von der Ressourcenauslastung her ist es eher auf der schlankeren Seite, also mehr als vertretbar.&lt;/p&gt;
&lt;h2 id="bedienung"&gt;Bedienung
&lt;/h2&gt;&lt;p&gt;Durch die Komplexität bzw. den Featureumfang ist Firefly anfangs etwas gewöhnungsbedürftig. Grundsätzlich bedient es sich aber ziemlich gut - Bestandskonten anlegen, danach kann man schon mit dem Anlegen von Transaktionen anfangen. Das lässt sich in Kategorien einteilen, Tags zuweisen und auch Budgets z.B. pro Monat definieren. Ich selbst nutze Firefly III nun seit 3 Jahren aktiv, dabei gemischt zwischen Rechner und Handy (via Waterfly III für mich und Abacus für die gemeinsamen Finanzen). Mittlerweile ist auch meine Partnerin von einer simplen Excel-Tabelle auf Firefly umgestiegen.&lt;/p&gt;
&lt;p&gt;Die Auswertungen gefallen mir sehr gut, ich kriege schnell einen guten Überblick darüber, wie der aktuelle Monat im Vergleich zu anderen dasteht und wohin das Geld geflossen ist. Über die Daueraufträge habe ich alle regelmäßigen Buchungen abgedeckt, was den Aufwand deutlich reduziert. Mit den Regeln lassen sich zusätzlich auch Kategorien, Tags und noch mehr automatisch anhand beispielsweise des Buchungstitels festlegen. So landen bei den gemeinsamen Finanzen alle Buchungen, die &lt;code&gt;&amp;lt;Laden&amp;gt; Lebensmittel&lt;/code&gt; als Titel haben, automatisch in der entsprechenden Kategorie und werden dem passenden Budget zugeordnet.&lt;/p&gt;
&lt;p&gt;Die UI ist nichts besonderes, erfüllt aber auf verschiedenen Bildschirmgrößen gut ihren Zweck.&lt;/p&gt;
&lt;h2 id="fazit"&gt;Fazit
&lt;/h2&gt;&lt;p&gt;Firefly III gehört für meine Partnerin und mich zu den zentralen Anwendungen, die am meisten verwendet werden. Wir nutzen es dabei fast täglich.&lt;/p&gt;</description></item><item><title>Matrix selbst hosten - Synapse, Element und warum das mehr als nur ein Messenger ist</title><link>https://secretmine.de/blog/2026/2026-05-29-matrix/</link><pubDate>Fri, 29 May 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-05-29-matrix/</guid><description>&lt;p&gt;Messenger gibt es viele. Matrix ist dabei eine Besonderheit, da es nicht direkt eine App meint, sondern ein offenes Protokoll für dezentrale Echtzeitkommunikation, auf dem verschiedene Clients und Server miteinander sprechen können.&lt;/p&gt;
&lt;h2 id="was-ist-matrix"&gt;Was ist Matrix?
&lt;/h2&gt;&lt;p&gt;Matrix ist ein offenes, föderiertes Kommunikationsprotokoll für Chat, Gruppenräume, Sprach- und Videokommunikation. Synapse ist dabei der klassische Matrix-Homeserver, also die Server-Software, die das eigenes Matrix-System betreibt.&lt;/p&gt;
&lt;p&gt;Als Client wird sehr häufig Element genutzt, weil es auf Desktop und Mobilgeräten breit verfügbar ist und den größten Teil der üblichen Messenger-Funktionen abdeckt und sich wie andere bekannte Messenger anfühlt. Es gibt auch diverse andere Clients wie z.B. Cinny, was von der UI her näher an Discord ist.&lt;/p&gt;
&lt;p&gt;Der größte Unterschied zu klassischen Messengern ist die Dezentralität. Man ist nicht an einen einzigen Anbieter gebunden, sondern kann mit anderen Matrix-Servern kommunizieren, ähnlich wie beispielsweise auch E-Mail über viele Anbieter hinweg funktioniert.&lt;/p&gt;
&lt;p&gt;Dazu kommt die offene Architektur: Der Server ist selbst hostbar und man kann den Client frei wählen. Das macht Matrix auch nicht nur für die private Kommunikation interessant, auch in Unternehmen und co bietet sich die Nutzung an. Dort kann man beispielsweise auch die Förderation für rein interne Kommunikation deaktivieren.&lt;/p&gt;
&lt;p&gt;Aber: Durch die Architektur und auch die Ende-zu-Ende-Verschlüsselung ist Matrix komplizierter in der Einrichtung, was schnell abschreckend wirkt.&lt;/p&gt;
&lt;h3 id="politischer-kontext"&gt;Politischer Kontext
&lt;/h3&gt;&lt;p&gt;Ja, ein Thema was ich sonst nicht betrachte. Finde ich hier aber erwähnenswert.&lt;/p&gt;
&lt;p&gt;Matrix passt ziemlich gut in die aktuelle Diskussion um digitale Souveränität, Datenschutz und unabhängige (Kommunikations-)Infrastruktur. Gerade in Europa und auch in Deutschland ist das Interesse an offenen Alternativen zu großen Plattformen seit Jahren hoch und auch diverse öffentliche Einrichtungen und Hochschulen nutzen Matrix als sicheren Messenger.&lt;/p&gt;
&lt;h2 id="installation"&gt;Installation
&lt;/h2&gt;&lt;p&gt;Ein alltagstaugliches Matrix-Setup besteht aus mehreren Komponenten:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Synapse als Homeserver (es gibt auch andere, aber Synapse ist der gängigste)&lt;/li&gt;
&lt;li&gt;Element als Frontend im Browser&lt;/li&gt;
&lt;li&gt;Synapse-Admin zur Benutzerverwaltung&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Von der Domaingestaltung habe ich bei mir etwa folgendes Setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Synapse unter &lt;code&gt;matrix.example.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Element unter &lt;code&gt;chat.example.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Wobei &lt;code&gt;example.com&lt;/code&gt; als Basis-URL für die Identitäten dient, das wird über eine &lt;code&gt;.well-known&lt;/code&gt; an die Subdomain &lt;code&gt;matrix.example.com&lt;/code&gt; delegiert&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Die Delegation hat den großen Vorteil, dass die Hauptdomain als Matrix-Adresse genutzt werden kann, aber nicht von Synapse belegt wird. Über eine kleine &amp;ldquo;Hinweis-Datei&amp;rdquo; auf der Hauptdomain wissen Clients (und andere Homeserver) dann, dass diese sich zu &lt;code&gt;matrix.example.com&lt;/code&gt; verbinden müssen.&lt;/p&gt;
&lt;p&gt;In diesem Fall wird Synapse mit sqlite installiert. Für kleine Setups ist das vollkommen in Ordnung, ich selbst nutze Postgres als Datenbank.&lt;/p&gt;
&lt;h3 id="docker"&gt;Docker
&lt;/h3&gt;&lt;p&gt;Wie das meiste betreibe ich Matrix in Docker. Meine Compose (&lt;code&gt;10.0.0.2&lt;/code&gt; ist die interne IP dieses Servers):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;synapse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;docker.io/matrixdotorg/synapse:latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;unless-stopped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;SYNAPSE_CONFIG_PATH=/data/homeserver.yaml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./data:/data&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;10.0.0.2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8008&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8008&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;vectorim/element-web:latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;unless-stopped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./config/element-config.json:/app/config.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;10.0.0.2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8009&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;synapse-admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ghcr.io/etkecc/synapse-admin:latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;unless-stopped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./config/synapse-admin-config.json:/app/config.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;10.0.0.2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8010&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Die grundlegende Konfiguration für Synapse lässt sich generieren:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose run --rm -e &lt;span class="nv"&gt;SYNAPSE_SERVER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;example.com synapse generate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Die relevanten Config-Files:&lt;/p&gt;
&lt;p&gt;Element (&lt;code&gt;config/element-config.json&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;default_server_config&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;m.homeserver&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;base_url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://matrix.example.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;disable_custom_urls&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;disable_guests&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;brand&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Mein Chatserver&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Synapse-Admin (&lt;code&gt;config/synapse-admin-config.json&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;restrictBaseUrl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;https://matrix.example.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Die eigentliche Config von Synapse (&lt;code&gt;data/homeserver.yaml&lt;/code&gt;) habe ich minimal modifiziert:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Configuration file for Synapse.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# This is a YAML file: see [1] for a quick introduction. Note in particular&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# that *indentation is important*: all the elements of a list or dictionary&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# should have the same indentation.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# For more information on how to configure Synapse, including a complete accounting of&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# each option, go to docs/usage/configuration/config_documentation.md or&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;server_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;example.com&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;pid_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/data/homeserver.pid&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8008&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;compress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;client&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;federation&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;tls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;http&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;x_forwarded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sqlite3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/data/homeserver.db&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;log_config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/data/example.com.log.config&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;media_store_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/data/media_store&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;registration_shared_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[...]&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;macaroon_secret_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[...]&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;form_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[...]&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;signing_key_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/data/example.com.signing.key&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;trusted_key_servers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;server_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;matrix.org&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Modifikationen&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;public_baseurl &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://matrix.example.com&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# URL, über die Synapse von außen erreichbar ist&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;report_stats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;enable_registration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="reverse-proxy"&gt;Reverse-Proxy
&lt;/h3&gt;&lt;p&gt;Ich habe Caddy als Reverse-Proxy im Einsatz. Die relevante Konfiguration in der Caddyfile sieht bei mir so aus:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;matrix.example.com {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; reverse_proxy 10.0.0.2:8008
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chat.example.com {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; reverse_proxy 10.0.0.2:8009
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;synapse-admin.example.com {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; # siehe https://caddyserver.com/docs/caddyfile/directives/basic_auth
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; basic_auth {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; # User hier
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; reverse_proxy 10.0.0.2:8010
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Synapse-Admin habe ich hinter Basic-Auth versteckt. Das ist an sich meiner Meinung nach nicht notwendig, aber fühlt sich besser an.&lt;/p&gt;
&lt;h3 id="delegation-einrichten"&gt;Delegation einrichten
&lt;/h3&gt;&lt;p&gt;Bei der Hauptdomain &lt;code&gt;example.com&lt;/code&gt; müssen einige Dateien angelegt werden.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.well-known/matrix/server&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;m.server&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;matrix.example.com:443&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;.well-known/matrix/client&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;m.homeserver&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;base_url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://matrix.example.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Damit sollte nun Förderation auch funktionieren. Mit dem Federation Tester (&lt;a class="link" href="https://federationtester.matrix.org/" target="_blank" rel="noopener"
 &gt;federationtester.matrix.org&lt;/a&gt;) lässt sich das prüfen.&lt;/p&gt;
&lt;h3 id="initialen-user-erstellen"&gt;Initialen User erstellen
&lt;/h3&gt;&lt;p&gt;Mittels&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose &lt;span class="nb"&gt;exec&lt;/span&gt; -it synapse register_new_matrix_user http://localhost:8008 -c /data/homeserver.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;lässt sich nun der initiale Admin-User (und auch weitere User natürlich) anlegen.&lt;/p&gt;
&lt;h2 id="clients-einrichten"&gt;Clients einrichten
&lt;/h2&gt;&lt;p&gt;An den Clients sollte ein Login auf &lt;code&gt;example.com&lt;/code&gt; nun funktionieren. Durch die Delegation werden Clients zu &lt;code&gt;matrix.example.com&lt;/code&gt; weitergeschickt.&lt;/p&gt;</description></item><item><title>Hochverfügbarkeit mit Keepalived</title><link>https://secretmine.de/blog/2026/2026-05-03-hochverfuegbarkeit-mit-keepalived/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-05-03-hochverfuegbarkeit-mit-keepalived/</guid><description>&lt;p&gt;Wer schon einmal mit mehreren Servern gearbeitet hat, kennt das Problem: Ein Dienst soll zwecks Ausfallsicherheit auf mehreren Servern laufen und parallel unter einer festen IP erreichbar sein. Natürlich mit Failover.&lt;/p&gt;
&lt;p&gt;An dieser Stelle kommt Keepalived ins Spiel. Das kleine, aber mächtige Tool übernimmt die Verwaltung von Floating IPs (oder Virtual IPs, kurz VIPs) und sorgt so für Hochverfügbarkeit für theoretisch beliebige Dienste.&lt;/p&gt;
&lt;h2 id="wie-funktioniert-das"&gt;Wie funktioniert das?
&lt;/h2&gt;&lt;p&gt;Keepalived setzt auf das VRRP-Protokoll (Virtual Router Redundancy Protocol), um eine virtuelle IP-Adresse an genau einen Master-Knoten zu vergeben.&lt;/p&gt;
&lt;p&gt;Wenn der Master ausfällt, erkennt Keepalived das automatisch und weist die IP einem Backup-Knoten zu. Meistens passiert das innerhalb von wenigen Sekunden. Dienste hinter dieser IP bleiben dadurch durchgängig erreichbar.&lt;/p&gt;
&lt;h2 id="typische-szenarien"&gt;Typische Szenarien
&lt;/h2&gt;&lt;h3 id="dns"&gt;DNS
&lt;/h3&gt;&lt;p&gt;Ein Klassiker. DNS hat zwar an sich durch die Angabe mehrerer DNS-Server schon auf der Ebene Ausfallsicherheit integriert, hier lässt sich aber auch Keepalived gut einsetzen. Zwei (oder mehr) Server sind dabei identisch eingerichtet und können beide jederzeit DNS-Anfragen beantworten.&lt;/p&gt;
&lt;p&gt;Keepalived sorgt hier nun für eine virtuelle IP, die immer auf einen der beiden Server gebunden ist und damit beim Ausfall des aktuell aktiven Servers innerhalb von kurzer Zeit auf den zweiten umgeschaltet wird.&lt;/p&gt;
&lt;h3 id="webserver"&gt;Webserver
&lt;/h3&gt;&lt;p&gt;Eigentlich exakt das selbe Prinzip wie bei DNS, hier müssen nur die Website-Daten auch auf einem gemeinsamen Storage liegen oder anderweitig allen Nodes bekannt sein.&lt;/p&gt;
&lt;h2 id="keepalived-und-docker-swarm"&gt;Keepalived und Docker Swarm
&lt;/h2&gt;&lt;p&gt;Hier wirds cool.&lt;/p&gt;
&lt;p&gt;Swarm hat Mesh Routing. Das heißt, dass jede Node Anfragen zu einem Service entgegen nehmen kann und intern dann zum richtigen Knoten weiterleitet. Heißt, es ist egal, welche Node man anspricht, die Antwort kommt auf jeden Fall.&lt;/p&gt;
&lt;p&gt;In Verbindung mit Keepalived kann man so Services in Docker Swarm wirklich hochverfügbar machen. Die virtuelle IP zeigt immer auf einen der Knoten, das interne Mesh-Routing erledigt dann den Rest. Wenn ein Knoten ausfällt, wird die IP umgeschaltet, parallel sorgt Swarm dafür, dass alle Services weiterhin verfügbar sind und bei Bedarf auf einem anderen Knoten neu gestartet werden.&lt;/p&gt;
&lt;p&gt;DNS-Einträge können damit immer auf die virtuelle IP eingestellt werden, alles weitere passiert automatisch.&lt;/p&gt;
&lt;h3 id="beispielkonfiguration"&gt;Beispielkonfiguration
&lt;/h3&gt;&lt;p&gt;Hier eine kurze Beispielconfig:&lt;/p&gt;
&lt;p&gt;Node &lt;code&gt;swarm-1&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;global_defs &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; router_id swarm-1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vrrp_instance VI_SWARM &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; state MASTER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; interface eth0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; virtual_router_id &lt;span class="m"&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; priority &lt;span class="m"&gt;255&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; advert_int &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; authentication &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; auth_type PASS
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; auth_pass meinsicherespw
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; virtual_ipaddress &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 192.168.1.60/24
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Node &lt;code&gt;swarm-2&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;global_defs &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; router_id swarm-2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vrrp_instance VI_SWARM &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; state BACKUP
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; interface eth0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; virtual_router_id &lt;span class="m"&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; priority &lt;span class="m"&gt;254&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; advert_int &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; authentication &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; auth_type PASS
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; auth_pass meinsicherespw
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; virtual_ipaddress &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 192.168.1.60/24
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Die &lt;code&gt;router_id&lt;/code&gt; ist hierbei immer Node-spezifisch. Die virtual_router_id ist letztendlich die Instanzkennung. Bei mir ist &lt;code&gt;swarm-1&lt;/code&gt; normalerweise der Master, hier ist durch die höhere Priorität auch ein Fail-Back (also das Zurückschalten von einer der anderen Nodes zurück zu &lt;code&gt;swarm-1&lt;/code&gt;) konfiguriert. Wenn die Prioritäten identisch sind, passiert kein Fail-Back.&lt;/p&gt;
&lt;p&gt;Die restlichen Optionen sind weitestgehend selbsterklärend, ansonsten findet man im Internet auch genügend Informationen zu Keepalived.&lt;/p&gt;
&lt;h3 id="kubernetes"&gt;Kubernetes
&lt;/h3&gt;&lt;p&gt;Bei Kubernetes lässt sich dieses Konstrukt mit MetalLB im Layer-2-Modus ziemlich identisch umsetzen. Die Erkennung von ausgefallenen Nodes läuft etwas anders, aber das Endergebnis (virtuelle IP, unter der immer eine Node erreichbar ist) ist das selbe.&lt;/p&gt;
&lt;h2 id="fazit"&gt;Fazit
&lt;/h2&gt;&lt;p&gt;Mir gefällt das Setup in dieser Kombination wirklich gut. Swarm kümmert sich um die Services, Keepalived sorgt für eine einheitliche IP für alle Services. Und dabei braucht Keepalived auch fast nichts an Ressourcen.&lt;/p&gt;</description></item><item><title>Latenzmessungen mit Smokeping</title><link>https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/</link><pubDate>Sat, 18 Apr 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/</guid><description>&lt;p&gt;Ich nutze nun schon seit vielen Jahren Smokeping zur Überwachung von Latenzen zu diversen Zielen. Primär mit dem Ziel, die Performance meiner Internetanbindung zu überwachen und Probleme gut identifizieren zu können, daneben aber auch, um Änderungen am Routing zwischen meinem Internetprovider und dem entsprechenden Ziel anhand von Latenzänderungen zu erkennen.&lt;/p&gt;
&lt;p&gt;Das Tool ist an sich recht alt, bietet aber trotzdem einen super Einblick in die Latenzen.&lt;/p&gt;
&lt;h2 id="ping-ziele"&gt;Ping-Ziele
&lt;/h2&gt;&lt;p&gt;Neben meinem Router (und dem Router im Keller im zweiten Homelab) pinge ich regelmäßig diverse Ziele im Internet. Dazu gehören neben den DNS-Servern von Google, Cloudflare und Quad9 auch die Test-Targets von Hetzner an allen derer Standorte, alle meine eigenen Server auch das VPN-Gateway meines Arbeitgebers.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Generische Internet-Ziele" class="gallery-image" data-flex-basis="317px" data-flex-grow="132" height="547" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/images/targets_internet.png" width="723"&gt;&lt;/p&gt;
&lt;h2 id="konfiguration"&gt;Konfiguration
&lt;/h2&gt;&lt;p&gt;Die gesamte Konfiguration liegt unter &lt;code&gt;/etc/smokeping/&lt;/code&gt;, wenn Smokeping einfach als Systempaket installiert wurde. In der Datei &lt;code&gt;/etc/smokeping/config.d/Targets&lt;/code&gt; lassen sich die Ziele definieren, die Syntax ist dabei recht einfach. Hier ein Auszug aus meiner Config:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;+ Local&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;menu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Local&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Local Network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;++ Gateway&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;menu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Gateway&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Gateway&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;192.168.1.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;+ Internet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;menu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Internet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Internet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;++ GoogleDNS1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;menu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Google DNS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Google DNS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;8.8.8.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;++ CloudflareDNS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;menu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Cloudflare DNS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Cloudflare DNS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1.1.1.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;++ Quad9DNS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;menu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Quad9 DNS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Quad9 DNS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;9.9.9.9&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Viel mehr außer die Targets habe ich nicht modifiziert, es lassen sich aber viel mehr Dinge noch konfigurieren.&lt;/p&gt;
&lt;h2 id="statistiken"&gt;Statistiken
&lt;/h2&gt;&lt;p&gt;Hier ein Beispiel von einem Ping-Ziel. Die Daten gehen dabei über ein Jahr zurück. Bei den längeren Statistiken sieht man hier und da ein bisschen Packetloss, das kann bei größeren Downloads bzw. generell Auslastung der Internetleitung zwischenzeitlich passieren.&lt;/p&gt;
&lt;p&gt;Schön zu sehen ist auch ein genereller Internetausfall im Juni für mehrere Tage mit Fallback zu LTE.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Google - 3 Stunden" class="gallery-image" data-flex-basis="542px" data-flex-grow="225" height="397" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/images/google_1.png" srcset="https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/images/google_1_hu_5bcc754b4c99f4cd.png 800w, https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/images/google_1.png 897w" width="897"&gt; &lt;img alt="Google - 30 Stunden" class="gallery-image" data-flex-basis="542px" data-flex-grow="225" height="397" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/images/google_2.png" srcset="https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/images/google_2_hu_f3160cd446fc58d4.png 800w, https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/images/google_2.png 897w" width="897"&gt; &lt;img alt="Google - 10 Tage" class="gallery-image" data-flex-basis="542px" data-flex-grow="225" height="397" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/images/google_3.png" srcset="https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/images/google_3_hu_2e3e6119a934b626.png 800w, https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/images/google_3.png 897w" width="897"&gt; &lt;img alt="Google - 365 Tage" class="gallery-image" data-flex-basis="542px" data-flex-grow="225" height="397" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/images/google_4.png" srcset="https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/images/google_4_hu_9d09d21ac71ee715.png 800w, https://secretmine.de/blog/2026/2026-04-18-latenzmessungen-mit-smokeping/images/google_4.png 897w" width="897"&gt;&lt;/p&gt;
&lt;h2 id="alternativen-zu-smokeping"&gt;Alternativen zu Smokeping
&lt;/h2&gt;&lt;p&gt;Als Alternativen zu Smokeping für Latenz-, Jitter- und Packetloss-Monitoring bietet sich Uptime Kuma oder der Blackbox Exporter an.&lt;/p&gt;
&lt;p&gt;Uptime Kuma ist ein selfhostbares Tool mit simplem Docker-Setup, das Ping-Monitoring inklusive Latenz, Packetloss und Up/Down-Status bietet – ergänzt durch schöne Dashboards, Status-Seiten und Notifications via Discord oder diverser anderer Kanäle. Jitter ist hier zwar nicht so detailliert wie bei Smokeping, aber die moderne Web-UI ist mobilfreundlich und ideal für Homelabs, wo man schnell loslegen will, ohne viel Konfigurationsaufwand. Uptime Kuma ist generell ein schönes Monitoring-Tool, mit dem man seine selbst betriebenen (oder auch externe) Dienste überwachen kann.&lt;/p&gt;
&lt;p&gt;Wenn man eh schon den klassischen Metriken-Stack mit Prometheus und Grafana hat, ist der Blackbox Exporter einen Blick wert. Der kann neben ICMP auch diverse andere Messungen bzw. Tests ausführen und Ziele überwachen. Durch die Nutzung von Grafana sind deutlich umfangreichere Visualisierungen möglich, auch mehrere Probe-Quellen sind gut umzusetzen. Der initiale Aufwand ist natürlich höher als bei Smokeping oder Uptime Kuma, aber der Blackbox Exporter skaliert deutlich weiter und kann am Ende mehr.&lt;/p&gt;
&lt;h2 id="fazit"&gt;Fazit
&lt;/h2&gt;&lt;p&gt;Obwohl die Software eher alt ist und visuell auch nicht die schönste ist, ist es trotzdem ein zentrales Tool in meinem Netzwerkmonitoring. Und das seit bald 10 Jahren. Das Tool funktioniert einfach, ist wartungsarm und liefert gute (wenn auch alt aussehende) Graphen. Daher wird es bei mir auch nicht in absehbarer Zeit abgelöst.&lt;/p&gt;</description></item><item><title>Nvidia Tesla P4</title><link>https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/</guid><description>&lt;p&gt;Ich experimentiere schon lange mit Ollama als Hosting-Plattform für LLMs. Bisher hatte ich dafür eine alte Nvidia GTX 1050 Ti mit 4GB, das reicht aber nur für kleine Modelle.&lt;/p&gt;
&lt;h2 id="auswahl-einer-gpu"&gt;Auswahl einer GPU
&lt;/h2&gt;&lt;p&gt;Mein Ziel war wie üblich eine gute Kombination aus Preis, Leistung und Stromverbrauch. Gebrauchte Datacenter-GPUs aus der Pascal-Reihe waren hierbei am interessantesten, da diese alt genug sind um im Preis deutlich gefallen zu sein und trotzdem CUDA-Versionen unterstützen, die aktuell genug sind, um eine gute Softwareunterstützung zu haben. 8GB VRAM waren mein Minimum, da sich damit schon viele der kleineren Modelle vollständig auf der GPU und viele mittlere Modelle zumindest zu einem guten Teil auf der GPU ausführen.&lt;/p&gt;
&lt;p&gt;Die Tesla P4 und die Tesla P40 fand ich hierbei am interessantesten. Die P40 hat 24GB VRAM, die P4 hat 8GB. Auch in der Rechenleistung unterscheiden sich die beiden Karten nennenswert. Da ich großen Wert auf einen niedrigen Energieverbrauch lege, habe ich mich für die Tesla P4 entschieden.&lt;/p&gt;
&lt;p&gt;Bestellt habe ich eine gebrauchte Datacenter-Karte, diese kam dann auch nach kurzer Zeit an.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Die Nvidia Tesla P4" class="gallery-image" data-flex-basis="326px" data-flex-grow="136" height="1468" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/packaging.jpg" srcset="https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/packaging_hu_fdef91378079bf43.jpg 800w, https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/packaging_hu_2c7ed1ad1c4da30e.jpg 1600w, https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/packaging.jpg 2000w" width="2000"&gt; &lt;img alt="Vorderseite" class="gallery-image" data-flex-basis="367px" data-flex-grow="153" height="1305" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/front.jpg" srcset="https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/front_hu_23c4a55254de7390.jpg 800w, https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/front_hu_7c07f2867ac2c954.jpg 1600w, https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/front.jpg 2000w" width="2000"&gt; &lt;img alt="Rückseite" class="gallery-image" data-flex-basis="359px" data-flex-grow="149" height="1336" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/back.jpg" srcset="https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/back_hu_b8613961d5f71614.jpg 800w, https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/back_hu_bd28667b075d4d5.jpg 1600w, https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/back.jpg 2000w" width="2000"&gt;&lt;/p&gt;
&lt;h2 id="kühlung"&gt;Kühlung
&lt;/h2&gt;&lt;p&gt;Es handelt sich bei der Tesla P4 um eine Karte, die für den Einsatz in Servern gebaut ist. Daher hat diese keine eigenen Lüfter, sondern ist auf Airflow vom Gehäuse angewiesen.&lt;/p&gt;
&lt;p&gt;Da ich die GPU allerdings nicht in einem klassischen Server, sondern in einem Desktop-Case verwenden möchte, musste ich hier etwas kreativ werden. Es gibt Adapter als 3D-Modell, mit denen man einen 40mm- bzw. 80mm-Lüfter für den Luftstrom durch die GPU nutzen kann. Das habe ich ausprobiert, allerdings habe ich keine passenden Lüfter, die mit dem Luftwiderstand vernünftig zurecht kommen.&lt;/p&gt;
&lt;p&gt;Daher wurde es eine etwas andere Lösung. Ich habe einfach zwei 40mm-Lüfter mit Kabelbindern auf der GPU platziert, vorher habe ich die Abdeckung entfernt und damit die Kühlrippen offengelegt. Das funktioniert tatsächlich brauchbar.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Meine Kühllösung" class="gallery-image" data-flex-basis="327px" data-flex-grow="136" height="1465" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/cooling.jpg" srcset="https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/cooling_hu_98d1eab2173248ba.jpg 800w, https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/cooling_hu_fc74624d34845ee4.jpg 1600w, https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/cooling.jpg 2000w" width="2000"&gt;&lt;/p&gt;
&lt;p&gt;Ich hatte temporär einen 120mm-Lüfter ausprobiert, der hatte noch eine bessere Kühlleistung, passte aber natürlich nicht ins Gehäuse.&lt;/p&gt;
&lt;p&gt;Von daher bleibt es bis auf Weiteres bei 2x 40mm-Lüftern für die GPU.&lt;/p&gt;
&lt;h2 id="performance"&gt;Performance
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Tesla P4 neben GTX 1050 Ti" class="gallery-image" data-flex-basis="255px" data-flex-grow="106" height="1876" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/p4-vs-1050ti.jpg" srcset="https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/p4-vs-1050ti_hu_98fe836fc8d34a30.jpg 800w, https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/p4-vs-1050ti_hu_72492fc3d3f54a50.jpg 1600w, https://secretmine.de/blog/2026/2026-04-10-nvidia-tesla-p4/images/p4-vs-1050ti.jpg 2000w" width="2000"&gt;&lt;/p&gt;
&lt;p&gt;Im Vergleich zur GTX 1050 Ti merkt man den doppelten VRAM schon deutlich.&lt;/p&gt;
&lt;p&gt;Die Performance gefällt mir soweit, auch wenn die Geschwindigkeit natürlich deutlich abfällt, sobald das Modell nicht mehr vollständig in den VRAM der GPU passt. Ich benutze weiterhin Ollama als Runtime, da ich nach Tests mit anderen Runtimes wie llama.cpp die Benutzerfreundlichkeit von Ollama bevorzuge.&lt;/p&gt;
&lt;h2 id="weitere-anwendungsfälle"&gt;Weitere Anwendungsfälle
&lt;/h2&gt;&lt;p&gt;Die P4 hat auch einen Hardware-Encoder für H.264 und H.265, damit kann ich auch gut altes Videomaterial (z.B. Aufnahmen von Spielen) gut komprimieren. Das geht aber mit der GTX 1050 Ti genauso gut, da der Hardware-Encoder am Ende der selbe ist.&lt;/p&gt;
&lt;h2 id="fazit"&gt;Fazit
&lt;/h2&gt;&lt;p&gt;Für die Nutzung mit Ollama ist die P4 brauchbar, die 8GB VRAM schränken allerdings spürbar ein. Trotzdem bereue ich den Kauf nicht und bin zufrieden, ich war mir dieser Einschränkungen von vornherein bewusst.&lt;/p&gt;</description></item><item><title>Memos - Notizen und Tagebuch</title><link>https://secretmine.de/blog/2026/2026-04-04-memos/</link><pubDate>Sat, 04 Apr 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-04-04-memos/</guid><description>&lt;p&gt;Ich nutze nun seit über zwei Jahren Memos (&lt;a class="link" href="https://usememos.com/" target="_blank" rel="noopener"
 &gt;https://usememos.com/&lt;/a&gt;) als Tool im Homelab. Es dient mir zum Notieren von Gedanken und Ideen, zusätzlich nutze ich es auch als eine Art unregelmäßig gepflegtes Tagebuch. Alles wird lokal gespeichert, es ist schlicht, braucht wenig Ressourcen und &amp;ldquo;just works&amp;rdquo; - perfekt, um mal eben etwas zu notieren.&lt;/p&gt;
&lt;h2 id="deployment"&gt;Deployment
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;neosmemo/memos:stable&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;5230&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;5230&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./data/:/var/opt/memos/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&amp;hellip; ja, das wars schon. Davor sitzt dann noch Caddy als Reverse-Proxy, um SSL zu machen und das ganze erreichbar zu bekommen. In Caddy ist auch noch eine zusätzliche Authentifizierungsschicht eingebaut, dazu schreibe ich irgendwann noch mal mehr.&lt;/p&gt;
&lt;h2 id="nutzung-im-alltag"&gt;Nutzung im Alltag
&lt;/h2&gt;&lt;p&gt;Ich habe Memos als Lesezeichen auf dem Rechner, auf dem Handy ist es als PWA installiert. Damit habe ich es immer griffbereit, um mir schnell etwas zu notieren und es später auch wiederzufinden. Dazu sind die Tags super hilfreich, damit kann man die Memos schön organisieren. Anhänge wie Bilder sind auch vorgesehen, diese lassen sich lokal, in der Datenbank oder in einem S3-Bucket speichern. Ich nutze dabei die lokale Option, das reicht vollkommen aus. Standardmäßig kommt Memos mit SQLite als Datenbank. Hier werden aber auch andere Datenbanktypen unterstützt.&lt;/p&gt;
&lt;p&gt;Zur Formatierung ist Markdown vorgesehen, das mag ich generell. Markdown ist für mich ein guter Mittelweg zwischen reinen Textdateien und erweiterten Formaten wie AsciiDoc oder LaTeX, kann aber trotzdem für das allermeiste genug.&lt;/p&gt;
&lt;p&gt;Auch die gemeinsame Nutzung ist vorgesehen - dafür gibt es mehrere Sichtbarkeitsstufen für einzelne Einträge. &lt;code&gt;Private&lt;/code&gt; ist nur für den Ersteller sichtbar, &lt;code&gt;Protected&lt;/code&gt; können angemeldete Benutzer sehen und &lt;code&gt;Public&lt;/code&gt; ist öffentlich. Gruppen oder ähnliches sind nicht vorgesehen, das fehlt mir allerdings auch nicht.&lt;/p&gt;
&lt;h2 id="fazit"&gt;Fazit
&lt;/h2&gt;&lt;p&gt;Mir gefällt Memos und ich nutze es im Alltag regelmäßig. Daher fand ich einen Post nach all der Zeit angemessen, vor allem, da es ein eher wenig bekanntes Open-Source-Produkt ist. Als Wiki-Lösung nutze ich allerdings zusätzlich noch eine andere Software, dazu folgt wahrscheinlich auch mal ein Beitrag.&lt;/p&gt;</description></item><item><title>Storage-Cluster - Part 2: Erster PoC und Planänderungen</title><link>https://secretmine.de/blog/2026/2026-03-28-storage-cluster-poc-und-planaenderungen/</link><pubDate>Sat, 28 Mar 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-03-28-storage-cluster-poc-und-planaenderungen/</guid><description>&lt;h1 id="storage-cluster---ein-erster-poc"&gt;Storage-Cluster - Ein erster PoC
&lt;/h1&gt;&lt;p&gt;Der Plan sieht Futro S740er mit SATA-Controllern vor. Einen entsprechenden Controller auf Basis des ASM1166 (energieeffizienter und gut unterstützter SATA-Controller) habe ich gekauft und mit einem NVMe-Adapter verbaut.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Proof of Concept" class="gallery-image" data-flex-basis="229px" data-flex-grow="95" height="2000" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-03-28-storage-cluster-poc-und-planaenderungen/images/storage_poc.jpg" srcset="https://secretmine.de/blog/2026/2026-03-28-storage-cluster-poc-und-planaenderungen/images/storage_poc_hu_1b3a6d9d9ab42c7c.jpg 800w, https://secretmine.de/blog/2026/2026-03-28-storage-cluster-poc-und-planaenderungen/images/storage_poc_hu_2fb146a9c9fd2dbc.jpg 1600w, https://secretmine.de/blog/2026/2026-03-28-storage-cluster-poc-und-planaenderungen/images/storage_poc.jpg 1915w" width="1915"&gt;&lt;/p&gt;
&lt;p&gt;Zugegeben, der PoC sieht etwas wild aus. Am HBA hängen drei SSDs, den Strom bekommen die SSDs über USB zu SATA-Strom-Adapter. Das funktioniert, da 2,5-Zoll-Laufwerke normalerweise nur 5V und keine 12V benötigen, SATA hat eigentlich auch 12V anliegen.&lt;/p&gt;
&lt;p&gt;Auf dem Futro läuft Proxmox, der Thin Client ist in meinen normalen Cluster eingebunden.&lt;/p&gt;
&lt;h2 id="ergebnisse-vom-poc"&gt;Ergebnisse vom PoC
&lt;/h2&gt;&lt;p&gt;Erst habe ich Ceph getestet. Hier hat sich schnell gezeigt, dass 4GB RAM für Ceph einfach zu wenig ist. Das ist schade, da ich Ceph eigentlich gerne genutzt hätte. Es war aber absehbar, dass das nicht klappt.&lt;/p&gt;
&lt;p&gt;Ohne Proxmox und weitere Services würde es vermutlich knapp passen, es ist mir allerdings zu heikel.&lt;/p&gt;
&lt;h2 id="alternative-zu-ceph"&gt;Alternative zu Ceph
&lt;/h2&gt;&lt;p&gt;Ich brauche eine Lösung, die halbwegs brauchbare IOPS liefern kann. SaunaFS ist zwar sehr flexibel und kann natürlich auch Replikation, erreicht aber nicht die IOPS-Werte, die ich benötige.&lt;/p&gt;
&lt;p&gt;Daher kommt vorerst GlusterFS zum Einsatz, das benutze ich auch jetzt schon für meinen Docker Swarm für persistenten Speicher. GlusterFS ist sehr viel schlanker als Ceph, allerdings auch langsamer und insgesamt schlechter zu administrieren. Ob es tatsächlich die bessere Wahl für mich ist wird sich mit der Zeit zeigen.&lt;/p&gt;
&lt;h2 id="wie-es-weitergeht"&gt;Wie es weitergeht
&lt;/h2&gt;&lt;p&gt;Ich habe die weitere Hardware für den Cluster hier schon liegen, von daher dürfte zeitnahe auch der Post zum vollständigen Setup kommen. Nebenbei bin ich dabei, Ansible-Rollen für die diversen Services zu entwickeln, da ich keine Lust habe, das meiste von Hand zu machen.&lt;/p&gt;
&lt;p&gt;Auch habe ich ein 3D-Modell für eine alternative Abdeckung mit großer Öffnung für die SATA-Kabel erstellt, das habe ich auch schon gedruckt und getestet. Damit kann ich die Futros trotzdem schließen und muss die originale Abdeckung nicht beschädigen.&lt;/p&gt;
&lt;p&gt;Zusätzlich habe ich auch ein Modell erstellt, was als Halterung für die Futros und die SSDs/HDDs dient. Davon passen dann auch drei nebeneinander in ein Kallax-Fach. Das dürfte insgesamt für mehr Ordnung sorgen und trotzdem Zugriff zu allem ermöglichen.&lt;/p&gt;</description></item><item><title>Code-Server</title><link>https://secretmine.de/blog/2026/2026-03-21-code-server/</link><pubDate>Sat, 21 Mar 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-03-21-code-server/</guid><description>&lt;p&gt;Ich nutze für meinen Blog mittlerweile Code-Server zum Schreiben und Bearbeiten der Beiträge. Da sich das für mich als überaus praktisch herausgestellt hat, möchte ich die Lösung hier einmal vorstellen.&lt;/p&gt;
&lt;h2 id="hintergrund"&gt;Hintergrund
&lt;/h2&gt;&lt;p&gt;Dieser Blog basiert auf Hugo, einem Static Site Generator. Ich habe ein Git-Repo, wo der gesamte Content (und natürlich Dinge wie CI-Files) enthalten sind. Bislang habe ich auf jedem Rechner, wo ich am Blog arbeiten möchte, Visual Studio Code und Hugo (sowie ein paar weitere Tools dafür) installiert. Ich musste auch immer daran denken, im Repo zu committen und zu pushen, wenn ich an einem anderen Rechner weiterarbeiten wollte. Das hat mehrfach dazu geführt, dass ich Merge-Konflikte hatte und auf verschiedenen Rechnern verschiedene Stände.&lt;/p&gt;
&lt;h2 id="meine-lösung"&gt;Meine Lösung
&lt;/h2&gt;&lt;p&gt;Ich kenne Code-Server schon einige Zeit und habe es in anderem Kontext schon verwendet. Letztendlich handelt es sich &amp;ldquo;einfach nur&amp;rdquo; um ein VS Code im Webbrowser, was auf einem Server läuft. Das habe ich in einem LXC installiert, daneben natürlich noch die notwendigen Tools wie Hugo.
Als Reverse-Proxy sitzt Caddy dazwischen, die Authentifizierung bzw. Absicherung ist über Forward-Auth zusammen mit Authentik gelöst. Damit kann ich sicher von überall auf den Code-Server zugreifen und an meinem Blog schreiben und bin nicht vom Rechner selbst abhängig.&lt;/p&gt;
&lt;h3 id="proxy-für-preview"&gt;Proxy für Preview
&lt;/h3&gt;&lt;p&gt;Standardmäßig lassen sich auf einem Subpfad von Code-Server freigegebene Ports (z.B. der integrierte Preview-Server von Hugo) unter &lt;code&gt;/proxy/&amp;lt;port&amp;gt;&lt;/code&gt; aufrufen. Hugo hat hier allerdings ein Problem, da der Subpfad für Hugo nicht sichtbar ist. Damit laden Assets wie CSS und JS nicht.&lt;/p&gt;
&lt;p&gt;Abhilfe schafft hier die Nutzung von &lt;code&gt;/absproxy/&amp;lt;port&amp;gt;&lt;/code&gt;, in Verbindung mit den Argumenten &lt;code&gt;--baserURL=https://coder.local/absproxy/1313/ --appendPort=false&lt;/code&gt; beim Aufruf von Hugo, damit einerseits der Subpfad vor Hugo nicht versteckt wird und andererseits Hugo für die Nutzung auf diesem Subpfad konfiguriert wird.&lt;/p&gt;
&lt;h2 id="fazit"&gt;Fazit
&lt;/h2&gt;&lt;p&gt;Der neue Workflow funktioniert für mich wirklich gut, ich kann auch während der Schreibphase für einen neuen Post problemlos zwischen Rechnern wechseln und habe immer die selbe Codebasis zur Verfügung. Das hat den Overhead durch genannte Merge-Konflikte (oder das Hochfahren von einem anderen Rechner, um die Änderungen noch ins Repo zu schreiben) spürbar reduziert.&lt;/p&gt;
&lt;p&gt;Ob das auf Dauer gut funktioniert weiß ich nicht, aber es sind mittlerweile 4 Monate, seit dem ich Code-Server nutze. Code-Server lässt sich auch für vieles anderes (und deutlich aufwändigeres) nutzen, als ich hier tue.&lt;/p&gt;</description></item><item><title>Lokales Multiroom-Audio Part 2 - Wechsel zu Snapcast</title><link>https://secretmine.de/blog/2026/2026-03-13-snapcast/</link><pubDate>Fri, 13 Mar 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-03-13-snapcast/</guid><description>&lt;p&gt;Bisher hatte ich für das lokale Multiroom-Audio-Setup &lt;a class="link" href="https://secretmine.de/blog/2025/2025-07-29-lyrion-music-server-und-squeezelite/" &gt;Lyrion Music Server&lt;/a&gt; im Einsatz. Grundsätzlich funktioniert das - es gibt allerdings einige Punkte, die mich stören.&lt;/p&gt;
&lt;h2 id="was-mir-fehlt-bzw-was-mich-stört"&gt;Was mir fehlt bzw. was mich stört
&lt;/h2&gt;&lt;h3 id="spotify-connect"&gt;Spotify Connect
&lt;/h3&gt;&lt;p&gt;Das Lyrion-Plugin für Spotify hatte ursprünglich Unterstützung für Spotify Connect, auch wenn das schon immer eher hakelig war und schnell die Verbindung verloren hat. Dieses Feature musste der Entwickler vom Plugin allerdings irgendwann entfernen, seit dem ist die Wiedergabe nur noch über LMS selbst möglich. Auf diesem Weg lässt sich zwar immer noch Musik von Spotify wiedergeben, trotzdem ist es deutlich schneller und angenehmer, einfach von der Spotify-App aus das Audio-Setup als Ziel auswählen zu können. Dieser Umweg hat auch dafür gesorgt, dass meine Partnerin das Soundsystem alleine ungefähr nie benutzt.&lt;/p&gt;
&lt;h3 id="synchronisierung"&gt;Synchronisierung
&lt;/h3&gt;&lt;p&gt;Die Synchronisierung von Lautsprechern funktioniert generell gut. Bei Verbindungsproblemen wird bei LMS allerdings die Wiedergabe auf allen Lautsprechern kurz unterbrochen. Snapcast lässt alle anderen Lautsprecher weiterlaufen, der Lautsprecher mit Verbindungsproblemen synchronisiert sich dann selbst wieder zum Rest. Die Aussetzer betreffen also nur den Player, der die Verbindungsprobleme hat.&lt;/p&gt;
&lt;h3 id="zu-viele-features"&gt;Zu viele Features
&lt;/h3&gt;&lt;p&gt;Wir brauchen zuhause eigentlich nur Spotify Connect. Lyrion bringt ein eigenes WebUI inkl. Verwaltungsmöglichkeiten für eine eigene Mediathek mit, das brauchen wir nicht. Snapcast ist insgesamt spürbar schlanker und braucht weniger Ressourcen.&lt;/p&gt;
&lt;h2 id="das-neue-setup"&gt;Das neue Setup
&lt;/h2&gt;&lt;p&gt;Bisher hatte ich bei den drei Playern jeweils piCorePlayer im Einsatz, das ist eine minimalistische Linux-Distribution speziell für die Nutzung eines Raspberry Pis für Squeezelite bzw. Lyrion Music Server. LMS selbst lief in Docker in einem LXC, mit einer Squeezelite-Instanz daneben für lokale Wiedergabe.&lt;/p&gt;
&lt;h3 id="server"&gt;Server
&lt;/h3&gt;&lt;p&gt;Der Server ist ein Debian 13 mit installiertem &lt;code&gt;snapserver&lt;/code&gt;. Librespot für Spotify Connect habe ich aus den Quellen selbst kompiliert, um eine möglichst neue Version zu haben. Ich habe zusätzlich noch &lt;code&gt;snapweb&lt;/code&gt; installiert, das ist ein moderner Webclient für Snapserver, der vor allem Gruppierung und Lautstärkeregelung ermöglicht und für Mobilgeräte optimiert ist. Damit entfällt die Notwendigkeit einer eigenen App für die Steuerung.&lt;/p&gt;
&lt;p&gt;Meine Konfiguration sieht wie folgt aus (&lt;code&gt;/etc/snapserver.conf&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[server]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[http]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;enabled = true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;doc_root = /usr/share/snapweb # for snapweb
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[tcp-control]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;enabled = true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[stream]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;source = pipe:///tmp/snapfifo?name=default
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;source = librespot:///librespot?name=Spotify%201&amp;amp;devicename=Home%201&amp;amp;bitrate=320&amp;amp;volume=90
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;source = librespot:///librespot?name=Spotify%202&amp;amp;devicename=Home%202&amp;amp;bitrate=320&amp;amp;volume=90
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[streaming_client]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;initial_volume = 80
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Die Config ist generell sehr minimal gehalten. Für Spotify Connect habe ich zwei Quellen definiert, damit beispielsweise ich im Arbeitszimmer und meine Partnerin im Wohnzimmer verschiedene Musik hören können.&lt;/p&gt;
&lt;h3 id="clients"&gt;Clients
&lt;/h3&gt;&lt;h4 id="thin-clients"&gt;Thin-Clients
&lt;/h4&gt;&lt;p&gt;Im Arbeitszimmer stehen an beiden Arbeitsplätzen Lautsprecher, diese sind mit je einem meiner Thin-Clients (Futro S740) verbunden. Der eine &lt;code&gt;snapclient&lt;/code&gt; läuft im selben LXC wie der Server, ich habe die Audiogeräte dafür durchgereicht. Für den zweiten läuft ein weiterer LXC, der eigentlich nur &lt;code&gt;snapclient&lt;/code&gt; installiert hat.&lt;/p&gt;
&lt;p&gt;Die Config (&lt;code&gt;/etc/defaults/snapclient&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SNAPCLIENT_OPTS=&amp;#34;--hostID arbeitszimmer-1 --soundcard hw:CARD=PCH,DEV=0 tcp://[Snapserver-IP]&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="raspberry-pis"&gt;Raspberry Pis
&lt;/h4&gt;&lt;p&gt;Hier bin ich einen anderen Weg gegangen als vorher. Alle drei Pis haben Raspberry Pi OS Lite bekommen, also die Variante ohne GUI.&lt;/p&gt;
&lt;p&gt;Auch hier ist Snapclient installiert, die Config sieht minimal anders aus (&lt;code&gt;/etc/defaults/snapclient&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SNAPCLIENT_OPTS=&amp;#34;--soundcard plug:plugequal --hostID wohnzimmer tcp://[Snapserver-IP]&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Das Zielgerät ist hier ein virtuelles Gerät von Alsa, um über den Equalizer Korrekturen vornehhmen zu können.&lt;/p&gt;
&lt;p&gt;Daher sieht die Konfiguration für Alsa so aus (&lt;code&gt;/etc/asound.conf&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;ctl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;pcm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugequal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;slave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pcm&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;plughw:CARD=Audio,DEV=0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;pcm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;plug&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;slave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pcm&lt;/span&gt; &lt;span class="nx"&gt;plugequal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Die Steuerung vom Equalizer ist via &lt;code&gt;sudo -u snapclient alsamixer -D equal&lt;/code&gt; möglich.&lt;/p&gt;
&lt;h5 id="relais-für-verstärker"&gt;Relais für Verstärker
&lt;/h5&gt;&lt;p&gt;An zwei Pis hängt an den GPIOs jeweils ein Relais, was einen Verstärker steuert. piCorePlayer hatte native Unterstützung dafür, dort musste ich nur den GPIO-Pin einstellen. Für Snapcast brauchte ich eine eigene Lösung.&lt;/p&gt;
&lt;p&gt;Das Ergebnis ist ein kleines Script, was in Dauerschleife prüft, ob auf einer Soundkarte Audio abgespielt wird und darüber einen GPIO-Output steuert.&lt;/p&gt;
&lt;p&gt;Das Script liegt bei mir unter &lt;code&gt;/usr/local/bin/amp-control.sh&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;GPIO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;CHECK_INTERVAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;POWER_ON_DELAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;IDLE_TIMEOUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;amp_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;last_audio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;audio_detected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pinctrl &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;$GPIO&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pinctrl &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;$GPIO&lt;/span&gt; dl
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; true&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; grep -q RUNNING /proc/asound/card*/pcm*/sub*/status&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;audio_detected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;last_audio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;date +%s&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;audio_detected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;now&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;date +%s&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Amp einschalten&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$audio_detected&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$amp_on&lt;/span&gt; -eq &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; pinctrl &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;$GPIO&lt;/span&gt; dh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;amp_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sleep &lt;span class="nv"&gt;$POWER_ON_DELAY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Amp ausschalten&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$amp_on&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;now-last_audio&lt;span class="k"&gt;))&lt;/span&gt; -gt &lt;span class="nv"&gt;$IDLE_TIMEOUT&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; pinctrl &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;$GPIO&lt;/span&gt; dl
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;amp_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sleep &lt;span class="nv"&gt;$CHECK_INTERVAL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Dazu gehört natürlich dann auch ein Systemd-Service, damit das ganze beim Systemstart mitstartet. Der Service liegt unter &lt;code&gt;/etc/systemd/system/amp-control.service&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[Unit]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Description=Snapclient Amp Control
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;After=snapclient.service
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[Service]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ExecStart=/usr/local/bin/amp-control.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Restart=always
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[Install]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;WantedBy=multi-user.target
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h5 id="read-only-root"&gt;Read-Only-Root
&lt;/h5&gt;&lt;p&gt;Nach der fertigen Konfiguration habe ich die SD-Karte via &lt;code&gt;overlayfs&lt;/code&gt; als Read-Only gemoutet, damit das System das Trennen vom Strom problemlos übersteht. Das geht einfach via &lt;code&gt;raspi-config&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="fazit"&gt;Fazit
&lt;/h2&gt;&lt;p&gt;Ich habe Snapcast jetzt seit wenigen Wochen im Einsatz, bisher bin ich zufrieden damit. Nach dem initialen Setup musste ich mich nicht mehr darum kümmern, Spotify Connect läuft auch sehr zuverlässig damit.&lt;/p&gt;</description></item><item><title>Planka - einfach nur Kanban</title><link>https://secretmine.de/blog/2026/2026-03-08-planka/</link><pubDate>Sun, 08 Mar 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-03-08-planka/</guid><description>&lt;blockquote class="alert alert-note"&gt;
 &lt;div class="alert-header"&gt;
 &lt;span class="alert-icon"&gt;📝&lt;/span&gt;
 &lt;span class="alert-title"&gt;Hinweis&lt;/span&gt;
 &lt;/div&gt;
 &lt;div class="alert-body"&gt;
 &lt;p&gt;Dieser Post entspricht nicht mehr dem aktuellen Stand, mehr dazu &lt;a class="link" href="https://secretmine.de/blog/2026/2026-08-10-planka-keine-empfehlung-mehr/" &gt;hier&lt;/a&gt;&lt;/p&gt;
 &lt;/div&gt;
 &lt;/blockquote&gt;
&lt;p&gt;Ich war lange auf der Suche nach einer ToDo-Lösung für mich.&lt;/p&gt;
&lt;h2 id="ein-bisschen-historie"&gt;Ein bisschen Historie
&lt;/h2&gt;&lt;p&gt;Ganz früher habe ich Wunderlist verwendet (heute Microsoft Todo), das hat aber nicht sonderlich lange funktioniert. Dann hatte ich über mehrere Jahre einen eigenen Jira-Server - das lief soweit gut, war aber für den normalen Alltag so viel Overkill wie nur geht. Klar, die Integration mit Confluence und auch die mobilen Apps dazu waren gut, trotzdem ist Jira eher für größeres Projektmanagement als für die eigene ToDo gebaut. Und die Server-Lizenzen für wenige Dollar gibts auch schon lange nicht mehr, Atlassian stellt die selbst hostbaren Produkte ja nach und nach komplett ein. Also zurück zu Microsoft Todo. Das war aber irgendwie immernoch nicht das Wahre. Auf der Suche nach einer weiteren neuen Lösung habe ich mir dann einige Softwareprodukte angesehen. Und gelandet bin ich am Ende bei Planka.&lt;/p&gt;
&lt;h2 id="was-kann-planka"&gt;Was kann Planka?
&lt;/h2&gt;&lt;p&gt;Ganz ehrlich? Nicht viel. Planka kann Kanban-Boards, genau dafür ist es entwickelt worden. Und abseits davon gibts auch nicht viel zu sehen. Aber die Boards sind übersichtlich, Planka braucht eher wenig Ressourcen, lässt sich mit SSO gut integrieren, hat fürs Handy zumindest die Möglichkeit für eine PWA und &lt;em&gt;just works&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Im Vergleich zu anderen Kanban-Lösungen ist Planka minimalistisch gehalten und konzentriert sich auf den Kerngedanken. Gefällt mir gut.&lt;/p&gt;
&lt;h2 id="wie-nutze-ich-planka"&gt;Wie nutze ich Planka?
&lt;/h2&gt;&lt;p&gt;Ich habe ein großes Board, mit Spalten für Irgendwann, Zeitnahe, Morgen, Heute, In Arbeit, Blocked und Fertig. Das ist also angelehnt an klassisches Kanban, aber mehr auf die Nutzung als persönliche ToDo getrimmt. Das ganze ist mit einer eigenen Software (&lt;a class="link" href="https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/" &gt;Plankaprinter&lt;/a&gt;) auch halbwegs automatisiert und bindet z.B. meinen Kalender an (und druckt ToDos für heute automatisch auf einem Kassenbondrucker aus). Dadurch kann ich auch sagen, dass sich die API gut nutzen lässt.&lt;/p&gt;
&lt;p&gt;Ich habe Planka als Web-App auf allen Rechnern &amp;ldquo;installiert&amp;rdquo;, auf dem Handy genauso. Die mobile Nutzung ist nicht perfekt, aber brauchbar.&lt;/p&gt;
&lt;p&gt;Das ganze funktioniert so gut, dass meine Partnerin jetzt ebenfalls von Microsoft Todo zu Planka gewechselt hat und Planka einer der zentralen Dienste neben Nextcloud und HomeAssistant geworden ist.&lt;/p&gt;
&lt;h2 id="was-fehlt-mir"&gt;Was fehlt mir?
&lt;/h2&gt;&lt;p&gt;Zugegeben, das neue Planka v2 habe ich mir noch nicht großartig angeschaut. Daher bezieht sich alles auf v1.&lt;/p&gt;
&lt;p&gt;Die mobile Nutzung ist nicht optimal. Vor allem die Meldung, dass die Serververbindung unterbrochen wurde, nachdem man die &amp;ldquo;App&amp;rdquo; eine Weile nicht benutzt hat, nervt. Verschieben von Karten funktioniert, teilweise scrollt der Ausschnitt aber zu weit zur Seite und man trifft nicht die korrekte Spalte.&lt;/p&gt;
&lt;p&gt;Auch das, was bei Jira die Epics sind, fehlt mir ein wenig. Also größere Aufgaben, zu der dann mehrere Karten gehören. Aber das ist eben nicht das Ziel von Planka, es ist kein volles Projektmanagement-Tool.&lt;/p&gt;
&lt;p&gt;Ansonsten wüsste ich aber nicht viel. Eigentlich funktioniert Planka wirklich solide.&lt;/p&gt;
&lt;h2 id="fazit"&gt;Fazit
&lt;/h2&gt;&lt;p&gt;Planka hat sich jetzt über mehrere Monate hinweg als hilfreich für die eigene Organisation erwiesen. Es kann genug (aber auch nicht zu viel), um als Kanban-Lösung für den Zweck ziemlich gut zu passen. Was will man mehr?&lt;/p&gt;</description></item><item><title>Storage-Cluster - Part 1: Der Plan</title><link>https://secretmine.de/blog/2026/2026-02-28-storage-cluster-der-plan/</link><pubDate>Sat, 28 Feb 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-02-28-storage-cluster-der-plan/</guid><description>&lt;h1 id="storage-cluster---der-plan"&gt;Storage-Cluster - der Plan
&lt;/h1&gt;&lt;p&gt;Wer regelmäßig mit Virtualisierung, Containern und Selfhosting arbeitet, kennt das Problem: der Storage wächst einem über den Kopf. Auch bei mir sind die bisherigen Nodes inzwischen so stark ausgelastet, dass für größere Storage-Aufgaben schlicht kein Platz mehr ist. Zeit also für den nächsten Ausbauschritt in meinem Homelab – ein (weitestgehend) dedizierter, energieeffizienter und möglichst robuster Storage-Cluster.&lt;/p&gt;
&lt;h2 id="das-ziel"&gt;Das Ziel
&lt;/h2&gt;&lt;p&gt;Wie auch in größeren IT-Projekten üblich, beginnt alles mit der Zielsetzung bzw. der Erfassung der Anforderungen.&lt;/p&gt;
&lt;p&gt;Ich brauche einerseits Storage, der schnell ist und den Ausfall einer von drei geplanten Nodes verkraftet, andererseits Massenspeicher für z.B. Medien, LXC-Templates, ISOs und co, der nicht hochverfügbar sein muss und auch nicht die selben Geschwindigkeiten erreichen muss. Auf dem schnellen Speicher sollen wichtige VMs/LXCs liegen (bspw. DNS und HomeAssistant), daneben soll dieser Bereich auch als Speicher für Docker Swarm dienen.&lt;/p&gt;
&lt;p&gt;Am liebsten hätte ich hier natürlich vollständige Hochverfügbarkeit, also keinen einzelnen Single-Point-of-Failure (redundantes Netzwerk, redundante Stromversorgung, &amp;hellip;). Das ist für Zuhause aber einerseits overkill und andererseits weder finanziell noch energetisch sinnvoll. Daher möchte ich eine möglichst gute Verfügbarkeit erreichen, ohne dabei komplett in den Enterprise-Bereich vorzudringen.&lt;/p&gt;
&lt;p&gt;Die bisherigen Nodes sind mit ihren eigentlichen Aufgaben zu stark ausgelastet, um hier sinnvoll auch noch Storage machen zu können. Das ist so gerade zwar im Einsatz, ich merke allerdings, dass ich mich damit zu nahe an der Grenze bewege.&lt;/p&gt;
&lt;h2 id="der-plan"&gt;Der Plan
&lt;/h2&gt;&lt;p&gt;Ich habe über die letzten Wochen einen Plan erarbeitet, PoCs aufgesetzt und vieles ausprobiert. Bisher ist der Cluster noch nicht aufgebaut, aber ich denke, dass ich mit der Auswahl an Hardware und Software mein Ziel erreiche.&lt;/p&gt;
&lt;h3 id="hardware-natürlich-thin-clients"&gt;Hardware. Natürlich Thin-Clients
&lt;/h3&gt;&lt;p&gt;Die Hardwarebasis bilden drei Fujitsu Futro S740 Thin Clients. Dass ich die Dinger mag, ist bekannt. Die Wahl fiel auf dieses Modell, da es günstig ist, sehr energieeffizient läuft und genügend Erweiterungsmöglichkeiten bietet. Der Plan ist hierbei, in der ersten Ausbaustufe den WiFi-Slot für einen SATA-Controller zu nutzen. Später soll der SATA-Controller dann in den SSD-Slot (der laut Internet neben SATA auch PCIe-Anbindung hat) wandern und der WiFi-Slot für eine 2,5 GBit/s Netzwerkkarte genutzt werden.&lt;/p&gt;
&lt;p&gt;Geplanter Ausbau pro Node:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;4 GB RAM&lt;/li&gt;
&lt;li&gt;1x 16/32GB für das OS (meistens dabei)&lt;/li&gt;
&lt;li&gt;1× 480 GB SSD (für Ceph)&lt;/li&gt;
&lt;li&gt;1× 1 TB HDD (für SaunaFS oder Alternativen)&lt;/li&gt;
&lt;li&gt;SATA-Controller über den WiFi-Slot (Adapterlösung)&lt;/li&gt;
&lt;li&gt;Später Upgrade auf 2,5 Gbit/s LAN&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Das Netzwerk wird anfangs über meine bestehende 1 GBit/s-Infrastruktur gelöst, später ist hier ein zusätzlicher 2,5 GBit/s Switch geplant. Ich rechne ohne Datenträger mit Kosten von ca 75 € pro Knoten, was ich für nicht viel halte. Ohne Datenträger bekomme ich damit für etwas über 200 € einen weitestgehend redundanten Storage-Cluster.&lt;/p&gt;
&lt;h3 id="software"&gt;Software
&lt;/h3&gt;&lt;p&gt;Für den Storage-Cluster sollen zwei Software-Lösungen zum Einsatz kommen. Ceph wird für den schnellen Speicher genutzt, der Massenspeicher wird nach aktuellem Stand auf SaunaFS aufbauen. Ceph habe ich ausgesucht, da es von Proxmox nativ unterstützt wird und dort sogar gut integriert ist. Neben Blockspeicher kann ich dort über CephFS auch Freigaben auf Dateiebene erzeugen, was ich für Docker Swarm benötige. Es ist zudem performant und im Enterprise-Bereich stark vertreten. Für den langsameren Speicher habe ich lange überlegt, was ich nutzen möchte. Erst hatte ich GlusterFS vorgesehen. Mittlerweile habe ich mich hier aber umentschieden und möchte SaunaFS einsetzen. Das ist ein Fork von LizardFS, was wiederum ein Fork von MooseFS ist. Es ist für verteilten Massenspeicher ausgelegt, hat eher wenig Overhead und bietet sogar die Option, die Anzahl der Replikas pro Ordner festzulegen.&lt;/p&gt;
&lt;p&gt;Auf jedem der Futros wird dementsprechend Proxmox VE installiert und die Nodes in den bisherigen Cluster eingefügt. Die SSDs werden als OSDs für Ceph genutzt, die zusätzlichen Datenträger dann als Mountpoints an einen LXC pro Node angebunden. Dort wird dann SaunaFS installiert. SaunaFS kann ich aufgrund der Voraussetzung von Ubuntu als OS leider nicht direkt auf den Knoten installieren, die Variante mit SaunaFS habe ich jetzt aber auch schon eine Weile im Haupt-Cluster im Testbetrieb erfolgreich genutzt.&lt;/p&gt;
&lt;p&gt;Das SaunaFS-Volume wird dann von einem Knoten via Samba und NFS freigegeben. Das hat den Hintergrund, dass es mit vielen Dingen kompatibel ist und sich so z.B. auch in Proxmox als Speicher für Templates und ISOs gut einbinden lässt.&lt;/p&gt;
&lt;h2 id="weitere-ideen-und-mögliche-dienste"&gt;Weitere Ideen und mögliche Dienste
&lt;/h2&gt;&lt;p&gt;Wenn die Performance passt, sollen auf dem Storage-Cluster später auch leichte Dienste laufen und damit die bestehenden Nodes etwas entlastet werden. Kandidaten dafür sind Dinge wie der Proxmox Backup Server oder auch Komponenten von meinem Monitoring-Stack.&lt;/p&gt;
&lt;p&gt;Ob das mit 4 GB RAM pro Node praktikabel ist, wird sich erst zeigen - mehr RAM kaufen ist aber aufgrund der Lage am Markt gerade keine Option.&lt;/p&gt;
&lt;h2 id="nächste-schritte"&gt;Nächste Schritte
&lt;/h2&gt;&lt;p&gt;Ich habe Hardware bestellt, um einen Futro mal mit einem SATA-Controller auszustatten und damit schonmal als Storage (ohne Ceph oder Replikation oder so) einzusetzen. Das dient als erster Test. Wenn das so funktioniert, wie ich mir das vorstelle, werden weitere Futro S740er gekauft und der Cluster aufgebaut.&lt;/p&gt;
&lt;p&gt;Ich nehme dafür den Futro, den ich eigentlich für den 3D-Drucker gekauft hatte. Dort kommt einfach wieder der S520 hin. Das bietet sich an, da ich für den 3D-Drucker wenig Leistung brauche, der Futro S520 eh nichts zu tun hat und ich so gut etwas Geld sparen kann.&lt;/p&gt;</description></item><item><title>Authentifizierung und Single-Sign-On im Homelab mit Authentik</title><link>https://secretmine.de/blog/2026/2026-02-22-authentifizierung-und-sso-im-homelab-mit-authentik/</link><pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-02-22-authentifizierung-und-sso-im-homelab-mit-authentik/</guid><description>&lt;p&gt;Zentrale Authentifizierung bringt selbst im Homelab klare Vorteile: Einmalige Anmeldung spart Zeit, erhöht die Sicherheit durch weniger Passwörter und erleichtert die Verwaltung von Zugriffsrechten – perfekt für ein Homelab mit vielen Services. Nebenbei bekommt man damit auch Anwendungen, die zwar SSO können, aber keine Multifaktorauthentisierung beherrschen, mit MFA ausgestattet.&lt;/p&gt;
&lt;h2 id="vorteile-gegenüber-anderen-lösungen-keycloak"&gt;Vorteile gegenüber anderen Lösungen (Keycloak)
&lt;/h2&gt;&lt;p&gt;Ich hatte früher Keycloak im Einsatz, was mir aber aufgrund seiner Komplexität im Homelab einfach zu überladen war. Keycloak kann sicherlich mehr als Authentik (denke ich zumindest), ist für mich zuhause allerdings overkill. Authentik ist zwar auch nicht trivial, trotzdem empfinde ich die Konfiguration als angenehmer. Für mich ist der Hauptzweck im Homelab OpenID Connect (OIDC), andere Protokolle wie SAML spielen bei mir keine Rolle.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Admin-Oberfläche von Authentik" class="gallery-image" data-flex-basis="460px" data-flex-grow="191" height="800" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-02-22-authentifizierung-und-sso-im-homelab-mit-authentik/images/admin.png" srcset="https://secretmine.de/blog/2026/2026-02-22-authentifizierung-und-sso-im-homelab-mit-authentik/images/admin_hu_3af7f69f9b865def.png 800w, https://secretmine.de/blog/2026/2026-02-22-authentifizierung-und-sso-im-homelab-mit-authentik/images/admin.png 1534w" width="1534"&gt;&lt;/p&gt;
&lt;h2 id="outposts"&gt;Outposts
&lt;/h2&gt;&lt;p&gt;Ein weiterer großer Vorteil von Authentik sind die sogenannten Outposts, mit denen neben SSO-Protokollen auch LDAP-Dienste, Remotedesktop-Zugriff und Proxy-Authentifizierung angeboten werden.&lt;/p&gt;
&lt;p&gt;Über den Proxy-Outpost kann man die Authentifizierung direkt in einem Reverse-Proxy bzw. Webserver integrieren. Dadurch lassen sich interne Anwendungen sehr sicher über das Internet erreichbar machen. Bei mir laufen viele Anwendungen mit OIDC-Anbindung, einige interne Dienste sind zudem über Caddy mit Forward-Auth abgesichert. Mein Proxmox-Cluster ist sogar direkt per LDAP-Outpost an Authentik angebunden, was den Sync von Usern und Gruppen einfach macht.​ Ob das sinnvoll ist, ist allerdings eine andere Sache, immerhin bin ich der einzige Benutzer. Andere Services wie beispielsweise Nextcloud oder Planka nutze ich hingegen nicht nur alleine.&lt;/p&gt;
&lt;p&gt;Relativ neu ist der Remote Access Control (RAC) Outpost, womit sich Remotedesktop-Dienste anbinden lassen. Die Technik dahinter ist am Ende allerdings &amp;ldquo;nur&amp;rdquo; ein Apache Guacamole, den ich schon &lt;a class="link" href="https://secretmine.de/blog/2025/2025-06-09-guacamole/" &gt;anderweitig betreibe&lt;/a&gt; und durch den getrennten Betrieb bessere Kontrolle und mehr Konfigurationsmöglichkeiten erhalte.&lt;/p&gt;
&lt;h3 id="konfiguration-für-forward-auth-mit-caddy"&gt;Konfiguration für Forward-Auth mit Caddy
&lt;/h3&gt;&lt;p&gt;Hier ein kurzes Config-Snippet für Auth mit Caddy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(authentik_forwardauth) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; reverse_proxy /outpost.goauthentik.io/* http://proxy-outpost:9000
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; forward_auth http://proxy-outpost:9000 {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; uri /outpost.goauthentik.io/auth/caddy
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Entitlements X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; trusted_proxies private_ranges
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;proxmox.example.com {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; import authentik_forwardauth
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; reverse_proxy 192.168.0.2:8006 {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; transport http {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tls_insecure_skip_verify
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;In Authentik muss Proxmox mit der entsprechenden Domain als Proxy-Provider angelegt werden, mit dem Modus &amp;ldquo;Forward Auth&amp;rdquo;. Beim Outpost selbst muss dieser Provider dann noch zugewiesen werden, danach sollte alles schon funktionieren.&lt;/p&gt;
&lt;p&gt;Der Proxy Outpost kann wohl auch direkt Proxy spielen - mit der Lösung via Caddy und Forward-Auth kann ich allerdings mehrere Anwendungen anbinden, ohne mehrere Outposts zu benötigen.&lt;/p&gt;
&lt;h2 id="weitere-praktische-features"&gt;Weitere praktische Features
&lt;/h2&gt;&lt;p&gt;Neben der eigentlichen Authentifizierung zeigt Authentik die eingerichteten Anwendungen auch auf einer übersichtlichen Hub-Oberfläche an. Die Anwendungen lassen sich in Gruppen ordnen, es werden auch nur die Anwendungen angezeigt, die zu denen der entsprechende Benutzer Zugang hat. Das erspart den Betrieb von einer getrennten Homepage bzw. Hub-Oberfläche.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Authentik Hub" class="gallery-image" data-flex-basis="482px" data-flex-grow="200" height="672" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-02-22-authentifizierung-und-sso-im-homelab-mit-authentik/images/hub.png" srcset="https://secretmine.de/blog/2026/2026-02-22-authentifizierung-und-sso-im-homelab-mit-authentik/images/hub_hu_c68b7ce971da903d.png 800w, https://secretmine.de/blog/2026/2026-02-22-authentifizierung-und-sso-im-homelab-mit-authentik/images/hub.png 1350w" width="1350"&gt;&lt;/p&gt;
&lt;h2 id="fazit"&gt;Fazit
&lt;/h2&gt;&lt;p&gt;Nach mehreren Jahren im Betrieb funktioniert Authentik zuverlässig und stabil. Es ist eine empfehlenswerte Alternative zu Keycloak, wenn man eine sichere, moderne Authentifizierungslösung mit erträglichem Konfigurationsaufwand im Homelab sucht.​ Vor allem die Outposts sind ein klarer Vorteil von Authentik gegenüber anderer Lösungen.&lt;/p&gt;</description></item><item><title>Harbor als lokale Container-Registry</title><link>https://secretmine.de/blog/2026/2026-02-06-harbor-als-lokale-container-registry/</link><pubDate>Fri, 06 Feb 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-02-06-harbor-als-lokale-container-registry/</guid><description>&lt;p&gt;Harbor ist eine Open-Source-Container-Registry-Plattform, die Docker-Images und Helm-Charts speichert und verwaltet. Neben der Nutzung als normale Registry zum Speichern von Images kann Harbor auch als Proxy-Registry dienen. Das ist bei mir der primäre Einsatzzweck. Nebenbei kann Harbor auch die Images mit Trivy auf bekannte Schwachstellen scannen. Die Übersichtsseite leuchtet leider schnell wie ein Weihnachtsbaum - man sieht schnell, welche Images lange nicht mehr aktualisiert wurden oder veraltete Softwareversionen nutzen. Trivy ist hierbei einer der gängigen Scanner, der die im Image vorhandenen Komponenten gegen Datenbanken mit bekannten Schwachstellen prüft (nicht nur normale Pakete, es werden bei diversen Programmiersprachen auch die verwendeten Libraries geprüft).&lt;/p&gt;
&lt;p&gt;Registries wie Docker Hub haben oft Pull-Limits, besonders bei häufigen Updates im Homelab ist das schnell nervig. Mit Harbor dazwischen reduziere ich die Anzahl der Pulls direkt von Docker Hub und kann auch schon gespeicherte Images oder Layer schneller ausliefern. Vor allem durch meine Verwendung von Docker Swarm ist das von Vorteil, da sonst bei der Migration eines Services von einer Node zu einer anderen das Image neu von der Quelle heruntergeladen wird. Mit Harbor als Proxy bzw. Cache kommt das Image dann direkt aus dem lokalen Netz.&lt;/p&gt;
&lt;h2 id="installation"&gt;Installation
&lt;/h2&gt;&lt;p&gt;Die Einrichtung ist unkompliziert, allerdings etwas anders, als man es von anderen containerisierten Diensten kennt. Man benötigt den Harbor-Installer von z.B. Github. Dann muss noch die harbor.yml erstellt bzw. angepasst werden (Hostname, HTTPS, Admin-Passwort), dann wird der Installer ausgeführt. Die Services sind am Ende ein normaler Docker Compose Stack.&lt;/p&gt;
&lt;p&gt;Sinnvoll ist hier auch die Nutzung eines Reverse-Proxies mit SSL, da Docker (und auch andere Container-Runtimes) Pulls aus unsicheren (=http) Registries ohne weitere Konfiguration verweigern. Am einfachsten ist ein Zertifikat von bspw. Lets Encrypt, mit der DNS-Verifizierungsmethode geht das dann auch, ohne dass Harbor von außen erreichbar ist. Selbstsignierte Zertifikate (z.B. via ACME von [Step-CA]({&amp;laquo; ref &amp;ldquo;/blog/2025/2025-02-28-lokale-ssl-zertifikate-mit-caddy-und-step-ca/index.md&amp;rdquo; &amp;raquo;})) sind möglich, dann muss das entsprechende Root-Zertifikat allerdings auf jedem System, das die lokale Registry nutzen soll, hinterlegt werden.&lt;/p&gt;
&lt;h2 id="cache-repo-einrichten"&gt;Cache-Repo einrichten
&lt;/h2&gt;&lt;p&gt;Damit man ein Cache-Repository einrichten kann, muss zuerst die Upstream-Registry unter Administration -&amp;gt; Registries hinzugefügt werden. Wenn ein Cache für Docker Hub angelegt werden soll, ist es hierbei empfehlenswert, Zugangsdaten mit anzugeben, um von den höheren Pull-Limits zu profitieren.&lt;/p&gt;
&lt;p&gt;Danach kann unter Projekte ein neues Projekt mit aktivem &lt;code&gt;Proxy Cache&lt;/code&gt; eingerichtet werden und der entsprechende Upstream (z.B. die angelegte Docker-Hub-Registry) ausgewählt werden.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Cache-Repository" class="gallery-image" data-flex-basis="460px" data-flex-grow="191" height="763" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2026/2026-02-06-harbor-als-lokale-container-registry/images/cache_repo.png" srcset="https://secretmine.de/blog/2026/2026-02-06-harbor-als-lokale-container-registry/images/cache_repo_hu_c1ee0ab411e3d054.png 800w, https://secretmine.de/blog/2026/2026-02-06-harbor-als-lokale-container-registry/images/cache_repo.png 1464w" width="1464"&gt;&lt;/p&gt;
&lt;h2 id="client-einrichten"&gt;Client einrichten
&lt;/h2&gt;&lt;p&gt;In Harbor lassen sich Robot-Zugänge unter Administration anlegen. Bei den Berechtigungen habe ich folgendes vergeben:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Artifact: Auflisten, Lesen&lt;/li&gt;
&lt;li&gt;Repository: Auflisten, Pull, Lesen&lt;/li&gt;
&lt;li&gt;Tag: Auflisten&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Danach muss nur noch der Client (hier Docker) angemeldet werden.&lt;/p&gt;
&lt;p&gt;Im Fall von Docker ist das schnell gemacht:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker login harbor.local
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Danach zieht man Images einfach über &lt;code&gt;harbor.local/dockerhub-cache/image:tag&lt;/code&gt; (natürlich mit korrektem Hostnamen und Registry-Namen) - Harbor übernimmt den Rest automatisch. In Compose-Dateien lässt sich die Cache-Registry dann natürlich auch nutzen.&lt;/p&gt;
&lt;h2 id="fazit"&gt;Fazit
&lt;/h2&gt;&lt;p&gt;Harbor funktioniert bei mir ziemlich gut, allerdings waren auch schon 1-2 Mal die Container morgens nicht mehr gestartet. Man schafft sich damit leider auch einen Single Point of Failure, da der Betrieb von allen Containern im Lab von der Registry abhängt. Vor allem beim Betrieb von vielen Containern oder bei Nutzung eines Orchestrators (wie Kubernetes oder Docker Swarm) ist ein lokaler Cache zur Verminderung von Pulls aber definitiv sinnvoll und verringert die Startzeiten von Containern auf neuen Nodes spürbar.&lt;/p&gt;</description></item><item><title>Outline als (persönliches) Wiki</title><link>https://secretmine.de/blog/2026/2026-01-31-outline/</link><pubDate>Sat, 31 Jan 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-01-31-outline/</guid><description>&lt;p&gt;Lange Zeit habe ich Confluence als Wiki genutzt. Funktionsreich, hübsch und mit Jira integriert. Aber irgendwann kamen die bekannten Punkte: hoher Ressourcenbedarf und aktiver Rückbau der selbstgehosteten Version durch Entfernung der günstigen Lizenzen (und zukünftig auch aller selbsthostbaren Varianten). Also begann die Suche nach einer Alternative, die möglichst schlank, aber trotzdem gut nutzbar sein sollte.&lt;/p&gt;
&lt;p&gt;Ich habe mir dabei diverses angesehen. XWiki kannte ich von der Arbeit, gefiel mir aber weder optisch noch von der Bedienung her. Von den Ressourcen war es auch eher auf dem Level von Confluence. MediaWiki hat für nicht funktioniert - als Wiki generell nicht schlecht, für &amp;ldquo;mal eben&amp;rdquo; aber irgendwie unhandlich. Wiki.js habe ich mir ebenfalls angesehen, aber auch damit bin ich nicht so wirklich warm geworden. Die Bedienung ging nicht so von der Hand, wie ich mir das vorgestellt habe.&lt;/p&gt;
&lt;p&gt;Dann hat mir mein Bruder Outline gezeigt. Was für mich ziemlich perfekt aussah. Minimalistisch, schnell, gute und intuitive Bedienung. Es fühlt sich modern an, ohne überladen zu sein. Und ist auch noch Markdown-basiert. Eine native App gibt es zwar nicht, die PWA funktioniert aber gut.&lt;/p&gt;
&lt;p&gt;Der Funktionsumfang und auch Gestaltungsmöglichkeiten sind natürlich deutlich unter dem, was Confluence kann, aber davon vermisse ich auch nach vielen Jahren wenig.&lt;/p&gt;
&lt;h2 id="betrieb"&gt;Betrieb
&lt;/h2&gt;&lt;p&gt;Selfhosting bei Outline ist nicht so einfach wie bei anderen Softwareprodukten. Neben einer Postgres-Datenbank wird Redis benötigt - soweit so gut. Zwingend erforderlich ist aber auch ein externer Auth-Provider - in meinem Fall OIDC via Authentik. Früher war auch ein S3-Bucket als Speicher für Anhänge erforderlich, die Anforderung ist aber dank dem neu hinzugekommenen lokalen Storage nicht mehr verpflichtend. Trotzdem fühlt es sich so an, als ob Selfhosting zwar offiziell unterstützt wird, der Hersteller aber den Fokus klar auf dem eigenen SaaS-Angebot in der Cloud hat.&lt;/p&gt;
&lt;p&gt;Hat man das Setup aber einmal laufen, ist Outline sehr stabil. Ich nutze es nun seit gut drei Jahren in meiner Umgebung, und es ist dabei geblieben – einfach, weil es funktioniert.&lt;/p&gt;
&lt;h2 id="fazit"&gt;Fazit
&lt;/h2&gt;&lt;p&gt;Outline hat sich bei mir nun wirklich bewährt. Minimalistisch, aber trotzdem genügend Features; selbst hostbar, gute Bedienung. Alternativ lohnt sich ein Blick auf Bookstack. Das habe ich mir zwar lange nicht mehr angesehen, war aber der Alternativkandidat, falls Outline mir nicht gefallen hätte.&lt;/p&gt;</description></item><item><title>Minecraft-Server - meine Best Practises</title><link>https://secretmine.de/blog/2026/2026-01-24-minecraft-server-best-practises/</link><pubDate>Sat, 24 Jan 2026 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2026/2026-01-24-minecraft-server-best-practises/</guid><description>&lt;p&gt;Da ich über 10 Jahre Erfahrung im Betrieb von Minecraft-Servern habe und das zwischenzeitlich auch im größeren Stil gemacht habe, möchte ich hier einmal meine Best Practises dokumentieren.&lt;/p&gt;
&lt;p&gt;Vieles werde ich hier anhand meines eigenen Servers beschreiben. Dabei handelt es sich um einen Survival-Server, der seit bald 6 Jahren aktiv ist (gestartet während der ersten Corona-Zeiten). Der Server hat einige Plugins, das meiste davon aber rein serverseitig und ohne direkten Einfluss auf das Spielerlebnis.&lt;/p&gt;
&lt;p&gt;Dieser Post bezieht sich komplett auf die Java-Edition. Mit der Bedrock-Edition habe ich wenig Erfahrung, da ich diese selbst nicht spiele.&lt;/p&gt;
&lt;h2 id="nutzungsprofil-erstellen"&gt;Nutzungsprofil erstellen
&lt;/h2&gt;&lt;p&gt;Bevor man überhaupt mit der genaueren Planung bzw. Umsetzung beginnt, sollte feststehen, was überhaupt das Ziel ist, wie viele Spieler erwartet werden (Gesamt/Peak/Durchschnitt). Auch ist es ein großer Unterschied, ob die Spieler in einem recht kleinen Bereich aktiv sind (bspw. bei Minigames) oder verteilt auf einer großen Welt spielen. Ich habe auch häufig beobachtet, dass der Ressourcenbedarf bei einem Server z.B. in einer Freundesgruppe in den ersten Tagen deutlich höher ist als in der darauf folgenden Zeit. Dort kann es sinnvoll sein, erst kurzzeitig einen größeren virtuellen Server bei einem Anbieter zu mieten, der stundenweise bzw. tagesweise abrechnet und danach für den längerfristigen Betrieb auf einen günstigeren Server oder z.B. auch den Betrieb im Homelab zu wechseln.&lt;/p&gt;
&lt;h2 id="hardware-bzw-plattform"&gt;Hardware bzw. Plattform
&lt;/h2&gt;&lt;h3 id="cpu"&gt;CPU
&lt;/h3&gt;&lt;p&gt;Bei Minecraft läuft, wie bei vielen anderen Spielen auch, ein großer Teil der Spiellogik in einem Haupt-Thread ab. Das heißt, dass man eher wenige schelle CPU-Kerne benötigt als viele. Große Server-CPUs mit vielen Kernen sind hier meistens eher weniger geeignet, außer man möchte viele Minecraft-Server parallel betreiben. Die (halbwegs) aktuellen Desktop-CPUs von Intel und AMD mit min. 4 (Performance) Cores sind hier meistens eine solide Wahl.&lt;/p&gt;
&lt;h3 id="ram"&gt;RAM
&lt;/h3&gt;&lt;p&gt;Als Minimum sehe ich 4GB, darunter kommt es schnell zu Problemen. Mehr ist bis zu einem gewissen Punkt besser, über ca 30GB RAM würde ich unter normalen Umständen nicht gehen. Mehr Mods und Plugins benötigen mehr RAM, die Sichtweite und Verteilung der Spieler (und damit die Zahl der geladenen Chunks) ist hier auch relevant. Mein eigener Server läuft normalerweise mit 8GB.&lt;/p&gt;
&lt;h3 id="ssd-vs-hdd"&gt;SSD vs HDD
&lt;/h3&gt;&lt;p&gt;Minecraft profitiert von schnellem Speicher. SSD ist in meinen Augen ein Muss, NVMe ist zu bevorzugen. Die benötigte Größe hängt hauptsächlich von der Weltgröße und eingesetzten Plugins (bspw. Dynmap) ab, 20GB reichen meistens aber mehr als aus.&lt;/p&gt;
&lt;h3 id="internetanbindung"&gt;Internetanbindung
&lt;/h3&gt;&lt;p&gt;Ein Punkt, der beim Betrieb zuhause öfter mal übersehen wird. Leistungsstarke Hardware bringt wenig, wenn nicht genügend Bandbreite zur Verfügung steht. Ich kalkuliere normalerweise mit ca 2-5 MBit/s pro gleichtzeitigem Spieler. Wenn keine Chunks geladen werden, ist der Bedarf an Bandbreite niedriger, bei einem Weltwechsel oder schnellem Bewegen auf der Karte entstehen deutlich größere Peaks. Mit ca 50 MBit/s an verfügbarer Upload-Bandbreite kann man aber schon einige Spieler bedienen.&lt;/p&gt;
&lt;p&gt;Wenn man zuhause hostet, ist aus verschiedenen Gründen das Tunneln über einen Server in einem Rechenzentrum hilfreich. Einerseits gibt man damit seine Heim-IP nicht preis, andererseits ist durch bessere Peerings von Rechenzentren im Vergleich zu ISPs untereinander auch die Verbindungsqualität höher. Nebenbei kann man damit auch Einschränkungen durch CGNAT umgehen und den Server auch bei Nutzung einer zweiten Internetleitung für Failover erreichbar halten.&lt;/p&gt;
&lt;h2 id="software"&gt;Software
&lt;/h2&gt;&lt;h3 id="betriebssystem"&gt;Betriebssystem
&lt;/h3&gt;&lt;p&gt;Hier ist irgendeine gängige Linux-Distribution, die Docker-Support hat, sinnvoll. Ich verwende meistens Debian oder Ubuntu. Docker benötige ich für die verwendete Verwaltungssoftware, aber auch ohne ist der Betrieb von einem MC-Server in Docker häufig angenehmer als direkt auf dem Server.&lt;/p&gt;
&lt;h3 id="management-tool"&gt;Management-Tool
&lt;/h3&gt;&lt;p&gt;Ich nutze bisher Pterodactyl (&lt;a class="link" href="https://pterodactyl.io/" target="_blank" rel="noopener"
 &gt;pterodactyl.io&lt;/a&gt;) als webbasiertes Gameserver-Panel. Lange Jahre kam bei mir Multicraft zum Einsatz - da ist allerdings das letzte Release von Februar 2022 und die letzte Preview-Version von November 2023. Alternativ zu Pterodactyl möchte ich zeitnahe Pelican (&lt;a class="link" href="https://pelican.dev/" target="_blank" rel="noopener"
 &gt;pelican.dev&lt;/a&gt;) testen. Pelican ist ein Fork von Pterodactyl und soll trotz Beta-Stadiums sehr gut nutzbar sein. Daneben gibt es noch einige weitere erwähnenswerte Panels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Crafty (&lt;a class="link" href="https://craftycontrol.com/" target="_blank" rel="noopener"
 &gt;craftycontrol.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;PufferPanel (&lt;a class="link" href="https://www.pufferpanel.com/" target="_blank" rel="noopener"
 &gt;pufferpanel.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;AMP (kommerziell, &lt;a class="link" href="https://cubecoders.com/AMP" target="_blank" rel="noopener"
 &gt;cubecoders.com&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vor ein paar Jahren hätte ich noch Multicraft empfohlen, das fand ich insgesamt am angenehmsten in der Nutzung; aber da dieses Produkt scheinbar nicht mehr gepflegt wird, würde ich es nicht mehr einsetzen.&lt;/p&gt;
&lt;h3 id="server-software"&gt;Server-Software
&lt;/h3&gt;&lt;p&gt;Kommen wir zum Kernpart. Hier ist erst einmal entscheidend, ob Mods, Plugins oder keins von beidem zum Einsatz kommen sollen. Plugins sind hierbei rein serverseitige Modifikationen bzw. Erweiterungen, normalerweise basierend auf der Spigot-API. Mods sind client- und/oder serverseitige Erweiterungen/Modifikationen, meistens geht es dabei mehr um Erweiterung des Spielinhalts (neue Blöcke, Biome, Kreaturen, Spielmechaniken, &amp;hellip;).&lt;/p&gt;
&lt;h4 id="server-mit-mods"&gt;Server mit Mods
&lt;/h4&gt;&lt;p&gt;Hier wird meistens Fabric (&lt;a class="link" href="https://fabricmc.net/" target="_blank" rel="noopener"
 &gt;fabricmc.net&lt;/a&gt;) als Modloader verwendet, dieser startet dann den eigentlichen Server.&lt;/p&gt;
&lt;h4 id="server-mit-plugins"&gt;Server mit Plugins
&lt;/h4&gt;&lt;p&gt;Für Plugins und generell als Serversoftware ist PaperMC (&lt;a class="link" href="https://papermc.io/" target="_blank" rel="noopener"
 &gt;papermc.io&lt;/a&gt;) der Quasi-Standard. Es fing mit Bukkit an (Vanilla-Server mit Pluginfähigkeit), daraus entstand dann SpigotMC (optimiertes Bukkit), wovon dann wieder PaperMC geforkt wurde. Auch unterhalb von PaperMC geht die Fork-Kette weiter, wobei PaperMC als guter Kompromiss von Performance und akkuraten Spielmechaniken gilt und meistens empfohlen wird. Es gibt im Wiki von PaperMC auch einen Artikel, der beschreibt, wie man PaperMC vom Gameplay-Verhalten möglichst nahe an Vanilla annähern kann.&lt;/p&gt;
&lt;p&gt;Kurz erwähnen möchte ich hier auch Folia, eine weitere Serversoftware von PaperMC - allerdings ausgelegt auf wirklich große survival-artige Server. Die Besonderheit bei Folia ist, dass es den einen großen Haupt-Game-Thread gibt, in dem alle Aktionen passieren. Folia teilt die Welt in distinkte Regionen auf, die getrennt voneinander arbeiten. Dadurch gibt es einen Game-Thread pro Region, wodurch sich die Last deutlich besser auf viele CPU-kerne verteilen lässt. Relevant wird das aber erst bei großen Spielerzahlen, Folia hat zudem auch viele Nachteile (Plugin-Kompatibilität, Entwicklungsstadium der Software, Bugs in Spielmechaniken, &amp;hellip;).&lt;/p&gt;
&lt;h3 id="proxies"&gt;Proxies
&lt;/h3&gt;&lt;p&gt;Es gibt für Minecraft spezielle Proxy-Software. Damit lassen sich mehrere Server zu einem &amp;ldquo;Netzwerk&amp;rdquo; verbinden und Spieler können via Commands oder andere Wege zwischen den Servern wechseln. Seit einiger Zeit gibt es im originalen Minecraft auch den Befehl &lt;code&gt;/transfer&lt;/code&gt;, mit dem Spieler zu einem anderen Server transferiert werden können.&lt;/p&gt;
&lt;p&gt;Ein Proxy ist sinnvoll, wenn man z.B. mehrere Minecraft-Server betreibt und zwischen diesen nahtlos wechseln können möchte oder auch dann, wenn man den eigentlichen Server zuhause betreibt, die Spieler aber über einen vServer beitreten sollen.&lt;/p&gt;
&lt;p&gt;Wenn nur ein einzelner Server hinter einem Proxy betrieben werden soll, kann auch einfach eine Software wie HAProxy eingesetzt werden; PaperMC unterstützt bspw. das Proxy-Protokoll, mit dem die originale Client-IP erhalten bleibt.&lt;/p&gt;
&lt;p&gt;Von den Entwicklern von SpigotMC gibt es hier Bungeecord, von den Entwicklern von PaperMC Velocity. Ich selbst nutze Velocity, da es die modernere Software ist und viele Plugins dafür existieren.&lt;/p&gt;
&lt;h4 id="geysermc"&gt;GeyserMC
&lt;/h4&gt;&lt;p&gt;Ein Spezialfall bei den Proxies ist Geyser. Das ist eine Software, die es Client mit der Bedrock Edition erlaubt, auf einem Server der Java-Edition zu spielen. Dazu werden fehlende Blöcke und Features möglichst nahe mit dem angehähert, was die Bedrock Edition bietet. Spieler verbinden sich mit Geyser, der dann wiederum die Verbindung zum eigentlichen Server aufbaut und übersetzt.
Geyser läuft entweder als Plugin/Mod direkt auf dem Server, als Plugin auf dem Proxy oder Standalone, ich selbst habe es als Plugin auf dem Proxy installiert.&lt;/p&gt;
&lt;h3 id="empfohlene-plugins"&gt;Empfohlene Plugins
&lt;/h3&gt;&lt;p&gt;Sofern der Server mit Plugins betrieben wird, habe ich hier einige Empfehlungen für Plugins, die ich fast immer installiere. Für Fabric gibt es diverse der genannten Plugins allerdings auch.&lt;/p&gt;
&lt;h4 id="chunky-plugin-und-mod"&gt;Chunky (Plugin und Mod)
&lt;/h4&gt;&lt;p&gt;Chunky gibt es für alle relevanten Modloader und Servertypen, also sowohl PaperMC als auch Fabric (und weitere). Das ist ein Tool, mit dem man Weltteile bzw. Chunks vorgenerieren kann. Vor dem Start eines neuen Servers generiere ich meistens mindestens 1000 Blöcke um den Spawn herum vor, da das Generieren von Chunks einer der rechenintensiveren Vorgänge auf einem Minecraft-Server ist. Vor allem am Anfang, wenn noch viele Spieler aktiv sind, merkt man einen Unterschied. Auch im Nether sollte man Weltteile vorgenerieren. Im End geht das auch, bringt aber aufgrund der Verteilung der End-Cities meiner Erfahrung nach nicht ganz so viel.&lt;/p&gt;
&lt;h4 id="worldedit-bzw-fastasyncworldedit"&gt;WorldEdit (bzw. FastAsyncWorldEdit)
&lt;/h4&gt;&lt;p&gt;Einer der Klassiker, das Plugin gibt es gefühlt schon immer. Wer es nicht kennt: Damit kann man schnell große Teile der Welt bearbeiten, Blöcke ersetzen, neu generieren usw.&lt;/p&gt;
&lt;p&gt;FastAsyncWorldEdit ist für Server die bessere Wahl, da die Änderungen an der Welt asynchron ausgeführt werden und damit der Server nicht einfriert.&lt;/p&gt;
&lt;h4 id="worldguard"&gt;WorldGuard
&lt;/h4&gt;&lt;p&gt;Auch ein Klassiker. Damit kann z.B. festgelegt werden, dass nur bestimmte Spieler/Gruppen in einem Bereich bauen dürfen, auch Mob-Spawning lässt sich damit für einzelne Bereiche deaktiveren.&lt;/p&gt;
&lt;h4 id="dynmap"&gt;Dynmap
&lt;/h4&gt;&lt;p&gt;Das ist auch ein eher altes Plugin, allerdings bis heute gut gepflegt. Es erlaubt das Generieren einer webbasierten Karte des Servers, und das in 2D und auch in 3D. Das Plugin benötigt (vor allem bei den höheren Detailstufen) viel Rechenpower zum Rendern und auch einiges an Speicher bei größeren Welten. Ein Webserver ist integriert, es lässt sich allerdings auch ein externer Webserver dafür nutzen. Ich habe &lt;code&gt;lowres&lt;/code&gt; als Qualitätslevel einstellt, das genügt mir. Wer etwas leichtgewichtigeres sucht, ist beim nächsten Plugin besser aufgehoben.&lt;/p&gt;
&lt;h4 id="squaremap"&gt;squaremap
&lt;/h4&gt;&lt;p&gt;squaremap bietet, ähnlich wie Dynmap, auch eine webbasierte Karte des Servers mit integriertem Webserver an, allerdings in geringerer Auflösung und ausschließlich in 2D. Dafür geht das Rendern sehr viel schneller und das Plugin ist generell auch leichtgewichtiger. Für einen kleinen privaten Server genügt squaremap vollkommen.&lt;/p&gt;
&lt;h4 id="bluemap"&gt;BlueMap
&lt;/h4&gt;&lt;p&gt;Das nächste Tool für eine webbasierte Karte. Bluemap hat die große Besonderheit, dass die Karte als 3D-Modelle gespeichert wird und sich die Welt im Browser daher frei erkunden lässt. Man kann beliebig zoomen, drehen usw, da die 3D-Modelle an den Browser übertragen werden und erst dort gerendet werden. BlueMap lässt sich zudem auch komplett unabhängig vom Server benutzen, so habe ich es auch im Einsatz. Ein kleines Script kopiert in regelmäßigen Abständen die Welt des Servers, führt BlueMap-CLI aus und stellt die generierten Daten über einen Webserver (Caddy, was auch sonst) bereit. Damit belastet es den Server selbst nicht, trotzdem finde ich es eine schöne Ergänzung zu Dynmap oder squaremap. Vor allem, um &amp;ldquo;mal eben&amp;rdquo; Freunden eigene Bauten auf dem Server auf dem Handy zeigen zu können, ist BlueMap wirklich super.&lt;/p&gt;
&lt;h4 id="plan"&gt;Plan
&lt;/h4&gt;&lt;p&gt;Plan ist ein Statistikplugin, das Einblicke in die Nutzung des Servers bietet. Man erfährt damit beispielsweise, wie die Auslastung des Servers aussieht, wann meistens gespielt wird und kann auch erkennen, ob neue Spieler erhalten bleiben oder eine hohe Fluktuation herrscht. Plan unterstützt auch diverse Plugins, um zusätzliche Daten anzuzeigen.&lt;/p&gt;
&lt;h4 id="viaversion-und-viabackwards"&gt;ViaVersion (und ViaBackwards)
&lt;/h4&gt;&lt;p&gt;Mit ViaVersion können Spieler mit neueren Minecraft-Versionen als der Server trotzdem beitreten. ViaBackwards hingegen erlaubt das Beitreten mit älteren Client-Versionen als der Server. Es gibt zusätzlich auch noch ViaRewind, damit können selbst Clients mit Version 1.7 und 1.8 noch beitreten - ob das sinnvoll ist, sei mal dahingestellt.&lt;/p&gt;
&lt;h4 id="luckperms"&gt;LuckPerms
&lt;/h4&gt;&lt;p&gt;LuckPerms ist so mit das bekannteste Plugin, um Permissions und Benutzergruppen zu verwalten. Mit MySQL oder einer anderen Datenbank als Backend lassen sich damit Berechtigungen auch über mehrere Server hinweg synchronisieren. Es gibt davon auch Versionen für Velocity, Fabric, Forge und weitere Server.&lt;/p&gt;
&lt;h4 id="discordsrv"&gt;DiscordSRV
&lt;/h4&gt;&lt;p&gt;DiscordSRV ist eine Verbindung zwischen Discord und einem Minecraft-Server. Damit lässt sich bspw. der Ingame-Chat mit einem Discord-Channel verbinden. Auch lassen sich Discord-Rollen mit Permissions-Gruppen koppeln.&lt;/p&gt;
&lt;h4 id="distant-horizons-server-plugin"&gt;Distant Horizons server plugin
&lt;/h4&gt;&lt;p&gt;Distant Horizons ist eine Client-Mod, die eine deutlich höhere Sichtweite erlaubt. Dafür werden Versionen der Karte mit niedrigeren &amp;ldquo;Auflösungen&amp;rdquo; (LODs) generiert und alles außerhalb der gerade normal geladenen Chunks damit aufgefüllt. Damit ist der Performance-Einfluss eher gering. Distant Horizons braucht an sich keinen serverseitigen Support, die Mod speichert einfach Weltteile vom Server zwischen und generiert die LODs lokal.&lt;/p&gt;
&lt;p&gt;Seit einiger Zeit gibt es ein Plugin für Server, womit die LOD-Dateien serverseitig generiert und vorgehalten werden können. Damit muss man nicht mehr jeden Weltteil besucht haben, um LODs dafür zu sehen. Es ist allerdings empfehlenswert, in der Config &lt;code&gt;generate_new_chunks: false&lt;/code&gt; zu setzen, da sonst für die LODs neue Chunks generiert werden, was deutlich Ressourcen kostet.&lt;/p&gt;
&lt;h3 id="weitere-plugins"&gt;Weitere Plugins
&lt;/h3&gt;&lt;h4 id="tabtps"&gt;TabTPS
&lt;/h4&gt;&lt;p&gt;Damit lässt sich die aktuelle Performance des Servers (TPS, MSPT und weitere) an verschiedenen Positionen (Tabliste, Bossbar, Actionbar) anzeigen. Ich nutze das Plugin gerne, um die Leistung beim Spielen laufend im Auge zu behalten.&lt;/p&gt;
&lt;h4 id="multiverse-core"&gt;Multiverse-Core
&lt;/h4&gt;&lt;p&gt;Damit lassen sich mehrere Welten anlegen und verwalten, Spieler können auch zwischen den Welten wechseln. Ich habe auf meinem Server beispielsweise eine Kreativwelt, wo Spieler Gebäude vorbauen oder mit Redstone experimentieren können.&lt;/p&gt;
&lt;p&gt;Mit weiteren Addons wie Multiverse-Inventories lassen sich die Features von Multiverse-Core noch erweitern.&lt;/p&gt;
&lt;h4 id="simplevoicechat"&gt;SimpleVoiceChat
&lt;/h4&gt;&lt;p&gt;Das ist ein Plugin, was Ingame Voicechat hinzufügt. Dieser ist auf die direkte Umgebung des Spielers begrenzt und ermöglicht ein realistischeres Spielerlebnis. Es wird allerdings auch Client-Seitig eine Mod benötigt.&lt;/p&gt;
&lt;h4 id="coreprotect"&gt;CoreProtect
&lt;/h4&gt;&lt;p&gt;Mit CoreProtect werden alle Aktionen auf dem Server geloggt und können damit auch rückgängig gemacht werden. Das ist vor allem für Survival-Server ohne ein System für &amp;ldquo;Grundstücke&amp;rdquo; praktisch und hat sich bei vergangenen Projekten bewährt.&lt;/p&gt;
&lt;h4 id="essentialsx"&gt;EssentialsX
&lt;/h4&gt;&lt;p&gt;Ein Plugin, was viele kleinere Features (z.B. Befehle für Home oder Teleport-Anfragen) bündelt. Den Vorgänger habe ich früher viel genutzt, heutzutage installiere ich die benötigten Features als einzelne Plugins.&lt;/p&gt;</description></item><item><title>Scriptausführung mit Healthchecks überwachen</title><link>https://secretmine.de/blog/2025/2025-10-31-scriptausfuehrung-mit-healthchecks-ueberwachen/</link><pubDate>Fri, 31 Oct 2025 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2025/2025-10-31-scriptausfuehrung-mit-healthchecks-ueberwachen/</guid><description>&lt;p&gt;Ich nutze für regelmäßige Backups von Daten (z.B. Nextcloud) ein kleines Script, das Restic verwendet. Ich hatte allerdings schon mehrfach den Fall, dass ein Backup nicht sauber durchlief und das Backup-Repository damit gesperrt war und weitere Backups fehlschlugen, was ich dann natürlich erst Wochen (okay, Monate) später bemerkt habe.&lt;/p&gt;
&lt;p&gt;Abhilfe schafft hier das Tool Healthchecks (&lt;a class="link" href="https://github.com/healthchecks/healthchecks%29" target="_blank" rel="noopener"
 &gt;https://github.com/healthchecks/healthchecks)&lt;/a&gt;. Das funktioniert so, dass man Checks einrichtet und dafür eine Webhook-URL bekommt. In einem Script ruft man dann diese URL auf und setzt damit den Timer zurück. Sofern der Webhook nicht innerhalb der richtigen Zeit aufgerufen wird, wird ein Alert ausgelöst. Insgesamt also recht simpel.&lt;/p&gt;
&lt;h2 id="installation"&gt;Installation
&lt;/h2&gt;&lt;p&gt;Ich nutze hier, wie üblich, Docker. Hier meine &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;healthchecks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;healthchecks/healthchecks:latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;container_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;healthchecks&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;DB=sqlite&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;DB_NAME=/data/hc.sqlite&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;DEBUG=False&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;DEFAULT_FROM_EMAIL=me@example.com&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;EMAIL_HOST=smtp.example.com&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;EMAIL_HOST_PASSWORD=MyMailPassword&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;EMAIL_HOST_USER=me@example.com&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;EMAIL_PORT=587&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;EMAIL_USE_TLS=True&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;SECRET_KEY=Hok4K0Va6EwaYltk4ah7muc223rUVmz3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;SITE_ROOT=https://checks.example.com&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;DISCORD_CLIENT_ID=123123&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;DISCORD_CLIENT_SECRET=123123&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;REGISTRATION_OPEN=False&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./data:/data&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;unless-stopped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;REGISTRATION_OPEN&lt;/code&gt; muss initial auf &lt;code&gt;True&lt;/code&gt; gestellt werden, damit man sich selbst registrieren kann, danach sollte das allerdings wieder auf &lt;code&gt;False&lt;/code&gt; gesetzt werden, damit sich keine fremden User registrieren können. Ich habe hier direkt die Discord-Integration eingerichtet, damit ich auf dem Weg auch Notifications verschicken kann. Die beiden Variablen sind aber natürlich optional.&lt;/p&gt;
&lt;p&gt;Das Datenverzeichnis muss die richtigen Zugriffsrechte haben, damit der Container starten kann. Das geht schnell mittels&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir ./data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chown -R 999:999 ./data
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Danach lässt sich der Stack mit &lt;code&gt;docker compose up -d&lt;/code&gt; starten und die Seite aufrufen. Dort kann man sich dann mit einer E-Mail-Adresse registrieren.&lt;/p&gt;
&lt;h2 id="einbindung-in-ein-script"&gt;Einbindung in ein Script
&lt;/h2&gt;&lt;p&gt;Um jetzt einen Check einzurichten, muss zuerst im Webinterface ein neuer Check erstellt werden. Dabei wird auch das normale Intervall (z.B. täglich) sowie eine Karenzzeit eingestellt.&lt;/p&gt;
&lt;p&gt;Nun wird eine Check-URL angezeigt, ich nehme hier &lt;code&gt;https://checks.example.com/ping/bla-bla-blub&lt;/code&gt; als Beispiel.&lt;/p&gt;
&lt;h3 id="simpler-check"&gt;Simpler Check
&lt;/h3&gt;&lt;p&gt;Für einen einfachen Check, ob das Script gelaufen ist, reicht es, einen Befehl wie z.B.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsS -m &lt;span class="m"&gt;10&lt;/span&gt; --retry &lt;span class="m"&gt;5&lt;/span&gt; https://checks.example.com/ping/bla-bla-blub
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;mit der Check-URL auszuführen. Der Befehl sollte im Script als letztes ausgeführt werden, damit er erst ausgeführt wird, wenn alles andere funktioniert hat.&lt;/p&gt;
&lt;h3 id="check-mit-laufzeitberechnung"&gt;Check mit Laufzeitberechnung
&lt;/h3&gt;&lt;p&gt;Healthchecks kann auch die Laufzeit eines Scripts mit prüfen. Dazu wird erst der Endpunkt &lt;code&gt;https://checks.example.com/ping/bla-bla-blub/start&lt;/code&gt; aufgerufen und nach Fertigstellung dann &lt;code&gt;https://checks.example.com/ping/bla-bla-blub&lt;/code&gt;. Hierbei sollte auch noch eine Request-ID mitgegeben werden, um die einzelnen Ausführungen voneinander unterscheiden zu können.&lt;/p&gt;
&lt;p&gt;Ich habe das bspw. so umgesetzt:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -e
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;RID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;uuidgen&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;HEALTHCHECK_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bla-bla-blub
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsS -m &lt;span class="m"&gt;10&lt;/span&gt; --retry &lt;span class="m"&gt;5&lt;/span&gt; https://checks.example.com/ping/&lt;span class="nv"&gt;$HEALTHCHECK_ID&lt;/span&gt;/start?rid&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$RID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# do backup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsS -m &lt;span class="m"&gt;10&lt;/span&gt; --retry &lt;span class="m"&gt;5&lt;/span&gt; https://checks.example.com/ping/&lt;span class="nv"&gt;$HEALTHCHECK_ID&lt;/span&gt;?rid&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$RID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Nachdem das Script ausgeführt wurde, sieht das im Webinterface so aus:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Ausgeführter Check" class="gallery-image" data-flex-basis="430px" data-flex-grow="179" height="573" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2025/2025-10-31-scriptausfuehrung-mit-healthchecks-ueberwachen/images/check.png" srcset="https://secretmine.de/blog/2025/2025-10-31-scriptausfuehrung-mit-healthchecks-ueberwachen/images/check_hu_5c0e8ac9055829a.png 800w, https://secretmine.de/blog/2025/2025-10-31-scriptausfuehrung-mit-healthchecks-ueberwachen/images/check.png 1027w" width="1027"&gt;&lt;/p&gt;
&lt;p&gt;Sollte ein Check nicht innerhalb der eingestellten Zeit aufgerufen werden, wird eine Notification ausgelöst. So sollte ich in Zukunft fehlschlagende Backups schneller bemerken.&lt;/p&gt;</description></item><item><title>PlankaPrinter - oder auch: Ich habe mir einen Bondrucker gekauft</title><link>https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/</link><pubDate>Sat, 20 Sep 2025 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/</guid><description>&lt;p&gt;Ich neige manchmal dazu, unkonventionelle Ideen zu haben und diese auch umzusetzen. In diesem Fall ist die Idee nicht komplett von mir, es passt trotzdem gut ins Bild.&lt;/p&gt;
&lt;p&gt;Aber der Reihe nach.&lt;/p&gt;
&lt;h2 id="die-idee"&gt;Die Idee
&lt;/h2&gt;&lt;p&gt;Ich habe bisher eine normale ToDo-App benutzt, aber das Problem, dass ich da wenig drauf schaue und daher auch eher wenig abarbeite. Durch Zufalll bin ich im Internet auf ein Video gestoßen, wo jemand einen Kassenbondrucker für ToDo-Tickets in Verbindung mit einer Pinnwand nutzt, um die zu erledigenden Aufgaben nicht aus dem Auge zu verlieren (wortwörtlich in dem Fall). Ich fand das so gut, dass ich recht schnell bei eBay unterwegs war und auch ein passendes Angebot gefunden hab, auch die Pläne für eine Software hatte ich ziemlich schnell im Kopf.&lt;/p&gt;
&lt;h2 id="der-drucker"&gt;Der Drucker
&lt;/h2&gt;&lt;p&gt;Ich habe mich umgeschaut, was es an Druckern gibt. Bei Kassen sind Geräte von Epson recht weit verbreitet, dementsprechend gibt es hier auch einen guten Gebrauchtmarkt. Ich wollte ein Gerät haben, was halbwegs aktuell ist und auch ans Netzwerk angebunden werden kann (ob LAN oder WLAN war mir egal). Auch musste es von gängigen Bibliotheken für Python (bei mir &lt;code&gt;python-escpos&lt;/code&gt; - auch wenn es für dieses spezielle Modell keinen direkten Support gibt; vergleichbare Modelle werden aber unterstützt und andere Profile (ich nutze &lt;code&gt;TM-T20II&lt;/code&gt;) funktionieren fehlerfrei).&lt;/p&gt;
&lt;p&gt;Gefunden habe ich einen Epson TM-m30II-S. Das ist ein Kassendrucker, der für den Einsatz bei Tabletkassen vorgesehen ist und dementsprechend auf der Oberseite auch eine Halterung für Tablets hat. Die Halterung brauche ich zwar nicht, aber das Angebot war wirklich gut (um die 25€ für ein Gerät in ordentlichem Zustand). Ich hab den dann auch bestellt.&lt;/p&gt;
&lt;p&gt;Der Drucker war ein wenig staubig, aber ansonsten einwandfrei. Die &amp;ldquo;Abnutzung&amp;rdquo; (also gedruckte Kilometer und Schnittvorgänge) passten zwar nicht zu den Verkäuferangaben, waren aber trotzdem brauchbar - der Käufer hatte angegeben, dass das Gerät keine 5 Bonrollen gedruckt hat, mit 80m-Rollen waren das allerdings eher 35 Rollen. Und knapp 10000 Schnitte. Beides sind aber trotzdem Werte, die vollkommen im Rahmen liegen dürften und eher geringe Nutzung sein dürften. Ich will nicht wissen, wie viele Rollen und Schnitte ein Drucker in einem gut besuchten Supermarkt an der Kasse so auf dem Zähler haben&amp;hellip;&lt;/p&gt;
&lt;p&gt;An Schnittstellen hat dieses Modell USB, LAN und Bluetooth direkt an Board, über ein (teures) Dongle lässt sich theoretisch auch WLAN nachrüsten. Mir genügt LAN vollkommen bzw. es ist sogar meine bevorzugte Anschlussmethode.&lt;/p&gt;
&lt;h2 id="die-software"&gt;Die Software
&lt;/h2&gt;&lt;h3 id="planka"&gt;Planka
&lt;/h3&gt;&lt;p&gt;Ich habe mich dazu entschlossen, für die Software keine reine ToDo-Liste, sondern ein Kanban-Board zu nutzen. Die Wahl fiel hierbei auf Planka, da es Open Source ist, OIDC enthalten ist und die API gut beschrieben ist und gute Bibliotheken für Python existieren.&lt;/p&gt;
&lt;p&gt;Planka läuft einfach als Docker, der User für den API-Zugriff ist ein normaler, lokaler Benutzer. Ansonsten hat das Board folgende Spalten:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Irgendwann&lt;/strong&gt;: Für ToDo-Einträge, die noch nicht zu erledigen sind, aber irgendwann gemacht werden sollten/müssen&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zeitnahe&lt;/strong&gt;: ToDos, die grob in den nächsten 1-2 Wochen zu erledigen sind&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Morgen&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Heute&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In Arbeit&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blocked&lt;/strong&gt;: Aufgaben, die angefangen oder noch anzufangen sind, die aber noch nicht gemach twerden können. Beispielsweise, wenn noch andere Dinge zu erledigen sind, Teile fehlen oder ich auf Dinge warten muss.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Done&lt;/strong&gt;: Fertige Aufgaben von heute&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Archiv&lt;/strong&gt;: Ältere fertige Aufgaben&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="plankaprinter"&gt;PlankaPrinter
&lt;/h3&gt;&lt;p&gt;Mein Python-Script sortiert täglich die Tickets. Dabei werden Tickets von Morgen nach Heute verschoben, Termine von Google importiert und in die passenden Spalten (Heute bzw. Morgen) verschoben, Aufgaben mit Zieldatum werden auch automatisch in die passende Spalte verschoben. Alles von Done wird dabei auch ins Archiv verschoben.&lt;/p&gt;
&lt;h4 id="termine"&gt;Termine
&lt;/h4&gt;&lt;p&gt;Ich importiere meine Kalender via ICS-URLs, mit einer passenden Library werden auch wiederkehrende Termine korrekt ausgewertet. Termine haben dabei ein eigenes Tag, um sie in der Übersicht direkt erkennen zu können.&lt;/p&gt;
&lt;h4 id="drucken"&gt;Drucken
&lt;/h4&gt;&lt;p&gt;Die Tickets, die heute zu erledigen sind und noch nicht gedruckt sind, werden an den Drucker geschickt. Bereits gedruckte Aufgaben haben ein &lt;code&gt;Printed&lt;/code&gt;-Tag.&lt;/p&gt;
&lt;h4 id="aufwandsschätzungen"&gt;Aufwandsschätzungen
&lt;/h4&gt;&lt;p&gt;Ich habe mir Tags mit geschätzten Zeitaufwänden (XS bis XL) erstellt. Der Tickettitel und die Beschreibung werden an ein lokal betriebenes LLM (konkret &lt;code&gt;gemma3n&lt;/code&gt;) geschickt und von diesem der Aufwand geschätzt. Die Schätzungen sind nicht perfekt, aber gehen meistens zumindest in die richtige Richtung. Braucht man wahrscheinlich nicht wirklich, aber ist ein cooles Zusatzfeature.&lt;/p&gt;
&lt;h4 id="homeassistant-integration"&gt;HomeAssistant-Integration
&lt;/h4&gt;&lt;p&gt;Die Aktionen (täglicher Durchlauf mit Import, Sortieren und co sowie das Drucken von den Tickets) werden via MQTT von HomeAssistant aus getriggert, damit dies in Abhängigkeit von meiner Anwesenheit zuhause und auch der Tageszeit passieren kann. Via MQTT lassen sich auch direkt Dinge mit Titel und Nachricht drucken, das nutze ich bisher allerdings noch nicht.&lt;/p&gt;
&lt;h4 id="sonstige-features"&gt;Sonstige Features
&lt;/h4&gt;&lt;p&gt;Ich habe ein kleines Script gebaut, was Bilder ausdrucken kann. Sehr simpel, aber funktioniert. Das muss allerdings noch irgendwie an den Rest mit angebunden werden, da das bisher nur ein eigenständiges Python-Script ist.&lt;/p&gt;
&lt;h2 id="mein-aufbau"&gt;Mein Aufbau
&lt;/h2&gt;&lt;p&gt;Ich habe den Drucker in einem Regal stehen, daneben dann ein Glas für die fertigen Aufgaben und oben drauf die Pinnwand für die aktuellen Aufgaben.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Der Gesamtaufbau" class="gallery-image" data-flex-basis="181px" data-flex-grow="75" height="2650" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/images/setup.jpg" srcset="https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/images/setup_hu_390f6dd8735c0516.jpg 800w, https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/images/setup_hu_5b1a9741a406c57.jpg 1600w, https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/images/setup.jpg 2000w" width="2000"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Der Epson TM-m30II-S mit dem Glas für erledigte Aufgaben" class="gallery-image" data-flex-basis="282px" data-flex-grow="117" height="2000" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/images/printer.jpg" srcset="https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/images/printer_hu_d252d21ca37943ea.jpg 800w, https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/images/printer_hu_2480a383f8b9da58.jpg 1600w, https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/images/printer.jpg 2356w" width="2356"&gt;&lt;/p&gt;
&lt;p&gt;Um die Pinnadeln aufzubewahren, habe ich für die Tablethalterung eine kleine Schlade zum darüberstecken designed und gedruckt. Damit sieht man auch die offenen Metallflächen für die Halterung nicht mehr, was nebenbei auch noch schicker ist.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Fach für Pinnadeln" class="gallery-image" data-flex-basis="320px" data-flex-grow="133" height="2000" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/images/printer_bin.jpg" srcset="https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/images/printer_bin_hu_c6b2eef9ab6b7baf.jpg 800w, https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/images/printer_bin_hu_214845c97c55ffcc.jpg 1600w, https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/images/printer_bin_hu_9c27466343785904.jpg 2400w, https://secretmine.de/blog/2025/2025-09-20-plankaprinter-bondrucker/images/printer_bin.jpg 2667w" width="2667"&gt;&lt;/p&gt;
&lt;p&gt;Der Drucker hängt per LAN am Netzwerk und läuft durchgängig. Der Stromverbrauch liegt tatsächlich nur bei etwas über 1 Watt, also nur etwas mehr als eine WLAN-Steckdose braucht; es lohnt sich also gar nicht, den Drucker abzuschalten bzw. über eine WLAN-Steckdose steuerbar zu machen.&lt;/p&gt;
&lt;h2 id="wie-es-weitergeht"&gt;Wie es weitergeht
&lt;/h2&gt;&lt;p&gt;Ich bin aktuell noch dabei, diverse kleinere Bugs zu beheben, dazu gehört beispielsweise das Erzeugen von neuen Tickets für wiederkehrende Termine - bisher verschiebt die Software dann das bestehende Ticket vom Archiv wieder auf Heute, behält dabei aber das &lt;code&gt;Printed&lt;/code&gt;-Tag bei&amp;hellip; Ansonsten läuft das meiste ziemlich gut, nur den Code könnte ich noch etwas aufräumen.&lt;/p&gt;
&lt;p&gt;Dokumentieren muss ich hier auch noch viel. Aber wer hat auf Doku schon Lust?&lt;/p&gt;
&lt;p&gt;Am Ende soll PlankaPrinter &lt;em&gt;irgendwann&lt;/em&gt; auch Open Source werden, damit andere sich hier etwas abschauen können oder die Software direkt so einsetzen können.&lt;/p&gt;</description></item><item><title>Lokales Multiroom-Audio mit dem Lyrion Music Server und Squeezelite</title><link>https://secretmine.de/blog/2025/2025-07-29-lyrion-music-server-und-squeezelite/</link><pubDate>Tue, 29 Jul 2025 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2025/2025-07-29-lyrion-music-server-und-squeezelite/</guid><description>&lt;p&gt;Ich bin vor einer Weile über Lyrion Music Server (&lt;a class="link" href="https://lyrion.org/" target="_blank" rel="noopener"
 &gt;https://lyrion.org/&lt;/a&gt;) gestolpert und fand das Konzept sehr spannend. Lyrion Music Server ist die Weiterentwicklung von Logitech Media Server, einem lokalen Musikserver für die Verwendung mit dafür gebauten Abspielgeräten (Squeezeboxen). Squeezeboxen gibt es abseits vom Gebrauchtmarkt nicht mehr, einen Softwarenachbau für die Player in Form von beispielsweise Squeezelite jedoch bereits vorhanden.&lt;/p&gt;
&lt;p&gt;Was die Software besonders macht ist das Feature, dass mehrere Player synchron bespielt werden können, wie es mit Multiroom-Setups von anderen Herstellern möglich ist. Primär ist Lyrion dafür gebaut, lokale Mediatheken und auch Radio abzuspielen, es gibt jedoch auch ein Plugin, mit dem das ganze über Spotify Connect direkt via Spotify ansteuerbar ist.&lt;/p&gt;
&lt;h2 id="hardware"&gt;Hardware
&lt;/h2&gt;&lt;p&gt;Ich habe aktuell in vier Räumen Player installiert. In einem weiteren Raum ist geplant, zumindest eine Buchse zum Anschluss von z.B. einer Bluetooth-Box izu installieren.&lt;/p&gt;
&lt;h3 id="arbeitszimmer"&gt;Arbeitszimmer
&lt;/h3&gt;&lt;p&gt;Hier stecken meine PC-Lautsprecher einfach über ein Klinke-Y-Kabel sowohl an meinem PC als auch an dem Thin Client, der LMS beherbergt. Dort läuft Squeezelite als Docker in LXC, das Audiogerät ist vom Host an den LXC und Docker durchgereicht und wird von dort bespielt.&lt;/p&gt;
&lt;h3 id="wohnzimmer"&gt;Wohnzimmer
&lt;/h3&gt;&lt;p&gt;Im Wohnzimmer ist ein Raspberry Pi 1b mit piCorePlayer wie im Arbeitszimmer, ebenfalls mit einem Y-Kabel, an das Soundsystem des Fernsehers angeschlossen. Via Homeassistant wird automatisch das Soundsystem eingeschaltet, sobald Musik abgespielt wird.&lt;/p&gt;
&lt;h3 id="schlafzimmer"&gt;Schlafzimmer
&lt;/h3&gt;&lt;p&gt;Hier befindet sich ein selbstgebauter Player. Dieser besteht aus einem Raspberry Pi 1b mit piCorePlayer, einem Verstärker-Board und Regallautsprechern. Der Verstärker kann über ein Relais vom Pi aus ein- und ausgeschaltet werden, um Grundrauschen oder andere Geräusche auf den Lautsprechern zu vermeiden, wenn keine Musik abgespielt wird. Nebenbei spart das natürlich auch Strom. Angetrieben wird das ganze von einem 19V Laptop-Netzteil.&lt;/p&gt;
&lt;h3 id="küche"&gt;Küche
&lt;/h3&gt;&lt;p&gt;Hier ist ähnlich wie im Schlafzimmer ein selbstgebauerter Player platziert, hier allerdings mit einem Raspberry Pi 3. Auch hier ist es ein günstiges Verstärker-Board, schaltbar über ein Relais. Ich hatte auch für diesen Pi erst einen WLAN-Stick bestellt und erst beim Einrichten von piCorePlayer festgestellt, dass der Pi 3 WLAN integriert hat und ich den Stick gar nicht brauche.&lt;/p&gt;
&lt;h2 id="software"&gt;Software
&lt;/h2&gt;&lt;p&gt;Der Lyrion Music Server läuft als Docker, hier ist einmal meine Compose-Datei inkl. des Squeezelite-Players:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;lms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;lmscommunity/lyrionmusicserver:dev&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;network_mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;host&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./config:/config:rw&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./music:/music:ro&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./playlist:/playlist:rw&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;/etc/localtime:/etc/localtime:ro&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;/etc/timezone:/etc/timezone:ro&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;HTTP_PORT=9000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;unless-stopped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;squeezelite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;giof71/squeezelite:latest-alsa&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;/dev/snd:/dev/snd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;USER_MODE=yes&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;PUID=0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;PGID=0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;AUDIO_GID=29&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;SQUEEZELITE_NAME=Arbeitszimmer&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;SQUEEZELITE_AUDIO_DEVICE=&amp;#34;hw:CARD=PCH,DEV=0&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./sl-config/:/config/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;unless-stopped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Bei den Plugins habe ich einerseits Spotty für Spotify Connect installiert, andererseits auch ein Plugin, um Gruppen vordefinieren zu können. Ich habe hierbei hauptsächlich eine Gruppe mit allen Lautsprechern definiert, ansonsten werden die Räume in der Regel nur einzeln genutzt.&lt;/p&gt;
&lt;h2 id="soundqualität-und-generelle-erfahrung"&gt;Soundqualität und generelle Erfahrung
&lt;/h2&gt;&lt;p&gt;Die Player mit eigenem Verstärker haben leider ein gutes Maß an Grundgeräuschen, hauptsächlich dürfte das von Spannungswandlern von 19V auf 5V für die Pis kommen. Bei den Playern, wo Soundsysteme angeschlossen sind, habe ich damit keine Probleme.&lt;/p&gt;
&lt;p&gt;Ich (bzw. wir) nutze das System hauptsächlich mit Spotify Connect. Das funktioniert nicht unbedingt perfekt - die Wiedergabe bleibt leider regelmäßig nach Ende eines Songs hängen und muss dann erstmal händisch wieder gestartet werden. Eine Lösung dafür habe ich bislang noch nicht, das Problem beim Spotty-Plugin ist allerdings bekannt. Playlists über die Integration in LMS und nicht via Spotify Connect zu starten soll das Problem beheben, ist für uns allerdings eher unpraktisch.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update&lt;/em&gt;: Beim Plugin, was für Spotify zum Einsatz kommt, wurde aufgrund von API-Änderungen bei Spotify der Connect-Modus vorerst entfernt, seit dem nutzen wir die Integration. Das funktioniert auch fehlerfrei, das System spielt stundenlang ohne einen einzigen Fehler Musik ab.&lt;/p&gt;
&lt;p&gt;Ansonsten funktioniert das System gut, die Lautsprecher laufen weitestgehend synchron. Zeitweise gab es mal mit einem Lautsprecher Probleme, weil aus irgendeinem Grund der WLAN-Empfang in der genauen Position des Players schlecht war, das hat dann dazu geführt, dass das gesamte System regelmäßig out of sync war und die Wiedergabe kurz unterbrochen wurde. Verschieben des Players um einige Zentimeter hat das beseitigt.&lt;/p&gt;</description></item><item><title>Webbasierter Remotezugriff mit Guacamole</title><link>https://secretmine.de/blog/2025/2025-06-09-guacamole/</link><pubDate>Mon, 09 Jun 2025 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2025/2025-06-09-guacamole/</guid><description>&lt;p&gt;Ich habe schon eine ganze Weile mit verschiedenen Möglichkeiten experimentiert, von unterwegs und ohne dabei einen eigenen Client zu benötigen. Dabei bin ich irgendwann auf &lt;strong&gt;Apache Guacamole&lt;/strong&gt; gestoßen, einem webbasierten Remotedesktop-Tool, das im Browser läuft und RDP, VNC und SSH unterstützt. Und sich mit 2FA sichern lässt. Also alles abdeckt, was ich benötige.&lt;/p&gt;
&lt;p&gt;Guacamole läuft bei mir als Docker-Container im Homelab und bietet mir über einen gesicherten Webzugang die Möglichkeit, sowohl meine Windows- als auch meine Linux-VMs zu nutzen. Ein Browser (und Internet) reicht, und ich habe Zugriff auf eine vollständige Desktop-Umgebung – mit gewohnter Software, Dateiablage und allem Drum und Dran. Das funktioniert in den meisten Fällen auch mit restriktiven Firewalls/Proxies, da alles über HTTPS und Websockets abgewickelt wird. Durch Zwei-Faktor-Authentifizierung und restriktive Firewall-Regeln ist das Ganze auch sicher genug, um es von außen erreichbar zu machen. Und es funktioniert meistens sogar hinter restriktiven Firewalls, solange Websockets funktionieren, und eignet sich damit auch für den Zugriff von z.B. der Arbeit oder der Schule/Uni - vorausgesetzt, die vorliegenden Regelungen lassen es zu.&lt;/p&gt;
&lt;h2 id="schematischer-aufbau"&gt;Schematischer Aufbau
&lt;/h2&gt;&lt;p&gt;Technisch ist das ganze eigentlich recht simpel aufgebaut:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Grober Aufbau" class="gallery-image" data-flex-basis="395px" data-flex-grow="164" height="644" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://secretmine.de/blog/2025/2025-06-09-guacamole/images/guacamole.png" srcset="https://secretmine.de/blog/2025/2025-06-09-guacamole/images/guacamole_hu_2ef0e3a094d815c7.png 800w, https://secretmine.de/blog/2025/2025-06-09-guacamole/images/guacamole.png 1062w" width="1062"&gt;&lt;/p&gt;
&lt;p&gt;Guacamole ist das Herzstück vom ganzen, es stellt das Webinterface bereit, kümmert sich um das Benutzermanagement und verwaltet auch die Verbindungen. Guacamole wiederum steuert Guacd an, das ist ein Dienst, der diverse Protokolle wie RDP und VNC in ein allgemeineres Format übersetzt. Für die Benutzer- und Verbindungsverwaltung wird auch noch eine MySQL-Datenbank benötigt. Davor steht bei mir ein Caddy, aber jeder beliebige andere Reverse-Proxy tut es auch.&lt;/p&gt;
&lt;h2 id="einrichtung"&gt;Einrichtung
&lt;/h2&gt;&lt;h3 id="docker-compose"&gt;Docker-Compose
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;guacdb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;container_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;guacamoledb&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;mariadb:latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;unless-stopped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;9ngmZtFToD3Ll4ncBA6V&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;guacamole_db&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;guacamole_user&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;qi1tqoSTRGx5eD4ghCAG&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;./db-data:/var/lib/mysql&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;./initdb.sql:/docker-entrypoint-initdb.d/initdb.sql&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;guacd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;container_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;guacd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;guacamole/guacd:latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;unless-stopped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;guacamole&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;container_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;guacamole&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;guacamole/guacamole:latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;unless-stopped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;GUACD_HOSTNAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;guacd&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MYSQL_HOSTNAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;guacdb&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;guacamole_db&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;guacamole_user&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;qi1tqoSTRGx5eD4ghCAG&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;TOTP_ENABLED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# optional, forciert 2FA&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;guacdb&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;guacd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Vor dem Start muss allerdings die Datenbank noch befüllt werden. Das Container-Image bringt ein Script mit, um den nötigen Dump zu erzeugen:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --mysql &amp;gt; initdb.sql
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Nun kann mit &lt;code&gt;docker compose up -d&lt;/code&gt; der Stack gestartet werden. Der MariaDB-Container importiert beim ersten Start alles, was unter &lt;code&gt;/docker-entrypoint-initdb.d/&lt;/code&gt; liegt in alphabetischer Reihenfolge - und damit auch den Dump, den wir eben angelegt haben.&lt;/p&gt;
&lt;h3 id="reverse-proxy"&gt;Reverse Proxy
&lt;/h3&gt;&lt;p&gt;Guacamole müsste nun unter dem angegebenen Port (hier 8080) erreichbar sein, es muss der Pfad &lt;code&gt;/guacamole&lt;/code&gt; aufgerufen werden. Jetzt kann noch ein beliebiger Reverse-Proxy auf Guacamole verwiesen werden, beispielsweise Caddy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;guacamole.example.com {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; rewrite / /guacamole
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; reverse_proxy http://192.168.1.2:8080
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Ich habe hierbei auch einen Rewrite von &lt;code&gt;/&lt;/code&gt; zu &lt;code&gt;/guacamole&lt;/code&gt; hinzugefügt, damit man den Pfad nicht händisch angeben muss.&lt;/p&gt;
&lt;h3 id="konfiguration"&gt;Konfiguration
&lt;/h3&gt;&lt;p&gt;Die initialen Logindaten sind &lt;code&gt;guacadmin&lt;/code&gt;/&lt;code&gt;guacadmin&lt;/code&gt;. Sofern TOTP über die Umgebungsvariablen aktiviert wurde, wird nun direkt nach einem zweiten Faktor gefragt. Oben rechts unter Einstellungen lässt sich dann eine Verbindung anlegen. Bei RDP lohnt es sich, den Haken bei &lt;code&gt;Grössenänderungs-Methode: Display Update&lt;/code&gt; zu setzen. Die Haken unten bei Geschwindigkeit kann man setzen, wie man möchte - ich hab hier &lt;code&gt;Desktophintergrund aktivieren&lt;/code&gt; und &lt;code&gt;Schriftartglättung (ClearType) aktivieren&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="nutzung"&gt;Nutzung
&lt;/h2&gt;&lt;p&gt;Auf der Startseite sollte nun die Verbindung angezeigt werden. Nach einem Klick sollte die Verbindung stehen und sich auch mit Änderung der Fenstergröße automatisch anpassen.&lt;/p&gt;
&lt;h3 id="performance"&gt;Performance
&lt;/h3&gt;&lt;p&gt;Nach meiner Erfahrung ist die Performance primär von der Single-Thread-Performance des Hosts abhängig - auf den Futros läuft Guacamole langsamer als auf dem Mini-PC mit Ryzen-Prozessor. Aber beides ist definitiv nutzbar, auch wenn ich nicht 24/7 darüber arbeiten wollen würde.&lt;/p&gt;</description></item><item><title>Blocky - eine Alternative zu AdGuard Home und Pi-hole</title><link>https://secretmine.de/blog/2025/2025-05-18-blocky-eine-alternative-zu-adguard-home-und-pihhole/</link><pubDate>Sun, 18 May 2025 00:00:00 +0000</pubDate><guid>https://secretmine.de/blog/2025/2025-05-18-blocky-eine-alternative-zu-adguard-home-und-pihhole/</guid><description>&lt;p&gt;Bisher habe ich als internen DNS-Resolver AdGuard Home eingesetzt. Das ist eine Alternative zu Pi-hole, die ebenfalls DNS-Blocking unterstützt. Die Wahl fiel damals auf AdGuard Home, da Pi-hole für eigene lokale Domains (die über einen anderen DNS-Server aufgelöst werden - das ist ein simpler &lt;code&gt;bind9&lt;/code&gt;-Server) eher hakelig war und sich AdGuard Home einfach runder angefühlt hat.&lt;/p&gt;
&lt;p&gt;Mit der Zeit gefiel mir aber die Ressourcenauslastung von AdGuard Home nicht, sowohl CPU als auch RAM waren etwas höher, als ich es mochte. Außerdem fielen mir teilweise längere Antwortzeiten beim DNS auf, wobei ich da nicht hundertprozentig sagen kann, ob es an AdGuard Home lag oder etwas anderes dahinter steckte. Außerdem wollte ich bessere Metriken bekommen, am besten über einen Prometheus-kompatiblen Metrikendpunkt.&lt;/p&gt;
&lt;h1 id="suche-nach-einer-alternative"&gt;Suche nach einer Alternative
&lt;/h1&gt;&lt;p&gt;Bei der Recherche nach einer Alternative zu AdGuard Home habe ich mir Pi-hole noch einmal angesehen, hatte dort aber Probleme, meine beiden lokalen Zonen über einen anderen Server aufzulösen. Den Technitium DNS-Server habe ich mir auch angesehen, aber der gefiel mir von der Konfiguration her nicht und auch hier habe ich nicht sofort herausfinden können, wie ich eine lokale Domain anders auflöse. Ein Setup mit einem Server wie Bind oder dnsmasq wollte ich ebenfalls nicht, da mir das von der Konfiguration her für die Blocklisten (benötigt ja dann Skripte, die die blockierten Einträge importieren) nicht gefallen hat. Bei den meisten Lösungen war auch die Herstellung einer Redundanz über einen zweiten Resolver nicht einfach.&lt;/p&gt;
&lt;h1 id="meine-anforderungen"&gt;Meine Anforderungen
&lt;/h1&gt;&lt;p&gt;Hier einmal meine Anforderungen als Liste:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Normale DNS-Auflösung und Caching&lt;/li&gt;
&lt;li&gt;Unterstützung von Block- und Allowlists, möglichst in einem der gängigen Formate, damit die bekannten Listen funktionieren&lt;/li&gt;
&lt;li&gt;Unterstützung von verschiedenen DNS-Servern für einzelne Domains, um lokale Domains an einen anderen Resolver weiterzuleiten&lt;/li&gt;
&lt;li&gt;Metriken als Prometheus-Endpunkt&lt;/li&gt;
&lt;li&gt;Redundanz über zweite Instanz auf anderem Host&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="blocky---eine-schlanke-alternative"&gt;Blocky - eine schlanke Alternative
&lt;/h1&gt;&lt;p&gt;Bei der Suche bin ich irgendwann auf Blocky gestoßen - das ist ein schlanker, in Go geschriebener DNS-Resolver, der als Alternative zu Pi-hole, AdGuard Home und co gedacht ist. Es gibt kein Webinterface, die Konfiguration findet über eine simple Yaml-Datei statt. Alle Features, die ich haben wollte, waren auch simpel einzurichten und funktionierten sofort. Die Performance ist ebenfalls super, gefühlt auch etwas flotter als AdGuard Home. Redundanz lässt sich simpel über das Kopieren der Config und Neustart vom Container lösen, das macht ein ganz einfaches Script alle x Stunden. Theoretisch könnte ich über Redis auch den Cache zentral halten, aber dafür müsste ich auch hier einen redundanten Redis-Service aufbauen - was ich hier auch einfach für Overkill halte.&lt;/p&gt;
&lt;h2 id="mein-erfahrung"&gt;Mein Erfahrung
&lt;/h2&gt;&lt;p&gt;Ich wollte Blocky erst einmal nur temporär zum Testen aktiv schalten - habe das dann aber vergessen. Nach ein paar Tagen fiel mir dann wieder ein, dass ich ja auf Blocky umgestellt hatte - bemerkt habe ich es ansonsten nicht. Was in diesem Fall ein wirklich gutes Zeichen ist. Blocky läuft bislang störungsfrei, liefert gute Metriken und benötigt wenig Ressourcen. Wir reden hier über ca 20MB RAM und vielleicht 0,1 % CPU-Auslastung von dem Container. AdGuard Home braucht, ohne aktive Clients seit einigen Tagen (aber auch ohne Neustart seit dem) grob 200MB RAM und hat immer wieder kurze Spikes auf &amp;gt;10 % CPU-Last, etwas was ich bei Blocky so überhaupt nicht wahrnehme.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update:&lt;/em&gt; Die Ressourcenauslastung im Livebetrieb ist jetzt doch etwas höher, mit ein paar Blocklisten und als primärer DNS-Server ist die Auslastung vom Container bei 50-70MB bei allerdings weiterhin im Schnitt deutlich unter 1 % CPU-Auslastung (mit kurzen Spikes auf einige wenige Prozent).
+&lt;/p&gt;
&lt;h2 id="meine-konfiguration"&gt;Meine Konfiguration
&lt;/h2&gt;&lt;p&gt;Hier einmal die Konfiguration, wie sie bei mir jetzt seit einiger Zeit im Einsatz ist:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# ich habe leider kein ipv6, daher habe ich hier v6 vollständig deaktiviert und weiter unten auch AAAA-Records gefiltert.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;connectIPVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;1.1.1.1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;8.8.8.8&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;blocking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;blockType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;nxDomain&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;blackLists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;https://raw.githubusercontent.com/RPiList/specials/master/Blocklisten/Win10Telemetry&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;https://raw.githubusercontent.com/RPiList/specials/master/Blocklisten/Phishing-Angriffe&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;https://raw.githubusercontent.com/RPiList/specials/master/Blocklisten/malware&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;https://raw.githubusercontent.com/RPiList/specials/master/Blocklisten/easylist&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;https://raw.githubusercontent.com/RPiList/specials/master/Blocklisten/MS-Office-Telemetry&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;whiteLists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;https://raw.githubusercontent.com/m0zgen/dns-hole/master/whitelist.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;https://raw.githubusercontent.com/m0zgen/dns-hole/master/vendors-wl/microsoft.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;https://raw.githubusercontent.com/m0zgen/dns-hole/master/vendors-wl/google.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;https://raw.githubusercontent.com/m0zgen/dns-hole/master/regex/common-wl.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;clientGroupsBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ads&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;caching&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cacheTimeNegative&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;1m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;filtering&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;queryTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;AAAA&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Konfiguration der lokalen Domains und Reverse-IP-Lookups zum DNS-Server unter 192.168.1.15&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;conditional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fallbackUpstream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;home.local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;192.168.1.15&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;homelab.local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;192.168.1.15&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;179.168.192.in-addr.arpa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;192.168.1.15&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;178.168.192.in-addr.arpa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;192.168.1.15&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;53&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;bootstrapDns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1.1.1.1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;prometheus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/metrics&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;queryLog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;none&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item></channel></rss>