<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>johanneskueber.com</title><link>https://johanneskueber.com/</link><description>Recent content on johanneskueber.com</description><generator>Hugo</generator><language>en_US</language><lastBuildDate>Sun, 23 Aug 2026 10:00:00 +0200</lastBuildDate><atom:link href="https://johanneskueber.com/index.xml" rel="self" type="application/rss+xml"/><item><title>OIDC authentication for Kubernetes on Talos</title><link>https://johanneskueber.com/posts/2026-08-23-oidc-kubernetes-talos/</link><pubDate>Sun, 23 Aug 2026 10:00:00 +0200</pubDate><guid>https://johanneskueber.com/posts/2026-08-23-oidc-kubernetes-talos/</guid><description>&lt;p&gt;Every kubeconfig for my clusters used to be the same thing: a client certificate for &lt;code&gt;system:masters&lt;/code&gt;, generated by &lt;code&gt;talosctl kubeconfig&lt;/code&gt;, valid for a year. That certificate is effectively root on the cluster. It has no name attached to it, it cannot be revoked without rotating the cluster CA, and it ends up on every device I want to administrate from. Once I copied it to my phone I knew this was the wrong path. And since I already run a &lt;a href="https://www.keycloak.org/"&gt;Keycloak&lt;/a&gt; for every other application in my homelab, I wantedto simply log in with the same SSO account as everything else.&lt;/p&gt;</description></item><item><title>A Backup Target on Backblaze B2 - k8up, Crossplane, and External Secrets in One Loop</title><link>https://johanneskueber.com/posts/2026-08-04-k8up-backblaze-crossplane-external-secrets/</link><pubDate>Tue, 04 Aug 2026 00:00:00 +0000</pubDate><guid>https://johanneskueber.com/posts/2026-08-04-k8up-backblaze-crossplane-external-secrets/</guid><description>&lt;h1 id="a-backup-target-on-backblaze-b2---k8up-crossplane-and-external-secrets-in-one-loop"&gt;A Backup Target on Backblaze B2 - k8up, Crossplane, and External Secrets in One Loop&lt;/h1&gt;&#10;&lt;p&gt;All my restic backups used to go to exactly one place: Hetzner Object Storage. That is a fine warm tier, except for one detail - the cluster&amp;rsquo;s infrastructure also runs on Hetzner. A provider outage, a billing mishap, or a compromised account takes out the workloads &lt;em&gt;and&lt;/em&gt; their backups in the same event. I had off-site backups on Scaleway once, but their object storage made restic crawl badly enough that I removed it and never looked back.&lt;/p&gt;</description></item><item><title>Country-based access control with Envoy Gateway (and the TCP-listener trap)</title><link>https://johanneskueber.com/posts/2026-07-27-envoy-gateway-geoip-country-blocking/</link><pubDate>Mon, 27 Jul 2026 00:00:00 +0000</pubDate><guid>https://johanneskueber.com/posts/2026-07-27-envoy-gateway-geoip-country-blocking/</guid><description>&lt;p&gt;The goal was simple: everything on the public gateway should only answer requests originating from one country. Most of the self-hosted services behind it have no business being reachable from the other side of the planet, and a country allowlist at the edge cuts the background noise of credential-stuffing and vulnerability scanners down to almost nothing before a request ever reaches an application.&lt;/p&gt;&#10;&lt;p&gt;Envoy Gateway can do this natively since v1.8 with the MaxMind GeoIP filter and an authorization &lt;code&gt;SecurityPolicy&lt;/code&gt;. The configuration is small. Getting it to actually apply, however, took a detour through a translator bug that freezes the entire control plane — so this article covers both the working configuration and the problems encountered on the way there.&lt;/p&gt;</description></item><item><title>OCI-First GitOps Promotion with Flux, Kargo, and Renovate</title><link>https://johanneskueber.com/posts/2026-06-19-oci-gitpos-automatic-promotion/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://johanneskueber.com/posts/2026-06-19-oci-gitpos-automatic-promotion/</guid><description>&lt;h1 id="oci-first-gitops-promotion-with-flux-kargo-and-renovate"&gt;OCI-First GitOps Promotion with Flux, Kargo, and Renovate&lt;/h1&gt;&#10;&lt;p&gt;This article describes a promotion architecture where &lt;strong&gt;the deployment definition lives in Git but is never deployed from Git&lt;/strong&gt;. Instead, every merge builds a signed OCI artifact containing the deployment manifests, and that artifact — immutable, versioned, digest-pinned — is what promotes through dev → staging → prod and what Flux actually deploys.&lt;/p&gt;&#10;&lt;p&gt;Three tools, one responsibility each:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;Renovate&lt;/strong&gt; updates the deployment definition in Git — new application image versions, third-party chart bumps, dependency updates. Its merged PRs are how change enters the pipeline.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Kargo&lt;/strong&gt; promotes the resulting OCI artifact across environments, running automated tests at each gate.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Flux&lt;/strong&gt; deploys the artifact into each cluster, verifying its signature before reconciling.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;No human touches the routine path. Humans appear only at the optional prod approval gate and in emergencies.&lt;/p&gt;</description></item><item><title>Signed OCI Artifacts for Flux with Cosign and Gitea</title><link>https://johanneskueber.com/posts/2026-06-15-signed-oci-deployments/</link><pubDate>Mon, 15 Jun 2026 00:00:00 +0000</pubDate><guid>https://johanneskueber.com/posts/2026-06-15-signed-oci-deployments/</guid><description>&lt;h1 id="signed-oci-artifacts-for-flux-with-cosign-and-gitea"&gt;Signed OCI Artifacts for Flux with Cosign and Gitea&lt;/h1&gt;&#10;&lt;p&gt;Flux will happily pull a Git repository, run &lt;code&gt;kustomize build&lt;/code&gt; over a folder, and apply the result. I ran it that way for a long time and it works. What it does not give you is any statement about &lt;em&gt;what&lt;/em&gt; is being applied or &lt;em&gt;where it came from&lt;/em&gt;. The source of truth is a mutable branch - anyone who can write to that path, or anything that can, changes what lands in the cluster on the next reconcile. There is no integrity check sitting between &amp;ldquo;a commit exists&amp;rdquo; and &amp;ldquo;this is what is running in production.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Bare-Metal LoadBalancer Services on Talos with Cilium L2 Announcements</title><link>https://johanneskueber.com/posts/2026-06-01-cilium-l2-announcements/</link><pubDate>Mon, 01 Jun 2026 00:00:00 +0000</pubDate><guid>https://johanneskueber.com/posts/2026-06-01-cilium-l2-announcements/</guid><description>&lt;p&gt;This article documents how to replace MetalLB with Cilium&amp;rsquo;s built-in L2 announcement feature, including the IPAM pool, the announcement policy, the supporting Cilium values, and what to verify on the wire.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="1-metallbs-role"&gt;1. MetalLB&amp;rsquo;s role&lt;/h2&gt;&#10;&lt;p&gt;On a bare-metal cluster, a &lt;code&gt;Service&lt;/code&gt; of &lt;code&gt;type: LoadBalancer&lt;/code&gt; is meaningless until something assigns it an external IP and answers ARP for that IP on the local segment. The classic answer is MetalLB in L2 mode: a controller allocates an IP from a pool; a speaker DaemonSet replies to ARP requests, advertising via gratuitous ARP after leader election.&lt;/p&gt;</description></item><item><title>Per-PVC Encryption with Longhorn and CSI Secret Templates</title><link>https://johanneskueber.com/posts/2026-05-28-longhorn-per-pvc-encryption/</link><pubDate>Thu, 28 May 2026 00:00:00 +0000</pubDate><guid>https://johanneskueber.com/posts/2026-05-28-longhorn-per-pvc-encryption/</guid><description>&lt;p&gt;This article documents how to configure a Longhorn &lt;code&gt;StorageClass&lt;/code&gt; that encrypts every PVC with its own per-volume key, derived through CSI&amp;rsquo;s secret-template parameters, and how to provision the matching secrets so the keys are scoped to the application namespace.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="1-what-encryption-longhorn-actually-does"&gt;1. What encryption Longhorn actually does&lt;/h2&gt;&#10;&lt;p&gt;Longhorn 1.4+ supports LUKS encryption at the block device layer. When a PVC&amp;rsquo;s StorageClass declares &lt;code&gt;encrypted: &amp;quot;true&amp;quot;&lt;/code&gt;, Longhorn calls &lt;code&gt;cryptsetup luksFormat&lt;/code&gt; on the underlying replica devices using a passphrase pulled from a Kubernetes Secret. The PVC is then exposed to the consuming Pod as an unencrypted filesystem — the kernel handles the encryption transparently through the device-mapper layer.&lt;/p&gt;</description></item><item><title>Signed Dependency Updates with Renovate and Gitea</title><link>https://johanneskueber.com/posts/2026-05-26-sign-renovate-commits/</link><pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate><guid>https://johanneskueber.com/posts/2026-05-26-sign-renovate-commits/</guid><description>&lt;p&gt;Keeping dependencies up to date is one of those chores that is easy to neglect and expensive to ignore. Renovate solves it nicely: it scans my repositories, opens pull requests for outdated dependencies, and - if I let it - merges them on its own. On a self-hosted Gitea instance with branch protection, however, there is a catch. If I require signed commits on my protected branches, an unsigned bot commit is simply rejected. So before automating anything, Renovate needs an identity and a signing key, and its commits need to show up as &lt;em&gt;Verified&lt;/em&gt;.&lt;/p&gt;</description></item><item><title>Running a rootless NetBird Routing Peer Inside Kubernetes</title><link>https://johanneskueber.com/posts/2026-05-25-netbird-routing-peer/</link><pubDate>Mon, 25 May 2026 00:00:00 +0000</pubDate><guid>https://johanneskueber.com/posts/2026-05-25-netbird-routing-peer/</guid><description>&lt;p&gt;This article documents how to run a NetBird client &lt;em&gt;inside&lt;/em&gt; a Kubernetes cluster as a &lt;strong&gt;routing peer&lt;/strong&gt; — rootless, with an in-memory state volume, a shell-based readiness probe, and dropped capabilities — so that remote mesh members can reach cluster-internal services over the encrypted overlay, without exposing kernel WireGuard and without making the cluster a peer of every workload.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="1-why-a-netbird-peer-in-the-cluster"&gt;1. Why a NetBird peer in the cluster&lt;/h2&gt;&#10;&lt;p&gt;NetBird is a WireGuard-based identity mesh. Each peer holds keys, exchanges them via the management plane, and routes encrypted traffic to other peers. The default deployment runs the client on a host (Linux VM, MacBook, etc.) and uses the host&amp;rsquo;s kernel WireGuard.&lt;/p&gt;</description></item><item><title>Envoy Gateway Configuration to Reach SSL Labs A+</title><link>https://johanneskueber.com/posts/2026-05-14-envoy-gateway-ssllabs-a-plus/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://johanneskueber.com/posts/2026-05-14-envoy-gateway-ssllabs-a-plus/</guid><description>&lt;p&gt;This article documents the Envoy Gateway configuration required to achieve an A+ rating on Qualys SSL Labs, with the rationale for each setting.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="1-tls-security-background"&gt;1. TLS security background&lt;/h2&gt;&#10;&lt;p&gt;Before any application data flows over &lt;code&gt;https://&lt;/code&gt;, the TLS session completes two steps:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;Authentication.&lt;/strong&gt; The server presents an X.509 certificate signed by a trusted Certificate Authority.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Key agreement and encryption.&lt;/strong&gt; Client and server negotiate a session key via a cipher suite (key exchange, signature, bulk cipher, MAC) and encrypt the channel.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Weaknesses in this chain enable specific attack classes:&lt;/p&gt;</description></item><item><title>Intel iGPU passthrough with Proxmox and Talos</title><link>https://johanneskueber.com/posts/2025-10-11-proxmox-passthrough-talos/</link><pubDate>Sat, 11 Oct 2025 21:06:26 +0000</pubDate><guid>https://johanneskueber.com/posts/2025-10-11-proxmox-passthrough-talos/</guid><description>&lt;h2 id="igpu-passthrough-with-proxmox-and-talos"&gt;iGPU Passthrough with Proxmox and Talos&lt;/h2&gt;&#10;&lt;p&gt;To use the GPU of the host system in K8s, it needs to be made aware of its existence. As I use VMs in Proxmox to run my Talos Cluster, there is an additional step to consider: passthrough of the hardware into the VM. The idea is the following:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Disable usage of GPU on the Proxmox Host - done via IOMMU and VFIO&lt;/li&gt;&#10;&lt;li&gt;Passthrough of the GPU into a Talos Worker Node - done via hostpci&lt;/li&gt;&#10;&lt;li&gt;Activation of GPU drivers in Talos - done via talhelper&lt;/li&gt;&#10;&lt;li&gt;GPU management in the cluster - done via Intel GPU Plugin&lt;/li&gt;&#10;&lt;li&gt;Usage of GPU in a deployment - by requesting the resource&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;There are quite some steps and all of them are specific to your environment. Different host system? Different steps. Different Cluster Software? You need to adept. Different GPU? You need other drivers and management. So please look out for the pitfalls and only copy and paste if you run proxmox with talos and have an Intel iGPU.&lt;/p&gt;</description></item><item><title>Use Longhorn with Talos 1.10 and userVolumes</title><link>https://johanneskueber.com/posts/2025-06-17-longhorn-uservolumes-talos/</link><pubDate>Tue, 17 Jun 2025 07:06:26 +0000</pubDate><guid>https://johanneskueber.com/posts/2025-06-17-longhorn-uservolumes-talos/</guid><description>&lt;p&gt;When building a cluster, especially in a homelab, local storage is needed for application data. Especially for databases fast read and write is required. Offloading the workload to a NAS most of the time is slower. The solution I use is to provision on-node storage with &lt;a href="https://longhorn.io/"&gt;Longhorn&lt;/a&gt;. Longhorn acts as a CSI and offers on-node storage, replication, backups and more.&lt;/p&gt;&#10;&lt;p&gt;As I am currently building a Talos cluster I need to integrate the longhorn CSI into the setup. This is not as straigt forward as with K3s oder K8s, as Talos has tighter security constraints and also needs additional plugins to handle SCSI - the underlying file system protocol used by longhorn. On top I am using &lt;a href="https://budimanjojo.github.io/talhelper/latest/"&gt;Talhelper&lt;/a&gt; to allow a GitOps style usage of talosctl. The main advantage is the encryption of secrets used by talos config files with &lt;a href="https://github.com/getsops/sops"&gt;SOPS&lt;/a&gt; - something that I already use for Tofu and fluxCD.&lt;/p&gt;</description></item><item><title>Enable hardware acceleration for Jellyfin in Kubernetes - AMD Edition</title><link>https://johanneskueber.com/posts/2024-06-24-hardware-acceleration-kubernetes-jellyfin/</link><pubDate>Mon, 24 Jun 2024 07:06:26 +0000</pubDate><guid>https://johanneskueber.com/posts/2024-06-24-hardware-acceleration-kubernetes-jellyfin/</guid><description>&lt;p&gt;&lt;a href="https://jellyfin.org/"&gt;Jellyfin&lt;/a&gt; is an open-source media server software that allows users to manage and stream their personal collection of movies, TV shows, music, and other media files. It is designed as an alternative to proprietary media server solutions like Plex and Emby, offering similar functionality but without any licensing costs or restrictions.&lt;/p&gt;&#10;&lt;p&gt;Jellyfin is running in my bare-metal kubernetes cluster. However, running the container without additional configuraion only gives Jellyfin access to the CPU for decoding video streams. If a GPU is available it would be better to use the GPU as it - most of the time - also has hardware acceleration for well-known codecs and in addition it takes some work off the CPU. In my case, the Athlon 3000G has an on-board Vega 3 graphics chip. Now the only thing left to do is to give Jellyfin access to the GPU.&lt;/p&gt;</description></item><item><title>Migrating data between baserow instances</title><link>https://johanneskueber.com/posts/2024-06-19-migrating-baserow-data-between-instances/</link><pubDate>Wed, 19 Jun 2024 07:06:26 +0000</pubDate><guid>https://johanneskueber.com/posts/2024-06-19-migrating-baserow-data-between-instances/</guid><description>&lt;p&gt;&lt;a href="https://baserow.io/"&gt;Baserow&lt;/a&gt; is an open-source alternative to tools like Airtable and Google Sheets. It allows users to create databases and manage data in a user-friendly, spreadsheet-like interface. With Baserow, you can store, share, and collaborate on data with your team, all while maintaining full control over your data as it&amp;rsquo;s self-hosted. It offers features like row-level permissions, different field types, views (like table, gallery, kanban), and more. It also provides APIs for developers to build custom apps or integrations.&lt;/p&gt;</description></item><item><title>Running an SPA and an API in the same Docker Container</title><link>https://johanneskueber.com/posts/2021-04-28-spa-and-server-in-one-dockerfile/</link><pubDate>Wed, 28 Apr 2021 18:12:26 +0000</pubDate><guid>https://johanneskueber.com/posts/2021-04-28-spa-and-server-in-one-dockerfile/</guid><description>&lt;p&gt;Hosting a static web page was never a problem. A simple Apache or nginx server and a local directory on the server was always enough. With cloud environment these tools are still available. And it is simple enough to mount a directory into an nginx container. However, you still need to spin up a container just for the purpose of hosting a static page. Using a JAMstack application, you also need an API container to connect to. In order to reduce the number of containers and the overhead, I will show how we can integrate the the server for the static page into our API server. This way, we will have everything included in a single container. This is all achieved by using dockers multi-stage builds.&lt;/p&gt;</description></item><item><title>Selfhosted Continuous Integration - all parts required</title><link>https://johanneskueber.com/posts/2020-01-08-selfhosted-continuous-integration-part-1-overview/</link><pubDate>Wed, 08 Jan 2020 21:06:26 +0100</pubDate><guid>https://johanneskueber.com/posts/2020-01-08-selfhosted-continuous-integration-part-1-overview/</guid><description>&lt;p&gt;As of late I came to wonder how dependent I want to be on cloud-solutions. After some researching I decided I wanted to be independent. This also includes leaving out great open source communities such as &lt;a href="https://github.com/"&gt;Github&lt;/a&gt;. There are plenty of great solutions for every aspect of the Software Development Lifecycle - most of them are hosted in the cloud, i.e. the computer of someone else. This is not only a problem once your internet is not working - but it also happens to become a problem when a certain service is shut down. Sometimes without warning. Sometimes forever. So i wondered: could I set up a basic continuous integration environment completely hosted in my environment.&lt;/p&gt;</description></item><item><title>Getting the color palette of an image with Coil in Android</title><link>https://johanneskueber.com/posts/2019-12-12-android-coil-palette/</link><pubDate>Thu, 12 Dec 2019 23:06:26 +0100</pubDate><guid>https://johanneskueber.com/posts/2019-12-12-android-coil-palette/</guid><description>&lt;p&gt;For part of my applications I wanted to load an image into an activity. Based on the images main colors, I wanted to adjust the UI of the activity. Loading of the image is the easy part. This can be done via a number of libraries. At the time of writing, the most prominent libraries are: &lt;a href="https://square.github.io/picasso/"&gt;Picasso&lt;/a&gt; and &lt;a href="https://bumptech.github.io/glide/"&gt;Glide&lt;/a&gt;. Lately however, there is a new library which targets Kotlin specifically: Coil (see &lt;a href="https://coil-kt.github.io/coil/"&gt;coil-kt.github.io/coil/&lt;/a&gt;).&lt;/p&gt;</description></item><item><title>About</title><link>https://johanneskueber.com/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://johanneskueber.com/about/</guid><description>&lt;h1 id="about-johanneskuebercom"&gt;About johanneskueber.com&lt;/h1&gt;&#10;&lt;p&gt;This homepage is dedicated to my work and pet projects all around IT. In no particular order I try to keep myself entertained with new technologies, programming, automation, (self-)hosting.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;Mobile Development&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;a href="https://johanneskueber.com/tags/android-architecture-components"&gt;Android Architecture Components&lt;/a&gt; &lt;a href="https://developer.android.com/topic/architecture"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/mvvm"&gt;MVVM&lt;/a&gt; &lt;a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/dagger"&gt;Dagger 2&lt;/a&gt; &lt;a href="https://dagger.dev/"&gt;(↗)&lt;/a&gt; (&lt;a href="https://johanneskueber.com/tags/hilt"&gt;Hilt&lt;/a&gt; &lt;a href="https://dagger.dev/hilt/"&gt;(↗)&lt;/a&gt;),&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/kotlin/"&gt;Kotlin&lt;/a&gt; &lt;a href="https://kotlinlang.org/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/grpc"&gt;GRPC&lt;/a&gt; &lt;a href="https://grpc.io/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/rest"&gt;REST&lt;/a&gt; &lt;a href="https://en.wikipedia.org/wiki/REST"&gt;(↗)&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;a href="https://johanneskueber.com/tags/angularjs"&gt;AngularJS&lt;/a&gt; &lt;a href="https://angularjs.org/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/angular"&gt;Angular&lt;/a&gt; &lt;a href="https://angular.dev/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/vue"&gt;Vue.js&lt;/a&gt; &lt;a href="https://vuejs.org/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/gridsome"&gt;Gridsome&lt;/a&gt; &lt;a href="https://gridsome.org/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/nuxt/"&gt;Nuxt&lt;/a&gt; &lt;a href="https://nuxt.com/"&gt;(↗)&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;Backend Development&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://johanneskueber.com/tags/go"&gt;Golang&lt;/a&gt; &lt;a href="https://go.dev/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/kotlin/"&gt;Kotlin&lt;/a&gt; &lt;a href="https://kotlinlang.org/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/nodejs"&gt;Node.js&lt;/a&gt; &lt;a href="https://nodejs.org/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/java"&gt;Java&lt;/a&gt; &lt;a href="https://www.java.com/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/php"&gt;PHP&lt;/a&gt; &lt;a href="https://www.php.net/"&gt;(↗)&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;Hosting and Toolchains&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://johanneskueber.com/tags/k8s/"&gt;K8s&lt;/a&gt; &lt;a href="https://kubernetes.io/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/k3s"&gt;K3s&lt;/a&gt; &lt;a href="https://k3s.io/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/k3d"&gt;K3d&lt;/a&gt; &lt;a href="https://k3d.io/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/minikube"&gt;Minikube&lt;/a&gt; &lt;a href="https://minikube.sigs.k8s.io/docs/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/argocd"&gt;ArgoCD&lt;/a&gt; &lt;a href="https://argo-cd.readthedocs.io/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/fluxcd/"&gt;FluxCD&lt;/a&gt; &lt;a href="https://fluxcd.io/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/github"&gt;GitHub&lt;/a&gt; &lt;a href="https://github.com/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/github-actions"&gt;GitHub Actions&lt;/a&gt; &lt;a href="https://github.com/features/actions"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/ghcr"&gt;GHCR&lt;/a&gt; &lt;a href="https://github.com/features/packages"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/renovate/"&gt;Renovate&lt;/a&gt; &lt;a href="https://www.mend.io/renovate/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/docker/"&gt;Docker&lt;/a&gt; &lt;a href="https://www.docker.com/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/docker-swarm"&gt;Docker Swarm&lt;/a&gt; &lt;a href="https://docs.docker.com/engine/swarm/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/drone/"&gt;Drone.io&lt;/a&gt; &lt;a href="https://www.drone.io/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/gitea/"&gt;Gitea&lt;/a&gt; &lt;a href="https://about.gitea.com/"&gt;(↗)&lt;/a&gt;,&lt;br&gt;&#10;&lt;a href="https://johanneskueber.com/tags/cicd"&gt;CI/CD&lt;/a&gt; &lt;a href="https://en.wikipedia.org/wiki/CI/CD"&gt;(↗)&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;</description></item></channel></rss>