<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tech on M1CH's Tech Log</title><link>https://blog.m1ch.eu/posts/</link><description>Recent content in Tech on M1CH's Tech Log</description><generator>Hugo</generator><language>en-us</language><copyright>Michael Klamminger</copyright><lastBuildDate>Wed, 11 Nov 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.m1ch.eu/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Moved to graz.social</title><link>https://blog.m1ch.eu/posts/20261111-mastodon-graz-social/</link><pubDate>Wed, 11 Nov 2026 00:00:00 +0000</pubDate><guid>https://blog.m1ch.eu/posts/20261111-mastodon-graz-social/</guid><description>&lt;p&gt;My Mastodon account has moved: I&amp;rsquo;m now
&lt;a href="https://graz.social/@m1ch"&gt;&lt;strong&gt;@m1ch@graz.social&lt;/strong&gt;&lt;/a&gt;. The old account,
&lt;code&gt;@mightymich@mastodon.social&lt;/code&gt;, points there, and Mastodon&amp;rsquo;s account migration
brought my followers along, so if you followed me before you don&amp;rsquo;t need to
do anything. &amp;ndash; &lt;strong&gt;TBH:&lt;/strong&gt; If I knew how easy this step would be, I&amp;rsquo;d done it long ago.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.m1ch.eu/images/posts/graz-social.webp" alt="graz.social logo: the Graz clock tower drawn in thin lines, its clock face standing in for the &amp;ldquo;o&amp;rdquo; in graz.social"&gt;&lt;/p&gt;
&lt;h2 id="why"&gt;Why&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://graz.social"&gt;graz.social&lt;/a&gt; is a Mastodon server for Graz and the
surrounding area, run by the association &lt;em&gt;graz.social &amp;ndash; Verein zur Förderung
ethischer Digitalkultur&lt;/em&gt; (&lt;a href="https://info.graz.social"&gt;info.graz.social&lt;/a&gt;). I&amp;rsquo;m
part of its admin team, and now my own account lives there too: on a server
I help look after, in the city I live in.&lt;/p&gt;</description></item><item><title>Coordinated Reboots for bootc Hosts</title><link>https://blog.m1ch.eu/posts/20261019-bootc-fleetlock-reboots/</link><pubDate>Mon, 19 Oct 2026 00:00:00 +0000</pubDate><guid>https://blog.m1ch.eu/posts/20261019-bootc-fleetlock-reboots/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; &amp;ndash; My bootc hosts stage new images every hour, but nothing
rebooted them into those images. Now a small timer on each host checks a
time window, its own health and its neighbours, takes a lock from a
~200-line Python FleetLock server, and only then reboots. No Kubernetes, no
etcd, and protocol-compatible with the Fedora CoreOS tooling in case I
outgrow it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In &lt;a href="https://blog.m1ch.eu/posts/20251122-bootc-tweeks/"&gt;how I tweak my base image&lt;/a&gt; I
described how I replaced &lt;code&gt;bootc-fetch-apply-updates.timer&lt;/code&gt; with my own
&lt;code&gt;bootc-fetch.timer&lt;/code&gt;: every hour &lt;code&gt;bootc update&lt;/code&gt; pulls the newest image and
stages it, but doesn&amp;rsquo;t reboot. I ended that post with &amp;ldquo;the actual reboot is a
separate step I control manually or via another timer&amp;rdquo;. This post is about
that missing step.&lt;/p&gt;</description></item><item><title>No Assignment, No Code: Planning with a Coding Agent in OpenProject</title><link>https://blog.m1ch.eu/posts/20261007-openproject-coding-agent/</link><pubDate>Wed, 07 Oct 2026 00:00:00 +0000</pubDate><guid>https://blog.m1ch.eu/posts/20261007-openproject-coding-agent/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; &amp;ndash; I plan my side project with a coding agent in OpenProject
instead of a chat window. I write user stories (&lt;em&gt;what&lt;/em&gt; and &lt;em&gt;why&lt;/em&gt;); the agent,
working as its own least-privilege user, turns them into features and tasks
with specs (&lt;em&gt;how&lt;/em&gt;) and hands them back for review. It only writes code for
items assigned to it, and only when I ask. It works, it costs some ceremony,
and the MCP tooling still has rough edges.&lt;/p&gt;</description></item><item><title>AI Agents in My Hosting Workflow</title><link>https://blog.m1ch.eu/posts/20260928-ai-agents-in-my-hosting-workflow/</link><pubDate>Mon, 28 Sep 2026 00:00:00 +0000</pubDate><guid>https://blog.m1ch.eu/posts/20260928-ai-agents-in-my-hosting-workflow/</guid><description>&lt;p&gt;My homelab is mostly text: Docker Swarm compose files, bootc Containerfiles,
Forgejo workflows, a few Ansible playbooks and this Hugo blog. All of it lives
in git, and for years the workflow has been the same: edit in an editor, render,
deploy, check the logs, repeat.&lt;/p&gt;
&lt;p&gt;Over the last months two coding agents moved into that loop:
&lt;a href="https://opencode.ai"&gt;opencode&lt;/a&gt; and
&lt;a href="https://claude.com/claude-code"&gt;Claude Code&lt;/a&gt;. This post covers how I set them
up, what they do for me, and where I still keep my hands on the wheel.&lt;/p&gt;</description></item><item><title>Why I Run My Models Locally</title><link>https://blog.m1ch.eu/posts/20260924-why-local-models/</link><pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate><guid>https://blog.m1ch.eu/posts/20260924-why-local-models/</guid><description>&lt;p&gt;Every prompt you send to a hosted LLM leaves your network. That includes the
text you type, the files you attach, and whatever context your tools decide to
pack around it. For a quick question about a regex that doesn&amp;rsquo;t matter. For
your mailbox, your customers&amp;rsquo; tickets or your company wiki, it matters a lot.&lt;/p&gt;
&lt;p&gt;That is the main reason I run most of my models on my own hardware with
&lt;a href="https://ollama.com"&gt;Ollama&lt;/a&gt;. This post covers why, and where these models
are useful in a self-hosted setup.&lt;/p&gt;</description></item><item><title>Conditional bootc build</title><link>https://blog.m1ch.eu/posts/20260507-forgejo-runneres-advanced/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://blog.m1ch.eu/posts/20260507-forgejo-runneres-advanced/</guid><description>&lt;p&gt;Running image builds on a schedule makes sense for security &amp;ndash; you want your bootc
images to pick up the latest Fedora updates automatically. But scheduling a daily
build that rebuilds every image every day, even when nothing upstream changed, wastes
runner time and fills the registry with identical layers.&lt;/p&gt;
&lt;p&gt;This post covers how I added upstream change detection to my Forgejo CI pipelines
for &lt;a href="https://codeberg.org/m1ch/gorgeOS"&gt;gorgeOS&lt;/a&gt;, so that scheduled builds are truly
no-ops when the upstream image hasn&amp;rsquo;t moved.&lt;/p&gt;</description></item><item><title>Bootc Matrix Build</title><link>https://blog.m1ch.eu/posts/20260505-bootc-matrix-build/</link><pubDate>Tue, 05 May 2026 00:00:00 +0000</pubDate><guid>https://blog.m1ch.eu/posts/20260505-bootc-matrix-build/</guid><description>&lt;p&gt;Running a Fedora-based bootc image stack means tracking multiple Fedora releases
in parallel. Instead of duplicating every job in the workflow, I wired up the
matrix strategy in Forgejo Actions to build F43 and F44 from a single job
definition.&lt;/p&gt;
&lt;h2 id="the-problem-with-hardcoded-versions"&gt;The Problem With Hardcoded Versions&lt;/h2&gt;
&lt;p&gt;Before, every Containerfile had a hardcoded base image:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-dockerfile" data-lang="dockerfile"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; &lt;span style="color:#e6db74"&gt;git.m1ch.eu/oci/c-os:base-43&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Supporting F44 would have meant either duplicating the whole workflow or a manual
find-and-replace for every release. Neither scales.&lt;/p&gt;</description></item><item><title>gorgeOS-Cloud</title><link>https://blog.m1ch.eu/posts/20251127-bootc-cloud-image/</link><pubDate>Thu, 27 Nov 2025 00:00:00 +0000</pubDate><guid>https://blog.m1ch.eu/posts/20251127-bootc-cloud-image/</guid><description>&lt;p&gt;In the &lt;a href="https://blog.m1ch.eu/posts/20251122-bootc-tweeks"&gt;last post&lt;/a&gt; I walked through the base image that every one of my servers shares.
This time I want to look at the first layer on top of it: the &lt;strong&gt;cloud image&lt;/strong&gt;, which adds everything a virtual machine running in a hypervisor environment needs.&lt;/p&gt;
&lt;p&gt;The full source is in my &lt;a href="https://codeberg.org/m1ch/gorgeOS"&gt;gorge.os&lt;/a&gt; repository.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="layered-images"&gt;Layered images&lt;/h2&gt;
&lt;p&gt;One of the things I really like about BootC is that you can layer images just like you layer application containers. The cloud image simply starts from the base:&lt;/p&gt;</description></item><item><title>How I tweak my bootc base image</title><link>https://blog.m1ch.eu/posts/20251122-bootc-tweeks/</link><pubDate>Sat, 22 Nov 2025 00:00:00 +0000</pubDate><guid>https://blog.m1ch.eu/posts/20251122-bootc-tweeks/</guid><description>&lt;p&gt;In the &lt;a href="https://blog.m1ch.eu/posts/20251114-bootc-motivation"&gt;last post&lt;/a&gt; I explained why I decided to switch to BootC for all my servers.
This time I want to walk through what I actually put into my base image - the packages, the config files, and the custom services that make every host feel like home.&lt;/p&gt;
&lt;p&gt;The full source is in my &lt;a href="https://codeberg.org/m1ch/gorgeOS"&gt;gorge.os&lt;/a&gt; repository.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-containerfile-structure"&gt;The Containerfile structure&lt;/h2&gt;
&lt;p&gt;Before getting into what&amp;rsquo;s in the image, I want to explain &lt;strong&gt;how&lt;/strong&gt; I put things into it. The Containerfile is split into three stages: package management, file placement, and service activation.&lt;/p&gt;</description></item><item><title>Using SSH Config `Match exec` to Route Hostnames Dynamically</title><link>https://blog.m1ch.eu/posts/20251120-ssh_config/</link><pubDate>Thu, 20 Nov 2025 15:06:14 +0100</pubDate><guid>https://blog.m1ch.eu/posts/20251120-ssh_config/</guid><description>&lt;h3 id="why-bother-with-a-fancy-match-block"&gt;Why bother with a fancy &lt;code&gt;Match&lt;/code&gt; block?&lt;/h3&gt;
&lt;p&gt;When you SSH into a fleet of servers that share a naming scheme, you usually end up copying-pasting the same options over and over. A &lt;code&gt;Match&lt;/code&gt; block that tests the host name and then tweaks the connection parameters lets you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Keep a single config file&lt;/strong&gt; that works for every member of the group.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automate proxying or port-translation&lt;/strong&gt; without having to write a wrapper script.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The example below demonstrates the trick.&lt;/p&gt;</description></item><item><title>The motivation to use BootC</title><link>https://blog.m1ch.eu/posts/20251114-bootc-motivation/</link><pubDate>Fri, 14 Nov 2025 11:06:14 +0100</pubDate><guid>https://blog.m1ch.eu/posts/20251114-bootc-motivation/</guid><description>&lt;p&gt;I have a fair amount of servers that I&amp;rsquo;m (co-)responsible for. To make my life easier I tried serveral managent tools to do the common tasks for all servers as smooth as possible.&lt;br&gt;
The two tools I mainly use at the moment are &lt;strong&gt;ansible&lt;/strong&gt; and &lt;strong&gt;cloud-init&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Cloud-init is a great tool to spin up a new server, or to resore an existing one.&lt;br&gt;
ansible is great to do common tasks, like updating servers, installing packages to multiple hosts, but when it comes to thinks like adding a systemd-service to just a view hosts, it never felt natural to me.&lt;/p&gt;</description></item><item><title>Glusterfs down 🫩</title><link>https://blog.m1ch.eu/posts/20251024-glusterfs/</link><pubDate>Fri, 24 Oct 2025 13:22:12 +0200</pubDate><guid>https://blog.m1ch.eu/posts/20251024-glusterfs/</guid><description>&lt;p&gt;This morning I woke up and the radio was not playing. I soon figured out, that my DNS, AdGuard, was for some reason down.&lt;/p&gt;
&lt;p&gt;I started with the debugging, even before I had breakfast. Not evan a coffee!&lt;/p&gt;
&lt;p&gt;The docker-container for AdGuard was running and reported no error. So I dug deeper and deeper, just to find out, that several containers showed strange behavior. For example did Traefik had som error that it could not find the middlewares, that where configured via environment variables.&lt;/p&gt;</description></item><item><title>Hello World!</title><link>https://blog.m1ch.eu/posts/20251020-first-post/</link><pubDate>Mon, 20 Oct 2025 17:14:34 +0200</pubDate><guid>https://blog.m1ch.eu/posts/20251020-first-post/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;M1CH&lt;/strong&gt; mich /mɪç/ &amp;ndash; In German “mich” is the accusative form of the first-person singular personal pronoun. (me; myself)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="-a-quick-hello"&gt;👋 A Quick Hello&lt;/h2&gt;
&lt;p&gt;I’m &lt;strong&gt;M1CH&lt;/strong&gt; &amp;ndash; the name you’ll find in the URL of this little corner of the web. I started this blog to share the things I’m excited about right now: bootable containers, Podman, Linux, open-source, privacy on the internet, Web 3.0 and all the decentralised ideas that are shaping the future.&lt;/p&gt;</description></item></channel></rss>