<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Thalamus</title><link>https://blog.thalamus.am/</link><description>Recent content on Thalamus</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><copyright>Thalamus TM</copyright><lastBuildDate>Sun, 20 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.thalamus.am/index.xml" rel="self" type="application/rss+xml"/><item><title>The 200 that nobody received</title><link>https://blog.thalamus.am/posts/the-200-that-nobody-received/</link><pubDate>Sun, 20 Sep 2026 00:00:00 +0000</pubDate><guid>https://blog.thalamus.am/posts/the-200-that-nobody-received/</guid><description>&lt;img src="https://blog.thalamus.am/posts/the-200-that-nobody-received/editorial-cover.png" alt="Featured image of post The 200 that nobody received" /&gt;&lt;p&gt;Clients of &lt;code&gt;api.thalamus.am&lt;/code&gt; were occasionally timing out. The gateway access log showed successful requests.&lt;/p&gt;
&lt;p&gt;The request had arrived. The upstream had answered. The log recorded a 200. There was no obvious pattern by endpoint or time of day, and the failures were rare enough to disappear into otherwise healthy dashboards.&lt;/p&gt;
&lt;p&gt;I spent too long looking for an application error. The useful question turned out to be: which machines did the connection depend on after the application had done its work?&lt;/p&gt;</description></item><item><title>df says full, du disagrees</title><link>https://blog.thalamus.am/posts/df-says-full-du-disagrees/</link><pubDate>Sat, 08 Aug 2026 00:00:00 +0000</pubDate><guid>https://blog.thalamus.am/posts/df-says-full-du-disagrees/</guid><description>&lt;img src="https://blog.thalamus.am/posts/df-says-full-du-disagrees/editorial-cover.png" alt="Featured image of post df says full, du disagrees" /&gt;&lt;p&gt;The third time the compactor volume filled up, I checked what was using the space before making it bigger.&lt;/p&gt;
&lt;p&gt;The alert was &lt;code&gt;KubePersistentVolumeFillingUp&lt;/code&gt;, on the compactor volume of our log store. The first time, we resized the volume. That bought about a month of quiet. It also made the diagnosis feel settled: more logs, not enough disk.&lt;/p&gt;
&lt;p&gt;Then the alert came back. And came back sooner.&lt;/p&gt;
&lt;p&gt;This time I opened a shell in the pod and compared two numbers:&lt;/p&gt;</description></item><item><title>The apply is still mine</title><link>https://blog.thalamus.am/posts/the-apply-is-still-mine/</link><pubDate>Sat, 18 Jul 2026 00:00:00 +0000</pubDate><guid>https://blog.thalamus.am/posts/the-apply-is-still-mine/</guid><description>&lt;img src="https://blog.thalamus.am/posts/the-apply-is-still-mine/editorial-cover.png" alt="Featured image of post The apply is still mine" /&gt;&lt;p&gt;Claude Code writes a useful share of my Terraform now. It drafts Helm values, reviews GitOps configuration and helps me write runbooks. I still decide which changes reach production.&lt;/p&gt;
&lt;p&gt;The useful output is a diff I can review. Getting that diff faster doesn&amp;rsquo;t remove the need to understand it.&lt;/p&gt;
&lt;p&gt;My workflow keeps commits, pushes and deployment commands with me. Claude Code&amp;rsquo;s permissions help enforce that split. But a list of blocked commands is only part of the control: a tool that has production credentials may have more than one way to use them.&lt;/p&gt;</description></item><item><title>Set up a Kubernetes cluster with MetalLB</title><link>https://blog.thalamus.am/posts/setup-kubernetes-cluster-with-metallb/</link><pubDate>Sat, 29 Apr 2023 00:00:00 +0000</pubDate><guid>https://blog.thalamus.am/posts/setup-kubernetes-cluster-with-metallb/</guid><description>&lt;img src="https://blog.thalamus.am/posts/setup-kubernetes-cluster-with-metallb/editorial-cover.png" alt="Featured image of post Set up a Kubernetes cluster with MetalLB" /&gt;&lt;p&gt;A Kubernetes cluster on your own VMs needs a way to give services an address that other machines can reach. This lab uses kubeadm to build the cluster, Calico for pod networking, and MetalLB for &lt;code&gt;LoadBalancer&lt;/code&gt; services.&lt;/p&gt;
&lt;p&gt;The setup had three Ubuntu VMs: one control plane node and two workers. These notes keep the 2023 versions, including Kubernetes &lt;code&gt;1.26.4&lt;/code&gt; and Calico &lt;code&gt;3.25.1&lt;/code&gt;. Some steps were missing from the first version, especially the MetalLB network configuration. I explain those gaps below, but the complete setup still needs a fresh test.&lt;/p&gt;</description></item><item><title>Build a certificate authority hierarchy with CFSSL</title><link>https://blog.thalamus.am/posts/setting-up-a-certificate-authority-ca-hierarchy-with-cfssl/</link><pubDate>Thu, 12 Jan 2023 00:00:00 +0000</pubDate><guid>https://blog.thalamus.am/posts/setting-up-a-certificate-authority-ca-hierarchy-with-cfssl/</guid><description>&lt;img src="https://blog.thalamus.am/posts/setting-up-a-certificate-authority-ca-hierarchy-with-cfssl/editorial-cover.png" alt="Featured image of post Build a certificate authority hierarchy with CFSSL" /&gt;&lt;p&gt;A server certificate is only one part of a trust chain. The server presents it, an intermediate CA signs it, and the client needs a trusted root to verify the chain.&lt;/p&gt;
&lt;p&gt;This 2023 lab uses CFSSL, Cloudflare&amp;rsquo;s PKI toolkit, to create those three certificates. The example domain is &lt;code&gt;example.com&lt;/code&gt;. Creating this private CA does not make browsers trust it, and generating the files does not configure a web server.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Example CA (root)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── Example Intermediate CA
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └── example.com (server)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The original article did not record its CFSSL version. Keep that limit in mind when reproducing it. You&amp;rsquo;ll need &lt;code&gt;cfssl&lt;/code&gt; and &lt;code&gt;cfssljson&lt;/code&gt;; use the &lt;a class="link" href="https://github.com/cloudflare/cfssl#installation" target="_blank" rel="noopener"
 &gt;upstream installation instructions&lt;/a&gt; for your platform.&lt;/p&gt;</description></item><item><title>Deploy a microservices demo with Rancher</title><link>https://blog.thalamus.am/posts/creating-microservices-deployments/</link><pubDate>Sat, 09 May 2020 00:00:00 +0000</pubDate><guid>https://blog.thalamus.am/posts/creating-microservices-deployments/</guid><description>&lt;img src="https://blog.thalamus.am/posts/creating-microservices-deployments/editorial-cover.png" alt="Featured image of post Deploy a microservices demo with Rancher" /&gt;&lt;p&gt;This lab follows a microservices demo from three Ubuntu machines to an application deployed on Kubernetes. Rancher provides the interface for creating the cluster and registering its nodes.&lt;/p&gt;
&lt;p&gt;The notes and screenshots come from my 2020 demo. The package repositories and Rancher screens have changed since then. The sequence is still useful to read, but it needs a fresh lab run before it can serve as a current tutorial.&lt;/p&gt;
&lt;h2 id="the-three-nodes-have-different-jobs"&gt;The three nodes have different jobs
&lt;/h2&gt;&lt;p&gt;The original cluster assigned these roles:&lt;/p&gt;</description></item><item><title>CI/CD for a Java project: manual installation</title><link>https://blog.thalamus.am/posts/ci-cd-for-java-project-manual-installation/</link><pubDate>Sun, 12 Apr 2020 00:00:00 +0000</pubDate><guid>https://blog.thalamus.am/posts/ci-cd-for-java-project-manual-installation/</guid><description>&lt;img src="https://blog.thalamus.am/posts/ci-cd-for-java-project-manual-installation/editorial-cover.png" alt="Featured image of post CI/CD for a Java project: manual installation" /&gt;&lt;p&gt;In this Java CI/CD lab, Jenkins runs the build jobs, Docker builds container images, and Nexus stores them. Nginx gives Jenkins and Nexus their own HTTPS addresses. Before connecting the pipeline, each service needs to work on its own.&lt;/p&gt;
&lt;p&gt;I split the original project into two parts: install the components by hand first, then automate the setup with Terraform and Ansible. This page covers the manual installation recorded in 2020. It stops before the Java build job and Kubernetes deployment.&lt;/p&gt;</description></item><item><title>Multi-touch gestures configuration</title><link>https://blog.thalamus.am/posts/multi-touch-gestures-configuration/</link><pubDate>Fri, 27 Mar 2020 00:00:00 +0000</pubDate><guid>https://blog.thalamus.am/posts/multi-touch-gestures-configuration/</guid><description>&lt;img src="https://blog.thalamus.am/posts/multi-touch-gestures-configuration/editorial-cover.png" alt="Featured image of post Multi-touch gestures configuration" /&gt;&lt;p&gt;A swipe can switch workspaces by triggering the same shortcut you would press on the keyboard. This note records two setups from 2020: &lt;code&gt;libinput-gestures&lt;/code&gt; for a touchpad, and an older Touchégg configuration for a touchscreen.&lt;/p&gt;
&lt;p&gt;The original Ubuntu/Linux Mint release and tool versions were not recorded. The examples below explain the setup, but they still need testing on a specified desktop and release.&lt;/p&gt;
&lt;h2 id="check-your-desktops-own-gestures-first"&gt;Check your desktop&amp;rsquo;s own gestures first
&lt;/h2&gt;&lt;p&gt;GNOME documents &lt;a class="link" href="https://help.gnome.org/gnome-help/touchscreen-gestures.html" target="_blank" rel="noopener"
 &gt;built-in touchpad and touchscreen gestures&lt;/a&gt;. Try the actions you need before adding another gesture service.&lt;/p&gt;</description></item></channel></rss>