<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <generator uri="https://gohugo.io/" version="0.157.0">Hugo</generator>
    <title>the Geeklab</title>
        <subtitle>Linux and Open Source blog</subtitle>
            <link href="https://thegeeklab.de/" rel="alternate" type="text/html" title="html" />
            <link href="https://thegeeklab.de/atom.xml" rel="self" type="application/atom+xml" title="atom" />
            <link href="https://thegeeklab.de/feed.json" rel="alternate" type="application/json" title="Json" />
    <updated>2026-03-03T06:38:19+00:00</updated>
        <author>
            <name>Robert Kaussow</name>
                <email>mail@thegeeklab.de</email>
        </author>
    <id>https://thegeeklab.de/</id>
        <entry>
            <title>Manage Univention DNS with Terraform</title>
            <link href="https://thegeeklab.de/posts/2022/09/manage-univention-dns-with-terraform/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2022/09/manage-univention-dns-with-terraform/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2022-09-11T13:57:16+02:00</published>
            <updated>2025-11-04T19:49:38+00:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2022/09/manage-univention-dns-with-terraform/images/feature_hu_c4c32a1dd5787ab1.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;Manage Univention DNS with Terraform&#34; /&gt;&lt;br/&gt;&lt;p&gt;Using &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;/posts/2022/09/store-terraform-state-on-backblaze-s3/&#34;
&gt;Terraform&lt;/a&gt; is a great way to manage infrastructure as code. To map all the different types of resources in a deployment, Terraform uses plugins. Plugins are executable binaries written in Go that communicate with Terraform Core via an RPC interface. Each plugin provides an implementation for a specific service.&lt;/p&gt;
&lt;p&gt;Sometimes there is no specific plugin for a service, but if the service provides a REST API, the generic &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://registry.terraform.io/providers/Mastercard/restapi/latest/docs&#34;
&gt;REST API provider&lt;/a&gt; can be helpful. This time, I was looking for a way to manage DNS records on a Univention Corporate Server (UCS) using Terraform. The Univention Directory Manager (UDM) API is well documented and can be used with the RESP API provider, but there are a few minor pitfalls to be aware of.&lt;/p&gt;
&lt;p&gt;First of all, a basic provider configuration needs to be added to Terraform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Terraform&#34; data-lang=&#34;Terraform&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;provider&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;restapi&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;uri&lt;/span&gt;                   &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://univention.example.com/univention/udm/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;              &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;my_user&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;password&lt;/span&gt;              &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;secure-password&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;id_attribute&lt;/span&gt;          &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;dn&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;debug&lt;/span&gt;                 &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;create_returns_object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;accept&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ensure that a full URL to the UDM is used in the &lt;code&gt;uri&lt;/code&gt; parameter. If &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; are used, the Terraform provider adds a basic authentication header. Alternatively, it is also possible to set other authentication headers manually. This is required to use e.g. token-based authentication instead of username and password. Terraform requires a unique ID for all objects under control. Because manually managing unique IDs is somewhat tedious, it is preferred to let the API handle it whenever possible. In the case of Univention, a basic understanding of object management is required. On a UCS, DNS objects are stored in the OpenLDAP objects. As a distinguished name (DN) uniquely identifies an LDAP record, this attribute can be perfectly used as &lt;code&gt;id_attribute&lt;/code&gt; for the Terraform provider. But it is also important to set &lt;code&gt;create_returns_object=true&lt;/code&gt;. This option tells the provider that each create operation (POST) will return an object. As a result, for creation events, the provider will parse the returned data, use the &lt;code&gt;id_attribute&lt;/code&gt; field as a unique ID, and stores the data along with the ID in Terraforms internal data structures. To finalize the provider configuration, the accept header need to be set to &lt;code&gt;application/json&lt;/code&gt;, otherwise the API will return HTML that can not be parsed by the Terraform provider.&lt;/p&gt;
&lt;p&gt;With this configuration, the &lt;code&gt;restapi_object&lt;/code&gt; resource can now be used to manage DNS records:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Terraform&#34; data-lang=&#34;Terraform&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;resource&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;restapi_object&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;ucs_server&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/dns/host_record/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; jsonencode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;position&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;zoneName=example.com,cn=dns,dc=example,dc=com,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    &amp;#34;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;properties&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; : {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;      &amp;#34;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; : &amp;#34;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;my_host&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;      &amp;#34;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; : [
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        192.168.0.10
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;      ],
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  })
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/sysadmin" term="sysadmin" label="Sysadmin" /> 
                                <category scheme="https://thegeeklab.de/tags/automation" term="automation" label="Automation" />
        </entry>
        <entry>
            <title>Store Terraform State on Backblaze S3</title>
            <link href="https://thegeeklab.de/posts/2022/09/store-terraform-state-on-backblaze-s3/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2022/09/store-terraform-state-on-backblaze-s3/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2022-09-04T20:47:53+02:00</published>
            <updated>2025-11-04T19:49:38+00:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2022/09/store-terraform-state-on-backblaze-s3/images/feature_hu_5a6ea21de9cc52de.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;Store Terraform State on Backblaze S3&#34; /&gt;&lt;br/&gt;&lt;p&gt;Terraform is an open source infrastructure-as-code tool for creating, modifying, and extending infrastructure in a secure and predictable way. Terraform needs to store a state about the managed infrastructure and configuration. This state is used by Terraform to map real-world resources to your configuration and track metadata. By default, this state is stored in a local file, but it can also be stored remotely.&lt;/p&gt;
&lt;p&gt;Terraform supports multiple remote backend provider including S3. I already use Backblaze for backups and have had good experiences with it. Since Backblaze also provides an S3 Compatible API, I wanted to use it for Terraform. How to use S3 as a state backend is well documented, but as it&amp;rsquo;s focused on Amazon S3 there are a few things to take care of. A basic working configuration will look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Terraform&#34; data-lang=&#34;Terraform&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;terraform&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;backend&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;s3&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;bucket&lt;/span&gt;                      &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;my-bucket&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;key&lt;/span&gt;                         &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;state.json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;skip_credentials_validation&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;skip_region_validation&lt;/span&gt;      &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;endpoint&lt;/span&gt;                    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://s3.us-west-004.backblazeb2.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;region&lt;/span&gt;                      &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;us-west-004&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;access_key&lt;/span&gt;                  &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0041234567899990000000004&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;secret_key&lt;/span&gt;                  &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;K001abcdefgklmnopqrstuvw&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It is required to enable &lt;code&gt;skip_credentials_validation&lt;/code&gt; and &lt;code&gt;skip_region_validation&lt;/code&gt; because Backblaze uses a different format for these values and the validation only covers Amazon S3. For security reasons, I would recommend setting at least the &lt;code&gt;access_key&lt;/code&gt; and &lt;code&gt;secret_key&lt;/code&gt; parameters as environment variables instead of writing them to the Terraform file. For the credentials, it is required to create an App Key on Backblaze. After creating the Key the &lt;code&gt;keyName&lt;/code&gt; need to be used as &lt;code&gt;access_key&lt;/code&gt; and &lt;code&gt;applicationKey&lt;/code&gt; as &lt;code&gt;secret_key&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s basically all. If you want to go a step further, it is possible to save the state encrypted with &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://www.backblaze.com/docs/cloud-storage-server-side-encryption&#34;
&gt;Server-Side Encryption&lt;/a&gt; (SSE). There are two options available. The first is &lt;code&gt;SSE-B2&lt;/code&gt;, where the key is managed and stored by Backblaze, which is quiet simple to configure. The second option is to use customer managed keys. Using this option, an AES-256 and base64 encoded key is used. To generate a proper key, the command &lt;code&gt;openssl rand -base64 32&lt;/code&gt; can be used.&lt;/p&gt;
&lt;p&gt;To enable encryption in the Terraform Provider, the configuration must be extended:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Terraform&#34; data-lang=&#34;Terraform&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;terraform&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;backend&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;s3&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;encrypt&lt;/span&gt;                     &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;sse_customer_key&lt;/span&gt;            &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;fsRb1SXBjiUqBM0rw/YqvDixScWnDCZsK7BhnPTc93Y=&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/sysadmin" term="sysadmin" label="Sysadmin" /> 
                                <category scheme="https://thegeeklab.de/tags/automation" term="automation" label="Automation" />
        </entry>
        <entry>
            <title>Use Tradfri Shortcut Button with Home Assistant</title>
            <link href="https://thegeeklab.de/posts/2022/08/use-tradfri-shortcut-button-with-home-assistant/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2022/08/use-tradfri-shortcut-button-with-home-assistant/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2022-08-22T21:35:25+02:00</published>
            <updated>2025-11-04T19:49:38+00:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2022/08/use-tradfri-shortcut-button-with-home-assistant/images/feature_hu_77ee090c95d6a906.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;Use Tradfri Shortcut Button with Home Assistant&#34; /&gt;&lt;br/&gt;&lt;p&gt;Sometimes it can be helpful if single actions or entire automation in your Home Automation can be triggered with a physical button and not only from the Web UI. I&amp;rsquo;m using Zigbee2MQTT as a generic Zigbee to MQTT bridge as it supports a lot of different Zigbee devices and integrates flawlessly into Home Assistant. As I have a lot of different Tradfri Lamps in use already, I have bought a few of the Tradfri Shortcut Button (E1812).&lt;/p&gt;
&lt;p&gt;Pairing the Buttons with the &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://www.zigbee2mqtt.io&#34;
&gt;Zigbee2MQTT&lt;/a&gt; coordinator was simple as usual, but I had some trouble to figure out how to use it in &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://www.home-assistant.io/docs&#34;
&gt;Home Assistant&lt;/a&gt;. Zigbee2MQTT recommends to use the &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://www.zigbee2mqtt.io/guide/usage/integrations/home_assistant.html#via-mqtt-device-trigger-recommended&#34;
&gt;MQTT device triggers&lt;/a&gt; but the documentation wasn&amp;rsquo;t that clear on this part.&lt;/p&gt;
&lt;p&gt;The first thing to do is to figure out the available triggers of the device. Triggers are published to the MQTT topic &lt;code&gt;&amp;lt;discovery_prefix&amp;gt;/device_automation/&lt;/code&gt; and you can subscribe to is to discover the messages. To subscribe to an MQTT topic &lt;code&gt;mosquitto_sub&lt;/code&gt; CLI command can be used e.g. &lt;code&gt;mosquitto_sub -h 192.168.0.1 -p 8883 -u user -P secure-password -t homeassistant/device_automation/#&lt;/code&gt; or the Home Assistant UI at &lt;strong&gt;&lt;em&gt;Settings&lt;/em&gt;&lt;/strong&gt; / &lt;strong&gt;&lt;em&gt;Devices &amp;amp; Services&lt;/em&gt;&lt;/strong&gt; / &lt;!-- spellchecker-disable --&gt;&lt;strong&gt;&lt;em&gt;Integrations&lt;/em&gt;&lt;/strong&gt;&lt;!-- spellchecker-enable --&gt; / &lt;strong&gt;&lt;em&gt;MQTT&lt;/em&gt;&lt;/strong&gt; / &lt;strong&gt;&lt;em&gt;Configure&lt;/em&gt;&lt;/strong&gt; / &lt;strong&gt;&lt;em&gt;Listen to a topic&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;After subscribing to the topic, remember that device triggers are not published until the event has been triggered at least once on the device. Afterwards the following triggers should be listed for the Tradfri Button:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Json&#34; data-lang=&#34;Json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;automation_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;trigger&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;device&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;identifiers&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;zigbee2mqtt_0x84b112233445566&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;manufacturer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;IKEA&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;TRADFRI shortcut button (E1812)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;livingroom/switch_test&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;sw_version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2.3.080&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;payload&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;off&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;subtype&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;off&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;topic&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;zigbee2mqtt/livingroom/switch_test/action&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;automation_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;trigger&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;device&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;identifiers&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;zigbee2mqtt_0x84b112233445566&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;manufacturer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;IKEA&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;TRADFRI shortcut button (E1812)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;livingroom/switch_test&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;sw_version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2.3.080&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;payload&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;brightness_stop&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;subtype&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;brightness_stop&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;topic&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;zigbee2mqtt/livingroom/switch_test/action&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;automation_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;trigger&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;device&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;identifiers&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;zigbee2mqtt_0x84b112233445566&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;manufacturer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;IKEA&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;TRADFRI shortcut button (E1812)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;livingroom/switch_test&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;sw_version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2.3.080&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;payload&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;brightness_move_up&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;subtype&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;brightness_move_up&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;topic&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;zigbee2mqtt/livingroom/switch_test/action&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;automation_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;trigger&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;device&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;identifiers&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;zigbee2mqtt_0x84b112233445566&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;manufacturer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;IKEA&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;TRADFRI shortcut button (E1812)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;livingroom/switch_test&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;sw_version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2.3.080&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;payload&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;on&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;subtype&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;on&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;topic&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;zigbee2mqtt/livingroom/switch_test/action&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Great, with this data determined, it&amp;rsquo;s almost done. The only missing information is the &lt;code&gt;device_id&lt;/code&gt;. I have not found a nice way to get this ID yet, but it is part of the URL after navigating to &lt;strong&gt;&lt;em&gt;Settings&lt;/em&gt;&lt;/strong&gt; / &lt;strong&gt;&lt;em&gt;Devices &amp;amp; Services&lt;/em&gt;&lt;/strong&gt; / &lt;strong&gt;&lt;em&gt;Devices&lt;/em&gt;&lt;/strong&gt; / &lt;strong&gt;&lt;em&gt;&amp;lt;device&amp;gt;&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Finally an action can be assigned to a button. The example below is starting the Vacuum cleaner on a &lt;code&gt;single_click&lt;/code&gt; (&lt;code&gt;on&lt;/code&gt;) trigger:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-YAML&#34; data-lang=&#34;YAML&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;alias&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Start Vacuum&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;trigger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;platform&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;device&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;domain&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;mqtt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;device_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;61b011e2e7d5e111111d8d804a029f61&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;subtype&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;on&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;discovery_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0x84b112233445566&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;action_on&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;service&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;vacuum.start&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;entity_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;vacuum.valetudo_rockrobo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/automation" term="automation" label="Automation" />
        </entry>
        <entry>
            <title>How to (not) migrate Graylog to Opensearch</title>
            <link href="https://thegeeklab.de/posts/2022/07/how-to-not-migrate-graylog-to-opensearch/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2022/07/how-to-not-migrate-graylog-to-opensearch/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2022-07-14T14:45:51+02:00</published>
            <updated>2025-11-04T19:49:38+00:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2022/07/how-to-not-migrate-graylog-to-opensearch/images/feature_hu_6c75932ae55ed5a8.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;How to (not) migrate Graylog to Opensearch&#34; /&gt;&lt;br/&gt;&lt;p&gt;Graylog is a centralized log management solution to capture, store and analyze log files in real-time. Starting with the latest minor release 4.3 Graylog announced to no longer support Elasticsearch (ES) due to licensing and structural changes Elastic introduced in v7.11. For this reason, the last supported ES version is 7.10, which has already reached EOL on May 11, 2022.&lt;/p&gt;
&lt;p&gt;Fortunately, &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://go2docs.graylog.org/current/home.htm&#34;
&gt;Graylog&lt;/a&gt; also knows this and recommends users to switch even if it is currently not enforced and ES 7.10 continues to work for now &lt;!-- spellchecker-disable --&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;!-- spellchecker-enable --&gt;
. As you usually don&amp;rsquo;t wants to operate software that no longer receives security updates, I have started to look into a migration and prepared the Container and Ansible setup. My fist mistake on this journey was to believe I can just use the latest Opensearch (OS) release. Had I read the documentation &lt;!-- spellchecker-disable --&gt;&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;!-- spellchecker-enable --&gt;
more carefully I would have saved myself a lot of trouble&amp;hellip;&lt;/p&gt;
&lt;p&gt;Anyway, the actual migration of the cluster from &lt;!-- spellchecker-disable --&gt;ES v7.10 to OS v2.1&lt;!-- spellchecker-enable --&gt; succeeded surprisingly smoothly. Well, almost, after all I had to rewrite the complete Ansible role because OS 2.x has changed almost all configuration parameters and API calls &amp;#x1f389; But as you can imagine, everything explodes while trying to start Graylog again. Dang. Just downgrading Opensearch was also not possible as the cluster and all indices were migrated successfully already. To get it back in a working state I decided to reset the entire cluster and restore the snapshots from the S3 backup repository before I start to start a next try, this time with a supported OS 1.x version :fingers_crossed: At least I have already completed the ES disaster recovery test for this year.&lt;/p&gt;
&lt;p&gt;Lessons Learned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Read documentations/upgrade instructions more carefully&lt;/li&gt;
&lt;li&gt;Ensure to have a working backup&lt;/li&gt;
&lt;li&gt;Test your recovery process frequently to stay calm and comfortable in case of an emergency&lt;/li&gt;
&lt;li&gt;Test upgrades in a staging environment whenever possible&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What annoys me a bit about the whole situation is the back and forth and the rather bad communication in the past from Graylog &lt;!-- spellchecker-disable --&gt;&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;!-- spellchecker-enable --&gt;
. Furthermore, the situation with Opensearch is not really better, as it is unclear if e.g. version 1.3 is still supported or not and a general lifecycle information is still missing &lt;!-- spellchecker-disable --&gt;&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;!-- spellchecker-enable --&gt;
.&lt;/p&gt;
&lt;!-- spellchecker-disable --&gt;
&lt;!-- markdownlint-capture --&gt;
&lt;!-- markdownlint-disable --&gt;
&lt;!-- markdownlint-restore --&gt;
&lt;!-- spellchecker-enable --&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;&lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://graylog.org/post/graylog-to-add-support-for-opensearch/&#34;
&gt;https://graylog.org/post/graylog-to-add-support-for-opensearch/&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;&lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://go2docs.graylog.org/5-0/planning_your_deployment/upgrading_to_opensearch.htm&#34;
&gt;https://go2docs.graylog.org/5-0/planning_your_deployment/upgrading_to_opensearch.htm&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;&lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://github.com/Graylog2/graylog2-server/issues/11804&#34;
&gt;https://github.com/Graylog2/graylog2-server/issues/11804&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:4&#34;&gt;
&lt;p&gt;&lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://github.com/opensearch-project/project-website/issues/661&#34;
&gt;https://github.com/opensearch-project/project-website/issues/661&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/sysadmin" term="sysadmin" label="sysadmin" />
        </entry>
        <entry>
            <title>Collect JSON metrics with Telegraf</title>
            <link href="https://thegeeklab.de/posts/2022/03/collect-json-metrics-with-telegraf/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2022/03/collect-json-metrics-with-telegraf/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2022-03-13T12:10:00+01:00</published>
            <updated>2025-11-04T19:49:38+00:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2022/03/collect-json-metrics-with-telegraf/images/feature_hu_3ec6ec3d7db2a063.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;Collect JSON metrics with Telegraf&#34; /&gt;&lt;br/&gt;&lt;p&gt;Telegraf is a powerful, plugin based metrics collector that also provides Prometheus compatible outputs. For various purposes, there are a number of input plugins that can collect metrics from various sources. Even more powerful are the processor plugins that allow metrics to be processed and manipulated as they pass through, and immediately output results based on the values they process. In this short blog post I&amp;rsquo;ll explain how to fetch JSON metrics from the Docker registry API to track some data of a DockerHub Repository.&lt;/p&gt;
&lt;p&gt;Even though Prometheus has become very popular, not all applications and API&amp;rsquo;s provide native Prometheus metrics. In this example, I&amp;rsquo;ll focus on the repository metrics provided by &lt;code&gt;https://hub.docker.com/v2/repositories&lt;/code&gt;. While the API displays the current pull numbers, storing this information to Prometheus has the advantage of displaying time-based information, e.g. the pull frequency/ratio or pulls in a certain period of time.&lt;/p&gt;
&lt;p&gt;To fetch metrics from an HTTP endpoint, the generic &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://github.com/influxdata/telegraf/tree/master/plugins/inputs/http&#34;
&gt;HTTP input plugin&lt;/a&gt; can be used. This plugin allows collecting metrics from one or more HTTP(S) endpoints and also supports a lot of different &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md&#34;
&gt;data formats&lt;/a&gt; not only &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://github.com/influxdata/telegraf/tree/master/plugins/parsers/json&#34;
&gt;JSON&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By default, only numeric values are extracted from the collected JSON data. In the case of the Docker Registry API endpoint, the status, star_count, pull_count, and collaborator_count fields are used. This behavior can be customized by adding fields for the &lt;code&gt;json_string_fields&lt;/code&gt; configuration option. Since Prometheus supports only numeric values for metrics, string fields are added as labels to each metric. In some cases, string fields contain useful information that can also be converted to numeric references. For example, status texts such as &lt;code&gt;OK&lt;/code&gt; and &lt;code&gt;ERROR&lt;/code&gt; can be converted to &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt; and used as metrics in Prometheus. In the case of the Docker Registry API, I wanted to use the &lt;code&gt;last_updated&lt;/code&gt; field as a dedicated metric in Prometheus instead of a label. Fortunately, the date time format used can be transformed to a numeric value by converting it to a Unix timestamp.&lt;/p&gt;
&lt;p&gt;For simple type conversions, I would recommend to always check the available &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://github.com/influxdata/telegraf/tree/master/plugins/processors/converter&#34;
&gt;converter processor&lt;/a&gt; first. Sadly there is no such converter for date or date time values available yet, so I had to go another way and utilized the starlark processor. This processor calls a starlark function for each matched metric, allowing for custom programmatic metric processing. While starlark is a Python dialect and might look familiar, it only supports a very limited subset of the language, as explained in the &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://github.com/google/starlark-go/blob/master/doc/spec.md&#34;
&gt;specification&lt;/a&gt;. But it&amp;rsquo;s powerful enough for what I wanted to do: Convert the date time value in the format of &lt;code&gt;2006-01-02T15:04:05.999999Z&lt;/code&gt; to a valid Unix timestamp. That&amp;rsquo;s how the final configuration looks like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-TOML&#34; data-lang=&#34;TOML&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;#jinja2: lstrip_blocks: True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;inputs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;name_override&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;dockerhub_repository&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;urls&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://hub.docker.com/v2/repositories/library/telegraf/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;json_string_fields&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;last_updated&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;namespace&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;repository_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;data_format&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;processors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;starlark&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;namepass&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;dockerhub_repository&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;source&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;load(&amp;#34;time.star&amp;#34;, &amp;#34;time&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;def apply(metric):
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;  metric.fields[&amp;#34;last_updated&amp;#34;] = time.parse_time(metric.fields[&amp;#34;last_updated&amp;#34;], format=&amp;#34;2006-01-02T15:04:05.999999Z&amp;#34;).unix
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;  return metric
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To debug and test the Telegraf configuration it&amp;rsquo;s useful to execute the binary with the &lt;code&gt;--test&lt;/code&gt; flag:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Plain&#34; data-lang=&#34;Plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./usr/bin/telegraf --debug --test --config etc/telegraf/telegraf.conf --config-directory etc/telegraf/telegraf.d/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2022-03-12T17:01:07Z I! Starting Telegraf 1.21.4
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2022-03-12T17:01:07Z I! Loaded inputs: http
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2022-03-12T17:01:07Z I! Loaded aggregators:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2022-03-12T17:01:07Z I! Loaded processors: starlark
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2022-03-12T17:01:07Z W! Outputs are not used in testing mode!
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2022-03-12T17:01:07Z I! Tags enabled: host=localhost project=prometheus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[...]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; dockerhub_repository,host=localhost,name=telegraf,namespace=library,project=prometheus,repository_type=image,url=https://hub.docker.com/v2/repositories/library/telegraf/ collaborator_count=0,last_updated=1646267076i,pull_count=519804664,star_count=560,status=1 1647104469000000000
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The final metrics generated by the Prometheus output plugin will look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Plain&#34; data-lang=&#34;Plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dockerhub_repository_collaborator_count{host=&amp;#34;localhost&amp;#34;,name=&amp;#34;telegraf&amp;#34;,namespace=&amp;#34;library&amp;#34;,project=&amp;#34;prometheus&amp;#34;,repository_type=&amp;#34;image&amp;#34;,url=&amp;#34;https://hub.docker.com/v2/repositories/library/telegraf/&amp;#34;} 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dockerhub_repository_last_updated{host=&amp;#34;localhost&amp;#34;,name=&amp;#34;telegraf&amp;#34;,namespace=&amp;#34;library&amp;#34;,project=&amp;#34;prometheus&amp;#34;,repository_type=&amp;#34;image&amp;#34;,url=&amp;#34;https://hub.docker.com/v2/repositories/library/telegraf/&amp;#34;} 1.646267076e+09
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dockerhub_repository_pull_count{host=&amp;#34;localhost&amp;#34;,name=&amp;#34;telegraf&amp;#34;,namespace=&amp;#34;library&amp;#34;,project=&amp;#34;prometheus&amp;#34;,repository_type=&amp;#34;image&amp;#34;,url=&amp;#34;https://hub.docker.com/v2/repositories/library/telegraf/&amp;#34;} 5.19802966e+08
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dockerhub_repository_star_count{host=&amp;#34;localhost&amp;#34;,name=&amp;#34;telegraf&amp;#34;,namespace=&amp;#34;library&amp;#34;,project=&amp;#34;prometheus&amp;#34;,repository_type=&amp;#34;image&amp;#34;,url=&amp;#34;https://hub.docker.com/v2/repositories/library/telegraf/&amp;#34;} 560
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dockerhub_repository_status{host=&amp;#34;localhost&amp;#34;,name=&amp;#34;telegraf&amp;#34;,namespace=&amp;#34;library&amp;#34;,project=&amp;#34;prometheus&amp;#34;,repository_type=&amp;#34;image&amp;#34;,url=&amp;#34;https://hub.docker.com/v2/repositories/library/telegraf/&amp;#34;} 1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. The Telegraf HTTP input plugin is a very flexible but generic way to collect and transform JSON metrics from various sources. If that&amp;rsquo;s still not powerful enough you can pass the raw data fetched by the HTTP input plugin to the starlark converter and write your own functions to parse the input and extract the required information into metrics.&lt;/p&gt;
            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/sysadmin" term="sysadmin" label="Sysadmin" />
        </entry>
        <entry>
            <title>SSL certificate monitoring pitfalls</title>
            <link href="https://thegeeklab.de/posts/2022/01/ssl-certificate-monitoring-pitfalls/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2022/01/ssl-certificate-monitoring-pitfalls/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2022-01-31T23:00:00+01:00</published>
            <updated>2025-11-04T19:49:38+00:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2022/01/ssl-certificate-monitoring-pitfalls/images/feature_hu_fc5288f8d82788cc.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;SSL certificate monitoring pitfalls&#34; /&gt;&lt;br/&gt;&lt;p&gt;Certificates are a fundamental part of the Internet&amp;rsquo;s security. At least since Let&amp;rsquo;s Encrypt, a free and automated Certificate Authority, has started its service, SSL is nearly used everywhere. To avoid Certificate issues and possible service outages, it&amp;rsquo;s a good idea to monitor the SSL certificates used by your services, especially as Let&amp;rsquo;s Encrypt certificates have a short lease time of 90 days.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m using Prometheus to monitor my infrastructure, and for Prometheus there are multiple ways to get started. Most of the tutorials and posts of the internet will cover the case of expired certificates, and it&amp;rsquo;s pretty easy to achieve. I prefer to use Telegraf, a plugin based metrics collector that also provides Prometheus compatible outputs, instead of dedicated Prometheus exporters. To monitor SSL certificates, I&amp;rsquo;m using the &lt;code&gt;x509_cert&lt;/code&gt; input plugin of Telegraf that provides a metric called &lt;code&gt;x509_cert_expiry&lt;/code&gt; which can be utilized to write simple alerting rules. That&amp;rsquo;s actually pretty cool already, as Prometheus will send out alerts a few weeks before the certificates would expire in case there is a problem within the automatic renewal process.&lt;/p&gt;
&lt;p&gt;A week ago, Let&amp;rsquo;s Encrypt has informed affected users that they need to &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://community.letsencrypt.org/t/questions-about-renewing-before-tls-alpn-01-revocations/170449&#34;
&gt;revoke faulty certificates&lt;/a&gt; issued and validated with the &lt;code&gt;TLS-ALPN-01&lt;/code&gt; challenge. Even if I&amp;rsquo;m using the &lt;code&gt;DNS-01&lt;/code&gt; for almost all of my certificates, I have also received a mail and started to look into it. Sadly, the notification mail only contained a &amp;ldquo;random&amp;rdquo; ACME registration ID, and I was not able to find the matching client. As mentioned, I don&amp;rsquo;t really use &lt;code&gt;TLS-ALPN-01&lt;/code&gt;, so I decided to stop the research and leave it to my monitoring to tell me which forgotten service is the evil one after the certificates were revoked. Nothing happened after the revocation, and the monitoring was not complaining. Good - well no, a user reported that one of the services is not reachable anymore and of course this was the one missing client that was using &lt;code&gt;TLS-ALPN-01&lt;/code&gt; verified certificates - dang. While the issue itself was easy to resolve by a force renew of the certificate, I was still wondering why the monitoring has not caught it.&lt;/p&gt;
&lt;p&gt;Well, this was the first time that I had to deal with &lt;em&gt;revoked&lt;/em&gt; certificates instead of &lt;em&gt;expired&lt;/em&gt; certificates. To be honest, I never thought about the detection of revoked certificates in my monitoring setup before, and therefore this case wasn&amp;rsquo;t covered. But it looks like a fix is also not that straight forward as expected. The used Telegraf input &lt;code&gt;x509_cert&lt;/code&gt; is not able to detect revoked certificates yet, and the common Prometheus &lt;a
  class=&#34;gblog-markdown__link--code&#34;
  href=&#34;https://github.com/prometheus/blackbox_exporter/issues/6&#34;
&gt;&lt;code&gt;blackbox_exporter&lt;/code&gt;&lt;/a&gt; also don&amp;rsquo;t want to handle this case. The only way I have found so far is to use the &lt;a
  class=&#34;gblog-markdown__link--code&#34;
  href=&#34;https://github.com/ribbybibby/ssl_exporter&#34;
&gt;&lt;code&gt;ssl_exporter&lt;/code&gt;&lt;/a&gt; that provides some revocation information of the certificates using OSCP. If you are already running multiple exporters, that might be the way to go for you. Personally, I prefer to handle as much as possible using Telegraf, so I might look into a &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://github.com/influxdata/telegraf/issues/10550&#34;
&gt;fix&lt;/a&gt; for the &lt;code&gt;x509_cert&lt;/code&gt; during the next weeks. However, lessons learned &amp;#x1f4d8;&lt;/p&gt;

            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/sysadmin" term="sysadmin" label="Sysadmin" /> 
                                <category scheme="https://thegeeklab.de/tags/today-i-learned" term="today-i-learned" label="Today I learned" />
        </entry>
        <entry>
            <title>Toolbox 2: git-plus</title>
            <link href="https://thegeeklab.de/posts/2021/06/toolbox-2-git-plus/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2021/06/toolbox-2-git-plus/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2021-06-19T09:50:00+01:00</published>
            <updated>2025-11-04T19:49:38+00:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2021/06/toolbox-2-git-plus/images/feature_hu_86445d6d71c3978.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;Toolbox 2: git-plus&#34; /&gt;&lt;br/&gt;&lt;p&gt;If you work with a lot of Git repositories on a regular basis, you&amp;rsquo;re bound to run into the situation where you need to make changes to multiple repositories sooner or later. While it would be possible to run your Git commands in a shell loop over everything repositories, it is often tedious to type the command or remember the correct syntax.&lt;/p&gt;
&lt;p&gt;This is where &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://github.com/tkrajina/git-plus&#34;
&gt;git-plus&lt;/a&gt; comes into play. It is a small collection of Git utilities that tries to simplify some common tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git multi&lt;/code&gt; execute a single Git command on multiple Git repositories&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git relation&lt;/code&gt; show a relation between two branches/commits/tags&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git old-branches&lt;/code&gt; find old/unused branches&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git recent&lt;/code&gt; list branches ordered by last commit time&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git semver&lt;/code&gt; lists and creates Git semver (semantic versioning) tags&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But there is one command that I use every day now, &lt;code&gt;git multi&lt;/code&gt;. The command works folder based, that means you have to have all target repositories in one directory. There is no grouping feature at the moment, if you want to group some repositories you would have to use multiple sub-directories. Since I also use &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;/posts/toolbox-2-git-plus/&#34;
&gt;direnv&lt;/a&gt;, this folder-based workflow works very well for me. Repositories can be temporarily excluded with the CLI flag &lt;code&gt;-e reponame&lt;/code&gt; or by a &lt;code&gt;.multigit_ignore&lt;/code&gt; file in the parent directory. Basically &lt;code&gt;git multi&lt;/code&gt; executes normal Git commands and is therefore quite easy to use. One example where the tool helps me is when I need to adjust the CI configuration in multiple repositories, for example. Only a few steps are needed for this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new branch: &lt;br&gt;
&lt;code&gt;git multi checkout -b &amp;quot;replace-ci-step&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Use your favorite search and replace tool to add your changes to all repositories&lt;/li&gt;
&lt;li&gt;Create a commit: &lt;br&gt;
&lt;code&gt;git multi add -A; git multi commit -m &amp;quot;ci: replace broken step in ci config&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Finally push your new branch: &lt;br&gt;
&lt;code&gt;git multi push origin replace-ci-step&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&amp;rsquo;s it. Simple, isn&amp;rsquo;t it? Of course it doesn&amp;rsquo;t work in all cases, especially for more complex and repository specific changes it&amp;rsquo;s getting harder, but for generic changes like CI configurations, the current year in a copyright string or a license file or globally used badges in the readme it&amp;rsquo;s really straight forward.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git-plus&lt;/code&gt; is written in Python and available on &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://pypi.org/project/git-plus/&#34;
&gt;PyPi&lt;/a&gt;.&lt;/p&gt;
            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/sysadmin" term="sysadmin" label="Sysadmin" /> 
                                <category scheme="https://thegeeklab.de/tags/automation" term="automation" label="Automation" />
        </entry>
        <entry>
            <title>Toolbox 1: direnv</title>
            <link href="https://thegeeklab.de/posts/2021/05/toolbox-1-direnv/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2021/05/toolbox-1-direnv/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2021-05-17T21:24:00+01:00</published>
            <updated>2025-11-04T19:49:38+00:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2021/05/toolbox-1-direnv/images/feature_hu_86445d6d71c3978.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;Toolbox 1: direnv&#34; /&gt;&lt;br/&gt;&lt;p&gt;We all use many different tools every day e.g. for our work, automation or better productivity. In the series &amp;ldquo;Toolbox&amp;rdquo; I would like to present such applications that have made my day-to-day work so much easier. All applications are free and open source software developed by big tech companies as well as lovingly handcrafted hobby projects. If you also know an awesome tool that has changed your life, I would love to hear from you on &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://social.tchncs.de/@xoxys&#34;
&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;flex align-center gblog-post__anchorwrap&#34;&gt;
    &lt;h2 id=&#34;direnv&#34;
    &gt;
        direnv
    &lt;/h2&gt;
    &lt;a data-clipboard-text=&#34;https://thegeeklab.de/posts/2021/05/toolbox-1-direnv/#direnv&#34; class=&#34;gblog-post__anchor clip flex align-center&#34; aria-label=&#34;Anchor direnv&#34; href=&#34;#direnv&#34;&gt;
        &lt;svg class=&#34;gblog-icon gblog_link&#34;&gt;&lt;use xlink:href=&#34;#gblog_link&#34;&gt;&lt;/use&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;The basic function of &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://direnv.net/&#34;
&gt;direnv&lt;/a&gt; is pretty simple, it manages environment variables depending on the current directory you are in. This may sound not very helpful, but it could save a lot of work, especially if you have to deal with many different project environments. I use it a lot combined with Ansible. Depending on the deployment environment I need to set a different remote user, also the roles should be loaded from a different base directory depending on whether it is a test or production environment.&lt;/p&gt;
&lt;p&gt;As long as the required configuration can be set by an environment variable, direnv can handle it. All you have to do is to create a &lt;code&gt;.envrc&lt;/code&gt; file in the directory where it should be loaded. A simple configuration could look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Shell&#34; data-lang=&#34;Shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ cat .envrc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ANSIBLE_ROLES_PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/home/xoxys/devel/.roles/staging
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ANSIBLE_REMOTE_USER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;project-1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each time you create or modify an &lt;code&gt;.envrc&lt;/code&gt; file, you must approve the modification by running &lt;code&gt;direnv allow&lt;/code&gt;. This prevents direnv from loading a file that has been modified or created by another malicious process, for example. After you have approved the file, direnv will automatically load it when you navigate to the directory where your configuration is located. To make it even better, it doesn&amp;rsquo;t have to be the root directory, the file will also be loaded if you navigate directly to a sub-directory. When you leave the directory, your environment will also be unloaded automatically.&lt;/p&gt;
&lt;p&gt;Direnv is written in Golang and available in most Linux Distribution &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://direnv.net/docs/installation.html#from-system-packages&#34;
&gt;repositories&lt;/a&gt;. Binary builds can be downloaded from the &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://github.com/direnv/direnv/releases&#34;
&gt;GitHub Release&lt;/a&gt; page as well.&lt;/p&gt;

            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/sysadmin" term="sysadmin" label="Sysadmin" /> 
                                <category scheme="https://thegeeklab.de/tags/automation" term="automation" label="Automation" />
        </entry>
        <entry>
            <title>Run an ARM32 Docker daemon on ARM64 servers</title>
            <link href="https://thegeeklab.de/posts/2020/09/run-an-arm32-docker-daemon-on-arm64-servers/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2020/09/run-an-arm32-docker-daemon-on-arm64-servers/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2020-09-24T10:30:00+02:00</published>
            <updated>2025-11-04T19:49:38+00:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2020/09/run-an-arm32-docker-daemon-on-arm64-servers/images/feature_hu_d038852b7e1d3540.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;Run an ARM32 Docker daemon on ARM64 servers&#34; /&gt;&lt;br/&gt;&lt;p&gt;In the last days I worked on a suitable setup for a &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://drone.io/&#34;
&gt;Drone CI&lt;/a&gt; server to support multi-arch builds. While the setup for common x86 Drone runners is easy, working with setups for ARM, especially ARM32, is a bit tricky. The easiest way would be to have native servers of the respective architecture available. However, it&amp;rsquo;s difficult to find hosting offers for ARM at all - for ARM32 this seems almost impossible. I decided to use Amazon EC2 ARM64 servers, they are relatively cheap and can also be used as a private customer.&lt;/p&gt;
&lt;p&gt;But how do you turn an ARM64 server into an ARM32 server? Basic requirement is an ARMv8 CPU. This type supports (in most cases) both &lt;code&gt;AArch32&lt;/code&gt; and &lt;code&gt;AArch64&lt;/code&gt;. The first step is to enable multi-arch support on your operating system. I use Ubuntu 18.10 for this setup:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Shell&#34; data-lang=&#34;Shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dpkg --add-architecture armhf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then add the Docker CE repository for ARM32 and install the packages:&lt;/p&gt;
&lt;!-- prettier-ignore-start --&gt;
&lt;!-- markdownlint-disable --&gt;
&lt;!-- spellchecker-disable --&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# add GPG key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -fsSL https://download.docker.com/linux/ubuntu/gpg &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo apt-key add -
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# add repos&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;add-apt-repository &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;s2&#34;&gt;&amp;#34;deb [arch=armhf] https://download.docker.com/linux/ubuntu/ \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$RELEASE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;   stable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;# install
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;apt-get install docker-ce:armhf&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;!-- spellchecker-enable --&gt;
&lt;!-- markdownlint-restore --&gt;
&lt;!-- prettier-ignore-end --&gt;
&lt;p&gt;To use the right architecture within Docker containers you still have to force the Docker daemon into ARM32 mode. This can be accomplished by two systemd overwrites:&lt;/p&gt;
&lt;!-- prettier-ignore-start --&gt;
&lt;!-- markdownlint-disable --&gt;
&lt;!-- spellchecker-disable --&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# overwrite docker.service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# /etc/systemd/system/docker.service.d/override.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Service&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ExecStart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ExecStart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/usr/bin/setarch linux32 -B /usr/bin/dockerd -H fd:// --containerd&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/run/containerd/containerd.sock
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# overwrite containerd.service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# /etc/systemd/system/containerd.service.d/override.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Service&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ExecStart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ExecStart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/usr/bin/setarch linux32 -B /usr/bin/containerd&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;!-- spellchecker-enable --&gt;
&lt;!-- markdownlint-restore --&gt;
&lt;!-- prettier-ignore-end --&gt;
&lt;p&gt;That&amp;rsquo;s it. Don&amp;rsquo;t forget to reload and restart the systemd daemon:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Shell&#34; data-lang=&#34;Shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl, daemon reload
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl, restart, docker
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What you get is a Docker daemon that runs in ARM32 mode. This can be used for example to start a Golang container and build apps without cross-compile.&lt;/p&gt;
&lt;!-- prettier-ignore-start --&gt;
&lt;!-- markdownlint-disable --&gt;
&lt;!-- spellchecker-disable --&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@ip-10-0-225-151:~# docker version
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Client: Docker Engine - Community
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; Version:           19.03.13
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; API version:       1.40
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; Go version:        go1.13.15
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; Git commit:        4484c46
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; Built:             Wed Sep &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt; 17:07:23 &lt;span class=&#34;m&#34;&gt;2020&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; OS/Arch:           linux/arm
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; Experimental:      &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Server: Docker Engine - Community
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; Engine:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Version:          19.03.13
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  API version:      1.40 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;minimum version 1.12&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Go version:       go1.13.15
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Git commit:       4484c46
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Built:            Wed Sep &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt; 17:01:08 &lt;span class=&#34;m&#34;&gt;2020&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  OS/Arch:          linux/arm
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Experimental:     &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; containerd:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Version:          1.3.7
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  GitCommit:        8fba4e9a7d01810a393d5d25a3621dc101981175
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; runc:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Version:          1.0.0-rc10
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; docker-init:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Version:          0.18.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  GitCommit:        fec3683
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@ip-10-0-225-151:~# docker run alpine uname -a
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Linux dbad5ddeb5ea 5.3.0-1035-aws &lt;span class=&#34;c1&#34;&gt;#37-Ubuntu SMP Sun Sep 6 01:17:41 UTC 2020 armv8l Linux&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;!-- spellchecker-enable --&gt;
&lt;!-- markdownlint-restore --&gt;
&lt;!-- prettier-ignore-end --&gt;
&lt;p&gt;Just a word of warning. I&amp;rsquo;m not a hardware specialist and there might be some situations where this setup does not work. Furthermore it seems that some operating systems have problems to recognize &lt;code&gt;armv8l&lt;/code&gt; as ARM32 architecture. For OpenSuse as an example I had to force &lt;code&gt;zypper&lt;/code&gt; into &lt;code&gt;armv7hl&lt;/code&gt; mode to get it working. After this step building ARM32 OpenSuse Docker images also works for me.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Shell&#34; data-lang=&#34;Shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sed -i &lt;span class=&#34;s1&#34;&gt;&amp;#39;s/# arch = s390/arch = armv7hl/g&amp;#39;&lt;/span&gt; /etc/zypp/zypp.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you want to give it a try, I&amp;rsquo;ve prepared a minimal Cloud-Init configuration:&lt;/p&gt;
&lt;!-- prettier-ignore-start --&gt;
&lt;!-- markdownlint-disable --&gt;
&lt;!-- spellchecker-disable --&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;#cloud-config&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;apt_reboot_if_required&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;package_update&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;package_upgrade&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;bootcmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;dpkg, --add-architecture, armhf  ]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;apt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;sources&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;docker.list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;deb [arch=armhf] https://download.docker.com/linux/ubuntu/ $RELEASE stable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;keyid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;0EBFCD88&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;packages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;s1&#34;&gt;&amp;#39;docker-ce:armhf&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;write_files&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/etc/systemd/system/docker.service.d/override.conf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;      [Service]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;      ExecStart=
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;      ExecStart=/usr/bin/setarch linux32 -B /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/etc/systemd/system/containerd.service.d/override.conf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;      [Service]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;      ExecStart=
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;      ExecStart=/usr/bin/setarch linux32 -B /usr/bin/containerd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;runcmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;systemctl, daemon-reload ]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;systemctl, restart, docker ]&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;!-- spellchecker-enable --&gt;
&lt;!-- markdownlint-restore --&gt;
&lt;!-- prettier-ignore-end --&gt;
            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/automation" term="automation" label="Automation" /> 
                                <category scheme="https://thegeeklab.de/tags/container" term="container" label="Container" /> 
                                <category scheme="https://thegeeklab.de/tags/sysadmin" term="sysadmin" label="Sysadmin" />
        </entry>
        <entry>
            <title>Color palettes on Game Boy Color and Advance</title>
            <link href="https://thegeeklab.de/posts/2020/09/color-palettes-on-game-boy-color-and-advance/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2020/09/color-palettes-on-game-boy-color-and-advance/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2020-09-15T21:45:00+02:00</published>
            <updated>2025-06-09T11:30:31+02:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2020/09/color-palettes-on-game-boy-color-and-advance/images/feature_hu_a28d99899ba89a1e.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;Color palettes on Game Boy Color and Advance&#34; /&gt;&lt;br/&gt;&lt;p&gt;In my last article about &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;/posts/modernize-a-game-boy-advance/&#34;
&gt;modernizing a Game Boy Advance&lt;/a&gt;, one of my complaints was the ugly coloring of old Game Boy Classic games and I thought about buying an additional Game Boy Classic. But first I wanted to check if there was a mod for the Game Boy Advance to fix this problem.&lt;/p&gt;
&lt;p&gt;Well, what I found out after a short research was mind-blowing for me! The gorgeous engineers at Nintendo had already integrated a function into the Game Boy Color and the Game Boy Advance to choose between 12 color palettes.&lt;/p&gt;
&lt;!-- markdownlint-capture --&gt;
&lt;!-- markdownlint-disable MD033 --&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Color&lt;/th&gt;
          &lt;th&gt;Shortcut&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;BROWN &lt;svg width=&#34;20&#34; height=&#34;12&#34;&gt;&lt;rect width=&#34;20&#34; height=&#34;12&#34; style=&#34;fill:#fac08f&#34; /&gt;&lt;/svg&gt;&lt;/td&gt;
          &lt;td&gt;UP&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;BLUE &lt;svg width=&#34;20&#34; height=&#34;12&#34;&gt;&lt;rect width=&#34;20&#34; height=&#34;12&#34; style=&#34;fill:#8db3e2&#34; /&gt;&lt;/svg&gt;&lt;/td&gt;
          &lt;td&gt;LEFT&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;PASTEL &lt;svg width=&#34;20&#34; height=&#34;12&#34;&gt;&lt;rect width=&#34;20&#34; height=&#34;12&#34; style=&#34;fill:#e5e0ec&#34; /&gt;&lt;/svg&gt;&lt;/td&gt;
          &lt;td&gt;DOWN&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;GREEN &lt;svg width=&#34;20&#34; height=&#34;12&#34;&gt;&lt;rect width=&#34;20&#34; height=&#34;12&#34; style=&#34;fill:#76923c&#34; /&gt;&lt;/svg&gt;&lt;/td&gt;
          &lt;td&gt;RIGHT&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;RED &lt;svg width=&#34;20&#34; height=&#34;12&#34;&gt;&lt;rect width=&#34;20&#34; height=&#34;12&#34; style=&#34;fill:#c0504d&#34; /&gt;&lt;/svg&gt;&lt;/td&gt;
          &lt;td&gt;UP + A&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;DARK BLUE &lt;svg width=&#34;20&#34; height=&#34;12&#34;&gt;&lt;rect width=&#34;20&#34; height=&#34;12&#34; style=&#34;fill:#366092&#34; /&gt;&lt;/svg&gt;&lt;/td&gt;
          &lt;td&gt;LEFT + A&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;ORANGE &lt;svg width=&#34;20&#34; height=&#34;12&#34;&gt;&lt;rect width=&#34;20&#34; height=&#34;12&#34; style=&#34;fill:#f79646&#34; /&gt;&lt;/svg&gt;&lt;/td&gt;
          &lt;td&gt;DOWN + A&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;DARK GREEN &lt;svg width=&#34;20&#34; height=&#34;12&#34;&gt;&lt;rect width=&#34;20&#34; height=&#34;12&#34; style=&#34;fill:#4f6128&#34; /&gt;&lt;/svg&gt;&lt;/td&gt;
          &lt;td&gt;RIGHT + A&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;DARK BROWN &lt;svg width=&#34;20&#34; height=&#34;12&#34;&gt;&lt;rect width=&#34;20&#34; height=&#34;12&#34; style=&#34;fill:#974806&#34; /&gt;&lt;/svg&gt;&lt;/td&gt;
          &lt;td&gt;UP + B&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;GRAY SCALE &lt;svg width=&#34;20&#34; height=&#34;12&#34;&gt;&lt;rect width=&#34;20&#34; height=&#34;12&#34; style=&#34;fill:#bfbfbf&#34; /&gt;&lt;/svg&gt;&lt;/td&gt;
          &lt;td&gt;LEFT + B&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;YELLOW &lt;svg width=&#34;20&#34; height=&#34;12&#34;&gt;&lt;rect width=&#34;20&#34; height=&#34;12&#34; style=&#34;fill:#ffe694&#34; /&gt;&lt;/svg&gt;&lt;/td&gt;
          &lt;td&gt;DOWN + B&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;NEGATIVE &lt;svg width=&#34;20&#34; height=&#34;12&#34;&gt;&lt;rect width=&#34;20&#34; height=&#34;12&#34; style=&#34;fill:#ffffff&#34; /&gt;&lt;/svg&gt;&lt;/td&gt;
          &lt;td&gt;RIGHT + B&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;!-- markdownlint-restore --&gt;
&lt;!-- spellchecker-disable --&gt;
&lt;div class=&#34;flex justify-center&#34;&gt;
    &lt;figure class=&#34;gblog-post__figure&#34;&gt;
      &lt;a class=&#34;gblog-markdown__link--raw&#34; href=&#34;https://thegeeklab.de/posts/2020/09/color-palettes-on-game-boy-color-and-advance/images/color-palette-example.jpg&#34;&gt;
        &lt;picture&gt;
            &lt;source
                srcset=&#34;https://thegeeklab.de/posts/2020/09/color-palettes-on-game-boy-color-and-advance/images/color-palette-example_hu_c7bee175c696a5de.jpg 600w, https://thegeeklab.de/posts/2020/09/color-palettes-on-game-boy-color-and-advance/images/color-palette-example_hu_15065ee9d1f280e8.jpg 1200w&#34; sizes=&#34;100vw&#34;
            /&gt;
          &lt;img loading=&#34;lazy&#34;
                src=&#34;https://thegeeklab.de/posts/2020/09/color-palettes-on-game-boy-color-and-advance/images/color-palette-example_hu_db2e7cf093f0a0e9.jpg&#34;
              alt=&#34;Comparison between default color (left) and gray scale (right).&#34;
          /&gt;
        &lt;/picture&gt;
      &lt;/a&gt;
        &lt;figcaption&gt;
          Comparison between default color (left) and gray scale (right).
        &lt;/figcaption&gt;
    &lt;/figure&gt;
  &lt;/div&gt;

&lt;!-- spellchecker-enable --&gt;
&lt;p&gt;Today I learned about a great feature that I&amp;rsquo;ve never known in all the years of my childhood. But there is still a small downside: Changing the color palette works only for GB classic games.&lt;/p&gt;
            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/gaming" term="gaming" label="Gaming" /> 
                                <category scheme="https://thegeeklab.de/tags/hardware" term="hardware" label="Hardware" /> 
                                <category scheme="https://thegeeklab.de/tags/today-i-learned" term="today-i-learned" label="Today I learned" />
        </entry>
        <entry>
            <title>How to modernize a Game Boy</title>
            <link href="https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2020-09-13T23:45:00+02:00</published>
            <updated>2025-11-04T19:49:38+00:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/images/feature_hu_a28d99899ba89a1e.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;How to modernize a Game Boy&#34; /&gt;&lt;br/&gt;&lt;p&gt;Sometimes it is quite nice to revel in memories. Back to the good old days of our childhood where life was so easy. At least I have those retro feelings from time to time. This time I decided to follow these feelings and take back a part of my childhood. I wanted a Game Boy again. A real Game Boy, not a fake one or any of those emulator handhelds.&lt;/p&gt;
&lt;p&gt;The first consideration was which model to choose: Game Boy (GB), Game Boy Color (GBC) or Game Boy Advance/SP (GBA)? From a practical point of view, the Game Boy Advance is the most worthwhile. It is backwards compatible and can play GB and GBC games as well as GBA games. I&amp;rsquo;ve never been a fan of the form factor of the SP version, so I decided to go for the classic model. Regardless of my retro feelings, the Game Boy has its weaknesses, which are simply no longer up to date. Even if you can argue that this is part of the feeling, that&amp;rsquo;s a bit too much for me.&lt;/p&gt;
&lt;!-- spellchecker-disable --&gt;
&lt;div class=&#34;flex justify-center&#34;&gt;
    &lt;figure class=&#34;gblog-post__figure&#34;&gt;
      &lt;a class=&#34;gblog-markdown__link--raw&#34; href=&#34;https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/images/parts-overview.jpg&#34;&gt;
        &lt;picture&gt;
            &lt;source
                srcset=&#34;https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/images/parts-overview_hu_e7c161a35ba0466d.jpg 600w, https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/images/parts-overview_hu_f6b0923759181a4d.jpg 1200w&#34; sizes=&#34;100vw&#34;
            /&gt;
          &lt;img loading=&#34;lazy&#34;
                src=&#34;https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/images/parts-overview_hu_c223de1a1389f4b8.jpg&#34;
              alt=&#34;Overview of all modding parts.&#34;
          /&gt;
        &lt;/picture&gt;
      &lt;/a&gt;
        &lt;figcaption&gt;
          Overview of all modding parts.
        &lt;/figcaption&gt;
    &lt;/figure&gt;
  &lt;/div&gt;

&lt;!-- spellchecker-enable --&gt;
&lt;p&gt;Fortunately the Game Boy is still very popular and has a strong modding community. After some research I found a sufficient IPS display mod as well as a replacement case that doesn&amp;rsquo;t need modifications to fit the new display. As most users were also very impressed by an audio mod kit I also wanted to give it a try. There are a hand full of different mod kit providers out there and a lot more if you want to look at eBay or Aliexpress. But as some users noticed a lot of quality ups and downs I decided to go with the parts from retrosix.&lt;/p&gt;
&lt;p&gt;After a few days all parts arrived and I could finally start. The updates are all very easy to install. The display doesn&amp;rsquo;t require any soldering. Optionally brightness control cables could be soldered to the shoulder buttons. The audio mod kit includes an amplifier and a new speaker. The installation in general is simple, but since the soldering points are sometimes very small, maybe this should not be your first soldering experience.&lt;/p&gt;
&lt;!-- spellchecker-disable --&gt;
&lt;div class=&#34;flex justify-center&#34;&gt;
    &lt;figure class=&#34;gblog-post__figure&#34;&gt;
      &lt;a class=&#34;gblog-markdown__link--raw&#34; href=&#34;https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/images/soldering.jpg&#34;&gt;
        &lt;picture&gt;
            &lt;source
                srcset=&#34;https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/images/soldering_hu_4f8ae61341344641.jpg 600w, https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/images/soldering_hu_8d65de3b3d18b87f.jpg 1200w&#34; sizes=&#34;100vw&#34;
            /&gt;
          &lt;img loading=&#34;lazy&#34;
                src=&#34;https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/images/soldering_hu_6b9e03f019520413.jpg&#34;
              alt=&#34;Soldered brightness control and audio mod kit.&#34;
          /&gt;
        &lt;/picture&gt;
      &lt;/a&gt;
        &lt;figcaption&gt;
          Soldered brightness control and audio mod kit.
        &lt;/figcaption&gt;
    &lt;/figure&gt;
  &lt;/div&gt;

&lt;!-- spellchecker-enable --&gt;
&lt;p&gt;After about one hour all parts were installed and cleaned and the Game Boy was reassembled. Time for a first test. What can I say, the effort was definitely worth it, the case feels valuable and all buttons have a good reaction and haptic feedback. Well, the display is the real star. Everything is bright and crisp and the colors are nice and natural.&lt;/p&gt;
&lt;!-- spellchecker-disable --&gt;
&lt;div class=&#34;flex justify-center&#34;&gt;
    &lt;figure class=&#34;gblog-post__figure&#34;&gt;
      &lt;a class=&#34;gblog-markdown__link--raw&#34; href=&#34;https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/images/gb-gba-comparison.jpg&#34;&gt;
        &lt;picture&gt;
            &lt;source
                srcset=&#34;https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/images/gb-gba-comparison_hu_fee6938571b5902f.jpg 600w, https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/images/gb-gba-comparison_hu_87130834229585a9.jpg 1200w&#34; sizes=&#34;100vw&#34;
            /&gt;
          &lt;img loading=&#34;lazy&#34;
                src=&#34;https://thegeeklab.de/posts/2020/09/how-to-modernize-a-game-boy/images/gb-gba-comparison_hu_3ea2ffa5280a20f1.jpg&#34;
              alt=&#34;Comparison between GB games (left) and GBA games (right).&#34;
          /&gt;
        &lt;/picture&gt;
      &lt;/a&gt;
        &lt;figcaption&gt;
          Comparison between GB games (left) and GBA games (right).
        &lt;/figcaption&gt;
    &lt;/figure&gt;
  &lt;/div&gt;

&lt;!-- spellchecker-enable --&gt;
&lt;p&gt;At the end I&amp;rsquo;m very happy with my &amp;ldquo;new&amp;rdquo; handheld but there are also some downsides I wanted to talk about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For me, the audio mod kit was a bad investment and I had to remove the amplifier board after a few hours of gaming. I agree the sound of the original speaker may be a bit flat and isn&amp;rsquo;t very loud. But thanks to the amplifier, the new speaker constantly screams into my face&amp;hellip; For me, it was impossible to find a sufficient and comfortable volume level. In the lower volume range the wheel jumped from still to loud to completely silent and could not be controlled precisely. Personally, the default maximum volume level is totally fine but that might be a personal preference.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To buy original Game Boy games could be a real challenge these days. I&amp;rsquo;m not really surprised about the prices, especially for rare games but there seems to be a lot fake games or replications out there. If you want to go with replications to save a bit money is up to you but if you just want authentic/original cartridges you have to be very careful.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;While I really love playing all GBA and GBC games with the GBA, I don&amp;rsquo;t really like the look of the classic GB games because of the ugly color scheme. This has nothing to do with the IPS screen directly, as far as I remember, this was already the case with the original GBC and GBA. The color scheme of some games really hurts my eyes and makes it almost impossible to play them. &lt;del&gt;Maybe I still need a Game Boy classic after all.&lt;/del&gt; There is already a &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;/posts/color-palettes-on-gbc-and-gba/&#34;
&gt;solution&lt;/a&gt;!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/gaming" term="gaming" label="Gaming" /> 
                                <category scheme="https://thegeeklab.de/tags/hardware" term="hardware" label="Hardware" />
        </entry>
        <entry>
            <title>Docker port publishing for localhost bindings</title>
            <link href="https://thegeeklab.de/posts/2020/09/docker-port-publishing-for-localhost-bindings/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2020/09/docker-port-publishing-for-localhost-bindings/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2020-09-08T22:15:00+02:00</published>
            <updated>2025-11-04T19:49:38+00:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2020/09/docker-port-publishing-for-localhost-bindings/images/feature_hu_4da0d60c7c984177.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;Docker port publishing for localhost bindings&#34; /&gt;&lt;br/&gt;&lt;p&gt;While preparing a custom Docker image for a tool I wanted to use I encountered a problem that kept me busy for some time. The container could be built and started without any problems but the application in the container was simply not accessible via the published port.&lt;/p&gt;
&lt;p&gt;Even after minutes of debugging and checking (and re-checking over and over again) that the right port was exposed and the application in the container is listening on that port I was not able to get it to work&amp;hellip; But I had a suspicion now. For some reason I had decided to bind the application in the container to localhost. Though it may sound obvious now, I didn&amp;rsquo;t expect that with a binding like &lt;code&gt;127.0.0.0:9000&lt;/code&gt; you can&amp;rsquo;t publish port &lt;code&gt;9000&lt;/code&gt; to the host; or maybe I&amp;rsquo;m just too stupid to fully understand Docker networking. After I changed the binding to &lt;code&gt;0.0.0.0:9000&lt;/code&gt; everything worked as expected. Anyway, lesson learned.&lt;/p&gt;

            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/sysadmin" term="sysadmin" label="Sysadmin" /> 
                                <category scheme="https://thegeeklab.de/tags/container" term="container" label="Container" /> 
                                <category scheme="https://thegeeklab.de/tags/today-i-learned" term="today-i-learned" label="Today I learned" />
        </entry>
        <entry>
            <title>Ansible and the relations to the inventory</title>
            <link href="https://thegeeklab.de/posts/2020/08/ansible-and-the-relations-to-the-inventory/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2020/08/ansible-and-the-relations-to-the-inventory/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2020-08-03T22:45:00+02:00</published>
            <updated>2025-11-04T19:49:38+00:00</updated>
            <content type="html">
                &lt;p&gt;I love Ansible and I&amp;rsquo;m pretty happy to have this configuration management solution. But some little &amp;ldquo;features&amp;rdquo; drive me crazy sometimes. My task for today was to switch from a static Ansible inventory file to multiple dynamic inventory script. In general this, should be really straight forward. Create an inventory folder and add some small Python scripts to create Ansible readable inventories from different providers.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve finished the first steps really quickly. After I&amp;rsquo;ve added a script for my home lab Proxmox VE host and a static inventory for a bunch of my Raspberries, I did a check with &lt;code&gt;ansible-inventory -i inventory/ --list&lt;/code&gt; and everything seems to be working as expected. All my hosts were listed and Ansible groups were applied as well. But here comes the clue: The test Playbook run failed, it looks like Ansible was not able to find the required variables. What the heck&amp;hellip;&lt;/p&gt;
&lt;blockquote class=&#34;gblog-hint note&#34;&gt;
  &lt;div class=&#34;gblog-hint__title flex gap-8 align-center&#34;&gt;&lt;i class=&#34;fa note&#34;&gt;&lt;/i&gt;
      &lt;span&gt;Note&lt;/span&gt;&lt;/div&gt;
  &lt;div class=&#34;gblog-hint__text&#34;&gt;&lt;strong&gt;Pro tip&lt;/strong&gt;&lt;br&gt;
There is one thing you should keep in mind, variables are related to the inventory file(s) or Playbooks.&lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let&amp;rsquo;s assume your Ansible structure looks like this:&lt;/p&gt;
&lt;!-- prettier-ignore-start --&gt;
&lt;!-- markdownlint-disable --&gt;
&lt;!-- spellchecker-disable --&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── group_vars
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── all.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── host_vars
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── inventory
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── playbooks
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&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;!-- spellchecker-enable --&gt;
&lt;!-- markdownlint-restore --&gt;
&lt;!-- prettier-ignore-end --&gt;
&lt;p&gt;Everything should work as expected, Ansible should be able to lookup the required &lt;code&gt;group_vars&lt;/code&gt; and &lt;code&gt;host_vars&lt;/code&gt;. After you switched to multiple inventories the structure might look this way:&lt;/p&gt;
&lt;!-- prettier-ignore-start --&gt;
&lt;!-- markdownlint-disable --&gt;
&lt;!-- spellchecker-disable --&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── group_vars
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── all.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── host_vars
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── inventory
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── dynamic_script.py
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── static
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── playbooks
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&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;!-- spellchecker-enable --&gt;
&lt;!-- markdownlint-restore --&gt;
&lt;!-- prettier-ignore-end --&gt;
&lt;p&gt;At this point Ansible will fail and complain about missing variables. Well, as mentioned above, variables are related to the inventory &lt;strong&gt;file(s)&lt;/strong&gt;! As &lt;code&gt;inventory&lt;/code&gt; is no longer a single file, this requirement is not met anymore. To get it working again I had to symlink the &lt;code&gt;group_vars&lt;/code&gt; and &lt;code&gt;host_vars&lt;/code&gt; folder into the &lt;code&gt;inventory&lt;/code&gt; folder. Obviously, right?&lt;/p&gt;
            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/sysadmin" term="sysadmin" label="Sysadmin" /> 
                                <category scheme="https://thegeeklab.de/tags/automation" term="automation" label="Automation" />
        </entry>
        <entry>
            <title>Create a static site hosting platform</title>
            <link href="https://thegeeklab.de/posts/2020/07/create-a-static-site-hosting-platform/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2020/07/create-a-static-site-hosting-platform/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2020-07-30T01:05:00+02:00</published>
            <updated>2026-01-30T10:48:54+01:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2020/07/create-a-static-site-hosting-platform/images/feature_hu_7b9d4c84d6b8d77.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;Create a static site hosting platform&#34; /&gt;&lt;br/&gt;&lt;p&gt;There are a lot of static site generators out there and users have a lot of possibilities to automate and continuously deploy static sites these days. Solutions like GitHub pages or Netlify are free to use and easy to set up, even a cheap web space could work. If one of these services is sufficient for your use case you could stop reading at this point.&lt;/p&gt;
&lt;p&gt;As I wanted to have more control over such a setup and because it might be fun I decided to create my own service. Before looking into the setup details, lets talk about some requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;deploy multiple project documentation&lt;/li&gt;
&lt;li&gt;use git repository name as subdomain&lt;/li&gt;
&lt;li&gt;easy CI workflow&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The required software stack is quite simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nginx as web server to deliver static files&lt;/li&gt;
&lt;li&gt;Minio S3 as files backend&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course, Minio could be removed from the stack but after a few tests, my personal impression was that Minio is a way better to handle file sync and uploads instead over SSH/SCP, at least in a CI driven workflow. The whole workflow is nearly the same as for GitHub pages. Right beside your projects source code in the Git repository you will have a &lt;code&gt;docs&lt;/code&gt; folder which contains the required files to build the documentation site. It&amp;rsquo;s up to you what static site generator to use. Instead of using e.g. the &lt;code&gt;gh-pages&lt;/code&gt; branch to publish the site, a CI system will build the documentation form the docs folder and publish it to a prepared Minio S3 bucket. The Nginx server will basically use the defined bucket as source directory and convert every sub-directory into something like &lt;code&gt;https://&amp;lt;reponame&amp;gt;.mydocs.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As a first step, install Minio and Nginx on a server, I will not cover the basic setup in this guide. To simplify the setup I will use a single server for Minio and Nginx but it&amp;rsquo;s also possible to split this into a Two-Tier architecture.&lt;/p&gt;
&lt;p&gt;After the basic setup it&amp;rsquo;s required to create a Minio bucket e.g. &lt;code&gt;mydocs&lt;/code&gt; using the Minio client command &lt;code&gt;mc mb local/mydocs&lt;/code&gt;. To allow Nginx to access these bucket to deliver the pages without authentication a bucket policy &lt;code&gt;mc policy set download local/mydocs&lt;/code&gt; need to be set. This policy will allow public read access. In theory, it should also be possible to add authentication headers to Nginx to server sites from private buckets but I have not tried that on my own.&lt;/p&gt;
&lt;p&gt;Preparing the Minio bucket was the easy part, now Nginx need to know how to rewrite the subdomains to sub-directories and properly deliver the sites. Let&amp;rsquo;s assume &lt;code&gt;mydocs&lt;/code&gt; is still used as the base Minio bucket and &lt;code&gt;mydocs.com&lt;/code&gt; as root domain. Here is how my current vHost configuration looks like:&lt;/p&gt;
&lt;!-- prettier-ignore-start --&gt;
&lt;!-- markdownlint-disable --&gt;
&lt;!-- spellchecker-disable --&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;upstream&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;backend_mydocs&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;localhost&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;61000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt;       &lt;span class=&#34;mi&#34;&gt;80&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;sr&#34;&gt;^(www\.)?(?&amp;lt;name&amp;gt;(.+\.)?mydocs\.com)$;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;301&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;https://&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name$request_uri&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt;       &lt;span class=&#34;mi&#34;&gt;443&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;ssl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;sr&#34;&gt;^((?&amp;lt;repo&amp;gt;.*)\.)mydocs\.com$;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;ssl_certificate&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;[..]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_certificate_key&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;[..]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;client_max_body_size&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;100M&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;recursive_error_pages&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_pass&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;http://backend_mydocs/mydocs/&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;${repo}${request_path}&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_http_version&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;.1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_buffering&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_connect_timeout&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;300&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_intercept_errors&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Host&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;X-Real-IP&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$remote_addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;X-Forwarded-For&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$scheme&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;rewrite&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;^([^.]*[^/])&lt;/span&gt;$ &lt;span class=&#34;nv&#34;&gt;$1/&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;permanent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;error_page&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;404&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/404_backend.html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/404_backend.html&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_pass&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;http://backend_mydocs/mydocs/&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;${repo}/404.html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_intercept_errors&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&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;!-- spellchecker-enable --&gt;
&lt;!-- markdownlint-restore --&gt;
&lt;!-- prettier-ignore-end --&gt;
&lt;p&gt;We will go through this configuration to understand how it works.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Lines 1-3&lt;/em&gt;&lt;/strong&gt; defines a backend, in this case it&amp;rsquo;s the Minio server running on &lt;code&gt;localhost:61000&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Lines 5-10&lt;/em&gt;&lt;/strong&gt; should also be straight forward, this block will redirect HTTP to HTTPS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Line 14&lt;/em&gt;&lt;/strong&gt; is where the magic starts. A named regular expression is used to capture the first part of the subdomain and translate it into the bucket sub-directory. For a given URL like &lt;code&gt;demo-project.mydocs.com&lt;/code&gt; Nginx will try to serve &lt;code&gt;mydocs/demo-project&lt;/code&gt; from the Minio server. That&amp;rsquo;s what &lt;strong&gt;&lt;em&gt;Line 23&lt;/em&gt;&lt;/strong&gt; does. Some of you may notice that the used variable &lt;code&gt;${request_path}&lt;/code&gt; is not defined in the vHost configuration.&lt;/p&gt;
&lt;p&gt;Right, another configuration snippet needs to be added to the &lt;code&gt;nginx.conf&lt;/code&gt;. But why is this variable required at all? For me, that was the hardest part to solve. As the setup is using &lt;code&gt;proxy_pass&lt;/code&gt; Nginx will &lt;em&gt;not&lt;/em&gt; try to lookup &lt;code&gt;index.html&lt;/code&gt; automatically. That&amp;rsquo;s a problem because every folder will at least contain an &lt;code&gt;index.html&lt;/code&gt;. In general, it&amp;rsquo;s required to tell Nginx to rewrite the request URI to &lt;code&gt;/index.html&lt;/code&gt; if the origin is a folder and ends with &lt;code&gt;/&lt;/code&gt;. One way would be an &lt;code&gt;if&lt;/code&gt; condition in the vHost configuration but such conditions are evil&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; in most cases and should be avoided if possible. Luckily there is a better option:&lt;/p&gt;
&lt;!-- prettier-ignore-start --&gt;
&lt;!-- markdownlint-disable --&gt;
&lt;!-- spellchecker-disable --&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;map&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$request_uri&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$request_path&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$request_uri&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;~/$&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;${request_uri}index.html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&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;!-- spellchecker-enable --&gt;
&lt;!-- markdownlint-restore --&gt;
&lt;!-- prettier-ignore-end --&gt;
&lt;p&gt;&lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://nginx.org/en/docs/http/ngx_http_map_module.html&#34;
&gt;Nginx maps&lt;/a&gt; are a solid way to create conditionals. In this example set &lt;code&gt;$request_uri&lt;/code&gt; as input and &lt;code&gt;$request_path&lt;/code&gt; as output. Each line between the braces is a condition. The first line will simply apply &lt;code&gt;$request_uri&lt;/code&gt; to the output variable if no other condition match. The second condition applies &lt;code&gt;${request_uri}index.html&lt;/code&gt; to the output variable if the input variable ends with a slash (and therefore is a directory).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Line 38-41&lt;/em&gt;&lt;/strong&gt; of the vHost configuration tries to deliver the custom error page of your site and will fallback to the default Nginx error page.&lt;/p&gt;
&lt;p&gt;We are done! Nginx should now be able to server your static sites from a sub-directory of the Minio source bucket. I&amp;rsquo;m using it since a few weeks and I&amp;rsquo;m really happy with the current setup.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;&lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/&#34;
&gt;This article&lt;/a&gt; from the Nginx team explains it very well.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/automation" term="automation" label="Automation" /> 
                                <category scheme="https://thegeeklab.de/tags/sysadmin" term="sysadmin" label="Sysadmin" />
        </entry>
        <entry>
            <title>Welcome (back)</title>
            <link href="https://thegeeklab.de/posts/2020/07/welcome-back/" rel="alternate" type="text/html"  hreflang="en" />
            <id>https://thegeeklab.de/posts/2020/07/welcome-back/</id>
                    <author>
                        <name>Robert Kaussow</name>
                    </author>
            <published>2020-07-21T23:00:08+02:00</published>
            <updated>2025-11-04T19:49:38+00:00</updated>
            <content type="html">
                &lt;img src=&#34;https://thegeeklab.de/posts/2020/07/welcome-back/images/feature_hu_634621ef87075917.jpg&#34; width=&#34;910&#34; height=&#34;280&#34; alt=&#34;Welcome (back)&#34; /&gt;&lt;br/&gt;&lt;p&gt;As some former readers may have noticed, &amp;ldquo;geeklabor.de&amp;rdquo; has been renamed to &amp;ldquo;thegeeklab.de&amp;rdquo;, welcome back nice to have you here again. If you are a first time visitor, a very warm welcome goes to you as well. This is my private blog, where I write about everything that comes to my mind but mainly about topics from the Linux and Open Source world.&lt;/p&gt;
&lt;p&gt;For those of you who are interested in the backgrounds about the blog migration, here you go:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;my old theme had to be reworked, &lt;a
  class=&#34;gblog-markdown__link&#34;
  href=&#34;https://github.com/thegeeklab/hugo-geekblog&#34;
&gt;hugo-geekblog&lt;/a&gt; was born
&lt;ul&gt;
&lt;li&gt;looks pretty much the same as before but with a more up to date technical implementation&lt;/li&gt;
&lt;li&gt;works with recent Hugo versions&lt;/li&gt;
&lt;li&gt;fully Open Source&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&amp;ldquo;geeklabor.de&amp;rdquo; does only work in German language, &amp;ldquo;thegeeklab.de&amp;rdquo; is bit more international&lt;/li&gt;
&lt;li&gt;the same applies to the entire blog, all content is only available in English for two simple reasons
&lt;ul&gt;
&lt;li&gt;English works for a lot more people&lt;/li&gt;
&lt;li&gt;I am too lazy to provide posts in multiple languages&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;the blog is running on a Hetzner Cloud machine; CI driven and using Minio S3 backend&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is a short summary right now. While I am currently still working on some remaining migration tasks, there will be more fresh posts during the next weeks - stay tuned!&lt;/p&gt;
            </content>  
                                <category scheme="https://thegeeklab.de/authors/robert-kaussow" term="robert-kaussow" label="robert-kaussow" />  
                                <category scheme="https://thegeeklab.de/tags/general" term="general" label="General" />
        </entry>
</feed>
